48 #include "scriptreader.h" |
48 #include "scriptreader.h" |
49 #include "events.h" |
49 #include "events.h" |
50 #include "commands.h" |
50 #include "commands.h" |
51 #include "stringtable.h" |
51 #include "stringtable.h" |
52 #include "variables.h" |
52 #include "variables.h" |
|
53 #include "array.h" |
53 |
54 |
54 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
55 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
55 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
56 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
56 |
57 |
57 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
58 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
68 unsigned int g_ScopeCursor = 0; |
69 unsigned int g_ScopeCursor = 0; |
69 DataBuffer* g_IfExpression = NULL; |
70 DataBuffer* g_IfExpression = NULL; |
70 bool g_CanElse = false; |
71 bool g_CanElse = false; |
71 str* g_UndefinedLabels[MAX_MARKS]; |
72 str* g_UndefinedLabels[MAX_MARKS]; |
72 bool g_Neurosphere = false; // neurosphere-compat |
73 bool g_Neurosphere = false; // neurosphere-compat |
|
74 array<constinfo_t> g_ConstInfo; |
73 |
75 |
74 // ============================================================================ |
76 // ============================================================================ |
75 // Main parser code. Begins read of the script file, checks the syntax of it |
77 // Main parser code. Begins read of the script file, checks the syntax of it |
76 // and writes the data to the object file via ObjWriter - which also takes care |
78 // and writes the data to the object file via ObjWriter - which also takes care |
77 // of necessary buffering so stuff is written in the correct order. |
79 // of necessary buffering so stuff is written in the correct order. |
256 |
258 |
257 if (token == "else") { |
259 if (token == "else") { |
258 MUST_NOT_TOPLEVEL |
260 MUST_NOT_TOPLEVEL |
259 MustNext ("{"); |
261 MustNext ("{"); |
260 |
262 |
261 // Don't use PushScope that will reset the scope. |
263 // Don't use PushScope as it resets the scope |
262 g_ScopeCursor++; |
264 g_ScopeCursor++; |
263 if (g_ScopeCursor >= MAX_SCOPE) |
265 if (g_ScopeCursor >= MAX_SCOPE) |
264 ParserError ("too deep scope"); |
266 ParserError ("too deep scope"); |
265 |
267 |
266 if (SCOPE(0).type != SCOPETYPE_IF) |
268 if (SCOPE(0).type != SCOPETYPE_IF) |
548 // Not found in unmarked lists, define it now |
550 // Not found in unmarked lists, define it now |
549 if (mark == -1) |
551 if (mark == -1) |
550 w->AddMark (token); |
552 w->AddMark (token); |
551 |
553 |
552 MustNext (":"); |
554 MustNext (":"); |
|
555 continue; |
|
556 } |
|
557 |
|
558 // ============================================================ |
|
559 if (token == "const") { |
|
560 constinfo_t info; |
|
561 |
|
562 // Get the type |
|
563 MustNext (); |
|
564 info.type = GetTypeByName (token); |
|
565 |
|
566 if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) |
|
567 ParserError ("unknown type `%s` for constant", (char*)token); |
|
568 |
|
569 MustNext (); |
|
570 info.name = token; |
|
571 |
|
572 MustNext ("="); |
|
573 |
|
574 switch (info.type) { |
|
575 case TYPE_BOOL: |
|
576 case TYPE_INT: |
|
577 MustNumber (false); |
|
578 info.val = token; |
|
579 break; |
|
580 case TYPE_STRING: |
|
581 MustString (); |
|
582 info.val = token; |
|
583 break; |
|
584 case TYPE_FLOAT: |
|
585 MustNext (); |
|
586 info.val = ParseFloat (); |
|
587 break; |
|
588 case TYPE_UNKNOWN: |
|
589 case TYPE_VOID: |
|
590 break; |
|
591 } |
|
592 |
|
593 g_ConstInfo << info; |
|
594 |
|
595 MustNext (";"); |
553 continue; |
596 continue; |
554 } |
597 } |
555 |
598 |
556 // ============================================================ |
599 // ============================================================ |
557 if (token == "}") { |
600 if (token == "}") { |
819 return 0; |
862 return 0; |
820 } |
863 } |
821 |
864 |
822 // ============================================================================ |
865 // ============================================================================ |
823 // Parses an expression, potentially recursively |
866 // Parses an expression, potentially recursively |
824 DataBuffer* ScriptReader::ParseExpression (int reqtype) { |
867 DataBuffer* ScriptReader::ParseExpression (type_e reqtype) { |
825 DataBuffer* retbuf = new DataBuffer (64); |
868 DataBuffer* retbuf = new DataBuffer (64); |
826 |
869 |
827 // Parse first operand |
870 // Parse first operand |
828 retbuf->Merge (ParseExprValue (reqtype)); |
871 retbuf->Merge (ParseExprValue (reqtype)); |
829 |
872 |
938 |
984 |
939 return o; |
985 return o; |
940 } |
986 } |
941 |
987 |
942 // ============================================================================ |
988 // ============================================================================ |
|
989 str ScriptReader::ParseFloat () { |
|
990 MustNumber (true); |
|
991 str floatstring = token; |
|
992 |
|
993 // Go after the decimal point |
|
994 if (PeekNext () == ".") { |
|
995 Next ("."); |
|
996 MustNumber (false); |
|
997 floatstring += "."; |
|
998 floatstring += token; |
|
999 } |
|
1000 |
|
1001 return floatstring; |
|
1002 } |
|
1003 |
|
1004 // ============================================================================ |
943 // Parses a value in the expression and returns the data needed to push |
1005 // Parses a value in the expression and returns the data needed to push |
944 // it, contained in a data buffer. A value can be either a variable, a command, |
1006 // it, contained in a data buffer. A value can be either a variable, a command, |
945 // a literal or an expression. |
1007 // a literal or an expression. |
946 DataBuffer* ScriptReader::ParseExprValue (int reqtype) { |
1008 DataBuffer* ScriptReader::ParseExprValue (type_e reqtype) { |
947 DataBuffer* b = new DataBuffer(16); |
1009 DataBuffer* b = new DataBuffer(16); |
948 |
1010 |
949 ScriptVar* g; |
1011 ScriptVar* g; |
950 |
1012 |
951 // Prefixing "!" means negation. |
1013 // Prefixing "!" means negation. |
952 bool negate = (token == "!"); |
1014 bool negate = (token == "!"); |
953 if (negate) // Jump past the "!" |
1015 if (negate) // Jump past the "!" |
954 Next (); |
1016 Next (); |
955 |
1017 |
956 if (token == "(") { |
1018 // Handle strlen |
|
1019 if (token == "strlen") { |
|
1020 MustNext ("("); |
|
1021 MustNext (); |
|
1022 |
|
1023 // By this token we should get a string constant. |
|
1024 constinfo_t* constant = FindConstant (token); |
|
1025 if (!constant || constant->type != TYPE_STRING) |
|
1026 ParserError ("strlen only works with const str"); |
|
1027 |
|
1028 if (reqtype != TYPE_INT) |
|
1029 ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); |
|
1030 |
|
1031 b->Write<word> (DH_PUSHNUMBER); |
|
1032 b->Write<word> (constant->val.len ()); |
|
1033 |
|
1034 MustNext (")"); |
|
1035 } else if (token == "(") { |
957 // Expression |
1036 // Expression |
958 MustNext (); |
1037 MustNext (); |
959 DataBuffer* c = ParseExpression (reqtype); |
1038 DataBuffer* c = ParseExpression (reqtype); |
960 b->Merge (c); |
1039 b->Merge (c); |
961 MustNext (")"); |
1040 MustNext (")"); |
964 |
1043 |
965 // Command |
1044 // Command |
966 if (reqtype && comm->returnvalue != reqtype) |
1045 if (reqtype && comm->returnvalue != reqtype) |
967 ParserError ("%s returns an incompatible data type", comm->name.chars()); |
1046 ParserError ("%s returns an incompatible data type", comm->name.chars()); |
968 b = ParseCommand (comm); |
1047 b = ParseCommand (comm); |
|
1048 } else if (constinfo_t* constant = FindConstant (token)) { |
|
1049 // Type check |
|
1050 if (reqtype != constant->type) |
|
1051 ParserError ("constant `%s` is %s, expression requires %s\n", |
|
1052 (char*)constant->name, (char*)GetTypeName (constant->type), |
|
1053 (char*)GetTypeName (reqtype)); |
|
1054 |
|
1055 switch (constant->type) { |
|
1056 case TYPE_BOOL: |
|
1057 case TYPE_INT: |
|
1058 b->Write<word> (DH_PUSHNUMBER); |
|
1059 b->Write<word> (atoi (constant->val)); |
|
1060 break; |
|
1061 case TYPE_FLOAT: |
|
1062 b->WriteFloat (constant->val); |
|
1063 break; |
|
1064 case TYPE_STRING: |
|
1065 b->WriteString (constant->val); |
|
1066 break; |
|
1067 case TYPE_VOID: |
|
1068 case TYPE_UNKNOWN: |
|
1069 break; |
|
1070 } |
969 } else if ((g = FindGlobalVariable (token))) { |
1071 } else if ((g = FindGlobalVariable (token))) { |
970 // Global variable |
1072 // Global variable |
971 b->Write<word> (DH_PUSHGLOBALVAR); |
1073 b->Write<word> (DH_PUSHGLOBALVAR); |
972 b->Write<word> (g->index); |
1074 b->Write<word> (g->index); |
973 } else { |
1075 } else { |
974 // If nothing else, check for literal |
1076 // If nothing else, check for literal |
975 printf ("reqtype: %d\n", reqtype); |
|
976 switch (reqtype) { |
1077 switch (reqtype) { |
977 case TYPE_VOID: |
1078 case TYPE_VOID: |
|
1079 case TYPE_UNKNOWN: |
978 ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); |
1080 ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); |
979 break; |
1081 break; |
980 case TYPE_BOOL: |
1082 case TYPE_BOOL: |
981 case TYPE_INT: { |
1083 case TYPE_INT: { |
982 MustNumber (true); |
1084 MustNumber (true); |
994 case TYPE_STRING: |
1096 case TYPE_STRING: |
995 // PushToStringTable either returns the string index of the |
1097 // PushToStringTable either returns the string index of the |
996 // string if it finds it in the table, or writes it to the |
1098 // string if it finds it in the table, or writes it to the |
997 // table and returns it index if it doesn't find it there. |
1099 // table and returns it index if it doesn't find it there. |
998 MustString (true); |
1100 MustString (true); |
999 b->Write<word> (DH_PUSHSTRINGINDEX); |
1101 b->WriteString (token); |
1000 b->Write<word> (PushToStringTable (token.chars())); |
|
1001 break; |
1102 break; |
1002 case TYPE_FLOAT: { |
1103 case TYPE_FLOAT: { |
1003 str floatstring; |
1104 str floatstring = ParseFloat (); |
1004 |
|
1005 MustNumber (true); |
|
1006 floatstring += token; |
|
1007 |
|
1008 // Go after the decimal point |
|
1009 if (PeekNext () == ".") { |
|
1010 MustNext ("."); |
|
1011 MustNumber (false); |
|
1012 floatstring += "."; |
|
1013 floatstring += token; |
|
1014 } |
|
1015 |
|
1016 // TODO: Casting float to word causes the decimal to be lost. |
|
1017 // Find a way to store the number without such loss. |
|
1018 float val = atof (floatstring); |
|
1019 b->Write<word> (DH_PUSHNUMBER); |
|
1020 b->Write<word> (static_cast<word> ((val > 0) ? val : -val)); |
|
1021 if (val < 0) |
|
1022 b->Write<word> (DH_UNARYMINUS); |
|
1023 |
1105 |
1024 // TODO: Keep this check after decimal loss is fixed, but make |
1106 // TODO: Keep this check after decimal loss is fixed, but make |
1025 // it a real precision loss check. 55.5123 -> 55.512299, this |
1107 // it a real precision loss check. 55.5123 -> 55.512299, this |
1026 // should probably be warned of. |
1108 // should probably be warned of. |
1027 float check = static_cast<float> (static_cast<word> (val)); |
1109 float check = static_cast<float> (static_cast<word> (atof (floatstring))); |
1028 if (val != check) |
1110 if (atof (floatstring) != check) |
1029 ParserWarning ("floating point number %f loses precision (-> %f)", val, check); |
1111 ParserWarning ("floating point number %f loses precision (-> %f)", |
|
1112 atof (floatstring), check); |
|
1113 |
|
1114 b->WriteFloat (floatstring); |
|
1115 break; |
1030 } |
1116 } |
1031 } |
1117 } |
1032 } |
1118 } |
1033 |
1119 |
1034 // Negate it now if desired |
1120 // Negate it now if desired |
1046 bool global = !var->statename.len (); |
1132 bool global = !var->statename.len (); |
1047 |
1133 |
1048 // Get an operator |
1134 // Get an operator |
1049 MustNext (); |
1135 MustNext (); |
1050 int oper = ParseOperator (); |
1136 int oper = ParseOperator (); |
1051 printf ("got operator %d\n", oper); |
|
1052 if (!IsAssignmentOperator (oper)) |
1137 if (!IsAssignmentOperator (oper)) |
1053 ParserError ("expected assignment operator"); |
1138 ParserError ("expected assignment operator"); |
1054 |
1139 |
1055 if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction |
1140 if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction |
1056 ParserError ("can't alter variables at top level"); |
1141 ParserError ("can't alter variables at top level"); |
1097 info->casenumbers[i] = -1; |
1182 info->casenumbers[i] = -1; |
1098 } |
1183 } |
1099 } |
1184 } |
1100 |
1185 |
1101 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
1186 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
|
1187 if (FindConstant (token)) // There should not be constants here. |
|
1188 ParserError ("invalid use for constant\n"); |
|
1189 |
1102 // If it's a variable, expect assignment. |
1190 // If it's a variable, expect assignment. |
1103 if (ScriptVar* var = FindGlobalVariable (token)) { |
1191 if (ScriptVar* var = FindGlobalVariable (token)) |
1104 DataBuffer* b = ParseAssignment (var); |
1192 return ParseAssignment (var); |
1105 return b; |
|
1106 } |
|
1107 |
1193 |
1108 return NULL; |
1194 return NULL; |
1109 } |
1195 } |
1110 |
1196 |
1111 void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { |
1197 void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { |
1128 |
1214 |
1129 // Init a buffer for the case block and tell the object |
1215 // Init a buffer for the case block and tell the object |
1130 // writer to record all written data to it. |
1216 // writer to record all written data to it. |
1131 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
1217 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
1132 } |
1218 } |
|
1219 |
|
1220 constinfo_t* FindConstant (str token) { |
|
1221 for (uint i = 0; i < g_ConstInfo.size(); i++) |
|
1222 if (g_ConstInfo[i].name == token) |
|
1223 return &g_ConstInfo[i]; |
|
1224 return NULL; |
|
1225 } |