Files
whetstone_DSL/editor/src/SkeletonAST.h
Bill 0d51a6fe4c Steps 309-319: Sprint 11 Phases 11d-e — Kotlin/C# languages + workflow annotation foundation
Phase 11d (Steps 309-313): Kotlin + C# parsers and generators
- KotlinParser (regex-based): fun, suspend fun, class, data class, val/var
- KotlinGenerator: idiomatic Kotlin output with type mappings
- CSharpParser (regex-based): methods, async, class, interface
- CSharpGenerator: Allman braces, foreach, Task async, LINQ types
- Pipeline integration for both languages, 10 parsers + 10 generators

Phase 11e (Steps 314-319): Workflow annotation foundation
- AnnotationInference: generalized multi-subject inference engine
- Subject 9 routing annotations: ContextWidth, Review, Ambiguity,
  Automatability, Priority, ImplementationStatus
- SkeletonAST: project specification before code exists
- Architect tooling: createSkeleton, addSkeletonNode, getProjectModel,
  inferAnnotations RPCs + 4 MCP tools (42+ total)
- Inference-to-routing bridge: complexity→ambiguity, getter→deterministic
- TrainingDataExporter + TrainingDataGenerator scaffolding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:50:06 -07:00

253 lines
8.7 KiB
C++

#pragma once
// Step 316: Skeleton AST Support
//
// Modules and functions with annotations but no implementation — the
// architect's project specification. A skeleton AST is the project
// model before code exists.
#include "ast/ASTNode.h"
#include "ast/Module.h"
#include "ast/Function.h"
#include "ast/Variable.h"
#include "ast/Parameter.h"
#include "ast/Annotation.h"
#include "ast/ClassDeclaration.h"
#include <string>
#include <vector>
#include <memory>
// --- SkeletonTask: a flat task entry derived from a skeleton node ---
struct SkeletonTask {
std::string nodeId;
std::string nodeName;
std::string nodeType; // "Function", "ClassDeclaration", etc.
std::string contextWidth; // from @ContextWidth or "local"
std::string automatability; // from @Automatability or "llm"
std::string priority; // from @Priority or "medium"
bool reviewRequired = false; // from @Review or inferred from @Ambiguity
std::string status; // from @ImplementationStatus or "skeleton"
std::vector<std::string> dependencies; // from @Priority.blockedBy
};
// --- Skeleton creation helpers ---
inline Module* createSkeletonModule(const std::string& name,
const std::string& language) {
auto* mod = new Module("skel_" + name, name, language);
return mod;
}
inline Function* addSkeletonFunction(Module* mod,
const std::string& name,
const std::vector<std::string>& paramNames,
const std::string& returnType,
const std::vector<ASTNode*>& annotations = {}) {
static int fnCounter = 0;
auto* fn = new Function("skfn_" + std::to_string(++fnCounter), name);
// returnType stored as annotation or metadata (Function has no returnType field)
for (const auto& pname : paramNames) {
static int pCounter = 0;
auto* p = new Parameter();
p->id = "skp_" + std::to_string(++pCounter);
p->name = pname;
fn->addChild("parameters", p);
}
for (auto* anno : annotations) {
fn->addChild("annotations", anno);
}
// Add ImplementationStatus(skeleton) if not already present
bool hasStatus = false;
for (auto* a : fn->getChildren("annotations")) {
if (a->conceptType == "ImplementationStatusAnnotation") {
hasStatus = true;
break;
}
}
if (!hasStatus) {
auto* implStatus = new ImplementationStatusAnnotation();
static int isCounter = 0;
implStatus->id = "skis_" + std::to_string(++isCounter);
implStatus->status = "skeleton";
fn->addChild("annotations", implStatus);
}
mod->addChild("functions", fn);
return fn;
}
inline ClassDeclaration* addSkeletonClass(Module* mod,
const std::string& name,
const std::vector<std::string>& methodNames,
const std::vector<std::string>& fieldNames = {}) {
static int clsCounter = 0;
auto* cls = new ClassDeclaration();
cls->id = "skcls_" + std::to_string(++clsCounter);
cls->name = name;
cls->conceptType = "ClassDeclaration";
for (const auto& mname : methodNames) {
static int mdCounter = 0;
auto* method = new MethodDeclaration("skmd_" + std::to_string(++mdCounter), mname);
method->className = name;
// Mark as skeleton
auto* implStatus = new ImplementationStatusAnnotation();
static int misCounter = 0;
implStatus->id = "skmis_" + std::to_string(++misCounter);
implStatus->status = "skeleton";
method->addChild("annotations", implStatus);
cls->addChild("methods", method);
}
for (const auto& fname : fieldNames) {
auto* field = new Variable("skfld_" + fname, fname);
cls->addChild("fields", field);
}
mod->addChild("functions", cls); // Module stores top-level items as "functions"
return cls;
}
// --- Skeleton detection ---
inline bool isSkeletonNode(const ASTNode* node) {
if (!node) return false;
auto annos = node->getChildren("annotations");
for (const auto* a : annos) {
if (a->conceptType == "ImplementationStatusAnnotation") {
auto* is = static_cast<const ImplementationStatusAnnotation*>(a);
return is->status == "skeleton";
}
}
// Also skeleton if function has empty body
if (node->conceptType == "Function" || node->conceptType == "AsyncFunction" ||
node->conceptType == "MethodDeclaration") {
auto body = node->getChildren("body");
return body.empty();
}
return false;
}
// --- Skeleton summary ---
struct SkeletonSummary {
int totalNodes = 0;
int skeletonNodes = 0;
int implementedNodes = 0;
std::map<std::string, int> byAnnotationType; // conceptType → count
};
inline void countSkeletonNodes(const ASTNode* node, SkeletonSummary& summary) {
if (!node) return;
bool isFnLike = (node->conceptType == "Function" ||
node->conceptType == "AsyncFunction" ||
node->conceptType == "MethodDeclaration" ||
node->conceptType == "ClassDeclaration");
if (isFnLike) {
summary.totalNodes++;
if (isSkeletonNode(node)) {
summary.skeletonNodes++;
} else {
summary.implementedNodes++;
}
}
// Count annotation types
auto annos = node->getChildren("annotations");
for (const auto* a : annos) {
summary.byAnnotationType[a->conceptType]++;
}
for (const auto* child : node->allChildren()) {
countSkeletonNodes(child, summary);
}
}
inline SkeletonSummary getSkeletonSummary(const ASTNode* module) {
SkeletonSummary summary;
countSkeletonNodes(module, summary);
return summary;
}
// --- Task list generation ---
inline void extractRoutingAnnotations(const ASTNode* node, SkeletonTask& task) {
auto annos = node->getChildren("annotations");
for (const auto* a : annos) {
if (a->conceptType == "ContextWidthAnnotation") {
task.contextWidth = static_cast<const ContextWidthAnnotation*>(a)->width;
}
else if (a->conceptType == "AutomatabilityAnnotation") {
task.automatability = static_cast<const AutomatabilityAnnotation*>(a)->strategy;
}
else if (a->conceptType == "PriorityAnnotation") {
auto* pa = static_cast<const PriorityAnnotation*>(a);
task.priority = pa->level;
task.dependencies = pa->blockedBy;
}
else if (a->conceptType == "ReviewAnnotation") {
task.reviewRequired = static_cast<const ReviewAnnotation*>(a)->required;
}
else if (a->conceptType == "ImplementationStatusAnnotation") {
task.status = static_cast<const ImplementationStatusAnnotation*>(a)->status;
}
else if (a->conceptType == "AmbiguityAnnotation") {
auto* amb = static_cast<const AmbiguityAnnotation*>(a);
if (amb->level == "high" || amb->level == "medium") {
task.reviewRequired = true;
}
}
}
// Defaults
if (task.contextWidth.empty()) task.contextWidth = "local";
if (task.automatability.empty()) task.automatability = "llm";
if (task.priority.empty()) task.priority = "medium";
if (task.status.empty()) task.status = "skeleton";
}
inline std::string getTaskNodeName(const ASTNode* node) {
if (node->conceptType == "Function" || node->conceptType == "AsyncFunction" ||
node->conceptType == "MethodDeclaration") {
return static_cast<const Function*>(node)->name;
}
if (node->conceptType == "ClassDeclaration") {
return static_cast<const ClassDeclaration*>(node)->name;
}
return node->id;
}
inline void collectSkeletonTasks(const ASTNode* node,
std::vector<SkeletonTask>& tasks) {
if (!node) return;
bool isFnLike = (node->conceptType == "Function" ||
node->conceptType == "AsyncFunction" ||
node->conceptType == "MethodDeclaration" ||
node->conceptType == "ClassDeclaration");
if (isFnLike) {
SkeletonTask task;
task.nodeId = node->id;
task.nodeName = getTaskNodeName(node);
task.nodeType = node->conceptType;
extractRoutingAnnotations(node, task);
tasks.push_back(task);
}
for (const auto* child : node->allChildren()) {
collectSkeletonTasks(child, tasks);
}
}
inline std::vector<SkeletonTask> skeletonToTaskList(const ASTNode* module) {
std::vector<SkeletonTask> tasks;
collectSkeletonTasks(module, tasks);
return tasks;
}