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>
This commit is contained in:
@@ -63,7 +63,9 @@ struct AgentPermissionPolicy {
|
||||
method == "getUnannotatedNodes" ||
|
||||
method == "getEnvironment" ||
|
||||
method == "validateEnvironment" ||
|
||||
method == "getLoweringHints") {
|
||||
method == "getLoweringHints" ||
|
||||
method == "getProjectModel" ||
|
||||
method == "inferAnnotations") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -85,7 +87,9 @@ struct AgentPermissionPolicy {
|
||||
method == "saveAnnotatedAST" ||
|
||||
method == "setSemanticAnnotation" ||
|
||||
method == "removeSemanticAnnotation" ||
|
||||
method == "setEnvironment") {
|
||||
method == "setEnvironment" ||
|
||||
method == "createSkeleton" ||
|
||||
method == "addSkeletonNode") {
|
||||
return role == AgentRole::Refactor || role == AgentRole::Generator;
|
||||
}
|
||||
|
||||
|
||||
380
editor/src/AnnotationInference.h
Normal file
380
editor/src/AnnotationInference.h
Normal file
@@ -0,0 +1,380 @@
|
||||
#pragma once
|
||||
#include "ast/ASTNode.h"
|
||||
#include "ast/Module.h"
|
||||
#include "ast/Function.h"
|
||||
#include "ast/Variable.h"
|
||||
#include "ast/Statement.h"
|
||||
#include "ast/Expression.h"
|
||||
#include "ast/Annotation.h"
|
||||
#include "MemoryStrategyInference.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
class AnnotationInference {
|
||||
public:
|
||||
struct InferredAnnotation {
|
||||
std::string nodeId;
|
||||
std::string annotationType;
|
||||
std::string key; // primary property name
|
||||
std::string value; // primary property value
|
||||
std::string reason;
|
||||
double confidence;
|
||||
};
|
||||
|
||||
std::vector<InferredAnnotation> inferAll(const ASTNode* root) const {
|
||||
std::vector<InferredAnnotation> out;
|
||||
if (!root) return out;
|
||||
|
||||
// Delegate memory annotations to existing MemoryStrategyInference
|
||||
if (root->conceptType == "Module") {
|
||||
MemoryStrategyInference memInf;
|
||||
auto memSuggestions = memInf.inferAnnotations(root);
|
||||
for (const auto& s : memSuggestions) {
|
||||
out.push_back({s.nodeId, s.annotationType, "strategy", s.strategy, s.reason, s.confidence});
|
||||
}
|
||||
}
|
||||
|
||||
// Infer all other annotation types
|
||||
inferNode(root, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
void inferNode(const ASTNode* node, std::vector<InferredAnnotation>& out) const {
|
||||
if (!node) return;
|
||||
|
||||
if (node->conceptType == "Function" || node->conceptType == "AsyncFunction" ||
|
||||
node->conceptType == "MethodDeclaration") {
|
||||
inferFunction(node, out);
|
||||
}
|
||||
else if (node->conceptType == "ForLoop" || node->conceptType == "WhileLoop") {
|
||||
inferLoop(node, out);
|
||||
}
|
||||
|
||||
for (auto* child : node->allChildren()) {
|
||||
inferNode(child, out);
|
||||
}
|
||||
}
|
||||
|
||||
void inferFunction(const ASTNode* fn, std::vector<InferredAnnotation>& out) const {
|
||||
// Skip if already annotated
|
||||
auto annos = fn->getChildren("annotations");
|
||||
std::set<std::string> existing;
|
||||
for (const auto* a : annos) existing.insert(a->conceptType);
|
||||
|
||||
auto body = fn->getChildren("body");
|
||||
|
||||
// Async detection
|
||||
if (fn->conceptType == "AsyncFunction" && !existing.count("ExecAnnotation")) {
|
||||
out.push_back({fn->id, "ExecAnnotation", "mode", "async",
|
||||
"async function detected", 0.95});
|
||||
}
|
||||
|
||||
// Pure function detection (no side effects)
|
||||
if (!existing.count("PureAnnotation") && isPure(fn)) {
|
||||
out.push_back({fn->id, "PureAnnotation", "", "",
|
||||
"No side effects detected", 0.70});
|
||||
}
|
||||
|
||||
// Recursive / tail call detection
|
||||
std::string fnName;
|
||||
if (fn->conceptType == "Function") {
|
||||
fnName = static_cast<const Function*>(fn)->name;
|
||||
} else if (fn->conceptType == "MethodDeclaration") {
|
||||
fnName = static_cast<const Function*>(fn)->name;
|
||||
}
|
||||
if (!fnName.empty() && !existing.count("TailCallAnnotation")) {
|
||||
if (isTailRecursive(fn, fnName)) {
|
||||
out.push_back({fn->id, "TailCallAnnotation", "", "",
|
||||
"Tail-recursive call detected", 0.80});
|
||||
}
|
||||
}
|
||||
|
||||
// Visibility inference for methods
|
||||
if (fn->conceptType == "MethodDeclaration" && !existing.count("VisibilityAnnotation")) {
|
||||
out.push_back({fn->id, "VisibilityAnnotation", "level", "public",
|
||||
"Default method visibility", 0.60});
|
||||
}
|
||||
|
||||
// Exception handling detection
|
||||
if (!existing.count("ExceptionAnnotation") && hasExceptionHandling(fn)) {
|
||||
out.push_back({fn->id, "ExceptionAnnotation", "style", "unchecked",
|
||||
"try/catch pattern detected", 0.75});
|
||||
}
|
||||
|
||||
// Blocking detection
|
||||
if (!existing.count("BlockingAnnotation") && hasBlockingCalls(fn)) {
|
||||
out.push_back({fn->id, "BlockingAnnotation", "kind", "io",
|
||||
"IO operations detected", 0.65});
|
||||
}
|
||||
|
||||
// Parallel detection (goroutine-like patterns)
|
||||
if (!existing.count("ParallelAnnotation") && hasParallelPatterns(fn)) {
|
||||
out.push_back({fn->id, "ParallelAnnotation", "kind", "task",
|
||||
"Parallel dispatch pattern detected", 0.70});
|
||||
}
|
||||
|
||||
// Complexity inference
|
||||
if (!existing.count("ComplexityAnnotation")) {
|
||||
int depth = nestingDepth(fn);
|
||||
std::string complexity = "O(1)";
|
||||
if (depth >= 2) complexity = "O(n^2)";
|
||||
else if (depth >= 1) complexity = "O(n)";
|
||||
out.push_back({fn->id, "ComplexityAnnotation", "timeComplexity", complexity,
|
||||
"Nesting depth analysis", 0.55});
|
||||
}
|
||||
}
|
||||
|
||||
void inferLoop(const ASTNode* loop, std::vector<InferredAnnotation>& out) const {
|
||||
auto annos = loop->getChildren("annotations");
|
||||
std::set<std::string> existing;
|
||||
for (const auto* a : annos) existing.insert(a->conceptType);
|
||||
|
||||
if (!existing.count("LoopAnnotation")) {
|
||||
out.push_back({loop->id, "LoopAnnotation", "hint", "vectorize",
|
||||
"Loop detected, vectorization candidate", 0.50});
|
||||
}
|
||||
}
|
||||
|
||||
bool isPure(const ASTNode* fn) const {
|
||||
auto body = fn->getChildren("body");
|
||||
for (auto* stmt : body) {
|
||||
if (hasSideEffect(stmt)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasSideEffect(const ASTNode* node) const {
|
||||
if (!node) return false;
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
if (fc->functionName == "print" || fc->functionName == "write" ||
|
||||
fc->functionName == "send" || fc->functionName == "log" ||
|
||||
fc->functionName == "console.log")
|
||||
return true;
|
||||
}
|
||||
if (node->conceptType == "Assignment") return true;
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (hasSideEffect(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isTailRecursive(const ASTNode* fn, const std::string& name) const {
|
||||
auto body = fn->getChildren("body");
|
||||
if (body.empty()) return false;
|
||||
auto* last = body.back();
|
||||
if (last->conceptType == "Return") {
|
||||
auto* val = last->getChild("value");
|
||||
if (val && val->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(val);
|
||||
return fc->functionName == name;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasExceptionHandling(const ASTNode* node) const {
|
||||
if (!node) return false;
|
||||
// Look for try/catch patterns in function calls
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
if (fc->functionName == "try" || fc->functionName == "catch" ||
|
||||
fc->functionName == "throw" || fc->functionName == "raise")
|
||||
return true;
|
||||
}
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (hasExceptionHandling(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasBlockingCalls(const ASTNode* node) const {
|
||||
if (!node) return false;
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
if (fc->functionName == "read" || fc->functionName == "write" ||
|
||||
fc->functionName == "open" || fc->functionName == "close" ||
|
||||
fc->functionName == "sleep" || fc->functionName == "recv" ||
|
||||
fc->functionName == "send" || fc->functionName == "connect")
|
||||
return true;
|
||||
}
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (hasBlockingCalls(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasParallelPatterns(const ASTNode* node) const {
|
||||
if (!node) return false;
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
if (fc->functionName == "go" || fc->functionName == "spawn" ||
|
||||
fc->functionName == "thread" || fc->functionName == "parallel_for")
|
||||
return true;
|
||||
}
|
||||
if (node->conceptType == "ScheduleTask") return true;
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (hasParallelPatterns(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int nestingDepth(const ASTNode* node) const {
|
||||
if (!node) return 0;
|
||||
int maxChild = 0;
|
||||
bool isLoop = (node->conceptType == "ForLoop" || node->conceptType == "WhileLoop");
|
||||
for (auto* child : node->allChildren()) {
|
||||
maxChild = std::max(maxChild, nestingDepth(child));
|
||||
}
|
||||
return maxChild + (isLoop ? 1 : 0);
|
||||
}
|
||||
|
||||
// --- Step 318: Inference-to-Routing Bridge ---
|
||||
|
||||
public:
|
||||
// Infer Subject 9 routing annotations from code patterns.
|
||||
std::vector<InferredAnnotation> inferRoutingAnnotations(const ASTNode* node) const {
|
||||
std::vector<InferredAnnotation> out;
|
||||
if (!node) return out;
|
||||
|
||||
// Check which routing annotations already exist
|
||||
auto annos = node->getChildren("annotations");
|
||||
std::set<std::string> existing;
|
||||
for (const auto* a : annos) existing.insert(a->conceptType);
|
||||
|
||||
int depth = nestingDepth(node);
|
||||
bool isAsync = (node->conceptType == "AsyncFunction");
|
||||
bool hasErrorHandling = hasExceptionHandling(node);
|
||||
bool hasBlocking = hasBlockingCalls(node);
|
||||
int bodySize = countNodes(node);
|
||||
bool hasCrossFileRefs = hasCrossFileCalls(node);
|
||||
|
||||
// --- ContextWidth ---
|
||||
if (!existing.count("ContextWidthAnnotation")) {
|
||||
if (hasCrossFileRefs) {
|
||||
out.push_back({node->id, "ContextWidthAnnotation", "width", "project",
|
||||
"Cross-file references detected", 0.80});
|
||||
} else if (isAsync || bodySize > 15) {
|
||||
out.push_back({node->id, "ContextWidthAnnotation", "width", "file",
|
||||
"Async or large function needs file context", 0.70});
|
||||
} else {
|
||||
out.push_back({node->id, "ContextWidthAnnotation", "width", "local",
|
||||
"Simple local function", 0.75});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Automatability ---
|
||||
if (!existing.count("AutomatabilityAnnotation")) {
|
||||
auto bodyChildren = node->getChildren("body");
|
||||
int bodyStmts = (int)bodyChildren.size();
|
||||
if (depth >= 2 || (isAsync && hasErrorHandling)) {
|
||||
out.push_back({node->id, "AutomatabilityAnnotation", "strategy", "llm",
|
||||
"Complex logic or async+error requires LLM", 0.75});
|
||||
} else if (bodyStmts <= 1) {
|
||||
out.push_back({node->id, "AutomatabilityAnnotation", "strategy", "deterministic",
|
||||
"Simple getter/setter pattern", 0.85});
|
||||
} else {
|
||||
out.push_back({node->id, "AutomatabilityAnnotation", "strategy", "template",
|
||||
"Straightforward transformation", 0.70});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ambiguity (high complexity) ---
|
||||
if (!existing.count("AmbiguityAnnotation") && depth >= 2) {
|
||||
out.push_back({node->id, "AmbiguityAnnotation", "level", "high",
|
||||
"High cyclomatic complexity (nesting depth " + std::to_string(depth) + ")",
|
||||
0.80});
|
||||
}
|
||||
|
||||
// --- Review (complex or cross-file) ---
|
||||
if (!existing.count("ReviewAnnotation") && (depth >= 2 || hasCrossFileRefs)) {
|
||||
out.push_back({node->id, "ReviewAnnotation", "required", "true",
|
||||
"Complex or cross-file code requires human review", 0.75});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Rough token count for the given context width.
|
||||
int estimateContextTokens(const ASTNode* node, const std::string& width) const {
|
||||
if (!node) return 0;
|
||||
int nodeTokens = countNodes(node) * 4; // ~4 tokens per AST node
|
||||
if (nodeTokens < 10) nodeTokens = 10;
|
||||
|
||||
if (width == "local") {
|
||||
return nodeTokens;
|
||||
} else if (width == "file") {
|
||||
// Walk up to find module root, estimate full file
|
||||
const ASTNode* root = node;
|
||||
while (root->parent) root = root->parent;
|
||||
int fileTokens = countNodes(root) * 4;
|
||||
return std::max(fileTokens, nodeTokens);
|
||||
} else if (width == "project" || width == "cross-project") {
|
||||
// Estimate multiple files worth of context
|
||||
const ASTNode* root = node;
|
||||
while (root->parent) root = root->parent;
|
||||
int fileTokens = countNodes(root) * 4;
|
||||
return std::max(fileTokens * 5, nodeTokens); // ~5 files estimated
|
||||
}
|
||||
return nodeTokens;
|
||||
}
|
||||
|
||||
// Given routing annotations, suggest worker type.
|
||||
std::string suggestWorkerType(const std::vector<InferredAnnotation>& annos) const {
|
||||
// Check for explicit automatability
|
||||
for (const auto& a : annos) {
|
||||
if (a.annotationType == "AutomatabilityAnnotation") {
|
||||
const std::string& v = a.value;
|
||||
if (v == "deterministic" || v == "template" ||
|
||||
v == "slm" || v == "llm" || v == "human")
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for review/ambiguity → human
|
||||
bool hasReview = false, hasHighAmbiguity = false;
|
||||
for (const auto& a : annos) {
|
||||
if (a.annotationType == "ReviewAnnotation") hasReview = true;
|
||||
if (a.annotationType == "AmbiguityAnnotation" &&
|
||||
(a.value == "high" || a.value == "critical"))
|
||||
hasHighAmbiguity = true;
|
||||
}
|
||||
if (hasReview && hasHighAmbiguity) return "human";
|
||||
|
||||
// Check context width for hints
|
||||
for (const auto& a : annos) {
|
||||
if (a.annotationType == "ContextWidthAnnotation") {
|
||||
if (a.value == "project" || a.value == "cross-project") return "llm";
|
||||
if (a.value == "local") return "template";
|
||||
}
|
||||
}
|
||||
|
||||
return "slm"; // default
|
||||
}
|
||||
|
||||
private:
|
||||
int countNodes(const ASTNode* node) const {
|
||||
if (!node) return 0;
|
||||
int count = 1;
|
||||
for (auto* child : node->allChildren()) {
|
||||
count += countNodes(child);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool hasCrossFileCalls(const ASTNode* node) const {
|
||||
if (!node) return false;
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
// Dotted names like "module.function" suggest cross-file reference
|
||||
if (fc->functionName.find('.') != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (hasCrossFileCalls(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "ast/Serialization.h"
|
||||
#include "ast/Annotation.h"
|
||||
#include "ast/HostBoundary.h"
|
||||
#include "ASTUtils.h"
|
||||
#include "EnvironmentSpec.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
@@ -321,6 +322,33 @@ inline json extractSemanticSummary(const ASTNode* node) {
|
||||
if (!da->selection.empty()) obj["selection"] = da->selection;
|
||||
if (!obj.empty()) sem["decision"] = obj;
|
||||
}
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
else if (a->conceptType == "ContextWidthAnnotation") {
|
||||
auto* cw = static_cast<const ContextWidthAnnotation*>(a);
|
||||
if (!cw->width.empty()) sem["contextWidth"] = cw->width;
|
||||
}
|
||||
else if (a->conceptType == "ReviewAnnotation") {
|
||||
auto* ra = static_cast<const ReviewAnnotation*>(a);
|
||||
json obj;
|
||||
obj["required"] = ra->required;
|
||||
if (!ra->reviewer.empty()) obj["reviewer"] = ra->reviewer;
|
||||
sem["review"] = obj;
|
||||
}
|
||||
else if (a->conceptType == "AutomatabilityAnnotation") {
|
||||
auto* aa = static_cast<const AutomatabilityAnnotation*>(a);
|
||||
json obj;
|
||||
if (!aa->strategy.empty()) obj["strategy"] = aa->strategy;
|
||||
if (aa->confidence > 0.0) obj["confidence"] = aa->confidence;
|
||||
sem["automatability"] = obj;
|
||||
}
|
||||
else if (a->conceptType == "PriorityAnnotation") {
|
||||
auto* pa = static_cast<const PriorityAnnotation*>(a);
|
||||
if (!pa->level.empty()) sem["priority"] = pa->level;
|
||||
}
|
||||
else if (a->conceptType == "ImplementationStatusAnnotation") {
|
||||
auto* isa = static_cast<const ImplementationStatusAnnotation*>(a);
|
||||
if (!isa->status.empty()) sem["implStatus"] = isa->status;
|
||||
}
|
||||
// Environment Layer (Step 285)
|
||||
else if (a->conceptType == "CapabilityRequirement") {
|
||||
auto* cr = static_cast<const CapabilityRequirement*>(a);
|
||||
|
||||
@@ -1716,6 +1716,12 @@ inline json handleHeadlessAgentRequest(HeadlessEditorState& state,
|
||||
else if (type == "tradeoff") conceptType = "TradeoffAnnotation";
|
||||
else if (type == "choice") conceptType = "ChoiceAnnotation";
|
||||
else if (type == "decision") conceptType = "DecisionAnnotation";
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
else if (type == "contextWidth") conceptType = "ContextWidthAnnotation";
|
||||
else if (type == "review") conceptType = "ReviewAnnotation";
|
||||
else if (type == "automatability") conceptType = "AutomatabilityAnnotation";
|
||||
else if (type == "priority") conceptType = "PriorityAnnotation";
|
||||
else if (type == "implementationStatus") conceptType = "ImplementationStatusAnnotation";
|
||||
// Environment Layer (Step 285)
|
||||
else if (type == "capabilityRequirement") conceptType = "CapabilityRequirement";
|
||||
else return headlessRpcError(id, -32602, "Unknown annotation type: " + type);
|
||||
@@ -2085,5 +2091,160 @@ inline json handleHeadlessAgentRequest(HeadlessEditorState& state,
|
||||
{"count", (int)hintsJson.size()}});
|
||||
}
|
||||
|
||||
// --- Step 317: Workflow Annotation Foundation ---
|
||||
|
||||
// createSkeleton — create a new skeleton module
|
||||
if (method == "createSkeleton") {
|
||||
if (!AgentPermissionPolicy::canInvoke(role, method))
|
||||
return headlessRpcError(id, -32031, "Role not permitted");
|
||||
auto params = request.contains("params") ? request["params"] : json::object();
|
||||
std::string name = params.value("name", "skeleton");
|
||||
std::string language = params.value("language", "python");
|
||||
|
||||
// Create skeleton module using openBuffer with empty content
|
||||
std::string bufPath = "skel_" + name;
|
||||
auto* buf = state.openBuffer(bufPath, "", language);
|
||||
// Replace the default empty module with a properly named skeleton
|
||||
auto* rawMod = createSkeletonModule(name, language);
|
||||
buf->sync.setAST(std::unique_ptr<Module>(rawMod));
|
||||
buf->modified = true;
|
||||
return headlessRpcResult(id, {{"bufferId", bufPath},
|
||||
{"name", name}, {"language", language}});
|
||||
}
|
||||
|
||||
// addSkeletonNode — add function/class skeleton with annotations
|
||||
if (method == "addSkeletonNode") {
|
||||
if (!AgentPermissionPolicy::canInvoke(role, method))
|
||||
return headlessRpcError(id, -32031, "Role not permitted");
|
||||
auto params = request.contains("params") ? request["params"] : json::object();
|
||||
if (!state.activeBuffer)
|
||||
return headlessRpcError(id, -32602, "No active buffer");
|
||||
Module* ast = state.activeAST();
|
||||
if (!ast)
|
||||
return headlessRpcError(id, -32602, "No AST available");
|
||||
|
||||
std::string nodeType = params.value("nodeType", "function");
|
||||
std::string name = params.value("name", "unnamed");
|
||||
std::vector<std::string> paramNames;
|
||||
if (params.contains("parameters") && params["parameters"].is_array()) {
|
||||
for (const auto& p : params["parameters"])
|
||||
if (p.is_string()) paramNames.push_back(p.get<std::string>());
|
||||
}
|
||||
|
||||
// Build annotations from params
|
||||
std::vector<ASTNode*> annos;
|
||||
if (params.contains("contextWidth")) {
|
||||
auto* cw = new ContextWidthAnnotation();
|
||||
static int cwC = 0;
|
||||
cw->id = "rpc_cw_" + std::to_string(++cwC);
|
||||
cw->width = params["contextWidth"].get<std::string>();
|
||||
annos.push_back(cw);
|
||||
}
|
||||
if (params.contains("automatability")) {
|
||||
auto* aa = new AutomatabilityAnnotation();
|
||||
static int aaC = 0;
|
||||
aa->id = "rpc_aa_" + std::to_string(++aaC);
|
||||
aa->strategy = params["automatability"].get<std::string>();
|
||||
annos.push_back(aa);
|
||||
}
|
||||
if (params.contains("priority")) {
|
||||
auto* pa = new PriorityAnnotation();
|
||||
static int paC = 0;
|
||||
pa->id = "rpc_pa_" + std::to_string(++paC);
|
||||
pa->level = params["priority"].get<std::string>();
|
||||
if (params.contains("blockedBy") && params["blockedBy"].is_array()) {
|
||||
for (const auto& b : params["blockedBy"])
|
||||
if (b.is_string()) pa->blockedBy.push_back(b.get<std::string>());
|
||||
}
|
||||
annos.push_back(pa);
|
||||
}
|
||||
|
||||
std::string nodeId;
|
||||
if (nodeType == "class") {
|
||||
std::vector<std::string> methods;
|
||||
if (params.contains("methods") && params["methods"].is_array()) {
|
||||
for (const auto& m : params["methods"])
|
||||
if (m.is_string()) methods.push_back(m.get<std::string>());
|
||||
}
|
||||
auto* cls = addSkeletonClass(ast, name, methods, {});
|
||||
nodeId = cls->id;
|
||||
} else {
|
||||
auto* fn = addSkeletonFunction(ast, name, paramNames, "", annos);
|
||||
nodeId = fn->id;
|
||||
}
|
||||
|
||||
state.activeBuffer->modified = true;
|
||||
return headlessRpcResult(id, {{"nodeId", nodeId}, {"name", name},
|
||||
{"nodeType", nodeType}});
|
||||
}
|
||||
|
||||
// getProjectModel — skeleton summary + task list
|
||||
if (method == "getProjectModel") {
|
||||
if (!AgentPermissionPolicy::canInvoke(role, method))
|
||||
return headlessRpcError(id, -32031, "Role not permitted");
|
||||
if (!state.activeBuffer)
|
||||
return headlessRpcError(id, -32602, "No active buffer");
|
||||
Module* ast = state.activeAST();
|
||||
if (!ast)
|
||||
return headlessRpcError(id, -32602, "No AST available");
|
||||
|
||||
auto summary = getSkeletonSummary(ast);
|
||||
auto tasks = skeletonToTaskList(ast);
|
||||
|
||||
json tasksJson = json::array();
|
||||
for (const auto& t : tasks) {
|
||||
json tj;
|
||||
tj["nodeId"] = t.nodeId;
|
||||
tj["nodeName"] = t.nodeName;
|
||||
tj["nodeType"] = t.nodeType;
|
||||
tj["contextWidth"] = t.contextWidth;
|
||||
tj["automatability"] = t.automatability;
|
||||
tj["priority"] = t.priority;
|
||||
tj["reviewRequired"] = t.reviewRequired;
|
||||
tj["status"] = t.status;
|
||||
if (!t.dependencies.empty())
|
||||
tj["dependencies"] = t.dependencies;
|
||||
tasksJson.push_back(tj);
|
||||
}
|
||||
|
||||
return headlessRpcResult(id, {
|
||||
{"totalNodes", summary.totalNodes},
|
||||
{"skeletonNodes", summary.skeletonNodes},
|
||||
{"implementedNodes", summary.implementedNodes},
|
||||
{"tasks", tasksJson}
|
||||
});
|
||||
}
|
||||
|
||||
// inferAnnotations — run AnnotationInference on active buffer
|
||||
if (method == "inferAnnotations") {
|
||||
if (!AgentPermissionPolicy::canInvoke(role, method))
|
||||
return headlessRpcError(id, -32031, "Role not permitted");
|
||||
if (!state.activeBuffer)
|
||||
return headlessRpcError(id, -32602, "No active buffer");
|
||||
Module* ast = state.activeAST();
|
||||
if (!ast)
|
||||
return headlessRpcError(id, -32602, "No AST available");
|
||||
|
||||
AnnotationInference infEngine;
|
||||
auto inferred = infEngine.inferAll(ast);
|
||||
|
||||
json suggestionsJson = json::array();
|
||||
for (const auto& inf : inferred) {
|
||||
json sj;
|
||||
sj["nodeId"] = inf.nodeId;
|
||||
sj["annotationType"] = inf.annotationType;
|
||||
if (!inf.key.empty()) sj["key"] = inf.key;
|
||||
if (!inf.value.empty()) sj["value"] = inf.value;
|
||||
sj["reason"] = inf.reason;
|
||||
sj["confidence"] = inf.confidence;
|
||||
suggestionsJson.push_back(sj);
|
||||
}
|
||||
|
||||
return headlessRpcResult(id, {
|
||||
{"suggestions", suggestionsJson},
|
||||
{"count", (int)suggestionsJson.size()}
|
||||
});
|
||||
}
|
||||
|
||||
return headlessRpcError(id, -32601, "Method not found");
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "SemanticTags.h"
|
||||
#include "ProjectState.h"
|
||||
#include "SidecarPersistence.h"
|
||||
#include "AnnotationInference.h"
|
||||
#include "SkeletonAST.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
@@ -1276,6 +1276,85 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
void registerWorkflowTools() {
|
||||
// whetstone_create_skeleton
|
||||
tools_.push_back({"whetstone_create_skeleton",
|
||||
"Create a new skeleton module — an architect's project specification "
|
||||
"with annotated function/class signatures but no implementation. "
|
||||
"Returns bufferId for the new skeleton module.",
|
||||
{{"type", "object"}, {"properties", {
|
||||
{"name", {{"type", "string"},
|
||||
{"description", "Module name"}}},
|
||||
{"language", {{"type", "string"},
|
||||
{"description", "Target language (python, cpp, rust, etc.)"}}}
|
||||
}}, {"required", json::array({"name", "language"})}}
|
||||
});
|
||||
toolHandlers_["whetstone_create_skeleton"] =
|
||||
[this](const json& args) {
|
||||
return callWhetstone("createSkeleton", args);
|
||||
};
|
||||
|
||||
// whetstone_add_skeleton_node
|
||||
tools_.push_back({"whetstone_add_skeleton_node",
|
||||
"Add a function or class skeleton with routing annotations to the "
|
||||
"active skeleton module. Annotations control how the task is dispatched: "
|
||||
"contextWidth (local/file/project), automatability (deterministic/template/slm/llm/human), "
|
||||
"priority (critical/high/medium/low).",
|
||||
{{"type", "object"}, {"properties", {
|
||||
{"nodeType", {{"type", "string"},
|
||||
{"enum", {"function", "class"}},
|
||||
{"description", "Type of skeleton node"}}},
|
||||
{"name", {{"type", "string"},
|
||||
{"description", "Function or class name"}}},
|
||||
{"parameters", {{"type", "array"}, {"items", {{"type", "string"}}},
|
||||
{"description", "Parameter names (for functions)"}}},
|
||||
{"contextWidth", {{"type", "string"},
|
||||
{"enum", {"local", "file", "project", "cross-project"}},
|
||||
{"description", "How much context needed"}}},
|
||||
{"automatability", {{"type", "string"},
|
||||
{"enum", {"deterministic", "template", "slm", "llm", "human"}},
|
||||
{"description", "What kind of worker should handle this"}}},
|
||||
{"priority", {{"type", "string"},
|
||||
{"enum", {"critical", "high", "medium", "low"}},
|
||||
{"description", "Task priority"}}},
|
||||
{"blockedBy", {{"type", "array"}, {"items", {{"type", "string"}}},
|
||||
{"description", "Task names this depends on"}}},
|
||||
{"methods", {{"type", "array"}, {"items", {{"type", "string"}}},
|
||||
{"description", "Method names (for classes)"}}}
|
||||
}}, {"required", json::array({"name"})}}
|
||||
});
|
||||
toolHandlers_["whetstone_add_skeleton_node"] =
|
||||
[this](const json& args) {
|
||||
return callWhetstone("addSkeletonNode", args);
|
||||
};
|
||||
|
||||
// whetstone_get_project_model
|
||||
tools_.push_back({"whetstone_get_project_model",
|
||||
"Get the skeleton summary and task list for the active buffer. "
|
||||
"Returns total/skeleton/implemented node counts plus a flat task "
|
||||
"list with routing annotations (contextWidth, automatability, "
|
||||
"priority, reviewRequired, status, dependencies).",
|
||||
{{"type", "object"}, {"properties", json::object()}}
|
||||
});
|
||||
toolHandlers_["whetstone_get_project_model"] =
|
||||
[this](const json& args) {
|
||||
return callWhetstone("getProjectModel", args);
|
||||
};
|
||||
|
||||
// whetstone_infer_annotations
|
||||
tools_.push_back({"whetstone_infer_annotations",
|
||||
"Auto-infer annotations on the active buffer's AST. Covers all "
|
||||
"8 annotation subjects: memory, async/exec, pure, tail-call, "
|
||||
"visibility, exception, blocking, parallel, complexity, loops. "
|
||||
"Returns suggestions with confidence scores.",
|
||||
{{"type", "object"}, {"properties", json::object()}}
|
||||
});
|
||||
toolHandlers_["whetstone_infer_annotations"] =
|
||||
[this](const json& args) {
|
||||
return callWhetstone("inferAnnotations", args);
|
||||
};
|
||||
}
|
||||
|
||||
void registerWhetstoneTools() {
|
||||
registerASTTools();
|
||||
registerAnnotationTools();
|
||||
@@ -1288,5 +1367,6 @@ private:
|
||||
registerSemanticAnnotationTools();
|
||||
registerEnvironmentTools();
|
||||
registerTrainingDataTools();
|
||||
registerWorkflowTools();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,11 +111,11 @@ public:
|
||||
diags = std::move(pr.diagnostics);
|
||||
return std::move(pr.module);
|
||||
} else if (language == "kotlin") {
|
||||
auto pr = TreeSitterParser::parseKotlinWithDiagnostics(source);
|
||||
auto pr = KotlinParser::parseKotlinWithDiagnostics(source);
|
||||
diags = std::move(pr.diagnostics);
|
||||
return std::move(pr.module);
|
||||
} else if (language == "csharp") {
|
||||
auto pr = TreeSitterParser::parseCSharpWithDiagnostics(source);
|
||||
auto pr = CSharpParser::parseCSharpWithDiagnostics(source);
|
||||
diags = std::move(pr.diagnostics);
|
||||
return std::move(pr.module);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,13 @@ public:
|
||||
std::string visitChoiceAnnotation(const ChoiceAnnotation* a) override { return semanno(a); }
|
||||
std::string visitDecisionAnnotation(const DecisionAnnotation* a) override { return semanno(a); }
|
||||
|
||||
// Subject 9: Workflow Routing
|
||||
std::string visitContextWidthAnnotation(const ContextWidthAnnotation* a) override { return semanno(a); }
|
||||
std::string visitReviewAnnotation(const ReviewAnnotation* a) override { return semanno(a); }
|
||||
std::string visitAutomatabilityAnnotation(const AutomatabilityAnnotation* a) override { return semanno(a); }
|
||||
std::string visitPriorityAnnotation(const PriorityAnnotation* a) override { return semanno(a); }
|
||||
std::string visitImplementationStatusAnnotation(const ImplementationStatusAnnotation* a) override { return semanno(a); }
|
||||
|
||||
// Semantic Core
|
||||
std::string visitIntentAnnotation(const IntentAnnotation* a) override { return semanno(a); }
|
||||
std::string visitComplexityAnnotation(const ComplexityAnnotation* a) override { return semanno(a); }
|
||||
|
||||
@@ -401,6 +401,8 @@ public:
|
||||
auto* a = static_cast<const AmbiguityAnnotation*>(anno);
|
||||
appendProp(props, "intent", a->intent, first);
|
||||
appendVec(props, "options", a->options, first);
|
||||
appendProp(props, "level", a->level, first);
|
||||
appendProp(props, "description", a->description, first);
|
||||
} else if (ct == "CandidateAnnotation") {
|
||||
tag = "candidate";
|
||||
auto* a = static_cast<const CandidateAnnotation*>(anno);
|
||||
@@ -424,6 +426,37 @@ public:
|
||||
appendProp(props, "author", a->author, first);
|
||||
appendProp(props, "reason", a->reason, first);
|
||||
|
||||
// --- Subject 9: Workflow Routing ---
|
||||
} else if (ct == "ContextWidthAnnotation") {
|
||||
tag = "contextwidth";
|
||||
auto* a = static_cast<const ContextWidthAnnotation*>(anno);
|
||||
appendProp(props, "width", a->width, first);
|
||||
} else if (ct == "ReviewAnnotation") {
|
||||
tag = "review";
|
||||
auto* a = static_cast<const ReviewAnnotation*>(anno);
|
||||
appendBool(props, "required", a->required, first);
|
||||
appendProp(props, "reviewer", a->reviewer, first);
|
||||
appendProp(props, "reason", a->reason, first);
|
||||
} else if (ct == "AutomatabilityAnnotation") {
|
||||
tag = "automatability";
|
||||
auto* a = static_cast<const AutomatabilityAnnotation*>(anno);
|
||||
appendProp(props, "strategy", a->strategy, first);
|
||||
if (a->confidence > 0.0) {
|
||||
if (!first) props += ",";
|
||||
props += "confidence=" + std::to_string(a->confidence);
|
||||
first = false;
|
||||
}
|
||||
} else if (ct == "PriorityAnnotation") {
|
||||
tag = "priority";
|
||||
auto* a = static_cast<const PriorityAnnotation*>(anno);
|
||||
appendProp(props, "level", a->level, first);
|
||||
appendVec(props, "blockedBy", a->blockedBy, first);
|
||||
} else if (ct == "ImplementationStatusAnnotation") {
|
||||
tag = "implstatus";
|
||||
auto* a = static_cast<const ImplementationStatusAnnotation*>(anno);
|
||||
appendProp(props, "status", a->status, first);
|
||||
appendProp(props, "assignee", a->assignee, first);
|
||||
|
||||
// --- Semantic Core ---
|
||||
} else if (ct == "IntentAnnotation") {
|
||||
tag = "intent";
|
||||
|
||||
@@ -95,6 +95,12 @@ inline bool isSemanticAnnotation(const std::string& conceptType) {
|
||||
conceptType == "TradeoffAnnotation" ||
|
||||
conceptType == "ChoiceAnnotation" ||
|
||||
conceptType == "DecisionAnnotation" ||
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
conceptType == "ContextWidthAnnotation" ||
|
||||
conceptType == "ReviewAnnotation" ||
|
||||
conceptType == "AutomatabilityAnnotation" ||
|
||||
conceptType == "PriorityAnnotation" ||
|
||||
conceptType == "ImplementationStatusAnnotation" ||
|
||||
// Environment Layer (Step 285)
|
||||
conceptType == "CapabilityRequirement";
|
||||
}
|
||||
|
||||
252
editor/src/SkeletonAST.h
Normal file
252
editor/src/SkeletonAST.h
Normal file
@@ -0,0 +1,252 @@
|
||||
#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;
|
||||
}
|
||||
67
editor/src/TrainingDataExporter.h
Normal file
67
editor/src/TrainingDataExporter.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include "TrainingDataGenerator.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct ExportStats {
|
||||
int totalPairs = 0;
|
||||
std::map<std::string, int> annotationCounts; // type -> count
|
||||
std::map<std::string, int> languageCounts; // language -> count
|
||||
};
|
||||
|
||||
class TrainingDataExporter {
|
||||
public:
|
||||
// HuggingFace instruction/input/output format (JSONL)
|
||||
static std::string exportHuggingFace(const std::vector<TrainingPair>& pairs) {
|
||||
std::ostringstream oss;
|
||||
for (const auto& p : pairs) {
|
||||
json line;
|
||||
line["instruction"] = "Add semantic annotations (Semanno format) to the following " +
|
||||
p.language + " code.";
|
||||
line["input"] = p.rawCode;
|
||||
line["output"] = p.annotatedCode;
|
||||
oss << line.dump() << "\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Raw/annotated pairs (JSONL)
|
||||
static std::string exportPairsJSONL(const std::vector<TrainingPair>& pairs) {
|
||||
std::ostringstream oss;
|
||||
for (const auto& p : pairs) {
|
||||
json line;
|
||||
line["raw_code"] = p.rawCode;
|
||||
line["annotated_code"] = p.annotatedCode;
|
||||
line["language"] = p.language;
|
||||
line["annotations"] = p.annotations;
|
||||
oss << line.dump() << "\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Compute statistics
|
||||
static ExportStats computeStats(const std::vector<TrainingPair>& pairs) {
|
||||
ExportStats stats;
|
||||
stats.totalPairs = (int)pairs.size();
|
||||
for (const auto& p : pairs) {
|
||||
stats.languageCounts[p.language]++;
|
||||
for (const auto& a : p.annotations) {
|
||||
stats.annotationCounts[a]++;
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
static json statsToJson(const ExportStats& stats) {
|
||||
json j;
|
||||
j["totalPairs"] = stats.totalPairs;
|
||||
j["annotationCounts"] = stats.annotationCounts;
|
||||
j["languageCounts"] = stats.languageCounts;
|
||||
return j;
|
||||
}
|
||||
};
|
||||
143
editor/src/TrainingDataGenerator.h
Normal file
143
editor/src/TrainingDataGenerator.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
#include "Pipeline.h"
|
||||
#include "AnnotationInference.h"
|
||||
#include "SemannoFormat.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct TrainingPair {
|
||||
std::string rawCode;
|
||||
std::string annotatedCode;
|
||||
std::string language;
|
||||
std::vector<std::string> annotations; // list of annotation types applied
|
||||
};
|
||||
|
||||
class TrainingDataGenerator {
|
||||
public:
|
||||
TrainingPair generate(const std::string& source,
|
||||
const std::string& language) {
|
||||
TrainingPair pair;
|
||||
pair.rawCode = source;
|
||||
pair.language = language;
|
||||
|
||||
Pipeline pipeline;
|
||||
std::vector<ParseDiagnostic> diags;
|
||||
auto mod = pipeline.parse(source, language, diags);
|
||||
if (!mod) {
|
||||
pair.annotatedCode = source;
|
||||
return pair;
|
||||
}
|
||||
|
||||
// Infer annotations
|
||||
AnnotationInference inferrer;
|
||||
auto inferred = inferrer.inferAll(mod.get());
|
||||
|
||||
// Apply inferred annotations to AST
|
||||
for (const auto& inf : inferred) {
|
||||
ASTNode* target = findNodeById(mod.get(), inf.nodeId);
|
||||
if (!target) continue;
|
||||
|
||||
Annotation* anno = createAnnotation(inf);
|
||||
if (anno) {
|
||||
target->addChild("annotations", anno);
|
||||
pair.annotations.push_back(inf.annotationType);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate annotated code
|
||||
pair.annotatedCode = pipeline.generate(mod.get(), language);
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
std::vector<TrainingPair> generateBatch(
|
||||
const std::vector<std::pair<std::string, std::string>>& samples) {
|
||||
std::vector<TrainingPair> pairs;
|
||||
for (const auto& [source, lang] : samples) {
|
||||
pairs.push_back(generate(source, lang));
|
||||
}
|
||||
return pairs;
|
||||
}
|
||||
|
||||
private:
|
||||
Annotation* createAnnotation(const AnnotationInference::InferredAnnotation& inf) {
|
||||
if (inf.annotationType == "ExecAnnotation") {
|
||||
auto* a = new ExecAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->mode = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "PureAnnotation") {
|
||||
auto* a = new PureAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "TailCallAnnotation") {
|
||||
auto* a = new TailCallAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "VisibilityAnnotation") {
|
||||
auto* a = new VisibilityAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->level = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "ExceptionAnnotation") {
|
||||
auto* a = new ExceptionAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->style = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "BlockingAnnotation") {
|
||||
auto* a = new BlockingAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->kind = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "ParallelAnnotation") {
|
||||
auto* a = new ParallelAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->kind = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "LoopAnnotation") {
|
||||
auto* a = new LoopAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->hint = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "ComplexityAnnotation") {
|
||||
auto* a = new ComplexityAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->timeComplexity = inf.value;
|
||||
return a;
|
||||
}
|
||||
// Memory annotations - delegate
|
||||
if (inf.annotationType == "ReclaimAnnotation") {
|
||||
auto* a = new ReclaimAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->strategy = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "LifetimeAnnotation") {
|
||||
auto* a = new LifetimeAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->strategy = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "OwnerAnnotation") {
|
||||
auto* a = new OwnerAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->strategy = inf.value;
|
||||
return a;
|
||||
}
|
||||
if (inf.annotationType == "DeallocateAnnotation") {
|
||||
auto* a = new DeallocateAnnotation();
|
||||
a->id = "inferred_" + inf.nodeId;
|
||||
a->strategy = inf.value;
|
||||
return a;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
@@ -506,6 +506,9 @@ class AmbiguityAnnotation : public Annotation {
|
||||
public:
|
||||
std::string intent;
|
||||
std::vector<std::string> options;
|
||||
// Workflow routing fields (Subject 9)
|
||||
std::string level; // "none" | "low" | "medium" | "high"
|
||||
std::string description;
|
||||
AmbiguityAnnotation() { conceptType = "AmbiguityAnnotation"; }
|
||||
};
|
||||
|
||||
@@ -538,3 +541,40 @@ public:
|
||||
std::string reason;
|
||||
DecisionAnnotation() { conceptType = "DecisionAnnotation"; }
|
||||
};
|
||||
|
||||
// ── Subject 9: Workflow Routing Annotations ──────────────────────
|
||||
|
||||
class ContextWidthAnnotation : public Annotation {
|
||||
public:
|
||||
std::string width; // "local" | "file" | "project" | "cross-project"
|
||||
ContextWidthAnnotation() { conceptType = "ContextWidthAnnotation"; }
|
||||
};
|
||||
|
||||
class ReviewAnnotation : public Annotation {
|
||||
public:
|
||||
bool required = false;
|
||||
std::string reviewer; // "human" | "agent" | "either"
|
||||
std::string reason;
|
||||
ReviewAnnotation() { conceptType = "ReviewAnnotation"; }
|
||||
};
|
||||
|
||||
class AutomatabilityAnnotation : public Annotation {
|
||||
public:
|
||||
std::string strategy; // "deterministic" | "template" | "slm" | "llm" | "human"
|
||||
double confidence = 0.0;
|
||||
AutomatabilityAnnotation() { conceptType = "AutomatabilityAnnotation"; }
|
||||
};
|
||||
|
||||
class PriorityAnnotation : public Annotation {
|
||||
public:
|
||||
std::string level; // "critical" | "high" | "medium" | "low"
|
||||
std::vector<std::string> blockedBy;
|
||||
PriorityAnnotation() { conceptType = "PriorityAnnotation"; }
|
||||
};
|
||||
|
||||
class ImplementationStatusAnnotation : public Annotation {
|
||||
public:
|
||||
std::string status; // "skeleton" | "partial" | "complete" | "needs-review"
|
||||
std::string assignee;
|
||||
ImplementationStatusAnnotation() { conceptType = "ImplementationStatusAnnotation"; }
|
||||
};
|
||||
|
||||
@@ -75,6 +75,13 @@ public:
|
||||
virtual std::string visitChoiceAnnotation(const ChoiceAnnotation*) = 0;
|
||||
virtual std::string visitDecisionAnnotation(const DecisionAnnotation*) = 0;
|
||||
|
||||
// Subject 9: Workflow Routing
|
||||
virtual std::string visitContextWidthAnnotation(const ContextWidthAnnotation*) = 0;
|
||||
virtual std::string visitReviewAnnotation(const ReviewAnnotation*) = 0;
|
||||
virtual std::string visitAutomatabilityAnnotation(const AutomatabilityAnnotation*) = 0;
|
||||
virtual std::string visitPriorityAnnotation(const PriorityAnnotation*) = 0;
|
||||
virtual std::string visitImplementationStatusAnnotation(const ImplementationStatusAnnotation*) = 0;
|
||||
|
||||
// Semantic Core
|
||||
virtual std::string visitIntentAnnotation(const IntentAnnotation*) = 0;
|
||||
virtual std::string visitComplexityAnnotation(const ComplexityAnnotation*) = 0;
|
||||
|
||||
357
editor/src/ast/CSharpGenerator.h
Normal file
357
editor/src/ast/CSharpGenerator.h
Normal file
@@ -0,0 +1,357 @@
|
||||
#pragma once
|
||||
#include "ProjectionGenerator.h"
|
||||
#include "ClassDeclaration.h"
|
||||
#include "GenericType.h"
|
||||
#include "AsyncNodes.h"
|
||||
#include "../SemannoAnnotationImpl.h"
|
||||
|
||||
class CSharpGenerator : public ProjectionGenerator, public SemannoAnnotationImpl<CSharpGenerator> {
|
||||
public:
|
||||
std::string commentPrefix() const { return "// "; }
|
||||
|
||||
std::string generate(const ASTNode* node) override {
|
||||
return dispatchGenerate(this, node, "// Unknown concept: ");
|
||||
}
|
||||
|
||||
std::string visitModule(const Module* module) override {
|
||||
std::ostringstream oss;
|
||||
oss << "// Module: " << module->name << "\n\n";
|
||||
auto variables = module->getChildren("variables");
|
||||
for (const auto* var : variables) oss << generate(var) << "\n";
|
||||
if (!variables.empty()) oss << "\n";
|
||||
auto functions = module->getChildren("functions");
|
||||
for (size_t i = 0; i < functions.size(); ++i) {
|
||||
if (i > 0) oss << "\n";
|
||||
oss << generate(functions[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitFunction(const Function* function) override {
|
||||
std::ostringstream oss;
|
||||
auto annotations = function->getChildren("annotations");
|
||||
for (const auto* anno : annotations) oss << generate(anno) << "\n";
|
||||
oss << "public void " << function->name << "(";
|
||||
auto params = function->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")\n{\n";
|
||||
auto body = function->getChildren("body");
|
||||
for (const auto* stmt : body)
|
||||
oss << " " << generate(stmt) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitVariable(const Variable* v) override {
|
||||
std::ostringstream oss;
|
||||
auto type = v->getChild("type");
|
||||
oss << (type ? generate(type) : "var") << " " << v->name;
|
||||
auto init = v->getChild("initializer");
|
||||
if (init) oss << " = " << generate(init);
|
||||
oss << ";";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitParameter(const Parameter* p) override {
|
||||
auto type = p->getChild("type");
|
||||
return (type ? generate(type) : "object") + " " + p->name;
|
||||
}
|
||||
|
||||
std::string visitAssignment(const Assignment* a) override {
|
||||
auto t = a->getChild("target"); auto v = a->getChild("value");
|
||||
return (t ? generate(t) : "") + " = " + (v ? generate(v) : "null") + ";";
|
||||
}
|
||||
std::string visitReturn(const Return* r) override {
|
||||
auto v = r->getChild("value");
|
||||
return v ? "return " + generate(v) + ";" : "return;";
|
||||
}
|
||||
std::string visitBinaryOperation(const BinaryOperation* b) override {
|
||||
auto l = b->getChild("left"); auto r = b->getChild("right");
|
||||
return (l ? generate(l) : "") + " " + b->op + " " + (r ? generate(r) : "");
|
||||
}
|
||||
std::string visitVariableReference(const VariableReference* v) override { return v->variableName; }
|
||||
std::string visitIntegerLiteral(const IntegerLiteral* l) override { return std::to_string(l->value); }
|
||||
std::string visitFloatLiteral(const FloatLiteral* l) override { return l->value; }
|
||||
std::string visitStringLiteral(const StringLiteral* l) override { return "\"" + l->value + "\""; }
|
||||
std::string visitBooleanLiteral(const BooleanLiteral* l) override { return l->value ? "true" : "false"; }
|
||||
std::string visitNullLiteral(const NullLiteral*) override { return "null"; }
|
||||
std::string visitIfStatement(const IfStatement* s) override {
|
||||
std::ostringstream oss;
|
||||
auto c = s->getChild("condition");
|
||||
oss << "if (" << (c ? generate(c) : "true") << ")\n{\n";
|
||||
for (const auto* st : s->getChildren("thenBranch"))
|
||||
oss << " " << generate(st) << "\n";
|
||||
oss << "}";
|
||||
auto el = s->getChildren("elseBranch");
|
||||
if (!el.empty()) {
|
||||
oss << "\nelse\n{\n";
|
||||
for (const auto* st : el) oss << " " << generate(st) << "\n";
|
||||
oss << "}";
|
||||
}
|
||||
oss << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitWhileLoop(const WhileLoop* l) override {
|
||||
std::ostringstream oss;
|
||||
auto c = l->getChild("condition");
|
||||
oss << "while (" << (c ? generate(c) : "true") << ")\n{\n";
|
||||
for (const auto* s : l->getChildren("body")) oss << " " << generate(s) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitForLoop(const ForLoop* l) override {
|
||||
std::ostringstream oss;
|
||||
auto iter = l->getChild("iterable");
|
||||
oss << "foreach (var " << (l->iteratorName.empty() ? "item" : l->iteratorName);
|
||||
oss << " in " << (iter ? generate(iter) : "Array.Empty<object>()") << ")\n{\n";
|
||||
for (const auto* s : l->getChildren("body")) oss << " " << generate(s) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitExpressionStatement(const ExpressionStatement* s) override {
|
||||
auto e = s->getChild("expression");
|
||||
return e ? generate(e) + ";" : "";
|
||||
}
|
||||
std::string visitUnaryOperation(const UnaryOperation* u) override {
|
||||
auto o = u->getChild("operand");
|
||||
return u->op + (o ? generate(o) : "");
|
||||
}
|
||||
std::string visitFunctionCall(const FunctionCall* c) override {
|
||||
std::ostringstream oss;
|
||||
oss << c->functionName << "(";
|
||||
auto args = c->getChildren("arguments");
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(args[i]);
|
||||
}
|
||||
oss << ")";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitBlock(const Block* b) override {
|
||||
std::ostringstream oss;
|
||||
for (const auto* s : b->getChildren("statements")) oss << generate(s) << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitListLiteral(const ListLiteral* l) override {
|
||||
std::ostringstream oss;
|
||||
oss << "new List<object> { ";
|
||||
auto e = l->getChildren("elements");
|
||||
for (size_t i = 0; i < e.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(e[i]);
|
||||
}
|
||||
oss << " }";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitIndexAccess(const IndexAccess* a) override {
|
||||
auto t = a->getChild("target"); auto i = a->getChild("index");
|
||||
return (t ? generate(t) : "") + "[" + (i ? generate(i) : "") + "]";
|
||||
}
|
||||
std::string visitMemberAccess(const MemberAccess* a) override {
|
||||
auto t = a->getChild("target");
|
||||
return (t ? generate(t) : "") + "." + a->memberName;
|
||||
}
|
||||
std::string visitPrimitiveType(const PrimitiveType* t) override {
|
||||
if (t->kind == "int") return "int";
|
||||
if (t->kind == "float") return "float";
|
||||
if (t->kind == "double") return "double";
|
||||
if (t->kind == "string") return "string";
|
||||
if (t->kind == "bool") return "bool";
|
||||
if (t->kind == "void") return "void";
|
||||
return t->kind;
|
||||
}
|
||||
std::string visitListType(const ListType* t) override {
|
||||
auto e = t->getChild("elementType");
|
||||
return "List<" + (e ? generate(e) : "object") + ">";
|
||||
}
|
||||
std::string visitSetType(const SetType* t) override {
|
||||
auto e = t->getChild("elementType");
|
||||
return "HashSet<" + (e ? generate(e) : "object") + ">";
|
||||
}
|
||||
std::string visitMapType(const MapType* t) override {
|
||||
auto k = t->getChild("keyType"); auto v = t->getChild("valueType");
|
||||
return "Dictionary<" + (k ? generate(k) : "object") + ", " + (v ? generate(v) : "object") + ">";
|
||||
}
|
||||
std::string visitTupleType(const TupleType* t) override {
|
||||
std::ostringstream oss;
|
||||
oss << "(";
|
||||
auto types = t->getChildren("elementTypes");
|
||||
for (size_t i = 0; i < types.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(types[i]);
|
||||
}
|
||||
oss << ")";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitArrayType(const ArrayType* t) override {
|
||||
auto e = t->getChild("elementType");
|
||||
return (e ? generate(e) : "object") + "[]";
|
||||
}
|
||||
std::string visitOptionalType(const OptionalType* t) override {
|
||||
auto i = t->getChild("innerType");
|
||||
return (i ? generate(i) : "object") + "?";
|
||||
}
|
||||
std::string visitCustomType(const CustomType* t) override { return t->typeName; }
|
||||
|
||||
// --- New AST node visitors (Step 312) ---
|
||||
|
||||
std::string visitClassDeclaration(const ASTNode* node) override {
|
||||
auto* cls = static_cast<const ClassDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = cls->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
if (cls->isAbstract) oss << "abstract ";
|
||||
oss << "class " << cls->name;
|
||||
if (!cls->superClass.empty()) oss << " : " << cls->superClass;
|
||||
oss << "\n{\n";
|
||||
auto fields = cls->getChildren("fields");
|
||||
for (const auto* f : fields) oss << " " << generate(f) << "\n";
|
||||
if (!fields.empty() && !cls->getChildren("methods").empty()) oss << "\n";
|
||||
auto methods = cls->getChildren("methods");
|
||||
for (const auto* m : methods) oss << generate(m) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitInterfaceDeclaration(const ASTNode* node) override {
|
||||
auto* iface = static_cast<const InterfaceDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "interface " << iface->name << "\n{\n";
|
||||
auto methods = iface->getChildren("methods");
|
||||
for (const auto* m : methods) {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(m);
|
||||
oss << " void " << meth->name << "();\n";
|
||||
}
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitMethodDeclaration(const ASTNode* node) override {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = meth->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << " " << generate(a) << "\n";
|
||||
oss << " ";
|
||||
if (!meth->visibility.empty()) oss << meth->visibility << " ";
|
||||
if (meth->isStatic) oss << "static ";
|
||||
if (meth->isVirtual) oss << "virtual ";
|
||||
if (meth->isOverride) oss << "override ";
|
||||
std::string returnType = "void";
|
||||
auto retType = meth->getChild("returnType");
|
||||
if (retType) returnType = generate(retType);
|
||||
oss << returnType << " " << meth->name << "(";
|
||||
auto params = meth->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")\n";
|
||||
auto body = meth->getChildren("body");
|
||||
if (body.empty()) {
|
||||
oss << " {\n }\n";
|
||||
} else {
|
||||
oss << " {\n";
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << " }\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitGenericType(const ASTNode* node) override {
|
||||
auto* gen = static_cast<const GenericType*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << gen->baseName << "<";
|
||||
auto params = gen->getChildren("typeParameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ">";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitTypeParameter(const ASTNode* node) override {
|
||||
auto* tp = static_cast<const TypeParameter*>(node);
|
||||
return tp->name;
|
||||
}
|
||||
|
||||
std::string visitAsyncFunction(const ASTNode* node) override {
|
||||
auto* af = static_cast<const AsyncFunction*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = af->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "public async Task " << af->name << "(";
|
||||
auto params = af->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")\n{\n";
|
||||
auto body = af->getChildren("body");
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitAwaitExpression(const ASTNode* node) override {
|
||||
auto* aw = static_cast<const AwaitExpression*>(node);
|
||||
auto* expr = aw->getChild("expression");
|
||||
return "await " + (expr ? generate(expr) : "Task.CompletedTask");
|
||||
}
|
||||
|
||||
std::string visitLambdaExpression(const ASTNode* node) override {
|
||||
auto* lam = static_cast<const LambdaExpression*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "(";
|
||||
auto params = lam->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << static_cast<const Parameter*>(params[i])->name;
|
||||
}
|
||||
oss << ") => ";
|
||||
auto body = lam->getChildren("body");
|
||||
if (body.size() == 1) {
|
||||
oss << generate(body[0]);
|
||||
} else if (body.empty()) {
|
||||
oss << "{ }";
|
||||
} else {
|
||||
oss << "{ ";
|
||||
for (const auto* s : body) oss << generate(s) << "; ";
|
||||
oss << "}";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDecoratorAnnotation(const ASTNode* node) override {
|
||||
auto* dec = static_cast<const DecoratorAnnotation*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "[" << dec->name;
|
||||
auto args = dec->getChildren("arguments");
|
||||
if (!args.empty()) {
|
||||
oss << "(";
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(args[i]);
|
||||
}
|
||||
oss << ")";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDerefStrategy(const DerefStrategy* a) override { return "// @deref(" + a->strategy + ")"; }
|
||||
std::string visitOptimizationLock(const OptimizationLock* a) override { return "// @lock(" + a->lockedBy + ")"; }
|
||||
std::string visitLangSpecific(const LangSpecific* a) override { return "// @lang_specific(" + a->language + ")"; }
|
||||
std::string visitDeallocateAnnotation(const DeallocateAnnotation* a) override { return semanno(a); }
|
||||
std::string visitLifetimeAnnotation(const LifetimeAnnotation* a) override { return semanno(a); }
|
||||
std::string visitReclaimAnnotation(const ReclaimAnnotation* a) override { return semanno(a); }
|
||||
std::string visitOwnerAnnotation(const OwnerAnnotation* a) override { return semanno(a); }
|
||||
std::string visitAllocateAnnotation(const AllocateAnnotation* a) override { return semanno(a); }
|
||||
std::string visitHotColdAnnotation(const HotColdAnnotation* a) override { return semanno(a); }
|
||||
std::string visitInlineAnnotation(const InlineAnnotation* a) override { return semanno(a); }
|
||||
std::string visitPureAnnotation(const PureAnnotation* a) override { return semanno(a); }
|
||||
std::string visitConstExprAnnotation(const ConstExprAnnotation* a) override { return semanno(a); }
|
||||
};
|
||||
144
editor/src/ast/CSharpParser.h
Normal file
144
editor/src/ast/CSharpParser.h
Normal file
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
#include "ASTNode.h"
|
||||
#include "Module.h"
|
||||
#include "Function.h"
|
||||
#include "Variable.h"
|
||||
#include "Parameter.h"
|
||||
#include "Statement.h"
|
||||
#include "Expression.h"
|
||||
#include "Type.h"
|
||||
#include "Annotation.h"
|
||||
#include "ClassDeclaration.h"
|
||||
#include "GenericType.h"
|
||||
#include "AsyncNodes.h"
|
||||
#include "../ast/Parser.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
class CSharpParser {
|
||||
public:
|
||||
static std::unique_ptr<Module> parseCSharp(const std::string& source) {
|
||||
auto module = std::make_unique<Module>();
|
||||
module->id = IdGenerator::next("mod");
|
||||
module->name = "parsed_csharp_module";
|
||||
module->targetLanguage = "csharp";
|
||||
|
||||
parseTopLevel(source, module.get());
|
||||
return module;
|
||||
}
|
||||
|
||||
static ParseResult parseCSharpWithDiagnostics(const std::string& source) {
|
||||
ParseResult result;
|
||||
result.module = parseCSharp(source);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static void parseTopLevel(const std::string& source, Module* module) {
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
int braceDepth = 0;
|
||||
bool inFunction = false;
|
||||
bool inClass = false;
|
||||
std::string currentName;
|
||||
bool isAsync = false;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
std::string trimmed = trim(line);
|
||||
if (trimmed.empty() || trimmed.substr(0, 2) == "//") continue;
|
||||
if (trimmed.find("using ") == 0) continue; // skip using directives
|
||||
if (trimmed.find("namespace ") == 0) continue; // skip namespace
|
||||
|
||||
if (braceDepth <= 1) {
|
||||
if (hasMethodSignature(trimmed)) {
|
||||
isAsync = trimmed.find("async ") != std::string::npos;
|
||||
currentName = extractMethodName(trimmed);
|
||||
inFunction = true;
|
||||
}
|
||||
else if (trimmed.find("class ") != std::string::npos) {
|
||||
currentName = extractAfterKeyword(trimmed, "class ");
|
||||
inClass = true;
|
||||
}
|
||||
else if (trimmed.find("interface ") != std::string::npos) {
|
||||
currentName = extractAfterKeyword(trimmed, "interface ");
|
||||
auto* iface = new InterfaceDeclaration();
|
||||
iface->id = IdGenerator::next("iface");
|
||||
iface->name = currentName;
|
||||
module->addChild("interfaces", iface);
|
||||
}
|
||||
}
|
||||
|
||||
for (char c : trimmed) {
|
||||
if (c == '{') braceDepth++;
|
||||
else if (c == '}') braceDepth--;
|
||||
}
|
||||
|
||||
if (braceDepth <= 1 && inFunction) {
|
||||
if (isAsync) {
|
||||
auto* fn = new AsyncFunction();
|
||||
fn->id = IdGenerator::next("fn");
|
||||
fn->name = currentName;
|
||||
module->addChild("functions", fn);
|
||||
} else {
|
||||
auto* fn = new Function();
|
||||
fn->id = IdGenerator::next("fn");
|
||||
fn->name = currentName;
|
||||
module->addChild("functions", fn);
|
||||
}
|
||||
inFunction = false;
|
||||
isAsync = false;
|
||||
}
|
||||
if (braceDepth == 0 && inClass) {
|
||||
auto* cls = new ClassDeclaration();
|
||||
cls->id = IdGenerator::next("cls");
|
||||
cls->name = currentName;
|
||||
module->addChild("classes", cls);
|
||||
inClass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasMethodSignature(const std::string& line) {
|
||||
// Look for patterns like "public void Method(" or "static async Task<int> Method("
|
||||
if (line.find('(') == std::string::npos) return false;
|
||||
if (line.find("class ") != std::string::npos) return false;
|
||||
if (line.find("if ") != std::string::npos || line.find("if(") != std::string::npos) return false;
|
||||
if (line.find("while ") != std::string::npos) return false;
|
||||
if (line.find("for ") != std::string::npos || line.find("foreach ") != std::string::npos) return false;
|
||||
// Has visibility or return type keyword
|
||||
return line.find("void ") != std::string::npos ||
|
||||
line.find("int ") != std::string::npos ||
|
||||
line.find("string ") != std::string::npos ||
|
||||
line.find("Task") != std::string::npos ||
|
||||
line.find("public ") != std::string::npos ||
|
||||
line.find("private ") != std::string::npos ||
|
||||
line.find("static ") != std::string::npos;
|
||||
}
|
||||
|
||||
static std::string extractMethodName(const std::string& line) {
|
||||
auto parenPos = line.find('(');
|
||||
if (parenPos == std::string::npos) return "unknown";
|
||||
// Walk back from '(' to find the method name
|
||||
auto nameEnd = parenPos;
|
||||
auto nameStart = line.rfind(' ', nameEnd - 1);
|
||||
if (nameStart == std::string::npos) nameStart = 0;
|
||||
else nameStart++;
|
||||
return trim(line.substr(nameStart, nameEnd - nameStart));
|
||||
}
|
||||
|
||||
static std::string extractAfterKeyword(const std::string& line, const std::string& keyword) {
|
||||
auto pos = line.find(keyword);
|
||||
if (pos == std::string::npos) return "unknown";
|
||||
pos += keyword.size();
|
||||
auto end = line.find_first_of("{: <(", pos);
|
||||
if (end == std::string::npos) end = line.size();
|
||||
return trim(line.substr(pos, end - pos));
|
||||
}
|
||||
|
||||
static std::string trim(const std::string& s) {
|
||||
auto start = s.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) return "";
|
||||
auto end = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
};
|
||||
377
editor/src/ast/KotlinGenerator.h
Normal file
377
editor/src/ast/KotlinGenerator.h
Normal file
@@ -0,0 +1,377 @@
|
||||
#pragma once
|
||||
#include "ProjectionGenerator.h"
|
||||
#include "ClassDeclaration.h"
|
||||
#include "GenericType.h"
|
||||
#include "AsyncNodes.h"
|
||||
#include "../SemannoAnnotationImpl.h"
|
||||
|
||||
class KotlinGenerator : public ProjectionGenerator, public SemannoAnnotationImpl<KotlinGenerator> {
|
||||
public:
|
||||
std::string commentPrefix() const { return "// "; }
|
||||
|
||||
std::string generate(const ASTNode* node) override {
|
||||
return dispatchGenerate(this, node, "// Unknown concept: ");
|
||||
}
|
||||
|
||||
std::string visitModule(const Module* module) override {
|
||||
std::ostringstream oss;
|
||||
oss << "// Module: " << module->name << "\n\n";
|
||||
auto variables = module->getChildren("variables");
|
||||
for (const auto* var : variables) {
|
||||
oss << generate(var) << "\n";
|
||||
}
|
||||
if (!variables.empty()) oss << "\n";
|
||||
auto functions = module->getChildren("functions");
|
||||
for (size_t i = 0; i < functions.size(); ++i) {
|
||||
if (i > 0) oss << "\n";
|
||||
oss << generate(functions[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitFunction(const Function* function) override {
|
||||
std::ostringstream oss;
|
||||
auto annotations = function->getChildren("annotations");
|
||||
for (const auto* anno : annotations) oss << generate(anno) << "\n";
|
||||
oss << "fun " << function->name << "(";
|
||||
auto params = function->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")";
|
||||
auto retType = function->getChild("returnType");
|
||||
if (retType) oss << ": " << generate(retType);
|
||||
oss << " {\n";
|
||||
auto body = function->getChildren("body");
|
||||
for (const auto* stmt : body) {
|
||||
std::string code = generate(stmt);
|
||||
oss << " " << code << "\n";
|
||||
}
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitVariable(const Variable* variable) override {
|
||||
std::ostringstream oss;
|
||||
oss << "val " << variable->name;
|
||||
auto type = variable->getChild("type");
|
||||
if (type) oss << ": " << generate(type);
|
||||
auto init = variable->getChild("initializer");
|
||||
if (init) oss << " = " << generate(init);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitParameter(const Parameter* parameter) override {
|
||||
std::ostringstream oss;
|
||||
oss << parameter->name;
|
||||
auto type = parameter->getChild("type");
|
||||
if (type) oss << ": " << generate(type);
|
||||
auto def = parameter->getChild("defaultValue");
|
||||
if (def) oss << " = " << generate(def);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitAssignment(const Assignment* a) override {
|
||||
std::ostringstream oss;
|
||||
auto target = a->getChild("target");
|
||||
auto value = a->getChild("value");
|
||||
if (target) oss << generate(target);
|
||||
oss << " = ";
|
||||
if (value) oss << generate(value);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitReturn(const Return* r) override {
|
||||
auto value = r->getChild("value");
|
||||
return value ? "return " + generate(value) : "return";
|
||||
}
|
||||
|
||||
std::string visitBinaryOperation(const BinaryOperation* b) override {
|
||||
std::ostringstream oss;
|
||||
auto left = b->getChild("left");
|
||||
auto right = b->getChild("right");
|
||||
if (left) oss << generate(left);
|
||||
oss << " " << b->op << " ";
|
||||
if (right) oss << generate(right);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitVariableReference(const VariableReference* v) override { return v->variableName; }
|
||||
std::string visitIntegerLiteral(const IntegerLiteral* l) override { return std::to_string(l->value); }
|
||||
std::string visitFloatLiteral(const FloatLiteral* l) override { return l->value; }
|
||||
std::string visitStringLiteral(const StringLiteral* l) override { return "\"" + l->value + "\""; }
|
||||
std::string visitBooleanLiteral(const BooleanLiteral* l) override { return l->value ? "true" : "false"; }
|
||||
std::string visitNullLiteral(const NullLiteral*) override { return "null"; }
|
||||
|
||||
std::string visitIfStatement(const IfStatement* s) override {
|
||||
std::ostringstream oss;
|
||||
auto cond = s->getChild("condition");
|
||||
oss << "if (" << (cond ? generate(cond) : "true") << ") {\n";
|
||||
for (const auto* stmt : s->getChildren("thenBranch"))
|
||||
oss << " " << generate(stmt) << "\n";
|
||||
oss << "}";
|
||||
auto elseBranch = s->getChildren("elseBranch");
|
||||
if (!elseBranch.empty()) {
|
||||
oss << " else {\n";
|
||||
for (const auto* stmt : elseBranch)
|
||||
oss << " " << generate(stmt) << "\n";
|
||||
oss << "}";
|
||||
}
|
||||
oss << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitWhileLoop(const WhileLoop* l) override {
|
||||
std::ostringstream oss;
|
||||
auto cond = l->getChild("condition");
|
||||
oss << "while (" << (cond ? generate(cond) : "true") << ") {\n";
|
||||
for (const auto* stmt : l->getChildren("body"))
|
||||
oss << " " << generate(stmt) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitForLoop(const ForLoop* l) override {
|
||||
std::ostringstream oss;
|
||||
auto iter = l->getChild("iterable");
|
||||
oss << "for (" << (l->iteratorName.empty() ? "item" : l->iteratorName);
|
||||
oss << " in " << (iter ? generate(iter) : "listOf()") << ") {\n";
|
||||
for (const auto* stmt : l->getChildren("body"))
|
||||
oss << " " << generate(stmt) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitExpressionStatement(const ExpressionStatement* s) override {
|
||||
auto expr = s->getChild("expression");
|
||||
return expr ? generate(expr) : "";
|
||||
}
|
||||
std::string visitUnaryOperation(const UnaryOperation* u) override {
|
||||
auto op = u->getChild("operand");
|
||||
return u->op + (op ? generate(op) : "");
|
||||
}
|
||||
std::string visitFunctionCall(const FunctionCall* c) override {
|
||||
std::ostringstream oss;
|
||||
oss << c->functionName << "(";
|
||||
auto args = c->getChildren("arguments");
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(args[i]);
|
||||
}
|
||||
oss << ")";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitBlock(const Block* b) override {
|
||||
std::ostringstream oss;
|
||||
for (const auto* s : b->getChildren("statements"))
|
||||
oss << generate(s) << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitListLiteral(const ListLiteral* l) override {
|
||||
std::ostringstream oss;
|
||||
oss << "listOf(";
|
||||
auto elems = l->getChildren("elements");
|
||||
for (size_t i = 0; i < elems.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(elems[i]);
|
||||
}
|
||||
oss << ")";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitIndexAccess(const IndexAccess* a) override {
|
||||
auto t = a->getChild("target");
|
||||
auto i = a->getChild("index");
|
||||
return (t ? generate(t) : "") + "[" + (i ? generate(i) : "") + "]";
|
||||
}
|
||||
std::string visitMemberAccess(const MemberAccess* a) override {
|
||||
auto t = a->getChild("target");
|
||||
return (t ? generate(t) : "") + "." + a->memberName;
|
||||
}
|
||||
std::string visitPrimitiveType(const PrimitiveType* t) override {
|
||||
if (t->kind == "int") return "Int";
|
||||
if (t->kind == "float" || t->kind == "double") return "Double";
|
||||
if (t->kind == "string") return "String";
|
||||
if (t->kind == "bool") return "Boolean";
|
||||
if (t->kind == "void") return "Unit";
|
||||
return t->kind;
|
||||
}
|
||||
std::string visitListType(const ListType* t) override {
|
||||
auto el = t->getChild("elementType");
|
||||
return "List<" + (el ? generate(el) : "Any") + ">";
|
||||
}
|
||||
std::string visitSetType(const SetType* t) override {
|
||||
auto el = t->getChild("elementType");
|
||||
return "Set<" + (el ? generate(el) : "Any") + ">";
|
||||
}
|
||||
std::string visitMapType(const MapType* t) override {
|
||||
auto k = t->getChild("keyType");
|
||||
auto v = t->getChild("valueType");
|
||||
return "Map<" + (k ? generate(k) : "Any") + ", " + (v ? generate(v) : "Any") + ">";
|
||||
}
|
||||
std::string visitTupleType(const TupleType* t) override {
|
||||
std::ostringstream oss;
|
||||
oss << "Pair<";
|
||||
auto types = t->getChildren("elementTypes");
|
||||
for (size_t i = 0; i < types.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(types[i]);
|
||||
}
|
||||
oss << ">";
|
||||
return oss.str();
|
||||
}
|
||||
std::string visitArrayType(const ArrayType* t) override {
|
||||
auto el = t->getChild("elementType");
|
||||
return "Array<" + (el ? generate(el) : "Any") + ">";
|
||||
}
|
||||
std::string visitOptionalType(const OptionalType* t) override {
|
||||
auto inner = t->getChild("innerType");
|
||||
return (inner ? generate(inner) : "Any") + "?";
|
||||
}
|
||||
std::string visitCustomType(const CustomType* t) override { return t->typeName; }
|
||||
|
||||
// --- New AST node visitors (Step 310) ---
|
||||
|
||||
std::string visitClassDeclaration(const ASTNode* node) override {
|
||||
auto* cls = static_cast<const ClassDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = cls->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
if (cls->isAbstract) oss << "abstract ";
|
||||
oss << "class " << cls->name;
|
||||
if (!cls->superClass.empty()) oss << " : " << cls->superClass << "()";
|
||||
oss << " {\n";
|
||||
auto fields = cls->getChildren("fields");
|
||||
for (const auto* f : fields) oss << " " << generate(f) << "\n";
|
||||
auto methods = cls->getChildren("methods");
|
||||
for (const auto* m : methods) oss << generate(m);
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitInterfaceDeclaration(const ASTNode* node) override {
|
||||
auto* iface = static_cast<const InterfaceDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "interface " << iface->name << " {\n";
|
||||
auto methods = iface->getChildren("methods");
|
||||
for (const auto* m : methods) {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(m);
|
||||
oss << " fun " << meth->name << "()\n";
|
||||
}
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitMethodDeclaration(const ASTNode* node) override {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = meth->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << " " << generate(a) << "\n";
|
||||
oss << " ";
|
||||
if (meth->isOverride) oss << "override ";
|
||||
oss << "fun " << meth->name << "(";
|
||||
auto params = meth->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")";
|
||||
auto retType = meth->getChild("returnType");
|
||||
if (retType) oss << ": " << generate(retType);
|
||||
auto body = meth->getChildren("body");
|
||||
if (body.empty()) {
|
||||
oss << " {}\n";
|
||||
} else {
|
||||
oss << " {\n";
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << " }\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitGenericType(const ASTNode* node) override {
|
||||
auto* gen = static_cast<const GenericType*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << gen->baseName << "<";
|
||||
auto params = gen->getChildren("typeParameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ">";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitTypeParameter(const ASTNode* node) override {
|
||||
auto* tp = static_cast<const TypeParameter*>(node);
|
||||
if (!tp->constraint.empty()) return tp->name + " : " + tp->constraint;
|
||||
return tp->name;
|
||||
}
|
||||
|
||||
std::string visitAsyncFunction(const ASTNode* node) override {
|
||||
auto* af = static_cast<const AsyncFunction*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = af->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "suspend fun " << af->name << "(";
|
||||
auto params = af->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ")";
|
||||
auto retType = af->getChild("returnType");
|
||||
if (retType) oss << ": " << generate(retType);
|
||||
oss << " {\n";
|
||||
auto body = af->getChildren("body");
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitAwaitExpression(const ASTNode* node) override {
|
||||
auto* aw = static_cast<const AwaitExpression*>(node);
|
||||
auto* expr = aw->getChild("expression");
|
||||
return expr ? generate(expr) : "/* missing */";
|
||||
}
|
||||
|
||||
std::string visitLambdaExpression(const ASTNode* node) override {
|
||||
auto* lam = static_cast<const LambdaExpression*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "{ ";
|
||||
auto params = lam->getChildren("parameters");
|
||||
if (!params.empty()) {
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << static_cast<const Parameter*>(params[i])->name;
|
||||
}
|
||||
oss << " -> ";
|
||||
}
|
||||
auto body = lam->getChildren("body");
|
||||
for (size_t i = 0; i < body.size(); ++i) {
|
||||
if (i > 0) oss << "; ";
|
||||
oss << generate(body[i]);
|
||||
}
|
||||
oss << " }";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDecoratorAnnotation(const ASTNode* node) override {
|
||||
auto* dec = static_cast<const DecoratorAnnotation*>(node);
|
||||
return "@" + dec->name;
|
||||
}
|
||||
|
||||
// Legacy annotation visitors
|
||||
std::string visitDerefStrategy(const DerefStrategy* a) override { return "// @deref(" + a->strategy + ")"; }
|
||||
std::string visitOptimizationLock(const OptimizationLock* a) override { return "// @lock(" + a->lockedBy + ")"; }
|
||||
std::string visitLangSpecific(const LangSpecific* a) override { return "// @lang_specific(" + a->language + ")"; }
|
||||
std::string visitDeallocateAnnotation(const DeallocateAnnotation* a) override { return semanno(a); }
|
||||
std::string visitLifetimeAnnotation(const LifetimeAnnotation* a) override { return semanno(a); }
|
||||
std::string visitReclaimAnnotation(const ReclaimAnnotation* a) override { return semanno(a); }
|
||||
std::string visitOwnerAnnotation(const OwnerAnnotation* a) override { return semanno(a); }
|
||||
std::string visitAllocateAnnotation(const AllocateAnnotation* a) override { return semanno(a); }
|
||||
std::string visitHotColdAnnotation(const HotColdAnnotation* a) override { return semanno(a); }
|
||||
std::string visitInlineAnnotation(const InlineAnnotation* a) override { return semanno(a); }
|
||||
std::string visitPureAnnotation(const PureAnnotation* a) override { return semanno(a); }
|
||||
std::string visitConstExprAnnotation(const ConstExprAnnotation* a) override { return semanno(a); }
|
||||
};
|
||||
154
editor/src/ast/KotlinParser.h
Normal file
154
editor/src/ast/KotlinParser.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
#include "ASTNode.h"
|
||||
#include "Module.h"
|
||||
#include "Function.h"
|
||||
#include "Variable.h"
|
||||
#include "Parameter.h"
|
||||
#include "Statement.h"
|
||||
#include "Expression.h"
|
||||
#include "Type.h"
|
||||
#include "Annotation.h"
|
||||
#include "ClassDeclaration.h"
|
||||
#include "GenericType.h"
|
||||
#include "AsyncNodes.h"
|
||||
#include "../ast/Parser.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
class KotlinParser {
|
||||
public:
|
||||
static std::unique_ptr<Module> parseKotlin(const std::string& source) {
|
||||
auto module = std::make_unique<Module>();
|
||||
module->id = IdGenerator::next("mod");
|
||||
module->name = "parsed_kotlin_module";
|
||||
module->targetLanguage = "kotlin";
|
||||
|
||||
parseTopLevel(source, module.get());
|
||||
return module;
|
||||
}
|
||||
|
||||
static ParseResult parseKotlinWithDiagnostics(const std::string& source) {
|
||||
ParseResult result;
|
||||
result.module = parseKotlin(source);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static void parseTopLevel(const std::string& source, Module* module) {
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
std::string buffer;
|
||||
int braceDepth = 0;
|
||||
bool inFunction = false;
|
||||
bool inClass = false;
|
||||
std::string currentName;
|
||||
bool isSuspend = false;
|
||||
bool isDataClass = false;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
std::string trimmed = trim(line);
|
||||
|
||||
// Skip empty lines and comments
|
||||
if (trimmed.empty() || trimmed.substr(0, 2) == "//") continue;
|
||||
|
||||
if (braceDepth == 0) {
|
||||
// Top-level declarations
|
||||
if (trimmed.find("fun ") != std::string::npos ||
|
||||
trimmed.find("suspend fun ") != std::string::npos) {
|
||||
isSuspend = trimmed.find("suspend ") != std::string::npos;
|
||||
currentName = extractFunName(trimmed);
|
||||
inFunction = true;
|
||||
buffer = trimmed;
|
||||
}
|
||||
else if (trimmed.find("class ") != std::string::npos ||
|
||||
trimmed.find("data class ") != std::string::npos) {
|
||||
isDataClass = trimmed.find("data class ") != std::string::npos;
|
||||
currentName = extractClassName(trimmed);
|
||||
inClass = true;
|
||||
buffer = trimmed;
|
||||
}
|
||||
else if (trimmed.find("val ") == 0 || trimmed.find("var ") == 0) {
|
||||
auto* var = new Variable();
|
||||
var->id = IdGenerator::next("var");
|
||||
var->name = extractValName(trimmed);
|
||||
module->addChild("variables", var);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Count braces
|
||||
for (char c : trimmed) {
|
||||
if (c == '{') braceDepth++;
|
||||
else if (c == '}') braceDepth--;
|
||||
}
|
||||
|
||||
if (braceDepth > 0 || (inFunction || inClass)) {
|
||||
if (buffer != trimmed) buffer += "\n" + trimmed;
|
||||
}
|
||||
|
||||
if (braceDepth == 0 && (inFunction || inClass)) {
|
||||
if (inFunction) {
|
||||
if (isSuspend) {
|
||||
auto* fn = new AsyncFunction();
|
||||
fn->id = IdGenerator::next("fn");
|
||||
fn->name = currentName;
|
||||
fn->isAsync = true;
|
||||
module->addChild("functions", fn);
|
||||
} else {
|
||||
auto* fn = new Function();
|
||||
fn->id = IdGenerator::next("fn");
|
||||
fn->name = currentName;
|
||||
module->addChild("functions", fn);
|
||||
}
|
||||
inFunction = false;
|
||||
isSuspend = false;
|
||||
}
|
||||
if (inClass) {
|
||||
auto* cls = new ClassDeclaration();
|
||||
cls->id = IdGenerator::next("cls");
|
||||
cls->name = currentName;
|
||||
module->addChild("classes", cls);
|
||||
inClass = false;
|
||||
isDataClass = false;
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string extractFunName(const std::string& line) {
|
||||
auto pos = line.find("fun ");
|
||||
if (pos == std::string::npos) return "unknown";
|
||||
pos += 4;
|
||||
auto end = line.find('(', pos);
|
||||
if (end == std::string::npos) end = line.size();
|
||||
return trim(line.substr(pos, end - pos));
|
||||
}
|
||||
|
||||
static std::string extractClassName(const std::string& line) {
|
||||
std::string search = "class ";
|
||||
auto pos = line.find(search);
|
||||
if (pos == std::string::npos) return "unknown";
|
||||
pos += search.size();
|
||||
auto end = line.find_first_of("({: ", pos);
|
||||
if (end == std::string::npos) end = line.size();
|
||||
return trim(line.substr(pos, end - pos));
|
||||
}
|
||||
|
||||
static std::string extractValName(const std::string& line) {
|
||||
auto pos = line.find(' ');
|
||||
if (pos == std::string::npos) return "unknown";
|
||||
pos++;
|
||||
auto end = line.find_first_of(":= ", pos);
|
||||
if (end == std::string::npos) end = line.size();
|
||||
return trim(line.substr(pos, end - pos));
|
||||
}
|
||||
|
||||
static std::string trim(const std::string& s) {
|
||||
auto start = s.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) return "";
|
||||
auto end = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
};
|
||||
@@ -294,6 +294,18 @@ std::string dispatchGenerate(Gen* gen, const ASTNode* node, const std::string& u
|
||||
} else if (node->conceptType == "DecisionAnnotation") {
|
||||
return gen->visitDecisionAnnotation(static_cast<const DecisionAnnotation*>(node));
|
||||
}
|
||||
// Subject 9: Workflow Routing
|
||||
else if (node->conceptType == "ContextWidthAnnotation") {
|
||||
return gen->visitContextWidthAnnotation(static_cast<const ContextWidthAnnotation*>(node));
|
||||
} else if (node->conceptType == "ReviewAnnotation") {
|
||||
return gen->visitReviewAnnotation(static_cast<const ReviewAnnotation*>(node));
|
||||
} else if (node->conceptType == "AutomatabilityAnnotation") {
|
||||
return gen->visitAutomatabilityAnnotation(static_cast<const AutomatabilityAnnotation*>(node));
|
||||
} else if (node->conceptType == "PriorityAnnotation") {
|
||||
return gen->visitPriorityAnnotation(static_cast<const PriorityAnnotation*>(node));
|
||||
} else if (node->conceptType == "ImplementationStatusAnnotation") {
|
||||
return gen->visitImplementationStatusAnnotation(static_cast<const ImplementationStatusAnnotation*>(node));
|
||||
}
|
||||
// Semantic Core
|
||||
else if (node->conceptType == "IntentAnnotation") {
|
||||
return gen->visitIntentAnnotation(static_cast<const IntentAnnotation*>(node));
|
||||
|
||||
@@ -377,6 +377,8 @@ inline json propertiesToJson(const ASTNode* node) {
|
||||
auto* n = static_cast<const AmbiguityAnnotation*>(node);
|
||||
if (!n->intent.empty()) props["intent"] = n->intent;
|
||||
if (!n->options.empty()) props["options"] = n->options;
|
||||
if (!n->level.empty()) props["level"] = n->level;
|
||||
if (!n->description.empty()) props["description"] = n->description;
|
||||
}
|
||||
else if (ct == "CandidateAnnotation") {
|
||||
auto* n = static_cast<const CandidateAnnotation*>(node);
|
||||
@@ -400,6 +402,32 @@ inline json propertiesToJson(const ASTNode* node) {
|
||||
if (!n->author.empty()) props["author"] = n->author;
|
||||
if (!n->reason.empty()) props["reason"] = n->reason;
|
||||
}
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
else if (ct == "ContextWidthAnnotation") {
|
||||
auto* n = static_cast<const ContextWidthAnnotation*>(node);
|
||||
if (!n->width.empty()) props["width"] = n->width;
|
||||
}
|
||||
else if (ct == "ReviewAnnotation") {
|
||||
auto* n = static_cast<const ReviewAnnotation*>(node);
|
||||
props["required"] = n->required;
|
||||
if (!n->reviewer.empty()) props["reviewer"] = n->reviewer;
|
||||
if (!n->reason.empty()) props["reason"] = n->reason;
|
||||
}
|
||||
else if (ct == "AutomatabilityAnnotation") {
|
||||
auto* n = static_cast<const AutomatabilityAnnotation*>(node);
|
||||
if (!n->strategy.empty()) props["strategy"] = n->strategy;
|
||||
if (n->confidence > 0.0) props["confidence"] = n->confidence;
|
||||
}
|
||||
else if (ct == "PriorityAnnotation") {
|
||||
auto* n = static_cast<const PriorityAnnotation*>(node);
|
||||
if (!n->level.empty()) props["level"] = n->level;
|
||||
if (!n->blockedBy.empty()) props["blockedBy"] = n->blockedBy;
|
||||
}
|
||||
else if (ct == "ImplementationStatusAnnotation") {
|
||||
auto* n = static_cast<const ImplementationStatusAnnotation*>(node);
|
||||
if (!n->status.empty()) props["status"] = n->status;
|
||||
if (!n->assignee.empty()) props["assignee"] = n->assignee;
|
||||
}
|
||||
// Host Boundary (Step 288)
|
||||
else if (ct == "HostCall") {
|
||||
auto* n = static_cast<const HostCall*>(node);
|
||||
@@ -604,6 +632,12 @@ inline ASTNode* createNode(const std::string& conceptName) {
|
||||
if (conceptName == "TradeoffAnnotation") return new TradeoffAnnotation();
|
||||
if (conceptName == "ChoiceAnnotation") return new ChoiceAnnotation();
|
||||
if (conceptName == "DecisionAnnotation") return new DecisionAnnotation();
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
if (conceptName == "ContextWidthAnnotation") return new ContextWidthAnnotation();
|
||||
if (conceptName == "ReviewAnnotation") return new ReviewAnnotation();
|
||||
if (conceptName == "AutomatabilityAnnotation") return new AutomatabilityAnnotation();
|
||||
if (conceptName == "PriorityAnnotation") return new PriorityAnnotation();
|
||||
if (conceptName == "ImplementationStatusAnnotation") return new ImplementationStatusAnnotation();
|
||||
// Host Boundary (Step 288)
|
||||
if (conceptName == "HostCall") return new HostCall();
|
||||
if (conceptName == "ScheduleTask") return new ScheduleTask();
|
||||
@@ -994,6 +1028,8 @@ inline void setPropertiesFromJson(ASTNode* node, const json& props) {
|
||||
for (const auto& o : props["options"])
|
||||
if (o.is_string()) n->options.push_back(o.get<std::string>());
|
||||
}
|
||||
if (props.contains("level")) n->level = props["level"].get<std::string>();
|
||||
if (props.contains("description")) n->description = props["description"].get<std::string>();
|
||||
}
|
||||
else if (ct == "CandidateAnnotation") {
|
||||
auto* n = static_cast<CandidateAnnotation*>(node);
|
||||
@@ -1025,6 +1061,36 @@ inline void setPropertiesFromJson(ASTNode* node, const json& props) {
|
||||
if (props.contains("author")) n->author = props["author"].get<std::string>();
|
||||
if (props.contains("reason")) n->reason = props["reason"].get<std::string>();
|
||||
}
|
||||
// Subject 9: Workflow Routing (Step 315)
|
||||
else if (ct == "ContextWidthAnnotation") {
|
||||
auto* n = static_cast<ContextWidthAnnotation*>(node);
|
||||
if (props.contains("width")) n->width = props["width"].get<std::string>();
|
||||
}
|
||||
else if (ct == "ReviewAnnotation") {
|
||||
auto* n = static_cast<ReviewAnnotation*>(node);
|
||||
if (props.contains("required")) n->required = props["required"].get<bool>();
|
||||
if (props.contains("reviewer")) n->reviewer = props["reviewer"].get<std::string>();
|
||||
if (props.contains("reason")) n->reason = props["reason"].get<std::string>();
|
||||
}
|
||||
else if (ct == "AutomatabilityAnnotation") {
|
||||
auto* n = static_cast<AutomatabilityAnnotation*>(node);
|
||||
if (props.contains("strategy")) n->strategy = props["strategy"].get<std::string>();
|
||||
if (props.contains("confidence")) n->confidence = props["confidence"].get<double>();
|
||||
}
|
||||
else if (ct == "PriorityAnnotation") {
|
||||
auto* n = static_cast<PriorityAnnotation*>(node);
|
||||
if (props.contains("level")) n->level = props["level"].get<std::string>();
|
||||
if (props.contains("blockedBy") && props["blockedBy"].is_array()) {
|
||||
n->blockedBy.clear();
|
||||
for (const auto& b : props["blockedBy"])
|
||||
if (b.is_string()) n->blockedBy.push_back(b.get<std::string>());
|
||||
}
|
||||
}
|
||||
else if (ct == "ImplementationStatusAnnotation") {
|
||||
auto* n = static_cast<ImplementationStatusAnnotation*>(node);
|
||||
if (props.contains("status")) n->status = props["status"].get<std::string>();
|
||||
if (props.contains("assignee")) n->assignee = props["assignee"].get<std::string>();
|
||||
}
|
||||
// Host Boundary (Step 288)
|
||||
else if (ct == "HostCall") {
|
||||
auto* n = static_cast<HostCall*>(node);
|
||||
|
||||
Reference in New Issue
Block a user