]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler/ast_to_src.mlua
ast_to_src: format function calls and unary operators without space
[metalua.git] / metalua / compiler / ast_to_src.mlua
1 -------------------------------------------------------------------------------
2 -- Copyright (c) 2006-2013 Fabien Fleutot and others.
3 --
4 -- All rights reserved.
5 --
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
10 --
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
14 --
15 -- Contributors:
16 --     Fabien Fleutot - API and implementation
17 --
18 -------------------------------------------------------------------------------
19
20 -{ extension ('match', ...) }
21
22 local M = { }
23 M.__index = M
24 M.__call = |self, ...| self:run(...)
25
26 local pp=require 'metalua.pprint'
27
28 --------------------------------------------------------------------------------
29 -- Instanciate a new AST->source synthetizer
30 --------------------------------------------------------------------------------
31 function M.new ()
32    local self = {
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'
36    }
37    return setmetatable (self, M)
38 end
39
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 --------------------------------------------------------------------------------
45 function M:run (ast)
46    if not ast then
47       self, ast = M.new(), self
48    end
49    self._acc = { }
50    self:node (ast)
51    return table.concat (self._acc)
52 end
53
54 --------------------------------------------------------------------------------
55 -- Accumulate a piece of source file in the synthetizer.
56 --------------------------------------------------------------------------------
57 function M:acc (x)
58    if x then table.insert (self._acc, x) end
59 end
60
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 --------------------------------------------------------------------------------
66 function M:nl ()
67    if self.current_indent == 0 then self:acc "\n"  end
68    self:acc ("\n" .. self.indent_step:rep (self.current_indent))
69 end
70
71 --------------------------------------------------------------------------------
72 -- Increase indentation and accumulate a new line.
73 --------------------------------------------------------------------------------
74 function M:nlindent ()
75    self.current_indent = self.current_indent + 1
76    self:nl ()
77 end
78
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))
85 end
86
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",
95    "while" }
96 local keywords = { }
97 for _, kw in pairs(keywords_list) do keywords[kw]=true end
98
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]
104 end
105
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)
113    match ast with
114    | `Id{ _ }                     -> return true
115    | `Index{ left, `String{ _ } } -> return is_idx_stack (left)
116    | _                            -> return false
117    end
118 end
119
120 --------------------------------------------------------------------------------
121 -- Operator precedences, in increasing order.
122 -- This is not directly used, it's used to generate op_prec below.
123 --------------------------------------------------------------------------------
124 local op_preprec = {
125    { "or", "and" },
126    { "lt", "le", "eq", "ne" },
127    { "concat" },
128    { "add", "sub" },
129    { "mul", "div", "mod" },
130    { "unm", "not", "len" },
131    { "pow" },
132    { "index" } }
133
134 --------------------------------------------------------------------------------
135 -- operator --> precedence table, generated from op_preprec.
136 --------------------------------------------------------------------------------
137 local op_prec = { }
138
139 for prec, ops in ipairs (op_preprec) do
140    for _, op in ipairs (ops) do
141       op_prec[op] = prec
142    end
143 end
144
145 --------------------------------------------------------------------------------
146 -- operator --> source representation.
147 --------------------------------------------------------------------------------
148 local op_symbol = {
149    add    = " + ",   sub     = " - ",   mul     = " * ",
150    div    = " / ",   mod     = " % ",   pow     = " ^ ",
151    concat = " .. ",  eq      = " == ",  ne      = " ~= ",
152    lt     = " < ",   le      = " <= ",  ["and"] = " and ",
153    ["or"] = " or ",  ["not"] = "not ",  len     = "#",
154    ["unm"] = "-",
155 }
156
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)
170        else
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.
175               self:acc (f)
176           else -- No appropriate method, fall back to splice dumping.
177               -- This cannot happen in a plain Lua AST.
178               self:acc " -{ "
179               self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
180               self:acc " }"
181           end
182       end
183    end
184 end
185
186 function M:block(body)
187     if not self.custom_printer or not self.custom_printer (self, body) then
188         self:nlindent ()
189         self:list     (body, self.nl)
190         self:nldedent ()
191     end
192 end
193
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
200 -- of a list.
201 --------------------------------------------------------------------------------
202 function M:list (list, sep, start)
203    for i = start or 1, # list do
204       self:node (list[i])
205       if list[i + 1] then
206          if not sep then
207          elseif type (sep) == "function" then sep (self)
208          elseif type (sep) == "string"   then self:acc (sep)
209          else   error "Invalid list separator" end
210       end
211    end
212 end
213
214 --------------------------------------------------------------------------------
215 --
216 -- Tag methods.
217 -- ------------
218 --
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.
223 --
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.
226 --
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.
233 --
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.
236 --
237 --------------------------------------------------------------------------------
238
239 function M:Do (node)
240    self:acc      "do"
241    self:block    (node)
242    self:acc      "end"
243 end
244
245 function M:Set (node)
246    match node with
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'' --
251       self:acc      "function "
252       self:node     (lhs)
253       self:acc      ":"
254       self:acc      (method)
255       self:acc      " ("
256       self:list     (params, ", ", 2)
257       self:acc      ")"
258       self:block    (body)
259       self:acc      "end"
260
261    | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
262       -- ``function foo(...) ... end'' --
263       self:acc      "function "
264       self:node     (lhs)
265       self:acc      " ("
266       self:list     (params, ", ")
267       self:acc      ")"
268       self:block    (body)
269       self:acc      "end"
270
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.
277       self:node     (lhs1)
278       if lhs[2] then -- more than one lhs variable
279          self:acc   ", "
280          self:list  (lhs, ", ", 2)
281       end
282       self:acc      " = "
283       self:list     (rhs, ", ")
284
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, ", ")
292       self:acc   " = "
293       self:list  (rhs, ", ")
294    | `Set{ lhs, rhs, annot } ->
295       -- ``... = ...'', no syntax sugar, annotation --
296       local n = #lhs
297       for i=1,n do
298           local ell, a = lhs[i], annot[i]
299           self:node (ell)
300           if a then
301               self:acc ' --'
302               self:node(a)
303           end
304           if i~=n then self:acc ', ' end
305       end
306       self:acc   " = "
307       self:list  (rhs, ", ")
308    end
309 end
310
311 function M:While (node, cond, body)
312    self:acc      "while "
313    self:node     (cond)
314    self:acc      " do"
315    self:block    (body)
316    self:acc      "end"
317 end
318
319 function M:Repeat (node, body, cond)
320    self:acc      "repeat"
321    self:block    (body)
322    self:acc      "until "
323    self:node     (cond)
324 end
325
326 function M:If (node)
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 ")
331       self:node     (cond)
332       self:acc      " then"
333       self:block    (body)
334    end
335    -- odd number of children --> last one is an `else' clause --
336    if #node%2 == 1 then
337       self:acc      "else"
338       self:block    (node[#node])
339    end
340    self:acc "end"
341 end
342
343 function M:Fornum (node, var, first, last)
344    local body = node[#node]
345    self:acc      "for "
346    self:node     (var)
347    self:acc      " = "
348    self:node     (first)
349    self:acc      ", "
350    self:node     (last)
351    if #node==5 then -- 5 children --> child #4 is a step increment.
352       self:acc   ", "
353       self:node  (node[4])
354    end
355    self:acc      " do"
356    self:block    (body)
357    self:acc      "end"
358 end
359
360 function M:Forin (node, vars, generators, body)
361    self:acc      "for "
362    self:list     (vars, ", ")
363    self:acc      " in "
364    self:list     (generators, ", ")
365    self:acc      " do"
366    self:block    (body)
367    self:acc      "end"
368 end
369
370 function M:Local (node, lhs, rhs, annots)
371     if next (lhs) then
372         self:acc     "local "
373         if annots then
374             local n = #lhs
375             for i=1, n do
376                 self:node (lhs)
377                 local a = annots[i]
378                 if a then
379                     self:acc ' #'
380                     self:node (a)
381                 end
382                 if i~=n then self:acc ', ' end
383             end
384         else
385             self:list    (lhs, ", ")
386         end
387         if rhs[1] then
388             self:acc  " = "
389             self:list (rhs, ", ")
390         end
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}))
393     end
394 end
395
396 function M:Localrec (node, lhs, rhs)
397    match node with
398    | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
399          if is_ident (name) ->
400       -- ``local function name() ... end'' --
401       self:acc      "local function "
402       self:acc      (name)
403       self:acc      " ("
404       self:list     (params, ", ")
405       self:acc      ")"
406       self:block    (body)
407       self:acc      "end"
408
409    | _ ->
410       -- Other localrec are unprintable ==> splice them --
411           -- This cannot happen in a plain Lua AST. --
412       self:acc "-{ "
413       self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
414       self:acc " }"
415    end
416 end
417
418 function M:Call (node, f)
419    -- single string or table literal arg ==> no need for parentheses. --
420   local parens, wrap
421   match node with
422   | `Call{`Function{...}, _} -> wrap = true
423   | `Call{`Function{...}} -> wrap = true
424   | _ -> wrap = false
425   end --[[ !! I doubt this should be here. Code that produces this AST should
426               produce a syntax error instead. ]]
427    match node with
428    | `Call{ _, `String{_} }
429    | `Call{ _, `Table{...}} -> parens = false
430    | _ -> parens = true
431    end
432    self:acc  (wrap and "(" or "")
433    self:node (f)
434    self:acc  (wrap and ")" or "")
435    self:acc  (parens and "(" or  " ")
436    self:list (node, ", ", 2) -- skip `f'.
437    self:acc  (parens and ")")
438 end
439
440 function M:Invoke (node, f, method)
441    -- single string or table literal arg ==> no need for parentheses. --
442    local parens, wrap
443    match node with
444    | `Invoke{ `String{_}, ...} -> wrap = true
445    | _ -> wrap = false
446    end
447    match node with
448    | `Invoke{ _, _, `String{_} }
449    | `Invoke{ _, _, `Table{...}} -> parens = false
450    | _ -> parens = true
451    end
452    self:acc    (wrap and "(" or "")
453    self:node   (f)
454    self:acc    (wrap and ")" or "")
455    self:acc    ":"
456    self:acc    (method[1])
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 ")")
460 end
461
462 function M:Return (node)
463    self:acc  "return "
464    self:list (node, ", ")
465 end
466
467 M.Break = "break"
468 M.Nil   = "nil"
469 M.False = "false"
470 M.True  = "true"
471 M.Dots  = "..."
472
473 function M:Number (node, n)
474    self:acc (tostring (n))
475 end
476
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"))
481 end
482
483 function M:Function (node, params, body, annots)
484     self:acc      "function ("
485     if annots then
486         local n = #params
487         for i=1,n do
488             local p, a = params[i], annots[i]
489             self:node(p)
490             if annots then
491                 self:acc " #"
492                 self:node(a)
493             end
494             if i~=n then self:acc ', ' end
495         end
496     else
497         self:list (params, ", ")
498     end
499     self:acc      ")"
500     self:block    (body)
501     self:acc      "end"
502 end
503
504 function M:Table (node)
505    if not node[1] then self:acc "{ }" else
506       self:acc "{"
507       if #node > 1 then self:nlindent () else self:acc " " end
508       for i, elem in ipairs (node) do
509          match elem with
510          | `Pair{ `String{ key }, value } if is_ident (key) ->
511             -- ``key = value''. --
512             self:acc  (key)
513             self:acc  " = "
514             self:node (value)
515
516          | `Pair{ key, value } ->
517             -- ``[key] = value''. --
518             self:acc  "["
519             self:node (key)
520             self:acc  "] = "
521             self:node (value)
522
523          | _ ->
524             -- ``value''. --
525             self:node (elem)
526          end
527          if node [i+1] then
528             self:acc ","
529             self:nl  ()
530          end
531       end
532       if #node > 1 then self:nldedent () else self:acc " " end
533       self:acc       "}"
534    end
535 end
536
537 function M:Op (node, op, a, b)
538    -- Transform ``not (a == b)'' into ``a ~= b''. --
539    match node with
540    | `Op{ "not", `Op{ "eq", _a, _b } }
541    | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
542       op, a, b = "ne", _a, _b
543    | _ ->
544    end
545
546    if b then -- binary operator.
547       local left_paren, right_paren
548       match a with
549       | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
550       | _ -> left_paren = false
551       end
552
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
556       end
557
558       self:acc  (left_paren and "(")
559       self:node (a)
560       self:acc  (left_paren and ")")
561
562       self:acc  (op_symbol [op])
563
564       self:acc  (right_paren and "(")
565       self:node (b)
566       self:acc  (right_paren and ")")
567
568    else -- unary operator.
569       local paren
570       match a with
571       | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
572       | _ -> paren = false
573       end
574       self:acc  (op_symbol[op])
575       self:acc  (paren and "(")
576       self:node (a)
577       self:acc  (paren and ")")
578    end
579 end
580
581 function M:Paren (node, content)
582    self:acc  "("
583    self:node (content)
584    self:acc  ")"
585 end
586
587 function M:Index (node, table, key)
588    local paren_table
589    -- Check precedence, see if parens are needed around the table --
590    match table with
591    | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
592    | _ -> paren_table = false
593    end
594
595    self:acc  (paren_table and "(")
596    self:node (table)
597    self:acc  (paren_table and ")")
598
599    match key with
600    | `String{ field } if is_ident (field) ->
601       -- ``table.key''. --
602       self:acc "."
603       self:acc (field)
604    | _ ->
605       -- ``table [key]''. --
606       self:acc   "["
607       self:node (key)
608       self:acc   "]"
609    end
610 end
611
612 local function sanitize_name(name)
613   return name:gsub('%.', '__')
614 end
615
616 function M:Id (node, name)
617    if is_ident (name) then
618       self:acc (name)
619   else -- Unprintable identifier
620     local sanitized_name = sanitize_name(name)
621     if is_ident(sanitized_name) then
622       self:acc(sanitized_name)
623       return nil
624     end
625       self:acc    "-{`Id "
626       self:String (node, name)
627       self:acc    "}"
628    end
629 end
630
631
632 M.TDyn    = '*'
633 M.TDynbar = '**'
634 M.TPass   = 'pass'
635 M.TField  = 'field'
636 M.TIdbar  = M.TId
637 M.TReturn = M.Return
638
639
640 function M:TId (node, name) self:acc(name) end
641
642
643 function M:TCatbar(node, te, tebar)
644     self:acc'('
645     self:node(te)
646     self:acc'|'
647     self:tebar(tebar)
648     self:acc')'
649 end
650
651 function M:TFunction(node, p, r)
652     self:tebar(p)
653     self:acc '->'
654     self:tebar(r)
655 end
656
657 function M:TTable (node, default, pairs)
658     self:acc '['
659     self:list (pairs, ', ')
660     if default.tag~='TField' then
661         self:acc '|'
662         self:node (default)
663     end
664     self:acc ']'
665 end
666
667 function M:TPair (node, k, v)
668     self:node (k)
669     self:acc '='
670     self:node (v)
671 end
672
673 function M:TIdbar (node, name)
674     self :acc (name)
675 end
676
677 function M:TCatbar (node, a, b)
678     self:node(a)
679     self:acc ' ++ '
680     self:node(b)
681 end
682
683 function M:tebar(node)
684     if node.tag then self:node(node) else
685         self:acc '('
686         self:list(node, ', ')
687         self:acc ')'
688     end
689 end
690
691 function M:TUnkbar(node, name)
692     self:acc '~~'
693     self:acc (name)
694 end
695
696 function M:TUnk(node, name)
697     self:acc '~'
698     self:acc (name)
699 end
700
701 for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do
702     M[tag] = function(self, node, te)
703         self:acc (name..' ')
704         self:node(te)
705     end
706 end
707
708 function M:Label(node, name)
709   match name with
710   | `Id{n} -> self:Label(node, n)
711   | _ ->
712     if is_ident(name) then
713       self:acc "::"
714       self:acc(name)
715       self:acc "::"
716     else -- Unprintable identifier
717       local sanitized_name = sanitize_name(name)
718       if is_ident(sanitized_name) then
719         self:acc "::"
720         self:acc(sanitized_name)
721         self:acc "::"
722         return nil
723       end
724       self:acc "-{`Id "
725       self:String(node, name)
726       self:acc "}"
727     end 
728   end
729 end
730
731 function M:Goto(node, name)
732   match name with
733   | `Id{n} -> self:Goto(node, n)
734   | _ ->
735     if is_ident(name) then
736       self:acc "goto "
737       self:acc(name)
738     else -- Unprintable identifier
739       local sanitized_name = sanitize_name(name)
740       if is_ident(sanitized_name) then
741         self:acc "goto "
742         self:acc(sanitized_name)
743         return nil
744       end
745       self:acc "-{`Goto "
746       self:String(node, name)
747       self:acc "}"
748     end 
749   end
750 end
751
752 function M:Stat(node, body, ret)
753   self:acc    "(function()"
754   self:nlindent()
755   self:list    (body, self.nl)
756   self:nl      ()
757   self:acc     "return "
758   self:node    (ret)
759   self:nldedent()
760   self:acc     "end)()"
761 end
762
763 return M