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 ----------------------------------------------------------------------
18 --------------------------------------------------------------------------------
23 -- * [mlp.for_header()]
25 --------------------------------------------------------------------------------
27 --------------------------------------------------------------------------------
28 -- eta-expansions to break circular dependency
29 --------------------------------------------------------------------------------
30 local expr
= function (lx
) return mlp
.expr (lx
) end
31 local func_val
= function (lx
) return mlp
.func_val (lx
) end
32 local expr_list
= function (lx
) return mlp
.expr_list(lx
) end
34 module ("mlp", package
.seeall
)
36 --------------------------------------------------------------------------------
37 -- List of all keywords that indicate the end of a statement block. Users are
38 -- likely to extend this list when designing extensions.
39 --------------------------------------------------------------------------------
42 local block_terminators
= { "else", "elseif", "end", "until", ")", "}", "]" }
44 -- FIXME: this must be handled from within GG!!!
45 function block_terminators
:add(x
)
46 if type (x
) == "table" then for _
, y
in ipairs(x
) do self
:add (y
) end
47 else _G
.table.insert (self
, x
) end
50 --------------------------------------------------------------------------------
51 -- list of statements, possibly followed by semicolons
52 --------------------------------------------------------------------------------
54 name
= "statements block",
55 terminators
= block_terminators
,
56 primary
= function (lx
)
57 -- FIXME use gg.optkeyword()
59 if lx
:is_keyword (lx
:peek(), ";") then lx
:next() end
63 --------------------------------------------------------------------------------
64 -- Helper function for "return <expr_list>" parsing.
65 -- Called when parsing return statements.
66 -- The specific test for initial ";" is because it's not a block terminator,
67 -- so without itgg.list would choke on "return ;" statements.
68 -- We don't make a modified copy of block_terminators because this list
69 -- is sometimes modified at runtime, and the return parser would get out of
70 -- sync if it was relying on a copy.
71 --------------------------------------------------------------------------------
72 local return_expr_list_parser
= gg
.multisequence
{
73 { ";" , builder
= function() return { } end },
75 expr
, separators
= ",", terminators
= block_terminators
} }
77 --------------------------------------------------------------------------------
78 -- for header, between [for] and [do] (exclusive).
79 -- Return the `Forxxx{...} AST, without the body element (the last one).
80 --------------------------------------------------------------------------------
81 function for_header (lx
)
82 local var
= mlp
.id (lx
)
83 if lx
:is_keyword (lx
:peek(), "=") then
84 -- Fornum: only 1 variable
86 local e
= expr_list (lx
)
87 assert (2 <= #e
and #e
<= 3, "2 or 3 values in a fornum")
88 return { tag="Fornum", var
, unpack (e
) }
90 -- Forin: there might be several vars
91 local a
= lx
:is_keyword (lx
:next(), ",", "in")
92 if a
=="in" then var_list
= { var
, lineinfo
= var
.lineinfo
} else
93 -- several vars; first "," skipped, read other vars
95 primary
= id
, separators
= ",", terminators
= "in" } (lx
)
96 _G
.table.insert (var_list
, 1, var
) -- put back the first variable
97 lx
:next() -- skip "in"
99 local e
= expr_list (lx
)
100 return { tag="Forin", var_list
, e
}
104 --------------------------------------------------------------------------------
105 -- Function def parser helper: id ( . id ) *
106 --------------------------------------------------------------------------------
107 local function fn_builder (list
)
109 for i
= 2, #list
do r
= { tag="Index", r
, id2string(list
[i
]) } end
112 local func_name
= gg
.list
{ id
, separators
= ".", builder
= fn_builder
}
114 --------------------------------------------------------------------------------
115 -- Function def parser helper: ( : id )?
116 --------------------------------------------------------------------------------
117 local method_name
= gg
.onkeyword
{ name
= "method invocation", ":", id
,
118 transformers
= { function(x
) return x
and id2string(x
) end } }
120 --------------------------------------------------------------------------------
121 -- Function def builder
122 --------------------------------------------------------------------------------
123 local function funcdef_builder(x
)
124 local name
, method
, func
= x
[1], x
[2], x
[3]
126 name
= { tag="Index", name
, method
, lineinfo
= {
127 first
= name
.lineinfo
.first
,
128 last
= method
.lineinfo
.last
} }
129 _G
.table.insert (func
[1], 1, {tag="Id", "self"})
131 local r
= { tag="Set", {name
}, {func
} }
132 r
[1].lineinfo
= name
.lineinfo
133 r
[2].lineinfo
= func
.lineinfo
138 --------------------------------------------------------------------------------
139 -- if statement builder
140 --------------------------------------------------------------------------------
141 local function if_builder (x
)
142 local cb_pairs
, else_block
, r
= x
[1], x
[2], {tag="If"}
143 for i
=1,#cb_pairs
do r
[2*i
-1]=cb_pairs
[i
][1]; r
[2*i
]=cb_pairs
[i
][2] end
144 if else_block
then r
[#r
+1] = else_block
end
148 --------------------------------------------------------------------------------
149 -- produce a list of (expr,block) pairs
150 --------------------------------------------------------------------------------
151 local elseifs_parser
= gg
.list
{
152 gg
.sequence
{ expr
, "then", block
},
153 separators
= "elseif",
154 terminators
= { "else", "end" } }
156 --------------------------------------------------------------------------------
157 -- assignments and calls: statements that don't start with a keyword
158 --------------------------------------------------------------------------------
159 local function assign_or_call_stat_parser (lx
)
160 local e
= expr_list (lx
)
161 local a
= lx
:is_keyword(lx
:peek())
162 local op
= a
and stat
.assignments
[a
]
164 --FIXME: check that [e] is a LHS
166 local v
= expr_list (lx
)
167 if type(op
)=="string" then return { tag=op
, e
, v
}
168 else return op (e
, v
) end
172 gg
.parse_error (lx
, "comma is not a valid statement separator") end
173 if e
[1].tag ~= "Call" and e
[1].tag ~= "Invoke" then
174 gg
.parse_error (lx
, "This expression is of type '%s'; "..
175 "only function and method calls make valid statements",
176 e
[1].tag or "<list>")
182 local local_stat_parser
= gg
.multisequence
{
183 -- local function <name> <func_val>
184 { "function", id
, func_val
, builder
=
186 local vars
= { x
[1], lineinfo
= x
[1].lineinfo
}
187 local vals
= { x
[2], lineinfo
= x
[2].lineinfo
}
188 return { tag="Localrec", vars
, vals
}
190 -- local <id_list> ( = <expr_list> )?
191 default
= gg
.sequence
{ id_list
, gg
.onkeyword
{ "=", expr_list
},
192 builder
= function(x
) return {tag="Local", x
[1], x
[2] or { } } end } }
194 --------------------------------------------------------------------------------
196 --------------------------------------------------------------------------------
197 stat
= gg
.multisequence
{
199 { "do", block
, "end", builder
=
200 function (x
) return { tag="Do", unpack (x
[1]) } end },
201 { "for", for_header
, "do", block
, "end", builder
=
202 function (x
) x
[1][#x
[1]+1] = x
[2]; return x
[1] end },
203 { "function", func_name
, method_name
, func_val
, builder
=funcdef_builder
},
204 { "while", expr
, "do", block
, "end", builder
= "While" },
205 { "repeat", block
, "until", expr
, builder
= "Repeat" },
206 { "local", local_stat_parser
, builder
= fget (1) },
207 { "return", return_expr_list_parser
, builder
= fget (1, "Return") },
208 { "break", builder
= function() return { tag="Break" } end },
209 { "-{", splice_content
, "}", builder
= fget(1) },
210 { "if", elseifs_parser
, gg
.onkeyword
{ "else", block
}, "end",
211 builder
= if_builder
},
212 default
= assign_or_call_stat_parser
}
217 function stat
.assignments
:add(k
, v
) self
[k
] = v
end