diff --git a/headers/ASTPrinter.h b/headers/ASTPrinter.h deleted file mode 100644 index fe66abf..0000000 --- a/headers/ASTPrinter.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "Expression.h" -#include "TypeWrapper.h" -#include "helperFunctions/ShortHands.h" -#include -#include - - -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 exprs); - -}; \ No newline at end of file diff --git a/headers/Environment.h b/headers/Environment.h index 65d34b2..3fa33d6 100644 --- a/headers/Environment.h +++ b/headers/Environment.h @@ -1,33 +1,49 @@ #pragma once #include -#include "TypeWrapper.h" -#include "helperFunctions/ShortHands.h" +#include +#include +#include "Value.h" #include "Lexer.h" -class Environment -{ +class Environment { public: - - void define(std::string name, sptr(Object) value); - - void assign(Token name, sptr(Object) value); - - std::shared_ptr get(Token name); - - sptr(Environment) enclosing; - - Environment(){ - enclosing = nullptr; + Environment() : parent(nullptr) {} + Environment(std::shared_ptr parent_env) : parent(parent_env) {} + + // 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 getParent() const { return parent; } + inline void clear() { variables.clear(); } + private: - std::unordered_map variables; - - - + std::unordered_map variables; + std::shared_ptr parent; }; \ No newline at end of file diff --git a/headers/Expression.h b/headers/Expression.h index 078880c..ddb798b 100644 --- a/headers/Expression.h +++ b/headers/Expression.h @@ -4,9 +4,11 @@ #pragma once #include +#include #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& expr) = 0; + virtual Value visitBinaryExpr(const std::shared_ptr& expr) = 0; + virtual Value visitGroupingExpr(const std::shared_ptr& expr) = 0; + virtual Value visitLiteralExpr(const std::shared_ptr& expr) = 0; + virtual Value visitUnaryExpr(const std::shared_ptr& expr) = 0; + virtual Value visitVariableExpr(const std::shared_ptr& expr) = 0; + virtual Value visitCallExpr(const std::shared_ptr& expr) = 0; }; -struct Expr{ - virtual sptr(Object) accept(ExprVisitor* visitor) = 0; +struct Expr : public std::enable_shared_from_this { + virtual Value accept(ExprVisitor* visitor) = 0; virtual ~Expr() = default; }; -struct AssignExpr : Expr, public std::enable_shared_from_this +struct AssignExpr : Expr { const Token name; - const sptr(Expr) value; - AssignExpr(Token name, sptr(Expr) value) : name(name), value(value) + std::shared_ptr value; + AssignExpr(Token name, std::shared_ptr 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(shared_from_this())); } }; -struct BinaryExpr : Expr, public std::enable_shared_from_this +struct BinaryExpr : Expr { - const std::shared_ptr left; + std::shared_ptr left; const Token oper; - const std::shared_ptr right; + std::shared_ptr right; - BinaryExpr(sptr(Expr) left, Token oper, sptr(Expr) right) : left(left), oper(oper), right(right) + BinaryExpr(std::shared_ptr left, Token oper, std::shared_ptr 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(shared_from_this())); } }; -struct GroupingExpr : Expr, public std::enable_shared_from_this +struct GroupingExpr : Expr { - const std::shared_ptr expression; + std::shared_ptr expression; - explicit GroupingExpr(sptr(Expr) expression) : expression(expression) + explicit GroupingExpr(std::shared_ptr 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(shared_from_this())); } }; -struct LiteralExpr : Expr, public std::enable_shared_from_this +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(shared_from_this())); } }; -struct UnaryExpr : Expr, public std::enable_shared_from_this +struct UnaryExpr : Expr { const Token oper; - const std::shared_ptr right; + std::shared_ptr right; - UnaryExpr(Token oper, sptr(Expr) right) : oper(oper), right(right) + UnaryExpr(Token oper, std::shared_ptr 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(shared_from_this())); } }; -struct VarExpr : Expr, public std::enable_shared_from_this +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(shared_from_this())); } }; -struct CallExpr : Expr, public std::enable_shared_from_this +struct CallExpr : Expr { - const sptr(Expr) callee; + std::shared_ptr callee; const Token paren; - const std::vector arguments; + std::vector> arguments; - CallExpr(sptr(Expr) callee, Token paren, std::vector arguments) - : callee(callee), paren(paren), arguments(arguments) {} - - sptr(Object) accept(ExprVisitor* visitor) override + CallExpr(std::shared_ptr callee, Token paren, std::vector> 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(shared_from_this())); } }; diff --git a/headers/Interpreter.h b/headers/Interpreter.h index b9191a7..0294d77 100644 --- a/headers/Interpreter.h +++ b/headers/Interpreter.h @@ -4,43 +4,52 @@ #include "helperFunctions/ShortHands.h" #include "TypeWrapper.h" #include "Environment.h" +#include "Value.h" #include "StdLib.h" +#include +#include +#include +#include + 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& expression) override; + Value visitGroupingExpr(const std::shared_ptr& expression) override; + Value visitLiteralExpr(const std::shared_ptr& expression) override; + Value visitUnaryExpr(const std::shared_ptr& expression) override; + Value visitVariableExpr(const std::shared_ptr& expression) override; + Value visitAssignExpr(const std::shared_ptr& expression) override; + Value visitCallExpr(const std::shared_ptr& expression) override; - void visitBlockStmt(sptr(BlockStmt) statement) override; - void visitExpressionStmt(sptr(ExpressionStmt) statement) override; + void visitBlockStmt(const std::shared_ptr& statement) override; + void visitExpressionStmt(const std::shared_ptr& statement) override; + void visitVarStmt(const std::shared_ptr& statement) override; + void visitFunctionStmt(const std::shared_ptr& statement) override; + void visitReturnStmt(const std::shared_ptr& statement) override; + void visitIfStmt(const std::shared_ptr& 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 statements); + void interpret(std::vector> statements); explicit Interpreter(bool IsInteractive) : IsInteractive(IsInteractive){ - environment = msptr(Environment)(); + environment = std::make_shared(); + + // Pre-allocate environment pool + for (size_t i = 0; i < POOL_SIZE; ++i) { + envPool.push(std::make_shared()); + } // Add standard library functions addStdLibFunctions(); @@ -48,18 +57,32 @@ public: virtual ~Interpreter() = default; private: - - sptr(Environment) environment; + std::shared_ptr environment; bool IsInteractive; + std::vector> builtinFunctions; + std::vector> functions; + + // Environment pool for fast function calls + std::stack> 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 returnStack; - sptr(Object) evaluate(sptr(Expr) expr); - bool isTruthy(sptr(Object) object); - bool isEqual(sptr(Object) a, sptr(Object) b); + Value evaluate(const std::shared_ptr& expr); + bool isEqual(Value a, Value b); bool isWholeNumer(double num); - void execute(sptr(Stmt) statement); - void executeBlock(std::vector statements, sptr(Environment) env); + void execute(const std::shared_ptr& statement); + void executeBlock(std::vector> statements, std::shared_ptr env); void addStdLibFunctions(); public: - std::string stringify(sptr(Object) object); + bool isTruthy(Value object); + std::string stringify(Value object); + void addBuiltinFunction(std::shared_ptr func); }; diff --git a/headers/Statement.h b/headers/Statement.h index ddb3b89..4872457 100644 --- a/headers/Statement.h +++ b/headers/Statement.h @@ -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& stmt) = 0; + virtual void visitExpressionStmt(const std::shared_ptr& stmt) = 0; + virtual void visitVarStmt(const std::shared_ptr& stmt) = 0; + virtual void visitFunctionStmt(const std::shared_ptr& stmt) = 0; + virtual void visitReturnStmt(const std::shared_ptr& stmt) = 0; + virtual void visitIfStmt(const std::shared_ptr& stmt) = 0; }; -struct Stmt +struct Stmt : public std::enable_shared_from_this { - const sptr(Expr) expression; + std::shared_ptr expression; virtual void accept(StmtVisitor* visitor) = 0; virtual ~Stmt(){}; }; -struct BlockStmt : Stmt, public std::enable_shared_from_this +struct BlockStmt : Stmt { - const std::vector statements; - explicit BlockStmt(std::vector statements) : statements(statements) + std::vector> statements; + explicit BlockStmt(std::vector> statements) : statements(statements) { } void accept(StmtVisitor* visitor) override { - visitor->visitBlockStmt(shared_from_this()); + visitor->visitBlockStmt(std::static_pointer_cast(shared_from_this())); } }; -struct ExpressionStmt : Stmt, public std::enable_shared_from_this +struct ExpressionStmt : Stmt { - const sptr(Expr) expression; - explicit ExpressionStmt(sptr(Expr) expression) : expression(expression) + std::shared_ptr expression; + explicit ExpressionStmt(std::shared_ptr expression) : expression(expression) { } void accept(StmtVisitor* visitor) override { - visitor->visitExpressionStmt(shared_from_this()); + visitor->visitExpressionStmt(std::static_pointer_cast(shared_from_this())); } }; -struct VarStmt : Stmt, public std::enable_shared_from_this +struct VarStmt : Stmt { Token name; - const sptr(Expr) initializer; - VarStmt(Token name, sptr(Expr) initializer) : name(name), initializer(initializer) + std::shared_ptr initializer; + VarStmt(Token name, std::shared_ptr initializer) : name(name), initializer(initializer) { } void accept(StmtVisitor* visitor) override { - visitor->visitVarStmt(shared_from_this()); + visitor->visitVarStmt(std::static_pointer_cast(shared_from_this())); } }; -struct FunctionStmt : Stmt, public std::enable_shared_from_this +struct FunctionStmt : Stmt { const Token name; const std::vector params; - const std::vector body; + std::vector> body; - FunctionStmt(Token name, std::vector params, std::vector body) + FunctionStmt(Token name, std::vector params, std::vector> body) : name(name), params(params), body(body) {} void accept(StmtVisitor* visitor) override { - visitor->visitFunctionStmt(shared_from_this()); + visitor->visitFunctionStmt(std::static_pointer_cast(shared_from_this())); } }; -struct ReturnStmt : Stmt, public std::enable_shared_from_this +struct ReturnStmt : Stmt { const Token keyword; - const sptr(Expr) value; + std::shared_ptr value; - ReturnStmt(Token keyword, sptr(Expr) value) : keyword(keyword), value(value) {} + ReturnStmt(Token keyword, std::shared_ptr value) : keyword(keyword), value(value) {} void accept(StmtVisitor* visitor) override { - visitor->visitReturnStmt(shared_from_this()); + visitor->visitReturnStmt(std::static_pointer_cast(shared_from_this())); } }; -struct IfStmt : Stmt, public std::enable_shared_from_this +struct IfStmt : Stmt { - const sptr(Expr) condition; - const sptr(Stmt) thenBranch; - const sptr(Stmt) elseBranch; + std::shared_ptr condition; + std::shared_ptr thenBranch; + std::shared_ptr elseBranch; - IfStmt(sptr(Expr) condition, sptr(Stmt) thenBranch, sptr(Stmt) elseBranch) + IfStmt(std::shared_ptr condition, std::shared_ptr thenBranch, std::shared_ptr elseBranch) : condition(condition), thenBranch(thenBranch), elseBranch(elseBranch) {} void accept(StmtVisitor* visitor) override { - visitor->visitIfStmt(shared_from_this()); + visitor->visitIfStmt(std::static_pointer_cast(shared_from_this())); } }; \ No newline at end of file diff --git a/headers/StdLib.h b/headers/StdLib.h index 4dad781..67a2c17 100644 --- a/headers/StdLib.h +++ b/headers/StdLib.h @@ -1,12 +1,12 @@ #pragma once -#include "TypeWrapper.h" +#include "Value.h" #include "Environment.h" -#include +#include -class Interpreter; // Forward declaration +class Interpreter; class StdLib { public: - static void addToEnvironment(sptr(Environment) env, Interpreter* interpreter); + static void addToEnvironment(std::shared_ptr env, Interpreter& interpreter); }; \ No newline at end of file diff --git a/headers/TypeWrapper.h b/headers/TypeWrapper.h index 2d26041..84ff817 100644 --- a/headers/TypeWrapper.h +++ b/headers/TypeWrapper.h @@ -4,6 +4,12 @@ #include #include #include +#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 params; - const std::vector> body; // Will cast to Stmt* when needed - const std::shared_ptr closure; // Will cast to Environment* when needed + const std::vector> body; + const std::shared_ptr closure; Function(std::string name, std::vector params, - std::vector> body, - std::shared_ptr closure) + std::vector> body, + std::shared_ptr closure) : name(name), params(params), body(body), closure(closure) {} }; struct BuiltinFunction : public Object { const std::string name; - const std::function(std::vector >)> func; + const std::function)> func; - BuiltinFunction(std::string name, std::function(std::vector >)> func) + BuiltinFunction(std::string name, std::function)> func) : name(name), func(func) {} }; diff --git a/headers/Value.h b/headers/Value.h new file mode 100644 index 0000000..3ee661f --- /dev/null +++ b/headers/Value.h @@ -0,0 +1,161 @@ +#pragma once +#include +#include +#include +#include + +// 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(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 ""; + case ValueType::VAL_BUILTIN_FUNCTION: return ""; + 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; \ No newline at end of file diff --git a/source/ASTPrinter.cpp b/source/ASTPrinter.cpp deleted file mode 100644 index eac80d4..0000000 --- a/source/ASTPrinter.cpp +++ /dev/null @@ -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{expression->left, expression->right}); -} - -sptr(Object) ASTPrinter::visitGroupingExpr(sptr(GroupingExpr) expression){ - return parenthesize("group", std::vector{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{expression->right}); -} - -sptr(Object) ASTPrinter::print(sptr(Expr) expr) { - return expr->accept(this); -} - -sptr(Object) ASTPrinter::parenthesize(std::string name, std::vector exprs) { - std::string builder; - - builder += "(" + name; - - for(const sptr(Expr)& expr : exprs) - { - builder += " "; - builder += std::dynamic_pointer_cast(expr->accept(this))->value; - } - - builder += ")"; - - return msptr(String)(builder); - -} - -sptr(Object) ASTPrinter::visitAssignExpr(std::shared_ptr expr) { - return std::shared_ptr(); -} - -sptr(Object) ASTPrinter::visitVariableExpr(std::shared_ptr expr) { - return std::shared_ptr(); -} diff --git a/source/Environment.cpp b/source/Environment.cpp deleted file mode 100644 index ce5d97d..0000000 --- a/source/Environment.cpp +++ /dev/null @@ -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 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 + "'."); -} diff --git a/source/Interpreter.cpp b/source/Interpreter.cpp index 4875647..548155d 100644 --- a/source/Interpreter.cpp +++ b/source/Interpreter.cpp @@ -11,10 +11,16 @@ #include "../headers/helperFunctions/HelperFunctions.h" #include +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& 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(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& expression) { return evaluate(expression->expression); } -sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression) +Value Interpreter::visitUnaryExpr(const std::shared_ptr& expression) { - sptr(Object) right = evaluate(expression->right); + Value right = evaluate(expression->right); if(expression->oper.type == MINUS) { - if(std::dynamic_pointer_cast(right)) + if(right.isNumber()) { - double value = std::dynamic_pointer_cast(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(right)) + if(right.isNumber()) { - double value = std::dynamic_pointer_cast(right)->value; - return msptr(Number)((~(long)value)); + double value = right.asNumber(); + return Value(static_cast(~(static_cast(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& 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(left) && std::dynamic_pointer_cast(right)) + if(left.isNumber() && right.isNumber()) { - double left_double = std::dynamic_pointer_cast(left)->value; - double right_double = std::dynamic_pointer_cast(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(left_double - right_double); + return Value(left_double - right_double); case PLUS: - return std::make_shared(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(left_double / right_double); + return Value(left_double / right_double); case STAR: - return std::make_shared(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(left) && std::dynamic_pointer_cast(right)) + else if(left.isString() && right.isString()) { switch (expression->oper.type) { - case PLUS: - std::string left_string = std::dynamic_pointer_cast(left)->value; - std::string right_string = std::dynamic_pointer_cast(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(left) && std::dynamic_pointer_cast(right)) + else if(left.isString() && right.isNumber()) { - std::string left_string = std::dynamic_pointer_cast(left)->value; - double right_number = std::dynamic_pointer_cast(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(left) && std::dynamic_pointer_cast(right)) + else if(left.isNumber() && right.isString()) { - double left_number = std::dynamic_pointer_cast(left)->value; - std::string right_string = std::dynamic_pointer_cast(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(left) && std::dynamic_pointer_cast(right)) + else if(left.isBoolean() && right.isString()) { - bool left_bool = std::dynamic_pointer_cast(left)->value; - std::string right_string = std::dynamic_pointer_cast(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(left) && std::dynamic_pointer_cast(right)) + else if(left.isString() && right.isBoolean()) { - std::string left_string = std::dynamic_pointer_cast(left)->value; - bool right_bool = std::dynamic_pointer_cast(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& 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 func) { + builtinFunctions.push_back(func); +} + +Value Interpreter::visitAssignExpr(const std::shared_ptr& 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& expression) { + Value callee = evaluate(expression->callee); - std::vector arguments; - for (sptr(Expr) argument : expression->arguments) { + std::vector arguments; + for (const std::shared_ptr& argument : expression->arguments) { arguments.push_back(evaluate(argument)); } - if (auto builtin = std::dynamic_pointer_cast(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(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()) + + 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(std::static_pointer_cast(function->closure)); + auto previousEnv = environment; + environment = std::make_shared(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 bodyStatements; - for (const auto& stmtPtr : function->body) { - bodyStatements.push_back(std::static_pointer_cast(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& statement) { + auto newEnv = std::make_shared(environment); + executeBlock(statement->statements, newEnv); } -void Interpreter::visitExpressionStmt(sptr(ExpressionStmt) statement) { - sptr(Object) value = evaluate(statement->expression); +void Interpreter::visitExpressionStmt(const std::shared_ptr& 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& statement) { - sptr(Object) value = msptr(None)(); - if(!std::dynamic_pointer_cast(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& statement) { // Convert Token parameters to string parameters std::vector 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> bodyStatements; - for (const sptr(Stmt)& stmt : statement->body) { - bodyStatements.push_back(std::static_pointer_cast(stmt)); - } - auto function = msptr(Function)(statement->name.lexeme, paramNames, - bodyStatements, - std::static_pointer_cast(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& statement) { - sptr(Object) value = msptr(None)(); - if (!std::dynamic_pointer_cast(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& statement) { if (isTruthy(evaluate(statement->condition))) { execute(statement->thenBranch); @@ -379,63 +416,43 @@ void Interpreter::visitIfStmt(sptr(IfStmt) statement) } } -void Interpreter::interpret(std::vector statements) { - - - for(sptr(Stmt) s : statements) +void Interpreter::interpret(std::vector> statements) { + for(const std::shared_ptr& 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& 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 - } + statement->accept(this); } -void Interpreter::executeBlock(std::vector statements, sptr(Environment) env) +void Interpreter::executeBlock(std::vector> statements, std::shared_ptr env) { - sptr(Environment) previous = this->environment; + std::shared_ptr previous = this->environment; this->environment = env; - try { - for(sptr(Stmt) s : statements) - { - execute(s); - } - } catch (Return& returnValue) { - this->environment = previous; - throw returnValue; + for(const std::shared_ptr& s : statements) + { + execute(s); } this->environment = previous; } -sptr(Object) Interpreter::evaluate(sptr(Expr) expr) { +Value Interpreter::evaluate(const std::shared_ptr& expr) { return expr->accept(this); } -bool Interpreter::isTruthy(sptr(Object) object) { +bool Interpreter::isTruthy(Value object) { - if(auto boolean = std::dynamic_pointer_cast(object)) + if(object.isBoolean()) { - return boolean->value; + return object.asBoolean(); } - if(auto obj = std::dynamic_pointer_cast(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(a)) +bool Interpreter::isEqual(Value a, Value b) { + if(a.isNumber()) { - if(auto right = std::dynamic_pointer_cast(b)) + if(b.isNumber()) { - return left->value == right->value; + return a.asNumber() == b.asNumber(); } return false; } - else if(auto left = std::dynamic_pointer_cast(a)) + else if(a.isBoolean()) { - if(auto right = std::dynamic_pointer_cast(b)) + if(b.isBoolean()) { - return left->value == right->value; + return a.asBoolean() == b.asBoolean(); } return false; } - else if(auto left = std::dynamic_pointer_cast(a)) + else if(a.isString()) { - if(auto right = std::dynamic_pointer_cast(b)) + if(b.isString()) { - return left->value == right->value; + return a.asString() == b.asString(); } return false; } - else if(auto left = std::dynamic_pointer_cast(a)) + else if(a.isNone()) { - if(auto right = std::dynamic_pointer_cast(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(object)) +std::string Interpreter::stringify(Value object) { + if(object.isNone()) { return "none"; } - else if(auto num = std::dynamic_pointer_cast(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::epsilon()) @@ -502,7 +519,7 @@ std::string Interpreter::stringify(sptr(Object) object) { } else { - ss << std::fixed << std::setprecision(std::numeric_limits::digits10 - 1) << num->value; + ss << std::fixed << std::setprecision(std::numeric_limits::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(object)) + else if(object.isString()) { - return string->value; + return object.asString(); } - else if(auto Bool = std::dynamic_pointer_cast(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(object)) + else if(object.isFunction()) { - return "name + ">"; + return "name + ">"; } - else if(auto builtinFunc = std::dynamic_pointer_cast(object)) + else if(object.isBuiltinFunction()) { - return "name + ">"; + return "name + ">"; } throw std::runtime_error("Could not convert object to string"); diff --git a/source/Parser.cpp b/source/Parser.cpp index bf210cf..30a8d3d 100644 --- a/source/Parser.cpp +++ b/source/Parser.cpp @@ -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(); diff --git a/source/StdLib.cpp b/source/StdLib.cpp index 35741f8..c211ceb 100644 --- a/source/StdLib.cpp +++ b/source/StdLib.cpp @@ -2,59 +2,73 @@ #include "../headers/Interpreter.h" #include -void StdLib::addToEnvironment(sptr(Environment) env, Interpreter* interpreter) { +void StdLib::addToEnvironment(std::shared_ptr env, Interpreter& interpreter) { // Create a built-in toString function auto toStringFunc = std::make_shared("toString", - [interpreter](std::vector> args) -> std::shared_ptr { + [&interpreter](std::vector args) -> Value { if (args.size() != 1) { throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + "."); } - return std::make_shared(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("print", - [interpreter](std::vector> args) -> std::shared_ptr { + [&interpreter](std::vector 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(); + 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("assert", - [interpreter](std::vector> args) -> std::shared_ptr { + [](std::vector 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(args[0])) { - if (!boolObj->value) { - std::string message = "Assertion failed: condition is false"; - if (args.size() == 2) { - if (auto strObj = std::dynamic_pointer_cast(args[1])) { - message += " - " + strObj->value; - } - } - throw std::runtime_error(message); - } + // 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 { - 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(); + 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) auto timeFunc = std::make_shared("time", - [](std::vector> args) -> std::shared_ptr { + [](std::vector 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(duration).count(); - return std::make_shared(microseconds); + return Value(static_cast(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("input", - [interpreter](std::vector> args) -> std::shared_ptr { + [&interpreter](std::vector 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(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("type", - [](std::vector> args) -> std::shared_ptr { + [](std::vector 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(args[0])) { + if (args[0].isNumber()) { typeName = "number"; - } else if (std::dynamic_pointer_cast(args[0])) { + } else if (args[0].isString()) { typeName = "string"; - } else if (std::dynamic_pointer_cast(args[0])) { + } else if (args[0].isBoolean()) { typeName = "boolean"; - } else if (std::dynamic_pointer_cast(args[0])) { + } else if (args[0].isNone()) { typeName = "none"; - } else if (std::dynamic_pointer_cast(args[0])) { + } else if (args[0].isFunction()) { typeName = "function"; - } else if (std::dynamic_pointer_cast(args[0])) { + } else if (args[0].isBuiltinFunction()) { typeName = "builtin_function"; } else { typeName = "unknown"; } - return std::make_shared(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("toNumber", - [](std::vector> args) -> std::shared_ptr { + [](std::vector 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(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(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); } \ No newline at end of file diff --git a/source/Value.cpp b/source/Value.cpp new file mode 100644 index 0000000..9179617 --- /dev/null +++ b/source/Value.cpp @@ -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); \ No newline at end of file diff --git a/source/bob.cpp b/source/bob.cpp index ff3d9a0..4c9dfed 100644 --- a/source/bob.cpp +++ b/source/bob.cpp @@ -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 tokens = lexer.Tokenize(std::move(source)); -// for(Token t : tokens){ -// cout << "{type: " << enum_mapping[t.type] << ", value: " << t.lexeme << "}" << endl; -// } - - Parser p(tokens); vector 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; + } diff --git a/test_bob_language.bob b/test_bob_language.bob index aa71e0f..a4f0d5a 100644 --- a/test_bob_language.bob +++ b/test_bob_language.bob @@ -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"); diff --git a/test_fib.bob b/test_fib.bob new file mode 100644 index 0000000..573f15b --- /dev/null +++ b/test_fib.bob @@ -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); \ No newline at end of file