events renaming
[gaemu.git] / gmlparser / parser.d
blobb210c8f1d8fd0edf98c48a6d6cc8175ddd0e942d
1 /* GML parser
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module gmlparser.parser is aliced;
20 import gmlparser.ast;
21 import gmlparser.lexer;
22 import gmlparser.tokens;
25 final class Parser {
26 bool strict = false;
27 bool warnings = false;
28 Lexer lex;
29 Node curbreak, curcont; // current nodes for `break` and `continue`
31 this (Lexer alex, bool astrict=false) {
32 lex = alex;
33 strict = astrict;
36 this(T) (const(char)[] atext, T afname, bool astrict=false) if (is(T : const(char)[])) {
37 lex = new Lexer(atext, afname);
38 strict = astrict;
41 static void errorAt (Loc loc, string msg, string file=__FILE__, usize line=__LINE__) {
42 throw new ErrorAt(loc, msg, null, file, line);
45 // ////////////////////////////////////////////////////////////////////// //
46 // expression parser helpers
47 Node parseExprBinOp(string me, string upfunc, T...) (bool stopOnAss) {
48 auto e = mixin(upfunc~"(stopOnAss)");
49 assert(e !is null);
50 mainloop: while (lex.isKw) {
51 foreach (immutable idx, auto _; T) {
52 static if (idx%2 == 0) {
53 if (lex == T[idx]) {
54 static if (T[idx] == Keyword.Ass) {
55 if (stopOnAss) break mainloop;
56 warning(lex.loc, "'=' instead of '=='");
58 auto loc = lex.loc;
59 lex.popFront();
60 auto r = mixin(me~"(stopOnAss)");
61 assert(r !is null);
62 // hacks
63 static if (T[idx] == Keyword.Ass) { enum textual = true; alias tp = NodeBinaryEqu; }
64 else static if (T[idx] == Keyword.And) { enum textual = true; alias tp = NodeBinaryLogAnd; }
65 else static if (T[idx] == Keyword.Or) { enum textual = true; alias tp = NodeBinaryLogOr; }
66 else static if (T[idx] == Keyword.Xor) { enum textual = true; alias tp = NodeBinaryLogXor; }
67 else { enum textual = false; alias tp = T[idx+1]; }
68 e = new tp(e, r);
69 e.textual = textual;
70 e.loc = loc;
71 assert(e !is null);
72 continue mainloop;
76 break;
78 return e;
81 mixin template BuildExprBinOp(string name, string upfunc, T...) {
82 static private template BuildOps(T...) {
83 static if (T.length == 0)
84 enum BuildOps = "";
85 else
86 enum BuildOps = "Keyword."~T[0]~", NodeBinary"~T[0]~", "~BuildOps!(T[1..$]);
88 mixin(
89 "Node parseExpr"~name~" (bool stopOnAss) {"~
90 " return parseExprBinOp!(\"parseExpr"~upfunc~"\", \"parseExpr"~upfunc~"\", "~BuildOps!T~")(stopOnAss);"~
91 "}");
94 // ////////////////////////////////////////////////////////////////////// //
95 // expression parser
97 // lparen eaten; returns fc
98 Node parseFCallArgs (NodeFCall fc) {
99 while (lex != Keyword.RParen) {
100 fc.args ~= parseExpr();
101 if (lex.eatKw(Keyword.Comma)) continue;
102 break;
104 lex.expect(Keyword.RParen);
105 return fc;
108 Node parseExprPrimary () {
109 auto loc = lex.loc;
111 // literals and id
112 switch (lex.front.type) {
113 case Token.Type.Num: auto n = lex.front.num; lex.popFront(); return new NodeLiteralNum(loc, n);
114 case Token.Type.Str: auto n = lex.front.istr; lex.popFront(); return new NodeLiteralString(loc, n);
115 case Token.Type.Id: return new NodeId(loc, lex.expectId);
116 default: break;
119 // "(...)"
120 if (lex.eatKw(Keyword.LParen)) {
121 auto res = parseExpr();
122 if (lex != Keyword.RParen) errorAt(lex.loc, "`)` expected for `(` at "~loc.toStringNoFile);
123 lex.expect(Keyword.RParen);
124 return res;
127 // `true`, `false`, and other funny keywords
128 if (lex.eatKw(Keyword.True)) return new NodeLiteralNum(loc, 1);
129 if (lex.eatKw(Keyword.False)) return new NodeLiteralNum(loc, 0);
130 if (lex.eatKw(Keyword.All)) return new NodeId(loc, "all");
131 if (lex.eatKw(Keyword.Noone)) return new NodeId(loc, "noone");
132 if (lex.eatKw(Keyword.Self)) return new NodeId(loc, "self");
133 if (lex.eatKw(Keyword.Other)) return new NodeId(loc, "other");
134 if (lex.eatKw(Keyword.Global)) return new NodeId(loc, "global");
135 if (lex.eatKw(Keyword.Pi)) { import std.math : PI; return new NodeLiteralNum(loc, PI); }
137 // global scope
138 if (lex.eatKw(Keyword.Dot)) errorAt(loc, "no global scope access is supported yet");
140 if (lex.isKw(Keyword.PlusPlus)) errorAt(loc, "GML doesn't have '++'");
141 if (lex.isKw(Keyword.MinusMinus)) errorAt(loc, "GML doesn't have '--'");
143 errorAt(loc, "primary expression expected");
144 assert(0);
147 Node parseIndexing (Node n) {
148 auto loc = lex.loc;
149 //lex.expect(Keyword.LBracket); // eaten
150 auto res = new NodeIndex(n, loc);
151 res.ei0 = parseExpr();
152 if (lex.eatKw(Keyword.Comma)) {
153 res.ei1 = parseExpr();
155 lex.expect(Keyword.RBracket);
156 return res;
159 Node parseExprPostfix (Node n) {
160 for (;;) {
161 auto nn = lex.select!(Node, "pop-nondefault")(
162 Keyword.Dot, (Loc aloc) => new NodeDot(n, aloc, lex.expectId),
163 Keyword.LParen, (Loc aloc) => parseFCallArgs(new NodeFCall(aloc, n)),
164 Keyword.LBracket, (Loc aloc) => parseIndexing(n),
165 () => null, // special
167 if (nn is null) return n;
168 n = nn;
172 Node parseExprUnary (bool stopOnAss=false) {
173 auto loc = lex.loc;
175 if (lex.eatKw(Keyword.Add)) return parseExprUnary();
176 if (lex.eatKw(Keyword.Sub)) return new NodeUnaryNeg(parseExprUnary(), loc);
177 if (lex.eatKw(Keyword.LogNot)) return new NodeUnaryNot(parseExprUnary(), loc);
178 if (lex.eatKw(Keyword.Not)) { auto res = new NodeUnaryNot(parseExprUnary(), loc); res.textual = true; return res; }
179 if (lex.eatKw(Keyword.BitNeg)) return new NodeUnaryBitNeg(parseExprUnary(), loc);
181 auto res = parseExprPrimary();
182 return parseExprPostfix(res);
185 // name upfunc tokens
186 mixin BuildExprBinOp!("Mul", "Unary", "Mul", "Div", "RDiv", "Mod");
187 mixin BuildExprBinOp!("Add", "Mul", "Add", "Sub"); // binop `~` is here too, but we don't have it
188 mixin BuildExprBinOp!("Shift", "Add", "LShift", "RShift");
189 mixin BuildExprBinOp!("Cmp", "Shift", "Less", "Great", "Equ", "NotEqu", "LessEqu", "GreatEqu", "Ass"); // `a is b`, `a in b` are here too
190 mixin BuildExprBinOp!("BitAnd", "Cmp", "BitAnd");
191 mixin BuildExprBinOp!("BitOr", "BitAnd", "BitOr", "BitXor");
192 mixin BuildExprBinOp!("LogAnd", "BitOr", "LogAnd", "And");
193 mixin BuildExprBinOp!("LogOr", "LogAnd", "LogOr", "LogXor", "Or", "Xor");
195 Node parseExpr () {
196 return parseExprLogOr(false);
199 // this can be assign expression, check it
200 Node parseAssExpr () {
201 auto e = parseExprLogOr(true); // stop on assign
202 auto loc = lex.loc;
203 if (lex.eatKw(Keyword.Ass)) return new NodeBinaryAss(e, parseExpr(), loc);
204 if (lex.eatKw(Keyword.AssAdd)) return new NodeBinaryAss(e, new NodeBinaryAdd(e, parseExpr(), lex.loc), loc);
205 if (lex.eatKw(Keyword.AssSub)) return new NodeBinaryAss(e, new NodeBinarySub(e, parseExpr(), lex.loc), loc);
206 if (lex.eatKw(Keyword.AssMul)) return new NodeBinaryAss(e, new NodeBinaryMul(e, parseExpr(), lex.loc), loc);
207 if (lex.eatKw(Keyword.AssDiv)) return new NodeBinaryAss(e, new NodeBinaryRDiv(e, parseExpr(), lex.loc), loc);
208 if (lex.eatKw(Keyword.AssBitAnd)) return new NodeBinaryAss(e, new NodeBinaryBitAnd(e, parseExpr(), lex.loc), loc);
209 if (lex.eatKw(Keyword.AssBitOr)) return new NodeBinaryAss(e, new NodeBinaryBitOr(e, parseExpr(), lex.loc), loc);
210 if (lex.eatKw(Keyword.AssBitXor)) return new NodeBinaryAss(e, new NodeBinaryBitXor(e, parseExpr(), lex.loc), loc);
211 if (lex.eatKw(Keyword.AssLShift)) return new NodeBinaryAss(e, new NodeBinaryLShift(e, parseExpr(), lex.loc), loc);
212 if (lex.eatKw(Keyword.AssRShift)) return new NodeBinaryAss(e, new NodeBinaryRShift(e, parseExpr(), lex.loc), loc);
213 return e;
216 // ////////////////////////////////////////////////////////////////////// //
217 void warning(A...) (Loc loc, A args) {
218 if (warnings) {
219 import std.stdio : stderr;
220 stderr.writeln("WARNING at ", loc, ": ", args);
224 void endOfStatement () {
225 if (!lex.eatKw(Keyword.Semi)) {
226 if (strict) {
227 lex.expect(Keyword.Semi);
228 } else {
229 warning(lex.peloc, "';' missing");
234 Node exprInParens () {
235 if (strict) {
236 lex.expect(Keyword.LParen);
237 auto ec = parseExpr();
238 lex.expect(Keyword.RParen);
239 return ec;
240 } else {
241 if (!lex.isKw(Keyword.LParen)) warning(lex.loc, "'(' missing");
242 return parseExpr();
246 // higher-level parsers
247 // can create new block
248 Node parseCodeBlock () {
249 auto loc = lex.loc;
250 lex.expect(Keyword.LCurly);
251 auto blk = new NodeBlock(loc);
252 // "{}" is just an empty statement, but we'll still create empty code block
253 while (!lex.isKw(Keyword.RCurly)) blk.addStatement(parseStatement());
254 lex.expect(Keyword.RCurly);
255 return blk;
258 Node parseReturn () {
259 auto loc = lex.loc;
260 lex.expect(Keyword.Return);
261 auto res = new NodeReturn(parseExpr(), loc);
262 endOfStatement();
263 return res;
266 Node parseExit () {
267 auto loc = lex.loc;
268 lex.expect(Keyword.Exit);
269 auto res = new NodeReturn(null, loc);
270 endOfStatement();
271 return res;
274 Node parseIf () {
275 auto loc = lex.loc;
276 lex.expect(Keyword.If);
277 auto ec = exprInParens();
278 auto et = parseStatement();
279 if (!strict && lex.isKw(Keyword.Semi)) {
280 int pos = 1;
281 while (lex.peek(pos).isKw(Keyword.Semi)) ++pos;
282 if (lex.peek(pos).isKw(Keyword.Else)) {
283 if (strict) throw new ErrorAt(lex.peek(pos-1).loc, "unexpected ';'");
284 warning(lex.loc, "extra ';'");
285 while (lex.eatKw(Keyword.Semi)) {}
288 auto ef = (lex.eatKw(Keyword.Else) ? parseStatement() : null);
289 return new NodeIf(ec, et, ef, loc);
292 Node parseWhile () {
293 auto res = new NodeWhile(lex.loc);
294 auto oldbreak = curbreak;
295 auto oldcont = curcont;
296 scope(exit) { curbreak = oldbreak; curcont = oldcont; }
297 curbreak = curcont = res;
298 lex.expect(Keyword.While);
299 res.econd = exprInParens();
300 res.ebody = parseStatement();
301 return res;
304 Node parseDoUntil () {
305 auto res = new NodeDoUntil(lex.loc);
306 auto oldbreak = curbreak;
307 auto oldcont = curcont;
308 scope(exit) { curbreak = oldbreak; curcont = oldcont; }
309 curbreak = curcont = res;
310 lex.expect(Keyword.Do);
311 res.ebody = parseStatement();
312 lex.expect(Keyword.Until);
313 res.econd = exprInParens();
314 endOfStatement();
315 return res;
318 Node parseRepeat () {
319 auto res = new NodeRepeat(lex.loc);
320 auto oldbreak = curbreak;
321 auto oldcont = curcont;
322 scope(exit) { curbreak = oldbreak; curcont = oldcont; }
323 curbreak = curcont = res;
324 lex.expect(Keyword.Repeat);
325 res.ecount = exprInParens();
326 res.ebody = parseStatement();
327 return res;
330 Node parseWith () {
331 auto loc = lex.loc;
332 lex.expect(Keyword.With);
333 auto wc = exprInParens();
334 auto res = new NodeWith(wc, loc);
335 auto oldbreak = curbreak;
336 auto oldcont = curcont;
337 scope(exit) { curbreak = oldbreak; curcont = oldcont; }
338 curbreak = curcont = res;
339 res.ebody = parseStatement();
340 return res;
343 Node parseVar () {
344 auto loc = lex.loc;
345 bool gvar = false;
346 if (lex.eatKw(Keyword.Globalvar)) {
347 gvar = true;
348 } else {
349 lex.expect(Keyword.Var);
351 if (!lex.isId) lex.error("identifier expected");
352 auto vd = new NodeVarDecl(loc);
353 vd.asGlobal = gvar;
354 while (lex.isId) {
355 //if (vd.hasVar(lex.front.tstr)) lex.error("duplicate variable name '"~lex.front.istr~"'");
356 vd.names ~= lex.expectId;
357 if (lex.isKw(Keyword.Ass)) lex.error("GML doesn't support variable initialization");
358 if (!lex.eatKw(Keyword.Comma)) break;
360 endOfStatement();
361 return vd;
364 Node parseBreak () {
365 if (curbreak is null) {
366 if (strict) lex.error("`break` without loop/switch");
367 warning(lex.loc, "`break` without loop/switch");
369 auto loc = lex.loc;
370 lex.expect(Keyword.Break);
371 auto res = new NodeStatementBreak(loc, curbreak);
372 endOfStatement();
373 return res;
376 Node parseCont () {
377 if (curcont is null) {
378 if (strict) lex.error("`continue` without loop/switch");
379 warning(lex.loc, "`continue` without loop/switch");
381 auto loc = lex.loc;
382 lex.expect(Keyword.Continue);
383 auto res = new NodeStatementContinue(loc, curcont);
384 endOfStatement();
385 return res;
388 Node parseFor () {
389 auto forn = new NodeFor(lex.loc);
390 auto oldbreak = curbreak;
391 auto oldcont = curcont;
392 scope(exit) { curbreak = oldbreak; curcont = oldcont; }
393 curbreak = curcont = forn;
394 lex.expect(Keyword.For);
395 lex.expect(Keyword.LParen);
396 // init
397 forn.einit = parseAssExpr();
398 lex.expect(Keyword.Semi);
399 // condition
400 forn.econd = parseExpr();
401 lex.expect(Keyword.Semi);
402 // next
403 forn.enext = parseAssExpr();
404 lex.expect(Keyword.RParen);
405 forn.ebody = parseStatement();
406 return forn;
409 Node parseSwitch () {
410 auto sw = new NodeSwitch(lex.loc);
411 auto oldbreak = curbreak;
412 scope(exit) { curbreak = oldbreak; }
413 curbreak = sw;
414 lex.expect(Keyword.Switch);
415 sw.e = exprInParens();
416 lex.expect(Keyword.LCurly);
417 // parse case nodes; i won't support insane things like Duff's device here
418 while (lex != Keyword.RCurly) {
419 Node e;
420 if (lex.eatKw(Keyword.Default)) {
421 // do nothing here
422 } else if (lex.eatKw(Keyword.Case)) {
423 e = parseExpr();
424 } else {
425 lex.expect(Keyword.Case);
427 lex.expect(Keyword.Colon);
428 // `case` without body
429 if (lex != Keyword.Case && lex != Keyword.Default && lex != Keyword.RCurly) {
430 auto blk = new NodeBlock(lex.loc);
431 while (lex != Keyword.Case && lex != Keyword.Default && lex != Keyword.RCurly) {
432 blk.addStatement(parseStatement());
434 sw.appendCase(e, blk);
435 } else {
436 sw.appendCase(e, null);
439 lex.expect(Keyword.RCurly);
440 return sw;
443 // can create new block
444 Node parseStatement () {
445 // var declaration
446 auto loc = lex.loc;
447 // empty statement
448 if (lex.eatKw(Keyword.Semi)) {
449 warning(loc, "use '{}' instead of ';' for empty statement");
450 return new NodeStatementEmpty(loc);
452 // block statement
453 if (lex.isKw(Keyword.LCurly)) return parseCodeBlock();
454 // operators and other keyworded things
455 if (lex.isKw) {
456 // some keyword
457 switch (lex.front.kw) {
458 case Keyword.If: return parseIf();
459 case Keyword.Return: return parseReturn();
460 case Keyword.Exit: return parseExit();
461 case Keyword.For: return parseFor();
462 case Keyword.While: return parseWhile();
463 case Keyword.Do: return parseDoUntil();
464 case Keyword.Repeat: return parseRepeat();
465 case Keyword.Break: return parseBreak();
466 case Keyword.Continue: return parseCont();
467 case Keyword.Switch: return parseSwitch();
468 case Keyword.Var: return parseVar();
469 case Keyword.Globalvar: return parseVar();
470 case Keyword.With: return parseWith();
471 case Keyword.Case: lex.error("you cannot use `case` here"); return null;
472 case Keyword.Default: lex.error("you cannot use `default` here"); return null;
473 case Keyword.LParen:
474 case Keyword.Add:
475 case Keyword.Sub:
476 case Keyword.True:
477 case Keyword.False:
478 case Keyword.All:
479 case Keyword.Noone:
480 case Keyword.Self:
481 case Keyword.Other:
482 case Keyword.Global:
483 goto estat;
484 default:
486 lex.error("unexpected keyword: `"~keywordtext(lex.front.kw)~"`");
487 return null;
489 // should be an expression
490 estat:
491 auto res = new NodeStatementExpr(parseAssExpr());
492 endOfStatement();
493 return res;
496 // whole input
497 NodeFunc parseFunctionBody (NodeFunc fn) {
498 fn.ebody = new NodeBlock(lex.loc);
499 while (!lex.empty) fn.ebody.addStatement(parseStatement());
500 return fn;
503 // whole input
504 NodeFunc parseFunctionBody (string name) {
505 auto fn = new NodeFunc(name, lex.loc);
506 fn.ebody = new NodeBlock(lex.loc);
507 while (!lex.empty) fn.ebody.addStatement(parseStatement());
508 return fn;
511 // from `function` keyword
512 NodeFunc parseFunction () {
513 auto loc = lex.loc;
514 lex.expect(Keyword.Function);
515 if (!lex.isId) lex.error("function name expected");
516 string name = lex.expectId;
517 auto fn = new NodeFunc(name, lex.loc);
518 fn.ebody = cast(NodeBlock)parseCodeBlock();
519 assert(fn.ebody !is null);
520 return fn;