Skip to content

Commit

Permalink
use bind for nullary functions
Browse files Browse the repository at this point in the history
  • Loading branch information
tek committed Apr 12, 2024
1 parent df66180 commit c47f720
Show file tree
Hide file tree
Showing 27 changed files with 1,163 additions and 1,164 deletions.
6 changes: 3 additions & 3 deletions grammar/conflicts.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ module.exports = {
*/

// Function vs bind vs splice
[$._fun_name, $.pat_name],
[$._fun_name, $.exp_name],
[$._fun_name, $.exp_name, $.pat_name],
[$._function_name, $.pat_name],
[$._function_name, $.exp_name],
[$._function_name, $.exp_name, $.pat_name],

// Signature vs bind
[$.signature, $.pat_name],
Expand Down
2 changes: 1 addition & 1 deletion grammar/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = {
)),

implicit_param: $ => prec.left('implicit', seq(
$.implicit_parid,
$.implicit_variable,
$._type_annotation,
)),

Expand Down
97 changes: 30 additions & 67 deletions grammar/decl.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,103 +51,66 @@ module.exports = {
/**
* Declare a `varop`.
*/
_funlhs_infix: $ => prec.right('infix', seq(
_funlhs_infix: $ => seq(
field('left_operand', $._infixpat),
optional($._cond_no_section_op),
field('operator', choice($._operator_minus, $._varop)),
field('right_operand', $._infixpat),
)),
),

guard_equation: $ => seq($._guards, '=', $._cmd_texp_end, field('expression', $._exp)),

_fun_guards: $ => repeat1($.guard_equation),
_bind_guards: $ => repeat1($.guard_equation),

_funrhs: $ => seq(
_bind_match: $ => seq(
choice(
seq('=', field('expression', $._exp)),
$._fun_guards,
$._bind_guards,
),
optional($._where_binds),
),

/**
* The `implicit_parid` here is for:
* g = let ?par = Impy 5 in f
*/
_fun_name: $ => choice($._var, $.implicit_parid),
_function_name: $ => field('name', $._var),

_funvar: $ => seq(
field('name', $._fun_name),
optional(field('patterns', $.patterns)),
function_head_parens: $ => parens(
$,
choice(
$._function_head,
$._function_head_patterns,
),
),

_funlhs_parens: $ => choice(
$._funvar,
$._funlhs_common,
_function_head_patterns: $ => choice(
$._function_name,
$.function_head_parens,
),

/**
* Patterns following a parenthesized pattern are always optional for simplicity, even though GHC requires them for
* the outermost set.
*/
_funlhs_common: $ => choice(
_function_head: $ => choice(
seq($._function_head_patterns, field('patterns', $.patterns)),
alias($._funlhs_infix, $.infix),
seq(
parens($, $._funlhs_parens),
optional(field('patterns', $.patterns)),
),
),

_funlhs: $ => choice(
seq(
field('name', $._fun_name),
field('patterns', $.patterns),
),
$._funlhs_common,
function: $ => seq(
$._function_head,
$._bind_match,
),

/**
* This little contortion ensures that a nullary function declaration/variable binding is not considered for a dynamic
* conflict:
*
* > fun = exp
*
* vs.
*
* > fun a b c = exp
* > Con a b c = exp
*
* This works by creating a shift/reduce conflict between `function` and `bind`.
* The shift step is from `_var` to `_funrhs` (the latter of which starts with either `=` or `|`, so lookahead is
* clear-cut) and the reduce step is from `_var` to `pat_name`.
* The shift precedence is set by giving _both_ nodes the prec 'function-nullary', and the reduce precedence is set by
* giving `_var` in `pat_name` the prec 'pat-name'.
* An entry in the `precedences` config in `grammar.js` defines the order of these precedences, causing the parser to
* create only a state transition to `_funrhs` when encountering a `=` or `|` after a `variable`.
*/
function: $ => choice(
prec('function-nullary', seq(
field('name', choice($._var, $.implicit_parid)),
$._funrhs,
)),
seq(
$._funlhs,
$._funrhs,
),
),

bind: $ => seq(
field('pattern', $._pat),
$._funrhs,
),
* The `implicit_variable` here is for:
* g = let ?par = Impy 5 in f
*/
bind: $ => prec('bind', seq(
choice(field('pattern', $._pat), field('name', $._var), field('implicit', $.implicit_variable)),
$._bind_match,
)),

/**
* The difference between a `function` with a `_funlhs_infix` and a `bind` is that the former is for _declaring_ a
* `varop` and the latter uses a `conop` to pattern match on the rhs expression.
* The difference between a `function` with a `_funlhs_infix` and a `bind` with `pat_infix` is that the former is for
* _declaring_ a `varop` and the latter uses a `conop` to pattern match on the rhs expression.
* The former may not have a type annotation, while the latter may.
*
* > h : t :: [Int] = undefined
* > a <> b = undefined
* > h : t :: [Int] = undefined
*/
_decl: $ => choice(
$._gendecl,
Expand Down
2 changes: 1 addition & 1 deletion grammar/exp.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ module.exports = {
exp_name: $ => choice(
$._cons,
$._vars,
$.implicit_parid,
$.implicit_variable,
$.label,
),

Expand Down
5 changes: 2 additions & 3 deletions grammar/inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ module.exports = {
// function vs bind
// ------------------------------------------------

$._funlhs,
$._funlhs_parens,
$._funvar,
$._function_head_patterns,
$._function_head,

],

Expand Down
2 changes: 1 addition & 1 deletion grammar/lexeme.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = {

_varid: _ => token(seq(varid_start_char, id_char, /#*/)),

implicit_parid: _ => token(seq('?', varid_start_char, id_char)),
implicit_variable: _ => token(seq('?', varid_start_char, id_char)),

_conid: _ => token(seq(conid_start_char, id_char, /#*/)),

Expand Down
2 changes: 1 addition & 1 deletion grammar/precedences.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports = {
// ------------------------------------------------

[
'function-nullary',
'bind',
'pat-name',
],

Expand Down
Loading

0 comments on commit c47f720

Please sign in to comment.