]> git.lizzy.rs Git - metalua.git/blobdiff - src/samples/ifexpr.mlua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[metalua.git] / src / samples / ifexpr.mlua
diff --git a/src/samples/ifexpr.mlua b/src/samples/ifexpr.mlua
deleted file mode 100644 (file)
index d0627af..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-----------------------------------------------------------------------------------
--- 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:
---
--- > local foo = if bar then 1 else 2
---
--- 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
---
---
--- 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', 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.
-   -- v:                the variable in which the result will be stored.
-   -- ifstat:           the if-then-else statement that will be generated from
-   --                   then if-then-else expression, then embedded in a `Stat{}
-   --
-   -- The builder simply turns all expressions into blocks, so that they fit in
-   -- a regular if-then-else statement. Then the resulting if-then-else is
-   -- embedded in a `Stat{ } node, so that it can be placed where an expression
-   -- is expected.
-   --
-   -- 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.
-   -- 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)
-
-      local v = mlp.gensym 'ife' -- the selected expr will be stored in this var.
-      local ifstat = `If{ }
-      for y in ivalues (elseifthen_list) do
-         local cond, val = unpack (y)
-         table.insert (ifstat, cond)
-         table.insert (ifstat, { `Set{ {v}, {val} } }) -- change expr into stat.
-      end
-      if opt_else then -- the same for else clause, except that there's no cond.
-         table.insert (ifstat, { `Set{ {v}, {opt_else} } })
-      end
-      return `Stat{ +{block: local -{v}; -{ifstat}}, v }
-   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;
-   --
-   -- In Lua, 'end' traditionally terminates a block, not an
-   -- expression. Should there be a 'end' to respect if-then-else
-   -- usual syntax, or should there be none, to respect usual implicit
-   -- expression ending? I chose not to put an 'end' here, but other people
-   -- might have other tastes...
-   -------------------------------------------------------------------------------
-   mlp.expr:add{ name = 'if-expression',
-      'if',
-      gg.list { gg.sequence{mlp.expr, "then", mlp.expr}, separators="elseif" },
-      gg.onkeyword{ 'else', mlp.expr }, 
-      builder = builder } 
-
-} -- Back to metalevel 1, with the new feature enabled
-
-local foo, bar
-
-------------------------------------------------------------
--- The parser will read this as:
---    { { { `Id 'foo', `Number 1 }, 
---        { `Id 'bar', `Number 2 } }, 
---      `Number 3 },
--- then feed it to 'builder', which will turn it into an AST
-------------------------------------------------------------
-
-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$" }
-------------------------------------------------------------
-
-assert (x == 3)
-print "It seems to work..."
\ No newline at end of file