new samples: synthesis of source file from an AST, weaving sources and bits of genera...
[metalua.git] / src / samples / weaver.mlua
blob8a000370f71ce5809c9eb2b0ea32e32b21625cf3
1 require 'mlc'
2 require 'walk'
4 function weave_ast (src, ast, name)
6    -------------------------------------------------------------------
7    -- translation: associate an AST node to its recomposed source
8    -- ast_children: associate an AST node to the list of its children
9    -- ast_parent: associate an AST node to the list of its parent
10    -- weavable: whether an AST node supports weaving of its children
11    -- node: common walker config for exprs, stats & blocks
12    -------------------------------------------------------------------
13    local translation, ast_children, ast_parent, weaveable, node =
14       { }, { }, { }, { }, { }
16    -------------------------------------------------------------------
17    -- Build up the parent/children relationships. This is not the same
18    -- as inclusion between tables: the relation we're building only
19    -- relates blocks, expressions and statements; in the AST, some
20    -- tables don't represent any of these node kinds.
21    -- For instance in `Local{ { `Id "x" }, { } }, `Id"x" is a child of
22    -- the `Local{ } node, although it's not directly included in it.
23    -------------------------------------------------------------------
24    function node.down(ast, parent)
25       if not ast.lineinfo then 
26          weaveable [ast], weaveable [parent] = false, false
27       else
28          weaveable [ast] = true
29       end
30       ast_children [ast] = { }
31       ast_parent [ast] = parent
32       if parent then table.insert (ast_children [parent], ast) end
33    end
35    -------------------------------------------------------------------
36    -- Visit up, from leaves to upper-level nodes, and weave leaves
37    -- back into the text of their parent node, recursively.  Since the
38    -- visitor is imperative, we can't easily make it return a value
39    -- (the resulting recomposed source, here). Therefore we
40    -- imperatively store results in the association table
41    -- `translation'.
42    -------------------------------------------------------------------
43    function node.up(ast)
44       local _acc = { }
45       local function acc(x) table.insert (_acc, x) end
46       
47       -- ast Can't be weaved normally, try something else --
48       local function synthetize (ast)         
49          acc "-{expr: "
50          acc (table.tostring (ast, 'nohash', 80, 8))
51          acc " }"
52       end
54       -- regular weaving of chidren in the parent's sources --
55       local function weave (ast)
56          local li = ast.lineinfo
57          if not li then return synthetize (ast) end
58          local a, d = li.first[3], li.last[3]
59          for child in ivalues (ast_children [ast]) do
60             local li = child.lineinfo
61             local b, c = li.first[3], li.last[3]
62             acc (src:sub (a, b-1))
63             acc (translation [child])
64             a = c+1
65          end
66          acc (src:sub (a, d))
67       end
69       -- compute the translation from the children's ones --
70       if not translation [ast] then
71          if weaveable [ast] then weave (ast) else synthetize (ast) end
72          translation [ast] = table.concat (_acc)
73       end
74    end
76    local cfg = { expr=node; stat=node; block=node }
77    walk.block (cfg, ast)
79    return translation [ast]
80 end
82 -- Get the source. If none is given, use itself as an example. --
83 local filename = arg[2] or arg[1] or arg[0]
84 local f = io.open (filename, 'r')
85 local src = f:read '*a'
86 f:close()
88 local ast = mlc.luastring_to_ast (src, name)
90 print (weave_ast (src, ast))