/**
* @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 */
///
// @ts-check
const PREC = {
SEQ_EXPR: 1,
APP_EXPR: 16,
THEN_EXPR: 2,
RARROW: 3,
INFIX_OP: 4,
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,
PREFIX_EXPR: 15,
SPECIAL_INFIX: 16,
LARROW: 16,
TUPLE_EXPR: 16,
CE_EXPR: 15,
SPECIAL_PREFIX: 17,
// DO_EXPR: 17,
IF_EXPR: 18,
DOT: 19,
INDEX_EXPR: 20,
PAREN_APP: 21,
TYPED_EXPR: 22,
PAREN_EXPR: 21,
DOTDOT: 22,
DOTDOT_SLICE: 23,
NEW_OBJ: 24,
};
module.exports = grammar({
name: "fsharp",
extras: ($) => [
/[ \s\f\uFEFF\u2060\u200B]|\\\r?n/,
$.block_comment,
$.line_comment,
$.preproc_line,
$.compiler_directive_decl,
$.fsi_directive_decl,
";",
],
// 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",
"#if",
"#else",
"#endif",
"class",
$._struct_begin,
$._interface_begin,
"end",
"and",
$._triple_quoted_content,
$.block_comment_content,
$._inside_string_marker,
$._newline_not_aligned,
$._tuple_marker,
$._error_sentinel, // unused token to detect parser errors in external parser.
],
conflicts: ($) => [
[$.long_identifier, $._identifier_or_op],
[$.simple_type, $.type_argument],
[$.preproc_if, $.preproc_if_in_expression],
[$.rules],
],
word: ($) => $.identifier,
inline: ($) => [
$._module_elem,
$._expression_or_range,
$._object_expression_inner,
$._record_type_defn_inner,
$._union_type_defn_inner,
$._then_expression,
],
supertypes: ($) => [
$._module_elem,
$._pattern,
$._expression,
$._type,
$._type_defn_body,
$._static_parameter,
],
rules: {
//
// Top-level rules (BEGIN)
//
file: ($) =>
choice($.named_module, repeat1($.namespace), repeat($._module_elem)),
namespace: ($) =>
seq(
"namespace",
choice(
"global",
field("name", seq(optional("rec"), $.long_identifier)),
),
repeat($._module_elem),
),
named_module: ($) =>
seq(
optional($.attributes),
"module",
optional($.access_modifier),
field("name", $.long_identifier),
repeat($._module_elem),
),
_module_elem: ($) =>
choice(
$.value_declaration,
$.module_defn,
$.module_abbrev,
$.import_decl,
$.fsi_directive_decl,
$.type_definition,
$.exception_definition,
$._expression,
$.preproc_if,
// $.exception_defn
),
module_abbrev: ($) =>
seq(
optional($.attributes),
"module",
$.identifier,
"=",
scoped($.long_identifier, $._indent, $._dedent),
),
module_defn: ($) =>
prec.left(
seq(
optional($.attributes),
"module",
optional($.access_modifier),
$.identifier,
"=",
scoped($._module_body, $._indent, $._dedent),
),
),
_module_body: ($) =>
seq(
$._module_elem,
repeat(
prec(
// Make sure to parse a module node before a sequential expression
// NOTE: This removes all sequential expressions from module bodies
PREC.SEQ_EXPR + 1,
seq(alias($._newline, ";"), $._module_elem),
),
),
),
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)
//
class_as_reference: ($) => seq("as", $.identifier),
primary_constr_args: ($) =>
seq(
optional($.attributes),
optional($.access_modifier),
"(",
optional($._pattern),
")",
optional($.class_as_reference),
),
//
// Pattern rules (BEGIN)
repeat_pattern: ($) =>
prec.right(seq($._pattern, repeat1(prec(1, seq(",", $._pattern))))),
_pattern: ($) =>
choice(
"null",
alias("_", $.wildcard_pattern),
$.const,
$.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,
$.named_field_pattern,
),
optional_pattern: ($) => prec.left(seq("?", $._pattern)),
type_check_pattern: ($) =>
prec.right(seq(":?", $.atomic_type, optional(seq("as", $.identifier)))),
attribute_pattern: ($) => prec.left(seq($.attributes, $._pattern)),
paren_pattern: ($) => prec(1, 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(
-1,
seq(
$._pattern,
":",
$._type,
field("constraints", optional($.type_argument_constraints)),
),
),
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_content: ($) =>
scoped(
seq(
optional($._newline),
$._pattern,
repeat(seq($._newline, $._pattern)),
),
$._indent,
$._dedent,
),
list_pattern: ($) => seq("[", optional($._list_pattern_content), "]"),
array_pattern: ($) => seq("[|", optional($._list_pattern_content), "|]"),
record_pattern: ($) =>
prec.left(
seq(
"{",
$.field_pattern,
repeat(seq($._newline, $.field_pattern)),
"}",
),
),
named_field: ($) => seq(optional(seq($.identifier, "=")), $._pattern),
named_field_pattern: ($) =>
prec.left(
seq("(", $.named_field, repeat(seq($._newline, $.named_field)), ")"),
),
identifier_pattern: ($) =>
prec.left(
-1,
seq(
$.long_identifier_or_op,
optional($._pattern),
optional($._pattern),
),
),
//
// Pattern rules (END)
//
//
// Expressions (BEGIN)
//
_expression_block: ($) => seq($._indent, $._expression, $._dedent),
_expression: ($) =>
choice(
"null",
$.const,
$.paren_expression,
$.begin_end_expression,
$.long_identifier_or_op,
$.typed_expression,
$.infix_expression,
$.index_expression,
$.mutate_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,
$.tuple_expression,
$.application_expression,
$.dot_expression,
alias($.preproc_if_in_expression, $.preproc_if),
// (static-typars : (member-sig) expr)
),
literal_expression: ($) =>
prec(
PREC.PAREN_EXPR,
choice(
seq("<@", $._expression, "@>"),
seq("<@@", $._expression, "@@>"),
),
),
long_identifier_or_op: ($) =>
prec.right(
choice(
$.long_identifier,
seq($.long_identifier, ".", $._identifier_or_op),
$._identifier_or_op,
),
),
tuple_expression: ($) =>
prec.right(
PREC.TUPLE_EXPR,
seq($._expression, ",", optional($._tuple_marker), $._expression),
),
brace_expression: ($) =>
prec(
PREC.CE_EXPR + 1,
seq(
"{",
scoped(
choice(
$.field_initializers,
$.object_expression,
$.with_field_expression,
),
$._indent,
$._dedent,
),
"}",
),
),
anon_record_expression: ($) =>
prec(
PREC.PAREN_EXPR,
seq(
"{|",
scoped(
choice($.field_initializers, $.with_field_expression),
$._indent,
$._dedent,
),
"|}",
),
),
_object_expression_inner: ($) =>
seq($._object_members, repeat($.interface_implementation)),
object_expression: ($) =>
prec(
PREC.NEW_OBJ + 1,
seq(
"new",
$._expression,
optional(seq("as", $.identifier)),
$._object_expression_inner,
),
),
with_field_expression: ($) =>
seq(
$._expression,
"with",
scoped($.field_initializers, $._indent, $._dedent),
),
prefixed_expression: ($) =>
seq(
choice(
"return",
"return!",
"yield",
"yield!",
"lazy",
"assert",
"upcast",
"downcast",
"new",
$.prefix_op,
),
prec.right(PREC.PREFIX_EXPR, $._expression),
),
typecast_expression: ($) =>
prec.right(
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("else", field("else", $._expression_block)),
_then_expression: ($) => seq("then", field("then", $._expression_block)),
elif_expression: ($) =>
seq("elif", field("guard", $._expression_block), $._then_expression),
_if_branch: ($) => seq("if", field("guard", $._expression_block)),
if_expression: ($) =>
seq(
$._if_branch,
$._then_expression,
repeat($.elif_expression),
optional($._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",
choice(seq($._newline, $.rules), scoped($.rules, $._indent, $._dedent)),
),
function_expression: ($) =>
prec(
PREC.MATCH_EXPR,
seq("function", scoped($.rules, $._indent, $._dedent)),
),
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),
"]",
),
),
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(
PREC.DOTDOT,
seq(
$._expression,
"..",
$._expression,
optional(seq("..", $._expression)),
),
),
_expression_or_range: ($) => choice($._expression, $.range_expression),
rule: ($) =>
prec.right(
seq(
field("pattern", $._pattern),
optional(seq("when", field("guard", $._expression))),
"->",
field("block", $._expression_block),
),
),
rules: ($) =>
seq(
optional("|"),
$.rule,
repeat(seq(optional($._newline), "|", $.rule)),
),
begin_end_expression: ($) =>
prec(
PREC.PAREN_EXPR,
seq("begin", scoped($._expression, $._indent, $._dedent), "end"),
),
paren_expression: ($) =>
prec(PREC.PAREN_EXPR, seq("(", $._expression_block, ")")),
_high_prec_app: ($) =>
prec.left(
PREC.DOT + 1,
seq(
$._expression,
choice(
$.unit,
seq(token.immediate(prec(10000, "(")), $._expression_block, ")"),
),
),
),
_low_prec_app: ($) =>
prec.left(PREC.APP_EXPR, seq($._expression, $._expression)),
application_expression: ($) => choice($._high_prec_app, $._low_prec_app),
dot_expression: ($) =>
prec.right(
PREC.DOT,
seq(
field("base", $._expression),
".",
field("field", $.long_identifier_or_op),
),
),
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,
$.anon_record_type,
),
),
simple_type: ($) => choice($.long_identifier, $._static_type_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("#", $._type)),
anon_record_type: ($) =>
seq("{|", scoped($.record_fields, $._indent, $._dedent), "|}"),
types: ($) =>
seq($._type, repeat(prec.left(PREC.COMMA - 1, seq(",", $._type)))),
_static_type_identifier: ($) =>
prec(10, seq(choice("^", token(prec(100, "'"))), $.identifier)),
_static_parameter: ($) =>
// $.named_static_parameter,
choice($.static_parameter_value, $.named_static_parameter),
named_static_parameter: ($) =>
prec(3, seq($.identifier, "=", $.static_parameter_value)),
type_attribute: ($) =>
choice(
$._type,
$._static_parameter,
// measure
),
type_attributes: ($) =>
seq(
$.type_attribute,
repeat(prec.right(PREC.COMMA, seq(",", $.type_attribute))),
),
atomic_type: ($) =>
prec.right(
choice(
seq("#", $._type),
$.type_argument,
seq("(", $._type, ")"),
$.long_identifier,
seq($.long_identifier, "<", $.type_attributes, ">"),
),
),
constraint: ($) =>
prec(
1000000,
choice(
seq($.type_argument, ":>", $._type),
seq($.type_argument, ":", "null"),
seq(
$.type_argument,
":",
"(",
choice(
$.trait_member_constraint,
seq("new", ":", "unit", "->", $._type),
),
")",
),
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,
">",
),
seq("default", $.type_argument, ":", $._type),
),
),
type_argument_constraints: ($) =>
seq("when", $.constraint, repeat(seq("and", $.constraint))),
type_argument: ($) =>
prec(
10,
choice(
"_",
seq(
$._static_type_identifier,
repeat(seq("or", $._static_type_identifier)),
),
),
),
type_argument_defn: ($) => seq(optional($.attributes), $.type_argument),
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_or_op, ":", $._type),
member_signature: ($) =>
prec.left(
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)),
exception_definition: ($) =>
seq(
optional($.attributes),
"exception",
field("exception_name", $.long_identifier),
"of",
$._type,
),
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,
$.interface_type_defn,
$.anon_type_defn,
$.enum_type_defn,
$.type_abbrev_defn,
$.type_extension,
),
type_name: ($) =>
prec(
2,
seq(
optional($.attributes),
optional($.access_modifier),
choice(
seq(
field("type_name", $.long_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_body_inner: ($) =>
choice($.class_inherits_decl, $.type_extension_elements),
_class_type_body: ($) =>
seq(
$._class_type_body_inner,
repeat(seq($._newline, $._class_type_body_inner)),
),
_record_type_defn_inner: ($) =>
seq(
optional($.access_modifier),
"{",
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)),
optional($._newline),
),
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(
optional($.access_modifier),
$.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))),
interface_type_defn: ($) =>
prec.left(
1,
seq(
$.type_name,
"=",
seq(
alias($._interface_begin, "interface"),
scoped(repeat1($._type_defn_elements), $._indent, $._dedent),
"end",
),
),
),
anon_type_defn: ($) =>
prec.left(
seq(
$.type_name,
optional($.primary_constr_args),
"=",
choice(
scoped($._class_type_body, $._indent, $._dedent),
seq(
choice("begin", "class"),
scoped(optional($._class_type_body), $._indent, $._dedent),
"end",
),
seq(
alias($._struct_begin, "struct"),
scoped(repeat($._type_defn_elements), $._indent, $._dedent),
"end",
),
),
),
),
_class_function_or_value_defn: ($) =>
seq(
optional($.attributes),
optional("static"),
choice($.function_or_value_defn, seq("do", $._expression_block)),
),
_type_extension_inner: ($) =>
repeat1(
choice(
$._class_function_or_value_defn,
$._type_defn_elements,
alias($.preproc_if_in_class_definition, $.preproc_if),
),
),
type_extension_elements: ($) =>
prec.left(
seq(
choice(
seq("with", scoped($._type_extension_inner, $._indent, $._dedent)),
$._type_extension_inner,
),
),
),
_type_defn_elements: ($) =>
choice(
$.member_defn,
$.interface_implementation,
// $._interface_signature
),
interface_implementation: ($) =>
prec.left(seq("interface", $._type, optional($._object_members))),
_member_defns: ($) =>
prec.left(
seq($.member_defn, repeat(seq(optional($._newline), $.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"),
"member",
optional("inline"),
optional($.access_modifier),
$.method_or_prop_defn,
),
seq(
"abstract",
optional("member"),
optional($.access_modifier),
$.member_signature,
),
seq("member", "val", $.property_or_ident, $._val_property_defn),
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,
),
seq("static", $.value_declaration),
$.additional_constr_defn,
),
),
),
property_or_ident: ($) =>
choice(
seq(
field("instance", $.identifier),
".",
field("method", $.identifier),
),
$._identifier_or_op,
),
_method_defn: ($) =>
choice(
seq(
optional($.type_arguments),
field("args", repeat1($._pattern)),
"=",
$._expression_block,
),
),
_property_accessor_body: ($) =>
seq($.argument_patterns, optional(seq(":", $._type)), "=", $._expression),
property_accessor: ($) =>
seq(choice("get", "set"), $._property_accessor_body),
_property_defn: ($) =>
prec.left(
PREC.APP_EXPR + 100001,
seq(
optional(seq(":", $._type)),
choice(
seq("=", $._expression_block),
seq(
"with",
scoped(
seq(
$.property_accessor,
repeat(seq("and", $.property_accessor)),
),
$._indent,
$._dedent,
),
),
),
),
),
_val_property_defn: ($) =>
prec.left(
PREC.APP_EXPR + 100000,
seq(
optional(seq(":", $._type)),
"=",
$._expression,
optional(
seq(
"with",
choice(
"get",
"set",
seq("get", ",", "set"),
seq("set", ",", "get"),
),
),
),
),
),
method_or_prop_defn: ($) =>
prec(
3,
seq(
field("name", $.property_or_ident),
choice(
$._method_defn,
$._property_defn,
seq(
"with",
scoped($._function_or_value_defns, $._indent, $._dedent),
),
),
),
),
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",
scoped(seq($._type, optional($._expression)), $._indent, $._dedent),
),
),
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'\\]/),
_unicodegraph_short: (_) => /\\u[0-9a-fA-F]{4}/,
_unicodegraph_long: (_) => /\\u[0-9a-fA-F]{8}/,
_trigraph: (_) => /\\[0-9]{3}/,
_char_char: ($) =>
choice(
$._simple_char_char,
$._escape_char,
$._trigraph,
$._unicodegraph_short,
),
// note: \n is allowed in strings
_simple_string_char: ($) =>
choice(
$._inside_string_marker,
token.immediate(prec(1, /[^\t\r\u0008\a\f\v\\"]/)),
),
_string_char: ($) =>
choice(
$._simple_string_char,
$._escape_char,
$._trigraph,
$._unicodegraph_short,
$._non_escape_char,
$._unicodegraph_long,
),
char: (_) =>
prec(
-1,
/'([^\n\t\r\u0008\a\f\v\\]|\\["\'ntbrafv]|\\[0-9]{3}|\\u[0-9a-fA-F]{4}|(\\\\))?'B?/,
),
format_string_eval: ($) =>
seq(token.immediate(prec(1000, "{")), $._expression, "}"),
format_string: ($) =>
seq(
token(prec(100, '$"')),
repeat(choice($.format_string_eval, $._string_char)),
'"',
),
_string_literal: ($) => seq('"', repeat($._string_char), '"'),
string: ($) => choice($._string_literal, $.format_string),
_verbatim_string_char: ($) =>
choice($._simple_string_char, $._non_escape_char, "\\", /\"\"/),
verbatim_string: ($) =>
seq('@"', repeat($._verbatim_string_char), token.immediate('"')),
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: (_) => token(prec(100000, "()")),
const: ($) =>
choice(
$.sbyte,
$.int16,
$.int32,
$.int64,
$.byte,
$.uint16,
$.uint32,
$.int,
$.xint,
$.nativeint,
$.unativeint,
$.decimal,
$.float,
$.uint64,
$.ieee32,
$.ieee64,
$.bignum,
$.char,
$.string,
$.verbatim_string,
$.triple_quoted_string,
$.bytearray,
$.verbatim_bytearray,
$.bool,
$.unit,
),
// Identifiers:
long_identifier: ($) =>
prec.right(seq($.identifier, repeat(seq(".", $.identifier)))),
active_pattern: ($) =>
prec(
1000,
seq(
"(|",
alias($.identifier, $.active_pattern_op_name),
repeat(seq("|", alias($.identifier, $.active_pattern_op_name))),
optional(seq("|", alias("_", $.wildcard_active_pattern_op))),
"|)",
),
),
op_identifier: (_) =>
token(
prec(
1000,
seq(
"(",
/\s*/,
choice("?", /[!%&*+-./<=>@^|~$][!%&*+-./<=>@^|~?]*/, ".. .."),
/\s*/,
")",
),
),
),
_identifier_or_op: ($) =>
choice($.identifier, $.op_identifier, $.active_pattern),
_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,
token.immediate(prec(1, /[+-]/)),
/[-+<>|&^*/'%@][!%&*+./<=>@^|~?-]*/,
"||",
"=",
"!=",
":=",
"::",
"$",
"or",
"?",
"?",
"?<-",
),
),
// Numbers
int: (_) => token(/[+-]?([0-9]_?)+/),
xint: (_) =>
token(
choice(/0[xX]([0-9a-fA-F]_?)+/, /0[oO]([0-7]_?)+/, /0[bB]([0-1]_?)+/),
),
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: ($) =>
prec.right(
alias(
choice(
seq($.int, token.immediate("."), optional($.int)),
seq(
$.int,
optional(seq(token.immediate("."), $.int)),
token.immediate(/[eE][+-]?/),
$.int,
),
),
"float",
),
),
//
// Constants (END)
//
//
block_comment: ($) =>
seq("(*", $.block_comment_content, token.immediate("*)")),
line_comment: (_) => token(/\/\/+[^\n\r]*/),
identifier: (_) =>
token(
choice(/[_\p{XID_Start}][_'\p{XID_Continue}]*/, /``([^`\n\r\t])+``/),
),
// preprocessors
compiler_directive_decl: ($) =>
prec(
100000,
choice(
seq(
"#nowarn",
alias($._string_literal, $.string),
$._newline_not_aligned,
),
seq("#light", $._newline_not_aligned),
),
),
fsi_directive_decl: ($) =>
seq(choice("#r", "#load"), alias($._string_literal, $.string), /\n/),
preproc_line: ($) =>
seq(
alias(/#(line)?/, "#line"),
$.int,
optional(choice(alias($._string_literal, $.string), $.verbatim_string)),
$._newline_not_aligned,
),
...preprocIf("", ($) => $._module_elem),
...preprocIf(
"_in_expression",
($) => repeat(seq(optional($._newline), $._expression)),
-2,
),
...preprocIf(
"_in_class_definition",
($) => repeat(seq(optional($._newline), $._class_type_body_inner)),
-2,
),
},
});
/**
*
* @param {Rule} rule
*
* @param {Rule} indent
*
* @param {Rule} dedent
*
* @return {Rule}
*/
function scoped(rule, indent, dedent) {
return field("block", seq(indent, rule, dedent));
}
/**
*
* @param {string} suffix
*
* @param {RuleBuilder} content
*
* @param {number} precedence
*
* @return {RuleBuilders}
*/
function preprocIf(suffix, content, precedence = 0) {
/**
*
* @param {GrammarSymbols} $
*
* @return {Rule}
*
*/
function alternativeBlock($) {
return suffix
? alias($["preproc_else" + suffix], $.preproc_else)
: $.preproc_else;
}
return {
["preproc_if" + suffix]: ($) =>
prec(
precedence,
seq(
"#if",
field("condition", $.identifier),
$._newline_not_aligned,
content($),
field("alternative", optional(alternativeBlock($))),
"#endif",
),
),
["preproc_else" + suffix]: ($) =>
prec(precedence, seq("#else", content($))),
};
}