Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compatibility/differential-expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
{"fixture": "synthetic/preprocessing", "nativeStatus": "success", "classification": "match", "ruleNames": false, "evidence": ["oracle:synthetic/preprocessing/oracle.json", "implementation-invariant:preprocessor-include-define-undef"], "note": "Include, object-like and function-like defines, and undef are preserved through preprocessing."},
{"fixture": "synthetic/settings", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/settings/oracle.json", "implementation-invariant:settings-structure-only"], "note": "Settings are structurally represented; Workshop key and leaf validation remains lowering-dependent."},
{"fixture": "synthetic/receiver-calls", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/receiver-calls/oracle.json", "implementation-invariant:receiver-call-resolution"], "note": "The exercised receiver/member forms resolve through the OPY semantic model."},
{"fixture": "synthetic/receiver-playervar", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/receiver-playervar/oracle.json", "implementation-invariant:receiver-playervar-member-expression"], "note": "A bare variable member expression is retained as an OPY HIR member node; canonical Workshop member validation remains lowering-dependent."},
{"fixture": "synthetic/chase-enums", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/chase-enums/oracle.json", "implementation-invariant:opaque-workshop-enum-identities"], "note": "Declared chase enum identities resolve without duplicating Workshop catalog validation."},
{"fixture": "synthetic/chase-condition-agentlab", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/chase-condition-agentlab/oracle.json", "regression:agent-lab-chase-condition-shape"], "note": "The agent-lab chase condition shape is retained as an OPY frontend regression."},
{"fixture": "synthetic/chase-keywords", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/chase-keywords/oracle.json", "implementation-invariant:keyword-binding"], "note": "Generic keyword binding and the chase contextual form resolve."},
Expand Down
20 changes: 20 additions & 0 deletions compatibility/fixtures/synthetic/receiver-playervar/fixture.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"category": "synthetic",
"expectedStatus": "success",
"features": [
"semantics/receiver-playervar"
],
"files": {
"source.opy": "c8040ee474759c5b27541b2b9a514d350f605f471bc23246d1baeadc37a5eadc"
},
"id": "synthetic/receiver-playervar",
"provenance": {
"kind": "original",
"license": "AGPL-3.0-or-later",
"origin": "Wright compatibility corpus; minimized source-language regression for the pinned OverPy receiver-member behavior.",
"redistributable": true
},
"provenanceNote": "Synthetic-original fixture authored from the pinned oracle observation that a bare variable member expression is accepted. It does not reproduce Workshop catalog data; member existence remains an integration concern.",
"schemaVersion": 1,
"source": "source.opy"
}
27 changes: 27 additions & 0 deletions compatibility/fixtures/synthetic/receiver-playervar/oracle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compile": {
"diagnostics": [],
"exitCode": 0,
"status": "success",
"stdout": "\u2713 Lockfile passes supply-chain policies (verified 1d ago)\nLockfile is up to date, resolution step is skipped\nProgress: resolved 1, reused 0, downloaded 0, added 0\nPackages: +1\n+\nProgress: resolved 1, reused 1, downloaded 0, added 1, done\n\ndependencies:\n+ overpy 9.7.10\n\nDone in 334ms using pnpm v11.10.0\n",
"workshop": "variables {\n global:\n 0: A\n 1: B\n player:\n 0: B\n 2: C\n}\n\nrule (\"receiver\") {\n event {\n Ongoing - Each Player;\n All;\n All;\n }\n actions {\n Set Global Variable(A, (Global.B).C);\n }\n}\n",
"workshopExact": "variables {\n global:\n 0: A\n 1: B\n player:\n 0: B\n 2: C\n}\n\nrule (\"receiver\") {\n event {\n Ongoing - Each Player;\n All;\n All;\n }\n actions {\n Set Global Variable(A, (Global.B).C);\n }\n}\n\n",
"workshopSha256": "8273c1fcb0e1e6c2f2bece1cc5f4730d1f93e62d65a629cfb8c6f0f06382fb6c"
},
"fixture": "synthetic/receiver-playervar",
"input": {
"sha256": "c8040ee474759c5b27541b2b9a514d350f605f471bc23246d1baeadc37a5eadc",
"source": "source.opy"
},
"oracle": {
"gitHead": "1e2688954302a402d076944b46db07efb14d7b61",
"integrity": "sha512-oX17nauJcPTaKIrRFY/rD0Rl8atqFUVv9Hg2TKH+A68/fC8+ZO344Mkd1A/Y0oOVp1hr5tktMBjzMEDDnMEYUw==",
"language": "en-US",
"license": "GPL-3.0-only",
"name": "overpy",
"registryTarball": "https://registry.npmjs.org/overpy/-/overpy-9.7.10.tgz",
"repository": "https://github.com/Zezombye/overpy",
"version": "9.7.10"
},
"schemaVersion": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
globalvar A
playervar B
rule "receiver":
@Event eachPlayer
A = B.C
73 changes: 59 additions & 14 deletions compatibility/support-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"snapshot": {
"date": "2026-08-16",
"note": "Merged-main baseline (PRs #9-#14; issues #2-#6 delivered, #7 partially delivered). Frontend (PR #9), evidence base (PR #10), JS runtime (PR #11), tooling API/CLI (PR #12), differential suite (PR #13), and cross-platform runtime CI (PR #14) are merged on main. Frontend-supported rows: declared syntax surface, settings blocks, structured diagnostics, preprocessing (include/define/undef), macro statements, rule directives/model, JavaScript macros, and runtime hooks. Semantic-supported rows: declaration resolution, for-loop binders, modules, keyword arguments, and the declared alias surface. Remaining planned rows: full builtin/enum/receiver surfaces beyond the manifest-declared evidence, advanced directives, translations, and optimization controls. Workshop-dependent features are lowering-dependent and inventory-only until the workshop-rs integration stage (#8). #!postCompileHook is parsed/validated/recorded by the frontend; execution against the final Workshop text is lowering-dependent (#8).",
"note": "Merged-main baseline (PRs #9-#14; issues #2-#6 delivered, #7 partially delivered). Frontend (PR #9), evidence base (PR #10), JS runtime (PR #11), tooling API/CLI (PR #12), differential suite (PR #13), and cross-platform runtime CI (PR #14) are merged on main. Frontend-supported rows: declared syntax surface, settings blocks, structured diagnostics, preprocessing (include/define/undef), macro statements, rule directives/model, JavaScript macros, and runtime hooks. Semantic-supported rows: declaration resolution, for-loop binders, modules, keyword arguments, the declared alias surface, and the OPY-owned manifest overlay for builtin/member/enum semantics. After #30, canonical Workshop builtin/member/enum breadth is represented by separate lowering-dependent rows; remaining planned rows are the pure syntax and directive/preprocessing work tracked by #28 and #29 plus the explicitly recorded bare playervar receiver residual. Workshop-dependent features are lowering-dependent and inventory-only until the workshop-rs integration stage (#8). #!postCompileHook is parsed/validated/recorded by the frontend; execution against the final Workshop text is lowering-dependent (#8).",
"asOfCommit": "cbb7242f4be36a803ebc0a4a3147a6574840f734"
},
"states": {
Expand Down Expand Up @@ -157,9 +157,9 @@
},
{
"id": "semantics/builtin-actions-values",
"name": "Builtin actions and values (manifest-declared surface; full 225 action / 267 value Workshop surface)",
"name": "OPY builtin actions and values: manifest identities, signatures, aliases, and call semantics",
"category": "semantics",
"state": "planned",
"state": "semantic-supported",
"evidence": [
"probes:generic-builtins",
"probes:chase-over-time",
Expand All @@ -173,13 +173,24 @@
"fixtures:synthetic/basic-rule",
"fixtures:synthetic/chase-condition-agentlab"
],
"notes": "Issue #4. Resolves through the opy-rs semantic compatibility manifest (crates/opy-frontend/src/manifest/, spec: docs/opy/compat-manifest-spec.md); every entry probe-validated against the pinned oracle. Full surface stays baseline-planned."
"notes": "Issue #30. The manifest-declared OPY overlay is semantic-supported: source identities, action/value position, signatures, keyword binding, aliases, and canonical catalogId links are probe-validated against the pinned oracle. Canonical Workshop existence/content/emission breadth is tracked separately as lowering-dependent."
},
{
"id": "semantics/workshop-builtin-catalog",
"name": "Canonical Workshop builtin action/value catalog existence, content, and emission",
"category": "semantics",
"state": "lowering-dependent",
"evidence": [
"probes:catalog-only-names",
"upstream:src/data/opy/functions.ts"
],
"notes": "Issue #30/#8. Canonical Workshop action/value catalog breadth, catalog existence, content domains, localized spellings, and emission are owned by workshop-rs. The integration adapter cross-checks manifest catalogId links; opy-rs does not copy the catalog or claim full 225/267 coverage."
},
{
"id": "semantics/receiver-members",
"name": "Receiver/member functions: receiver categories, explicit-argument signatures, variable receivers",
"name": "OPY receiver/member semantics: receiver categories and explicit-argument signatures",
"category": "semantics",
"state": "planned",
"state": "semantic-supported",
"evidence": [
"probes:receiver-calls",
"probes:enum-gated-members",
Expand All @@ -191,21 +202,55 @@
"fixtures:synthetic/receiver-calls",
"upstream:src/data/opy/memberFunctions.ts"
],
"notes": "Issue #4. Includes the bare-playervar-receiver residual item (A = B.C, baseline-planned). Workshop emission of member calls through the catalog is lowering-dependent."
"notes": "Issue #30. The manifest-declared OPY member overlay is semantic-supported: receiver syntax/category semantics, explicit-argument signatures, variable receiver rules, aliases, and catalogId links are probe-validated. Canonical member existence/content and Workshop emission remain lowering-dependent."
},
{
"id": "semantics/workshop-receiver-catalog",
"name": "Canonical Workshop receiver/member catalog existence, content, and emission",
"category": "semantics",
"state": "lowering-dependent",
"evidence": [
"probes:receiver-calls",
"upstream:src/data/opy/memberFunctions.ts"
],
"notes": "Issue #30/#8. Canonical receiver member lists, member existence, content-specific receiver/domain validation, localized spellings, and emission are Workshop-owned. The frontend preserves unknown/member diagnostics that can be decided from OPY metadata and defers catalog checks to integration."
},
{
"id": "semantics/receiver-playervar",
"name": "Bare playervar receiver member access (`A = B.C`)",
"category": "semantics",
"state": "semantic-supported",
"evidence": [
"fixtures:synthetic/receiver-playervar",
"upstream:src/tests/variables.opy",
"upstream:src/data/opy/memberFunctions.ts"
],
"notes": "Issue #30 repair. The pinned oracle accepts this OPY-owned receiver form and the frontend preserves its variable receiver and member identity in HIR with source provenance. Canonical member existence and emission remain Workshop-owned and lowering-dependent; #8 must not be used to claim the OPY expression incomplete."
},
{
"id": "semantics/enum-domains",
"name": "Builtin enum/constant domains and members (declared domains; 46 upstream domains incl. Hero/Map/Gamemode)",
"name": "OPY enum/domain semantics: declared domain identities and contextual dispatch",
"category": "semantics",
"state": "planned",
"state": "semantic-supported",
"evidence": [
"probes:unknown-enum",
"fixtures:synthetic/chase-enums",
"fixtures:synthetic/chase-keywords",
"upstream:src/tests/builtinenums.opy",
"upstream:src/data/opy/constants.ts"
],
"notes": "Issue #4. Workshop enum member/domain validation (member existence, domain mismatch) was removed from the core and is lowering-dependent (#8): member accesses on declared domain identities resolve as opaque identities, and the checks are never approximated. Full domain surface stays baseline-planned; emission spellings are lowering-dependent."
"notes": "Issue #30. Manifest parameter domains and contextual dispatch are semantic-supported OPY identity links with oracle/corpus evidence. Domain member lists, member existence, domain membership, localized spellings, and emission remain Workshop-owned and are never approximated in opy-rs."
},
{
"id": "semantics/workshop-enum-domains",
"name": "Canonical Workshop enum/domain member lists, membership, and emission",
"category": "semantics",
"state": "lowering-dependent",
"evidence": [
"probes:unknown-enum",
"upstream:src/data/opy/constants.ts"
],
"notes": "Issue #30/#8. Canonical enum domains and members (including Hero, Map, Gamemode, Team, and settings/content domains) are Workshop catalog data. opy-rs carries identity links only; workshop-rs performs member existence and domain compatibility checks at lowering."
},
{
"id": "semantics/aliases",
Expand Down Expand Up @@ -515,15 +560,15 @@
],
"summary": {
"byState": {
"planned": 8,
"planned": 5,
"frontend-supported": 14,
"semantic-supported": 5,
"lowering-dependent": 8,
"semantic-supported": 9,
"lowering-dependent": 11,
"end-to-end-supported": 0
},
"byCategory": {
"syntax": 7,
"semantics": 10,
"semantics": 14,
"preprocessing": 3,
"macros": 3,
"directives": 2,
Expand Down
2 changes: 1 addition & 1 deletion compatibility/tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_repository_fixture_metadata_and_snapshots_are_valid(self):
fixtures = run_oracle.discover_fixtures(
COMPATIBILITY_DIR / "fixtures"
)
self.assertEqual(len(fixtures), 27)
self.assertEqual(len(fixtures), 28)
for fixture_path, fixture in fixtures:
snapshot = fixture_path.parent / "oracle.json"
self.assertTrue(snapshot.is_file(), fixture["id"])
Expand Down
18 changes: 18 additions & 0 deletions compatibility/tests/test_support_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,24 @@ def test_summary_matches_features(self):
len(self.features),
)

def test_semantic_ownership_split_is_explicit(self):
by_id = {feature["id"]: feature for feature in self.features}
for overlay in (
"semantics/builtin-actions-values",
"semantics/receiver-members",
"semantics/enum-domains",
):
self.assertEqual(by_id[overlay]["state"], "semantic-supported")
for catalog in (
"semantics/workshop-builtin-catalog",
"semantics/workshop-receiver-catalog",
"semantics/workshop-enum-domains",
):
self.assertEqual(by_id[catalog]["state"], "lowering-dependent")
self.assertEqual(
by_id["semantics/receiver-playervar"]["state"], "semantic-supported"
)


if __name__ == "__main__":
unittest.main()
2 changes: 2 additions & 0 deletions crates/opy-frontend/src/cst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ pub enum Expr {
Member {
receiver: Box<Expr>,
member: String,
/// The exact span of the member identifier after `.`.
member_span: Span,
span: Span,
},
Index {
Expand Down
7 changes: 7 additions & 0 deletions crates/opy-frontend/src/hir/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,13 @@ fn render_expr(expr: &Expr, out: &mut String) {
out.push('.');
out.push_str(name);
}
Expr::Member {
receiver, member, ..
} => {
render_expr(receiver, out);
out.push('.');
out.push_str(member);
}
Expr::EventPlayer { .. } => out.push_str("eventPlayer"),
Expr::Constant { name, .. } => out.push_str(name),
Expr::Call { name, args, .. } => {
Expand Down
13 changes: 13 additions & 0 deletions crates/opy-frontend/src/hir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,17 @@ pub enum Expr {
#[serde(skip_serializing_if = "Option::is_none")]
span: Option<Span>,
},
/// An OPY member expression whose canonical Workshop meaning is deferred
/// to the integration catalog. The receiver and source member identity
/// remain available to tooling and lowering.
Member {
receiver: Box<Expr>,
member: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
member_span: Option<Span>,
#[serde(skip_serializing_if = "Option::is_none")]
span: Option<Span>,
},
EventPlayer {
#[serde(skip_serializing_if = "Option::is_none")]
span: Option<Span>,
Expand Down Expand Up @@ -486,6 +497,7 @@ impl Expr {
| Expr::Enum { span, .. }
| Expr::GlobalVar { span, .. }
| Expr::PlayerVar { span, .. }
| Expr::Member { span, .. }
| Expr::EventPlayer { span }
| Expr::Constant { span, .. }
| Expr::Call { span, .. }
Expand All @@ -511,6 +523,7 @@ impl Expr {
Expr::Enum { .. } => "enum",
Expr::GlobalVar { .. } => "globalVar",
Expr::PlayerVar { .. } => "playerVar",
Expr::Member { .. } => "member",
Expr::EventPlayer { .. } => "eventPlayer",
Expr::Constant { .. } => "constant",
Expr::Call { .. } => "call",
Expand Down
2 changes: 2 additions & 0 deletions crates/opy-frontend/src/hir/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const EXPR_KINDS: &[&str] = &[
"enum",
"globalVar",
"playerVar",
"member",
"eventPlayer",
"constant",
"call",
Expand Down Expand Up @@ -600,6 +601,7 @@ fn for_each_expr<'a>(expr: &'a Expr, f: &mut impl FnMut(&'a Expr)) {
for_each_expr(z, f);
}
Expr::PlayerVar { player, .. } => for_each_expr(player, f),
Expr::Member { receiver, .. } => for_each_expr(receiver, f),
Expr::Call { args, .. } | Expr::MacroCall { args, .. } | Expr::Format { args, .. } => {
for arg in args {
for_each_expr(arg, f);
Expand Down
Loading