+++ /dev/null
-
-require 'metalua.extension.match'
-
-module ('spmatch', package.seeall)
-
-require 'metalua.walk.id'
-
--{extension 'log'}
-
-----------------------------------------------------------------------
--- Back-end for statements
--- "match function ..." and "local match function...".
--- Tag must be either "Localrec" or "Set".
-----------------------------------------------------------------------
-named_match_function_builder = |tag| function (x)
- local func_name, _, cases = unpack(x)
- local arity = #cases[1][1][1]
- if arity==0 then
- error "There must be at least 1 case in match function"
- end
- local args = { }
- for i=1, arity do args[i] = mlp.gensym("arg."..i) end
- local body = match_builder{args, cases}
- return { tag=tag, {func_name}, { `Function{ args, {body} } } }
-end
-
--- Get rid of the former parser, it will be blended in a multiseq:
-mlp.stat:del 'match'
-
-----------------------------------------------------------------------
--- "match function", "match ... with"
-----------------------------------------------------------------------
-mlp.stat:add{ 'match',
- gg.multisequence{
-
- ----------------------------------------------------------------
- -- Shortcut for declaration of functions containing only a match:
- -- "function f($1) match $1 with $2 end end" can be written:
- -- "match function f $2 end"
- ----------------------------------------------------------------
- { 'function', mlp.expr, gg.optkeyword '|',
- match_cases_list_parser, 'end',
- builder = named_match_function_builder 'Set' },
-
- ----------------------------------------------------------------
- -- Reintroduce the original match statement:
- ----------------------------------------------------------------
- default = gg.sequence{
- mlp.expr_list, 'with', gg.optkeyword '|',
- match_cases_list_parser, 'end',
- builder = |x| match_builder{ x[1], x[3] } } } }
-
-----------------------------------------------------------------------
--- Shortcut: "local match function f $cases end" translates to:
--- "local function f($args) match $args with $cases end end"
-----------------------------------------------------------------------
-mlp.stat:get'local'[2]:add{
- 'match', 'function', mlp.expr, gg.optkeyword '|',
- match_cases_list_parser, 'end',
- builder = named_match_function_builder 'Localrec' }
-
-----------------------------------------------------------------------
--- "match...with" expressions and "match function..."
-----------------------------------------------------------------------
-mlp.expr:add{ 'match', builder = |x| x[1], gg.multisequence{
-
- ----------------------------------------------------------------
- -- Anonymous match functions:
- -- "function ($1) match $1 with $2 end end" can be written:
- -- "match function $2 end"
- ----------------------------------------------------------------
- { 'function', gg.optkeyword '|',
- match_cases_list_parser,
- 'end',
- builder = function(x)
- local _, cases = unpack(x)
- local v = mlp.gensym()
- local body = match_builder{v, cases}
- return `Function{ {v}, {body} }
- end },
-
- ----------------------------------------------------------------
- -- match expressions: you can put a match where an expression
- -- is expected. The case bodies are then expected to be
- -- expressions, not blocks.
- ----------------------------------------------------------------
- default = gg.sequence{
- mlp.expr_list, 'with', gg.optkeyword '|',
- gg.list{ name = "match cases list",
- gg.sequence{ name = "match expr case",
- gg.list{ name = "match expr case patterns list",
- primary = mlp.expr_list,
- separators = "|",
- terminators = { "->", "if" } },
- gg.onkeyword{ "if", mlp.expr, consume = true },
- "->",
- mlp.expr }, -- Notice: expression, not block!
- separators = "|" },
- -- Notice: no "end" keyword!
- builder = function (x)
- local tested_term_seq, _, cases = unpack(x)
- local v = mlp.gensym 'match_expr'
- -- Replace expressions with blocks
- for case in ivalues (cases) do
- local body = case[3]
- case[3] = { `Set{ {v}, {body} } }
- end
- local m = match_builder { tested_term_seq, cases }
- return `Stat{ { `Local{{v}}; m }, v }
- end } } }
-
-function bind (x)
- local patterns, values = unpack(x)
-
- -------------------------------------------------------------------
- -- Generate pattern code: "bind vars = vals" translates to:
- -- do
- -- pattern matching code, goto 'fail' on mismatch
- -- goto 'success'
- -- label 'fail': error "..."
- -- label success
- -- end
- -- vars is the set of variables used by the pattern
- -------------------------------------------------------------------
- local code, vars do
- local match_cfg = {
- on_failure = mlp.gensym 'mismatch' [1],
- locals = { },
- code = { } }
- pattern_seq_builder(patterns, values, match_cfg)
- local on_success = mlp.gensym 'on_success' [1]
- code = {
- match_cfg.code;
- `Goto{ on_success };
- `Label{ match_cfg.on_failure };
- +{error "bind error"};
- `Label{ on_success } }
- vars = match_cfg.locals
- end
-
- -------------------------------------------------------------------
- -- variables that actually appear in the pattern:
- -------------------------------------------------------------------
- local vars_in_pattern do
- vars_in_pattern = { }
- local walk_cfg = { id = { } }
- function walk_cfg.id.free(v) vars_in_pattern[v[1]]=true end
- walk_id.expr_list(walk_cfg, patterns)
- end
-
- -------------------------------------------------------------------
- -- temp variables that are generated for destructuring,
- -- but aren't explicitly typed by the user. These must be made
- -- local.
- -------------------------------------------------------------------
- local vars_not_in_pattern do
- vars_not_in_pattern = { }
- for k in keys(vars) do
- if not vars_in_pattern[k] then
- vars_not_in_pattern[k] = true
- end
- end
- end
-
- -------------------------------------------------------------------
- -- Declare the temp variables as local to the statement.
- -------------------------------------------------------------------
- if next(vars_not_in_pattern) then
- local loc = { }
- for k in keys (vars_not_in_pattern) do
- table.insert (loc, `Id{k})
- end
- table.insert (code, 1, `Local{ loc, { } })
- end
-
- -------------------------------------------------------------------
- -- Transform the set of pattern variable names into a list of `Id{}
- -------------------------------------------------------------------
- local decl_list do
- decl_list = { }
- for k in keys (vars_in_pattern) do
- table.insert (decl_list, `Id{k})
- end
- end
-
- return code, decl_list
-end
-
-function local_bind(x)
- local code, vars = bind (x)
- return { `Local{ vars, { } }; code }
-end
-
-function non_local_bind(x)
- local code, _ = bind (x)
- code.tag = 'Do'
- return code
-end
-
-----------------------------------------------------------------------
--- Syntax front-end
-----------------------------------------------------------------------
-mlp.lexer:add 'bind'
-
-----------------------------------------------------------------------
--- bind patterns = vars
-----------------------------------------------------------------------
-mlp.stat:add{ 'bind', mlp.expr_list, '=', mlp.expr_list,
- builder = non_local_bind }
-
-----------------------------------------------------------------------
--- local bind patterns = vars
--- Some monkey-patching of "local ..." must take place
-----------------------------------------------------------------------
-mlp.stat:get'local'[2]:add{ 'bind', mlp.expr_list, '=', mlp.expr_list,
- builder = local_bind }