parser.cxx

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

mercurial