1 /* |
|
2 * botc source code |
|
3 * Copyright (C) 2012 Santeri `Dusk` Piippo |
|
4 * All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright notice, |
|
10 * this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright notice, |
|
12 * this list of conditions and the following disclaimer in the documentation |
|
13 * and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of the developer nor the names of its contributors may |
|
15 * be used to endorse or promote products derived from this software without |
|
16 * specific prior written permission. |
|
17 * 4. Redistributions in any form must be accompanied by information on how to |
|
18 * obtain complete source code for the software and any accompanying |
|
19 * software that uses the software. The source code must either be included |
|
20 * in the distribution or be available for no more than the cost of |
|
21 * distribution plus a nominal fee, and must be freely redistributable |
|
22 * under reasonable conditions. For an executable file, complete source |
|
23 * code means the source code for all modules it contains. It does not |
|
24 * include source code for modules or files that typically accompany the |
|
25 * major components of the operating system on which the executable file |
|
26 * runs. |
|
27 * |
|
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
38 * POSSIBILITY OF SUCH DAMAGE. |
|
39 */ |
|
40 |
|
41 #define __PARSER_CXX__ |
|
42 |
|
43 #include <stdio.h> |
|
44 #include <stdlib.h> |
|
45 #include "common.h" |
|
46 #include "str.h" |
|
47 #include "objwriter.h" |
|
48 #include "scriptreader.h" |
|
49 #include "events.h" |
|
50 #include "commands.h" |
|
51 #include "stringtable.h" |
|
52 #include "variables.h" |
|
53 #include "array.h" |
|
54 |
|
55 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
|
56 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
|
57 |
|
58 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
|
59 ParserError ("%s-statements may not be defined at top level!", token.chars()); |
|
60 |
|
61 #define SCOPE(n) scopestack[g_ScopeCursor - n] |
|
62 |
|
63 int g_NumStates = 0; |
|
64 int g_NumEvents = 0; |
|
65 parsermode_e g_CurMode = MODE_TOPLEVEL; |
|
66 str g_CurState = ""; |
|
67 bool g_stateSpawnDefined = false; |
|
68 bool g_GotMainLoop = false; |
|
69 unsigned int g_ScopeCursor = 0; |
|
70 DataBuffer* g_IfExpression = NULL; |
|
71 bool g_CanElse = false; |
|
72 str* g_UndefinedLabels[MAX_MARKS]; |
|
73 bool g_Neurosphere = false; // neurosphere-compat |
|
74 array<constinfo_t> g_ConstInfo; |
|
75 |
|
76 // ============================================================================ |
|
77 // Main parser code. Begins read of the script file, checks the syntax of it |
|
78 // and writes the data to the object file via ObjWriter - which also takes care |
|
79 // of necessary buffering so stuff is written in the correct order. |
|
80 void ScriptReader::ParseBotScript (ObjWriter* w) { |
|
81 // Zero the entire block stack first |
|
82 for (int i = 0; i < MAX_SCOPE; i++) |
|
83 ZERO(scopestack[i]); |
|
84 |
|
85 for (int i = 0; i < MAX_MARKS; i++) |
|
86 g_UndefinedLabels[i] = NULL; |
|
87 |
|
88 while (Next()) { |
|
89 // Check if else is potentically valid |
|
90 if (token == "else" && !g_CanElse) |
|
91 ParserError ("else without preceding if"); |
|
92 if (token != "else") |
|
93 g_CanElse = false; |
|
94 |
|
95 // ============================================================ |
|
96 if (token == "state") { |
|
97 MUST_TOPLEVEL |
|
98 |
|
99 MustString (); |
|
100 |
|
101 // State name must be a word. |
|
102 if (token.first (" ") != token.len()) |
|
103 ParserError ("state name must be a single word, got `%s`", token.chars()); |
|
104 str statename = token; |
|
105 |
|
106 // stateSpawn is special - it *must* be defined. If we |
|
107 // encountered it, then mark down that we have it. |
|
108 if (-token == "statespawn") |
|
109 g_stateSpawnDefined = true; |
|
110 |
|
111 // Must end in a colon |
|
112 MustNext (":"); |
|
113 |
|
114 // Write the previous state's onenter and |
|
115 // mainloop buffers to file now |
|
116 if (g_CurState.len()) |
|
117 w->WriteBuffers(); |
|
118 |
|
119 w->Write (DH_STATENAME); |
|
120 w->WriteString (statename); |
|
121 w->Write (DH_STATEIDX); |
|
122 w->Write (g_NumStates); |
|
123 |
|
124 g_NumStates++; |
|
125 g_CurState = token; |
|
126 g_GotMainLoop = false; |
|
127 continue; |
|
128 } |
|
129 |
|
130 // ============================================================ |
|
131 if (token == "event") { |
|
132 MUST_TOPLEVEL |
|
133 |
|
134 // Event definition |
|
135 MustString (); |
|
136 |
|
137 EventDef* e = FindEventByName (token); |
|
138 if (!e) |
|
139 ParserError ("bad event, got `%s`\n", token.chars()); |
|
140 |
|
141 MustNext ("{"); |
|
142 |
|
143 g_CurMode = MODE_EVENT; |
|
144 |
|
145 w->Write (DH_EVENT); |
|
146 w->Write (e->number); |
|
147 g_NumEvents++; |
|
148 continue; |
|
149 } |
|
150 |
|
151 // ============================================================ |
|
152 if (token == "mainloop") { |
|
153 MUST_TOPLEVEL |
|
154 MustNext ("{"); |
|
155 |
|
156 // Mode must be set before dataheader is written here! |
|
157 g_CurMode = MODE_MAINLOOP; |
|
158 w->Write (DH_MAINLOOP); |
|
159 continue; |
|
160 } |
|
161 |
|
162 // ============================================================ |
|
163 if (token == "onenter" || token == "onexit") { |
|
164 MUST_TOPLEVEL |
|
165 bool onenter = token == "onenter"; |
|
166 MustNext ("{"); |
|
167 |
|
168 // Mode must be set before dataheader is written here, |
|
169 // because onenter goes to a separate buffer. |
|
170 g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; |
|
171 w->Write (onenter ? DH_ONENTER : DH_ONEXIT); |
|
172 continue; |
|
173 } |
|
174 |
|
175 // ============================================================ |
|
176 if (token == "int" || token == "str" || token == "bool") { |
|
177 // For now, only globals are supported |
|
178 if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) |
|
179 ParserError ("variables must only be global for now"); |
|
180 |
|
181 type_e type = (token == "int") ? TYPE_INT : |
|
182 (token == "str") ? TYPE_STRING : |
|
183 TYPE_BOOL; |
|
184 |
|
185 MustNext (); |
|
186 |
|
187 // Var name must not be a number |
|
188 if (token.isnumber()) |
|
189 ParserError ("variable name must not be a number"); |
|
190 |
|
191 str varname = token; |
|
192 ScriptVar* var = DeclareGlobalVariable (this, type, varname); |
|
193 |
|
194 if (!var) |
|
195 ParserError ("declaring %s variable %s failed", |
|
196 g_CurState.len() ? "state" : "global", varname.chars()); |
|
197 |
|
198 MustNext (";"); |
|
199 continue; |
|
200 } |
|
201 |
|
202 // ============================================================ |
|
203 // Goto |
|
204 if (token == "goto") { |
|
205 MUST_NOT_TOPLEVEL |
|
206 |
|
207 // Get the name of the label |
|
208 MustNext (); |
|
209 |
|
210 // Find the mark this goto statement points to |
|
211 unsigned int m = w->FindMark (token); |
|
212 |
|
213 // If not set, define it |
|
214 if (m == MAX_MARKS) { |
|
215 m = w->AddMark (token); |
|
216 g_UndefinedLabels[m] = new str (token); |
|
217 } |
|
218 |
|
219 // Add a reference to the mark. |
|
220 w->Write (DH_GOTO); |
|
221 w->AddReference (m); |
|
222 MustNext (";"); |
|
223 continue; |
|
224 } |
|
225 |
|
226 // ============================================================ |
|
227 // If |
|
228 if (token == "if") { |
|
229 MUST_NOT_TOPLEVEL |
|
230 PushScope (); |
|
231 |
|
232 // Condition |
|
233 MustNext ("("); |
|
234 |
|
235 // Read the expression and write it. |
|
236 MustNext (); |
|
237 DataBuffer* c = ParseExpression (TYPE_INT); |
|
238 w->WriteBuffer (c); |
|
239 |
|
240 MustNext (")"); |
|
241 MustNext ("{"); |
|
242 |
|
243 // Add a mark - to here temporarily - and add a reference to it. |
|
244 // Upon a closing brace, the mark will be adjusted. |
|
245 unsigned int marknum = w->AddMark (""); |
|
246 |
|
247 // Use DH_IFNOTGOTO - if the expression is not true, we goto the mark |
|
248 // we just defined - and this mark will be at the end of the scope block. |
|
249 w->Write (DH_IFNOTGOTO); |
|
250 w->AddReference (marknum); |
|
251 |
|
252 // Store it |
|
253 SCOPE(0).mark1 = marknum; |
|
254 SCOPE(0).type = SCOPETYPE_IF; |
|
255 continue; |
|
256 } |
|
257 |
|
258 if (token == "else") { |
|
259 MUST_NOT_TOPLEVEL |
|
260 MustNext ("{"); |
|
261 |
|
262 // Don't use PushScope as it resets the scope |
|
263 g_ScopeCursor++; |
|
264 if (g_ScopeCursor >= MAX_SCOPE) |
|
265 ParserError ("too deep scope"); |
|
266 |
|
267 if (SCOPE(0).type != SCOPETYPE_IF) |
|
268 ParserError ("else without preceding if"); |
|
269 |
|
270 // Write down to jump to the end of the else statement |
|
271 // Otherwise we have fall-throughs |
|
272 SCOPE(0).mark2 = w->AddMark (""); |
|
273 |
|
274 // Instruction to jump to the end after if block is complete |
|
275 w->Write (DH_GOTO); |
|
276 w->AddReference (SCOPE(0).mark2); |
|
277 |
|
278 // Move the ifnot mark here and set type to else |
|
279 w->MoveMark (SCOPE(0).mark1); |
|
280 SCOPE(0).type = SCOPETYPE_ELSE; |
|
281 continue; |
|
282 } |
|
283 |
|
284 // ============================================================ |
|
285 // While |
|
286 if (token == "while") { |
|
287 MUST_NOT_TOPLEVEL |
|
288 PushScope (); |
|
289 |
|
290 // While loops need two marks - one at the start of the loop and one at the |
|
291 // end. The condition is checked at the very start of the loop, if it fails, |
|
292 // we use goto to skip to the end of the loop. At the end, we loop back to |
|
293 // the beginning with a go-to statement. |
|
294 unsigned int mark1 = w->AddMark (""); // start |
|
295 unsigned int mark2 = w->AddMark (""); // end |
|
296 |
|
297 // Condition |
|
298 MustNext ("("); |
|
299 MustNext (); |
|
300 DataBuffer* expr = ParseExpression (TYPE_INT); |
|
301 MustNext (")"); |
|
302 MustNext ("{"); |
|
303 |
|
304 // Write condition |
|
305 w->WriteBuffer (expr); |
|
306 |
|
307 // Instruction to go to the end if it fails |
|
308 w->Write (DH_IFNOTGOTO); |
|
309 w->AddReference (mark2); |
|
310 |
|
311 // Store the needed stuff |
|
312 SCOPE(0).mark1 = mark1; |
|
313 SCOPE(0).mark2 = mark2; |
|
314 SCOPE(0).type = SCOPETYPE_WHILE; |
|
315 continue; |
|
316 } |
|
317 |
|
318 // ============================================================ |
|
319 // For loop |
|
320 if (token == "for") { |
|
321 MUST_NOT_TOPLEVEL |
|
322 PushScope (); |
|
323 |
|
324 // Initializer |
|
325 MustNext ("("); |
|
326 MustNext (); |
|
327 DataBuffer* init = ParseStatement (w); |
|
328 if (!init) |
|
329 ParserError ("bad statement for initializer of for"); |
|
330 |
|
331 MustNext (";"); |
|
332 |
|
333 // Condition |
|
334 MustNext (); |
|
335 DataBuffer* cond = ParseExpression (TYPE_INT); |
|
336 if (!cond) |
|
337 ParserError ("bad statement for condition of for"); |
|
338 |
|
339 MustNext (";"); |
|
340 |
|
341 // Incrementor |
|
342 MustNext (); |
|
343 DataBuffer* incr = ParseStatement (w); |
|
344 if (!incr) |
|
345 ParserError ("bad statement for incrementor of for"); |
|
346 |
|
347 MustNext (")"); |
|
348 MustNext ("{"); |
|
349 |
|
350 // First, write out the initializer |
|
351 w->WriteBuffer (init); |
|
352 |
|
353 // Init two marks |
|
354 int mark1 = w->AddMark (""); |
|
355 int mark2 = w->AddMark (""); |
|
356 |
|
357 // Add the condition |
|
358 w->WriteBuffer (cond); |
|
359 w->Write (DH_IFNOTGOTO); |
|
360 w->AddReference (mark2); |
|
361 |
|
362 // Store the marks and incrementor |
|
363 SCOPE(0).mark1 = mark1; |
|
364 SCOPE(0).mark2 = mark2; |
|
365 SCOPE(0).buffer1 = incr; |
|
366 SCOPE(0).type = SCOPETYPE_FOR; |
|
367 continue; |
|
368 } |
|
369 |
|
370 // ============================================================ |
|
371 // Do/while loop |
|
372 if (token == "do") { |
|
373 MUST_NOT_TOPLEVEL |
|
374 PushScope (); |
|
375 MustNext ("{"); |
|
376 SCOPE(0).mark1 = w->AddMark (""); |
|
377 SCOPE(0).type = SCOPETYPE_DO; |
|
378 continue; |
|
379 } |
|
380 |
|
381 // ============================================================ |
|
382 // Switch |
|
383 if (token == "switch") { |
|
384 /* This goes a bit tricky. switch is structured in the |
|
385 * bytecode followingly: |
|
386 * (expression) |
|
387 * case a: goto casemark1 |
|
388 * case b: goto casemark2 |
|
389 * case c: goto casemark3 |
|
390 * goto mark1 // jump to end if no matches |
|
391 * casemark1: ... |
|
392 * casemark2: ... |
|
393 * casemark3: ... |
|
394 * mark1: // end mark |
|
395 */ |
|
396 |
|
397 MUST_NOT_TOPLEVEL |
|
398 PushScope (); |
|
399 MustNext ("("); |
|
400 MustNext (); |
|
401 w->WriteBuffer (ParseExpression (TYPE_INT)); |
|
402 MustNext (")"); |
|
403 MustNext ("{"); |
|
404 SCOPE(0).type = SCOPETYPE_SWITCH; |
|
405 SCOPE(0).mark1 = w->AddMark (""); // end mark |
|
406 SCOPE(0).buffer1 = NULL; // default header |
|
407 continue; |
|
408 } |
|
409 |
|
410 // ============================================================ |
|
411 if (token == "case") { |
|
412 // case is only allowed inside switch |
|
413 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
|
414 ParserError ("case label outside switch"); |
|
415 |
|
416 // Get the literal (Zandronum does not support expressions here) |
|
417 MustNumber (); |
|
418 int num = atoi (token.chars ()); |
|
419 MustNext (":"); |
|
420 |
|
421 for (int i = 0; i < MAX_CASE; i++) |
|
422 if (SCOPE(0).casenumbers[i] == num) |
|
423 ParserError ("multiple case %d labels in one switch", num); |
|
424 |
|
425 // Write down the expression and case-go-to. This builds |
|
426 // the case tree. The closing event will write the actual |
|
427 // blocks and move the marks appropriately. |
|
428 // AddSwitchCase will add the reference to the mark |
|
429 // for the case block that this heralds, and takes care |
|
430 // of buffering setup and stuff like that. |
|
431 // NULL the switch buffer for the case-go-to statement, |
|
432 // we want it all under the switch, not into the case-buffers. |
|
433 w->SwitchBuffer = NULL; |
|
434 w->Write (DH_CASEGOTO); |
|
435 w->Write (num); |
|
436 AddSwitchCase (w, NULL); |
|
437 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
|
438 continue; |
|
439 } |
|
440 |
|
441 if (token == "default") { |
|
442 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
|
443 ParserError ("default label outside switch"); |
|
444 |
|
445 if (SCOPE(0).buffer1) |
|
446 ParserError ("multiple default labels in one switch"); |
|
447 |
|
448 MustNext (":"); |
|
449 |
|
450 // The default header is buffered into buffer1, since |
|
451 // it has to be the last of the case headers |
|
452 // |
|
453 // Since the expression is pushed into the switch |
|
454 // and is only popped when case succeeds, we have |
|
455 // to pop it with DH_DROP manually if we end up in |
|
456 // a default. |
|
457 DataBuffer* b = new DataBuffer; |
|
458 SCOPE(0).buffer1 = b; |
|
459 b->Write (DH_DROP); |
|
460 b->Write (DH_GOTO); |
|
461 AddSwitchCase (w, b); |
|
462 continue; |
|
463 } |
|
464 |
|
465 // ============================================================ |
|
466 // Break statement. |
|
467 if (token == "break") { |
|
468 if (!g_ScopeCursor) |
|
469 ParserError ("unexpected `break`"); |
|
470 |
|
471 w->Write (DH_GOTO); |
|
472 |
|
473 // switch and if use mark1 for the closing point, |
|
474 // for and while use mark2. |
|
475 switch (SCOPE(0).type) { |
|
476 case SCOPETYPE_IF: |
|
477 case SCOPETYPE_SWITCH: |
|
478 w->AddReference (SCOPE(0).mark1); |
|
479 break; |
|
480 case SCOPETYPE_FOR: |
|
481 case SCOPETYPE_WHILE: |
|
482 w->AddReference (SCOPE(0).mark2); |
|
483 break; |
|
484 default: |
|
485 ParserError ("unexpected `break`"); |
|
486 break; |
|
487 } |
|
488 |
|
489 MustNext (";"); |
|
490 continue; |
|
491 } |
|
492 |
|
493 // ============================================================ |
|
494 // Continue |
|
495 if (token == "continue") { |
|
496 MustNext (";"); |
|
497 |
|
498 int curs; |
|
499 bool found = false; |
|
500 |
|
501 // Drop through the scope until we find a loop block |
|
502 for (curs = g_ScopeCursor; curs > 0 && !found; curs--) { |
|
503 switch (scopestack[curs].type) { |
|
504 case SCOPETYPE_FOR: |
|
505 case SCOPETYPE_WHILE: |
|
506 case SCOPETYPE_DO: |
|
507 w->Write (DH_GOTO); |
|
508 w->AddReference (scopestack[curs].mark1); |
|
509 found = true; |
|
510 break; |
|
511 default: |
|
512 break; |
|
513 } |
|
514 } |
|
515 |
|
516 // No loop blocks |
|
517 if (!found) |
|
518 ParserError ("`continue`-statement not inside a loop"); |
|
519 |
|
520 continue; |
|
521 } |
|
522 |
|
523 // ============================================================ |
|
524 // Label |
|
525 if (PeekNext() == ":") { |
|
526 MUST_NOT_TOPLEVEL |
|
527 |
|
528 // want no conflicts.. |
|
529 if (IsKeyword (token)) |
|
530 ParserError ("label name `%s` conflicts with keyword\n", token.chars()); |
|
531 if (FindCommand (token)) |
|
532 ParserError ("label name `%s` conflicts with command name\n", token.chars()); |
|
533 if (FindGlobalVariable (token)) |
|
534 ParserError ("label name `%s` conflicts with variable\n", token.chars()); |
|
535 |
|
536 // See if a mark already exists for this label |
|
537 int mark = -1; |
|
538 for (int i = 0; i < MAX_MARKS; i++) { |
|
539 if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == token) { |
|
540 mark = i; |
|
541 w->MoveMark (i); |
|
542 |
|
543 // No longer undefinde |
|
544 delete g_UndefinedLabels[i]; |
|
545 g_UndefinedLabels[i] = NULL; |
|
546 } |
|
547 } |
|
548 |
|
549 // Not found in unmarked lists, define it now |
|
550 if (mark == -1) |
|
551 w->AddMark (token); |
|
552 |
|
553 MustNext (":"); |
|
554 continue; |
|
555 } |
|
556 |
|
557 // ============================================================ |
|
558 if (token == "const") { |
|
559 constinfo_t info; |
|
560 |
|
561 // Get the type |
|
562 MustNext (); |
|
563 info.type = GetTypeByName (token); |
|
564 |
|
565 if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) |
|
566 ParserError ("unknown type `%s` for constant", (char*)token); |
|
567 |
|
568 MustNext (); |
|
569 info.name = token; |
|
570 |
|
571 MustNext ("="); |
|
572 |
|
573 switch (info.type) { |
|
574 case TYPE_BOOL: |
|
575 case TYPE_INT: |
|
576 MustNumber (false); |
|
577 info.val = token; |
|
578 break; |
|
579 case TYPE_STRING: |
|
580 MustString (); |
|
581 info.val = token; |
|
582 break; |
|
583 case TYPE_UNKNOWN: |
|
584 case TYPE_VOID: |
|
585 break; |
|
586 } |
|
587 |
|
588 g_ConstInfo << info; |
|
589 |
|
590 MustNext (";"); |
|
591 continue; |
|
592 } |
|
593 |
|
594 // ============================================================ |
|
595 if (token == "}") { |
|
596 // Closing brace |
|
597 |
|
598 // If we're in the block stack, we're descending down from it now |
|
599 if (g_ScopeCursor > 0) { |
|
600 switch (SCOPE(0).type) { |
|
601 case SCOPETYPE_IF: |
|
602 // Adjust the closing mark. |
|
603 w->MoveMark (SCOPE(0).mark1); |
|
604 |
|
605 // We're returning from if, thus else can be next |
|
606 g_CanElse = true; |
|
607 break; |
|
608 case SCOPETYPE_ELSE: |
|
609 // else instead uses mark1 for itself (so if expression |
|
610 // fails, jump to else), mark2 means end of else |
|
611 w->MoveMark (SCOPE(0).mark2); |
|
612 break; |
|
613 case SCOPETYPE_FOR: |
|
614 // Write the incrementor at the end of the loop block |
|
615 w->WriteBuffer (SCOPE(0).buffer1); |
|
616 // fall-thru |
|
617 case SCOPETYPE_WHILE: |
|
618 // Write down the instruction to go back to the start of the loop |
|
619 w->Write (DH_GOTO); |
|
620 w->AddReference (SCOPE(0).mark1); |
|
621 |
|
622 // Move the closing mark here since we're at the end of the while loop |
|
623 w->MoveMark (SCOPE(0).mark2); |
|
624 break; |
|
625 case SCOPETYPE_DO: { |
|
626 MustNext ("while"); |
|
627 MustNext ("("); |
|
628 MustNext (); |
|
629 DataBuffer* expr = ParseExpression (TYPE_INT); |
|
630 MustNext (")"); |
|
631 MustNext (";"); |
|
632 |
|
633 // If the condition runs true, go back to the start. |
|
634 w->WriteBuffer (expr); |
|
635 w->Write (DH_IFGOTO); |
|
636 w->AddReference (SCOPE(0).mark1); |
|
637 break; |
|
638 } |
|
639 case SCOPETYPE_SWITCH: { |
|
640 // Switch closes. Move down to the record buffer of |
|
641 // the lower block. |
|
642 if (SCOPE(1).casecursor != -1) |
|
643 w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; |
|
644 else |
|
645 w->SwitchBuffer = NULL; |
|
646 |
|
647 // If there was a default in the switch, write its header down now. |
|
648 // If not, write instruction to jump to the end of switch after |
|
649 // the headers (thus won't fall-through if no case matched) |
|
650 if (SCOPE(0).buffer1) |
|
651 w->WriteBuffer (SCOPE(0).buffer1); |
|
652 else { |
|
653 w->Write (DH_DROP); |
|
654 w->Write (DH_GOTO); |
|
655 w->AddReference (SCOPE(0).mark1); |
|
656 } |
|
657 |
|
658 // Go through all of the buffers we |
|
659 // recorded down and write them. |
|
660 for (unsigned int u = 0; u < MAX_CASE; u++) { |
|
661 if (!SCOPE(0).casebuffers[u]) |
|
662 continue; |
|
663 |
|
664 w->MoveMark (SCOPE(0).casemarks[u]); |
|
665 w->WriteBuffer (SCOPE(0).casebuffers[u]); |
|
666 } |
|
667 |
|
668 // Move the closing mark here |
|
669 w->MoveMark (SCOPE(0).mark1); |
|
670 break; |
|
671 } |
|
672 case SCOPETYPE_UNKNOWN: |
|
673 break; |
|
674 } |
|
675 |
|
676 // Descend down the stack |
|
677 g_ScopeCursor--; |
|
678 continue; |
|
679 } |
|
680 |
|
681 int dataheader = (g_CurMode == MODE_EVENT) ? DH_ENDEVENT : |
|
682 (g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP : |
|
683 (g_CurMode == MODE_ONENTER) ? DH_ENDONENTER : |
|
684 (g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1; |
|
685 |
|
686 if (dataheader == -1) |
|
687 ParserError ("unexpected `}`"); |
|
688 |
|
689 // Data header must be written before mode is changed because |
|
690 // onenter and mainloop go into special buffers, and we want |
|
691 // the closing data headers into said buffers too. |
|
692 w->Write (dataheader); |
|
693 g_CurMode = MODE_TOPLEVEL; |
|
694 |
|
695 if (PeekNext() == ";") |
|
696 MustNext (";"); |
|
697 continue; |
|
698 } |
|
699 |
|
700 // Check if it's a command |
|
701 CommandDef* comm = FindCommand (token); |
|
702 if (comm) { |
|
703 w->GetCurrentBuffer()->Merge (ParseCommand (comm)); |
|
704 MustNext (";"); |
|
705 continue; |
|
706 } |
|
707 |
|
708 // ============================================================ |
|
709 // If nothing else, parse it as a statement |
|
710 DataBuffer* b = ParseStatement (w); |
|
711 if (!b) |
|
712 ParserError ("unknown token `%s`", token.chars()); |
|
713 |
|
714 w->WriteBuffer (b); |
|
715 MustNext (";"); |
|
716 } |
|
717 |
|
718 // =============================================================================== |
|
719 // Script file ended. Do some last checks and write the last things to main buffer |
|
720 if (g_CurMode != MODE_TOPLEVEL) |
|
721 ParserError ("script did not end at top level; did you forget a `}`?"); |
|
722 |
|
723 // stateSpawn must be defined! |
|
724 if (!g_stateSpawnDefined) |
|
725 ParserError ("script must have a state named `stateSpawn`!"); |
|
726 |
|
727 for (int i = 0; i < MAX_MARKS; i++) |
|
728 if (g_UndefinedLabels[i]) |
|
729 ParserError ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars()); |
|
730 |
|
731 // Dump the last state's onenter and mainloop |
|
732 w->WriteBuffers (); |
|
733 |
|
734 // String table |
|
735 w->WriteStringTable (); |
|
736 } |
|
737 |
|
738 // ============================================================================ |
|
739 // Parses a command call |
|
740 DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { |
|
741 DataBuffer* r = new DataBuffer(64); |
|
742 if (g_CurMode == MODE_TOPLEVEL) |
|
743 ParserError ("command call at top level"); |
|
744 |
|
745 MustNext ("("); |
|
746 MustNext (); |
|
747 |
|
748 int curarg = 0; |
|
749 while (1) { |
|
750 if (token == ")") { |
|
751 if (curarg < comm->numargs) |
|
752 ParserError ("too few arguments passed to %s\n\tprototype: %s", |
|
753 comm->name.chars(), GetCommandPrototype (comm).chars()); |
|
754 break; |
|
755 curarg++; |
|
756 } |
|
757 |
|
758 if (curarg >= comm->maxargs) |
|
759 ParserError ("too many arguments passed to %s\n\tprototype: %s", |
|
760 comm->name.chars(), GetCommandPrototype (comm).chars()); |
|
761 |
|
762 r->Merge (ParseExpression (comm->argtypes[curarg])); |
|
763 MustNext (); |
|
764 |
|
765 if (curarg < comm->numargs - 1) { |
|
766 MustThis (","); |
|
767 MustNext (); |
|
768 } else if (curarg < comm->maxargs - 1) { |
|
769 // Can continue, but can terminate as well. |
|
770 if (token == ")") { |
|
771 curarg++; |
|
772 break; |
|
773 } else { |
|
774 MustThis (","); |
|
775 MustNext (); |
|
776 } |
|
777 } |
|
778 |
|
779 curarg++; |
|
780 } |
|
781 |
|
782 // If the script skipped any optional arguments, fill in defaults. |
|
783 while (curarg < comm->maxargs) { |
|
784 r->Write (DH_PUSHNUMBER); |
|
785 r->Write (comm->defvals[curarg]); |
|
786 curarg++; |
|
787 } |
|
788 |
|
789 r->Write (DH_COMMAND); |
|
790 r->Write (comm->number); |
|
791 r->Write (comm->maxargs); |
|
792 |
|
793 return r; |
|
794 } |
|
795 |
|
796 // ============================================================================ |
|
797 // Is the given operator an assignment operator? |
|
798 static bool IsAssignmentOperator (int oper) { |
|
799 switch (oper) { |
|
800 case OPER_ASSIGNADD: |
|
801 case OPER_ASSIGNSUB: |
|
802 case OPER_ASSIGNMUL: |
|
803 case OPER_ASSIGNDIV: |
|
804 case OPER_ASSIGNMOD: |
|
805 case OPER_ASSIGNLEFTSHIFT: |
|
806 case OPER_ASSIGNRIGHTSHIFT: |
|
807 case OPER_ASSIGN: |
|
808 return true; |
|
809 } |
|
810 return false; |
|
811 } |
|
812 |
|
813 // ============================================================================ |
|
814 // Finds an operator's corresponding dataheader |
|
815 static word DataHeaderByOperator (ScriptVar* var, int oper) { |
|
816 if (IsAssignmentOperator (oper)) { |
|
817 if (!var) |
|
818 error ("operator %d requires left operand to be a variable\n", oper); |
|
819 |
|
820 // TODO: At the moment, vars only are global |
|
821 // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not |
|
822 // have data headers, instead they are expanded out in |
|
823 // the operator parser |
|
824 switch (oper) { |
|
825 case OPER_ASSIGNADD: return DH_ADDGLOBALVAR; |
|
826 case OPER_ASSIGNSUB: return DH_SUBGLOBALVAR; |
|
827 case OPER_ASSIGNMUL: return DH_MULGLOBALVAR; |
|
828 case OPER_ASSIGNDIV: return DH_DIVGLOBALVAR; |
|
829 case OPER_ASSIGNMOD: return DH_MODGLOBALVAR; |
|
830 case OPER_ASSIGN: return DH_ASSIGNGLOBALVAR; |
|
831 default: error ("bad assignment operator!!\n"); |
|
832 } |
|
833 } |
|
834 |
|
835 switch (oper) { |
|
836 case OPER_ADD: return DH_ADD; |
|
837 case OPER_SUBTRACT: return DH_SUBTRACT; |
|
838 case OPER_MULTIPLY: return DH_MULTIPLY; |
|
839 case OPER_DIVIDE: return DH_DIVIDE; |
|
840 case OPER_MODULUS: return DH_MODULUS; |
|
841 case OPER_EQUALS: return DH_EQUALS; |
|
842 case OPER_NOTEQUALS: return DH_NOTEQUALS; |
|
843 case OPER_LESSTHAN: return DH_LESSTHAN; |
|
844 case OPER_GREATERTHAN: return DH_GREATERTHAN; |
|
845 case OPER_LESSTHANEQUALS: return DH_LESSTHANEQUALS; |
|
846 case OPER_GREATERTHANEQUALS: return DH_GREATERTHANEQUALS; |
|
847 case OPER_LEFTSHIFT: return DH_LSHIFT; |
|
848 case OPER_RIGHTSHIFT: return DH_RSHIFT; |
|
849 case OPER_OR: return DH_ORLOGICAL; |
|
850 case OPER_AND: return DH_ANDLOGICAL; |
|
851 case OPER_BITWISEOR: return DH_ORBITWISE; |
|
852 case OPER_BITWISEEOR: return DH_EORBITWISE; |
|
853 case OPER_BITWISEAND: return DH_ANDBITWISE; |
|
854 } |
|
855 |
|
856 error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper); |
|
857 return 0; |
|
858 } |
|
859 |
|
860 // ============================================================================ |
|
861 // Parses an expression, potentially recursively |
|
862 DataBuffer* ScriptReader::ParseExpression (type_e reqtype) { |
|
863 DataBuffer* retbuf = new DataBuffer (64); |
|
864 |
|
865 // Parse first operand |
|
866 retbuf->Merge (ParseExprValue (reqtype)); |
|
867 |
|
868 // Parse any and all operators we get |
|
869 int oper; |
|
870 while ((oper = ParseOperator (true)) != -1) { |
|
871 // We peeked the operator, move forward now |
|
872 Next (); |
|
873 |
|
874 // Can't be an assignement operator, those belong in assignments. |
|
875 if (IsAssignmentOperator (oper)) |
|
876 ParserError ("assignment operator inside expression"); |
|
877 |
|
878 // Parse the right operand. |
|
879 MustNext (); |
|
880 DataBuffer* rb = ParseExprValue (reqtype); |
|
881 |
|
882 if (oper == OPER_TERNARY) { |
|
883 // Ternary operator requires - naturally - a third operand. |
|
884 MustNext (":"); |
|
885 MustNext (); |
|
886 DataBuffer* tb = ParseExprValue (reqtype); |
|
887 |
|
888 // It also is handled differently: there isn't a dataheader for ternary |
|
889 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. |
|
890 // Behold, big block of writing madness! :P |
|
891 int mark1 = retbuf->AddMark (""); // start of "else" case |
|
892 int mark2 = retbuf->AddMark (""); // end of expression |
|
893 retbuf->Write (DH_IFNOTGOTO); // if the first operand (condition) |
|
894 retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1 |
|
895 retbuf->Merge (rb); // otherwise, perform second operand (true case) |
|
896 retbuf->Write (DH_GOTO); // afterwards, jump to the end, which is |
|
897 retbuf->AddMarkReference (mark2); // marked by mark2. |
|
898 retbuf->MoveMark (mark1); // move mark1 at the end of the true case |
|
899 retbuf->Merge (tb); // perform third operand (false case) |
|
900 retbuf->MoveMark (mark2); // move the ending mark2 here |
|
901 } else { |
|
902 // Write to buffer |
|
903 retbuf->Merge (rb); |
|
904 retbuf->Write (DataHeaderByOperator (NULL, oper)); |
|
905 } |
|
906 } |
|
907 |
|
908 return retbuf; |
|
909 } |
|
910 |
|
911 // ============================================================================ |
|
912 // Parses an operator string. Returns the operator number code. |
|
913 #define ISNEXT(char) (!PeekNext (peek ? 1 : 0) == char) |
|
914 int ScriptReader::ParseOperator (bool peek) { |
|
915 str oper; |
|
916 if (peek) |
|
917 oper += PeekNext (); |
|
918 else |
|
919 oper += token; |
|
920 |
|
921 if (-oper == "strlen") |
|
922 return OPER_STRLEN; |
|
923 |
|
924 // Check one-char operators |
|
925 bool equalsnext = ISNEXT ("="); |
|
926 |
|
927 int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN : |
|
928 (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN : |
|
929 (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN : |
|
930 (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND : |
|
931 (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR : |
|
932 (oper == "+" && !equalsnext) ? OPER_ADD : |
|
933 (oper == "-" && !equalsnext) ? OPER_SUBTRACT : |
|
934 (oper == "*" && !equalsnext) ? OPER_MULTIPLY : |
|
935 (oper == "/" && !equalsnext) ? OPER_DIVIDE : |
|
936 (oper == "%" && !equalsnext) ? OPER_MODULUS : |
|
937 (oper == "^") ? OPER_BITWISEEOR : |
|
938 (oper == "?") ? OPER_TERNARY : |
|
939 -1; |
|
940 |
|
941 if (o != -1) { |
|
942 return o; |
|
943 } |
|
944 |
|
945 // Two-char operators |
|
946 oper += PeekNext (peek ? 1 : 0); |
|
947 equalsnext = PeekNext (peek ? 2 : 1) == ("="); |
|
948 |
|
949 o = (oper == "+=") ? OPER_ASSIGNADD : |
|
950 (oper == "-=") ? OPER_ASSIGNSUB : |
|
951 (oper == "*=") ? OPER_ASSIGNMUL : |
|
952 (oper == "/=") ? OPER_ASSIGNDIV : |
|
953 (oper == "%=") ? OPER_ASSIGNMOD : |
|
954 (oper == "==") ? OPER_EQUALS : |
|
955 (oper == "!=") ? OPER_NOTEQUALS : |
|
956 (oper == ">=") ? OPER_GREATERTHANEQUALS : |
|
957 (oper == "<=") ? OPER_LESSTHANEQUALS : |
|
958 (oper == "&&") ? OPER_AND : |
|
959 (oper == "||") ? OPER_OR : |
|
960 (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT : |
|
961 (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT : |
|
962 -1; |
|
963 |
|
964 if (o != -1) { |
|
965 MustNext (); |
|
966 return o; |
|
967 } |
|
968 |
|
969 // Three-char opers |
|
970 oper += PeekNext (peek ? 2 : 1); |
|
971 o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT : |
|
972 oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT : |
|
973 -1; |
|
974 |
|
975 if (o != -1) { |
|
976 MustNext (); |
|
977 MustNext (); |
|
978 } |
|
979 |
|
980 return o; |
|
981 } |
|
982 |
|
983 // ============================================================================ |
|
984 str ScriptReader::ParseFloat () { |
|
985 MustNumber (true); |
|
986 str floatstring = token; |
|
987 |
|
988 // Go after the decimal point |
|
989 if (PeekNext () == ".") { |
|
990 Next ("."); |
|
991 MustNumber (false); |
|
992 floatstring += "."; |
|
993 floatstring += token; |
|
994 } |
|
995 |
|
996 return floatstring; |
|
997 } |
|
998 |
|
999 // ============================================================================ |
|
1000 // Parses a value in the expression and returns the data needed to push |
|
1001 // it, contained in a data buffer. A value can be either a variable, a command, |
|
1002 // a literal or an expression. |
|
1003 DataBuffer* ScriptReader::ParseExprValue (type_e reqtype) { |
|
1004 DataBuffer* b = new DataBuffer(16); |
|
1005 |
|
1006 ScriptVar* g; |
|
1007 |
|
1008 // Prefixing "!" means negation. |
|
1009 bool negate = (token == "!"); |
|
1010 if (negate) // Jump past the "!" |
|
1011 Next (); |
|
1012 |
|
1013 // Handle strlen |
|
1014 if (token == "strlen") { |
|
1015 MustNext ("("); |
|
1016 MustNext (); |
|
1017 |
|
1018 // By this token we should get a string constant. |
|
1019 constinfo_t* constant = FindConstant (token); |
|
1020 if (!constant || constant->type != TYPE_STRING) |
|
1021 ParserError ("strlen only works with const str"); |
|
1022 |
|
1023 if (reqtype != TYPE_INT) |
|
1024 ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); |
|
1025 |
|
1026 b->Write (DH_PUSHNUMBER); |
|
1027 b->Write (constant->val.len ()); |
|
1028 |
|
1029 MustNext (")"); |
|
1030 } else if (token == "(") { |
|
1031 // Expression |
|
1032 MustNext (); |
|
1033 DataBuffer* c = ParseExpression (reqtype); |
|
1034 b->Merge (c); |
|
1035 MustNext (")"); |
|
1036 } else if (CommandDef* comm = FindCommand (token)) { |
|
1037 delete b; |
|
1038 |
|
1039 // Command |
|
1040 if (reqtype && comm->returnvalue != reqtype) |
|
1041 ParserError ("%s returns an incompatible data type", comm->name.chars()); |
|
1042 b = ParseCommand (comm); |
|
1043 } else if (constinfo_t* constant = FindConstant (token)) { |
|
1044 // Type check |
|
1045 if (reqtype != constant->type) |
|
1046 ParserError ("constant `%s` is %s, expression requires %s\n", |
|
1047 (char*)constant->name, (char*)GetTypeName (constant->type), |
|
1048 (char*)GetTypeName (reqtype)); |
|
1049 |
|
1050 switch (constant->type) { |
|
1051 case TYPE_BOOL: |
|
1052 case TYPE_INT: |
|
1053 b->Write (DH_PUSHNUMBER); |
|
1054 b->Write (atoi (constant->val)); |
|
1055 break; |
|
1056 case TYPE_STRING: |
|
1057 b->WriteString (constant->val); |
|
1058 break; |
|
1059 case TYPE_VOID: |
|
1060 case TYPE_UNKNOWN: |
|
1061 break; |
|
1062 } |
|
1063 } else if ((g = FindGlobalVariable (token))) { |
|
1064 // Global variable |
|
1065 b->Write (DH_PUSHGLOBALVAR); |
|
1066 b->Write (g->index); |
|
1067 } else { |
|
1068 // If nothing else, check for literal |
|
1069 switch (reqtype) { |
|
1070 case TYPE_VOID: |
|
1071 case TYPE_UNKNOWN: |
|
1072 ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); |
|
1073 break; |
|
1074 case TYPE_BOOL: |
|
1075 case TYPE_INT: { |
|
1076 MustNumber (true); |
|
1077 |
|
1078 // All values are written unsigned - thus we need to write the value's |
|
1079 // absolute value, followed by an unary minus for negatives. |
|
1080 b->Write (DH_PUSHNUMBER); |
|
1081 |
|
1082 long v = atol (token); |
|
1083 b->Write (static_cast<word> (abs (v))); |
|
1084 if (v < 0) |
|
1085 b->Write (DH_UNARYMINUS); |
|
1086 break; |
|
1087 } |
|
1088 case TYPE_STRING: |
|
1089 // PushToStringTable either returns the string index of the |
|
1090 // string if it finds it in the table, or writes it to the |
|
1091 // table and returns it index if it doesn't find it there. |
|
1092 MustString (true); |
|
1093 b->WriteString (token); |
|
1094 break; |
|
1095 } |
|
1096 } |
|
1097 |
|
1098 // Negate it now if desired |
|
1099 if (negate) |
|
1100 b->Write (DH_NEGATELOGICAL); |
|
1101 |
|
1102 return b; |
|
1103 } |
|
1104 |
|
1105 // ============================================================================ |
|
1106 // Parses an assignment. An assignment starts with a variable name, followed |
|
1107 // by an assignment operator, followed by an expression value. Expects current |
|
1108 // token to be the name of the variable, and expects the variable to be given. |
|
1109 DataBuffer* ScriptReader::ParseAssignment (ScriptVar* var) { |
|
1110 bool global = !var->statename.len (); |
|
1111 |
|
1112 // Get an operator |
|
1113 MustNext (); |
|
1114 int oper = ParseOperator (); |
|
1115 if (!IsAssignmentOperator (oper)) |
|
1116 ParserError ("expected assignment operator"); |
|
1117 |
|
1118 if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction |
|
1119 ParserError ("can't alter variables at top level"); |
|
1120 |
|
1121 // Parse the right operand |
|
1122 MustNext (); |
|
1123 DataBuffer* retbuf = new DataBuffer; |
|
1124 DataBuffer* expr = ParseExpression (var->type); |
|
1125 |
|
1126 // <<= and >>= do not have data headers. Solution: expand them. |
|
1127 // a <<= b -> a = a << b |
|
1128 // a >>= b -> a = a >> b |
|
1129 if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) { |
|
1130 retbuf->Write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR); |
|
1131 retbuf->Write (var->index); |
|
1132 retbuf->Merge (expr); |
|
1133 retbuf->Write ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT); |
|
1134 retbuf->Write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR); |
|
1135 retbuf->Write (var->index); |
|
1136 } else { |
|
1137 retbuf->Merge (expr); |
|
1138 long dh = DataHeaderByOperator (var, oper); |
|
1139 retbuf->Write (dh); |
|
1140 retbuf->Write (var->index); |
|
1141 } |
|
1142 |
|
1143 return retbuf; |
|
1144 } |
|
1145 |
|
1146 void ScriptReader::PushScope () { |
|
1147 g_ScopeCursor++; |
|
1148 if (g_ScopeCursor >= MAX_SCOPE) |
|
1149 ParserError ("too deep scope"); |
|
1150 |
|
1151 ScopeInfo* info = &SCOPE(0); |
|
1152 info->type = SCOPETYPE_UNKNOWN; |
|
1153 info->mark1 = 0; |
|
1154 info->mark2 = 0; |
|
1155 info->buffer1 = NULL; |
|
1156 info->casecursor = -1; |
|
1157 for (int i = 0; i < MAX_CASE; i++) { |
|
1158 info->casemarks[i] = MAX_MARKS; |
|
1159 info->casebuffers[i] = NULL; |
|
1160 info->casenumbers[i] = -1; |
|
1161 } |
|
1162 } |
|
1163 |
|
1164 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
|
1165 if (FindConstant (token)) // There should not be constants here. |
|
1166 ParserError ("invalid use for constant\n"); |
|
1167 |
|
1168 // If it's a variable, expect assignment. |
|
1169 if (ScriptVar* var = FindGlobalVariable (token)) |
|
1170 return ParseAssignment (var); |
|
1171 |
|
1172 return NULL; |
|
1173 } |
|
1174 |
|
1175 void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { |
|
1176 ScopeInfo* info = &SCOPE(0); |
|
1177 |
|
1178 info->casecursor++; |
|
1179 if (info->casecursor >= MAX_CASE) |
|
1180 ParserError ("too many cases in one switch"); |
|
1181 |
|
1182 // Init a mark for the case buffer |
|
1183 int m = w->AddMark (""); |
|
1184 info->casemarks[info->casecursor] = m; |
|
1185 |
|
1186 // Add a reference to the mark. "case" and "default" both |
|
1187 // add the necessary bytecode before the reference. |
|
1188 if (b) |
|
1189 b->AddMarkReference (m); |
|
1190 else |
|
1191 w->AddReference (m); |
|
1192 |
|
1193 // Init a buffer for the case block and tell the object |
|
1194 // writer to record all written data to it. |
|
1195 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
|
1196 } |
|
1197 |
|
1198 constinfo_t* FindConstant (str token) { |
|
1199 for (uint i = 0; i < g_ConstInfo.size(); i++) |
|
1200 if (g_ConstInfo[i].name == token) |
|
1201 return &g_ConstInfo[i]; |
|
1202 return NULL; |
|
1203 } |
|