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, ')'); }