Step 337: Preprocessor AST Nodes (12/12 tests)
Added IncludeDirective, PragmaDirective, MacroDefinition as first-class AST nodes. Full serialization roundtrip and CompactAST support. Preprocessor directives are statement-level nodes with macro bodies kept as unparsed text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2027,4 +2027,8 @@ target_link_libraries(step336_test PRIVATE
|
|||||||
tree_sitter_javascript tree_sitter_typescript
|
tree_sitter_javascript tree_sitter_typescript
|
||||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||||
|
|
||||||
|
add_executable(step337_test tests/step337_test.cpp)
|
||||||
|
target_include_directories(step337_test PRIVATE src)
|
||||||
|
target_link_libraries(step337_test PRIVATE nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||||
|
|||||||
@@ -424,6 +424,13 @@ inline std::string getNodeName(const ASTNode* node) {
|
|||||||
return static_cast<const ModuleLoad*>(node)->moduleName;
|
return static_cast<const ModuleLoad*>(node)->moduleName;
|
||||||
if (ct == "ScheduleTask")
|
if (ct == "ScheduleTask")
|
||||||
return static_cast<const ScheduleTask*>(node)->queue;
|
return static_cast<const ScheduleTask*>(node)->queue;
|
||||||
|
// Preprocessor nodes (Step 337)
|
||||||
|
if (ct == "IncludeDirective")
|
||||||
|
return static_cast<const IncludeDirective*>(node)->path;
|
||||||
|
if (ct == "PragmaDirective")
|
||||||
|
return static_cast<const PragmaDirective*>(node)->directive;
|
||||||
|
if (ct == "MacroDefinition")
|
||||||
|
return static_cast<const MacroDefinition*>(node)->name;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
editor/src/ast/PreprocessorNodes.h
Normal file
47
editor/src/ast/PreprocessorNodes.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ASTNode.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Preprocessor directive AST nodes — statement-level, added to Module children.
|
||||||
|
// Preprocessor operates on text, not AST; macro bodies stay as unparsed text.
|
||||||
|
|
||||||
|
class IncludeDirective : public ASTNode {
|
||||||
|
public:
|
||||||
|
std::string path; // "ast/ASTNode.h" or "string"
|
||||||
|
bool isSystem = false; // angle brackets (true) vs quotes (false)
|
||||||
|
|
||||||
|
IncludeDirective() { conceptType = "IncludeDirective"; }
|
||||||
|
IncludeDirective(const std::string& id, const std::string& p, bool sys = false)
|
||||||
|
: path(p), isSystem(sys) {
|
||||||
|
this->id = id;
|
||||||
|
conceptType = "IncludeDirective";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PragmaDirective : public ASTNode {
|
||||||
|
public:
|
||||||
|
std::string directive; // "once", "pack(push, 1)", etc.
|
||||||
|
|
||||||
|
PragmaDirective() { conceptType = "PragmaDirective"; }
|
||||||
|
PragmaDirective(const std::string& id, const std::string& dir)
|
||||||
|
: directive(dir) {
|
||||||
|
this->id = id;
|
||||||
|
conceptType = "PragmaDirective";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroDefinition : public ASTNode {
|
||||||
|
public:
|
||||||
|
std::string name; // Macro name
|
||||||
|
std::vector<std::string> parameters; // Empty for object-like macros
|
||||||
|
std::string body; // Unparsed macro body text
|
||||||
|
bool isFunctionLike = false; // Has parameter list
|
||||||
|
|
||||||
|
MacroDefinition() { conceptType = "MacroDefinition"; }
|
||||||
|
MacroDefinition(const std::string& id, const std::string& n)
|
||||||
|
: name(n) {
|
||||||
|
this->id = id;
|
||||||
|
conceptType = "MacroDefinition";
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "ClassDeclaration.h"
|
#include "ClassDeclaration.h"
|
||||||
#include "GenericType.h"
|
#include "GenericType.h"
|
||||||
#include "AsyncNodes.h"
|
#include "AsyncNodes.h"
|
||||||
|
#include "PreprocessorNodes.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -505,6 +506,23 @@ inline json propertiesToJson(const ASTNode* node) {
|
|||||||
auto* n = static_cast<const DecoratorAnnotation*>(node);
|
auto* n = static_cast<const DecoratorAnnotation*>(node);
|
||||||
props["name"] = n->name;
|
props["name"] = n->name;
|
||||||
}
|
}
|
||||||
|
// Preprocessor nodes (Step 337)
|
||||||
|
else if (ct == "IncludeDirective") {
|
||||||
|
auto* n = static_cast<const IncludeDirective*>(node);
|
||||||
|
props["path"] = n->path;
|
||||||
|
if (n->isSystem) props["isSystem"] = n->isSystem;
|
||||||
|
}
|
||||||
|
else if (ct == "PragmaDirective") {
|
||||||
|
auto* n = static_cast<const PragmaDirective*>(node);
|
||||||
|
props["directive"] = n->directive;
|
||||||
|
}
|
||||||
|
else if (ct == "MacroDefinition") {
|
||||||
|
auto* n = static_cast<const MacroDefinition*>(node);
|
||||||
|
props["name"] = n->name;
|
||||||
|
if (!n->body.empty()) props["body"] = n->body;
|
||||||
|
if (n->isFunctionLike) props["isFunctionLike"] = n->isFunctionLike;
|
||||||
|
if (!n->parameters.empty()) props["parameters"] = n->parameters;
|
||||||
|
}
|
||||||
// NullLiteral, ListLiteral, IndexAccess, Block, Assignment, IfStatement,
|
// NullLiteral, ListLiteral, IndexAccess, Block, Assignment, IfStatement,
|
||||||
// WhileLoop, Return, ExpressionStatement, ListType, SetType, MapType,
|
// WhileLoop, Return, ExpressionStatement, ListType, SetType, MapType,
|
||||||
// TupleType, ArrayType, OptionalType — no extra properties
|
// TupleType, ArrayType, OptionalType — no extra properties
|
||||||
@@ -668,6 +686,9 @@ inline ASTNode* createNode(const std::string& conceptName) {
|
|||||||
if (conceptName == "AwaitExpression") return new AwaitExpression();
|
if (conceptName == "AwaitExpression") return new AwaitExpression();
|
||||||
if (conceptName == "LambdaExpression") return new LambdaExpression();
|
if (conceptName == "LambdaExpression") return new LambdaExpression();
|
||||||
if (conceptName == "DecoratorAnnotation") return new DecoratorAnnotation();
|
if (conceptName == "DecoratorAnnotation") return new DecoratorAnnotation();
|
||||||
|
if (conceptName == "IncludeDirective") return new IncludeDirective();
|
||||||
|
if (conceptName == "PragmaDirective") return new PragmaDirective();
|
||||||
|
if (conceptName == "MacroDefinition") return new MacroDefinition();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,6 +1209,27 @@ inline void setPropertiesFromJson(ASTNode* node, const json& props) {
|
|||||||
auto* n = static_cast<DecoratorAnnotation*>(node);
|
auto* n = static_cast<DecoratorAnnotation*>(node);
|
||||||
if (props.contains("name")) n->name = props["name"].get<std::string>();
|
if (props.contains("name")) n->name = props["name"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
// Preprocessor nodes (Step 337)
|
||||||
|
else if (ct == "IncludeDirective") {
|
||||||
|
auto* n = static_cast<IncludeDirective*>(node);
|
||||||
|
if (props.contains("path")) n->path = props["path"].get<std::string>();
|
||||||
|
if (props.contains("isSystem")) n->isSystem = props["isSystem"].get<bool>();
|
||||||
|
}
|
||||||
|
else if (ct == "PragmaDirective") {
|
||||||
|
auto* n = static_cast<PragmaDirective*>(node);
|
||||||
|
if (props.contains("directive")) n->directive = props["directive"].get<std::string>();
|
||||||
|
}
|
||||||
|
else if (ct == "MacroDefinition") {
|
||||||
|
auto* n = static_cast<MacroDefinition*>(node);
|
||||||
|
if (props.contains("name")) n->name = props["name"].get<std::string>();
|
||||||
|
if (props.contains("body")) n->body = props["body"].get<std::string>();
|
||||||
|
if (props.contains("isFunctionLike")) n->isFunctionLike = props["isFunctionLike"].get<bool>();
|
||||||
|
if (props.contains("parameters") && props["parameters"].is_array()) {
|
||||||
|
n->parameters.clear();
|
||||||
|
for (const auto& p : props["parameters"])
|
||||||
|
if (p.is_string()) n->parameters.push_back(p.get<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string generateNodeId() {
|
inline std::string generateNodeId() {
|
||||||
|
|||||||
210
editor/tests/step337_test.cpp
Normal file
210
editor/tests/step337_test.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
// Step 337: Preprocessor AST Nodes (12 tests)
|
||||||
|
// Tests IncludeDirective, PragmaDirective, MacroDefinition construction,
|
||||||
|
// serialization roundtrip, and CompactAST output.
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "ast/PreprocessorNodes.h"
|
||||||
|
#include "ast/Serialization.h"
|
||||||
|
#include "CompactAST.h"
|
||||||
|
#include "ast/Module.h"
|
||||||
|
#include "ast/Function.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
// Test 1: IncludeDirective construction — local include
|
||||||
|
{
|
||||||
|
auto* inc = new IncludeDirective("inc1", "ast/ASTNode.h", false);
|
||||||
|
assert(inc->conceptType == "IncludeDirective");
|
||||||
|
assert(inc->path == "ast/ASTNode.h");
|
||||||
|
assert(!inc->isSystem);
|
||||||
|
delete inc;
|
||||||
|
std::cout << "Test 1 PASSED: IncludeDirective local include\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: IncludeDirective — system include
|
||||||
|
{
|
||||||
|
auto* inc = new IncludeDirective("inc2", "string", true);
|
||||||
|
assert(inc->isSystem);
|
||||||
|
assert(inc->path == "string");
|
||||||
|
delete inc;
|
||||||
|
std::cout << "Test 2 PASSED: IncludeDirective system include\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: IncludeDirective JSON roundtrip
|
||||||
|
{
|
||||||
|
auto* inc = new IncludeDirective("inc3", "vector", true);
|
||||||
|
json j = toJson(inc);
|
||||||
|
auto* restored = fromJson(j);
|
||||||
|
assert(restored->conceptType == "IncludeDirective");
|
||||||
|
auto* ri = static_cast<IncludeDirective*>(restored);
|
||||||
|
assert(ri->path == "vector");
|
||||||
|
assert(ri->isSystem == true);
|
||||||
|
delete inc;
|
||||||
|
delete restored;
|
||||||
|
std::cout << "Test 3 PASSED: IncludeDirective JSON roundtrip\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: PragmaDirective construction
|
||||||
|
{
|
||||||
|
auto* prag = new PragmaDirective("prag1", "once");
|
||||||
|
assert(prag->conceptType == "PragmaDirective");
|
||||||
|
assert(prag->directive == "once");
|
||||||
|
delete prag;
|
||||||
|
std::cout << "Test 4 PASSED: PragmaDirective construction\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: PragmaDirective JSON roundtrip
|
||||||
|
{
|
||||||
|
auto* prag = new PragmaDirective("prag2", "pack(push, 1)");
|
||||||
|
json j = toJson(prag);
|
||||||
|
auto* restored = fromJson(j);
|
||||||
|
assert(restored->conceptType == "PragmaDirective");
|
||||||
|
auto* rp = static_cast<PragmaDirective*>(restored);
|
||||||
|
assert(rp->directive == "pack(push, 1)");
|
||||||
|
delete prag;
|
||||||
|
delete restored;
|
||||||
|
std::cout << "Test 5 PASSED: PragmaDirective JSON roundtrip\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: MacroDefinition — object-like macro
|
||||||
|
{
|
||||||
|
auto* mac = new MacroDefinition("mac1", "MAX_SIZE");
|
||||||
|
mac->body = "1024";
|
||||||
|
mac->isFunctionLike = false;
|
||||||
|
assert(mac->conceptType == "MacroDefinition");
|
||||||
|
assert(mac->name == "MAX_SIZE");
|
||||||
|
assert(mac->body == "1024");
|
||||||
|
assert(!mac->isFunctionLike);
|
||||||
|
assert(mac->parameters.empty());
|
||||||
|
delete mac;
|
||||||
|
std::cout << "Test 6 PASSED: MacroDefinition object-like macro\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: MacroDefinition — function-like macro with params
|
||||||
|
{
|
||||||
|
auto* mac = new MacroDefinition("mac2", "MIN");
|
||||||
|
mac->isFunctionLike = true;
|
||||||
|
mac->parameters = {"a", "b"};
|
||||||
|
mac->body = "((a) < (b) ? (a) : (b))";
|
||||||
|
assert(mac->isFunctionLike);
|
||||||
|
assert(mac->parameters.size() == 2);
|
||||||
|
assert(mac->parameters[0] == "a");
|
||||||
|
assert(mac->parameters[1] == "b");
|
||||||
|
delete mac;
|
||||||
|
std::cout << "Test 7 PASSED: MacroDefinition function-like macro\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: MacroDefinition JSON roundtrip
|
||||||
|
{
|
||||||
|
auto* mac = new MacroDefinition("mac3", "CONCAT");
|
||||||
|
mac->isFunctionLike = true;
|
||||||
|
mac->parameters = {"x", "y"};
|
||||||
|
mac->body = "x##y";
|
||||||
|
json j = toJson(mac);
|
||||||
|
auto* restored = fromJson(j);
|
||||||
|
assert(restored->conceptType == "MacroDefinition");
|
||||||
|
auto* rm = static_cast<MacroDefinition*>(restored);
|
||||||
|
assert(rm->name == "CONCAT");
|
||||||
|
assert(rm->isFunctionLike);
|
||||||
|
assert(rm->parameters.size() == 2);
|
||||||
|
assert(rm->parameters[0] == "x");
|
||||||
|
assert(rm->body == "x##y");
|
||||||
|
delete mac;
|
||||||
|
delete restored;
|
||||||
|
std::cout << "Test 8 PASSED: MacroDefinition JSON roundtrip\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 9: CompactAST names
|
||||||
|
{
|
||||||
|
auto* inc = new IncludeDirective("inc4", "iostream", true);
|
||||||
|
assert(getNodeName(inc) == "iostream");
|
||||||
|
auto* prag = new PragmaDirective("prag3", "once");
|
||||||
|
assert(getNodeName(prag) == "once");
|
||||||
|
auto* mac = new MacroDefinition("mac4", "DEBUG_LOG");
|
||||||
|
assert(getNodeName(mac) == "DEBUG_LOG");
|
||||||
|
delete inc; delete prag; delete mac;
|
||||||
|
std::cout << "Test 9 PASSED: CompactAST node names\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 10: Module with mixed preprocessor + function nodes
|
||||||
|
{
|
||||||
|
auto* mod = new Module();
|
||||||
|
mod->id = "mod1";
|
||||||
|
mod->name = "test_module";
|
||||||
|
auto* inc = new IncludeDirective("inc5", "stdio.h", true);
|
||||||
|
auto* prag = new PragmaDirective("prag4", "once");
|
||||||
|
auto* mac = new MacroDefinition("mac5", "VERSION");
|
||||||
|
mac->body = "\"1.0\"";
|
||||||
|
auto* func = new Function("fn1", "main");
|
||||||
|
mod->addChild("statements", inc);
|
||||||
|
mod->addChild("statements", prag);
|
||||||
|
mod->addChild("statements", mac);
|
||||||
|
mod->addChild("functions", func);
|
||||||
|
|
||||||
|
auto stmts = mod->getChildren("statements");
|
||||||
|
assert(stmts.size() == 3);
|
||||||
|
assert(stmts[0]->conceptType == "IncludeDirective");
|
||||||
|
assert(stmts[1]->conceptType == "PragmaDirective");
|
||||||
|
assert(stmts[2]->conceptType == "MacroDefinition");
|
||||||
|
delete mod;
|
||||||
|
std::cout << "Test 10 PASSED: Module with mixed preprocessor + function\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 11: Empty macro body
|
||||||
|
{
|
||||||
|
auto* mac = new MacroDefinition("mac6", "NDEBUG");
|
||||||
|
assert(mac->body.empty());
|
||||||
|
json j = toJson(mac);
|
||||||
|
auto* restored = fromJson(j);
|
||||||
|
auto* rm = static_cast<MacroDefinition*>(restored);
|
||||||
|
assert(rm->name == "NDEBUG");
|
||||||
|
assert(rm->body.empty());
|
||||||
|
assert(!rm->isFunctionLike);
|
||||||
|
delete mac;
|
||||||
|
delete restored;
|
||||||
|
std::cout << "Test 11 PASSED: Empty macro body\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 12: Serialization createNode dispatch for all 3 types
|
||||||
|
{
|
||||||
|
json jInc = {{"concept", "IncludeDirective"}, {"id", "test_inc"},
|
||||||
|
{"properties", {{"path", "test.h"}, {"isSystem", false}}}};
|
||||||
|
auto* rInc = fromJson(jInc);
|
||||||
|
assert(rInc != nullptr);
|
||||||
|
assert(rInc->conceptType == "IncludeDirective");
|
||||||
|
assert(static_cast<IncludeDirective*>(rInc)->path == "test.h");
|
||||||
|
|
||||||
|
json jPrag = {{"concept", "PragmaDirective"}, {"id", "test_prag"},
|
||||||
|
{"properties", {{"directive", "warning disable"}}}};
|
||||||
|
auto* rPrag = fromJson(jPrag);
|
||||||
|
assert(rPrag != nullptr);
|
||||||
|
assert(static_cast<PragmaDirective*>(rPrag)->directive == "warning disable");
|
||||||
|
|
||||||
|
json jMac = {{"concept", "MacroDefinition"}, {"id", "test_mac"},
|
||||||
|
{"properties", {{"name", "FOO"}, {"body", "bar"}}}};
|
||||||
|
auto* rMac = fromJson(jMac);
|
||||||
|
assert(rMac != nullptr);
|
||||||
|
assert(static_cast<MacroDefinition*>(rMac)->name == "FOO");
|
||||||
|
|
||||||
|
delete rInc; delete rPrag; delete rMac;
|
||||||
|
std::cout << "Test 12 PASSED: Serialization createNode dispatch\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\nResults: " << passed << "/12 tests passed\n";
|
||||||
|
return (passed == 12) ? 0 : 1;
|
||||||
|
}
|
||||||
23
progress.md
23
progress.md
@@ -1561,6 +1561,29 @@ verify diamond detection, and JSON serialization roundtrip.
|
|||||||
parse→generate roundtrip, C++→Java extends+implements, C++→Python multi-base, template
|
parse→generate roundtrip, C++→Java extends+implements, C++→Python multi-base, template
|
||||||
3 params, diamond inheritance detection, mixed template+inheritance+virtual pipeline
|
3 params, diamond inheritance detection, mixed template+inheritance+virtual pipeline
|
||||||
|
|
||||||
|
## Phase 12d: C++ AST Depth — Preprocessor, Enums, Namespaces
|
||||||
|
|
||||||
|
### Step 337: Preprocessor AST Nodes
|
||||||
|
**Status:** PASS (12/12 tests)
|
||||||
|
|
||||||
|
Added first-class AST nodes for C++ preprocessor directives: IncludeDirective (path,
|
||||||
|
isSystem), PragmaDirective (directive), MacroDefinition (name, parameters, body,
|
||||||
|
isFunctionLike). Statement-level nodes that attach to Module children. Full JSON
|
||||||
|
serialization roundtrip and CompactAST node name support.
|
||||||
|
|
||||||
|
**Files created:**
|
||||||
|
- `editor/src/ast/PreprocessorNodes.h` — IncludeDirective, PragmaDirective, MacroDefinition
|
||||||
|
- `editor/tests/step337_test.cpp` — 12 tests: construction, system vs local include,
|
||||||
|
JSON roundtrip, pragma, function-like vs object-like macros, CompactAST names, module
|
||||||
|
with mixed nodes, empty body, createNode dispatch
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
- `editor/src/ast/Serialization.h` — propertiesToJson/createNode/setPropertiesFromJson
|
||||||
|
for all 3 preprocessor types
|
||||||
|
- `editor/src/CompactAST.h` — getNodeName for IncludeDirective, PragmaDirective,
|
||||||
|
MacroDefinition
|
||||||
|
- `editor/CMakeLists.txt` — step337_test target
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Roadmap Planning — Sprints 12-25+
|
# Roadmap Planning — Sprints 12-25+
|
||||||
|
|||||||
Reference in New Issue
Block a user