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 #pragma once
#include <unordered_map> #include <unordered_map>
#include "TypeWrapper.h" #include <string>
#include "helperFunctions/ShortHands.h" #include <memory>
#include "Value.h"
#include "Lexer.h" #include "Lexer.h"
class Environment class Environment {
{
public: public:
Environment() : parent(nullptr) {}
Environment(std::shared_ptr<Environment> parent_env) : parent(parent_env) {}
void define(std::string name, sptr(Object) value); // Optimized define with inline
inline void define(const std::string& name, const Value& value) {
void assign(Token name, sptr(Object) value); variables[name] = value;
std::shared_ptr<Object> get(Token name);
sptr(Environment) enclosing;
Environment(){
enclosing = nullptr;
} }
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: 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 #pragma once
#include <iostream> #include <iostream>
#include <memory>
#include "Lexer.h" #include "Lexer.h"
#include "helperFunctions/ShortHands.h" #include "helperFunctions/ShortHands.h"
#include "TypeWrapper.h" #include "TypeWrapper.h"
#include "Value.h"
struct AssignExpr; struct AssignExpr;
struct BinaryExpr; struct BinaryExpr;
@ -16,111 +18,116 @@ struct UnaryExpr;
struct VarExpr; struct VarExpr;
struct CallExpr; struct CallExpr;
// AST nodes use shared_ptr for proper memory management
struct ExprVisitor struct ExprVisitor
{ {
virtual sptr(Object) visitAssignExpr(sptr(AssignExpr) expr) = 0; virtual Value visitAssignExpr(const std::shared_ptr<AssignExpr>& expr) = 0;
virtual sptr(Object) visitBinaryExpr(sptr(BinaryExpr) expr) = 0; virtual Value visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expr) = 0;
virtual sptr(Object) visitGroupingExpr(sptr(GroupingExpr) expr) = 0; virtual Value visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expr) = 0;
virtual sptr(Object) visitLiteralExpr(sptr(LiteralExpr) expr) = 0; virtual Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) = 0;
virtual sptr(Object) visitUnaryExpr(sptr(UnaryExpr) expr) = 0; virtual Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expr) = 0;
virtual sptr(Object) visitVariableExpr(sptr(VarExpr) expr) = 0; virtual Value visitVariableExpr(const std::shared_ptr<VarExpr>& expr) = 0;
virtual sptr(Object) visitCallExpr(sptr(CallExpr) expr) = 0; virtual Value visitCallExpr(const std::shared_ptr<CallExpr>& expr) = 0;
}; };
struct Expr{ struct Expr : public std::enable_shared_from_this<Expr> {
virtual sptr(Object) accept(ExprVisitor* visitor) = 0; virtual Value accept(ExprVisitor* visitor) = 0;
virtual ~Expr() = default; virtual ~Expr() = default;
}; };
struct AssignExpr : Expr, public std::enable_shared_from_this<AssignExpr> struct AssignExpr : Expr
{ {
const Token name; const Token name;
const sptr(Expr) value; std::shared_ptr<Expr> value;
AssignExpr(Token name, sptr(Expr) value) : name(name), value(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 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{ Value accept(ExprVisitor* visitor) override{
return visitor->visitBinaryExpr(shared_from_this() ); 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{ Value accept(ExprVisitor* visitor) override{
return visitor->visitGroupingExpr(shared_from_this()); 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; std::string value;
const bool isNumber; bool isNumber;
const bool isNull; bool isNull;
LiteralExpr(std::string value, bool isNumber, bool isNull) : value(value), isNumber(isNumber), isNull(isNull) bool isBoolean;
{
} LiteralExpr(const std::string& value, bool isNumber, bool isNull, bool isBoolean)
sptr(Object) accept(ExprVisitor* visitor) override{ : value(value), isNumber(isNumber), isNull(isNull), isBoolean(isBoolean) {}
return visitor->visitLiteralExpr(shared_from_this()); 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 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{ Value accept(ExprVisitor* visitor) override{
return visitor->visitUnaryExpr(shared_from_this()); 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; const Token name;
explicit VarExpr(Token name) : name(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 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) CallExpr(std::shared_ptr<Expr> callee, Token paren, std::vector<std::shared_ptr<Expr>> arguments)
: callee(callee), paren(paren), arguments(arguments) {} : callee(callee), paren(paren), arguments(arguments)
sptr(Object) accept(ExprVisitor* visitor) override
{ {
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 "helperFunctions/ShortHands.h"
#include "TypeWrapper.h" #include "TypeWrapper.h"
#include "Environment.h" #include "Environment.h"
#include "Value.h"
#include "StdLib.h" #include "StdLib.h"
#include <vector>
#include <memory>
#include <unordered_map>
#include <stack>
class Return : public std::exception { class Return : public std::exception {
public: public:
sptr(Object) value; Value value;
Return(sptr(Object) value) : value(value) {} Return(Value value) : value(value) {}
const char* what() const noexcept override { const char* what() const noexcept override {
return "Return"; return "Return";
} }
}; };
class Interpreter : ExprVisitor, StmtVisitor class Interpreter : public ExprVisitor, public StmtVisitor {
{
public: public:
sptr(Object) visitBinaryExpr(sptr(BinaryExpr) expression) override; Value visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expression) override;
sptr(Object) visitGroupingExpr(sptr(GroupingExpr) expression) override; Value visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expression) override;
sptr(Object) visitLiteralExpr(sptr(LiteralExpr) expression) override; Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expression) override;
sptr(Object) visitUnaryExpr(sptr(UnaryExpr) expression) override; Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expression) override;
sptr(Object) visitVariableExpr(sptr(VarExpr) expression) override; Value visitVariableExpr(const std::shared_ptr<VarExpr>& expression) override;
sptr(Object) visitAssignExpr(sptr(AssignExpr) expression) override; Value visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) override;
sptr(Object) visitCallExpr(sptr(CallExpr) expression) override; Value visitCallExpr(const std::shared_ptr<CallExpr>& expression) override;
void visitBlockStmt(sptr(BlockStmt) statement) override; void visitBlockStmt(const std::shared_ptr<BlockStmt>& statement) override;
void visitExpressionStmt(sptr(ExpressionStmt) 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 interpret(std::vector<std::shared_ptr<Stmt>> statements);
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);
explicit Interpreter(bool IsInteractive) : IsInteractive(IsInteractive){ 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 // Add standard library functions
addStdLibFunctions(); addStdLibFunctions();
@ -48,18 +57,32 @@ public:
virtual ~Interpreter() = default; virtual ~Interpreter() = default;
private: private:
std::shared_ptr<Environment> environment;
sptr(Environment) environment;
bool IsInteractive; bool IsInteractive;
std::vector<std::shared_ptr<BuiltinFunction>> builtinFunctions;
std::vector<std::shared_ptr<Function>> functions;
sptr(Object) evaluate(sptr(Expr) expr); // Environment pool for fast function calls
bool isTruthy(sptr(Object) object); std::stack<std::shared_ptr<Environment>> envPool;
bool isEqual(sptr(Object) a, sptr(Object) b); 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); bool isWholeNumer(double num);
void execute(sptr(Stmt) statement); void execute(const std::shared_ptr<Stmt>& statement);
void executeBlock(std::vector<sptr(Stmt)> statements, sptr(Environment) env); void executeBlock(std::vector<std::shared_ptr<Stmt>> statements, std::shared_ptr<Environment> env);
void addStdLibFunctions(); void addStdLibFunctions();
public: 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 struct StmtVisitor
{ {
virtual void visitBlockStmt(sptr(BlockStmt) stmt) = 0; virtual void visitBlockStmt(const std::shared_ptr<BlockStmt>& stmt) = 0;
virtual void visitExpressionStmt(sptr(ExpressionStmt) stmt) = 0; virtual void visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& stmt) = 0;
virtual void visitVarStmt(sptr(VarStmt) stmt) = 0; virtual void visitVarStmt(const std::shared_ptr<VarStmt>& stmt) = 0;
virtual void visitFunctionStmt(sptr(FunctionStmt) stmt) = 0; virtual void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stmt) = 0;
virtual void visitReturnStmt(sptr(ReturnStmt) stmt) = 0; virtual void visitReturnStmt(const std::shared_ptr<ReturnStmt>& stmt) = 0;
virtual void visitIfStmt(sptr(IfStmt) 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 void accept(StmtVisitor* visitor) = 0;
virtual ~Stmt(){}; virtual ~Stmt(){};
}; };
struct BlockStmt : Stmt, public std::enable_shared_from_this<BlockStmt> struct BlockStmt : Stmt
{ {
const std::vector<sptr(Stmt)> statements; std::vector<std::shared_ptr<Stmt>> statements;
explicit BlockStmt(std::vector<sptr(Stmt)> statements) : statements(statements) explicit BlockStmt(std::vector<std::shared_ptr<Stmt>> statements) : statements(statements)
{ {
} }
void accept(StmtVisitor* visitor) override 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; std::shared_ptr<Expr> expression;
explicit ExpressionStmt(sptr(Expr) expression) : expression(expression) explicit ExpressionStmt(std::shared_ptr<Expr> expression) : expression(expression)
{ {
} }
void accept(StmtVisitor* visitor) override 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; Token name;
const sptr(Expr) initializer; std::shared_ptr<Expr> initializer;
VarStmt(Token name, sptr(Expr) initializer) : name(name), initializer(initializer) VarStmt(Token name, std::shared_ptr<Expr> initializer) : name(name), initializer(initializer)
{ {
} }
void accept(StmtVisitor* visitor) override 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 Token name;
const std::vector<Token> params; 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) {} : name(name), params(params), body(body) {}
void accept(StmtVisitor* visitor) override 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 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 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; std::shared_ptr<Expr> condition;
const sptr(Stmt) thenBranch; std::shared_ptr<Stmt> thenBranch;
const sptr(Stmt) elseBranch; 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) {} : condition(condition), thenBranch(thenBranch), elseBranch(elseBranch) {}
void accept(StmtVisitor* visitor) override 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 #pragma once
#include "TypeWrapper.h" #include "Value.h"
#include "Environment.h" #include "Environment.h"
#include <functional> #include <memory>
class Interpreter; // Forward declaration class Interpreter;
class StdLib { class StdLib {
public: 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 <vector>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include "Value.h"
// Forward declarations
struct Stmt;
struct Environment;
struct Object struct Object
{ {
virtual ~Object(){}; virtual ~Object(){};
@ -39,21 +45,21 @@ struct Function : public Object
{ {
const std::string name; const std::string name;
const std::vector<std::string> params; const std::vector<std::string> params;
const std::vector<std::shared_ptr<void>> body; // Will cast to Stmt* when needed const std::vector<std::shared_ptr<Stmt>> body;
const std::shared_ptr<void> closure; // Will cast to Environment* when needed const std::shared_ptr<Environment> closure;
Function(std::string name, std::vector<std::string> params, Function(std::string name, std::vector<std::string> params,
std::vector<std::shared_ptr<void>> body, std::vector<std::shared_ptr<Stmt>> body,
std::shared_ptr<void> closure) std::shared_ptr<Environment> closure)
: name(name), params(params), body(body), closure(closure) {} : name(name), params(params), body(body), closure(closure) {}
}; };
struct BuiltinFunction : public Object struct BuiltinFunction : public Object
{ {
const std::string name; 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) {} : 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 "../headers/helperFunctions/HelperFunctions.h"
#include <unordered_map> #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) { Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
if(expr->isNull) return msptr(None)(); if(expr->isNull) return NONE_VALUE;
if(expr->isNumber){ if(expr->isNumber){
double num; double num;
if(expr->value[1] == 'b') if(expr->value[1] == 'b')
@ -23,31 +29,32 @@ sptr(Object) Interpreter::visitLiteralExpr(sptr(LiteralExpr) expr) {
} }
else else
{ {
// Use stod for all numbers to handle both integers and decimals correctly
num = std::stod(expr->value); num = std::stod(expr->value);
} }
return std::make_shared<Number>(num); return Value(num);
} }
if(expr->value == "true") return msptr(Boolean)(true); if(expr->isBoolean) {
if(expr->value == "false") return msptr(Boolean)(false); if(expr->value == "true") return TRUE_VALUE;
return msptr(String)(expr->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); 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(expression->oper.type == MINUS)
{ {
if(std::dynamic_pointer_cast<Number>(right)) if(right.isNumber())
{ {
double value = std::dynamic_pointer_cast<Number>(right)->value; double value = right.asNumber();
return msptr(Number)(-value); return Value(-value);
} }
else else
{ {
@ -58,15 +65,15 @@ sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression)
if(expression->oper.type == BANG) if(expression->oper.type == BANG)
{ {
return msptr(Boolean)(!isTruthy(right)); return Value(!isTruthy(right));
} }
if(expression->oper.type == BIN_NOT) 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; double value = right.asNumber();
return msptr(Number)((~(long)value)); return Value(static_cast<double>(~(static_cast<long>(value))));
} }
else 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); Value left = evaluate(expression->left);
sptr(Object) right = evaluate(expression->right); Value right = evaluate(expression->right);
switch (expression->oper.type) { switch (expression->oper.type) {
case BANG_EQUAL: case BANG_EQUAL:
return msptr(Boolean)(!isEqual(left, right)); return Value(!isEqual(left, right));
case DOUBLE_EQUAL: case DOUBLE_EQUAL:
return msptr(Boolean)(isEqual(left, right)); return Value(isEqual(left, right));
default: 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 left_double = left.asNumber();
double right_double = std::dynamic_pointer_cast<Number>(right)->value; double right_double = right.asNumber();
switch (expression->oper.type) { switch (expression->oper.type) {
case GREATER: case GREATER:
return msptr(Boolean)(left_double > right_double); return Value(left_double > right_double);
case GREATER_EQUAL: case GREATER_EQUAL:
return msptr(Boolean)(left_double >= right_double); return Value(left_double >= right_double);
case LESS: case LESS:
return msptr(Boolean)(left_double < right_double); return Value(left_double < right_double);
case LESS_EQUAL: case LESS_EQUAL:
return msptr(Boolean)(left_double <= right_double); return Value(left_double <= right_double);
case MINUS: case MINUS:
return std::make_shared<Number>(left_double - right_double); return Value(left_double - right_double);
case PLUS: case PLUS:
return std::make_shared<Number>(left_double + right_double); return Value(left_double + right_double);
case SLASH: case SLASH:
if(right_double == 0) throw std::runtime_error("DivisionByZeroError: Cannot divide by 0"); 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: case STAR:
return std::make_shared<Number>(left_double * right_double); return Value(left_double * right_double);
case PERCENT: case PERCENT:
return msptr(Number)(fmod(left_double, right_double)); return Value(fmod(left_double, right_double));
default: 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) { switch (expression->oper.type) {
case PLUS: case PLUS: {
std::string left_string = std::dynamic_pointer_cast<String>(left)->value; std::string left_string = left.asString();
std::string right_string = std::dynamic_pointer_cast<String>(right)->value; std::string right_string = right.asString();
return msptr(String)(left_string + right_string); return Value(left_string + right_string);
}
} }
throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on two strings"); 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; std::string left_string = left.asString();
double right_number = std::dynamic_pointer_cast<Number>(right)->value; double right_number = right.asNumber();
switch (expression->oper.type) { switch (expression->oper.type) {
case PLUS: { case PLUS: {
@ -156,9 +164,9 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
if (str.back() == '.') { if (str.back() == '.') {
str.pop_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: case STAR:
if(isWholeNumer(right_number)) if(isWholeNumer(right_number))
@ -167,7 +175,7 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
for (int i = 0; i < (int)right_number; ++i) { for (int i = 0; i < (int)right_number; ++i) {
s += left_string; s += left_string;
} }
return msptr(String)(s); return Value(s);
} }
else 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"); 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; double left_number = left.asNumber();
std::string right_string = std::dynamic_pointer_cast<String>(right)->value; std::string right_string = right.asString();
switch (expression->oper.type) { switch (expression->oper.type) {
case PLUS: { case PLUS: {
@ -200,9 +208,9 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
if (str.back() == '.') { if (str.back() == '.') {
str.pop_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: case STAR:
if(isWholeNumer(left_number)) if(isWholeNumer(left_number))
@ -211,7 +219,7 @@ sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression)
for (int i = 0; i < (int)left_number; ++i) { for (int i = 0; i < (int)left_number; ++i) {
s += right_string; s += right_string;
} }
return msptr(String)(s); return Value(s);
} }
else 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"); 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; bool left_bool = left.asBoolean();
std::string right_string = std::dynamic_pointer_cast<String>(right)->value; std::string right_string = right.asString();
switch (expression->oper.type) { switch (expression->oper.type) {
case PLUS: { case PLUS: {
std::string bool_str = left_bool ? "true" : "false"; 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"); 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; std::string left_string = left.asString();
bool right_bool = std::dynamic_pointer_cast<Boolean>(right)->value; bool right_bool = right.asBoolean();
switch (expression->oper.type) { switch (expression->oper.type) {
case PLUS: { case PLUS: {
std::string bool_str = right_bool ? "true" : "false"; 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"); 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 else
{ {
throw std::runtime_error("Operands must be of same type when using: " + expression->oper.lexeme); 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); return environment->get(expression->name);
} }
void Interpreter::addStdLibFunctions() { void Interpreter::addStdLibFunctions() {
// Add standard library functions to the environment // Add standard library functions to the environment
StdLib::addToEnvironment(environment, this); StdLib::addToEnvironment(environment, *this);
} }
sptr(Object) Interpreter::visitAssignExpr(sptr(AssignExpr) expression) { void Interpreter::addBuiltinFunction(std::shared_ptr<BuiltinFunction> func) {
sptr(Object) value = evaluate(expression->value); builtinFunctions.push_back(func);
}
Value Interpreter::visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) {
Value value = evaluate(expression->value);
environment->assign(expression->name, value); environment->assign(expression->name, value);
return value; return value;
} }
sptr(Object) Interpreter::visitCallExpr(sptr(CallExpr) expression) { Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
sptr(Object) callee = evaluate(expression->callee); Value callee = evaluate(expression->callee);
std::vector<sptr(Object)> arguments; std::vector<Value> arguments;
for (sptr(Expr) argument : expression->arguments) { for (const std::shared_ptr<Expr>& argument : expression->arguments) {
arguments.push_back(evaluate(argument)); arguments.push_back(evaluate(argument));
} }
if (auto builtin = std::dynamic_pointer_cast<BuiltinFunction>(callee)) { if (callee.isBuiltinFunction()) {
return builtin->func(arguments); // 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()) { if (arguments.size() != function->params.size()) {
throw std::runtime_error("Expected " + std::to_string(function->params.size()) + throw std::runtime_error("Expected " + std::to_string(function->params.size()) +
" arguments but got " + std::to_string(arguments.size()) + "."); " arguments but got " + std::to_string(arguments.size()) + ".");
} }
// Create new environment for function execution auto previousEnv = environment;
sptr(Environment) functionEnv = std::make_shared<Environment>(std::static_pointer_cast<Environment>(function->closure)); environment = std::make_shared<Environment>(function->closure);
// Bind parameters to arguments
for (size_t i = 0; i < function->params.size(); i++) { 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 Value returnValue = NONE_VALUE;
try { bool hasReturn = false;
// Convert void pointers back to statements
std::vector<sptr(Stmt)> bodyStatements; for (const auto& stmt : function->body) {
for (const auto& stmtPtr : function->body) { // Reset return context for each statement
bodyStatements.push_back(std::static_pointer_cast<Stmt>(stmtPtr)); 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."); throw std::runtime_error("Can only call functions and classes.");
} }
void Interpreter::visitBlockStmt(sptr(BlockStmt) statement) { void Interpreter::visitBlockStmt(const std::shared_ptr<BlockStmt>& statement) {
executeBlock(statement->statements, msptr(Environment)(environment)); auto newEnv = std::make_shared<Environment>(environment);
executeBlock(statement->statements, newEnv);
} }
void Interpreter::visitExpressionStmt(sptr(ExpressionStmt) statement) { void Interpreter::visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement) {
sptr(Object) value = evaluate(statement->expression); Value value = evaluate(statement->expression);
if(IsInteractive) if(IsInteractive)
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m" << std::endl; 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)(); Value value = NONE_VALUE;
if(!std::dynamic_pointer_cast<None>(statement->initializer)) if(statement->initializer != nullptr)
{ {
value = evaluate(statement->initializer); value = evaluate(statement->initializer);
} }
@ -339,7 +380,7 @@ void Interpreter::visitVarStmt(sptr(VarStmt) statement)
environment->define(statement->name.lexeme, value); 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 // Convert Token parameters to string parameters
std::vector<std::string> paramNames; std::vector<std::string> paramNames;
@ -347,30 +388,26 @@ void Interpreter::visitFunctionStmt(sptr(FunctionStmt) statement)
paramNames.push_back(param.lexeme); 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, auto function = msptr(Function)(statement->name.lexeme,
paramNames, paramNames,
bodyStatements, statement->body,
std::static_pointer_cast<void>(environment)); environment);
environment->define(statement->name.lexeme, function); 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)(); Value value = NONE_VALUE;
if (!std::dynamic_pointer_cast<None>(statement->value)) { if (statement->value != nullptr) {
value = evaluate(statement->value); 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))) { if (isTruthy(evaluate(statement->condition))) {
execute(statement->thenBranch); execute(statement->thenBranch);
@ -379,63 +416,43 @@ void Interpreter::visitIfStmt(sptr(IfStmt) statement)
} }
} }
void Interpreter::interpret(std::vector<sptr(Stmt)> statements) { void Interpreter::interpret(std::vector<std::shared_ptr<Stmt>> statements) {
for(const std::shared_ptr<Stmt>& s : statements)
for(sptr(Stmt) s : statements)
{ {
execute(s); 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);
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; this->environment = env;
try { for(const std::shared_ptr<Stmt>& s : statements)
for(sptr(Stmt) s : statements) {
{ execute(s);
execute(s);
}
} catch (Return& returnValue) {
this->environment = previous;
throw returnValue;
} }
this->environment = previous; this->environment = previous;
} }
sptr(Object) Interpreter::evaluate(sptr(Expr) expr) { Value Interpreter::evaluate(const std::shared_ptr<Expr>& expr) {
return expr->accept(this); 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; return false;
} }
@ -443,37 +460,37 @@ bool Interpreter::isTruthy(sptr(Object) object) {
return true; return true;
} }
bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) { bool Interpreter::isEqual(Value a, Value b) {
if(auto left = std::dynamic_pointer_cast<Number>(a)) 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; 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; 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; 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; return true;
} }
@ -484,15 +501,15 @@ bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) {
throw std::runtime_error("Invalid isEqual compariosn"); throw std::runtime_error("Invalid isEqual compariosn");
} }
std::string Interpreter::stringify(sptr(Object) object) { std::string Interpreter::stringify(Value object) {
if(std::dynamic_pointer_cast<None>(object)) if(object.isNone())
{ {
return "none"; return "none";
} }
else if(auto num = std::dynamic_pointer_cast<Number>(object)) else if(object.isNumber())
{ {
double integral = num->value; double integral = object.asNumber();
double fractional = std::modf(num->value, &integral); double fractional = std::modf(object.asNumber(), &integral);
std::stringstream ss; std::stringstream ss;
if(std::abs(fractional) < std::numeric_limits<double>::epsilon()) if(std::abs(fractional) < std::numeric_limits<double>::epsilon())
@ -502,7 +519,7 @@ std::string Interpreter::stringify(sptr(Object) object) {
} }
else 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(); std::string str = ss.str();
str.erase(str.find_last_not_of('0') + 1, std::string::npos); str.erase(str.find_last_not_of('0') + 1, std::string::npos);
if (str.back() == '.') { if (str.back() == '.') {
@ -512,21 +529,21 @@ std::string Interpreter::stringify(sptr(Object) object) {
return str; 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"); throw std::runtime_error("Could not convert object to string");

View File

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

View File

@ -2,59 +2,73 @@
#include "../headers/Interpreter.h" #include "../headers/Interpreter.h"
#include <chrono> #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 // Create a built-in toString function
auto toStringFunc = std::make_shared<BuiltinFunction>("toString", 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) { if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + "."); 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 // Create a built-in print function
auto printFunc = std::make_shared<BuiltinFunction>("print", 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) { if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + "."); throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
} }
// Use the interpreter's stringify function // Use the interpreter's stringify function
std::cout << interpreter->stringify(args[0]) << std::endl; std::cout << interpreter.stringify(args[0]) << std::endl;
return std::make_shared<None>(); 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 // Create a built-in assert function
auto assertFunc = std::make_shared<BuiltinFunction>("assert", 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) { if (args.size() != 1 && args.size() != 2) {
throw std::runtime_error("Expected 1 or 2 arguments but got " + std::to_string(args.size()) + "."); 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 // Simple truthy check without calling interpreter.isTruthy
if (auto boolObj = std::dynamic_pointer_cast<Boolean>(args[0])) { bool isTruthy = false;
if (!boolObj->value) { if (args[0].isBoolean()) {
std::string message = "Assertion failed: condition is false"; isTruthy = args[0].asBoolean();
if (args.size() == 2) { } else if (args[0].isNone()) {
if (auto strObj = std::dynamic_pointer_cast<String>(args[1])) { isTruthy = false;
message += " - " + strObj->value;
}
}
throw std::runtime_error(message);
}
} else { } else {
throw std::runtime_error("Assertion failed: expected boolean condition, got " + interpreter->stringify(args[0])); isTruthy = true; // Numbers, strings, functions are truthy
} }
return std::make_shared<None>(); if (!isTruthy) {
std::string message = "Assertion failed: condition is false";
if (args.size() == 2) {
if (args[1].isString()) {
message += " - " + std::string(args[1].asString());
}
}
throw std::runtime_error(message);
}
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) // Create a built-in time function (returns microseconds since Unix epoch)
auto timeFunc = std::make_shared<BuiltinFunction>("time", 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) { if (args.size() != 0) {
throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + "."); 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 duration = now.time_since_epoch();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration).count(); 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 // Create a built-in input function
auto inputFunc = std::make_shared<BuiltinFunction>("input", 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) { if (args.size() > 1) {
throw std::runtime_error("Expected 0 or 1 arguments but got " + std::to_string(args.size()) + "."); throw std::runtime_error("Expected 0 or 1 arguments but got " + std::to_string(args.size()) + ".");
} }
// Optional prompt // Optional prompt
if (args.size() == 1) { if (args.size() == 1) {
std::cout << interpreter->stringify(args[0]); std::cout << interpreter.stringify(args[0]);
} }
// Get user input // Get user input
std::string userInput; std::string userInput;
std::getline(std::cin, 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 // Create a built-in type function
auto typeFunc = std::make_shared<BuiltinFunction>("type", 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) { if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + "."); throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
} }
std::string typeName; std::string typeName;
if (std::dynamic_pointer_cast<Number>(args[0])) { if (args[0].isNumber()) {
typeName = "number"; typeName = "number";
} else if (std::dynamic_pointer_cast<String>(args[0])) { } else if (args[0].isString()) {
typeName = "string"; typeName = "string";
} else if (std::dynamic_pointer_cast<Boolean>(args[0])) { } else if (args[0].isBoolean()) {
typeName = "boolean"; typeName = "boolean";
} else if (std::dynamic_pointer_cast<None>(args[0])) { } else if (args[0].isNone()) {
typeName = "none"; typeName = "none";
} else if (std::dynamic_pointer_cast<Function>(args[0])) { } else if (args[0].isFunction()) {
typeName = "function"; typeName = "function";
} else if (std::dynamic_pointer_cast<BuiltinFunction>(args[0])) { } else if (args[0].isBuiltinFunction()) {
typeName = "builtin_function"; typeName = "builtin_function";
} else { } else {
typeName = "unknown"; 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 // Create a built-in toNumber function for string-to-number conversion
auto toNumberFunc = std::make_shared<BuiltinFunction>("toNumber", 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) { if (args.size() != 1) {
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + "."); throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
} }
auto strObj = std::dynamic_pointer_cast<String>(args[0]); if (!args[0].isString()) {
if (!strObj) {
throw std::runtime_error("toNumber() expects a string argument."); throw std::runtime_error("toNumber() expects a string argument.");
} }
std::string str = strObj->value; std::string str = args[0].asString();
// Remove leading/trailing whitespace // Remove leading/trailing whitespace
str.erase(0, str.find_first_not_of(" \t\n\r")); str.erase(0, str.find_first_not_of(" \t\n\r"));
@ -139,12 +161,15 @@ void StdLib::addToEnvironment(sptr(Environment) env, Interpreter* interpreter) {
try { try {
double value = std::stod(str); double value = std::stod(str);
return std::make_shared<Number>(value); return Value(value);
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
throw std::runtime_error("Cannot convert '" + str + "' to number."); throw std::runtime_error("Cannot convert '" + str + "' to number.");
} catch (const std::out_of_range&) { } catch (const std::out_of_range&) {
throw std::runtime_error("Number '" + str + "' is 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/bob.h"
#include "../headers/Parser.h" #include "../headers/Parser.h"
#include "../headers/ASTPrinter.h"
using namespace std; using namespace std;
void Bob::runFile(const string& path) void Bob::runFile(const string& path)
@ -54,24 +53,20 @@ void Bob::run(string source)
{ {
try { try {
vector<Token> tokens = lexer.Tokenize(std::move(source)); 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); Parser p(tokens);
vector<sptr(Stmt)> statements = p.parse(); vector<sptr(Stmt)> statements = p.parse();
interpreter->interpret(statements); interpreter->interpret(statements);
//cout << "=========================" << endl;
} }
catch(std::exception &e) catch(std::exception &e)
{ {
cout << "ERROR OCCURRED: " << e.what() << endl; cout << "ERROR OCCURRED: " << e.what() << endl;
return; return;
} }
catch(...)
{
cout << "UNKNOWN ERROR OCCURRED" << endl;
return;
}

View File

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