Skip to content

Commit b917ad1

Browse files
committed
Fix ghc-core parse failures
- prefix `/` operator, shadowed by the `/` lambda head - `λ` lambda head - mid-dump local-rules block, only reachable at end of file
1 parent 06ac3c9 commit b917ad1

6 files changed

Lines changed: 28192 additions & 25393 deletions

File tree

test/fixtures/Classes.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ data Point = Point {px :: Int, py :: Int}
3737

3838
-- Newtype deriving: $fNumMetres reuses Num Double via a representation coercion.
3939
newtype Metres = Metres Double
40-
deriving (Eq, Ord, Show, Num)
40+
deriving (Eq, Ord, Show, Num, Fractional)
4141

4242
stride :: Metres -> Metres
4343
stride m = m + Metres 1
44+
45+
-- Core prints `/` in prefix position, where it collides with the `/` lambda
46+
-- head the GHC testsuite normaliser produces.
47+
halve :: Metres -> Metres
48+
halve m = m / 2

tree-sitter-ghc-core/grammar.js

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ export default grammar({
6868
// A parenthesised operator is either a binder name `(:|) = ..` or a
6969
// parenthesised atom (in a bare expression or an argument).
7070
[$.paren_operator, $._atom],
71+
[$.paren_operator, $._operator_atom],
72+
// A leading `/` opens a lambda or a prefix division. See `_operator_atom`.
73+
[$._operator_atom, $.lambda],
7174
// A binding's trailing `;` (the -ddump-late-cc layout terminator) collides
7275
// with the `;` that separates bindings inside a `let { b1; b2 }`. GLR keeps
7376
// whichever completes: the separator reading inside a let, the terminator
@@ -91,6 +94,8 @@ export default grammar({
9194
optional($._item_sep),
9295
sepBy($._item_sep, $._group),
9396
optional($._item_sep),
97+
optional($.rules_block),
98+
optional($._item_sep),
9499
repeat($._later_section),
95100
optional($.trailing_sections),
96101
optional($._item_sep),
@@ -105,8 +110,15 @@ export default grammar({
105110
optional($._item_sep),
106111
sepBy($._item_sep, $._group),
107112
optional($._item_sep),
113+
optional($.rules_block),
114+
optional($._item_sep),
108115
),
109116

117+
// GHC appends the local-rules block to the emitting pass's own dump_doc
118+
// (GHC.Core.Lint pp_rules), so it tails that section and the next `====`
119+
// banner still opens the next one.
120+
rules_block: ($) => seq($.dash_header, repeat($._soup)),
121+
110122
// Simplifier-iteration dumps print a counts preamble whose `---- .. ----` lines lex as
111123
// comments, so only the `Total ticks: N` line needs a rule.
112124
simplifier_stats: ($) => token(/Total ticks:[^\n]*/),
@@ -150,7 +162,7 @@ export default grammar({
150162
choice(
151163
$.variable,
152164
$.constructor,
153-
$.operator,
165+
$._operator_atom,
154166
$.con_operator,
155167
$.operator_name,
156168
$.special_con,
@@ -160,12 +172,12 @@ export default grammar({
160172
$.foreign_call,
161173
),
162174

163-
// Header-delimited sections after the Tidy Core: `==== .. ====` banners and `---- ..
164-
// ----` markers (e.g. `------ Local rules for imported ids --------`, bannerless
165-
// rules). Coarse balanced soup per section, stopping at the next header (which
166-
// out-lexes a soup token by longest match).
167-
trailing_sections: ($) =>
168-
repeat1(seq(choice($.banner, $.dash_header), repeat($._soup))),
175+
// Banner-led sections after the Tidy Core whose body is not Core, such as
176+
// `==== Tidy Core rules ====` and CorePrep tails. Soup runs to the next
177+
// header, which out-lexes a soup token by longest match.
178+
//
179+
// A dash header never opens a section, see `rules_block`.
180+
trailing_sections: ($) => repeat1(seq($.banner, repeat($._soup))),
169181

170182
// ------ Local rules for imported ids -------- (4+ dashes both ends, so it
171183
// out-precedences the `--` line comment).
@@ -299,11 +311,21 @@ export default grammar({
299311
seq($._soup_token, optional(seq($._dcolon, $._type))),
300312
),
301313

314+
// A lone `/` lexes as the lambda head, because an anonymous string beats the
315+
// `operator` regex. Spelling the token out as an operator too recovers Core's
316+
// prefix division, `(/ @Double $fFractionalDouble x y)`.
317+
//
318+
// Both readings then complete, since an unparenthesised `->` also lexes as an
319+
// `operator`. The negative dynamic precedence hands that tie to the lambda.
320+
// Where no `->` follows, the lambda derivation has already died.
321+
_operator_atom: ($) =>
322+
choice($.operator, prec.dynamic(-1, alias("/", $.operator))),
323+
302324
_atom: ($) =>
303325
choice(
304326
$.variable,
305327
$.constructor,
306-
$.operator,
328+
$._operator_atom,
307329
$.con_operator,
308330
$.operator_name,
309331
$.literal,
@@ -395,9 +417,16 @@ export default grammar({
395417
type_arg: ($) => seq("@", $._type_atom),
396418
coercion_arg: ($) => seq("@~", $.coercion),
397419

398-
// GHC prints the lambda head as `\`. Some newer dumps render it `/`.
420+
// GHC prints the head as `\`, or `λ` under -fprint-unicode-syntax
421+
// (Outputable.lambda). The `/` is the testsuite normaliser's, which rewrites
422+
// `\` before comparing .stderr. See `_operator_atom` for what it costs.
399423
lambda: ($) =>
400-
seq(choice("\\", "/"), repeat1($._binder), choice("->", "→"), $._expr),
424+
seq(
425+
choice("\\", "λ", "/"),
426+
repeat1($._binder),
427+
choice("->", "→"),
428+
$._expr,
429+
),
401430

402431
// The join target is a variable, or `(v :: t)` under -dppr-debug.
403432
jump: ($) => seq("jump", $._atom, repeat($._arg)),

tree-sitter-ghc-core/src/grammar.json

Lines changed: 102 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tree-sitter-ghc-core/src/node-types.json

Lines changed: 23 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)