module ('spmatch', package.seeall)
+----------------------------------------------------------------------
+-- This would have been best done through library 'metalua.walk',
+-- but walk depends on match, so we have to break the dependency.
+-- It replaces all instances of `...' in `ast' with `term', unless
+-- it appears in a function.
+----------------------------------------------------------------------
+function replace_dots (ast, term)
+ local function rec (x)
+ if type(x) == 'table' then
+ if x.tag=='Dots' then
+ if term=='ambiguous' then
+ error ("You can't use `...' on the right of a match case when it appears "..
+ "more than once on the left")
+ else
+ x <- term
+ end
+ elseif x.tag=='Function' then return
+ else for y in ivalues (x) do rec (y) end end
+ end
+ end
+ return rec (ast)
+end
+
tmpvar_base = mlp.gensym 'submatch.' [1]
function next_tmpvar(cfg)
assert (cfg.ntmp, "No cfg.ntmp imbrication level in the match compiler")
acc_test (+{ -{sub_pattern} == nil }, cfg)
end
elseif sub_pattern.tag == "Dots" then
- -- Remember to suppress arity checking
+ -- Remember where the capture is, and thatt arity checking shouldn't occur
seen_dots = true
else
-- Business as usual:
-- TODO: restore ntmp?
end
end
- if not seen_dots then -- Check arity
+ if seen_dots then -- remember how to retrieve `...'
+ -- FIXME: check, but there might be cases where the variable -{term}
+ -- will be overridden in contrieved tables.
+ -- ==> save it now, and clean the setting statement if unused
+ if cfg.dots_replacement then cfg.dots_replacement = 'ambiguous'
+ else cfg.dots_replacement = +{ select (-{`Number{len}}, unpack(-{term})) } end
+ else -- Check arity
acc_test (+{ #-{term} ~= -{`Number{len}} }, cfg)
end
end
for i = 1, #patterns_group do
local pattern_seq = patterns_group[i]
cfg.on_failure = mlp.gensym 'match_fail' [1]
+ cfg.dots_replacement = false
pattern_seq_builder (pattern_seq, term_seq, cfg)
if i<#patterns_group then
acc_stat (`Goto{on_success}, cfg)
end
acc_stat (`Label{on_success}, cfg)
if guard then acc_test (+{not -{guard}}, cfg) end
+ if cfg.dots_replacement then
+ eprintf ("Dots replacement required in a match")
+ replace_dots (block, cfg.dots_replacement)
+ end
block.tag = 'Do'
acc_stat (block, cfg)
acc_stat (`Goto{cfg.after_success}, cfg)
for i=1, #cases do
local case_cfg = {
- after_success = cfg.after_success,
- code = `Do{ },
- locals = { } }
+ after_success = cfg.after_success,
+ code = `Do{ }
+ -- locals = { } -- unnecessary, done by pattern_seq_builder
+ }
case_builder (cases[i], term_seq, case_cfg)
if next (case_cfg.locals) then
local case_locals = { }
local r = supercfg.expr.down(x, ...)
if r then return r end
end
+ local parents = {...}
match x with
| `Id{ name } ->
local binder, r = scope.current[name] -- binder :: ast which bound var
if binder then
--$log( 'walk.id found a bound var:', x, binder)
- r = visit_bound_var(x, binder, ...)
+ r = visit_bound_var(x, binder, unpack(parents))
else
--$log( 'walk.id found a free var:', x, scope.current)
- r = visit_free_var(x, ...)
+ r = visit_free_var(x, unpack(parents))
end
if r then return r end
| `Function{ params, _ } -> scope:push (params, x)
-------------------------------------------------------------
scope:push()
for stat in values (block) do walk.stat(cfg, stat, x, ...) end
- walk.expr(cfg, expr, x, ...)
+ walk.expr(cfg, expr, x, unpack(parents))
scope:pop()
return 'break'
| _ -> -- pass