API changes
[gaemu.git] / checker.d
blobeb8f5defaebdbb893a72d4e1e05825e409e32a70
1 module checker is aliced;
3 import std.stdio;
5 import gmlparser;
6 import ungmk;
8 import gmlparser.anal;
11 // ////////////////////////////////////////////////////////////////////////// //
12 NodeFunc[] loadScript (string filename, bool warnings=true) {
13 import std.algorithm : startsWith;
14 import std.file : readText;
15 import std.path : baseName, extension;
16 import std.stdio;
17 import std.string : replace;
19 auto s = readText(filename);
21 NodeFunc[] res;
22 auto parser = new Parser(s, filename);
23 parser.warnings = warnings;
24 bool asGmx = (filename.extension == ".gmx");
25 try {
26 if (asGmx) {
27 while (!parser.lex.empty) res ~= parser.parseFunction();
28 } else {
29 string scname = filename.baseName(".gml");
30 res ~= parser.parseFunctionBody(scname);
32 assert(parser.lex.empty);
33 } catch (ErrorAt e) {
34 import std.stdio;
35 writeln("ERROR at ", e.loc, ": ", e.msg);
36 writeln(typeid(e).name, "@", e.file, "(", e.line, "): ", e.msg);
37 assert(0);
38 } catch (Exception e) {
39 import std.stdio;
40 writeln(typeid(e).name, "@", e.file, "(", e.line, "): ", e.msg);
41 assert(0);
43 return res;
47 // ////////////////////////////////////////////////////////////////////////// //
48 NodeFunc parseScript (string code, string scname, bool warnings=true) {
49 auto parser = new Parser(code, scname);
50 parser.warnings = warnings;
51 try {
52 return parser.parseFunctionBody(scname);
53 } catch (ErrorAt e) {
54 import std.stdio;
55 writeln("ERROR at ", e.loc, ": ", e.msg);
56 writeln(typeid(e).name, "@", e.file, "(", e.line, "): ", e.msg);
57 writeln(code);
58 throw e;
59 } catch (Exception e) {
60 import std.stdio;
61 writeln(typeid(e).name, "@", e.file, "(", e.line, "): ", e.msg);
62 throw e;
64 assert(0);
68 // ////////////////////////////////////////////////////////////////////////// //
69 NodeFunc[] gmkLoadScripts (Gmk gmk, bool doScripts, bool doActions) {
70 NodeFunc[] funcs;
72 import std.conv : to;
74 void setupObject (GMObject obj, GMObject oparent) {
75 string parent = (oparent !is null ? oparent.name : null);
77 void parseECode (ref string evcode, string evname) {
78 import iv.strex;
79 import std.string : replace;
80 scope(exit) evcode = null;
81 evcode = evcode.replace("\r\n", "\n").replace("\r", "\n").outdentAll;
82 //while (evcode.length && evcode[0] <= ' ') evcode = evcode[1..$];
83 while (evcode.length && evcode[$-1] <= ' ') evcode = evcode[0..$-1];
84 if (evcode.length) {
85 auto fn = evcode.parseScript(obj.name~":"~evname);
86 if (!isEmpty(fn)) {
87 if (hasReturn(fn)) throw new Exception("event '"~evname~"' for object '"~obj.name~"' contains `return`");
88 funcs ~= fn;
93 void createEvent (GMEvent.Type evtype) {
94 import std.conv : to;
95 string evcode;
96 foreach (immutable evidx, auto ev; obj.events[evtype]) {
97 foreach (immutable aidx, auto act; ev.actions) {
98 if (act.type == act.Type.Nothing) continue; // comment
99 if (act.kind == act.Kind.act_normal) {
100 // normal action
101 if (act.type == act.Type.Function) {
102 if (act.funcname == "action_inherited") {
103 assert(parent.length);
104 evcode ~= "_action_inherited(\""~to!string(evtype)~"\", \""~parent~"\");\n";
105 continue;
107 if (act.funcname == "action_kill_object") {
108 evcode ~= "_action_kill_object();\n";
109 continue;
111 if (act.funcname == "action_execute_script") {
112 import std.conv : to;
113 if (act.argused < 1 || act.argtypes[0] != act.ArgType.t_script) assert(0, "invalid action function arguments: '"~act.funcname~"' for object '"~obj.name~"'");
114 string s = gmk.scriptByNum(to!int(act.argvals[0])).name~"(";
115 foreach (immutable idx; 1..act.argused) {
116 if (act.argtypes[idx] != act.ArgType.t_expr) assert(0, "invalid action type for execscript: "~to!string(act.argtypes[idx])~" for object '"~obj.name~"'");
117 if (idx != 1) s ~= ", ";
118 s ~= act.argvals[idx];
120 s ~= "); // action_execute_script\n";
121 evcode ~= s;
122 continue;
124 assert(0, "invalid action function: '"~act.funcname~"' for object '"~obj.name~"'");
126 assert(0, "invalid normal action type");
128 if (act.kind == act.Kind.act_code) {
129 // script
130 if (act.type == act.Type.Code) {
131 if (act.argused < 1 || act.argtypes[0] != act.ArgType.t_string) {
132 import std.conv : to;
133 assert(0, "invalid action code arguments for '"~obj.name~"': used="~to!string(act.argused)~"; kinds="~to!string(act.argtypes));
135 import std.string : format;
136 evcode ~= act.argvals[0];
137 while (evcode.length && evcode[$-1] <= ' ') evcode = evcode[0..$-1];
138 if (evcode.length > 0) evcode ~= "\n";
139 continue;
141 assert(0, "invalid code action type: "~to!string(act.type));
143 if (act.kind == act.Kind.act_var) {
144 // variable assignment
145 if (act.argused != 2 || act.argtypes[0] != act.ArgType.t_string || act.argtypes[1] != act.ArgType.t_expr) {
146 assert(0, "invalid action code arguments for '"~obj.name~"': used="~to!string(act.argused)~"; kinds="~to!string(act.argtypes));
148 evcode ~= act.argvals[0]~" = "~act.argvals[1]~"; // act_var";
149 continue;
152 assert(0, "FUUUCK: "~to!string(act.kind));
155 string baseevname = to!string(evtype);
156 if (evidx > 0) baseevname ~= to!string(evidx);
157 if (evtype == GMEvent.Type.ev_alarm) {
158 parseECode(evcode, "ev_alarm:"~to!string(ev.id));
159 //{ import std.stdio; writeln("alarm #", evidx, " for '", obj.name, "'"); }
160 } else if (evtype == GMEvent.Type.ev_step) {
161 if (ev.id == 0) {
162 // normal
163 parseECode(evcode, baseevname);
164 } else if (ev.id == 1) {
165 // begin
166 parseECode(evcode, to!string(evtype)~":begin");
167 } else if (ev.id == 2) {
168 // end
169 parseECode(evcode, to!string(evtype)~":end");
170 } else {
171 assert(0);
173 } else if (evtype == GMEvent.Type.ev_keypress || evtype == GMEvent.Type.ev_keyrelease || evtype == GMEvent.Type.ev_keyboard) {
174 if (auto keyName = cast(uint)ev.id in evKeyNames) {
175 import std.string : replace;
176 string kn = (*keyName).replace(" ", "_");
177 parseECode(evcode, to!string(evtype)~":"~kn);
178 } else {
179 parseECode(evcode, to!string(evtype)~":vcode_"~to!string(ev.id));
181 } else if (evtype == GMEvent.Type.ev_mouse) {
182 if (auto msName = cast(uint)ev.id in evMouseNames) {
183 import std.string : replace;
184 string kn = (*msName).replace(" ", "_");
185 parseECode(evcode, to!string(evtype)~":"~kn);
186 } else {
187 parseECode(evcode, to!string(evtype)~":mcode_"~to!string(ev.id));
189 } else if (evtype == GMEvent.Type.ev_collision) {
190 auto co = gmk.objByNum(ev.id);
191 if (co is null) assert(0, "no collision object for 'ev_collision' for '"~obj.name~"'");
192 parseECode(evcode, to!string(evtype)~":"~co.name);
193 } else if (evtype == GMEvent.Type.ev_other) {
194 auto nmp = cast(uint)ev.id in evOtherNames;
195 if (nmp is null) assert(0, "unknown event id "~to!string(ev.id)~" for 'ev_other' for '"~obj.name~"'");
196 import std.string : replace;
197 string nm = (*nmp).replace(" ", "_");
198 parseECode(evcode, to!string(evtype)~":"~nm);
199 } else if (evtype == GMEvent.Type.ev_draw || evtype == GMEvent.Type.ev_destroy || evtype == GMEvent.Type.ev_create) {
200 parseECode(evcode, baseevname);
201 } else {
202 /*if (evidx > 0)*/ {
203 { import std.stdio; writeln("fuck! ", evtype, " #", evidx, " for '", obj.name, "'"); }
204 assert(0);
206 parseECode(evcode, to!string(evtype)~to!string(evidx)~"_"~to!string(ev.id));
211 foreach (immutable evtype; 0..GMEvent.Type.max+1) {
212 if (evtype != GMEvent.Type.ev_create) {
213 if (obj.name == "oGamepad") continue;
215 createEvent(cast(GMEvent.Type)evtype);
219 void processChildren (string parent) {
220 auto po = gmk.objByName(parent);
221 if (po is null) assert(0, "wtf?! "~parent);
222 gmk.forEachObject((o) {
223 if (o.parentobjidx == po.idx) {
224 if (doActions) setupObject(o, po);
225 processChildren(o.name);
227 return false;
231 // objects
232 gmk.forEachObject((o) {
233 if (o.parentobjidx < 0) {
234 if (doActions) setupObject(o, null);
235 processChildren(o.name);
237 return false;
240 // scripts
241 gmk.forEachScript((sc) {
242 assert(sc.name.length);
243 if (doScripts) {
244 NodeFunc fn = sc.code.parseScript(sc.name);
245 assert(fn.ebody !is null);
246 funcs ~= fn;
248 return false;
251 return funcs;
255 // ////////////////////////////////////////////////////////////////////////// //
256 void main (string[] args) {
257 NodeFunc[] funcs;
259 bool dumpFileNames = false;
260 bool styleWarnings = false;
261 bool doScripts = true, doActions = true;
263 bool nomore = false;
264 string[] scargs;
265 foreach (string fname; args[1..$]) {
266 import std.file;
267 import std.path;
268 if (nomore) {
269 scargs ~= fname;
270 } else {
271 if (fname.length == 0) continue;
272 if (fname == "--") { nomore = true; continue; }
273 if (fname == "-d") { dumpFileNames = true; continue; }
274 if (fname == "-w") { styleWarnings = true; continue; }
275 if (fname == "-S") { doScripts = false; continue; }
276 if (fname == "-A") { doActions = false; continue; }
277 if (fname[0] == '@') {
278 if (fname.length < 2) assert(0, "gmk file?");
279 auto gmk = new Gmk(fname[1..$]);
280 funcs ~= gmkLoadScripts(gmk, doScripts:doScripts, doActions:doActions);
281 continue;
283 if (isDir(fname)) {
284 foreach (auto de; dirEntries(fname, "*.gm[lx]", SpanMode.breadth)) {
285 bool doit = true;
286 foreach (auto pt; pathSplitter(de.dirName)) {
287 if (pt.length && pt[0] == '_') { doit = false; break; }
289 if (doit) {
290 if (dumpFileNames) { import std.stdio; writeln("loading '", de.name, "'..."); }
291 funcs ~= loadScript(de.name, true);
294 } else {
295 if (dumpFileNames) { import std.stdio; writeln("loading '", fname, "'..."); }
296 funcs ~= loadScript(fname, true);
301 if (funcs.length > 0) {
302 writeln(funcs.length, " function", (funcs.length > 1 ? "s" : ""), " parsed");
303 foreach (auto fn; funcs) {
304 bool skip = false;
305 foreach (string name; [
306 "scrCreateTile",
307 "scrCreateTileObj",
308 "scrLoadCheckpoint",
309 "scrLoadLevel",
310 "scrMakeItem",
311 "scrSetCursorTile",
312 "scrSetVendingItem",
313 "scrTestLevel",
314 "scrMagicSigns",
315 ]) {
316 if (fn.name == name) { skip = true; break; }
318 if (skip) continue;
319 if (fn.name.length > 3 && fn.name[0..3] == "sui") continue;
320 analVars(null, fn);