]> git.lizzy.rs Git - metalua.git/commitdiff
xloop and xmatch fixes
authorFabien Fleutot <fabien@macfabien.local>
Tue, 26 Feb 2008 21:33:05 +0000 (22:33 +0100)
committerFabien Fleutot <fabien@macfabien.local>
Tue, 26 Feb 2008 21:33:05 +0000 (22:33 +0100)
src/lib/extension/xloop.mlua
src/lib/extension/xmatch.mlua
src/samples/bind_test.mlua [deleted file]
src/samples/clist_test.mlua
src/samples/ifexpr.mlua
src/samples/xmatch_test.mlua

index 9964504ae7d1623c2b1728f5cd1b13d523fc2fbc..bce65b08261ce0bfdbbe176beb6b59c7b8180c95 100644 (file)
@@ -1,35 +1,51 @@
 -{ extension 'match' }
 -{ extension 'log' }
+
 require '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] } },
-   { 'for',   mlp.for_header, builder = |x| x[1] } }
+   { '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)
 
-   -- Breaks might have to escape several loops, so we'll rather use
-   -- a goto
+   -------------------------------------------------------------------
+   -- 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
@@ -42,7 +58,9 @@ function xloop_builder(x)
       end
    end
 
+   -------------------------------------------------------------------
    -- Change breaks into gotos that escape all loops at once.
+   -------------------------------------------------------------------
    local cfg = { stat = { }, expr = { } }
    function cfg.stat.down(x)
       match x with
@@ -54,14 +72,20 @@ function xloop_builder(x)
    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
 
-loop_element_list = gg.list{ loop_element, terminators='do' }
+----------------------------------------------------------------------
+-- Front-end:
+----------------------------------------------------------------------
+
+mlp.lexer:add 'unless'
+mlp.stat:del  'for'
+mlp.stat:del  'while'
 
-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', 
@@ -71,3 +95,6 @@ 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} }
index 8201beb50da80e85031b23c01443abd2c12926d3..2be675106b47291664de42c754cc0036a60d99cd 100755 (executable)
@@ -2,7 +2,11 @@ require 'extension.match'
 module ('spmatch', package.seeall)\r
 -{extension 'log'}\r
 \r
-match_function_builder = |tag| function (x)                      \r
+----------------------------------------------------------------------\r
+-- Back-end for "match function ..." and "local match function..."\r
+-- Tag must be either "Localrec" or "Set".\r
+----------------------------------------------------------------------\r
+named_match_function_builder = |tag| function (x)                      \r
    local func_name, _, cases = unpack(x)\r
    local arity = #cases[1][1][1]\r
    if arity==0 then \r
@@ -17,6 +21,9 @@ end
 -- Get rid of the former parser, it will be blended in a multiseq:\r
 mlp.stat:del 'match'\r
 \r
+----------------------------------------------------------------------\r
+-- "match function", "match ... with"\r
+----------------------------------------------------------------------\r
 mlp.stat:add{ 'match', \r
    gg.multisequence{\r
 \r
@@ -46,6 +53,9 @@ mlp.stat:get'local'[2]:add{
    match_cases_list_parser, 'end',\r
    builder = match_function_builder 'Localrec' }\r
 \r
+----------------------------------------------------------------------\r
+-- "match...with" expressions and "match function..."\r
+----------------------------------------------------------------------\r
 mlp.expr:add{ 'match', builder = |x| x[1], gg.multisequence{\r
 \r
       ----------------------------------------------------------------\r
@@ -78,10 +88,9 @@ mlp.expr:add{ 'match', builder = |x| x[1], gg.multisequence{
                   terminators = { "->", "if" } },\r
                gg.onkeyword{ "if", mlp.expr, consume = true },\r
                "->",\r
-               mlp.expr },\r
-            separators  = "|",\r
-            terminators = "end" },\r
-         'end',\r
+               mlp.expr }, -- Notice: expression, not block!\r
+            separators  = "|" },\r
+         -- Notice: no "end" keyword!\r
          builder = function (x)\r
             local tested_term_seq, _, cases = unpack(x)\r
             local v = mlp.gensym 'match_expr'\r
diff --git a/src/samples/bind_test.mlua b/src/samples/bind_test.mlua
deleted file mode 100644 (file)
index 056eee4..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
--{ extension 'xmatch' }
-
-bind {a, b} = {'o', 'k'}
-print(a..b)
-
-c, d = 'k', 'o'
-do
-   local bind {c, {d}} = {'o', {'k'}}
-   print(c..d)
-end
-
-print(d..c)
-   
index 0db236cc18015e9a0763d40c36bf444962d21207..7c3133df3657a28c8007ef1952772253f26abd07 100644 (file)
@@ -15,5 +15,6 @@ while n < #sieve do
    n += 1
 end
 
+print "Prime numbers < 100, computed with lists by comprehension:"
 table.print(sieve)
 
index 3ac76d6cc9ea39c796746443499f481155eb058e..5b3093f2aae4ce4f98758ff44eaa351640c645a9 100644 (file)
@@ -1,33 +1,32 @@
 ----------------------------------------------------------------------------------
+-- This samples walks you through the writing of a simple extension.
 --
 -- Lua makes a difference between statements and expressions, and it's sometimes
--- cumbersome to put a statement where an expression is expected.
--- Among others, if-then-else constructs are statements, so you cannot write:
+-- cumbersome to put a statement where an expression is expected. Among others,
+-- if-then-else constructs are statements, so you cannot write:
 --
 -- > local foo = if bar then 1 else 2
 --
--- Indeed, an expression is expected at the right of the equal, and statements are
--- expected after a 'then' or an 'else'. The example above must be written:
+-- Indeed, an expression is expected at the right of the equal, and "if ..." is
+-- a statement, which expects nested statements as "then" and "else" clauses.
+-- The example above must therefore be written:
 --
 -- > local foo
 -- > if bar then foo=1 else foo=2 end
 --
--- The present sample allows if-then-[elseif*]-[else] constructs to be used in
--- an expression's context. In such a context, 'then' and 'else' are expected to
--- be followed by an expression, not a block of statements.
 --
--- Stuff you probably need to understand, at least summarily, to follow this code:
+-- Let's allow if-then-[elseif*]-[else] constructs to be used in an expression's
+-- context. In such a context, 'then' and 'else' are expected to be followed by
+-- expressions, not statement blocks.
 --
+-- Stuff you probably need to understand, at least summarily, to follow this
+-- code:
 -- * Lua syntax
---
 -- * the fact that -{ ... } switches metalua into compile time mode
---
 -- * mlp, the dynamically extensible metalua parser, which will be extended with
 --   the new construct at compile time.
---
 -- * gg, the grammar generator that allows to build and extend parsers, and with
 --   which mlp is implemented.
---
 -- * the fact that code can be interchangeably represented as abstract syntax
 --   trees with `Foo{ bar } notations (easy to manipulate) or as quotes inside a
 --   +{ ... } (easy to read).
 
 
 ----------------------------------------------------------------------------------
---
 -- How to turn this file in a proper syntax extension.
 -- ===================================================
 --
 -- To turn this example's metalevel 0 code into a regular extension:
---
 -- * Put everything inside the -{block: ... } in a separate .mlua file;
---
--- * save it in a directory called 'extension-compiler', which is itself
+-- * save it in a directory called 'extension', which is itself
 --   in your $LUA_MPATH. For instance, if your $LUA_MPATH contains
 --   '~/local/metalua/?.mlua', you can save it as 
 --   '~/local/metalua/extension-compiler/ifexpr.mlua'
---
 -- * Load the extension with "-{ extension 'ifexpr' }", whenever you want to
 --   use it.
---
 ----------------------------------------------------------------------------------
 
 -{ block: -- Enter metalevel 0, where we'll start hacking the parser.
 
    -------------------------------------------------------------------------------
+   -- Most extension implementations are cut in two parts: a front-end which
+   -- parses the syntax into some custom tree, and a back-end which turns that
+   -- tree into a compilable AST. Since the front-end calls the back-end, the
+   -- later has to be declared first.
+   -------------------------------------------------------------------------------
+
+   -------------------------------------------------------------------------------
+   -- Back-end:
+   -- =========
    -- This is the builder that turns the parser's result into an expression AST.
-   --
+   -- Local vars:
+   -- -----------
    -- elseifthen_list : list of { condition, expression_if_true } pairs,
    -- opt_else:         either the expression in the 'else' final clause if any,
    --                   or false if there's no else clause.
@@ -73,7 +77,8 @@
    --
    -- The variable in which the result is stored has its name generated by
    -- mlp.gensym(). This way we're sure there will be no variable capture.
-   -- This won't be necessary anymore once hygienic macros will be in place.
+   -- When macro hygiene problems are more complex, it's generally a good
+   -- idea to give a look at the extension 'H'.
    -------------------------------------------------------------------------------
    local function builder (x)
       local elseifthen_list, opt_else = unpack (x)
@@ -92,6 +97,8 @@
    end
 
    -------------------------------------------------------------------------------
+   -- Front-end:
+   -- ==========
    -- This is mostly the same as the regular if-then-else parser, except that:
    -- * it's added to the expression parser, not the statement parser;
    -- * blocks after conditions are replaced by exprs;
@@ -114,11 +121,9 @@ local foo, bar
 
 ------------------------------------------------------------
 -- The parser will read this as:
---
--- { { { `Id 'foo', `Number 1 }, 
---     { `Id 'bar', `Number 2 } }, 
---   `Number 3 }
---
+--    { { { `Id 'foo', `Number 1 }, 
+--        { `Id 'bar', `Number 2 } }, 
+--      `Number 3 },
 -- then feed it to 'builder', which will turn it into an AST
 ------------------------------------------------------------
 
@@ -126,12 +131,11 @@ local x = if false then 1 elseif bar then 2 else 3
 
 ------------------------------------------------------------
 -- The result after builder will be:
---
--- `Stat{ +{block: local $v$
---                 if     foo then $v$ = 1
---                 elseif bar then $v$ = 2
---                 else            $v$ = 3
---                 end }, `Id "$v$" }
+--    `Stat{ +{block: local $v$
+--                    if     foo then $v$ = 1
+--                    elseif bar then $v$ = 2
+--                    else            $v$ = 3
+--                    end }, `Id "$v$" }
 ------------------------------------------------------------
 
 assert (x == 3)
index ab8d6260638bfb3793d963fb7e1077d07cb6a03e..de907e8cfca6f2a059a7d8b643f37b986d6d5b8d 100755 (executable)
@@ -5,7 +5,7 @@ function p(msg) io.write(msg..' ':rep(WIDTH-#msg)) end
 \r
 ----------------------------------------------------------------------\r
 p"match as an expression"\r
-print(match 1 with 1 -> 'ok' | 2 -> 'KO' end)\r
+print(match 1 with 1 -> 'ok' | 2 -> 'KO')\r
 \r
 ----------------------------------------------------------------------\r
 p "global match function"\r