+++ /dev/null
--{ extension 'match' }
--{ extension 'log' }
-
-require 'metalua.walk'
-
-----------------------------------------------------------------------
--- Back-end:
-----------------------------------------------------------------------
-
--- Parse additional elements in a loop
-loop_element = gg.multisequence{
- { 'while', mlp.expr, builder = |x| `Until{ `Op{ 'not', x[1] } } },
- { 'until', mlp.expr, builder = |x| `Until{ x[1] } },
- { 'if', mlp.expr, builder = |x| `If{ x[1] } },
- { 'unless', mlp.expr, builder = |x| `If{ `Op{ 'not', x[1] } } },
- { 'for', mlp.for_header, builder = |x| x[1] } }
-
--- Recompose the loop
-function xloop_builder(x)
- local first, elements, body = unpack(x)
-
- -------------------------------------------------------------------
- -- If it's a regular loop, don't bloat the code
- -------------------------------------------------------------------
- if not next(elements) then
- table.insert(first, body)
- return first
- end
-
- -------------------------------------------------------------------
- -- There's no reason to treat the first element in a special way
- -------------------------------------------------------------------
- table.insert(elements, 1, first)
-
- -------------------------------------------------------------------
- -- if a header or a break must be able to exit the loops, ti will
- -- set exit_label and use it (a regular break wouldn't be enough,
- -- as it couldn't escape several nested loops.)
- -------------------------------------------------------------------
- local exit_label
- local function exit()
- if not exit_label then exit_label = mlp.gensym 'break' [1] end
- return `Goto{ exit_label }
- end
-
- -------------------------------------------------------------------
- -- Compile all headers elements, from last to first
- -------------------------------------------------------------------
- for i = #elements, 1, -1 do
- local e = elements[i]
- match e with
- | `If{ cond } ->
- body = `If{ cond, {body} }
- | `Until{ cond } ->
- body = +{stat: if -{cond} then -{exit()} else -{body} end }
- | `Forin{ ... } | `Fornum{ ... } ->
- table.insert (e, {body}); body=e
- end
- end
-
- -------------------------------------------------------------------
- -- Change breaks into gotos that escape all loops at once.
- -------------------------------------------------------------------
- local cfg = { stat = { }, expr = { } }
- function cfg.stat.down(x)
- match x with
- | `Break -> x <- exit()
- | `Forin{ ... } | `Fornum{ ... } | `While{ ... } | `Repeat{ ... } ->
- return 'break'
- | _ -> -- pass
- end
- end
- function cfg.expr.down(x) if x.tag=='Function' then return 'break' end end
- walk.stat(cfg, body)
-
- if exit_label then body = { body, `Label{ exit_label } } end
- return body
-end
-
-----------------------------------------------------------------------
--- Front-end:
-----------------------------------------------------------------------
-
-mlp.lexer:add 'unless'
-mlp.stat:del 'for'
-mlp.stat:del 'while'
-
-loop_element_list = gg.list{ loop_element, terminators='do' }
-
-mlp.stat:add{
- 'for', mlp.for_header, loop_element_list, 'do', mlp.block, 'end',
- builder = xloop_builder }
-
-mlp.stat:add{
- 'while', mlp.expr, loop_element_list, 'do', mlp.block, 'end',
- builder = |x| xloop_builder{ `While{x[1]}, x[2], x[3] } }
-
-mlp.stat:add{
- 'unless', mlp.expr, 'then', mlp.block, 'end',
- builder = |x| +{stat: if not -{x[1]} then -{x[2]} end} }