Bob/src/sources/runtime/Evaluator.cpp
2025-08-10 16:33:48 -04:00

475 lines
19 KiB
C++

#include "Evaluator.h"
#include "Interpreter.h"
#include "Environment.h"
#include "AssignmentUtils.h"
#include "helperFunctions/HelperFunctions.h"
Evaluator::Evaluator(Interpreter* interpreter) : interpreter(interpreter) {}
Value Evaluator::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
if (expr->isNull) {
return NONE_VALUE;
}
if (expr->isNumber) {
double num;
if (expr->value.length() > 2 && expr->value[0] == '0' && expr->value[1] == 'b') {
num = binaryStringToLong(expr->value);
} else {
num = std::stod(expr->value);
}
return Value(num);
}
if (expr->isBoolean) {
if (expr->value == "true") return TRUE_VALUE;
if (expr->value == "false") return FALSE_VALUE;
}
return Value(expr->value);
}
Value Evaluator::visitGroupingExpr(const std::shared_ptr<GroupingExpr>& expression) {
return interpreter->evaluate(expression->expression);
}
Value Evaluator::visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expression)
{
Value right = interpreter->evaluate(expression->right);
switch (expression->oper.type) {
case MINUS:
if (!right.isNumber()) {
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
"Operand must be a number when using: " + expression->oper.lexeme, expression->oper.lexeme);
throw std::runtime_error("Operand must be a number when using: " + expression->oper.lexeme);
}
return Value(-right.asNumber());
case BANG:
return Value(!interpreter->isTruthy(right));
case BIN_NOT:
if (!right.isNumber()) {
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
"Operand must be a number when using: " + expression->oper.lexeme, expression->oper.lexeme);
throw std::runtime_error("Operand must be a number when using: " + expression->oper.lexeme);
}
return Value(static_cast<double>(~(static_cast<long>(right.asNumber()))));
default:
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
"Invalid unary operator: " + expression->oper.lexeme, expression->oper.lexeme);
throw std::runtime_error("Invalid unary operator: " + expression->oper.lexeme);
}
}
Value Evaluator::visitBinaryExpr(const std::shared_ptr<BinaryExpr>& expression) {
Value left = interpreter->evaluate(expression->left);
Value right = interpreter->evaluate(expression->right);
// Handle logical operators (AND, OR) - these work with any types
if (expression->oper.type == AND) {
return interpreter->isTruthy(left) ? right : left;
}
if (expression->oper.type == OR) {
return interpreter->isTruthy(left) ? left : right;
}
// Handle equality operators - these work with any types
if (expression->oper.type == DOUBLE_EQUAL || expression->oper.type == BANG_EQUAL) {
bool equal = interpreter->isEqual(left, right);
return Value(expression->oper.type == DOUBLE_EQUAL ? equal : !equal);
}
// Handle comparison operators - only work with numbers
if (expression->oper.type == GREATER || expression->oper.type == GREATER_EQUAL ||
expression->oper.type == LESS || expression->oper.type == LESS_EQUAL) {
if (left.isNumber() && right.isNumber()) {
double leftNum = left.asNumber();
double rightNum = right.asNumber();
switch (expression->oper.type) {
case GREATER: return Value(leftNum > rightNum);
case GREATER_EQUAL: return Value(leftNum >= rightNum);
case LESS: return Value(leftNum < rightNum);
case LESS_EQUAL: return Value(leftNum <= rightNum);
default: break; // Unreachable
}
}
// Error for non-number comparisons
std::string opName;
switch (expression->oper.type) {
case GREATER: opName = ">"; break;
case GREATER_EQUAL: opName = ">="; break;
case LESS: opName = "<"; break;
case LESS_EQUAL: opName = "<="; break;
default: break; // Unreachable
}
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
ErrorUtils::makeOperatorError(opName, left.getType(), right.getType()), opName);
throw std::runtime_error(ErrorUtils::makeOperatorError(opName, left.getType(), right.getType()));
}
// Handle all other operators using Value's operator overloads
try {
switch (expression->oper.type) {
case PLUS: return left + right;
case MINUS: return left - right;
case STAR: return left * right;
case SLASH: return left / right;
case PERCENT: return left % right;
case BIN_AND: return left & right;
case BIN_OR: return left | right;
case BIN_XOR: return left ^ right;
case BIN_SLEFT: return left << right;
case BIN_SRIGHT: return left >> right;
default:
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
"Unknown operator: " + expression->oper.lexeme, expression->oper.lexeme);
throw std::runtime_error("Unknown operator: " + expression->oper.lexeme);
}
} catch (const std::runtime_error& e) {
// The Value operators provide good error messages, just add context
interpreter->reportError(expression->oper.line, expression->oper.column, "Runtime Error",
e.what(), expression->oper.lexeme);
throw;
}
}
Value Evaluator::visitVarExpr(const std::shared_ptr<VarExpr>& expression)
{
return interpreter->getEnvironment()->get(expression->name);
}
Value Evaluator::visitIncrementExpr(const std::shared_ptr<IncrementExpr>& expression) {
// Get the current value of the operand
Value currentValue = interpreter->evaluate(expression->operand);
if (!currentValue.isNumber()) {
interpreter->reportError(expression->oper.line, expression->oper.column,
"Runtime Error", "Increment/decrement can only be applied to numbers.", "");
throw std::runtime_error("Increment/decrement can only be applied to numbers.");
}
double currentNum = currentValue.asNumber();
double newValue;
// Determine the operation based on the operator
if (expression->oper.type == PLUS_PLUS) {
newValue = currentNum + 1.0;
} else if (expression->oper.type == MINUS_MINUS) {
newValue = currentNum - 1.0;
} else {
interpreter->reportError(expression->oper.line, expression->oper.column,
"Runtime Error", "Invalid increment/decrement operator.", "");
throw std::runtime_error("Invalid increment/decrement operator.");
}
// Update the variable or array element
if (auto varExpr = std::dynamic_pointer_cast<VarExpr>(expression->operand)) {
interpreter->getEnvironment()->assign(varExpr->name, Value(newValue));
} else if (auto arrayExpr = std::dynamic_pointer_cast<ArrayIndexExpr>(expression->operand)) {
// Handle array indexing increment/decrement
Value array = interpreter->evaluate(arrayExpr->array);
Value index = interpreter->evaluate(arrayExpr->index);
if (!array.isArray()) {
interpreter->reportError(expression->oper.line, expression->oper.column,
"Runtime Error", "Can only index arrays", "");
throw std::runtime_error("Can only index arrays");
}
if (!index.isNumber()) {
interpreter->reportError(expression->oper.line, expression->oper.column,
"Runtime Error", "Array index must be a number", "");
throw std::runtime_error("Array index must be a number");
}
int idx = static_cast<int>(index.asNumber());
std::vector<Value>& arr = array.asArray();
if (idx < 0 || idx >= static_cast<int>(arr.size())) {
interpreter->reportError(arrayExpr->bracket.line, arrayExpr->bracket.column,
"Runtime Error", "Array index out of bounds", "");
throw std::runtime_error("Array index out of bounds");
}
// Update the array element
arr[idx] = Value(newValue);
} else {
interpreter->reportError(expression->oper.line, expression->oper.column,
"Runtime Error", "Increment/decrement can only be applied to variables or array elements.", "");
throw std::runtime_error("Increment/decrement can only be applied to variables or array elements.");
}
// Return the appropriate value based on prefix/postfix
if (expression->isPrefix) {
return Value(newValue); // Prefix: return new value
} else {
return currentValue; // Postfix: return old value
}
}
Value Evaluator::visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) {
Value value = interpreter->evaluate(expression->value);
if (expression->op.type == EQUAL) {
// Assign first to release references held by the old values
interpreter->getEnvironment()->assign(expression->name, value);
// Perform cleanup on any reassignment
interpreter->forceCleanup();
return value;
}
// Compound assignment operators
Value currentValue = interpreter->getEnvironment()->get(expression->name);
try {
Value newValue = computeCompoundAssignment(currentValue, expression->op.type, value);
interpreter->getEnvironment()->assign(expression->name, newValue);
return newValue;
} catch (const std::runtime_error&) {
interpreter->reportError(expression->op.line, expression->op.column, "Runtime Error",
"Unknown assignment operator: " + expression->op.lexeme, "");
throw;
}
}
Value Evaluator::visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expression) {
Value condition = interpreter->evaluate(expression->condition);
if (interpreter->isTruthy(condition)) {
return interpreter->evaluate(expression->thenExpr);
} else {
return interpreter->evaluate(expression->elseExpr);
}
}
Value Evaluator::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
// Delegate to inline implementation in Interpreter for performance
return interpreter->evaluateCallExprInline(expression);
}
Value Evaluator::visitArrayLiteralExpr(const std::shared_ptr<ArrayLiteralExpr>& expr) {
std::vector<Value> elements;
for (const auto& element : expr->elements) {
elements.push_back(interpreter->evaluate(element));
}
return Value(elements);
}
Value Evaluator::visitArrayIndexExpr(const std::shared_ptr<ArrayIndexExpr>& expr) {
Value array = expr->array->accept(this);
Value index = expr->index->accept(this);
if (array.isArray()) {
// Handle array indexing
if (!index.isNumber()) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Array index must be a number", "");
throw std::runtime_error("Array index must be a number");
}
int idx = static_cast<int>(index.asNumber());
const std::vector<Value>& arr = array.asArray();
if (idx < 0 || idx >= static_cast<int>(arr.size())) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Array index out of bounds", "");
throw std::runtime_error("Array index out of bounds");
}
return arr[idx];
} else if (array.isDict()) {
// Handle dictionary indexing
if (!index.isString()) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Dictionary key must be a string", "");
throw std::runtime_error("Dictionary key must be a string");
}
std::string key = index.asString();
const std::unordered_map<std::string, Value>& dict = array.asDict();
auto it = dict.find(key);
if (it != dict.end()) {
return it->second;
} else {
return NONE_VALUE; // Return none for missing keys
}
} else {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Can only index arrays and dictionaries", "");
throw std::runtime_error("Can only index arrays and dictionaries");
}
}
Value Evaluator::visitPropertyExpr(const std::shared_ptr<PropertyExpr>& expr) {
Value object = expr->object->accept(this);
std::string propertyName = expr->name.lexeme;
if (object.isDict()) {
return getDictProperty(object, propertyName);
} else if (object.isArray()) {
return getArrayProperty(object, propertyName);
} else {
interpreter->reportError(expr->name.line, expr->name.column, "Runtime Error",
"Cannot access property '" + propertyName + "' on this type", "");
throw std::runtime_error("Cannot access property '" + propertyName + "' on this type");
}
}
Value Evaluator::visitArrayAssignExpr(const std::shared_ptr<ArrayAssignExpr>& expr) {
Value array = expr->array->accept(this);
Value index = expr->index->accept(this);
Value value = expr->value->accept(this);
if (array.isArray()) {
// Handle array assignment
if (!index.isNumber()) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Array index must be a number", "");
throw std::runtime_error("Array index must be a number");
}
int idx = static_cast<int>(index.asNumber());
std::vector<Value>& arr = array.asArray();
if (idx < 0 || idx >= static_cast<int>(arr.size())) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Array index out of bounds", "");
throw std::runtime_error("Array index out of bounds");
}
arr[idx] = value;
return value;
} else if (array.isDict()) {
// Handle dictionary assignment
if (!index.isString()) {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Dictionary key must be a string", "");
throw std::runtime_error("Dictionary key must be a string");
}
std::string key = index.asString();
std::unordered_map<std::string, Value>& dict = array.asDict();
dict[key] = value;
return value;
} else {
interpreter->reportError(expr->bracket.line, expr->bracket.column, "Runtime Error",
"Can only assign to array or dictionary elements", "");
throw std::runtime_error("Can only assign to array or dictionary elements");
}
}
Value Evaluator::visitDictLiteralExpr(const std::shared_ptr<DictLiteralExpr>& expr) {
std::unordered_map<std::string, Value> dict;
for (const auto& pair : expr->pairs) {
Value value = interpreter->evaluate(pair.second);
dict[pair.first] = value;
}
return Value(dict);
}
Value Evaluator::visitPropertyAssignExpr(const std::shared_ptr<PropertyAssignExpr>& expr) {
Value object = expr->object->accept(this);
Value value = expr->value->accept(this);
std::string propertyName = expr->name.lexeme;
if (object.isDict()) {
// Modify the dictionary in place
std::unordered_map<std::string, Value>& dict = object.asDict();
dict[propertyName] = value;
return value; // Return the assigned value
} else {
interpreter->reportError(expr->name.line, expr->name.column, "Runtime Error",
"Cannot assign property '" + propertyName + "' on non-object", "");
throw std::runtime_error("Cannot assign property '" + propertyName + "' on non-object");
}
}
Value Evaluator::visitFunctionExpr(const std::shared_ptr<FunctionExpr>& expression) {
std::vector<std::string> paramNames;
for (const Token& param : expression->params) {
paramNames.push_back(param.lexeme);
}
auto closureEnv = std::make_shared<Environment>(*interpreter->getEnvironment());
closureEnv->pruneForClosureCapture();
auto function = std::make_shared<Function>("", paramNames, expression->body, closureEnv);
return Value(function);
}
Value Evaluator::getArrayProperty(const Value& arrayValue, const std::string& propertyName) {
const std::vector<Value>& arr = arrayValue.asArray();
// Create builtin array properties as an actual dictionary
std::unordered_map<std::string, Value> arrayProperties;
arrayProperties["length"] = Value(static_cast<double>(arr.size()));
arrayProperties["empty"] = Value(arr.empty());
if (!arr.empty()) {
arrayProperties["first"] = arr[0];
arrayProperties["last"] = arr[arr.size() - 1];
} else {
arrayProperties["first"] = NONE_VALUE;
arrayProperties["last"] = NONE_VALUE;
}
// Look up the requested property
auto it = arrayProperties.find(propertyName);
if (it != arrayProperties.end()) {
return it->second;
} else {
return NONE_VALUE; // Unknown property
}
}
Value Evaluator::getDictProperty(const Value& dictValue, const std::string& propertyName) {
const std::unordered_map<std::string, Value>& dict = dictValue.asDict();
// First check if it's a user-defined property
auto userProp = dict.find(propertyName);
if (userProp != dict.end()) {
return userProp->second;
}
// If not found, check for builtin dictionary properties
std::unordered_map<std::string, Value> dictProperties;
dictProperties["length"] = Value(static_cast<double>(dict.size()));
dictProperties["empty"] = Value(dict.empty());
// Create keys array
std::vector<Value> keysArray;
for (const auto& pair : dict) {
keysArray.push_back(Value(pair.first));
}
dictProperties["keys"] = Value(keysArray);
// Create values array
std::vector<Value> valuesArray;
for (const auto& pair : dict) {
valuesArray.push_back(pair.second);
}
dictProperties["values"] = Value(valuesArray);
auto builtinProp = dictProperties.find(propertyName);
if (builtinProp != dictProperties.end()) {
return builtinProp->second;
}
// Property not found
return NONE_VALUE;
}