General fixes
This commit is contained in:
parent
eacb86ec77
commit
b97715e549
@ -1,6 +1,4 @@
|
||||
//
|
||||
// Created by Bobby Lucero on 5/21/23.
|
||||
//
|
||||
// Expression AST nodes for Bob language
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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()));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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}));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user