1 -------------------------------------------------------------------------------
2 -- Copyright (c) 2006-2013 Fabien Fleutot and others.
4 -- All rights reserved.
6 -- This program and the accompanying materials are made available
7 -- under the terms of the Eclipse Public License v1.0 which
8 -- accompanies this distribution, and is available at
9 -- http://www.eclipse.org/legal/epl-v10.html
11 -- This program and the accompanying materials are also made available
12 -- under the terms of the MIT public license which accompanies this
13 -- distribution, and is available at http://www.lua.org/license.html
16 -- Fabien Fleutot - API and implementation
18 -------------------------------------------------------------------------------
20 -{ extension ('match', ...) }
24 M.__call = |self, ...| self:run(...)
26 local pp=require 'metalua.pprint'
28 --------------------------------------------------------------------------------
29 -- Instanciate a new AST->source synthetizer
30 --------------------------------------------------------------------------------
33 _acc = { }, -- Accumulates pieces of source as strings
34 current_indent = 0, -- Current level of line indentation
35 indent_step = " " -- Indentation symbol, normally spaces or '\t'
37 return setmetatable (self, M)
40 --------------------------------------------------------------------------------
41 -- Run a synthetizer on the `ast' arg and return the source as a string.
42 -- Can also be used as a static method `M.run (ast)'; in this case,
43 -- a temporary Metizer is instanciated on the fly.
44 --------------------------------------------------------------------------------
47 self, ast = M.new(), self
51 return table.concat (self._acc)
54 --------------------------------------------------------------------------------
55 -- Accumulate a piece of source file in the synthetizer.
56 --------------------------------------------------------------------------------
58 if x then table.insert (self._acc, x) end
61 --------------------------------------------------------------------------------
62 -- Accumulate an indented newline.
63 -- Jumps an extra line if indentation is 0, so that
64 -- toplevel definitions are separated by an extra empty line.
65 --------------------------------------------------------------------------------
67 if self.current_indent == 0 then self:acc "\n" end
68 self:acc ("\n" .. self.indent_step:rep (self.current_indent))
71 --------------------------------------------------------------------------------
72 -- Increase indentation and accumulate a new line.
73 --------------------------------------------------------------------------------
74 function M:nlindent ()
75 self.current_indent = self.current_indent + 1
79 --------------------------------------------------------------------------------
80 -- Decrease indentation and accumulate a new line.
81 --------------------------------------------------------------------------------
82 function M:nldedent ()
83 self.current_indent = self.current_indent - 1
84 self:acc ("\n" .. self.indent_step:rep (self.current_indent))
87 --------------------------------------------------------------------------------
88 -- Keywords, which are illegal as identifiers.
89 --------------------------------------------------------------------------------
90 local keywords_list = {
91 "and", "break", "do", "else", "elseif",
92 "end", "false", "for", "function", "if",
93 "in", "local", "nil", "not", "or",
94 "repeat", "return", "then", "true", "until",
97 for _, kw in pairs(keywords_list) do keywords[kw]=true end
99 --------------------------------------------------------------------------------
100 -- Return true iff string `id' is a legal identifier name.
101 --------------------------------------------------------------------------------
102 local function is_ident (id)
103 return string['match'](id, "^[%a_][%w_]*$") and not keywords[id]
106 --------------------------------------------------------------------------------
107 -- Return true iff ast represents a legal function name for
108 -- syntax sugar ``function foo.bar.gnat() ... end'':
109 -- a series of nested string indexes, with an identifier as
110 -- the innermost node.
111 --------------------------------------------------------------------------------
112 local function is_idx_stack (ast)
114 | `Id{ _ } -> return true
115 | `Index{ left, `String{ _ } } -> return is_idx_stack (left)
120 --------------------------------------------------------------------------------
121 -- Operator precedences, in increasing order.
122 -- This is not directly used, it's used to generate op_prec below.
123 --------------------------------------------------------------------------------
126 { "lt", "le", "eq", "ne" },
129 { "mul", "div", "mod" },
130 { "unary", "not", "len" },
134 --------------------------------------------------------------------------------
135 -- operator --> precedence table, generated from op_preprec.
136 --------------------------------------------------------------------------------
139 for prec, ops in ipairs (op_preprec) do
140 for _, op in ipairs (ops) do
145 --------------------------------------------------------------------------------
146 -- operator --> source representation.
147 --------------------------------------------------------------------------------
149 add = " + ", sub = " - ", mul = " * ",
150 div = " / ", mod = " % ", pow = " ^ ",
151 concat = " .. ", eq = " == ", ne = " ~= ",
152 lt = " < ", le = " <= ", ["and"] = " and ",
153 ["or"] = " or ", ["not"] = "not ", len = "# " }
155 --------------------------------------------------------------------------------
156 -- Accumulate the source representation of AST `node' in
157 -- the synthetizer. Most of the work is done by delegating to
158 -- the method having the name of the AST tag.
159 -- If something can't be converted to normal sources, it's
160 -- instead dumped as a `-{ ... }' splice in the source accumulator.
161 --------------------------------------------------------------------------------
162 function M:node (node)
163 assert (self~=M and self._acc)
164 if node==nil then self:acc'<<error>>'
165 elseif not self.custom_printer or not self.custom_printer (self, node) then
166 if not node.tag then -- tagless (henceunindented) block.
167 self:list (node, self.nl)
169 local f = M[node.tag]
170 if type (f) == "function" then -- Delegate to tag method.
171 f (self, node, unpack (node))
172 elseif type (f) == "string" then -- tag string.
174 else -- No appropriate method, fall back to splice dumping.
175 -- This cannot happen in a plain Lua AST.
177 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
184 function M:block(body)
185 if not self.custom_printer or not self.custom_printer (self, body) then
187 self:list (body, self.nl)
192 --------------------------------------------------------------------------------
193 -- Convert every node in the AST list `list' passed as 1st arg.
194 -- `sep' is an optional separator to be accumulated between each list element,
195 -- it can be a string or a synth method.
196 -- `start' is an optional number (default == 1), indicating which is the
197 -- first element of list to be converted, so that we can skip the begining
199 --------------------------------------------------------------------------------
200 function M:list (list, sep, start)
201 for i = start or 1, # list do
205 elseif type (sep) == "function" then sep (self)
206 elseif type (sep) == "string" then self:acc (sep)
207 else error "Invalid list separator" end
212 --------------------------------------------------------------------------------
217 -- Specific AST node dumping methods, associated to their node kinds
218 -- by their name, which is the corresponding AST tag.
219 -- synth:node() is in charge of delegating a node's treatment to the
220 -- appropriate tag method.
222 -- Such tag methods are called with the AST node as 1st arg.
223 -- As a convenience, the n node's children are passed as args #2 ... n+1.
225 -- There are several things that could be refactored into common subroutines
226 -- here: statement blocks dumping, function dumping...
227 -- However, given their small size and linear execution
228 -- (they basically perform series of :acc(), :node(), :list(),
229 -- :nl(), :nlindent() and :nldedent() calls), it seems more readable
230 -- to avoid multiplication of such tiny functions.
232 -- To make sense out of these, you need to know metalua's AST syntax, as
233 -- found in the reference manual or in metalua/doc/ast.txt.
235 --------------------------------------------------------------------------------
243 function M:Set (node)
245 | `Set{ { `Index{ lhs, `String{ method } } },
246 { `Function{ { `Id "self", ... } == params, body } } }
247 if is_idx_stack (lhs) and is_ident (method) ->
248 -- ``function foo:bar(...) ... end'' --
254 self:list (params, ", ", 2)
259 | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
260 -- ``function foo(...) ... end'' --
264 self:list (params, ", ")
269 | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs }
270 if not is_ident (lhs1name) ->
271 -- ``foo, ... = ...'' when foo is *not* a valid identifier.
272 -- In that case, the spliced 1st variable must get parentheses,
273 -- to be distinguished from a statement splice.
274 -- This cannot happen in a plain Lua AST.
278 if lhs[2] then -- more than one lhs variable
280 self:list (lhs, ", ", 2)
283 self:list (rhs, ", ")
285 | `Set{ lhs, rhs } ->
286 -- ``... = ...'', no syntax sugar --
287 self:list (lhs, ", ")
289 self:list (rhs, ", ")
290 | `Set{ lhs, rhs, annot } ->
291 -- ``... = ...'', no syntax sugar, annotation --
294 local ell, a = lhs[i], annot[i]
300 if i~=n then self:acc ', ' end
303 self:list (rhs, ", ")
307 function M:While (node, cond, body)
315 function M:Repeat (node, body, cond)
323 for i = 1, #node-1, 2 do
324 -- for each ``if/then'' and ``elseif/then'' pair --
325 local cond, body = node[i], node[i+1]
326 self:acc (i==1 and "if " or "elseif ")
331 -- odd number of children --> last one is an `else' clause --
334 self:block (node[#node])
339 function M:Fornum (node, var, first, last)
340 local body = node[#node]
347 if #node==5 then -- 5 children --> child #4 is a step increment.
356 function M:Forin (node, vars, generators, body)
358 self:list (vars, ", ")
360 self:list (generators, ", ")
366 function M:Local (node, lhs, rhs, annots)
378 if i~=n then self:acc ', ' end
381 self:list (lhs, ", ")
385 self:list (rhs, ", ")
387 else -- Can't create a local statement with 0 variables in plain Lua
388 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
392 function M:Localrec (node, lhs, rhs)
394 | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
395 if is_ident (name) ->
396 -- ``local function name() ... end'' --
397 self:acc "local function "
400 self:list (params, ", ")
406 -- Other localrec are unprintable ==> splice them --
407 -- This cannot happen in a plain Lua AST. --
409 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
414 function M:Call (node, f)
415 -- single string or table literal arg ==> no need for parentheses. --
418 | `Call{ _, `String{_} }
419 | `Call{ _, `Table{...}} -> parens = false
423 self:acc (parens and " (" or " ")
424 self:list (node, ", ", 2) -- skip `f'.
425 self:acc (parens and ")")
428 function M:Invoke (node, f, method)
429 -- single string or table literal arg ==> no need for parentheses. --
432 | `Invoke{ _, _, `String{_} }
433 | `Invoke{ _, _, `Table{...}} -> parens = false
439 self:acc (parens and " (" or " ")
440 self:list (node, ", ", 3) -- Skip args #1 and #2, object and method name.
441 self:acc (parens and ")")
444 function M:Return (node)
446 self:list (node, ", ")
455 function M:Number (node, n)
456 self:acc (tostring (n))
459 function M:String (node, str)
460 -- format "%q" prints '\n' in an umpractical way IMO,
461 -- so this is fixed with the :gsub( ) call.
462 self:acc (string.format ("%q", str):gsub ("\\\n", "\\n"))
465 function M:Function (node, params, body, annots)
466 self:acc "function ("
470 local p, a = params[i], annots[i]
476 if i~=n then self:acc ', ' end
479 self:list (params, ", ")
486 function M:Table (node)
487 if not node[1] then self:acc "{ }" else
489 if #node > 1 then self:nlindent () else self:acc " " end
490 for i, elem in ipairs (node) do
492 | `Pair{ `String{ key }, value } if is_ident (key) ->
493 -- ``key = value''. --
498 | `Pair{ key, value } ->
499 -- ``[key] = value''. --
514 if #node > 1 then self:nldedent () else self:acc " " end
519 function M:Op (node, op, a, b)
520 -- Transform ``not (a == b)'' into ``a ~= b''. --
522 | `Op{ "not", `Op{ "eq", _a, _b } }
523 | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
524 op, a, b = "ne", _a, _b
528 if b then -- binary operator.
529 local left_paren, right_paren
531 | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
532 | _ -> left_paren = false
535 match b with -- FIXME: might not work with right assoc operators ^ and ..
536 | `Op{ op_b, ...} if op_prec[op] >= op_prec[op_b] -> right_paren = true
537 | _ -> right_paren = false
540 self:acc (left_paren and "(")
542 self:acc (left_paren and ")")
544 self:acc (op_symbol [op])
546 self:acc (right_paren and "(")
548 self:acc (right_paren and ")")
550 else -- unary operator.
553 | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
556 self:acc (op_symbol[op])
557 self:acc (paren and "(")
559 self:acc (paren and ")")
563 function M:Paren (node, content)
569 function M:Index (node, table, key)
571 -- Check precedence, see if parens are needed around the table --
573 | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
574 | _ -> paren_table = false
577 self:acc (paren_table and "(")
579 self:acc (paren_table and ")")
582 | `String{ field } if is_ident (field) ->
587 -- ``table [key]''. --
594 function M:Id (node, name)
595 if is_ident (name) then
597 else -- Unprintable identifier, fall back to splice representation.
598 -- This cannot happen in a plain Lua AST.
600 self:String (node, name)
614 function M:TId (node, name) self:acc(name) end
617 function M:TCatbar(node, te, tebar)
625 function M:TFunction(node, p, r)
631 function M:TTable (node, default, pairs)
633 self:list (pairs, ', ')
634 if default.tag~='TField' then
641 function M:TPair (node, k, v)
647 function M:TIdbar (node, name)
651 function M:TCatbar (node, a, b)
657 function M:tebar(node)
658 if node.tag then self:node(node) else
660 self:list(node, ', ')
665 function M:TUnkbar(node, name)
670 function M:TUnk(node, name)
675 for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do
676 M[tag] = function(self, node, te)