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:
parent
cb14221429
commit
1e65b344ae
@ -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);
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -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()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -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);
|
||||||
};
|
};
|
||||||
@ -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
161
headers/Value.h
Normal 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;
|
||||||
@ -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>();
|
|
||||||
}
|
|
||||||
@ -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 + "'.");
|
|
||||||
}
|
|
||||||
@ -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");
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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
8
source/Value.cpp
Normal 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);
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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
12
test_fib.bob
Normal 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);
|
||||||
Loading…
Reference in New Issue
Block a user