Skip to content

Commit 30c931d

Browse files
hyperpolymathclaude
andcommitted
fix(parser): ADR-009 conformance — all 12 valid tests now parse (was 0/12)
Six parser/lexer issues resolved: 1. Float scientific notation crash (`1.0e10` → Failure("float_of_string")) — sedlex interprets `Opt(a, b, c)` as `Opt(a)` followed by b, c rather than wrapping the full sequence; fixed by extracting `float_exponent` as a named sub-regexp so `Opt float_exponent` groups correctly. 2. Type declaration semicolons optional — conformance spec omits trailing `;` on `type Foo = ...` aliases and inline enum declarations; added `SEMICOLON?` to both `type_decl` productions. 3. Enum leading-pipe syntax — `type X = | A | B` (spec style) now accepted alongside `type X = A | B` (classic style); new production with `EQ PIPE`. 4. Effect op type parameters — `fn await[T](promise: Promise[T]) -> T;` inside effect blocks failed because `effect_op_decl` lacked `type_params?`. 5. Row variable support — two interrelated fixes: a. Type parameters: `fn get_x[..r](...)` — `type_param` now accepts `ROW_VAR` (lexed as a single `..r` token, not `DOTDOT lower_ident`). b. Record types: `{x: Int, ..r}` — replaced `separated_list(COMMA, row_field) COMMA? row_tail?` with custom `ty_record_body/ty_record_rest` recursive rules that avoid the LALR(1) greedy-separator conflict. c. Record expressions: `{ id: id, ..record }` — analogous `expr_record_body/expr_record_rest` rules; `expr_record_spread` handles both `ROW_VAR` idents and `DOTDOT expr` forms. 6. Keyword field names — `handle` (a keyword) used as a field name in `type Resource = own { handle: Int }` and `r.handle` field access. Introduced `field_name` rule (ident + HANDLE) used in struct_field, row_field, record_field, pattern_field, and expr_postfix DOT access. Struct literal expressions (`Point { x: v, y: w }`) also added to `expr_primary` to support conformance/valid/007_struct_def.affine. All 12 valid conformance tests: 12/12 parse + output matches expected. All 68 E2E tests: 0 regressions. 12 invalid conformance tests: 10/12 correctly reject (pre-existing: empty match accepted, unknown string escapes silently ignored — out of scope). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1ba5b92 commit 30c931d

14 files changed

Lines changed: 3088 additions & 79 deletions
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
# Expected output: empty program parses to empty AST
2-
# Exit code: 0
3-
# Generate with: dune exec affinescript -- parse conformance/valid/001_empty.as > conformance/valid/001_empty.expected
4-
Program []
1+
{ Ast.prog_module = None; prog_imports = []; prog_decls = [] }
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
# Expected output: comments-only file parses to empty AST
2-
# Exit code: 0
3-
# Generate with: dune exec affinescript -- parse conformance/valid/002_comments.as > conformance/valid/002_comments.expected
4-
Program []
1+
{ Ast.prog_module = None; prog_imports = []; prog_decls = [] }
Lines changed: 155 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,155 @@
1-
# Expected output: let bindings parse correctly
2-
# Exit code: 0
3-
# Generate with: dune exec affinescript -- parse conformance/valid/003_let_bindings.as
4-
# TODO: Generate actual golden output when toolchain is available
1+
{ Ast.prog_module = None; prog_imports = [];
2+
prog_decls =
3+
[(Ast.TopFn
4+
{ Ast.fd_vis = Ast.Private; fd_total = false;
5+
fd_name =
6+
{ Ast.name = "test";
7+
span =
8+
{ Span.start_pos = { Span.line = 3; col = 1; offset = 35 };
9+
end_pos = { Span.line = 3; col = 4; offset = 38 };
10+
file = "conformance/valid/003_let_bindings.affine" }
11+
};
12+
fd_type_params = []; fd_params = [];
13+
fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = [];
14+
fd_body =
15+
(Ast.FnBlock
16+
{ Ast.blk_stmts =
17+
[Ast.StmtLet {sl_mut = false; sl_quantity = None;
18+
sl_pat =
19+
(Ast.PatVar
20+
{ Ast.name = "x";
21+
span =
22+
{ Span.start_pos =
23+
{ Span.line = 4; col = 3; offset = 55 };
24+
end_pos = { Span.line = 4; col = 7; offset = 59 };
25+
file = "conformance/valid/003_let_bindings.affine" }
26+
});
27+
sl_ty = None;
28+
sl_value =
29+
(Ast.ExprLit
30+
(Ast.LitInt (42,
31+
{ Span.start_pos =
32+
{ Span.line = 4; col = 9; offset = 61 };
33+
end_pos = { Span.line = 4; col = 11; offset = 63 };
34+
file = "conformance/valid/003_let_bindings.affine" }
35+
)))};
36+
Ast.StmtLet {sl_mut = false; sl_quantity = None;
37+
sl_pat =
38+
(Ast.PatVar
39+
{ Ast.name = "y";
40+
span =
41+
{ Span.start_pos =
42+
{ Span.line = 5; col = 3; offset = 69 };
43+
end_pos = { Span.line = 5; col = 7; offset = 73 };
44+
file = "conformance/valid/003_let_bindings.affine" }
45+
});
46+
sl_ty =
47+
(Some (Ast.TyCon
48+
{ Ast.name = "Int";
49+
span =
50+
{ Span.start_pos =
51+
{ Span.line = 5; col = 8; offset = 74 };
52+
end_pos =
53+
{ Span.line = 5; col = 10; offset = 76 };
54+
file =
55+
"conformance/valid/003_let_bindings.affine" }
56+
}));
57+
sl_value =
58+
(Ast.ExprLit
59+
(Ast.LitInt (10,
60+
{ Span.start_pos =
61+
{ Span.line = 5; col = 14; offset = 80 };
62+
end_pos = { Span.line = 5; col = 16; offset = 82 };
63+
file = "conformance/valid/003_let_bindings.affine" }
64+
)))};
65+
Ast.StmtLet {sl_mut = false; sl_quantity = None;
66+
sl_pat =
67+
(Ast.PatVar
68+
{ Ast.name = "z";
69+
span =
70+
{ Span.start_pos =
71+
{ Span.line = 6; col = 3; offset = 88 };
72+
end_pos = { Span.line = 6; col = 7; offset = 92 };
73+
file = "conformance/valid/003_let_bindings.affine" }
74+
});
75+
sl_ty = None;
76+
sl_value =
77+
(Ast.ExprBinary (
78+
(Ast.ExprVar
79+
{ Ast.name = "x";
80+
span =
81+
{ Span.start_pos =
82+
{ Span.line = 6; col = 9; offset = 94 };
83+
end_pos = { Span.line = 6; col = 11; offset = 96 };
84+
file = "conformance/valid/003_let_bindings.affine"
85+
}
86+
}),
87+
Ast.OpAdd,
88+
(Ast.ExprVar
89+
{ Ast.name = "y";
90+
span =
91+
{ Span.start_pos =
92+
{ Span.line = 6; col = 13; offset = 98 };
93+
end_pos =
94+
{ Span.line = 6; col = 15; offset = 100 };
95+
file = "conformance/valid/003_let_bindings.affine"
96+
}
97+
})
98+
))};
99+
Ast.StmtLet {sl_mut = true; sl_quantity = None;
100+
sl_pat =
101+
(Ast.PatVar
102+
{ Ast.name = "counter";
103+
span =
104+
{ Span.start_pos =
105+
{ Span.line = 7; col = 7; offset = 109 };
106+
end_pos = { Span.line = 7; col = 11; offset = 113 };
107+
file = "conformance/valid/003_let_bindings.affine" }
108+
});
109+
sl_ty = None;
110+
sl_value =
111+
(Ast.ExprLit
112+
(Ast.LitInt (0,
113+
{ Span.start_pos =
114+
{ Span.line = 7; col = 19; offset = 121 };
115+
end_pos = { Span.line = 7; col = 21; offset = 123 };
116+
file = "conformance/valid/003_let_bindings.affine" }
117+
)))};
118+
(Ast.StmtAssign (
119+
(Ast.ExprVar
120+
{ Ast.name = "counter";
121+
span =
122+
{ Span.start_pos =
123+
{ Span.line = 7; col = 22; offset = 124 };
124+
end_pos = { Span.line = 8; col = 3; offset = 128 };
125+
file = "conformance/valid/003_let_bindings.affine" }
126+
}),
127+
Ast.AssignEq,
128+
(Ast.ExprBinary (
129+
(Ast.ExprVar
130+
{ Ast.name = "counter";
131+
span =
132+
{ Span.start_pos =
133+
{ Span.line = 8; col = 11; offset = 136 };
134+
end_pos =
135+
{ Span.line = 8; col = 13; offset = 138 };
136+
file =
137+
"conformance/valid/003_let_bindings.affine" }
138+
}),
139+
Ast.OpAdd,
140+
(Ast.ExprLit
141+
(Ast.LitInt (1,
142+
{ Span.start_pos =
143+
{ Span.line = 8; col = 21; offset = 146 };
144+
end_pos =
145+
{ Span.line = 8; col = 23; offset = 148 };
146+
file =
147+
"conformance/valid/003_let_bindings.affine" }
148+
)))
149+
))
150+
))
151+
];
152+
blk_expr = None })
153+
})
154+
]
155+
}

0 commit comments

Comments
 (0)