General fixes
This commit is contained in:
parent
eacb86ec77
commit
b97715e549
@ -1,6 +1,4 @@
|
|||||||
//
|
// Expression AST nodes for Bob language
|
||||||
// Created by Bobby Lucero on 5/21/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|||||||
@ -86,14 +86,14 @@ public:
|
|||||||
|
|
||||||
void interpret(std::vector<std::shared_ptr<Stmt>> statements);
|
void interpret(std::vector<std::shared_ptr<Stmt>> statements);
|
||||||
|
|
||||||
explicit Interpreter(bool IsInteractive) : IsInteractive(IsInteractive), errorReporter(nullptr){
|
explicit Interpreter(bool isInteractive) : isInteractive(isInteractive), errorReporter(nullptr){
|
||||||
environment = std::make_shared<Environment>();
|
environment = std::make_shared<Environment>();
|
||||||
}
|
}
|
||||||
virtual ~Interpreter() = default;
|
virtual ~Interpreter() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Environment> environment;
|
std::shared_ptr<Environment> environment;
|
||||||
bool IsInteractive;
|
bool isInteractive;
|
||||||
std::vector<std::shared_ptr<BuiltinFunction>> builtinFunctions;
|
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; // Store thunks to prevent memory leaks
|
std::vector<std::shared_ptr<Thunk>> thunks; // Store thunks to prevent memory leaks
|
||||||
@ -104,6 +104,7 @@ private:
|
|||||||
int functionCreationCount = 0;
|
int functionCreationCount = 0;
|
||||||
int thunkCreationCount = 0;
|
int thunkCreationCount = 0;
|
||||||
static const int CLEANUP_THRESHOLD = 1000000; // Cleanup every 1M creations (effectively disabled for performance)
|
static const int CLEANUP_THRESHOLD = 1000000; // Cleanup every 1M creations (effectively disabled for performance)
|
||||||
|
static const int MAX_FUNCTION_PARAMETERS = 255; // Maximum number of function parameters
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "../headers/Parser.h"
|
#include "../headers/Parser.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter, ErrorReporter* errorReporter) {
|
void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter, ErrorReporter* errorReporter) {
|
||||||
// Create a built-in toString function
|
// Create a built-in toString function
|
||||||
@ -36,7 +37,7 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter&
|
|||||||
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
|
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
|
||||||
}
|
}
|
||||||
// Use the interpreter's stringify function
|
// Use the interpreter's stringify function
|
||||||
std::cout << interpreter.stringify(args[0]) << std::endl;
|
std::cout << interpreter.stringify(args[0]) << '\n';
|
||||||
return NONE_VALUE;
|
return NONE_VALUE;
|
||||||
});
|
});
|
||||||
env->define("print", Value(printFunc.get()));
|
env->define("print", Value(printFunc.get()));
|
||||||
@ -405,7 +406,6 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter&
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::exit(exitCode);
|
std::exit(exitCode);
|
||||||
return NONE_VALUE; // This line should never be reached
|
|
||||||
});
|
});
|
||||||
env->define("exit", Value(exitFunc.get()));
|
env->define("exit", Value(exitFunc.get()));
|
||||||
|
|
||||||
@ -414,18 +414,30 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter&
|
|||||||
|
|
||||||
// Create a built-in sleep function for animations and timing
|
// Create a built-in sleep function for animations and timing
|
||||||
auto sleepFunc = std::make_shared<BuiltinFunction>("sleep",
|
auto sleepFunc = std::make_shared<BuiltinFunction>("sleep",
|
||||||
[](std::vector<Value> args, int line, int column) -> Value {
|
[errorReporter](std::vector<Value> args, int line, int column) -> Value {
|
||||||
if (args.size() != 1) {
|
if (args.size() != 1) {
|
||||||
return NONE_VALUE; // Return none for wrong argument count
|
if (errorReporter) {
|
||||||
|
errorReporter->reportError(line, column, "StdLib Error",
|
||||||
|
"Expected 1 argument but got " + std::to_string(args.size()) + ".", "", true);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args[0].isNumber()) {
|
if (!args[0].isNumber()) {
|
||||||
return NONE_VALUE; // Return none for wrong type
|
if (errorReporter) {
|
||||||
|
errorReporter->reportError(line, column, "StdLib Error",
|
||||||
|
"sleep() argument must be a number", "", true);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("sleep() argument must be a number");
|
||||||
}
|
}
|
||||||
|
|
||||||
double seconds = args[0].asNumber();
|
double seconds = args[0].asNumber();
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return NONE_VALUE; // Return none for negative time
|
if (errorReporter) {
|
||||||
|
errorReporter->reportError(line, column, "StdLib Error",
|
||||||
|
"sleep() argument cannot be negative", "", true);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("sleep() argument cannot be negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to milliseconds and sleep
|
// Convert to milliseconds and sleep
|
||||||
@ -450,6 +462,13 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter&
|
|||||||
throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + ".");
|
throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seed the random number generator if not already done
|
||||||
|
static bool seeded = false;
|
||||||
|
if (!seeded) {
|
||||||
|
srand(static_cast<unsigned int>(time(nullptr)));
|
||||||
|
seeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
return Value(static_cast<double>(rand()) / RAND_MAX);
|
return Value(static_cast<double>(rand()) / RAND_MAX);
|
||||||
});
|
});
|
||||||
env->define("random", Value(randomFunc.get()));
|
env->define("random", Value(randomFunc.get()));
|
||||||
|
|||||||
@ -107,7 +107,8 @@ void ErrorReporter::displaySourceContext(int line, int column, const std::string
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxWidth = 65;
|
static const int ERROR_DISPLAY_MAX_WIDTH = 65;
|
||||||
|
int maxWidth = ERROR_DISPLAY_MAX_WIDTH;
|
||||||
int startLine = std::max(1, line - 4);
|
int startLine = std::max(1, line - 4);
|
||||||
int endLine = std::min(static_cast<int>(sourceLines.size()), line + 2);
|
int endLine = std::min(static_cast<int>(sourceLines.size()), line + 2);
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ void ErrorReporter::displaySourceContext(int line, int column, const std::string
|
|||||||
|
|
||||||
int errorLineWidth = 8 + column + 1 + static_cast<int>(message.length());
|
int errorLineWidth = 8 + column + 1 + static_cast<int>(message.length());
|
||||||
maxWidth = std::max(maxWidth, errorLineWidth);
|
maxWidth = std::max(maxWidth, errorLineWidth);
|
||||||
maxWidth = std::max(maxWidth, 65);
|
maxWidth = std::max(maxWidth, ERROR_DISPLAY_MAX_WIDTH);
|
||||||
|
|
||||||
std::cout << colorize("Source Code Context:", Colors::BOLD) << "\n";
|
std::cout << colorize("Source Code Context:", Colors::BOLD) << "\n";
|
||||||
std::cout << colorize("┌" + std::string(maxWidth, '-') + "┐", Colors::BLUE) << "\n";
|
std::cout << colorize("┌" + std::string(maxWidth, '-') + "┐", Colors::BLUE) << "\n";
|
||||||
@ -163,7 +164,8 @@ void ErrorReporter::displaySourceContext(int line, int column, const std::string
|
|||||||
void ErrorReporter::displayCallStack(const std::vector<std::string>& callStack) {
|
void ErrorReporter::displayCallStack(const std::vector<std::string>& callStack) {
|
||||||
if (callStack.empty()) return;
|
if (callStack.empty()) return;
|
||||||
|
|
||||||
int maxWidth = 65;
|
static const int CALL_STACK_MAX_WIDTH = 65;
|
||||||
|
int maxWidth = CALL_STACK_MAX_WIDTH;
|
||||||
for (const auto& func : callStack) {
|
for (const auto& func : callStack) {
|
||||||
int funcWidth = static_cast<int>(func.length()) + 6;
|
int funcWidth = static_cast<int>(func.length()) + 6;
|
||||||
maxWidth = std::max(maxWidth, funcWidth);
|
maxWidth = std::max(maxWidth, funcWidth);
|
||||||
|
|||||||
@ -13,46 +13,35 @@
|
|||||||
#include "../headers/helperFunctions/HelperFunctions.h"
|
#include "../headers/helperFunctions/HelperFunctions.h"
|
||||||
#include "../headers/BobStdLib.h"
|
#include "../headers/BobStdLib.h"
|
||||||
|
|
||||||
// 🎪 The Great Return Context Circus! 🎪
|
// Return context for managing function return values
|
||||||
// Where return values go to party before coming back home
|
|
||||||
struct ReturnContext {
|
struct ReturnContext {
|
||||||
Value returnValue; // The star of the show
|
Value returnValue;
|
||||||
bool hasReturn; // Did someone say "return"?
|
bool hasReturn;
|
||||||
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
|
ReturnContext() : returnValue(NONE_VALUE), hasReturn(false) {}
|
||||||
// Constructor: "Hello, I'm a return context, and I'm here to make your day better!"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 🎭 Trampoline-based tail call optimization - no exceptions needed
|
// Trampoline-based tail call optimization to prevent stack overflow
|
||||||
// Because we're too cool for stack overflow exceptions
|
|
||||||
// We bounce around like kangaroos on a trampoline! 🦘
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 🎯 Literal Expression Interpreter - The Truth Teller! 🎯
|
|
||||||
// "I speak only the truth, and sometimes binary!"
|
|
||||||
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
|
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
|
||||||
if (expr->isNull) {
|
if (expr->isNull) {
|
||||||
// Ah, the philosophical question: "To be or not to be?"
|
|
||||||
// Answer: "Not to be" (none)
|
|
||||||
return NONE_VALUE;
|
return NONE_VALUE;
|
||||||
}
|
}
|
||||||
if (expr->isNumber) {
|
if (expr->isNumber) {
|
||||||
double num;
|
double num;
|
||||||
if (expr->value[1] == 'b') {
|
if (expr->value[1] == 'b') {
|
||||||
// Binary numbers: Because 10 types of people exist - those who understand binary and those who don't! 🤓
|
|
||||||
num = binaryStringToLong(expr->value);
|
num = binaryStringToLong(expr->value);
|
||||||
} else {
|
} else {
|
||||||
// Decimal numbers: The boring but reliable ones
|
|
||||||
num = std::stod(expr->value);
|
num = std::stod(expr->value);
|
||||||
}
|
}
|
||||||
return Value(num);
|
return Value(num);
|
||||||
}
|
}
|
||||||
if (expr->isBoolean) {
|
if (expr->isBoolean) {
|
||||||
if (expr->value == "true") return TRUE_VALUE; // The optimist
|
if (expr->value == "true") return TRUE_VALUE;
|
||||||
if (expr->value == "false") return FALSE_VALUE; // The pessimist
|
if (expr->value == "false") return FALSE_VALUE;
|
||||||
}
|
}
|
||||||
// Everything else is just a string, and strings are like people - unique and special! 💫
|
|
||||||
return Value(expr->value);
|
return Value(expr->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,13 +409,13 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
|
|||||||
if (callee.isFunction()) {
|
if (callee.isFunction()) {
|
||||||
Function* function = callee.asFunction();
|
Function* function = callee.asFunction();
|
||||||
if (arguments.size() != function->params.size()) {
|
if (arguments.size() != function->params.size()) {
|
||||||
std::string errorMsg = "Expected " + std::to_string(function->params.size()) +
|
|
||||||
" arguments but got " + std::to_string(arguments.size()) + ".";
|
|
||||||
if (errorReporter) {
|
if (errorReporter) {
|
||||||
errorReporter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
errorReporter->reportError(expression->paren.line, expression->paren.column, "Runtime Error",
|
||||||
errorMsg, "");
|
"Expected " + std::to_string(function->params.size()) +
|
||||||
|
" arguments but got " + std::to_string(arguments.size()) + ".", "");
|
||||||
}
|
}
|
||||||
throw std::runtime_error(errorMsg);
|
throw std::runtime_error("Expected " + std::to_string(function->params.size()) +
|
||||||
|
" arguments but got " + std::to_string(arguments.size()) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a tail call
|
// Check if this is a tail call
|
||||||
@ -662,12 +651,10 @@ void Interpreter::visitBlockStmt(const std::shared_ptr<BlockStmt>& statement, Ex
|
|||||||
void Interpreter::visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement, ExecutionContext* context) {
|
void Interpreter::visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement, ExecutionContext* context) {
|
||||||
Value value = evaluate(statement->expression);
|
Value value = evaluate(statement->expression);
|
||||||
|
|
||||||
if(IsInteractive)
|
if(isInteractive)
|
||||||
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m" << std::endl;
|
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement, ExecutionContext* context)
|
void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement, ExecutionContext* context)
|
||||||
{
|
{
|
||||||
Value value = NONE_VALUE;
|
Value value = NONE_VALUE;
|
||||||
@ -676,7 +663,7 @@ void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement, Execut
|
|||||||
value = evaluate(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);
|
environment->define(statement->name.lexeme, value);
|
||||||
}
|
}
|
||||||
@ -799,9 +786,7 @@ void Interpreter::visitDoWhileStmt(const std::shared_ptr<DoWhileStmt>& statement
|
|||||||
|
|
||||||
void Interpreter::visitForStmt(const std::shared_ptr<ForStmt>& statement, ExecutionContext* context)
|
void Interpreter::visitForStmt(const std::shared_ptr<ForStmt>& statement, ExecutionContext* context)
|
||||||
{
|
{
|
||||||
// For loops are desugared into while loops in the parser
|
// For loop implementation - executes initializer, condition, body, and increment
|
||||||
// This method should never be called, but we implement it for completeness
|
|
||||||
// The actual execution happens through the desugared while loop
|
|
||||||
if (statement->initializer != nullptr) {
|
if (statement->initializer != nullptr) {
|
||||||
execute(statement->initializer, context);
|
execute(statement->initializer, context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
#include "../headers/Parser.h"
|
#include "../headers/Parser.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
// Precedence
|
// Operator Precedence Rules
|
||||||
// to all the morons on facebook who don't know what pemdas is, fuck you
|
// Following standard mathematical order of operations
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
|
|
||||||
sptr(Expr) Parser::expression()
|
sptr(Expr) Parser::expression()
|
||||||
@ -369,8 +367,6 @@ sptr(Expr) Parser::call()
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<sptr(Stmt)> Parser::parse() {
|
std::vector<sptr(Stmt)> Parser::parse() {
|
||||||
|
|
||||||
@ -442,12 +438,13 @@ std::shared_ptr<Expr> Parser::functionExpression() {
|
|||||||
std::vector<Token> parameters;
|
std::vector<Token> parameters;
|
||||||
if (!check(CLOSE_PAREN)) {
|
if (!check(CLOSE_PAREN)) {
|
||||||
do {
|
do {
|
||||||
if (parameters.size() >= 255) {
|
static const size_t MAX_FUNCTION_PARAMETERS = 255;
|
||||||
|
if (parameters.size() >= MAX_FUNCTION_PARAMETERS) {
|
||||||
if (errorReporter) {
|
if (errorReporter) {
|
||||||
errorReporter->reportError(peek().line, 0, "Parse Error",
|
errorReporter->reportError(peek().line, 0, "Parse Error",
|
||||||
"Cannot have more than 255 parameters", "");
|
"Cannot have more than " + std::to_string(MAX_FUNCTION_PARAMETERS) + " parameters", "");
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Cannot have more than 255 parameters.");
|
throw std::runtime_error("Cannot have more than " + std::to_string(MAX_FUNCTION_PARAMETERS) + " parameters.");
|
||||||
}
|
}
|
||||||
parameters.push_back(consume(IDENTIFIER, "Expect parameter name."));
|
parameters.push_back(consume(IDENTIFIER, "Expect parameter name."));
|
||||||
} while (match({COMMA}));
|
} while (match({COMMA}));
|
||||||
|
|||||||
@ -15,7 +15,7 @@ void Bob::runFile(const std::string& path)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "File not found" << std::endl;
|
std::cout << "File not found\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,8 +32,8 @@ void Bob::runPrompt()
|
|||||||
{
|
{
|
||||||
this->interpreter = msptr(Interpreter)(true);
|
this->interpreter = msptr(Interpreter)(true);
|
||||||
|
|
||||||
std::cout << "Bob v" << VERSION << ", 2025" << std::endl;
|
std::cout << "Bob v" << VERSION << ", 2025\n";
|
||||||
for(;;)
|
while(true)
|
||||||
{
|
{
|
||||||
std::string line;
|
std::string line;
|
||||||
std::cout << "\033[0;36m" << "-> " << "\033[0;37m";
|
std::cout << "\033[0;36m" << "-> " << "\033[0;37m";
|
||||||
@ -81,13 +81,13 @@ void Bob::run(std::string source)
|
|||||||
|
|
||||||
// For errors that weren't reported (like parser errors, undefined variables, etc.)
|
// For errors that weren't reported (like parser errors, undefined variables, etc.)
|
||||||
// print them normally
|
// print them normally
|
||||||
std::cout << "Error: " << e.what() << std::endl;
|
std::cout << "Error: " << e.what() << '\n';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(const std::exception& e)
|
||||||
{
|
{
|
||||||
// Unknown error - report it since it wasn't handled by the interpreter
|
// Unknown error - report it since it wasn't handled by the interpreter
|
||||||
errorReporter.reportError(0, 0, "Unknown Error", "An unknown error occurred");
|
errorReporter.reportError(0, 0, "Unknown Error", "An unknown error occurred: " + std::string(e.what()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user