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:
Bill
2026-02-16 00:36:25 -07:00
parent 2eef074fa1
commit 96799dd51f
6 changed files with 333 additions and 0 deletions

View 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";
}
};

View File

@@ -17,6 +17,7 @@
#include "ClassDeclaration.h"
#include "GenericType.h"
#include "AsyncNodes.h"
#include "PreprocessorNodes.h"
using json = nlohmann::json;
@@ -505,6 +506,23 @@ inline json propertiesToJson(const ASTNode* node) {
auto* n = static_cast<const DecoratorAnnotation*>(node);
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,
// WhileLoop, Return, ExpressionStatement, ListType, SetType, MapType,
// 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 == "LambdaExpression") return new LambdaExpression();
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;
}
@@ -1188,6 +1209,27 @@ inline void setPropertiesFromJson(ASTNode* node, const json& props) {
auto* n = static_cast<DecoratorAnnotation*>(node);
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() {