Skip to content

Commit 3f39c3f

Browse files
hyperpolymathclaude
andcommitted
feat(grammar): self expression + inherent impl blocks (Refs #122)
Two pre-existing AffineScript grammar gaps blocked idiomatic OO and made even stdlib/traits.affine unparseable: 1. SELF_KW had no expression production. The receiver-param productions already bind a parameter named "self", but the body could never reference it — `self.field` / `self.method(x)` was always a parse error. Add `| SELF_KW { ExprVar (mk_ident "self" ...) }` to expr_primary; it resolves/typechecks as the ordinary "self" parameter binding (no resolver/typechecker change needed). 2. Inherent `impl Type { ... }` was a parse error at the `{`: the rule pre-committed to `impl_trait_ref? self_ty` where both alternatives start with an ident, an unresolved ambiguity. Restructure to parse one type_expr unconditionally, then an optional `FOR type_expr`; when `FOR` is present the leading type was the trait (trait_ref_of_type_expr recovers its name/args). Conflict-free; trait impls (`impl Trait for Type`) still parse identically. Verified: `impl Counter { fn bumped(ref self, by: Int) -> Int { self.start + by } }` compiles; trait-impl + receiver-first probes still compile; `dune runtest` unchanged (same 2 pre-existing E2E Node-CJS vscode failures, no new regressions); Deno-ESM suite green. Residual (separate, tracked): `Self` as a *type annotation* in trait default methods (stdlib/traits.affine:12 `ref other: Self`) — a narrow grammar/typecheck item, distinct from `self`-the-expression. Refs #122. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 76bafec commit 3f39c3f

1 file changed

Lines changed: 30 additions & 7 deletions

File tree

lib/parser.mly

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ let mk_span startpos endpos =
2020
let mk_ident name startpos endpos =
2121
{ name; span = mk_span startpos endpos }
2222

23+
(* issue #122 v2: inherent-impl support. The old grammar pre-committed to
24+
`impl_trait_ref? self_ty` where both alternatives start with an ident,
25+
so `impl Counter {` (inherent) was a parse error at `{`. The rule now
26+
parses one type_expr unconditionally, then an optional `FOR type_expr`;
27+
when the `FOR` is present the leading type was the trait. This recovers
28+
the trait name/args from that leading type_expr. *)
29+
let trait_ref_of_type_expr (t : type_expr) : trait_ref =
30+
match t with
31+
| TyCon id | TyVar id -> { tr_name = id; tr_args = [] }
32+
| TyApp (id, args) -> { tr_name = id; tr_args = args }
33+
| _ -> failwith "impl: trait reference must be a named type"
34+
2335
%}
2436

2537
/* Tokens with values */
@@ -535,20 +547,23 @@ fn_sig:
535547

536548
impl_block:
537549
| IMPL type_params = type_params?
538-
trait_ref = impl_trait_ref?
539-
self_ty = type_expr
550+
head = type_expr
551+
forspec = impl_for?
540552
where_clause = where_clause?
541553
LBRACE items = list(impl_item) RBRACE
542-
{ { ib_type_params = Option.value type_params ~default:[];
554+
{ let trait_ref, self_ty =
555+
match forspec with
556+
| Some st -> (Some (trait_ref_of_type_expr head), st)
557+
| None -> (None, head)
558+
in
559+
{ ib_type_params = Option.value type_params ~default:[];
543560
ib_trait_ref = trait_ref;
544561
ib_self_ty = self_ty;
545562
ib_where = Option.value where_clause ~default:[];
546563
ib_items = items } }
547564

548-
impl_trait_ref:
549-
| name = ident FOR { { tr_name = name; tr_args = [] } }
550-
| name = ident LBRACKET args = separated_list(COMMA, type_arg) RBRACKET FOR
551-
{ { tr_name = name; tr_args = args } }
565+
impl_for:
566+
| FOR self_ty = type_expr { self_ty }
552567

553568
impl_item:
554569
| f = fn_decl { ImplFn f }
@@ -646,6 +661,14 @@ expr_primary:
646661
| FALSE { ExprLit (LitBool (false, mk_span $startpos $endpos)) }
647662

648663
/* Identifiers */
664+
/* `self` in expression position (issue #122 v2). The method-receiver
665+
param productions already bind a parameter named "self"; this lets
666+
the body actually reference it (`self.field`, `self.method(x)`).
667+
Without this production SELF_KW had no expression form, so every
668+
method body referencing self was a parse error — even
669+
stdlib/traits.affine failed to parse. Resolves/typechecks as an
670+
ordinary parameter binding named "self". */
671+
| SELF_KW { ExprVar (mk_ident "self" $startpos $endpos) }
649672
| name = lower_ident { ExprVar (mk_ident name $startpos $endpos) }
650673
/* Struct literal: `Point { x: v, y: w }`. Must come before the plain
651674
upper_ident production so Menhir shifts LBRACE rather than reducing

0 commit comments

Comments
 (0)