diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 30fddb7..aa23c38 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -6,3 +6,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(step1_test tests/step1_test.cpp) target_include_directories(step1_test PRIVATE src) + +add_executable(step2_test tests/step2_test.cpp) +target_include_directories(step2_test PRIVATE src) diff --git a/editor/src/ast/ASTNode.h b/editor/src/ast/ASTNode.h index 2fd9bd1..ea4629d 100644 --- a/editor/src/ast/ASTNode.h +++ b/editor/src/ast/ASTNode.h @@ -1,5 +1,7 @@ #pragma once #include +#include +#include class ASTNode { public: @@ -8,4 +10,50 @@ public: ASTNode* parent = nullptr; virtual ~ASTNode() = default; + + // Multi-valued child: append to role + void addChild(const std::string& role, ASTNode* child) { + child->parent = this; + children_[role].push_back(child); + } + + // Single-valued child: set (replaces if exists) + void setChild(const std::string& role, ASTNode* child) { + auto& vec = children_[role]; + if (!vec.empty()) { + vec[0]->parent = nullptr; + vec[0] = child; + } else { + vec.push_back(child); + } + child->parent = this; + } + + // Get single-valued child (nullptr if not set) + ASTNode* getChild(const std::string& role) const { + auto it = children_.find(role); + if (it != children_.end() && !it->second.empty()) { + return it->second[0]; + } + return nullptr; + } + + // Get multi-valued children (empty vector if role not present) + const std::vector& getChildren(const std::string& role) const { + static const std::vector empty; + auto it = children_.find(role); + return it != children_.end() ? it->second : empty; + } + + // Get all children across all roles + std::vector allChildren() const { + std::vector result; + for (const auto& [role, kids] : children_) { + result.insert(result.end(), kids.begin(), kids.end()); + } + return result; + } + +private: + std::map> children_; }; diff --git a/editor/src/ast/Function.h b/editor/src/ast/Function.h new file mode 100644 index 0000000..6559ba7 --- /dev/null +++ b/editor/src/ast/Function.h @@ -0,0 +1,14 @@ +#pragma once +#include "ASTNode.h" + +class Function : public ASTNode { +public: + std::string name; + + Function() { conceptType = "Function"; } + Function(const std::string& id, const std::string& name) + : name(name) { + this->id = id; + this->conceptType = "Function"; + } +}; diff --git a/editor/src/ast/Variable.h b/editor/src/ast/Variable.h new file mode 100644 index 0000000..3714292 --- /dev/null +++ b/editor/src/ast/Variable.h @@ -0,0 +1,14 @@ +#pragma once +#include "ASTNode.h" + +class Variable : public ASTNode { +public: + std::string name; + + Variable() { conceptType = "Variable"; } + Variable(const std::string& id, const std::string& name) + : name(name) { + this->id = id; + this->conceptType = "Variable"; + } +}; diff --git a/editor/tests/step2_test.cpp b/editor/tests/step2_test.cpp new file mode 100644 index 0000000..c6a59c9 --- /dev/null +++ b/editor/tests/step2_test.cpp @@ -0,0 +1,73 @@ +#include "../src/ast/Module.h" +#include "../src/ast/Function.h" +#include "../src/ast/Variable.h" +#include +#include + +int main() { + // Build tree: Module -> [Function, Function, Variable] + Module mod("M001", "Calculator", "python"); + Function add("F001", "add"); + Function subtract("F002", "subtract"); + Variable pi("V001", "pi"); + + mod.addChild("functions", &add); + mod.addChild("functions", &subtract); + mod.addChild("variables", &pi); + + // Verify parent pointers + assert(add.parent == &mod); + assert(subtract.parent == &mod); + assert(pi.parent == &mod); + assert(mod.parent == nullptr); + + // Verify children by role + const auto& functions = mod.getChildren("functions"); + assert(functions.size() == 2); + assert(functions[0] == &add); + assert(functions[1] == &subtract); + + const auto& variables = mod.getChildren("variables"); + assert(variables.size() == 1); + assert(variables[0] == &pi); + + // Empty role returns empty vector + const auto& empty = mod.getChildren("annotations"); + assert(empty.empty()); + + // Walk down and back up + for (auto* child : mod.getChildren("functions")) { + assert(child->parent == &mod); + assert(child->parent->conceptType == "Module"); + auto* fn = static_cast(child); + assert(!fn->name.empty()); + } + + // Verify names through downcast + assert(static_cast(functions[0])->name == "add"); + assert(static_cast(functions[1])->name == "subtract"); + assert(static_cast(variables[0])->name == "pi"); + + // allChildren returns everything + auto all = mod.allChildren(); + assert(all.size() == 3); + + // Single-valued child (setChild / getChild) + Function entry("F003", "main"); + mod.setChild("entryPoint", &entry); + assert(mod.getChild("entryPoint") == &entry); + assert(entry.parent == &mod); + + // Replace single-valued child + Function alt("F004", "alt_main"); + mod.setChild("entryPoint", &alt); + assert(mod.getChild("entryPoint") == &alt); + assert(alt.parent == &mod); + assert(entry.parent == nullptr); // old child's parent cleared + + // getChild on unset role returns nullptr + assert(mod.getChild("nonexistent") == nullptr); + + std::cout << "Step 2: PASS" << std::endl; + return 0; +}