Step 376: Add Scheme-specific annotation differentiation

This commit is contained in:
Bill
2026-02-16 11:36:22 -07:00
parent d06c32d5ff
commit 923021d1c1
5 changed files with 287 additions and 6 deletions

View File

@@ -2263,4 +2263,13 @@ target_link_libraries(step375_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step376_test tests/step376_test.cpp)
target_include_directories(step376_test PRIVATE src)
target_link_libraries(step376_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -16,6 +16,31 @@
out.push_back({fn->id, "PolicyAnnotation", "style", "literal",
"optimize declaration favors literal policy output", 0.86});
}
if ((hasCallNamed(fn, "call/cc") || hasCallNamed(fn, "call-with-current-continuation")) &&
!hasInferred(out, fn->id, "ExecAnnotation", "continuation")) {
out.push_back({fn->id, "ExecAnnotation", "mode", "continuation",
"Scheme continuation primitives detected", 0.92});
}
if (hasCallNamed(fn, "make-parameter") &&
!hasInferred(out, fn->id, "BindingAnnotation", "parameter")) {
out.push_back({fn->id, "BindingAnnotation", "time", "parameter",
"Scheme make-parameter indicates parameter binding model", 0.90});
}
if (hasCallNamed(fn, "guard") &&
!hasInferred(out, fn->id, "ExceptionAnnotation", "guard")) {
out.push_back({fn->id, "ExceptionAnnotation", "style", "guard",
"Scheme guard form indicates guard-style exception handling", 0.88});
}
if (hasCallNamed(fn, "handler-case") &&
!hasInferred(out, fn->id, "ExceptionAnnotation", "condition")) {
out.push_back({fn->id, "ExceptionAnnotation", "style", "condition",
"Common Lisp condition-system form detected", 0.88});
}
if (!fnName.empty() && isTailRecursive(fn, fnName) &&
!hasInferred(out, fn->id, "LoopAnnotation", "tail-recursive")) {
out.push_back({fn->id, "LoopAnnotation", "hint", "tail-recursive",
"Tail recursion pattern maps to Scheme loop idiom", 0.85});
}
if (fn->conceptType == "MethodDeclaration" && fnName.size() > 0 &&
!hasInferred(out, fn->id, "SyntheticAnnotation", "clos-method")) {
out.push_back({fn->id, "SyntheticAnnotation", "generator", "clos-method",
@@ -37,9 +62,15 @@
}
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});
std::string metaState = "quoted";
for (auto* anno : macroNode->getChildren("annotations")) {
if (anno->conceptType != "MetaAnnotation") continue;
auto* m = static_cast<const MetaAnnotation*>(anno);
if (m->state == "hygienic") metaState = "hygienic";
}
if (!hasInferred(out, macroNode->id, "MetaAnnotation", metaState)) {
out.push_back({macroNode->id, "MetaAnnotation", "state", metaState,
"Macro form state inferred from language macro style", 0.92});
}
if (!hasInferred(out, macroNode->id, "SyntheticAnnotation", "macro")) {
out.push_back({macroNode->id, "SyntheticAnnotation", "generator", "macro",

View File

@@ -81,15 +81,15 @@ private:
if (lang == "python" || lang == "elisp" || lang == "ruby" ||
lang == "javascript" || lang == "java" || lang == "csharp" ||
lang == "kotlin" || lang == "common-lisp" || lang == "commonlisp" ||
lang == "lisp" || lang == "cl") {
lang == "lisp" || lang == "cl" || lang == "scheme" || lang == "scm") {
// 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") {
lang == "lisp" || lang == "cl" || lang == "scheme" || lang == "scm") {
out.push_back({mod->id, "OwnerAnnotation", "Shared_GC",
"Common Lisp values are GC-managed shared objects",
"Lisp-family values are GC-managed shared objects",
0.92});
}
}

View File

@@ -0,0 +1,192 @@
// Step 376: Scheme-Specific Annotations + CL Differentiation (12 tests)
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include "AnnotationInference.h"
#include "MemoryStrategyInference.h"
#include "CrossLanguageProjector.h"
#include "Pipeline.h"
#include "ast/SchemeParser.h"
#include "ast/CommonLispParser.h"
static bool hasInference(const std::vector<AnnotationInference::InferredAnnotation>& inferred,
const std::string& annotationType,
const std::string& value = "") {
for (const auto& a : inferred) {
if (a.annotationType != annotationType) continue;
if (value.empty() || a.value == value) return true;
}
return false;
}
static bool hasMemory(const std::vector<MemoryStrategyInference::Suggestion>& suggestions,
const std::string& annotationType,
const std::string& strategy) {
for (const auto& s : suggestions) {
if (s.annotationType == annotationType && s.strategy == strategy) return true;
}
return false;
}
int main() {
int passed = 0;
// Test 1: Scheme tail recursion -> TailCall
{
auto mod = SchemeParser::parseScheme("(define (loopf n) (loopf n))");
AnnotationInference inf;
auto inferred = inf.inferAll(mod.get());
assert(hasInference(inferred, "TailCallAnnotation"));
std::cout << "Test 1 PASSED: Scheme tail recursion -> TailCall\n";
passed++;
}
// Test 2: call/cc -> Exec(continuation)
{
auto mod = SchemeParser::parseScheme("(define (cc f) (call/cc f))");
AnnotationInference inf;
auto inferred = inf.inferAll(mod.get());
assert(hasInference(inferred, "ExecAnnotation", "continuation"));
std::cout << "Test 2 PASSED: call/cc -> Exec(continuation)\n";
passed++;
}
// Test 3: define-syntax -> Meta(hygienic)
{
auto mod = SchemeParser::parseScheme("(define-syntax m (syntax-rules () ((_ x) x)))");
AnnotationInference inf;
auto inferred = inf.inferAll(mod.get());
assert(hasInference(inferred, "MetaAnnotation", "hygienic"));
std::cout << "Test 3 PASSED: define-syntax -> Meta(hygienic)\n";
passed++;
}
// Test 4: CL -> Scheme projection path works
{
Pipeline p;
auto res = p.run("(defun id (x) x)", "common-lisp", "scheme");
assert(res.success);
assert(!res.generatedCode.empty());
std::cout << "Test 4 PASSED: CL -> Scheme projection path\n";
passed++;
}
// Test 5: Scheme -> CL projection path works
{
Pipeline p;
auto res = p.run("(define (id x) x)", "scheme", "common-lisp");
assert(res.success);
assert(!res.generatedCode.empty());
std::cout << "Test 5 PASSED: Scheme -> CL projection path\n";
passed++;
}
// Test 6: both Scheme and CL use GC defaults
{
MemoryStrategyInference mem;
Module scm("m1", "scm", "scheme");
Module cl("m2", "cl", "common-lisp");
auto s1 = mem.inferAnnotations(&scm);
auto s2 = mem.inferAnnotations(&cl);
assert(hasMemory(s1, "ReclaimAnnotation", "Tracing"));
assert(hasMemory(s2, "ReclaimAnnotation", "Tracing"));
std::cout << "Test 6 PASSED: Scheme/CL GC defaults\n";
passed++;
}
// Test 7: differentiate @Meta between CL and Scheme macros
{
auto cl = CommonLispParser::parseCommonLisp("(defmacro m (x) x)");
auto scm = SchemeParser::parseScheme("(define-syntax m (syntax-rules () ((_ x) x)))");
AnnotationInference inf;
auto clInf = inf.inferAll(cl.get());
auto scmInf = inf.inferAll(scm.get());
assert(hasInference(clInf, "MetaAnnotation", "quoted"));
assert(hasInference(scmInf, "MetaAnnotation", "hygienic"));
std::cout << "Test 7 PASSED: differentiated macro meta states\n";
passed++;
}
// Test 8: differentiate @Binding types
{
auto cl = CommonLispParser::parseCommonLisp("(defvar *special* 1)");
auto scm = SchemeParser::parseScheme("(define (mk) (make-parameter 42))");
AnnotationInference inf;
auto clInf = inf.inferAll(cl.get());
auto scmInf = inf.inferAll(scm.get());
assert(hasInference(clInf, "BindingAnnotation", "dynamic"));
assert(hasInference(scmInf, "BindingAnnotation", "parameter"));
std::cout << "Test 8 PASSED: differentiated binding types\n";
passed++;
}
// Test 9: differentiate @Exception types
{
auto cl = CommonLispParser::parseCommonLisp("(defun f () (handler-case x))");
auto scm = SchemeParser::parseScheme("(define (g) (guard (e (#t e)) e))");
AnnotationInference inf;
auto clInf = inf.inferAll(cl.get());
auto scmInf = inf.inferAll(scm.get());
assert(hasInference(clInf, "ExceptionAnnotation", "condition"));
assert(hasInference(scmInf, "ExceptionAnnotation", "guard"));
std::cout << "Test 9 PASSED: differentiated exception styles\n";
passed++;
}
// Test 10: cross-language annotation fidelity Scheme <-> CL
{
Module mod("m1", "scheme", "scheme");
auto* fn = new Function("f1", "cc");
auto* ex = new ExecAnnotation();
ex->id = "a1";
ex->mode = "continuation";
fn->addChild("annotations", ex);
mod.addChild("functions", fn);
auto* mac = new MacroDefinition("m1", "mac");
auto* meta = new MetaAnnotation();
meta->id = "a2";
meta->state = "hygienic";
mac->addChild("annotations", meta);
mod.addChild("statements", mac);
CrossLanguageProjector projector;
auto cl = projector.project(&mod, "common-lisp");
auto back = projector.project(cl.get(), "scheme");
assert(projector.annotationsPreserved(&mod, back.get()));
std::cout << "Test 10 PASSED: annotation fidelity across Scheme/CL\n";
passed++;
}
// Test 11: Scheme tail recursion also maps to Loop(tail-recursive)
{
auto mod = SchemeParser::parseScheme("(define (loopf n) (loopf n))");
AnnotationInference inf;
auto inferred = inf.inferAll(mod.get());
assert(hasInference(inferred, "LoopAnnotation", "tail-recursive"));
std::cout << "Test 11 PASSED: Scheme loop tail-recursive signal\n";
passed++;
}
// Test 12: confidence values remain bounded
{
auto mod = SchemeParser::parseScheme(
"(define-syntax m (syntax-rules () ((_ x) x)))\n"
"(define (cc f) (call/cc f))\n");
AnnotationInference inf;
auto inferred = inf.inferAll(mod.get());
bool strong = false;
for (const auto& a : inferred) {
assert(a.confidence >= 0.0 && a.confidence <= 1.0);
if (a.confidence >= 0.85) strong = true;
}
assert(strong);
std::cout << "Test 12 PASSED: confidence bounds\n";
passed++;
}
std::cout << "\nResults: " << passed << "/12\n";
assert(passed == 12);
return 0;
}

View File

@@ -2656,6 +2656,55 @@ annotation handling.
- `editor/src/ast/SchemeGenerator.h` is within header size limit
(`159` lines <= `600`)
### Step 376: Scheme-Specific Annotations + CL Differentiation
**Status:** PASS (12/12 tests)
Added Scheme-specific inference signals and explicit Common Lisp vs Scheme
differentiation rules in annotation inference, plus Scheme memory defaults in
memory-strategy inference.
**Files created:**
- `editor/tests/step376_test.cpp` — 12 tests covering:
1. Scheme tail recursion -> `TailCall`
2. `call/cc` -> `Exec(continuation)`
3. `define-syntax` -> `Meta(hygienic)`
4. CL -> Scheme projection path
5. Scheme -> CL projection path
6. GC defaults for both Scheme and CL
7. differentiated macro meta states (quoted vs hygienic)
8. differentiated binding states (dynamic vs parameter)
9. differentiated exception styles (condition vs guard)
10. Scheme/CL annotation fidelity through projection
11. tail-recursive loop idiom signal (`Loop(tail-recursive)`)
12. confidence bound checks
**Files modified:**
- `editor/src/AnnotationInferenceLisp.h` — add Lisp/Scheme-specific inference:
- continuation detection (`call/cc`, `call-with-current-continuation`)
- parameter-binding detection (`make-parameter`)
- condition-vs-guard exception style differentiation
- tail-recursive loop signal (`LoopAnnotation: tail-recursive`)
- macro meta-state differentiation via existing hygienic/quoted metadata
- `editor/src/MemoryStrategyInference.h` — add Scheme defaults:
- include `scheme/scm` in tracing-GC defaults
- shared GC ownership default for Lisp-family modules
- `editor/CMakeLists.txt``step376_test` target
**Verification run:**
- `step376_test` — PASS (12/12) new step coverage
- `step375_test` — PASS (12/12) regression coverage
- `step374_test` — PASS (12/12) regression coverage
- `step373_test` — PASS (8/8) regression coverage
- `step372_test` — PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/AnnotationInference.h` remains within header-size limit
(`599` lines <= `600`)
- `editor/src/MemoryStrategyInference.h` remains within header-size limit
(`384` lines <= `600`)
- `editor/src/AnnotationInferenceLisp.h` remains within header-size guidance
(`129` lines)
# Roadmap Planning — Sprints 12-25+
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)