From ffe8e626e13a705a0ebdf9efa4b68ef6d1b196ad Mon Sep 17 00:00:00 2001 From: Teakowa Date: Wed, 2 Sep 2026 04:39:35 +0800 Subject: [PATCH 1/3] fix(opy): converge parser source-form conformance Align standalone lambda diagnostics with the pinned parse frontier and move Workshop-owned or lowering-only probes out of parser contracts. Fixes #160 --- compatibility/compiler-expectations.json | 4 ++-- compatibility/conformance-manifest.json | 19 ++++++++----------- .../src/compiler/tests/issue_93_frontend.rs | 2 +- crates/opy-rs/src/lower.rs | 4 ++-- crates/opy-rs/tests/differential.rs | 2 +- docs/opy/conformance-baseline.md | 8 ++++++++ 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/compatibility/compiler-expectations.json b/compatibility/compiler-expectations.json index be9ef4e..566c3e9 100644 --- a/compatibility/compiler-expectations.json +++ b/compatibility/compiler-expectations.json @@ -822,9 +822,9 @@ "test:opy-rs::compiler::integration_tests::issue_93_frontend::frontend_diagnostics_are_public_and_source_attributed" ], "owner": "opy-rs#93", - "note": "The public compiler reports standalone lambda use as a source-attributed lambda-context diagnostic.", + "note": "The public compiler reports standalone lambda use at the source-attributed parse frontier.", "failureClass": "frontend", - "diagnosticCode": "lambda-context" + "diagnosticCode": "parse-error" }, { "fixture": "synthetic/preprocessing", diff --git a/compatibility/conformance-manifest.json b/compatibility/conformance-manifest.json index 932bca8..d966e1b 100644 --- a/compatibility/conformance-manifest.json +++ b/compatibility/conformance-manifest.json @@ -34,7 +34,7 @@ "id": "compound-expression-tokens", "claim": "Compound expression punctuation and token adjacency are preserved for the parser.", "probeKinds": ["positive", "contextual"], - "probes": ["synthetic/issue-28-syntax"] + "probes": ["synthetic/expressions-values"] }, { "id": "malformed-lexical-input", @@ -45,7 +45,7 @@ ], "probeFixtures": [ "synthetic/issue-28-string-modifiers", - "synthetic/issue-28-syntax", + "synthetic/expressions-values", "synthetic/issue-28-invalid-syntax", "synthetic/issue-33-f-string", "real-world/overpy-zencopter" @@ -76,7 +76,7 @@ "id": "branches-loops-and-switches", "claim": "Branches, loops, switch ordering, and break shapes are parsed distinctly.", "probeKinds": ["positive", "contextual", "negative"], - "probes": ["synthetic/control-flow", "synthetic/issue-47-control-flow", "synthetic/issue-47-do-while-invalid-placement", "synthetic/issue-47-do-while-shapes", "synthetic/issue-47-switch-multiple-break", "synthetic/issue-47-switch-order", "synthetic/issue-47-switch-structured-target", "synthetic/issue-47-unsupported", "synthetic/issue-33-switch-break", "synthetic/for-range-agentlab"] + "probes": ["synthetic/control-flow", "synthetic/issue-47-control-flow", "synthetic/issue-47-do-while-invalid-placement", "synthetic/issue-47-do-while-shapes", "synthetic/issue-47-switch-order", "synthetic/issue-47-switch-structured-target", "synthetic/issue-33-switch-break", "synthetic/for-range-agentlab"] }, { "id": "lambda-and-postfix-context", @@ -86,30 +86,26 @@ }, { "id": "indexed-assignment-depth", - "claim": "Indexed assignment depth has explicit supported and unsupported parser shapes.", - "probeKinds": ["contextual", "negative"], - "probes": ["synthetic/issue-46-primitives", "synthetic/issue-60-4d-negative"] + "claim": "Indexed assignment and access forms retain their structural boundaries before lowering.", + "probeKinds": ["contextual"], + "probes": ["synthetic/issue-46-primitives"] } ], "probeFixtures": [ "synthetic/basic-rule", "synthetic/expressions-values", "synthetic/control-flow", - "synthetic/issue-28-syntax", "synthetic/issue-33-lambda-negative", "synthetic/issue-33-switch-break", "synthetic/issue-47-control-flow", "synthetic/issue-47-do-while-invalid-placement", "synthetic/issue-47-do-while-shapes", - "synthetic/issue-47-switch-multiple-break", "synthetic/issue-47-switch-order", "synthetic/issue-47-switch-structured-target", - "synthetic/issue-47-unsupported", "synthetic/issue-33-switch-break", "synthetic/issue-59-embedded-postfix-negative", "synthetic/issue-59-postfix-assignment", "synthetic/issue-59-postfix-negative", - "synthetic/issue-60-4d-negative", "synthetic/issue-60-nested-index", "synthetic/for-range-agentlab", "synthetic/issue-46-primitives", @@ -276,7 +272,7 @@ "id": "vertical-slice", "claim": "A successful source-to-WIR vertical slice is comparable through the canonical Workshop contract.", "probeKinds": ["positive"], - "probes": ["synthetic/issue-35-integration", "synthetic/issue-40-structural"] + "probes": ["synthetic/issue-35-integration", "synthetic/issue-40-structural", "synthetic/issue-28-syntax"] }, { "id": "assignments-and-indexing", @@ -318,6 +314,7 @@ "probeFixtures": [ "synthetic/issue-35-integration", "synthetic/issue-40-structural", + "synthetic/issue-28-syntax", "synthetic/issue-46-primitives", "synthetic/issue-46-unsupported", "synthetic/issue-47-control-flow", diff --git a/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs b/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs index fceeb22..8494567 100644 --- a/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs +++ b/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs @@ -24,7 +24,7 @@ fn frontend_diagnostics_are_public_and_source_attributed() { ( "issue-33-lambda-negative.opy", "globalvar value\n\nrule \"issue 33 lambda negative\":\n @Event global\n debug(lambda item: item)\n", - "lambda-context", + "parse-error", ), ]; let compiler = Compiler::new().expect("released Workshop contract must load"); diff --git a/crates/opy-rs/src/lower.rs b/crates/opy-rs/src/lower.rs index fcd6aa1..bd19c99 100644 --- a/crates/opy-rs/src/lower.rs +++ b/crates/opy-rs/src/lower.rs @@ -1089,7 +1089,7 @@ impl Lowerer { Expr::Lambda { params, body, span } => { if position != CallPosition::LambdaArgument { self.error_at( - "lambda-context", + "parse-error", "lambda expressions are only valid as array operation arguments" .to_string(), *span, @@ -3423,7 +3423,7 @@ mod tests { "globalvar x\nrule \"r\":\n @Event global\n x = lambda value: value\n", 4, ); - assert_eq!(lambda_error.code, "lambda-context"); + assert_eq!(lambda_error.code, "parse-error"); } #[test] diff --git a/crates/opy-rs/tests/differential.rs b/crates/opy-rs/tests/differential.rs index f849509..ffd88ee 100644 --- a/crates/opy-rs/tests/differential.rs +++ b/crates/opy-rs/tests/differential.rs @@ -322,7 +322,7 @@ fn declared_corpus() -> BTreeMap<&'static str, Case> { diagnostic( &mut cases, "synthetic/issue-33-lambda-negative", - Some("lambda-context"), + Some("parse-error"), "Issue #33 standalone lambda use remains rejected outside a signature-approved argument position.", ); diagnostic( diff --git a/docs/opy/conformance-baseline.md b/docs/opy/conformance-baseline.md index 63c7e06..0597eb1 100644 --- a/docs/opy/conformance-baseline.md +++ b/docs/opy/conformance-baseline.md @@ -61,3 +61,11 @@ Its summary groups results by the manifest's root capability and owner, and its fixture entries retain both reference and native diagnostics. It is the current divergence inventory used to group follow-up work. No current native failure is entered into the manifest as an expected pass. + +The broad `synthetic/issue-28-syntax` probe is inventoried under the +canonical-WIR boundary rather than lexical conformance. Its source compiles +successfully, but the pinned Workshop output contains `0X20`, which the +released `workshop-rs` parser does not currently reparse; this is a +`workshop-rs` evidence limitation, not an OPY lexer divergence. Likewise, +switch multi-break and nested switch-break probes remain lowering-only gaps +and are not parser failures. From 7e7723e0db87793d4cb29d7064bc38c65023fb10 Mon Sep 17 00:00:00 2001 From: Teakowa Date: Wed, 2 Sep 2026 10:35:36 +0800 Subject: [PATCH 2/3] fix(opy): preserve lambda diagnostic contract Keep the stable lambda-context diagnostic while normalizing only the conformance frontier to the pinned parse-error construct. --- compatibility/compiler-expectations.json | 4 ++-- compatibility/conformance.py | 5 ++++- compatibility/tests/test_conformance.py | 13 +++++++++++++ .../opy-rs/src/compiler/tests/issue_93_frontend.rs | 2 +- crates/opy-rs/src/lower.rs | 4 ++-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/compatibility/compiler-expectations.json b/compatibility/compiler-expectations.json index 566c3e9..be9ef4e 100644 --- a/compatibility/compiler-expectations.json +++ b/compatibility/compiler-expectations.json @@ -822,9 +822,9 @@ "test:opy-rs::compiler::integration_tests::issue_93_frontend::frontend_diagnostics_are_public_and_source_attributed" ], "owner": "opy-rs#93", - "note": "The public compiler reports standalone lambda use at the source-attributed parse frontier.", + "note": "The public compiler reports standalone lambda use as a source-attributed lambda-context diagnostic.", "failureClass": "frontend", - "diagnosticCode": "parse-error" + "diagnosticCode": "lambda-context" }, { "fixture": "synthetic/preprocessing", diff --git a/compatibility/conformance.py b/compatibility/conformance.py index 2b12750..9f2ff46 100644 --- a/compatibility/conformance.py +++ b/compatibility/conformance.py @@ -27,6 +27,9 @@ "w_already_imported": "preprocess", "do-while-placement": "semantic", } +FRONTIER_CONSTRUCTS = { + "lambda-context": "parse-error", +} PROBE_KINDS = {"positive", "negative", "contextual", "composition"} @@ -220,7 +223,7 @@ def native_frontier(result: dict[str, Any]) -> dict[str, str] | None: if stage is None: failure_class = compile_result.get("failureClass") stage = "lowering" if failure_class == "integration" else "semantic" - return {"stage": stage, "construct": code} + return {"stage": stage, "construct": FRONTIER_CONSTRUCTS.get(code, code)} def run_compile(binary: Path, directory: Path, metadata: dict[str, Any]) -> dict[str, Any]: diff --git a/compatibility/tests/test_conformance.py b/compatibility/tests/test_conformance.py index e5eb861..26a0a14 100644 --- a/compatibility/tests/test_conformance.py +++ b/compatibility/tests/test_conformance.py @@ -71,6 +71,19 @@ def test_native_frontier_skips_warnings_before_the_first_error(self): {"stage": "parse", "construct": "parse-error"}, ) + def test_native_frontier_normalizes_lambda_context_to_parse_frontier(self): + result = { + "compile": { + "status": "failure", + "failureClass": "frontend", + "diagnostics": [{"code": "lambda-context"}], + } + } + self.assertEqual( + conformance.native_frontier(result), + {"stage": "parse", "construct": "parse-error"}, + ) + def test_reference_success_native_failure_is_divergence(self): oracle = { "compile": {"status": "success", "diagnostics": []}, diff --git a/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs b/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs index 8494567..fceeb22 100644 --- a/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs +++ b/crates/opy-rs/src/compiler/tests/issue_93_frontend.rs @@ -24,7 +24,7 @@ fn frontend_diagnostics_are_public_and_source_attributed() { ( "issue-33-lambda-negative.opy", "globalvar value\n\nrule \"issue 33 lambda negative\":\n @Event global\n debug(lambda item: item)\n", - "parse-error", + "lambda-context", ), ]; let compiler = Compiler::new().expect("released Workshop contract must load"); diff --git a/crates/opy-rs/src/lower.rs b/crates/opy-rs/src/lower.rs index bd19c99..fcd6aa1 100644 --- a/crates/opy-rs/src/lower.rs +++ b/crates/opy-rs/src/lower.rs @@ -1089,7 +1089,7 @@ impl Lowerer { Expr::Lambda { params, body, span } => { if position != CallPosition::LambdaArgument { self.error_at( - "parse-error", + "lambda-context", "lambda expressions are only valid as array operation arguments" .to_string(), *span, @@ -3423,7 +3423,7 @@ mod tests { "globalvar x\nrule \"r\":\n @Event global\n x = lambda value: value\n", 4, ); - assert_eq!(lambda_error.code, "parse-error"); + assert_eq!(lambda_error.code, "lambda-context"); } #[test] From 680b9bd3e037104e8aaa0e588190389513f64c00 Mon Sep 17 00:00:00 2001 From: Teakowa Date: Wed, 2 Sep 2026 11:01:26 +0800 Subject: [PATCH 3/3] fix(opy): keep lambda differential code stable Preserve lambda-context in the native diagnostic expectation while the conformance runner compares its canonical parse frontier. --- crates/opy-rs/tests/differential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/opy-rs/tests/differential.rs b/crates/opy-rs/tests/differential.rs index ffd88ee..f849509 100644 --- a/crates/opy-rs/tests/differential.rs +++ b/crates/opy-rs/tests/differential.rs @@ -322,7 +322,7 @@ fn declared_corpus() -> BTreeMap<&'static str, Case> { diagnostic( &mut cases, "synthetic/issue-33-lambda-negative", - Some("parse-error"), + Some("lambda-context"), "Issue #33 standalone lambda use remains rejected outside a signature-approved argument position.", ); diagnostic(