Skip to content

Commit

Permalink
create terminals for explicit layout braces
Browse files Browse the repository at this point in the history
  • Loading branch information
tek committed Mar 27, 2024
1 parent f9f5e9b commit a326186
Show file tree
Hide file tree
Showing 8 changed files with 263,248 additions and 259,620 deletions.
15 changes: 15 additions & 0 deletions grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,25 @@ module.exports = grammar({
$._cmd_layout_start_if,
$._cmd_layout_start_let,
$._cmd_layout_start_quote,
// This variant is used in a `choice` with the others, and serves only to create a terminal node for explicit
// braces.
// If the scanner emitted the same symbol for virtual and explicit braces, we would either get an anonymous node
// ranging over the brace, or a terminal brace node even for virtual starts if we were to alias the symbol to '{'
// unconditionally.
// So we use separate symbols and alias only this one.
// The same reasoning applies to `_cond_layout_end_explicit`.
// The terminal could be ensured in different ways – adding an `optional('{')` after the start symbol, using
// `seq($._cmd_layout_start_explicit, '{')` instead of including the brace in the scanner range, or branching the
// entire layout on the start token to unconditionally use `_cmd_brace_close` instead of
// `_cond_layout_end_explicit`.
// However, these solutions are all very expensive, adding between 500 and 1000kB to the shared object size, and up
// to a second in generation time.
$._cmd_layout_start_explicit,

// Emitted when a new line's indent mandates ending the current layout (depending on the layout sort), or when a
// special inline layout-ending token is encountered, like an `in`.
$._cond_layout_end,
$._cond_layout_end_explicit,

// Instruct the scanner to push or pop a brace context.
$._cmd_brace_open,
Expand Down
7 changes: 6 additions & 1 deletion grammar/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ module.exports = {
declarations: $ => seq(semis($, $._topdecl), semi_opt($)),

_body: $ => seq(
$._cmd_layout_start,
choice($._cmd_layout_start, alias($._cmd_layout_start_explicit, '{')),
semi_opt($),
field('imports', optional($.imports)),
field('declarations', optional($.declarations)),
$._layout_end,
),

_layout_end: $ => choice(
$._cond_layout_end,
alias($._cond_layout_end_explicit, '}'),
),

}
8 changes: 4 additions & 4 deletions grammar/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,23 @@ semis = ($, rule) => sep1(semi($), rule),
/**
* Wrap a repeated rule in a layout.
* This is used for `where`, `let`, `of`, `if` and `do`, and the toplevel module.
* The `start` rule must be one of the externals starting with `_cond_layout_<type>`, which instruct the scanner to push
* The `start` rule must be one of the externals starting with `_cmd_layout_<type>`, which instruct the scanner to push
* a layout context with the current column as its indentation.
* When a `_cond_layout_end` or `_cond_layout_semicolon` is encountered by the scanner, the recorded indent is compared
* to the current one to make a decision.
*/
layout_sort = ($, start, rule) => seq(
start,
choice(start, alias($._cmd_layout_start_explicit, '{')),
optional(seq(
semi_opt($),
semis($, rule),
semi_opt($),
)),
$._cond_layout_end,
$._layout_end,
),

/**
* Alias of `layout_sort` using the common layout type for the start token, which corresponds to declarations and GADT
* Alias for `layout_sort` using the common layout type for the start token, which corresponds to declarations and GADT
* constructors.
*/
layout = ($, rule) => layout_sort($, $._cmd_layout_start, rule)
Expand Down
Loading

0 comments on commit a326186

Please sign in to comment.