2 require 'metalua.extension.match'
4 module ('spmatch', package.seeall)
6 require 'metalua.walk.id'
10 ----------------------------------------------------------------------
11 -- Back-end for statements
12 -- "match function ..." and "local match function...".
13 -- Tag must be either "Localrec" or "Set".
14 ----------------------------------------------------------------------
15 named_match_function_builder = |tag| function (x)
16 local func_name, _, cases = unpack(x)
17 local arity = #cases[1][1][1]
19 error "There must be at least 1 case in match function"
22 for i=1, arity do args[i] = mlp.gensym("arg."..i) end
23 local body = match_builder{args, cases}
24 return { tag=tag, {func_name}, { `Function{ args, {body} } } }
27 -- Get rid of the former parser, it will be blended in a multiseq:
30 ----------------------------------------------------------------------
31 -- "match function", "match ... with"
32 ----------------------------------------------------------------------
33 mlp.stat:add{ 'match',
36 ----------------------------------------------------------------
37 -- Shortcut for declaration of functions containing only a match:
38 -- "function f($1) match $1 with $2 end end" can be written:
39 -- "match function f $2 end"
40 ----------------------------------------------------------------
41 { 'function', mlp.expr, gg.optkeyword '|',
42 match_cases_list_parser, 'end',
43 builder = named_match_function_builder 'Set' },
45 ----------------------------------------------------------------
46 -- Reintroduce the original match statement:
47 ----------------------------------------------------------------
48 default = gg.sequence{
49 mlp.expr_list, 'with', gg.optkeyword '|',
50 match_cases_list_parser, 'end',
51 builder = |x| match_builder{ x[1], x[3] } } } }
53 ----------------------------------------------------------------------
54 -- Shortcut: "local match function f $cases end" translates to:
55 -- "local function f($args) match $args with $cases end end"
56 ----------------------------------------------------------------------
57 mlp.stat:get'local'[2]:add{
58 'match', 'function', mlp.expr, gg.optkeyword '|',
59 match_cases_list_parser, 'end',
60 builder = named_match_function_builder 'Localrec' }
62 ----------------------------------------------------------------------
63 -- "match...with" expressions and "match function..."
64 ----------------------------------------------------------------------
65 mlp.expr:add{ 'match', builder = |x| x[1], gg.multisequence{
67 ----------------------------------------------------------------
68 -- Anonymous match functions:
69 -- "function ($1) match $1 with $2 end end" can be written:
70 -- "match function $2 end"
71 ----------------------------------------------------------------
72 { 'function', gg.optkeyword '|',
73 match_cases_list_parser,
76 local _, cases = unpack(x)
77 local v = mlp.gensym()
78 local body = match_builder{v, cases}
79 return `Function{ {v}, {body} }
82 ----------------------------------------------------------------
83 -- match expressions: you can put a match where an expression
84 -- is expected. The case bodies are then expected to be
85 -- expressions, not blocks.
86 ----------------------------------------------------------------
87 default = gg.sequence{
88 mlp.expr_list, 'with', gg.optkeyword '|',
89 gg.list{ name = "match cases list",
90 gg.sequence{ name = "match expr case",
91 gg.list{ name = "match expr case patterns list",
92 primary = mlp.expr_list,
94 terminators = { "->", "if" } },
95 gg.onkeyword{ "if", mlp.expr, consume = true },
97 mlp.expr }, -- Notice: expression, not block!
99 -- Notice: no "end" keyword!
100 builder = function (x)
101 local tested_term_seq, _, cases = unpack(x)
102 local v = mlp.gensym 'match_expr'
103 -- Replace expressions with blocks
104 for case in ivalues (cases) do
106 case[3] = { `Set{ {v}, {body} } }
108 local m = match_builder { tested_term_seq, cases }
109 return `Stat{ { `Local{{v}}; m }, v }
113 local patterns, values = unpack(x)
115 -------------------------------------------------------------------
116 -- Generate pattern code: "bind vars = vals" translates to:
118 -- pattern matching code, goto 'fail' on mismatch
120 -- label 'fail': error "..."
123 -- vars is the set of variables used by the pattern
124 -------------------------------------------------------------------
127 on_failure = mlp.gensym 'mismatch' [1],
130 pattern_seq_builder(patterns, values, match_cfg)
131 local on_success = mlp.gensym 'on_success' [1]
135 `Label{ match_cfg.on_failure };
136 +{error "bind error"};
137 `Label{ on_success } }
138 vars = match_cfg.locals
141 -------------------------------------------------------------------
142 -- variables that actually appear in the pattern:
143 -------------------------------------------------------------------
144 local vars_in_pattern do
145 vars_in_pattern = { }
146 local walk_cfg = { id = { } }
147 function walk_cfg.id.free(v) vars_in_pattern[v[1]]=true end
148 walk_id.expr_list(walk_cfg, patterns)
151 -------------------------------------------------------------------
152 -- temp variables that are generated for destructuring,
153 -- but aren't explicitly typed by the user. These must be made
155 -------------------------------------------------------------------
156 local vars_not_in_pattern do
157 vars_not_in_pattern = { }
158 for k in keys(vars) do
159 if not vars_in_pattern[k] then
160 vars_not_in_pattern[k] = true
165 -------------------------------------------------------------------
166 -- Declare the temp variables as local to the statement.
167 -------------------------------------------------------------------
168 if next(vars_not_in_pattern) then
170 for k in keys (vars_not_in_pattern) do
171 table.insert (loc, `Id{k})
173 table.insert (code, 1, `Local{ loc, { } })
176 -------------------------------------------------------------------
177 -- Transform the set of pattern variable names into a list of `Id{}
178 -------------------------------------------------------------------
181 for k in keys (vars_in_pattern) do
182 table.insert (decl_list, `Id{k})
186 return code, decl_list
189 function local_bind(x)
190 local code, vars = bind (x)
191 return { `Local{ vars, { } }; code }
194 function non_local_bind(x)
195 local code, _ = bind (x)
200 ----------------------------------------------------------------------
202 ----------------------------------------------------------------------
205 ----------------------------------------------------------------------
206 -- bind patterns = vars
207 ----------------------------------------------------------------------
208 mlp.stat:add{ 'bind', mlp.expr_list, '=', mlp.expr_list,
209 builder = non_local_bind }
211 ----------------------------------------------------------------------
212 -- local bind patterns = vars
213 -- Some monkey-patching of "local ..." must take place
214 ----------------------------------------------------------------------
215 mlp.stat:get'local'[2]:add{ 'bind', mlp.expr_list, '=', mlp.expr_list,
216 builder = local_bind }