Compare commits
No commits in common. "linux-cross-platform-fix" and "master" have entirely different histories.
linux-cros
...
master
@ -12,46 +12,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <optional>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
class Interpreter;
|
|
||||||
|
|
||||||
// RAII helper for thunk execution flag
|
|
||||||
struct ScopedThunkFlag {
|
|
||||||
bool& flag;
|
|
||||||
bool prev;
|
|
||||||
ScopedThunkFlag(bool& f) : flag(f), prev(f) { flag = true; }
|
|
||||||
~ScopedThunkFlag() { flag = prev; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// RAII helper for environment management
|
|
||||||
struct ScopedEnv {
|
|
||||||
std::shared_ptr<Environment>& target;
|
|
||||||
std::shared_ptr<Environment> prev;
|
|
||||||
ScopedEnv(std::shared_ptr<Environment>& e) : target(e), prev(e) {}
|
|
||||||
~ScopedEnv() { target = prev; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Thunk class for trampoline-based tail call optimization
|
|
||||||
class Thunk {
|
|
||||||
public:
|
|
||||||
using ThunkFunction = std::function<Value()>;
|
|
||||||
|
|
||||||
explicit Thunk(ThunkFunction func) : func(std::move(func)) {}
|
|
||||||
|
|
||||||
Value execute() const {
|
|
||||||
return func();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isThunk() const { return true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ThunkFunction func;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Interpreter : public ExprVisitor, public StmtVisitor {
|
class Interpreter : public ExprVisitor, public StmtVisitor {
|
||||||
|
|
||||||
@ -86,21 +46,14 @@ private:
|
|||||||
std::vector<std::shared_ptr<BuiltinFunction> > builtinFunctions;
|
std::vector<std::shared_ptr<BuiltinFunction> > builtinFunctions;
|
||||||
std::vector<std::shared_ptr<Function> > functions;
|
std::vector<std::shared_ptr<Function> > functions;
|
||||||
ErrorReporter* errorReporter;
|
ErrorReporter* errorReporter;
|
||||||
bool inThunkExecution = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Value evaluate(const std::shared_ptr<Expr>& expr);
|
Value evaluate(const std::shared_ptr<Expr>& expr);
|
||||||
Value evaluateWithoutTrampoline(const std::shared_ptr<Expr>& expr);
|
|
||||||
bool isEqual(Value a, Value b);
|
bool isEqual(Value a, Value b);
|
||||||
bool isWholeNumer(double num);
|
bool isWholeNumer(double num);
|
||||||
void execute(const std::shared_ptr<Stmt>& statement, ExecutionContext* context = nullptr);
|
void execute(const std::shared_ptr<Stmt>& statement, ExecutionContext* context = nullptr);
|
||||||
void executeBlock(std::vector<std::shared_ptr<Stmt> > statements, std::shared_ptr<Environment> env, ExecutionContext* context = nullptr);
|
void executeBlock(std::vector<std::shared_ptr<Stmt> > statements, std::shared_ptr<Environment> env, ExecutionContext* context = nullptr);
|
||||||
void addStdLibFunctions();
|
void addStdLibFunctions();
|
||||||
|
|
||||||
// Trampoline execution
|
|
||||||
Value runTrampoline(Value initialResult);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isTruthy(Value object);
|
bool isTruthy(Value object);
|
||||||
std::string stringify(Value object);
|
std::string stringify(Value object);
|
||||||
|
|||||||
@ -11,17 +11,15 @@
|
|||||||
class Environment;
|
class Environment;
|
||||||
class Function;
|
class Function;
|
||||||
class BuiltinFunction;
|
class BuiltinFunction;
|
||||||
class Thunk;
|
|
||||||
|
|
||||||
// Type tags for the Value union
|
// Type tags for the Value union
|
||||||
enum ValueType {
|
enum ValueType : uint8_t {
|
||||||
VAL_NONE,
|
VAL_NONE,
|
||||||
VAL_NUMBER,
|
VAL_NUMBER,
|
||||||
VAL_BOOLEAN,
|
VAL_BOOLEAN,
|
||||||
VAL_STRING,
|
VAL_STRING,
|
||||||
VAL_FUNCTION,
|
VAL_FUNCTION,
|
||||||
VAL_BUILTIN_FUNCTION,
|
VAL_BUILTIN_FUNCTION
|
||||||
VAL_THUNK
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tagged value system (like Lua) - no heap allocation for simple values
|
// Tagged value system (like Lua) - no heap allocation for simple values
|
||||||
@ -31,7 +29,6 @@ struct Value {
|
|||||||
bool boolean;
|
bool boolean;
|
||||||
Function* function;
|
Function* function;
|
||||||
BuiltinFunction* builtin_function;
|
BuiltinFunction* builtin_function;
|
||||||
Thunk* thunk;
|
|
||||||
};
|
};
|
||||||
ValueType type;
|
ValueType type;
|
||||||
std::string string_value; // Store strings outside the union for safety
|
std::string string_value; // Store strings outside the union for safety
|
||||||
@ -45,7 +42,6 @@ struct Value {
|
|||||||
Value(std::string&& s) : type(ValueType::VAL_STRING), string_value(std::move(s)) {}
|
Value(std::string&& s) : type(ValueType::VAL_STRING), string_value(std::move(s)) {}
|
||||||
Value(Function* f) : function(f), type(ValueType::VAL_FUNCTION) {}
|
Value(Function* f) : function(f), type(ValueType::VAL_FUNCTION) {}
|
||||||
Value(BuiltinFunction* bf) : builtin_function(bf), type(ValueType::VAL_BUILTIN_FUNCTION) {}
|
Value(BuiltinFunction* bf) : builtin_function(bf), type(ValueType::VAL_BUILTIN_FUNCTION) {}
|
||||||
Value(Thunk* t) : thunk(t), type(ValueType::VAL_THUNK) {}
|
|
||||||
|
|
||||||
// Move constructor
|
// Move constructor
|
||||||
Value(Value&& other) noexcept
|
Value(Value&& other) noexcept
|
||||||
@ -98,7 +94,6 @@ struct Value {
|
|||||||
inline bool isString() const { return type == ValueType::VAL_STRING; }
|
inline bool isString() const { return type == ValueType::VAL_STRING; }
|
||||||
inline bool isFunction() const { return type == ValueType::VAL_FUNCTION; }
|
inline bool isFunction() const { return type == ValueType::VAL_FUNCTION; }
|
||||||
inline bool isBuiltinFunction() const { return type == ValueType::VAL_BUILTIN_FUNCTION; }
|
inline bool isBuiltinFunction() const { return type == ValueType::VAL_BUILTIN_FUNCTION; }
|
||||||
inline bool isThunk() const { return type == ValueType::VAL_THUNK; }
|
|
||||||
inline bool isNone() const { return type == ValueType::VAL_NONE; }
|
inline bool isNone() const { return type == ValueType::VAL_NONE; }
|
||||||
|
|
||||||
// Value extraction (safe, with type checking) - inline for performance
|
// Value extraction (safe, with type checking) - inline for performance
|
||||||
@ -107,7 +102,6 @@ struct Value {
|
|||||||
inline const std::string& asString() const { return string_value; }
|
inline const std::string& asString() const { return string_value; }
|
||||||
inline Function* asFunction() const { return isFunction() ? function : nullptr; }
|
inline Function* asFunction() const { return isFunction() ? function : nullptr; }
|
||||||
inline BuiltinFunction* asBuiltinFunction() const { return isBuiltinFunction() ? builtin_function : nullptr; }
|
inline BuiltinFunction* asBuiltinFunction() const { return isBuiltinFunction() ? builtin_function : nullptr; }
|
||||||
inline Thunk* asThunk() const { return isThunk() ? thunk : nullptr; }
|
|
||||||
|
|
||||||
// Truthiness check - inline for performance
|
// Truthiness check - inline for performance
|
||||||
inline bool isTruthy() const {
|
inline bool isTruthy() const {
|
||||||
@ -118,7 +112,6 @@ struct Value {
|
|||||||
case ValueType::VAL_STRING: return !string_value.empty();
|
case ValueType::VAL_STRING: return !string_value.empty();
|
||||||
case ValueType::VAL_FUNCTION: return function != nullptr;
|
case ValueType::VAL_FUNCTION: return function != nullptr;
|
||||||
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function != nullptr;
|
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function != nullptr;
|
||||||
case ValueType::VAL_THUNK: return thunk != nullptr;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +127,6 @@ struct Value {
|
|||||||
case ValueType::VAL_STRING: return string_value == other.string_value;
|
case ValueType::VAL_STRING: return string_value == other.string_value;
|
||||||
case ValueType::VAL_FUNCTION: return function == other.function;
|
case ValueType::VAL_FUNCTION: return function == other.function;
|
||||||
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function == other.builtin_function;
|
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function == other.builtin_function;
|
||||||
case ValueType::VAL_THUNK: return thunk == other.thunk;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +151,6 @@ struct Value {
|
|||||||
case ValueType::VAL_STRING: return string_value;
|
case ValueType::VAL_STRING: return string_value;
|
||||||
case ValueType::VAL_FUNCTION: return "<function>";
|
case ValueType::VAL_FUNCTION: return "<function>";
|
||||||
case ValueType::VAL_BUILTIN_FUNCTION: return "<builtin_function>";
|
case ValueType::VAL_BUILTIN_FUNCTION: return "<builtin_function>";
|
||||||
case ValueType::VAL_THUNK: return "<thunk>";
|
|
||||||
default: return "unknown";
|
default: return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
#include "../headers/helperFunctions/ShortHands.h"
|
#include "../headers/helperFunctions/ShortHands.h"
|
||||||
#include "../headers/ErrorReporter.h"
|
#include "../headers/ErrorReporter.h"
|
||||||
|
|
||||||
#define VERSION "0.0.2"
|
#define VERSION "0.0.1"
|
||||||
|
|
||||||
class Bob
|
class Bob
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,10 +24,6 @@ struct ReturnContext {
|
|||||||
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
|
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trampoline-based tail call optimization - no exceptions needed
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
|
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
|
||||||
if(expr->isNull) return NONE_VALUE;
|
if(expr->isNull) return NONE_VALUE;
|
||||||
@ -600,61 +596,27 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
|
|||||||
" arguments but got " + std::to_string(arguments.size()) + ".");
|
" arguments but got " + std::to_string(arguments.size()) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a tail call
|
auto previousEnv = environment;
|
||||||
if (expression->isTailCall) {
|
environment = std::make_shared<Environment>(function->closure);
|
||||||
// Create a thunk for tail call optimization
|
environment->setErrorReporter(errorReporter);
|
||||||
auto thunk = new Thunk([this, function, arguments]() -> Value {
|
|
||||||
// Use RAII to manage environment
|
for (size_t i = 0; i < function->params.size(); i++) {
|
||||||
ScopedEnv _env(environment);
|
environment->define(function->params[i], arguments[i]);
|
||||||
environment = std::make_shared<Environment>(function->closure);
|
|
||||||
environment->setErrorReporter(errorReporter);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < function->params.size(); i++) {
|
|
||||||
environment->define(function->params[i], arguments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutionContext context;
|
|
||||||
context.isFunctionBody = true;
|
|
||||||
|
|
||||||
// Use RAII to manage thunk execution flag
|
|
||||||
ScopedThunkFlag _inThunk(inThunkExecution);
|
|
||||||
|
|
||||||
// Execute function body
|
|
||||||
for (const auto& stmt : function->body) {
|
|
||||||
execute(stmt, &context);
|
|
||||||
if (context.hasReturn) {
|
|
||||||
return context.returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.returnValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the thunk as a Value
|
|
||||||
return Value(thunk);
|
|
||||||
} else {
|
|
||||||
// Normal function call - create new environment
|
|
||||||
ScopedEnv _env(environment);
|
|
||||||
environment = std::make_shared<Environment>(function->closure);
|
|
||||||
environment->setErrorReporter(errorReporter);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < function->params.size(); i++) {
|
|
||||||
environment->define(function->params[i], arguments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutionContext context;
|
|
||||||
context.isFunctionBody = true;
|
|
||||||
|
|
||||||
// Execute function body
|
|
||||||
for (const auto& stmt : function->body) {
|
|
||||||
execute(stmt, &context);
|
|
||||||
if (context.hasReturn) {
|
|
||||||
return context.returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExecutionContext context;
|
||||||
|
context.isFunctionBody = true;
|
||||||
|
|
||||||
|
for (const auto& stmt : function->body) {
|
||||||
|
execute(stmt, &context);
|
||||||
|
if (context.hasReturn) {
|
||||||
|
environment = previousEnv;
|
||||||
|
return context.returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment = previousEnv;
|
||||||
|
return context.returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Can only call functions and classes.");
|
throw std::runtime_error("Can only call functions and classes.");
|
||||||
@ -720,8 +682,6 @@ void Interpreter::visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement,
|
|||||||
{
|
{
|
||||||
Value value = NONE_VALUE;
|
Value value = NONE_VALUE;
|
||||||
if (statement->value != nullptr) {
|
if (statement->value != nullptr) {
|
||||||
// For tail calls, the trampoline handling is done in visitCallExpr
|
|
||||||
// We just need to evaluate normally
|
|
||||||
value = evaluate(statement->value);
|
value = evaluate(statement->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,28 +731,9 @@ void Interpreter::executeBlock(std::vector<std::shared_ptr<Stmt> > statements, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
Value Interpreter::evaluate(const std::shared_ptr<Expr>& expr) {
|
Value Interpreter::evaluate(const std::shared_ptr<Expr>& expr) {
|
||||||
Value result = expr->accept(this);
|
|
||||||
if (inThunkExecution) {
|
|
||||||
return result; // Don't use trampoline when inside a thunk
|
|
||||||
}
|
|
||||||
return runTrampoline(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value Interpreter::evaluateWithoutTrampoline(const std::shared_ptr<Expr>& expr) {
|
|
||||||
return expr->accept(this);
|
return expr->accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Interpreter::runTrampoline(Value initialResult) {
|
|
||||||
Value current = initialResult;
|
|
||||||
|
|
||||||
while (current.isThunk()) {
|
|
||||||
// Execute the thunk to get the next result
|
|
||||||
current = current.asThunk()->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interpreter::isTruthy(Value object) {
|
bool Interpreter::isTruthy(Value object) {
|
||||||
|
|
||||||
if(object.isBoolean())
|
if(object.isBoolean())
|
||||||
|
|||||||
@ -421,7 +421,7 @@ std::vector<Token> Lexer::Tokenize(std::string source){
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
tokens.push_back({END_OF_FILE, "eof", line, column});
|
tokens.push_back({END_OF_FILE, "eof", line});
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ void Bob::runPrompt()
|
|||||||
{
|
{
|
||||||
this->interpreter = msptr(Interpreter)(true);
|
this->interpreter = msptr(Interpreter)(true);
|
||||||
|
|
||||||
cout << "Bob v" << VERSION << ", 2025" << endl;
|
cout << "Bob v" << VERSION << ", 2023" << endl;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user