Step 367: Add WAT annotation mapping and tests
This commit is contained in:
@@ -2182,4 +2182,13 @@ target_link_libraries(step366_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step367_test tests/step367_test.cpp)
|
||||
target_include_directories(step367_test PRIVATE src)
|
||||
target_link_libraries(step367_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)
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
}
|
||||
if (mod->targetLanguage == "c") {
|
||||
inferCModule(mod, out);
|
||||
} else if (mod->targetLanguage == "wat" || mod->targetLanguage == "wasm") {
|
||||
inferWatModule(mod, out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +397,53 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void inferWatModule(const Module* mod, std::vector<InferredAnnotation>& out) const {
|
||||
if (!mod) return;
|
||||
|
||||
for (auto* fnNode : mod->getChildren("functions")) {
|
||||
if (fnNode->conceptType != "Function") continue;
|
||||
if (!hasInferred(out, fnNode->id, "ExecAnnotation", "stack")) {
|
||||
out.push_back({fnNode->id, "ExecAnnotation", "mode", "stack",
|
||||
"WAT executes in a stack-based VM", 0.95});
|
||||
}
|
||||
}
|
||||
|
||||
if (!mod->getChildren("imports").empty() &&
|
||||
!hasInferred(out, mod->id, "LinkAnnotation", "import")) {
|
||||
out.push_back({mod->id, "LinkAnnotation", "kind", "import",
|
||||
"WAT module imports host/runtime functions", 0.90});
|
||||
}
|
||||
|
||||
bool hasExport = false;
|
||||
bool hasTable = false;
|
||||
bool hasMemory = false;
|
||||
for (auto* stmt : mod->getChildren("statements")) {
|
||||
if (stmt->conceptType != "PragmaDirective") continue;
|
||||
auto* p = static_cast<const PragmaDirective*>(stmt);
|
||||
std::string d = lowerAscii(p->directive);
|
||||
hasExport = hasExport || d == "export";
|
||||
hasTable = hasTable || d == "table";
|
||||
hasMemory = hasMemory || d == "memory";
|
||||
}
|
||||
|
||||
if (hasExport && !hasInferred(out, mod->id, "VisibilityAnnotation", "public")) {
|
||||
out.push_back({mod->id, "VisibilityAnnotation", "level", "public",
|
||||
"Exported WAT symbols are public API", 0.90});
|
||||
}
|
||||
if (hasTable && !hasInferred(out, mod->id, "ShimAnnotation", "vtable")) {
|
||||
out.push_back({mod->id, "ShimAnnotation", "kind", "vtable",
|
||||
"WAT table dispatch resembles virtual table shims", 0.82});
|
||||
}
|
||||
if (hasMemory && !hasInferred(out, mod->id, "AlignAnnotation", "4")) {
|
||||
out.push_back({mod->id, "AlignAnnotation", "bytes", "4",
|
||||
"Linear memory defaults to aligned word operations", 0.70});
|
||||
}
|
||||
if (hasMemory && !hasInferred(out, mod->id, "LayoutAnnotation", "linear")) {
|
||||
out.push_back({mod->id, "LayoutAnnotation", "mode", "linear",
|
||||
"WAT memory is a linear buffer layout", 0.86});
|
||||
}
|
||||
}
|
||||
|
||||
void inferCHeaderGuard(const Module* mod, std::vector<InferredAnnotation>& out) const {
|
||||
bool hasIfndef = false;
|
||||
bool hasEndif = false;
|
||||
|
||||
@@ -100,6 +100,9 @@ private:
|
||||
out.push_back({mod->id, "OwnerAnnotation", "Single",
|
||||
"Rust uses single-owner lifetime tracking", 0.95});
|
||||
}
|
||||
else if (lang == "wat" || lang == "wasm") {
|
||||
inferWatModule(mod, out);
|
||||
}
|
||||
else if (lang == "swift" || lang == "objc") {
|
||||
out.push_back({mod->id, "OwnerAnnotation", "Shared_ARC",
|
||||
lang + " uses automatic reference counting", 0.90});
|
||||
@@ -130,6 +133,14 @@ private:
|
||||
"C uses explicit reclamation (free)", 0.95});
|
||||
}
|
||||
|
||||
void inferWatModule(const Module* mod,
|
||||
std::vector<Suggestion>& out) const {
|
||||
out.push_back({mod->id, "OwnerAnnotation", "Manual",
|
||||
"WAT linear memory is manually managed", 0.92});
|
||||
out.push_back({mod->id, "ReclaimAnnotation", "Explicit",
|
||||
"WAT has explicit memory lifetime operations", 0.88});
|
||||
}
|
||||
|
||||
void inferFunction(const Function* fn, const std::string& lang,
|
||||
std::vector<Suggestion>& out) const {
|
||||
// Skip if function already has a memory annotation
|
||||
@@ -139,6 +150,10 @@ private:
|
||||
inferCFunction(fn, out);
|
||||
return;
|
||||
}
|
||||
if (lang == "wat" || lang == "wasm") {
|
||||
inferWatFunction(fn, out);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check body for patterns
|
||||
bool hasAlloc = false;
|
||||
@@ -213,6 +228,19 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void inferWatFunction(const Function* fn,
|
||||
std::vector<Suggestion>& out) const {
|
||||
bool hasMemoryOps = false;
|
||||
for (auto* stmt : fn->getChildren("body")) {
|
||||
hasMemoryOps = hasMemoryOps || containsWatMemoryOp(stmt);
|
||||
}
|
||||
if (hasMemoryOps) {
|
||||
out.push_back({fn->id, "OwnerAnnotation", "Manual",
|
||||
"WAT memory/load/store operations require manual ownership",
|
||||
0.90});
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasMemoryAnnotation(const ASTNode* node) {
|
||||
for (auto* anno : node->getChildren("annotations")) {
|
||||
const auto& ct = anno->conceptType;
|
||||
@@ -255,6 +283,23 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool containsWatMemoryOp(const ASTNode* node) {
|
||||
if (!node) return false;
|
||||
if (node->conceptType == "FunctionCall") {
|
||||
auto* fc = static_cast<const FunctionCall*>(node);
|
||||
std::string fn = lowerAscii(fc->functionName);
|
||||
if (fn.find("memory.") != std::string::npos ||
|
||||
fn.find(".load") != std::string::npos ||
|
||||
fn.find(".store") != std::string::npos ||
|
||||
fn == "memory.grow")
|
||||
return true;
|
||||
}
|
||||
for (auto* child : node->allChildren()) {
|
||||
if (containsWatMemoryOp(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void inferCLocalLifetime(const ASTNode* node,
|
||||
std::vector<Suggestion>& out) {
|
||||
if (!node) return;
|
||||
|
||||
199
editor/tests/step367_test.cpp
Normal file
199
editor/tests/step367_test.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
// Step 367: WAT Annotation Mapping (12 tests)
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "MemoryStrategyInference.h"
|
||||
#include "AnnotationInference.h"
|
||||
#include "CrossLanguageProjector.h"
|
||||
#include "SemannoFormat.h"
|
||||
#include "Pipeline.h"
|
||||
#include "ast/WatParser.h"
|
||||
|
||||
static bool hasMemorySuggestion(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;
|
||||
}
|
||||
|
||||
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 && a.value == value) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int passed = 0;
|
||||
|
||||
// Test 1: WAT function gets Exec(stack)
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("functions", new Function("f1", "run"));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
assert(hasInference(inferred, "ExecAnnotation", "stack"));
|
||||
std::cout << "Test 1 PASSED: function -> Exec(stack)\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 2: memory op gets Owner(Manual)
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
auto* fn = new Function("f1", "grow");
|
||||
auto* call = new FunctionCall();
|
||||
call->id = "c1";
|
||||
call->functionName = "memory.grow";
|
||||
fn->addChild("body", call);
|
||||
mod.addChild("functions", fn);
|
||||
|
||||
MemoryStrategyInference mem;
|
||||
auto suggestions = mem.inferAnnotations(&mod);
|
||||
assert(hasMemorySuggestion(suggestions, "OwnerAnnotation", "Manual"));
|
||||
std::cout << "Test 2 PASSED: memory op -> Owner(Manual)\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 3: export -> Visibility(public)
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("statements", new PragmaDirective("p1", "export"));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
assert(hasInference(inferred, "VisibilityAnnotation", "public"));
|
||||
std::cout << "Test 3 PASSED: export -> Visibility(public)\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 4: import -> Link(import)
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("imports", new Import("i1", "env.print", "runtime", ""));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
assert(hasInference(inferred, "LinkAnnotation", "import"));
|
||||
std::cout << "Test 4 PASSED: import -> Link(import)\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 5: WAT defaults applied
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
MemoryStrategyInference mem;
|
||||
auto suggestions = mem.inferAnnotations(&mod);
|
||||
assert(hasMemorySuggestion(suggestions, "OwnerAnnotation", "Manual"));
|
||||
assert(hasMemorySuggestion(suggestions, "ReclaimAnnotation", "Explicit"));
|
||||
std::cout << "Test 5 PASSED: WAT defaults applied\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 6: table dispatch -> Shim(vtable)
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("statements", new PragmaDirective("p1", "table"));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
assert(hasInference(inferred, "ShimAnnotation", "vtable"));
|
||||
std::cout << "Test 6 PASSED: table -> Shim(vtable)\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 7: memory declarations -> Align + Layout
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("statements", new PragmaDirective("p1", "memory"));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
assert(hasInference(inferred, "AlignAnnotation", "4"));
|
||||
assert(hasInference(inferred, "LayoutAnnotation", "linear"));
|
||||
std::cout << "Test 7 PASSED: memory -> Align/Layout\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 8: confidence values stay bounded
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
mod.addChild("functions", new Function("f1", "run"));
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(&mod);
|
||||
bool foundStrong = false;
|
||||
for (const auto& a : inferred) {
|
||||
assert(a.confidence >= 0.0 && a.confidence <= 1.0);
|
||||
if (a.confidence >= 0.75) foundStrong = true;
|
||||
}
|
||||
assert(foundStrong);
|
||||
std::cout << "Test 8 PASSED: confidence bounded and useful\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 9: WAT annotations preserved projecting to C
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
auto* fn = new Function("f1", "run");
|
||||
auto* exec = new ExecAnnotation();
|
||||
exec->id = "a1";
|
||||
exec->mode = "stack";
|
||||
fn->addChild("annotations", exec);
|
||||
mod.addChild("functions", fn);
|
||||
|
||||
CrossLanguageProjector projector;
|
||||
auto cMod = projector.project(&mod, "c");
|
||||
assert(projector.annotationsPreserved(&mod, cMod.get()));
|
||||
std::cout << "Test 9 PASSED: projection preserves WAT annotations\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 10: Semanno roundtrip for Exec(stack)
|
||||
{
|
||||
ExecAnnotation exec;
|
||||
exec.id = "a1";
|
||||
exec.mode = "stack";
|
||||
exec.runtimeHint = "wasm";
|
||||
std::string line = SemannoEmitter::emit(&exec);
|
||||
auto parsed = SemannoParser::parse(line);
|
||||
assert(parsed.type == "exec");
|
||||
assert(parsed.properties["mode"] == "stack");
|
||||
std::cout << "Test 10 PASSED: semanno roundtrip\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 11: pipeline parse wat + infer stack annotation
|
||||
{
|
||||
Pipeline p;
|
||||
std::vector<ParseDiagnostic> diags;
|
||||
auto mod = p.parse("(module (func $f (result i32)))", "wat", diags);
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(mod.get());
|
||||
assert(hasInference(inferred, "ExecAnnotation", "stack"));
|
||||
std::cout << "Test 11 PASSED: pipeline wat parse supports inference\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 12: annotation survives wat -> c -> wat projection
|
||||
{
|
||||
Module mod("m1", "wat", "wat");
|
||||
auto* fn = new Function("f1", "run");
|
||||
auto* vis = new VisibilityAnnotation();
|
||||
vis->id = "v1";
|
||||
vis->level = "public";
|
||||
fn->addChild("annotations", vis);
|
||||
mod.addChild("functions", fn);
|
||||
|
||||
CrossLanguageProjector projector;
|
||||
auto cMod = projector.project(&mod, "c");
|
||||
auto watBack = projector.project(cMod.get(), "wat");
|
||||
assert(projector.annotationsPreserved(&mod, watBack.get()));
|
||||
std::cout << "Test 12 PASSED: annotation roundtrip through projection\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
std::cout << "\nResults: " << passed << "/12\n";
|
||||
assert(passed == 12);
|
||||
return 0;
|
||||
}
|
||||
42
progress.md
42
progress.md
@@ -2286,6 +2286,48 @@ the shared pipeline.
|
||||
**Architecture gate check:**
|
||||
- `editor/src/ast/WatGenerator.h` is within header size limit (`219` lines)
|
||||
|
||||
### Step 367: WAT Annotation Mapping
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Implemented WAT-specific annotation inference for execution model, linkage, and
|
||||
memory-layout signals, plus WAT memory-strategy defaults.
|
||||
|
||||
**Files created:**
|
||||
- `editor/tests/step367_test.cpp` — 12 tests covering:
|
||||
1. function -> `Exec(stack)`
|
||||
2. memory operations -> `Owner(Manual)`
|
||||
3. export -> `Visibility(public)`
|
||||
4. import -> `Link(import)`
|
||||
5. WAT defaults (`Owner(Manual)`, `Reclaim(Explicit)`)
|
||||
6. table dispatch -> `Shim(vtable)`
|
||||
7. memory declaration -> `Align` + `Layout(linear)`
|
||||
8. confidence bounds
|
||||
9. annotation preservation through projection to C
|
||||
10. Semanno emit/parse roundtrip
|
||||
11. pipeline parse + inference integration
|
||||
12. WAT -> C -> WAT annotation roundtrip preservation
|
||||
|
||||
**Files modified:**
|
||||
- `editor/src/MemoryStrategyInference.h` — WAT defaults and memory-op rule:
|
||||
- module defaults for `wat/wasm`
|
||||
- function memory-op detection (`memory.*`, `*.load`, `*.store`)
|
||||
- `editor/src/AnnotationInference.h` — WAT-specific pattern inference:
|
||||
- function-level `Exec(stack)`
|
||||
- import/export mapping to `Link`/`Visibility`
|
||||
- table mapping to `Shim(vtable)`
|
||||
- memory mapping to `Align` + `Layout(linear)`
|
||||
- `editor/CMakeLists.txt` — `step367_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `step367_test` — PASS (12/12) new step coverage
|
||||
- `step366_test` — PASS (12/12) regression coverage
|
||||
- `step365_test` — PASS (12/12) regression coverage
|
||||
- `step364_test` — PASS (8/8) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/AnnotationInference.h` remains within header size limit
|
||||
(`589` lines <= `600`)
|
||||
|
||||
# Roadmap Planning — Sprints 12-25+
|
||||
|
||||
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
||||
|
||||
Reference in New Issue
Block a user