1 ----------------------------------------------------------------------
2 -- Metalua: $Id: mlp_stat.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
4 -- Summary: metalua parser, statement/block parser. This is part of
5 -- the definition of module [mlp].
7 ----------------------------------------------------------------------
9 -- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
11 -- This software is released under the MIT Licence, see licence.txt
14 ----------------------------------------------------------------------
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.
21 -- Revision 1.6 2006/11/10 02:11:17 fab13n
22 -- compiler faithfulness to 5.1 improved
24 -- mlp.expr refactored
26 -- Revision 1.5 2006/11/09 09:39:57 fab13n
29 -- Revision 1.4 2006/11/07 21:29:02 fab13n
30 -- improved quasi-quoting
32 -- Revision 1.3 2006/11/07 04:38:00 fab13n
33 -- first bootstrapping version.
35 -- Revision 1.2 2006/11/05 15:08:34 fab13n
36 -- updated code generation, to be compliant with 5.1
38 ----------------------------------------------------------------------
40 --------------------------------------------------------------------------------
45 -- * [mlp.for_header()]
46 -- * [mlp.add_block_terminators()]
48 --------------------------------------------------------------------------------
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
63 module ("mlp", package.seeall)
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 --------------------------------------------------------------------------------
71 local block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
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
79 --------------------------------------------------------------------------------
80 -- list of statements, possibly followed by semicolons
81 --------------------------------------------------------------------------------
83 name = "statements block",
84 terminators = block_terminators,
85 primary = function (lx)
87 if lx:is_keyword (lx:peek(), ";") then lx:next() end
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 }
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) }
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
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"
120 local e = expr_list (lx)
121 return { tag="Forin", var_list, e }
125 --------------------------------------------------------------------------------
126 -- Function def parser helper: id ( . id ) *
127 --------------------------------------------------------------------------------
128 local function fn_builder (list)
130 for i = 2, #list do r = { tag="Index", r, id2string(list[i]) } end
133 local func_name = gg.list{ id, separators = ".", builder = fn_builder }
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 } }
141 --------------------------------------------------------------------------------
142 -- Function def builder
143 --------------------------------------------------------------------------------
144 local function funcdef_builder(x)
145 local name, method, func = x[1], x[2], x[3]
147 name = { tag="Index", name, method }
148 _G.table.insert (func[1], 1, {tag="Id", "self"})
150 return { tag="Set", {name}, {func} }
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
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" } }
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]
180 --FIXME: check that [e] is a LHS
182 local v = expr_list (lx)
183 if type(op)=="string" then return { tag=op, e, v }
184 else return op (e, v) end
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>")
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 } }
206 --------------------------------------------------------------------------------
208 --------------------------------------------------------------------------------
209 stat = gg.multisequence {
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 }
229 function stat.assignments:add(k, v) self[k] = v end