difftastic/vendored_parsers/tree-sitter-f-sharp/grammar.js

1830 lines
43 KiB
JavaScript

/**
* @file FSharp grammar for tree-sitter
* @author Nikolaj Sidorenco
* @license MIT
* @see {@link https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf f# grammar}
*/
/* eslint-disable arrow-parens */
/* eslint-disable camelcase */
/* eslint-disable-next-line spaced-comment */
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const PREC = {
SEQ_EXPR: 1,
APP_EXPR: 16,
THEN_EXPR: 2,
RARROW: 3,
INFIX_OP: 4,
NEW_EXPR: 5,
LET_EXPR: 60,
LET_DECL: 7,
DO_EXPR: 8,
FUN_EXPR: 8,
MATCH_EXPR: 8,
MATCH_DECL: 9,
DO_DECL: 10,
ELSE_EXPR: 11,
INTERFACE: 12,
COMMA: 13,
DOTDOT: 14,
PREFIX_EXPR: 15,
SPECIAL_INFIX: 16,
LARROW: 16,
TUPLE_EXPR: 16,
CE_EXPR: 15,
SPECIAL_PREFIX: 17,
// DO_EXPR: 17,
IF_EXPR: 18,
NEW_OBJ: 18,
DOT: 19,
INDEX_EXPR: 20,
PAREN_APP: 21,
TYPED_EXPR: 22,
PAREN_EXPR: 21,
DOTDOT_SLICE: 22,
};
module.exports = grammar({
name: 'fsharp',
extras: $ => [
$.block_comment,
$.line_comment,
$.xml_doc,
/[ \s\f\uFEFF\u2060\u200B]|\\\r?n/,
],
// The external scanner (scanner.c) allows us to inject "dummy" tokens into the grammar.
// These tokens are used to track the indentation-based scoping used in F#
externals: $ => [
$._newline, // we distinguish new scoped based on newlines.
$._indent, // starts a new indentation-based scope.
$._dedent, // signals that the current indentation scope has ended.
$._then,
$._else,
$._elif,
$._triple_quoted_content,
$.block_comment_content,
$.line_comment,
$._error_sentinel, // unused token to detect parser errors in external parser.
],
conflicts: $ => [
[$.long_identifier, $._identifier_or_op],
[$.type_argument, $.static_type_argument],
[$.file],
[$.rules],
],
word: $ => $.identifier,
inline: $ => [
$._module_elem,
$._infix_or_prefix_op,
$._base_call,
$._expression_or_range,
$._object_expression_inner,
$._record_type_defn_inner,
$._union_type_defn_inner,
$._then_expression,
],
supertypes: $ => [$._module_elem, $._pattern, $._expression, $._type_defn_body],
rules: {
//
// Top-level rules (BEGIN)
//
file: $ =>
seq(
repeat($.compiler_directive_decl),
choice(
$.named_module,
repeat1($.namespace),
repeat($._module_elem),
),
),
namespace: $ =>
seq(
token.immediate('namespace'),
choice(
'global',
field('name', seq(optional('rec'), $.long_identifier)),
),
repeat($._module_elem),
),
named_module: $ =>
seq(
'module',
optional($.access_modifier),
field('name', $.long_identifier),
repeat($._module_elem),
),
_module_elem: $ =>
choice(
$.value_declaration,
$.module_defn,
$.module_abbrev,
$.import_decl,
$.compiler_directive_decl,
$.fsi_directive_decl,
$.type_definition,
$._expression,
// $.exception_defn
),
module_abbrev: $ =>
seq(
'module',
$.identifier,
'=',
scoped($.long_identifier, $._indent, $._dedent),
),
module_defn: $ =>
prec.left(
seq(
optional($.attributes),
'module',
optional($.access_modifier),
$.identifier,
'=',
scoped(repeat1($._module_elem), $._indent, $._dedent),
)),
compiler_directive_decl: $ => seq('#nowarn', $.string),
fsi_directive_decl: $ =>
choice(
seq('#r', $.string),
seq('#load', $.string),
),
import_decl: $ => seq('open', $.long_identifier),
//
// Attributes (BEGIN)
//
attributes: $ => prec.left(repeat1($.attribute_set)),
attribute_set: $ =>
seq(
'[<',
$.attribute,
prec(PREC.SEQ_EXPR + 1, repeat(seq($._newline, $.attribute))),
'>]',
),
attribute: $ => seq(
optional(seq($.attribute_target, ':')),
$.object_construction,
),
attribute_target: _ => choice(
'assembly',
'module',
'return',
'field',
'property',
'param',
'type',
'constructor',
'event',
),
object_construction: $ =>
prec.left(PREC.SEQ_EXPR + 1,
seq(
$.type,
optional($._expression),
)),
//
// Attributes (END)
//
value_declaration: $ =>
seq(
optional($.attributes),
choice(
prec(PREC.LET_DECL, $.function_or_value_defn),
prec(PREC.DO_DECL, $.do),
),
),
do: $ => prec(PREC.DO_EXPR + 1,
seq(
'do',
$._expression_block,
)),
_function_or_value_defns: $ =>
prec.right(
seq($._function_or_value_defn_body, repeat(seq('and', $._function_or_value_defn_body))),
),
function_or_value_defn: $ =>
seq(
choice('let', 'let!'),
choice(
$._function_or_value_defn_body,
seq('rec', $._function_or_value_defns),
),
),
_function_or_value_defn_body: $ =>
seq(
choice(
$.function_declaration_left,
$.value_declaration_left,
),
optional(seq(':', $.type)),
'=',
field('body', $._expression_block),
),
function_declaration_left: $ =>
prec.left(3, seq(
optional('inline'),
optional($.access_modifier),
prec(100, $._identifier_or_op),
optional($.type_arguments),
$.argument_patterns,
)),
value_declaration_left: $ =>
prec.left(2, seq(
optional('mutable'),
optional($.access_modifier),
$._pattern,
optional($.type_arguments),
)),
access_modifier: _ => prec(100, token(prec(1000, choice('private', 'internal', 'public')))),
//
// Top-level rules (END)
//
//
// Pattern rules (BEGIN)
_pattern: $ =>
choice(
alias('null', $.null_pattern),
alias('_', $.wildcard_pattern),
alias($.const, $.const_pattern),
$.as_pattern,
$.disjunct_pattern,
$.conjunct_pattern,
$.cons_pattern,
$.repeat_pattern,
$.paren_pattern,
$.list_pattern,
$.array_pattern,
$.record_pattern,
$.typed_pattern,
$.attribute_pattern,
$.type_check_pattern,
$.optional_pattern,
$.identifier_pattern,
),
optional_pattern: $ => prec.right(
seq(
'?',
$._pattern,
),
),
type_check_pattern: $ =>
prec.right(
seq(
':?',
$.atomic_type,
optional(seq('as', $.identifier)),
),
),
attribute_pattern: $ => prec.left(seq($.attributes, $._pattern)),
paren_pattern: $ => seq('(', $._pattern, ')'),
repeat_pattern: $ =>
prec.right(
seq(
$._pattern,
repeat1(prec.right(seq(',', $._pattern))),
)),
as_pattern: $ => prec.left(0, seq($._pattern, 'as', $.identifier)),
cons_pattern: $ => prec.left(0, seq($._pattern, '::', $._pattern)),
disjunct_pattern: $ => prec.left(0, seq($._pattern, '|', $._pattern)),
conjunct_pattern: $ => prec.left(0, seq($._pattern, '&', $._pattern)),
typed_pattern: $ => prec.left(3, seq($._pattern, ':', $.type)),
argument_patterns: $ =>
// argument patterns are generally no different from normal patterns.
// however, any time an argument pattern is a valid node, (i.e. inside a beginning fun decl)
// it is always the correct node to construct.
prec.left(1000, repeat1($._atomic_pattern)),
field_pattern: $ => prec(1, seq($.long_identifier, '=', $._pattern)),
_atomic_pattern: $ =>
prec(1000,
choice(
'null',
'_',
$.const,
$.long_identifier,
$.list_pattern,
$.record_pattern,
$.array_pattern,
seq('(', $._pattern, ')'),
// :? atomic_type
)),
list_pattern: $ => choice(
seq('[', ']'),
seq('[', $._pattern, repeat(seq(';', $._pattern)), ']')),
array_pattern: $ => choice(
seq('[|', '|]'),
seq('[|', $._pattern, repeat(seq(';', $._pattern)), '|]')),
record_pattern: $ =>
prec.left(
seq(
'{', $.field_pattern, repeat(seq(';', $.field_pattern)))),
identifier_pattern: $ =>
prec.left(-1,
seq($.long_identifier, optional($._pattern_param), optional($._pattern)),
),
_pattern_param: $ =>
prec(2,
choice(
$.const,
$.long_identifier,
// seq($.long_identifier, $._pattern_param),
// seq($._pattern_param, ":", $.type),
// seq(
// "[",
// $._pattern_param,
// repeat(seq($._seperator, $._pattern_param)),
// "]",
// ),
// seq(
// "(",
// $._pattern_param,
// repeat(seq($._seperator, $._pattern_param)),
// ")",
// ),
// seq("<@", $._expression, "@>"),
// seq("<@@", $._expression, "@@>"),
'null',
),
),
//
// Pattern rules (END)
//
//
// Expressions (BEGIN)
//
_expression_block: $ =>
seq(
$._indent,
$._expression,
$._dedent,
),
_expression: $ =>
choice(
'null',
$.const,
$.paren_expression,
$.begin_end_expression,
$.long_identifier_or_op,
$.dot_expression,
$.typed_expression,
$.infix_expression,
$.index_expression,
$.mutate_expression,
$.object_instantiation_expression,
$.list_expression,
$.array_expression,
$.ce_expression,
$.prefixed_expression,
$.brace_expression,
$.anon_record_expression,
$.typecast_expression,
$.declaration_expression,
$.do_expression,
$.fun_expression,
$.function_expression,
$.sequential_expression,
$.if_expression,
$.while_expression,
$.for_expression,
$.match_expression,
$.try_expression,
$.literal_expression,
// $.call_expression,
$.tuple_expression,
$.application_expression,
// (static-typars : (member-sig) expr)
),
tuple_expression: $ =>
prec.left(PREC.TUPLE_EXPR,
seq(
$._expression,
',',
$._expression,
),
),
brace_expression: $ =>
prec(PREC.CE_EXPR + 1,
seq(
'{',
scoped(
choice(
$.field_initializers,
$.with_field_expression,
$.object_expression,
),
$._indent,
$._dedent),
'}',
)),
anon_record_expression: $ =>
prec(PREC.PAREN_EXPR,
seq(
'{|',
scoped(
$.field_initializers,
$._indent,
$._dedent),
'|}',
)),
with_field_expression: $ =>
seq(
$._expression,
'with',
scoped($.field_initializers, $._indent, $._dedent),
),
_object_expression_inner: $ =>
seq(
$._object_members,
repeat($.interface_implementation),
),
object_expression: $ =>
prec(PREC.NEW_EXPR,
seq(
'new',
$._base_call,
$._object_expression_inner,
)),
_base_call: $ =>
seq(
$.object_construction,
optional(seq('as', $.identifier)),
),
prefixed_expression: $ =>
seq(
choice('return', 'return!', 'yield', 'yield!', 'lazy', 'assert', 'upcast', 'downcast', $.prefix_op),
prec.right(PREC.PREFIX_EXPR, $._expression),
),
literal_expression: $ =>
prec(PREC.PAREN_EXPR,
choice(
seq('<@', $._expression, '@>'),
seq('<@@', $._expression, '@@>'),
)),
typecast_expression: $ =>
prec(PREC.SPECIAL_INFIX,
seq(
$._expression,
choice(
':',
':>',
':?',
':?>',
),
$.type,
)),
for_expression: $ =>
prec(PREC.DO_EXPR + 1,
seq(
'for',
choice(
seq($._pattern, 'in', $._expression_or_range),
seq($.identifier, '=', $._expression, choice('to', 'downto'), $._expression),
),
'do',
$._expression_block,
optional('done'),
)),
while_expression: $ =>
prec(PREC.DO_EXPR + 1,
seq(
'while',
$._expression,
'do',
$._expression_block,
optional('done'),
)),
_else_expression: $ =>
seq(
alias($._else, 'else'),
field('else', $._expression_block),
),
_then_expression: $ =>
seq(
alias($._then, 'then'),
$._indent,
field('then', $._expression),
),
elif_expression: $ =>
seq(
alias($._elif, 'elif'),
field('guard', $._expression),
$._then_expression,
),
_if_then_else_expression: $ =>
prec.left(PREC.IF_EXPR,
seq(
'if',
field('guard', $._expression),
$._then_expression,
repeat($.elif_expression),
$._else_expression,
)),
_if_then_expression: $ =>
prec.left(PREC.IF_EXPR,
seq(
'if',
field('guard', $._expression),
$._then_expression,
$._dedent
)),
if_expression: $ => choice($._if_then_expression, $._if_then_else_expression),
fun_expression: $ =>
prec.right(PREC.FUN_EXPR,
seq(
'fun',
$.argument_patterns,
'->',
$._expression_block,
)),
try_expression: $ =>
prec(PREC.MATCH_EXPR,
seq(
'try',
$._expression_block,
optional($._newline),
choice(
seq('with', $.rules),
seq('finally', $._expression_block)
),
)),
match_expression: $ =>
seq(
choice('match', 'match!'),
$._expression,
optional($._newline),
'with',
$.rules,
),
function_expression: $ =>
prec(PREC.MATCH_EXPR,
seq(
'function',
$.rules,
)),
object_instantiation_expression: $ =>
prec(PREC.NEW_OBJ,
seq(
'new',
$.type,
$._expression,
)),
mutate_expression: $ =>
prec.right(PREC.LARROW,
seq(
field('assignee', $._expression),
'<-',
field('value', $._expression),
)),
index_expression: $ =>
prec(PREC.INDEX_EXPR,
seq(
$._expression,
'.[',
choice(
field('index', $._expression),
$.slice_ranges,
),
']',
)),
dot_expression: $ =>
prec.right(PREC.DOT,
seq(
field('base', $._expression),
'.',
field('field', $.long_identifier_or_op),
)),
typed_expression: $ =>
prec(PREC.PAREN_EXPR,
seq(
$._expression,
token.immediate(prec(PREC.PAREN_EXPR, '<')),
optional($.types),
prec(PREC.PAREN_EXPR, '>'),
)),
declaration_expression: $ =>
seq(
choice(
seq(choice('use', 'use!'), $.identifier, '=', $._expression_block),
$.function_or_value_defn,
),
field('in', $._expression)
),
do_expression: $ =>
prec(PREC.DO_EXPR,
seq(
choice('do', 'do!'),
$._expression_block,
)),
_list_elements: $ =>
prec.right(PREC.COMMA + 100,
seq(
optional($._newline),
$._expression,
repeat(prec.right(PREC.COMMA + 100,
seq(
alias($._newline, ';'),
$._expression,
),
)),
),
),
_list_element: $ =>
seq(
$._indent,
choice(
$._list_elements,
$._comp_or_range_expression,
$.slice_ranges,
),
$._dedent,
),
list_expression: $ =>
seq(
'[',
optional($._list_element),
']',
),
array_expression: $ =>
seq(
'[|',
optional($._list_element),
'|]',
),
range_expression: $ =>
prec.left(PREC.DOTDOT,
seq(
$._expression,
'..',
$._expression,
optional(seq(
'..',
$._expression,
)))),
_expression_or_range: $ =>
choice(
$._expression,
$.range_expression,
),
rule: $ =>
prec.right(
seq(
$._pattern,
optional(seq('when', $._expression)),
'->',
$._expression_block,
)),
rules: $ =>
seq(
optional('|'), $.rule,
repeat(seq(optional($._newline), '|', $.rule)),
),
begin_end_expression: $ => prec(PREC.PAREN_EXPR, seq('begin', $._expression, 'end')),
paren_expression: $ => prec(PREC.PAREN_EXPR,
seq(
'(',
$._expression_block,
')',
)),
application_expression: $ =>
prec.left(PREC.APP_EXPR,
seq(
$._expression,
choice(
prec(PREC.APP_EXPR, $._expression),
prec(PREC.PAREN_APP + 100, seq(token.immediate(prec(10000, '(')), optional($._expression_block), ')'),
)
)
)),
infix_expression: $ =>
prec.left(PREC.SPECIAL_INFIX,
seq(
$._expression,
$.infix_op,
$._expression,
)),
ce_expression: $ =>
prec.left(PREC.CE_EXPR,
seq(
prec(-1, $._expression),
'{',
scoped($._comp_or_range_expression, $._indent, $._dedent),
'}',
)),
sequential_expression: $ =>
prec.right(PREC.SEQ_EXPR,
seq(
$._expression,
repeat1(
prec.right(PREC.SEQ_EXPR,
seq(
alias($._newline, ';'),
$._expression,
),
),
),
),
),
//
// Expressions (END)
//
//
// Computation expression (BEGIN)
//
_comp_or_range_expression: $ =>
choice(
$._expression,
$.short_comp_expression,
// $.range_expression, TODO
),
// _comp_expressions: $ =>
// choice(
// $.let_ce_expressions,
// $.do_ce_expressions,
// $.use_ce_expressions,
// $.yield_ce_expressions,
// $.return_ce_expressions,
// $.if_ce_expressions,
// $.match_ce_expressions,
// $.try_ce_expressions,
// $.while_expressions,
// $.for_ce_expressions,
// $.sequential_ce_expressions,
// $._expression,
// ),
// for_ce_expressions: $ =>
// prec.left(
// seq(
// "for",
// choice(
// seq($._pattern, "in", $._expression_or_range),
// seq($.identifier, "=", $._expression, "to", $._expression),
// ),
// "do",
// $._virtual_open_section,
// $._comp_expressions,
// $._virtual_end_section,
// optional("done"),
// )),
//
// try_ce_expressions: $ =>
// prec(PREC.MATCH_EXPR,
// seq(
// "try",
// $._virtual_open_section,
// $._comp_expressions,
// $._virtual_end_section,
// choice(
// seq("with", $.comp_rules),
// seq("finally", $.comp_rules)
// ),
// )),
//
// match_ce_expressions: $ =>
// prec(PREC.MATCH_EXPR,
// seq(
// "match",
// $._expression,
// "with",
// $.comp_rules,
// )),
//
// sequential_ce_expressions: $ =>
// prec.left(PREC.SEQ_EXPR,
// seq(
// $._comp_expressions,
// repeat1(prec.right(PREC.SEQ_EXPR, seq(choice(";", $._newline), $._comp_expressions))),
// )),
//
// _else_ce_expressions: $ =>
// prec(PREC.ELSE_EXPR,
// seq(
// "else",
// $._virtual_open_section,
// field("else_branch", $._comp_expressions),
// $._virtual_end_section,
// )),
//
// elif_ce_expressions: $ =>
// prec(PREC.ELSE_EXPR,
// seq(
// "elif",
// $._virtual_open_section,
// field("guard", $._expression),
// $._virtual_end_section,
// "then",
// $._virtual_open_section,
// field("then", $._comp_expressions),
// $._virtual_end_section,
// )),
//
// if_ce_expressions: $ =>
// prec.left(PREC.IF_EXPR,
// seq(
// "if",
// $._virtual_open_section,
// field("guard", $._expression),
// $._virtual_end_section,
// "then",
// field("then", $._comp_expressions),
// repeat($.elif_ce_expressions),
// optional($._else_ce_expressions),
// )),
//
// return_ce_expressions: $ =>
// prec.left(PREC.PREFIX_EXPR,
// seq(
// choice("return!", "return"),
// $._expression,
// )),
//
// yield_ce_expressions: $ =>
// prec.left(PREC.PREFIX_EXPR,
// seq(
// choice("yield!", "yield"),
// $._expression,
// )),
//
// do_ce_expressions: $ =>
// seq(
// choice("do!", "do"),
// $._expression,
// $._comp_expressions,
// ),
//
// use_ce_expressions: $ =>
// seq(
// choice("use!", "use"),
// $._pattern,
// "=",
// $._virtual_open_section,
// $._expression,
// $._virtual_end_section,
// $._comp_expressions,
// ),
//
// let_ce_expressions: $ =>
// seq(
// choice("let!", "let"),
// $._pattern,
// "=",
// $._virtual_open_section,
// $._expression,
// $._virtual_end_section,
// $._comp_expressions,
// ),
short_comp_expression: $ =>
seq(
'for',
$._pattern,
'in',
$._expression_or_range,
'->',
$._expression,
),
// comp_rule: $ =>
// seq(
// $._pattern,
// "->",
// $._comp_expressions,
// ),
//
// comp_rules: $ =>
// prec.left(2,
// seq(
// optional("|"),
// $.comp_rule,
// repeat(seq("|", $.comp_rule)),
// )),
slice_ranges: $ =>
seq($.slice_range, repeat(seq(',', $.slice_range))),
_slice_range_special: $ =>
prec.left(PREC.DOTDOT_SLICE,
choice(
seq(field('from', $._expression), token(prec(PREC.DOTDOT, '..'))),
seq(token(prec(PREC.DOTDOT + 100000, '..')), field('to', $._expression)),
seq(field('from', $._expression), token(prec(PREC.DOTDOT, '..')), field('to', $._expression)),
),
),
slice_range: $ =>
choice(
$._slice_range_special,
$._expression,
'*',
),
//
// Computation expression (END)
//
//
// Type rules (BEGIN)
//
type: $ =>
prec(4, choice(
$._simple_type,
$._generic_type,
$._paren_type,
$._function_type,
$._compound_type,
$._postfix_type,
$._list_type,
$._static_type,
$.type_argument,
$._constrained_type,
$._flexible_type,
)),
_simple_type: $ => $.long_identifier,
_generic_type: $ => prec.right(5, seq($.long_identifier, '<', optional($.type_attributes), '>')),
_paren_type: $ => seq('(', $.type, ')'),
_function_type: $ => prec.right(seq($.type, '->', $.type)),
_compound_type: $ => prec.right(seq($.type, repeat1(prec.right(seq('*', $.type))))),
_postfix_type: $ => prec.left(4, seq($.type, $.long_identifier)),
_list_type: $ => seq($.type, '[]'),
_static_type: $ => prec(10, seq($.type, $.type_arguments)),
_constrained_type: $ => prec.right(seq($.type_argument, ':>', $.type)),
_flexible_type: $ => prec.right(seq(token.immediate('#'), $.type)),
types: $ =>
seq(
$.type,
repeat(prec.left(PREC.COMMA, seq(',', $.type))),
),
type_attribute: $ =>
choice(
$.type,
// measure
// static-parameter
),
type_attributes: $ => seq($.type_attribute, repeat(prec.left(PREC.COMMA, seq(',', $.type_attribute)))),
atomic_type: $ =>
prec.right(
choice(
seq('#', $.type),
$.type_argument,
seq('(', $.type, ')'),
$.long_identifier,
seq($.long_identifier, '<', $.type_attributes, '>'),
)),
constraint: $ =>
choice(
seq($.type_argument, ':>', $.type),
seq($.type_argument, ':', 'null'),
seq($.static_type_argument, ':', '(', $.trait_member_constraint, ')'),
seq($.type_argument, ':', '(', 'new', ':', 'unit', '->', '\'T', ')'),
seq($.type_argument, ':', 'struct'),
seq($.type_argument, ':', 'not', 'struct'),
seq($.type_argument, ':', 'enum', '<', $.type, '>'),
seq($.type_argument, ':', 'unmanaged'),
seq($.type_argument, ':', 'equality'),
seq($.type_argument, ':', 'comparison'),
seq($.type_argument, ':', 'delegate', '<', $.type, ',', $.type, '>'),
),
type_argument_constraints: $ =>
seq(
'when',
$.constraint,
repeat(seq('and', $.constraint)),
),
type_argument: $ =>
choice(
'_',
seq('\'', $.identifier),
seq('^', $.identifier),
),
type_argument_defn: $ =>
seq(
optional($.attributes),
$.type_argument,
),
static_type_argument: $ =>
choice(
seq(choice('^', '\''), $.identifier),
seq(choice('^', '\''), $.identifier, repeat(seq('or', choice('^', '\''), $.identifier))),
),
type_arguments: $ =>
seq(
'<',
$.type_argument_defn,
repeat(prec.left(PREC.COMMA, seq(',', $.type_argument_defn))),
optional($.type_argument_constraints),
'>',
),
trait_member_constraint: $ =>
seq(
optional('static'),
'member',
$.identifier,
':',
$.type,
),
member_signature: $ =>
seq(
$.identifier,
optional($.type_arguments),
':',
$.curried_spec,
optional(
choice(
seq('with', 'get'),
seq('with', 'set'),
seq('with', 'get', ',', 'set'),
seq('with', 'set', ',', 'get'),
),
),
),
curried_spec: $ =>
seq(
repeat(seq($.arguments_spec, '->')),
$.type
),
argument_spec: $ =>
prec.left(
seq(
optional($.attributes),
optional($.argument_name_spec),
$.type,
)),
arguments_spec: $ =>
seq(
$.argument_spec,
repeat(seq('*', $.argument_spec)),
),
argument_name_spec: $ =>
seq(
optional('?'),
field('name', $.identifier),
':',
),
interface_spec: $ =>
seq(
'interface',
$.type,
),
static_parameter: $ =>
choice(
$.static_parameter_value,
seq('id', '=', $.static_parameter_value),
),
static_parameter_value: $ =>
choice(
$.const,
seq($.const, $._expression),
),
type_definition: $ =>
prec.left(seq(
optional($.attributes),
'type',
$._type_defn_body,
repeat(seq(
optional($.attributes),
'and',
$._type_defn_body,
)))),
_type_defn_body: $ =>
choice(
$.delegate_type_defn,
$.record_type_defn,
$.union_type_defn,
$.anon_type_defn,
// $.class_type_defn,
// $.struct_type_defn,
// $.interface_type_defn,
$.enum_type_defn,
$.type_abbrev_defn,
$.type_extension,
),
type_name: $ =>
seq(
optional($.attributes),
optional($.access_modifier),
choice(
seq(field('type_name', $.identifier), optional($.type_arguments)),
seq(optional($.type_argument), field('type_name', $.identifier)), // Covers `type 'a option = Option<'a>`
),
),
type_extension: $ =>
seq(
$.type_name,
$.type_extension_elements,
),
delegate_type_defn: $ =>
seq(
$.type_name,
'=',
scoped($.delegate_signature, $._indent, $._dedent),
),
delegate_signature: $ =>
seq(
'delegate',
'of',
$.type,
),
type_abbrev_defn: $ =>
seq(
$.type_name,
'=',
scoped($.type, $._indent, $._dedent),
),
// class_type_defn: $ =>
// seq(
// $.type_name,
// optional($.primary_constr_args),
// "=",
// "class",
// $.class_type_body,
// "end",
// ),
//
// struct_type_defn: $ =>
// seq(
// $.type_name,
// optional($.primary_constr_args),
// "=",
// "struct",
// $.class_type_body,
// "end",
// ),
//
// interface_type_defn: $ =>
// seq(
// $.type_name,
// optional($.primary_constr_args),
// "=",
// "interface",
// $.class_type_body,
// "end",
// ),
_class_type_body_inner: $ =>
choice(
$.class_inherits_decl,
$._class_function_or_value_defn,
$._type_defn_elements,
),
_class_type_body: $ =>
seq(
$._class_type_body_inner,
repeat(seq($._newline, $._class_type_body_inner)),
),
_record_type_defn_inner: $ =>
seq(
'{',
scoped($.record_fields, $._indent, $._dedent),
'}',
optional($.type_extension_elements),
),
record_type_defn: $ =>
prec.left(
seq(
$.type_name,
'=',
scoped($._record_type_defn_inner, $._indent, $._dedent),
)),
record_fields: $ =>
seq(
$.record_field,
repeat(
seq(
$._newline,
$.record_field),
),
),
record_field: $ =>
seq(
optional($.attributes),
optional('mutable'),
optional($.access_modifier),
$.identifier,
':',
$.type,
),
enum_type_defn: $ =>
seq(
$.type_name,
'=',
scoped($.enum_type_cases, $._indent, $._dedent),
),
enum_type_cases: $ =>
choice(
seq(optional('|'), $.enum_type_case),
seq(
seq('|', $.enum_type_case),
repeat1(seq('|', $.enum_type_case)),
),
),
enum_type_case: $ =>
seq(
$.identifier,
'=',
$.const,
),
_union_type_defn_inner: $ =>
seq(
$.union_type_cases,
optional($.type_extension_elements),
),
union_type_defn: $ =>
prec.left(
seq(
$.type_name,
'=',
scoped($._union_type_defn_inner, $._indent, $._dedent),
)),
union_type_cases: $ =>
seq(
optional('|'),
$.union_type_case,
repeat(seq('|', $.union_type_case)),
),
union_type_case: $ =>
prec(8,
seq(
optional($.attributes),
choice(
$.identifier,
seq($.identifier, 'of', $.union_type_fields),
seq($.identifier, ':', $.type),
))),
union_type_fields: $ =>
seq(
$.union_type_field,
repeat(seq('*', $.union_type_field)),
),
union_type_field: $ =>
prec.left(choice(
$.type,
seq($.identifier, ':', $.type),
)),
anon_type_defn: $ =>
prec.left(
seq(
$.type_name,
optional($.primary_constr_args),
'=',
scoped($._class_type_body, $._indent, $._dedent),
)),
primary_constr_args: $ =>
field('constructor',
seq(
optional($.attributes),
optional($.access_modifier),
'(',
optional(seq(
$.simple_pattern,
repeat(prec.left(PREC.COMMA, seq(',', $.simple_pattern))))),
')',
)),
simple_pattern: $ =>
choice(
$.identifier,
seq($.simple_pattern, ':', $.type),
),
_class_function_or_value_defn: $ =>
seq(
optional($.attributes),
optional('static'),
choice(
$.function_or_value_defn,
seq('do', $._expression_block),
),
),
type_extension_elements: $ =>
seq(
choice(
seq(
'with',
scoped($._type_defn_elements, $._indent, $._dedent),
),
$._type_defn_elements,
),
),
_type_defn_elements: $ =>
choice(
$._member_defns,
prec.left(repeat1($.interface_implementation)),
// $._interface_signature
),
interface_implementation: $ =>
prec.left(
seq(
'interface',
$.type,
optional($._object_members),
)),
_member_defns: $ =>
prec.left(
seq(
$.member_defn,
repeat($.member_defn),
)),
_object_members: $ =>
seq(
'with',
scoped($._member_defns, $._indent, $._dedent),
),
member_defn: $ =>
prec(PREC.APP_EXPR + 100000,
seq(
optional($.attributes),
choice(
seq(optional('static'), optional($.access_modifier), 'member', $.method_or_prop_defn),
seq('abstract', optional($.access_modifier), optional('member'), $.member_signature),
seq('override', optional($.access_modifier), $.method_or_prop_defn),
seq('default', optional($.access_modifier), $.method_or_prop_defn),
seq(optional('static'), 'val', optional('mutable'), optional($.access_modifier), $.identifier, ':', $.type),
$.additional_constr_defn,
),
)),
property_or_ident: $ =>
choice(
seq(field('instance', $.identifier), '.', field('method', $.identifier)),
$.identifier,
),
_method_defn: $ =>
choice(
seq($.property_or_ident, optional($.type_arguments), field('args', repeat1($._pattern)), '=', $._expression_block),
),
_property_defn: $ =>
seq(
$.property_or_ident,
'=',
$._expression_block,
optional(
seq(
'with',
choice(
'get',
'set',
seq('get', ',', 'set'),
seq('set', ',', 'get'),
),
),
),
),
method_or_prop_defn: $ =>
prec(3,
choice(
seq($.property_or_ident, 'with', scoped($._function_or_value_defns, $._indent, $._dedent)),
$._method_defn,
$._property_defn,
),
),
additional_constr_defn: $ =>
seq(
optional($.access_modifier),
'new',
$._pattern,
'=',
$._expression_block,
),
// additional_constr_expr: $ =>
// prec.left(
// choice(
// // seq($.additional_constr_expr, ';', $.additional_constr_expr),
// // seq($.additional_constr_expr, 'then', $._expression),
// // seq('if', $._expression, 'then', $.additional_constr_expr, 'else', $.additional_constr_expr),
// // seq('let', $._function_or_value_defn_body, 'in', $.additional_constr_expr), // TODO: "in" is optional?
// $.additional_constr_init_expr,
// )),
//
// additional_constr_init_expr: $ =>
// choice(
// // seq('{', $.class_inherits_decl, $.field_initializers, '}'),
// $._expression,
// ),
class_inherits_decl: $ =>
prec.left(
seq(
'inherit',
$.type,
optional($._expression_block),
),
),
field_initializer: $ =>
prec(PREC.SPECIAL_INFIX + 1,
seq(
field('field', $.long_identifier),
token(prec(10000000, '=')),
field('value', $._expression)),
),
field_initializers: $ =>
prec(10000000,
seq(
$.field_initializer,
repeat(seq($._newline, $.field_initializer))
)),
//
// Type rules (END)
//
//
// Constants (BEGIN)
//
_escape_char: _ => token.immediate(prec(100, /\\["\'ntbrafv]/)),
_non_escape_char: _ => token.immediate(prec(100, /\\[^"\'ntbrafv]/)),
// using \u0008 to model \b
_simple_char_char: _ => token.immediate(/[^\n\t\r\u0008\a\f\v'\\]/),
_hex_digit_imm: _ => token.immediate(/[0-9a-fA-F]/),
_digit_char_imm: _ => token.immediate(/[0-9]/),
_unicodegraph_short: $ => seq(
token.immediate('\\u'),
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
),
_unicodegraph_long: $ => seq(
token.immediate('\\U'),
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
$._hex_digit_imm,
),
_trigraph: $ => seq(token.immediate('\\'), $._digit_char_imm, $._digit_char_imm, $._digit_char_imm),
_char_char: $ => choice(
$._simple_char_char,
$._escape_char,
$._trigraph,
$._unicodegraph_short,
),
// note: \n is allowed in strings
_simple_string_char: _ => token.immediate(prec(1, /[^\t\r\u0008\a\f\v\\"]/)),
_string_char: $ => choice(
$._simple_string_char,
$._escape_char,
$._non_escape_char,
$._trigraph,
$._unicodegraph_short,
$._unicodegraph_long,
),
_string_elem: $ => choice(
$._string_char,
seq('\\', $._string_elem),
),
char: $ => seq('\'', $._char_char, token.immediate('\'')),
format_string_eval: $ =>
seq(token.immediate(prec(1000, '{')), $._expression, '}'),
format_string: $ =>
seq(
token(prec(100, '$"')),
repeat(choice($.format_string_eval, $._string_char)),
'"',
),
string: $ =>
choice(
seq('"', repeat($._string_char), '"'),
$.format_string,
),
_verbatim_string_char: $ => choice(
$._simple_string_char,
$._non_escape_char,
'\\',
),
verbatim_string: $ => seq('@"', repeat($._verbatim_string_char), token.immediate('"')),
bytechar: $ => seq('\'', $._char_char, token.immediate('\'B')),
bytearray: $ => seq('"', repeat($._string_char), token.immediate('"B')),
verbatim_bytearray: $ => seq('@"', repeat($._verbatim_string_char), token.immediate('"B')),
format_triple_quoted_string: $ =>
seq(
token(prec(100, '$"""')),
// repeat(choice($.format_string_eval, $._string_char)),
$._triple_quoted_content,
'"""',
),
triple_quoted_string: $ =>
choice(
seq(
'"""',
$._triple_quoted_content,
'"""'),
$.format_triple_quoted_string,
),
bool: _ => token(choice('true', 'false')),
unit: _ => '()',
const: $ => choice(
$.sbyte, $.int16, $.int32, $.int64, $.byte, $.uint16, $.uint32, $.int,
$.nativeint, $.unativeint, $.decimal,
$.uint64, $.ieee32, $.ieee64, $.bignum, $.char, $.string,
$.verbatim_string, $.triple_quoted_string, $.bytearray,
$.verbatim_bytearray, $.bytechar, $.bool, $.unit),
// Identifiers:
long_identifier_or_op: $ => prec.right(
alias(
choice(
$.long_identifier,
seq($.long_identifier, '.', $._identifier_or_op),
$._identifier_or_op,
),
$.long_identifier),
),
long_identifier: $ =>
prec.right(seq($.identifier, repeat(seq('.', $.identifier)))),
_identifier_or_op: $ => choice(
$.identifier,
token(prec(1000,
seq(
'(',
choice(
'?',
/[!%&*+-./<=>@^|~][!%&*+-./<=>@^|~?]*/,
'..',
'.. ..',
),
')'))),
seq('(', $.active_pattern_op_name, ')'),
token.immediate('(*)'),
),
active_pattern_op_name: $ => choice(
// full pattern
seq('|', $.identifier, repeat1(seq('|', $.identifier)), '|'),
// partial pattern
seq('|', $.identifier, repeat(seq('|', $.identifier)), '|', '_', '|'),
),
_infix_or_prefix_op: _ =>
choice(
'+',
'-',
'+.',
'-.',
'%',
'&',
'&&',
),
prefix_op: $ =>
prec.left(
choice(
$._infix_or_prefix_op,
repeat1('~'),
/[!][!%&*+-./<>@^|~?]+[!%&*+-./<=>@^|~?]*/,
)),
infix_op: $ =>
prec(PREC.INFIX_OP,
choice(
$._infix_or_prefix_op,
/[-+<>|&^*/%][!%&*+-./<=>@^|~?]*/,
'||',
'=',
'!=',
':=',
'::',
'$',
'or',
'?',
'<@',
'<@@',
'@>',
'@@>',
'?',
'?<-',
)),
// Numbers
_octaldigit_imm: _ => token.immediate(/[0-7]/),
_bitdigit_imm: _ => token.immediate(/[0-1]/),
int: $ => seq(/[0-9]/, repeat($._digit_char_imm)),
xint: $ => choice(
seq(/0[xX]/, repeat1($._hex_digit_imm)),
seq(/0[oO]/, repeat1($._octaldigit_imm)),
seq(/0[bB]/, repeat1($._bitdigit_imm)),
),
sbyte: $ => seq(choice($.int, $.xint), token.immediate('y')),
byte: $ => seq(choice($.int, $.xint), token.immediate('uy')),
int16: $ => seq(choice($.int, $.xint), token.immediate('s')),
uint16: $ => seq(choice($.int, $.xint), token.immediate('us')),
int32: $ => seq(choice($.int, $.xint), token.immediate('l')),
uint32: $ => seq(choice($.int, $.xint), token.immediate(choice('ul', 'u'))),
nativeint: $ => seq(choice($.int, $.xint), token.immediate('n')),
unativeint: $ => seq(choice($.int, $.xint), token.immediate('un')),
int64: $ => seq(choice($.int, $.xint), token.immediate('L')),
uint64: $ => seq(choice($.int, $.xint), token.immediate(choice('UL', 'uL'))),
ieee32: $ => choice(seq($.float, token.immediate('f')), seq($.xint, token.immediate('lf'))),
ieee64: $ => seq($.xint, token.immediate('LF')),
bignum: $ => seq($.int, token.immediate(/[QRZING]/)),
decimal: $ => seq(choice($.float, $.int), token.immediate(/[Mm]/)),
float: $ =>
alias(
choice(
seq(
$.int,
token.immediate('.'),
optional($.int)
),
seq(
$.int,
optional(seq(token.immediate('.'), $.int)),
token.immediate(/[eE][+-]?/),
$.int
),
), 'float'),
//
// Constants (END)
//
//
xml_doc: $ => seq(
'///',
alias(token.immediate(/[^\/][^\n\r]*/), $.xml_doc_content)
),
block_comment: $ => seq('(*', $.block_comment_content, token.immediate('*)')),
line_comment: _ => /\/\/[^\/][^\n\r]*/,
identifier: _ =>
token(
choice(
/[_\p{XID_Start}][_'\p{XID_Continue}]*/,
/``([^`\n\r\t])+``/,
),
),
},
});
function scoped(rule, indent, dedent) {
return field('block', seq(indent, rule, dedent));
}