Initial support for require statements
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
2f912afe92
commit
c39f3a0884
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -26,7 +26,8 @@ namespace Porygon::Diagnostics {
|
|||
UserDataFieldNoSetter,
|
||||
NumericalForArgumentNotANumber,
|
||||
CantIterateExpression,
|
||||
InvalidFunctionParameters
|
||||
InvalidFunctionParameters,
|
||||
ModuleDoesntExist
|
||||
};
|
||||
}
|
||||
#endif //PORYGONLANG_DIAGNOSTICCODE_HPP
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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*)){
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue