Implements indexing, currently can only be used with strings
This commit is contained in:
		| @@ -128,6 +128,9 @@ BoundExpression* Binder::BindExpression(ParsedExpression* expression){ | |||||||
|         case ParsedExpressionKind ::FunctionCall: |         case ParsedExpressionKind ::FunctionCall: | ||||||
|             return this->BindFunctionCall((FunctionCallExpression*)expression); |             return this->BindFunctionCall((FunctionCallExpression*)expression); | ||||||
|  |  | ||||||
|  |         case ParsedExpressionKind ::Indexer: | ||||||
|  |             return this->BindIndexExpression((IndexExpression*)expression); | ||||||
|  |  | ||||||
|         case ParsedExpressionKind ::Bad: |         case ParsedExpressionKind ::Bad: | ||||||
|             return new BoundBadExpression(expression->GetStartPosition(), expression-> GetLength()); |             return new BoundBadExpression(expression->GetStartPosition(), expression-> GetLength()); | ||||||
|     } |     } | ||||||
| @@ -309,3 +312,16 @@ BoundExpression* Binder::BindFunctionCall(FunctionCallExpression* expression){ | |||||||
|     return new BoundFunctionCallExpression(functionExpression, boundParameters, functionType.get()->GetReturnType(), |     return new BoundFunctionCallExpression(functionExpression, boundParameters, functionType.get()->GetReturnType(), | ||||||
|             expression->GetStartPosition(), expression->GetLength()); |             expression->GetStartPosition(), expression->GetLength()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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<ScriptType>(indexer->GetType()->GetIndexedType(index->GetType().get())); | ||||||
|  |     return new BoundIndexExpression(indexer, index, resultType, expression->GetStartPosition(), expression->GetLength()); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ class Binder { | |||||||
|     BoundExpression *BindBinaryOperator(BinaryExpression *expression); |     BoundExpression *BindBinaryOperator(BinaryExpression *expression); | ||||||
|     BoundExpression *BindUnaryOperator(UnaryExpression *expression); |     BoundExpression *BindUnaryOperator(UnaryExpression *expression); | ||||||
|     BoundExpression *BindFunctionCall(FunctionCallExpression *expression); |     BoundExpression *BindFunctionCall(FunctionCallExpression *expression); | ||||||
|  |     BoundExpression *BindIndexExpression(IndexExpression *expression); | ||||||
| public: | public: | ||||||
|     static BoundScriptStatement* Bind(Script* script, ParsedScriptStatement* s, BoundScope* scriptScope); |     static BoundScriptStatement* Bind(Script* script, ParsedScriptStatement* s, BoundScope* scriptScope); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| #include <utility> | #include <utility> | ||||||
|  |  | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifndef PORYGONLANG_BOUNDEXPRESSION_HPP | #ifndef PORYGONLANG_BOUNDEXPRESSION_HPP | ||||||
| #define PORYGONLANG_BOUNDEXPRESSION_HPP | #define PORYGONLANG_BOUNDEXPRESSION_HPP | ||||||
| @@ -26,6 +28,7 @@ enum class BoundExpressionKind{ | |||||||
|     Unary, |     Unary, | ||||||
|     Binary, |     Binary, | ||||||
|     FunctionCall, |     FunctionCall, | ||||||
|  |     Index, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class BoundExpression{ | class BoundExpression{ | ||||||
| @@ -36,7 +39,7 @@ public: | |||||||
|     BoundExpression(unsigned int start, unsigned int length, std::shared_ptr<ScriptType> type){ |     BoundExpression(unsigned int start, unsigned int length, std::shared_ptr<ScriptType> type){ | ||||||
|         _start = start; |         _start = start; | ||||||
|         _length = length; |         _length = length; | ||||||
|         _type = type; |         _type = std::move(type); | ||||||
|     } |     } | ||||||
|     virtual ~BoundExpression() = default; |     virtual ~BoundExpression() = default; | ||||||
|  |  | ||||||
| @@ -247,5 +250,31 @@ public: | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class BoundIndexExpression : public BoundExpression { | ||||||
|  |     BoundExpression* _indexableExpression; | ||||||
|  |     BoundExpression* _indexExpression; | ||||||
|  | public: | ||||||
|  |     BoundIndexExpression(BoundExpression* indexableExpression, BoundExpression* indexExpression, shared_ptr<ScriptType> 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 | #endif //PORYGONLANG_BOUNDEXPRESSION_HPP | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ enum class DiagnosticCode{ | |||||||
|     ExpressionIsNotAFunction, |     ExpressionIsNotAFunction, | ||||||
|     ParameterCountMismatch, |     ParameterCountMismatch, | ||||||
|     ParameterTypeMismatch, |     ParameterTypeMismatch, | ||||||
|  |     CantIndex, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif //PORYGONLANG_DIAGNOSTICCODE_HPP | #endif //PORYGONLANG_DIAGNOSTICCODE_HPP | ||||||
|   | |||||||
| @@ -34,6 +34,10 @@ public: | |||||||
|     virtual std::string* EvaluateString(){ |     virtual std::string* EvaluateString(){ | ||||||
|         throw EvaluationException("Can't evaluate this EvalValue as string."); |         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{ | class BooleanEvalValue : public EvalValue{ | ||||||
|   | |||||||
| @@ -32,6 +32,12 @@ public: | |||||||
|     shared_ptr<EvalValue> Clone() final{ |     shared_ptr<EvalValue> Clone() final{ | ||||||
|         return make_shared<StringEvalValue>(_value); |         return make_shared<StringEvalValue>(_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])); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -89,6 +89,7 @@ shared_ptr<NumericEvalValue> Evaluator::EvaluateIntegerExpression(BoundExpressio | |||||||
|         case BoundExpressionKind ::Binary: return this -> EvaluateIntegerBinary((BoundBinaryExpression*)expression); |         case BoundExpressionKind ::Binary: return this -> EvaluateIntegerBinary((BoundBinaryExpression*)expression); | ||||||
|         case BoundExpressionKind::Variable: return dynamic_pointer_cast<NumericEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); |         case BoundExpressionKind::Variable: return dynamic_pointer_cast<NumericEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); | ||||||
|         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<NumericEvalValue>(this->EvaluateFunctionCallExpression(expression)); |         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<NumericEvalValue>(this->EvaluateFunctionCallExpression(expression)); | ||||||
|  |         case BoundExpressionKind ::Index: return dynamic_pointer_cast<NumericEvalValue>(this->EvaluateIndexExpression(expression)); | ||||||
|  |  | ||||||
|         case BoundExpressionKind ::LiteralString: |         case BoundExpressionKind ::LiteralString: | ||||||
|         case BoundExpressionKind ::LiteralBool: |         case BoundExpressionKind ::LiteralBool: | ||||||
| @@ -104,6 +105,7 @@ shared_ptr<BooleanEvalValue> Evaluator::EvaluateBoolExpression(BoundExpression * | |||||||
|         case BoundExpressionKind::Binary: return this -> EvaluateBooleanBinary((BoundBinaryExpression*)expression); |         case BoundExpressionKind::Binary: return this -> EvaluateBooleanBinary((BoundBinaryExpression*)expression); | ||||||
|         case BoundExpressionKind::Variable: return dynamic_pointer_cast<BooleanEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); |         case BoundExpressionKind::Variable: return dynamic_pointer_cast<BooleanEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); | ||||||
|         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<BooleanEvalValue>(this->EvaluateFunctionCallExpression(expression)); |         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<BooleanEvalValue>(this->EvaluateFunctionCallExpression(expression)); | ||||||
|  |         case BoundExpressionKind ::Index: return dynamic_pointer_cast<BooleanEvalValue>(this->EvaluateIndexExpression(expression)); | ||||||
|  |  | ||||||
|         case BoundExpressionKind::Bad: |         case BoundExpressionKind::Bad: | ||||||
|         case BoundExpressionKind::LiteralInteger: |         case BoundExpressionKind::LiteralInteger: | ||||||
| @@ -122,6 +124,7 @@ shared_ptr<StringEvalValue> Evaluator::EvaluateStringExpression(BoundExpression | |||||||
|             return this -> EvaluateStringBinary((BoundBinaryExpression*)expression); |             return this -> EvaluateStringBinary((BoundBinaryExpression*)expression); | ||||||
|         case BoundExpressionKind::Variable: return dynamic_pointer_cast<StringEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); |         case BoundExpressionKind::Variable: return dynamic_pointer_cast<StringEvalValue>(this->GetVariable((BoundVariableExpression*)expression)); | ||||||
|         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<StringEvalValue>(this->EvaluateFunctionCallExpression(expression)); |         case BoundExpressionKind ::FunctionCall: return dynamic_pointer_cast<StringEvalValue>(this->EvaluateFunctionCallExpression(expression)); | ||||||
|  |         case BoundExpressionKind ::Index: return dynamic_pointer_cast<StringEvalValue>(this->EvaluateIndexExpression(expression)); | ||||||
|  |  | ||||||
|         case BoundExpressionKind::Bad: |         case BoundExpressionKind::Bad: | ||||||
|         case BoundExpressionKind::LiteralInteger: |         case BoundExpressionKind::LiteralInteger: | ||||||
| @@ -191,3 +194,10 @@ EvalValue* Evaluator::EvaluateFunction(ScriptFunctionEvalValue *function, vector | |||||||
|     this->EvaluateBlockStatement(function->GetInnerBlock().get()); |     this->EvaluateBlockStatement(function->GetInnerBlock().get()); | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | shared_ptr<EvalValue> Evaluator::EvaluateIndexExpression(BoundExpression *expression) { | ||||||
|  |     auto indexExpression = (BoundIndexExpression*)expression; | ||||||
|  |     auto index = this -> EvaluateExpression(indexExpression->GetIndexExpression()); | ||||||
|  |     auto indexable = this -> EvaluateExpression(indexExpression->GetIndexableExpression()); | ||||||
|  |     return shared_ptr<EvalValue>(indexable -> IndexValue(index.get())); | ||||||
|  | } | ||||||
| @@ -41,6 +41,7 @@ class Evaluator { | |||||||
|     shared_ptr<NumericEvalValue> EvaluateIntegerUnary(BoundUnaryExpression* expression); |     shared_ptr<NumericEvalValue> EvaluateIntegerUnary(BoundUnaryExpression* expression); | ||||||
|     shared_ptr<BooleanEvalValue> EvaluateBooleanUnary(BoundUnaryExpression *expression); |     shared_ptr<BooleanEvalValue> EvaluateBooleanUnary(BoundUnaryExpression *expression); | ||||||
|     shared_ptr<EvalValue> EvaluateFunctionCallExpression(BoundExpression *expression); |     shared_ptr<EvalValue> EvaluateFunctionCallExpression(BoundExpression *expression); | ||||||
|  |     shared_ptr<EvalValue> EvaluateIndexExpression(BoundExpression* expression); | ||||||
|  |  | ||||||
|     shared_ptr<EvalValue> GetVariable(BoundVariableExpression *expression); |     shared_ptr<EvalValue> GetVariable(BoundVariableExpression *expression); | ||||||
| public: | public: | ||||||
| @@ -56,10 +57,6 @@ public: | |||||||
|     void Evaluate(BoundScriptStatement* statement); |     void Evaluate(BoundScriptStatement* statement); | ||||||
|     EvalValue* EvaluateFunction(ScriptFunctionEvalValue* func, vector<EvalValue*> parameters); |     EvalValue* EvaluateFunction(ScriptFunctionEvalValue* func, vector<EvalValue*> parameters); | ||||||
|  |  | ||||||
|     EvaluationScope* GetScope(){ |  | ||||||
|         return _evaluationScope; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     EvalValue* GetLastValue(){ |     EvalValue* GetLastValue(){ | ||||||
|         return _lastValue.get(); |         return _lastValue.get(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -34,4 +34,3 @@ shared_ptr<BooleanEvalValue> Evaluator::EvaluateBooleanUnary(BoundUnaryExpressio | |||||||
|             throw; |             throw; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ enum class ParsedExpressionKind{ | |||||||
|     Binary, |     Binary, | ||||||
|     Parenthesized, |     Parenthesized, | ||||||
|     FunctionCall, |     FunctionCall, | ||||||
|  |     Indexer, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ParsedExpression { | class ParsedExpression { | ||||||
| @@ -246,5 +247,28 @@ public: | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class IndexExpression : public ParsedExpression{ | ||||||
|  |     std::unique_ptr<ParsedExpression> _indexerExpression; | ||||||
|  |     std::unique_ptr<ParsedExpression> _indexExpression; | ||||||
|  | public: | ||||||
|  |     IndexExpression(ParsedExpression* indexer, ParsedExpression* index, unsigned int start, unsigned int length) | ||||||
|  |     :ParsedExpression(start, length){ | ||||||
|  |         _indexerExpression = std::unique_ptr<ParsedExpression>(indexer); | ||||||
|  |         _indexExpression = std::unique_ptr<ParsedExpression>(index); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ParsedExpressionKind GetKind() final{ | ||||||
|  |         return ParsedExpressionKind::Indexer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ParsedExpression* GetIndexer(){ | ||||||
|  |         return _indexerExpression.get(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ParsedExpression* GetIndex(){ | ||||||
|  |         return _indexExpression.get(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif //PORYGONLANG_PARSEDEXPRESSION_HPP | #endif //PORYGONLANG_PARSEDEXPRESSION_HPP | ||||||
|   | |||||||
| @@ -146,10 +146,11 @@ ParsedExpression* Parser::ParseExpression(IToken* current){ | |||||||
|         if (peekKind == TokenKind::OpenParenthesis){ |         if (peekKind == TokenKind::OpenParenthesis){ | ||||||
|             expression = this->ParseFunctionCallExpression(expression); |             expression = this->ParseFunctionCallExpression(expression); | ||||||
|         } else if (peekKind == TokenKind::OpenSquareBracket){ |         } else if (peekKind == TokenKind::OpenSquareBracket){ | ||||||
|             //TODO: index expression |             expression = this->ParseIndexExpression(expression); | ||||||
|         } else { |         } else { | ||||||
|             //TODO: index period expression |             //TODO: index period expression | ||||||
|         } |         } | ||||||
|  |         peekKind = this->Peek()->GetKind(); | ||||||
|     } |     } | ||||||
|     return expression; |     return expression; | ||||||
| } | } | ||||||
| @@ -283,7 +284,17 @@ ParsedExpression *Parser::ParseFunctionCallExpression(ParsedExpression* function | |||||||
|     return new FunctionCallExpression(functionExpression, parameters, start, peeked->GetEndPosition() - start); |     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); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -34,7 +34,9 @@ class Parser { | |||||||
|     ParsedExpression* ParseBinaryExpression(IToken* current, OperatorPrecedence parentPrecedence); |     ParsedExpression* ParseBinaryExpression(IToken* current, OperatorPrecedence parentPrecedence); | ||||||
|     ParsedExpression* ParsePrimaryExpression(IToken* current); |     ParsedExpression* ParsePrimaryExpression(IToken* current); | ||||||
|     ParsedExpression* ParseParenthesizedExpression(IToken *current); |     ParsedExpression* ParseParenthesizedExpression(IToken *current); | ||||||
|  |  | ||||||
|     ParsedExpression* ParseFunctionCallExpression(ParsedExpression* functionExpression); |     ParsedExpression* ParseFunctionCallExpression(ParsedExpression* functionExpression); | ||||||
|  |     ParsedExpression *ParseIndexExpression(ParsedExpression *indexingExpression); | ||||||
| public: | public: | ||||||
|     ParsedScriptStatement* Parse(); |     ParsedScriptStatement* Parse(); | ||||||
|     explicit Parser(vector<IToken*> tokens, Script* scriptData){ |     explicit Parser(vector<IToken*> tokens, Script* scriptData){ | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								src/ScriptType.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/ScriptType.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||||
|  | } | ||||||
| @@ -52,6 +52,10 @@ public: | |||||||
|     virtual bool operator !=(ScriptType* b){ |     virtual bool operator !=(ScriptType* b){ | ||||||
|         return ! (operator==(b)); |         return ! (operator==(b)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     virtual bool CanBeIndexedWith(ScriptType* indexer); | ||||||
|  |  | ||||||
|  |     virtual ScriptType* GetIndexedType(ScriptType* indexer); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class NumericScriptType : public ScriptType{ | class NumericScriptType : public ScriptType{ | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								tests/integration/IndexTests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/integration/IndexTests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | #ifdef TESTS_BUILD | ||||||
|  | #include <catch.hpp> | ||||||
|  | #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 | ||||||
		Reference in New Issue
	
	Block a user