diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index 5e4a87d..4433e0d 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -128,6 +128,9 @@ BoundExpression* Binder::BindExpression(ParsedExpression* expression){ case ParsedExpressionKind ::FunctionCall: return this->BindFunctionCall((FunctionCallExpression*)expression); + case ParsedExpressionKind ::Indexer: + return this->BindIndexExpression((IndexExpression*)expression); + case ParsedExpressionKind ::Bad: return new BoundBadExpression(expression->GetStartPosition(), expression-> GetLength()); } @@ -308,4 +311,17 @@ BoundExpression* Binder::BindFunctionCall(FunctionCallExpression* expression){ return new BoundFunctionCallExpression(functionExpression, boundParameters, functionType.get()->GetReturnType(), expression->GetStartPosition(), expression->GetLength()); -} \ No newline at end of file +} + +BoundExpression *Binder::BindIndexExpression(IndexExpression *expression) { + auto indexer = this->BindExpression(expression->GetIndexer()); + auto index = this->BindExpression(expression->GetIndex()); + + if (!indexer->GetType()->CanBeIndexedWith(index->GetType().get())){ + this->_scriptData->Diagnostics->LogError(DiagnosticCode::CantIndex, index->GetStartPosition(), + index->GetLength()); + return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); + } + auto resultType = shared_ptr(indexer->GetType()->GetIndexedType(index->GetType().get())); + return new BoundIndexExpression(indexer, index, resultType, expression->GetStartPosition(), expression->GetLength()); +} diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index e13127e..4279a46 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -24,6 +24,7 @@ class Binder { BoundExpression *BindBinaryOperator(BinaryExpression *expression); BoundExpression *BindUnaryOperator(UnaryExpression *expression); BoundExpression *BindFunctionCall(FunctionCallExpression *expression); + BoundExpression *BindIndexExpression(IndexExpression *expression); public: static BoundScriptStatement* Bind(Script* script, ParsedScriptStatement* s, BoundScope* scriptScope); diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index 9585bca..b7532eb 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -2,6 +2,8 @@ #include +#include + #ifndef PORYGONLANG_BOUNDEXPRESSION_HPP #define PORYGONLANG_BOUNDEXPRESSION_HPP @@ -26,6 +28,7 @@ enum class BoundExpressionKind{ Unary, Binary, FunctionCall, + Index, }; class BoundExpression{ @@ -36,7 +39,7 @@ public: BoundExpression(unsigned int start, unsigned int length, std::shared_ptr type){ _start = start; _length = length; - _type = type; + _type = std::move(type); } virtual ~BoundExpression() = default; @@ -247,5 +250,31 @@ public: } }; +class BoundIndexExpression : public BoundExpression { + BoundExpression* _indexableExpression; + BoundExpression* _indexExpression; +public: + BoundIndexExpression(BoundExpression* indexableExpression, BoundExpression* indexExpression, shared_ptr result, + unsigned int start, unsigned int length) + : BoundExpression(start, length, std::move(result)), _indexableExpression(indexableExpression), _indexExpression(indexExpression) {} + + ~BoundIndexExpression() final{ + delete _indexableExpression; + delete _indexExpression; + } + + BoundExpressionKind GetKind() final{ + return BoundExpressionKind ::Index; + } + + BoundExpression* GetIndexableExpression(){ + return _indexableExpression; + } + + BoundExpression* GetIndexExpression(){ + return _indexExpression; + } +}; + #endif //PORYGONLANG_BOUNDEXPRESSION_HPP diff --git a/src/Diagnostics/DiagnosticCode.hpp b/src/Diagnostics/DiagnosticCode.hpp index 9d49fe1..b1c567b 100644 --- a/src/Diagnostics/DiagnosticCode.hpp +++ b/src/Diagnostics/DiagnosticCode.hpp @@ -18,6 +18,7 @@ enum class DiagnosticCode{ ExpressionIsNotAFunction, ParameterCountMismatch, ParameterTypeMismatch, + CantIndex, }; #endif //PORYGONLANG_DIAGNOSTICCODE_HPP diff --git a/src/Evaluator/EvalValues/EvalValue.hpp b/src/Evaluator/EvalValues/EvalValue.hpp index 202d6b9..ddd1237 100644 --- a/src/Evaluator/EvalValues/EvalValue.hpp +++ b/src/Evaluator/EvalValues/EvalValue.hpp @@ -34,6 +34,10 @@ public: virtual std::string* EvaluateString(){ throw EvaluationException("Can't evaluate this EvalValue as string."); } + + virtual EvalValue* IndexValue(EvalValue* val){ + throw EvaluationException("Can't index this EvalValue"); + } }; class BooleanEvalValue : public EvalValue{ diff --git a/src/Evaluator/EvalValues/StringEvalValue.hpp b/src/Evaluator/EvalValues/StringEvalValue.hpp index d366c2e..d052768 100644 --- a/src/Evaluator/EvalValues/StringEvalValue.hpp +++ b/src/Evaluator/EvalValues/StringEvalValue.hpp @@ -32,6 +32,12 @@ public: shared_ptr Clone() final{ return make_shared(_value); } + + EvalValue* IndexValue(EvalValue* val) final{ + // Porygon is 1-indexed, so we convert to that. + auto l = val->EvaluateInteger() - 1; + return new StringEvalValue(string(1, _value[l])); + } }; diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index 94637a7..f5bf255 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -89,6 +89,7 @@ shared_ptr Evaluator::EvaluateIntegerExpression(BoundExpressio case BoundExpressionKind ::Binary: return this -> EvaluateIntegerBinary((BoundBinaryExpression*)expression); case BoundExpressionKind::Variable: return dynamic_pointer_cast(this->GetVariable((BoundVariableExpression*)expression)); case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast(this->EvaluateFunctionCallExpression(expression)); + case BoundExpressionKind ::Index: return dynamic_pointer_cast(this->EvaluateIndexExpression(expression)); case BoundExpressionKind ::LiteralString: case BoundExpressionKind ::LiteralBool: @@ -104,6 +105,7 @@ shared_ptr Evaluator::EvaluateBoolExpression(BoundExpression * case BoundExpressionKind::Binary: return this -> EvaluateBooleanBinary((BoundBinaryExpression*)expression); case BoundExpressionKind::Variable: return dynamic_pointer_cast(this->GetVariable((BoundVariableExpression*)expression)); case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast(this->EvaluateFunctionCallExpression(expression)); + case BoundExpressionKind ::Index: return dynamic_pointer_cast(this->EvaluateIndexExpression(expression)); case BoundExpressionKind::Bad: case BoundExpressionKind::LiteralInteger: @@ -122,6 +124,7 @@ shared_ptr Evaluator::EvaluateStringExpression(BoundExpression return this -> EvaluateStringBinary((BoundBinaryExpression*)expression); case BoundExpressionKind::Variable: return dynamic_pointer_cast(this->GetVariable((BoundVariableExpression*)expression)); case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast(this->EvaluateFunctionCallExpression(expression)); + case BoundExpressionKind ::Index: return dynamic_pointer_cast(this->EvaluateIndexExpression(expression)); case BoundExpressionKind::Bad: case BoundExpressionKind::LiteralInteger: @@ -190,4 +193,11 @@ EvalValue* Evaluator::EvaluateFunction(ScriptFunctionEvalValue *function, vector } this->EvaluateBlockStatement(function->GetInnerBlock().get()); return nullptr; +} + +shared_ptr Evaluator::EvaluateIndexExpression(BoundExpression *expression) { + auto indexExpression = (BoundIndexExpression*)expression; + auto index = this -> EvaluateExpression(indexExpression->GetIndexExpression()); + auto indexable = this -> EvaluateExpression(indexExpression->GetIndexableExpression()); + return shared_ptr(indexable -> IndexValue(index.get())); } \ No newline at end of file diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index 3b63ff7..cf5ddb3 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -41,6 +41,7 @@ class Evaluator { shared_ptr EvaluateIntegerUnary(BoundUnaryExpression* expression); shared_ptr EvaluateBooleanUnary(BoundUnaryExpression *expression); shared_ptr EvaluateFunctionCallExpression(BoundExpression *expression); + shared_ptr EvaluateIndexExpression(BoundExpression* expression); shared_ptr GetVariable(BoundVariableExpression *expression); public: @@ -56,10 +57,6 @@ public: void Evaluate(BoundScriptStatement* statement); EvalValue* EvaluateFunction(ScriptFunctionEvalValue* func, vector parameters); - EvaluationScope* GetScope(){ - return _evaluationScope; - } - EvalValue* GetLastValue(){ return _lastValue.get(); } diff --git a/src/Evaluator/UnaryEvaluation.cpp b/src/Evaluator/UnaryEvaluation.cpp index e883ae1..cb4e925 100644 --- a/src/Evaluator/UnaryEvaluation.cpp +++ b/src/Evaluator/UnaryEvaluation.cpp @@ -34,4 +34,3 @@ shared_ptr Evaluator::EvaluateBooleanUnary(BoundUnaryExpressio throw; } } - diff --git a/src/Parser/ParsedExpressions/ParsedExpression.hpp b/src/Parser/ParsedExpressions/ParsedExpression.hpp index e58aff7..299903c 100644 --- a/src/Parser/ParsedExpressions/ParsedExpression.hpp +++ b/src/Parser/ParsedExpressions/ParsedExpression.hpp @@ -22,6 +22,7 @@ enum class ParsedExpressionKind{ Binary, Parenthesized, FunctionCall, + Indexer, }; class ParsedExpression { @@ -246,5 +247,28 @@ public: } }; +class IndexExpression : public ParsedExpression{ + std::unique_ptr _indexerExpression; + std::unique_ptr _indexExpression; +public: + IndexExpression(ParsedExpression* indexer, ParsedExpression* index, unsigned int start, unsigned int length) + :ParsedExpression(start, length){ + _indexerExpression = std::unique_ptr(indexer); + _indexExpression = std::unique_ptr(index); + } + + ParsedExpressionKind GetKind() final{ + return ParsedExpressionKind::Indexer; + } + + ParsedExpression* GetIndexer(){ + return _indexerExpression.get(); + } + + ParsedExpression* GetIndex(){ + return _indexExpression.get(); + } +}; + #endif //PORYGONLANG_PARSEDEXPRESSION_HPP diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index d6ca0ed..c677e6b 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -146,10 +146,11 @@ ParsedExpression* Parser::ParseExpression(IToken* current){ if (peekKind == TokenKind::OpenParenthesis){ expression = this->ParseFunctionCallExpression(expression); } else if (peekKind == TokenKind::OpenSquareBracket){ - //TODO: index expression + expression = this->ParseIndexExpression(expression); } else { //TODO: index period expression } + peekKind = this->Peek()->GetKind(); } return expression; } @@ -283,7 +284,17 @@ ParsedExpression *Parser::ParseFunctionCallExpression(ParsedExpression* function return new FunctionCallExpression(functionExpression, parameters, start, peeked->GetEndPosition() - start); } - +ParsedExpression* Parser::ParseIndexExpression(ParsedExpression* indexingExpression){ + this->Next(); // consume '[' token + auto indexExpression = this -> ParseExpression(this -> Next()); + auto closeBracket = this->Next(); + if (closeBracket->GetKind() != TokenKind::CloseSquareBracket){ + this->ScriptData->Diagnostics->LogError(DiagnosticCode::UnexpectedToken, closeBracket->GetStartPosition(), closeBracket->GetLength()); + return new BadExpression(closeBracket->GetStartPosition(), closeBracket->GetLength()); + } + auto start = indexingExpression->GetStartPosition(); + return new IndexExpression(indexingExpression, indexExpression, start, closeBracket->GetEndPosition() - start); +} diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index 3901392..9877e0b 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -34,7 +34,9 @@ class Parser { ParsedExpression* ParseBinaryExpression(IToken* current, OperatorPrecedence parentPrecedence); ParsedExpression* ParsePrimaryExpression(IToken* current); ParsedExpression* ParseParenthesizedExpression(IToken *current); + ParsedExpression* ParseFunctionCallExpression(ParsedExpression* functionExpression); + ParsedExpression *ParseIndexExpression(ParsedExpression *indexingExpression); public: ParsedScriptStatement* Parse(); explicit Parser(vector tokens, Script* scriptData){ diff --git a/src/ScriptType.cpp b/src/ScriptType.cpp new file mode 100644 index 0000000..e1124b2 --- /dev/null +++ b/src/ScriptType.cpp @@ -0,0 +1,13 @@ +#include "Script.hpp" + +bool ScriptType::CanBeIndexedWith(ScriptType *indexer) { + // String type is the only simple script type we want to + return _class == TypeClass::String && indexer->_class == TypeClass::Number && !((NumericScriptType*)indexer)->IsFloat(); +} + +ScriptType *ScriptType::GetIndexedType(ScriptType *indexer) { + if (_class == TypeClass::String){ + return new ScriptType(TypeClass::String); + } + return new ScriptType(TypeClass::Error); +} diff --git a/src/ScriptType.hpp b/src/ScriptType.hpp index 0097ed4..d9d78c8 100644 --- a/src/ScriptType.hpp +++ b/src/ScriptType.hpp @@ -52,6 +52,10 @@ public: virtual bool operator !=(ScriptType* b){ return ! (operator==(b)); } + + virtual bool CanBeIndexedWith(ScriptType* indexer); + + virtual ScriptType* GetIndexedType(ScriptType* indexer); }; class NumericScriptType : public ScriptType{ diff --git a/tests/integration/IndexTests.cpp b/tests/integration/IndexTests.cpp new file mode 100644 index 0000000..6fb1592 --- /dev/null +++ b/tests/integration/IndexTests.cpp @@ -0,0 +1,14 @@ +#ifdef TESTS_BUILD +#include +#include "../src/Script.hpp" + +TEST_CASE( "String indexing", "[integration]" ) { + auto script = Script::Create("'foobar'[4]"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto lastValue = script->GetLastValue(); + REQUIRE(*lastValue->EvaluateString() == "b"); + delete script; +} + +#endif