Initial support for require statements
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2019-08-10 16:45:15 +02:00
parent 2f912afe92
commit c39f3a0884
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
11 changed files with 202 additions and 15 deletions

View File

@ -4,6 +4,7 @@
#include "../ScriptTypes/TableScriptType.hpp"
#include "BoundExpressions/BoundTableExpression.hpp"
#include "BoundExpressions/BoundFunctionCallExpression.hpp"
#include "BoundExpressions/BoundRequireExpression.hpp"
#include "../UserData/UserDataScriptType.hpp"
using namespace Porygon::Parser;
@ -16,7 +17,7 @@ namespace Porygon::Binder {
binder._scope = scriptScope;
auto statements = s->GetStatements();
vector<const BoundStatement *> boundStatements(statements->size());
for (int i = 0; i < statements->size(); i++) {
for (size_t i = 0; i < statements->size(); i++) {
boundStatements[i] = binder.BindStatement(statements->at(i));
}
return new BoundScriptStatement(boundStatements, scriptScope->GetLocalVariableCount());
@ -63,7 +64,7 @@ namespace Porygon::Binder {
auto statements = ((ParsedBlockStatement *) statement)->GetStatements();
vector<const BoundStatement *> boundStatements(statements->size());
this->_scope->GoInnerScope();
for (int i = 0; i < statements->size(); i++) {
for (size_t i = 0; i < statements->size(); i++) {
boundStatements[i] = this->BindStatement(statements->at(i));
}
this->_scope->GoOuterScope();
@ -137,7 +138,7 @@ namespace Porygon::Binder {
auto parameterKeys = vector<shared_ptr<const BoundVariableKey>>(parameters->size());
this->_scope->GoInnerScope();
for (long i = 0; i < parameters->size(); i++) {
for (size_t i = 0; i < parameters->size(); i++) {
auto var = parameters->at(i);
auto parsedType = ParseTypeIdentifier(var->GetType());
if (parsedType == nullptr) {
@ -567,7 +568,14 @@ namespace Porygon::Binder {
}
BoundExpression *Binder::BindFunctionCall(const FunctionCallExpression *expression) {
auto functionExpression = BindExpression(expression->GetFunction());
auto func = expression->GetFunction();
if (func->GetKind() == ParsedExpressionKind::Variable){
auto variable = dynamic_cast<const VariableExpression*>(func);
if (variable->GetValue().GetHash() == HashedString::ConstHash("require")){
return this->BindRequire(expression);
}
}
auto functionExpression = BindExpression(func);
auto type = functionExpression->GetType();
if (type->GetClass() != TypeClass::Function) {
this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::ExpressionIsNotAFunction,
@ -579,7 +587,7 @@ namespace Porygon::Binder {
auto givenParameters = expression->GetParameters();
auto givenParameterTypes = vector<shared_ptr<const ScriptType>>(givenParameters->size());
vector<BoundExpression *> boundParameters = vector<BoundExpression *>(givenParameters->size());
for (long i = 0; i < givenParameters->size(); i++){
for (size_t i = 0; i < givenParameters->size(); i++){
boundParameters[i] = this -> BindExpression(givenParameters->at(i));
givenParameterTypes[i] = boundParameters[i]->GetType();
}
@ -596,6 +604,44 @@ namespace Porygon::Binder {
expression->GetStartPosition(), expression->GetLength());
}
BoundExpression *Binder::BindRequire(const FunctionCallExpression* exp){
auto parameters = exp->GetParameters();
if (parameters->size() != 1){
this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::InvalidFunctionParameters,
exp->GetStartPosition(),
exp->GetLength());
return new BoundBadExpression(exp->GetStartPosition(), exp ->GetLength());
}
auto parameter = parameters->at(0);
auto boundParameter = this -> BindExpression(parameter);
if (boundParameter->GetKind() != BoundExpressionKind::LiteralString){
this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::InvalidFunctionParameters,
exp->GetStartPosition(),
exp->GetLength());
return new BoundBadExpression(exp->GetStartPosition(), exp ->GetLength());
}
auto key = *dynamic_cast<BoundLiteralStringExpression*>(boundParameter)->GetValue();
auto opt = this->_scriptData->GetScriptOptions();
auto transformedKey = Utilities::StringUtils::FromUTF8(key);
delete boundParameter;
if (!opt->DoesModuleExist(transformedKey)){
this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::ModuleDoesntExist,
exp->GetStartPosition(),
exp->GetLength());
return new BoundBadExpression(exp->GetStartPosition(), exp ->GetLength());
}
auto module = opt->ResolveModule(transformedKey);
if (module -> GetReturnType() == nullptr){
for (const auto& v: *module->GetScriptVariables()){
//TODO: Currently a hack, will always make all variables nil
auto type = make_shared<const ScriptType>(TypeClass::Nil);
this -> _scope -> AssignVariable(v.first, type);
}
}
return new BoundRequireExpression(module, exp->GetStartPosition(), exp ->GetLength());
}
BoundExpression *Binder::BindIndexExpression(const IndexExpression *expression, bool setter) {
auto indexer = this->BindExpression(expression->GetIndexer());
auto index = this->BindExpression(expression->GetIndex());
@ -672,7 +718,7 @@ namespace Porygon::Binder {
if (!boundExpressions.empty()) {
boundExpressions[0] = this->BindExpression(expressions->at(0));
valueType = boundExpressions[0]->GetType();
for (long i = 1; i < expressions->size(); i++) {
for (size_t i = 1; i < expressions->size(); i++) {
boundExpressions[i] = this->BindExpression(expressions->at(i));
if (boundExpressions[i]->GetType().get()->operator!=(valueType.get())) {
this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::InvalidTableValueType,

View File

@ -7,6 +7,7 @@
#include "BoundVariables/BoundScope.hpp"
#include "../Parser/ParsedExpressions/ParsedTableExpression.hpp"
#include "../ScriptTypes/FunctionScriptType.hpp"
#include "BoundExpressions/BoundFunctionCallExpression.hpp"
using namespace std;
using namespace Porygon::Parser;
@ -40,6 +41,7 @@ namespace Porygon::Binder {
BoundExpression *BindBinaryOperator(const BinaryExpression *expression);
BoundExpression *BindUnaryOperator(const UnaryExpression *expression);
BoundExpression *BindFunctionCall(const FunctionCallExpression *expression);
BoundExpression *BindRequire(const FunctionCallExpression *exp);
BoundExpression *BindIndexExpression(const IndexExpression *expression, bool setter);
BoundExpression *BindNumericalTableExpression(const ParsedNumericalTableExpression *expression);
BoundExpression *BindTableExpression(const ParsedTableExpression *expression);

View File

@ -28,6 +28,7 @@ namespace Porygon::Binder {
PeriodIndex,
NumericalTable,
Table,
Require,
};
class BoundExpression {
@ -331,6 +332,7 @@ namespace Porygon::Binder {
return &_expressions;
}
};
}
#endif //PORYGONLANG_BOUNDEXPRESSION_HPP

View File

@ -0,0 +1,32 @@
#ifndef PORYGONLANG_BOUNDREQUIREEXPRESSION_HPP
#define PORYGONLANG_BOUNDREQUIREEXPRESSION_HPP
#include "BoundExpression.hpp"
#include "../../Script.hpp"
namespace Porygon::Binder {
class BoundRequireExpression : public BoundExpression {
Script* _module;
public:
BoundRequireExpression(Script* script, unsigned int start,
unsigned int length)
: BoundExpression(start, length, script->GetReturnType()),
_module(script){}
~BoundRequireExpression() final{
delete _module;
}
[[nodiscard]]
inline Script* GetModule() const{
return _module;
}
[[nodiscard]]
inline BoundExpressionKind GetKind() const final {
return BoundExpressionKind::Require;
}
};
}
#endif //PORYGONLANG_BOUNDREQUIREEXPRESSION_HPP

View File

@ -26,7 +26,8 @@ namespace Porygon::Diagnostics {
UserDataFieldNoSetter,
NumericalForArgumentNotANumber,
CantIterateExpression,
InvalidFunctionParameters
InvalidFunctionParameters,
ModuleDoesntExist
};
}
#endif //PORYGONLANG_DIAGNOSTICCODE_HPP

View File

@ -7,6 +7,7 @@
#include "EvalValues/TableEvalValue.hpp"
#include "../Binder/BoundExpressions/BoundTableExpression.hpp"
#include "../Binder/BoundExpressions/BoundFunctionCallExpression.hpp"
#include "../Binder/BoundExpressions/BoundRequireExpression.hpp"
#include "../ScriptTypes/TableScriptType.hpp"
#include "../UserData/UserDataFunction.hpp"
#include "EvalValues/NumericalTableEvalValue.hpp"
@ -254,6 +255,8 @@ namespace Porygon::Evaluation {
return this->EvaluateNumericTableExpression(expression);
case BoundExpressionKind::Table:
return this->EvaluateComplexTableExpression(expression);
case BoundExpressionKind::Require:
return this -> EvaluateRequireExpression(expression);
}
}
@ -396,4 +399,16 @@ namespace Porygon::Evaluation {
this->_evaluationScope = currentEvaluator;
return new TableEvalValue(variables);
}
EvalValuePointer Evaluator::EvaluateRequireExpression(const BoundExpression* expression) {
auto module = dynamic_cast<const BoundRequireExpression*>(expression)->GetModule();
if (module ->GetReturnType() == nullptr){
for (const auto& v: *module->GetScriptVariables()){
this->_scriptVariables->insert({v.first, v.second.Clone()});
}
return nullptr;
} else{
return module -> Evaluate().Take();
}
}
}

View File

@ -47,6 +47,8 @@ namespace Porygon::Evaluation{
EvalValuePointer EvaluateNumericTableExpression(const BoundExpression *expression);
EvalValuePointer EvaluateComplexTableExpression(const BoundExpression *expression);
EvalValuePointer EvaluateRequireExpression(const BoundExpression* expression);
EvalValuePointer GetVariable(const BoundVariableExpression *expression);
public:
explicit Evaluator(map<Utilities::HashedString, EvalValuePointer>* scriptVariables, const ScriptOptions* options)

View File

@ -20,7 +20,7 @@ namespace Porygon{
Evaluator* _evaluator;
map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
shared_ptr<Binder::BoundScriptStatement> _boundScript;
shared_ptr<const ScriptType> _returnType;
shared_ptr<const ScriptType> _returnType = nullptr;
ScriptOptions* _scriptOptions;
explicit Script(const u16string&);
@ -57,6 +57,10 @@ namespace Porygon{
const EvalValue* CallFunction(const u16string& key, const vector<EvalValue*>& variables);
bool HasFunction(const u16string& key);
const map<Utilities::HashedString, EvalValuePointer>* GetScriptVariables(){
return _scriptVariables;
}
};
}

View File

@ -1,18 +1,30 @@
#include <iostream>
#include <filesystem>
#include <fstream>
#include "ScriptOptions.hpp"
#include "Utilities/StringUtils.hpp"
#include "Script.hpp"
Porygon::ScriptOptions Porygon::ScriptOptions::DefaultScriptOptions;
std::streambuf* Porygon::ScriptOptions::_printBuffer = std::cout.rdbuf();
std::ostream* Porygon::ScriptOptions::_printStream = new std::ostream(Porygon::ScriptOptions::_printBuffer);
static void DefaultPrint(const char16_t* s){
void Porygon::ScriptOptions::DefaultPrint(const char16_t *s) {
Porygon::ScriptOptions::GetDefaultScriptOptions()->GetPrintStream() << Porygon::Utilities::StringUtils::FromUTF8(s) << std::endl;
}
void (*Porygon::ScriptOptions::_print)(const char16_t*) = DefaultPrint;
bool Porygon::ScriptOptions::DefaultModuleExists(const std::string& moduleName) {
return std::filesystem::exists(moduleName);
}
Porygon::Script *Porygon::ScriptOptions::DefaultResolveModule(const std::string& moduleName) {
auto stream = std::ifstream(moduleName);
std::basic_stringstream<char16_t> stringStream;
stringStream << stream.rdbuf();
auto str = std::u16string(stringStream.str());
return Porygon::Script::Create(str);
}
extern "C"{
void SetDefaultPrintFunc(void (*func)(const char16_t*)){

View File

@ -2,28 +2,54 @@
#define PORYGONLANG_SCRIPTOPTIONS_HPP
#include <string>
#include <utility>
namespace Porygon{
class Script;
class ScriptOptions{
static Porygon::ScriptOptions DefaultScriptOptions;
static void (*_print)(const char16_t* s);
static void DefaultPrint(const char16_t* s);
static bool DefaultModuleExists(const std::string& moduleName);
static Script* DefaultResolveModule(const std::string& moduleName);
void (*_print)(const char16_t* s) = DefaultPrint;
bool (*_doesModuleExist)(const std::string& moduleName) = DefaultModuleExists;
Script* (*_resolveModule)(const std::string& moduleName) = DefaultResolveModule;
static std::streambuf* _printBuffer;
static std::ostream* _printStream;
public:
static Porygon::ScriptOptions* GetDefaultScriptOptions(){
return &DefaultScriptOptions;
}
inline void Print(const char16_t* s) const{
ScriptOptions::_print(s);
this -> _print(s);
}
inline bool DoesModuleExist(std::string moduleName) const{
return _doesModuleExist(std::move(moduleName));
}
inline Script* ResolveModule(std::string moduleName) const{
return _resolveModule(std::move(moduleName));
}
void SetPrintFunc(void (*print)(const char16_t *)){
ScriptOptions::_print = print;
this -> _print = print;
}
void SetModuleExistsFunc(bool (*doesModuleExist)(const std::string& moduleName)){
this ->_doesModuleExist = doesModuleExist;
}
void SetResolveModuleFunc(Script* (*resolveModule)(const std::string& moduleName)){
this ->_resolveModule = resolveModule;
}
std::ostream& GetPrintStream(){
return *ScriptOptions::_printStream;
return *Porygon::ScriptOptions::_printStream;
}
void SetPrintStream(std::ostream* stream){

View File

@ -0,0 +1,45 @@
#ifdef TESTS_BUILD
#include <catch.hpp>
#include "../src/Script.hpp"
using namespace Porygon;
class ModuleHandler{
static unordered_map<string, Script*> MODULES;
~ModuleHandler(){
for (auto v: MODULES){
delete v.second;
}
}
inline static bool DoesModuleExist(const string& moduleName){
return MODULES.find(moduleName) != MODULES.end();
}
inline static Script* ResolveModule(const string& moduleName){
return MODULES[moduleName];
}
public:
static void Initialize(){
ScriptOptions::GetDefaultScriptOptions()->SetModuleExistsFunc(DoesModuleExist);
ScriptOptions::GetDefaultScriptOptions()->SetResolveModuleFunc(ResolveModule);
}
};
unordered_map<string, Script*> ModuleHandler::MODULES = unordered_map<string, Script*>{
{"simple_return", Script::Create(u"return 500")}
};
TEST_CASE( "Require simple return script", "[integration]" ) {
ModuleHandler::Initialize();
auto script = Script::Create(uR"(
return require("simple_return")
)");
REQUIRE(!script->Diagnostics -> HasErrors());
auto var = script->Evaluate();
REQUIRE(var->EvaluateInteger() == 500);
delete script;
}
#endif