diff --git a/src/runner/runner.cpp b/src/runner/runner.cpp index 890a49b..6d1bd36 100644 --- a/src/runner/runner.cpp +++ b/src/runner/runner.cpp @@ -6,6 +6,11 @@ #include "../parser/parser.h" +Object::Object(ASTValue value) : value(value) {} +Object::Object(ASTFunction function) : value(function) {} +Object::Object() = default; + + std::optional Executor::consume() { if (iterator < code.nodes.size()) { return code.nodes[iterator++]; @@ -30,10 +35,8 @@ std::optional Executor::peek(int ahead) { * @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. * 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. - * @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. * * @details @@ -48,9 +51,19 @@ std::optional Executor::peek(int ahead) { * * @note Exits the process if critical execution errors occur (e.g., unexpected nodes or missing values). */ -Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map scopeVals, std::map scopeFns, std::vector args) : code(std::move(in)), variables(std::move(scopeVals)), functions(std::move(scopeFns)) { +Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map scopeVals, std::vector args) : code(std::move(in)) { 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) { std::optional node = consume(); @@ -63,7 +76,7 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map>(next.value())) { - functions[std::get>(node.value())->name] = *std::get>(next.value()); + variables.emplace(std::get>(node.value())->name, *std::get>(next.value())); } else if (std::holds_alternative>(next.value())) { std::string id = std::get>(next.value())->name; if (id == "=") { @@ -72,9 +85,9 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map>(valueNode.value())) { - variables[std::get>(node.value())->name] = *std::get>(valueNode.value()); + variables.emplace(std::get>(node.value())->name, *std::get>(valueNode.value())); } else if (std::holds_alternative>(valueNode.value())) { - functions[std::get>(node.value())->name] = *std::get>(valueNode.value()); + variables.emplace(std::get>(node.value())->name, *std::get>(valueNode.value())); } else { std::cout << "Expected value or function after = sign" << std::endl; exit(1); @@ -96,10 +109,10 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map>(node.value())) { std::string fnName = std::get>(node.value())->func; std::vector callArgNodes = std::get>(node.value())->args; - std::vector callArgs; + std::vector callArgs; for (auto &callArgNode : callArgNodes) { if (std::holds_alternative>(callArgNode)) { - callArgs.push_back(*std::get>(callArgNode)); + callArgs.emplace_back(*std::get>(callArgNode)); } else if (std::holds_alternative>(callArgNode)) { if (variables.find(std::get>(callArgNode)->name) != variables.end()) { callArgs.push_back(variables[std::get>(callArgNode)->name]); @@ -116,7 +129,14 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map(object.value)) { + arg = std::get(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) { std::optional argString = arg.getString(); if (argString.has_value()) { @@ -150,7 +170,14 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map(object.value)) { + arg = std::get(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) { std::optional argString = arg.getString(); if (argString.has_value()) { @@ -175,7 +202,7 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map argBool = arg.getBool(); if (argBool.has_value()) { - std::cout << argBool.value() << std::endl; + std::cout << (argBool.value() ? "true" : "false") << std::endl; } else { std::cout << "Type mismatch - expecting bool but got something else" << std::endl; } @@ -188,8 +215,15 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map(callArgs[0].value)) { + arg = std::get(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); } std::optional block = consume(); @@ -197,35 +231,53 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map>(block.value())) { - Executor(*std::get>(block.value()), false, variables, functions); + Executor(*std::get>(block.value()), false, variables); } } } else if (fnName == "while") { - if (callArgs.empty()) { - std::cout << "Expected at least one argument to if statement" << std::endl; + ASTValue arg; + if (callArgs[0].isCustomObject == false && std::holds_alternative(callArgs[0].value)) { + arg = std::get(callArgs[0].value); + } else { + std::cout << "Type mismatch - expecting boolean in while statement" << std::endl; exit(1); } - if (callArgs[0].type != ValueType::Bool) { - std::cout << "Expected first argument to if statement to be a boolean" << std::endl; + if (callArgs.empty()) { + 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); } std::optional block = consume(); if (!block.has_value()) { - std::cout << "If statement expects a body" << std::endl; + std::cout << "While statement expects a body" << std::endl; exit(1); } - if (callArgs[0].getBool().value()) { - while (callArgs[0].getBool().value()) { + if (arg.getBool().value()) { + while (arg.getBool().value()) { if (std::holds_alternative>(block.value())) { - Executor(*std::get>(block.value()), false, variables, functions); + Executor(*std::get>(block.value()), false, variables); } } } } else { - if (functions.find(fnName) != functions.end()) { - Executor(functions[fnName].body, false, variables, functions, callArgs); + if (variables.find(fnName) != variables.end()) { + if (variables[fnName].isCustomObject) { + if (std::holds_alternative(variables[fnName].children["handler"])) { + Executor(std::get(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(variables[fnName].value)) { + Executor(std::get(variables[fnName].value).body, false, {}, callArgs); + + } } else { std::cout << "Function " << fnName << " not found" << std::endl; exit(1); @@ -237,6 +289,6 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map(variables[std::string("main")].value).body, false, variables); } } \ No newline at end of file diff --git a/src/runner/runner.h b/src/runner/runner.h index ebe465b..76d2833 100644 --- a/src/runner/runner.h +++ b/src/runner/runner.h @@ -1,9 +1,23 @@ #pragma once #include +#include +#include +#include +#include #include "../parser/parser.h" +class Object { +public: + bool isCustomObject = false; + std::variant value; + std::map> children; + Object(ASTValue value); + Object(ASTFunction function); + Object(); +}; + /** * @class Executor * @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. */ -class Object { - public: - std::vector children; -}; - class Executor { private: - std::map functions; - std::map variables; + std::map variables; ASTCodeBlock code; size_t iterator = 0; std::optional consume(); std::optional peek(int ahead = 1); public: - explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map scopeVals = {}, std::map scopeFns = {}, std::vector args = {}); + explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map scopeVals = {}, std::vector args = {}); }; diff --git a/tests/test.funk b/tests/test.funk index 034b89d..404da0c 100644 --- a/tests/test.funk +++ b/tests/test.funk @@ -1,17 +1,30 @@ -import("io") - dingus func { + // println is an inbuilt function, you can also use print 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) { println("yay") } + // types available are int, float, string, bool 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 { - dingus() + // call a function (with arguments!) + dingus("hehe") } +// run this if you dare! try adding it in the main function! infinity func { while (true) { println("to infinity and beyond!")