]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler/ast_to_src.mlua
Bring ast_to_src up to date
[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    { "unary", "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
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)
168        else
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.
173               self:acc (f)
174           else -- No appropriate method, fall back to splice dumping.
175               -- This cannot happen in a plain Lua AST.
176               self:acc " -{ "
177               self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
178               self:acc " }"
179           end
180       end
181    end
182 end
183
184 function M:block(body)
185     if not self.custom_printer or not self.custom_printer (self, body) then
186         self:nlindent ()
187         self:list     (body, self.nl)
188         self:nldedent ()
189     end
190 end
191
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
198 -- of a list.
199 --------------------------------------------------------------------------------
200 function M:list (list, sep, start)
201    for i = start or 1, # list do
202       self:node (list[i])
203       if list[i + 1] then
204          if not sep then
205          elseif type (sep) == "function" then sep (self)
206          elseif type (sep) == "string"   then self:acc (sep)
207          else   error "Invalid list separator" end
208       end
209    end
210 end
211
212 --------------------------------------------------------------------------------
213 --
214 -- Tag methods.
215 -- ------------
216 --
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.
221 --
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.
224 --
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.
231 --
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.
234 --
235 --------------------------------------------------------------------------------
236
237 function M:Do (node)
238    self:acc      "do"
239    self:block    (node)
240    self:acc      "end"
241 end
242
243 function M:Set (node)
244    match node with
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'' --
249       self:acc      "function "
250       self:node     (lhs)
251       self:acc      ":"
252       self:acc      (method)
253       self:acc      " ("
254       self:list     (params, ", ", 2)
255       self:acc      ")"
256       self:block    (body)
257       self:acc      "end"
258
259    | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
260       -- ``function foo(...) ... end'' --
261       self:acc      "function "
262       self:node     (lhs)
263       self:acc      " ("
264       self:list     (params, ", ")
265       self:acc      ")"
266       self:block    (body)
267       self:acc      "end"
268
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.
275       self:acc      "("
276       self:node     (lhs1)
277       self:acc      ")"
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{ lhs, rhs } ->
286       -- ``... = ...'', no syntax sugar --
287       self:list  (lhs, ", ")
288       self:acc   " = "
289       self:list  (rhs, ", ")
290    | `Set{ lhs, rhs, annot } ->
291       -- ``... = ...'', no syntax sugar, annotation --
292       local n = #lhs
293       for i=1,n do
294           local ell, a = lhs[i], annot[i]
295           self:node (ell)
296           if a then
297               self:acc ' #'
298               self:node(a)
299           end
300           if i~=n then self:acc ', ' end
301       end
302       self:acc   " = "
303       self:list  (rhs, ", ")
304    end
305 end
306
307 function M:While (node, cond, body)
308    self:acc      "while "
309    self:node     (cond)
310    self:acc      " do"
311    self:block    (body)
312    self:acc      "end"
313 end
314
315 function M:Repeat (node, body, cond)
316    self:acc      "repeat"
317    self:block    (body)
318    self:acc      "until "
319    self:node     (cond)
320 end
321
322 function M:If (node)
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 ")
327       self:node     (cond)
328       self:acc      " then"
329       self:block    (body)
330    end
331    -- odd number of children --> last one is an `else' clause --
332    if #node%2 == 1 then
333       self:acc      "else"
334       self:block    (node[#node])
335    end
336    self:acc "end"
337 end
338
339 function M:Fornum (node, var, first, last)
340    local body = node[#node]
341    self:acc      "for "
342    self:node     (var)
343    self:acc      " = "
344    self:node     (first)
345    self:acc      ", "
346    self:node     (last)
347    if #node==5 then -- 5 children --> child #4 is a step increment.
348       self:acc   ", "
349       self:node  (node[4])
350    end
351    self:acc      " do"
352    self:block    (body)
353    self:acc      "end"
354 end
355
356 function M:Forin (node, vars, generators, body)
357    self:acc      "for "
358    self:list     (vars, ", ")
359    self:acc      " in "
360    self:list     (generators, ", ")
361    self:acc      " do"
362    self:block    (body)
363    self:acc      "end"
364 end
365
366 function M:Local (node, lhs, rhs, annots)
367     if next (lhs) then
368         self:acc     "local "
369         if annots then
370             local n = #lhs
371             for i=1, n do
372                 self:node (lhs)
373                 local a = annots[i]
374                 if a then
375                     self:acc ' #'
376                     self:node (a)
377                 end
378                 if i~=n then self:acc ', ' end
379             end
380         else
381             self:list    (lhs, ", ")
382         end
383         if rhs[1] then
384             self:acc  " = "
385             self:list (rhs, ", ")
386         end
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}))
389     end
390 end
391
392 function M:Localrec (node, lhs, rhs)
393    match node with
394    | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
395          if is_ident (name) ->
396       -- ``local function name() ... end'' --
397       self:acc      "local function "
398       self:acc      (name)
399       self:acc      " ("
400       self:list     (params, ", ")
401       self:acc      ")"
402       self:block    (body)
403       self:acc      "end"
404
405    | _ ->
406       -- Other localrec are unprintable ==> splice them --
407           -- This cannot happen in a plain Lua AST. --
408       self:acc "-{ "
409       self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
410       self:acc " }"
411    end
412 end
413
414 function M:Call (node, f)
415    -- single string or table literal arg ==> no need for parentheses. --
416    local parens
417    match node with
418    | `Call{ _, `String{_} }
419    | `Call{ _, `Table{...}} -> parens = false
420    | _ -> parens = true
421    end
422    self:node (f)
423    self:acc  (parens and " (" or  " ")
424    self:list (node, ", ", 2) -- skip `f'.
425    self:acc  (parens and ")")
426 end
427
428 function M:Invoke (node, f, method)
429    -- single string or table literal arg ==> no need for parentheses. --
430    local parens
431    match node with
432    | `Invoke{ _, _, `String{_} }
433    | `Invoke{ _, _, `Table{...}} -> parens = false
434    | _ -> parens = true
435    end
436    self:node   (f)
437    self:acc    ":"
438    self:acc    (method[1])
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 ")")
442 end
443
444 function M:Return (node)
445    self:acc  "return "
446    self:list (node, ", ")
447 end
448
449 M.Break = "break"
450 M.Nil   = "nil"
451 M.False = "false"
452 M.True  = "true"
453 M.Dots  = "..."
454
455 function M:Number (node, n)
456    self:acc (tostring (n))
457 end
458
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"))
463 end
464
465 function M:Function (node, params, body, annots)
466     self:acc      "function ("
467     if annots then
468         local n = #params
469         for i=1,n do
470             local p, a = params[i], annots[i]
471             self:node(p)
472             if annots then
473                 self:acc " #"
474                 self:node(a)
475             end
476             if i~=n then self:acc ', ' end
477         end
478     else
479         self:list (params, ", ")
480     end
481     self:acc      ")"
482     self:block    (body)
483     self:acc      "end"
484 end
485
486 function M:Table (node)
487    if not node[1] then self:acc "{ }" else
488       self:acc "{"
489       if #node > 1 then self:nlindent () else self:acc " " end
490       for i, elem in ipairs (node) do
491          match elem with
492          | `Pair{ `String{ key }, value } if is_ident (key) ->
493             -- ``key = value''. --
494             self:acc  (key)
495             self:acc  " = "
496             self:node (value)
497
498          | `Pair{ key, value } ->
499             -- ``[key] = value''. --
500             self:acc  "["
501             self:node (key)
502             self:acc  "] = "
503             self:node (value)
504
505          | _ ->
506             -- ``value''. --
507             self:node (elem)
508          end
509          if node [i+1] then
510             self:acc ","
511             self:nl  ()
512          end
513       end
514       if #node > 1 then self:nldedent () else self:acc " " end
515       self:acc       "}"
516    end
517 end
518
519 function M:Op (node, op, a, b)
520    -- Transform ``not (a == b)'' into ``a ~= b''. --
521    match node with
522    | `Op{ "not", `Op{ "eq", _a, _b } }
523    | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
524       op, a, b = "ne", _a, _b
525    | _ ->
526    end
527
528    if b then -- binary operator.
529       local left_paren, right_paren
530       match a with
531       | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
532       | _ -> left_paren = false
533       end
534
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
538       end
539
540       self:acc  (left_paren and "(")
541       self:node (a)
542       self:acc  (left_paren and ")")
543
544       self:acc  (op_symbol [op])
545
546       self:acc  (right_paren and "(")
547       self:node (b)
548       self:acc  (right_paren and ")")
549
550    else -- unary operator.
551       local paren
552       match a with
553       | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
554       | _ -> paren = false
555       end
556       self:acc  (op_symbol[op])
557       self:acc  (paren and "(")
558       self:node (a)
559       self:acc  (paren and ")")
560    end
561 end
562
563 function M:Paren (node, content)
564    self:acc  "("
565    self:node (content)
566    self:acc  ")"
567 end
568
569 function M:Index (node, table, key)
570    local paren_table
571    -- Check precedence, see if parens are needed around the table --
572    match table with
573    | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
574    | _ -> paren_table = false
575    end
576
577    self:acc  (paren_table and "(")
578    self:node (table)
579    self:acc  (paren_table and ")")
580
581    match key with
582    | `String{ field } if is_ident (field) ->
583       -- ``table.key''. --
584       self:acc "."
585       self:acc (field)
586    | _ ->
587       -- ``table [key]''. --
588       self:acc   "["
589       self:node (key)
590       self:acc   "]"
591    end
592 end
593
594 function M:Id (node, name)
595    if is_ident (name) then
596       self:acc (name)
597    else -- Unprintable identifier, fall back to splice representation.
598         -- This cannot happen in a plain Lua AST.
599       self:acc    "-{`Id "
600       self:String (node, name)
601       self:acc    "}"
602    end
603 end
604
605
606 M.TDyn    = '*'
607 M.TDynbar = '**'
608 M.TPass   = 'pass'
609 M.TField  = 'field'
610 M.TIdbar  = M.TId
611 M.TReturn = M.Return
612
613
614 function M:TId (node, name) self:acc(name) end
615
616
617 function M:TCatbar(node, te, tebar)
618     self:acc'('
619     self:node(te)
620     self:acc'|'
621     self:tebar(tebar)
622     self:acc')'
623 end
624
625 function M:TFunction(node, p, r)
626     self:tebar(p)
627     self:acc '->'
628     self:tebar(r)
629 end
630
631 function M:TTable (node, default, pairs)
632     self:acc '['
633     self:list (pairs, ', ')
634     if default.tag~='TField' then
635         self:acc '|'
636         self:node (default)
637     end
638     self:acc ']'
639 end
640
641 function M:TPair (node, k, v)
642     self:node (k)
643     self:acc '='
644     self:node (v)
645 end
646
647 function M:TIdbar (node, name)
648     self :acc (name)
649 end
650
651 function M:TCatbar (node, a, b)
652     self:node(a)
653     self:acc ' ++ '
654     self:node(b)
655 end
656
657 function M:tebar(node)
658     if node.tag then self:node(node) else
659         self:acc '('
660         self:list(node, ', ')
661         self:acc ')'
662     end
663 end
664
665 function M:TUnkbar(node, name)
666     self:acc '~~'
667     self:acc (name)
668 end
669
670 function M:TUnk(node, name)
671     self:acc '~'
672     self:acc (name)
673 end
674
675 for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do
676     M[tag] = function(self, node, te)
677         self:acc (name..' ')
678         self:node(te)
679     end
680 end
681
682 return M