| ------------------------------------------------------------------------------- |
| -- Copyright (c) 2006-2013 Fabien Fleutot and others. |
| -- |
| -- All rights reserved. |
| -- |
| -- This program and the accompanying materials are made available |
| -- under the terms of the Eclipse Public License v1.0 which |
| -- accompanies this distribution, and is available at |
| -- http://www.eclipse.org/legal/epl-v10.html |
| -- |
| -- This program and the accompanying materials are also made available |
| -- under the terms of the MIT public license which accompanies this |
| -- distribution, and is available at http://www.lua.org/license.html |
| -- |
| -- Contributors: |
| -- Fabien Fleutot - API and implementation |
| -- |
| ------------------------------------------------------------------------------- |
| |
| -{ 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} } |