Implements basic numerical tables
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-06-09 20:15:09 +02:00
parent ec2419bc7d
commit 081def0be0
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
21 changed files with 324 additions and 24 deletions

View File

@ -1,3 +1,5 @@
#include <memory>
#include "Binder.hpp"
#include <memory>
@ -174,6 +176,8 @@ BoundExpression* Binder::BindExpression(ParsedExpression* expression){
case ParsedExpressionKind ::Indexer:
return this->BindIndexExpression((IndexExpression*)expression);
case ParsedExpressionKind::NumericalTable:
return this -> BindNumericalTableExpression((ParsedNumericalTableExpression*)expression);
case ParsedExpressionKind ::Bad:
return new BoundBadExpression(expression->GetStartPosition(), expression-> GetLength());
@ -386,7 +390,29 @@ BoundExpression *Binder::BindIndexExpression(IndexExpression *expression) {
index->GetLength());
return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength());
}
auto resultType = shared_ptr<ScriptType>(indexer->GetType()->GetIndexedType(index->GetType().get()));
auto resultType = indexer->GetType()->GetIndexedType(index->GetType().get());
return new BoundIndexExpression(indexer, index, resultType, expression->GetStartPosition(), expression->GetLength());
}
BoundExpression* Binder::BindNumericalTableExpression(ParsedNumericalTableExpression* expression){
auto expressions = expression->GetExpressions();
auto boundExpressions = vector<BoundExpression*>(expressions.size());
shared_ptr<ScriptType> valueType = nullptr;
if (!boundExpressions.empty()){
boundExpressions[0] = this -> BindExpression(expressions[0]);
valueType = boundExpressions[0] -> GetType();
for (int i = 1; i < expressions.size(); i++){
boundExpressions[i] = this -> BindExpression(expressions[i]);
if (boundExpressions[i] -> GetType().get()->operator!=(valueType.get())){
this->_scriptData->Diagnostics->LogError(DiagnosticCode::InvalidTableValueType, boundExpressions[i]->GetStartPosition(),
boundExpressions[i]->GetLength());
}
}
}
if (valueType == nullptr){
valueType = std::make_shared<ScriptType>(TypeClass::Nil);
}
auto keyType = std::make_shared<ScriptType>(TypeClass::Number);
auto tableType = std::make_shared<TableScriptType>(keyType, valueType);
return new BoundNumericalTableExpression(boundExpressions, tableType, expression->GetStartPosition(), expression->GetLength());
}

View File

@ -28,6 +28,7 @@ class Binder {
BoundExpression *BindUnaryOperator(UnaryExpression *expression);
BoundExpression *BindFunctionCall(FunctionCallExpression *expression);
BoundExpression *BindIndexExpression(IndexExpression *expression);
BoundExpression *BindNumericalTableExpression(ParsedNumericalTableExpression *expression);
public:
static BoundScriptStatement* Bind(Script* script, ParsedScriptStatement* s, BoundScope* scriptScope);

View File

@ -1,15 +1,10 @@
#include <utility>
#include <utility>
#include <utility>
#ifndef PORYGONLANG_BOUNDEXPRESSION_HPP
#define PORYGONLANG_BOUNDEXPRESSION_HPP
#include <string>
#include <memory>
#include <utility>
#include "../../ScriptType.hpp"
#include "../BoundOperators.hpp"
#include "../BoundVariables/BoundVariableKey.hpp"
@ -29,6 +24,7 @@ enum class BoundExpressionKind{
Binary,
FunctionCall,
Index,
NumericalTable,
};
class BoundExpression{
@ -276,5 +272,28 @@ public:
}
};
class BoundNumericalTableExpression : public BoundExpression{
vector<BoundExpression*> _expressions;
public:
BoundNumericalTableExpression(vector<BoundExpression*> expressions, shared_ptr<ScriptType> type, unsigned int start, unsigned int length)
: BoundExpression(start, length, type){
_expressions = std::move(expressions);
}
~BoundNumericalTableExpression(){
for (auto e: _expressions){
delete e;
}
}
BoundExpressionKind GetKind() final{
return BoundExpressionKind ::NumericalTable;
}
vector<BoundExpression*> GetExpressions(){
return _expressions;
}
};
#endif //PORYGONLANG_BOUNDEXPRESSION_HPP

View File

@ -21,6 +21,7 @@ enum class DiagnosticCode{
CantIndex,
InvalidReturnType,
ConditionNotABool,
InvalidTableValueType,
};
#endif //PORYGONLANG_DIAGNOSTICCODE_HPP

View File

@ -35,7 +35,9 @@ public:
throw EvaluationException("Can't evaluate this EvalValue as string.");
}
virtual EvalValue* IndexValue(EvalValue* val){
virtual std::size_t GetHashCode() = 0;
virtual shared_ptr<EvalValue> IndexValue(EvalValue* val){
throw EvaluationException("Can't index this EvalValue");
}
};
@ -66,6 +68,10 @@ public:
return false;
return this->EvaluateBool() == b->EvaluateBool();
};
std::size_t GetHashCode() final{
return _value;
}
};
#endif //PORYGONLANG_EVALVALUE_HPP

View File

@ -49,6 +49,10 @@ public:
shared_ptr<EvalValue> Clone() final{
return make_shared<IntegerEvalValue>(_value);
}
std::size_t GetHashCode() final{
return std::hash<long>{}(_value);
}
};
class FloatEvalValue : public NumericEvalValue{
@ -71,6 +75,10 @@ public:
shared_ptr<EvalValue> Clone() final{
return make_shared<FloatEvalValue>(_value);
}
std::size_t GetHashCode() final{
return std::hash<double >{}(_value);
}
};
#endif //PORYGONLANG_NUMERICEVALVALUE_HPP

View File

@ -14,11 +14,20 @@
class ScriptFunctionEvalValue : public EvalValue{
std::shared_ptr<BoundBlockStatement> _innerBlock;
std::shared_ptr<FunctionScriptType> _type;
std::size_t _hash;
explicit ScriptFunctionEvalValue(std::shared_ptr<BoundBlockStatement> innerBlock, std::shared_ptr<FunctionScriptType> type, size_t hash)
: _type(std::move(type))
{
_innerBlock = std::move(innerBlock);
_hash = hash;
}
public:
explicit ScriptFunctionEvalValue(std::shared_ptr<BoundBlockStatement> innerBlock, std::shared_ptr<FunctionScriptType> type)
: _type(std::move(type))
{
_innerBlock = std::move(innerBlock);
_hash = rand();
}
std::shared_ptr<ScriptType> GetType() final{
@ -26,20 +35,25 @@ public:
}
shared_ptr<EvalValue> Clone() final{
return make_shared<ScriptFunctionEvalValue>(_innerBlock, _type);
return shared_ptr<ScriptFunctionEvalValue>(new ScriptFunctionEvalValue(_innerBlock, _type, _hash));
}
bool operator ==(EvalValue* b) final{
if (b->GetType()->GetClass() != TypeClass::Function)
return false;
return this->_innerBlock == ((ScriptFunctionEvalValue*)b)->_innerBlock;
return this->_hash == ((ScriptFunctionEvalValue*)b)->_hash;
};
std::shared_ptr<BoundBlockStatement> GetInnerBlock(){
return _innerBlock;
}
std::size_t GetHashCode(){
return _hash;
}
};

View File

@ -9,11 +9,13 @@ using namespace std;
class StringEvalValue : public EvalValue{
string _value;
size_t _hash;
std::shared_ptr<ScriptType> _type;
public:
explicit StringEvalValue(string s){
_value = move(s);
_type = std::make_shared<ScriptType>(TypeClass::String);
_hash = std::hash<string>{}(_value);
}
std::shared_ptr<ScriptType> GetType() final{
@ -22,7 +24,7 @@ public:
bool operator ==(EvalValue* b) final{
if (b->GetType()->GetClass() != TypeClass::String)
return false;
return this->_value == *b->EvaluateString();
return this->_hash == b->GetHashCode();
};
string* EvaluateString() final{
@ -33,10 +35,14 @@ public:
return make_shared<StringEvalValue>(_value);
}
EvalValue* IndexValue(EvalValue* val) final{
shared_ptr<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]));
return make_shared<StringEvalValue>(string(1, _value[l]));
}
std::size_t GetHashCode() final{
return _hash;
}
};

View File

@ -0,0 +1,2 @@
#include "TableEvalValue.hpp"

View File

@ -0,0 +1,51 @@
#include <utility>
#ifndef PORYGONLANG_TABLEEVALVALUE_HPP
#define PORYGONLANG_TABLEEVALVALUE_HPP
#include <utility>
#include <unordered_map>
#include "EvalValue.hpp"
using namespace std;
class TableEvalValue : public EvalValue {
shared_ptr<unordered_map<size_t, shared_ptr<EvalValue>>> _table;
shared_ptr<ScriptType> _type;
size_t _hash;
explicit TableEvalValue(shared_ptr<unordered_map<size_t, shared_ptr<EvalValue>>> table, shared_ptr<ScriptType> type, size_t hash){
_table = std::move(table);
_type = std::move(type);
_hash = hash;
}
public:
explicit TableEvalValue(shared_ptr<unordered_map<size_t, shared_ptr<EvalValue>>> table, shared_ptr<ScriptType> type){
_table = std::move(table);
_type = std::move(type);
_hash = rand();
}
std::shared_ptr<ScriptType> GetType() final{
return _type;
}
size_t GetHashCode() final{
return _hash;
}
bool operator ==(EvalValue* b) final{
return this -> _hash == b->GetHashCode();
}
shared_ptr<EvalValue> Clone() final{
return shared_ptr<EvalValue>(new TableEvalValue(_table, _type, _hash));
}
shared_ptr<EvalValue> IndexValue(EvalValue* val) final{
auto hash = val->GetHashCode();
return this -> _table->at(hash);
}
};
#endif //PORYGONLANG_TABLEEVALVALUE_HPP

View File

@ -5,6 +5,7 @@
#include "../Script.hpp"
#include "EvaluationScope/EvaluationScope.hpp"
#include "EvalValues/ScriptFunctionEvalValue.hpp"
#include "EvalValues/TableEvalValue.hpp"
using namespace std;
@ -97,6 +98,7 @@ shared_ptr<EvalValue> Evaluator::EvaluateExpression(BoundExpression *expression)
case TypeClass ::String: return this -> EvaluateStringExpression(expression);
case TypeClass ::Function: return this->EvaluateFunctionExpression(expression);
case TypeClass ::Nil: return this->EvaluateNilExpression(expression);
case TypeClass ::Table: return this-> EvaluateTableExpression(expression);
default: throw;
}
}
@ -119,6 +121,7 @@ shared_ptr<NumericEvalValue> Evaluator::EvaluateIntegerExpression(BoundExpressio
case BoundExpressionKind ::LiteralString:
case BoundExpressionKind ::LiteralBool:
case BoundExpressionKind ::Bad:
case BoundExpressionKind::NumericalTable:
throw;
}
}
@ -136,6 +139,7 @@ shared_ptr<BooleanEvalValue> Evaluator::EvaluateBoolExpression(BoundExpression *
case BoundExpressionKind::LiteralInteger:
case BoundExpressionKind::LiteralFloat:
case BoundExpressionKind::LiteralString:
case BoundExpressionKind::NumericalTable:
throw;
}
@ -156,6 +160,7 @@ shared_ptr<StringEvalValue> Evaluator::EvaluateStringExpression(BoundExpression
case BoundExpressionKind::LiteralFloat:
case BoundExpressionKind::LiteralBool:
case BoundExpressionKind::Unary:
case BoundExpressionKind::NumericalTable:
throw;
}
@ -175,6 +180,19 @@ shared_ptr<EvalValue> Evaluator::EvaluateNilExpression(BoundExpression * express
return nullptr;
}
}
shared_ptr<EvalValue> Evaluator::EvaluateTableExpression(BoundExpression * expression){
switch (expression->GetKind()){
case BoundExpressionKind ::FunctionCall:
return this->EvaluateFunctionCallExpression(expression);
case BoundExpressionKind ::Variable: return this->GetVariable((BoundVariableExpression*)expression);
case BoundExpressionKind ::Index: return this->EvaluateIndexExpression(expression);
case BoundExpressionKind ::NumericalTable: return this-> EvaluateNumericTableExpression(expression);
default:
return nullptr;
}
}
shared_ptr<EvalValue> Evaluator::EvaluateFunctionCallExpression(BoundExpression* expression){
@ -239,6 +257,18 @@ shared_ptr<EvalValue> Evaluator::EvaluateIndexExpression(BoundExpression *expres
auto indexExpression = (BoundIndexExpression*)expression;
auto index = this -> EvaluateExpression(indexExpression->GetIndexExpression());
auto indexable = this -> EvaluateExpression(indexExpression->GetIndexableExpression());
return shared_ptr<EvalValue>(indexable -> IndexValue(index.get()));
return indexable -> IndexValue(index.get()) -> Clone();
}
shared_ptr<EvalValue> Evaluator::EvaluateNumericTableExpression(BoundExpression *expression) {
auto tableExpression = (BoundNumericalTableExpression*)expression;
auto valueExpressions = tableExpression->GetExpressions();
auto values = new unordered_map<size_t, shared_ptr<EvalValue>>(valueExpressions.size());
for (int i = 0; i < valueExpressions.size(); i++){
auto val = this -> EvaluateExpression(valueExpressions[i]);
values -> insert({i + 1, val});
}
auto valuesPointer = shared_ptr<unordered_map<size_t, shared_ptr<EvalValue>>>(values);
return make_shared<TableEvalValue>(valuesPointer, tableExpression->GetType());
}

View File

@ -36,6 +36,7 @@ class Evaluator {
shared_ptr<StringEvalValue> EvaluateStringExpression(BoundExpression* expression);
shared_ptr<EvalValue> EvaluateFunctionExpression(BoundExpression *expression);
shared_ptr<EvalValue>EvaluateNilExpression(BoundExpression *expression);
shared_ptr<EvalValue>EvaluateTableExpression(BoundExpression *expression);
shared_ptr<NumericEvalValue> EvaluateIntegerBinary(BoundBinaryExpression* expression);
shared_ptr<BooleanEvalValue> EvaluateBooleanBinary(BoundBinaryExpression *expression);
@ -45,6 +46,7 @@ class Evaluator {
shared_ptr<BooleanEvalValue> EvaluateBooleanUnary(BoundUnaryExpression *expression);
shared_ptr<EvalValue> EvaluateFunctionCallExpression(BoundExpression *expression);
shared_ptr<EvalValue> EvaluateIndexExpression(BoundExpression* expression);
shared_ptr<EvalValue> EvaluateNumericTableExpression(BoundExpression* expression);
shared_ptr<EvalValue> GetVariable(BoundVariableExpression *expression);
public:

View File

@ -69,6 +69,10 @@ IToken* Lexer::LexNext(char c){
return new SimpleToken(TokenKind::OpenSquareBracket, this -> _position - 1, 1);
case ']':
return new SimpleToken(TokenKind::CloseSquareBracket, this -> _position - 1, 1);
case '{':
return new SimpleToken(TokenKind::OpenCurlyBracket, this -> _position - 1, 1);
case '}':
return new SimpleToken(TokenKind::CloseCurlyBracket, this -> _position - 1, 1);
case ',':
return new SimpleToken(TokenKind::CommaToken, this -> _position - 1, 1);
case '.':

View File

@ -23,6 +23,7 @@ enum class ParsedExpressionKind{
Parenthesized,
FunctionCall,
Indexer,
NumericalTable,
};
class ParsedExpression {
@ -270,5 +271,28 @@ public:
}
};
class ParsedNumericalTableExpression : public ParsedExpression{
vector<ParsedExpression*> _expressions;
public:
ParsedNumericalTableExpression(vector<ParsedExpression*> expressions, unsigned int start, unsigned int length)
: ParsedExpression(start, length){
_expressions = std::move(expressions);
}
~ParsedNumericalTableExpression() final{
for (auto s: _expressions){
delete s;
}
}
vector<ParsedExpression*> GetExpressions(){
return _expressions;
}
ParsedExpressionKind GetKind() final{
return ParsedExpressionKind::NumericalTable;
}
};
#endif //PORYGONLANG_PARSEDEXPRESSION_HPP

View File

@ -1,3 +1,5 @@
#include <utility>
#ifndef PORYGONLANG_PARSEDSTATEMENT_HPP
#define PORYGONLANG_PARSEDSTATEMENT_HPP

View File

@ -307,6 +307,7 @@ ParsedExpression *Parser::ParsePrimaryExpression(IToken *current) {
case TokenKind ::FalseKeyword: return new LiteralBoolExpression(current);
case TokenKind ::Identifier: return new VariableExpression((IdentifierToken*)current);
case TokenKind ::OpenParenthesis: return this -> ParseParenthesizedExpression(current);
case TokenKind ::OpenCurlyBracket: return this -> ParseTableExpression(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());
default:
@ -365,5 +366,41 @@ ParsedExpression* Parser::ParseIndexExpression(ParsedExpression* indexingExpress
return new IndexExpression(indexingExpression, indexExpression, start, closeBracket->GetEndPosition() - start);
}
ParsedExpression* Parser::ParseTableExpression(IToken* current){
if (this -> Peek() -> GetKind() == TokenKind::CloseCurlyBracket){
this -> Next();
auto start = current->GetStartPosition();
return new ParsedNumericalTableExpression({}, start, this -> Peek()->GetEndPosition() - start);
}
auto firstItem = this->ParseStatement(this -> Next());
// If the first item is an expression, and is followed by a comma, we're dealing with a simple {1, 2, 3} kind of array
if (firstItem->GetKind() == ParsedStatementKind::Expression &&
(this->Peek()->GetKind() == TokenKind::CommaToken )){
auto expr = ((ParsedExpressionStatement*)firstItem)->GetExpression();
auto expressions = vector<ParsedExpression*>{expr};
auto n = this -> Next(); // consume the comma
bool hasErrors = false;
while (n->GetKind() != TokenKind::CloseCurlyBracket){
auto expression = this->ParseExpression(this->Next());
expressions.push_back(expression);
n = this->Next();
if (n->GetKind() != TokenKind::CommaToken && n->GetKind() != TokenKind ::CloseCurlyBracket && !hasErrors){
this->ScriptData->Diagnostics->LogError(DiagnosticCode::UnexpectedToken, n->GetStartPosition(), n->GetLength());
hasErrors = true;
}
}
auto start = current->GetStartPosition();
if (hasErrors){
return new BadExpression(start, n->GetEndPosition() - start);
}
return new ParsedNumericalTableExpression(expressions, start, n->GetEndPosition() - start);
}
// Otherwise we have a more complex table, which can be defined by a block
else {
auto block = (ParsedBlockStatement*)this -> ParseBlock({TokenKind ::CloseCurlyBracket});
auto statements = block->GetStatements();
statements.insert(statements.begin(), firstItem);
throw "not implemented TODO";
}
}

View File

@ -49,6 +49,8 @@ public:
IToken *PeekAt(int offset);
ParsedExpression *ParseTableExpression(IToken *current);
};

View File

@ -22,6 +22,8 @@ enum class TokenKind{
CloseParenthesis,
OpenSquareBracket,
CloseSquareBracket,
OpenCurlyBracket,
CloseCurlyBracket,
PeriodToken,
CommaToken,

View File

@ -5,9 +5,9 @@ bool ScriptType::CanBeIndexedWith(ScriptType *indexer) {
return _class == TypeClass::String && indexer->_class == TypeClass::Number && !((NumericScriptType*)indexer)->IsFloat();
}
ScriptType *ScriptType::GetIndexedType(ScriptType *indexer) {
shared_ptr<ScriptType> ScriptType::GetIndexedType(ScriptType *indexer) {
if (_class == TypeClass::String){
return new ScriptType(TypeClass::String);
return make_shared<ScriptType>(TypeClass::String);
}
return new ScriptType(TypeClass::Error);
return make_shared<ScriptType>(TypeClass::Error);
}

View File

@ -1,8 +1,3 @@
#include <utility>
#include <utility>
#include <utility>
#ifndef PORYGONLANG_SCRIPTTYPE_HPP
#define PORYGONLANG_SCRIPTTYPE_HPP
@ -55,7 +50,7 @@ public:
virtual bool CanBeIndexedWith(ScriptType* indexer);
virtual ScriptType* GetIndexedType(ScriptType* indexer);
virtual shared_ptr<ScriptType> GetIndexedType(ScriptType* indexer);
};
class NumericScriptType : public ScriptType{
@ -109,4 +104,24 @@ public:
}
};
class TableScriptType : public ScriptType{
shared_ptr<ScriptType> _keyType;
shared_ptr<ScriptType> _valueType;
// Consider adding a check whether the table actually contains a type if every key is static.
public:
TableScriptType(shared_ptr<ScriptType> keyType, shared_ptr<ScriptType> valueType) : ScriptType(TypeClass::Table){
_keyType = std::move(keyType);
_valueType = std::move(valueType);
}
bool CanBeIndexedWith(ScriptType* indexer) final{
return _keyType.get()->operator==(indexer);
}
shared_ptr<ScriptType> GetIndexedType(ScriptType* indexer) final{
return _valueType;
}
};
#endif //PORYGONLANG_SCRIPTTYPE_HPP

View File

@ -0,0 +1,48 @@
#ifdef TESTS_BUILD
#include <catch.hpp>
#include "../src/Script.hpp"
TEST_CASE( "Create empty table", "[integration]" ) {
Script* script = Script::Create("table = {}");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto variable = script->GetVariable("table");
REQUIRE(variable != nullptr);
delete script;
}
TEST_CASE( "Create simple integer table", "[integration]" ) {
Script* script = Script::Create("table = {100, 50, 20, 5, -100, 50+50}");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto variable = script->GetVariable("table");
REQUIRE(variable != nullptr);
delete script;
}
TEST_CASE( "Create simple string table", "[integration]" ) {
Script* script = Script::Create("table = {'bla', 'test', 'foo', 'bar'}");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto variable = script->GetVariable("table");
REQUIRE(variable != nullptr);
delete script;
}
TEST_CASE( "Index string table", "[integration]" ) {
Script* script = Script::Create(
R"(
table = {'bla', 'test', 'foo', 'bar'}
result = table[3]
)");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto variable = script->GetVariable("result");
REQUIRE(variable != nullptr);
REQUIRE(*variable->EvaluateString() == "foo");
delete script;
}
#endif