561 lines
18 KiB
C++
561 lines
18 KiB
C++
//
|
|
// Created by Bobby Lucero on 5/27/23.
|
|
//
|
|
#include <utility>
|
|
#include <sstream>
|
|
#include <cmath>
|
|
#include <iomanip>
|
|
#include <limits>
|
|
#include <cmath>
|
|
#include "../headers/Interpreter.h"
|
|
#include "../headers/helperFunctions/HelperFunctions.h"
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
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 std::make_shared<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<Number>(right))
|
|
{
|
|
double value = std::dynamic_pointer_cast<Number>(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<Number>(right))
|
|
{
|
|
double value = std::dynamic_pointer_cast<Number>(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<Number>(left) && std::dynamic_pointer_cast<Number>(right))
|
|
{
|
|
double left_double = std::dynamic_pointer_cast<Number>(left)->value;
|
|
double right_double = std::dynamic_pointer_cast<Number>(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 std::make_shared<Number>(left_double - right_double);
|
|
case PLUS:
|
|
return std::make_shared<Number>(left_double + right_double);
|
|
case SLASH:
|
|
if(right_double == 0) throw std::runtime_error("DivisionByZeroError: Cannot divide by 0");
|
|
return std::make_shared<Number>(left_double / right_double);
|
|
case STAR:
|
|
return std::make_shared<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<String>(left) && std::dynamic_pointer_cast<String>(right))
|
|
{
|
|
switch (expression->oper.type) {
|
|
case PLUS:
|
|
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
|
|
std::string right_string = std::dynamic_pointer_cast<String>(right)->value;
|
|
return msptr(String)(left_string + right_string);
|
|
}
|
|
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))
|
|
{
|
|
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
|
|
double right_number = std::dynamic_pointer_cast<Number>(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<double>::epsilon())
|
|
{
|
|
ss << std::fixed << std::setprecision(0) << integral;
|
|
}
|
|
else
|
|
{
|
|
ss << std::fixed << std::setprecision(std::numeric_limits<double>::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<Number>(left) && std::dynamic_pointer_cast<String>(right))
|
|
{
|
|
double left_number = std::dynamic_pointer_cast<Number>(left)->value;
|
|
std::string right_string = std::dynamic_pointer_cast<String>(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<double>::epsilon())
|
|
{
|
|
ss << std::fixed << std::setprecision(0) << integral;
|
|
}
|
|
else
|
|
{
|
|
ss << std::fixed << std::setprecision(std::numeric_limits<double>::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<Boolean>(left) && std::dynamic_pointer_cast<String>(right))
|
|
{
|
|
bool left_bool = std::dynamic_pointer_cast<Boolean>(left)->value;
|
|
std::string right_string = std::dynamic_pointer_cast<String>(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<String>(left) && std::dynamic_pointer_cast<Boolean>(right))
|
|
{
|
|
std::string left_string = std::dynamic_pointer_cast<String>(left)->value;
|
|
bool right_bool = std::dynamic_pointer_cast<Boolean>(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<sptr(Object)> arguments;
|
|
for (sptr(Expr) argument : expression->arguments) {
|
|
arguments.push_back(evaluate(argument));
|
|
}
|
|
|
|
if (auto builtin = std::dynamic_pointer_cast<BuiltinFunction>(callee)) {
|
|
return builtin->func(arguments);
|
|
}
|
|
|
|
if (auto function = std::dynamic_pointer_cast<Function>(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 = std::make_shared<Environment>(std::static_pointer_cast<Environment>(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<sptr(Stmt)> bodyStatements;
|
|
for (const auto& stmtPtr : function->body) {
|
|
bodyStatements.push_back(std::static_pointer_cast<Stmt>(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<None>(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<std::string> paramNames;
|
|
for (const Token& param : statement->params) {
|
|
paramNames.push_back(param.lexeme);
|
|
}
|
|
|
|
// Convert statements to void pointers for storage
|
|
std::vector<std::shared_ptr<void>> bodyStatements;
|
|
for (const sptr(Stmt)& stmt : statement->body) {
|
|
bodyStatements.push_back(std::static_pointer_cast<void>(stmt));
|
|
}
|
|
|
|
auto function = msptr(Function)(statement->name.lexeme,
|
|
paramNames,
|
|
bodyStatements,
|
|
std::static_pointer_cast<void>(environment));
|
|
environment->define(statement->name.lexeme, function);
|
|
}
|
|
|
|
void Interpreter::visitReturnStmt(sptr(ReturnStmt) statement)
|
|
{
|
|
sptr(Object) value = msptr(None)();
|
|
if (!std::dynamic_pointer_cast<None>(statement->value)) {
|
|
value = evaluate(statement->value);
|
|
}
|
|
|
|
throw Return(value);
|
|
}
|
|
|
|
void Interpreter::visitIfStmt(sptr(IfStmt) statement)
|
|
{
|
|
if (isTruthy(evaluate(statement->condition))) {
|
|
execute(statement->thenBranch);
|
|
} else if (statement->elseBranch != nullptr) {
|
|
execute(statement->elseBranch);
|
|
}
|
|
}
|
|
|
|
void Interpreter::interpret(std::vector<sptr(Stmt)> 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<sptr(Stmt)> 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<Boolean>(object))
|
|
{
|
|
return boolean->value;
|
|
}
|
|
|
|
if(auto obj = std::dynamic_pointer_cast<None>(object))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Interpreter::isEqual(sptr(Object) a, sptr(Object) b) {
|
|
if(auto left = std::dynamic_pointer_cast<Number>(a))
|
|
{
|
|
if(auto right = std::dynamic_pointer_cast<Number>(b))
|
|
{
|
|
return left->value == right->value;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else if(auto left = std::dynamic_pointer_cast<Boolean>(a))
|
|
{
|
|
if(auto right = std::dynamic_pointer_cast<Boolean>(b))
|
|
{
|
|
return left->value == right->value;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else if(auto left = std::dynamic_pointer_cast<String>(a))
|
|
{
|
|
if(auto right = std::dynamic_pointer_cast<String>(b))
|
|
{
|
|
return left->value == right->value;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else if(auto left = std::dynamic_pointer_cast<None>(a))
|
|
{
|
|
if(auto right = std::dynamic_pointer_cast<None>(b))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
throw std::runtime_error("Invalid isEqual compariosn");
|
|
}
|
|
|
|
std::string Interpreter::stringify(sptr(Object) object) {
|
|
if(std::dynamic_pointer_cast<None>(object))
|
|
{
|
|
return "none";
|
|
}
|
|
else if(auto num = std::dynamic_pointer_cast<Number>(object))
|
|
{
|
|
double integral = num->value;
|
|
double fractional = std::modf(num->value, &integral);
|
|
|
|
std::stringstream ss;
|
|
if(std::abs(fractional) < std::numeric_limits<double>::epsilon())
|
|
{
|
|
ss << std::fixed << std::setprecision(0) << integral;
|
|
return ss.str();
|
|
}
|
|
else
|
|
{
|
|
ss << std::fixed << std::setprecision(std::numeric_limits<double>::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<String>(object))
|
|
{
|
|
return string->value;
|
|
}
|
|
else if(auto Bool = std::dynamic_pointer_cast<Boolean>(object))
|
|
{
|
|
return Bool->value == 1 ? "true" : "false";
|
|
}
|
|
else if(auto func = std::dynamic_pointer_cast<Function>(object))
|
|
{
|
|
return "<function " + func->name + ">";
|
|
}
|
|
else if(auto builtinFunc = std::dynamic_pointer_cast<BuiltinFunction>(object))
|
|
{
|
|
return "<builtin_function " + builtinFunc->name + ">";
|
|
}
|
|
|
|
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<double>::epsilon())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|