General fixes

This commit is contained in:
Bobby Lucero 2025-08-07 00:33:16 -04:00
parent eacb86ec77
commit b97715e549
7 changed files with 61 additions and 59 deletions

View File

@ -1,6 +1,4 @@
//
// Created by Bobby Lucero on 5/21/23.
//
// Expression AST nodes for Bob language
#pragma once
#include <iostream>

View File

@ -86,14 +86,14 @@ public:
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>();
}
virtual ~Interpreter() = default;
private:
std::shared_ptr<Environment> environment;
bool IsInteractive;
bool isInteractive;
std::vector<std::shared_ptr<BuiltinFunction>> builtinFunctions;
std::vector<std::shared_ptr<Function>> functions;
std::vector<std::shared_ptr<Thunk>> thunks; // Store thunks to prevent memory leaks
@ -104,6 +104,7 @@ private:
int functionCreationCount = 0;
int thunkCreationCount = 0;
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

View File

@ -5,6 +5,7 @@
#include "../headers/Parser.h"
#include <chrono>
#include <thread>
#include <ctime>
void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter, ErrorReporter* errorReporter) {
// 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()) + ".");
}
// Use the interpreter's stringify function
std::cout << interpreter.stringify(args[0]) << std::endl;
std::cout << interpreter.stringify(args[0]) << '\n';
return NONE_VALUE;
});
env->define("print", Value(printFunc.get()));
@ -405,7 +406,6 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter&
}
std::exit(exitCode);
return NONE_VALUE; // This line should never be reached
});
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
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) {
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()) {
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();
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
@ -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()) + ".");
}
// 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);
});
env->define("random", Value(randomFunc.get()));

View File

@ -107,7 +107,8 @@ void ErrorReporter::displaySourceContext(int line, int column, const std::string
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 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());
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("" + 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) {
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) {
int funcWidth = static_cast<int>(func.length()) + 6;
maxWidth = std::max(maxWidth, funcWidth);

View File

@ -13,46 +13,35 @@
#include "../headers/helperFunctions/HelperFunctions.h"
#include "../headers/BobStdLib.h"
// 🎪 The Great Return Context Circus! 🎪
// Where return values go to party before coming back home
// Return context for managing function return values
struct ReturnContext {
Value returnValue; // The star of the show
bool hasReturn; // Did someone say "return"?
Value returnValue;
bool hasReturn;
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
// Because we're too cool for stack overflow exceptions
// We bounce around like kangaroos on a trampoline! 🦘
// Trampoline-based tail call optimization to prevent stack overflow
// 🎯 Literal Expression Interpreter - The Truth Teller! 🎯
// "I speak only the truth, and sometimes binary!"
Value Interpreter::visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) {
if (expr->isNull) {
// Ah, the philosophical question: "To be or not to be?"
// Answer: "Not to be" (none)
return NONE_VALUE;
}
if (expr->isNumber) {
double num;
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);
} else {
// Decimal numbers: The boring but reliable ones
num = std::stod(expr->value);
}
return Value(num);
}
if (expr->isBoolean) {
if (expr->value == "true") return TRUE_VALUE; // The optimist
if (expr->value == "false") return FALSE_VALUE; // The pessimist
if (expr->value == "true") return TRUE_VALUE;
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);
}
@ -420,13 +409,13 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
if (callee.isFunction()) {
Function* function = callee.asFunction();
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) {
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
@ -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) {
Value value = evaluate(statement->expression);
if(IsInteractive)
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m" << std::endl;
if(isInteractive)
std::cout << "\u001b[38;5;8m[" << stringify(value) << "]\u001b[38;5;15m\n";
}
void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement, ExecutionContext* context)
{
Value value = NONE_VALUE;
@ -676,7 +663,7 @@ void Interpreter::visitVarStmt(const std::shared_ptr<VarStmt>& statement, Execut
value = evaluate(statement->initializer);
}
//std::cout << "Visit var stmt: " << statement->name.lexeme << " set to: " << stringify(value) << std::endl;
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)
{
// For loops are desugared into while loops in the parser
// This method should never be called, but we implement it for completeness
// The actual execution happens through the desugared while loop
// For loop implementation - executes initializer, condition, body, and increment
if (statement->initializer != nullptr) {
execute(statement->initializer, context);
}

View File

@ -1,12 +1,10 @@
//
//
#include "../headers/Parser.h"
#include <stdexcept>
// Precedence
// to all the morons on facebook who don't know what pemdas is, fuck you
// Operator Precedence Rules
// Following standard mathematical order of operations
///////////////////////////////////////////
sptr(Expr) Parser::expression()
@ -369,8 +367,6 @@ sptr(Expr) Parser::call()
return expr;
}
///////////////////////////////////////////
std::vector<sptr(Stmt)> Parser::parse() {
@ -442,12 +438,13 @@ std::shared_ptr<Expr> Parser::functionExpression() {
std::vector<Token> parameters;
if (!check(CLOSE_PAREN)) {
do {
if (parameters.size() >= 255) {
static const size_t MAX_FUNCTION_PARAMETERS = 255;
if (parameters.size() >= MAX_FUNCTION_PARAMETERS) {
if (errorReporter) {
errorReporter->reportError(peek().line, 0, "Parse Error",
"Cannot have more than 255 parameters", "");
}
throw std::runtime_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 " + std::to_string(MAX_FUNCTION_PARAMETERS) + " parameters.");
}
parameters.push_back(consume(IDENTIFIER, "Expect parameter name."));
} while (match({COMMA}));

View File

@ -15,7 +15,7 @@ void Bob::runFile(const std::string& path)
}
else
{
std::cout << "File not found" << std::endl;
std::cout << "File not found\n";
return;
}
@ -32,8 +32,8 @@ void Bob::runPrompt()
{
this->interpreter = msptr(Interpreter)(true);
std::cout << "Bob v" << VERSION << ", 2025" << std::endl;
for(;;)
std::cout << "Bob v" << VERSION << ", 2025\n";
while(true)
{
std::string line;
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.)
// print them normally
std::cout << "Error: " << e.what() << std::endl;
std::cout << "Error: " << e.what() << '\n';
return;
}
catch(...)
catch(const std::exception& e)
{
// 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;
}
}