Sun, 09 Feb 2014 21:27:55 +0200
- highly reworked variable support, variable declarations now are declared with 'var', uses are prefixed with '$', merged constant handling into variables
CMakeLists.txt | file | annotate | diff | comparison | revisions | |
src/Commands.h | file | annotate | diff | comparison | revisions | |
src/Containers.h | file | annotate | diff | comparison | revisions | |
src/Expression.cc | file | annotate | diff | comparison | revisions | |
src/Format.cc | file | annotate | diff | comparison | revisions | |
src/Lexer.cc | file | annotate | diff | comparison | revisions | |
src/Lexer.h | file | annotate | diff | comparison | revisions | |
src/Main.cc | file | annotate | diff | comparison | revisions | |
src/Parser.cc | file | annotate | diff | comparison | revisions | |
src/Parser.h | file | annotate | diff | comparison | revisions | |
src/Variables.cc | file | annotate | diff | comparison | revisions | |
src/Variables.h | file | annotate | diff | comparison | revisions |
--- a/CMakeLists.txt Sun Feb 09 15:13:02 2014 +0200 +++ b/CMakeLists.txt Sun Feb 09 21:27:55 2014 +0200 @@ -13,7 +13,6 @@ src/Parser.cc src/String.cc src/StringTable.cc - src/Variables.cc ) get_target_property (UPDATEREVISION_EXE updaterevision LOCATION)
--- a/src/Commands.h Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Commands.h Sun Feb 09 21:27:55 2014 +0200 @@ -46,6 +46,7 @@ int minargs; EType returnvalue; List<CommandArgument> args; + String origin; String GetSignature(); };
--- a/src/Containers.h Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Containers.h Sun Feb 09 21:27:55 2014 +0200 @@ -144,10 +144,10 @@ // ===================================================================== // - void Merge (const SelfType& vals) + void Merge (const SelfType& other) { - for (const T & val : vals) - Append (val); + m_data.resize (Size() + other.Size()); + std::copy (other.begin(), other.end(), begin() + other.Size()); } // ===================================================================== @@ -333,6 +333,15 @@ return Find (a) != -1; } + // ===================================================================== + // + SelfType operator+ (const SelfType& other) const + { + SelfType out (*this); + out.Merge (other); + return out; + } + private: ListType m_data; };
--- a/src/Expression.cc Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Expression.cc Sun Feb 09 21:27:55 2014 +0200 @@ -1,7 +1,6 @@ #include "Expression.h" #include "DataBuffer.h" #include "Lexer.h" -#include "Variables.h" struct OperatorInfo { @@ -107,17 +106,16 @@ return op; } - // Check global variable - // TODO: handle locals too when they're implemented + // Check for variables if (mLexer->GetTokenType() == tkDollarSign) { mLexer->MustGetNext (tkSymbol); - ScriptVariable* globalvar = FindGlobalVariable (GetTokenString()); + Variable* globalvar = mParser->FindVariable (GetTokenString()); if (globalvar == null) Error ("unknown variable %1", GetTokenString()); - if (globalvar->writelevel == ScriptVariable::WRITE_Constexpr) + if (globalvar->writelevel == Variable::WRITE_Constexpr) op->SetValue (globalvar->value); else {
--- a/src/Format.cc Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Format.cc Sun Feb 09 21:27:55 2014 +0200 @@ -74,7 +74,7 @@ char mod = '\0'; // handle modifiers - if (fmt[pos + ofs] == 's' || fmt[pos + ofs] == 'x') + if (fmt[pos + ofs] == 's' || fmt[pos + ofs] == 'x' || fmt[pos + ofs] == 'd') { mod = fmt[pos + ofs]; ofs++;
--- a/src/Lexer.cc Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Lexer.cc Sun Feb 09 21:27:55 2014 +0200 @@ -321,7 +321,14 @@ // ============================================================================= // -String Lexer::DescribePosition() +String Lexer::DescribeCurrentPosition() +{ + return GetToken()->file + ":" + GetToken()->line; +} + +// ============================================================================= +// +String Lexer::DescribeTokenPosition() { return Format ("%1 / %2", mTokenPosition - mTokens.begin(), mTokens.Size()); } \ No newline at end of file
--- a/src/Lexer.h Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Lexer.h Sun Feb 09 21:27:55 2014 +0200 @@ -60,7 +60,8 @@ bool PeekNext (Token* tk = null); bool PeekNextType (EToken req); String PeekNextString (int a = 1); - String DescribePosition(); + String DescribeCurrentPosition(); + String DescribeTokenPosition(); static Lexer* GetCurrentLexer();
--- a/src/Main.cc Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Main.cc Sun Feb 09 21:27:55 2014 +0200 @@ -30,7 +30,6 @@ #include "Events.h" #include "Commands.h" #include "StringTable.h" -#include "Variables.h" #include "DataBuffer.h" #include "Parser.h" #include "Lexer.h" @@ -98,24 +97,21 @@ Print ("Script parsed successfully.\n"); // Parse done, print statistics and write to file - int globalcount = g_GlobalVariables.Size(); + int globalcount = parser->GetScope (0).globalVariables.Size(); int stringcount = CountStringsInTable(); - Print ("%1 / %2 strings written\n", stringcount, gMaxStringlistSize); - Print ("%1 / %2 global variables\n", globalcount, gMaxGlobalVars); + Print ("%1 / %2 strings\n", stringcount, gMaxStringlistSize); + Print ("%1 / %2 global variable indices\n", globalcount, gMaxGlobalVars); Print ("%1 / %2 events\n", parser->GetNumEvents(), gMaxEvents); Print ("%1 state%s1\n", parser->GetNumStates()); parser->WriteToFile (outfile); - - // Clear out the junk delete parser; - - // Done! - exit (0); + return 0; } catch (ScriptError& e) { PrintTo (stderr, "error: %1\n", e.what()); + return 1; } }
--- a/src/Parser.cc Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Parser.cc Sun Feb 09 21:27:55 2014 +0200 @@ -30,7 +30,6 @@ #include "Events.h" #include "Commands.h" #include "StringTable.h" -#include "Variables.h" #include "Containers.h" #include "Lexer.h" #include "DataBuffer.h" @@ -42,7 +41,17 @@ // BotscriptParser::BotscriptParser() : mReadOnly (false), - mLexer (new Lexer) {} + mMainBuffer (new DataBuffer), + mOnEnterBuffer (new DataBuffer), + mMainLoopBuffer (new DataBuffer), + mLexer (new Lexer), + mNumStates (0), + mNumEvents (0), + mCurrentMode (ETopLevelMode), + mStateSpawnDefined (false), + mGotMainLoop (false), + mScopeCursor (-1), + mCanElse (false) {} // ============================================================================ // @@ -75,18 +84,6 @@ { // Lex and preprocess the file mLexer->ProcessFile (fileName); - - mMainBuffer = new DataBuffer; - mOnEnterBuffer = new DataBuffer; - mMainLoopBuffer = new DataBuffer; - mCurrentMode = ETopLevelMode; - mNumStates = 0; - mNumEvents = 0; - mScopeCursor = -1; - mStateSpawnDefined = false; - mGotMainLoop = false; - mIfExpression = null; - mCanElse = false; PushScope(); while (mLexer->GetNext()) @@ -325,37 +322,50 @@ // void BotscriptParser::ParseVar() { + Variable* var = new Variable; + var->origin = mLexer->DescribeCurrentPosition(); const bool isconst = mLexer->GetNext (tkConst); + const bool isglobal = true; mLexer->MustGetAnyOf ({tkInt, tkStr, tkVoid}); - // TODO - if (mCurrentMode != ETopLevelMode || mCurrentState.IsEmpty() == false) - Error ("variables must only be global for now"); - - EType type = (TokenIs (tkInt)) ? EIntType : + EType vartype = (TokenIs (tkInt)) ? EIntType : (TokenIs (tkStr)) ? EStringType : EBoolType; mLexer->MustGetNext (tkSymbol); - String varname = GetTokenString(); + String name = GetTokenString(); + + /* + * TODO + if (isglobal && mScopeStack[0].globalVariables.Size() >= gMaxGlobalVars) + Error ("too many global variables!"); + */ - if (varname[0] >= '0' && varname[0] <= '9') - Error ("variable name must not start with a number"); + for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables) + { + if (var->name == name) + Error ("Variable $%1 is already declared on this scope; declared at %2", + var->name, var->origin); + } - ScriptVariable* var = DeclareGlobalVariable (type, varname); + var->name = name; + var->statename = ""; + var->type = vartype; if (isconst == false) { - var->writelevel = ScriptVariable::WRITE_Mutable; + var->writelevel = Variable::WRITE_Mutable; } else { mLexer->MustGetNext (tkAssign); - Expression expr (this, mLexer, type); + Expression expr (this, mLexer, vartype); + // If the expression was constexpr, we know its value and thus + // can store it in the variable. if (expr.GetResult()->IsConstexpr()) { - var->writelevel = ScriptVariable::WRITE_Constexpr; + var->writelevel = Variable::WRITE_Constexpr; var->value = expr.GetResult()->GetValue(); } else @@ -365,7 +375,28 @@ } } + // Assign an index for the variable if it is not constexpr. Constexpr + // variables can simply be substituted out for their value when used + // so they need no index. + if (var->writelevel != Variable::WRITE_Constexpr) + { + bool isglobal = IsInGlobalState(); + var->index = isglobal ? SCOPE(0).globalVarIndexBase++ : SCOPE(0).localVarIndexBase++; + + if ((isglobal == true && var->index >= gMaxGlobalVars) || + (isglobal == false && var->index >= gMaxStateVars)) + { + Error ("too many %1 variables", isglobal ? "global" : "state-local"); + } + } + + if (IsInGlobalState()) + SCOPE(0).globalVariables << var; + else + SCOPE(0).localVariables << var; + mLexer->MustGetNext (tkSemicolon); + Print ("Declared %3 variable #%1 $%2\n", var->index, var->name, IsInGlobalState() ? "global" : "state-local"); } // ============================================================================ @@ -714,30 +745,36 @@ switch (SCOPE (0).type) { case eIfScope: + { // Adjust the closing mark. buffer()->AdjustMark (SCOPE (0).mark1); - // We're returning from if, thus else can be next + // We're returning from `if`, thus `else` follow mCanElse = true; break; + } case eElseScope: + { // else instead uses mark1 for itself (so if expression // fails, jump to else), mark2 means end of else buffer()->AdjustMark (SCOPE (0).mark2); break; + } case eForScope: - // write the incrementor at the end of the loop block + { // write the incrementor at the end of the loop block buffer()->MergeAndDestroy (SCOPE (0).buffer1); + } case eWhileScope: - // write down the instruction to go back to the start of the loop + { // write down the instruction to go back to the start of the loop buffer()->WriteDWord (dhGoto); buffer()->AddReference (SCOPE (0).mark1); // Move the closing mark here since we're at the end of the while loop buffer()->AdjustMark (SCOPE (0).mark2); break; + } case eDoScope: { @@ -821,13 +858,6 @@ String labelName = GetTokenString(); ByteMark* mark = null; - // want no conflicts.. - if (FindCommandByName (labelName)) - Error ("label name `%1` conflicts with command name\n", labelName); - - if (FindGlobalVariable (labelName)) - Error ("label name `%1` conflicts with variable\n", labelName); - // See if a mark already exists for this label for (UndefinedLabel& label : mUndefinedLabels) { @@ -871,6 +901,7 @@ void BotscriptParser::ParseFuncdef() { CommandInfo* comm = new CommandInfo; + comm->origin = mLexer->DescribeCurrentPosition(); // Return value mLexer->MustGetAnyOf ({tkInt, tkVoid, tkBool, tkStr}); @@ -1067,7 +1098,7 @@ // ============================================================================ // -EDataHeader BotscriptParser::GetAssigmentDataHeader (EAssignmentOperator op, ScriptVariable* var) +EDataHeader BotscriptParser::GetAssigmentDataHeader (EAssignmentOperator op, Variable* var) { if (var->IsGlobal()) { @@ -1106,9 +1137,9 @@ // by an assignment operator, followed by an expression value. Expects current // token to be the name of the variable, and expects the variable to be given. // -DataBuffer* BotscriptParser::ParseAssignment (ScriptVariable* var) +DataBuffer* BotscriptParser::ParseAssignment (Variable* var) { - if (var->writelevel != ScriptVariable::WRITE_Mutable) + if (var->writelevel != Variable::WRITE_Mutable) { Error ("cannot alter read-only variable $%1", var->name); } @@ -1168,6 +1199,16 @@ info->cases.Clear(); info->casecursor = info->cases.begin() - 1; } + + // Reset variable stuff in any case + SCOPE(0).globalVarIndexBase = (mScopeCursor == 0) ? 0 : SCOPE(1).globalVarIndexBase; + SCOPE(0).localVarIndexBase = (mScopeCursor == 0) ? 0 : SCOPE(1).localVarIndexBase; + + for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables) + delete var; + + SCOPE(0).localVariables.Clear(); + SCOPE(0).globalVariables.Clear(); } // ============================================================================ @@ -1194,7 +1235,7 @@ if (mLexer->GetNext (tkDollarSign)) { mLexer->MustGetNext (tkSymbol); - ScriptVariable* var = FindGlobalVariable (GetTokenString()); + Variable* var = FindVariable (GetTokenString()); if (var == null) Error ("unknown variable $%1", var->name); @@ -1325,16 +1366,39 @@ // First, resolve references for (MarkReference* ref : mMainBuffer->GetReferences()) - { - // Substitute the placeholder with the mark position for (int i = 0; i < 4; ++i) mMainBuffer->GetBuffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF; - // Print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos); - } - // Then, dump the main buffer to the file fwrite (mMainBuffer->GetBuffer(), 1, mMainBuffer->GetWrittenSize(), fp); Print ("-- %1 byte%s1 written to %2\n", mMainBuffer->GetWrittenSize(), outfile); fclose (fp); -} \ No newline at end of file +} + +// ============================================================================ +// +// Attempt to find the variable by the given name. Looks from current scope +// downwards. +// +Variable* BotscriptParser::FindVariable (const String& name) +{ + for (int i = mScopeCursor; i >= 0; --i) + { + for (Variable* var : mScopeStack[i].globalVariables + mScopeStack[i].localVariables) + { + if (var->name == name) + return var; + } + } + + return null; +} + +// ============================================================================ +// +// Is the parser currently in global state (i.e. not in any specific state)? +// +bool BotscriptParser::IsInGlobalState() const +{ + return mCurrentState.IsEmpty(); +}
--- a/src/Parser.h Sun Feb 09 15:13:02 2014 +0200 +++ b/src/Parser.h Sun Feb 09 21:27:55 2014 +0200 @@ -43,7 +43,7 @@ class DataBuffer; class Lexer; -class ScriptVariable; +class Variable; // ============================================================================ // @@ -91,6 +91,31 @@ // ============================================================================ // +struct Variable +{ + enum EWritability + { + WRITE_Mutable, // normal read-many-write-many variable + WRITE_Const, // write-once const variable + WRITE_Constexpr, // const variable whose value is known to compiler + }; + + String name; + String statename; + EType type; + int index; + EWritability writelevel; + int value; + String origin; + + inline bool IsGlobal() const + { + return statename.IsEmpty(); + } +}; + +// ============================================================================ +// struct CaseInfo { ByteMark* mark; @@ -108,10 +133,14 @@ ByteMark* mark2; EScopeType type; DataBuffer* buffer1; + int globalVarIndexBase; + int localVarIndexBase; // switch-related stuff List<CaseInfo>::Iterator casecursor; List<CaseInfo> cases; + List<Variable*> localVariables; + List<Variable*> globalVariables; }; // ============================================================================ @@ -131,7 +160,7 @@ ~BotscriptParser(); void ParseBotscript (String fileName); DataBuffer* ParseCommand (CommandInfo* comm); - DataBuffer* ParseAssignment (ScriptVariable* var); + DataBuffer* ParseAssignment (Variable* var); EAssignmentOperator ParseAssignmentOperator (); String ParseFloat(); void PushScope (EReset reset = eResetScope); @@ -143,6 +172,13 @@ String GetTokenString(); String DescribePosition() const; void WriteToFile (String outfile); + Variable* FindVariable (const String& name); + bool IsInGlobalState() const; + + inline ScopeInfo& GetScope (int offset) + { + return mScopeStack[mScopeCursor - offset]; + } inline int GetNumEvents() const { @@ -179,7 +215,6 @@ bool mStateSpawnDefined; bool mGotMainLoop; int mScopeCursor; - DataBuffer* mIfExpression; bool mCanElse; List<UndefinedLabel> mUndefinedLabels; @@ -213,7 +248,7 @@ void writeMemberBuffers(); void WriteStringTable(); DataBuffer* ParseExpression (EType reqtype, bool fromhere = false); - EDataHeader GetAssigmentDataHeader (EAssignmentOperator op, ScriptVariable* var); + EDataHeader GetAssigmentDataHeader (EAssignmentOperator op, Variable* var); }; #endif // BOTC_PARSER_H
--- a/src/Variables.cc Sun Feb 09 15:13:02 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* - Copyright 2012-2014 Santeri Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "StringTable.h" -#include "Variables.h" -#include "Parser.h" - -List<ScriptVariable> g_GlobalVariables; -List<ScriptVariable> g_LocalVariables; - -// ============================================================================ -// Tries to declare a new global-scope variable. Returns pointer -// to new global variable, null if declaration failed. -ScriptVariable* DeclareGlobalVariable (EType type, String name) -{ - // Unfortunately the VM does not support string variables so yeah. - if (type == EStringType) - Error ("variables cannot be string\n"); - - // Check that the variable is valid - if (FindCommandByName (name)) - Error ("name of variable-to-be `%s` conflicts with that of a command", name.CString()); - - if (g_GlobalVariables.Size() >= gMaxGlobalVars) - Error ("too many global variables!"); - - for (int i = 0; i < g_GlobalVariables.Size(); i++) - if (g_GlobalVariables[i].name == name) - Error ("attempted redeclaration of global variable `%s`", name.CString()); - - ScriptVariable g; - g.index = g_GlobalVariables.Size(); - g.name = name; - g.statename = ""; - g.type = type; - - g_GlobalVariables << g; - return &g_GlobalVariables[g.index]; -} - -// ============================================================================ -// Find a global variable by name -ScriptVariable* FindGlobalVariable (String name) -{ - for (ScriptVariable& var : g_GlobalVariables) - if (var.name == name) - return &var; - - return null; -} \ No newline at end of file
--- a/src/Variables.h Sun Feb 09 15:13:02 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - Copyright 2012-2014 Santeri Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef BOTC_VARIABLES_H -#define BOTC_VARIABLES_H - -#include "Main.h" - -class ExpressionValue; -class BotscriptParser; - -struct ScriptVariable -{ - enum EWritability - { - WRITE_Mutable, // normal read-many-write-many variable - WRITE_Const, // write-once const variable - WRITE_Constexpr, // const variable whose value is known to compiler - }; - - String name; - String statename; - EType type; - int index; - EWritability writelevel; - int value; - - inline bool IsGlobal() const - { - return statename.IsEmpty(); - } -}; - -extern List<ScriptVariable> g_GlobalVariables; -extern List<ScriptVariable> g_LocalVariables; - -ScriptVariable* DeclareGlobalVariable (EType type, String name); -ScriptVariable* FindGlobalVariable (String name); - -#endif // BOTC_VARIABLES_H