Add the missing very basic evaluatator

This commit is contained in:
Sam Vervaeck 2023-06-06 17:30:24 +02:00
parent abae39c791
commit c0c36ca698
Signed by: samvv
SSH key fingerprint: SHA256:dIg0ywU1OP+ZYifrYxy8c5esO72cIKB+4/9wkZj1VaY
2 changed files with 314 additions and 0 deletions

187
include/bolt/Evaluator.hpp Normal file
View file

@ -0,0 +1,187 @@
#pragma once
#include <unordered_map>
#include <functional>
#include "bolt/ByteString.hpp"
#include "bolt/CST.hpp"
namespace bolt {
enum class ValueKind {
Empty,
String,
Integer,
Tuple,
SourceFunction,
NativeFunction,
};
class Value {
using NativeFunction = std::function<Value(std::vector<Value>)>;
using Tuple = std::vector<Value>;
ValueKind Kind;
union {
ByteString S;
Integer I;
LetDeclaration* D;
NativeFunction F;
Tuple T;
};
public:
Value():
Kind(ValueKind::Empty) {}
Value(ByteString S):
Kind(ValueKind::String), S(S) {}
Value(Integer I):
Kind(ValueKind::Integer), I(I) {}
Value(LetDeclaration* D):
Kind(ValueKind::SourceFunction), D(D) {}
Value(NativeFunction F):
Kind(ValueKind::NativeFunction), F(F) {}
Value(std::vector<Value> T):
Kind(ValueKind::Tuple), T(T) {}
Value(const Value& V):
Kind(V.Kind) {
switch (Kind) {
case ValueKind::String:
new (&S) ByteString(V.S);
break;
case ValueKind::Integer:
new (&I) Integer(V.I);
break;
case ValueKind::Tuple:
new (&I) Tuple(V.T);
break;
case ValueKind::SourceFunction:
new (&D) LetDeclaration*(V.D);
break;
case ValueKind::NativeFunction:
new (&F) NativeFunction(V.F);
break;
case ValueKind::Empty:
break;
}
}
Value& operator=(const Value& Other) noexcept {
Kind = Other.Kind;
switch (Kind) {
case ValueKind::String:
new (&S) ByteString(Other.S);
break;
case ValueKind::Integer:
new (&I) Integer(Other.I);
break;
case ValueKind::Tuple:
new (&I) Tuple(Other.T);
break;
case ValueKind::SourceFunction:
new (&D) LetDeclaration*(Other.D);
break;
case ValueKind::NativeFunction:
new (&F) NativeFunction(Other.F);
break;
case ValueKind::Empty:
break;
}
return *this;
}
// Add move constructor and move assignment methods
inline ValueKind getKind() const noexcept {
return Kind;
}
inline ByteString& asString() {
ZEN_ASSERT(Kind == ValueKind::String);
return S;
}
inline LetDeclaration* getDeclaration() {
ZEN_ASSERT(Kind == ValueKind::SourceFunction);
return D;
}
inline NativeFunction getBinding() {
ZEN_ASSERT(Kind == ValueKind::NativeFunction);
return F;
}
static Value binding(NativeFunction F) {
return Value(F);
}
static Value unit() {
return Value(Tuple {});
}
~Value() {
switch (Kind) {
case ValueKind::String:
S.~ByteString();
break;
case ValueKind::Integer:
I.~Integer();
break;
case ValueKind::Tuple:
T.~Tuple();
break;
case ValueKind::SourceFunction:
break;
case ValueKind::NativeFunction:
F.~NativeFunction();
break;
case ValueKind::Empty:
break;
}
}
};
class Env {
std::unordered_map<ByteString, Value> Bindings;
public:
void add(const ByteString& Name, Value V) {
Bindings.emplace(Name, V);
}
Value& lookup(const ByteString& Name) {
auto Match = Bindings.find(Name);
ZEN_ASSERT(Match != Bindings.end());
return Match->second;
}
};
class Evaluator {
public:
void assignPattern(Pattern* P, Value& V, Env& E);
Value apply(Value Op, std::vector<Value> Args);
Value evaluateExpression(Expression* N, Env& E);
void evaluate(Node* N, Env& E);
};
}

127
src/Evaluator.cc Normal file
View file

@ -0,0 +1,127 @@
#include "zen/range.hpp"
#include "bolt/CST.hpp"
#include "bolt/Evaluator.hpp"
namespace bolt {
Value Evaluator::evaluateExpression(Expression* X, Env& E) {
switch (X->getKind()) {
case NodeKind::ReferenceExpression:
{
auto RE = static_cast<ReferenceExpression*>(X);
return E.lookup(RE->Name->getCanonicalText());
// auto Decl = RE->getScope()->lookup(RE->getSymbolPath());
// ZEN_ASSERT(Decl && Decl->getKind() == NodeKind::FunctionDeclaration);
// return static_cast<FunctionDeclaration*>(Decl);
}
case NodeKind::ConstantExpression:
{
auto CE = static_cast<ConstantExpression*>(X);
switch (CE->Token->getKind()) {
case NodeKind::IntegerLiteral:
return static_cast<IntegerLiteral*>(CE->Token)->V;
case NodeKind::StringLiteral:
return static_cast<StringLiteral*>(CE->Token)->Text;
default:
ZEN_UNREACHABLE
}
}
case NodeKind::CallExpression:
{
auto CE = static_cast<CallExpression*>(X);
auto Op = evaluateExpression(CE->Function, E);
std::vector<Value> Args;
for (auto Arg: CE->Args) {
Args.push_back(evaluateExpression(Arg, E));
}
return apply(Op, Args);
}
default:
ZEN_UNREACHABLE
}
}
void Evaluator::assignPattern(Pattern* P, Value& V, Env& E) {
switch (P->getKind()) {
case NodeKind::BindPattern:
{
auto BP = static_cast<BindPattern*>(P);
E.add(BP->Name->getCanonicalText(), V);
break;
}
default:
ZEN_UNREACHABLE
}
}
Value Evaluator::apply(Value Op, std::vector<Value> Args) {
switch (Op.getKind()) {
case ValueKind::SourceFunction:
{
auto Fn = Op.getDeclaration();
Env NewEnv;
for (auto [Param, Arg]: zen::zip(Fn->Params, Args)) {
assignPattern(Param->Pattern, Arg, NewEnv);
}
switch (Fn->Body->getKind()) {
case NodeKind::LetExprBody:
return evaluateExpression(static_cast<LetExprBody*>(Fn->Body)->Expression, NewEnv);
default:
ZEN_UNREACHABLE
}
}
case ValueKind::NativeFunction:
{
auto Fn = Op.getBinding();
return Fn(Args);
}
default:
ZEN_UNREACHABLE
}
}
void Evaluator::evaluate(Node* N, Env& E) {
switch (N->getKind()) {
case NodeKind::SourceFile:
{
auto SF = static_cast<SourceFile*>(N);
for (auto Element: SF->Elements) {
evaluate(Element, E);
}
break;
}
case NodeKind::ExpressionStatement:
{
auto ES = static_cast<ExpressionStatement*>(N);
evaluateExpression(ES->Expression, E);
break;
}
case NodeKind::LetDeclaration:
{
auto Decl = static_cast<LetDeclaration*>(N);
if (Decl->isFunction()) {
E.add(Decl->getNameAsString(), Decl);
} else {
Value V;
if (Decl->Body) {
switch (Decl->Body->getKind()) {
case NodeKind::LetExprBody:
{
auto Body = static_cast<LetExprBody*>(Decl->Body);
V = evaluateExpression(Body->Expression, E);
}
default:
ZEN_UNREACHABLE
}
}
}
break;
}
default:
ZEN_UNREACHABLE
}
}
}