Code cleanup
This commit is contained in:
parent
85d3381575
commit
266cca5b42
35
src/headers/runtime/AssignmentUtils.h
Normal file
35
src/headers/runtime/AssignmentUtils.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
#include "Lexer.h"
|
||||||
|
|
||||||
|
// Utility to compute the result of a compound assignment (e.g., +=, -=, etc.)
|
||||||
|
// Leaves error reporting to callers; throws std::runtime_error on unknown operator
|
||||||
|
inline Value computeCompoundAssignment(const Value& currentValue, TokenType opType, const Value& rhs) {
|
||||||
|
switch (opType) {
|
||||||
|
case PLUS_EQUAL:
|
||||||
|
return currentValue + rhs;
|
||||||
|
case MINUS_EQUAL:
|
||||||
|
return currentValue - rhs;
|
||||||
|
case STAR_EQUAL:
|
||||||
|
return currentValue * rhs;
|
||||||
|
case SLASH_EQUAL:
|
||||||
|
return currentValue / rhs;
|
||||||
|
case PERCENT_EQUAL:
|
||||||
|
return currentValue % rhs;
|
||||||
|
case BIN_AND_EQUAL:
|
||||||
|
return currentValue & rhs;
|
||||||
|
case BIN_OR_EQUAL:
|
||||||
|
return currentValue | rhs;
|
||||||
|
case BIN_XOR_EQUAL:
|
||||||
|
return currentValue ^ rhs;
|
||||||
|
case BIN_SLEFT_EQUAL:
|
||||||
|
return currentValue << rhs;
|
||||||
|
case BIN_SRIGHT_EQUAL:
|
||||||
|
return currentValue >> rhs;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unknown compound assignment operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -41,19 +41,11 @@ public:
|
|||||||
// Enhanced get with error reporting
|
// Enhanced get with error reporting
|
||||||
Value get(const Token& name);
|
Value get(const Token& name);
|
||||||
|
|
||||||
// Get by string name with error reporting
|
|
||||||
Value get(const std::string& name);
|
|
||||||
|
|
||||||
// Prune heavy containers in a snapshot to avoid capture cycles
|
// Prune heavy containers in a snapshot to avoid capture cycles
|
||||||
void pruneForClosureCapture();
|
void pruneForClosureCapture();
|
||||||
|
|
||||||
std::shared_ptr<Environment> getParent() const { return parent; }
|
std::shared_ptr<Environment> getParent() const { return parent; }
|
||||||
inline void clear() { variables.clear(); }
|
|
||||||
|
|
||||||
// Set parent environment for TCO environment reuse
|
|
||||||
inline void setParent(std::shared_ptr<Environment> newParent) {
|
|
||||||
parent = newParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, Value> variables;
|
std::unordered_map<std::string, Value> variables;
|
||||||
|
|||||||
@ -1,15 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Expression.h"
|
|
||||||
#include "Statement.h"
|
|
||||||
#include "helperFunctions/ShortHands.h"
|
|
||||||
#include "TypeWrapper.h"
|
|
||||||
#include "Environment.h"
|
|
||||||
#include "Value.h"
|
|
||||||
#include "BobStdLib.h"
|
|
||||||
#include "ErrorReporter.h"
|
|
||||||
#include "ExecutionContext.h"
|
|
||||||
#include "RuntimeDiagnostics.h"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -17,6 +6,21 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
#include "RuntimeDiagnostics.h"
|
||||||
|
|
||||||
|
struct Expr;
|
||||||
|
struct Stmt;
|
||||||
|
struct Environment;
|
||||||
|
struct BuiltinFunction;
|
||||||
|
struct Function;
|
||||||
|
struct Thunk;
|
||||||
|
class ErrorReporter;
|
||||||
|
struct ExecutionContext;
|
||||||
|
struct CallExpr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
class Evaluator;
|
class Evaluator;
|
||||||
|
|
||||||
@ -66,9 +70,8 @@ private:
|
|||||||
bool inThunkExecution = false;
|
bool inThunkExecution = false;
|
||||||
|
|
||||||
// Automatic cleanup tracking
|
// Automatic cleanup tracking
|
||||||
int functionCreationCount = 0;
|
|
||||||
int thunkCreationCount = 0;
|
int thunkCreationCount = 0;
|
||||||
static const int CLEANUP_THRESHOLD = 1000000;
|
static const int CLEANUP_THRESHOLD = 10000;
|
||||||
|
|
||||||
RuntimeDiagnostics diagnostics; // Utility functions for runtime operations
|
RuntimeDiagnostics diagnostics; // Utility functions for runtime operations
|
||||||
std::unique_ptr<Evaluator> evaluator;
|
std::unique_ptr<Evaluator> evaluator;
|
||||||
@ -93,25 +96,17 @@ public:
|
|||||||
bool isInteractiveMode() const;
|
bool isInteractiveMode() const;
|
||||||
std::shared_ptr<Environment> getEnvironment();
|
std::shared_ptr<Environment> getEnvironment();
|
||||||
void setEnvironment(std::shared_ptr<Environment> env);
|
void setEnvironment(std::shared_ptr<Environment> env);
|
||||||
void addThunk(std::shared_ptr<Thunk> thunk);
|
|
||||||
void addFunction(std::shared_ptr<Function> function);
|
void addFunction(std::shared_ptr<Function> function);
|
||||||
void reportError(int line, int column, const std::string& errorType, const std::string& message, const std::string& lexeme = "");
|
void reportError(int line, int column, const std::string& errorType, const std::string& message, const std::string& lexeme = "");
|
||||||
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
|
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
|
||||||
void cleanupUnusedFunctions();
|
void cleanupUnusedFunctions();
|
||||||
void cleanupUnusedThunks();
|
void cleanupUnusedThunks();
|
||||||
void forceCleanup();
|
void forceCleanup();
|
||||||
|
void addStdLibFunctions();
|
||||||
|
|
||||||
// Function creation count management
|
|
||||||
void incrementFunctionCreationCount();
|
|
||||||
int getFunctionCreationCount() const;
|
|
||||||
void resetFunctionCreationCount();
|
|
||||||
int getCleanupThreshold() const;
|
|
||||||
|
|
||||||
// Public access for Evaluator
|
|
||||||
bool& getInThunkExecutionRef() { return inThunkExecution; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Value evaluateWithoutTrampoline(const std::shared_ptr<Expr>& expr);
|
|
||||||
void addStdLibFunctions();
|
|
||||||
Value runTrampoline(Value initialResult);
|
Value runTrampoline(Value initialResult);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -28,8 +28,6 @@ public:
|
|||||||
void cleanupUnusedFunctions(std::vector<std::shared_ptr<BuiltinFunction>>& functions);
|
void cleanupUnusedFunctions(std::vector<std::shared_ptr<BuiltinFunction>>& functions);
|
||||||
void cleanupUnusedFunctions(std::vector<std::shared_ptr<Function>>& functions);
|
void cleanupUnusedFunctions(std::vector<std::shared_ptr<Function>>& functions);
|
||||||
void cleanupUnusedThunks(std::vector<std::shared_ptr<Thunk>>& thunks);
|
void cleanupUnusedThunks(std::vector<std::shared_ptr<Thunk>>& thunks);
|
||||||
void forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& functions,
|
|
||||||
std::vector<std::shared_ptr<Thunk>>& thunks);
|
|
||||||
void forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& builtinFunctions,
|
void forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& builtinFunctions,
|
||||||
std::vector<std::shared_ptr<Function>>& functions,
|
std::vector<std::shared_ptr<Function>>& functions,
|
||||||
std::vector<std::shared_ptr<Thunk>>& thunks);
|
std::vector<std::shared_ptr<Thunk>>& thunks);
|
||||||
|
|||||||
@ -10,38 +10,7 @@
|
|||||||
struct Stmt;
|
struct Stmt;
|
||||||
struct Environment;
|
struct Environment;
|
||||||
|
|
||||||
struct Object
|
struct Function
|
||||||
{
|
|
||||||
virtual ~Object(){};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Number : Object
|
|
||||||
{
|
|
||||||
double value;
|
|
||||||
explicit Number(double value) : value(value) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct String : Object
|
|
||||||
{
|
|
||||||
std::string value;
|
|
||||||
explicit String(std::string str) : value(str) {}
|
|
||||||
~String(){
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Boolean : Object
|
|
||||||
{
|
|
||||||
bool value;
|
|
||||||
explicit Boolean(bool value) : value(value) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct None : public Object
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Function : public Object
|
|
||||||
{
|
{
|
||||||
const std::string name;
|
const std::string name;
|
||||||
const std::vector<std::string> params;
|
const std::vector<std::string> params;
|
||||||
@ -54,7 +23,7 @@ struct Function : public Object
|
|||||||
: name(name), params(params), body(body), closure(closure) {}
|
: name(name), params(params), body(body), closure(closure) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFunction : public Object
|
struct BuiltinFunction
|
||||||
{
|
{
|
||||||
const std::string name;
|
const std::string name;
|
||||||
const std::function<Value(std::vector<Value>, int, int)> func;
|
const std::function<Value(std::vector<Value>, int, int)> func;
|
||||||
|
|||||||
@ -424,5 +424,3 @@ struct Value {
|
|||||||
extern const Value NONE_VALUE;
|
extern const Value NONE_VALUE;
|
||||||
extern const Value TRUE_VALUE;
|
extern const Value TRUE_VALUE;
|
||||||
extern const Value FALSE_VALUE;
|
extern const Value FALSE_VALUE;
|
||||||
extern const Value ZERO_VALUE;
|
|
||||||
extern const Value ONE_VALUE;
|
|
||||||
@ -22,8 +22,8 @@ void Bob::runFile(const std::string& path)
|
|||||||
// Load source code into error reporter for context
|
// Load source code into error reporter for context
|
||||||
errorReporter.loadSource(source, path);
|
errorReporter.loadSource(source, path);
|
||||||
|
|
||||||
// Connect error reporter to interpreter
|
|
||||||
interpreter->setErrorReporter(&errorReporter);
|
interpreter->setErrorReporter(&errorReporter);
|
||||||
|
interpreter->addStdLibFunctions();
|
||||||
|
|
||||||
this->run(source);
|
this->run(source);
|
||||||
}
|
}
|
||||||
@ -52,6 +52,7 @@ void Bob::runPrompt()
|
|||||||
|
|
||||||
// Connect error reporter to interpreter
|
// Connect error reporter to interpreter
|
||||||
interpreter->setErrorReporter(&errorReporter);
|
interpreter->setErrorReporter(&errorReporter);
|
||||||
|
interpreter->addStdLibFunctions();
|
||||||
|
|
||||||
this->run(line);
|
this->run(line);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,27 +37,14 @@ Value Environment::get(const Token& name) {
|
|||||||
throw std::runtime_error("Undefined variable '" + name.lexeme + "'");
|
throw std::runtime_error("Undefined variable '" + name.lexeme + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Environment::get(const std::string& name) {
|
|
||||||
auto it = variables.find(name);
|
|
||||||
if (it != variables.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent != nullptr) {
|
|
||||||
return parent->get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Undefined variable '" + name + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Environment::pruneForClosureCapture() {
|
void Environment::pruneForClosureCapture() {
|
||||||
for (auto &entry : variables) {
|
for (auto &entry : variables) {
|
||||||
Value &v = entry.second;
|
Value &v = entry.second;
|
||||||
if (v.isArray()) {
|
if (v.isArray()) {
|
||||||
// Replace with a new empty array to avoid mutating original shared storage
|
|
||||||
entry.second = Value(std::vector<Value>{});
|
entry.second = Value(std::vector<Value>{});
|
||||||
} else if (v.isDict()) {
|
} else if (v.isDict()) {
|
||||||
// Replace with a new empty dict to avoid mutating original shared storage
|
|
||||||
entry.second = Value(std::unordered_map<std::string, Value>{});
|
entry.second = Value(std::unordered_map<std::string, Value>{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "Evaluator.h"
|
#include "Evaluator.h"
|
||||||
#include "Interpreter.h"
|
#include "Interpreter.h"
|
||||||
|
#include "Environment.h"
|
||||||
|
#include "AssignmentUtils.h"
|
||||||
#include "helperFunctions/HelperFunctions.h"
|
#include "helperFunctions/HelperFunctions.h"
|
||||||
|
|
||||||
Evaluator::Evaluator(Interpreter* interpreter) : interpreter(interpreter) {}
|
Evaluator::Evaluator(Interpreter* interpreter) : interpreter(interpreter) {}
|
||||||
@ -214,74 +216,24 @@ Value Evaluator::visitAssignExpr(const std::shared_ptr<AssignExpr>& expression)
|
|||||||
Value value = interpreter->evaluate(expression->value);
|
Value value = interpreter->evaluate(expression->value);
|
||||||
|
|
||||||
if (expression->op.type == EQUAL) {
|
if (expression->op.type == EQUAL) {
|
||||||
try {
|
|
||||||
// Check if the variable existed and whether it held a collection
|
|
||||||
bool existed = false;
|
|
||||||
bool wasCollection = false;
|
|
||||||
try {
|
|
||||||
Value oldValue = interpreter->getEnvironment()->get(expression->name);
|
|
||||||
existed = true;
|
|
||||||
wasCollection = oldValue.isArray() || oldValue.isDict();
|
|
||||||
} catch (...) {
|
|
||||||
existed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign first to release references held by the old values
|
// Assign first to release references held by the old values
|
||||||
interpreter->getEnvironment()->assign(expression->name, value);
|
interpreter->getEnvironment()->assign(expression->name, value);
|
||||||
|
// Perform cleanup on any reassignment
|
||||||
// Now that the old values are released, perform cleanup on any reassignment
|
|
||||||
interpreter->forceCleanup();
|
interpreter->forceCleanup();
|
||||||
} catch (const std::exception& e) {
|
return value;
|
||||||
std::cerr << "Error during assignment: " << e.what() << std::endl;
|
|
||||||
throw; // Re-throw to see the full stack trace
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Handle compound assignment operators
|
// Compound assignment operators
|
||||||
Value currentValue = interpreter->getEnvironment()->get(expression->name);
|
Value currentValue = interpreter->getEnvironment()->get(expression->name);
|
||||||
Value newValue;
|
try {
|
||||||
|
Value newValue = computeCompoundAssignment(currentValue, expression->op.type, value);
|
||||||
switch (expression->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(expression->op.line, expression->op.column, "Runtime Error",
|
|
||||||
"Unknown assignment operator: " + expression->op.lexeme, "");
|
|
||||||
throw std::runtime_error("Unknown assignment operator: " + expression->op.lexeme);
|
|
||||||
}
|
|
||||||
|
|
||||||
interpreter->getEnvironment()->assign(expression->name, newValue);
|
interpreter->getEnvironment()->assign(expression->name, newValue);
|
||||||
return 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Evaluator::visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expression) {
|
Value Evaluator::visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expression) {
|
||||||
@ -453,10 +405,8 @@ Value Evaluator::visitFunctionExpr(const std::shared_ptr<FunctionExpr>& expressi
|
|||||||
paramNames.push_back(param.lexeme);
|
paramNames.push_back(param.lexeme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture a snapshot of the current environment so loop vars like 'i' are frozen per iteration
|
|
||||||
auto closureEnv = std::make_shared<Environment>(*interpreter->getEnvironment());
|
auto closureEnv = std::make_shared<Environment>(*interpreter->getEnvironment());
|
||||||
closureEnv->pruneForClosureCapture();
|
closureEnv->pruneForClosureCapture();
|
||||||
|
|
||||||
auto function = std::make_shared<Function>("", paramNames, expression->body, closureEnv);
|
auto function = std::make_shared<Function>("", paramNames, expression->body, closureEnv);
|
||||||
return Value(function);
|
return Value(function);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#include "Executor.h"
|
#include "Executor.h"
|
||||||
#include "Evaluator.h"
|
#include "Evaluator.h"
|
||||||
#include "Interpreter.h"
|
#include "Interpreter.h"
|
||||||
|
#include "Environment.h"
|
||||||
|
#include "AssignmentUtils.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
Executor::Executor(Interpreter* interpreter, Evaluator* evaluator)
|
Executor::Executor(Interpreter* interpreter, Evaluator* evaluator)
|
||||||
@ -194,66 +196,24 @@ void Executor::visitContinueStmt(const std::shared_ptr<ContinueStmt>& statement,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Executor::visitAssignStmt(const std::shared_ptr<AssignStmt>& statement, ExecutionContext* context) {
|
void Executor::visitAssignStmt(const std::shared_ptr<AssignStmt>& statement, ExecutionContext* context) {
|
||||||
try {
|
|
||||||
Value value = statement->value->accept(evaluator);
|
Value value = statement->value->accept(evaluator);
|
||||||
|
|
||||||
if (statement->op.type == EQUAL) {
|
if (statement->op.type == EQUAL) {
|
||||||
try {
|
|
||||||
// Assign first to release references held by the old value
|
// Assign first to release references held by the old value
|
||||||
interpreter->getEnvironment()->assign(statement->name, value);
|
interpreter->getEnvironment()->assign(statement->name, value);
|
||||||
|
// Clean up on any reassignment
|
||||||
// Clean up on any reassignment, regardless of old/new type
|
|
||||||
interpreter->forceCleanup();
|
interpreter->forceCleanup();
|
||||||
} catch (const std::exception& e) {
|
return;
|
||||||
std::cerr << "Error during assignment: " << e.what() << std::endl;
|
|
||||||
throw; // Re-throw to see the full stack trace
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Handle compound assignment operators
|
|
||||||
Value currentValue = interpreter->getEnvironment()->get(statement->name);
|
|
||||||
Value newValue;
|
|
||||||
|
|
||||||
switch (statement->op.type) {
|
// Compound assignment operators
|
||||||
case PLUS_EQUAL:
|
Value currentValue = interpreter->getEnvironment()->get(statement->name);
|
||||||
newValue = currentValue + value;
|
try {
|
||||||
break;
|
Value newValue = computeCompoundAssignment(currentValue, statement->op.type, value);
|
||||||
case MINUS_EQUAL:
|
interpreter->getEnvironment()->assign(statement->name, newValue);
|
||||||
newValue = currentValue - value;
|
} catch (const std::runtime_error&) {
|
||||||
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",
|
interpreter->reportError(statement->op.line, statement->op.column, "Runtime Error",
|
||||||
"Unknown assignment operator: " + statement->op.lexeme, "");
|
"Unknown assignment operator: " + statement->op.lexeme, "");
|
||||||
throw std::runtime_error("Unknown assignment operator: " + statement->op.lexeme);
|
throw;
|
||||||
}
|
|
||||||
|
|
||||||
interpreter->getEnvironment()->assign(statement->name, newValue);
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error in visitAssignStmt: " << e.what() << std::endl;
|
|
||||||
throw; // Re-throw to see the full stack trace
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
#include "Evaluator.h"
|
#include "Evaluator.h"
|
||||||
#include "Executor.h"
|
#include "Executor.h"
|
||||||
#include "BobStdLib.h"
|
#include "BobStdLib.h"
|
||||||
|
#include "ErrorReporter.h"
|
||||||
|
#include "Environment.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
Interpreter::Interpreter(bool isInteractive)
|
Interpreter::Interpreter(bool isInteractive)
|
||||||
@ -61,9 +63,7 @@ void Interpreter::addBuiltinFunction(std::shared_ptr<BuiltinFunction> func) {
|
|||||||
builtinFunctions.push_back(func);
|
builtinFunctions.push_back(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::addThunk(std::shared_ptr<Thunk> thunk) {
|
|
||||||
thunks.push_back(thunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interpreter::addFunction(std::shared_ptr<Function> function) {
|
void Interpreter::addFunction(std::shared_ptr<Function> function) {
|
||||||
functions.push_back(function);
|
functions.push_back(function);
|
||||||
@ -74,7 +74,6 @@ void Interpreter::setErrorReporter(ErrorReporter* reporter) {
|
|||||||
if (environment) {
|
if (environment) {
|
||||||
environment->setErrorReporter(reporter);
|
environment->setErrorReporter(reporter);
|
||||||
}
|
}
|
||||||
addStdLibFunctions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interpreter::isInteractiveMode() const {
|
bool Interpreter::isInteractiveMode() const {
|
||||||
@ -121,7 +120,6 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!callee.isFunction()) {
|
if (!callee.isFunction()) {
|
||||||
// Provide better error message with type information (like original)
|
|
||||||
std::string errorMsg = "Can only call functions, got " + callee.getType();
|
std::string errorMsg = "Can only call functions, got " + callee.getType();
|
||||||
if (errorReporter) {
|
if (errorReporter) {
|
||||||
errorReporter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
errorReporter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
||||||
@ -150,9 +148,8 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
|
|
||||||
// Check if this is a tail call for inline TCO
|
// Check if this is a tail call for inline TCO
|
||||||
if (expression->isTailCall) {
|
if (expression->isTailCall) {
|
||||||
// Create a thunk for tail call optimization - original inline version
|
|
||||||
auto thunk = std::make_shared<Thunk>([this, function, arguments]() -> Value {
|
auto thunk = std::make_shared<Thunk>([this, function, arguments]() -> Value {
|
||||||
// Use RAII to manage environment (exactly like original)
|
|
||||||
ScopedEnv _env(environment);
|
ScopedEnv _env(environment);
|
||||||
environment = std::make_shared<Environment>(function->closure);
|
environment = std::make_shared<Environment>(function->closure);
|
||||||
environment->setErrorReporter(errorReporter);
|
environment->setErrorReporter(errorReporter);
|
||||||
@ -164,12 +161,10 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
ExecutionContext context;
|
ExecutionContext context;
|
||||||
context.isFunctionBody = true;
|
context.isFunctionBody = true;
|
||||||
|
|
||||||
// Use RAII to manage thunk execution flag
|
|
||||||
ScopedThunkFlag _inThunk(inThunkExecution);
|
ScopedThunkFlag _inThunk(inThunkExecution);
|
||||||
|
|
||||||
// Execute function body (inline like original - direct accept for performance)
|
|
||||||
for (const auto& stmt : function->body) {
|
for (const auto& stmt : function->body) {
|
||||||
stmt->accept(executor.get(), &context); // Direct call like original
|
stmt->accept(executor.get(), &context);
|
||||||
if (context.hasReturn) {
|
if (context.hasReturn) {
|
||||||
return context.returnValue;
|
return context.returnValue;
|
||||||
}
|
}
|
||||||
@ -178,10 +173,8 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
return context.returnValue;
|
return context.returnValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the thunk to keep it alive and return as Value (exactly like original)
|
|
||||||
thunks.push_back(thunk);
|
thunks.push_back(thunk);
|
||||||
|
|
||||||
// Automatic cleanup check
|
|
||||||
thunkCreationCount++;
|
thunkCreationCount++;
|
||||||
if (thunkCreationCount >= CLEANUP_THRESHOLD) {
|
if (thunkCreationCount >= CLEANUP_THRESHOLD) {
|
||||||
cleanupUnusedThunks();
|
cleanupUnusedThunks();
|
||||||
@ -190,7 +183,6 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
|
|
||||||
return Value(thunk);
|
return Value(thunk);
|
||||||
} else {
|
} else {
|
||||||
// Normal function call - create new environment (exactly like original)
|
|
||||||
ScopedEnv _env(environment);
|
ScopedEnv _env(environment);
|
||||||
environment = std::make_shared<Environment>(function->closure);
|
environment = std::make_shared<Environment>(function->closure);
|
||||||
environment->setErrorReporter(errorReporter);
|
environment->setErrorReporter(errorReporter);
|
||||||
@ -202,9 +194,8 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
ExecutionContext context;
|
ExecutionContext context;
|
||||||
context.isFunctionBody = true;
|
context.isFunctionBody = true;
|
||||||
|
|
||||||
// Execute function body (exactly like original - direct accept for performance)
|
|
||||||
for (const auto& stmt : function->body) {
|
for (const auto& stmt : function->body) {
|
||||||
stmt->accept(executor.get(), &context); // Direct call like original
|
stmt->accept(executor.get(), &context);
|
||||||
if (context.hasReturn) {
|
if (context.hasReturn) {
|
||||||
return context.returnValue;
|
return context.returnValue;
|
||||||
}
|
}
|
||||||
@ -214,19 +205,3 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr<CallExpr>& expre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function creation count management
|
|
||||||
void Interpreter::incrementFunctionCreationCount() {
|
|
||||||
functionCreationCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Interpreter::getFunctionCreationCount() const {
|
|
||||||
return functionCreationCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interpreter::resetFunctionCreationCount() {
|
|
||||||
functionCreationCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Interpreter::getCleanupThreshold() const {
|
|
||||||
return 1000000; // Same as CLEANUP_THRESHOLD used for thunks
|
|
||||||
}
|
|
||||||
|
|||||||
@ -13,122 +13,19 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
bool RuntimeDiagnostics::isTruthy(Value object) {
|
bool RuntimeDiagnostics::isTruthy(Value object) { return object.isTruthy(); }
|
||||||
if(object.isBoolean()) {
|
|
||||||
return object.asBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(object.isNone()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(object.isNumber()) {
|
|
||||||
return object.asNumber() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(object.isString()) {
|
|
||||||
return object.asString().length() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeDiagnostics::isEqual(Value a, Value b) {
|
bool RuntimeDiagnostics::isEqual(Value a, Value b) {
|
||||||
// Handle none comparisons first
|
|
||||||
if (a.isNone() || b.isNone()) {
|
|
||||||
return a.isNone() && b.isNone();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle same type comparisons
|
|
||||||
if (a.isNumber() && b.isNumber()) {
|
|
||||||
return a.asNumber() == b.asNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isBoolean() && b.isBoolean()) {
|
|
||||||
return a.asBoolean() == b.asBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isString() && b.isString()) {
|
|
||||||
return a.asString() == b.asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isArray() && b.isArray()) {
|
|
||||||
const std::vector<Value>& arrA = a.asArray();
|
|
||||||
const std::vector<Value>& arrB = b.asArray();
|
|
||||||
|
|
||||||
if (arrA.size() != arrB.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < arrA.size(); i++) {
|
|
||||||
if (!isEqual(arrA[i], arrB[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isFunction() && b.isFunction()) {
|
|
||||||
// Functions are equal only if they are the same object
|
|
||||||
return a.asFunction() == b.asFunction();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isBuiltinFunction() && b.isBuiltinFunction()) {
|
|
||||||
// Builtin functions are equal only if they are the same object
|
|
||||||
return a.asBuiltinFunction() == b.asBuiltinFunction();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cross-type comparisons that make sense
|
|
||||||
if (a.isNumber() && b.isBoolean()) {
|
if (a.isNumber() && b.isBoolean()) {
|
||||||
// Numbers and booleans: 0 and false are equal, non-zero and true are equal
|
return b.asBoolean() ? (a.asNumber() != 0.0) : (a.asNumber() == 0.0);
|
||||||
if (b.asBoolean()) {
|
|
||||||
return a.asNumber() != 0.0;
|
|
||||||
} else {
|
|
||||||
return a.asNumber() == 0.0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isBoolean() && b.isNumber()) {
|
if (a.isBoolean() && b.isNumber()) {
|
||||||
// Same as above, but reversed
|
return a.asBoolean() ? (b.asNumber() != 0.0) : (b.asNumber() == 0.0);
|
||||||
if (a.asBoolean()) {
|
|
||||||
return b.asNumber() != 0.0;
|
|
||||||
} else {
|
|
||||||
return b.asNumber() == 0.0;
|
|
||||||
}
|
}
|
||||||
}
|
return a.equals(b);
|
||||||
|
|
||||||
// For all other type combinations, return false
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RuntimeDiagnostics::stringify(Value object) {
|
std::string RuntimeDiagnostics::stringify(Value object) { return object.toString(); }
|
||||||
if(object.isNone()) {
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
else if(object.isNumber()) {
|
|
||||||
return formatNumber(object.asNumber());
|
|
||||||
}
|
|
||||||
else if(object.isString()) {
|
|
||||||
return object.asString();
|
|
||||||
}
|
|
||||||
else if(object.isBoolean()) {
|
|
||||||
return object.asBoolean() == 1 ? "true" : "false";
|
|
||||||
}
|
|
||||||
else if(object.isFunction()) {
|
|
||||||
return "<function " + object.asFunction()->name + ">";
|
|
||||||
}
|
|
||||||
else if(object.isBuiltinFunction()) {
|
|
||||||
return "<builtin_function " + object.asBuiltinFunction()->name + ">";
|
|
||||||
}
|
|
||||||
else if(object.isArray()) {
|
|
||||||
return formatArray(object.asArray());
|
|
||||||
}
|
|
||||||
else if(object.isDict()) {
|
|
||||||
return formatDict(object.asDict());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Could not convert object to string");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RuntimeDiagnostics::formatNumber(double value) {
|
std::string RuntimeDiagnostics::formatNumber(double value) {
|
||||||
double integral = value;
|
double integral = value;
|
||||||
@ -150,31 +47,9 @@ std::string RuntimeDiagnostics::formatNumber(double value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RuntimeDiagnostics::formatArray(const std::vector<Value>& arr) {
|
std::string RuntimeDiagnostics::formatArray(const std::vector<Value>& arr) { return Value(arr).toString(); }
|
||||||
std::string result = "[";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < arr.size(); i++) {
|
std::string RuntimeDiagnostics::formatDict(const std::unordered_map<std::string, Value>& dict) { return Value(dict).toString(); }
|
||||||
if (i > 0) result += ", ";
|
|
||||||
result += stringify(arr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
result += "]";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RuntimeDiagnostics::formatDict(const std::unordered_map<std::string, Value>& dict) {
|
|
||||||
std::string result = "{";
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
for (const auto& pair : dict) {
|
|
||||||
if (!first) result += ", ";
|
|
||||||
result += "\"" + pair.first + "\": " + stringify(pair.second);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += "}";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeDiagnostics::cleanupUnusedFunctions(std::vector<std::shared_ptr<BuiltinFunction>>& functions) {
|
void RuntimeDiagnostics::cleanupUnusedFunctions(std::vector<std::shared_ptr<BuiltinFunction>>& functions) {
|
||||||
// Only remove functions that are definitely not referenced anywhere (use_count == 1)
|
// Only remove functions that are definitely not referenced anywhere (use_count == 1)
|
||||||
@ -212,25 +87,7 @@ void RuntimeDiagnostics::cleanupUnusedThunks(std::vector<std::shared_ptr<Thunk>>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeDiagnostics::forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& functions,
|
|
||||||
std::vector<std::shared_ptr<Thunk>>& thunks) {
|
|
||||||
// More aggressive cleanup when breaking array references
|
|
||||||
functions.erase(
|
|
||||||
std::remove_if(functions.begin(), functions.end(),
|
|
||||||
[](const std::shared_ptr<BuiltinFunction>& func) {
|
|
||||||
return func.use_count() <= 2; // More aggressive than == 1
|
|
||||||
}),
|
|
||||||
functions.end()
|
|
||||||
);
|
|
||||||
|
|
||||||
thunks.erase(
|
|
||||||
std::remove_if(thunks.begin(), thunks.end(),
|
|
||||||
[](const std::shared_ptr<Thunk>& thunk) {
|
|
||||||
return thunk.use_count() <= 2; // More aggressive than == 1
|
|
||||||
}),
|
|
||||||
thunks.end()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeDiagnostics::forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& builtinFunctions,
|
void RuntimeDiagnostics::forceCleanup(std::vector<std::shared_ptr<BuiltinFunction>>& builtinFunctions,
|
||||||
std::vector<std::shared_ptr<Function>>& functions,
|
std::vector<std::shared_ptr<Function>>& functions,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
|
#include "TypeWrapper.h"
|
||||||
#include "TypeWrapper.h"
|
#include "TypeWrapper.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|||||||
@ -4,5 +4,4 @@
|
|||||||
const Value NONE_VALUE = Value();
|
const Value NONE_VALUE = Value();
|
||||||
const Value TRUE_VALUE = Value(true);
|
const Value TRUE_VALUE = Value(true);
|
||||||
const Value FALSE_VALUE = Value(false);
|
const Value FALSE_VALUE = Value(false);
|
||||||
const Value ZERO_VALUE = Value(0.0);
|
|
||||||
const Value ONE_VALUE = Value(1.0);
|
|
||||||
@ -42,7 +42,7 @@ if (len(a[3692]) > 0) {
|
|||||||
if (a[3693]["func"]) {
|
if (a[3693]["func"]) {
|
||||||
a[3693]["func"](); // Nested dict function
|
a[3693]["func"](); // Nested dict function
|
||||||
}
|
}
|
||||||
print(a);
|
//print(a);
|
||||||
//writeFile("array_contents.txt", toString(a));
|
//writeFile("array_contents.txt", toString(a));
|
||||||
print("Array contents written to array_contents.txt");
|
print("Array contents written to array_contents.txt");
|
||||||
print("Memory before cleanup: " + memoryUsage() + " MB");
|
print("Memory before cleanup: " + memoryUsage() + " MB");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user