]> git.lizzy.rs Git - metalua.git/blob - src/samples/weaver.mlua
Merge remote branch 'origin/master'
[metalua.git] / src / samples / weaver.mlua
1 require 'metalua.mlc'
2 require 'metalua.walk'
3
4 function weave_ast (src, ast, name)
5
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       { }, { }, { }, { }, { }
15
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       ----------------------------------------------------
26       -- `Do{ } blocks are processed twice:
27       --  * once as a statement
28       --  * once as a block, child of itself
29       -- This prevents them from becoming their own child.
30       ----------------------------------------------------
31       if ast==parent then return end
32
33       if not ast.lineinfo then 
34          weaveable [ast] = false, false
35          if parent then weaveable [parent] = false end
36       else
37          weaveable [ast] = true
38          
39          -- normalize lineinfo
40          -- TODO: FIXME
41          if ast.lineinfo.first[3] > ast.lineinfo.last[3] then
42            ast.lineinfo.first, ast.lineinfo.last = ast.lineinfo.last, ast.lineinfo.first
43          end
44       end
45       ast_children [ast] = { }
46       ast_parent [ast] = parent
47       if parent then table.insert (ast_children [parent], ast) end
48    end
49
50    -------------------------------------------------------------------
51    -- Visit up, from leaves to upper-level nodes, and weave leaves
52    -- back into the text of their parent node, recursively.  Since the
53    -- visitor is imperative, we can't easily make it return a value
54    -- (the resulting recomposed source, here). Therefore we
55    -- imperatively store results in the association table
56    -- `translation'.
57    -------------------------------------------------------------------
58    function node.up(ast)
59       local _acc = { }
60       local function acc(x) table.insert (_acc, x) end
61       
62       if not next(ast) then -- shadow node, remove from ast_children
63          local x = ast_children[ast_parent[ast]]
64          for i,a in ipairs (x) do if a==ast then table.remove (x, i); break end end
65          return "" -- no need to continue, we know that the node is empty!
66       end
67       
68       -- ast Can't be weaved normally, try something else --
69       local function synthetize (ast)
70          acc "-{expr: "
71          acc (table.tostring (ast, 'nohash', 80, 8))
72          acc " }"
73       end
74
75       -- regular weaving of chidren in the parent's sources --
76       local function weave (ast)
77          -- sort children in appearence order
78          local comp = |a,b| a.lineinfo.first[3] < b.lineinfo.first[3]
79          table.sort (ast_children [ast], comp)
80  
81          local li = ast.lineinfo
82          if not li then return synthetize (ast) end
83          local a, d = li.first[3], li.last[3]
84          for child in ivalues (ast_children [ast]) do
85             local li = child.lineinfo
86             local b, c = li.first[3], li.last[3]
87             acc (src:sub (a, b - 1))
88             acc (translation [child])
89             a = c + 1
90          end
91          acc (src:sub (a, d))
92       end
93
94       -- compute the translation from the children's ones --
95       if not translation [ast] then
96          if weaveable [ast] then weave (ast) else synthetize (ast) end
97          translation [ast] = table.concat (_acc)
98       end
99    end
100
101    local cfg = { expr=node; stat=node; block=node }
102    walk.block (cfg, ast)
103
104    return translation [ast]
105 end
106
107 -- Get the source. If none is given, use itself as an example. --
108 local filename = arg[2] or arg[1] or arg[0]
109 local f = assert (io.open (filename, 'r'))
110 local src = f:read '*a'
111 f:close()
112
113 local ast = mlc.luastring_to_ast (src, name)
114 if not next(ast) then
115    io.write (src) -- Empty ast, probably empty file, or comments only
116 else
117    local before = src:sub (1, ast.lineinfo.first[3]-1)
118    local after  = src:sub (ast.lineinfo.last[3]+1, -1)
119    io.write (before .. weave_ast (src, ast) .. after)
120 end