Add the missing very basic evaluatator
This commit is contained in:
parent
abae39c791
commit
c0c36ca698
2 changed files with 314 additions and 0 deletions
187
include/bolt/Evaluator.hpp
Normal file
187
include/bolt/Evaluator.hpp
Normal 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
127
src/Evaluator.cc
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue