gmk unpacker: better event export
[gaemu.git] / gaem / parser / astools.d
blob432b9c76428cad95ccba91417e866d268f8a1c87
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 gaem.parser.astools is aliced;
20 import gaem.parser.lexer;
21 import gaem.parser.tokens;
22 import gaem.parser.utils;
23 import gaem.parser.ast;
26 // ////////////////////////////////////////////////////////////////////////// //
27 enum VisitRes { Continue, Stop, SkipChildren }
29 private final class NodeSkipChildren : Node {}
30 private __gshared NodeSkipChildren nnc;
31 shared static this () { nnc = new NodeSkipChildren(); }
34 Node visitNodes (Node nn, VisitRes delegate (Node n) dg) {
35 if (nn is null) return null;
36 if (auto r = dg(nn)) return (r == VisitRes.Stop ? nn : null);
37 Node res;
38 if (cast(NodeStatement)nn) {
39 res = selectNode!(Node)(nn,
40 (NodeVarDecl n) => null,
41 (NodeBlock n) {
42 foreach (Node st; n.stats) if (auto r = visitNodes(st, dg)) return r;
43 return null;
45 (NodeStatementEmpty n) => null,
46 (NodeStatementExpr n) => visitNodes(n.e, dg),
47 (NodeReturn n) => visitNodes(n.e, dg),
48 (NodeWith n) {
49 if (auto r = visitNodes(n.e, dg)) return r;
50 return visitNodes(n.ebody, dg);
52 (NodeIf n) {
53 if (auto r = visitNodes(n.ec, dg)) return r;
54 if (auto r = visitNodes(n.et, dg)) return r;
55 return visitNodes(n.ef, dg);
57 (NodeStatementBreak n) => null,
58 (NodeStatementContinue n) => null,
59 (NodeFor n) {
60 if (auto r = visitNodes(n.einit, dg)) return r;
61 if (auto r = visitNodes(n.econd, dg)) return r;
62 if (auto r = visitNodes(n.enext, dg)) return r;
63 return visitNodes(n.ebody, dg);
65 (NodeWhile n) {
66 if (auto r = visitNodes(n.econd, dg)) return r;
67 return visitNodes(n.ebody, dg);
69 (NodeDoUntil n) {
70 if (auto r = visitNodes(n.econd, dg)) return r;
71 return visitNodes(n.ebody, dg);
73 (NodeRepeat n) {
74 if (auto r = visitNodes(n.ecount, dg)) return r;
75 return visitNodes(n.ebody, dg);
77 (NodeSwitch n) {
78 if (auto r = visitNodes(n.e, dg)) return r;
79 foreach (ref ci; n.cases) {
80 if (auto r = visitNodes(ci.e, dg)) return r;
81 if (auto r = visitNodes(ci.st, dg)) return r;
83 return null;
85 () { assert(0, "unimplemented node: "~typeid(nn).name); },
87 } else {
88 res = selectNode!(Node)(nn,
89 (NodeLiteral n) => null,
90 (NodeUnary n) => visitNodes(n.e, dg),
91 (NodeBinary n) {
92 if (auto r = visitNodes(n.el, dg)) return r;
93 return visitNodes(n.er, dg);
95 (NodeFCall n) {
96 if (auto r = visitNodes(n.fe, dg)) return r;
97 foreach (immutable idx, Node a; n.args) if (auto r = visitNodes(a, dg)) return r;
98 return null;
100 (NodeId n) => null,
101 (NodeDot n) => visitNodes(n.e, dg),
102 (NodeIndex n) {
103 if (auto r = visitNodes(n.ei0, dg)) return r;
104 if (auto r = visitNodes(n.ei1, dg)) return r;
105 return visitNodes(n.e, dg);
107 (NodeFunc n) => visitNodes(n.ebody, dg),
108 () { assert(0, "unimplemented node: "~typeid(nn).name); },
111 if (res is nnc) res = null;
112 return res;
116 // ////////////////////////////////////////////////////////////////////////// //
117 bool isEmpty (Node nn) {
118 if (nn is null) return true;
119 if (auto n = cast(NodeFunc)nn) return isEmpty(n.ebody);
120 if (cast(NodeStatement)nn) {
121 if (cast(NodeStatementEmpty)nn) return true;
122 if (auto n = cast(NodeBlock)nn) {
123 foreach (Node st; n.stats) if (!isEmpty(st)) return false;
124 return true;
127 return false;
131 // ////////////////////////////////////////////////////////////////////////// //
132 bool hasReturn (Node nn) {
133 if (nn is null) return false;
134 if (cast(NodeStatement)nn) {
135 return selectNode!(bool)(nn,
136 (NodeVarDecl n) => false,
137 (NodeBlock n) {
138 foreach (Node st; n.stats) if (hasReturn(st)) return true;
139 return false;
141 (NodeStatementEmpty n) => false,
142 (NodeStatementExpr n) => hasReturn(n.e),
143 (NodeReturn n) => true,
144 (NodeWith n) => (hasReturn(n.e) || hasReturn(n.ebody)),
145 (NodeIf n) => (hasReturn(n.ec) || hasReturn(n.et) || hasReturn(n.ef)),
146 (NodeStatementBreak n) => false,
147 (NodeStatementContinue n) => false,
148 (NodeFor n) => (hasReturn(n.einit) || hasReturn(n.econd) || hasReturn(n.enext) || hasReturn(n.ebody)),
149 (NodeWhile n) => (hasReturn(n.econd) || hasReturn(n.ebody)),
150 (NodeDoUntil n) => (hasReturn(n.econd) || hasReturn(n.ebody)),
151 (NodeRepeat n) => (hasReturn(n.ecount) || hasReturn(n.ebody)),
152 (NodeSwitch n) {
153 if (hasReturn(n.e)) return true;
154 foreach (ref ci; n.cases) if (hasReturn(ci.e) || hasReturn(ci.st)) return true;
155 return false;
157 () { assert(0, "unimplemented node: "~typeid(nn).name); },
159 } else {
160 return selectNode!(bool)(nn,
161 (NodeLiteral n) => false,
162 (NodeUnary n) => hasReturn(n.e),
163 (NodeBinary n) => hasReturn(n.el) || hasReturn(n.er),
164 (NodeFCall n) {
165 if (hasReturn(n.fe)) return true;
166 foreach (immutable idx, Node a; n.args) if (hasReturn(a)) return true;
167 return false;
169 (NodeId n) => false,
170 (NodeDot n) => hasReturn(n.e),
171 (NodeIndex n) => (hasReturn(n.e) || hasReturn(n.ei0) || hasReturn(n.ei1)),
172 (NodeFunc n) => hasReturn(n.ebody),
173 () { assert(0, "unimplemented node: "~typeid(nn).name); },
179 // ////////////////////////////////////////////////////////////////////////// //
180 //x > view_xview[0]-16 && x < view_xview[0]+view_wview[0]+16 &&
181 //y > view_yview[0]-16 && y < view_yview[0]+view_hview[0]+16
182 struct FCInfo {
183 Node n;
184 bool isNotFrozen;
187 void findFrozenChecks (ref Node[] cn, Node nn) {
188 import std.stdio;
189 import std.string : replace;
191 static bool isViewAccess (Node nn, char what) {
192 if (auto n = cast(NodeIndex)nn) {
193 // has second index?
194 if (n.ei1 !is null) return false;
195 // id
196 if (auto id = cast(NodeId)n.e) {
197 if (id.name.length != 10) return false;
198 if (id.name.ptr[5] != what) return false;
199 if (id.name[0..5] != "view_") return false;
200 if (id.name[6..$] != "view") return false;
201 } else {
202 return false;
204 // [0]
205 if (auto l = cast(NodeLiteralNum)n.ei0) {
206 if (l.val != 0) return false;
207 } else {
208 return false;
210 return true;
212 return false;
215 static bool isXYStartExpr (Node nn, char what) {
216 if (auto n = cast(NodeBinary)nn) {
217 if (!isViewAccess(n.el, what)) return false;
218 if (cast(NodeBinaryAdd)nn is null && cast(NodeBinarySub)nn is null) return false;
219 if (cast(NodeLiteralNum)n.er is null) return false;
220 return true;
221 } else if (isViewAccess(nn, what)) {
222 return true;
224 return false;
227 static bool isXYEndExpr (Node nn, char what) {
228 if (auto n = cast(NodeBinary)nn) {
229 //TODO: check for "+"
230 if (isViewAccess(n.el, (what == 'w' ? 'x' : 'y')) && isViewAccess(n.er, what)) return true;
232 if (auto n = cast(NodeBinaryAdd)nn) {
233 //writeln("x00(", what, "): ", nn.toString.replace("\n", " "), " : ", typeid(n.el).name);
234 //writeln("x01(", what, "): ", n.el.toString.replace("\n", " "), " : ", typeid(n.el).name);
235 if (cast(NodeBinaryAdd)n.el is null && cast(NodeBinarySub)n.el is null) return false;
236 //writeln("x02(", what, "): ", n.el.toString.replace("\n", " "), " : ", typeid(n.el).name);
237 auto x = cast(NodeBinary)n.el;
238 assert(x !is null);
239 if (!isViewAccess(x.el, (what == 'w' ? 'x' : 'y'))) return false;
240 //writeln("x03(", what, "): ", n.el.toString.replace("\n", " "), " : ", typeid(n.el).name);
241 if (!isViewAccess(x.er, what)) return false;
242 //writeln("x04(", what, "): ", n.el.toString.replace("\n", " "), " : ", typeid(n.el).name);
243 return true;
245 return false;
248 static bool isCompare(alias exprcheck) (Node nn, char what) {
249 if (auto n = cast(NodeBinaryCmp)nn) {
250 if (auto id = cast(NodeId)n.el) {
251 if (id.name != "x" && id.name != "y") return false;
252 //writeln("cmp(", what, "): ", n.er.toString.replace("\n", " "));
253 return exprcheck(n.er, what);
254 } else if (auto id = cast(NodeId)n.er) {
255 if (id.name != "x" && id.name != "y") return false;
256 return exprcheck(n.el, what);
259 return false;
262 static bool isFrozenExpr3 (Node nn) {
263 if (auto a3 = cast(NodeBinaryLogAnd)nn) {
264 //writeln("fe3: ", nn.toString.replace("\n", " "));
265 //writeln("fe3.l: ", a3.el.toString.replace("\n", " "));
266 //writeln("fe3.r: ", a3.er.toString.replace("\n", " "));
267 //writeln(" : ", isCompare!isXYStartExpr(a3.el, 'x'));
268 //writeln(" : ", isCompare!isXYEndExpr(a3.er, 'w'));
269 return
270 isCompare!isXYStartExpr(a3.el, 'x') &&
271 isCompare!isXYEndExpr(a3.er, 'w');
273 return false;
276 static bool isFrozenExpr2 (Node nn) {
277 if (auto a2 = cast(NodeBinaryLogAnd)nn) {
278 //writeln("fe2: ", nn.toString.replace("\n", " "));
279 //writeln("fe2.l: ", a2.el.toString.replace("\n", " "));
280 //writeln("fe2.r: ", a2.er.toString.replace("\n", " "));
281 //writeln(" : ", isCompare!isXYStartExpr(a2.er, 'y'));
282 return
283 isFrozenExpr3(a2.el) &&
284 isCompare!isXYStartExpr(a2.er, 'y');
286 return false;
289 static bool isFrozenExpr1 (Node nn) {
290 if (auto a1 = cast(NodeBinaryLogAnd)nn) {
291 //writeln("fe1: ", nn.toString.replace("\n", " "));
292 //writeln("fe1.l: ", a1.el.toString.replace("\n", " "));
293 //writeln("fe1.r: ", a1.er.toString.replace("\n", " "));
294 //writeln(" : ", isCompare!isXYEndExpr(a1.er, 'y'));
295 return
296 isFrozenExpr2(a1.el) &&
297 isCompare!isXYEndExpr(a1.er, 'h');
299 return false;
302 bool isFrozenExpr (Node nn) {
303 auto res = visitNodes(nn, (n) {
304 if (isFrozenExpr1(n)) return VisitRes.Stop;
305 if (auto id = cast(NodeId)n) {
306 if (id.name == "isNotFrozen") return VisitRes.Stop;
308 return VisitRes.Continue;
310 return (res !is null);
313 visitNodes(nn, (nn) {
314 if (auto n = cast(NodeIf)nn) {
315 if (isFrozenExpr(n.ec)) cn ~= n;
317 return VisitRes.Continue;