Step 372: Add Lisp annotation inference mapping

This commit is contained in:
Bill
2026-02-16 11:22:40 -07:00
parent e9ec11200e
commit c5a4fddf96
6 changed files with 422 additions and 10 deletions

View File

@@ -14,7 +14,6 @@
#include <set>
#include <algorithm>
#include <cctype>
class AnnotationInference {
public:
struct InferredAnnotation {
@@ -57,6 +56,15 @@ private:
if (node->conceptType == "Function" || node->conceptType == "AsyncFunction" ||
node->conceptType == "MethodDeclaration") {
inferFunction(node, out);
} else if (node->conceptType == "Variable") {
inferVariable(node, out);
} else if (node->conceptType == "MacroDefinition") {
inferMacro(node, out);
} else if (node->conceptType == "ClassDeclaration") {
if (!hasInferred(out, node->id, "VisibilityAnnotation", "public")) {
out.push_back({node->id, "VisibilityAnnotation", "level", "public",
"Class declarations default to public surface", 0.70});
}
}
else if (node->conceptType == "ForLoop" || node->conceptType == "WhileLoop") {
inferLoop(node, out);
@@ -73,8 +81,6 @@ private:
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",
@@ -100,31 +106,27 @@ private:
"Tail-recursive call detected", 0.80});
}
}
inferLispFunctionPatterns(fn, existing, fnName, out);
// 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);
@@ -135,7 +137,6 @@ private:
"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;
@@ -182,9 +183,18 @@ private:
return fc->functionName == name;
}
}
if (last->conceptType == "ExpressionStatement") {
auto* expr = last->getChild("expression");
if (expr && expr->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(expr);
return fc->functionName == name;
}
}
return false;
}
#include "AnnotationInferenceLisp.h"
bool hasExceptionHandling(const ASTNode* node) const {
if (!node) return false;
// Look for try/catch patterns in function calls

View File

@@ -0,0 +1,98 @@
void inferLispFunctionPatterns(const ASTNode* fn,
const std::set<std::string>& existing,
const std::string& fnName,
std::vector<InferredAnnotation>& out) const {
if (hasCallNamed(fn, "values") && !existing.count("ContractAnnotation")) {
out.push_back({fn->id, "ContractAnnotation", "returns", "multiple-values",
"values form indicates multi-value return contract", 0.88});
}
if (hasDeclaredTypePattern(fn) && !existing.count("ComplexityAnnotation")) {
out.push_back({fn->id, "ComplexityAnnotation", "timeComplexity", "declared-type",
"Type declaration forms constrain complexity interpretation", 0.72});
}
if (hasOptimizePattern(fn) && !hasInferred(out, fn->id, "PolicyAnnotation", "critical")) {
out.push_back({fn->id, "PolicyAnnotation", "perf", "critical",
"optimize speed hint implies performance-critical policy", 0.90});
out.push_back({fn->id, "PolicyAnnotation", "style", "literal",
"optimize declaration favors literal policy output", 0.86});
}
if (fn->conceptType == "MethodDeclaration" && fnName.size() > 0 &&
!hasInferred(out, fn->id, "SyntheticAnnotation", "clos-method")) {
out.push_back({fn->id, "SyntheticAnnotation", "generator", "clos-method",
"Method declaration maps to CLOS generic dispatch", 0.76});
}
}
void inferVariable(const ASTNode* varNode, std::vector<InferredAnnotation>& out) const {
auto* var = static_cast<const Variable*>(varNode);
if (var->name.size() > 2 && var->name.front() == '*' && var->name.back() == '*' &&
!hasInferred(out, var->id, "BindingAnnotation", "dynamic")) {
out.push_back({var->id, "BindingAnnotation", "time", "dynamic",
"Earmuff naming indicates dynamic/special binding", 0.95});
} else if (varNode->parent && varNode->parent->conceptType == "Block" &&
!hasInferred(out, var->id, "BindingAnnotation", "static")) {
out.push_back({var->id, "BindingAnnotation", "time", "static",
"Lexical block variable inferred as static binding", 0.70});
}
}
void inferMacro(const ASTNode* macroNode, std::vector<InferredAnnotation>& out) const {
if (!hasInferred(out, macroNode->id, "MetaAnnotation", "quoted")) {
out.push_back({macroNode->id, "MetaAnnotation", "state", "quoted",
"Macro definitions operate over quoted forms", 0.92});
}
if (!hasInferred(out, macroNode->id, "SyntheticAnnotation", "macro")) {
out.push_back({macroNode->id, "SyntheticAnnotation", "generator", "macro",
"Macro expands synthetic code structures", 0.90});
}
}
bool hasCallNamed(const ASTNode* node, const std::string& name) const {
if (!node) return false;
if (node->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(node);
if (lowerAscii(fc->functionName) == lowerAscii(name)) return true;
}
for (auto* child : node->allChildren()) {
if (hasCallNamed(child, name)) return true;
}
return false;
}
bool hasDeclaredTypePattern(const ASTNode* node) const {
if (!node) return false;
if (node->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(node);
if (lowerAscii(fc->functionName) == "declare") {
for (auto* arg : fc->getChildren("arguments")) {
if (arg->conceptType != "FunctionCall") continue;
auto* inner = static_cast<const FunctionCall*>(arg);
if (lowerAscii(inner->functionName) == "type") return true;
}
}
}
for (auto* child : node->allChildren()) {
if (hasDeclaredTypePattern(child)) return true;
}
return false;
}
bool hasOptimizePattern(const ASTNode* node) const {
if (!node) return false;
if (node->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(node);
std::string fn = lowerAscii(fc->functionName);
if (fn == "optimize") return true;
if (fn == "declare") {
for (auto* arg : fc->getChildren("arguments")) {
if (arg->conceptType != "FunctionCall") continue;
auto* inner = static_cast<const FunctionCall*>(arg);
if (lowerAscii(inner->functionName) == "optimize") return true;
}
}
}
for (auto* child : node->allChildren()) {
if (hasOptimizePattern(child)) return true;
}
return false;
}

View File

@@ -80,11 +80,18 @@ private:
if (lang == "python" || lang == "elisp" || lang == "ruby" ||
lang == "javascript" || lang == "java" || lang == "csharp" ||
lang == "kotlin") {
lang == "kotlin" || lang == "common-lisp" || lang == "commonlisp" ||
lang == "lisp" || lang == "cl") {
// GC languages → @Reclaim(Tracing) on the module
out.push_back({mod->id, "ReclaimAnnotation", "Tracing",
lang + " uses tracing garbage collection",
0.95});
if (lang == "common-lisp" || lang == "commonlisp" ||
lang == "lisp" || lang == "cl") {
out.push_back({mod->id, "OwnerAnnotation", "Shared_GC",
"Common Lisp values are GC-managed shared objects",
0.92});
}
}
else if (lang == "go") {
out.push_back({mod->id, "ReclaimAnnotation", "Escape",