149 lines
4.4 KiB
C++
149 lines
4.4 KiB
C++
#include "Environment.h"
|
|
|
|
#include <stdexcept>
|
|
|
|
namespace NVL::Environment {
|
|
Environment ENVIRONMENT;
|
|
|
|
Variable::Variable() : type(Type::Nil), length(0) { }
|
|
Variable::Variable(Type t) : type(t), length(0) {
|
|
if (t != Type::Nil)
|
|
throw std::runtime_error("Cannot create non-nil object with type alone");
|
|
}
|
|
Variable::Variable(const Number& v, bool is_ref) : type(is_ref ? Type::StaticReference : Type::Number), value(v), length(1) {}
|
|
Variable::Variable(const String& v) : type(Type::String), value(v), length(1) {}
|
|
Variable::Variable(const Vector<Variable>& v) : type(Type::Array), value(v), length(v.size()) {}
|
|
Variable::Variable(const std::function < Variable(Vector<Variable>)>& v, u32 l) : type(Type::Procedure), value(v), length(l) {}
|
|
|
|
bool Variable::operator==(const Variable& other) const {
|
|
if (other.type != type)
|
|
return false;
|
|
switch (type) {
|
|
case Type::Number:
|
|
case Type::StaticReference: // Only used with serialization
|
|
return std::get<Number>(other.value) == std::get<Number>(value);
|
|
case Type::String:
|
|
return std::get<String>(other.value) == std::get<String>(value);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Environment::enter(const String& name, Variable p) {
|
|
if (!env.emplace(name, p).second)
|
|
throw std::runtime_error("Redefinition of symbol in environment");
|
|
}
|
|
|
|
void Environment::enter(std::initializer_list<std::pair<String, Variable>> p) {
|
|
for (auto& i : p)
|
|
enter(i.first, i.second);
|
|
}
|
|
|
|
void Environment::set(const String& name, Variable p) {
|
|
if (env.find(name) == env.end())
|
|
throw std::runtime_error("Attempting to set undefined variable");
|
|
env[name] = p;
|
|
}
|
|
|
|
const Variable& Environment::get(const String& name) const {
|
|
return env.at(name);
|
|
}
|
|
|
|
bool Environment::exists(const String& name) {
|
|
return env.find(name) != env.end();
|
|
}
|
|
|
|
Variable Eval(const Parse::Object& obj) {
|
|
switch (obj.type) {
|
|
case Parse::Type::Symbol:
|
|
return ENVIRONMENT.get(std::get<String>(obj.value));
|
|
case Parse::Type::Number:
|
|
return Variable(std::get<Number>(obj.value));
|
|
case Parse::Type::String:
|
|
return Variable(std::get<String>(obj.value));
|
|
case Parse::Type::Array:
|
|
{
|
|
Vector<Variable> v{};
|
|
for (const auto& x : std::get<Vector<Parse::Object>>(obj.value))
|
|
v.push_back(Eval(x));
|
|
return Variable(v);
|
|
}
|
|
case Parse::Type::Subexpression:
|
|
{
|
|
return Apply(std::get<Vector<Parse::Object>>(obj.value));
|
|
}
|
|
}
|
|
}
|
|
|
|
Variable Apply(const Parse::Command& c) {
|
|
Vector<Variable> args{};
|
|
|
|
Variable f = Eval(c[0]);
|
|
for (u32 i = 1; i < c.size(); i++) {
|
|
args.push_back(Eval(c[i]));
|
|
}
|
|
if (f.length != -1 && f.length != c.size() - 1) throw std::runtime_error("Function arity mismatch");
|
|
return std::get<std::function<Variable(Vector<Variable>)>>(f.value)(args);
|
|
}
|
|
|
|
void EvalScene(const Parse::Scene& s) {
|
|
for (const auto& c : s.get())
|
|
Apply(c);
|
|
}
|
|
|
|
// Check MatchMarkup for more information
|
|
MarkupString UnpackMarkupVariable(const Vector<Variable>& v) {
|
|
MarkupString ms{};
|
|
for (const Variable& segment : v) {
|
|
const auto& pair = std::get<Vector<Variable>>(segment.value);
|
|
const String s = std::get<String>(pair[0].value);
|
|
|
|
Vector<std::pair<String, Vector<String>>> efs;
|
|
for (const Variable& combination : std::get<Vector<Variable>>(pair[1].value)) {
|
|
if (combination.type == Type::String) {
|
|
const String ef = std::get<String>(combination.value);
|
|
efs.push_back({ ef, {} });
|
|
}
|
|
else if (combination.type == Type::Array) {
|
|
const Vector<Variable>& ef_t = std::get<Vector<Variable>>(combination.value);
|
|
const String& ef = std::get<String>(ef_t[0].value);
|
|
const Vector<Variable>& args = std::get<Vector<Variable>>(ef_t[1].value);
|
|
Vector<String> ss{};
|
|
for (const auto& s : args) { ss.push_back(std::get<String>(s.value)); }
|
|
efs.push_back({ ef, ss });
|
|
}
|
|
}
|
|
ms.segments.push_back({ s, efs });
|
|
}
|
|
return ms;
|
|
}
|
|
|
|
size_t MarkupString::length() const { // you do realize this memoization fails if we change the var
|
|
if (mem_length == -1) {
|
|
size_t r = 0;
|
|
for (const auto& s : segments)
|
|
r += s.str.length();
|
|
return r;
|
|
}
|
|
else
|
|
return mem_length;
|
|
}
|
|
|
|
MarkupString MarkupString::substr(size_t i) const {
|
|
size_t r = 0;
|
|
MarkupString res;
|
|
for (const auto& s : segments) {
|
|
size_t last_r = r;
|
|
r += s.str.length();
|
|
if (r <= i)
|
|
res.segments.push_back(s);
|
|
else {
|
|
auto copy = s;
|
|
copy.str = copy.str.substr(0, i - last_r);
|
|
res.segments.push_back(copy);
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
}
|