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
;
21 import gmlparser
.lexer
;
22 import gmlparser
.tokens
;
27 bool warnings
= false;
29 Node curbreak
, curcont
; // current nodes for `break` and `continue`
31 this (Lexer alex
, bool astrict
=false) {
36 this(T
) (const(char)[] atext
, T afname
, bool astrict
=false) if (is(T
: const(char)[])) {
37 lex
= new Lexer(atext
, afname
);
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)");
50 mainloop
: while (lex
.isKw
) {
51 foreach (immutable idx
, auto _
; T
) {
52 static if (idx
%2 == 0) {
54 static if (T
[idx
] == Keyword
.Ass
) {
55 if (stopOnAss
) break mainloop
;
56 warning(lex
.loc
, "'=' instead of '=='");
60 auto r
= mixin(me
~"(stopOnAss)");
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]; }
81 mixin template BuildExprBinOp(string name
, string upfunc
, T
...) {
82 static private template BuildOps(T
...) {
83 static if (T
.length
== 0)
86 enum BuildOps
= "Keyword."~T
[0]~", NodeBinary"~T
[0]~", "~BuildOps
!(T
[1..$]);
89 "Node parseExpr"~name
~" (bool stopOnAss) {"~
90 " return parseExprBinOp!(\"parseExpr"~upfunc
~"\", \"parseExpr"~upfunc
~"\", "~BuildOps
!T
~")(stopOnAss);"~
94 // ////////////////////////////////////////////////////////////////////// //
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;
104 lex
.expect(Keyword
.RParen
);
108 Node
parseExprPrimary () {
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
);
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
);
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
); }
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");
147 Node
parseIndexing (Node n
) {
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
);
159 Node
parseExprPostfix (Node n
) {
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
;
172 Node
parseExprUnary (bool stopOnAss
=false) {
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");
196 return parseExprLogOr(false);
199 // this can be assign expression, check it
200 Node
parseAssExpr () {
201 auto e
= parseExprLogOr(true); // stop on assign
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
);
216 // ////////////////////////////////////////////////////////////////////// //
217 void warning(A
...) (Loc loc
, A args
) {
219 import std
.stdio
: stderr
;
220 stderr
.writeln("WARNING at ", loc
, ": ", args
);
224 void endOfStatement () {
225 if (!lex
.eatKw(Keyword
.Semi
)) {
227 lex
.expect(Keyword
.Semi
);
229 warning(lex
.peloc
, "';' missing");
234 Node
exprInParens () {
236 lex
.expect(Keyword
.LParen
);
237 auto ec
= parseExpr();
238 lex
.expect(Keyword
.RParen
);
241 if (!lex
.isKw(Keyword
.LParen
)) warning(lex
.loc
, "'(' missing");
246 // higher-level parsers
247 // can create new block
248 Node
parseCodeBlock () {
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
);
258 Node
parseReturn () {
260 lex
.expect(Keyword
.Return
);
261 auto res
= new NodeReturn(parseExpr(), loc
);
268 lex
.expect(Keyword
.Exit
);
269 auto res
= new NodeReturn(null, loc
);
276 lex
.expect(Keyword
.If
);
277 auto ec
= exprInParens();
278 auto et
= parseStatement();
279 if (!strict
&& lex
.isKw(Keyword
.Semi
)) {
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
);
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();
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();
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();
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();
346 if (lex
.eatKw(Keyword
.Globalvar
)) {
349 lex
.expect(Keyword
.Var
);
351 if (!lex
.isId
) lex
.error("identifier expected");
352 auto vd
= new NodeVarDecl(loc
);
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;
365 if (curbreak
is null) {
366 if (strict
) lex
.error("`break` without loop/switch");
367 warning(lex
.loc
, "`break` without loop/switch");
370 lex
.expect(Keyword
.Break
);
371 auto res
= new NodeStatementBreak(loc
, curbreak
);
377 if (curcont
is null) {
378 if (strict
) lex
.error("`continue` without loop/switch");
379 warning(lex
.loc
, "`continue` without loop/switch");
382 lex
.expect(Keyword
.Continue
);
383 auto res
= new NodeStatementContinue(loc
, curcont
);
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
);
397 forn
.einit
= parseAssExpr();
398 lex
.expect(Keyword
.Semi
);
400 forn
.econd
= parseExpr();
401 lex
.expect(Keyword
.Semi
);
403 forn
.enext
= parseAssExpr();
404 lex
.expect(Keyword
.RParen
);
405 forn
.ebody
= parseStatement();
409 Node
parseSwitch () {
410 auto sw
= new NodeSwitch(lex
.loc
);
411 auto oldbreak
= curbreak
;
412 scope(exit
) { curbreak
= oldbreak
; }
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
) {
420 if (lex
.eatKw(Keyword
.Default
)) {
422 } else if (lex
.eatKw(Keyword
.Case
)) {
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
);
436 sw
.appendCase(e
, null);
439 lex
.expect(Keyword
.RCurly
);
443 // can create new block
444 Node
parseStatement () {
448 if (lex
.eatKw(Keyword
.Semi
)) {
449 warning(loc
, "use '{}' instead of ';' for empty statement");
450 return new NodeStatementEmpty(loc
);
453 if (lex
.isKw(Keyword
.LCurly
)) return parseCodeBlock();
454 // operators and other keyworded things
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;
486 lex
.error("unexpected keyword: `"~keywordtext(lex
.front
.kw
)~"`");
489 // should be an expression
491 auto res
= new NodeStatementExpr(parseAssExpr());
497 NodeFunc
parseFunctionBody (NodeFunc fn
) {
498 fn
.ebody
= new NodeBlock(lex
.loc
);
499 while (!lex
.empty
) fn
.ebody
.addStatement(parseStatement());
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());
511 // from `function` keyword
512 NodeFunc
parseFunction () {
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);