40 |
39 |
41 // ============================================================================ |
40 // ============================================================================ |
42 // |
41 // |
43 BotscriptParser::BotscriptParser() : |
42 BotscriptParser::BotscriptParser() : |
44 mReadOnly (false), |
43 mReadOnly (false), |
45 mLexer (new Lexer) {} |
44 mMainBuffer (new DataBuffer), |
|
45 mOnEnterBuffer (new DataBuffer), |
|
46 mMainLoopBuffer (new DataBuffer), |
|
47 mLexer (new Lexer), |
|
48 mNumStates (0), |
|
49 mNumEvents (0), |
|
50 mCurrentMode (ETopLevelMode), |
|
51 mStateSpawnDefined (false), |
|
52 mGotMainLoop (false), |
|
53 mScopeCursor (-1), |
|
54 mCanElse (false) {} |
46 |
55 |
47 // ============================================================================ |
56 // ============================================================================ |
48 // |
57 // |
49 BotscriptParser::~BotscriptParser() |
58 BotscriptParser::~BotscriptParser() |
50 { |
59 { |
73 // of necessary buffering so stuff is written in the correct order. |
82 // of necessary buffering so stuff is written in the correct order. |
74 void BotscriptParser::ParseBotscript (String fileName) |
83 void BotscriptParser::ParseBotscript (String fileName) |
75 { |
84 { |
76 // Lex and preprocess the file |
85 // Lex and preprocess the file |
77 mLexer->ProcessFile (fileName); |
86 mLexer->ProcessFile (fileName); |
78 |
|
79 mMainBuffer = new DataBuffer; |
|
80 mOnEnterBuffer = new DataBuffer; |
|
81 mMainLoopBuffer = new DataBuffer; |
|
82 mCurrentMode = ETopLevelMode; |
|
83 mNumStates = 0; |
|
84 mNumEvents = 0; |
|
85 mScopeCursor = -1; |
|
86 mStateSpawnDefined = false; |
|
87 mGotMainLoop = false; |
|
88 mIfExpression = null; |
|
89 mCanElse = false; |
|
90 PushScope(); |
87 PushScope(); |
91 |
88 |
92 while (mLexer->GetNext()) |
89 while (mLexer->GetNext()) |
93 { |
90 { |
94 // Check if else is potentically valid |
91 // Check if else is potentically valid |
323 |
320 |
324 // ============================================================================ |
321 // ============================================================================ |
325 // |
322 // |
326 void BotscriptParser::ParseVar() |
323 void BotscriptParser::ParseVar() |
327 { |
324 { |
|
325 Variable* var = new Variable; |
|
326 var->origin = mLexer->DescribeCurrentPosition(); |
328 const bool isconst = mLexer->GetNext (tkConst); |
327 const bool isconst = mLexer->GetNext (tkConst); |
|
328 const bool isglobal = true; |
329 mLexer->MustGetAnyOf ({tkInt, tkStr, tkVoid}); |
329 mLexer->MustGetAnyOf ({tkInt, tkStr, tkVoid}); |
330 |
330 |
331 // TODO |
331 EType vartype = (TokenIs (tkInt)) ? EIntType : |
332 if (mCurrentMode != ETopLevelMode || mCurrentState.IsEmpty() == false) |
|
333 Error ("variables must only be global for now"); |
|
334 |
|
335 EType type = (TokenIs (tkInt)) ? EIntType : |
|
336 (TokenIs (tkStr)) ? EStringType : |
332 (TokenIs (tkStr)) ? EStringType : |
337 EBoolType; |
333 EBoolType; |
338 |
334 |
339 mLexer->MustGetNext (tkSymbol); |
335 mLexer->MustGetNext (tkSymbol); |
340 String varname = GetTokenString(); |
336 String name = GetTokenString(); |
341 |
337 |
342 if (varname[0] >= '0' && varname[0] <= '9') |
338 /* |
343 Error ("variable name must not start with a number"); |
339 * TODO |
344 |
340 if (isglobal && mScopeStack[0].globalVariables.Size() >= gMaxGlobalVars) |
345 ScriptVariable* var = DeclareGlobalVariable (type, varname); |
341 Error ("too many global variables!"); |
|
342 */ |
|
343 |
|
344 for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables) |
|
345 { |
|
346 if (var->name == name) |
|
347 Error ("Variable $%1 is already declared on this scope; declared at %2", |
|
348 var->name, var->origin); |
|
349 } |
|
350 |
|
351 var->name = name; |
|
352 var->statename = ""; |
|
353 var->type = vartype; |
346 |
354 |
347 if (isconst == false) |
355 if (isconst == false) |
348 { |
356 { |
349 var->writelevel = ScriptVariable::WRITE_Mutable; |
357 var->writelevel = Variable::WRITE_Mutable; |
350 } |
358 } |
351 else |
359 else |
352 { |
360 { |
353 mLexer->MustGetNext (tkAssign); |
361 mLexer->MustGetNext (tkAssign); |
354 Expression expr (this, mLexer, type); |
362 Expression expr (this, mLexer, vartype); |
355 |
363 |
|
364 // If the expression was constexpr, we know its value and thus |
|
365 // can store it in the variable. |
356 if (expr.GetResult()->IsConstexpr()) |
366 if (expr.GetResult()->IsConstexpr()) |
357 { |
367 { |
358 var->writelevel = ScriptVariable::WRITE_Constexpr; |
368 var->writelevel = Variable::WRITE_Constexpr; |
359 var->value = expr.GetResult()->GetValue(); |
369 var->value = expr.GetResult()->GetValue(); |
360 } |
370 } |
361 else |
371 else |
362 { |
372 { |
363 // TODO: might need a VM-wise oninit for this... |
373 // TODO: might need a VM-wise oninit for this... |
364 Error ("const variables must be constexpr for now"); |
374 Error ("const variables must be constexpr for now"); |
365 } |
375 } |
366 } |
376 } |
367 |
377 |
|
378 // Assign an index for the variable if it is not constexpr. Constexpr |
|
379 // variables can simply be substituted out for their value when used |
|
380 // so they need no index. |
|
381 if (var->writelevel != Variable::WRITE_Constexpr) |
|
382 { |
|
383 bool isglobal = IsInGlobalState(); |
|
384 var->index = isglobal ? SCOPE(0).globalVarIndexBase++ : SCOPE(0).localVarIndexBase++; |
|
385 |
|
386 if ((isglobal == true && var->index >= gMaxGlobalVars) || |
|
387 (isglobal == false && var->index >= gMaxStateVars)) |
|
388 { |
|
389 Error ("too many %1 variables", isglobal ? "global" : "state-local"); |
|
390 } |
|
391 } |
|
392 |
|
393 if (IsInGlobalState()) |
|
394 SCOPE(0).globalVariables << var; |
|
395 else |
|
396 SCOPE(0).localVariables << var; |
|
397 |
368 mLexer->MustGetNext (tkSemicolon); |
398 mLexer->MustGetNext (tkSemicolon); |
|
399 Print ("Declared %3 variable #%1 $%2\n", var->index, var->name, IsInGlobalState() ? "global" : "state-local"); |
369 } |
400 } |
370 |
401 |
371 // ============================================================================ |
402 // ============================================================================ |
372 // |
403 // |
373 void BotscriptParser::ParseGoto() |
404 void BotscriptParser::ParseGoto() |
712 if (mScopeCursor > 0) |
743 if (mScopeCursor > 0) |
713 { |
744 { |
714 switch (SCOPE (0).type) |
745 switch (SCOPE (0).type) |
715 { |
746 { |
716 case eIfScope: |
747 case eIfScope: |
|
748 { |
717 // Adjust the closing mark. |
749 // Adjust the closing mark. |
718 buffer()->AdjustMark (SCOPE (0).mark1); |
750 buffer()->AdjustMark (SCOPE (0).mark1); |
719 |
751 |
720 // We're returning from if, thus else can be next |
752 // We're returning from `if`, thus `else` follow |
721 mCanElse = true; |
753 mCanElse = true; |
722 break; |
754 break; |
|
755 } |
723 |
756 |
724 case eElseScope: |
757 case eElseScope: |
|
758 { |
725 // else instead uses mark1 for itself (so if expression |
759 // else instead uses mark1 for itself (so if expression |
726 // fails, jump to else), mark2 means end of else |
760 // fails, jump to else), mark2 means end of else |
727 buffer()->AdjustMark (SCOPE (0).mark2); |
761 buffer()->AdjustMark (SCOPE (0).mark2); |
728 break; |
762 break; |
|
763 } |
729 |
764 |
730 case eForScope: |
765 case eForScope: |
731 // write the incrementor at the end of the loop block |
766 { // write the incrementor at the end of the loop block |
732 buffer()->MergeAndDestroy (SCOPE (0).buffer1); |
767 buffer()->MergeAndDestroy (SCOPE (0).buffer1); |
|
768 } |
733 case eWhileScope: |
769 case eWhileScope: |
734 // write down the instruction to go back to the start of the loop |
770 { // write down the instruction to go back to the start of the loop |
735 buffer()->WriteDWord (dhGoto); |
771 buffer()->WriteDWord (dhGoto); |
736 buffer()->AddReference (SCOPE (0).mark1); |
772 buffer()->AddReference (SCOPE (0).mark1); |
737 |
773 |
738 // Move the closing mark here since we're at the end of the while loop |
774 // Move the closing mark here since we're at the end of the while loop |
739 buffer()->AdjustMark (SCOPE (0).mark2); |
775 buffer()->AdjustMark (SCOPE (0).mark2); |
740 break; |
776 break; |
|
777 } |
741 |
778 |
742 case eDoScope: |
779 case eDoScope: |
743 { |
780 { |
744 mLexer->MustGetNext (tkWhile); |
781 mLexer->MustGetNext (tkWhile); |
745 mLexer->MustGetNext (tkParenStart); |
782 mLexer->MustGetNext (tkParenStart); |
819 { |
856 { |
820 CheckNotToplevel(); |
857 CheckNotToplevel(); |
821 String labelName = GetTokenString(); |
858 String labelName = GetTokenString(); |
822 ByteMark* mark = null; |
859 ByteMark* mark = null; |
823 |
860 |
824 // want no conflicts.. |
|
825 if (FindCommandByName (labelName)) |
|
826 Error ("label name `%1` conflicts with command name\n", labelName); |
|
827 |
|
828 if (FindGlobalVariable (labelName)) |
|
829 Error ("label name `%1` conflicts with variable\n", labelName); |
|
830 |
|
831 // See if a mark already exists for this label |
861 // See if a mark already exists for this label |
832 for (UndefinedLabel& label : mUndefinedLabels) |
862 for (UndefinedLabel& label : mUndefinedLabels) |
833 { |
863 { |
834 if (label.name != labelName) |
864 if (label.name != labelName) |
835 continue; |
865 continue; |
869 // ============================================================================= |
899 // ============================================================================= |
870 // |
900 // |
871 void BotscriptParser::ParseFuncdef() |
901 void BotscriptParser::ParseFuncdef() |
872 { |
902 { |
873 CommandInfo* comm = new CommandInfo; |
903 CommandInfo* comm = new CommandInfo; |
|
904 comm->origin = mLexer->DescribeCurrentPosition(); |
874 |
905 |
875 // Return value |
906 // Return value |
876 mLexer->MustGetAnyOf ({tkInt, tkVoid, tkBool, tkStr}); |
907 mLexer->MustGetAnyOf ({tkInt, tkVoid, tkBool, tkStr}); |
877 comm->returnvalue = GetTypeByName (mLexer->GetToken()->text); // TODO |
908 comm->returnvalue = GetTypeByName (mLexer->GetToken()->text); // TODO |
878 assert (comm->returnvalue != -1); |
909 assert (comm->returnvalue != -1); |
1104 // |
1135 // |
1105 // Parses an assignment. An assignment starts with a variable name, followed |
1136 // Parses an assignment. An assignment starts with a variable name, followed |
1106 // by an assignment operator, followed by an expression value. Expects current |
1137 // by an assignment operator, followed by an expression value. Expects current |
1107 // token to be the name of the variable, and expects the variable to be given. |
1138 // token to be the name of the variable, and expects the variable to be given. |
1108 // |
1139 // |
1109 DataBuffer* BotscriptParser::ParseAssignment (ScriptVariable* var) |
1140 DataBuffer* BotscriptParser::ParseAssignment (Variable* var) |
1110 { |
1141 { |
1111 if (var->writelevel != ScriptVariable::WRITE_Mutable) |
1142 if (var->writelevel != Variable::WRITE_Mutable) |
1112 { |
1143 { |
1113 Error ("cannot alter read-only variable $%1", var->name); |
1144 Error ("cannot alter read-only variable $%1", var->name); |
1114 } |
1145 } |
1115 |
1146 |
1116 // Get an operator |
1147 // Get an operator |
1166 info->mark2 = null; |
1197 info->mark2 = null; |
1167 info->buffer1 = null; |
1198 info->buffer1 = null; |
1168 info->cases.Clear(); |
1199 info->cases.Clear(); |
1169 info->casecursor = info->cases.begin() - 1; |
1200 info->casecursor = info->cases.begin() - 1; |
1170 } |
1201 } |
|
1202 |
|
1203 // Reset variable stuff in any case |
|
1204 SCOPE(0).globalVarIndexBase = (mScopeCursor == 0) ? 0 : SCOPE(1).globalVarIndexBase; |
|
1205 SCOPE(0).localVarIndexBase = (mScopeCursor == 0) ? 0 : SCOPE(1).localVarIndexBase; |
|
1206 |
|
1207 for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables) |
|
1208 delete var; |
|
1209 |
|
1210 SCOPE(0).localVariables.Clear(); |
|
1211 SCOPE(0).globalVariables.Clear(); |
1171 } |
1212 } |
1172 |
1213 |
1173 // ============================================================================ |
1214 // ============================================================================ |
1174 // |
1215 // |
1175 DataBuffer* BotscriptParser::ParseExpression (EType reqtype, bool fromhere) |
1216 DataBuffer* BotscriptParser::ParseExpression (EType reqtype, bool fromhere) |
1323 if (fp == null) |
1364 if (fp == null) |
1324 Error ("couldn't open %1 for writing: %2", outfile, strerror (errno)); |
1365 Error ("couldn't open %1 for writing: %2", outfile, strerror (errno)); |
1325 |
1366 |
1326 // First, resolve references |
1367 // First, resolve references |
1327 for (MarkReference* ref : mMainBuffer->GetReferences()) |
1368 for (MarkReference* ref : mMainBuffer->GetReferences()) |
1328 { |
|
1329 // Substitute the placeholder with the mark position |
|
1330 for (int i = 0; i < 4; ++i) |
1369 for (int i = 0; i < 4; ++i) |
1331 mMainBuffer->GetBuffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF; |
1370 mMainBuffer->GetBuffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF; |
1332 |
|
1333 // Print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos); |
|
1334 } |
|
1335 |
1371 |
1336 // Then, dump the main buffer to the file |
1372 // Then, dump the main buffer to the file |
1337 fwrite (mMainBuffer->GetBuffer(), 1, mMainBuffer->GetWrittenSize(), fp); |
1373 fwrite (mMainBuffer->GetBuffer(), 1, mMainBuffer->GetWrittenSize(), fp); |
1338 Print ("-- %1 byte%s1 written to %2\n", mMainBuffer->GetWrittenSize(), outfile); |
1374 Print ("-- %1 byte%s1 written to %2\n", mMainBuffer->GetWrittenSize(), outfile); |
1339 fclose (fp); |
1375 fclose (fp); |
1340 } |
1376 } |
|
1377 |
|
1378 // ============================================================================ |
|
1379 // |
|
1380 // Attempt to find the variable by the given name. Looks from current scope |
|
1381 // downwards. |
|
1382 // |
|
1383 Variable* BotscriptParser::FindVariable (const String& name) |
|
1384 { |
|
1385 for (int i = mScopeCursor; i >= 0; --i) |
|
1386 { |
|
1387 for (Variable* var : mScopeStack[i].globalVariables + mScopeStack[i].localVariables) |
|
1388 { |
|
1389 if (var->name == name) |
|
1390 return var; |
|
1391 } |
|
1392 } |
|
1393 |
|
1394 return null; |
|
1395 } |
|
1396 |
|
1397 // ============================================================================ |
|
1398 // |
|
1399 // Is the parser currently in global state (i.e. not in any specific state)? |
|
1400 // |
|
1401 bool BotscriptParser::IsInGlobalState() const |
|
1402 { |
|
1403 return mCurrentState.IsEmpty(); |
|
1404 } |