]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler/parser/stat.lua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[metalua.git] / metalua / compiler / parser / stat.lua
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 -------------------------------------------------------------------------------
21 --
22 -- Summary: metalua parser, statement/block parser. This is part of the
23 -- definition of module [mlp].
24 --
25 -------------------------------------------------------------------------------
26
27 -------------------------------------------------------------------------------
28 --
29 -- Exports API:
30 -- * [mlp.stat()]
31 -- * [mlp.block()]
32 -- * [mlp.for_header()]
33 --
34 -------------------------------------------------------------------------------
35
36 local lexer    = require 'metalua.grammar.lexer'
37 local gg       = require 'metalua.grammar.generator'
38
39 local annot = require 'metalua.compiler.parser.annot.generator'
40
41 --------------------------------------------------------------------------------
42 -- List of all keywords that indicate the end of a statement block. Users are
43 -- likely to extend this list when designing extensions.
44 --------------------------------------------------------------------------------
45
46
47 return function(M)
48     local _M = gg.future(M)
49
50     M.block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
51
52     -- FIXME: this must be handled from within GG!!!
53     -- FIXME: there's no :add method in the list anyway. Added by gg.list?!
54     function M.block_terminators :add(x)
55         if type (x) == "table" then for _, y in ipairs(x) do self :add (y) end
56         else table.insert (self, x) end
57     end
58
59     ----------------------------------------------------------------------------
60     -- list of statements, possibly followed by semicolons
61     ----------------------------------------------------------------------------
62     M.block = gg.list {
63         name        = "statements block",
64         terminators = M.block_terminators,
65         primary     = function (lx)
66             -- FIXME use gg.optkeyword()
67             local x = M.stat (lx)
68             if lx:is_keyword (lx:peek(), ";") then lx:next() end
69             return x
70         end }
71
72     ----------------------------------------------------------------------------
73     -- Helper function for "return <expr_list>" parsing.
74     -- Called when parsing return statements.
75     -- The specific test for initial ";" is because it's not a block terminator,
76     -- so without it gg.list would choke on "return ;" statements.
77     -- We don't make a modified copy of block_terminators because this list
78     -- is sometimes modified at runtime, and the return parser would get out of
79     -- sync if it was relying on a copy.
80     ----------------------------------------------------------------------------
81     local return_expr_list_parser = gg.multisequence{
82         { ";" , builder = function() return { } end },
83         default = gg.list {
84             _M.expr, separators = ",", terminators = M.block_terminators } }
85
86
87     local for_vars_list = gg.list{
88         name        = "for variables list",
89         primary     = _M.id,
90         separators  = ",",
91         terminators = "in" }
92
93     ----------------------------------------------------------------------------
94     -- for header, between [for] and [do] (exclusive).
95     -- Return the `Forxxx{...} AST, without the body element (the last one).
96     ----------------------------------------------------------------------------
97     function M.for_header (lx)
98         local vars = M.id_list(lx)
99         if lx :is_keyword (lx:peek(), "=") then
100             if #vars ~= 1 then
101                 gg.parse_error (lx, "numeric for only accepts one variable")
102             end
103             lx:next() -- skip "="
104             local exprs = M.expr_list (lx)
105             if #exprs < 2 or #exprs > 3 then
106                 gg.parse_error (lx, "numeric for requires 2 or 3 boundaries")
107             end
108             return { tag="Fornum", vars[1], unpack (exprs) }
109         else
110             if not lx :is_keyword (lx :next(), "in") then
111                 gg.parse_error (lx, '"=" or "in" expected in for loop')
112             end
113             local exprs = M.expr_list (lx)
114             return { tag="Forin", vars, exprs }
115         end
116     end
117
118     ----------------------------------------------------------------------------
119     -- Function def parser helper: id ( . id ) *
120     ----------------------------------------------------------------------------
121     local function fn_builder (list)
122         local acc = list[1]
123         local first = acc.lineinfo.first
124         for i = 2, #list do
125             local index = M.id2string(list[i])
126             local li = lexer.new_lineinfo(first, index.lineinfo.last)
127             acc = { tag="Index", acc, index, lineinfo=li }
128         end
129         return acc
130     end
131     local func_name = gg.list{ _M.id, separators = ".", builder = fn_builder }
132
133     ----------------------------------------------------------------------------
134     -- Function def parser helper: ( : id )?
135     ----------------------------------------------------------------------------
136     local method_name = gg.onkeyword{ name = "method invocation", ":", _M.id,
137         transformers = { function(x) return x and x.tag=='Id' and M.id2string(x) end } }
138
139     ----------------------------------------------------------------------------
140     -- Function def builder
141     ----------------------------------------------------------------------------
142     local function funcdef_builder(x)
143         local name, method, func = unpack(x)
144         if method then
145             name = { tag="Index", name, method,
146                      lineinfo = {
147                          first = name.lineinfo.first,
148                          last  = method.lineinfo.last } }
149             table.insert (func[1], 1, {tag="Id", "self"})
150         end
151         local r = { tag="Set", {name}, {func} }
152         r[1].lineinfo = name.lineinfo
153         r[2].lineinfo = func.lineinfo
154         return r
155     end
156
157
158     ----------------------------------------------------------------------------
159     -- if statement builder
160     ----------------------------------------------------------------------------
161     local function if_builder (x)
162         local cond_block_pairs, else_block, r = x[1], x[2], {tag="If"}
163         local n_pairs = #cond_block_pairs
164         for i = 1, n_pairs do
165             local cond, block = unpack(cond_block_pairs[i])
166             r[2*i-1], r[2*i] = cond, block
167         end
168         if else_block then table.insert(r, #r+1, else_block) end
169         return r
170     end
171
172     --------------------------------------------------------------------------------
173     -- produce a list of (expr,block) pairs
174     --------------------------------------------------------------------------------
175     local elseifs_parser = gg.list {
176         gg.sequence { _M.expr, "then", _M.block , name='elseif parser' },
177         separators  = "elseif",
178         terminators = { "else", "end" }
179     }
180
181     local annot_expr = gg.sequence {
182         _M.expr,
183         gg.onkeyword{ "#", gg.future(M, 'annot').tf },
184         builder = function(x)
185             local e, a = unpack(x)
186             if a then return { tag='Annot', e, a }
187             else return e end
188         end }
189
190     local annot_expr_list = gg.list {
191         primary = annot.opt(M, _M.expr, 'tf'), separators = ',' }
192
193     ------------------------------------------------------------------------
194     -- assignments and calls: statements that don't start with a keyword
195     ------------------------------------------------------------------------
196     local function assign_or_call_stat_parser (lx)
197         local e = annot_expr_list (lx)
198         local a = lx:is_keyword(lx:peek())
199         local op = a and M.assignments[a]
200         -- TODO: refactor annotations
201         if op then
202             --FIXME: check that [e] is a LHS
203             lx :next()
204             local annots
205             e, annots = annot.split(e)
206             local v = M.expr_list (lx)
207             if type(op)=="string" then return { tag=op, e, v, annots }
208             else return op (e, v) end
209         else
210             assert (#e > 0)
211             if #e > 1 then
212                 gg.parse_error (lx,
213                     "comma is not a valid statement separator; statement can be "..
214                     "separated by semicolons, or not separated at all")
215             elseif e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then
216                 local typename
217                 if e[1].tag == 'Id' then
218                     typename = '("'..e[1][1]..'") is an identifier'
219                 elseif e[1].tag == 'Op' then
220                     typename = "is an arithmetic operation"
221                 else typename = "is of type '"..(e[1].tag or "<list>").."'" end
222                 gg.parse_error (lx,
223                      "This expression %s; "..
224                      "a statement was expected, and only function and method call "..
225                      "expressions can be used as statements", typename);
226             end
227             return e[1]
228         end
229     end
230
231     M.local_stat_parser = gg.multisequence{
232         -- local function <name> <func_val>
233         { "function", _M.id, _M.func_val, builder =
234           function(x)
235               local vars = { x[1], lineinfo = x[1].lineinfo }
236               local vals = { x[2], lineinfo = x[2].lineinfo }
237               return { tag="Localrec", vars, vals }
238           end },
239         -- local <id_list> ( = <expr_list> )?
240         default = gg.sequence{
241             gg.list{
242                 primary = annot.opt(M, _M.id, 'tf'),
243                 separators = ',' },
244             gg.onkeyword{ "=", _M.expr_list },
245             builder = function(x)
246                  local annotated_left, right = unpack(x)
247                  local left, annotations = annot.split(annotated_left)
248                  return {tag="Local", left, right or { }, annotations }
249              end } }
250
251     ------------------------------------------------------------------------
252     -- statement
253     ------------------------------------------------------------------------
254     M.stat = gg.multisequence {
255         name = "statement",
256         { "do", _M.block, "end", builder =
257           function (x) return { tag="Do", unpack (x[1]) } end },
258         { "for", _M.for_header, "do", _M.block, "end", builder =
259           function (x) x[1][#x[1]+1] = x[2]; return x[1] end },
260         { "function", func_name, method_name, _M.func_val, builder=funcdef_builder },
261         { "while", _M.expr, "do", _M.block, "end", builder = "While" },
262         { "repeat", _M.block, "until", _M.expr, builder = "Repeat" },
263         { "local", _M.local_stat_parser, builder = unpack },
264         { "return", return_expr_list_parser, builder =
265           function(x) x[1].tag='Return'; return x[1] end },
266         { "break", builder = function() return { tag="Break" } end },
267         { "-{", gg.future(M, 'meta').splice_content, "}", builder = unpack },
268         { "if", gg.nonempty(elseifs_parser), gg.onkeyword{ "else", M.block }, "end",
269           builder = if_builder },
270         default = assign_or_call_stat_parser }
271
272     M.assignments = {
273         ["="] = "Set"
274     }
275
276     function M.assignments:add(k, v) self[k] = v end
277
278     return M
279 end