src/Parser.cc

changeset 88
5def6ff8b466
child 89
029a330a9bef
equal deleted inserted replaced
87:8f65914e7046 88:5def6ff8b466
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 "Variables.h"
34 #include "Containers.h"
35 #include "Lexer.h"
36 #include "DataBuffer.h"
37
38 #define SCOPE(n) (mScopeStack[mScopeCursor - n])
39
40 // ============================================================================
41 //
42 BotscriptParser::BotscriptParser() :
43 mReadOnly (false),
44 mLexer (new Lexer) {}
45
46 // ============================================================================
47 //
48 BotscriptParser::~BotscriptParser()
49 {
50 delete mLexer;
51 }
52
53 // ============================================================================
54 //
55 void BotscriptParser::CheckToplevel()
56 {
57 if (mCurrentMode != ETopLevelMode)
58 Error ("%1-statements may only be defined at top level!", GetTokenString());
59 }
60
61 // ============================================================================
62 //
63 void BotscriptParser::CheckNotToplevel()
64 {
65 if (mCurrentMode == ETopLevelMode)
66 Error ("%1-statements must not be defined at top level!", GetTokenString());
67 }
68
69 // ============================================================================
70 // Main Parser code. Begins read of the script file, checks the syntax of it
71 // and writes the data to the object file via Objwriter - which also takes care
72 // of necessary buffering so stuff is written in the correct order.
73 void BotscriptParser::ParseBotscript (String fileName)
74 {
75 // Lex and preprocess the file
76 mLexer->ProcessFile (fileName);
77
78 mCurrentMode = ETopLevelMode;
79 mNumStates = 0;
80 mNumEvents = 0;
81 mScopeCursor = 0;
82 mStateSpawnDefined = false;
83 mGotMainLoop = false;
84 mIfExpression = null;
85 mCanElse = false;
86
87 // Zero the entire block stack first
88 // TODO: this shouldn't be necessary
89 for (int i = 0; i < MAX_SCOPE; i++)
90 ZERO (mScopeStack[i]);
91
92 while (mLexer->GetNext())
93 {
94 // Check if else is potentically valid
95 if (TokenIs (tkElse) && mCanElse == false)
96 Error ("else without preceding if");
97
98 if (TokenIs (tkElse) == false)
99 mCanElse = false;
100
101 switch (mLexer->GetToken()->type)
102 {
103 case tkState:
104 ParseStateBlock();
105 break;
106
107 case tkEvent:
108 ParseEventBlock();
109 break;
110
111 case tkMainloop:
112 ParseMainloop();
113 break;
114
115 case tkOnenter:
116 case tkOnexit:
117 ParseOnEnterExit();
118 break;
119
120 case tkInt:
121 case tkStr:
122 case tkVoid:
123 ParseVariableDeclaration();
124 break;
125
126 case tkGoto:
127 ParseGoto();
128 break;
129
130 case tkIf:
131 ParseIf();
132 break;
133
134 case tkElse:
135 ParseElse();
136 break;
137
138 case tkWhile:
139 ParseWhileBlock();
140 break;
141
142 case tkFor:
143 ParseForBlock();
144 break;
145
146 case tkDo:
147 ParseDoBlock();
148 break;
149
150 case tkSwitch:
151 ParseSwitchBlock();
152 break;
153
154 case tkCase:
155 ParseSwitchCase();
156 break;
157
158 case tkDefault:
159 ParseSwitchDefault();
160 break;
161
162 case tkBreak:
163 ParseBreak();
164 break;
165
166 case tkContinue:
167 ParseContinue();
168 break;
169
170 case tkBraceEnd:
171 ParseBlockEnd();
172 break;
173
174 case tkConst:
175 ParseConst();
176 break;
177
178 case tkEventdef:
179 ParseEventdef();
180 break;
181
182 case tkFuncdef:
183 ParseFuncdef();
184 break;
185
186 default:
187 {
188 // Check for labels
189 Lexer::Token next;
190
191 if (TokenIs (tkSymbol) &&
192 mLexer->PeekNext (&next) &&
193 next.type == tkColon)
194 {
195 ParseLabel();
196 break;
197 }
198
199 // Check if it's a command
200 CommandInfo* comm = FindCommandByName (GetTokenString());
201
202 if (comm)
203 {
204 buffer()->MergeAndDestroy (ParseCommand (comm));
205 mLexer->MustGetNext (tkSemicolon);
206 continue;
207 }
208
209 // If nothing else, parse it as a statement
210 DataBuffer* b = ParseStatement();
211
212 if (b == false)
213 Error ("unknown token `%1`", GetTokenString());
214
215 buffer()->MergeAndDestroy (b);
216 mLexer->MustGetNext (tkSemicolon);
217 }
218 break;
219 }
220 }
221
222 // ===============================================================================
223 // Script file ended. Do some last checks and write the last things to main buffer
224 if (mCurrentMode != ETopLevelMode)
225 Error ("script did not end at top level; a `}` is missing somewhere");
226
227 if (IsReadOnly() == false)
228 {
229 // stateSpawn must be defined!
230 if (mStateSpawnDefined == false)
231 Error ("script must have a state named `stateSpawn`!");
232
233 // Ensure no goto target is left undefined
234 if (mUndefinedLabels.IsEmpty() == false)
235 {
236 StringList names;
237
238 for (UndefinedLabel& undf : mUndefinedLabels)
239 names << undf.name;
240
241 Error ("labels `%1` are referenced via `goto` but are not defined\n", names);
242 }
243
244 // Dump the last state's onenter and mainloop
245 writeMemberBuffers();
246
247 // String table
248 WriteStringTable();
249 }
250 }
251
252 // ============================================================================
253 //
254 void BotscriptParser::ParseStateBlock()
255 {
256 CheckToplevel();
257 mLexer->MustGetNext (tkString);
258 String statename = GetTokenString();
259
260 // State name must be a word.
261 if (statename.FirstIndexOf (" ") != -1)
262 Error ("state name must be a single word, got `%1`", statename);
263
264 // stateSpawn is special - it *must* be defined. If we
265 // encountered it, then mark down that we have it.
266 if (-statename == "statespawn")
267 mStateSpawnDefined = true;
268
269 // Must end in a colon
270 mLexer->MustGetNext (tkColon);
271
272 // write the previous state's onenter and
273 // mainloop buffers to file now
274 if (mCurrentState.IsEmpty() == false)
275 writeMemberBuffers();
276
277 buffer()->WriteDWord (dhStateName);
278 buffer()->WriteString (statename);
279 buffer()->WriteDWord (dhStateIndex);
280 buffer()->WriteDWord (mNumStates);
281
282 mNumStates++;
283 mCurrentState = statename;
284 mGotMainLoop = false;
285 }
286
287 // ============================================================================
288 //
289 void BotscriptParser::ParseEventBlock()
290 {
291 CheckToplevel();
292 mLexer->MustGetNext (tkString);
293
294 EventDefinition* e = FindEventByName (GetTokenString());
295
296 if (e == null)
297 Error ("bad event, got `%1`\n", GetTokenString());
298
299 mLexer->MustGetNext (tkBraceStart);
300 mCurrentMode = EEventMode;
301 buffer()->WriteDWord (dhEvent);
302 buffer()->WriteDWord (e->number);
303 mNumEvents++;
304 }
305
306 // ============================================================================
307 //
308 void BotscriptParser::ParseMainloop()
309 {
310 CheckToplevel();
311 mLexer->MustGetNext (tkBraceStart);
312
313 mCurrentMode = EMainLoopMode;
314 mMainLoopBuffer->WriteDWord (dhMainLoop);
315 }
316
317 // ============================================================================
318 //
319 void BotscriptParser::ParseOnEnterExit()
320 {
321 CheckToplevel();
322 bool onenter = (TokenIs (tkOnenter));
323 mLexer->MustGetNext (tkBraceStart);
324
325 mCurrentMode = onenter ? EOnenterMode : EOnexitMode;
326 buffer()->WriteDWord (onenter ? dhOnEnter : dhOnExit);
327 }
328
329 // ============================================================================
330 //
331 void BotscriptParser::ParseVariableDeclaration()
332 {
333 // For now, only globals are supported
334 if (mCurrentMode != ETopLevelMode || mCurrentState.IsEmpty() == false)
335 Error ("variables must only be global for now");
336
337 EType type = (TokenIs (tkInt)) ? EIntType :
338 (TokenIs (tkStr)) ? EStringType :
339 EBoolType;
340
341 mLexer->MustGetNext();
342 String varname = GetTokenString();
343
344 // Var name must not be a number
345 if (varname.IsNumeric())
346 Error ("variable name must not be a number");
347
348 ScriptVariable* var = DeclareGlobalVariable (type, varname);
349 (void) var;
350 mLexer->MustGetNext (tkSemicolon);
351 }
352
353 // ============================================================================
354 //
355 void BotscriptParser::ParseGoto()
356 {
357 CheckNotToplevel();
358
359 // Get the name of the label
360 mLexer->MustGetNext();
361
362 // Find the mark this goto statement points to
363 String target = GetTokenString();
364 ByteMark* mark = buffer()->FindMarkByName (target);
365
366 // If not set, define it
367 if (mark == null)
368 {
369 UndefinedLabel undf;
370 undf.name = target;
371 undf.target = buffer()->AddMark (target);
372 mUndefinedLabels << undf;
373 }
374
375 // Add a reference to the mark.
376 buffer()->WriteDWord (dhGoto);
377 buffer()->AddReference (mark);
378 mLexer->MustGetNext (tkSemicolon);
379 }
380
381 // ============================================================================
382 //
383 void BotscriptParser::ParseIf()
384 {
385 CheckNotToplevel();
386 PushScope();
387
388 // Condition
389 mLexer->MustGetNext (tkParenStart);
390
391 // Read the expression and write it.
392 mLexer->MustGetNext();
393 DataBuffer* c = ParseExpression (EIntType);
394 buffer()->MergeAndDestroy (c);
395
396 mLexer->MustGetNext (tkParenEnd);
397 mLexer->MustGetNext (tkBraceStart);
398
399 // Add a mark - to here temporarily - and add a reference to it.
400 // Upon a closing brace, the mark will be adjusted.
401 ByteMark* mark = buffer()->AddMark ("");
402
403 // Use dhIfNotGoto - if the expression is not true, we goto the mark
404 // we just defined - and this mark will be at the end of the scope block.
405 buffer()->WriteDWord (dhIfNotGoto);
406 buffer()->AddReference (mark);
407
408 // Store it
409 SCOPE (0).mark1 = mark;
410 SCOPE (0).type = eIfScope;
411 }
412
413 // ============================================================================
414 //
415 void BotscriptParser::ParseElse()
416 {
417 CheckNotToplevel();
418 mLexer->MustGetNext (tkBraceStart);
419
420 // Don't use PushScope as it resets the scope
421 mScopeCursor++;
422
423 if (mScopeCursor >= MAX_SCOPE)
424 Error ("too deep scope");
425
426 if (SCOPE (0).type != eIfScope)
427 Error ("else without preceding if");
428
429 // write down to jump to the end of the else statement
430 // Otherwise we have fall-throughs
431 SCOPE (0).mark2 = buffer()->AddMark ("");
432
433 // Instruction to jump to the end after if block is complete
434 buffer()->WriteDWord (dhGoto);
435 buffer()->AddReference (SCOPE (0).mark2);
436
437 // Move the ifnot mark here and set type to else
438 buffer()->AdjustMark (SCOPE (0).mark1);
439 SCOPE (0).type = eElseScope;
440 }
441
442 // ============================================================================
443 //
444 void BotscriptParser::ParseWhileBlock()
445 {
446 CheckNotToplevel();
447 PushScope();
448
449 // While loops need two marks - one at the start of the loop and one at the
450 // end. The condition is checked at the very start of the loop, if it fails,
451 // we use goto to skip to the end of the loop. At the end, we loop back to
452 // the beginning with a go-to statement.
453 ByteMark* mark1 = buffer()->AddMark (""); // start
454 ByteMark* mark2 = buffer()->AddMark (""); // end
455
456 // Condition
457 mLexer->MustGetNext (tkParenStart);
458 mLexer->MustGetNext();
459 DataBuffer* expr = ParseExpression (EIntType);
460 mLexer->MustGetNext (tkParenEnd);
461 mLexer->MustGetNext (tkBraceStart);
462
463 // write condition
464 buffer()->MergeAndDestroy (expr);
465
466 // Instruction to go to the end if it fails
467 buffer()->WriteDWord (dhIfNotGoto);
468 buffer()->AddReference (mark2);
469
470 // Store the needed stuff
471 SCOPE (0).mark1 = mark1;
472 SCOPE (0).mark2 = mark2;
473 SCOPE (0).type = eWhileScope;
474 }
475
476 // ============================================================================
477 //
478 void BotscriptParser::ParseForBlock()
479 {
480 CheckNotToplevel();
481 PushScope();
482
483 // Initializer
484 mLexer->MustGetNext (tkParenStart);
485 mLexer->MustGetNext();
486 DataBuffer* init = ParseStatement();
487
488 if (init == null)
489 Error ("bad statement for initializer of for");
490
491 mLexer->MustGetNext (tkSemicolon);
492
493 // Condition
494 mLexer->MustGetNext();
495 DataBuffer* cond = ParseExpression (EIntType);
496
497 if (cond == null)
498 Error ("bad statement for condition of for");
499
500 mLexer->MustGetNext (tkSemicolon);
501
502 // Incrementor
503 mLexer->MustGetNext();
504 DataBuffer* incr = ParseStatement();
505
506 if (incr == null)
507 Error ("bad statement for incrementor of for");
508
509 mLexer->MustGetNext (tkParenEnd);
510 mLexer->MustGetNext (tkBraceStart);
511
512 // First, write out the initializer
513 buffer()->MergeAndDestroy (init);
514
515 // Init two marks
516 ByteMark* mark1 = buffer()->AddMark ("");
517 ByteMark* mark2 = buffer()->AddMark ("");
518
519 // Add the condition
520 buffer()->MergeAndDestroy (cond);
521 buffer()->WriteDWord (dhIfNotGoto);
522 buffer()->AddReference (mark2);
523
524 // Store the marks and incrementor
525 SCOPE (0).mark1 = mark1;
526 SCOPE (0).mark2 = mark2;
527 SCOPE (0).buffer1 = incr;
528 SCOPE (0).type = eForScope;
529 }
530
531 // ============================================================================
532 //
533 void BotscriptParser::ParseDoBlock()
534 {
535 CheckNotToplevel();
536 PushScope();
537 mLexer->MustGetNext (tkBraceStart);
538 SCOPE (0).mark1 = buffer()->AddMark ("");
539 SCOPE (0).type = eDoScope;
540 }
541
542 // ============================================================================
543 //
544 void BotscriptParser::ParseSwitchBlock()
545 {
546 // This gets a bit tricky. switch is structured in the
547 // bytecode followingly:
548 //
549 // (expression)
550 // case a: goto casemark1
551 // case b: goto casemark2
552 // case c: goto casemark3
553 // goto mark1 // jump to end if no matches
554 // casemark1: ...
555 // casemark2: ...
556 // casemark3: ...
557 // mark1: // end mark
558
559 CheckNotToplevel();
560 PushScope();
561 mLexer->MustGetNext (tkParenStart);
562 mLexer->MustGetNext();
563 buffer()->MergeAndDestroy (ParseExpression (EIntType));
564 mLexer->MustGetNext (tkParenEnd);
565 mLexer->MustGetNext (tkBraceStart);
566 SCOPE (0).type = eSwitchScope;
567 SCOPE (0).mark1 = buffer()->AddMark (""); // end mark
568 SCOPE (0).buffer1 = null; // default header
569 }
570
571 // ============================================================================
572 //
573 void BotscriptParser::ParseSwitchCase()
574 {
575 // case is only allowed inside switch
576 if (SCOPE (0).type != eSwitchScope)
577 Error ("case label outside switch");
578
579 // Get the literal (Zandronum does not support expressions here)
580 mLexer->MustGetNext (tkNumber);
581 int num = mLexer->GetToken()->text.ToLong();
582 mLexer->MustGetNext (tkColon);
583
584 for (int i = 0; i < MAX_CASE; i++)
585 if (SCOPE (0).casenumbers[i] == num)
586 Error ("multiple case %d labels in one switch", num);
587
588 // Write down the expression and case-go-to. This builds
589 // the case tree. The closing event will write the actual
590 // blocks and move the marks appropriately.
591 //
592 // AddSwitchCase will add the reference to the mark
593 // for the case block that this heralds, and takes care
594 // of buffering setup and stuff like that.
595 //
596 // We null the switch buffer for the case-go-to statement as
597 // we want it all under the switch, not into the case-buffers.
598 mSwitchBuffer = null;
599 buffer()->WriteDWord (dhCaseGoto);
600 buffer()->WriteDWord (num);
601 AddSwitchCase (null);
602 SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
603 }
604
605 // ============================================================================
606 //
607 void BotscriptParser::ParseSwitchDefault()
608 {
609 if (SCOPE (0).type != eSwitchScope)
610 Error ("default label outside switch");
611
612 if (SCOPE (0).buffer1)
613 Error ("multiple default labels in one switch");
614
615 mLexer->MustGetNext (tkColon);
616
617 // The default header is buffered into buffer1, since
618 // it has to be the last of the case headers
619 //
620 // Since the expression is pushed into the switch
621 // and is only popped when case succeeds, we have
622 // to pop it with dhDrop manually if we end up in
623 // a default.
624 DataBuffer* b = new DataBuffer;
625 SCOPE (0).buffer1 = b;
626 b->WriteDWord (dhDrop);
627 b->WriteDWord (dhGoto);
628 AddSwitchCase (b);
629 }
630
631 // ============================================================================
632 //
633 void BotscriptParser::ParseBreak()
634 {
635 if (mScopeCursor == 0)
636 Error ("unexpected `break`");
637
638 buffer()->WriteDWord (dhGoto);
639
640 // switch and if use mark1 for the closing point,
641 // for and while use mark2.
642 switch (SCOPE (0).type)
643 {
644 case eIfScope:
645 case eSwitchScope:
646 {
647 buffer()->AddReference (SCOPE (0).mark1);
648 } break;
649
650 case eForScope:
651 case eWhileScope:
652 {
653 buffer()->AddReference (SCOPE (0).mark2);
654 } break;
655
656 default:
657 {
658 Error ("unexpected `break`");
659 } break;
660 }
661
662 mLexer->MustGetNext (tkSemicolon);
663 }
664
665 // ============================================================================
666 //
667 void BotscriptParser::ParseContinue()
668 {
669 mLexer->MustGetNext (tkSemicolon);
670
671 int curs;
672 bool found = false;
673
674 // Fall through the scope until we find a loop block
675 for (curs = mScopeCursor; curs > 0 && !found; curs--)
676 {
677 switch (mScopeStack[curs].type)
678 {
679 case eForScope:
680 case eWhileScope:
681 case eDoScope:
682 {
683 buffer()->WriteDWord (dhGoto);
684 buffer()->AddReference (mScopeStack[curs].mark1);
685 found = true;
686 } break;
687
688 default:
689 break;
690 }
691 }
692
693 // No loop blocks
694 if (found == false)
695 Error ("`continue`-statement not inside a loop");
696 }
697
698 // ============================================================================
699 //
700 void BotscriptParser::ParseBlockEnd()
701 {
702 // Closing brace
703 // If we're in the block stack, we're descending down from it now
704 if (mScopeCursor > 0)
705 {
706 switch (SCOPE (0).type)
707 {
708 case eIfScope:
709 // Adjust the closing mark.
710 buffer()->AdjustMark (SCOPE (0).mark1);
711
712 // We're returning from if, thus else can be next
713 mCanElse = true;
714 break;
715
716 case eElseScope:
717 // else instead uses mark1 for itself (so if expression
718 // fails, jump to else), mark2 means end of else
719 buffer()->AdjustMark (SCOPE (0).mark2);
720 break;
721
722 case eForScope:
723 // write the incrementor at the end of the loop block
724 buffer()->MergeAndDestroy (SCOPE (0).buffer1);
725 case eWhileScope:
726 // write down the instruction to go back to the start of the loop
727 buffer()->WriteDWord (dhGoto);
728 buffer()->AddReference (SCOPE (0).mark1);
729
730 // Move the closing mark here since we're at the end of the while loop
731 buffer()->AdjustMark (SCOPE (0).mark2);
732 break;
733
734 case eDoScope:
735 {
736 mLexer->MustGetNext (tkWhile);
737 mLexer->MustGetNext (tkParenStart);
738 mLexer->MustGetNext();
739 DataBuffer* expr = ParseExpression (EIntType);
740 mLexer->MustGetNext (tkParenEnd);
741 mLexer->MustGetNext (tkSemicolon);
742
743 // If the condition runs true, go back to the start.
744 buffer()->MergeAndDestroy (expr);
745 buffer()->WriteDWord (dhIfGoto);
746 buffer()->AddReference (SCOPE (0).mark1);
747 break;
748 }
749
750 case eSwitchScope:
751 {
752 // Switch closes. Move down to the record buffer of
753 // the lower block.
754 if (SCOPE (1).casecursor != -1)
755 mSwitchBuffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
756 else
757 mSwitchBuffer = null;
758
759 // If there was a default in the switch, write its header down now.
760 // If not, write instruction to jump to the end of switch after
761 // the headers (thus won't fall-through if no case matched)
762 if (SCOPE (0).buffer1)
763 buffer()->MergeAndDestroy (SCOPE (0).buffer1);
764 else
765 {
766 buffer()->WriteDWord (dhDrop);
767 buffer()->WriteDWord (dhGoto);
768 buffer()->AddReference (SCOPE (0).mark1);
769 }
770
771 // Go through all of the buffers we
772 // recorded down and write them.
773 for (int u = 0; u < MAX_CASE; u++)
774 {
775 if (SCOPE (0).casebuffers[u] == null)
776 continue;
777
778 buffer()->AdjustMark (SCOPE (0).casemarks[u]);
779 buffer()->MergeAndDestroy (SCOPE (0).casebuffers[u]);
780 }
781
782 // Move the closing mark here
783 buffer()->AdjustMark (SCOPE (0).mark1);
784 break;
785 }
786
787 case eUnknownScope:
788 break;
789 }
790
791 // Descend down the stack
792 mScopeCursor--;
793 return;
794 }
795
796 int dataheader = (mCurrentMode == EEventMode) ? dhEndEvent :
797 (mCurrentMode == EMainLoopMode) ? dhEndMainLoop :
798 (mCurrentMode == EOnenterMode) ? dhEndOnEnter :
799 (mCurrentMode == EOnexitMode) ? dhEndOnExit : -1;
800
801 if (dataheader == -1)
802 Error ("unexpected `}`");
803
804 // Data header must be written before mode is changed because
805 // onenter and mainloop go into special buffers, and we want
806 // the closing data headers into said buffers too.
807 buffer()->WriteDWord (dataheader);
808 mCurrentMode = ETopLevelMode;
809 mLexer->GetNext (tkSemicolon);
810 }
811
812 // ============================================================================
813 //
814 void BotscriptParser::ParseConst()
815 {
816 ConstantInfo info;
817
818 // Get the type
819 mLexer->MustGetNext();
820 String typestring = GetTokenString();
821 info.type = GetTypeByName (typestring);
822
823 if (info.type == EUnknownType || info.type == EVoidType)
824 Error ("unknown type `%1` for constant", typestring);
825
826 mLexer->MustGetNext();
827 info.name = GetTokenString();
828
829 mLexer->MustGetNext (tkAssign);
830
831 switch (info.type)
832 {
833 case EBoolType:
834 case EIntType:
835 {
836 mLexer->MustGetNext (tkNumber);
837 } break;
838
839 case EStringType:
840 {
841 mLexer->MustGetNext (tkString);
842 } break;
843
844 case EUnknownType:
845 case EVoidType:
846 break;
847 }
848
849 info.val = mLexer->GetToken()->text;
850 mConstants << info;
851
852 mLexer->MustGetNext (tkSemicolon);
853 }
854
855 // ============================================================================
856 //
857 void BotscriptParser::ParseLabel()
858 {
859 CheckNotToplevel();
860 String labelName = GetTokenString();
861 ByteMark* mark = null;
862
863 // want no conflicts..
864 if (FindCommandByName (labelName))
865 Error ("label name `%1` conflicts with command name\n", labelName);
866
867 if (FindGlobalVariable (labelName))
868 Error ("label name `%1` conflicts with variable\n", labelName);
869
870 // See if a mark already exists for this label
871 for (UndefinedLabel& undf : mUndefinedLabels)
872 {
873 if (undf.name != labelName)
874 continue;
875
876 mark = undf.target;
877 buffer()->AdjustMark (mark);
878
879 // No longer undefined
880 mUndefinedLabels.Remove (undf);
881 break;
882 }
883
884 // Not found in unmarked lists, define it now
885 if (mark == null)
886 buffer()->AddMark (labelName);
887
888 mLexer->MustGetNext (tkColon);
889 }
890
891 // =============================================================================
892 //
893 void BotscriptParser::ParseEventdef()
894 {
895 EventDefinition* e = new EventDefinition;
896
897 mLexer->MustGetNext (tkNumber);
898 e->number = GetTokenString().ToLong();
899 mLexer->MustGetNext (tkColon);
900 mLexer->MustGetNext (tkSymbol);
901 e->name = mLexer->GetToken()->text;
902 mLexer->MustGetNext (tkParenStart);
903 mLexer->MustGetNext (tkParenEnd);
904 mLexer->MustGetNext (tkSemicolon);
905 AddEvent (e);
906 }
907
908 // =============================================================================
909 //
910 void BotscriptParser::ParseFuncdef()
911 {
912 CommandInfo* comm = new CommandInfo;
913
914 // Number
915 mLexer->MustGetNext (tkNumber);
916 comm->number = mLexer->GetToken()->text.ToLong();
917
918 mLexer->MustGetNext (tkColon);
919
920 // Name
921 mLexer->MustGetNext (tkSymbol);
922 comm->name = mLexer->GetToken()->text;
923
924 mLexer->MustGetNext (tkColon);
925
926 // Return value
927 mLexer->MustGetAnyOf ({tkInt, tkVoid, tkBool, tkStr});
928 comm->returnvalue = GetTypeByName (mLexer->GetToken()->text); // TODO
929 assert (comm->returnvalue != -1);
930
931 mLexer->MustGetNext (tkColon);
932
933 // Num args
934 mLexer->MustGetNext (tkNumber);
935 comm->numargs = mLexer->GetToken()->text.ToLong();
936
937 mLexer->MustGetNext (tkColon);
938
939 // Max args
940 mLexer->MustGetNext (tkNumber);
941 comm->maxargs = mLexer->GetToken()->text.ToLong();
942
943 // Argument types
944 int curarg = 0;
945
946 while (curarg < comm->maxargs)
947 {
948 CommandArgument arg;
949 mLexer->MustGetNext (tkColon);
950 mLexer->MustGetAnyOf ({tkInt, tkBool, tkStr});
951 EType type = GetTypeByName (mLexer->GetToken()->text);
952 assert (type != -1 && type != EVoidType);
953 arg.type = type;
954
955 mLexer->MustGetNext (tkParenStart);
956 mLexer->MustGetNext (tkSymbol);
957 arg.name = mLexer->GetToken()->text;
958
959 // If this is an optional parameter, we need the default value.
960 if (curarg >= comm->numargs)
961 {
962 mLexer->MustGetNext (tkAssign);
963
964 switch (type)
965 {
966 case EIntType:
967 case EBoolType:
968 mLexer->MustGetNext (tkNumber);
969 break;
970
971 case EStringType:
972 mLexer->MustGetNext (tkString);
973 break;
974
975 case EUnknownType:
976 case EVoidType:
977 break;
978 }
979
980 arg.defvalue = mLexer->GetToken()->text.ToLong();
981 }
982
983 mLexer->MustGetNext (tkParenEnd);
984 comm->args << arg;
985 curarg++;
986 }
987
988 mLexer->MustGetNext (tkSemicolon);
989 AddCommandDefinition (comm);
990 }
991
992 // ============================================================================
993 // Parses a command call
994 DataBuffer* BotscriptParser::ParseCommand (CommandInfo* comm)
995 {
996 DataBuffer* r = new DataBuffer (64);
997
998 if (mCurrentMode == ETopLevelMode)
999 Error ("command call at top level");
1000
1001 mLexer->MustGetNext (tkParenStart);
1002 mLexer->MustGetNext();
1003
1004 int curarg = 0;
1005
1006 while (1)
1007 {
1008 if (TokenIs (tkParenEnd))
1009 {
1010 if (curarg < comm->numargs)
1011 Error ("too few arguments passed to %1\n\tusage is: %2",
1012 comm->name, comm->GetSignature());
1013
1014 break;
1015 curarg++;
1016 }
1017
1018 if (curarg >= comm->maxargs)
1019 Error ("too many arguments passed to %1\n\tusage is: %2",
1020 comm->name, comm->GetSignature());
1021
1022 r->MergeAndDestroy (ParseExpression (comm->args[curarg].type));
1023 mLexer->MustGetNext();
1024
1025 if (curarg < comm->numargs - 1)
1026 {
1027 mLexer->TokenMustBe (tkComma);
1028 mLexer->MustGetNext();
1029 }
1030 else if (curarg < comm->maxargs - 1)
1031 {
1032 // Can continue, but can terminate as well.
1033 if (TokenIs (tkParenEnd))
1034 {
1035 curarg++;
1036 break;
1037 }
1038 else
1039 {
1040 mLexer->TokenMustBe (tkComma);
1041 mLexer->MustGetNext();
1042 }
1043 }
1044
1045 curarg++;
1046 }
1047
1048 // If the script skipped any optional arguments, fill in defaults.
1049 while (curarg < comm->maxargs)
1050 {
1051 r->WriteDWord (dhPushNumber);
1052 r->WriteDWord (comm->args[curarg].defvalue);
1053 curarg++;
1054 }
1055
1056 r->WriteDWord (dhCommand);
1057 r->WriteDWord (comm->number);
1058 r->WriteDWord (comm->maxargs);
1059
1060 return r;
1061 }
1062
1063 // ============================================================================
1064 // Is the given operator an assignment operator?
1065 //
1066 static bool IsAssignmentOperator (int oper)
1067 {
1068 switch (oper)
1069 {
1070 case OPER_ASSIGNADD:
1071 case OPER_ASSIGNSUB:
1072 case OPER_ASSIGNMUL:
1073 case OPER_ASSIGNDIV:
1074 case OPER_ASSIGNMOD:
1075 case OPER_ASSIGNLEFTSHIFT:
1076 case OPER_ASSIGNRIGHTSHIFT:
1077 case OPER_ASSIGN:
1078 return true;
1079 }
1080
1081 return false;
1082 }
1083
1084 // ============================================================================
1085 // Finds an operator's corresponding dataheader
1086 //
1087 static word GetDataHeaderByOperator (ScriptVariable* var, int oper)
1088 {
1089 if (IsAssignmentOperator (oper))
1090 {
1091 if (var == null)
1092 Error ("operator %d requires left operand to be a variable\n", oper);
1093
1094 // TODO: At the moment, vars only are global
1095 // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not
1096 // have data headers, instead they are expanded out in
1097 // the operator Parser
1098 switch (oper)
1099 {
1100 case OPER_ASSIGNADD: return dhAddGlobalVar;
1101 case OPER_ASSIGNSUB: return dhSubtractGlobalVar;
1102 case OPER_ASSIGNMUL: return dhMultiplyGlobalVar;
1103 case OPER_ASSIGNDIV: return dhDivideGlobalVar;
1104 case OPER_ASSIGNMOD: return dhModGlobalVar;
1105 case OPER_ASSIGN: return dhAssignGlobalVar;
1106
1107 default: Error ("bad assignment operator!\n");
1108 }
1109 }
1110
1111 switch (oper)
1112 {
1113 case OPER_ADD: return dhAdd;
1114 case OPER_SUBTRACT: return dhSubtract;
1115 case OPER_MULTIPLY: return dhMultiply;
1116 case OPER_DIVIDE: return dhDivide;
1117 case OPER_MODULUS: return dhModulus;
1118 case OPER_EQUALS: return dhEquals;
1119 case OPER_NOTEQUALS: return dhNotEquals;
1120 case OPER_LESSTHAN: return dhLessThan;
1121 case OPER_GREATERTHAN: return dhGreaterThan;
1122 case OPER_LESSTHANEQUALS: return dhAtMost;
1123 case OPER_GREATERTHANEQUALS: return dhAtLeast;
1124 case OPER_LEFTSHIFT: return dhLeftShift;
1125 case OPER_RIGHTSHIFT: return dhRightShift;
1126 case OPER_OR: return dhOrLogical;
1127 case OPER_AND: return dhAndLogical;
1128 case OPER_BITWISEOR: return dhOrBitwise;
1129 case OPER_BITWISEEOR: return dhEorBitwise;
1130 case OPER_BITWISEAND: return dhAndBitwise;
1131 }
1132
1133 Error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
1134 return 0;
1135 }
1136
1137 // ============================================================================
1138 // Parses an expression, potentially recursively
1139 //
1140 DataBuffer* BotscriptParser::ParseExpression (EType reqtype)
1141 {
1142 DataBuffer* retbuf = new DataBuffer (64);
1143
1144 // Parse first operand
1145 retbuf->MergeAndDestroy (ParseExprValue (reqtype));
1146
1147 // Parse any and all operators we get
1148 int oper;
1149
1150 while ( (oper = ParseOperator (true)) != -1)
1151 {
1152 // We peeked the operator, move forward now
1153 mLexer->Skip();
1154
1155 // Can't be an assignement operator, those belong in assignments.
1156 if (IsAssignmentOperator (oper))
1157 Error ("assignment operator inside expression");
1158
1159 // Parse the right operand.
1160 mLexer->MustGetNext();
1161 DataBuffer* rb = ParseExprValue (reqtype);
1162
1163 if (oper == OPER_TERNARY)
1164 {
1165 // Ternary operator requires - naturally - a third operand.
1166 mLexer->MustGetNext (tkColon);
1167 mLexer->MustGetNext();
1168 DataBuffer* tb = ParseExprValue (reqtype);
1169
1170 // It also is handled differently: there isn't a dataheader for ternary
1171 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this.
1172 // Behold, big block of writing madness! :P
1173 ByteMark* mark1 = retbuf->AddMark (""); // start of "else" case
1174 ByteMark* mark2 = retbuf->AddMark (""); // end of expression
1175 retbuf->WriteDWord (dhIfNotGoto); // if the first operand (condition)
1176 retbuf->AddReference (mark1); // didn't eval true, jump into mark1
1177 retbuf->MergeAndDestroy (rb); // otherwise, perform second operand (true case)
1178 retbuf->WriteDWord (dhGoto); // afterwards, jump to the end, which is
1179 retbuf->AddReference (mark2); // marked by mark2.
1180 retbuf->AdjustMark (mark1); // move mark1 at the end of the true case
1181 retbuf->MergeAndDestroy (tb); // perform third operand (false case)
1182 retbuf->AdjustMark (mark2); // move the ending mark2 here
1183 }
1184 else
1185 {
1186 // write to buffer
1187 retbuf->MergeAndDestroy (rb);
1188 retbuf->WriteDWord (GetDataHeaderByOperator (null, oper));
1189 }
1190 }
1191
1192 return retbuf;
1193 }
1194
1195 // ============================================================================
1196 // Parses an operator string. Returns the operator number code.
1197 //
1198 #define ISNEXT(C) (mLexer->PeekNextString (peek ? 1 : 0) == C)
1199
1200 int BotscriptParser::ParseOperator (bool peek)
1201 {
1202 String oper;
1203
1204 if (peek)
1205 oper += mLexer->PeekNextString();
1206 else
1207 oper += GetTokenString();
1208
1209 if (-oper == "strlen")
1210 return OPER_STRLEN;
1211
1212 // Check one-char operators
1213 bool equalsnext = ISNEXT ("=");
1214
1215 int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN :
1216 (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN :
1217 (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN :
1218 (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND :
1219 (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR :
1220 (oper == "+" && !equalsnext) ? OPER_ADD :
1221 (oper == "-" && !equalsnext) ? OPER_SUBTRACT :
1222 (oper == "*" && !equalsnext) ? OPER_MULTIPLY :
1223 (oper == "/" && !equalsnext) ? OPER_DIVIDE :
1224 (oper == "%" && !equalsnext) ? OPER_MODULUS :
1225 (oper == "^") ? OPER_BITWISEEOR :
1226 (oper == "?") ? OPER_TERNARY :
1227 -1;
1228
1229 if (o != -1)
1230 {
1231 return o;
1232 }
1233
1234 // Two-char operators
1235 oper += mLexer->PeekNextString (peek ? 1 : 0);
1236 equalsnext = mLexer->PeekNextString (peek ? 2 : 1) == ("=");
1237
1238 o = (oper == "+=") ? OPER_ASSIGNADD :
1239 (oper == "-=") ? OPER_ASSIGNSUB :
1240 (oper == "*=") ? OPER_ASSIGNMUL :
1241 (oper == "/=") ? OPER_ASSIGNDIV :
1242 (oper == "%=") ? OPER_ASSIGNMOD :
1243 (oper == "==") ? OPER_EQUALS :
1244 (oper == "!=") ? OPER_NOTEQUALS :
1245 (oper == ">=") ? OPER_GREATERTHANEQUALS :
1246 (oper == "<=") ? OPER_LESSTHANEQUALS :
1247 (oper == "&&") ? OPER_AND :
1248 (oper == "||") ? OPER_OR :
1249 (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT :
1250 (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT :
1251 -1;
1252
1253 if (o != -1)
1254 {
1255 mLexer->MustGetNext();
1256 return o;
1257 }
1258
1259 // Three-char opers
1260 oper += mLexer->PeekNextString (peek ? 2 : 1);
1261 o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT :
1262 oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT :
1263 -1;
1264
1265 if (o != -1)
1266 {
1267 mLexer->MustGetNext();
1268 mLexer->MustGetNext();
1269 }
1270
1271 return o;
1272 }
1273
1274 // ============================================================================
1275 //
1276 String BotscriptParser::ParseFloat()
1277 {
1278 mLexer->TokenMustBe (tkNumber);
1279 String floatstring = GetTokenString();
1280 Lexer::Token tok;
1281
1282 // Go after the decimal point
1283 if (mLexer->PeekNext (&tok) && tok.type == tkDot)
1284 {
1285 mLexer->Skip();
1286 mLexer->MustGetNext (tkNumber);
1287 floatstring += ".";
1288 floatstring += GetTokenString();
1289 }
1290
1291 return floatstring;
1292 }
1293
1294 // ============================================================================
1295 // Parses a value in the expression and returns the data needed to push
1296 // it, contained in a data buffer. A value can be either a variable, a command,
1297 // a literal or an expression.
1298 //
1299 DataBuffer* BotscriptParser::ParseExprValue (EType reqtype)
1300 {
1301 DataBuffer* b = new DataBuffer (16);
1302 ScriptVariable* g;
1303
1304 // Prefixing "!" means negation.
1305 bool negate = TokenIs (tkExclamationMark);
1306
1307 if (negate) // Jump past the "!"
1308 mLexer->Skip();
1309
1310 if (TokenIs (tkParenStart))
1311 {
1312 // Expression
1313 mLexer->MustGetNext();
1314 DataBuffer* c = ParseExpression (reqtype);
1315 b->MergeAndDestroy (c);
1316 mLexer->MustGetNext (tkParenEnd);
1317 }
1318 else if (CommandInfo* comm = FindCommandByName (GetTokenString()))
1319 {
1320 delete b;
1321
1322 // Command
1323 if (reqtype && comm->returnvalue != reqtype)
1324 Error ("%1 returns an incompatible data type", comm->name);
1325
1326 b = ParseCommand (comm);
1327 }
1328 else if (ConstantInfo* constant = FindConstant (GetTokenString()))
1329 {
1330 // Type check
1331 if (reqtype != constant->type)
1332 Error ("constant `%1` is %2, expression requires %3\n",
1333 constant->name, GetTypeName (constant->type),
1334 GetTypeName (reqtype));
1335
1336 switch (constant->type)
1337 {
1338 case EBoolType:
1339 case EIntType:
1340 {
1341 b->WriteDWord (dhPushNumber);
1342 b->WriteDWord (constant->val.ToLong());
1343 break;
1344 }
1345
1346 case EStringType:
1347 {
1348 b->WriteStringIndex (constant->val);
1349 break;
1350 }
1351
1352 case EVoidType:
1353 case EUnknownType:
1354 break;
1355 }
1356 }
1357 else if ((g = FindGlobalVariable (GetTokenString())))
1358 {
1359 // Global variable
1360 b->WriteDWord (dhPushGlobalVar);
1361 b->WriteDWord (g->index);
1362 }
1363 else
1364 {
1365 // If nothing else, check for literal
1366 switch (reqtype)
1367 {
1368 case EVoidType:
1369 case EUnknownType:
1370 {
1371 Error ("unknown identifier `%1` (expected keyword, function or variable)", GetTokenString());
1372 break;
1373 }
1374
1375 case EBoolType:
1376 case EIntType:
1377 {
1378 mLexer->TokenMustBe (tkNumber);
1379
1380 // All values are written unsigned - thus we need to write the value's
1381 // absolute value, followed by an unary minus for negatives.
1382 b->WriteDWord (dhPushNumber);
1383
1384 long v = GetTokenString().ToLong();
1385 b->WriteDWord (static_cast<word> (abs (v)));
1386
1387 if (v < 0)
1388 b->WriteDWord (dhUnaryMinus);
1389
1390 break;
1391 }
1392
1393 case EStringType:
1394 {
1395 // PushToStringTable either returns the string index of the
1396 // string if it finds it in the table, or writes it to the
1397 // table and returns it index if it doesn't find it there.
1398 mLexer->TokenMustBe (tkString);
1399 b->WriteStringIndex (GetTokenString());
1400 break;
1401 }
1402 }
1403 }
1404
1405 // Negate it now if desired
1406 if (negate)
1407 b->WriteDWord (dhNegateLogical);
1408
1409 return b;
1410 }
1411
1412 // ============================================================================
1413 // Parses an assignment. An assignment starts with a variable name, followed
1414 // by an assignment operator, followed by an expression value. Expects current
1415 // token to be the name of the variable, and expects the variable to be given.
1416 //
1417 DataBuffer* BotscriptParser::ParseAssignment (ScriptVariable* var)
1418 {
1419 // Get an operator
1420 mLexer->MustGetNext();
1421 int oper = ParseOperator();
1422
1423 if (IsAssignmentOperator (oper) == false)
1424 Error ("expected assignment operator");
1425
1426 if (mCurrentMode == ETopLevelMode)
1427 Error ("can't alter variables at top level");
1428
1429 // Parse the right operand
1430 mLexer->MustGetNext();
1431 DataBuffer* retbuf = new DataBuffer;
1432 DataBuffer* expr = ParseExpression (var->type);
1433
1434 // <<= and >>= do not have data headers. Solution: expand them.
1435 // a <<= b -> a = a << b
1436 // a >>= b -> a = a >> b
1437 if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT)
1438 {
1439 retbuf->WriteDWord (var->IsGlobal() ? dhPushGlobalVar : dhPushLocalVar);
1440 retbuf->WriteDWord (var->index);
1441 retbuf->MergeAndDestroy (expr);
1442 retbuf->WriteDWord ((oper == OPER_ASSIGNLEFTSHIFT) ? dhLeftShift : dhRightShift);
1443 retbuf->WriteDWord (var->IsGlobal() ? dhAssignGlobalVar : dhAssignLocalVar);
1444 retbuf->WriteDWord (var->index);
1445 }
1446 else
1447 {
1448 retbuf->MergeAndDestroy (expr);
1449 long dh = GetDataHeaderByOperator (var, oper);
1450 retbuf->WriteDWord (dh);
1451 retbuf->WriteDWord (var->index);
1452 }
1453
1454 return retbuf;
1455 }
1456
1457 // ============================================================================
1458 //
1459 void BotscriptParser::PushScope()
1460 {
1461 mScopeCursor++;
1462
1463 if (mScopeCursor >= MAX_SCOPE)
1464 Error ("too deep scope");
1465
1466 ScopeInfo* info = &SCOPE (0);
1467 info->type = eUnknownScope;
1468 info->mark1 = null;
1469 info->mark2 = null;
1470 info->buffer1 = null;
1471 info->casecursor = -1;
1472
1473 for (int i = 0; i < MAX_CASE; i++)
1474 {
1475 info->casemarks[i] = null;
1476 info->casebuffers[i] = null;
1477 info->casenumbers[i] = -1;
1478 }
1479 }
1480
1481 // ============================================================================
1482 //
1483 DataBuffer* BotscriptParser::ParseStatement()
1484 {
1485 if (FindConstant (GetTokenString())) // There should not be constants here.
1486 Error ("invalid use for constant\n");
1487
1488 // If it's a variable, expect assignment.
1489 if (ScriptVariable* var = FindGlobalVariable (GetTokenString()))
1490 return ParseAssignment (var);
1491
1492 return null;
1493 }
1494
1495 // ============================================================================
1496 //
1497 void BotscriptParser::AddSwitchCase (DataBuffer* b)
1498 {
1499 ScopeInfo* info = &SCOPE (0);
1500
1501 info->casecursor++;
1502
1503 if (info->casecursor >= MAX_CASE)
1504 Error ("too many cases in one switch");
1505
1506 // Init a mark for the case buffer
1507 ByteMark* casemark = buffer()->AddMark ("");
1508 info->casemarks[info->casecursor] = casemark;
1509
1510 // Add a reference to the mark. "case" and "default" both
1511 // add the necessary bytecode before the reference.
1512 if (b)
1513 b->AddReference (casemark);
1514 else
1515 buffer()->AddReference (casemark);
1516
1517 // Init a buffer for the case block and tell the object
1518 // writer to record all written data to it.
1519 info->casebuffers[info->casecursor] = mSwitchBuffer = new DataBuffer;
1520 }
1521
1522 // ============================================================================
1523 //
1524 ConstantInfo* BotscriptParser::FindConstant (const String& tok)
1525 {
1526 for (int i = 0; i < mConstants.Size(); i++)
1527 if (mConstants[i].name == tok)
1528 return &mConstants[i];
1529
1530 return null;
1531 }
1532
1533 // ============================================================================
1534 //
1535 bool BotscriptParser::TokenIs (EToken a)
1536 {
1537 return (mLexer->GetTokenType() == a);
1538 }
1539
1540 // ============================================================================
1541 //
1542 String BotscriptParser::GetTokenString()
1543 {
1544 return mLexer->GetToken()->text;
1545 }
1546
1547 // ============================================================================
1548 //
1549 String BotscriptParser::DescribePosition() const
1550 {
1551 Lexer::Token* tok = mLexer->GetToken();
1552 return tok->file + ":" + String (tok->line) + ":" + String (tok->column);
1553 }
1554
1555 // ============================================================================
1556 //
1557 DataBuffer* BotscriptParser::buffer()
1558 {
1559 if (mSwitchBuffer != null)
1560 return mSwitchBuffer;
1561
1562 if (mCurrentMode == EMainLoopMode)
1563 return mMainLoopBuffer;
1564
1565 if (mCurrentMode == EOnenterMode)
1566 return mOnEnterBuffer;
1567
1568 return mMainBuffer;
1569 }
1570
1571 // ============================================================================
1572 //
1573 void BotscriptParser::writeMemberBuffers()
1574 {
1575 // If there was no mainloop defined, write a dummy one now.
1576 if (mGotMainLoop == false)
1577 {
1578 mMainLoopBuffer->WriteDWord (dhMainLoop);
1579 mMainLoopBuffer->WriteDWord (dhEndMainLoop);
1580 }
1581
1582 // Write the onenter and mainloop buffers, in that order in particular.
1583 for (DataBuffer** bufp : List<DataBuffer**> ({&mOnEnterBuffer, &mMainLoopBuffer}))
1584 {
1585 buffer()->MergeAndDestroy (*bufp);
1586
1587 // Clear the buffer afterwards for potential next state
1588 *bufp = new DataBuffer;
1589 }
1590
1591 // Next state definitely has no mainloop yet
1592 mGotMainLoop = false;
1593 }
1594
1595 // ============================================================================
1596 //
1597 // Write string table
1598 //
1599 void BotscriptParser::WriteStringTable()
1600 {
1601 int stringcount = CountStringsInTable();
1602
1603 if (stringcount == 0)
1604 return;
1605
1606 // Write header
1607 mMainBuffer->WriteDWord (dhStringList);
1608 mMainBuffer->WriteDWord (stringcount);
1609
1610 // Write all strings
1611 for (int i = 0; i < stringcount; i++)
1612 mMainBuffer->WriteString (GetStringTable()[i]);
1613 }
1614
1615 // ============================================================================
1616 //
1617 // Write the compiled bytecode to a file
1618 //
1619 void BotscriptParser::WriteToFile (String outfile)
1620 {
1621 FILE* fp = fopen (outfile, "w");
1622
1623 if (fp == null)
1624 Error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
1625
1626 // First, resolve references
1627 for (MarkReference* ref : mMainBuffer->GetReferences())
1628 {
1629 // Substitute the placeholder with the mark position
1630 for (int v = 0; v < 4; v++)
1631 mMainBuffer->GetBuffer()[ref->pos + v] = ((ref->target->pos) << (8 * v)) & 0xFF;
1632
1633 Print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos);
1634 }
1635
1636 // Then, dump the main buffer to the file
1637 fwrite (mMainBuffer->GetBuffer(), 1, mMainBuffer->GetWrittenSize(), fp);
1638 Print ("-- %1 byte%s1 written to %2\n", mMainBuffer->GetWrittenSize(), outfile);
1639 fclose (fp);
1640 }

mercurial