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
.ast
is aliced
;
20 import gmlparser
.lexer
;
21 import gmlparser
.tokens
;
22 import gmlparser
.utils
;
25 // ////////////////////////////////////////////////////////////////////////// //
26 auto selectNode(RetType
=void, A
...) (Node node
, scope A args
) => selector
!RetType(node
, args
);
29 // ////////////////////////////////////////////////////////////////////////// //
30 private string
killEB (string r
) {
31 if (r
.length
< 2 || r
[0] != '(' || r
[$-1] != ')') return r
;
34 while (pos
< r
.length
-1) {
35 if (r
.ptr
[pos
] == '(') {
37 } else if (r
.ptr
[pos
] == ')') {
38 if (--depth
== 0) return r
;
46 // ////////////////////////////////////////////////////////////////////////// //
48 enum DontVisitMe
; // mark nodes that should not be visited with this
49 enum CompoundVisitor
; // mark struct/class fields that should be processed by visitor with this
50 enum SkipChildren
= int.min
;
53 bool textual
; // used for unary and binary nodes where, for example, "and" is used instead of "&&"
55 this (Node n
=null) { if (n
!is null) loc
= n
.loc
; }
56 this (Loc aloc
) { loc
= aloc
; }
58 override string
toString () const { return "<invalid node:"~typeof(this).stringof
~">"; }
62 // ////////////////////////////////////////////////////////////////////////// //
63 class NodeLiteral
: Node
{
64 this (Node n
=null) { super(n
); }
65 this (Loc aloc
) { super(aloc
); }
68 class NodeLiteralString
: NodeLiteral
{
71 //this (Node n, string aval) { val = aval; super(n); }
72 this (Loc aloc
, string aval
) { val
= aval
; super(aloc
); }
74 override string
toString () const {
75 import std
.array
: appender
;
76 import std
.format
: formatElement
, FormatSpec
;
77 auto res
= appender
!string();
78 FormatSpec
!char fspc
; // defaults to 's'
79 formatElement(res
, val
, fspc
);
84 class NodeLiteralNum
: NodeLiteral
{
87 //this (float aval, Node n=null) { val = aval; super(n); }
88 this (Loc aloc
, float aval
) { val
= aval
; super(aloc
); }
90 override string
toString () const { import std
.string
: format
; return "%s".format(val
); }
94 // ////////////////////////////////////////////////////////////////////////// //
95 class NodeExpr
: Node
{
98 this (string aname
) { name
= aname
; super(); }
99 this (Node ae
, string aname
) { name
= aname
; super(ae
); }
100 this (Loc aloc
, string aname
) { name
= aname
; super(aloc
); }
101 this (Node ae
, Loc aloc
, string aname
) { name
= aname
; super(aloc
); }
105 // ////////////////////////////////////////////////////////////////////////// //
106 class NodeUnary
: NodeExpr
{
109 this (Node ae
, string aname
) { e
= ae
; super(ae
, aname
); }
110 this (Node ae
, Loc aloc
, string aname
) { e
= ae
; super(ae
, aloc
, aname
); }
112 override string
toString () const { return name
~e
.toString
; }
115 private enum UnaryOpMixin(string name
, string opstr
) =
116 "class NodeUnary"~name
~" : NodeUnary {\n"~
117 " this (Node ae) { super(ae, \""~opstr
~"\"); }\n"~
118 " this (Node ae, Loc aloc) { super(ae, loc, \""~opstr
~"\"); }\n"~
121 mixin(UnaryOpMixin
!("Not", "!"));
122 mixin(UnaryOpMixin
!("Neg", "-"));
123 mixin(UnaryOpMixin
!("BitNeg", "~"));
126 // ////////////////////////////////////////////////////////////////////////// //
127 class NodeBinary
: NodeExpr
{
130 this (Node ael
, Node aer
, string aname
) { el
= ael
; er
= aer
; super(ael
, aname
); }
131 this (Node ael
, Node aer
, Loc aloc
, string aname
) { el
= ael
; er
= aer
; super(ael
, aloc
, aname
); }
133 override string
toString () const {
134 if (name
== "=") return el
.toString
~" = "~killEB(er
.toString
);
148 return "("~el
.toString
~spc
~name
~spc
~er
.toString
~")";
152 private enum BinaryOpMixin(string name
, string opstr
) =
153 "class NodeBinary"~name
~" : NodeBinary {\n"~
154 " this (Node ael, Node aer) { super(ael, aer, \""~opstr
~"\"); }\n"~
155 " this (Node ael, Node aer, Loc aloc) { super(ael, aer, aloc, \""~opstr
~"\"); }\n"~
158 mixin(BinaryOpMixin
!("Add", "+"));
159 mixin(BinaryOpMixin
!("Sub", "-"));
160 mixin(BinaryOpMixin
!("Mul", "*"));
161 mixin(BinaryOpMixin
!("RDiv", "/"));
162 mixin(BinaryOpMixin
!("Div", "div"));
163 mixin(BinaryOpMixin
!("Mod", "mod"));
164 mixin(BinaryOpMixin
!("BitOr", "|"));
165 mixin(BinaryOpMixin
!("BitAnd", "&"));
166 mixin(BinaryOpMixin
!("BitXor", "^"));
167 mixin(BinaryOpMixin
!("LShift", "<<"));
168 mixin(BinaryOpMixin
!("RShift", ">>"));
170 mixin(BinaryOpMixin
!("Less", "<"));
171 mixin(BinaryOpMixin
!("Great", ">"));
172 mixin(BinaryOpMixin
!("LessEqu", "<="));
173 mixin(BinaryOpMixin
!("GreatEqu", ">="));
174 mixin(BinaryOpMixin
!("Equ", "=="));
175 mixin(BinaryOpMixin
!("NotEqu", "!="));
177 mixin(BinaryOpMixin
!("LogOr", "||"));
178 mixin(BinaryOpMixin
!("LogAnd", "&&"));
179 mixin(BinaryOpMixin
!("LogXor", "^^"));
181 mixin(BinaryOpMixin
!("Ass", "=")); // assign ;-)
183 // these nodes will never end up in AST, it's for parser
184 mixin(BinaryOpMixin
!("And", "and"));
185 mixin(BinaryOpMixin
!("Or", "or"));
186 mixin(BinaryOpMixin
!("Xor", "xor"));
189 // ////////////////////////////////////////////////////////////////////////// //
190 // variable access (as lvalue and as rvalue)
191 class NodeId
: NodeExpr
{
192 //string name; // for semantic
194 this (string aname
) { super(aname
); }
195 this (Loc aloc
, string aname
) { super(aloc
, aname
); }
197 override string
toString () const { return name
; }
201 // ////////////////////////////////////////////////////////////////////////// //
202 // field access (as lvalue and as rvalue)
203 class NodeDot
: NodeExpr
{
206 this (Node ae
, string name
) { e
= ae
; super(ae
, name
); }
207 this (Node ae
, Loc aloc
, string name
) { e
= ae
; super(ae
, aloc
, name
); }
209 override string
toString () const {
210 if (cast(NodeId
)e
) return e
.toString
~"."~name
;
211 return "("~e
.toString
~")."~name
;
216 // ////////////////////////////////////////////////////////////////////////// //
217 // field access (as lvalue and as rvalue)
218 class NodeIndex
: NodeExpr
{
220 Node ei0
, ei1
; // indicies, `ei1` can be `null`
222 this (Node ae
) { e
= ae
; super(ae
, "index"); }
223 this (Node ae
, Loc aloc
) { e
= ae
; super(ae
, aloc
, "index"); }
225 override string
toString () const {
227 if (cast(NodeId
)e
) res
= e
.toString
; else res
= "("~e
.toString
~")";
228 res
~= "["~ei0
.toString
;
229 if (ei1
!is null) res
~= ", "~ei1
.toString
;
235 // ////////////////////////////////////////////////////////////////////////// //
237 class NodeFCall
: NodeExpr
{
238 Node fe
; // function expression
241 //this (Loc aloc, string aname) { fe = new NodeId(aname, aloc); super(aloc); }
242 this (Loc aloc
, Node afe
) { fe
= afe
; super(aloc
, "fcall"); }
244 override string
toString () const {
245 string res
= fe
.toString
~"(";
246 foreach (immutable idx
, const Node a
; args
) {
247 if (idx
!= 0) res
~= ", ";
248 res
~= killEB(a
.toString
);
255 // ////////////////////////////////////////////////////////////////////////// //
256 class NodeStatement
: Node
{
258 this (Node ae
) { super(ae
); }
259 this (Loc aloc
) { super(aloc
); }
263 // ////////////////////////////////////////////////////////////////////////// //
265 class NodeVarDecl
: NodeStatement
{
269 this (Loc aloc
) { super(aloc
); }
271 bool hasVar (const(char)[] name
) {
272 foreach (string n
; names
) if (n
== name
) return true;
276 override string
toString () const {
277 string res
= (asGlobal ?
"globalvar " : "var ");
278 foreach (immutable idx
, string n
; names
) {
279 if (idx
!= 0) res
~= ", ";
287 // ////////////////////////////////////////////////////////////////////////// //
289 class NodeBlock
: NodeStatement
{
293 this (Loc aloc
) { loc
= aloc
; }
295 void addStatement (Node n
) {
296 if (n
is null) return;
300 override string
toString () const {
302 foreach (const n
; stats
) res
~= "\n"~killEB(n
.toString
);
309 // ////////////////////////////////////////////////////////////////////////// //
310 class NodeStatementEmpty
: NodeStatement
{
312 this (Loc aloc
) { loc
= aloc
; }
314 override string
toString () const { return "{}"; }
318 // ////////////////////////////////////////////////////////////////////////// //
319 // `expression` operator
320 class NodeStatementExpr
: NodeStatement
{
321 Node e
; // expression
323 this (Node ae
) { e
= ae
; super(ae
); if (ae
!is null) loc
= ae
.loc
; }
324 this (Node ae
, Loc aloc
) { e
= ae
; loc
= aloc
; }
326 override string
toString () const {
327 return killEB(e
.toString
)~";";
332 // ////////////////////////////////////////////////////////////////////////// //
333 // `return` and `exit` operators
334 class NodeReturn
: NodeStatement
{
335 Node e
; // return expression (if any); can be `null`
337 this (Node ae
) { e
= ae
; if (ae
!is null) loc
= ae
.loc
; }
338 this (Node ae
, Loc aloc
) { e
= ae
; loc
= aloc
; }
340 override string
toString () const { return (e
!is null ?
"return "~e
.toString
~";" : "exit;"); }
344 // ////////////////////////////////////////////////////////////////////////// //
346 class NodeWith
: NodeStatement
{
350 this (Node ae
) { e
= ae
; if (ae
!is null) loc
= ae
.loc
; }
351 this (Node ae
, Loc aloc
) { e
= ae
; loc
= aloc
; }
353 override string
toString () const { return "with ("~killEB(e
.toString
)~") "~ebody
.toString
; }
357 // ////////////////////////////////////////////////////////////////////////// //
358 // `with_object` operator
359 class NodeWithObject
: NodeStatement
{
363 this (Node ae
) { e
= ae
; if (ae
!is null) loc
= ae
.loc
; }
364 this (Node ae
, Loc aloc
) { e
= ae
; loc
= aloc
; }
366 override string
toString () const { return "with_object ("~killEB(e
.toString
)~") "~ebody
.toString
; }
370 // ////////////////////////////////////////////////////////////////////////// //
372 class NodeIf
: NodeStatement
{
375 this (Node aec
, Node aet
, Node aef
) { ec
= aec
; et
= aet
; ef
= aef
; super(aec
); }
376 this (Node aec
, Node aet
, Node aef
, Loc aloc
) { ec
= aec
; et
= aet
; ef
= aef
; loc
= aloc
; }
378 override string
toString () const {
379 return "if ("~killEB(ec
.toString
)~") "~et
.toString
~(ef
!is null && !cast(NodeStatementEmpty
)ef ?
" else "~ef
.toString
: "");
384 // ////////////////////////////////////////////////////////////////////////// //
385 // `break` and `continue` operators
386 class NodeStatementBreakCont
: NodeStatement
{
387 Node ewhich
; // loop/switch node
389 this (Loc aloc
, Node awhich
) { ewhich
= awhich
; super(aloc
); }
393 class NodeStatementBreak
: NodeStatementBreakCont
{
394 this (Loc aloc
, Node awhich
) { super(aloc
, awhich
); }
396 override string
toString () const { return "break;"; }
399 // `continue` operator
400 class NodeStatementContinue
: NodeStatementBreakCont
{
401 this (Loc aloc
, Node awhich
) { super(aloc
, awhich
); }
403 override string
toString () const { return "continue;"; }
407 // ////////////////////////////////////////////////////////////////////////// //
409 class NodeFor
: NodeStatement
{
410 Node einit
, econd
, enext
;
414 this (Loc aloc
) { loc
= aloc
; }
416 override string
toString () const {
417 if (cast(NodeBlock
)ebody
) {
418 return "for ("~killEB(einit
.toString
)~"; "~killEB(econd
.toString
)~"; "~killEB(enext
.toString
)~") "~killEB(ebody
.toString
);
420 return "for ("~killEB(einit
.toString
)~"; "~killEB(econd
.toString
)~"; "~killEB(enext
.toString
)~") {\n"~killEB(ebody
.toString
)~"\n}";
426 // ////////////////////////////////////////////////////////////////////////// //
428 class NodeWhile
: NodeStatement
{
433 this (Loc aloc
) { loc
= aloc
; }
435 override string
toString () const {
436 if (cast(NodeBlock
)ebody
) {
437 return "while ("~killEB(econd
.toString
)~" "~killEB(ebody
.toString
);
439 return "while ("~killEB(econd
.toString
)~" {\n"~ebody
.toString
~"\n}";
445 // ////////////////////////////////////////////////////////////////////////// //
446 // `do/until` operator
447 class NodeDoUntil
: NodeStatement
{
452 this (Loc aloc
) { loc
= aloc
; }
454 override string
toString () const {
455 if (cast(NodeBlock
)ebody
) {
456 return "do "~ebody
.toString
~"\n while ("~killEB(econd
.toString
)~");";
458 return "do {\n"~killEB(ebody
.toString
)~"\n} while ("~killEB(econd
.toString
)~");";
464 // ////////////////////////////////////////////////////////////////////////// //
466 class NodeRepeat
: NodeStatement
{
471 this (Loc aloc
) { loc
= aloc
; }
473 override string
toString () const {
474 if (cast(NodeBlock
)ebody
) {
475 return "repeat("~killEB(ecount
.toString
)~") "~ebody
.toString
;
477 return "repeat("~killEB(ecount
.toString
)~") {\n"~killEB(ebody
.toString
)~"\n}";
483 // ////////////////////////////////////////////////////////////////////////// //
485 class NodeSwitch
: NodeStatement
{
487 Node e
; // condition; `null` means "default"
488 Node st
; // can be `null`
490 Node e
; // switch expression
491 Case
[] cases
; // never mutate directly!
494 this (Loc aloc
) { loc
= aloc
; }
496 void appendCase (Node ae
, Node ast
) {
498 foreach (ref cc
; cases
) {
499 if (cc
.e
is null) throw new ErrorAt(loc
, "duplicate `default`");
502 cases
~= Case(ae
, ast
);
505 override string
toString () const {
506 string res
= "switch ("~killEB(e
.toString
)~") {";
507 foreach (ref c
; cases
) {
509 res
~= "\ncase "~killEB(c
.e
.toString
)~":";
513 res
~= (c
.st
!is null ?
" "~killEB(c
.st
.toString
) : "");
520 // ////////////////////////////////////////////////////////////////////////// //
521 // function with body
522 class NodeFunc
: Node
{
526 this (string aname
, Loc aloc
) { name
= aname
; loc
= aloc
; }
528 override string
toString () const {
529 string res
= "function "~name
~" ";
530 if (cast(NodeBlock
)ebody
) {
531 res
~= ebody
.toString
;
533 res
~= "{\n"~killEB(ebody
.toString
)~"\n}";