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 ----------------------------------------------------
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
33 if not ast.lineinfo then
34 weaveable [ast] = false, false
35 if parent then weaveable [parent] = false end
37 weaveable [ast] = true
41 if ast.lineinfo.first[3] > ast.lineinfo.last[3] then
42 ast.lineinfo.first, ast.lineinfo.last = ast.lineinfo.last, ast.lineinfo.first
45 ast_children [ast] = { }
46 ast_parent [ast] = parent
47 if parent then table.insert (ast_children [parent], ast) end
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
57 -------------------------------------------------------------------
60 local function acc(x) table.insert (_acc, x) end
62 -- ast Can't be weaved normally, try something else --
63 local function synthetize (ast)
64 if next(ast) then -- Dumping non-empty nodes only
66 acc (table.tostring (ast, 'nohash', 80, 8))
71 -- regular weaving of chidren in the parent's sources --
72 local function weave (ast)
73 local li = ast.lineinfo
74 if not li then return synthetize (ast) end
75 local a, d = li.first[3], li.last[3]
76 for child in ivalues (ast_children [ast]) do
77 local li = child.lineinfo
78 local b, c = li.first[3], li.last[3]
79 acc (src:sub (a, b-1))
80 acc (translation [child])
86 -- compute the translation from the children's ones --
87 if not translation [ast] then
88 if weaveable [ast] then weave (ast) else synthetize (ast) end
89 translation [ast] = table.concat (_acc)
93 local cfg = { expr=node; stat=node; block=node }
96 return translation [ast]
99 -- Get the source. If none is given, use itself as an example. --
100 local filename = arg[2] or arg[1] or arg[0]
101 local f = assert (io.open (filename, 'r'))
102 local src = f:read '*a'
105 local ast = mlc.luastring_to_ast (src, name)
106 if not next(ast) then
107 io.write (src) -- Empty ast, probably empty file, or comments only
109 local before = src:sub (1, ast.lineinfo.first[3]-1)
110 local after = src:sub (ast.lineinfo.last[3]+1, -1)
111 io.write (before .. weave_ast (src, ast) .. after)