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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module gaem
.parser
.ast
is aliced
;
19 import gaem
.parser
.lexer
;
20 import gaem
.parser
.tokens
;
21 import gaem
.parser
.utils
;
24 // ////////////////////////////////////////////////////////////////////////// //
25 auto selectNode(RetType
=void, A
...) (Node node
, scope A args
) => selector
!RetType(node
, args
);
28 // ////////////////////////////////////////////////////////////////////////// //
30 protected import std
.string
: stripLeft
, stripRight
;
33 bool textual
; // used for unary and binary nodes where, for example, "and" is used instead of "&&"
34 uint pcs
, pce
; // for codegen: first and last compiled instruction; pce points after last instr
37 this (Node n
) { if (n
!is null) loc
= n
.loc
; }
38 this (Loc aloc
) { loc
= aloc
; }
40 string
toStringInd (int indent
) const => indentStr(indent
)~"<invalid node:"~typeof(this).stringof
~">";
42 override string
toString () const => toStringInd(0);
44 // add outer "()" if there is none
45 string
asCondStr () const => (cast(NodeUnaryParens
)this ?
this.toString
: "("~this.toString
~")");
47 static string
indentStr (int indent
) {
48 if (indent
< 1) return null;
49 auto s
= new char[](indent
);
51 return cast(string
)s
; // it is safe here
56 // ////////////////////////////////////////////////////////////////////////// //
57 abstract class NodeLiteral
: Node
{
59 this (Loc aloc
) { super(aloc
); }
62 class NodeLiteralStr
: NodeLiteral
{
66 this (Loc aloc
, string aval
) { val
= aval
; super(aloc
); }
68 override string
toStringInd (int indent
) const {
69 import std
.array
: appender
;
70 import std
.format
: formatElement
, FormatSpec
;
71 auto res
= appender
!string();
72 res
.put(indentStr(indent
));
73 FormatSpec
!char fspc
; // defaults to 's'
74 formatElement(res
, val
, fspc
);
79 class NodeLiteralNum
: NodeLiteral
{
83 this (Loc aloc
, float aval
) { val
= aval
; super(aloc
); }
85 override string
toStringInd (int indent
) const { import std
.string
: format
; return "%s%s".format(indentStr(indent
), val
); }
89 // ////////////////////////////////////////////////////////////////////////// //
90 abstract class NodeExpr
: Node
{
91 string name
; // various purposes
94 this (string aname
) { name
= aname
; super(); }
95 this (Node ae
, string aname
) { name
= aname
; super(ae
); }
96 this (Loc aloc
, string aname
) { name
= aname
; super(aloc
); }
100 // ////////////////////////////////////////////////////////////////////////// //
101 abstract class NodeUnary
: NodeExpr
{
105 this (Node ae
, string aname
) { e
= ae
; super(ae
, aname
); }
106 this (Loc aloc
, Node ae
, string aname
) { e
= ae
; super(aloc
, aname
); }
108 override string
toStringInd (int indent
) const => indentStr(indent
)~name
~e
.toString
;
112 // ////////////////////////////////////////////////////////////////////////// //
114 class NodeUnaryParens
: NodeUnary
{
116 this (Node ae
) { super(ae
, "()"); }
117 this (Loc aloc
, Node ae
) { super(aloc
, ae
, "()"); }
119 override string
toStringInd (int indent
) const => indentStr(indent
)~"("~e
.toString
~")";
123 // ////////////////////////////////////////////////////////////////////////// //
124 private enum UnaryOpMixin(string name
, string opstr
) =
125 "class NodeUnary"~name
~" : NodeUnary {\n"~
127 " this (Node ae) { super(ae, \""~opstr
~"\"); }\n"~
128 " this (Loc aloc, Node ae) { super(loc, ae, \""~opstr
~"\"); }\n"~
131 mixin(UnaryOpMixin
!("Not", "!"));
132 mixin(UnaryOpMixin
!("Neg", "-"));
133 mixin(UnaryOpMixin
!("BitNeg", "~"));
136 // ////////////////////////////////////////////////////////////////////////// //
137 abstract class NodeBinary
: NodeExpr
{
141 this (Node ael
, Node aer
, string aname
) { el
= ael
; er
= aer
; super((ael
!is null ? ael
: aer
), aname
); }
142 this (Loc aloc
, Node ael
, Node aer
, string aname
) { el
= ael
; er
= aer
; super(aloc
, aname
); }
144 override string
toStringInd (int indent
) const {
145 if (name
.length
> 1 && name
!= "<<" && name
!= ">>") return indentStr(indent
)~el
.toString
~" "~name
~" "~er
.toString
;
146 // to correctly emit "a- -1"
148 if (auto l
= cast(NodeLiteralNum
)er
) return indentStr(indent
)~el
.toString
~(l
.val
< 0 ?
" " : "")~name
~(l
.val
< 0 ?
" " : "")~er
.toString
;
150 return indentStr(indent
)~el
.toString
~name
~er
.toString
;
154 abstract class NodeBinaryCmp
: NodeBinary
{
156 this (Node ael
, Node aer
, string aname
) { super(ael
, aer
, aname
); }
157 this (Loc aloc
, Node ael
, Node aer
, string aname
) { super(aloc
, ael
, aer
, aname
); }
159 override string
toStringInd (int indent
) const => indentStr(indent
)~el
.toString
~" "~name
~" "~er
.toString
;
162 abstract class NodeBinaryLogic
: NodeBinary
{
164 this (Node ael
, Node aer
, string aname
) { super(ael
, aer
, aname
); }
165 this (Loc aloc
, Node ael
, Node aer
, string aname
) { super(aloc
, ael
, aer
, aname
); }
167 override string
toStringInd (int indent
) const => indentStr(indent
)~el
.toString
~" "~name
~" "~er
.toString
;
170 private enum BinaryOpMixin(string name
, string opstr
, string base
="") =
171 "class NodeBinary"~name
~" : NodeBinary"~base
~" {\n"~
173 " this (Node ael, Node aer) { super(ael, aer, \""~opstr
~"\"); }\n"~
174 " this (Loc aloc, Node ael, Node aer) { super(aloc, ael, aer, \""~opstr
~"\"); }\n"~
177 mixin(BinaryOpMixin
!("Add", "+"));
178 mixin(BinaryOpMixin
!("Sub", "-"));
179 mixin(BinaryOpMixin
!("Mul", "*"));
180 mixin(BinaryOpMixin
!("RDiv", "/"));
181 mixin(BinaryOpMixin
!("Div", "div"));
182 mixin(BinaryOpMixin
!("Mod", "mod"));
183 mixin(BinaryOpMixin
!("BitOr", "|"));
184 mixin(BinaryOpMixin
!("BitAnd", "&"));
185 mixin(BinaryOpMixin
!("BitXor", "^"));
186 mixin(BinaryOpMixin
!("LShift", "<<"));
187 mixin(BinaryOpMixin
!("RShift", ">>"));
189 mixin(BinaryOpMixin
!("Less", "<", "Cmp"));
190 mixin(BinaryOpMixin
!("Great", ">", "Cmp"));
191 mixin(BinaryOpMixin
!("LessEqu", "<=", "Cmp"));
192 mixin(BinaryOpMixin
!("GreatEqu", ">=", "Cmp"));
193 mixin(BinaryOpMixin
!("Equ", "==", "Cmp"));
194 mixin(BinaryOpMixin
!("NotEqu", "!=", "Cmp"));
196 mixin(BinaryOpMixin
!("LogOr", "||", "Logic"));
197 mixin(BinaryOpMixin
!("LogAnd", "&&", "Logic"));
198 mixin(BinaryOpMixin
!("LogXor", "^^", "Logic"));
200 // these nodes will never end up in AST, they are here for parser needs
201 mixin(BinaryOpMixin
!("And", "and", "Logic"));
202 mixin(BinaryOpMixin
!("Or", "or", "Logic"));
203 mixin(BinaryOpMixin
!("Xor", "xor", "Logic"));
206 // ////////////////////////////////////////////////////////////////////////// //
207 // variable access (as lvalue and as rvalue)
208 class NodeId
: NodeExpr
{
210 this (string aname
) { super(aname
); }
211 this (Loc aloc
, string aname
) { super(aloc
, aname
); }
213 override string
toStringInd (int indent
) const => indentStr(indent
)~name
;
217 // ////////////////////////////////////////////////////////////////////////// //
218 // field access (as lvalue and as rvalue)
219 class NodeDot
: NodeExpr
{
223 this (Node ae
, string name
) { e
= ae
; super(ae
, name
); }
224 this (Loc aloc
, Node ae
, string name
) { e
= ae
; super(aloc
, name
); }
226 override string
toStringInd (int indent
) const => indentStr(indent
)~e
.toString
~"."~name
;
230 // ////////////////////////////////////////////////////////////////////////// //
231 // field access (as lvalue and as rvalue)
232 class NodeIndex
: NodeExpr
{
234 Node ei0
, ei1
; // indicies, `ei1` can be `null`
237 this (Node ae
) { e
= ae
; super(ae
, "index"); }
238 this (Loc aloc
, Node ae
) { e
= ae
; super(aloc
, "index"); }
240 override string
toStringInd (int indent
) const {
241 string res
= indentStr(indent
)~e
.toString
~"["~ei0
.toString
;
242 if (ei1
!is null) res
~= ", "~ei1
.toString
;
248 // ////////////////////////////////////////////////////////////////////////// //
250 class NodeFCall
: NodeExpr
{
251 Node fe
; // function expression
255 this (Loc aloc
, Node afe
) { fe
= afe
; super(aloc
, "fcall"); }
257 override string
toStringInd (int indent
) const {
258 string res
= indentStr(indent
)~fe
.toString
~"(";
259 foreach (immutable idx
, const Node a
; args
) {
260 if (idx
!= 0) res
~= ", ";
268 // ////////////////////////////////////////////////////////////////////////// //
269 abstract class NodeStatement
: Node
{
271 this (Node ae
) { super(ae
); }
272 this (Loc aloc
) { super(aloc
); }
276 // ////////////////////////////////////////////////////////////////////////// //
278 class NodeVarDecl
: NodeStatement
{
280 Loc
[] locs
; // for names
284 this (Loc aloc
) { super(aloc
); }
286 bool hasVar (const(char)[] name
) {
287 foreach (string n
; names
) if (n
== name
) return true;
291 override string
toStringInd (int indent
) const {
292 string res
= indentStr(indent
)~(asGlobal ?
"globalvar " : "var ");
293 foreach (immutable idx
, string n
; names
) {
294 if (idx
!= 0) res
~= ", ";
302 // ////////////////////////////////////////////////////////////////////////// //
304 class NodeBlock
: NodeStatement
{
308 this (Loc aloc
) { loc
= aloc
; }
310 void addStatement (Node n
) {
311 if (n
is null) return;
315 override string
toStringInd (int indent
) const {
317 foreach (const n
; stats
) res
~= "\n"~n
.toStringInd(indent
+2);
318 res
~= "\n"~indentStr(indent
)~"}";
324 // ////////////////////////////////////////////////////////////////////////// //
325 class NodeStatementEmpty
: NodeStatement
{
327 this (Loc aloc
) { loc
= aloc
; }
329 override string
toStringInd (int indent
) const => indentStr(indent
)~"{}";
333 // ////////////////////////////////////////////////////////////////////////// //
334 class NodeStatementAss
: NodeStatement
{
336 bool expanded
; // this is expanded opOpAssign
339 this (Loc aloc
, Node ael
, Node aer
, bool aexpanded
=false) { super(aloc
); el
= ael
; er
= aer
; expanded
= aexpanded
; }
341 override string
toStringInd (int indent
) const {
342 string res
= indentStr(indent
)~el
.toString
~" ";
344 if (auto x
= cast(NodeBinary
)er
) {
345 res
~= x
.name
~"= "~x
.er
.toString
;
350 res
~= "= "~er
.toString
;
357 // ////////////////////////////////////////////////////////////////////////// //
358 // `expression` operator
359 class NodeStatementExpr
: NodeStatement
{
360 Node e
; // expression
363 this (Node ae
) { e
= ae
; super(ae
); }
364 this (Loc aloc
, Node ae
) { e
= ae
; super(aloc
); }
366 override string
toStringInd (int indent
) const => e
.toStringInd(indent
)~";";
370 // ////////////////////////////////////////////////////////////////////////// //
371 // `return` and `exit` operators
372 class NodeReturn
: NodeStatement
{
373 Node e
; // return expression (if any); can be `null`
376 this (Node ae
) { e
= ae
; super(ae
); }
377 this (Loc aloc
, Node ae
=null) { e
= ae
; super(aloc
); }
379 override string
toStringInd (int indent
) const => indentStr(indent
)~(e
!is null ?
"return "~e
.toString
~";" : "exit;");
383 // ////////////////////////////////////////////////////////////////////////// //
385 class NodeWith
: NodeStatement
{
390 this (Node ae
) { e
= ae
; super(ae
); }
391 this (Loc aloc
, Node ae
) { e
= ae
; super(aloc
); }
393 override string
toStringInd (int indent
) const => indentStr(indent
)~"with "~e
.asCondStr
~" "~ebody
.toStringInd(indent
).stripLeft
;
397 // ////////////////////////////////////////////////////////////////////////// //
399 class NodeIf
: NodeStatement
{
401 NodeStatement et
, ef
;
404 this (Node aec
, NodeStatement aet
, NodeStatement aef
) { ec
= aec
; et
= aet
; ef
= aef
; super((aec
!is null ? aec
: aet
! is null ? aet
: aef
)); }
405 this (Loc aloc
, Node aec
, NodeStatement aet
, NodeStatement aef
) { ec
= aec
; et
= aet
; ef
= aef
; super(aloc
); }
407 override string
toStringInd (int indent
) const {
408 //{ import std.stdio : stderr; stderr.writeln(loc); }
410 string res
= indentStr(indent
)~"if "~ec
.asCondStr
~" "~et
.toStringInd(indent
).stripLeft
;
411 if (ef
is null) return res
;
412 if (cast(NodeBlock
)et ||
cast(NodeBlock
)ef
) return res
~" else "~ef
.toStringInd(indent
).stripLeft
;
413 if (cast(NodeIf
)et
) return res
~" else "~ef
.toStringInd(indent
).stripLeft
;
414 if (cast(NodeIf
)ef
) return res
~"\n"~indentStr(indent
)~"else "~ef
.toStringInd(indent
).stripLeft
;
415 string st
= et
.toString
;
416 string sf
= ef
.toString
;
417 if (st
.length
+sf
.length
<= 68) return res
~" else "~sf
;
418 return res
~"\n"~indentStr(indent
)~"else "~ef
.toStringInd(indent
).stripLeft
;
423 // ////////////////////////////////////////////////////////////////////////// //
424 // `break` and `continue` operators
425 abstract class NodeStatementBreakCont
: NodeStatement
{
426 Node ewhich
; // loop/switch node
429 this (Loc aloc
, Node awhich
) { ewhich
= awhich
; super(aloc
); }
433 class NodeStatementBreak
: NodeStatementBreakCont
{
434 this (Loc aloc
, Node awhich
) { super(aloc
, awhich
); }
436 override string
toStringInd (int indent
) const => indentStr(indent
)~"break;";
439 // `continue` operator
440 class NodeStatementContinue
: NodeStatementBreakCont
{
441 this (Loc aloc
, Node awhich
) { super(aloc
, awhich
); }
443 override string
toStringInd (int indent
) const => indentStr(indent
)~"continue;";
447 // ////////////////////////////////////////////////////////////////////////// //
449 class NodeFor
: NodeStatement
{
450 Node einit
, econd
, enext
;
454 this (Loc aloc
) { super(aloc
); }
456 override string
toStringInd (int indent
) const => indentStr(indent
)~"for ("~einit
.toString
~"; "~econd
.toString
~"; "~enext
.toString
~") "~ebody
.toStringInd(indent
).stripLeft
;
460 // ////////////////////////////////////////////////////////////////////////// //
462 class NodeWhile
: NodeStatement
{
467 this (Loc aloc
) { super(aloc
); }
469 override string
toStringInd (int indent
) const => indentStr(indent
)~"while "~econd
.asCondStr
~" "~ebody
.toStringInd(indent
).stripLeft
;
473 // ////////////////////////////////////////////////////////////////////////// //
474 // `do/until` operator
475 class NodeDoUntil
: NodeStatement
{
480 this (Loc aloc
) { super(aloc
); }
482 override string
toStringInd (int indent
) const => indentStr(indent
)~"do "~ebody
.toStringInd(indent
).stripLeft
~" until "~econd
.asCondStr
~";";
486 // ////////////////////////////////////////////////////////////////////////// //
488 class NodeRepeat
: NodeStatement
{
493 this (Loc aloc
) { super(aloc
); }
495 override string
toStringInd (int indent
) const => indentStr(indent
)~"repeat "~ecount
.asCondStr
~" "~ebody
.toStringInd(indent
).stripLeft
;
499 // ////////////////////////////////////////////////////////////////////////// //
501 class NodeSwitch
: NodeStatement
{
504 Node e
; // condition; `null` means "default"
505 NodeBlock st
; // can be `null`
507 Node e
; // switch expression
508 Case
[] cases
; // never mutate directly!
511 this (Loc aloc
) { super(aloc
); }
513 void appendCase (Loc aloc
, Node ae
, NodeBlock ast
) {
514 cases
~= Case(aloc
, ae
, ast
);
517 override string
toStringInd (int indent
) const {
518 string res
= indentStr(indent
)~"switch "~e
.asCondStr
~" {";
520 foreach (immutable idx
, ref c
; cases
) {
522 res
~= "\n"~indentStr(indent
)~"case "~c
.e
.toString
~":";
524 res
~= "\n"~indentStr(indent
)~"default:";
526 if (c
.st
!is null && c
.st
.stats
.length
> 0) {
528 if (auto blk = cast(NodeBlock)c.st) {
529 import std.string : stripRight;
530 res ~= " "~c.st.toStringInd(indent).stripLeft.stripRight[1..$-1].stripLeft.stripRight;
532 res ~= " "~c.st.toStringInd(indent).stripLeft;
535 if (c
.st
.stats
.length
== 1) {
536 res
~= " "~c
.st
.toStringInd(indent
).stripLeft
.stripRight
[1..$-1].stripLeft
.stripRight
;
537 } else if (c
.st
.stats
.length
== 2 && (idx
== 0 || cases
[idx
-1].st
!is null)) {
538 string stx
= c
.st
.stats
[0].toString
~" "~c
.st
.stats
[1].toString
;
539 if (stx
.length
<= 69) {
542 res
~= "\n"~indentStr(indent
+2)~c
.st
.toStringInd(indent
).stripLeft
.stripRight
[1..$-1].stripLeft
.stripRight
;
545 res
~= "\n"~indentStr(indent
+2)~c
.st
.toStringInd(indent
).stripLeft
.stripRight
[1..$-1].stripLeft
.stripRight
;
550 return res
~"\n"~indentStr(indent
)~"}";
555 // ////////////////////////////////////////////////////////////////////////// //
556 // function with body
557 class NodeFunc
: Node
{
558 import gaem
.parser
.parser
: Parser
;
566 this (Loc aloc
, string aname
) { name
= aname
; super(aloc
); }
568 override string
toStringInd (int indent
) const => indentStr(indent
)~"function "~name
~" "~ebody
.toStringInd(indent
).stripLeft
;