| 67 bool g_GotMainLoop = false; |
67 bool g_GotMainLoop = false; |
| 68 unsigned int g_ScopeCursor = 0; |
68 unsigned int g_ScopeCursor = 0; |
| 69 DataBuffer* g_IfExpression = NULL; |
69 DataBuffer* g_IfExpression = NULL; |
| 70 bool g_CanElse = false; |
70 bool g_CanElse = false; |
| 71 str* g_UndefinedLabels[MAX_MARKS]; |
71 str* g_UndefinedLabels[MAX_MARKS]; |
| |
72 bool g_Neurosphere = false; // neurosphere-compat |
| 72 |
73 |
| 73 // ============================================================================ |
74 // ============================================================================ |
| 74 // Main parser code. Begins read of the script file, checks the syntax of it |
75 // Main parser code. Begins read of the script file, checks the syntax of it |
| 75 // and writes the data to the object file via ObjWriter - which also takes care |
76 // and writes the data to the object file via ObjWriter - which also takes care |
| 76 // of necessary buffering so stuff is written in the correct order. |
77 // of necessary buffering so stuff is written in the correct order. |
| 82 for (int i = 0; i < MAX_MARKS; i++) |
83 for (int i = 0; i < MAX_MARKS; i++) |
| 83 g_UndefinedLabels[i] = NULL; |
84 g_UndefinedLabels[i] = NULL; |
| 84 |
85 |
| 85 while (Next()) { |
86 while (Next()) { |
| 86 // Check if else is potentically valid |
87 // Check if else is potentically valid |
| 87 if (!token.compare ("else") && !g_CanElse) |
88 if (token == "else" && !g_CanElse) |
| 88 ParserError ("else without preceding if"); |
89 ParserError ("else without preceding if"); |
| 89 if (token.compare ("else") != 0) |
90 if (token != "else") |
| 90 g_CanElse = false; |
91 g_CanElse = false; |
| 91 |
92 |
| 92 // ============================================================ |
93 // ============================================================ |
| 93 if (!token.compare ("state")) { |
94 if (token == "state") { |
| 94 MUST_TOPLEVEL |
95 MUST_TOPLEVEL |
| 95 |
96 |
| 96 MustString (); |
97 MustString (); |
| 97 |
98 |
| 98 // State name must be a word. |
99 // State name must be a word. |
| 144 g_NumEvents++; |
145 g_NumEvents++; |
| 145 continue; |
146 continue; |
| 146 } |
147 } |
| 147 |
148 |
| 148 // ============================================================ |
149 // ============================================================ |
| 149 if (!token.compare ("mainloop")) { |
150 if (token == "mainloop") { |
| 150 MUST_TOPLEVEL |
151 MUST_TOPLEVEL |
| 151 MustNext ("{"); |
152 MustNext ("{"); |
| 152 |
153 |
| 153 // Mode must be set before dataheader is written here! |
154 // Mode must be set before dataheader is written here! |
| 154 g_CurMode = MODE_MAINLOOP; |
155 g_CurMode = MODE_MAINLOOP; |
| 155 w->Write (DH_MAINLOOP); |
156 w->Write (DH_MAINLOOP); |
| 156 continue; |
157 continue; |
| 157 } |
158 } |
| 158 |
159 |
| 159 // ============================================================ |
160 // ============================================================ |
| 160 if (!token.compare ("onenter") || !token.compare ("onexit")) { |
161 if (token == "onenter" || token == "onexit") { |
| 161 MUST_TOPLEVEL |
162 MUST_TOPLEVEL |
| 162 bool onenter = !token.compare ("onenter"); |
163 bool onenter = token == "onenter"; |
| 163 MustNext ("{"); |
164 MustNext ("{"); |
| 164 |
165 |
| 165 // Mode must be set before dataheader is written here, |
166 // Mode must be set before dataheader is written here, |
| 166 // because onenter goes to a separate buffer. |
167 // because onenter goes to a separate buffer. |
| 167 g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; |
168 g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; |
| 168 w->Write (onenter ? DH_ONENTER : DH_ONEXIT); |
169 w->Write (onenter ? DH_ONENTER : DH_ONEXIT); |
| 169 continue; |
170 continue; |
| 170 } |
171 } |
| 171 |
172 |
| 172 // ============================================================ |
173 // ============================================================ |
| 173 if (!token.compare ("var")) { |
174 if (token == "var") { |
| 174 // For now, only globals are supported |
175 // For now, only globals are supported |
| 175 if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) |
176 if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) |
| 176 ParserError ("variables must only be global for now"); |
177 ParserError ("variables must only be global for now"); |
| 177 |
178 |
| 178 MustNext (); |
179 MustNext (); |
| 274 continue; |
275 continue; |
| 275 } |
276 } |
| 276 |
277 |
| 277 // ============================================================ |
278 // ============================================================ |
| 278 // While |
279 // While |
| 279 if (!token.compare ("while")) { |
280 if (token == "while") { |
| 280 MUST_NOT_TOPLEVEL |
281 MUST_NOT_TOPLEVEL |
| 281 PushScope (); |
282 PushScope (); |
| 282 |
283 |
| 283 // While loops need two marks - one at the start of the loop and one at the |
284 // While loops need two marks - one at the start of the loop and one at the |
| 284 // end. The condition is checked at the very start of the loop, if it fails, |
285 // end. The condition is checked at the very start of the loop, if it fails, |
| 351 continue; |
352 continue; |
| 352 } |
353 } |
| 353 |
354 |
| 354 // ============================================================ |
355 // ============================================================ |
| 355 // Do/while loop |
356 // Do/while loop |
| 356 if (!token.compare ("do")) { |
357 if (token == "do") { |
| 357 MUST_NOT_TOPLEVEL |
358 MUST_NOT_TOPLEVEL |
| 358 PushScope (); |
359 PushScope (); |
| 359 MustNext ("{"); |
360 MustNext ("{"); |
| 360 SCOPE(0).mark1 = w->AddMark (""); |
361 SCOPE(0).mark1 = w->AddMark (""); |
| 361 SCOPE(0).type = SCOPETYPE_DO; |
362 SCOPE(0).type = SCOPETYPE_DO; |
| 362 continue; |
363 continue; |
| 363 } |
364 } |
| 364 |
365 |
| 365 // ============================================================ |
366 // ============================================================ |
| 366 // Switch |
367 // Switch |
| 367 if (!token.compare ("switch")) { |
368 if (token == "switch") { |
| 368 /* This goes a bit tricky. switch is structured in the |
369 /* This goes a bit tricky. switch is structured in the |
| 369 * bytecode followingly: |
370 * bytecode followingly: |
| 370 * (expression) |
371 * (expression) |
| 371 * case a: goto casemark1 |
372 * case a: goto casemark1 |
| 372 * case b: goto casemark2 |
373 * case b: goto casemark2 |
| 390 SCOPE(0).buffer1 = NULL; // default header |
391 SCOPE(0).buffer1 = NULL; // default header |
| 391 continue; |
392 continue; |
| 392 } |
393 } |
| 393 |
394 |
| 394 // ============================================================ |
395 // ============================================================ |
| 395 if (!token.compare ("case")) { |
396 if (token == "case") { |
| 396 // case is only allowed inside switch |
397 // case is only allowed inside switch |
| 397 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
398 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
| 398 ParserError ("case label outside switch"); |
399 ParserError ("case label outside switch"); |
| 399 |
400 |
| 400 // Get the literal (Zandronum does not support expressions here) |
401 // Get the literal (Zandronum does not support expressions here) |
| 420 AddSwitchCase (w, NULL); |
421 AddSwitchCase (w, NULL); |
| 421 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
422 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
| 422 continue; |
423 continue; |
| 423 } |
424 } |
| 424 |
425 |
| 425 if (!token.compare ("default")) { |
426 if (token == "default") { |
| 426 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
427 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
| 427 ParserError ("default label outside switch"); |
428 ParserError ("default label outside switch"); |
| 428 |
429 |
| 429 if (SCOPE(0).buffer1) |
430 if (SCOPE(0).buffer1) |
| 430 ParserError ("multiple default labels in one switch"); |
431 ParserError ("multiple default labels in one switch"); |
| 637 if (!PeekNext().compare (";")) |
643 if (!PeekNext().compare (";")) |
| 638 MustNext (";"); |
644 MustNext (";"); |
| 639 continue; |
645 continue; |
| 640 } |
646 } |
| 641 |
647 |
| 642 // ============================================================ |
648 // Check if it's a command |
| 643 // If nothing else, parse it as a statement (which is either |
649 CommandDef* comm = FindCommand (token); |
| 644 // assignment or expression) |
650 if (comm) { |
| |
651 w->GetCurrentBuffer()->Merge (ParseCommand (comm)); |
| |
652 MustNext (";"); |
| |
653 continue; |
| |
654 } |
| |
655 |
| |
656 // ============================================================ |
| |
657 // If nothing else, parse it as a statement |
| 645 DataBuffer* b = ParseStatement (w); |
658 DataBuffer* b = ParseStatement (w); |
| 646 w->WriteBuffer (b); |
659 w->WriteBuffer (b); |
| 647 MustNext (";"); |
660 MustNext (";"); |
| 648 } |
661 } |
| 649 |
662 |
| |
663 // =============================================================================== |
| |
664 // Script file ended. Do some last checks and write the last things to main buffer |
| 650 if (g_CurMode != MODE_TOPLEVEL) |
665 if (g_CurMode != MODE_TOPLEVEL) |
| 651 ParserError ("script did not end at top level; did you forget a `}`?"); |
666 ParserError ("script did not end at top level; did you forget a `}`?"); |
| 652 |
667 |
| 653 // stateSpawn must be defined! |
668 // stateSpawn must be defined! |
| 654 if (!g_stateSpawnDefined) |
669 if (!g_stateSpawnDefined) |
| 815 MustNext (); |
830 MustNext (); |
| 816 DataBuffer* tb = ParseExprValue (reqtype); |
831 DataBuffer* tb = ParseExprValue (reqtype); |
| 817 |
832 |
| 818 // It also is handled differently: there isn't a dataheader for ternary |
833 // It also is handled differently: there isn't a dataheader for ternary |
| 819 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. |
834 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. |
| |
835 // Behold, big block of writing madness! :P |
| 820 int mark1 = retbuf->AddMark (""); // start of "else" case |
836 int mark1 = retbuf->AddMark (""); // start of "else" case |
| 821 int mark2 = retbuf->AddMark (""); // end of expression |
837 int mark2 = retbuf->AddMark (""); // end of expression |
| 822 retbuf->Write<word> (DH_IFNOTGOTO); |
838 retbuf->Write<word> (DH_IFNOTGOTO); // if the first operand (condition) |
| 823 retbuf->AddMarkReference (mark1); |
839 retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1 |
| 824 retbuf->Merge (rb); |
840 retbuf->Merge (rb); // otherwise, perform second operand (true case) |
| 825 retbuf->Write<word> (DH_GOTO); |
841 retbuf->Write<word> (DH_GOTO); // afterwards, jump to the end, which is |
| 826 retbuf->AddMarkReference (mark2); |
842 retbuf->AddMarkReference (mark2); // marked by mark2. |
| 827 retbuf->MoveMark (mark1); // start of "else" case |
843 retbuf->MoveMark (mark1); // move mark1 at the end of the true case |
| 828 retbuf->Merge (tb); |
844 retbuf->Merge (tb); // perform third operand (false case) |
| 829 retbuf->Write<word> (DH_GOTO); |
845 retbuf->MoveMark (mark2); // move the ending mark2 here |
| 830 retbuf->AddMarkReference (mark2); |
|
| 831 retbuf->MoveMark (mark2); // move endmark to end |
|
| 832 } else { |
846 } else { |
| 833 // Write to buffer |
847 // Write to buffer |
| 834 retbuf->Merge (rb); |
848 retbuf->Merge (rb); |
| 835 retbuf->Write<word> (DataHeaderByOperator (NULL, oper)); |
849 retbuf->Write<word> (DataHeaderByOperator (NULL, oper)); |
| 836 } |
850 } |
| 1051 g_ScopeCursor++; |
1065 g_ScopeCursor++; |
| 1052 if (g_ScopeCursor >= MAX_SCOPE) |
1066 if (g_ScopeCursor >= MAX_SCOPE) |
| 1053 ParserError ("too deep scope"); |
1067 ParserError ("too deep scope"); |
| 1054 |
1068 |
| 1055 ScopeInfo* info = &SCOPE(0); |
1069 ScopeInfo* info = &SCOPE(0); |
| 1056 info->type = 0; |
1070 info->type = SCOPETYPE_UNKNOWN; |
| 1057 info->mark1 = 0; |
1071 info->mark1 = 0; |
| 1058 info->mark2 = 0; |
1072 info->mark2 = 0; |
| 1059 info->buffer1 = NULL; |
1073 info->buffer1 = NULL; |
| 1060 info->casecursor = -1; |
1074 info->casecursor = -1; |
| 1061 for (int i = 0; i < MAX_CASE; i++) { |
1075 for (int i = 0; i < MAX_CASE; i++) { |