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" |
1 #include "objwriter.h" |
48 #include "scriptreader.h" |
2 #include "scriptreader.h" |
49 #include "events.h" |
3 #include "events.h" |
50 #include "commands.h" |
4 #include "commands.h" |
51 #include "stringtable.h" |
5 #include "stringtable.h" |
52 #include "variables.h" |
6 #include "variables.h" |
53 #include "array.h" |
7 #include "containers.h" |
54 |
8 |
55 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
9 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
56 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
10 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
57 |
11 |
58 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
12 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
61 #define SCOPE(n) scopestack[g_ScopeCursor - n] |
15 #define SCOPE(n) scopestack[g_ScopeCursor - n] |
62 |
16 |
63 int g_NumStates = 0; |
17 int g_NumStates = 0; |
64 int g_NumEvents = 0; |
18 int g_NumEvents = 0; |
65 parsermode_e g_CurMode = MODE_TOPLEVEL; |
19 parsermode_e g_CurMode = MODE_TOPLEVEL; |
66 str g_CurState = ""; |
20 string g_CurState = ""; |
67 bool g_stateSpawnDefined = false; |
21 bool g_stateSpawnDefined = false; |
68 bool g_GotMainLoop = false; |
22 bool g_GotMainLoop = false; |
69 unsigned int g_ScopeCursor = 0; |
23 unsigned int g_ScopeCursor = 0; |
70 DataBuffer* g_IfExpression = NULL; |
24 DataBuffer* g_IfExpression = null; |
71 bool g_CanElse = false; |
25 bool g_CanElse = false; |
72 str* g_UndefinedLabels[MAX_MARKS]; |
26 string* g_UndefinedLabels[MAX_MARKS]; |
73 bool g_Neurosphere = false; // neurosphere-compat |
27 bool g_Neurosphere = false; // neurosphere-compat |
74 array<constinfo_t> g_ConstInfo; |
28 list<constinfo_t> g_ConstInfo; |
75 |
29 |
76 // ============================================================================ |
30 // ============================================================================ |
77 // Main parser code. Begins read of the script file, checks the syntax of it |
31 // 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 |
32 // 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. |
33 // of necessary buffering so stuff is written in the correct order. |
81 // Zero the entire block stack first |
35 // Zero the entire block stack first |
82 for (int i = 0; i < MAX_SCOPE; i++) |
36 for (int i = 0; i < MAX_SCOPE; i++) |
83 ZERO(scopestack[i]); |
37 ZERO(scopestack[i]); |
84 |
38 |
85 for (int i = 0; i < MAX_MARKS; i++) |
39 for (int i = 0; i < MAX_MARKS; i++) |
86 g_UndefinedLabels[i] = NULL; |
40 g_UndefinedLabels[i] = null; |
87 |
41 |
88 while (Next()) { |
42 while (Next()) { |
89 // Check if else is potentically valid |
43 // Check if else is potentically valid |
90 if (token == "else" && !g_CanElse) |
44 if (token == "else" && !g_CanElse) |
91 ParserError ("else without preceding if"); |
45 ParserError ("else without preceding if"); |
99 MustString (); |
53 MustString (); |
100 |
54 |
101 // State name must be a word. |
55 // State name must be a word. |
102 if (token.first (" ") != token.len()) |
56 if (token.first (" ") != token.len()) |
103 ParserError ("state name must be a single word, got `%s`", token.chars()); |
57 ParserError ("state name must be a single word, got `%s`", token.chars()); |
104 str statename = token; |
58 string statename = token; |
105 |
59 |
106 // stateSpawn is special - it *must* be defined. If we |
60 // stateSpawn is special - it *must* be defined. If we |
107 // encountered it, then mark down that we have it. |
61 // encountered it, then mark down that we have it. |
108 if (-token == "statespawn") |
62 if (-token == "statespawn") |
109 g_stateSpawnDefined = true; |
63 g_stateSpawnDefined = true; |
183 TYPE_BOOL; |
137 TYPE_BOOL; |
184 |
138 |
185 MustNext (); |
139 MustNext (); |
186 |
140 |
187 // Var name must not be a number |
141 // Var name must not be a number |
188 if (token.isnumber()) |
142 if (token.is_numeric()) |
189 ParserError ("variable name must not be a number"); |
143 ParserError ("variable name must not be a number"); |
190 |
144 |
191 str varname = token; |
145 string varname = token; |
192 ScriptVar* var = DeclareGlobalVariable (this, type, varname); |
146 ScriptVar* var = DeclareGlobalVariable (this, type, varname); |
193 |
147 |
194 if (!var) |
148 if (!var) |
195 ParserError ("declaring %s variable %s failed", |
149 ParserError ("declaring %s variable %s failed", |
196 g_CurState.len() ? "state" : "global", varname.chars()); |
150 g_CurState.len() ? "state" : "global", varname.chars()); |
426 // the case tree. The closing event will write the actual |
380 // the case tree. The closing event will write the actual |
427 // blocks and move the marks appropriately. |
381 // blocks and move the marks appropriately. |
428 // AddSwitchCase will add the reference to the mark |
382 // AddSwitchCase will add the reference to the mark |
429 // for the case block that this heralds, and takes care |
383 // for the case block that this heralds, and takes care |
430 // of buffering setup and stuff like that. |
384 // of buffering setup and stuff like that. |
431 // NULL the switch buffer for the case-go-to statement, |
385 // null the switch buffer for the case-go-to statement, |
432 // we want it all under the switch, not into the case-buffers. |
386 // we want it all under the switch, not into the case-buffers. |
433 w->SwitchBuffer = NULL; |
387 w->SwitchBuffer = null; |
434 w->Write (DH_CASEGOTO); |
388 w->Write (DH_CASEGOTO); |
435 w->Write (num); |
389 w->Write (num); |
436 AddSwitchCase (w, NULL); |
390 AddSwitchCase (w, null); |
437 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
391 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
438 continue; |
392 continue; |
439 } |
393 } |
440 |
394 |
441 if (token == "default") { |
395 if (token == "default") { |
640 // Switch closes. Move down to the record buffer of |
594 // Switch closes. Move down to the record buffer of |
641 // the lower block. |
595 // the lower block. |
642 if (SCOPE(1).casecursor != -1) |
596 if (SCOPE(1).casecursor != -1) |
643 w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; |
597 w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; |
644 else |
598 else |
645 w->SwitchBuffer = NULL; |
599 w->SwitchBuffer = null; |
646 |
600 |
647 // If there was a default in the switch, write its header down now. |
601 // 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 |
602 // If not, write instruction to jump to the end of switch after |
649 // the headers (thus won't fall-through if no case matched) |
603 // the headers (thus won't fall-through if no case matched) |
650 if (SCOPE(0).buffer1) |
604 if (SCOPE(0).buffer1) |
899 retbuf->Merge (tb); // perform third operand (false case) |
853 retbuf->Merge (tb); // perform third operand (false case) |
900 retbuf->MoveMark (mark2); // move the ending mark2 here |
854 retbuf->MoveMark (mark2); // move the ending mark2 here |
901 } else { |
855 } else { |
902 // Write to buffer |
856 // Write to buffer |
903 retbuf->Merge (rb); |
857 retbuf->Merge (rb); |
904 retbuf->Write (DataHeaderByOperator (NULL, oper)); |
858 retbuf->Write (DataHeaderByOperator (null, oper)); |
905 } |
859 } |
906 } |
860 } |
907 |
861 |
908 return retbuf; |
862 return retbuf; |
909 } |
863 } |
910 |
864 |
911 // ============================================================================ |
865 // ============================================================================ |
912 // Parses an operator string. Returns the operator number code. |
866 // Parses an operator string. Returns the operator number code. |
913 #define ISNEXT(char) (!PeekNext (peek ? 1 : 0) == char) |
867 #define ISNEXT(C) (PeekNext (peek ? 1 : 0) == C) |
914 int ScriptReader::ParseOperator (bool peek) { |
868 int ScriptReader::ParseOperator (bool peek) { |
915 str oper; |
869 string oper; |
916 if (peek) |
870 if (peek) |
917 oper += PeekNext (); |
871 oper += PeekNext (); |
918 else |
872 else |
919 oper += token; |
873 oper += token; |
920 |
874 |
1019 constinfo_t* constant = FindConstant (token); |
973 constinfo_t* constant = FindConstant (token); |
1020 if (!constant || constant->type != TYPE_STRING) |
974 if (!constant || constant->type != TYPE_STRING) |
1021 ParserError ("strlen only works with const str"); |
975 ParserError ("strlen only works with const str"); |
1022 |
976 |
1023 if (reqtype != TYPE_INT) |
977 if (reqtype != TYPE_INT) |
1024 ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); |
978 ParserError ("strlen returns int but %s is expected\n", GetTypeName (reqtype).c_str()); |
1025 |
979 |
1026 b->Write (DH_PUSHNUMBER); |
980 b->Write (DH_PUSHNUMBER); |
1027 b->Write (constant->val.len ()); |
981 b->Write (constant->val.len ()); |
1028 |
982 |
1029 MustNext (")"); |
983 MustNext (")"); |
1042 b = ParseCommand (comm); |
996 b = ParseCommand (comm); |
1043 } else if (constinfo_t* constant = FindConstant (token)) { |
997 } else if (constinfo_t* constant = FindConstant (token)) { |
1044 // Type check |
998 // Type check |
1045 if (reqtype != constant->type) |
999 if (reqtype != constant->type) |
1046 ParserError ("constant `%s` is %s, expression requires %s\n", |
1000 ParserError ("constant `%s` is %s, expression requires %s\n", |
1047 (char*)constant->name, (char*)GetTypeName (constant->type), |
1001 constant->name.c_str(), GetTypeName (constant->type).c_str(), |
1048 (char*)GetTypeName (reqtype)); |
1002 GetTypeName (reqtype).c_str()); |
1049 |
1003 |
1050 switch (constant->type) { |
1004 switch (constant->type) { |
1051 case TYPE_BOOL: |
1005 case TYPE_BOOL: |
1052 case TYPE_INT: |
1006 case TYPE_INT: |
1053 b->Write (DH_PUSHNUMBER); |
1007 b->Write (DH_PUSHNUMBER); |
1150 |
1104 |
1151 ScopeInfo* info = &SCOPE(0); |
1105 ScopeInfo* info = &SCOPE(0); |
1152 info->type = SCOPETYPE_UNKNOWN; |
1106 info->type = SCOPETYPE_UNKNOWN; |
1153 info->mark1 = 0; |
1107 info->mark1 = 0; |
1154 info->mark2 = 0; |
1108 info->mark2 = 0; |
1155 info->buffer1 = NULL; |
1109 info->buffer1 = null; |
1156 info->casecursor = -1; |
1110 info->casecursor = -1; |
1157 for (int i = 0; i < MAX_CASE; i++) { |
1111 for (int i = 0; i < MAX_CASE; i++) { |
1158 info->casemarks[i] = MAX_MARKS; |
1112 info->casemarks[i] = MAX_MARKS; |
1159 info->casebuffers[i] = NULL; |
1113 info->casebuffers[i] = null; |
1160 info->casenumbers[i] = -1; |
1114 info->casenumbers[i] = -1; |
1161 } |
1115 } |
1162 } |
1116 } |
1163 |
1117 |
1164 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
1118 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
1193 // Init a buffer for the case block and tell the object |
1147 // Init a buffer for the case block and tell the object |
1194 // writer to record all written data to it. |
1148 // writer to record all written data to it. |
1195 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
1149 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
1196 } |
1150 } |
1197 |
1151 |
1198 constinfo_t* FindConstant (str token) { |
1152 constinfo_t* FindConstant (string token) { |
1199 for (uint i = 0; i < g_ConstInfo.size(); i++) |
1153 for (uint i = 0; i < g_ConstInfo.size(); i++) |
1200 if (g_ConstInfo[i].name == token) |
1154 if (g_ConstInfo[i].name == token) |
1201 return &g_ConstInfo[i]; |
1155 return &g_ConstInfo[i]; |
1202 return NULL; |
1156 return null; |
1203 } |
1157 } |