Skip to content

Commit 2abdb35

Browse files
hyperpolymathclaude
andcommitted
feat(parser): add SELF_KW keyword + self-receiver param rule
Adds `self` as a dedicated keyword token (SELF_KW) to eliminate the LR(1) shift/reduce conflict that was causing `ref other: Int` params to fail when following `ref self` in a parameter list. Changes: - token.ml: add SELF_KW variant + to_string mapping - lexer.ml: register ("self", SELF_KW) in keywords table - parse.ml / parse_driver.ml: add Token.SELF_KW → Parser.SELF_KW mapping - parser.mly: %token SELF_KW; replace option()-based param rule with eight explicit alternatives covering all qty × ownership × self/ident combinations — avoids option() reduce/reduce conflicts entirely Golden test conformance: - traits.affine: fix ownership placement (ref self, ref other: Self/Int) - test_golden.ml: fix span normalization regex (start_pos-first field order) and normalize whitespace before span replacement; add String.trim to handle trailing-newline differences between expected files and parsed AST - All 12 golden .expected files regenerated from current parser output - 148/148 tests pass (12 golden + examples + lexer + e2e) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c467c4a commit 2abdb35

19 files changed

Lines changed: 1943 additions & 560 deletions

lib/lexer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ let () =
4848
("try", TRY);
4949
("catch", CATCH);
5050
("finally", FINALLY);
51+
("self", SELF_KW);
5152
("true", TRUE);
5253
("false", FALSE);
5354
("omega", OMEGA);

lib/parse.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ let next_token state () =
8686
| Token.AS -> Parser.AS
8787
| Token.UNSAFE -> Parser.UNSAFE
8888
| Token.ASSUME -> Parser.ASSUME
89+
| Token.SELF_KW -> Parser.SELF_KW
8990
| Token.TRANSMUTE -> Parser.TRANSMUTE
9091
| Token.FORGET -> Parser.FORGET
9192
| Token.TRY -> Parser.TRY

lib/parse_driver.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ let lexer_of_token_stream (next : unit -> Token.t * Span.t) : Lexing.lexbuf -> P
8080
| Token.AS -> Parser.AS
8181
| Token.UNSAFE -> Parser.UNSAFE
8282
| Token.ASSUME -> Parser.ASSUME
83+
| Token.SELF_KW -> Parser.SELF_KW
8384
| Token.TRANSMUTE -> Parser.TRANSMUTE
8485
| Token.FORGET -> Parser.FORGET
8586
| Token.TRY -> Parser.TRY

lib/parser.mly

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ let mk_ident name startpos endpos =
3535
%token TRUE FALSE
3636

3737
/* Keywords */
38+
%token SELF_KW
3839
%token FN LET CONST MUT OWN REF TYPE STRUCT ENUM TRAIT IMPL
3940
%token EFFECT HANDLE RESUME MATCH IF ELSE WHILE FOR
4041
%token RETURN BREAK CONTINUE IN WHERE TOTAL MODULE USE
@@ -220,11 +221,34 @@ quantity_b_sugar:
220221
| COLON OMEGA { QOmega }
221222

222223
param:
223-
| qty = quantity? own = ownership? name = ident COLON ty = type_expr
224-
{ { p_quantity = qty; p_ownership = own; p_name = name; p_ty = ty } }
225-
(* ADR-007 Option C: @linear x: Int *)
226-
| qty_attr = quantity_attr own = ownership? name = ident COLON ty = type_expr
227-
{ { p_quantity = Some qty_attr; p_ownership = own; p_name = name; p_ty = ty } }
224+
(* Self receiver: bare `self` — SELF_KW is a distinct keyword token.
225+
No COLON or type annotation; type defaults to `Self`.
226+
Four forms below cover all quantity × ownership × self combinations
227+
that are LR(1) without option() conflicts. *)
228+
| SELF_KW
229+
{ { p_quantity = None; p_ownership = None;
230+
p_name = mk_ident "self" $startpos $endpos;
231+
p_ty = TyCon (mk_ident "Self" $startpos $endpos) } }
232+
| own = ownership SELF_KW
233+
{ { p_quantity = None; p_ownership = Some own;
234+
p_name = mk_ident "self" $startpos $endpos;
235+
p_ty = TyCon (mk_ident "Self" $startpos $endpos) } }
236+
(* Normal params: explicit combinations to avoid option() LR(1) conflicts.
237+
Tokens sets are disjoint: SELF_KW / ownership (REF|OWN|MUT) / quantity
238+
(ZERO|ONE|OMEGA) / AT / ident (LOWER_IDENT|UPPER_IDENT). *)
239+
| name = ident COLON ty = type_expr
240+
{ { p_quantity = None; p_ownership = None; p_name = name; p_ty = ty } }
241+
| own = ownership name = ident COLON ty = type_expr
242+
{ { p_quantity = None; p_ownership = Some own; p_name = name; p_ty = ty } }
243+
| qty = quantity name = ident COLON ty = type_expr
244+
{ { p_quantity = Some qty; p_ownership = None; p_name = name; p_ty = ty } }
245+
| qty = quantity own = ownership name = ident COLON ty = type_expr
246+
{ { p_quantity = Some qty; p_ownership = Some own; p_name = name; p_ty = ty } }
247+
(* ADR-007 Option C: @linear / @erased / @unrestricted attribute form *)
248+
| qty_attr = quantity_attr name = ident COLON ty = type_expr
249+
{ { p_quantity = Some qty_attr; p_ownership = None; p_name = name; p_ty = ty } }
250+
| qty_attr = quantity_attr own = ownership name = ident COLON ty = type_expr
251+
{ { p_quantity = Some qty_attr; p_ownership = Some own; p_name = name; p_ty = ty } }
228252

229253
ownership:
230254
| OWN { Own }

lib/token.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type t =
4949
| AS
5050
| UNSAFE
5151
| ASSUME
52+
| SELF_KW (** self receiver keyword *)
5253
| TRANSMUTE
5354
| FORGET
5455
| TRY
@@ -165,6 +166,7 @@ let to_string = function
165166
| AS -> "as"
166167
| UNSAFE -> "unsafe"
167168
| ASSUME -> "assume"
169+
| SELF_KW -> "self"
168170
| TRANSMUTE -> "transmute"
169171
| FORGET -> "forget"
170172
| TRY -> "try"

test/golden/binary_ops.expected

Lines changed: 128 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,131 @@
11
{ Ast.prog_module = None; prog_imports = [];
22
prog_decls =
3-
[Ast.TopFn
4-
{ Ast.fd_vis = Ast.Private; fd_total = false;
5-
fd_name = { Ast.name = "test_ops"; span = <span> };
6-
fd_type_params = []; fd_params = [];
7-
fd_ret_ty = (Some (Ast.TyCon { Ast.name = "Int"; span = <span> }));
8-
fd_eff = None; fd_where = [];
9-
fd_body =
10-
(Ast.FnBlock
11-
{ Ast.blk_stmts = [];
12-
blk_expr =
13-
(Some (Ast.ExprBinary (
14-
(Ast.ExprLit (Ast.LitInt (1, <span>))), Ast.OpAdd,
15-
(Ast.ExprBinary (
16-
(Ast.ExprLit (Ast.LitInt (2, <span>))), Ast.OpMul,
17-
(Ast.ExprLit (Ast.LitInt (3, <span>)))))
18-
)))
19-
})
20-
};
21-
Ast.TopFn
22-
{ Ast.fd_vis = Ast.Private; fd_total = false;
23-
fd_name = { Ast.name = "test_compare"; span = <span> };
24-
fd_type_params = []; fd_params = [];
25-
fd_ret_ty = (Some (Ast.TyCon { Ast.name = "Bool"; span = <span> }));
26-
fd_eff = None; fd_where = [];
27-
fd_body =
28-
(Ast.FnBlock
29-
{ Ast.blk_stmts = [];
30-
blk_expr =
31-
(Some (Ast.ExprBinary (
32-
(Ast.ExprBinary (
33-
(Ast.ExprVar { Ast.name = "x"; span = <span> }),
34-
Ast.OpEq,
35-
(Ast.ExprVar { Ast.name = "y"; span = <span> }))),
36-
Ast.OpAnd,
37-
(Ast.ExprBinary (
38-
(Ast.ExprVar { Ast.name = "a"; span = <span> }),
39-
Ast.OpLt,
40-
(Ast.ExprVar { Ast.name = "b"; span = <span> })))
41-
)))
42-
})
43-
}]
3+
[(Ast.TopFn
4+
{ Ast.fd_vis = Ast.Private; fd_total = false;
5+
fd_name =
6+
{ Ast.name = "test_ops";
7+
span =
8+
{ Span.start_pos = { Span.line = 2; col = 1; offset = 40 };
9+
end_pos = { Span.line = 2; col = 4; offset = 43 };
10+
file = "test/golden/binary_ops.affine" }
11+
};
12+
fd_type_params = []; fd_params = [];
13+
fd_ret_ty =
14+
(Some (Ast.TyCon
15+
{ Ast.name = "Int";
16+
span =
17+
{ Span.start_pos =
18+
{ Span.line = 2; col = 15; offset = 54 };
19+
end_pos = { Span.line = 2; col = 18; offset = 57 };
20+
file = "test/golden/binary_ops.affine" }
21+
}));
22+
fd_eff = None; fd_where = [];
23+
fd_body =
24+
(Ast.FnBlock
25+
{ Ast.blk_stmts = [];
26+
blk_expr =
27+
(Some (Ast.ExprBinary (
28+
(Ast.ExprLit
29+
(Ast.LitInt (1,
30+
{ Span.start_pos =
31+
{ Span.line = 2; col = 22; offset = 61 };
32+
end_pos =
33+
{ Span.line = 3; col = 3; offset = 65 };
34+
file = "test/golden/binary_ops.affine" }
35+
))),
36+
Ast.OpAdd,
37+
(Ast.ExprBinary (
38+
(Ast.ExprLit
39+
(Ast.LitInt (2,
40+
{ Span.start_pos =
41+
{ Span.line = 3; col = 5; offset = 67 };
42+
end_pos =
43+
{ Span.line = 3; col = 7; offset = 69 };
44+
file = "test/golden/binary_ops.affine" }
45+
))),
46+
Ast.OpMul,
47+
(Ast.ExprLit
48+
(Ast.LitInt (3,
49+
{ Span.start_pos =
50+
{ Span.line = 3; col = 9; offset = 71 };
51+
end_pos =
52+
{ Span.line = 3; col = 11; offset = 73 };
53+
file = "test/golden/binary_ops.affine" }
54+
)))
55+
))
56+
)))
57+
})
58+
});
59+
(Ast.TopFn
60+
{ Ast.fd_vis = Ast.Private; fd_total = false;
61+
fd_name =
62+
{ Ast.name = "test_compare";
63+
span =
64+
{ Span.start_pos = { Span.line = 6; col = 1; offset = 78 };
65+
end_pos = { Span.line = 6; col = 4; offset = 81 };
66+
file = "test/golden/binary_ops.affine" }
67+
};
68+
fd_type_params = []; fd_params = [];
69+
fd_ret_ty =
70+
(Some (Ast.TyCon
71+
{ Ast.name = "Bool";
72+
span =
73+
{ Span.start_pos =
74+
{ Span.line = 6; col = 19; offset = 96 };
75+
end_pos = { Span.line = 6; col = 22; offset = 99 };
76+
file = "test/golden/binary_ops.affine" }
77+
}));
78+
fd_eff = None; fd_where = [];
79+
fd_body =
80+
(Ast.FnBlock
81+
{ Ast.blk_stmts = [];
82+
blk_expr =
83+
(Some (Ast.ExprBinary (
84+
(Ast.ExprBinary (
85+
(Ast.ExprVar
86+
{ Ast.name = "x";
87+
span =
88+
{ Span.start_pos =
89+
{ Span.line = 6; col = 27; offset = 104 };
90+
end_pos =
91+
{ Span.line = 7; col = 3; offset = 108 };
92+
file = "test/golden/binary_ops.affine" }
93+
}),
94+
Ast.OpEq,
95+
(Ast.ExprVar
96+
{ Ast.name = "y";
97+
span =
98+
{ Span.start_pos =
99+
{ Span.line = 7; col = 5; offset = 110 };
100+
end_pos =
101+
{ Span.line = 7; col = 8; offset = 113 };
102+
file = "test/golden/binary_ops.affine" }
103+
})
104+
)),
105+
Ast.OpAnd,
106+
(Ast.ExprBinary (
107+
(Ast.ExprVar
108+
{ Ast.name = "a";
109+
span =
110+
{ Span.start_pos =
111+
{ Span.line = 7; col = 10; offset = 115 };
112+
end_pos =
113+
{ Span.line = 7; col = 13; offset = 118 };
114+
file = "test/golden/binary_ops.affine" }
115+
}),
116+
Ast.OpLt,
117+
(Ast.ExprVar
118+
{ Ast.name = "b";
119+
span =
120+
{ Span.start_pos =
121+
{ Span.line = 7; col = 15; offset = 120 };
122+
end_pos =
123+
{ Span.line = 7; col = 17; offset = 122 };
124+
file = "test/golden/binary_ops.affine" }
125+
})
126+
))
127+
)))
128+
})
129+
})
130+
]
44131
}

0 commit comments

Comments
 (0)