]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler/ast_to_src.mlua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[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
25 local pp=require 'metalua.pprint'
26
27 --------------------------------------------------------------------------------
28 -- Instanciate a new AST->source synthetizer
29 --------------------------------------------------------------------------------
30 function M.new ()
31    local self = {
32       _acc           = { },  -- Accumulates pieces of source as strings
33       current_indent = 0,    -- Current level of line indentation
34       indent_step    = "   " -- Indentation symbol, normally spaces or '\t'
35    }
36    return setmetatable (self, M)
37 end
38
39 --------------------------------------------------------------------------------
40 -- Run a synthetizer on the `ast' arg and return the source as a string.
41 -- Can also be used as a static method `M.run (ast)'; in this case,
42 -- a temporary Metizer is instanciated on the fly.
43 --------------------------------------------------------------------------------
44 function M:run (ast)
45    if not ast then
46       self, ast = M.new(), self
47    end
48    self._acc = { }
49    self:node (ast)
50    return table.concat (self._acc)
51 end
52
53 --------------------------------------------------------------------------------
54 -- Accumulate a piece of source file in the synthetizer.
55 --------------------------------------------------------------------------------
56 function M:acc (x)
57    if x then table.insert (self._acc, x) end
58 end
59
60 --------------------------------------------------------------------------------
61 -- Accumulate an indented newline.
62 -- Jumps an extra line if indentation is 0, so that
63 -- toplevel definitions are separated by an extra empty line.
64 --------------------------------------------------------------------------------
65 function M:nl ()
66    if self.current_indent == 0 then self:acc "\n"  end
67    self:acc ("\n" .. self.indent_step:rep (self.current_indent))
68 end
69
70 --------------------------------------------------------------------------------
71 -- Increase indentation and accumulate a new line.
72 --------------------------------------------------------------------------------
73 function M:nlindent ()
74    self.current_indent = self.current_indent + 1
75    self:nl ()
76 end
77
78 --------------------------------------------------------------------------------
79 -- Decrease indentation and accumulate a new line.
80 --------------------------------------------------------------------------------
81 function M:nldedent ()
82    self.current_indent = self.current_indent - 1
83    self:acc ("\n" .. self.indent_step:rep (self.current_indent))
84 end
85
86 --------------------------------------------------------------------------------
87 -- Keywords, which are illegal as identifiers.
88 --------------------------------------------------------------------------------
89 local keywords_list = {
90    "and",    "break",   "do",    "else",   "elseif",
91    "end",    "false",   "for",   "function", "if",
92    "in",     "local",   "nil",   "not",    "or",
93    "repeat", "return",  "then",  "true",   "until",
94    "while" }
95 local keywords = { }
96 for _, kw in pairs(keywords_list) do keywords[kw]=true end
97
98 --------------------------------------------------------------------------------
99 -- Return true iff string `id' is a legal identifier name.
100 --------------------------------------------------------------------------------
101 local function is_ident (id)
102     return string['match'](id, "^[%a_][%w_]*$") and not keywords[id]
103 end
104
105 --------------------------------------------------------------------------------
106 -- Return true iff ast represents a legal function name for
107 -- syntax sugar ``function foo.bar.gnat() ... end'':
108 -- a series of nested string indexes, with an identifier as
109 -- the innermost node.
110 --------------------------------------------------------------------------------
111 local function is_idx_stack (ast)
112    match ast with
113    | `Id{ _ }                     -> return true
114    | `Index{ left, `String{ _ } } -> return is_idx_stack (left)
115    | _                            -> return false
116    end
117 end
118
119 --------------------------------------------------------------------------------
120 -- Operator precedences, in increasing order.
121 -- This is not directly used, it's used to generate op_prec below.
122 --------------------------------------------------------------------------------
123 local op_preprec = {
124    { "or", "and" },
125    { "lt", "le", "eq", "ne" },
126    { "concat" },
127    { "add", "sub" },
128    { "mul", "div", "mod" },
129    { "unary", "not", "len" },
130    { "pow" },
131    { "index" } }
132
133 --------------------------------------------------------------------------------
134 -- operator --> precedence table, generated from op_preprec.
135 --------------------------------------------------------------------------------
136 local op_prec = { }
137
138 for prec, ops in ipairs (op_preprec) do
139    for _, op in ipairs (ops) do
140       op_prec[op] = prec
141    end
142 end
143
144 --------------------------------------------------------------------------------
145 -- operator --> source representation.
146 --------------------------------------------------------------------------------
147 local op_symbol = {
148    add    = " + ",   sub     = " - ",   mul     = " * ",
149    div    = " / ",   mod     = " % ",   pow     = " ^ ",
150    concat = " .. ",  eq      = " == ",  ne      = " ~= ",
151    lt     = " < ",   le      = " <= ",  ["and"] = " and ",
152    ["or"] = " or ",  ["not"] = "not ",  len     = "# " }
153
154 --------------------------------------------------------------------------------
155 -- Accumulate the source representation of AST `node' in
156 -- the synthetizer. Most of the work is done by delegating to
157 -- the method having the name of the AST tag.
158 -- If something can't be converted to normal sources, it's
159 -- instead dumped as a `-{ ... }' splice in the source accumulator.
160 --------------------------------------------------------------------------------
161 function M:node (node)
162    assert (self~=M and self._acc)
163    if node==nil then self:acc'<<error>>'; return end
164    if not node.tag then -- tagless block.
165       self:list (node, self.nl)
166    else
167       local f = M[node.tag]
168       if type (f) == "function" then -- Delegate to tag method.
169          f (self, node, unpack (node))
170       elseif type (f) == "string" then -- tag string.
171          self:acc (f)
172       else -- No appropriate method, fall back to splice dumping.
173            -- This cannot happen in a plain Lua AST.
174          self:acc " -{ "
175          self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
176          self:acc " }"
177       end
178    end
179 end
180
181 --------------------------------------------------------------------------------
182 -- Convert every node in the AST list `list' passed as 1st arg.
183 -- `sep' is an optional separator to be accumulated between each list element,
184 -- it can be a string or a synth method.
185 -- `start' is an optional number (default == 1), indicating which is the
186 -- first element of list to be converted, so that we can skip the begining
187 -- of a list.
188 --------------------------------------------------------------------------------
189 function M:list (list, sep, start)
190    for i = start or 1, # list do
191       self:node (list[i])
192       if list[i + 1] then
193          if not sep then
194          elseif type (sep) == "function" then sep (self)
195          elseif type (sep) == "string"   then self:acc (sep)
196          else   error "Invalid list separator" end
197       end
198    end
199 end
200
201 --------------------------------------------------------------------------------
202 --
203 -- Tag methods.
204 -- ------------
205 --
206 -- Specific AST node dumping methods, associated to their node kinds
207 -- by their name, which is the corresponding AST tag.
208 -- synth:node() is in charge of delegating a node's treatment to the
209 -- appropriate tag method.
210 --
211 -- Such tag methods are called with the AST node as 1st arg.
212 -- As a convenience, the n node's children are passed as args #2 ... n+1.
213 --
214 -- There are several things that could be refactored into common subroutines
215 -- here: statement blocks dumping, function dumping...
216 -- However, given their small size and linear execution
217 -- (they basically perform series of :acc(), :node(), :list(),
218 -- :nl(), :nlindent() and :nldedent() calls), it seems more readable
219 -- to avoid multiplication of such tiny functions.
220 --
221 -- To make sense out of these, you need to know metalua's AST syntax, as
222 -- found in the reference manual or in metalua/doc/ast.txt.
223 --
224 --------------------------------------------------------------------------------
225
226 function M:Do (node)
227    self:acc      "do"
228    self:nlindent ()
229    self:list     (node, self.nl)
230    self:nldedent ()
231    self:acc      "end"
232 end
233
234 function M:Set (node)
235    match node with
236    | `Set{ { `Index{ lhs, `String{ method } } },
237            { `Function{ { `Id "self", ... } == params, body } } }
238          if is_idx_stack (lhs) and is_ident (method) ->
239       -- ``function foo:bar(...) ... end'' --
240       self:acc      "function "
241       self:node     (lhs)
242       self:acc      ":"
243       self:acc      (method)
244       self:acc      " ("
245       self:list     (params, ", ", 2)
246       self:acc      ")"
247       self:nlindent ()
248       self:list     (body, self.nl)
249       self:nldedent ()
250       self:acc      "end"
251
252    | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
253       -- ``function foo(...) ... end'' --
254       self:acc      "function "
255       self:node     (lhs)
256       self:acc      " ("
257       self:list     (params, ", ")
258       self:acc      ")"
259       self:nlindent ()
260       self:list    (body, self.nl)
261       self:nldedent ()
262       self:acc      "end"
263
264    | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs }
265          if not is_ident (lhs1name) ->
266       -- ``foo, ... = ...'' when foo is *not* a valid identifier.
267       -- In that case, the spliced 1st variable must get parentheses,
268       -- to be distinguished from a statement splice.
269       -- This cannot happen in a plain Lua AST.
270       self:acc      "("
271       self:node     (lhs1)
272       self:acc      ")"
273       if lhs[2] then -- more than one lhs variable
274          self:acc   ", "
275          self:list  (lhs, ", ", 2)
276       end
277       self:acc      " = "
278       self:list     (rhs, ", ")
279
280    | `Set{ lhs, rhs } ->
281       -- ``... = ...'', no syntax sugar --
282       self:list  (lhs, ", ")
283       self:acc   " = "
284       self:list  (rhs, ", ")
285    | `Set{ lhs, rhs, annot } ->
286       -- ``... = ...'', no syntax sugar, annotation --
287       local n = #lhs
288       for i=1,n do
289           local ell, a = lhs[i], annot[i]
290           self:node (ell)
291           if a then
292               self:acc ' #'
293               self:node(a)
294           end
295           if i~=n then self:acc ', ' end
296       end
297       self:acc   " = "
298       self:list  (rhs, ", ")
299    end
300 end
301
302 function M:While (node, cond, body)
303    self:acc      "while "
304    self:node     (cond)
305    self:acc      " do"
306    self:nlindent ()
307    self:list     (body, self.nl)
308    self:nldedent ()
309    self:acc      "end"
310 end
311
312 function M:Repeat (node, body, cond)
313    self:acc      "repeat"
314    self:nlindent ()
315    self:list     (body, self.nl)
316    self:nldedent ()
317    self:acc      "until "
318    self:node     (cond)
319 end
320
321 function M:If (node)
322    for i = 1, #node-1, 2 do
323       -- for each ``if/then'' and ``elseif/then'' pair --
324       local cond, body = node[i], node[i+1]
325       self:acc      (i==1 and "if " or "elseif ")
326       self:node     (cond)
327       self:acc      " then"
328       self:nlindent ()
329       self:list     (body, self.nl)
330       self:nldedent ()
331    end
332    -- odd number of children --> last one is an `else' clause --
333    if #node%2 == 1 then
334       self:acc      "else"
335       self:nlindent ()
336       self:list     (node[#node], self.nl)
337       self:nldedent ()
338    end
339    self:acc "end"
340 end
341
342 function M:Fornum (node, var, first, last)
343    local body = node[#node]
344    self:acc      "for "
345    self:node     (var)
346    self:acc      " = "
347    self:node     (first)
348    self:acc      ", "
349    self:node     (last)
350    if #node==5 then -- 5 children --> child #4 is a step increment.
351       self:acc   ", "
352       self:node  (node[4])
353    end
354    self:acc      " do"
355    self:nlindent ()
356    self:list     (body, self.nl)
357    self:nldedent ()
358    self:acc      "end"
359 end
360
361 function M:Forin (node, vars, generators, body)
362    self:acc      "for "
363    self:list     (vars, ", ")
364    self:acc      " in "
365    self:list     (generators, ", ")
366    self:acc      " do"
367    self:nlindent ()
368    self:list     (body, self.nl)
369    self:nldedent ()
370    self:acc      "end"
371 end
372
373 function M:Local (node, lhs, rhs, annots)
374     if next (lhs) then
375         self:acc     "local "
376         if annots then
377             local n = #lhs
378             for i=1, n do
379                 self:node (lhs)
380                 local a = annots[i]
381                 if a then
382                     self:acc ' #'
383                     self:node (a)
384                 end
385                 if i~=n then self:acc ', ' end
386             end
387         else
388             self:list    (lhs, ", ")
389         end
390         if rhs[1] then
391             self:acc  " = "
392             self:list (rhs, ", ")
393         end
394     else -- Can't create a local statement with 0 variables in plain Lua
395         self:acc (table.tostring (node, 'nohash', 80))
396     end
397 end
398
399 function M:Localrec (node, lhs, rhs)
400    match node with
401    | `Localrec{ { `Id{name} }, { `Function{ params, body } } }
402          if is_ident (name) ->
403       -- ``local function name() ... end'' --
404       self:acc      "local function "
405       self:acc      (name)
406       self:acc      " ("
407       self:list     (params, ", ")
408       self:acc      ")"
409       self:nlindent ()
410       self:list     (body, self.nl)
411       self:nldedent ()
412       self:acc      "end"
413
414    | _ ->
415       -- Other localrec are unprintable ==> splice them --
416           -- This cannot happen in a plain Lua AST. --
417       self:acc "-{ "
418       self:acc (table.tostring (node, 'nohash', 80))
419       self:acc " }"
420    end
421 end
422
423 function M:Call (node, f)
424    -- single string or table literal arg ==> no need for parentheses. --
425    local parens
426    match node with
427    | `Call{ _, `String{_} }
428    | `Call{ _, `Table{...}} -> parens = false
429    | _ -> parens = true
430    end
431    self:node (f)
432    self:acc  (parens and " (" or  " ")
433    self:list (node, ", ", 2) -- skip `f'.
434    self:acc  (parens and ")")
435 end
436
437 function M:Invoke (node, f, method)
438    -- single string or table literal arg ==> no need for parentheses. --
439    local parens
440    match node with
441    | `Invoke{ _, _, `String{_} }
442    | `Invoke{ _, _, `Table{...}} -> parens = false
443    | _ -> parens = true
444    end
445    self:node   (f)
446    self:acc    ":"
447    self:acc    (method[1])
448    self:acc    (parens and " (" or  " ")
449    self:list   (node, ", ", 3) -- Skip args #1 and #2, object and method name.
450    self:acc    (parens and ")")
451 end
452
453 function M:Return (node)
454    self:acc  "return "
455    self:list (node, ", ")
456 end
457
458 M.Break = "break"
459 M.Nil   = "nil"
460 M.False = "false"
461 M.True  = "true"
462 M.Dots  = "..."
463
464 function M:Number (node, n)
465    self:acc (tostring (n))
466 end
467
468 function M:String (node, str)
469    -- format "%q" prints '\n' in an umpractical way IMO,
470    -- so this is fixed with the :gsub( ) call.
471    self:acc (string.format ("%q", str):gsub ("\\\n", "\\n"))
472 end
473
474 function M:Function (node, params, body, annots)
475     self:acc      "function ("
476     if annots then
477         local n = #params
478         for i=1,n do
479             local p, a = params[i], annots[i]
480             self:node(p)
481             if annots then
482                 self:acc " #"
483                 self:node(a)
484             end
485             if i~=n then self:acc ', ' end
486         end
487     else
488         self:list (params, ", ")
489     end
490     self:acc      ")"
491     self:nlindent ()
492     self:list     (body, self.nl)
493     self:nldedent ()
494     self:acc      "end"
495 end
496
497 function M:Table (node)
498    if not node[1] then self:acc "{ }" else
499       self:acc "{"
500       if #node > 1 then self:nlindent () else self:acc " " end
501       for i, elem in ipairs (node) do
502          match elem with
503          | `Pair{ `String{ key }, value } if is_ident (key) ->
504             -- ``key = value''. --
505             self:acc  (key)
506             self:acc  " = "
507             self:node (value)
508
509          | `Pair{ key, value } ->
510             -- ``[key] = value''. --
511             self:acc  "["
512             self:node (key)
513             self:acc  "] = "
514             self:node (value)
515
516          | _ ->
517             -- ``value''. --
518             self:node (elem)
519          end
520          if node [i+1] then
521             self:acc ","
522             self:nl  ()
523          end
524       end
525       if #node > 1 then self:nldedent () else self:acc " " end
526       self:acc       "}"
527    end
528 end
529
530 function M:Op (node, op, a, b)
531    -- Transform ``not (a == b)'' into ``a ~= b''. --
532    match node with
533    | `Op{ "not", `Op{ "eq", _a, _b } }
534    | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } ->
535       op, a, b = "ne", _a, _b
536    | _ ->
537    end
538
539    if b then -- binary operator.
540       local left_paren, right_paren
541       match a with
542       | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true
543       | _ -> left_paren = false
544       end
545
546       match b with -- FIXME: might not work with right assoc operators ^ and ..
547       | `Op{ op_b, ...} if op_prec[op] >= op_prec[op_b] -> right_paren = true
548       | _ -> right_paren = false
549       end
550
551       self:acc  (left_paren and "(")
552       self:node (a)
553       self:acc  (left_paren and ")")
554
555       self:acc  (op_symbol [op])
556
557       self:acc  (right_paren and "(")
558       self:node (b)
559       self:acc  (right_paren and ")")
560
561    else -- unary operator.
562       local paren
563       match a with
564       | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true
565       | _ -> paren = false
566       end
567       self:acc  (op_symbol[op])
568       self:acc  (paren and "(")
569       self:node (a)
570       self:acc  (paren and ")")
571    end
572 end
573
574 function M:Paren (node, content)
575    self:acc  "("
576    self:node (content)
577    self:acc  ")"
578 end
579
580 function M:Index (node, table, key)
581    local paren_table
582    -- Check precedence, see if parens are needed around the table --
583    match table with
584    | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true
585    | _ -> paren_table = false
586    end
587
588    self:acc  (paren_table and "(")
589    self:node (table)
590    self:acc  (paren_table and ")")
591
592    match key with
593    | `String{ field } if is_ident (field) ->
594       -- ``table.key''. --
595       self:acc "."
596       self:acc (field)
597    | _ ->
598       -- ``table [key]''. --
599       self:acc   "["
600       self:node (key)
601       self:acc   "]"
602    end
603 end
604
605 function M:Id (node, name)
606    if is_ident (name) then
607       self:acc (name)
608    else -- Unprintable identifier, fall back to splice representation.
609         -- This cannot happen in a plain Lua AST.
610       self:acc    "-{`Id "
611       self:String (node, name)
612       self:acc    "}"
613    end
614 end
615
616
617 M.TDyn    = '*'
618 M.TDynbar = '**'
619 M.TPass   = 'pass'
620 M.TField  = 'field'
621 M.TIdbar  = M.TId
622 M.TReturn = M.Return
623
624
625 function M:TId (node, name) self:acc(name) end
626
627
628 function M:TCatbar(node, te, tebar)
629     self:acc'('
630     self:node(te)
631     self:acc'|'
632     self:tebar(tebar)
633     self:acc')'
634 end
635
636 function M:TFunction(node, p, r)
637     self:tebar(p)
638     self:acc '->'
639     self:tebar(r)
640 end
641
642 function M:TTable (node, default, pairs)
643     self:acc '['
644     self:list (pairs, ', ')
645     if default.tag~='TField' then
646         self:acc '|'
647         self:node (default)
648     end
649     self:acc ']'
650 end
651
652 function M:TPair (node, k, v)
653     self:node (k)
654     self:acc '='
655     self:node (v)
656 end
657
658 function M:TIdbar (node, name)
659     self :acc (name)
660 end
661
662 function M:TCatbar (node, a, b)
663     self:node(a)
664     self:acc ' ++ '
665     self:node(b)
666 end
667
668 function M:tebar(node)
669     if node.tag then self:node(node) else
670         self:acc '('
671         self:list(node, ', ')
672         self:acc ')'
673     end
674 end
675
676 function M:TUnkbar(node, name)
677     self:acc '~~'
678     self:acc (name)
679 end
680
681 function M:TUnk(node, name)
682     self:acc '~'
683     self:acc (name)
684 end
685
686 for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do
687     M[tag] = function(self, node, te)
688         self:acc (name..' ')
689         self:node(te)
690     end
691 end
692
693 return (|x| M.run(x))