8 --------------------------------------------------------------------------------
9 -- Instanciate a new AST->source synthetizer
10 --------------------------------------------------------------------------------
13 _acc = { }, -- Accumulates pieces of source as strings
14 current_indent = 0, -- Current level of line indentation
15 indent_step = " " -- Indentation symbol, normally spaces or '\t'
17 return setmetatable (self, synth)
20 --------------------------------------------------------------------------------
21 -- Run a synthetizer on the `ast' arg and return the source as a string.
22 -- Can also be used as a static method `synth.run (ast)'; in this case,
23 -- a temporary synthetizer is instanciated on the fly.
24 --------------------------------------------------------------------------------
25 function synth:run (ast)
27 self, ast = synth.new(), self
31 return table.concat (self._acc)
34 --------------------------------------------------------------------------------
35 -- Accumulate a piece of source file in the synthetizer.
36 --------------------------------------------------------------------------------
37 function synth:acc (x)
38 if x then table.insert (self._acc, x) end
41 --------------------------------------------------------------------------------
42 -- Accumulate an indented newline.
43 -- Jumps an extra line if indentation is 0, so that
44 -- toplevel definitions are separated by an extra empty line.
45 --------------------------------------------------------------------------------
47 if self.current_indent == 0 then self:acc "\n" end
48 self:acc ("\n" .. self.indent_step:rep (self.current_indent))
51 --------------------------------------------------------------------------------
52 -- Increase indentation and accumulate a new line.
53 --------------------------------------------------------------------------------
54 function synth:nlindent ()
55 self.current_indent = self.current_indent + 1
59 --------------------------------------------------------------------------------
60 -- Decrease indentation and accumulate a new line.
61 --------------------------------------------------------------------------------
62 function synth:nldedent ()
63 self.current_indent = self.current_indent - 1
64 self:acc ("\n" .. self.indent_step:rep (self.current_indent))
67 --------------------------------------------------------------------------------
68 -- Keywords, which are illegal as identifiers.
69 --------------------------------------------------------------------------------
70 local keywords = table.transpose {
71 "and", "break", "do", "else", "elseif",
72 "end", "false", "for", "function", "if",
73 "in", "local", "nil", "not", "or",
74 "repeat", "return", "then", "true", "until",
77 --------------------------------------------------------------------------------
78 -- Return true iff string `id' is a legal identifier name.
79 --------------------------------------------------------------------------------
80 local function is_ident (id)
81 return id:strmatch "^[%a_][%w_]*$" and not keywords[id]
84 --------------------------------------------------------------------------------
85 -- Return true iff ast represents a legal function name for
86 -- syntax sugar ``function foo.bar.gnat() ... end'':
87 -- a series of nested string indexes, with an identifier as
88 -- the innermost node.
89 --------------------------------------------------------------------------------
90 local function is_idx_stack (ast)
92 | `Id{ _ } -> return true
93 | `Index{ left, `String{ _ } } -> return is_idx_stack (left)
98 --------------------------------------------------------------------------------
99 -- Operator precedences, in increasing order.
100 -- This is not directly used, it's used to generate op_prec below.
101 --------------------------------------------------------------------------------
104 { "lt", "le", "eq", "ne" },
107 { "mul", "div", "mod" },
108 { "unary", "not", "len" },
112 --------------------------------------------------------------------------------
113 -- operator --> precedence table, generated from op_preprec.
114 --------------------------------------------------------------------------------
117 for prec, ops in ipairs (op_preprec) do
118 for op in ivalues (ops) do
123 --------------------------------------------------------------------------------
124 -- operator --> source representation.
125 --------------------------------------------------------------------------------
127 add = " + ", sub = " - ", mul = " * ",
128 div = " / ", mod = " % ", pow = " ^ ",
129 concat = " .. ", eq = " == ", ne = " ~= ",
130 lt = " < ", le = " <= ", ["and"] = " and ",
131 ["or"] = " or ", ["not"] = "not ", len = "# " }
133 --------------------------------------------------------------------------------
134 -- Accumulate the source representation of AST `node' in
135 -- the synthetizer. Most of the work is done by delegating to
136 -- the method having the name of the AST tag.
137 -- If something can't be converted to normal sources, it's
138 -- instead dumped as a `-{ ... }' splice in the source accumulator.
139 --------------------------------------------------------------------------------
140 function synth:node (node)
141 assert (self~=synth and self._acc)
142 if not node.tag then -- tagless block.
143 self:list (node, self.nl)
145 local f = synth[node.tag]
146 if type (f) == "function" then -- Delegate to tag method.
147 f (self, node, unpack (node))
148 elseif type (f) == "string" then -- tag string.
150 else -- No appropriate method, fall back to splice dumping.
151 -- This cannot happen in a plain Lua AST.
153 self:acc (table.tostring (node, "nohash"), 80)
159 --------------------------------------------------------------------------------
160 -- Convert every node in the AST list `list' passed as 1st arg.
161 -- `sep' is an optional separator to be accumulated between each list element,
162 -- it can be a string or a synth method.
163 -- `start' is an optional number (default == 1), indicating which is the
164 -- first element of list to be converted, so that we can skip the begining
166 --------------------------------------------------------------------------------
167 function synth:list (list, sep, start)
168 for i = start or 1, # list do
172 elseif type (sep) == "function" then sep (self)
173 elseif type (sep) == "string" then self:acc (sep)
174 else error "Invalid list separator" end
179 --------------------------------------------------------------------------------
184 -- Specific AST node dumping methods, associated to their node kinds
185 -- by their name, which is the corresponding AST tag.
186 -- synth:node() is in charge of delegating a node's treatment to the
187 -- appropriate tag method.
189 -- Such tag methods are called with the AST node as 1st arg.
190 -- As a convenience, the n node's children are passed as args #2 ... n+1.
192 -- There are several things that could be refactored into common subroutines
193 -- here: statement blocks dumping, function dumping...
194 -- However, given their small size and linear execution
195 -- (they basically perform series of :acc(), :node(), :list(),
196 -- :nl(), :nlindent() and :nldedent() calls), it seems more readable
197 -- to avoid multiplication of such tiny functions.
199 -- To make sense out of these, you need to know metalua's AST syntax, as
200 -- found in the reference manual or in metalua/doc/ast.txt.
202 --------------------------------------------------------------------------------
204 function synth:Do (node)
207 self:list (node, self.nl)
212 function synth:Set (node)
214 | `Set{ { `Index{ lhs, `String{ method } } },
215 { `Function{ { `Id "self", ... } == params, body } } }
216 if is_idx_stack (lhs) and is_ident (method) ->
217 -- ``function foo:bar(...) ... end'' --
223 self:list (params, ", ", 2)
226 self:list (body, self.nl)
230 | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
231 -- ``function foo(...) ... end'' --
235 self:list (params, ", ")
238 self:list (body, self.nl)
242 | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs }
243 if not is_ident (lhs1name) ->
244 -- ``foo, ... = ...'' when foo is *not* a valid identifier.
245 -- In that case, the spliced 1st variable must get parentheses,
246 -- to be distinguished from a statement splice.
247 -- This cannot happen in a plain Lua AST.
251 if lhs[2] then -- more than one lhs variable
253 self:list (lhs, ", ", 2)
256 self:list (rhs, ", ")
258 | `Set{ lhs, rhs } ->
259 -- ``... = ...'', no syntax sugar --
260 self:list (lhs, ", ")
262 self:list (rhs, ", ")
266 function synth:While (node, cond, body)
271 self:list (body, self.nl)
276 function synth:Repeat (node, body, cond)
279 self:list (body, self.nl)
285 function synth:If (node)
286 for i = 1, #node-1, 2 do
287 -- for each ``if/then'' and ``elseif/then'' pair --
288 local cond, body = node[i], node[i+1]
289 self:acc (i==1 and "if " or "elseif ")
293 self:list (body, self.nl)
296 -- odd number of children --> last one is an `else' clause --
300 self:list (node[#node], self.nl)
306 function synth:Fornum (node, var, first, last)
307 local body = node[#node]
314 if #node==5 then -- 5 children --> child #4 is a step increment.
320 self:list (body, self.nl)
325 function synth:Forin (node, vars, generators, body)
327 self:list (vars, ", ")
329 self:list (generators, ", ")
332 self:list (body, self.nl)
337 function synth:Local (node, lhs, rhs)
339 self:list (lhs, ", ")
342 self:list (rhs, ", ")
346 function synth:Localrec (node, lhs, rhs)
348 | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
349 if is_ident (name) ->
350 -- ``local function name() ... end'' --
351 self:acc "local function "
354 self:list (params, ", ")
357 self:list (body, self.nl)
362 -- Other localrec are unprintable ==> splice them --
363 -- This cannot happen in a plain Lua AST. --
365 self:acc (table.tostring (node, 'nohash', 80))
370 function synth:Call (node, f)
371 -- single string or table literal arg ==> no need for parentheses. --
374 | `Call{ _, `String{_} }
375 | `Call{ _, `Table{...}} -> parens = false
379 self:acc (parens and " (" or " ")
380 self:list (node, ", ", 2) -- skip `f'.
381 self:acc (parens and ")")
384 function synth:Invoke (node, f, method)
385 -- single string or table literal arg ==> no need for parentheses. --
388 | `Invoke{ _, _, `String{_} }
389 | `Invoke{ _, _, `Table{...}} -> parens = false
395 self:acc (parens and " (" or " ")
396 self:list (node, ", ", 3) -- Skip args #1 and #2, object and method name.
397 self:acc (parens and ")")
400 function synth:Return (node)
402 self:list (node, ", ")
405 synth.Break = "break"
407 synth.False = "false"
411 function synth:Number (node, n)
412 self:acc (tostring (n))
415 function synth:String (node, str)
416 -- format "%q" prints '\n' in an umpractical way IMO,
417 -- so this is fixed with the :gsub( ) call.
418 self:acc (string.format ("%q", str):gsub ("\\\n", "\\n"))
421 function synth:Function (node, params, body)
424 self:list (params, ", ")
427 self:list (body, self.nl)
432 function synth:Table (node)
433 if not node[1] then self:acc "{ }" else
436 for i, elem in ipairs (node) do
438 | `Pair{ `String{ key }, value } if is_ident (key) ->
439 -- ``key = value''. --
444 | `Pair{ key, value } ->
445 -- ``[key] = value''. --
465 function synth:Op (node, op, a, b)
466 -- Transform ``not (a == b)'' into ``a ~= b''. --
468 | `Op{ "not", `Op{ "eq", _a, _b } }
469 | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
470 op, a, b = "ne", _a, _b
474 if b then -- binary operator.
475 local left_paren, right_paren
477 | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
478 | _ -> left_paren = false
481 match b with -- FIXME: might not work with right assoc operators ^ and ..
482 | `Op{ op_b, ...} if op_prec[op] >= op_prec[op_b] -> right_paren = true
483 | _ -> right_paren = false
486 self:acc (left_paren and "(")
488 self:acc (left_paren and ")")
490 self:acc (op_symbol [op])
492 self:acc (right_paren and "(")
494 self:acc (right_paren and ")")
496 else -- unary operator.
499 | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
502 self:acc (op_symbol[op])
503 self:acc (paren and "(")
505 self:acc (paren and ")")
509 function synth:Paren (node, content)
515 function synth:Index (node, table, key)
517 -- Check precedence, see if parens are needed around the table --
519 | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
520 | _ -> paren_table = false
523 self:acc (paren_table and "(")
525 self:acc (paren_table and ")")
528 | `String{ field } if is_ident (field) ->
533 -- ``table [key]''. --
540 function synth:Id (node, name)
541 if is_ident (name) then
543 else -- Unprintable identifier, fall back to splice representation.
544 -- This cannot happen in a plain Lua AST.
546 self:String (node, name)
552 --------------------------------------------------------------------------------
553 -- Read a file, get its AST, use synth to regenerate sources
555 --------------------------------------------------------------------------------
556 require 'metalua.compiler'
557 local filename = (arg[2] or arg[1]) or arg[0]
558 local ast = mlc.luafile_to_ast (filename)
560 print(synth.run(ast))