diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index 7da3ea5..ddb7d3c 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -72,6 +72,8 @@ BoundExpression* Binder::BindExpression(ParsedExpression* expression){ return new BoundLiteralStringExpression(((LiteralStringExpression*)expression)->GetValue(), expression->GetStartPosition(), expression->GetLength()); case ParsedExpressionKind ::LiteralBool: return new BoundLiteralBoolExpression(((LiteralBoolExpression*)expression)->GetValue(), expression->GetStartPosition(), expression->GetLength()); + case ParsedExpressionKind ::Variable: + return this -> BindVariableExpression((VariableExpression*)expression); case ParsedExpressionKind ::Binary: return this -> BindBinaryOperator((BinaryExpression*)expression); @@ -86,6 +88,18 @@ BoundExpression* Binder::BindExpression(ParsedExpression* expression){ } } +BoundExpression* Binder::BindVariableExpression(VariableExpression* expression){ + auto key = expression->GetValue(); + auto scope = this->_scope->Exists(key.GetHash()); + if (scope == -1){ + this -> _scriptData -> Diagnostics->LogError(DiagnosticCode::VariableNotFound, expression->GetStartPosition(), expression->GetLength()); + return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); + } + auto var = this->_scope->GetVariable(scope, key.GetHash()); + auto type = var->GetType(); + return new BoundVariableExpression(scope, key.GetHash(), type, expression->GetStartPosition(), expression->GetLength()); +} + BoundExpression* Binder::BindBinaryOperator(BinaryExpression* expression){ auto boundLeft = this -> BindExpression(expression->GetLeft()); auto boundRight = this -> BindExpression(expression->GetRight()); diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index 2ba7b8c..3bcaf11 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -19,6 +19,7 @@ class Binder { BoundStatement *BindAssignmentStatement(ParsedStatement *statement); BoundExpression *BindExpression(ParsedExpression *expression); + BoundExpression *BindVariableExpression(VariableExpression *expression); BoundExpression *BindBinaryOperator(BinaryExpression *expression); BoundExpression *BindUnaryOperator(UnaryExpression *expression); public: diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index 20f8b19..40eb2b2 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -7,6 +7,7 @@ #include #include "../../ScriptType.hpp" #include "../BoundOperators.hpp" +#include "../BoundVariables/BoundVariableKey.hpp" using namespace std; @@ -17,6 +18,7 @@ enum class BoundExpressionKind{ LiteralFloat, LiteralString, LiteralBool, + Variable, Unary, Binary, @@ -129,6 +131,36 @@ public: } }; +class BoundVariableExpression : public BoundExpression{ + int _scope; + int _id; + ScriptType _type; +public: + BoundVariableExpression(int scope, int id, const ScriptType& type, unsigned int start, unsigned int length) + : BoundExpression(start, length, nullptr), _type(type){ + _scope = scope; + _id = id; + } + + ~BoundVariableExpression() override = default; + + ScriptType* GetType() final{ + return &_type; + }; + + BoundExpressionKind GetKind() final{ + return BoundExpressionKind ::Variable; + } + + int GetScope(){ + return _scope; + } + + int GetId(){ + return _id; + } +}; + class BoundBinaryExpression : public BoundExpression { BoundExpression* _left; BoundExpression* _right; diff --git a/src/Binder/BoundVariables/BoundScope.cpp b/src/Binder/BoundVariables/BoundScope.cpp index fb91d1d..2b4c771 100644 --- a/src/Binder/BoundVariables/BoundScope.cpp +++ b/src/Binder/BoundVariables/BoundScope.cpp @@ -57,7 +57,7 @@ BoundVariable *BoundScope::GetVariable(int scope, int identifier) { } return nullptr; } else{ - auto s = this->_localScope.at(scope); + auto s = this->_localScope.at(scope - 1); auto find = s -> find(identifier); if (find != s -> end()){ return find -> second; @@ -72,7 +72,7 @@ VariableAssignment BoundScope::CreateExplicitLocal(int identifier, const ScriptT return VariableAssignment(VariableAssignmentResult::ExplicitLocalVariableExists, nullptr); } scope -> insert({identifier, new BoundVariable(type)}); - return VariableAssignment(VariableAssignmentResult::Ok, new BoundVariableKey(identifier, this->_currentScope - 1, true)); + return VariableAssignment(VariableAssignmentResult::Ok, new BoundVariableKey(identifier, this->_currentScope, true)); } VariableAssignment BoundScope::AssignVariable(int identifier, const ScriptType& type) { diff --git a/src/Binder/BoundVariables/BoundVariable.hpp b/src/Binder/BoundVariables/BoundVariable.hpp index 295559c..786e366 100644 --- a/src/Binder/BoundVariables/BoundVariable.hpp +++ b/src/Binder/BoundVariables/BoundVariable.hpp @@ -7,7 +7,7 @@ class BoundVariable{ ScriptType _type; public: - explicit BoundVariable(ScriptType type) : _type(type){ + explicit BoundVariable(const ScriptType& type) : _type(type){ } ~BoundVariable(){ } diff --git a/src/Diagnostics/DiagnosticCode.hpp b/src/Diagnostics/DiagnosticCode.hpp index c481752..3b8e088 100644 --- a/src/Diagnostics/DiagnosticCode.hpp +++ b/src/Diagnostics/DiagnosticCode.hpp @@ -14,6 +14,7 @@ enum class DiagnosticCode{ NoBinaryOperationFound, NoUnaryOperationFound, CantAssignVariable, + VariableNotFound, }; #endif //PORYGONLANG_DIAGNOSTICCODE_HPP diff --git a/src/Evaluator/EvalValues/EvalValue.hpp b/src/Evaluator/EvalValues/EvalValue.hpp index 205d66a..164d887 100644 --- a/src/Evaluator/EvalValues/EvalValue.hpp +++ b/src/Evaluator/EvalValues/EvalValue.hpp @@ -9,6 +9,7 @@ class EvalValue{ public: + EvalValue() = default; virtual ~EvalValue() = default; virtual ScriptType* GetType() = 0; @@ -18,6 +19,8 @@ public: return ! (this->operator==(b)); } + virtual EvalValue* Clone() = 0; + virtual long EvaluateInteger(){ throw EvaluationException("Can't evaluate this EvalValue as integer."); } @@ -41,6 +44,10 @@ public: _type = new ScriptType(TypeClass::Bool); } + EvalValue* Clone() final{ + return new BooleanEvalValue(_value); + } + ~BooleanEvalValue() final{ delete _type; } diff --git a/src/Evaluator/EvalValues/NumericEvalValue.hpp b/src/Evaluator/EvalValues/NumericEvalValue.hpp index afd237c..3238da8 100644 --- a/src/Evaluator/EvalValues/NumericEvalValue.hpp +++ b/src/Evaluator/EvalValues/NumericEvalValue.hpp @@ -50,6 +50,10 @@ public: strs << _value; return strs.str(); } + + EvalValue* Clone() final{ + return new IntegerEvalValue(_value); + } }; class FloatEvalValue : public NumericEvalValue{ @@ -74,6 +78,10 @@ public: strs << _value; return strs.str(); } + + EvalValue* Clone() final{ + return new FloatEvalValue(_value); + } }; #endif //PORYGONLANG_NUMERICEVALVALUE_HPP diff --git a/src/Evaluator/EvalValues/StringEvalValue.hpp b/src/Evaluator/EvalValues/StringEvalValue.hpp index 66681da..2426b5b 100644 --- a/src/Evaluator/EvalValues/StringEvalValue.hpp +++ b/src/Evaluator/EvalValues/StringEvalValue.hpp @@ -32,6 +32,9 @@ public: return _value; } + EvalValue* Clone() final{ + return new StringEvalValue(_value); + } }; diff --git a/src/Evaluator/EvaluationScope/EvaluationScope.cpp b/src/Evaluator/EvaluationScope/EvaluationScope.cpp index 1d86772..99a0376 100644 --- a/src/Evaluator/EvaluationScope/EvaluationScope.cpp +++ b/src/Evaluator/EvaluationScope/EvaluationScope.cpp @@ -4,6 +4,7 @@ EvaluationScope::EvaluationScope(unordered_map *scriptVariables, int deepestScope) { _scriptScope = scriptVariables; _localScope = vector>(deepestScope); + _currentScope = -1; } EvaluationScope::~EvaluationScope() { @@ -27,5 +28,21 @@ void EvaluationScope::SetVariable(int scope, int id, EvalValue *value) { } EvalValue *EvaluationScope::GetVariable(int scope, int id) { + if (scope == 0){ + return _scriptScope->at(id); + } return _localScope[scope - 1][id]; } + +void EvaluationScope::OuterScope() { + _currentScope++; +} + +void EvaluationScope::InnerScope() { + auto scope = this->_localScope[_currentScope]; + for (auto v: scope){ + delete v.second; + } + _currentScope--; + +} diff --git a/src/Evaluator/EvaluationScope/EvaluationScope.hpp b/src/Evaluator/EvaluationScope/EvaluationScope.hpp index 9b0f69c..67fe182 100644 --- a/src/Evaluator/EvaluationScope/EvaluationScope.hpp +++ b/src/Evaluator/EvaluationScope/EvaluationScope.hpp @@ -9,12 +9,15 @@ class EvaluationScope { unordered_map* _scriptScope; vector> _localScope; + int _currentScope; public: explicit EvaluationScope(unordered_map* scriptVariables, int deepestScope); ~EvaluationScope(); void CreateVariable(int scope, int id, EvalValue* value); void SetVariable(int scope, int id, EvalValue* value); + void OuterScope(); + void InnerScope(); EvalValue* GetVariable(int scope, int id); }; diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index 0cdf00e..ff207ef 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -22,9 +22,11 @@ void Evaluator::EvaluateStatement(BoundStatement *statement) { } void Evaluator::EvaluateBlockStatement(BoundBlockStatement* statement) { + this->_evaluationScope->OuterScope(); for (auto s: statement->GetStatements()){ this -> EvaluateStatement(s); } + this->_evaluationScope->InnerScope(); } void Evaluator::EvaluateExpressionStatement(BoundExpressionStatement *statement) { @@ -54,12 +56,17 @@ EvalValue *Evaluator::EvaluateExpression(BoundExpression *expression) { } } +EvalValue* Evaluator::GetVariable(BoundVariableExpression* expression){ + return this->_evaluationScope->GetVariable(expression->GetScope(), expression->GetId())->Clone(); +} + NumericEvalValue* Evaluator::EvaluateIntegerExpression(BoundExpression *expression) { switch (expression->GetKind()){ case BoundExpressionKind ::LiteralInteger: return new IntegerEvalValue(((BoundLiteralIntegerExpression*)expression)->GetValue()); case BoundExpressionKind ::LiteralFloat: return new FloatEvalValue(((BoundLiteralFloatExpression*)expression)->GetValue()); case BoundExpressionKind::Unary: return this -> EvaluateIntegerUnary((BoundUnaryExpression*)expression); case BoundExpressionKind ::Binary: return this -> EvaluateIntegerBinary((BoundBinaryExpression*)expression); + case BoundExpressionKind::Variable: return (NumericEvalValue*)this->GetVariable((BoundVariableExpression*)expression); case BoundExpressionKind ::LiteralString: case BoundExpressionKind ::LiteralBool: @@ -73,6 +80,7 @@ BooleanEvalValue* Evaluator::EvaluateBoolExpression(BoundExpression *expression) case BoundExpressionKind::LiteralBool: return new BooleanEvalValue(((BoundLiteralBoolExpression*)expression)->GetValue()); case BoundExpressionKind::Unary: return this -> EvaluateBooleanUnary((BoundUnaryExpression*)expression); case BoundExpressionKind::Binary: return this -> EvaluateBooleanBinary((BoundBinaryExpression*)expression); + case BoundExpressionKind::Variable: return (BooleanEvalValue*)this->GetVariable((BoundVariableExpression*)expression); case BoundExpressionKind::Bad: case BoundExpressionKind::LiteralInteger: @@ -89,6 +97,8 @@ StringEvalValue* Evaluator::EvaluateStringExpression(BoundExpression *expression return new StringEvalValue(((BoundLiteralStringExpression*)expression)->GetValue()); case BoundExpressionKind::Binary: return this -> EvaluateStringBinary((BoundBinaryExpression*)expression); + case BoundExpressionKind::Variable: return (StringEvalValue*)this->GetVariable((BoundVariableExpression*)expression); + case BoundExpressionKind::Bad: case BoundExpressionKind::LiteralInteger: diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index c93e1b0..a43b59f 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -35,6 +35,8 @@ class Evaluator { NumericEvalValue* EvaluateIntegerUnary(BoundUnaryExpression* expression); BooleanEvalValue *EvaluateBooleanUnary(BoundUnaryExpression *expression); + + EvalValue *GetVariable(BoundVariableExpression *expression); public: explicit Evaluator(Script* script){ _scriptData = script; diff --git a/src/Parser/ParsedExpressions/ParsedExpression.hpp b/src/Parser/ParsedExpressions/ParsedExpression.hpp index 5b6941a..c74f871 100644 --- a/src/Parser/ParsedExpressions/ParsedExpression.hpp +++ b/src/Parser/ParsedExpressions/ParsedExpression.hpp @@ -5,6 +5,7 @@ #include "../Token.hpp" #include "../UnaryOperatorKind.hpp" #include "../BinaryOperatorKind.hpp" +#include "../../Utilities/HashedString.hpp" enum class ParsedExpressionKind{ Bad, @@ -13,6 +14,7 @@ enum class ParsedExpressionKind{ LiteralFloat, LiteralString, LiteralBool, + Variable, Unary, Binary, @@ -113,6 +115,23 @@ public: } }; +class VariableExpression : public ParsedExpression{ + HashedString _value; +public: + ParsedExpressionKind GetKind() final{ + return ParsedExpressionKind::Variable; + } + explicit VariableExpression(IdentifierToken* token) : ParsedExpression(token -> GetStartPosition(), token -> GetLength()) + , _value(HashedString(token -> Value)) + { + } + + HashedString GetValue(){ + return _value; + } +}; + + class ParenthesizedExpression : public ParsedExpression{ ParsedExpression* _expression; public: diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index e2f2faa..ad445a2 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -151,6 +151,7 @@ ParsedExpression *Parser::ParsePrimaryExpression(IToken *current) { case TokenKind ::String: return new LiteralStringExpression((StringToken*)current); case TokenKind ::TrueKeyword: return new LiteralBoolExpression(current); case TokenKind ::FalseKeyword: return new LiteralBoolExpression(current); + case TokenKind ::Identifier: return new VariableExpression((IdentifierToken*)current); case TokenKind ::OpenParenthesis: return this -> ParseParenthesizedExpression(current); // If we find a bad token here, we should have already logged it in the lexer, so don't log another error. case TokenKind ::BadToken: return new BadExpression(current->GetStartPosition(), current->GetLength()); diff --git a/src/Script.cpp b/src/Script.cpp index 27a51ae..774cc65 100644 --- a/src/Script.cpp +++ b/src/Script.cpp @@ -28,7 +28,7 @@ Script::~Script() { delete this -> BoundScript; delete this -> _lastValue; delete this -> _evaluator; - for (auto v : *this -> _scriptVariables){ + for (auto v : *this->_scriptVariables){ delete v.second; } this->_scriptVariables->clear(); diff --git a/tests/integration/Variables.cpp b/tests/integration/Variables.cpp index bd4971e..d25610a 100644 --- a/tests/integration/Variables.cpp +++ b/tests/integration/Variables.cpp @@ -22,4 +22,27 @@ TEST_CASE( "Create local variable", "[integration]" ) { delete script; } +TEST_CASE( "Create script variable and use", "[integration]" ) { + Script* script = Script::Create("foo = false\n" + "bar = not foo"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto variable = script->GetVariable("bar"); + REQUIRE(variable != nullptr); + CHECK(variable->EvaluateBool()); + delete script; +} + +TEST_CASE( "Create local variable and use", "[integration]" ) { + Script* script = Script::Create("local foo = false\n" + "bar = not foo"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto variable = script->GetVariable("bar"); + REQUIRE(variable != nullptr); + CHECK(variable->EvaluateBool()); + delete script; +} + + #endif