src/Parser.cc

changeset 119
bdf8d46c145f
parent 118
e3361cf7cbf4
child 120
5ea0faefa82a
equal deleted inserted replaced
118:e3361cf7cbf4 119:bdf8d46c145f
1 /*
2 Copyright 2012-2014 Santeri Piippo
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "Parser.h"
30 #include "Events.h"
31 #include "Commands.h"
32 #include "StringTable.h"
33 #include "Containers.h"
34 #include "Lexer.h"
35 #include "DataBuffer.h"
36 #include "Expression.h"
37
38 #define SCOPE(n) (m_scopeStack[m_scopeCursor - n])
39
40 static const StringList g_validZandronumVersions = {"1.2", "1.3", "2.0"};
41
42 // ============================================================================
43 //
44 BotscriptParser::BotscriptParser() :
45 m_isReadOnly (false),
46 m_mainBuffer (new DataBuffer),
47 m_onenterBuffer (new DataBuffer),
48 m_mainLoopBuffer (new DataBuffer),
49 m_lexer (new Lexer),
50 m_numStates (0),
51 m_numEvents (0),
52 m_currentMode (PARSERMODE_TopLevel),
53 m_isStateSpawnDefined (false),
54 m_gotMainLoop (false),
55 m_scopeCursor (-1),
56 m_isElseAllowed (false),
57 m_highestGlobalVarIndex (0),
58 m_highestStateVarIndex (0),
59 m_zandronumVersion (10200), // 1.2
60 m_defaultZandronumVersion (true) {}
61
62 // ============================================================================
63 //
64 BotscriptParser::~BotscriptParser()
65 {
66 delete m_lexer;
67 }
68
69 // ============================================================================
70 //
71 void BotscriptParser::checkToplevel()
72 {
73 if (m_currentMode != PARSERMODE_TopLevel)
74 error ("%1-statements may only be defined at top level!", getTokenString());
75 }
76
77 // ============================================================================
78 //
79 void BotscriptParser::checkNotToplevel()
80 {
81 if (m_currentMode == PARSERMODE_TopLevel)
82 error ("%1-statements must not be defined at top level!", getTokenString());
83 }
84
85 // ============================================================================
86 //
87 // Main compiler code. Begins read of the script file, checks the syntax of it
88 // and writes the data to the object file via Objwriter - which also takes care
89 // of necessary buffering so stuff is written in the correct order.
90 //
91 void BotscriptParser::parseBotscript (String fileName)
92 {
93 // Lex and preprocess the file
94 m_lexer->processFile (fileName);
95 pushScope();
96
97 while (m_lexer->next())
98 {
99 // Check if else is potentically valid
100 if (tokenIs (TK_Else) && m_isElseAllowed == false)
101 error ("else without preceding if");
102
103 if (tokenIs (TK_Else) == false)
104 m_isElseAllowed = false;
105
106 switch (m_lexer->token()->type)
107 {
108 case TK_State:
109 parseStateBlock();
110 break;
111
112 case TK_Event:
113 parseEventBlock();
114 break;
115
116 case TK_Mainloop:
117 parseMainloop();
118 break;
119
120 case TK_Onenter:
121 case TK_Onexit:
122 parseOnEnterExit();
123 break;
124
125 case TK_Var:
126 parseVar();
127 break;
128
129 case TK_If:
130 parseIf();
131 break;
132
133 case TK_Else:
134 parseElse();
135 break;
136
137 case TK_While:
138 parseWhileBlock();
139 break;
140
141 case TK_For:
142 parseForBlock();
143 break;
144
145 case TK_Do:
146 parseDoBlock();
147 break;
148
149 case TK_Switch:
150 parseSwitchBlock();
151 break;
152
153 case TK_Case:
154 parseSwitchCase();
155 break;
156
157 case TK_Default:
158 parseSwitchDefault();
159 break;
160
161 case TK_Break:
162 parseBreak();
163 break;
164
165 case TK_Continue:
166 parseContinue();
167 break;
168
169 case TK_BraceEnd:
170 parseBlockEnd();
171 break;
172
173 case TK_Eventdef:
174 parseEventdef();
175 break;
176
177 case TK_Funcdef:
178 parseFuncdef();
179 break;
180
181 case TK_Semicolon:
182 break;
183
184 case TK_Using:
185 parseUsing();
186 break;
187
188 default:
189 {
190 // Check if it's a command
191 CommandInfo* comm = findCommandByName (getTokenString());
192
193 if (comm)
194 {
195 currentBuffer()->mergeAndDestroy (parseCommand (comm));
196 m_lexer->mustGetNext (TK_Semicolon);
197 continue;
198 }
199
200 // If nothing else, parse it as a statement
201 m_lexer->skip (-1);
202 DataBuffer* b = parseStatement();
203
204 if (b == false)
205 {
206 m_lexer->next();
207 error ("unknown token `%1`", getTokenString());
208 }
209
210 currentBuffer()->mergeAndDestroy (b);
211 m_lexer->mustGetNext (TK_Semicolon);
212 break;
213 }
214 }
215 }
216
217 // ===============================================================================
218 // Script file ended. Do some last checks and write the last things to main buffer
219 if (m_currentMode != PARSERMODE_TopLevel)
220 error ("script did not end at top level; a `}` is missing somewhere");
221
222 if (isReadOnly() == false)
223 {
224 // stateSpawn must be defined!
225 if (m_isStateSpawnDefined == false)
226 error ("script must have a state named `stateSpawn`!");
227
228 if (m_defaultZandronumVersion)
229 {
230 print ("\n");
231 print ("note: use the 'using' directive to define a target Zandronum version\n");
232 print ("usage: using zandronum <version>, possible versions: %1\n", g_validZandronumVersions);
233 print ("\n");
234 }
235
236 // Dump the last state's onenter and mainloop
237 writeMemberBuffers();
238
239 // String table
240 writeStringTable();
241 }
242 }
243
244 // ============================================================================
245 //
246 void BotscriptParser::parseStateBlock()
247 {
248 checkToplevel();
249 m_lexer->mustGetNext (TK_String);
250 String statename = getTokenString();
251
252 // State name must be a word.
253 if (statename.firstIndexOf (" ") != -1)
254 error ("state name must be a single word, got `%1`", statename);
255
256 // stateSpawn is special - it *must* be defined. If we
257 // encountered it, then mark down that we have it.
258 if (statename.toLowercase() == "statespawn")
259 m_isStateSpawnDefined = true;
260
261 // Must end in a colon
262 m_lexer->mustGetNext (TK_Colon);
263
264 // write the previous state's onenter and
265 // mainloop buffers to file now
266 if (m_currentState.isEmpty() == false)
267 writeMemberBuffers();
268
269 currentBuffer()->writeDWord (DH_StateName);
270 currentBuffer()->writeString (statename);
271 currentBuffer()->writeDWord (DH_StateIndex);
272 currentBuffer()->writeDWord (m_numStates);
273
274 m_numStates++;
275 m_currentState = statename;
276 m_gotMainLoop = false;
277 }
278
279 // ============================================================================
280 //
281 void BotscriptParser::parseEventBlock()
282 {
283 checkToplevel();
284 m_lexer->mustGetNext (TK_String);
285
286 EventDefinition* e = findEventByName (getTokenString());
287
288 if (e == null)
289 error ("bad event, got `%1`\n", getTokenString());
290
291 m_lexer->mustGetNext (TK_BraceStart);
292 m_currentMode = PARSERMODE_Event;
293 currentBuffer()->writeDWord (DH_Event);
294 currentBuffer()->writeDWord (e->number);
295 m_numEvents++;
296 }
297
298 // ============================================================================
299 //
300 void BotscriptParser::parseMainloop()
301 {
302 checkToplevel();
303 m_lexer->mustGetNext (TK_BraceStart);
304
305 m_currentMode = PARSERMODE_MainLoop;
306 m_mainLoopBuffer->writeDWord (DH_MainLoop);
307 }
308
309 // ============================================================================
310 //
311 void BotscriptParser::parseOnEnterExit()
312 {
313 checkToplevel();
314 bool onenter = (tokenIs (TK_Onenter));
315 m_lexer->mustGetNext (TK_BraceStart);
316
317 m_currentMode = onenter ? PARSERMODE_Onenter : PARSERMODE_Onexit;
318 currentBuffer()->writeDWord (onenter ? DH_OnEnter : DH_OnExit);
319 }
320
321 // ============================================================================
322 //
323 void BotscriptParser::parseVar()
324 {
325 Variable* var = new Variable;
326 var->origin = m_lexer->describeCurrentPosition();
327 var->isarray = false;
328 const bool isconst = m_lexer->next (TK_Const);
329 m_lexer->mustGetAnyOf ({TK_Int,TK_Str,TK_Void});
330
331 DataType vartype = (tokenIs (TK_Int)) ? TYPE_Int :
332 (tokenIs (TK_Str)) ? TYPE_String :
333 TYPE_Bool;
334
335 m_lexer->mustGetNext (TK_DollarSign);
336 m_lexer->mustGetNext (TK_Symbol);
337 String name = getTokenString();
338
339 if (m_lexer->next (TK_BracketStart))
340 {
341 m_lexer->mustGetNext (TK_BracketEnd);
342 var->isarray = true;
343
344 if (isconst)
345 error ("arrays cannot be const");
346 }
347
348 for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
349 {
350 if (var->name == name)
351 error ("Variable $%1 is already declared on this scope; declared at %2",
352 var->name, var->origin);
353 }
354
355 var->name = name;
356 var->statename = "";
357 var->type = vartype;
358
359 if (isconst == false)
360 {
361 var->writelevel = WRITE_Mutable;
362 }
363 else
364 {
365 m_lexer->mustGetNext (TK_Assign);
366 Expression expr (this, m_lexer, vartype);
367
368 // If the expression was constexpr, we know its value and thus
369 // can store it in the variable.
370 if (expr.getResult()->isConstexpr())
371 {
372 var->writelevel = WRITE_Constexpr;
373 var->value = expr.getResult()->value();
374 }
375 else
376 {
377 // TODO: might need a VM-wise oninit for this...
378 error ("const variables must be constexpr");
379 }
380 }
381
382 // Assign an index for the variable if it is not constexpr. Constexpr
383 // variables can simply be substituted out for their value when used
384 // so they need no index.
385 if (var->writelevel != WRITE_Constexpr)
386 {
387 bool isglobal = isInGlobalState();
388 var->index = isglobal ? SCOPE(0).globalVarIndexBase++ : SCOPE(0).localVarIndexBase++;
389
390 if ((isglobal == true && var->index >= gMaxGlobalVars) ||
391 (isglobal == false && var->index >= gMaxStateVars))
392 {
393 error ("too many %1 variables", isglobal ? "global" : "state-local");
394 }
395 }
396
397 if (isInGlobalState())
398 SCOPE(0).globalVariables << var;
399 else
400 SCOPE(0).localVariables << var;
401
402 suggestHighestVarIndex (isInGlobalState(), var->index);
403 m_lexer->mustGetNext (TK_Semicolon);
404 print ("Declared %3 variable #%1 $%2\n", var->index, var->name, isInGlobalState() ? "global" : "state-local");
405 }
406
407 // ============================================================================
408 //
409 void BotscriptParser::parseIf()
410 {
411 checkNotToplevel();
412 pushScope();
413
414 // Condition
415 m_lexer->mustGetNext (TK_ParenStart);
416
417 // Read the expression and write it.
418 DataBuffer* c = parseExpression (TYPE_Int);
419 currentBuffer()->mergeAndDestroy (c);
420
421 m_lexer->mustGetNext (TK_ParenEnd);
422 m_lexer->mustGetNext (TK_BraceStart);
423
424 // Add a mark - to here temporarily - and add a reference to it.
425 // Upon a closing brace, the mark will be adjusted.
426 ByteMark* mark = currentBuffer()->addMark ("");
427
428 // Use DH_IfNotGoto - if the expression is not true, we goto the mark
429 // we just defined - and this mark will be at the end of the scope block.
430 currentBuffer()->writeDWord (DH_IfNotGoto);
431 currentBuffer()->addReference (mark);
432
433 // Store it
434 SCOPE (0).mark1 = mark;
435 SCOPE (0).type = SCOPE_If;
436 }
437
438 // ============================================================================
439 //
440 void BotscriptParser::parseElse()
441 {
442 checkNotToplevel();
443 m_lexer->mustGetNext (TK_BraceStart);
444 pushScope (eNoReset);
445
446 if (SCOPE (0).type != SCOPE_If)
447 error ("else without preceding if");
448
449 // write down to jump to the end of the else statement
450 // Otherwise we have fall-throughs
451 SCOPE (0).mark2 = currentBuffer()->addMark ("");
452
453 // Instruction to jump to the end after if block is complete
454 currentBuffer()->writeDWord (DH_Goto);
455 currentBuffer()->addReference (SCOPE (0).mark2);
456
457 // Move the ifnot mark here and set type to else
458 currentBuffer()->adjustMark (SCOPE (0).mark1);
459 SCOPE (0).type = SCOPE_Else;
460 }
461
462 // ============================================================================
463 //
464 void BotscriptParser::parseWhileBlock()
465 {
466 checkNotToplevel();
467 pushScope();
468
469 // While loops need two marks - one at the start of the loop and one at the
470 // end. The condition is checked at the very start of the loop, if it fails,
471 // we use goto to skip to the end of the loop. At the end, we loop back to
472 // the beginning with a go-to statement.
473 ByteMark* mark1 = currentBuffer()->addMark (""); // start
474 ByteMark* mark2 = currentBuffer()->addMark (""); // end
475
476 // Condition
477 m_lexer->mustGetNext (TK_ParenStart);
478 DataBuffer* expr = parseExpression (TYPE_Int);
479 m_lexer->mustGetNext (TK_ParenEnd);
480 m_lexer->mustGetNext (TK_BraceStart);
481
482 // write condition
483 currentBuffer()->mergeAndDestroy (expr);
484
485 // Instruction to go to the end if it fails
486 currentBuffer()->writeDWord (DH_IfNotGoto);
487 currentBuffer()->addReference (mark2);
488
489 // Store the needed stuff
490 SCOPE (0).mark1 = mark1;
491 SCOPE (0).mark2 = mark2;
492 SCOPE (0).type = SCOPE_While;
493 }
494
495 // ============================================================================
496 //
497 void BotscriptParser::parseForBlock()
498 {
499 checkNotToplevel();
500 pushScope();
501
502 // Initializer
503 m_lexer->mustGetNext (TK_ParenStart);
504 DataBuffer* init = parseStatement();
505
506 if (init == null)
507 error ("bad statement for initializer of for");
508
509 m_lexer->mustGetNext (TK_Semicolon);
510
511 // Condition
512 DataBuffer* cond = parseExpression (TYPE_Int);
513
514 if (cond == null)
515 error ("bad statement for condition of for");
516
517 m_lexer->mustGetNext (TK_Semicolon);
518
519 // Incrementor
520 DataBuffer* incr = parseStatement();
521
522 if (incr == null)
523 error ("bad statement for incrementor of for");
524
525 m_lexer->mustGetNext (TK_ParenEnd);
526 m_lexer->mustGetNext (TK_BraceStart);
527
528 // First, write out the initializer
529 currentBuffer()->mergeAndDestroy (init);
530
531 // Init two marks
532 ByteMark* mark1 = currentBuffer()->addMark ("");
533 ByteMark* mark2 = currentBuffer()->addMark ("");
534
535 // Add the condition
536 currentBuffer()->mergeAndDestroy (cond);
537 currentBuffer()->writeDWord (DH_IfNotGoto);
538 currentBuffer()->addReference (mark2);
539
540 // Store the marks and incrementor
541 SCOPE (0).mark1 = mark1;
542 SCOPE (0).mark2 = mark2;
543 SCOPE (0).buffer1 = incr;
544 SCOPE (0).type = SCOPE_For;
545 }
546
547 // ============================================================================
548 //
549 void BotscriptParser::parseDoBlock()
550 {
551 checkNotToplevel();
552 pushScope();
553 m_lexer->mustGetNext (TK_BraceStart);
554 SCOPE (0).mark1 = currentBuffer()->addMark ("");
555 SCOPE (0).type = SCOPE_Do;
556 }
557
558 // ============================================================================
559 //
560 void BotscriptParser::parseSwitchBlock()
561 {
562 // This gets a bit tricky. switch is structured in the
563 // bytecode followingly:
564 //
565 // (expression)
566 // case a: goto casemark1
567 // case b: goto casemark2
568 // case c: goto casemark3
569 // goto mark1 // jump to end if no matches
570 // casemark1: ...
571 // casemark2: ...
572 // casemark3: ...
573 // mark1: // end mark
574
575 checkNotToplevel();
576 pushScope();
577 m_lexer->mustGetNext (TK_ParenStart);
578 currentBuffer()->mergeAndDestroy (parseExpression (TYPE_Int));
579 m_lexer->mustGetNext (TK_ParenEnd);
580 m_lexer->mustGetNext (TK_BraceStart);
581 SCOPE (0).type = SCOPE_Switch;
582 SCOPE (0).mark1 = currentBuffer()->addMark (""); // end mark
583 SCOPE (0).buffer1 = null; // default header
584 }
585
586 // ============================================================================
587 //
588 void BotscriptParser::parseSwitchCase()
589 {
590 // case is only allowed inside switch
591 if (SCOPE (0).type != SCOPE_Switch)
592 error ("case label outside switch");
593
594 // Get a literal value for the case block. Zandronum does not support
595 // expressions here.
596 m_lexer->mustGetNext (TK_Number);
597 int num = m_lexer->token()->text.toLong();
598 m_lexer->mustGetNext (TK_Colon);
599
600 for (const CaseInfo& info : SCOPE(0).cases)
601 if (info.number == num)
602 error ("multiple case %1 labels in one switch", num);
603
604 // Write down the expression and case-go-to. This builds
605 // the case tree. The closing event will write the actual
606 // blocks and move the marks appropriately.
607 //
608 // AddSwitchCase will add the reference to the mark
609 // for the case block that this heralds, and takes care
610 // of buffering setup and stuff like that.
611 //
612 // We null the switch buffer for the case-go-to statement as
613 // we want it all under the switch, not into the case-buffers.
614 m_switchBuffer = null;
615 currentBuffer()->writeDWord (DH_CaseGoto);
616 currentBuffer()->writeDWord (num);
617 addSwitchCase (null);
618 SCOPE (0).casecursor->number = num;
619 }
620
621 // ============================================================================
622 //
623 void BotscriptParser::parseSwitchDefault()
624 {
625 if (SCOPE (0).type != SCOPE_Switch)
626 error ("default label outside switch");
627
628 if (SCOPE (0).buffer1 != null)
629 error ("multiple default labels in one switch");
630
631 m_lexer->mustGetNext (TK_Colon);
632
633 // The default header is buffered into buffer1, since
634 // it has to be the last of the case headers
635 //
636 // Since the expression is pushed into the switch
637 // and is only popped when case succeeds, we have
638 // to pop it with DH_Drop manually if we end up in
639 // a default.
640 DataBuffer* buf = new DataBuffer;
641 SCOPE (0).buffer1 = buf;
642 buf->writeDWord (DH_Drop);
643 buf->writeDWord (DH_Goto);
644 addSwitchCase (buf);
645 }
646
647 // ============================================================================
648 //
649 void BotscriptParser::parseBreak()
650 {
651 if (m_scopeCursor == 0)
652 error ("unexpected `break`");
653
654 currentBuffer()->writeDWord (DH_Goto);
655
656 // switch and if use mark1 for the closing point,
657 // for and while use mark2.
658 switch (SCOPE (0).type)
659 {
660 case SCOPE_If:
661 case SCOPE_Switch:
662 {
663 currentBuffer()->addReference (SCOPE (0).mark1);
664 } break;
665
666 case SCOPE_For:
667 case SCOPE_While:
668 {
669 currentBuffer()->addReference (SCOPE (0).mark2);
670 } break;
671
672 default:
673 {
674 error ("unexpected `break`");
675 } break;
676 }
677
678 m_lexer->mustGetNext (TK_Semicolon);
679 }
680
681 // ============================================================================
682 //
683 void BotscriptParser::parseContinue()
684 {
685 m_lexer->mustGetNext (TK_Semicolon);
686
687 int curs;
688 bool found = false;
689
690 // Fall through the scope until we find a loop block
691 for (curs = m_scopeCursor; curs > 0 && !found; curs--)
692 {
693 switch (m_scopeStack[curs].type)
694 {
695 case SCOPE_For:
696 case SCOPE_While:
697 case SCOPE_Do:
698 {
699 currentBuffer()->writeDWord (DH_Goto);
700 currentBuffer()->addReference (m_scopeStack[curs].mark1);
701 found = true;
702 } break;
703
704 default:
705 break;
706 }
707 }
708
709 // No loop blocks
710 if (found == false)
711 error ("`continue`-statement not inside a loop");
712 }
713
714 // ============================================================================
715 //
716 void BotscriptParser::parseBlockEnd()
717 {
718 // Closing brace
719 // If we're in the block stack, we're descending down from it now
720 if (m_scopeCursor > 0)
721 {
722 switch (SCOPE (0).type)
723 {
724 case SCOPE_If:
725 {
726 // Adjust the closing mark.
727 currentBuffer()->adjustMark (SCOPE (0).mark1);
728
729 // We're returning from `if`, thus `else` follow
730 m_isElseAllowed = true;
731 break;
732 }
733
734 case SCOPE_Else:
735 {
736 // else instead uses mark1 for itself (so if expression
737 // fails, jump to else), mark2 means end of else
738 currentBuffer()->adjustMark (SCOPE (0).mark2);
739 break;
740 }
741
742 case SCOPE_For:
743 { // write the incrementor at the end of the loop block
744 currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
745 }
746 case SCOPE_While:
747 { // write down the instruction to go back to the start of the loop
748 currentBuffer()->writeDWord (DH_Goto);
749 currentBuffer()->addReference (SCOPE (0).mark1);
750
751 // Move the closing mark here since we're at the end of the while loop
752 currentBuffer()->adjustMark (SCOPE (0).mark2);
753 break;
754 }
755
756 case SCOPE_Do:
757 {
758 m_lexer->mustGetNext (TK_While);
759 m_lexer->mustGetNext (TK_ParenStart);
760 DataBuffer* expr = parseExpression (TYPE_Int);
761 m_lexer->mustGetNext (TK_ParenEnd);
762 m_lexer->mustGetNext (TK_Semicolon);
763
764 // If the condition runs true, go back to the start.
765 currentBuffer()->mergeAndDestroy (expr);
766 currentBuffer()->writeDWord (DH_IfGoto);
767 currentBuffer()->addReference (SCOPE (0).mark1);
768 break;
769 }
770
771 case SCOPE_Switch:
772 {
773 // Switch closes. Move down to the record buffer of
774 // the lower block.
775 if (SCOPE (1).casecursor != SCOPE (1).cases.begin() - 1)
776 m_switchBuffer = SCOPE (1).casecursor->data;
777 else
778 m_switchBuffer = null;
779
780 // If there was a default in the switch, write its header down now.
781 // If not, write instruction to jump to the end of switch after
782 // the headers (thus won't fall-through if no case matched)
783 if (SCOPE (0).buffer1)
784 currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
785 else
786 {
787 currentBuffer()->writeDWord (DH_Drop);
788 currentBuffer()->writeDWord (DH_Goto);
789 currentBuffer()->addReference (SCOPE (0).mark1);
790 }
791
792 // Go through all of the buffers we
793 // recorded down and write them.
794 for (CaseInfo& info : SCOPE (0).cases)
795 {
796 currentBuffer()->adjustMark (info.mark);
797 currentBuffer()->mergeAndDestroy (info.data);
798 }
799
800 // Move the closing mark here
801 currentBuffer()->adjustMark (SCOPE (0).mark1);
802 break;
803 }
804
805 case SCOPE_Unknown:
806 break;
807 }
808
809 // Descend down the stack
810 m_scopeCursor--;
811 return;
812 }
813
814 int dataheader = (m_currentMode == PARSERMODE_Event) ? DH_EndEvent :
815 (m_currentMode == PARSERMODE_MainLoop) ? DH_EndMainLoop :
816 (m_currentMode == PARSERMODE_Onenter) ? DH_EndOnEnter :
817 (m_currentMode == PARSERMODE_Onexit) ? DH_EndOnExit : -1;
818
819 if (dataheader == -1)
820 error ("unexpected `}`");
821
822 // Data header must be written before mode is changed because
823 // onenter and mainloop go into special buffers, and we want
824 // the closing data headers into said buffers too.
825 currentBuffer()->writeDWord (dataheader);
826 m_currentMode = PARSERMODE_TopLevel;
827 m_lexer->next (TK_Semicolon);
828 }
829
830 // =============================================================================
831 //
832 void BotscriptParser::parseEventdef()
833 {
834 EventDefinition* e = new EventDefinition;
835
836 m_lexer->mustGetNext (TK_Number);
837 e->number = getTokenString().toLong();
838 m_lexer->mustGetNext (TK_Colon);
839 m_lexer->mustGetNext (TK_Symbol);
840 e->name = m_lexer->token()->text;
841 m_lexer->mustGetNext (TK_ParenStart);
842 m_lexer->mustGetNext (TK_ParenEnd);
843 m_lexer->mustGetNext (TK_Semicolon);
844 addEvent (e);
845 }
846
847 // =============================================================================
848 //
849 void BotscriptParser::parseFuncdef()
850 {
851 CommandInfo* comm = new CommandInfo;
852 comm->origin = m_lexer->describeCurrentPosition();
853
854 // Return value
855 m_lexer->mustGetAnyOf ({TK_Int,TK_Void,TK_Bool,TK_Str});
856 comm->returnvalue = getTypeByName (m_lexer->token()->text); // TODO
857 assert (comm->returnvalue != -1);
858
859 // Number
860 m_lexer->mustGetNext (TK_Number);
861 comm->number = m_lexer->token()->text.toLong();
862 m_lexer->mustGetNext (TK_Colon);
863
864 // Name
865 m_lexer->mustGetNext (TK_Symbol);
866 comm->name = m_lexer->token()->text;
867
868 // Arguments
869 m_lexer->mustGetNext (TK_ParenStart);
870 comm->minargs = 0;
871
872 while (m_lexer->peekNextType (TK_ParenEnd) == false)
873 {
874 if (comm->args.isEmpty() == false)
875 m_lexer->mustGetNext (TK_Comma);
876
877 CommandArgument arg;
878 m_lexer->mustGetAnyOf ({TK_Int,TK_Bool,TK_Str});
879 DataType type = getTypeByName (m_lexer->token()->text); // TODO
880 assert (type != -1 && type != TYPE_Void);
881 arg.type = type;
882
883 m_lexer->mustGetNext (TK_Symbol);
884 arg.name = m_lexer->token()->text;
885
886 // If this is an optional parameter, we need the default value.
887 if (comm->minargs < comm->args.size() || m_lexer->peekNextType (TK_Assign))
888 {
889 m_lexer->mustGetNext (TK_Assign);
890
891 switch (type)
892 {
893 case TYPE_Int:
894 case TYPE_Bool:
895 m_lexer->mustGetNext (TK_Number);
896 break;
897
898 case TYPE_String:
899 error ("string arguments cannot have default values");
900
901 case TYPE_Unknown:
902 case TYPE_Void:
903 break;
904 }
905
906 arg.defvalue = m_lexer->token()->text.toLong();
907 }
908 else
909 comm->minargs++;
910
911 comm->args << arg;
912 }
913
914 m_lexer->mustGetNext (TK_ParenEnd);
915 m_lexer->mustGetNext (TK_Semicolon);
916 addCommandDefinition (comm);
917 }
918
919 // ============================================================================
920 //
921 // Parses a using statement
922 //
923 void BotscriptParser::parseUsing()
924 {
925 checkToplevel();
926 m_lexer->mustGetSymbol ("zandronum");
927 String versionText;
928
929 while (m_lexer->next() && (m_lexer->tokenType() == TK_Number || m_lexer->tokenType() == TK_Dot))
930 versionText += getTokenString();
931
932 // Note: at this point the lexer's pointing at the token after the version.
933 if (versionText.isEmpty())
934 error ("expected version string, got `%1`", getTokenString());
935 if (g_validZandronumVersions.contains (versionText) == false)
936 error ("unknown version string `%2`: valid versions: `%1`\n", g_validZandronumVersions, versionText);
937
938 StringList versionTokens = versionText.split (".");
939 m_zandronumVersion = versionTokens[0].toLong() * 10000 + versionTokens[1].toLong() * 100;
940 m_defaultZandronumVersion = false;
941 m_lexer->tokenMustBe (TK_Semicolon);
942 }
943
944 // ============================================================================/
945 //
946 // Parses a command call
947 //
948 DataBuffer* BotscriptParser::parseCommand (CommandInfo* comm)
949 {
950 DataBuffer* r = new DataBuffer (64);
951
952 if (m_currentMode == PARSERMODE_TopLevel && comm->returnvalue == TYPE_Void)
953 error ("command call at top level");
954
955 m_lexer->mustGetNext (TK_ParenStart);
956 m_lexer->mustGetNext (TK_Any);
957
958 int curarg = 0;
959
960 for (;;)
961 {
962 if (tokenIs (TK_ParenEnd))
963 {
964 if (curarg < comm->minargs)
965 error ("too few arguments passed to %1\n\tusage is: %2",
966 comm->name, comm->signature());
967
968 break;
969 }
970
971 if (curarg >= comm->args.size())
972 error ("too many arguments (%3) passed to %1\n\tusage is: %2",
973 comm->name, comm->signature());
974
975 r->mergeAndDestroy (parseExpression (comm->args[curarg].type, true));
976 m_lexer->mustGetNext (TK_Any);
977
978 if (curarg < comm->minargs - 1)
979 {
980 m_lexer->tokenMustBe (TK_Comma);
981 m_lexer->mustGetNext (TK_Any);
982 }
983 else if (curarg < comm->args.size() - 1)
984 {
985 // Can continue, but can terminate as well.
986 if (tokenIs (TK_ParenEnd))
987 {
988 curarg++;
989 break;
990 }
991 else
992 {
993 m_lexer->tokenMustBe (TK_Comma);
994 m_lexer->mustGetNext (TK_Any);
995 }
996 }
997
998 curarg++;
999 }
1000
1001 // If the script skipped any optional arguments, fill in defaults.
1002 while (curarg < comm->args.size())
1003 {
1004 r->writeDWord (DH_PushNumber);
1005 r->writeDWord (comm->args[curarg].defvalue);
1006 curarg++;
1007 }
1008
1009 r->writeDWord (DH_Command);
1010 r->writeDWord (comm->number);
1011 r->writeDWord (comm->args.size());
1012
1013 return r;
1014 }
1015
1016 // ============================================================================
1017 //
1018 String BotscriptParser::parseFloat()
1019 {
1020 m_lexer->tokenMustBe (TK_Number);
1021 String floatstring = getTokenString();
1022 Lexer::TokenInfo tok;
1023
1024 // Go after the decimal point
1025 if (m_lexer->peekNext (&tok) && tok.type ==TK_Dot)
1026 {
1027 m_lexer->skip();
1028 m_lexer->mustGetNext (TK_Number);
1029 floatstring += ".";
1030 floatstring += getTokenString();
1031 }
1032
1033 return floatstring;
1034 }
1035
1036 // ============================================================================
1037 //
1038 // Parses an assignment operator.
1039 //
1040 AssignmentOperator BotscriptParser::parseAssignmentOperator()
1041 {
1042 const List<ETokenType> tokens =
1043 {
1044 TK_Assign,
1045 TK_AddAssign,
1046 TK_SubAssign,
1047 TK_MultiplyAssign,
1048 TK_DivideAssign,
1049 TK_ModulusAssign,
1050 TK_DoublePlus,
1051 TK_DoubleMinus,
1052 };
1053
1054 m_lexer->mustGetAnyOf (tokens);
1055
1056 switch (m_lexer->tokenType())
1057 {
1058 case TK_Assign: return ASSIGNOP_Assign;
1059 case TK_AddAssign: return ASSIGNOP_Add;
1060 case TK_SubAssign: return ASSIGNOP_Subtract;
1061 case TK_MultiplyAssign: return ASSIGNOP_Multiply;
1062 case TK_DivideAssign: return ASSIGNOP_Divide;
1063 case TK_ModulusAssign: return ASSIGNOP_Modulus;
1064 case TK_DoublePlus: return ASSIGNOP_Increase;
1065 case TK_DoubleMinus: return ASSIGNOP_Decrease;
1066 default: break;
1067 }
1068
1069 assert (false);
1070 return (AssignmentOperator) 0;
1071 }
1072
1073 // ============================================================================
1074 //
1075 struct AssignmentDataHeaderInfo
1076 {
1077 AssignmentOperator op;
1078 DataHeader local;
1079 DataHeader global;
1080 DataHeader array;
1081 };
1082
1083 const AssignmentDataHeaderInfo gAssignmentDataHeaders[] =
1084 {
1085 { ASSIGNOP_Assign, DH_AssignLocalVar, DH_AssignGlobalVar, DH_AssignGlobalArray },
1086 { ASSIGNOP_Add, DH_AddLocalVar, DH_AddGlobalVar, DH_AddGlobalArray },
1087 { ASSIGNOP_Subtract, DH_SubtractLocalVar, DH_SubtractGlobalVar, DH_SubtractGlobalArray },
1088 { ASSIGNOP_Multiply, DH_MultiplyLocalVar, DH_MultiplyGlobalVar, DH_MultiplyGlobalArray },
1089 { ASSIGNOP_Divide, DH_DivideLocalVar, DH_DivideGlobalVar, DH_DivideGlobalArray },
1090 { ASSIGNOP_Modulus, DH_ModLocalVar, DH_ModGlobalVar, DH_ModGlobalArray },
1091 { ASSIGNOP_Increase, DH_IncreaseLocalVar, DH_IncreaseGlobalVar, DH_IncreaseGlobalArray },
1092 { ASSIGNOP_Decrease, DH_DecreaseLocalVar, DH_DecreaseGlobalVar, DH_DecreaseGlobalArray },
1093 };
1094
1095 DataHeader BotscriptParser::getAssigmentDataHeader (AssignmentOperator op, Variable* var)
1096 {
1097 for (const auto& a : gAssignmentDataHeaders)
1098 {
1099 if (a.op != op)
1100 continue;
1101
1102 if (var->isarray)
1103 return a.array;
1104
1105 if (var->IsGlobal())
1106 return a.global;
1107
1108 return a.local;
1109 }
1110
1111 error ("WTF: couldn't find data header for operator #%1", op);
1112 return (DataHeader) 0;
1113 }
1114
1115 // ============================================================================
1116 //
1117 // Parses an assignment. An assignment starts with a variable name, followed
1118 // by an assignment operator, followed by an expression value. Expects current
1119 // token to be the name of the variable, and expects the variable to be given.
1120 //
1121 DataBuffer* BotscriptParser::parseAssignment (Variable* var)
1122 {
1123 DataBuffer* retbuf = new DataBuffer;
1124 DataBuffer* arrayindex = null;
1125
1126 if (var->writelevel != WRITE_Mutable)
1127 error ("cannot alter read-only variable $%1", var->name);
1128
1129 if (var->isarray)
1130 {
1131 m_lexer->mustGetNext (TK_BracketStart);
1132 Expression expr (this, m_lexer, TYPE_Int);
1133 expr.getResult()->convertToBuffer();
1134 arrayindex = expr.getResult()->buffer()->clone();
1135 m_lexer->mustGetNext (TK_BracketEnd);
1136 }
1137
1138 // Get an operator
1139 AssignmentOperator oper = parseAssignmentOperator();
1140
1141 if (m_currentMode == PARSERMODE_TopLevel)
1142 error ("can't alter variables at top level");
1143
1144 if (var->isarray)
1145 retbuf->mergeAndDestroy (arrayindex);
1146
1147 // Parse the right operand
1148 if (oper != ASSIGNOP_Increase && oper != ASSIGNOP_Decrease)
1149 {
1150 DataBuffer* expr = parseExpression (var->type);
1151 retbuf->mergeAndDestroy (expr);
1152 }
1153
1154 #if 0
1155 // <<= and >>= do not have data headers. Solution: expand them.
1156 // a <<= b -> a = a << b
1157 // a >>= b -> a = a >> b
1158 retbuf->WriteDWord (var->IsGlobal() ? DH_PushGlobalVar : DH_PushLocalVar);
1159 retbuf->WriteDWord (var->index);
1160 retbuf->MergeAndDestroy (expr);
1161 retbuf->WriteDWord ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LeftShift : DH_RightShift);
1162 retbuf->WriteDWord (var->IsGlobal() ? DH_AssignGlobalVar : DH_AssignLocalVar);
1163 retbuf->WriteDWord (var->index);
1164 #endif
1165
1166 DataHeader dh = getAssigmentDataHeader (oper, var);
1167 retbuf->writeDWord (dh);
1168 retbuf->writeDWord (var->index);
1169 return retbuf;
1170 }
1171
1172 // ============================================================================
1173 //
1174 void BotscriptParser::pushScope (EReset reset)
1175 {
1176 m_scopeCursor++;
1177
1178 if (m_scopeStack.size() < m_scopeCursor + 1)
1179 {
1180 ScopeInfo newscope;
1181 m_scopeStack << newscope;
1182 reset = SCOPE_Reset;
1183 }
1184
1185 if (reset == SCOPE_Reset)
1186 {
1187 ScopeInfo* info = &SCOPE (0);
1188 info->type = SCOPE_Unknown;
1189 info->mark1 = null;
1190 info->mark2 = null;
1191 info->buffer1 = null;
1192 info->cases.clear();
1193 info->casecursor = info->cases.begin() - 1;
1194 }
1195
1196 // Reset variable stuff in any case
1197 SCOPE(0).globalVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).globalVarIndexBase;
1198 SCOPE(0).localVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).localVarIndexBase;
1199
1200 for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
1201 delete var;
1202
1203 SCOPE(0).localVariables.clear();
1204 SCOPE(0).globalVariables.clear();
1205 }
1206
1207 // ============================================================================
1208 //
1209 DataBuffer* BotscriptParser::parseExpression (DataType reqtype, bool fromhere)
1210 {
1211 // hehe
1212 if (fromhere == true)
1213 m_lexer->skip (-1);
1214
1215 Expression expr (this, m_lexer, reqtype);
1216 expr.getResult()->convertToBuffer();
1217
1218 // The buffer will be destroyed once the function ends so we need to
1219 // clone it now.
1220 return expr.getResult()->buffer()->clone();
1221 }
1222
1223 // ============================================================================
1224 //
1225 DataBuffer* BotscriptParser::parseStatement()
1226 {
1227 // If it's a variable, expect assignment.
1228 if (m_lexer->next (TK_DollarSign))
1229 {
1230 m_lexer->mustGetNext (TK_Symbol);
1231 Variable* var = findVariable (getTokenString());
1232
1233 if (var == null)
1234 error ("unknown variable $%1", var->name);
1235
1236 return parseAssignment (var);
1237 }
1238
1239 return null;
1240 }
1241
1242 // ============================================================================
1243 //
1244 void BotscriptParser::addSwitchCase (DataBuffer* casebuffer)
1245 {
1246 ScopeInfo* info = &SCOPE (0);
1247 CaseInfo casedata;
1248
1249 // Init a mark for the case buffer
1250 ByteMark* casemark = currentBuffer()->addMark ("");
1251 casedata.mark = casemark;
1252
1253 // Add a reference to the mark. "case" and "default" both
1254 // add the necessary bytecode before the reference.
1255 if (casebuffer != null)
1256 casebuffer->addReference (casemark);
1257 else
1258 currentBuffer()->addReference (casemark);
1259
1260 // Init a buffer for the case block and tell the object
1261 // writer to record all written data to it.
1262 casedata.data = m_switchBuffer = new DataBuffer;
1263 SCOPE(0).cases << casedata;
1264 info->casecursor++;
1265 }
1266
1267 // ============================================================================
1268 //
1269 bool BotscriptParser::tokenIs (ETokenType a)
1270 {
1271 return (m_lexer->tokenType() == a);
1272 }
1273
1274 // ============================================================================
1275 //
1276 String BotscriptParser::getTokenString()
1277 {
1278 return m_lexer->token()->text;
1279 }
1280
1281 // ============================================================================
1282 //
1283 String BotscriptParser::describePosition() const
1284 {
1285 Lexer::TokenInfo* tok = m_lexer->token();
1286 return tok->file + ":" + String (tok->line) + ":" + String (tok->column);
1287 }
1288
1289 // ============================================================================
1290 //
1291 DataBuffer* BotscriptParser::currentBuffer()
1292 {
1293 if (m_switchBuffer != null)
1294 return m_switchBuffer;
1295
1296 if (m_currentMode == PARSERMODE_MainLoop)
1297 return m_mainLoopBuffer;
1298
1299 if (m_currentMode == PARSERMODE_Onenter)
1300 return m_onenterBuffer;
1301
1302 return m_mainBuffer;
1303 }
1304
1305 // ============================================================================
1306 //
1307 void BotscriptParser::writeMemberBuffers()
1308 {
1309 // If there was no mainloop defined, write a dummy one now.
1310 if (m_gotMainLoop == false)
1311 {
1312 m_mainLoopBuffer->writeDWord (DH_MainLoop);
1313 m_mainLoopBuffer->writeDWord (DH_EndMainLoop);
1314 }
1315
1316 // Write the onenter and mainloop buffers, in that order in particular.
1317 for (DataBuffer** bufp : List<DataBuffer**> ({&m_onenterBuffer, &m_mainLoopBuffer}))
1318 {
1319 currentBuffer()->mergeAndDestroy (*bufp);
1320
1321 // Clear the buffer afterwards for potential next state
1322 *bufp = new DataBuffer;
1323 }
1324
1325 // Next state definitely has no mainloop yet
1326 m_gotMainLoop = false;
1327 }
1328
1329 // ============================================================================
1330 //
1331 // Write string table
1332 //
1333 void BotscriptParser::writeStringTable()
1334 {
1335 int stringcount = countStringsInTable();
1336
1337 if (stringcount == 0)
1338 return;
1339
1340 // Write header
1341 m_mainBuffer->writeDWord (DH_StringList);
1342 m_mainBuffer->writeDWord (stringcount);
1343
1344 // Write all strings
1345 for (int i = 0; i < stringcount; i++)
1346 m_mainBuffer->writeString (getStringTable()[i]);
1347 }
1348
1349 // ============================================================================
1350 //
1351 // Write the compiled bytecode to a file
1352 //
1353 void BotscriptParser::writeToFile (String outfile)
1354 {
1355 FILE* fp = fopen (outfile, "wb");
1356
1357 if (fp == null)
1358 error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
1359
1360 // First, resolve references
1361 for (MarkReference* ref : m_mainBuffer->references())
1362 for (int i = 0; i < 4; ++i)
1363 m_mainBuffer->buffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF;
1364
1365 // Then, dump the main buffer to the file
1366 fwrite (m_mainBuffer->buffer(), 1, m_mainBuffer->writtenSize(), fp);
1367 print ("-- %1 byte%s1 written to %2\n", m_mainBuffer->writtenSize(), outfile);
1368 fclose (fp);
1369 }
1370
1371 // ============================================================================
1372 //
1373 // Attempt to find the variable by the given name. Looks from current scope
1374 // downwards.
1375 //
1376 Variable* BotscriptParser::findVariable (const String& name)
1377 {
1378 for (int i = m_scopeCursor; i >= 0; --i)
1379 {
1380 for (Variable* var : m_scopeStack[i].globalVariables + m_scopeStack[i].localVariables)
1381 {
1382 if (var->name == name)
1383 return var;
1384 }
1385 }
1386
1387 return null;
1388 }
1389
1390 // ============================================================================
1391 //
1392 // Is the parser currently in global state (i.e. not in any specific state)?
1393 //
1394 bool BotscriptParser::isInGlobalState() const
1395 {
1396 return m_currentState.isEmpty();
1397 }
1398
1399 // ============================================================================
1400 //
1401 void BotscriptParser::suggestHighestVarIndex (bool global, int index)
1402 {
1403 if (global)
1404 m_highestGlobalVarIndex = max (m_highestGlobalVarIndex, index);
1405 else
1406 m_highestStateVarIndex = max (m_highestStateVarIndex, index);
1407 }
1408
1409 // ============================================================================
1410 //
1411 int BotscriptParser::getHighestVarIndex (bool global)
1412 {
1413 if (global)
1414 return m_highestGlobalVarIndex;
1415
1416 return m_highestStateVarIndex;
1417 }

mercurial