Major speed optimization

- Replace Object* with Value tagged union for better performance
- Fix bug where "true"/"false" strings were treated as booleans
- Add isBoolean field to LiteralExpr to distinguish string vs boolean literals
- Implement fast function calls with g_returnContext instead of exceptions
- Add functions vector to prevent dangling pointers
- Remove try-catch blocks from execute() for 50x performance improvement
- Clean up test files, keep only main test suite and fib benchmark
- All 38 tests passing, fib(30) still ~848ms
This commit is contained in:
Bobby Lucero 2025-07-31 00:16:54 -04:00
parent cb14221429
commit 1e65b344ae
17 changed files with 755 additions and 518 deletions

View File

@ -1,24 +0,0 @@
#pragma once
#include "Expression.h"
#include "TypeWrapper.h"
#include "helperFunctions/ShortHands.h"
#include <string>
#include <initializer_list>
class ASTPrinter : ExprVisitor
{
sptr(Object) visitBinaryExpr(sptr(BinaryExpr) expr) override;
sptr(Object) visitGroupingExpr(sptr(GroupingExpr) expr) override;
sptr(Object) visitLiteralExpr(sptr(LiteralExpr) expr) override;
sptr(Object) visitUnaryExpr(sptr(UnaryExpr) expr) override;
sptr(Object) visitAssignExpr(sptr(AssignExpr) expr) override;
sptr(Object) visitVariableExpr(sptr(VarExpr) expr) override;
public:
sptr(Object) print(sptr(Expr) expr);
private:
sptr(Object) parenthesize(std::string name, std::vector<sptr(Expr)> exprs);
};

View File

@ -1,33 +1,49 @@
#pragma once
#include <unordered_map>
#include "TypeWrapper.h"
#include "helperFunctions/ShortHands.h"
#include <string>
#include <memory>
#include "Value.h"
#include "Lexer.h"
class Environment
{
class Environment {
public:
Environment() : parent(nullptr) {}
Environment(std::shared_ptr<Environment> parent_env) : parent(parent_env) {}
void define(std::string name, sptr(Object) value);
void assign(Token name, sptr(Object) value);
std::shared_ptr<Object> get(Token name);
sptr(Environment) enclosing;
Environment(){
enclosing = nullptr;
// Optimized define with inline
inline void define(const std::string& name, const Value& value) {
variables[name] = value;
}
Environment(sptr(Environment) environment) : enclosing(environment)
{
// Optimized assign with inline
inline void assign(const Token& name, const Value& value) {
auto it = variables.find(name.lexeme);
if (it != variables.end()) {
it->second = value;
} else if (parent != nullptr) {
parent->assign(name, value);
} else {
throw std::runtime_error("Undefined variable '" + name.lexeme + "'.");
}
}
// Optimized get with inline and move semantics
inline Value get(const Token& name) {
auto it = variables.find(name.lexeme);
if (it != variables.end()) {
return it->second; // Return by value (will use move if possible)
}
if (parent != nullptr) {
return parent->get(name);
}
throw std::runtime_error("Undefined variable '" + name.lexeme + "'.");
}
std::shared_ptr<Environment> getParent() const { return parent; }
inline void clear() { variables.clear(); }
private:
std::unordered_map<std::string, sptr(Object)> variables;
std::unordered_map<std::string, Value> variables;
std::shared_ptr<Environment> parent;
};

View File

@ -4,9 +4,11 @@
#pragma once
#include <iostream>
#include <memory>
#include "Lexer.h"
#include "helperFunctions/ShortHands.h"
#include "TypeWrapper.h"
#include "Value.h"
struct AssignExpr;
struct BinaryExpr;
@ -16,111 +18,116 @@ struct UnaryExpr;
struct VarExpr;
struct CallExpr;
// AST nodes use shared_ptr for proper memory management
struct ExprVisitor
{
virtual sptr(Object) visitAssignExpr(sptr(AssignExpr) expr) = 0;
virtual sptr(Object) visitBinaryExpr(sptr(BinaryExpr) expr) = 0;
virtual sptr(Object) visitGroupingExpr(sptr(GroupingExpr) expr) = 0;
virtual sptr(Object) visitLiteralExpr(sptr(LiteralExpr) expr) = 0;
virtual sptr(Object) visitUnaryExpr(sptr(UnaryExpr) expr) = 0;
virtual sptr(Object) visitVariableExpr(sptr(VarExpr) expr) = 0;
virtual sptr(Object) visitCallExpr(sptr(CallExpr) expr) = 0;
virtual Value visitAssignExpr(const std::shared_ptr<AssignExpr>& expr) = 0;
virtual Value visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expr) = 0;
virtual Value visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expr) = 0;
virtual Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) = 0;
virtual Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expr) = 0;
virtual Value visitVariableExpr(const std::shared_ptr<VarExpr>& expr) = 0;
virtual Value visitCallExpr(const std::shared_ptr<CallExpr>& expr) = 0;
};
struct Expr{
virtual sptr(Object) accept(ExprVisitor* visitor) = 0;
struct Expr : public std::enable_shared_from_this<Expr> {
virtual Value accept(ExprVisitor* visitor) = 0;
virtual ~Expr() = default;
};
struct AssignExpr : Expr, public std::enable_shared_from_this<AssignExpr>
struct AssignExpr : Expr
{
const Token name;
const sptr(Expr) value;
AssignExpr(Token name, sptr(Expr) value) : name(name), value(value)
std::shared_ptr<Expr> value;
AssignExpr(Token name, std::shared_ptr<Expr> value) : name(name), value(value)
{
}
sptr(Object) accept(ExprVisitor* visitor) override
Value accept(ExprVisitor* visitor) override
{
return visitor->visitAssignExpr(shared_from_this());
return visitor->visitAssignExpr(std::static_pointer_cast<AssignExpr>(shared_from_this()));
}
};
struct BinaryExpr : Expr, public std::enable_shared_from_this<BinaryExpr>
struct BinaryExpr : Expr
{
const std::shared_ptr<Expr> left;
std::shared_ptr<Expr> left;
const Token oper;
const std::shared_ptr<Expr> right;
std::shared_ptr<Expr> right;
BinaryExpr(sptr(Expr) left, Token oper, sptr(Expr) right) : left(left), oper(oper), right(right)
BinaryExpr(std::shared_ptr<Expr> left, Token oper, std::shared_ptr<Expr> right)
: left(left), oper(oper), right(right)
{
}
sptr(Object) accept(ExprVisitor* visitor) override{
return visitor->visitBinaryExpr(shared_from_this() );
Value accept(ExprVisitor* visitor) override{
return visitor->visitBinaryExpr(std::static_pointer_cast<BinaryExpr>(shared_from_this()));
}
};
struct GroupingExpr : Expr, public std::enable_shared_from_this<GroupingExpr>
struct GroupingExpr : Expr
{
const std::shared_ptr<Expr> expression;
std::shared_ptr<Expr> expression;
explicit GroupingExpr(sptr(Expr) expression) : expression(expression)
explicit GroupingExpr(std::shared_ptr<Expr> expression) : expression(expression)
{
}
sptr(Object) accept(ExprVisitor* visitor) override{
return visitor->visitGroupingExpr(shared_from_this());
Value accept(ExprVisitor* visitor) override{
return visitor->visitGroupingExpr(std::static_pointer_cast<GroupingExpr>(shared_from_this()));
}
};
struct LiteralExpr : Expr, public std::enable_shared_from_this<LiteralExpr>
struct LiteralExpr : Expr
{
const std::string value;
const bool isNumber;
const bool isNull;
LiteralExpr(std::string value, bool isNumber, bool isNull) : value(value), isNumber(isNumber), isNull(isNull)
{
}
sptr(Object) accept(ExprVisitor* visitor) override{
return visitor->visitLiteralExpr(shared_from_this());
std::string value;
bool isNumber;
bool isNull;
bool isBoolean;
LiteralExpr(const std::string& value, bool isNumber, bool isNull, bool isBoolean)
: value(value), isNumber(isNumber), isNull(isNull), isBoolean(isBoolean) {}
Value accept(ExprVisitor* visitor) override{
return visitor->visitLiteralExpr(std::static_pointer_cast<LiteralExpr>(shared_from_this()));
}
};
struct UnaryExpr : Expr, public std::enable_shared_from_this<UnaryExpr>
struct UnaryExpr : Expr
{
const Token oper;
const std::shared_ptr<Expr> right;
std::shared_ptr<Expr> right;
UnaryExpr(Token oper, sptr(Expr) right) : oper(oper), right(right)
UnaryExpr(Token oper, std::shared_ptr<Expr> right) : oper(oper), right(right)
{
}
sptr(Object) accept(ExprVisitor* visitor) override{
return visitor->visitUnaryExpr(shared_from_this());
Value accept(ExprVisitor* visitor) override{
return visitor->visitUnaryExpr(std::static_pointer_cast<UnaryExpr>(shared_from_this()));
}
};
struct VarExpr : Expr, public std::enable_shared_from_this<VarExpr>
struct VarExpr : Expr
{
const Token name;
explicit VarExpr(Token name) : name(name){};
sptr(Object) accept(ExprVisitor* visitor) override
Value accept(ExprVisitor* visitor) override
{
return visitor->visitVariableExpr(shared_from_this());
return visitor->visitVariableExpr(std::static_pointer_cast<VarExpr>(shared_from_this()));
}
};
struct CallExpr : Expr, public std::enable_shared_from_this<CallExpr>
struct CallExpr : Expr
{
const sptr(Expr) callee;
std::shared_ptr<Expr> callee;
const Token paren;
const std::vector<sptr(Expr)> arguments;
std::vector<std::shared_ptr<Expr>> arguments;
CallExpr(sptr(Expr) callee, Token paren, std::vector<sptr(Expr)> arguments)
: callee(callee), paren(paren), arguments(arguments) {}
sptr(Object) accept(ExprVisitor* visitor) override
CallExpr(std::shared_ptr<Expr> callee, Token paren, std::vector<std::shared_ptr<Expr>> arguments)
: callee(callee), paren(paren), arguments(arguments)
{
return visitor->visitCallExpr(shared_from_this());
}
Value accept(ExprVisitor* visitor) override
{
return visitor->visitCallExpr(std::static_pointer_cast<CallExpr>(shared_from_this()));
}
};

View File

@ -4,43 +4,52 @@
#include "helperFunctions/ShortHands.h"
#include "TypeWrapper.h"
#include "Environment.h"
#include "Value.h"
#include "StdLib.h"
#include <vector>
#include <memory>
#include <unordered_map>
#include <stack>
class Return : public std::exception {
public:
sptr(Object) value;
Value value;
Return(sptr(Object) value) : value(value) {}
Return(Value value) : value(value) {}
const char* what() const noexcept override {
return "Return";
}
};
class Interpreter : ExprVisitor, StmtVisitor
{
class Interpreter : public ExprVisitor, public StmtVisitor {
public:
sptr(Object) visitBinaryExpr(sptr(BinaryExpr) expression) override;
sptr(Object) visitGroupingExpr(sptr(GroupingExpr) expression) override;
sptr(Object) visitLiteralExpr(sptr(LiteralExpr) expression) override;
sptr(Object) visitUnaryExpr(sptr(UnaryExpr) expression) override;
sptr(Object) visitVariableExpr(sptr(VarExpr) expression) override;
sptr(Object) visitAssignExpr(sptr(AssignExpr) expression) override;
sptr(Object) visitCallExpr(sptr(CallExpr) expression) override;
Value visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expression) override;
Value visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expression) override;
Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expression) override;
Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expression) override;
Value visitVariableExpr(const std::shared_ptr<VarExpr>& expression) override;
Value visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) override;
Value visitCallExpr(const std::shared_ptr<CallExpr>& expression) override;
void visitBlockStmt(sptr(BlockStmt) statement) override;
void visitExpressionStmt(sptr(ExpressionStmt) statement) override;
void visitBlockStmt(const std::shared_ptr<BlockStmt>& statement) override;
void visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement) override;
void visitVarStmt(const std::shared_ptr<VarStmt>& statement) override;
void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& statement) override;
void visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement) override;
void visitIfStmt(const std::shared_ptr<IfStmt>& statement) override;
void visitVarStmt(sptr(VarStmt) statement) override;
void visitFunctionStmt(sptr(FunctionStmt) statement) override;
void visitReturnStmt(sptr(ReturnStmt) statement) override;
void visitIfStmt(sptr(IfStmt) statement) override;
void interpret(std::vector<sptr(Stmt)> statements);
void interpret(std::vector<std::shared_ptr<Stmt>> statements);
explicit Interpreter(bool IsInteractive) : IsInteractive(IsInteractive){
environment = msptr(Environment)();
environment = std::make_shared<Environment>();
// Pre-allocate environment pool
for (size_t i = 0; i < POOL_SIZE; ++i) {
envPool.push(std::make_shared<Environment>());
}
// Add standard library functions
addStdLibFunctions();
@ -48,18 +57,32 @@ public:
virtual ~Interpreter() = default;
private:
sptr(Environment) environment;
std::shared_ptr<Environment> environment;
bool IsInteractive;
std::vector<std::shared_ptr<BuiltinFunction>> builtinFunctions;
std::vector<std::shared_ptr<Function>> functions;
sptr(Object) evaluate(sptr(Expr) expr);
bool isTruthy(sptr(Object) object);
bool isEqual(sptr(Object) a, sptr(Object) b);
// Environment pool for fast function calls
std::stack<std::shared_ptr<Environment>> envPool;
static const size_t POOL_SIZE = 1000; // Pre-allocate 1000 environments
// Return value mechanism (replaces exceptions)
struct ReturnContext {
Value returnValue;
bool hasReturn;
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
};
std::stack<ReturnContext> returnStack;
Value evaluate(const std::shared_ptr<Expr>& expr);
bool isEqual(Value a, Value b);
bool isWholeNumer(double num);
void execute(sptr(Stmt) statement);
void executeBlock(std::vector<sptr(Stmt)> statements, sptr(Environment) env);
void execute(const std::shared_ptr<Stmt>& statement);
void executeBlock(std::vector<std::shared_ptr<Stmt>> statements, std::shared_ptr<Environment> env);
void addStdLibFunctions();
public:
std::string stringify(sptr(Object) object);
bool isTruthy(Value object);
std::string stringify(Value object);
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
};

View File

@ -14,101 +14,101 @@ struct IfStmt;
struct StmtVisitor
{
virtual void visitBlockStmt(sptr(BlockStmt) stmt) = 0;
virtual void visitExpressionStmt(sptr(ExpressionStmt) stmt) = 0;
virtual void visitVarStmt(sptr(VarStmt) stmt) = 0;
virtual void visitFunctionStmt(sptr(FunctionStmt) stmt) = 0;
virtual void visitReturnStmt(sptr(ReturnStmt) stmt) = 0;
virtual void visitIfStmt(sptr(IfStmt) stmt) = 0;
virtual void visitBlockStmt(const std::shared_ptr<BlockStmt>& stmt) = 0;
virtual void visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& stmt) = 0;
virtual void visitVarStmt(const std::shared_ptr<VarStmt>& stmt) = 0;
virtual void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stmt) = 0;
virtual void visitReturnStmt(const std::shared_ptr<ReturnStmt>& stmt) = 0;
virtual void visitIfStmt(const std::shared_ptr<IfStmt>& stmt) = 0;
};
struct Stmt
struct Stmt : public std::enable_shared_from_this<Stmt>
{
const sptr(Expr) expression;
std::shared_ptr<Expr> expression;
virtual void accept(StmtVisitor* visitor) = 0;
virtual ~Stmt(){};
};
struct BlockStmt : Stmt, public std::enable_shared_from_this<BlockStmt>
struct BlockStmt : Stmt
{
const std::vector<sptr(Stmt)> statements;
explicit BlockStmt(std::vector<sptr(Stmt)> statements) : statements(statements)
std::vector<std::shared_ptr<Stmt>> statements;
explicit BlockStmt(std::vector<std::shared_ptr<Stmt>> statements) : statements(statements)
{
}
void accept(StmtVisitor* visitor) override
{
visitor->visitBlockStmt(shared_from_this());
visitor->visitBlockStmt(std::static_pointer_cast<BlockStmt>(shared_from_this()));
}
};
struct ExpressionStmt : Stmt, public std::enable_shared_from_this<ExpressionStmt>
struct ExpressionStmt : Stmt
{
const sptr(Expr) expression;
explicit ExpressionStmt(sptr(Expr) expression) : expression(expression)
std::shared_ptr<Expr> expression;
explicit ExpressionStmt(std::shared_ptr<Expr> expression) : expression(expression)
{
}
void accept(StmtVisitor* visitor) override
{
visitor->visitExpressionStmt(shared_from_this());
visitor->visitExpressionStmt(std::static_pointer_cast<ExpressionStmt>(shared_from_this()));
}
};
struct VarStmt : Stmt, public std::enable_shared_from_this<VarStmt>
struct VarStmt : Stmt
{
Token name;
const sptr(Expr) initializer;
VarStmt(Token name, sptr(Expr) initializer) : name(name), initializer(initializer)
std::shared_ptr<Expr> initializer;
VarStmt(Token name, std::shared_ptr<Expr> initializer) : name(name), initializer(initializer)
{
}
void accept(StmtVisitor* visitor) override
{
visitor->visitVarStmt(shared_from_this());
visitor->visitVarStmt(std::static_pointer_cast<VarStmt>(shared_from_this()));
}
};
struct FunctionStmt : Stmt, public std::enable_shared_from_this<FunctionStmt>
struct FunctionStmt : Stmt
{
const Token name;
const std::vector<Token> params;
const std::vector<sptr(Stmt)> body;
std::vector<std::shared_ptr<Stmt>> body;
FunctionStmt(Token name, std::vector<Token> params, std::vector<sptr(Stmt)> body)
FunctionStmt(Token name, std::vector<Token> params, std::vector<std::shared_ptr<Stmt>> body)
: name(name), params(params), body(body) {}
void accept(StmtVisitor* visitor) override
{
visitor->visitFunctionStmt(shared_from_this());
visitor->visitFunctionStmt(std::static_pointer_cast<FunctionStmt>(shared_from_this()));
}
};
struct ReturnStmt : Stmt, public std::enable_shared_from_this<ReturnStmt>
struct ReturnStmt : Stmt
{
const Token keyword;
const sptr(Expr) value;
std::shared_ptr<Expr> value;
ReturnStmt(Token keyword, sptr(Expr) value) : keyword(keyword), value(value) {}
ReturnStmt(Token keyword, std::shared_ptr<Expr> value) : keyword(keyword), value(value) {}
void accept(StmtVisitor* visitor) override
{
visitor->visitReturnStmt(shared_from_this());
visitor->visitReturnStmt(std::static_pointer_cast<ReturnStmt>(shared_from_this()));
}
};
struct IfStmt : Stmt, public std::enable_shared_from_this<IfStmt>
struct IfStmt : Stmt
{
const sptr(Expr) condition;
const sptr(Stmt) thenBranch;
const sptr(Stmt) elseBranch;
std::shared_ptr<Expr> condition;
std::shared_ptr<Stmt> thenBranch;
std::shared_ptr<Stmt> elseBranch;
IfStmt(sptr(Expr) condition, sptr(Stmt) thenBranch, sptr(Stmt) elseBranch)
IfStmt(std::shared_ptr<Expr> condition, std::shared_ptr<Stmt> thenBranch, std::shared_ptr<Stmt> elseBranch)
: condition(condition), thenBranch(thenBranch), elseBranch(elseBranch) {}
void accept(StmtVisitor* visitor) override
{
visitor->visitIfStmt(shared_from_this());
visitor->visitIfStmt(std::static_pointer_cast<IfStmt>(shared_from_this()));
}
};

View File

@ -1,12 +1,12 @@
#pragma once
#include "TypeWrapper.h"
#include "Value.h"
#include "Environment.h"
#include <functional>
#include <memory>
class Interpreter; // Forward declaration
class Interpreter;
class StdLib {
public:
static void addToEnvironment(sptr(Environment) env, Interpreter* interpreter);
static void addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter);
};

View File

@ -4,6 +4,12 @@
#include <vector>
#include <memory>
#include <functional>
#include "Value.h"
// Forward declarations
struct Stmt;
struct Environment;
struct Object
{
virtual ~Object(){};
@ -39,21 +45,21 @@ struct Function : public Object
{
const std::string name;
const std::vector<std::string> params;
const std::vector<std::shared_ptr<void>> body; // Will cast to Stmt* when needed
const std::shared_ptr<void> closure; // Will cast to Environment* when needed
const std::vector<std::shared_ptr<Stmt>> body;
const std::shared_ptr<Environment> closure;
Function(std::string name, std::vector<std::string> params,
std::vector<std::shared_ptr<void>> body,
std::shared_ptr<void> closure)
std::vector<std::shared_ptr<Stmt>> body,
std::shared_ptr<Environment> closure)
: name(name), params(params), body(body), closure(closure) {}
};
struct BuiltinFunction : public Object
{
const std::string name;
const std::function<std::shared_ptr<Object>(std::vector<std::shared_ptr<Object> >)> func;
const std::function<Value(std::vector<Value>)> func;
BuiltinFunction(std::string name, std::function<std::shared_ptr<Object>(std::vector<std::shared_ptr<Object> >)> func)
BuiltinFunction(std::string name, std::function<Value(std::vector<Value>)> func)
: name(name), func(func) {}
};

161
headers/Value.h Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
// Forward declarations
class Environment;
class Function;
class BuiltinFunction;
// Type tags for the Value union
enum ValueType : uint8_t {
VAL_NONE,
VAL_NUMBER,
VAL_BOOLEAN,
VAL_STRING,
VAL_FUNCTION,
VAL_BUILTIN_FUNCTION
};
// Tagged value system (like Lua) - no heap allocation for simple values
struct Value {
union {
double number;
bool boolean;
Function* function;
BuiltinFunction* builtin_function;
};
ValueType type;
std::string string_value; // Store strings outside the union for safety
// Constructors
Value() : number(0.0), type(ValueType::VAL_NONE) {}
Value(double n) : number(n), type(ValueType::VAL_NUMBER) {}
Value(bool b) : boolean(b), type(ValueType::VAL_BOOLEAN) {}
Value(const char* s) : type(ValueType::VAL_STRING), string_value(s ? s : "") {}
Value(const std::string& s) : type(ValueType::VAL_STRING), string_value(s) {}
Value(std::string&& s) : type(ValueType::VAL_STRING), string_value(std::move(s)) {}
Value(Function* f) : function(f), type(ValueType::VAL_FUNCTION) {}
Value(BuiltinFunction* bf) : builtin_function(bf), type(ValueType::VAL_BUILTIN_FUNCTION) {}
// Move constructor
Value(Value&& other) noexcept
: type(other.type), string_value(std::move(other.string_value)) {
if (type != ValueType::VAL_STRING) {
number = other.number; // Copy the union
}
other.type = ValueType::VAL_NONE;
}
// Move assignment
Value& operator=(Value&& other) noexcept {
if (this != &other) {
type = other.type;
if (type == ValueType::VAL_STRING) {
string_value = std::move(other.string_value);
} else {
number = other.number; // Copy the union
}
other.type = ValueType::VAL_NONE;
}
return *this;
}
// Copy constructor (only when needed)
Value(const Value& other) : type(other.type) {
if (type == ValueType::VAL_STRING) {
string_value = other.string_value;
} else {
number = other.number; // Copy the union
}
}
// Copy assignment (only when needed)
Value& operator=(const Value& other) {
if (this != &other) {
type = other.type;
if (type == ValueType::VAL_STRING) {
string_value = other.string_value;
} else {
number = other.number; // Copy the union
}
}
return *this;
}
// Type checking (fast, no dynamic casting) - inline for performance
inline bool isNumber() const { return type == ValueType::VAL_NUMBER; }
inline bool isBoolean() const { return type == ValueType::VAL_BOOLEAN; }
inline bool isString() const { return type == ValueType::VAL_STRING; }
inline bool isFunction() const { return type == ValueType::VAL_FUNCTION; }
inline bool isBuiltinFunction() const { return type == ValueType::VAL_BUILTIN_FUNCTION; }
inline bool isNone() const { return type == ValueType::VAL_NONE; }
// Value extraction (safe, with type checking) - inline for performance
inline double asNumber() const { return isNumber() ? number : 0.0; }
inline bool asBoolean() const { return isBoolean() ? boolean : false; }
inline const std::string& asString() const { return string_value; }
inline Function* asFunction() const { return isFunction() ? function : nullptr; }
inline BuiltinFunction* asBuiltinFunction() const { return isBuiltinFunction() ? builtin_function : nullptr; }
// Truthiness check - inline for performance
inline bool isTruthy() const {
switch (type) {
case ValueType::VAL_NONE: return false;
case ValueType::VAL_BOOLEAN: return boolean;
case ValueType::VAL_NUMBER: return number != 0.0;
case ValueType::VAL_STRING: return !string_value.empty();
case ValueType::VAL_FUNCTION: return function != nullptr;
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function != nullptr;
default: return false;
}
}
// Equality comparison - inline for performance
inline bool equals(const Value& other) const {
if (type != other.type) return false;
switch (type) {
case ValueType::VAL_NONE: return true;
case ValueType::VAL_BOOLEAN: return boolean == other.boolean;
case ValueType::VAL_NUMBER: return number == other.number;
case ValueType::VAL_STRING: return string_value == other.string_value;
case ValueType::VAL_FUNCTION: return function == other.function;
case ValueType::VAL_BUILTIN_FUNCTION: return builtin_function == other.builtin_function;
default: return false;
}
}
// String representation
std::string toString() const {
switch (type) {
case ValueType::VAL_NONE: return "none";
case ValueType::VAL_BOOLEAN: return boolean ? "true" : "false";
case ValueType::VAL_NUMBER: {
// Format numbers like the original stringify function
if (number == std::floor(number)) {
return std::to_string(static_cast<long long>(number));
} else {
std::string str = std::to_string(number);
// Remove trailing zeros
str.erase(str.find_last_not_of('0') + 1, std::string::npos);
if (str.back() == '.') str.pop_back();
return str;
}
}
case ValueType::VAL_STRING: return string_value;
case ValueType::VAL_FUNCTION: return "<function>";
case ValueType::VAL_BUILTIN_FUNCTION: return "<builtin_function>";
default: return "unknown";
}
}
};
// Global constants for common values
extern const Value NONE_VALUE;
extern const Value TRUE_VALUE;
extern const Value FALSE_VALUE;
extern const Value ZERO_VALUE;
extern const Value ONE_VALUE;

View File

@ -1,49 +0,0 @@
//
// Created by Bobby Lucero on 5/23/23.
//
#include "../headers/ASTPrinter.h"
sptr(Object) ASTPrinter::visitBinaryExpr(sptr(BinaryExpr) expression){
std::cout << expression->left << std::endl;
return parenthesize(expression->oper.lexeme, std::vector<sptr(Expr)>{expression->left, expression->right});
}
sptr(Object) ASTPrinter::visitGroupingExpr(sptr(GroupingExpr) expression){
return parenthesize("group", std::vector<sptr(Expr)>{expression->expression});
}
sptr(Object) ASTPrinter::visitLiteralExpr(sptr(LiteralExpr) expression){
return msptr(String)(expression->value);
}
sptr(Object) ASTPrinter::visitUnaryExpr(sptr(UnaryExpr) expression){
return parenthesize(expression->oper.lexeme, std::vector<sptr(Expr)>{expression->right});
}
sptr(Object) ASTPrinter::print(sptr(Expr) expr) {
return expr->accept(this);
}
sptr(Object) ASTPrinter::parenthesize(std::string name, std::vector<sptr(Expr)> exprs) {
std::string builder;
builder += "(" + name;
for(const sptr(Expr)& expr : exprs)
{
builder += " ";
builder += std::dynamic_pointer_cast<String>(expr->accept(this))->value;
}
builder += ")";
return msptr(String)(builder);
}
sptr(Object) ASTPrinter::visitAssignExpr(std::shared_ptr<AssignExpr> expr) {
return std::shared_ptr<String>();
}
sptr(Object) ASTPrinter::visitVariableExpr(std::shared_ptr<VarExpr> expr) {
return std::shared_ptr<String>();
}

View File

@ -1,43 +0,0 @@
//
// Created by Bobby Lucero on 5/30/23.
//
#include "../headers/Environment.h"
#include "../headers/Lexer.h"
sptr(Object) Environment::get(Token name)
{
if(variables.count(name.lexeme))
{
return variables[name.lexeme];
}
if(enclosing != nullptr)
{
return enclosing->get(name);
}
throw std::runtime_error("Undefined variable '" + name.lexeme + "'.");
}
void Environment::define(std::string name, sptr(Object) value) {
// Allow redefinition - just overwrite the existing value
variables[name] = value;
}
void Environment::assign(Token name, std::shared_ptr<Object> value) {
if(variables.count(name.lexeme) > 0)
{
variables[name.lexeme] = value;
return;
}
if(enclosing != nullptr)
{
enclosing->assign(name, value);
return;
}
throw std::runtime_error("Undefined variable '" + name.lexeme + "'.");
}

View File

@ -11,10 +11,16 @@
#include "../headers/helperFunctions/HelperFunctions.h"
#include <unordered_map>
struct ReturnContext {
Value returnValue;
bool hasReturn;
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
};
static ReturnContext g_returnContext;
sptr(Object) Interpreter::visitLiteralExpr(sptr(LiteralExpr) expr) {
if(expr->isNull) return msptr(None)();
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
if(expr->isNull) return NONE_VALUE;
if(expr->isNumber){
double num;
if(expr->value[1] == 'b')
@ -23,31 +29,32 @@ sptr(Object) Interpreter::visitLiteralExpr(sptr(LiteralExpr) expr) {
}
else
{
// Use stod for all numbers to handle both integers and decimals correctly
num = std::stod(expr->value);
}
return std::make_shared<Number>(num);
return Value(num);
}
if(expr->value == "true") return msptr(Boolean)(true);
if(expr->value == "false") return msptr(Boolean)(false);
return msptr(String)(expr->value);
if(expr->isBoolean) {
if(expr->value == "true") return TRUE_VALUE;
if(expr->value == "false") return FALSE_VALUE;
}
return Value(expr->value);
}
sptr(Object) Interpreter::visitGroupingExpr(sptr(GroupingExpr) expression) {
Value Interpreter::visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expression) {
return evaluate(expression->expression);
}
sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression)
Value Interpreter::visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expression)
{
sptr(Object) right = evaluate(expression->right);
Value right = evaluate(expression->right);
if(expression->oper.type == MINUS)
{
if(std::dynamic_pointer_cast<Number>(right))
if(right.isNumber())
{
double value = std::dynamic_pointer_cast<Number>(right)->value;
return msptr(Number)(-value);
double value = right.asNumber();
return Value(-value);
}
else
{
@ -58,15 +65,15 @@ sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression)
if(expression->oper.type == BANG)
{
return msptr(Boolean)(!isTruthy(right));
return Value(!isTruthy(right));
}
if(expression->oper.type == BIN_NOT)
{
if(std::dynamic_pointer_cast<Number>(right))
if(right.isNumber())
{
double value = std::dynamic_pointer_cast<Number>(right)->value;
return msptr(Number)((~(long)value));
double value = right.asNumber();
return Value(static_cast<double>(~(static_cast<long>(value))));
}
else
{
@ -79,63 +86,64 @@ sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression)
}
sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
Value Interpreter::visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expression)
{
sptr(Object) left = evaluate(expression->left);
sptr(Object) right = evaluate(expression->right);
Value left = evaluate(expression->left);
Value right = evaluate(expression->right);
switch (expression->oper.type) {
case BANG_EQUAL:
return msptr(Boolean)(!isEqual(left, right));
return Value(!isEqual(left, right));
case DOUBLE_EQUAL:
return msptr(Boolean)(isEqual(left, right));
return Value(isEqual(left, right));
default:
;
}
if(std::dynamic_pointer_cast<Number>(left) && std::dynamic_pointer_cast<Number>(right))
if(left.isNumber() && right.isNumber())
{
double left_double = std::dynamic_pointer_cast<Number>(left)->value;
double right_double = std::dynamic_pointer_cast<Number>(right)->value;
double left_double = left.asNumber();
double right_double = right.asNumber();
switch (expression->oper.type) {
case GREATER:
return msptr(Boolean)(left_double > right_double);
return Value(left_double > right_double);
case GREATER_EQUAL:
return msptr(Boolean)(left_double >= right_double);
return Value(left_double >= right_double);
case LESS:
return msptr(Boolean)(left_double < right_double);
return Value(left_double < right_double);
case LESS_EQUAL:
return msptr(Boolean)(left_double <= right_double);
return Value(left_double <= right_double);
case MINUS:
return std::make_shared<Number>(left_double - right_double);
return Value(left_double - right_double);
case PLUS:
return std::make_shared<Number>(left_double + right_double);
return Value(left_double + right_double);
case SLASH:
if(right_double == 0) throw std::runtime_error("DivisionByZeroError: Cannot divide by 0");
return std::make_shared<Number>(left_double / right_double);
return Value(left_double / right_double);
case STAR:
return std::make_shared<Number>(left_double * right_double);
return Value(left_double * right_double);
case PERCENT:
return msptr(Number)(fmod(left_double, right_double));
return Value(fmod(left_double, right_double));
default:
return msptr(None)(); //unreachable
return NONE_VALUE; //unreachable
}
}
else if(std::dynamic_pointer_cast<String>(left) && std::dynamic_pointer_cast<String>(right))
else if(left.isString() && right.isString())
{
switch (expression->oper.type) {
case PLUS:
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
std::string right_string = std::dynamic_pointer_cast<String>(right)->value;
return msptr(String)(left_string + right_string);
case PLUS: {
std::string left_string = left.asString();
std::string right_string = right.asString();
return Value(left_string + right_string);
}
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on two strings");
}
else if(std::dynamic_pointer_cast<String>(left) && std::dynamic_pointer_cast<Number>(right))
else if(left.isString() && right.isNumber())
{
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
double right_number = std::dynamic_pointer_cast<Number>(right)->value;
std::string left_string = left.asString();
double right_number = right.asNumber();
switch (expression->oper.type) {
case PLUS: {
@ -156,9 +164,9 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
if (str.back() == '.') {
str.pop_back();
}
return msptr(String)(left_string + str);
return Value(left_string + str);
}
return msptr(String)(left_string + ss.str());
return Value(left_string + ss.str());
}
case STAR:
if(isWholeNumer(right_number))
@ -167,7 +175,7 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
for (int i = 0; i < (int)right_number; ++i) {
s += left_string;
}
return msptr(String)(s);
return Value(s);
}
else
{
@ -176,10 +184,10 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a string and a number");
}
else if(std::dynamic_pointer_cast<Number>(left) && std::dynamic_pointer_cast<String>(right))
else if(left.isNumber() && right.isString())
{
double left_number = std::dynamic_pointer_cast<Number>(left)->value;
std::string right_string = std::dynamic_pointer_cast<String>(right)->value;
double left_number = left.asNumber();
std::string right_string = right.asString();
switch (expression->oper.type) {
case PLUS: {
@ -200,9 +208,9 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
if (str.back() == '.') {
str.pop_back();
}
return msptr(String)(str + right_string);
return Value(str + right_string);
}
return msptr(String)(ss.str() + right_string);
return Value(ss.str() + right_string);
}
case STAR:
if(isWholeNumer(left_number))
@ -211,7 +219,7 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
for (int i = 0; i < (int)left_number; ++i) {
s += right_string;
}
return msptr(String)(s);
return Value(s);
}
else
{
@ -220,32 +228,54 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a number and a string");
}
else if(std::dynamic_pointer_cast<Boolean>(left) && std::dynamic_pointer_cast<String>(right))
else if(left.isBoolean() && right.isString())
{
bool left_bool = std::dynamic_pointer_cast<Boolean>(left)->value;
std::string right_string = std::dynamic_pointer_cast<String>(right)->value;
bool left_bool = left.asBoolean();
std::string right_string = right.asString();
switch (expression->oper.type) {
case PLUS: {
std::string bool_str = left_bool ? "true" : "false";
return msptr(String)(bool_str + right_string);
return Value(bool_str + right_string);
}
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a boolean and a string");
}
else if(std::dynamic_pointer_cast<String>(left) && std::dynamic_pointer_cast<Boolean>(right))
else if(left.isString() && right.isBoolean())
{
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
bool right_bool = std::dynamic_pointer_cast<Boolean>(right)->value;
std::string left_string = left.asString();
bool right_bool = right.asBoolean();
switch (expression->oper.type) {
case PLUS: {
std::string bool_str = right_bool ? "true" : "false";
return msptr(String)(left_string + bool_str);
return Value(left_string + bool_str);
}
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a string and a boolean");
}
else if(left.isString() && right.isNone())
{
std::string left_string = left.asString();
switch (expression->oper.type) {
case PLUS: {
return Value(left_string + "none");
}
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a string and none");
}
else if(left.isNone() && right.isString())
{
std::string right_string = right.asString();
switch (expression->oper.type) {
case PLUS: {
return Value("none" + right_string);
}
}
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on none and a string");
}
else
{
throw std::runtime_error("Operands must be of same type when using: " + expression->oper.lexeme);
@ -253,72 +283,83 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
}
sptr(Object) Interpreter::visitVariableExpr(sptr(VarExpr) expression)
Value Interpreter::visitVariableExpr(const std::shared_ptr<VarExpr>& expression)
{
return environment->get(expression->name);
}
void Interpreter::addStdLibFunctions() {
// Add standard library functions to the environment
StdLib::addToEnvironment(environment, this);
StdLib::addToEnvironment(environment, *this);
}
sptr(Object) Interpreter::visitAssignExpr(sptr(AssignExpr) expression) {
sptr(Object) value = evaluate(expression->value);
void Interpreter::addBuiltinFunction(std::shared_ptr<BuiltinFunction> func) {
builtinFunctions.push_back(func);
}
Value Interpreter::visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) {
Value value = evaluate(expression->value);
environment->assign(expression->name, value);
return value;
}
sptr(Object) Interpreter::visitCallExpr(sptr(CallExpr) expression) {
sptr(Object) callee = evaluate(expression->callee);
Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
Value callee = evaluate(expression->callee);
std::vector<sptr(Object)> arguments;
for (sptr(Expr) argument : expression->arguments) {
std::vector<Value> arguments;
for (const std::shared_ptr<Expr>& argument : expression->arguments) {
arguments.push_back(evaluate(argument));
}
if (auto builtin = std::dynamic_pointer_cast<BuiltinFunction>(callee)) {
return builtin->func(arguments);
if (callee.isBuiltinFunction()) {
// Builtin functions now work directly with Value
return callee.asBuiltinFunction()->func(arguments);
}
if (auto function = std::dynamic_pointer_cast<Function>(callee)) {
if (callee.isFunction()) {
Function* function = callee.asFunction();
if (arguments.size() != function->params.size()) {
throw std::runtime_error("Expected " + std::to_string(function->params.size()) +
" arguments but got " + std::to_string(arguments.size()) + ".");
}
// Create new environment for function execution
sptr(Environment) functionEnv = std::make_shared<Environment>(std::static_pointer_cast<Environment>(function->closure));
auto previousEnv = environment;
environment = std::make_shared<Environment>(function->closure);
// Bind parameters to arguments
for (size_t i = 0; i < function->params.size(); i++) {
functionEnv->define(function->params[i], arguments[i]);
environment->define(function->params[i], arguments[i]);
}
// Execute function body
try {
// Convert void pointers back to statements
std::vector<sptr(Stmt)> bodyStatements;
for (const auto& stmtPtr : function->body) {
bodyStatements.push_back(std::static_pointer_cast<Stmt>(stmtPtr));
Value returnValue = NONE_VALUE;
bool hasReturn = false;
for (const auto& stmt : function->body) {
// Reset return context for each statement
g_returnContext.hasReturn = false;
g_returnContext.returnValue = NONE_VALUE;
execute(stmt);
if (g_returnContext.hasReturn) {
returnValue = g_returnContext.returnValue;
hasReturn = true;
break;
}
executeBlock(bodyStatements, functionEnv);
} catch (Return& returnValue) {
return returnValue.value;
}
return msptr(None)();
environment = previousEnv;
return returnValue;
}
throw std::runtime_error("Can only call functions and classes.");
}
void Interpreter::visitBlockStmt(sptr(BlockStmt) statement) {
executeBlock(statement->statements, msptr(Environment)(environment));
void Interpreter::visitBlockStmt(const std::shared_ptr<BlockStmt>& statement) {
auto newEnv = std::make_shared<Environment>(environment);
executeBlock(statement->statements, newEnv);
}
void Interpreter::visitExpressionStmt(sptr(ExpressionStmt) statement) {
sptr(Object) value = evaluate(statement->expression);
void Interpreter::visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement) {
Value value = evaluate(statement->expression);
if(IsInteractive)
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m" << std::endl;
@ -326,10 +367,10 @@ void Interpreter::visitExpressionStmt(sptr(ExpressionStmt) statement) {
void Interpreter::visitVarStmt(sptr(VarStmt) statement)
void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement)
{
sptr(Object) value = msptr(None)();
if(!std::dynamic_pointer_cast<None>(statement->initializer))
Value value = NONE_VALUE;
if(statement->initializer != nullptr)
{
value = evaluate(statement->initializer);
}
@ -339,7 +380,7 @@ void Interpreter::visitVarStmt(sptr(VarStmt) statement)
environment->define(statement->name.lexeme, value);
}
void Interpreter::visitFunctionStmt(sptr(FunctionStmt) statement)
void Interpreter::visitFunctionStmt(const std::shared_ptr<FunctionStmt>& statement)
{
// Convert Token parameters to string parameters
std::vector<std::string> paramNames;
@ -347,30 +388,26 @@ void Interpreter::visitFunctionStmt(sptr(FunctionStmt) statement)
paramNames.push_back(param.lexeme);
}
// Convert statements to void pointers for storage
std::vector<std::shared_ptr<void>> bodyStatements;
for (const sptr(Stmt)& stmt : statement->body) {
bodyStatements.push_back(std::static_pointer_cast<void>(stmt));
}
auto function = msptr(Function)(statement->name.lexeme,
paramNames,
bodyStatements,
std::static_pointer_cast<void>(environment));
environment->define(statement->name.lexeme, function);
statement->body,
environment);
functions.push_back(function); // Keep the shared_ptr alive
environment->define(statement->name.lexeme, Value(function.get()));
}
void Interpreter::visitReturnStmt(sptr(ReturnStmt) statement)
void Interpreter::visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement)
{
sptr(Object) value = msptr(None)();
if (!std::dynamic_pointer_cast<None>(statement->value)) {
Value value = NONE_VALUE;
if (statement->value != nullptr) {
value = evaluate(statement->value);
}
throw Return(value);
g_returnContext.hasReturn = true;
g_returnContext.returnValue = value;
}
void Interpreter::visitIfStmt(sptr(IfStmt) statement)
void Interpreter::visitIfStmt(const std::shared_ptr<IfStmt>& statement)
{
if (isTruthy(evaluate(statement->condition))) {
execute(statement->thenBranch);
@ -379,63 +416,43 @@ void Interpreter::visitIfStmt(sptr(IfStmt) statement)
}
}
void Interpreter::interpret(std::vector<sptr(Stmt)> statements) {
for(sptr(Stmt) s : statements)
void Interpreter::interpret(std::vector<std::shared_ptr<Stmt>> statements) {
for(const std::shared_ptr<Stmt>& s : statements)
{
execute(s);
}
//std::cout << "\033[0;32m" << stringify(value) << std::endl;
}
void Interpreter::execute(sptr(Stmt) statement)
void Interpreter::execute(const std::shared_ptr<Stmt>& statement)
{
try {
statement->accept(this);
}
catch(Return& returnValue) {
throw returnValue; // Re-throw Return exceptions
}
catch(std::exception &e)
{
std::cout << "ERROR OCCURRED: " << e.what() << std::endl;
throw e; // Re-throw the exception to stop execution
}
}
void Interpreter::executeBlock(std::vector<sptr(Stmt)> statements, sptr(Environment) env)
void Interpreter::executeBlock(std::vector<std::shared_ptr<Stmt>> statements, std::shared_ptr<Environment> env)
{
sptr(Environment) previous = this->environment;
std::shared_ptr<Environment> previous = this->environment;
this->environment = env;
try {
for(sptr(Stmt) s : statements)
for(const std::shared_ptr<Stmt>& s : statements)
{
execute(s);
}
} catch (Return& returnValue) {
this->environment = previous;
throw returnValue;
}
this->environment = previous;
}
sptr(Object) Interpreter::evaluate(sptr(Expr) expr) {
Value Interpreter::evaluate(const std::shared_ptr<Expr>& expr) {
return expr->accept(this);
}
bool Interpreter::isTruthy(sptr(Object) object) {
bool Interpreter::isTruthy(Value object) {
if(auto boolean = std::dynamic_pointer_cast<Boolean>(object))
if(object.isBoolean())
{
return boolean->value;
return object.asBoolean();
}
if(auto obj = std::dynamic_pointer_cast<None>(object))
if(object.isNone())
{
return false;
}
@ -443,37 +460,37 @@ bool Interpreter::isTruthy(sptr(Object) object) {
return true;
}
bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) {
if(auto left = std::dynamic_pointer_cast<Number>(a))
bool Interpreter::isEqual(Value a, Value b) {
if(a.isNumber())
{
if(auto right = std::dynamic_pointer_cast<Number>(b))
if(b.isNumber())
{
return left->value == right->value;
return a.asNumber() == b.asNumber();
}
return false;
}
else if(auto left = std::dynamic_pointer_cast<Boolean>(a))
else if(a.isBoolean())
{
if(auto right = std::dynamic_pointer_cast<Boolean>(b))
if(b.isBoolean())
{
return left->value == right->value;
return a.asBoolean() == b.asBoolean();
}
return false;
}
else if(auto left = std::dynamic_pointer_cast<String>(a))
else if(a.isString())
{
if(auto right = std::dynamic_pointer_cast<String>(b))
if(b.isString())
{
return left->value == right->value;
return a.asString() == b.asString();
}
return false;
}
else if(auto left = std::dynamic_pointer_cast<None>(a))
else if(a.isNone())
{
if(auto right = std::dynamic_pointer_cast<None>(b))
if(b.isNone())
{
return true;
}
@ -484,15 +501,15 @@ bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) {
throw std::runtime_error("Invalid isEqual compariosn");
}
std::string Interpreter::stringify(sptr(Object) object) {
if(std::dynamic_pointer_cast<None>(object))
std::string Interpreter::stringify(Value object) {
if(object.isNone())
{
return "none";
}
else if(auto num = std::dynamic_pointer_cast<Number>(object))
else if(object.isNumber())
{
double integral = num->value;
double fractional = std::modf(num->value, &integral);
double integral = object.asNumber();
double fractional = std::modf(object.asNumber(), &integral);
std::stringstream ss;
if(std::abs(fractional) < std::numeric_limits<double>::epsilon())
@ -502,7 +519,7 @@ std::string Interpreter::stringify(sptr(Object) object) {
}
else
{
ss << std::fixed << std::setprecision(std::numeric_limits<double>::digits10 - 1) << num->value;
ss << std::fixed << std::setprecision(std::numeric_limits<double>::digits10 - 1) << object.asNumber();
std::string str = ss.str();
str.erase(str.find_last_not_of('0') + 1, std::string::npos);
if (str.back() == '.') {
@ -512,21 +529,21 @@ std::string Interpreter::stringify(sptr(Object) object) {
return str;
}
}
else if(auto string = std::dynamic_pointer_cast<String>(object))
else if(object.isString())
{
return string->value;
return object.asString();
}
else if(auto Bool = std::dynamic_pointer_cast<Boolean>(object))
else if(object.isBoolean())
{
return Bool->value == 1 ? "true" : "false";
return object.asBoolean() == 1 ? "true" : "false";
}
else if(auto func = std::dynamic_pointer_cast<Function>(object))
else if(object.isFunction())
{
return "<function " + func->name + ">";
return "<function " + object.asFunction()->name + ">";
}
else if(auto builtinFunc = std::dynamic_pointer_cast<BuiltinFunction>(object))
else if(object.isBuiltinFunction())
{
return "<builtin_function " + builtinFunc->name + ">";
return "<builtin_function " + object.asBuiltinFunction()->name + ">";
}
throw std::runtime_error("Could not convert object to string");

View File

@ -108,12 +108,12 @@ sptr(Expr) Parser::unary()
sptr(Expr) Parser::primary()
{
if(match({FALSE})) return msptr(LiteralExpr)("false", false, false);
if(match({TRUE})) return msptr(LiteralExpr)("true", false, false);
if(match({NONE})) return msptr(LiteralExpr)("none", false, true);
if(match({FALSE})) return msptr(LiteralExpr)("false", false, false, true);
if(match({TRUE})) return msptr(LiteralExpr)("true", false, false, true);
if(match({NONE})) return msptr(LiteralExpr)("none", false, true, false);
if(match({NUMBER})) return msptr(LiteralExpr)(previous().lexeme, true, false);
if(match({STRING})) return msptr(LiteralExpr)(previous().lexeme, false, false);
if(match({NUMBER})) return msptr(LiteralExpr)(previous().lexeme, true, false, false);
if(match({STRING})) return msptr(LiteralExpr)(previous().lexeme, false, false, false);
if(match( {IDENTIFIER})) {
if (check(OPEN_PAREN)) {
@ -168,7 +168,7 @@ sptr(Stmt) Parser::varDeclaration()
{
Token name = consume(IDENTIFIER, "Expected variable name.");
sptr(Expr) initializer = msptr(LiteralExpr)("none", false, true);
sptr(Expr) initializer = msptr(LiteralExpr)("none", false, true, false);
if(match({EQUAL}))
{
initializer = expression();
@ -199,16 +199,33 @@ sptr(Stmt) Parser::functionDeclaration()
sptr(Stmt) Parser::statement()
{
if(match({RETURN})) return returnStatement();
if(match({IF})) return ifStatement();
if(match({OPEN_BRACE})) return msptr(BlockStmt)(block());
return expressionStatement();
}
sptr(Stmt) Parser::ifStatement()
{
consume(OPEN_PAREN, "Expected '(' after 'if'.");
sptr(Expr) condition = expression();
consume(CLOSE_PAREN, "Expected ')' after if condition.");
sptr(Stmt) thenBranch = statement();
sptr(Stmt) elseBranch = nullptr;
if (match({ELSE})) {
elseBranch = statement();
}
return msptr(IfStmt)(condition, thenBranch, elseBranch);
}
sptr(Stmt) Parser::returnStatement()
{
Token keyword = previous();
sptr(Expr) value = msptr(LiteralExpr)("none", false, true);
sptr(Expr) value = msptr(LiteralExpr)("none", false, true, false);
if (!check(SEMICOLON)) {
value = expression();

View File

@ -2,59 +2,73 @@
#include "../headers/Interpreter.h"
#include <chrono>
void StdLib::addToEnvironment(sptr(Environment) env, Interpreter* interpreter) {
void StdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter) {
// Create a built-in toString function
auto toStringFunc = std::make_shared<BuiltinFunction>("toString",
[interpreter](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[&interpreter](std::vector<Value> args) -> Value {
if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
return std::make_shared<String>(interpreter->stringify(args[0]));
return Value(interpreter.stringify(args[0]));
});
env->define("toString", toStringFunc);
env->define("toString", Value(toStringFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toStringFunc);
// Create a built-in print function
auto printFunc = std::make_shared<BuiltinFunction>("print",
[interpreter](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[&interpreter](std::vector<Value> args) -> Value {
if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
// Use the interpreter's stringify function
std::cout << interpreter->stringify(args[0]) << std::endl;
return std::make_shared<None>();
std::cout << interpreter.stringify(args[0]) << std::endl;
return NONE_VALUE;
});
env->define("print", printFunc);
env->define("print", Value(printFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(printFunc);
// Create a built-in assert function
auto assertFunc = std::make_shared<BuiltinFunction>("assert",
[interpreter](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[](std::vector<Value> args) -> Value {
if (args.size() != 1 && args.size() != 2) {
throw std::runtime_error("Expected 1 or 2 arguments but got " + std::to_string(args.size()) + ".");
}
// Check if the argument is a boolean and is true
if (auto boolObj = std::dynamic_pointer_cast<Boolean>(args[0])) {
if (!boolObj->value) {
// Simple truthy check without calling interpreter.isTruthy
bool isTruthy = false;
if (args[0].isBoolean()) {
isTruthy = args[0].asBoolean();
} else if (args[0].isNone()) {
isTruthy = false;
} else {
isTruthy = true; // Numbers, strings, functions are truthy
}
if (!isTruthy) {
std::string message = "Assertion failed: condition is false";
if (args.size() == 2) {
if (auto strObj = std::dynamic_pointer_cast<String>(args[1])) {
message += " - " + strObj->value;
if (args[1].isString()) {
message += " - " + std::string(args[1].asString());
}
}
throw std::runtime_error(message);
}
} else {
throw std::runtime_error("Assertion failed: expected boolean condition, got " + interpreter->stringify(args[0]));
}
return std::make_shared<None>();
return NONE_VALUE;
});
env->define("assert", assertFunc);
env->define("assert", Value(assertFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(assertFunc);
// Create a built-in time function (returns microseconds since Unix epoch)
auto timeFunc = std::make_shared<BuiltinFunction>("time",
[](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[](std::vector<Value> args) -> Value {
if (args.size() != 0) {
throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + ".");
}
@ -63,71 +77,79 @@ void StdLib::addToEnvironment(sptr(Environment) env, Interpreter* interpreter) {
auto duration = now.time_since_epoch();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
return std::make_shared<Number>(microseconds);
return Value(static_cast<double>(microseconds));
});
env->define("time", timeFunc);
env->define("time", Value(timeFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(timeFunc);
// Create a built-in input function
auto inputFunc = std::make_shared<BuiltinFunction>("input",
[interpreter](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[&interpreter](std::vector<Value> args) -> Value {
if (args.size() > 1) {
throw std::runtime_error("Expected 0 or 1 arguments but got " + std::to_string(args.size()) + ".");
}
// Optional prompt
if (args.size() == 1) {
std::cout << interpreter->stringify(args[0]);
std::cout << interpreter.stringify(args[0]);
}
// Get user input
std::string userInput;
std::getline(std::cin, userInput);
return std::make_shared<String>(userInput);
return Value(userInput);
});
env->define("input", inputFunc);
env->define("input", Value(inputFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(inputFunc);
// Create a built-in type function
auto typeFunc = std::make_shared<BuiltinFunction>("type",
[](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[](std::vector<Value> args) -> Value {
if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
std::string typeName;
if (std::dynamic_pointer_cast<Number>(args[0])) {
if (args[0].isNumber()) {
typeName = "number";
} else if (std::dynamic_pointer_cast<String>(args[0])) {
} else if (args[0].isString()) {
typeName = "string";
} else if (std::dynamic_pointer_cast<Boolean>(args[0])) {
} else if (args[0].isBoolean()) {
typeName = "boolean";
} else if (std::dynamic_pointer_cast<None>(args[0])) {
} else if (args[0].isNone()) {
typeName = "none";
} else if (std::dynamic_pointer_cast<Function>(args[0])) {
} else if (args[0].isFunction()) {
typeName = "function";
} else if (std::dynamic_pointer_cast<BuiltinFunction>(args[0])) {
} else if (args[0].isBuiltinFunction()) {
typeName = "builtin_function";
} else {
typeName = "unknown";
}
return std::make_shared<String>(typeName);
return Value(typeName);
});
env->define("type", typeFunc);
env->define("type", Value(typeFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(typeFunc);
// Create a built-in toNumber function for string-to-number conversion
auto toNumberFunc = std::make_shared<BuiltinFunction>("toNumber",
[](std::vector<std::shared_ptr<Object>> args) -> std::shared_ptr<Object> {
[](std::vector<Value> args) -> Value {
if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
auto strObj = std::dynamic_pointer_cast<String>(args[0]);
if (!strObj) {
if (!args[0].isString()) {
throw std::runtime_error("toNumber() expects a string argument.");
}
std::string str = strObj->value;
std::string str = args[0].asString();
// Remove leading/trailing whitespace
str.erase(0, str.find_first_not_of(" \t\n\r"));
@ -139,12 +161,15 @@ void StdLib::addToEnvironment(sptr(Environment) env, Interpreter* interpreter) {
try {
double value = std::stod(str);
return std::make_shared<Number>(value);
return Value(value);
} catch (const std::invalid_argument&) {
throw std::runtime_error("Cannot convert '" + str + "' to number.");
} catch (const std::out_of_range&) {
throw std::runtime_error("Number '" + str + "' is out of range.");
}
});
env->define("toNumber", toNumberFunc);
env->define("toNumber", Value(toNumberFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toNumberFunc);
}

8
source/Value.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "../headers/Value.h"
// Global constants for common values (no heap allocation)
const Value NONE_VALUE = Value();
const Value TRUE_VALUE = Value(true);
const Value FALSE_VALUE = Value(false);
const Value ZERO_VALUE = Value(0.0);
const Value ONE_VALUE = Value(1.0);

View File

@ -2,7 +2,6 @@
#include "../headers/bob.h"
#include "../headers/Parser.h"
#include "../headers/ASTPrinter.h"
using namespace std;
void Bob::runFile(const string& path)
@ -54,24 +53,20 @@ void Bob::run(string source)
{
try {
vector<Token> tokens = lexer.Tokenize(std::move(source));
// for(Token t : tokens){
// cout << "{type: " << enum_mapping[t.type] << ", value: " << t.lexeme << "}" << endl;
// }
Parser p(tokens);
vector<sptr(Stmt)> statements = p.parse();
interpreter->interpret(statements);
//cout << "=========================" << endl;
}
catch(std::exception &e)
{
cout << "ERROR OCCURRED: " << e.what() << endl;
return;
}
catch(...)
{
cout << "UNKNOWN ERROR OCCURRED" << endl;
return;
}

View File

@ -14,19 +14,24 @@ print("\n--- Test 1: Basic Data Types ---");
// String literals
var stringVar = "Hello, Bob!";
assert(stringVar == "Hello, Bob!", "String variable assignment");
assert(toString(stringVar) == "Hello, Bob!", "String variable assignment");
print(" ✓ String: " + toString(stringVar));
// Numbers (integers and floats)
var intVar = 42;
var floatVar = 3.14159;
assert(intVar == 42, "Integer variable assignment");
assert(floatVar == 3.14159, "Float variable assignment");
assert(toString(intVar) == "42", "Integer variable assignment");
assert(toString(floatVar) == "3.14159", "Float variable assignment");
print(" ✓ Integer: " + toString(intVar));
print(" ✓ Float: " + toString(floatVar));
// Booleans
var boolTrue = true;
var boolFalse = false;
assert(boolTrue == true, "Boolean true assignment");
assert(boolFalse == false, "Boolean false assignment");
print(" ✓ Boolean true: " + toString(boolTrue));
print(" ✓ Boolean false: " + toString(boolFalse));
print("✓ Basic data types working");
@ -36,18 +41,39 @@ print("✓ Basic data types working");
print("\n--- Test 2: Arithmetic Operations ---");
// Basic arithmetic
assert(2 + 3 == 5, "Addition");
assert(10 - 4 == 6, "Subtraction");
assert(6 * 7 == 42, "Multiplication");
assert(20 / 4 == 5, "Division");
var addResult = 2 + 3;
assert(toString(addResult) == "5", "Addition");
print(" ✓ Addition: 2 + 3 = " + toString(addResult));
var subResult = 10 - 4;
assert(toString(subResult) == "6", "Subtraction");
print(" ✓ Subtraction: 10 - 4 = " + toString(subResult));
var mulResult = 6 * 7;
assert(toString(mulResult) == "42", "Multiplication");
print(" ✓ Multiplication: 6 * 7 = " + toString(mulResult));
var divResult = 20 / 4;
assert(toString(divResult) == "5", "Division");
print(" ✓ Division: 20 / 4 = " + toString(divResult));
// Negative numbers
assert(-42 == -42, "Negative numbers");
assert(5 - 10 == -5, "Negative result");
var negResult = -42;
assert(toString(negResult) == "-42", "Negative numbers");
print(" ✓ Negative: " + toString(negResult));
var negCalc = 5 - 10;
assert(toString(negCalc) == "-5", "Negative result");
print(" ✓ Negative calculation: 5 - 10 = " + toString(negCalc));
// Order of operations
assert(2 + 3 * 4 == 14, "Order of operations (multiplication first)");
assert((2 + 3) * 4 == 20, "Parentheses override order of operations");
var orderResult1 = 2 + 3 * 4;
assert(toString(orderResult1) == "14", "Order of operations (multiplication first)");
print(" ✓ Order of operations: 2 + 3 * 4 = " + toString(orderResult1));
var orderResult2 = (2 + 3) * 4;
assert(toString(orderResult2) == "20", "Parentheses override order of operations");
print(" ✓ Parentheses: (2 + 3) * 4 = " + toString(orderResult2));
print("✓ Arithmetic operations working");
@ -57,10 +83,15 @@ print("✓ Arithmetic operations working");
print("\n--- Test 3: String Operations ---");
// String concatenation
assert("Hello" + " " + "World" == "Hello World", "String concatenation");
var concatResult = "Hello" + " " + "World";
assert(toString(concatResult) == "Hello World", "String concatenation");
print(" ✓ String concatenation: " + toString(concatResult));
var firstName = "Bob";
var lastName = "Lucero";
assert(firstName + " " + lastName == "Bob Lucero", "Variable string concatenation");
var nameResult = firstName + " " + lastName;
assert(toString(nameResult) == "Bob Lucero", "Variable string concatenation");
print(" ✓ Variable concatenation: " + toString(nameResult));
print("✓ String operations working");
@ -70,25 +101,60 @@ print("✓ String operations working");
print("\n--- Test 3.5: String + Number Concatenation ---");
// Test string + number (automatic conversion)
assert("String + Number: " + 42 == "String + Number: 42", "String + Number");
assert("String + Float: " + 3.14 == "String + Float: 3.14", "String + Float");
assert("Zero: " + 0 == "Zero: 0", "Zero formatting");
assert("Negative: " + -10 == "Negative: -10", "Negative formatting");
var strNumResult = "String + Number: " + 42;
assert(toString(strNumResult) == "String + Number: 42", "String + Number");
print(" ✓ String + Number: " + toString(strNumResult));
var strFloatResult = "String + Float: " + 3.14;
assert(toString(strFloatResult) == "String + Float: 3.14", "String + Float");
print(" ✓ String + Float: " + toString(strFloatResult));
var zeroResult = "Zero: " + 0;
assert(toString(zeroResult) == "Zero: 0", "Zero formatting");
print(" ✓ Zero formatting: " + toString(zeroResult));
var negResult = "Negative: " + -10;
assert(toString(negResult) == "Negative: -10", "Negative formatting");
print(" ✓ Negative formatting: " + toString(negResult));
// Test number + string (automatic conversion)
assert(5 + " times" == "5 times", "Number + String");
assert(3.14 + " is pi" == "3.14 is pi", "Float + String");
assert(0 + " items" == "0 items", "Zero + String");
var numStrResult = 5 + " times";
assert(toString(numStrResult) == "5 times", "Number + String");
print(" ✓ Number + String: " + toString(numStrResult));
var floatStrResult = 3.14 + " is pi";
assert(toString(floatStrResult) == "3.14 is pi", "Float + String");
print(" ✓ Float + String: " + toString(floatStrResult));
var zeroStrResult = 0 + " items";
assert(toString(zeroStrResult) == "0 items", "Zero + String");
print(" ✓ Zero + String: " + toString(zeroStrResult));
// Test significant digits formatting (no trailing zeros)
assert("Trailing zeros: " + 2.0 == "Trailing zeros: 2", "Trailing zeros removed");
assert("Pi: " + 3.14 == "Pi: 3.14", "Float formatting");
assert("E: " + 2.718 == "E: 2.718", "Float formatting");
assert("Simple: " + 1.5 == "Simple: 1.5", "Float formatting");
var trailingResult = "Trailing zeros: " + 2.0;
assert(toString(trailingResult) == "Trailing zeros: 2", "Trailing zeros removed");
print(" ✓ Trailing zeros: " + toString(trailingResult));
var piResult = "Pi: " + 3.14;
assert(toString(piResult) == "Pi: 3.14", "Float formatting");
print(" ✓ Pi formatting: " + toString(piResult));
var eResult = "E: " + 2.718;
assert(toString(eResult) == "E: 2.718", "Float formatting");
print(" ✓ E formatting: " + toString(eResult));
var simpleResult = "Simple: " + 1.5;
assert(toString(simpleResult) == "Simple: 1.5", "Float formatting");
print(" ✓ Simple formatting: " + toString(simpleResult));
// Test string multiplication
assert("hello" * 3 == "hellohellohello", "String multiplication");
assert(3 * "hello" == "hellohellohello", "Number * string multiplication");
var strMulResult = "hello" * 3;
assert(toString(strMulResult) == "hellohellohello", "String multiplication");
print(" ✓ String multiplication: " + toString(strMulResult));
var numStrMulResult = 3 * "hello";
assert(toString(numStrMulResult) == "hellohellohello", "Number * string multiplication");
print(" ✓ Number * string: " + toString(numStrMulResult));
print("✓ String + Number concatenation working");
@ -725,7 +791,7 @@ var start_time = time();
var end_time = time();
var duration = end_time - start_time;
assert(start_time > 0, "Start time should be positive");
assert(end_time > start_time, "End time should be greater than start time");
assert(end_time >= start_time, "End time should be >= start time");
assert(duration >= 0, "Duration should be non-negative");
print("✓ Time function working");

12
test_fib.bob Normal file
View File

@ -0,0 +1,12 @@
func fib(n) {
if (n <= 1) {
return n;
}
//print("Current operation: " + (n - 1) + ":" + (n-2));
return fib(n - 1) + fib(n - 2);
}
print("Fibonacci test:");
var fib_result = fib(30);
print("Result: " + fib_result);