Initial work on standard library
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
ecfc1ae3b7
commit
24c560b52d
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "BoundScope.hpp"
|
#include "BoundScope.hpp"
|
||||||
|
#include "../../StandardLibraries/StaticScope.hpp"
|
||||||
|
|
||||||
namespace Porygon::Binder {
|
namespace Porygon::Binder {
|
||||||
BoundScope::BoundScope(map<Utilities::HashedString, BoundVariable *> *tableScope) {
|
BoundScope::BoundScope(map<Utilities::HashedString, BoundVariable *> *tableScope) {
|
||||||
|
@ -40,16 +41,21 @@ namespace Porygon::Binder {
|
||||||
}
|
}
|
||||||
|
|
||||||
int BoundScope::Exists(const Utilities::HashedString& key) {
|
int BoundScope::Exists(const Utilities::HashedString& key) {
|
||||||
|
for (int i = _currentScope - 1; i >= 0; i--) {
|
||||||
|
auto scope = _localScope.at(i);
|
||||||
|
auto found = scope->find(key);
|
||||||
|
if (found != scope->end()) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto found = this->_tableScope->find(key);
|
auto found = this->_tableScope->find(key);
|
||||||
if (found != _tableScope->end()) {
|
if (found != _tableScope->end()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for (int i = _currentScope - 1; i >= 0; i--) {
|
auto result = StandardLibraries::StaticScope::GetBoundVariable(key);
|
||||||
auto scope = _localScope.at(i);
|
if (result != nullptr){
|
||||||
found = scope->find(key);
|
return -2;
|
||||||
if (found != scope->end()) {
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +67,8 @@ namespace Porygon::Binder {
|
||||||
return find->second;
|
return find->second;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
} else if (scope == -2){
|
||||||
|
return StandardLibraries::StaticScope::GetBoundVariable(identifier);
|
||||||
} else {
|
} else {
|
||||||
auto s = this->_localScope.at(scope - 1);
|
auto s = this->_localScope.at(scope - 1);
|
||||||
auto find = s->find(identifier);
|
auto find = s->find(identifier);
|
||||||
|
@ -83,7 +91,7 @@ namespace Porygon::Binder {
|
||||||
|
|
||||||
VariableAssignment BoundScope::AssignVariable(const Utilities::HashedString& identifier, const std::shared_ptr<ScriptType> &type) {
|
VariableAssignment BoundScope::AssignVariable(const Utilities::HashedString& identifier, const std::shared_ptr<ScriptType> &type) {
|
||||||
int exists = this->Exists(identifier);
|
int exists = this->Exists(identifier);
|
||||||
if (exists == -1) {
|
if (exists < 0) {
|
||||||
// Creation
|
// Creation
|
||||||
_tableScope->insert({identifier, new BoundVariable(type)});
|
_tableScope->insert({identifier, new BoundVariable(type)});
|
||||||
return VariableAssignment(VariableAssignmentResult::Ok, new BoundVariableKey(identifier, 0, true));
|
return VariableAssignment(VariableAssignmentResult::Ok, new BoundVariableKey(identifier, 0, true));
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace Porygon::Evaluation{
|
||||||
return new BooleanEvalValue(b);
|
return new BooleanEvalValue(b);
|
||||||
}
|
}
|
||||||
static EvalValue* Create(const string& s){
|
static EvalValue* Create(const string& s){
|
||||||
return new StringEvalValue(Utilities::StringUtils::StringToU16String(s));
|
return new StringEvalValue(Utilities::StringUtils::ToUTF8(s));
|
||||||
}
|
}
|
||||||
static EvalValue* Create(u16string s){
|
static EvalValue* Create(u16string s){
|
||||||
return new StringEvalValue(std::move(s));
|
return new StringEvalValue(std::move(s));
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef PORYGONLANG_NILEVALVALUE_HPP
|
||||||
|
#define PORYGONLANG_NILEVALVALUE_HPP
|
||||||
|
|
||||||
|
#include "EvalValue.hpp"
|
||||||
|
|
||||||
|
namespace Porygon::Evaluation{
|
||||||
|
class NilEvalValue : public EvalValue{
|
||||||
|
const TypeClass GetTypeClass() const final{
|
||||||
|
return TypeClass ::Nil;
|
||||||
|
}
|
||||||
|
const bool operator==(EvalValue *b) const final{
|
||||||
|
return b->GetTypeClass() == TypeClass ::Nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shared_ptr<EvalValue> Clone() const final{
|
||||||
|
return make_shared<NilEvalValue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t GetHashCode() const final{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //PORYGONLANG_NILEVALVALUE_HPP
|
|
@ -11,14 +11,14 @@ namespace Porygon::Evaluation {
|
||||||
class EvaluationException : public std::exception {
|
class EvaluationException : public std::exception {
|
||||||
string _message;
|
string _message;
|
||||||
public:
|
public:
|
||||||
explicit EvaluationException(string message) {
|
explicit EvaluationException(const string& message) {
|
||||||
_message = std::move(message);
|
_message = defaultErrorText +message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string defaultErrorText = "An evaluation exception occurred: ";
|
const string defaultErrorText = "An evaluation exception occurred: ";
|
||||||
|
|
||||||
const char *what() const noexcept final {
|
const char *what() const noexcept final {
|
||||||
return (defaultErrorText + _message).c_str();
|
return _message.c_str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
#include "EvaluationScope.hpp"
|
#include "EvaluationScope.hpp"
|
||||||
|
#include "../../StandardLibraries/StaticScope.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace Porygon::Evaluation {
|
namespace Porygon::Evaluation {
|
||||||
|
@ -29,8 +30,11 @@ namespace Porygon::Evaluation {
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<EvalValue> EvaluationScope::GetVariable(const BoundVariableKey *key) {
|
shared_ptr<EvalValue> EvaluationScope::GetVariable(const BoundVariableKey *key) {
|
||||||
if (key->GetScopeId() == 0) {
|
auto scopeId = key -> GetScopeId();
|
||||||
|
if (scopeId== 0) {
|
||||||
return _scriptScope->at(key->GetIdentifier());
|
return _scriptScope->at(key->GetIdentifier());
|
||||||
|
} else if(scopeId == -2){
|
||||||
|
return StandardLibraries::StaticScope::GetVariable(key->GetIdentifier());
|
||||||
} else {
|
} else {
|
||||||
return _localScope[key->GetHash()];
|
return _localScope[key->GetHash()];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef PORYGONLANG_BASICLIBRARY_HPP
|
||||||
|
#define PORYGONLANG_BASICLIBRARY_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include "../Evaluator/EvaluationException.hpp"
|
||||||
|
#include "../Evaluator/EvalValues/EvalValue.hpp"
|
||||||
|
#include "../Evaluator/EvalValues/NilEvalValue.hpp"
|
||||||
|
#include "../Utilities/StringUtils.hpp"
|
||||||
|
#include "../Binder/BoundVariables/BoundVariable.hpp"
|
||||||
|
#include "../UserData/UserDataFunction.hpp"
|
||||||
|
#include "../UserData/UserDataFunctionType.hpp"
|
||||||
|
|
||||||
|
namespace Porygon::StandardLibraries{
|
||||||
|
class BasicLibrary{
|
||||||
|
static Evaluation::EvalValue* _error(void*, Evaluation::EvalValue* parameters[], int parameterCount){
|
||||||
|
auto message = parameters[0]->EvaluateString();
|
||||||
|
auto conv = Utilities::StringUtils::FromUTF8(message);
|
||||||
|
throw Evaluation::EvaluationException(conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Evaluation::EvalValue* _assert(void*, Evaluation::EvalValue* parameters[], int parameterCount){
|
||||||
|
auto assertion = parameters[0]->EvaluateBool();
|
||||||
|
if (!assertion){
|
||||||
|
throw Evaluation::EvaluationException("assertion failed!");
|
||||||
|
}
|
||||||
|
return new Evaluation::BooleanEvalValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void RegisterVariables(std::map<Utilities::HashedString, Binder::BoundVariable *>* bound,
|
||||||
|
std::map<Utilities::HashedString, shared_ptr<Evaluation::EvalValue>>* values){
|
||||||
|
// Register error function
|
||||||
|
auto errorFuncType = make_shared<UserData::UserDataFunctionType>(
|
||||||
|
make_shared<ScriptType>(TypeClass::Nil),
|
||||||
|
vector<shared_ptr<ScriptType>>{make_shared<StringScriptType>(false, 0)});
|
||||||
|
auto errorFunc = make_shared<UserData::UserDataFunction>(_error, nullptr);
|
||||||
|
auto errorLookup = Utilities::HashedString::CreateLookup(u"error");
|
||||||
|
bound->insert({errorLookup, new Binder::BoundVariable(errorFuncType)});
|
||||||
|
values->insert({errorLookup, errorFunc});
|
||||||
|
|
||||||
|
// Register assert function
|
||||||
|
auto assertFuncType = make_shared<UserData::UserDataFunctionType>(
|
||||||
|
make_shared<ScriptType>(TypeClass::Bool),
|
||||||
|
vector<shared_ptr<ScriptType>>{make_shared<ScriptType>(TypeClass::Bool)});
|
||||||
|
auto assertFunc = make_shared<UserData::UserDataFunction>(_assert, nullptr);
|
||||||
|
auto assertLookup = Utilities::HashedString::CreateLookup(u"assert");
|
||||||
|
bound->insert({assertLookup, new Binder::BoundVariable(assertFuncType)});
|
||||||
|
values->insert({assertLookup, assertFunc});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //PORYGONLANG_BASICLIBRARY_HPP
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "StaticScope.hpp"
|
||||||
|
|
||||||
|
Porygon::StandardLibraries::StaticScope::InternalScope Porygon::StandardLibraries::StaticScope::_internal;
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifndef PORYGONLANG_STATICSCOPE_HPP
|
||||||
|
#define PORYGONLANG_STATICSCOPE_HPP
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "../Utilities/HashedString.hpp"
|
||||||
|
#include "../Binder/BoundVariables/BoundVariable.hpp"
|
||||||
|
#include "../Evaluator/EvalValues/EvalValue.hpp"
|
||||||
|
#include "BasicLibrary.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
namespace Porygon::StandardLibraries{
|
||||||
|
/*!
|
||||||
|
\class StaticScope
|
||||||
|
\brief The static shared scope for all scripts. Variables registered in here should be stateless.
|
||||||
|
*/
|
||||||
|
class StaticScope {
|
||||||
|
class InternalScope{
|
||||||
|
public:
|
||||||
|
map<Utilities::HashedString, Binder::BoundVariable *> _boundVariables;
|
||||||
|
map<Utilities::HashedString, shared_ptr<Evaluation::EvalValue>> _variables;
|
||||||
|
|
||||||
|
InternalScope(){
|
||||||
|
BasicLibrary::RegisterVariables(&_boundVariables, &_variables);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static InternalScope _internal;
|
||||||
|
public:
|
||||||
|
static void RegisterVariable(const Utilities::HashedString& identifier, shared_ptr<ScriptType> type, Evaluation::EvalValue* value){
|
||||||
|
_internal._boundVariables.insert({identifier, new Binder::BoundVariable(std::move(type))});
|
||||||
|
_internal._variables.insert({identifier, shared_ptr<Evaluation::EvalValue>(value)});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Binder::BoundVariable* GetBoundVariable(const Utilities::HashedString &identifier){
|
||||||
|
auto found = _internal._boundVariables.find(identifier);
|
||||||
|
if (found != _internal._boundVariables.end()) {
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static shared_ptr<Evaluation::EvalValue> GetVariable(const Utilities::HashedString &identifier){
|
||||||
|
return _internal._variables[identifier];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //PORYGONLANG_STATICSCOPE_HPP
|
|
@ -23,11 +23,7 @@ namespace Porygon::UserData{
|
||||||
}
|
}
|
||||||
|
|
||||||
EvalValue* Call(EvalValue* parameters[], int parameterCount){
|
EvalValue* Call(EvalValue* parameters[], int parameterCount){
|
||||||
try{
|
return _call(_obj, parameters, parameterCount);
|
||||||
return _call(_obj, parameters, parameterCount);
|
|
||||||
} catch (...){
|
|
||||||
throw Evaluation::EvaluationException("An error occurred while executing a userdata function.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const shared_ptr<EvalValue> Clone() const final {
|
const shared_ptr<EvalValue> Clone() const final {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "StringUtils.hpp"
|
#include "StringUtils.hpp"
|
||||||
|
|
||||||
namespace Porygon::Utilities{
|
namespace Porygon::Utilities{
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, std::little_endian>, char16_t> StringUtils::conv;
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, std::little_endian>, char16_t> StringUtils::to_16;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,17 @@
|
||||||
|
|
||||||
namespace Porygon::Utilities{
|
namespace Porygon::Utilities{
|
||||||
class StringUtils{
|
class StringUtils{
|
||||||
static std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, std::little_endian>, char16_t> conv;
|
private:
|
||||||
|
static std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, std::little_endian>, char16_t> to_16;
|
||||||
public:
|
public:
|
||||||
static std::u16string IntToString(long const &i) {
|
static std::u16string IntToString(long const &i) {
|
||||||
return conv.from_bytes(std::to_string(i));
|
return to_16.from_bytes(std::to_string(i));
|
||||||
}
|
}
|
||||||
static std::u16string StringToU16String(const std::string& s) {
|
static std::u16string ToUTF8(const std::string &s) {
|
||||||
return conv.from_bytes(s);
|
return to_16.from_bytes(s);
|
||||||
|
}
|
||||||
|
static std::string FromUTF8(const std::u16string &s) {
|
||||||
|
return to_16.to_bytes(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifdef TESTS_BUILD
|
||||||
|
#include <catch.hpp>
|
||||||
|
#include "../src/Script.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace Porygon;
|
||||||
|
|
||||||
|
TEST_CASE( "Error func throws error", "[integration]" ) {
|
||||||
|
Script* script = Script::Create(u"error('foo bar')");
|
||||||
|
REQUIRE(!script->Diagnostics -> HasErrors());
|
||||||
|
try{
|
||||||
|
script -> Evaluate();
|
||||||
|
throw;
|
||||||
|
} catch (const EvaluationException& e){
|
||||||
|
auto err = e.what();
|
||||||
|
REQUIRE(std::strcmp(err, "An evaluation exception occurred: foo bar") == 0);
|
||||||
|
}
|
||||||
|
delete script;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "Assert func throws error if argument is false", "[integration]" ) {
|
||||||
|
Script* script = Script::Create(u"assert(false)");
|
||||||
|
REQUIRE(!script->Diagnostics -> HasErrors());
|
||||||
|
try{
|
||||||
|
script -> Evaluate();
|
||||||
|
throw;
|
||||||
|
} catch (const EvaluationException& e){
|
||||||
|
auto err = e.what();
|
||||||
|
REQUIRE(std::strcmp(err, "An evaluation exception occurred: assertion failed!") == 0);
|
||||||
|
}
|
||||||
|
delete script;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "Assert func does not throw if argument is true", "[integration]" ) {
|
||||||
|
Script* script = Script::Create(u"assert(true)");
|
||||||
|
REQUIRE(!script->Diagnostics -> HasErrors());
|
||||||
|
script -> Evaluate();
|
||||||
|
delete script;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue