Implements indexing, currently can only be used with strings
This commit is contained in:
parent
b275e1fbd6
commit
cb5d9e2f62
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -308,4 +311,17 @@ 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:
|
||||||
|
@ -190,4 +193,11 @@ 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){
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue