25580669d337d96e1c25fababe1e7fc1b2c9b769
[metalua.git] / src / lib / extension / clist.mlua
blob25580669d337d96e1c25fababe1e7fc1b2c9b769
1 ----------------------------------------------------------------------\r
2 -- Metalua samples:  $Id$\r
3 --\r
4 -- Summary: Lists by comprehension\r
5 --\r
6 ----------------------------------------------------------------------\r
7 --\r
8 -- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>.\r
9 --\r
10 -- This software is released under the MIT Licence, see licence.txt\r
11 -- for details.\r
12 --\r
13 --------------------------------------------------------------------------------\r
14 --\r
15 -- This extension implements list comprehensions, similar to Haskell and\r
16 -- Python syntax, to easily describe lists.\r
17 --\r
18 --------------------------------------------------------------------------------\r
20 -{ extension "match" }\r
22 local function dots_builder (x) return `Dots{ x } end\r
24 local function for_builder (x, h)\r
25    match x with\r
26    | `Comp{ _, acc } -> table.insert (acc, h[1]); return x\r
27    | `Pair{ _, _ }   -> error "No explicit key in a for list generator"\r
28    |  _              -> return `Comp{ x, {h[1]} }\r
29    end\r
30 end\r
32 local function if_builder (x, p)\r
33    match x with\r
34    | `Comp{ _, acc } -> table.insert (acc, `If{ p[1] }); return x\r
35    | `Pair{ _, _ }   -> error "No explicit key in a list guard"\r
36    |  _              -> return `Comp{ x, p[1] }\r
37    end\r
38 end\r
39    \r
40 local function comp_builder(core, list, no_unpack)\r
41    -- [ti] = temp var holding table.insert\r
42    -- [v]  = variable holding the table being built\r
43    -- [r]  = the core of the list being built\r
44    local ti, v, r = mlp.gensym "table_insert", mlp.gensym "table"\r
46    -----------------------------------------------------------------------------\r
47    -- 1 - Build the loop's core: if it has suffix "...", every elements of the\r
48    --     multi-return must be inserted, hence the extra [for] loop.\r
49    -----------------------------------------------------------------------------\r
50    match core with\r
51    | `Dots{ x } -> \r
52       local w = mlp.gensym()\r
53       r = +{stat: for -{w} in values( -{x} ) do -{ `Call{ ti, v, w } } end }\r
54    | `Pair{ k, w } ->\r
55       r = `Set{ { `Index{ v, k } }, { w } }\r
56    |  x -> r = `Call{ ti, v, x }\r
57    end\r
59    -----------------------------------------------------------------------------\r
60    -- 2 - Stack the if and for control structures, from outside to inside.\r
61    --     This is done in a destructive way for the elements of [list].\r
62    -----------------------------------------------------------------------------\r
63    for i = #list, 1, -1 do\r
64       table.insert (list[i], {r})\r
65       r = list[i]\r
66    end\r
67    if no_unpack then\r
68       return `Stat{ { `Local{ {ti, v}, { +{table.insert}, `Table} }, r }, v }\r
69    else\r
70       return +{ function() \r
71                    local -{ti}, -{v} = table.insert, { }\r
72                    -{r}; return unpack(-{v}) \r
73                 end () }\r
74    end\r
75 end\r
77 local function table_content_builder (list)\r
78    match list with\r
79    | { `Comp{ y, acc } } -> return comp_builder( y, acc, "no unpack")\r
80    | _ ->\r
81       local tables = { `Table }\r
82       local ctable = tables[1]\r
83       local function flush() ctable=`Table; table.insert(tables, ctable) end\r
84       for x in values(list) do\r
85          match x with\r
86          | `Comp{ y, acc } -> table.insert(ctable, comp_builder(y, acc)); flush()\r
87          | `Dots{ y }      -> table.insert(ctable, y); flush()\r
88          |  _              -> table.insert (ctable, x); \r
89          end\r
90       end\r
91       match tables with\r
92       | { x } | { x, { } } -> return x\r
93       | _ ->\r
94          if #tables[#tables]==0 then table.remove(tables) end --suppress empty table      \r
95          return `Call{ +{table.cat}, unpack(tables) }\r
96       end\r
97    end\r
98 end\r
100 mlp.table_field = gg.expr{ name="table cell",\r
101    primary = mlp.table_field,\r
102    suffix  = { name="table cell suffix",\r
103       { "...",                 builder = dots_builder },\r
104       { "for", mlp.for_header, builder = for_builder  },\r
105       { "if",  mlp.expr,       builder = if_builder   } } }\r
107 mlp.table_content.builder = table_content_builder\r
109 --[[\r
110 mlp.stat:add{ "for", gg.expr {\r
111       primary = for_header,\r
112       suffix = {\r
113          { "for", mlp.for_header, builder = for_builder  },\r
114          { "if",  mlp.expr,       builder = if_builder   } } }, \r
115    "do", mlp.block, "end", builder = for_stat_builder }\r
116 --]]\r
118 local function index_builder(a, suffix)\r
119    match suffix[1] with\r
120    | { { e, false } } -> return `Index{ a, e }\r
121    | ranges ->\r
122       local r = `Call{ +{table.isub}, a }\r
123       local function acc(x,y) table.insert(r,x); table.insert(r,y) end\r
124       for seq in values(ranges) do\r
125          match seq with\r
126          | { e, false } -> acc(e,e)\r
127          | { e, f }     -> acc(e,f)\r
128          end\r
129       end\r
130       return r\r
131    end\r
132 end\r
134 mlp.expr.suffix:del '['\r
135 mlp.expr.suffix:add{ name="table index/range",\r
136    "[", gg.list{\r
137       gg.sequence { mlp.expr, gg.onkeyword{ "...", mlp.expr } } , \r
138       separators = { ",", ";" } }, \r
139    "]", builder = index_builder }\r