tree-sitter-julia
Version:
Julia grammar for tree-sitter
1,208 lines (1,040 loc) • 29.2 kB
JavaScript
const PREC = [
'afunc',
'pair',
'conditional',
'arrow',
'lazy_or',
'lazy_and',
'where', // FIXME
'comparison',
'pipe_left',
'pipe_right',
'colon',
'plus',
'times',
'rational',
'bitshift',
'prefix',
'postfix',
'power',
'decl',
'call',
'dot',
].reduce((result, name, index) => {
result[name] = index + 10;
return result;
}, {});
PREC.array = -1;
PREC.tuple = -1;
PREC.assign = -2;
PREC.stmt = -3;
PREC.macro_arg = -4;
const OPERATORS = {
assignment: `
+= -= *= /= //= \\= ^= %= <<= >>= >>>= |= &=
−= ÷= ⊻= ≔ ⩴ ≕
`,
arrow: `
<-- --> <-->
← → ↔ ↚ ↛ ↞ ↠ ↢ ↣ ↦ ↤ ↮ ⇎ ⇍ ⇏ ⇐ ⇒ ⇔ ⇴ ⇶ ⇷ ⇸ ⇹ ⇺ ⇻ ⇼ ⇽ ⇾ ⇿ ⟵ ⟶ ⟷ ⟹ ⟺ ⟻ ⟼ ⟽ ⟾ ⟿
⤀ ⤁ ⤂ ⤃ ⤄ ⤅ ⤆ ⤇ ⤌ ⤍ ⤎ ⤏ ⤐ ⤑ ⤔ ⤕ ⤖ ⤗ ⤘ ⤝ ⤞ ⤟ ⤠ ⥄ ⥅ ⥆ ⥇ ⥈ ⥊ ⥋ ⥎ ⥐ ⥒ ⥓ ⥖ ⥗ ⥚ ⥛ ⥞
⥟ ⥢ ⥤ ⥦ ⥧ ⥨ ⥩ ⥪ ⥫ ⥬ ⥭ ⥰ ⧴ ⬱ ⬰ ⬲ ⬳ ⬴ ⬵ ⬶ ⬷ ⬸ ⬹ ⬺ ⬻ ⬼ ⬽ ⬾ ⬿ ⭀ ⭁ ⭂ ⭃ ⥷ ⭄ ⥺ ⭇ ⭈ ⭉
⭊ ⭋ ⭌ ← → ⇜ ⇝ ↜ ↝ ↩ ↪ ↫ ↬ ↼ ↽ ⇀ ⇁ ⇄ ⇆ ⇇ ⇉ ⇋ ⇌ ⇚ ⇛ ⇠ ⇢ ↷ ↶ ↺ ↻
`,
comparison: `
> < >= <= == === != !==
≥ ≤ ≡ ≠ ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≂ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐
≑ ≒ ≓ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾
≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠
⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧
⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑
⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸
⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣
⟂ ⫪ ⫫
`,
ellipsis: '… ⁝ ⋮ ⋱ ⋰ ⋯',
plus: `
++ |
− ¦ ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⟇ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁
⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣
`,
times: `
* / % & \\
⌿ ÷ · · ⋅ ∘ × ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷
⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗ ⨟
`,
bitshift: '<< >> >>>',
power: `
^
↑ ↓ ⇵ ⟰ ⟱ ⤈ ⤉ ⤊ ⤋ ⤒ ⤓ ⥉ ⥌ ⥍ ⥏ ⥑ ⥔ ⥕ ⥘ ⥙ ⥜ ⥝ ⥠ ⥡ ⥣ ⥥ ⥮ ⥯ ↑ ↓
`,
unary: '! ¬ √ ∛ ∜',
unary_plus: '+ - ± ∓',
};
const ESCAPE_SEQUENCE = token(seq(
'\\',
choice(
/[^uUx0-7]/,
/[uU][0-9a-fA-F]{1,6}/, // unicode codepoints
/[0-7]{1,3}/,
/x[0-9a-fA-F]{2}/,
),
));
// Keywords that can be quoted. Some still fail depending on the context.
const KEYWORDS = choice(
'baremodule',
'module',
'abstract',
'primitive',
'mutable',
'struct',
'quote',
'let',
'if',
'else',
'elseif',
'try',
'catch',
'finally',
'for',
'while',
'break',
'continue',
'using',
'import',
'const',
'global',
'local',
'end',
);
module.exports = grammar({
name: 'julia',
word: $ => $._word_identifier,
inline: $ => [
$._top_level,
$._terminator,
$._definition,
$._statement,
$._operation,
],
externals: $ => [
$._block_comment_rest,
$._immediate_paren,
$._immediate_bracket,
$._immediate_brace,
$._immediate_string_start,
$._immediate_command_start,
$._content_cmd_1,
$._content_cmd_1_raw,
$._content_cmd_3,
$._content_cmd_3_raw,
$._content_str_1,
$._content_str_1_raw,
$._content_str_3,
$._content_str_3_raw,
$._end_cmd,
$._end_str,
],
conflicts: $ => [
[$.juxtaposition_expression, $._primary_expression], // adjoint
[$.juxtaposition_expression, $._expression],
[$.matrix_row, $.comprehension_expression], // Comprehensions with newlines
[$.argument_list, $.tuple_expression],
[$.argument_list, $.parenthesized_expression],
[$.named_field, $._primary_expression],
],
supertypes: $ => [
$._expression,
$._statement,
$._definition,
],
extras: $ => [
/\s/,
$.line_comment,
$.block_comment,
],
rules: {
source_file: $ => optional($._block),
_block: $ => seq(
sep1($._terminator, $._top_level),
optional($._terminator)
),
_top_level: $ => choice(
$._expression,
$.assignment,
$.open_tuple,
),
open_tuple: $ => prec(PREC.tuple, seq(
$._expression,
repeat1(seq(',', $._expression))
)),
// assignments inside blocks (including top-level)
assignment: $ => prec.right(PREC.assign, seq(
choice(
$._primary_expression,
$.open_tuple,
$._operation,
$.operator,
),
alias('=', $.operator),
$._top_level,
)),
// assignments inside brackets
_closed_assignment: $ => prec.right(PREC.assign, seq(
choice(
$._primary_expression,
$._operation,
$.operator,
),
alias('=', $.operator),
choice(
$._expression,
alias($._closed_assignment, $.assignment),
),
)),
_expression: $ => choice(
$._definition,
$._statement,
$._primary_expression,
$._operation,
$.compound_assignment_expression,
$.macrocall_expression,
$.arrow_function_expression,
$.juxtaposition_expression,
$.ternary_expression,
$.operator,
$.integer_literal,
$.float_literal,
prec(-1, alias('begin', $.identifier)),
),
// Definitions
_definition: $ => choice(
$.module_definition,
$.abstract_definition,
$.primitive_definition,
$.struct_definition,
$.function_definition,
$.macro_definition,
),
module_definition: $ => seq(
choice('module', 'baremodule'),
field('name', choice($.identifier, $.interpolation_expression)),
optional($._terminator),
optional($._block),
'end',
),
// TODO: Rename
type_head: $ => prec(PREC.stmt, choice(
$._primary_expression,
$.binary_expression,
)),
abstract_definition: $ => seq(
'abstract',
'type',
$.type_head,
'end',
),
primitive_definition: $ => seq(
'primitive',
'type',
$.type_head,
$.integer_literal,
'end',
),
struct_definition: $ => seq(
optional('mutable'),
'struct',
$.type_head,
optional($._terminator),
optional($._block),
'end',
),
signature: $ => prec(PREC.stmt, choice(
$.identifier, // zero-method definition
$.call_expression,
$.argument_list, // anonymous function
$.typed_expression,
$.where_expression,
)),
function_definition: $ => seq(
'function',
$.signature,
optional($._terminator),
optional($._block),
'end',
),
macro_definition: $ => seq(
'macro',
$.signature,
optional($._terminator),
optional($._block),
'end',
),
// Statements
_statement: $ => choice(
// block statements:
$.compound_statement,
$.quote_statement,
$.let_statement,
$.if_statement,
$.try_statement,
$.for_statement,
$.while_statement,
// simple statements:
$.break_statement,
$.continue_statement,
$.return_statement,
$.const_statement,
$.global_statement,
$.local_statement,
$.export_statement,
$.import_statement,
$.public_statement,
$.using_statement,
),
compound_statement: $ => seq('begin', optional($._terminator), optional($._block), 'end'),
quote_statement: $ => seq('quote', optional($._terminator), optional($._block), 'end'),
let_statement: $ => seq(
'let',
sep(',', choice(
$.identifier,
alias($._closed_assignment, $.let_binding),
)),
$._terminator,
optional($._block),
'end',
),
if_statement: $ => seq(
'if',
field('condition', $._expression),
optional($._terminator),
optional($._block),
field('alternative', repeat($.elseif_clause)),
field('alternative', optional($.else_clause)),
'end',
),
elseif_clause: $ => seq(
'elseif',
field('condition', $._expression),
optional($._terminator),
optional($._block),
),
else_clause: $ => seq(
'else',
optional($._terminator),
optional($._block),
),
try_statement: $ => seq(
'try',
optional($._terminator),
optional($._block),
choice(
seq(
$.catch_clause,
optional($.else_clause),
optional($.finally_clause),
),
seq(
$.finally_clause,
optional($.catch_clause),
// `else` is not valid here.
),
),
'end',
),
catch_clause: $ => prec(1, seq(
'catch',
optional($.identifier),
optional($._terminator),
optional($._block),
)),
finally_clause: $ => seq(
'finally',
optional($._terminator),
optional($._block),
),
for_statement: $ => seq(
'for',
sep1(',', $.for_binding),
optional($._terminator),
optional($._block),
'end',
),
while_statement: $ => seq(
'while',
field('condition', $._expression),
optional($._terminator),
optional($._block),
'end',
),
break_statement: _ => 'break',
continue_statement: _ => 'continue',
return_statement: $ => prec.right(PREC.stmt, seq(
'return',
optional($._top_level),
)),
const_statement: $ => prec.right(PREC.stmt, seq(
'const',
$.assignment,
)),
global_statement: $ => prec.right(PREC.stmt, seq(
'global',
$._top_level,
)),
local_statement: $ => prec.right(PREC.stmt, seq(
'local',
$._top_level,
)),
import_alias: $ => seq($._importable, 'as', $._exportable),
import_path: $ => seq(
token(repeat1('.')),
choice(
$.identifier,
$.scoped_identifier,
),
),
_exportable: $ => choice(
$.identifier,
$.macro_identifier,
$.operator,
$.interpolation_expression,
parenthesize($._exportable),
),
_importable: $ => choice(
$._exportable,
$.scoped_identifier,
$.import_path,
),
_import_list: $ => prec.right(sep1(',', choice(
$._importable,
$.import_alias,
))),
selected_import: $ => seq(
$._importable,
token.immediate(':'),
$._import_list,
),
export_statement: $ => seq('export', prec.right(sep1(',', $._exportable))),
public_statement: $ => seq('public', prec.right(sep1(',', $._exportable))),
import_statement: $ => seq(
'import',
choice(
$._import_list,
$.selected_import,
),
),
using_statement: $ => seq(
'using',
choice(
$._import_list,
$.selected_import,
),
),
// Primary expressions can be called, indexed, accessed, and type parametrized.
_primary_expression: $ => choice(
$.identifier,
$.boolean_literal,
$.curly_expression, // Only valid in macros
$.parenthesized_expression,
$.tuple_expression,
$._array,
$._string,
$.adjoint_expression,
$.broadcast_call_expression,
$.call_expression,
alias($._closed_macrocall_expression, $.macrocall_expression),
$.parametrized_type_expression,
$.field_expression,
$.index_expression,
$.interpolation_expression,
$.quote_expression,
),
_array: $ => choice(
$.comprehension_expression,
$.matrix_expression,
$.vector_expression,
),
comprehension_expression: $ => prec(PREC.array, seq(
'[',
choice(
$._expression,
alias($._closed_assignment, $.assignment),
),
optional($._terminator),
$._comprehension_clause,
']',
)),
_comprehension_clause: $ => seq(
$.for_clause,
repeat(choice(
$.for_clause,
$.if_clause,
)),
),
if_clause: $ => seq(
'if',
$._expression,
),
for_clause: $ => prec.right(seq(
'for',
sep1(',', $.for_binding),
)),
for_binding: $ => prec(1, seq(
optional('outer'),
choice(
$.identifier,
$.tuple_expression,
$.typed_expression,
$.interpolation_expression,
),
alias(choice('in', '=', '∈'), $.operator),
$._expression,
)),
matrix_expression: $ => prec(PREC.array, seq(
'[',
$.matrix_row,
repeat(seq($._terminator, $.matrix_row)),
optional($._terminator),
']',
)),
matrix_row: $ => repeat1(prec(PREC.array, choice(
$._expression,
alias($._closed_assignment, $.assignment),
))),
vector_expression: $ => seq(
'[',
sep(',', choice(
$._expression,
alias($._closed_assignment, $.assignment),
)),
optional(','),
']',
),
parenthesized_expression: $ => parenthesize(
sep1(';', choice(
$._expression,
alias($._closed_assignment, $.assignment),
)),
optional($._comprehension_clause),
optional(';'),
),
tuple_expression: $ => parenthesize(optional(
choice(
// Singleton requires comma
seq(
choice($._expression, $.named_field),
',',
),
seq(
choice($._expression, $.named_field),
repeat1(seq(',', choice($._expression, $.named_field))),
optional(choice(
$._comprehension_clause,
',',
)),
),
';', // Empty NamedTuple
// NamedTuple with leading semicolon
seq(
';',
sep1(',', choice($._expression, $.named_field)),
optional(','),
),
),
)),
curly_expression: $ => seq(
'{',
sep(',', choice(
$._expression,
alias($._closed_assignment, $.assignment),
)),
optional(','),
'}',
),
adjoint_expression: $ => prec(PREC.postfix, seq(
$._primary_expression,
token.immediate('\''),
)),
field_expression: $ => prec(PREC.dot, seq(
field('value', $._primary_expression),
token.immediate('.'),
choice(
$.identifier,
$.interpolation_expression,
$.quote_expression,
$._string,
),
)),
index_expression: $ => seq(
$._primary_expression,
$._immediate_bracket,
$._array,
),
parametrized_type_expression: $ => seq(
$._primary_expression,
$._immediate_brace,
$.curly_expression,
),
call_expression: $ => prec(PREC.call, seq(
choice($._primary_expression, $.operator),
$._immediate_paren,
$.argument_list,
optional($.do_clause),
)),
broadcast_call_expression: $ => prec(PREC.call, seq(
$._primary_expression,
token.immediate('.'),
$._immediate_paren,
$.argument_list,
optional($.do_clause),
)),
_closed_macrocall_expression: $ => prec(PREC.call, seq(
optional(seq(
$._primary_expression,
token.immediate('.'),
)),
$.macro_identifier,
choice(
seq($._immediate_brace, $.curly_expression),
seq($._immediate_bracket, $._array),
seq($._immediate_paren, $.argument_list, optional($.do_clause)),
),
)),
macrocall_expression: $ => prec.right(seq(
optional(seq(
$._primary_expression,
token.immediate('.'),
)),
$.macro_identifier,
optional($.macro_argument_list),
)),
macro_argument_list: $ => prec.left(repeat1(prec(PREC.macro_arg, $._top_level))),
argument_list: $ => parenthesize(
optional(';'),
sep(choice(',', ';'), choice(
$._expression,
alias($._closed_assignment, $.named_argument),
seq($._expression, $._comprehension_clause),
)),
optional(','),
),
do_clause: $ => seq(
'do',
alias($._do_parameter_list, $.argument_list),
optional($._block),
'end',
),
_do_parameter_list: $ => seq(
sep(',', choice(
$.identifier,
$.splat_expression,
$.typed_expression,
$.tuple_expression,
$.parenthesized_expression,
)),
$._terminator,
),
named_field: $ => seq(
$.identifier,
'=',
$._expression,
),
interpolation_expression: $ => prec.right(PREC.prefix, seq(
'$',
choice(
$.integer_literal,
$.float_literal,
$.identifier,
$.curly_expression,
$.parenthesized_expression,
$.tuple_expression,
$._array,
$._string,
),
)),
quote_expression: $ => prec.right(PREC.prefix, seq(
':',
choice(
$.integer_literal,
$.float_literal,
$._string,
$.identifier,
$.operator,
seq($._immediate_brace, $.curly_expression),
seq($._immediate_bracket, $._array),
seq($._immediate_paren, choice(
$.parenthesized_expression,
$.tuple_expression,
// Syntactic operators in parentheses
parenthesize(
alias(
choice(
'::', ':=', '.=', '=',
$._assignment_operator,
$._lazy_or_operator,
$._lazy_and_operator,
$._syntactic_operator,
),
$.operator,
),
),
)),
// Syntactic operators without parentheses
alias(
choice(
$._assignment_operator,
$._lazy_or_operator,
$._lazy_and_operator,
$._syntactic_operator,
),
$.operator,
),
alias(token.immediate(KEYWORDS), $.identifier),
),
)),
// Operations
_operation: $ => choice(
$.unary_expression,
$.binary_expression,
$.range_expression,
$.splat_expression,
$.typed_expression,
$.unary_typed_expression,
$.where_expression,
),
binary_expression: $ => {
const table = [
[prec.right, PREC.pair, $._pair_operator],
[prec.right, PREC.arrow, $._arrow_operator],
[prec.left, PREC.lazy_or, $._lazy_or_operator],
[prec.left, PREC.lazy_and, $._lazy_and_operator],
[prec.left, PREC.comparison, choice('in', 'isa', $._comparison_operator, $._type_order_operator)],
[prec.right, PREC.pipe_left, $._pipe_left_operator],
[prec.left, PREC.pipe_right, $._pipe_right_operator],
[prec.left, PREC.colon, $._ellipsis_operator],
[prec.left, PREC.plus, choice($._unary_plus_operator, $._plus_operator)],
[prec.left, PREC.times, $._times_operator],
[prec.left, PREC.rational, $._rational_operator],
[prec.left, PREC.bitshift, $._bitshift_operator],
[prec.left, PREC.power, $._power_operator],
];
return choice(...table.map(([fn, prec, op]) => fn(prec, seq(
$._expression,
alias(op, $.operator),
$._expression,
))));
},
unary_expression: $ => prec.right(PREC.prefix, seq(
alias(choice(
$._tilde_operator,
$._type_order_operator,
$._unary_operator,
$._unary_plus_operator,
), $.operator),
$._expression,
)),
range_expression: $ => prec.left(PREC.colon, seq(
$._expression,
token.immediate(':'),
$._expression,
)),
splat_expression: $ => prec(PREC.colon, seq($._expression, '...')),
ternary_expression: $ => prec.right(PREC.conditional, seq(
$._expression,
'?',
choice(
$._expression,
$.assignment,
),
':',
choice(
$._expression,
$.assignment,
),
)),
typed_expression: $ => prec(PREC.decl, seq(
$._expression,
'::',
$._primary_expression,
)),
unary_typed_expression: $ => prec.right(PREC.prefix, seq(
'::',
$._primary_expression,
)),
arrow_function_expression: $ => prec.right(PREC.afunc, seq(
choice(
$.identifier,
$.argument_list,
$.typed_expression,
),
'->',
choice(
$._expression,
alias($._closed_assignment, $.assignment),
),
)),
juxtaposition_expression: $ => prec.left(seq(
choice(
$.integer_literal,
$.float_literal,
$.adjoint_expression,
),
$._primary_expression,
)),
compound_assignment_expression: $ => prec.right(PREC.assign, seq(
$._primary_expression,
alias(choice($._assignment_operator, $._tilde_operator), $.operator),
$._expression,
)),
where_expression: $ => prec.left(PREC.where, seq(
$._expression,
'where',
$._expression,
)),
// Tokens
macro_identifier: $ => seq('@', choice(
$.identifier,
$.scoped_identifier,
$.operator,
alias($._syntactic_operator, $.operator),
)),
scoped_identifier: $ => seq(
choice($.identifier, $.scoped_identifier),
token.immediate('.'),
choice(
$.identifier,
$.interpolation_expression,
$.quote_expression,
),
),
_word_identifier: _ => {
const nonIdentifierCharacters = [
'#',
'$',
',',
':',
';',
'@',
'~',
'(', ')',
'{', '}',
...Object.values(OPERATORS),
].join(' ')
.trim()
.replace(/!/g, '')
.replace(/-/g, '')
.replace(/\\/g, '\\\\')
.replace(/\s+/g, '');
// Some symbols in Sm and So unicode categories that are identifiers
const validMathSymbols = '°∀-∇∎-∑∫-∳';
const start = `[_\\p{XID_Start}${validMathSymbols}\\p{Emoji}&&[^0-9#*]]`;
const rest = `[^"'\`\\s\\.\\-\\[\\]${nonIdentifierCharacters}]*`;
return new RegExp(start + rest);
},
identifier: $ => $._word_identifier,
// Literals
boolean_literal: _ => choice('true', 'false'),
integer_literal: _ => choice(
token(seq('0b', numeral('01'))),
token(seq('0o', numeral('0-7'))),
token(seq('0x', numeral('0-9a-fA-F'))),
numeral('0-9'),
),
float_literal: _ => {
const dec = numeral('0-9');
const hex = numeral('0-9a-fA-F');
const exponent = /[eEf][+-]?\d+/;
const hex_exponent = /p[+-]?\d+/;
const leading_period = token(seq(
'.',
dec,
optional(exponent),
));
// This has to be split into two tokens to avoid conflicts with ellipsis
const trailing_period = seq(
dec,
token.immediate(seq(
'.',
optional(dec),
optional(exponent),
)),
);
const just_exponent = token(seq(dec, exponent));
const hex_float = token(seq(
choice(
seq('0x', hex, optional('.'), optional(hex)),
seq('0x.', hex),
),
hex_exponent,
));
return choice(leading_period, trailing_period, just_exponent, hex_float);
},
_string: $ => choice(
$.character_literal,
$.string_literal,
$.command_literal,
$.prefixed_string_literal,
$.prefixed_command_literal,
),
escape_sequence: _ => ESCAPE_SEQUENCE,
character_literal: _ => token(seq(
'\'',
choice(
/[^'\\]/,
ESCAPE_SEQUENCE,
),
'\'',
)),
_delimiter_str_1: _ => '"',
_delimiter_str_3: _ => '"""',
_delimiter_cmd_1: _ => '`',
_delimiter_cmd_3: _ => '```',
string_literal: $ => choice(
seq(
$._delimiter_str_1,
repeat(choice(alias($._content_str_1, $.content), $.string_interpolation, $.escape_sequence)),
$._end_str,
),
seq(
$._delimiter_str_3,
repeat(choice(alias($._content_str_3, $.content), $.string_interpolation, $.escape_sequence)),
$._end_str,
),
),
command_literal: $ => choice(
seq(
$._delimiter_cmd_1,
repeat(choice(alias($._content_cmd_1, $.content), $.string_interpolation, $.escape_sequence)),
$._end_cmd,
),
seq(
$._delimiter_cmd_3,
repeat(choice(alias($._content_cmd_3, $.content), $.string_interpolation, $.escape_sequence)),
$._end_cmd,
),
),
prefixed_string_literal: $ => prec.left(seq(
field('prefix', $.identifier),
$._immediate_string_start,
choice(
seq(
$._delimiter_str_1,
repeat(choice(alias($._content_str_1_raw, $.content), $.escape_sequence)),
$._end_str,
),
seq(
$._delimiter_str_3,
repeat(choice(alias($._content_str_3_raw, $.content), $.escape_sequence)),
$._end_str,
),
),
optional(field('suffix', $.identifier)),
)),
prefixed_command_literal: $ => prec.left(seq(
field('prefix', $.identifier),
$._immediate_command_start,
choice(
seq(
$._delimiter_cmd_1,
repeat(choice(alias($._content_cmd_1_raw, $.content), $.escape_sequence)),
$._end_cmd,
),
seq(
$._delimiter_cmd_3,
repeat(choice(alias($._content_cmd_3_raw, $.content), $.escape_sequence)),
$._end_cmd,
),
),
optional(field('suffix', $.identifier)),
)),
string_interpolation: $ => seq(
'$',
choice(
$.identifier,
seq($._immediate_paren, parenthesize(choice(
$._expression,
alias($.named_field, $.assignment),
))),
),
),
operator: $ => choice(
// NOTE: Syntactic operators (&&, +=, etc) cannot be used as identifiers.
$._pair_operator,
$._arrow_operator,
$._comparison_operator,
$._pipe_left_operator,
$._pipe_right_operator,
$._ellipsis_operator,
':',
$._plus_operator,
$._times_operator,
$._rational_operator,
$._bitshift_operator,
$._power_operator,
$._tilde_operator,
$._type_order_operator,
$._unary_operator,
$._unary_plus_operator,
),
_assignment_operator: _ => choice(':=', '$=', '.=', addDot(OPERATORS.assignment)),
_pair_operator: _ => addDot('=>'),
_arrow_operator: _ => addDot(OPERATORS.arrow),
_lazy_or_operator: _ => addDot('||'),
_lazy_and_operator: _ => addDot('&&'),
_comparison_operator: _ => addDot(OPERATORS.comparison),
_pipe_right_operator: _ => addDot('|>'),
_pipe_left_operator: _ => addDot('<|'),
_ellipsis_operator: _ => token(choice('..', addDot(OPERATORS.ellipsis))),
_plus_operator: _ => addDot(OPERATORS.plus),
_times_operator: _ => addDot(OPERATORS.times),
_rational_operator: _ => addDot('//'),
_bitshift_operator: _ => addDot(OPERATORS.bitshift),
_power_operator: _ => addDot(OPERATORS.power),
_tilde_operator: _ => addDot('~'), // unary or assignment
_type_order_operator: _ => addDot('<: >:'), // unary or comparison
_unary_operator: _ => addDot(OPERATORS.unary),
_unary_plus_operator: _ => addDot(OPERATORS.unary_plus),
_syntactic_operator: _ => choice('$', '.', '...', '->', '?'),
_terminator: _ => choice(/\r?\n/, /;+/),
block_comment: $ => seq(/#=/, $._block_comment_rest),
// FIXME: This is currently a seq to avoid conflicts with block_comment
line_comment: _ => seq(/#/, /.*/),
},
});
/**
*
* @param {RuleOrLiteral} separator
* @param {RuleOrLiteral} rule
*/
function sep(separator, rule) {
return optional(sep1(separator, rule));
}
/**
*
* @param {RuleOrLiteral} separator
* @param {RuleOrLiteral} rule
*/
function sep1(separator, rule) {
return seq(rule, repeat(seq(separator, rule)));
}
/**
*
* @param {string} operatorString
*/
function addDot(operatorString) {
const operators = operatorString.trim().split(/\s+/);
return token(seq(optional('.'), choice(...operators)));
}
/**
* @param {string} range
*/
function numeral(range) {
return RegExp(`[${range}]([${range}]|_[${range}])*`);
}
/**
*
* @param {...any} rules
*/
function parenthesize(...rules) {
return seq('(', ...rules, ')');
}