// // Created by Bobby Lucero on 5/27/23. // #include #include #include #include #include #include #include "../headers/Interpreter.h" #include "../headers/helperFunctions/HelperFunctions.h" sptr(Object) Interpreter::visitLiteralExpr(sptr(LiteralExpr) expr) { if(expr->isNull) return msptr(None)(); if(expr->isNumber){ double num; if(expr->value[1] == 'b') { num = binaryStringToLong(expr->value); } else { // Use stod for all numbers to handle both integers and decimals correctly num = std::stod(expr->value); } return msptr(Number)(num); } if(expr->value == "true") return msptr(Boolean)(true); if(expr->value == "false") return msptr(Boolean)(false); return msptr(String)(expr->value); } sptr(Object) Interpreter::visitGroupingExpr(sptr(GroupingExpr) expression) { return evaluate(expression->expression); } sptr(Object) Interpreter::visitUnaryExpr(sptr(UnaryExpr) expression) { sptr(Object) right = evaluate(expression->right); if(expression->oper.type == MINUS) { if(std::dynamic_pointer_cast(right)) { double value = std::dynamic_pointer_cast(right)->value; return msptr(Number)(-value); } else { throw std::runtime_error("Operand must be a number when using: " + expression->oper.lexeme); } } if(expression->oper.type == BANG) { return msptr(Boolean)(!isTruthy(right)); } if(expression->oper.type == BIN_NOT) { if(std::dynamic_pointer_cast(right)) { double value = std::dynamic_pointer_cast(right)->value; return msptr(Number)((~(long)value)); } else { throw std::runtime_error("Operand must be an int when using: " + expression->oper.lexeme); } } //unreachable throw std::runtime_error("Invalid unary expression"); } sptr(Object) Interpreter::visitBinaryExpr(sptr(BinaryExpr) expression) { sptr(Object) left = evaluate(expression->left); sptr(Object) right = evaluate(expression->right); switch (expression->oper.type) { case BANG_EQUAL: return msptr(Boolean)(!isEqual(left, right)); case DOUBLE_EQUAL: return msptr(Boolean)(isEqual(left, right)); default: ; } if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { double left_double = std::dynamic_pointer_cast(left)->value; double right_double = std::dynamic_pointer_cast(right)->value; switch (expression->oper.type) { case GREATER: return msptr(Boolean)(left_double > right_double); case GREATER_EQUAL: return msptr(Boolean)(left_double >= right_double); case LESS: return msptr(Boolean)(left_double < right_double); case LESS_EQUAL: return msptr(Boolean)(left_double <= right_double); case MINUS: return msptr(Number)(left_double - right_double); case PLUS: return msptr(Number)(left_double + right_double); case SLASH: if(right_double == 0) throw std::runtime_error("DivisionByZeroError: Cannot divide by 0"); return msptr(Number)(left_double / right_double); case STAR: return msptr(Number)(left_double * right_double); case PERCENT: return msptr(Number)(fmod(left_double, right_double)); default: return msptr(None)(); //unreachable } } else if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { switch (expression->oper.type) { case PLUS: std::string left_string = std::dynamic_pointer_cast(left)->value; std::string right_string = std::dynamic_pointer_cast(right)->value; return msptr(String)(left_string + right_string); } throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on two strings"); } else if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { std::string left_string = std::dynamic_pointer_cast(left)->value; double right_number = std::dynamic_pointer_cast(right)->value; switch (expression->oper.type) { case PLUS: { // Use the same logic as stringify for consistent formatting double integral = right_number; double fractional = std::modf(right_number, &integral); std::stringstream ss; if(std::abs(fractional) < std::numeric_limits::epsilon()) { ss << std::fixed << std::setprecision(0) << integral; } else { ss << std::fixed << std::setprecision(std::numeric_limits::digits10 - 1) << right_number; std::string str = ss.str(); str.erase(str.find_last_not_of('0') + 1, std::string::npos); if (str.back() == '.') { str.pop_back(); } return msptr(String)(left_string + str); } return msptr(String)(left_string + ss.str()); } case STAR: if(isWholeNumer(right_number)) { std::string s; for (int i = 0; i < (int)right_number; ++i) { s += left_string; } return msptr(String)(s); } else { throw std::runtime_error("String multiplier must be whole number"); } } throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a string and a number"); } else if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { double left_number = std::dynamic_pointer_cast(left)->value; std::string right_string = std::dynamic_pointer_cast(right)->value; switch (expression->oper.type) { case PLUS: { // Use the same logic as stringify for consistent formatting double integral = left_number; double fractional = std::modf(left_number, &integral); std::stringstream ss; if(std::abs(fractional) < std::numeric_limits::epsilon()) { ss << std::fixed << std::setprecision(0) << integral; } else { ss << std::fixed << std::setprecision(std::numeric_limits::digits10 - 1) << left_number; std::string str = ss.str(); str.erase(str.find_last_not_of('0') + 1, std::string::npos); if (str.back() == '.') { str.pop_back(); } return msptr(String)(str + right_string); } return msptr(String)(ss.str() + right_string); } case STAR: if(isWholeNumer(left_number)) { std::string s; for (int i = 0; i < (int)left_number; ++i) { s += right_string; } return msptr(String)(s); } else { throw std::runtime_error("String multiplier must be whole number"); } } throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a number and a string"); } else if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { bool left_bool = std::dynamic_pointer_cast(left)->value; std::string right_string = std::dynamic_pointer_cast(right)->value; switch (expression->oper.type) { case PLUS: { std::string bool_str = left_bool ? "true" : "false"; return msptr(String)(bool_str + right_string); } } throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a boolean and a string"); } else if(std::dynamic_pointer_cast(left) && std::dynamic_pointer_cast(right)) { std::string left_string = std::dynamic_pointer_cast(left)->value; bool right_bool = std::dynamic_pointer_cast(right)->value; switch (expression->oper.type) { case PLUS: { std::string bool_str = right_bool ? "true" : "false"; return msptr(String)(left_string + bool_str); } } throw std::runtime_error("Cannot use '" + expression->oper.lexeme + "' on a string and a boolean"); } else { throw std::runtime_error("Operands must be of same type when using: " + expression->oper.lexeme); } } sptr(Object) Interpreter::visitVariableExpr(sptr(VarExpr) expression) { return environment->get(expression->name); } void Interpreter::addStdLibFunctions() { // Add standard library functions to the environment StdLib::addToEnvironment(environment, this); } sptr(Object) Interpreter::visitAssignExpr(sptr(AssignExpr) expression) { sptr(Object) value = evaluate(expression->value); environment->assign(expression->name, value); return value; } sptr(Object) Interpreter::visitCallExpr(sptr(CallExpr) expression) { sptr(Object) callee = evaluate(expression->callee); std::vector arguments; for (sptr(Expr) argument : expression->arguments) { arguments.push_back(evaluate(argument)); } if (auto builtin = std::dynamic_pointer_cast(callee)) { return builtin->func(arguments); } if (auto function = std::dynamic_pointer_cast(callee)) { if (arguments.size() != function->params.size()) { throw std::runtime_error("Expected " + std::to_string(function->params.size()) + " arguments but got " + std::to_string(arguments.size()) + "."); } // Create new environment for function execution sptr(Environment) functionEnv = msptr(Environment)(std::static_pointer_cast(function->closure)); // Bind parameters to arguments for (size_t i = 0; i < function->params.size(); i++) { functionEnv->define(function->params[i], arguments[i]); } // Execute function body try { // Convert void pointers back to statements std::vector bodyStatements; for (const auto& stmtPtr : function->body) { bodyStatements.push_back(std::static_pointer_cast(stmtPtr)); } executeBlock(bodyStatements, functionEnv); } catch (Return& returnValue) { return returnValue.value; } return msptr(None)(); } throw std::runtime_error("Can only call functions and classes."); } void Interpreter::visitBlockStmt(sptr(BlockStmt) statement) { executeBlock(statement->statements, msptr(Environment)(environment)); } void Interpreter::visitExpressionStmt(sptr(ExpressionStmt) statement) { sptr(Object) value = evaluate(statement->expression); if(IsInteractive) std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m" << std::endl; } void Interpreter::visitVarStmt(sptr(VarStmt) statement) { sptr(Object) value = msptr(None)(); if(!std::dynamic_pointer_cast(statement->initializer)) { value = evaluate(statement->initializer); } //std::cout << "Visit var stmt: " << statement->name.lexeme << " set to: " << stringify(value) << std::endl; environment->define(statement->name.lexeme, value); } void Interpreter::visitFunctionStmt(sptr(FunctionStmt) statement) { // Convert Token parameters to string parameters std::vector paramNames; for (const Token& param : statement->params) { paramNames.push_back(param.lexeme); } // Convert statements to void pointers for storage std::vector> bodyStatements; for (const sptr(Stmt)& stmt : statement->body) { bodyStatements.push_back(std::static_pointer_cast(stmt)); } auto function = msptr(Function)(statement->name.lexeme, paramNames, bodyStatements, std::static_pointer_cast(environment)); environment->define(statement->name.lexeme, function); } void Interpreter::visitReturnStmt(sptr(ReturnStmt) statement) { sptr(Object) value = msptr(None)(); if (!std::dynamic_pointer_cast(statement->value)) { value = evaluate(statement->value); } throw Return(value); } void Interpreter::interpret(std::vector statements) { for(sptr(Stmt) s : statements) { execute(s); } //std::cout << "\033[0;32m" << stringify(value) << std::endl; } void Interpreter::execute(sptr(Stmt) statement) { try { statement->accept(this); } catch(Return& returnValue) { throw returnValue; // Re-throw Return exceptions } catch(std::exception &e) { std::cout << "ERROR OCCURRED: " << e.what() << std::endl; throw e; // Re-throw the exception to stop execution } } void Interpreter::executeBlock(std::vector statements, sptr(Environment) env) { sptr(Environment) previous = this->environment; this->environment = env; try { for(sptr(Stmt) s : statements) { execute(s); } } catch (Return& returnValue) { this->environment = previous; throw returnValue; } this->environment = previous; } sptr(Object) Interpreter::evaluate(sptr(Expr) expr) { return expr->accept(this); } bool Interpreter::isTruthy(sptr(Object) object) { if(auto boolean = std::dynamic_pointer_cast(object)) { return boolean->value; } if(auto obj = std::dynamic_pointer_cast(object)) { return false; } return true; } bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) { if(auto left = std::dynamic_pointer_cast(a)) { if(auto right = std::dynamic_pointer_cast(b)) { return left->value == right->value; } return false; } else if(auto left = std::dynamic_pointer_cast(a)) { if(auto right = std::dynamic_pointer_cast(b)) { return left->value == right->value; } return false; } else if(auto left = std::dynamic_pointer_cast(a)) { if(auto right = std::dynamic_pointer_cast(b)) { return left->value == right->value; } return false; } else if(auto left = std::dynamic_pointer_cast(a)) { if(auto right = std::dynamic_pointer_cast(b)) { return true; } return false; } throw std::runtime_error("Invalid isEqual compariosn"); } std::string Interpreter::stringify(sptr(Object) object) { if(std::dynamic_pointer_cast(object)) { return "none"; } else if(auto num = std::dynamic_pointer_cast(object)) { double integral = num->value; double fractional = std::modf(num->value, &integral); std::stringstream ss; if(std::abs(fractional) < std::numeric_limits::epsilon()) { ss << std::fixed << std::setprecision(0) << integral; return ss.str(); } else { ss << std::fixed << std::setprecision(std::numeric_limits::digits10 - 1) << num->value; std::string str = ss.str(); str.erase(str.find_last_not_of('0') + 1, std::string::npos); if (str.back() == '.') { str.pop_back(); } return str; } } else if(auto string = std::dynamic_pointer_cast(object)) { return string->value; } else if(auto Bool = std::dynamic_pointer_cast(object)) { return Bool->value == 1 ? "true" : "false"; } throw std::runtime_error("Could not convert object to string"); } bool Interpreter::isWholeNumer(double num) { double integral = num; double fractional = std::modf(num, &integral); if(std::abs(fractional) < std::numeric_limits::epsilon()) { return true; } else { return false; } }