Refactoring for OOP

This commit is contained in:
2025-10-26 20:44:47 +11:00
parent 191410d174
commit 81f836bc94
3 changed files with 112 additions and 39 deletions

View File

@@ -6,6 +6,11 @@
#include "../parser/parser.h" #include "../parser/parser.h"
Object::Object(ASTValue value) : value(value) {}
Object::Object(ASTFunction function) : value(function) {}
Object::Object() = default;
std::optional<ASTNode> Executor::consume() { std::optional<ASTNode> Executor::consume() {
if (iterator < code.nodes.size()) { if (iterator < code.nodes.size()) {
return code.nodes[iterator++]; return code.nodes[iterator++];
@@ -30,10 +35,8 @@ std::optional<ASTNode> Executor::peek(int ahead) {
* @param in The abstract syntax tree (AST) code block to execute. * @param in The abstract syntax tree (AST) code block to execute.
* @param isInitCall A boolean flag to determine if this is the initial entry point to execution. * @param isInitCall A boolean flag to determine if this is the initial entry point to execution.
* If true, it runs the "main" function after setting up the context. * If true, it runs the "main" function after setting up the context.
* @param scopeVals A map of variable names (strings) to their corresponding `ASTValue` objects used as * @param scopeVals A map of variable names (strings) to their corresponding `Object` objects used as
* the current scope of variables. * the current scope of variables.
* @param scopeFns A map of function names (strings) to their corresponding `ASTFunction` objects used as
* the current set of functions within scope.
* @param args A vector of `ASTValue` objects passed as arguments for the context of this execution. * @param args A vector of `ASTValue` objects passed as arguments for the context of this execution.
* *
* @details * @details
@@ -48,9 +51,19 @@ std::optional<ASTNode> Executor::peek(int ahead) {
* *
* @note Exits the process if critical execution errors occur (e.g., unexpected nodes or missing values). * @note Exits the process if critical execution errors occur (e.g., unexpected nodes or missing values).
*/ */
Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTValue> scopeVals, std::map<std::string, ASTFunction> scopeFns, std::vector<ASTValue> args) : code(std::move(in)), variables(std::move(scopeVals)), functions(std::move(scopeFns)) { Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Object> scopeVals, std::vector<Object> args) : code(std::move(in)) {
for (size_t i = 0; i < args.size(); i++) { for (size_t i = 0; i < args.size(); i++) {
variables["arg" + std::to_string(i)] = args[i]; variables.emplace("arg" + std::to_string(i), args[i]);
}
for (auto &pair : scopeVals) {
variables.emplace(pair.first, pair.second);
}
if (isInitCall) {
// add a main function if it doesn't exist
if (variables.find("main") == variables.end()) {
ASTFunction mainFunc;
mainFunc.body = ASTCodeBlock({"println(\"Hey there! Looks like your main function doesn't exist, so we made one for you :)\")"});
}
} }
while (true) { while (true) {
std::optional<ASTNode> node = consume(); std::optional<ASTNode> node = consume();
@@ -63,7 +76,7 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
// eg: main func { ... } // eg: main func { ... }
// eg: dingus func { ... } // eg: dingus func { ... }
if (std::holds_alternative<std::shared_ptr<ASTFunction>>(next.value())) { if (std::holds_alternative<std::shared_ptr<ASTFunction>>(next.value())) {
functions[std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name] = *std::get<std::shared_ptr<ASTFunction>>(next.value()); variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTFunction>>(next.value()));
} else if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(next.value())) { } else if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(next.value())) {
std::string id = std::get<std::shared_ptr<ASTIdentifier>>(next.value())->name; std::string id = std::get<std::shared_ptr<ASTIdentifier>>(next.value())->name;
if (id == "=") { if (id == "=") {
@@ -72,9 +85,9 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
if (valueNode.has_value()) { if (valueNode.has_value()) {
ASTValue next; ASTValue next;
if (std::holds_alternative<std::shared_ptr<ASTValue>>(valueNode.value())) { if (std::holds_alternative<std::shared_ptr<ASTValue>>(valueNode.value())) {
variables[std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name] = *std::get<std::shared_ptr<ASTValue>>(valueNode.value()); variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTValue>>(valueNode.value()));
} else if (std::holds_alternative<std::shared_ptr<ASTFunction>>(valueNode.value())) { } else if (std::holds_alternative<std::shared_ptr<ASTFunction>>(valueNode.value())) {
functions[std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name] = *std::get<std::shared_ptr<ASTFunction>>(valueNode.value()); variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTFunction>>(valueNode.value()));
} else { } else {
std::cout << "Expected value or function after = sign" << std::endl; std::cout << "Expected value or function after = sign" << std::endl;
exit(1); exit(1);
@@ -96,10 +109,10 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
if (std::holds_alternative<std::shared_ptr<ASTFunctionCall>>(node.value())) { if (std::holds_alternative<std::shared_ptr<ASTFunctionCall>>(node.value())) {
std::string fnName = std::get<std::shared_ptr<ASTFunctionCall>>(node.value())->func; std::string fnName = std::get<std::shared_ptr<ASTFunctionCall>>(node.value())->func;
std::vector<ASTNode> callArgNodes = std::get<std::shared_ptr<ASTFunctionCall>>(node.value())->args; std::vector<ASTNode> callArgNodes = std::get<std::shared_ptr<ASTFunctionCall>>(node.value())->args;
std::vector<ASTValue> callArgs; std::vector<Object> callArgs;
for (auto &callArgNode : callArgNodes) { for (auto &callArgNode : callArgNodes) {
if (std::holds_alternative<std::shared_ptr<ASTValue>>(callArgNode)) { if (std::holds_alternative<std::shared_ptr<ASTValue>>(callArgNode)) {
callArgs.push_back(*std::get<std::shared_ptr<ASTValue>>(callArgNode)); callArgs.emplace_back(*std::get<std::shared_ptr<ASTValue>>(callArgNode));
} else if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(callArgNode)) { } else if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(callArgNode)) {
if (variables.find(std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name) != variables.end()) { if (variables.find(std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name) != variables.end()) {
callArgs.push_back(variables[std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name]); callArgs.push_back(variables[std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name]);
@@ -116,7 +129,14 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
exit(1); exit(1);
} }
if (fnName == "print") { if (fnName == "print") {
for (ASTValue &arg : callArgs) { for (Object &object : callArgs) {
ASTValue arg;
if (object.isCustomObject == false && std::holds_alternative<ASTValue>(object.value)) {
arg = std::get<ASTValue>(object.value);
} else {
std::cout << "Type mismatch - expecting string, int, float, or bool but got something else" << std::endl;
exit(1);
}
if (arg.type == ValueType::String) { if (arg.type == ValueType::String) {
std::optional<std::string> argString = arg.getString(); std::optional<std::string> argString = arg.getString();
if (argString.has_value()) { if (argString.has_value()) {
@@ -150,7 +170,14 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
} }
} }
} else if (fnName == "println") { } else if (fnName == "println") {
for (ASTValue &arg : callArgs) { for (Object &object : callArgs) {
ASTValue arg;
if (object.isCustomObject == false && std::holds_alternative<ASTValue>(object.value)) {
arg = std::get<ASTValue>(object.value);
} else {
std::cout << "Type mismatch - expecting string, int, float, or bool but got something else" << std::endl;
exit(1);
}
if (arg.type == ValueType::String) { if (arg.type == ValueType::String) {
std::optional<std::string> argString = arg.getString(); std::optional<std::string> argString = arg.getString();
if (argString.has_value()) { if (argString.has_value()) {
@@ -175,7 +202,7 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
} else if (arg.type == ValueType::Bool) { } else if (arg.type == ValueType::Bool) {
std::optional<bool> argBool = arg.getBool(); std::optional<bool> argBool = arg.getBool();
if (argBool.has_value()) { if (argBool.has_value()) {
std::cout << argBool.value() << std::endl; std::cout << (argBool.value() ? "true" : "false") << std::endl;
} else { } else {
std::cout << "Type mismatch - expecting bool but got something else" << std::endl; std::cout << "Type mismatch - expecting bool but got something else" << std::endl;
} }
@@ -188,8 +215,15 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
std::cout << "Expected at least one argument to if statement" << std::endl; std::cout << "Expected at least one argument to if statement" << std::endl;
exit(1); exit(1);
} }
if (callArgs[0].type != ValueType::Bool) { ASTValue arg;
std::cout << "Expected first argument to if statement to be a boolean" << std::endl; if (callArgs[0].isCustomObject == false && std::holds_alternative<ASTValue>(callArgs[0].value)) {
arg = std::get<ASTValue>(callArgs[0].value);
} else {
std::cout << "Type mismatch - expecting boolean in if statement" << std::endl;
exit(1);
}
if (arg.type != ValueType::Bool) {
std::cout << "Type mismatch - expecting boolean in if statement" << std::endl;
exit(1); exit(1);
} }
std::optional<ASTNode> block = consume(); std::optional<ASTNode> block = consume();
@@ -197,35 +231,53 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
std::cout << "If statement expects a body" << std::endl; std::cout << "If statement expects a body" << std::endl;
exit(1); exit(1);
} }
if (callArgs[0].getBool().value()) { if (arg.getBool().value()) {
if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) { if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) {
Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables, functions); Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables);
} }
} }
} else if (fnName == "while") { } else if (fnName == "while") {
if (callArgs.empty()) { ASTValue arg;
std::cout << "Expected at least one argument to if statement" << std::endl; if (callArgs[0].isCustomObject == false && std::holds_alternative<ASTValue>(callArgs[0].value)) {
arg = std::get<ASTValue>(callArgs[0].value);
} else {
std::cout << "Type mismatch - expecting boolean in while statement" << std::endl;
exit(1); exit(1);
} }
if (callArgs[0].type != ValueType::Bool) { if (callArgs.empty()) {
std::cout << "Expected first argument to if statement to be a boolean" << std::endl; std::cout << "Expected at least one argument to while statement" << std::endl;
exit(1);
}
if (arg.type != ValueType::Bool) {
std::cout << "Expected first argument to while statement to be a boolean" << std::endl;
exit(1); exit(1);
} }
std::optional<ASTNode> block = consume(); std::optional<ASTNode> block = consume();
if (!block.has_value()) { if (!block.has_value()) {
std::cout << "If statement expects a body" << std::endl; std::cout << "While statement expects a body" << std::endl;
exit(1); exit(1);
} }
if (callArgs[0].getBool().value()) { if (arg.getBool().value()) {
while (callArgs[0].getBool().value()) { while (arg.getBool().value()) {
if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) { if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) {
Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables, functions); Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables);
} }
} }
} }
} else { } else {
if (functions.find(fnName) != functions.end()) { if (variables.find(fnName) != variables.end()) {
Executor(functions[fnName].body, false, variables, functions, callArgs); if (variables[fnName].isCustomObject) {
if (std::holds_alternative<ASTFunction>(variables[fnName].children["handler"])) {
Executor(std::get<ASTFunction>(variables[fnName].children["handler"]).body, false, {}, callArgs);
} else {
std::cout << "Object " << fnName << " does not have a handler function" << std::endl;
exit(1);
}
}
if (std::holds_alternative<ASTFunction>(variables[fnName].value)) {
Executor(std::get<ASTFunction>(variables[fnName].value).body, false, {}, callArgs);
}
} else { } else {
std::cout << "Function " << fnName << " not found" << std::endl; std::cout << "Function " << fnName << " not found" << std::endl;
exit(1); exit(1);
@@ -237,6 +289,6 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, ASTVa
} }
} }
if (isInitCall) { if (isInitCall) {
Executor(functions["main"].body, false, variables, functions); Executor(std::get<ASTFunction>(variables[std::string("main")].value).body, false, variables);
} }
} }

View File

@@ -1,9 +1,23 @@
#pragma once #pragma once
#include <map> #include <map>
#include <optional>
#include <variant>
#include <string>
#include <vector>
#include "../parser/parser.h" #include "../parser/parser.h"
class Object {
public:
bool isCustomObject = false;
std::variant<ASTValue, ASTFunction> value;
std::map<std::string, std::variant<Object, ASTFunction, ASTValue>> children;
Object(ASTValue value);
Object(ASTFunction function);
Object();
};
/** /**
* @class Executor * @class Executor
* @brief Responsible for executing a sequence of operations defined in an abstract syntax tree (AST). * @brief Responsible for executing a sequence of operations defined in an abstract syntax tree (AST).
@@ -14,20 +28,14 @@
* and consuming or peeking at individual nodes. * and consuming or peeking at individual nodes.
*/ */
class Object {
public:
std::vector<Object> children;
};
class Executor { class Executor {
private: private:
std::map<std::string, ASTFunction> functions; std::map<std::string, Object> variables;
std::map<std::string, ASTValue> variables;
ASTCodeBlock code; ASTCodeBlock code;
size_t iterator = 0; size_t iterator = 0;
std::optional<ASTNode> consume(); std::optional<ASTNode> consume();
std::optional<ASTNode> peek(int ahead = 1); std::optional<ASTNode> peek(int ahead = 1);
public: public:
explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map<std::string, ASTValue> scopeVals = {}, std::map<std::string, ASTFunction> scopeFns = {}, std::vector<ASTValue> args = {}); explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map<std::string, Object> scopeVals = {}, std::vector<Object> args = {});
}; };

View File

@@ -1,17 +1,30 @@
import("io")
dingus func { dingus func {
// println is an inbuilt function, you can also use print
println("you got dingused") println("you got dingused")
// for now, args for functions are accessed with arg0, arg1, etc
print("You said: ")
println(arg0)
// set a variable, similar to Python
x = 5
println(x)
// if statements are pretty standard
if (true) { if (true) {
println("yay") println("yay")
} }
// types available are int, float, string, bool
println(321) println(321)
println(3.14)
println("I am a string!")
println(true)
} }
// main is the entry point of the program, like many other languages
main func { main func {
dingus() // call a function (with arguments!)
dingus("hehe")
} }
// run this if you dare! try adding it in the main function!
infinity func { infinity func {
while (true) { while (true) {
println("to infinity and beyond!") println("to infinity and beyond!")