Skip to content

experiment: public (…) parenthetical for (e.g.) encoder/decoder codecs - #6360

Draft
ggreif wants to merge 4 commits into
masterfrom
gabor/vis-experiment
Draft

ggreif wants to merge 4 commits into
masterfrom
gabor/vis-experiment

Conversation

@ggreif

@ggreif ggreif commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Experiment — not for merge. Explores placing the codec parenthetical after public rather than before it:

public (with encoder = toJsonBlob; decoder = func () ...) func foo() : async T { … }
public (with encoder) func bar() : async T { … }   // punned to sibling `encoder`/`decoder`

The grammar insight

Making the visibility keyword own the continuation keeps the grammar conflict-free. A public member is restricted to a manifest func/let/type (the new dec_pub nonterminal) — never a bare expression — so it cannot begin with LPAR. That makes public ( unambiguously the codec parenthetical, with no bare-LPAR declaration to shift/reduce against:

dec_field_ac:
  | PUBLIC parenthetical? s=stab d=dec_pub

Two commits:

  • public (…) func via the vis-owned continuation (dec_pub = func only);
  • extend dec_pub to {func, let, type}, still conflict-free.

Currently wired into the mixin body (obj_body_ac). Rebased onto current master; printers.ml gains symbol cases for the new nonterminals so the release build stays warning-clean. moc --check accepts both the explicit and punned forms.

Prior art on the sibling branch gabor/encoder places the same parenthetical before public; this branch is the after-public variant.

ggreif and others added 2 commits September 9, 2026 17:25
…tinuation

Grammar sketch (NOT for merge) exploring how to attach an encoder
parenthetical `(base with encoder = …)` to a public actor member without
the shift/reduce conflict the naive `PUBLIC LPAR exp RPAR` form triggers
(state 354/433: `public (` is ambiguous between the encoder and a bare
parenthesized-expression field, since `dec` can begin with LPAR).

Idea (per Gabor): `vis` owns its continuation — `dec_field(ac|lo)`:
  - `lo` (module/object): vis + general `dec` (unchanged);
  - `ac` (actor/actor-class/mixin): public = `PUBLIC parenthetical? <func>`,
    where the continuation is func-only (`dec_func`).
Because the `ac` public continuation starts with `shared`/`func` (never
`(`), there is no bare-LPAR dec to reduce into, so `public (` is
unambiguously the encoder. Reuses the existing `parenthetical` (which
requires `with`). menhir --strict reports no shift/reduce conflict, and
it enforces M0125 (public actor field = manifest function) at the grammar
level.

Wired only the `mixin` body to `obj_body_ac` as the demo `ac` site; a real
implementation would also route actor blocks + actor classes, and thread
the encoder value into the AST. Actions ignore the encoder value for now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…flict-free

Refines the earlier func-only sketch. The actor/mixin public continuation
must admit `public func`, `public let = func`, and `public type` (all legal
public members; the first two marshal into the service, `public let=func`
being the desugaring of `public func`). A bare-expression public field
(`public 5`, `public (1,2)`) is dropped -> now a parse error instead of
M0125 (grammar-as-spec).

Tried the natural `dec` - `exp_nondec` factoring first, but `dec_nonvar`'s
`parenthetical_opt obj_or_class_dec` (the `(base with …) obj/class`
extension) is also LPAR-leading and re-introduced the shift/reduce. So the
continuation is the explicit `dec_pub = { let, type, func }`, none of which
begin with LPAR -> `public (` is unambiguously the encoder parenthetical.

menhir --strict: no shift/reduce conflict.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ggreif ggreif changed the title experiment: public (…) parenthetical for encoder/decoder codecs experiment: public (…) parenthetical for (e.g.) encoder/decoder codecs Sep 9, 2026
`make -C src grammar` after adding `dec_pub`/`dec_field_ac`/`obj_body_ac`
and rewiring the `mixin` body to `obj_body_ac`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Comparing from 23a82b4 to 1b347df:
The produced WebAssembly code seems to be completely unchanged.
In terms of gas, no changes are observed in 5 tests.
In terms of size, no changes are observed in 5 tests.

…egion

Routing the `mixin` body through `dec_field_ac` makes the inlined `system`
visibility's `$sloc` span the whole member, so the M0130 region widens from
`4.3-4.9` to `4.3-4.30` (same error). Regenerate both `.tc` and `.tc-human`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ggreif

ggreif commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Note: widened M0130 region on system in a mixin

Routing the mixin body through dec_field_ac inlines the visibility keyword into the field rule, so the system/public arms compute their region from the whole-rule $sloc rather than a token-scoped span. As a result the M0130 "misplaced system visibility" diagnostic now underlines the entire offending member instead of just the system keyword:

- mixins/Mixin1.mo:4.3-4.9:  type error [M0130], misplaced system visibility, did you mean private?
+ mixins/Mixin1.mo:4.3-4.30: type error [M0130], misplaced system visibility, did you mean private?

Same error, wider underline — this is why the mixins-system fail golden (both .tc and .tc-human) was regenerated in 1b347dfb2e.

If we'd rather keep the tight region pointing at just system, give the system/public arms of dec_field_ac a token-scoped location ($loc(...) / $startpos(...)..$endpos(...) on the visibility token) instead of the whole-rule $sloc; that also reverts the golden. Left as the wider span for now since it's an experiment.

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.

1 participant