]> git.lizzy.rs Git - metalua.git/blob - src/compiler/mlp_stat.lua
Made AST definition changes, in the compiler and in the libs.
[metalua.git] / src / compiler / mlp_stat.lua
1 ----------------------------------------------------------------------
2 -- Metalua:  $Id: mlp_stat.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
3 --
4 -- Summary: metalua parser, statement/block parser. This is part of
5 --   the definition of module [mlp].
6 --
7 ----------------------------------------------------------------------
8 --
9 -- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
10 --
11 -- This software is released under the MIT Licence, see licence.txt
12 -- for details.
13 --
14 ----------------------------------------------------------------------
15 -- History:
16 -- $Log: mlp_stat.lua,v $
17 -- Revision 1.7  2006/11/15 09:07:50  fab13n
18 -- debugged meta operators.
19 -- Added command line options handling.
20 --
21 -- Revision 1.6  2006/11/10 02:11:17  fab13n
22 -- compiler faithfulness to 5.1 improved
23 -- gg.expr extended
24 -- mlp.expr refactored
25 --
26 -- Revision 1.5  2006/11/09 09:39:57  fab13n
27 -- some cleanup
28 --
29 -- Revision 1.4  2006/11/07 21:29:02  fab13n
30 -- improved quasi-quoting
31 --
32 -- Revision 1.3  2006/11/07 04:38:00  fab13n
33 -- first bootstrapping version.
34 --
35 -- Revision 1.2  2006/11/05 15:08:34  fab13n
36 -- updated code generation, to be compliant with 5.1
37 --
38 ----------------------------------------------------------------------
39
40 --------------------------------------------------------------------------------
41 --
42 -- Exports API:
43 -- * [mlp.stat()]
44 -- * [mlp.block()]
45 -- * [mlp.for_header()]
46 -- * [mlp.add_block_terminators()]
47 --
48 --------------------------------------------------------------------------------
49
50 --require "gg"
51 --require "mll"
52 --require "mlp_misc"
53 --require "mlp_expr"
54 --require "mlp_meta"
55
56 --------------------------------------------------------------------------------
57 -- eta-expansions to break circular dependency
58 --------------------------------------------------------------------------------
59 local expr      = function (lx) return mlp.expr     (lx) end
60 local func_val  = function (lx) return mlp.func_val (lx) end
61 local expr_list = function (lx) return mlp.expr_list(lx) end
62
63 module ("mlp", package.seeall)
64
65 --------------------------------------------------------------------------------
66 -- List of all keywords that indicate the end of a statement block. Users are
67 -- likely to extend this list when designing extensions.
68 --------------------------------------------------------------------------------
69
70
71 local block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
72
73 -- FIXME: this must be handled from within GG!!!
74 function block_terminators:add(x) 
75    if type (x) == "table" then for _, y in ipairs(x) do self:add (y) end
76    else _G.table.insert (self, x) end
77 end
78
79 --------------------------------------------------------------------------------
80 -- list of statements, possibly followed by semicolons
81 --------------------------------------------------------------------------------
82 block = gg.list {
83    name        = "statements block",
84    terminators = block_terminators,
85    primary     = function (lx)
86       local x = stat (lx)
87       if lx:is_keyword (lx:peek(), ";") then lx:next() end
88       return x
89    end }
90
91 --------------------------------------------------------------------------------
92 -- Helper function for "return <expr_list>" parsing.
93 -- Called when parsing return statements
94 --------------------------------------------------------------------------------
95 local return_expr_list_parser = gg.list { 
96    expr, separators = ",", terminators = block_terminators }
97
98 --------------------------------------------------------------------------------
99 -- for header, between [for] and [do] (exclusive).
100 -- Return the `Forxxx{...} AST, without the body element (the last one).
101 --------------------------------------------------------------------------------
102 function for_header (lx)
103    local var = mlp.id (lx)
104    if lx:is_keyword (lx:peek(), "=") then 
105       -- Fornum: only 1 variable
106       lx:next() -- skip "="
107       local e = expr_list (lx)
108       assert (2 <= #e and #e <= 3, "2 or 3 values in a fornum")
109       return { tag="Fornum", var, unpack (e) }
110    else
111       -- Forin: there might be several vars
112       local a = lx:is_keyword (lx:next(), ",", "in")
113       if a=="in" then var_list = { var } else
114          -- several vars; first "," skipped, read other vars
115          var_list = gg.list{ 
116             primary = id, separators = ",", terminators = "in" } (lx)
117          _G.table.insert (var_list, 1, var) -- put back the first variable
118          lx:next() -- skip "in"
119       end
120       local e = expr_list (lx)
121       return { tag="Forin", var_list, e }
122    end
123 end
124
125 --------------------------------------------------------------------------------
126 -- Function def parser helper: id ( . id ) *
127 --------------------------------------------------------------------------------
128 local function fn_builder (list)
129    local r = list[1]
130    for i = 2, #list do r = { tag="Index", r, id2string(list[i]) } end
131    return r
132 end
133 local func_name = gg.list{ id, separators = ".", builder = fn_builder }
134
135 --------------------------------------------------------------------------------
136 -- Function def parser helper: ( : id )?
137 --------------------------------------------------------------------------------
138 local method_name = gg.onkeyword{ name = "method invocation", ":", id, 
139    transformers = { function(x) return x and id2string(x) end } }
140
141 --------------------------------------------------------------------------------
142 -- Function def builder
143 --------------------------------------------------------------------------------
144 local function funcdef_builder(x)
145    local name, method, func = x[1], x[2], x[3]
146    if method then 
147       name = { tag="Index", name, method }
148       _G.table.insert (func[1], 1, {tag="Id", "self"}) 
149    end
150    return { tag="Set", {name}, {func} } 
151 end 
152
153
154 --------------------------------------------------------------------------------
155 -- if statement builder
156 --------------------------------------------------------------------------------
157 local function if_builder (x)
158    local cb_pairs, else_block, r = x[1], x[2], {tag="If"}
159    for i=1,#cb_pairs do r[2*i-1]=cb_pairs[i][1]; r[2*i]=cb_pairs[i][2] end
160    if else_block then r[#r+1] = else_block end
161    return r
162 end 
163
164 --------------------------------------------------------------------------------
165 -- produce a list of (expr,block) pairs
166 --------------------------------------------------------------------------------
167 local elseifs_parser = gg.list {
168    gg.sequence { expr, "then", block },
169    separators  = "elseif",
170    terminators = { "else", "end" } }
171
172 --------------------------------------------------------------------------------
173 -- assignments and calls: statements that don't start with a keyword
174 --------------------------------------------------------------------------------
175 local function assign_or_call_stat_parser (lx)
176    local e = expr_list (lx)
177    local a = lx:is_keyword(lx:peek())
178    local op = a and stat.assignments[a]
179    if op then
180       --FIXME: check that [e] is a LHS
181       lx:next()
182       local v = expr_list (lx)
183       if type(op)=="string" then return { tag=op, e, v }
184       else return op (e, v) end
185    else 
186       assert (#e > 0)
187       if #e > 1 then 
188          gg.parse_error (lx, "comma is not a valid statement separator") end
189       if e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then
190          gg.parse_error (lx, "This expression is of type '%s'; "..
191             "only function and method calls make valid statements", 
192             e[1].tag or "<list>")
193       end
194       return e[1]
195    end
196 end
197
198 local local_stat_parser = gg.multisequence{
199    -- local function <name> <func_val>
200    { "function", id, func_val, builder = 
201       function(x) return { tag="Localrec", { x[1] }, { x[2] } } end },
202    -- local <id_list> ( = <expr_list> )?
203    default = gg.sequence{ id_list, gg.onkeyword{ "=", expr_list },
204       builder = function(x) return {tag="Local", x[1], x[2] or { } } end } }
205
206 --------------------------------------------------------------------------------
207 -- statement
208 --------------------------------------------------------------------------------
209 stat = gg.multisequence { 
210    name="statement",
211    { "do", block, "end", builder = 
212       function (x) return { tag="Do", unpack (x[1]) } end },
213    { "for", for_header, "do", block, "end", builder = 
214       function (x) x[1][#x[1]+1] = x[2]; return x[1] end },
215    { "function", func_name, method_name, func_val, builder=funcdef_builder },
216    { "while", expr, "do", block, "end", builder = "While" },
217    { "repeat", block, "until", expr, builder = "Repeat" },
218    { "local", local_stat_parser, builder = fget (1) },
219    { "return", return_expr_list_parser, builder = fget (1, "Return") },
220    { "break", builder = function() return { tag="Break" } end },
221    { "-{", splice_content, "}", builder = fget(1) },
222    { "if", elseifs_parser, gg.onkeyword{ "else", block }, "end", 
223      builder = if_builder },
224    default = assign_or_call_stat_parser }
225
226 stat.assignments = {
227    ["="] = "Set" }
228
229 function stat.assignments:add(k, v) self[k] = v end