Cleanup
This commit is contained in:
parent
f70c6abd77
commit
85d3381575
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ build/
|
|||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build-ninja
|
build-ninja
|
||||||
|
build-release
|
||||||
|
|||||||
@ -266,6 +266,38 @@ var lines = readLines("config.txt");
|
|||||||
var exists = fileExists("test.txt");
|
var exists = fileExists("test.txt");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Standard Library Reference
|
||||||
|
|
||||||
|
The following built-ins are available by default. Unless specified, functions throw on invalid argument counts/types.
|
||||||
|
|
||||||
|
- print(x): prints x with newline
|
||||||
|
- printRaw(x): prints x without newline
|
||||||
|
- input(prompt?): reads a line from stdin (optional prompt)
|
||||||
|
- toString(x): returns string representation
|
||||||
|
- toNumber(s): parses string to number or returns none
|
||||||
|
- toInt(n): truncates number to integer
|
||||||
|
- toBoolean(x): converts to boolean using truthiness rules
|
||||||
|
- type(x): returns the type name as string
|
||||||
|
- len(x): length of array/string/dict
|
||||||
|
- push(arr, ...values): appends values to array in place, returns arr
|
||||||
|
- pop(arr): removes and returns last element
|
||||||
|
- keys(dict): returns array of keys
|
||||||
|
- values(dict): returns array of values
|
||||||
|
- has(dict, key): returns true if key exists
|
||||||
|
- readFile(path): returns entire file contents as string
|
||||||
|
- writeFile(path, content): writes content to file
|
||||||
|
- readLines(path): returns array of lines
|
||||||
|
- fileExists(path): boolean
|
||||||
|
- time(): microseconds since Unix epoch
|
||||||
|
- sleep(seconds): pauses execution
|
||||||
|
- random(): float in [0,1)
|
||||||
|
- eval(code): executes code string in current environment
|
||||||
|
- exit(code?): terminates the program
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Arrays support properties: length, first, last, empty
|
||||||
|
- Dicts support properties: length, empty, keys, values
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced Features
|
||||||
|
|
||||||
### String Interpolation
|
### String Interpolation
|
||||||
|
|||||||
@ -83,6 +83,10 @@ for (var i = 0; i < 1000000; i++) {
|
|||||||
print("Created " + len(selfRef) + " self-referencing structures");
|
print("Created " + len(selfRef) + " self-referencing structures");
|
||||||
print("Memory: " + memoryUsage() + " MB");
|
print("Memory: " + memoryUsage() + " MB");
|
||||||
input("Press Enter to clear self-ref structures...");
|
input("Press Enter to clear self-ref structures...");
|
||||||
|
// Break cycles explicitly so reference counting can reclaim memory deterministically
|
||||||
|
for (var i = 0; i < len(selfRef); i++) {
|
||||||
|
selfRef[i]["self"] = none;
|
||||||
|
}
|
||||||
selfRef = 123;
|
selfRef = 123;
|
||||||
print("Memory after clear: " + memoryUsage() + " MB");
|
print("Memory after clear: " + memoryUsage() + " MB");
|
||||||
input("Cleared. Check memory usage...");
|
input("Cleared. Check memory usage...");
|
||||||
|
|||||||
@ -1,434 +0,0 @@
|
|||||||
#include "Evaluator.h"
|
|
||||||
#include "Interpreter.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);
|
|
||||||
|
|
||||||
switch (expression->op.type) {
|
|
||||||
case PLUS_EQUAL:
|
|
||||||
case MINUS_EQUAL:
|
|
||||||
case STAR_EQUAL:
|
|
||||||
case SLASH_EQUAL:
|
|
||||||
case PERCENT_EQUAL:
|
|
||||||
case BIN_AND_EQUAL:
|
|
||||||
case BIN_OR_EQUAL:
|
|
||||||
case BIN_XOR_EQUAL:
|
|
||||||
case BIN_SLEFT_EQUAL:
|
|
||||||
case BIN_SRIGHT_EQUAL: {
|
|
||||||
Value currentValue = interpreter->getEnvironment()->get(expression->name);
|
|
||||||
|
|
||||||
// ... (rest of compound assignment logic) ...
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
interpreter->getEnvironment()->assign(expression->name, value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
Value callee = expression->callee->accept(this);
|
|
||||||
|
|
||||||
std::vector<Value> arguments;
|
|
||||||
for (const auto& argument : expression->arguments) {
|
|
||||||
arguments.push_back(argument->accept(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callee.isFunction()) {
|
|
||||||
Function* function = callee.asFunction();
|
|
||||||
|
|
||||||
// Check arity
|
|
||||||
if (arguments.size() != function->params.size()) {
|
|
||||||
interpreter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
|
||||||
"Expected " + std::to_string(function->params.size()) + " arguments but got " +
|
|
||||||
std::to_string(arguments.size()) + ".", "");
|
|
||||||
throw std::runtime_error("Wrong number of arguments.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new environment for function call
|
|
||||||
auto environment = std::make_shared<Environment>(function->closure);
|
|
||||||
for (size_t i = 0; i < function->params.size(); i++) {
|
|
||||||
environment->define(function->params[i], arguments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute function body
|
|
||||||
auto previous = interpreter->getEnvironment();
|
|
||||||
interpreter->setEnvironment(environment);
|
|
||||||
|
|
||||||
ExecutionContext context;
|
|
||||||
context.isFunctionBody = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const auto& stmt : function->body) {
|
|
||||||
interpreter->execute(stmt, &context);
|
|
||||||
if (context.hasReturn) {
|
|
||||||
interpreter->setEnvironment(previous);
|
|
||||||
return context.returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
interpreter->setEnvironment(previous);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
interpreter->setEnvironment(previous);
|
|
||||||
return NONE_VALUE;
|
|
||||||
|
|
||||||
} else if (callee.isBuiltinFunction()) {
|
|
||||||
BuiltinFunction* builtinFunction = callee.asBuiltinFunction();
|
|
||||||
return builtinFunction->func(arguments, expression->paren.line, expression->paren.column);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
interpreter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
|
||||||
"Can only call functions and classes.", "");
|
|
||||||
throw std::runtime_error("Can only call functions and classes.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::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::visitFunctionExpr(const std::shared_ptr<FunctionExpr>& expression) {
|
|
||||||
std::vector<std::string> paramNames;
|
|
||||||
for (const Token& param : expression->params) {
|
|
||||||
paramNames.push_back(param.lexeme);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto function = std::make_shared<Function>("", paramNames, expression->body, interpreter->getEnvironment());
|
|
||||||
interpreter->addFunction(function);
|
|
||||||
return Value(function);
|
|
||||||
}
|
|
||||||
@ -1,245 +0,0 @@
|
|||||||
#include "Executor.h"
|
|
||||||
#include "Evaluator.h"
|
|
||||||
#include "Interpreter.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
Executor::Executor(Interpreter* interpreter, Evaluator* evaluator)
|
|
||||||
: interpreter(interpreter), evaluator(evaluator) {}
|
|
||||||
|
|
||||||
Executor::~Executor() {}
|
|
||||||
|
|
||||||
void Executor::interpret(const std::vector<std::shared_ptr<Stmt>>& statements) {
|
|
||||||
for (const auto& statement : statements) {
|
|
||||||
execute(statement, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::execute(const std::shared_ptr<Stmt>& statement, ExecutionContext* context) {
|
|
||||||
statement->accept(this, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::executeBlock(const std::vector<std::shared_ptr<Stmt>>& statements, std::shared_ptr<Environment> env, ExecutionContext* context) {
|
|
||||||
std::shared_ptr<Environment> previous = interpreter->getEnvironment();
|
|
||||||
interpreter->setEnvironment(env);
|
|
||||||
|
|
||||||
for (const auto& statement : statements) {
|
|
||||||
execute(statement, context);
|
|
||||||
if (context && (context->hasReturn || context->shouldBreak || context->shouldContinue)) {
|
|
||||||
interpreter->setEnvironment(previous);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interpreter->setEnvironment(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitBlockStmt(const std::shared_ptr<BlockStmt>& statement, ExecutionContext* context) {
|
|
||||||
auto newEnv = std::make_shared<Environment>(interpreter->getEnvironment());
|
|
||||||
executeBlock(statement->statements, newEnv, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement, ExecutionContext* context) {
|
|
||||||
Value value = statement->expression->accept(evaluator);
|
|
||||||
|
|
||||||
if (interpreter->isInteractiveMode())
|
|
||||||
std::cout << "\u001b[38;5;8m[" << interpreter->stringify(value) << "]\u001b[38;5;15m\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitVarStmt(const std::shared_ptr<VarStmt>& statement, ExecutionContext* context) {
|
|
||||||
Value value = NONE_VALUE;
|
|
||||||
if (statement->initializer != nullptr) {
|
|
||||||
value = statement->initializer->accept(evaluator);
|
|
||||||
}
|
|
||||||
interpreter->getEnvironment()->define(statement->name.lexeme, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitFunctionStmt(const std::shared_ptr<FunctionStmt>& statement, ExecutionContext* context) {
|
|
||||||
std::vector<std::string> paramNames;
|
|
||||||
for (const Token& param : statement->params) {
|
|
||||||
paramNames.push_back(param.lexeme);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto function = std::make_shared<Function>(statement->name.lexeme,
|
|
||||||
paramNames,
|
|
||||||
statement->body,
|
|
||||||
interpreter->getEnvironment());
|
|
||||||
interpreter->addFunction(function);
|
|
||||||
interpreter->getEnvironment()->define(statement->name.lexeme, Value(function));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement, ExecutionContext* context) {
|
|
||||||
Value value = NONE_VALUE;
|
|
||||||
if (statement->value != nullptr) {
|
|
||||||
value = statement->value->accept(evaluator);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context && context->isFunctionBody) {
|
|
||||||
context->hasReturn = true;
|
|
||||||
context->returnValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitIfStmt(const std::shared_ptr<IfStmt>& statement, ExecutionContext* context) {
|
|
||||||
if (interpreter->isTruthy(statement->condition->accept(evaluator))) {
|
|
||||||
execute(statement->thenBranch, context);
|
|
||||||
} else if (statement->elseBranch != nullptr) {
|
|
||||||
execute(statement->elseBranch, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitWhileStmt(const std::shared_ptr<WhileStmt>& statement, ExecutionContext* context) {
|
|
||||||
ExecutionContext loopContext;
|
|
||||||
if (context) {
|
|
||||||
loopContext.isFunctionBody = context->isFunctionBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (interpreter->isTruthy(statement->condition->accept(evaluator))) {
|
|
||||||
execute(statement->body, &loopContext);
|
|
||||||
|
|
||||||
if (loopContext.hasReturn) {
|
|
||||||
if (context) {
|
|
||||||
context->hasReturn = true;
|
|
||||||
context->returnValue = loopContext.returnValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldBreak) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldContinue) {
|
|
||||||
loopContext.shouldContinue = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitDoWhileStmt(const std::shared_ptr<DoWhileStmt>& statement, ExecutionContext* context) {
|
|
||||||
ExecutionContext loopContext;
|
|
||||||
if (context) {
|
|
||||||
loopContext.isFunctionBody = context->isFunctionBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
execute(statement->body, &loopContext);
|
|
||||||
|
|
||||||
if (loopContext.hasReturn) {
|
|
||||||
if (context) {
|
|
||||||
context->hasReturn = true;
|
|
||||||
context->returnValue = loopContext.returnValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldBreak) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldContinue) {
|
|
||||||
loopContext.shouldContinue = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} while (interpreter->isTruthy(statement->condition->accept(evaluator)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitForStmt(const std::shared_ptr<ForStmt>& statement, ExecutionContext* context) {
|
|
||||||
if (statement->initializer != nullptr) {
|
|
||||||
execute(statement->initializer, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutionContext loopContext;
|
|
||||||
if (context) {
|
|
||||||
loopContext.isFunctionBody = context->isFunctionBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (statement->condition == nullptr || interpreter->isTruthy(statement->condition->accept(evaluator))) {
|
|
||||||
execute(statement->body, &loopContext);
|
|
||||||
|
|
||||||
if (loopContext.hasReturn) {
|
|
||||||
if (context) {
|
|
||||||
context->hasReturn = true;
|
|
||||||
context->returnValue = loopContext.returnValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldBreak) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopContext.shouldContinue) {
|
|
||||||
loopContext.shouldContinue = false;
|
|
||||||
if (statement->increment != nullptr) {
|
|
||||||
statement->increment->accept(evaluator);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statement->increment != nullptr) {
|
|
||||||
statement->increment->accept(evaluator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitBreakStmt(const std::shared_ptr<BreakStmt>& statement, ExecutionContext* context) {
|
|
||||||
if (context) {
|
|
||||||
context->shouldBreak = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitContinueStmt(const std::shared_ptr<ContinueStmt>& statement, ExecutionContext* context) {
|
|
||||||
if (context) {
|
|
||||||
context->shouldContinue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Executor::visitAssignStmt(const std::shared_ptr<AssignStmt>& statement, ExecutionContext* context) {
|
|
||||||
Value value = statement->value->accept(evaluator);
|
|
||||||
|
|
||||||
if (statement->op.type == EQUAL) {
|
|
||||||
interpreter->getEnvironment()->assign(statement->name, value);
|
|
||||||
} else {
|
|
||||||
// Handle compound assignment operators
|
|
||||||
Value currentValue = interpreter->getEnvironment()->get(statement->name);
|
|
||||||
Value newValue;
|
|
||||||
|
|
||||||
switch (statement->op.type) {
|
|
||||||
case PLUS_EQUAL:
|
|
||||||
newValue = currentValue + value;
|
|
||||||
break;
|
|
||||||
case MINUS_EQUAL:
|
|
||||||
newValue = currentValue - value;
|
|
||||||
break;
|
|
||||||
case STAR_EQUAL:
|
|
||||||
newValue = currentValue * value;
|
|
||||||
break;
|
|
||||||
case SLASH_EQUAL:
|
|
||||||
newValue = currentValue / value;
|
|
||||||
break;
|
|
||||||
case PERCENT_EQUAL:
|
|
||||||
newValue = currentValue % value;
|
|
||||||
break;
|
|
||||||
case BIN_AND_EQUAL:
|
|
||||||
newValue = currentValue & value;
|
|
||||||
break;
|
|
||||||
case BIN_OR_EQUAL:
|
|
||||||
newValue = currentValue | value;
|
|
||||||
break;
|
|
||||||
case BIN_XOR_EQUAL:
|
|
||||||
newValue = currentValue ^ value;
|
|
||||||
break;
|
|
||||||
case BIN_SLEFT_EQUAL:
|
|
||||||
newValue = currentValue << value;
|
|
||||||
break;
|
|
||||||
case BIN_SRIGHT_EQUAL:
|
|
||||||
newValue = currentValue >> value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
interpreter->reportError(statement->op.line, statement->op.column, "Runtime Error",
|
|
||||||
"Unknown assignment operator: " + statement->op.lexeme, "");
|
|
||||||
throw std::runtime_error("Unknown assignment operator: " + statement->op.lexeme);
|
|
||||||
}
|
|
||||||
|
|
||||||
interpreter->getEnvironment()->assign(statement->name, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user