difftastic/vendor/tree-sitter-haskell/grammar/exp.js

286 lines
7.2 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const {parens, brackets, braces, sep1, layouted, qualified} = require('./util.js')
module.exports = {
// ------------------------------------------------------------------------
// expression
// ------------------------------------------------------------------------
exp_parens: $ => parens($._exp),
/**
* This needs to be disambiguated from `gcon_tuple`, which is a constructor with _only_ commas.
* Tuple sections aren't allowed in patterns.
*
* Since tuple expressions can contain singular expressions in sections like `(a,)` and `(,a)`, it has to be ensured
* that there is _at least_ each one comma and one expression in there, but the comma may be on either side and be
* preceded by any number of further commas, like `(,,,a)`.
*
* The final `repeat` is simpler, it just has to ensure that no two `_exp`s can be successive, but this encoding
* means that the optional `_exp` after `(5,)` needs to be included in the `choice`, otherwise a simple pair would be
* impossible.
*/
_exp_tuple: $ => seq(
choice(seq(repeat1($.comma), $._exp), seq($._exp, $.comma, optional($._exp))),
repeat(seq($.comma, optional($._exp)))
),
exp_tuple: $ => parens($._exp_tuple),
/**
* Unlike their boxed variants, unboxed tuples may be nullary and unary, making it simpler to parse them.
* The nullary tuple may even have no space between the hashes, but this format coincides with the prefix notation of
* the `##` symop. Since the latter is already parsed by other rules and is valid in the same positions, it is left out
* here.
*
* The opening lexeme, `(#`, is parsed with a hardcoded trailing space in exp, pat and type. This is a hack that works
* around some peculiarities of the interactions with some features like TH and symbolic operators that would most
* likely be significantly more complex to implement correctly. As it stands, the grammar can't parse an unboxed sum
* exp without a leading space, as in `(#| x #)`.
*/
exp_unboxed_tuple: $ => seq($._unboxed_open, sep($.comma, optional($._exp)), $._unboxed_close),
/**
* Unboxed sums must have at least one separating `|`, otherwise the expression would be a unary or nullary tuple.
*/
_exp_unboxed_sum: $ => sep2('|', optional($._exp)),
exp_unboxed_sum: $ => seq($._unboxed_open, $._exp_unboxed_sum, $._unboxed_close),
exp_list: $ => brackets(sep1($.comma, $._exp)),
bind_pattern: $ => seq(
$._typed_pat,
'<-',
$._exp,
),
exp_arithmetic_sequence: $ => brackets(
field('from', $._exp),
optional(seq($.comma, field('step', $._exp))),
'..',
optional(field('to', $._exp)),
),
/**
* TransformListComp.
*
* These have to be spelled out because the keywords are valid varids when the extension is disabled and it causes
* errors if they are used individually.
*/
transform: $ => choice(
seq('then group by', $._exp, 'using', $._exp),
seq('then group using', $._exp),
seq('then', $._exp),
),
qual: $ => choice(
$.bind_pattern,
$.let,
$.transform,
$._exp,
),
exp_list_comprehension: $ => brackets(
$._exp,
'|',
sep1($.comma, $.qual),
),
exp_section_left: $ => parens(
$._exp_infix,
$._qop,
),
exp_section_right: $ => parens(
$._qop_nominus,
$._exp_infix,
),
exp_th_quoted_name: $ => choice(
seq(quote, choice($._qvar, $._qcon)),
seq(quote + quote, $._atype),
),
exp_field: $ => choice(
alias('..', $.wildcard),
seq($._qvar, optional(seq('=', $._exp)))
),
exp_type_application: $ => seq('@', $._atype),
exp_lambda: $ => seq(
'\\',
repeat1($._apat),
'->',
$._exp,
),
exp_in: $ => seq('in', $._exp),
let: $ => seq('let', optional($.decls)),
_let_decls: $ => layouted_without_end($, $._decl),
exp_let: $ => seq('let', optional(alias($._let_decls, $.decls))),
exp_let_in: $ => seq($.exp_let, $.exp_in),
exp_cond: $ => seq(
'if',
field('if', $._exp),
optional(';'),
'then',
field('then', $._exp),
optional(';'),
'else',
field('else', $._exp),
),
exp_if_guard: $ => seq('if', prec.left(repeat1($.gdpat))),
pattern_guard: $ => seq(
$._pat,
'<-',
$._exp_infix,
),
guard: $ => choice(
$.pattern_guard,
$.let,
$._exp_infix,
),
guards: $ => seq('|', sep1($.comma, $.guard)),
gdpat: $ => seq($.guards, '->', $._exp),
_alt_variants: $ => choice(
seq('->', $._exp),
repeat1($.gdpat),
),
alt: $ => seq($._pat, $._alt_variants, optional(seq($.where, optional($.decls)))),
alts: $ => layouted($, $.alt),
exp_case: $ => seq('case', $._exp, 'of', $.alts),
/**
* left associative because the alts are optional
*/
exp_lambda_case: $ => seq(
'\\',
'case',
optional($.alts),
),
rec: $ => seq(
'rec',
layouted($, $.stmt),
),
stmt: $ => choice(
$._exp,
$.bind_pattern,
$.let,
$.rec,
),
/**
* TODO does this hide the keyword entirely?
*/
_do_keyword: _ => choice('mdo', 'do'),
do_module: $ => qualified($, $._do_keyword),
exp_do: $ => seq(choice($.do_module, $._do_keyword), layouted($, $.stmt)),
exp_negation: $ => seq('-', $._aexp),
exp_record: $ => seq($._aexp, braces(sep1($.comma, $.exp_field))),
exp_name: $ => choice(
$._qvar,
$._qcon,
$.implicit_parid,
$.label,
),
_aexp: $ => choice(
$.exp_name,
$.exp_parens,
$.exp_tuple,
$.exp_list,
$.exp_th_quoted_name,
$.exp_type_application,
$.exp_lambda_case,
$.exp_do,
$.exp_record,
$.exp_arithmetic_sequence,
$.exp_list_comprehension,
$.exp_section_left,
$.exp_section_right,
$.exp_unboxed_tuple,
$.exp_unboxed_sum,
$.splice,
$.quasiquote,
alias($.literal, $.exp_literal),
),
/**
* Function application.
*
* This convoluted rule is necessary because of BlockArguments with lambda if `exp_lambda` is in `lexp` as is stated
* in the reference, it can only occur after an infix operator; if it is in `aexp`, it causes lots of problems.
* Furthermore, the strange way the recursion is done here is to avoid local conflicts.
*/
_exp_apply: $ => choice(
$._aexp,
seq($._aexp, $._exp_apply),
seq($._aexp, $.exp_lambda),
seq($._aexp, $.exp_let_in),
seq($._aexp, $.exp_cond),
seq($._aexp, $.exp_case),
),
/**
* The point of this `choice` is to get a node for function application only if there is more than one expression
* present.
*/
_fexp: $ => choice(
$._aexp,
alias($._exp_apply, $.exp_apply),
),
_lexp: $ => choice(
$.exp_let_in,
$.exp_cond,
$.exp_if_guard,
$.exp_case,
$.exp_negation,
$._fexp,
$.exp_lambda,
),
/**
* This is left-associative, although in reality this would depend on the fixity declaration for the operator.
* The default is left, even though the reerence specifies it the other way around.
* In any case, this seems to be more stable.
*/
exp_infix: $ => seq($._exp_infix, $._qop, $._lexp),
_exp_infix: $ => choice(
$.exp_infix,
$._lexp,
),
/**
* `prec.right` because:
*
* let x = 1 in x :: Int
*
* here the type annotation binds to `x`, not the entire expression
*/
_exp: $ => prec.right(seq($._exp_infix, optional($._type_annotation))),
}