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 { "unm", "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 = "#",
157 --------------------------------------------------------------------------------
158 -- Accumulate the source representation of AST `node' in
159 -- the synthetizer. Most of the work is done by delegating to
160 -- the method having the name of the AST tag.
161 -- If something can't be converted to normal sources, it's
162 -- instead dumped as a `-{ ... }' splice in the source accumulator.
163 --------------------------------------------------------------------------------
164 function M:node (node)
165 assert (self~=M and self._acc)
166 if node==nil then self:acc'<<error>>'
167 elseif not self.custom_printer or not self.custom_printer (self, node) then
168 if not node.tag then -- tagless (henceunindented) block.
169 self:list (node, self.nl)
171 local f = M[node.tag]
172 if type (f) == "function" then -- Delegate to tag method.
173 f (self, node, unpack (node))
174 elseif type (f) == "string" then -- tag string.
176 else -- No appropriate method, fall back to splice dumping.
177 -- This cannot happen in a plain Lua AST.
179 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
186 function M:block(body)
187 if not self.custom_printer or not self.custom_printer (self, body) then
189 self:list (body, self.nl)
194 --------------------------------------------------------------------------------
195 -- Convert every node in the AST list `list' passed as 1st arg.
196 -- `sep' is an optional separator to be accumulated between each list element,
197 -- it can be a string or a synth method.
198 -- `start' is an optional number (default == 1), indicating which is the
199 -- first element of list to be converted, so that we can skip the begining
201 --------------------------------------------------------------------------------
202 function M:list (list, sep, start)
203 for i = start or 1, # list do
207 elseif type (sep) == "function" then sep (self)
208 elseif type (sep) == "string" then self:acc (sep)
209 else error "Invalid list separator" end
214 --------------------------------------------------------------------------------
219 -- Specific AST node dumping methods, associated to their node kinds
220 -- by their name, which is the corresponding AST tag.
221 -- synth:node() is in charge of delegating a node's treatment to the
222 -- appropriate tag method.
224 -- Such tag methods are called with the AST node as 1st arg.
225 -- As a convenience, the n node's children are passed as args #2 ... n+1.
227 -- There are several things that could be refactored into common subroutines
228 -- here: statement blocks dumping, function dumping...
229 -- However, given their small size and linear execution
230 -- (they basically perform series of :acc(), :node(), :list(),
231 -- :nl(), :nlindent() and :nldedent() calls), it seems more readable
232 -- to avoid multiplication of such tiny functions.
234 -- To make sense out of these, you need to know metalua's AST syntax, as
235 -- found in the reference manual or in metalua/doc/ast.txt.
237 --------------------------------------------------------------------------------
245 function M:Set (node)
247 | `Set{ { `Index{ lhs, `String{ method } } },
248 { `Function{ { `Id "self", ... } == params, body } } }
249 if is_idx_stack (lhs) and is_ident (method) ->
250 -- ``function foo:bar(...) ... end'' --
256 self:list (params, ", ", 2)
261 | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
262 -- ``function foo(...) ... end'' --
266 self:list (params, ", ")
271 | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs }
272 if not is_ident (lhs1name) ->
273 -- ``foo, ... = ...'' when foo is *not* a valid identifier.
274 -- In that case, the spliced 1st variable must get parentheses,
275 -- to be distinguished from a statement splice.
276 -- 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{{`Paren{lhs}}, rhs} ->
286 self:Set(`Set{{lhs}, rhs})
287 | `Set{{`Paren{lhs}}, rhs, annot} ->
288 self:Set(`Set{{lhs}, rhs, annot})
289 | `Set{ lhs, rhs } ->
290 -- ``... = ...'', no syntax sugar --
291 self:list (lhs, ", ")
293 self:list (rhs, ", ")
294 | `Set{ lhs, rhs, annot } ->
295 -- ``... = ...'', no syntax sugar, annotation --
298 local ell, a = lhs[i], annot[i]
304 if i~=n then self:acc ', ' end
307 self:list (rhs, ", ")
311 function M:While (node, cond, body)
319 function M:Repeat (node, body, cond)
327 for i = 1, #node-1, 2 do
328 -- for each ``if/then'' and ``elseif/then'' pair --
329 local cond, body = node[i], node[i+1]
330 self:acc (i==1 and "if " or "elseif ")
335 -- odd number of children --> last one is an `else' clause --
338 self:block (node[#node])
343 function M:Fornum (node, var, first, last)
344 local body = node[#node]
351 if #node==5 then -- 5 children --> child #4 is a step increment.
360 function M:Forin (node, vars, generators, body)
362 self:list (vars, ", ")
364 self:list (generators, ", ")
370 function M:Local (node, lhs, rhs, annots)
382 if i~=n then self:acc ', ' end
385 self:list (lhs, ", ")
389 self:list (rhs, ", ")
391 else -- Can't create a local statement with 0 variables in plain Lua
392 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
396 function M:Localrec (node, lhs, rhs)
398 | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
399 if is_ident (name) ->
400 -- ``local function name() ... end'' --
401 self:acc "local function "
404 self:list (params, ", ")
410 -- Other localrec are unprintable ==> splice them --
411 -- This cannot happen in a plain Lua AST. --
413 self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
418 function M:Call (node, f)
419 -- single string or table literal arg ==> no need for parentheses. --
422 | `Call{`Function{...}, _} -> wrap = true
423 | `Call{`Function{...}} -> wrap = true
425 end --[[ !! I doubt this should be here. Code that produces this AST should
426 produce a syntax error instead. ]]
428 | `Call{ _, `String{_} }
429 | `Call{ _, `Table{...}} -> parens = false
432 self:acc (wrap and "(" or "")
434 self:acc (wrap and ")" or "")
435 self:acc (parens and "(" or " ")
436 self:list (node, ", ", 2) -- skip `f'.
437 self:acc (parens and ")")
440 function M:Invoke (node, f, method)
441 -- single string or table literal arg ==> no need for parentheses. --
444 | `Invoke{ `String{_}, ...} -> wrap = true
448 | `Invoke{ _, _, `String{_} }
449 | `Invoke{ _, _, `Table{...}} -> parens = false
452 self:acc (wrap and "(" or "")
454 self:acc (wrap and ")" or "")
457 self:acc (parens and " (" or " ")
458 self:list (node, ", ", 3) -- Skip args #1 and #2, object and method name.
459 self:acc (parens and ")")
462 function M:Return (node)
464 self:list (node, ", ")
473 function M:Number (node, n)
474 self:acc (tostring (n))
477 function M:String (node, str)
478 -- format "%q" prints '\n' in an umpractical way IMO,
479 -- so this is fixed with the :gsub( ) call.
480 self:acc (string.format ("%q", str):gsub ("\\\n", "\\n"))
483 function M:Function (node, params, body, annots)
484 self:acc "function ("
488 local p, a = params[i], annots[i]
494 if i~=n then self:acc ', ' end
497 self:list (params, ", ")
504 function M:Table (node)
505 if not node[1] then self:acc "{ }" else
507 if #node > 1 then self:nlindent () else self:acc " " end
508 for i, elem in ipairs (node) do
510 | `Pair{ `String{ key }, value } if is_ident (key) ->
511 -- ``key = value''. --
516 | `Pair{ key, value } ->
517 -- ``[key] = value''. --
532 if #node > 1 then self:nldedent () else self:acc " " end
537 function M:Op (node, op, a, b)
538 -- Transform ``not (a == b)'' into ``a ~= b''. --
540 | `Op{ "not", `Op{ "eq", _a, _b } }
541 | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
542 op, a, b = "ne", _a, _b
546 if b then -- binary operator.
547 local left_paren, right_paren
549 | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
550 | _ -> left_paren = false
553 match b with -- FIXME: might not work with right assoc operators ^ and ..
554 | `Op{ op_b, ...} if op_prec[op] >= op_prec[op_b] -> right_paren = true
555 | _ -> right_paren = false
558 self:acc (left_paren and "(")
560 self:acc (left_paren and ")")
562 self:acc (op_symbol [op])
564 self:acc (right_paren and "(")
566 self:acc (right_paren and ")")
568 else -- unary operator.
571 | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
574 self:acc (op_symbol[op])
575 self:acc (paren and "(")
577 self:acc (paren and ")")
581 function M:Paren (node, content)
587 function M:Index (node, table, key)
589 -- Check precedence, see if parens are needed around the table --
591 | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
592 | _ -> paren_table = false
595 self:acc (paren_table and "(")
597 self:acc (paren_table and ")")
600 | `String{ field } if is_ident (field) ->
605 -- ``table [key]''. --
612 local function sanitize_name(name)
613 return name:gsub('%.', '__')
616 function M:Id (node, name)
617 if is_ident (name) then
619 else -- Unprintable identifier
620 local sanitized_name = sanitize_name(name)
621 if is_ident(sanitized_name) then
622 self:acc(sanitized_name)
626 self:String (node, name)
640 function M:TId (node, name) self:acc(name) end
643 function M:TCatbar(node, te, tebar)
651 function M:TFunction(node, p, r)
657 function M:TTable (node, default, pairs)
659 self:list (pairs, ', ')
660 if default.tag~='TField' then
667 function M:TPair (node, k, v)
673 function M:TIdbar (node, name)
677 function M:TCatbar (node, a, b)
683 function M:tebar(node)
684 if node.tag then self:node(node) else
686 self:list(node, ', ')
691 function M:TUnkbar(node, name)
696 function M:TUnk(node, name)
701 for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do
702 M[tag] = function(self, node, te)
708 function M:Label(node, name)
710 | `Id{n} -> self:Label(node, n)
712 if is_ident(name) then
716 else -- Unprintable identifier
717 local sanitized_name = sanitize_name(name)
718 if is_ident(sanitized_name) then
720 self:acc(sanitized_name)
725 self:String(node, name)
731 function M:Goto(node, name)
733 | `Id{n} -> self:Goto(node, n)
735 if is_ident(name) then
738 else -- Unprintable identifier
739 local sanitized_name = sanitize_name(name)
740 if is_ident(sanitized_name) then
742 self:acc(sanitized_name)
746 self:String(node, name)
752 function M:Stat(node, body, ret)
753 self:acc "(function()"
755 self:list (body, self.nl)