Skip to content

feat(opy-compiler): lower non-control-flow primitives to canonical WIR - #54

Merged
Teakowa merged 6 commits into
mainfrom
feat/issue-46-primitives
Aug 24, 2026
Merged

feat(opy-compiler): lower non-control-flow primitives to canonical WIR#54
Teakowa merged 6 commits into
mainfrom
feat/issue-46-primitives

Conversation

@Teakowa

@Teakowa Teakowa commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes Issue #46 in the existing PR with workshop-rs v0.1.8 as the active dependency baseline.

  • Keeps the workspace and opy-compiler workshop-rs pin at =0.1.8.
  • Lowers value-position a ** b through canonical raiseToPower and preserves direct a **= b plus indexed a[i] **= b through canonical Workshop Operation contracts.
  • Implements OverPy implicit A-DX defaults in separate global and player namespaces, with fixed slots, independent reservation, explicit-index collision diagnostics, HIR validation, and declared-player coexistence.
  • Normalizes OPY hex literals at the OPY-to-WIR boundary (0x124BC to 74940 and 0x124 to 292) while preserving valid non-hex spellings.
  • Removes non-authoritative floor-division and operator aliases from frontend acceptance and compiler HIR compatibility; /, %, **, and, or, and not remain supported.
  • Updates the real OPY fixture, pinned OverPy oracle snapshot, support matrix, and source-attributed negative tests. No Workshop catalog or Raise To Power semantics are duplicated in opy-rs.

The real fixture exercises:

out = a ** 2
a **= 2
a[0] **= 2
eventPlayer.A = 1
eventPlayer.A += 2
eventPlayer.E = eventPlayer.A

The change remains limited to #46; control flow (#47) and catalog/member closure (#42) are not included.

Verification

  • cargo fmt --all -- --check
  • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
  • cargo test --locked --workspace --all-targets --all-features
  • python3 -m unittest discover -s compatibility/tests
  • python3 compatibility/run_oracle.py — all 44 pinned OverPy fixtures pass
  • issue_46_oracle — native and pinned Workshop output reparse through workshop-rs and compare with roundtrip::equivalent, not exact output-text identity
  • Audited all six end-to-end-supported matrix claims for executable source fixtures and test evidence
  • Hosted CI run 32691158303 — Rust stable, Rust 1.85, macOS, and Windows all green

Follow-up tracking for review findings intentionally outside #46: opy-rs #58 null initializers, #59 ++/-- assignments, #60 multidimensional indexed assignment, and wrightkit/workshop-rs #95 canonical min/max operations.

Fixes #46

Teakowa added a commit that referenced this pull request Aug 23, 2026
- Support OverPy implicit default global variables A-DX through the
  compiler at their fixed Workshop slots via the frontend
  default_var_index contract; used implicit slots are reserved for
  declared-variable allocation, explicit-index collisions fail with the
  source-attributed index-collision diagnostic, and variable tables are
  created in index order matching the pinned reference.
- Implement the missing **= lexer/parser path (pinned OverPy 9.7.10
  evidence); value-position power and indexed power modification now
  fail with stable source-attributed diagnostics at the pinned
  workshop-rs 0.1.5 emitter boundary instead of span-less emission
  errors.
- Constrain the native compiler with the #46 oracle through the existing
  workshop-rs parser + roundtrip::equivalent machinery (both sides
  parsed as canonical WIR), replacing parallel manual WIR assertions.
- Mirror the pinned oracle's observable lowering for not-on-comparison
  negation and literal zero-index reads (firstOf).
- Fix the player indexed-assignment canonical signature (the
  player-variable value node is the first argument) and make
  auto-allocation fill free slots below early explicit indices.
- Add the synthetic/issue-46-unsupported negative fixture with a stable
  source-attributed diagnostic, and correct the support-matrix span
  wording for indexed assignments.
Lower OPY global/player assignments, augmented assignments, array indexing assignments, value expressions, format calls, pass statements, and declaration initializers into canonical workshop-rs WIR with source and target span provenance.

Closes #46
- Support OverPy implicit default global variables A-DX through the
  compiler at their fixed Workshop slots via the frontend
  default_var_index contract; used implicit slots are reserved for
  declared-variable allocation, explicit-index collisions fail with the
  source-attributed index-collision diagnostic, and variable tables are
  created in index order matching the pinned reference.
- Implement the missing **= lexer/parser path (pinned OverPy 9.7.10
  evidence); value-position power and indexed power modification now
  fail with stable source-attributed diagnostics at the pinned
  workshop-rs 0.1.5 emitter boundary instead of span-less emission
  errors.
- Constrain the native compiler with the #46 oracle through the existing
  workshop-rs parser + roundtrip::equivalent machinery (both sides
  parsed as canonical WIR), replacing parallel manual WIR assertions.
- Mirror the pinned oracle's observable lowering for not-on-comparison
  negation and literal zero-index reads (firstOf).
- Fix the player indexed-assignment canonical signature (the
  player-variable value node is the first argument) and make
  auto-allocation fill free slots below early explicit indices.
- Add the synthetic/issue-46-unsupported negative fixture with a stable
  source-attributed diagnostic, and correct the support-matrix span
  wording for indexed assignments.
Lower value-position and indexed power operations through the canonical raiseToPower contracts, refresh the #46 oracle fixture, and update support claims.

Fixes #46
@Teakowa
Teakowa force-pushed the feat/issue-46-primitives branch from 3f40ab5 to 4bf9d52 Compare August 23, 2026 18:16

@Teakowa Teakowa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: changes required

The workshop-rs 0.1.8 rebaseline, canonical Raise To Power closure, source-level **= path, oracle-constrained native-vs-reference WIR comparison, provenance handling, and current CI are all directionally correct. I reviewed the remaining #46 surface as well rather than stopping at the power fix. The PR still has three correctness blockers before it can close #46.

Blockers

  1. Implicit default player variables (eventPlayer.AeventPlayer.DX) are still missing in opy-compiler.

    The pinned OverPy reference uses the same defaultVarNames for global and player variables. Its own variables.opy test uses undeclared eventPlayer.A / eventPlayer.E, and the expected Workshop table assigns those player variables the same fixed slots (A=0, E=4) independently of the global table.

    The native frontend already lowers any eventPlayer.<member> to HirExpr::PlayerVar, but this PR only collects/reserves implicit_default_globals; player allocation is built solely from explicit playervar declarations and lower_value/assignment require self.players.get(name). Valid OPY such as eventPlayer.A = 1 therefore resolves in the frontend and then fails as unknown player variable in the compiler.

    Add the player-side equivalent of implicit default-variable discovery/allocation/collision handling, preserving fixed slots independently from globals. The oracle evidence should cover reads, writes, augmented assignment, and coexistence with explicit/auto-indexed playervar declarations.

  2. Hexadecimal OPY numeric literals leak their OPY source spelling into canonical Workshop emission.

    The frontend intentionally preserves numeric source spelling in HIR. opy-compiler currently copies that text directly into wir::Value::Number, and the workshop-rs emitter prints the stored text verbatim. The pinned OverPy variables.opy corpus demonstrates that 0x124BC / 0x124 are emitted as decimal Workshop numbers (74940 / 292), while scientific spelling such as 1e10 is retained.

    This means the newly claimed E2E numeric-literal surface is not correct for an already-supported OPY literal form. Normalize OPY-only numeric spellings at the OPY→WIR boundary to a Workshop-compatible/reference-equivalent spelling while preserving spellings that are valid and observable (0.0, 1e10, etc.). Add an oracle-backed hex case.

  3. The compiler now promotes non-OverPy // / //= syntax into a WrightKit-only dialect by treating it as Workshop Divide.

    The pinned OverPy 9.7.10 operator table contains /, /=, %, %=, **, **=, min=, and max=, but no // or //=. The native frontend currently lexes/parses // and //=, and this PR newly maps those HIR operators to canonical divide / ModifyOp::Divide. That changes a frontend divergence into successful end-to-end compilation of syntax the reference language does not support.

    Do not accept this as an alias. Remove the compiler normalization and make the unsupported source form fail explicitly; ideally correct the frontend surface as part of the same compatibility repair or track that frontend correction explicitly. Likewise, avoid speculative backend aliases such as ^, &&, ||, or ! unless they are part of a documented HIR contract backed by source-language evidence.

Major findings

  1. = null variable initializers do not match the pinned initializer contract. OverPy treats a single literal null or source text 0 initializer as the default variable state and emits no initializer action. This PR only suppresses textual numeric 0; null synthesizes an unnecessary initialize rule/action. Treat Expr::Null as the same default/no-op case and add evidence. Keep 0.0 distinct: the reference checks source text exactly and does emit it.

  2. Do not interpret #46 closure as full OverPy mutation-syntax closure yet. The pinned operator corpus also includes min=, max=, A++, and A--. The current frontend does not expose those forms through this path, and workshop-rs 0.1.8's typed ModifyOp currently has no Min/Max variants. This should be explicitly routed rather than silently lost: identify the Workshop-owned Min/Max WIR gap in workshop-rs, and either keep the affected OPY forms as an explicit tracked gap or narrow the #46/roadmap claim. Do not duplicate the Workshop operation semantics in opy-rs.

  3. The indexed-assignment claim is broader than the executable surface. The compiler accepts an index whose immediate base is a global/player variable, while upstream assignment normalization supports deeper 2D/3D indexed writes. Either evidence and implement the intended nested surface in its owning follow-up, or describe #46 as single-level indexed variable assignment/modification rather than generic “array index assignments and modifications.”

Minor

  1. compatibility/support-matrix.json still carries the old snapshot text (42-fixture, 35 matches, old asOfCommit) while this PR updates the corpus/docs to 44 fixtures and adds new E2E rows. Since the matrix is the mechanically checked state source, refresh that snapshot metadata so it does not contradict the corpus it describes.

Verified good

  • exact workshop-rs = "=0.1.8" dependency baseline;
  • value-position **, direct **=, and indexed **= use the canonical Workshop-owned contracts;
  • the #46 oracle test now genuinely constrains native compiler output by reparsing both native and pinned-oracle Workshop through workshop-rs and comparing canonical WIR equivalence;
  • direct target-span vs indexed statement-span claims are now accurate;
  • unsupported primitive lowering has a source-attributed negative fixture;
  • pass is handled explicitly as a source-level no-op rather than silently swallowing arbitrary HIR;
  • no temporary Workshop IR or copied Workshop catalog semantics were introduced;
  • #47 control flow and #42 receiver/member breadth remain outside this PR;
  • CI run #112 is green on Rust 1.85/stable and the macOS/Windows JS runtime jobs.

After the three blockers and the claim/contract issues above are resolved, #54 should be suitable to close #46 and unblock #47.

@Teakowa

Teakowa commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up tracking outside #46: #58 null-initializer defaults; #59 ++/-- assignments; #60 multidimensional indexed-assignment lowering; wrightkit/workshop-rs#95 canonical min/max modification operations. PR #54 remains scoped to the evidenced single-level primitive and power lowering contract.

@Teakowa Teakowa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review verdict: implementation approved; metadata cleanup required before merge

The three prior correctness blockers are resolved on 8bf843f:

  • implicit eventPlayer.ADX variables now use an independent player namespace with fixed slots, allocation reservation, HIR validation, and collision diagnostics; the pinned oracle fixture covers A, E, and DX alongside declared player variables;
  • OPY hex literals are normalized at the OPY→WIR boundary (0x124BC74940, 0x124292) while ordinary numeric spellings remain preserved, and the same oracle-equivalence fixture constrains the result;
  • // / //= and speculative ^, &&, ||, ! aliases no longer become a WrightKit-only compilable dialect; source-level tests require them to fail at the frontend boundary.

workshop-rs = "=0.1.8", power value/direct/indexed modification lowering, canonical WIR equivalence, provenance, and negative diagnostics remain correct. Hosted CI #113 is green on Rust stable/1.85 and the macOS/Windows JS-runtime jobs.

I found no new lowering/correctness blocker. Before merge, please make the authoritative claims match the intentionally deferred follow-ups:

  1. Narrow compilation/opy-assignment-lowering wording to the executable #46 surface. It currently says generic “array index assignments and modifications” although #60 explicitly tracks deeper/multidimensional writes; call this single-level indexed variable assignment/modification. Likewise, either remove the broad “variable declaration initializers” claim or explicitly exclude the null default-initializer quirk tracked by #58.
  2. Reconcile syntax/support wording. docs/opy/support-matrix.md now lists the supported operator surface but omits the newly supported **=. The machine row syntax/assignments-control-flow still says generic “augmented ops”; narrow it to the evidenced subset (+= -= *= /= %= **=) or explicitly link the deferred min=/max= and ++/-- gaps.
  3. Reconcile issue/roadmap scope before closing #46. #46 still says “Complete global/player assignment and modification lowering”, and parent #41 says assignment/modification/indexing completes through #46, while #58/#59/#60 and workshop-rs#95 are intentionally non-gating follow-ups. Update #46/#41 to record those explicit non-goals/follow-ups so closing #46 does not contradict the authoritative roadmap.

The follow-up ownership is otherwise sensible: #58/#59/#60 belong to opy-rs; workshop-rs#95 correctly owns canonical Min/Max operations. For the cross-repo Min/Max path, link workshop-rs#95 to the existing opy-rs consumer stage (#42) or another already-authoritative consumer issue rather than leaving the integration step implicit.

After this small metadata/roadmap cleanup, I consider PR #54 ready to merge and close #46.

@Teakowa

Teakowa commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the re-review metadata/roadmap findings in 3d23ee9: narrowed #46 support claims to single-level indexed forms and non-null initializers, added **= to the support wording, narrowed the augmented-assignment row, updated #46/#41 non-goals and follow-ups, and linked workshop-rs#95 to opy-rs#42. Re-ran the full local evidence suite and hosted CI run 32692643686; all checks are green.

@Teakowa Teakowa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final review: merge-ready

Re-reviewed the full PR at 3d23ee9, including the previous correctness blockers, the follow-up routing, support claims, provenance, and the latest CI.

The prior merge blockers remain closed: implicit player A–DX fixed-slot semantics are oracle-backed, OPY hex literals normalize at the OPY→WIR boundary, non-OverPy operator aliases are rejected at the frontend boundary, and canonical power lowering remains on workshop-rs 0.1.8.

The final scope/metadata cleanup is also correct:

  • #46 now explicitly scopes the evidenced augmented-operator subset, single-level indexed forms, and non-null initializer evidence, with #58/#59/#60/workshop-rs#95 non-gating.
  • Parent #41 mirrors that boundary rather than treating #46 as complete mutation-syntax parity.
  • workshop-rs#95 now identifies opy-rs#42 as the downstream consumer path.
  • support-matrix and compatibility docs no longer claim //, ++/--, min=/max=, multidimensional indexed writes, or null-default initialization as completed #46 behavior; **= is explicitly listed.
  • Latest CI run #114 (32692643686) is fully green on Rust stable, Rust 1.85.0, macOS, and Windows.

No remaining correctness, architecture, ownership, provenance, test, or support-claim blocker found within #46 scope. The human compatibility baseline still summarizes declaration initializer support broadly in its category row, but the detailed support docs and authoritative matrix explicitly route the null-default quirk to #58; I do not consider that a merge blocker.

Verdict: merge-ready. PR #54 can merge and close #46; proceed to #47 afterward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lower OPY assignment and expression primitives into canonical Workshop WIR

1 participant