]> git.lizzy.rs Git - metalua.git/commitdiff
introduce xloop extended loops
authorFabien Fleutot <fabien@macfabien.local>
Thu, 21 Feb 2008 08:02:37 +0000 (09:02 +0100)
committerFabien Fleutot <fabien@macfabien.local>
Thu, 21 Feb 2008 08:02:37 +0000 (09:02 +0100)
doc/metalua-logo.gif [new file with mode: 0644]
src/lib/extension/xloop.mlua [new file with mode: 0644]
src/samples/xloop_test.mlua [new file with mode: 0644]

diff --git a/doc/metalua-logo.gif b/doc/metalua-logo.gif
new file mode 100644 (file)
index 0000000..39ba935
Binary files /dev/null and b/doc/metalua-logo.gif differ
diff --git a/src/lib/extension/xloop.mlua b/src/lib/extension/xloop.mlua
new file mode 100644 (file)
index 0000000..166c89f
--- /dev/null
@@ -0,0 +1,73 @@
+-{ extension 'match' }
+-{ extension 'log' }
+require 'walk'
+
+-- 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] } }
+
+-- 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
+   local exit_label
+   local function exit()
+      if not exit_label then exit_label = mlp.gensym 'break' [1] end
+      return `Goto{ exit_label }
+   end
+
+   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'
+      | _ -> _
+      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
+
+loop_element_list = gg.list{ loop_element, terminators='do' }
+
+mlp.stat:del 'for'
+mlp.stat:del 'while'
+
+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] } }
+
diff --git a/src/samples/xloop_test.mlua b/src/samples/xloop_test.mlua
new file mode 100644 (file)
index 0000000..be57bb2
--- /dev/null
@@ -0,0 +1,4 @@
+-{ extension 'xloop' }
+for i=1,9 for j=10,90,10 if i~=3 while i<8 do
+   print(i+j)
+end
\ No newline at end of file