693 lines
18 KiB
JavaScript
693 lines
18 KiB
JavaScript
/// <reference types="tree-sitter-cli/dsl" />
|
|
// @ts-check
|
|
|
|
const PREC = {
|
|
ASSIGNMENT: 1,
|
|
RANGE: 2,
|
|
OR: 3,
|
|
AND: 4,
|
|
NOT: 5,
|
|
COMPARE: 6,
|
|
BIT_OR: 7,
|
|
BIT_XOR: 8,
|
|
BIT_AND: 9,
|
|
ADD: 10,
|
|
MULTIPLY: 11,
|
|
PREFIX: 12,
|
|
POSTFIX: 13,
|
|
};
|
|
|
|
const RESERVED_WORDS = [
|
|
"and", "as", "bits", "break", "continue", "default", "distinct", "else",
|
|
"false", "for", "if", "in", "loop", "match", "nil", "not", "or", "pub",
|
|
"return", "true", "use",
|
|
"bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str",
|
|
"u8", "u16", "u32", "u64",
|
|
];
|
|
|
|
module.exports = grammar({
|
|
name: "lscript",
|
|
|
|
externals: $ => [
|
|
$._and_continuation,
|
|
$._or_continuation,
|
|
$._trailing_dot_float,
|
|
],
|
|
|
|
extras: $ => [/[ \t]/, $.comment],
|
|
word: $ => $.identifier,
|
|
reserved: {
|
|
global: _ => RESERVED_WORDS,
|
|
},
|
|
|
|
supertypes: $ => [
|
|
$.module_item,
|
|
$.expression,
|
|
$.value_expression,
|
|
$.type_expression,
|
|
$.primary_expression,
|
|
$.literal,
|
|
$.type_name,
|
|
$.binding_pattern,
|
|
$.pattern_atom,
|
|
$.pipeline_stage,
|
|
$.inline_pipeline_stage,
|
|
],
|
|
|
|
conflicts: $ => [
|
|
[$.product_pattern_field, $.primary_expression],
|
|
[$.pattern_atom, $.literal],
|
|
[$.pattern_atom, $.primary_expression],
|
|
[$.signed_numeric_literal],
|
|
[$.source_file, $._scope_separator],
|
|
[$.qualified_name],
|
|
[$.block, $._scope_separator],
|
|
[$.match_expression, $._scope_separator],
|
|
[$.product_constructor, $._scope_separator],
|
|
[$.tuple_type],
|
|
[$.type_application],
|
|
[$.tuple_pattern],
|
|
[$.product_pattern],
|
|
[$.array_pattern],
|
|
[$.tuple_value],
|
|
[$.collection_literal],
|
|
[$.block, $.product_pattern],
|
|
[$.array_pattern, $.collection_literal],
|
|
[$.typed_tuple_pattern],
|
|
[$.unit_pattern, $.unit_literal],
|
|
[$.sum_pattern, $.sum_constructor],
|
|
[$.bits_pattern, $.bits_constructor],
|
|
[$.primary_expression, $.qualified_name],
|
|
[$.array_pattern_entry, $.primary_expression],
|
|
[$.continue_expression],
|
|
[$.unit_type, $.unit_literal],
|
|
[$.unit_type, $.unit_pattern, $.unit_literal],
|
|
[$.product_pattern_field, $.primary_expression, $.type_name],
|
|
[$.pattern_atom, $.primary_expression, $.type_name],
|
|
[$.type_name, $.qualified_name],
|
|
[$.primary_expression, $.type_name],
|
|
[$.type_expression, $.function_type],
|
|
[$.array_type, $.collection_literal],
|
|
],
|
|
|
|
rules: {
|
|
source_file: $ => seq(
|
|
repeat($._newline),
|
|
optional(seq(
|
|
$.module_item,
|
|
repeat(seq($._scope_separator, $.module_item)),
|
|
repeat($._newline),
|
|
)),
|
|
),
|
|
|
|
module_item: $ => choice(
|
|
$.use_declaration,
|
|
$.pub_declaration,
|
|
$.expression,
|
|
),
|
|
|
|
use_declaration: $ => seq(
|
|
"use",
|
|
choice(
|
|
seq($.module_path, optional(seq("as", field("alias", $.identifier)))),
|
|
seq($.module_path, ".", "{", listBody($, $.import_member), "}"),
|
|
),
|
|
),
|
|
|
|
import_member: $ => seq(
|
|
field("name", $.identifier),
|
|
optional(seq("as", field("alias", $.identifier))),
|
|
),
|
|
|
|
module_path: $ => choice(
|
|
$.type_name,
|
|
seq(choice(".", ".."), $.type_name),
|
|
),
|
|
|
|
pub_declaration: $ => seq("pub", "{", scopeBody($, $.identifier), "}"),
|
|
|
|
expression: $ => choice(
|
|
$.declaration,
|
|
$.value_expression,
|
|
),
|
|
|
|
value_expression: $ => choice(
|
|
$.function_literal,
|
|
$.return_expression,
|
|
$.break_expression,
|
|
$.continue_expression,
|
|
$.pipeline_expression,
|
|
$._ordinary_expression,
|
|
),
|
|
|
|
declaration: $ => choice(
|
|
seq(
|
|
field("name", $.identifier),
|
|
"::",
|
|
field("value", choice($.type_definition, prec(1, $.value_expression))),
|
|
),
|
|
seq(
|
|
field("name", $.identifier),
|
|
":=",
|
|
field("value", $.value_expression),
|
|
),
|
|
seq(
|
|
field("name", $.identifier),
|
|
":",
|
|
field("type", $.type_expression),
|
|
choice(
|
|
seq(":", field("value", $.value_expression)),
|
|
optional(seq("=", field("value", $.value_expression))),
|
|
),
|
|
),
|
|
seq(
|
|
field("pattern", choice($.tuple_pattern, $.product_pattern)),
|
|
choice("::", ":="),
|
|
field("value", $.value_expression),
|
|
),
|
|
),
|
|
|
|
type_definition: $ => seq(optional("distinct"), $.type_expression),
|
|
|
|
type_expression: $ => choice(
|
|
$.function_type,
|
|
$._result_type,
|
|
),
|
|
|
|
function_type: $ => prec.right(seq(
|
|
field("input", $._result_type),
|
|
field("effect", choice("->", "=>")),
|
|
field("result", $.type_expression),
|
|
)),
|
|
|
|
_result_type: $ => choice(
|
|
$.result_type,
|
|
$.optional_type,
|
|
$._container_type,
|
|
),
|
|
|
|
result_type: $ => seq(
|
|
field("value", $._container_type),
|
|
"!",
|
|
field("error", $._container_type),
|
|
),
|
|
|
|
optional_type: $ => seq(
|
|
field("value", $._container_type),
|
|
repeat1("?"),
|
|
),
|
|
|
|
_container_type: $ => choice(
|
|
$.array_type,
|
|
$.map_type,
|
|
$._primary_type,
|
|
),
|
|
|
|
array_type: $ => prec.right(seq(
|
|
"[",
|
|
"]",
|
|
field("element", $._container_type),
|
|
)),
|
|
|
|
map_type: $ => prec.right(seq(
|
|
"[",
|
|
field("key", $.type_expression),
|
|
"]",
|
|
field("value", $._container_type),
|
|
)),
|
|
|
|
_primary_type: $ => choice(
|
|
$.primitive_type,
|
|
$.type_name,
|
|
$.unit_type,
|
|
$.tuple_type,
|
|
$.product_type,
|
|
$.sum_type,
|
|
$.bits_type,
|
|
$.type_application,
|
|
$.grouped_type,
|
|
),
|
|
|
|
grouped_type: $ => seq("{", field("value", $.type_expression), "}"),
|
|
|
|
unit_type: _ => seq("(", ")"),
|
|
|
|
tuple_type: $ => seq(
|
|
"(",
|
|
listBodyAtLeastTwo($, $.type_expression),
|
|
")",
|
|
),
|
|
|
|
type_application: $ => seq(
|
|
field("constructor", $.type_name),
|
|
"<",
|
|
listBody($, $.type_expression),
|
|
">",
|
|
),
|
|
|
|
product_type: $ => seq("&", "{", scopeBody($, $.product_field), "}"),
|
|
|
|
product_field: $ => seq(
|
|
field("name", $.identifier),
|
|
":",
|
|
field("type", $.type_expression),
|
|
choice(":", "="),
|
|
optional(field("default", $.expression)),
|
|
),
|
|
|
|
sum_type: $ => seq(
|
|
"|",
|
|
optional($.integer_type),
|
|
"{",
|
|
scopeBody($, $.sum_variant),
|
|
"}",
|
|
),
|
|
|
|
sum_variant: $ => seq(
|
|
field("name", $.identifier),
|
|
optional(choice(
|
|
seq(":", field("payload", $.type_expression)),
|
|
seq("::", field("discriminant", $.signed_integer_literal)),
|
|
)),
|
|
),
|
|
|
|
bits_type: $ => seq(
|
|
"bits",
|
|
"|",
|
|
$.unsigned_integer_type,
|
|
"{",
|
|
scopeBody($, $.bits_member),
|
|
"}",
|
|
),
|
|
|
|
bits_member: $ => seq($.identifier, "::", $.integer_literal),
|
|
|
|
primitive_type: _ => choice(
|
|
"f32", "f64", "bit", "chr", "str", "nvr",
|
|
"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64",
|
|
),
|
|
|
|
integer_type: _ => choice("i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"),
|
|
unsigned_integer_type: _ => choice("u8", "u16", "u32", "u64"),
|
|
|
|
function_literal: $ => choice(
|
|
prec(1, seq(
|
|
"@",
|
|
field("parameter", $.typed_parameter),
|
|
optional(seq(
|
|
field("effect", choice("->", "=>")),
|
|
field("result", $.type_expression),
|
|
)),
|
|
field("body", $.block),
|
|
)),
|
|
prec(1, seq(
|
|
"@",
|
|
field("parameter", $.typed_tuple_pattern),
|
|
optional(seq(
|
|
field("effect", choice("->", "=>")),
|
|
field("result", $.type_expression),
|
|
)),
|
|
field("body", $.block),
|
|
)),
|
|
prec(1, seq("@", field("body", $.block))),
|
|
),
|
|
|
|
typed_tuple_pattern: $ => seq("(", optional(listBodyAtLeastTwo($, $.typed_tuple_component)), ")"),
|
|
|
|
typed_parameter: $ => seq(
|
|
field("pattern", $.binding_pattern),
|
|
":",
|
|
field("type", $._result_type),
|
|
),
|
|
|
|
typed_tuple_component: $ => seq(
|
|
field("pattern", $.binding_pattern),
|
|
":",
|
|
field("type", $.type_expression),
|
|
),
|
|
|
|
return_expression: $ => prec.right(seq("return", optional($.expression))),
|
|
break_expression: $ => prec.right(seq("break", optional($.label), optional($.expression))),
|
|
continue_expression: $ => seq("continue", optional($.label)),
|
|
|
|
label: $ => seq("`", $.identifier),
|
|
|
|
if_expression: $ => prec.right(seq(
|
|
"if",
|
|
field("condition", $.expression),
|
|
field("consequence", $.block),
|
|
optional(seq("else", field("alternative", choice($.block, $.if_expression)))),
|
|
)),
|
|
|
|
match_expression: $ => seq(
|
|
"match",
|
|
field("value", $.expression),
|
|
"{",
|
|
scopeBody($, $.match_arm),
|
|
"}",
|
|
),
|
|
|
|
match_arm: $ => seq(
|
|
field("pattern", $.pattern),
|
|
optional(seq("if", field("guard", $.expression))),
|
|
"=>",
|
|
field("body", $.expression),
|
|
),
|
|
|
|
loop_expression: $ => seq("loop", optional($.label), $.block),
|
|
|
|
for_expression: $ => seq(
|
|
"for",
|
|
field("pattern", $.binding_pattern),
|
|
"in",
|
|
field("value", $.expression),
|
|
optional($.label),
|
|
field("body", $.block),
|
|
),
|
|
|
|
block: $ => seq("{", scopeBody($, $.expression), "}"),
|
|
|
|
pattern: $ => prec.left(seq($.pattern_atom, repeat(seq("or", $.pattern_atom)))),
|
|
|
|
binding_pattern: $ => choice(
|
|
"_",
|
|
$.identifier,
|
|
$.unit_pattern,
|
|
$.tuple_pattern,
|
|
$.product_pattern,
|
|
),
|
|
|
|
pattern_atom: $ => choice(
|
|
"_",
|
|
$.identifier,
|
|
$.signed_numeric_literal,
|
|
$.string_literal,
|
|
$.character_literal,
|
|
$.boolean_literal,
|
|
$.nil_literal,
|
|
$.sum_pattern,
|
|
$.bits_pattern,
|
|
$.unit_pattern,
|
|
$.tuple_pattern,
|
|
$.product_pattern,
|
|
$.array_pattern,
|
|
),
|
|
|
|
unit_pattern: _ => seq("(", ")"),
|
|
tuple_pattern: $ => seq("(", listBodyAtLeastTwo($, $.pattern), ")"),
|
|
|
|
product_pattern: $ => seq("{", listBody($, $.product_pattern_field), "}"),
|
|
product_pattern_field: $ => seq($.identifier, optional(seq("as", $.identifier))),
|
|
|
|
sum_pattern: $ => seq(
|
|
"|",
|
|
optional($.type_name),
|
|
".",
|
|
field("variant", $.identifier),
|
|
optional($.pattern_atom),
|
|
),
|
|
|
|
bits_pattern: $ => seq(
|
|
"|",
|
|
optional($.type_name),
|
|
".",
|
|
"{",
|
|
listBody($, $.identifier),
|
|
"}",
|
|
),
|
|
|
|
array_pattern: $ => seq("[", listBody($, $.array_pattern_entry), "]"),
|
|
array_pattern_entry: $ => choice($.pattern, seq("...", choice($.identifier, "_"))),
|
|
|
|
pipeline_expression: $ => prec.dynamic(1, prec.left(choice(
|
|
seq(field("input", choice($._prefix_expression, $.function_literal)), $.inline_pipeline_stage, repeat($.pipeline_stage)),
|
|
seq(field("input", choice($._non_assignment_expression, $.function_literal)), $.continuation_pipeline_stage, repeat($.pipeline_stage)),
|
|
))),
|
|
|
|
pipeline_stage: $ => choice($.inline_pipeline_stage, $.continuation_pipeline_stage),
|
|
inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage),
|
|
continuation_pipeline_stage: $ => choice(
|
|
seq(
|
|
$._direct_pipeline_continuation,
|
|
field("callable", $.pipeline_callable),
|
|
repeat(choice("?", "!")),
|
|
),
|
|
seq(
|
|
$._aggregate_pipeline_continuation,
|
|
field("callable", $.aggregate_pipeline_callable),
|
|
field("arguments", $.aggregate_tail),
|
|
repeat(choice("?", "!")),
|
|
),
|
|
),
|
|
|
|
direct_pipeline_stage: $ => seq(
|
|
"->",
|
|
field("callable", $.pipeline_callable),
|
|
repeat(choice("?", "!")),
|
|
),
|
|
|
|
aggregate_pipeline_stage: $ => seq(
|
|
"|>",
|
|
field("callable", $.aggregate_pipeline_callable),
|
|
field("arguments", $.aggregate_tail),
|
|
repeat(choice("?", "!")),
|
|
),
|
|
|
|
aggregate_tail: $ => choice(
|
|
seq("(", listBodyOneOrMore($, $.expression), ")"),
|
|
seq("[", optional(listBody($, $.array_tail_entry)), "]"),
|
|
),
|
|
|
|
array_tail_entry: $ => choice($.expression, seq("...", $.expression)),
|
|
|
|
pipeline_callable: $ => seq(
|
|
$.primary_expression,
|
|
repeat(choice(
|
|
seq(".", $.identifier),
|
|
seq("[", $.expression, "]"),
|
|
)),
|
|
),
|
|
|
|
aggregate_pipeline_callable: $ => seq(
|
|
$.primary_expression,
|
|
repeat(seq(".", $.identifier)),
|
|
),
|
|
|
|
_ordinary_expression: $ => choice(
|
|
$.assignment_expression,
|
|
$._non_assignment_expression,
|
|
),
|
|
|
|
_non_assignment_expression: $ => choice(
|
|
$.range_expression,
|
|
$._binary_expression,
|
|
),
|
|
|
|
_binary_expression: $ => choice(
|
|
$.binary_expression,
|
|
$._prefix_expression,
|
|
),
|
|
|
|
_prefix_expression: $ => choice(
|
|
$.unary_expression,
|
|
$.postfix_expression,
|
|
$.primary_expression,
|
|
),
|
|
|
|
assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
|
|
field("left", $._non_assignment_expression),
|
|
field("operator", choice("=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=")),
|
|
field("right", choice(
|
|
$.function_literal,
|
|
$.return_expression,
|
|
$.break_expression,
|
|
$.pipeline_expression,
|
|
$._non_assignment_expression,
|
|
)),
|
|
)),
|
|
|
|
range_expression: $ => prec.left(PREC.RANGE, seq(
|
|
field("left", $._binary_expression),
|
|
field("operator", choice("..<", "..=")),
|
|
field("right", $._binary_expression),
|
|
)),
|
|
|
|
binary_expression: $ => choice(
|
|
binaryExpression($, "or", PREC.OR, $._or_continuation, true),
|
|
binaryExpression($, "and", PREC.AND, $._and_continuation, true),
|
|
binaryExpression($, choice("<", "<=", ">", ">=", "=="), PREC.COMPARE, $._comparison_continuation, true),
|
|
binaryExpression($, "|", PREC.BIT_OR, null),
|
|
binaryExpression($, "^", PREC.BIT_XOR),
|
|
binaryExpression($, "&", PREC.BIT_AND, null),
|
|
binaryExpression($, choice("+", "-"), PREC.ADD, "+"),
|
|
binaryExpression($, choice("*", "/", "%"), PREC.MULTIPLY),
|
|
),
|
|
|
|
unary_expression: $ => prec.right(PREC.PREFIX, seq(
|
|
field("operator", choice("not", "!", "-", "~")),
|
|
field("operand", $._prefix_expression),
|
|
)),
|
|
|
|
postfix_expression: $ => prec.left(PREC.POSTFIX, seq(
|
|
field("value", $.primary_expression),
|
|
repeat1(field("operator", choice(
|
|
seq(".", field("member", $.identifier)),
|
|
seq("[", field("index", $.expression), "]"),
|
|
"?",
|
|
"!",
|
|
))),
|
|
)),
|
|
|
|
primary_expression: $ => choice(
|
|
$.literal,
|
|
$.identifier,
|
|
$.block,
|
|
$.if_expression,
|
|
$.match_expression,
|
|
$.loop_expression,
|
|
$.for_expression,
|
|
$.tuple_value,
|
|
$.collection_literal,
|
|
$.product_constructor,
|
|
$.sum_constructor,
|
|
$.bits_constructor,
|
|
$.default_expression,
|
|
),
|
|
|
|
tuple_value: $ => seq("(", listBodyAtLeastTwo($, $.expression), ")"),
|
|
|
|
collection_literal: $ => seq("[", listBody($, $.collection_entry), "]"),
|
|
collection_entry: $ => choice(
|
|
seq($.expression, optional(seq("=>", $.expression))),
|
|
seq("...", $.expression),
|
|
),
|
|
|
|
product_constructor: $ => seq(
|
|
"&",
|
|
optional($.type_name),
|
|
".",
|
|
"{",
|
|
scopeBody($, $.product_entry),
|
|
"}",
|
|
),
|
|
|
|
product_entry: $ => choice(
|
|
seq($.identifier, choice(":", "="), $.expression),
|
|
seq("...", $.expression),
|
|
),
|
|
|
|
sum_constructor: $ => seq("|", optional($.type_name), ".", $.identifier),
|
|
bits_constructor: $ => seq("|", optional($.type_name), ".", "{", listBody($, $.identifier), "}"),
|
|
|
|
literal: $ => choice(
|
|
$.signed_numeric_literal,
|
|
$.string_literal,
|
|
$.character_literal,
|
|
$.boolean_literal,
|
|
$.nil_literal,
|
|
$.unit_literal,
|
|
),
|
|
|
|
boolean_literal: _ => choice("true", "false"),
|
|
nil_literal: _ => "nil",
|
|
default_expression: _ => "default",
|
|
unit_literal: _ => seq("(", ")"),
|
|
signed_numeric_literal: $ => seq(optional("-"), choice(
|
|
$.integer_literal,
|
|
$.float_literal,
|
|
alias($._trailing_dot_float, $.float_literal),
|
|
)),
|
|
signed_integer_literal: $ => seq(optional("-"), $.integer_literal),
|
|
|
|
integer_literal: _ => token(choice(
|
|
/0[bB][01](?:_?[01])*/,
|
|
/0[oO][0-7](?:_?[0-7])*/,
|
|
/0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*/,
|
|
/[0-9](?:_?[0-9])*/,
|
|
)),
|
|
|
|
float_literal: _ => token(choice(
|
|
/[0-9](?:_?[0-9])*\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/,
|
|
/\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/,
|
|
/[0-9](?:_?[0-9])*[eE][+-]?[0-9](?:_?[0-9])*/,
|
|
)),
|
|
|
|
string_literal: _ => token(/'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))*'/),
|
|
character_literal: _ => token(/c'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))'/),
|
|
|
|
type_name: $ => choice($.identifier, $.qualified_name),
|
|
qualified_name: $ => seq($.identifier, repeat1(seq(".", $.identifier))),
|
|
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
|
|
comment: _ => token(seq("#", /[^\r\n]*/)),
|
|
|
|
_direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")),
|
|
_aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")),
|
|
_comparison_continuation: _ => token(seq(/\r?\n[ \t]*/, choice("<=", ">=", "==", "<", ">"))),
|
|
_newline: _ => /\r?\n/,
|
|
_newlines: $ => repeat1($._newline),
|
|
_scope_separator: $ => choice($._newlines, ";"),
|
|
},
|
|
});
|
|
|
|
function scopeBody($, rule) {
|
|
return seq(
|
|
repeat($._newline),
|
|
optional(seq(
|
|
rule,
|
|
repeat(seq($._scope_separator, rule)),
|
|
repeat($._newline),
|
|
)),
|
|
);
|
|
}
|
|
|
|
function listBody($, rule) {
|
|
return seq(
|
|
repeat($._newline),
|
|
optional(seq(
|
|
rule,
|
|
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
|
optional(","),
|
|
repeat($._newline),
|
|
)),
|
|
);
|
|
}
|
|
|
|
function listBodyOneOrMore($, rule) {
|
|
return seq(
|
|
repeat($._newline),
|
|
rule,
|
|
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
|
optional(","),
|
|
repeat($._newline),
|
|
);
|
|
}
|
|
|
|
function listBodyAtLeastTwo($, rule) {
|
|
return seq(
|
|
repeat($._newline),
|
|
rule,
|
|
choice(seq(",", repeat($._newline)), $._newlines),
|
|
rule,
|
|
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
|
optional(","),
|
|
repeat($._newline),
|
|
);
|
|
}
|
|
|
|
function binaryExpression($, operator, precedence, continuationOperator = operator, continuationIsToken = false) {
|
|
const operatorRule = continuationOperator === null
|
|
? seq(operator, optional($._newline))
|
|
: choice(
|
|
seq(operator, optional($._newline)),
|
|
continuationIsToken
|
|
? continuationOperator
|
|
: token(seq(/\r?\n[ \t]*/, continuationOperator)),
|
|
);
|
|
|
|
return prec.left(precedence, seq(
|
|
field("left", $._binary_expression),
|
|
field("operator", operatorRule),
|
|
field("right", $._binary_expression),
|
|
));
|
|
}
|