diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 31e477b..f0b3dfc 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -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) diff --git a/editor/src/AnnotationInferenceLisp.h b/editor/src/AnnotationInferenceLisp.h index d100ffb..b68b7c2 100644 --- a/editor/src/AnnotationInferenceLisp.h +++ b/editor/src/AnnotationInferenceLisp.h @@ -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& 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(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", diff --git a/editor/src/MemoryStrategyInference.h b/editor/src/MemoryStrategyInference.h index 61359c9..a72f0f4 100644 --- a/editor/src/MemoryStrategyInference.h +++ b/editor/src/MemoryStrategyInference.h @@ -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}); } } diff --git a/editor/tests/step376_test.cpp b/editor/tests/step376_test.cpp new file mode 100644 index 0000000..2e31fe3 --- /dev/null +++ b/editor/tests/step376_test.cpp @@ -0,0 +1,192 @@ +// Step 376: Scheme-Specific Annotations + CL Differentiation (12 tests) + +#include +#include +#include +#include +#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& 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& 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; +} diff --git a/progress.md b/progress.md index 6987641..b2d72d6 100644 --- a/progress.md +++ b/progress.md @@ -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)