]> git.lizzy.rs Git - metalua.git/commitdiff
introduce xmatch, the extended match library: destructuring binders, syntax suger...
authorFabien Fleutot <fabien@macfabien.local>
Wed, 20 Feb 2008 20:37:45 +0000 (21:37 +0100)
committerFabien Fleutot <fabien@macfabien.local>
Wed, 20 Feb 2008 20:37:45 +0000 (21:37 +0100)
src/compiler/compile.lua
src/compiler/lexer.lua
src/compiler/metalua.mlua
src/compiler/mlc.mlua
src/lib/clopts.mlua
src/lib/extension/dset.mlua [deleted file]
src/lib/extension/match.mlua
src/lib/extension/xmatch.mlua [new file with mode: 0755]
src/samples/bind_test.mlua [new file with mode: 0644]
src/samples/dset_test.mlua [deleted file]
src/samples/xmatch_test.mlua [new file with mode: 0755]

index 33c49f56b8b6567229fbb3784d55d399d3ebb32d..0e483bca7fd4a4d4c78ea45ad94350429adeb186 100644 (file)
@@ -1052,7 +1052,7 @@ function expr.expr (fs, ast, v)
    local parser = expr[ast.tag]
    if parser then parser (fs, ast, v)
    elseif not ast.tag then 
-      error ("No tag in expression "..table.tostring(ast))
+      error ("No tag in expression "..table.tostring(ast, 'nohash', 80))
    else 
       error ("No parser for node `"..ast.tag) end
    debugf (" - /`%s", ast.tag)
index 95a753eae5401bae26064e80ea78ab4e386138be..23da29c9ab6e5297861e544bc302ea23a2dc39dd 100644 (file)
@@ -359,11 +359,12 @@ function lexer:newstream (src_or_stream)
    if type(src_or_stream)=='table' then -- it's a stream
       return setmetatable({ }, self):takeover(src_or_stream)
    elseif type(src_or_stream)=='string' then -- it's a source string
+      local src = src_or_stream
       local stream = { 
-         src    = src_or_stream; -- The source, as a single string
-         peeked = { };           -- Already peeked, but not discarded yet, tokens
-         i      = 1;             -- Character offset in src
-         line   = 1;             -- Current line number
+         src    = src; -- The source, as a single string
+         peeked = { }; -- Already peeked, but not discarded yet, tokens
+         i      = 1;   -- Character offset in src
+         line   = 1;   -- Current line number
       }
       setmetatable (stream, self)
 
index 0f9249fe7a62495c63d8928e283afae2dd034a0d..9bb52bf5ac83532bf370de61613e498c19e6d9d2 100644 (file)
@@ -194,7 +194,7 @@ local function main (...)
    end
 
    -- FIXME: check for failures
-   -- FIXME: handle metabugs
+   mlc.metabugs = cfg.metabugs
    local bytecode = mlc.luacstring_of_ast (code)
    code = nil
 
index b4a3a46b798d23c3ab546c21d4af1e4f2d489ce5..6a8890a2b53ab592a3e68e859b018f219bc78c17 100644 (file)
@@ -89,7 +89,7 @@ function mlc.convert (x, src_fmt, dst_fmt, name)
 
    local status -- status = compilation success
    local lx=x
-   if SHOW_METABUGS
+   if mlc.metabugs
    -- If SHOW_METABUGS is true, errors should be attributed to a parser bug.
    then status, x = true, mlp.chunk (lx)
    -- If SHOW_METABUGS is false, errors should be attributed to an invalid entry.
index 896a2ea2560a9060317662d2126cf6fc0be22f10..7c9af30a4aeca6c7dcaab2b08c1c2b960bdf4ee8 100644 (file)
@@ -141,7 +141,7 @@ function clopts(cfg)
       local args = type(...)=='table' and ... or {...}
       local i, i_max = 1, #args
       while i <= i_max do         
-         local arg, flags, opts, opt = args[i]
+         local arg, flag, opt, opts = args[i]
          --printf('beginning of loop: i=%i/%i, arg=%q', i, i_max, arg)
          if arg=='-' then
             i=actionate (cfg, short, '-', '', i, args)
diff --git a/src/lib/extension/dset.mlua b/src/lib/extension/dset.mlua
deleted file mode 100644 (file)
index 6a88ad7..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-require 'extension.match'
-require 'walk.id'
-
-local function destructuring_set (x)
-   local patterns, values = unpack(x)
-   
-   local code, vars do
-      local mcfg = {
-         on_failure = mlp.gensym 'mismatch' [1],
-         locals = { },
-         code = { } }
-      spmatch.pattern_seq_builder(patterns, values, mcfg)
-      local on_success = mlp.gensym 'on_success' [1]
-      code = { 
-         mcfg.code; 
-         `Goto{ on_success }; 
-         `Label{ mcfg.on_failure }; 
-         +{error "Destructuring bind error"};
-         `Label{ on_success } }
-      vars = mcfg.locals
-   end
-   
-   local vars_in_pattern do
-      vars_in_pattern = { }
-      local wcfg = { id = { } }
-      function wcfg.id.free(v) printf("%s is free", v[1]); vars_in_pattern[v[1]]=true end
-      walk_id.expr_list(wcfg, patterns)
-   end
-
-   local vars_not_in_pattern do
-      vars_not_in_pattern = { }
-      for k in keys(vars) do
-         if not vars_in_pattern[k] then
-            vars_not_in_pattern[k] = true
-         end
-      end
-   end
-
-   if next(vars_not_in_pattern) then
-      local loc = { }
-      for k in keys (vars_not_in_pattern) do 
-         table.insert (loc, `Id{k})
-      end
-      table.insert (code, 1, `Local{ loc, { } })
-   end
-
-   local decl_list do
-      decl_list = { }
-      for k in keys (vars_in_pattern) do
-         table.insert (decl_list, `Id{k})
-      end
-   end
-
-   return code, decl_list
-end
-
-function local_destructuring_set(x)
-   local code, vars = destructuring_set (x)
-   return { `Local{ vars, { } }; code }
-end
-
-function non_local_destructuring_set(x)
-   local code, _ = destructuring_set (x)
-   code.tag = 'Do'
-   return code
-end
-
-mlp.lexer:add 'bind'
-mlp.stat:add{ 'bind', mlp.expr_list, '=', mlp.expr_list, 
-   builder = non_local_destructuring_set }
-mlp.stat:get'local'[2]:add{ 'bind', mlp.expr_list, '=', mlp.expr_list, 
-   builder = local_destructuring_set }
\ No newline at end of file
index ed9a24f04501b2d90bb996c02bed7ba54208d83b..5b4c53ab3e0548c9048ac302a54aab0d8775c64e 100644 (file)
@@ -70,7 +70,6 @@
 -- TODO: hygiene wrt type()
 
 module ('spmatch', package.seeall)
-require 'strict'
 
 tmpvar_base = mlp.gensym 'table_submatch.' [1]
 function next_tmpvar(cfg)
diff --git a/src/lib/extension/xmatch.mlua b/src/lib/extension/xmatch.mlua
new file mode 100755 (executable)
index 0000000..3fe781d
--- /dev/null
@@ -0,0 +1,194 @@
+require 'extension.match'\r
+module ('spmatch', package.seeall)\r
+-{extension 'log'}\r
+\r
+-- Get rid of the former parser, it will be blended in a multiseq:\r
+mlp.stat:del 'match'\r
+\r
+mlp.stat:add{ 'match', \r
+   gg.multisequence{\r
+\r
+      ----------------------------------------------------------------\r
+      -- Shortcut for declaration of functions containing only a match:\r
+      -- "function f($1) match $1 with $2 end end" can be written:\r
+      -- "match function f $2 end"\r
+      ----------------------------------------------------------------\r
+      { 'function', mlp.expr, gg.optkeyword '|',\r
+         match_cases_list_parser,\r
+         'end',\r
+         builder = function(x)                      \r
+            local func_name, _, cases = unpack(x)\r
+            local arity = #cases[1][1][1]\r
+            if arity==0 then \r
+               error "There must be at least 1 case in match function" \r
+            end\r
+            local args = { }\r
+            for i=1, arity do args[i] = mlp.gensym("arg."..i) end\r
+            local body = match_builder{args, cases}\r
+            return `Set{ {func_name}, { `Function{ args, {body} } } }\r
+         end },\r
+\r
+      ----------------------------------------------------------------\r
+      -- Reintroduce the original match statement:\r
+      ----------------------------------------------------------------\r
+      default = gg.sequence{\r
+         mlp.expr_list, 'with', gg.optkeyword '|',\r
+         match_cases_list_parser,\r
+         'end',\r
+         builder = |x| match_builder{ x[1], x[3] } } } }\r
+\r
+mlp.expr:add{ 'match', builder = |x| x[1], gg.multisequence{\r
+\r
+      ----------------------------------------------------------------\r
+      -- Anonymous match functions:\r
+      -- "function ($1) match $1 with $2 end end" can be written:\r
+      -- "match function $2 end"\r
+      ----------------------------------------------------------------\r
+      { 'function', gg.optkeyword '|',\r
+         match_cases_list_parser,\r
+         'end',\r
+         builder = function(x)\r
+            local _, cases = unpack(x)\r
+            local v        = mlp.gensym()\r
+            local body     = match_builder{v, cases}\r
+            return `Function{ {v}, {body} }\r
+         end },\r
+\r
+      ----------------------------------------------------------------\r
+      -- match expressions: you can put a match where an expression\r
+      -- is expected. The case bodies are then expected to be \r
+      -- expressions, not blocks.\r
+      ----------------------------------------------------------------\r
+      default = gg.sequence{\r
+         mlp.expr_list, 'with', gg.optkeyword '|',\r
+         gg.list{  name = "match cases list",\r
+            gg.sequence{ name = "match expr case",\r
+               gg.list{ name  = "match expr case patterns list",\r
+                  primary     = mlp.expr_list,\r
+                  separators  = "|",\r
+                  terminators = { "->", "if" } },\r
+               gg.onkeyword{ "if", mlp.expr, consume = true },\r
+               "->",\r
+               mlp.expr },\r
+            separators  = "|",\r
+            terminators = "end" },\r
+         'end',\r
+         builder = function (x)\r
+            local tested_term_seq, _, cases = unpack(x)\r
+            local v = mlp.gensym 'match_expr'\r
+            -- Replace expressions with blocks\r
+            for case in ivalues (cases) do \r
+               local body = case[3]\r
+               case[3] = { `Set{ {v}, {body} } }\r
+            end\r
+            local m = match_builder { tested_term_seq, cases }\r
+            return `Stat{ { `Local{{v}}; m }, v }\r
+         end } } }\r
+\r
+require 'walk.id'\r
+\r
+function bind (x)\r
+   local patterns, values = unpack(x)\r
+   \r
+   -------------------------------------------------------------------\r
+   -- Generate pattern code: "bind vars = vals" translates to:\r
+   -- do \r
+   --   pattern matching code, goto 'fail' on mismatch\r
+   --   goto 'success'\r
+   --   label 'fail': error "..."\r
+   --   label success\r
+   -- end\r
+   -- vars is the set of variables used by the pattern\r
+   -------------------------------------------------------------------\r
+   local code, vars do\r
+      local match_cfg = {\r
+         on_failure = mlp.gensym 'mismatch' [1],\r
+         locals = { },\r
+         code = { } }\r
+      pattern_seq_builder(patterns, values, match_cfg)\r
+      local on_success = mlp.gensym 'on_success' [1]\r
+      code = { \r
+         match_cfg.code; \r
+         `Goto{ on_success }; \r
+         `Label{ match_cfg.on_failure }; \r
+         +{error "bind error"};\r
+         `Label{ on_success } }\r
+      vars = match_cfg.locals\r
+   end\r
+   \r
+   -------------------------------------------------------------------\r
+   -- variables that actually appear in the pattern:\r
+   -------------------------------------------------------------------\r
+   local vars_in_pattern do\r
+      vars_in_pattern = { }\r
+      local walk_cfg = { id = { } }\r
+      function walk_cfg.id.free(v) vars_in_pattern[v[1]]=true end\r
+      walk_id.expr_list(walk_cfg, patterns)\r
+   end\r
+\r
+   -------------------------------------------------------------------\r
+   -- temp variables that are generated for destructuring,\r
+   -- but aren't explicitly typed by the user. These must be made\r
+   -- local.\r
+   -------------------------------------------------------------------\r
+   local vars_not_in_pattern do\r
+      vars_not_in_pattern = { }\r
+      for k in keys(vars) do\r
+         if not vars_in_pattern[k] then\r
+            vars_not_in_pattern[k] = true\r
+         end\r
+      end\r
+   end\r
+\r
+   -------------------------------------------------------------------\r
+   -- Declare the temp variables as local to the statement.\r
+   -------------------------------------------------------------------\r
+   if next(vars_not_in_pattern) then\r
+      local loc = { }\r
+      for k in keys (vars_not_in_pattern) do \r
+         table.insert (loc, `Id{k})\r
+      end\r
+      table.insert (code, 1, `Local{ loc, { } })\r
+   end\r
+\r
+   -------------------------------------------------------------------\r
+   -- Transform the set of pattern variable names into a list of `Id{}\r
+   -------------------------------------------------------------------\r
+   local decl_list do\r
+      decl_list = { }\r
+      for k in keys (vars_in_pattern) do\r
+         table.insert (decl_list, `Id{k})\r
+      end\r
+   end\r
+\r
+   return code, decl_list\r
+end\r
+\r
+function local_bind(x)\r
+   local code, vars = bind (x)\r
+   return { `Local{ vars, { } }; code }\r
+end\r
+\r
+function non_local_bind(x)\r
+   local code, _ = bind (x)\r
+   code.tag = 'Do'\r
+   return code\r
+end\r
+\r
+----------------------------------------------------------------------\r
+-- Syntax front-end\r
+----------------------------------------------------------------------\r
+mlp.lexer:add 'bind'\r
+\r
+----------------------------------------------------------------------\r
+-- bind patterns = vars\r
+----------------------------------------------------------------------\r
+mlp.stat:add{ 'bind', mlp.expr_list, '=', mlp.expr_list, \r
+   builder = non_local_bind }\r
+\r
+----------------------------------------------------------------------\r
+-- local bind patterns = vars\r
+-- Some monkey-patching of "local ..." must take place\r
+----------------------------------------------------------------------\r
+mlp.stat:get'local'[2]:add{ 'bind', mlp.expr_list, '=', mlp.expr_list, \r
+   builder = local_bind }
\ No newline at end of file
diff --git a/src/samples/bind_test.mlua b/src/samples/bind_test.mlua
new file mode 100644 (file)
index 0000000..056eee4
--- /dev/null
@@ -0,0 +1,13 @@
+-{ 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)
+   
diff --git a/src/samples/dset_test.mlua b/src/samples/dset_test.mlua
deleted file mode 100644 (file)
index 5581b39..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
--{ extension 'dset' }
-
-require 'strict'
-
-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)
-   
diff --git a/src/samples/xmatch_test.mlua b/src/samples/xmatch_test.mlua
new file mode 100755 (executable)
index 0000000..a81fb91
--- /dev/null
@@ -0,0 +1,37 @@
+-{ extension 'xmatch' }\r
+\r
+print(match 1 with 1 -> 'ok' | 2 -> 'KO' end)\r
+\r
+function f(x)\r
+   match x with\r
+   | y if y<10 -> print 'small'\r
+   | _ -> print 'big'\r
+   end\r
+end\r
+\r
+match function g\r
+| x if x<10 -> print 'small'\r
+| _         -> print 'big'\r
+end\r
+\r
+match function cmp\r
+| x, y if x<y -> print 'increasing'\r
+| _, _        -> print 'decreasing'\r
+end\r
+\r
+f(1) f(11) g(1) g(11)\r
+\r
+cmp(1,2) cmp(2,1)\r
+\r
+bind {a, b} = {'o', 'k'}\r
+\r
+print(a..b)\r
+\r
+c, d = 'k', 'o'\r
+\r
+do\r
+   local bind {c, {d}} = {'o', {'k'}}\r
+   print(c..d)\r
+end\r
+\r
+print(d..c)\r