Sprint 2 Step 2: child links, Function and Variable concepts
Adds generic child link support to ASTNode (addChild/setChild for multi/single- valued roles, getChildren/getChild/allChildren for traversal). Function and Variable as first child concepts of Module. Test builds Module->Function tree, walks parent/child pointers in both directions, verifies single-valued replace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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<ASTNode*>& getChildren(const std::string& role) const {
|
||||
static const std::vector<ASTNode*> empty;
|
||||
auto it = children_.find(role);
|
||||
return it != children_.end() ? it->second : empty;
|
||||
}
|
||||
|
||||
// Get all children across all roles
|
||||
std::vector<ASTNode*> allChildren() const {
|
||||
std::vector<ASTNode*> result;
|
||||
for (const auto& [role, kids] : children_) {
|
||||
result.insert(result.end(), kids.begin(), kids.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, std::vector<ASTNode*>> children_;
|
||||
};
|
||||
|
||||
14
editor/src/ast/Function.h
Normal file
14
editor/src/ast/Function.h
Normal file
@@ -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";
|
||||
}
|
||||
};
|
||||
14
editor/src/ast/Variable.h
Normal file
14
editor/src/ast/Variable.h
Normal file
@@ -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";
|
||||
}
|
||||
};
|
||||
73
editor/tests/step2_test.cpp
Normal file
73
editor/tests/step2_test.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "../src/ast/Module.h"
|
||||
#include "../src/ast/Function.h"
|
||||
#include "../src/ast/Variable.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
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<Function*>(child);
|
||||
assert(!fn->name.empty());
|
||||
}
|
||||
|
||||
// Verify names through downcast
|
||||
assert(static_cast<Function*>(functions[0])->name == "add");
|
||||
assert(static_cast<Function*>(functions[1])->name == "subtract");
|
||||
assert(static_cast<Variable*>(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;
|
||||
}
|
||||
Reference in New Issue
Block a user