From d9cd88625ee6ddd5fd9eab6786cc8e622b30022f Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sat, 4 Oct 2025 11:02:55 +1000 Subject: [PATCH] Refactoring datatypes start (unstable) --- src/datatypes/lists.cpp | 53 ----------- src/datatypes/lists.h | 8 -- src/datatypes/lists/lists.cpp | 112 ++++++++++++++++++++++ src/datatypes/lists/lists.h | 10 ++ src/datatypes/strings/strings.cpp | 71 ++++++++++++++ src/datatypes/strings/strings.h | 8 ++ src/defs/defs.cpp | 8 +- src/executor/executor.cpp | 149 ++++++++++++++++-------------- src/utils/evaluate/evaluate.cpp | 15 +++ src/utils/evaluate/evaluate.h | 5 + src/utils/trim/trim.cpp | 12 +++ src/utils/trim/trim.h | 5 + 12 files changed, 323 insertions(+), 133 deletions(-) delete mode 100644 src/datatypes/lists.cpp delete mode 100644 src/datatypes/lists.h create mode 100644 src/datatypes/lists/lists.cpp create mode 100644 src/datatypes/lists/lists.h create mode 100644 src/datatypes/strings/strings.cpp create mode 100644 src/datatypes/strings/strings.h create mode 100644 src/utils/evaluate/evaluate.cpp create mode 100644 src/utils/evaluate/evaluate.h create mode 100644 src/utils/trim/trim.cpp create mode 100644 src/utils/trim/trim.h diff --git a/src/datatypes/lists.cpp b/src/datatypes/lists.cpp deleted file mode 100644 index a8bbe26..0000000 --- a/src/datatypes/lists.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "lists.h" - -// Helper to trim whitespace from the start and end of a string -std::string trim(const std::string& str) { - size_t first = str.find_first_not_of(" \t\n\r"); - if (std::string::npos == first) { - return str; - } - size_t last = str.find_last_not_of(" \t\n\r"); - return str.substr(first, (last - first + 1)); -} - -std::vector parse_list_content(const std::string& content) { - std::vector items; - std::string current_item; - bool in_string = false; - int nested_level = 0; - - for (char c : content) { - if (c == '"') { - in_string = !in_string; - } - - if (c == ',' && !in_string && nested_level == 0) { - // End of an item - std::string trimmed = trim(current_item); - if (!trimmed.empty()) { - if (trimmed.front() == '"' && trimmed.back() == '"') { - items.push_back(Value(trimmed.substr(1, trimmed.length() - 2))); - } else { - items.push_back(Value(trimmed)); - } - } - current_item.clear(); - } else { - if (c == '[' || c == '(') nested_level++; - if (c == ']' || c == ')') nested_level--; - current_item += c; - } - } - - // Last item - std::string trimmed = trim(current_item); - if (!trimmed.empty()) { - if (trimmed.front() == '"' && trimmed.back() == '"') { - items.push_back(Value(trimmed.substr(1, trimmed.length() - 2))); - } else { - items.push_back(Value(trimmed)); - } - } - - return items; -} diff --git a/src/datatypes/lists.h b/src/datatypes/lists.h deleted file mode 100644 index d563eda..0000000 --- a/src/datatypes/lists.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include -#include "../defs/defs.h" - -std::vector parse_list_content(const std::string& content); -std::string trim(const std::string& str); \ No newline at end of file diff --git a/src/datatypes/lists/lists.cpp b/src/datatypes/lists/lists.cpp new file mode 100644 index 0000000..cd61974 --- /dev/null +++ b/src/datatypes/lists/lists.cpp @@ -0,0 +1,112 @@ +#include "lists.h" +#include "../../error/error.h" +#include "../../modules/math/math.h" +#include "../../data/data.h" +#include "../../utils/trim/trim.h" +#include "../../utils/evaluate/evaluate.h" + +Value handleListOp(std::vector args) { + // listName idx = value + if (args.size() == 4 && args[2].valtype == ValueType::Real && args[2].real == "=") { + std::string var_name; + if (args[0].valtype == ValueType::Variable) { + var_name = args[0].varName.key; + } else if (args[0].valtype == ValueType::Real) { + var_name = args[0].real; + } else { + error("Invalid target for indexed assignment"); + return Value(); + } + + Value subject = data::getValue(var_name); + Value index_val = evaluate(args[1]); + Value new_val = evaluate(args[3]); + + handleListSet(var_name, subject, index_val, new_val); + return Value(); + } + + return Value(); +} + +Value handleListGet(const Value& subject, const std::vector& args) { + if (args.size() != 1) { + error("Invalid number of arguments for list access"); + return Value(); + } + Value accessor = args[0]; + if (accessor.valtype != ValueType::Real) { + error("List accessor must be a real value"); + return Value(); + } + if (accessor.real == "size") { + return Value(std::to_string(subject.list.size())); + } + if (mathstuff::strisdigit(accessor.real)) { + int index = std::stoi(accessor.real); + if (index < 0 || (size_t)index >= subject.list.size()) { + error("List index out of bounds"); + return Value(); + } + return subject.list.at(index); + } + error("Invalid list accessor"); + return Value(); +} + +void handleListSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val) { + if (index_val.valtype != ValueType::Real || !mathstuff::strisdigit(index_val.real)) { + error("Index must be an integer"); + return; + } + int index = std::stoi(index_val.real); + + if (index < 0 || (size_t)index >= subject.list.size()) { + error("List index out of bounds"); + return; + } + subject.list[index] = new_val; + data::modifyValue(var_name, subject); +} + +std::vector parseListContent(const std::string& content) { + std::vector items; + std::string current_item; + bool in_string = false; + int nested_level = 0; + + for (char c : content) { + if (c == '"') { + in_string = !in_string; + } + + if (c == ',' && !in_string && nested_level == 0) { + // End of an item + std::string trimmed = trim(current_item); + if (!trimmed.empty()) { + if (trimmed.front() == '"' && trimmed.back() == '"') { + items.push_back(Value(trimmed.substr(1, trimmed.length() - 2))); + } else { + items.push_back(Value(trimmed)); + } + } + current_item.clear(); + } else { + if (c == '[' || c == '(') nested_level++; + if (c == ']' || c == ')') nested_level--; + current_item += c; + } + } + + // Last item + std::string trimmed = trim(current_item); + if (!trimmed.empty()) { + if (trimmed.front() == '"' && trimmed.back() == '"') { + items.push_back(Value(trimmed.substr(1, trimmed.length() - 2))); + } else { + items.push_back(Value(trimmed)); + } + } + + return items; +} diff --git a/src/datatypes/lists/lists.h b/src/datatypes/lists/lists.h new file mode 100644 index 0000000..7a6bc56 --- /dev/null +++ b/src/datatypes/lists/lists.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include "../../defs/defs.h" + +Value handleListOp(std::vector args); +std::vector parseListContent(const std::string& content); +Value handleListGet(const Value& subject, const std::vector& args); +void handleListSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val); diff --git a/src/datatypes/strings/strings.cpp b/src/datatypes/strings/strings.cpp new file mode 100644 index 0000000..d175573 --- /dev/null +++ b/src/datatypes/strings/strings.cpp @@ -0,0 +1,71 @@ +#include "strings.h" +#include "../../error/error.h" +#include "../../modules/math/math.h" +#include "../../data/data.h" +#include "../../utils/evaluate/evaluate.h" + +Value handleStringOp(std::vector args) { + if (args.size() == 4 && args[2].valtype == ValueType::Real && args[2].real == "=") { + std::string var_name; + if (args[0].valtype == ValueType::Variable) { + var_name = args[0].varName.key; + } else if (args[0].valtype == ValueType::Real) { + var_name = args[0].real; + } else { + error("Invalid target for indexed assignment"); + return Value(); + } + + Value subject = data::getValue(var_name); + Value index_val = evaluate(args[1]); + Value new_val = evaluate(args[3]); + + handleStringSet(var_name, subject, index_val, new_val); + return Value(); + } + +} + +Value handleStringGet(const Value& subject, const std::vector& args) { + if (args.size() != 1) { + error("Invalid number of arguments for string access"); + return Value(); + } + Value accessor = args[0]; + if (accessor.valtype != ValueType::Real) { + error("String accessor must be a real value"); + return Value(); + } + if (accessor.real == "size") { + return Value(std::to_string(subject.real.length())); + } + if (mathstuff::strisdigit(accessor.real)) { + int index = std::stoi(accessor.real); + if (index < 0 || (size_t)index >= subject.real.length()) { + error("String index out of bounds"); + return Value(); + } + return Value(std::string(1, subject.real.at(index))); + } + error("Invalid string accessor"); + return Value(); +} + +void handleStringSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val) { + if (index_val.valtype != ValueType::Real || !mathstuff::strisdigit(index_val.real)) { + error("Index must be an integer"); + return; + } + int index = std::stoi(index_val.real); + + if (new_val.valtype != ValueType::Real || new_val.real.length() != 1) { + error("Can only assign a single character to a string index"); + return; + } + if (index < 0 || (size_t)index >= subject.real.length()) { + error("String index out of bounds"); + return; + } + subject.real[index] = new_val.real[0]; + data::modifyValue(var_name, subject); +} diff --git a/src/datatypes/strings/strings.h b/src/datatypes/strings/strings.h new file mode 100644 index 0000000..4bc91d0 --- /dev/null +++ b/src/datatypes/strings/strings.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../../defs/defs.h" +#include + +Value handleStringOp(std::vector args); +Value handleStringGet(const Value& subject, const std::vector& args); +void handleStringSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val); diff --git a/src/defs/defs.cpp b/src/defs/defs.cpp index d431b0c..5bc4848 100644 --- a/src/defs/defs.cpp +++ b/src/defs/defs.cpp @@ -4,7 +4,7 @@ #include #include #include -#include "../datatypes/lists.h" +#include "../datatypes/lists/lists.h" InstructionType strToInstructionType(std::string in) { if (in == "println") return InstructionType::Println; @@ -45,8 +45,10 @@ Instruction::Instruction(std::vector toks) { // Check type of value in first token, then compute token if (toks[0].valtype == ValueType::Real) { instruction = strToInstructionType(toks[0].real); + } else if (toks[0].valtype == ValueType::Variable) { + instruction = InstructionType::Variable; } else { - error("Instruction should be a real"); + error("Instruction should be a real or variable value"); } if (instruction == InstructionType::Variable) { for (const auto& tok : toks) { @@ -202,7 +204,7 @@ std::vector split(std::string line) { if (bracket_type == '(') { splitvals.push_back(Value(Instruction(split(buf)))); } else { - splitvals.push_back(Value(parse_list_content(buf))); + splitvals.push_back(Value(parseListContent(buf))); } buf = ""; } diff --git a/src/executor/executor.cpp b/src/executor/executor.cpp index 325b147..a3db619 100644 --- a/src/executor/executor.cpp +++ b/src/executor/executor.cpp @@ -2,7 +2,8 @@ #include "../defs/defs.h" #include "../data/data.h" #include "../error/error.h" -#include "../vars/vars.h" +#include "../datatypes/lists/lists.h" +#include "../datatypes/strings/strings.h" #include "../modules/println/println.h" #include "../modules/print/print.h" #include "../modules/math/math.h" @@ -13,10 +14,12 @@ #include "../modules/compare/compare.h" #include "../modules/input/input.h" #include "../modules/function/function.h" -#include "../modules/concat/concat.h" + +// Forward declaration for mutual recursion +Value evaluate(Value val); Value execute(Instruction inst) { - // Special cases that need to manage their own argument evaluation + // Special cases that don't evaluate their arguments first if (inst.instruction == InstructionType::Let) { return modules::let(inst.args); } @@ -29,89 +32,98 @@ Value execute(Instruction inst) { if (inst.instruction == InstructionType::Function) { return modules::fndef(inst.args); } + if (inst.instruction == InstructionType::Variable) { + auto& args = inst.args; + if (args.empty()) { + return data::getValue(args[0].varName.key); + } + + // Special reassignment + if (args[1].real != "=") { + switch (args[0].valtype) { + case ValueType::List: + handleListOp(args); + break; + case ValueType::Real: + handleStringOp(args); + break; + default: + error("Working on it!"); + break; + } + } + + // Simple reassignment: myVar = 20 + if (args.size() == 3 && args[1].valtype == ValueType::Real && args[1].real == "=") { + if (args[0].valtype != ValueType::Real) { + error("The target of an assignment must be a variable name."); + return Value(); + } + std::string var_name = args[0].real; + Value new_val = evaluate(args[2]); + data::modifyValue(var_name, new_val); + return Value(); + } + + // GET operations and function calls + Value subject = evaluate(args[0]); + std::vector op_args(args.begin() + 1, args.end()); + + if (!op_args.empty()) { + if (subject.valtype == ValueType::List) { + return handleListGet(subject, op_args); + } else if (subject.valtype == ValueType::Real) { + return handleStringGet(subject, op_args); + } + } + + // Function call + if (args[0].valtype == ValueType::Real && data::functions.count(args[0].real)) { + const auto& func = data::functions.at(args[0].real); + if (func.arg_names.size() != args.size() - 1) { + error("Function " + args[0].real + " expects " + std::to_string(func.arg_names.size()) + " arguments, but got " + std::to_string(args.size() - 1)); + return Value(); + } + data::pushScope(); + for (size_t i = 0; i < func.arg_names.size(); ++i) { + Value arg_val = evaluate(args[i+1]); + data::scopes.back()[func.arg_names[i]] = arg_val; + } + Value return_val; + for (const auto& body_inst : func.body) { + return_val = execute(body_inst); + if (return_val.is_return) { + break; + } + } + data::popScope(); + return_val.is_return = false; + return return_val; + } + + // If we get here, it's just a variable by itself, so return its value. + return subject; + } // For all other instructions, evaluate the arguments first - size_t i = 0; - while (i < inst.args.size()) { - bool evaluated = false; - if (inst.args[i].valtype == ValueType::Processed) { - if (inst.args[i].processed) { - inst.args[i] = execute(*inst.args[i].processed); - evaluated = true; - } - } else if (inst.args[i].valtype == ValueType::Variable) { - inst.args[i] = data::getValue(inst.args[i].varName.key); - evaluated = true; - } - - if (!evaluated) { - i++; - } + for(auto& arg : inst.args) { + arg = evaluate(arg); } // Then, execute the instruction with the evaluated arguments switch (inst.instruction) { case InstructionType::Print: return modules::print(inst.args); - break; case InstructionType::Println: return modules::println(inst.args); - break; case InstructionType::Math: return modules::math(inst.args); - break; case InstructionType::Compare: return modules::compare(inst.args); - break; case InstructionType::Input: return modules::input(inst.args); - break; case InstructionType::Exit: return modules::exit(inst.args); - break; - case InstructionType::Concat: - return modules::concat(inst.args); - break; - case InstructionType::Variable: { - if (!inst.args.empty()) { - const std::string& name = inst.args[0].real; - if (data::functions.count(name)) { - const auto& func = data::functions.at(name); - - if (func.arg_names.size() != inst.args.size() - 1) { - error("Function " + name + " expects " + std::to_string(func.arg_names.size()) + " arguments, but got " + std::to_string(inst.args.size() - 1)); - return Value(); - } - - data::pushScope(); - - for (size_t i = 0; i < func.arg_names.size(); ++i) { - Value arg_val = inst.args[i+1]; - data::scopes.back()[func.arg_names[i]] = arg_val; - } - - Value return_val; - for (const auto& body_inst : func.body) { - return_val = execute(body_inst); - if (return_val.is_return) { - break; - } - } - - data::popScope(); - - return_val.is_return = false; - return return_val; - } - } - if (inst.args.size() > 2 && inst.args[1].valtype == ValueType::Real && inst.args[1].real == "=") { - return varmod(inst.args); - } else { - error("Unknown instruction or variable"); - return Value(""); - } - break; - } case InstructionType::Return: { if (inst.args.empty()) { Value val; @@ -123,7 +135,6 @@ Value execute(Instruction inst) { return return_val; } default: - // Note: 'If' and 'Let' are already handled. error("Unknown instruction"); return Value(""); } diff --git a/src/utils/evaluate/evaluate.cpp b/src/utils/evaluate/evaluate.cpp new file mode 100644 index 0000000..6d58948 --- /dev/null +++ b/src/utils/evaluate/evaluate.cpp @@ -0,0 +1,15 @@ +#include "evaluate.h" +#include "../../defs/defs.h" +#include "../../executor/executor.h" +#include "../../data/data.h" + +Value evaluate(Value val) { + if (val.valtype == ValueType::Processed) { + if (val.processed) { + return execute(*val.processed); + } + } else if (val.valtype == ValueType::Variable) { + return data::getValue(val.varName.key); + } + return val; +} diff --git a/src/utils/evaluate/evaluate.h b/src/utils/evaluate/evaluate.h new file mode 100644 index 0000000..3afd9b9 --- /dev/null +++ b/src/utils/evaluate/evaluate.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../../defs/defs.h" + +Value evaluate(Value val); diff --git a/src/utils/trim/trim.cpp b/src/utils/trim/trim.cpp new file mode 100644 index 0000000..cd79636 --- /dev/null +++ b/src/utils/trim/trim.cpp @@ -0,0 +1,12 @@ +#include "trim.h" + +// Helper to trim whitespace from the start and end of a string +std::string trim(const std::string& str) { + size_t first = str.find_first_not_of(" \t\n\r"); + if (std::string::npos == first) { + return str; + } + size_t last = str.find_last_not_of(" \t\n\r"); + return str.substr(first, (last - first + 1)); +} + diff --git a/src/utils/trim/trim.h b/src/utils/trim/trim.h new file mode 100644 index 0000000..8a76b69 --- /dev/null +++ b/src/utils/trim/trim.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string trim(const std::string& str);