UNPKG

tree-sitter-quik

Version:
676 lines (621 loc) 20.8 kB
const PREC = Object.freeze({ NAMED_INFIX: 1, OPERATOR_INFIX: 2, IMPLICATION: 3, OR: 4, AND: 5, EQUALITY: 6, ORDER: 7, SUM: 8, PRODUCT: 9, EXPONENTIATION: 10, NOT: 11, PREFIX: 12, INLINE_EXPRESSION: 13, TYPE: 14, CALL: 15, PIPELINE: 16, DESTRUCTURING_PATTERN: 17 }); module.exports = grammar({ name: 'quik', externals: $ => [ $._newline, $._indent, $._dedent, $._string_start, $._string_content, $._string_end ], extras: $ => [$.comment, /\s+/], word: $ => $._identifier_without_operators, conflicts: $ => [[$.parameter, $._simple_expression], [$.type_constructor, $.polymorphic_type_constructor], [$.polymorphic_type]], rules: { program: $ => seq(optional($.hash_bang_line), optional($._statement_seq)), _statement: $ => choice($._simple_statement, $._compound_statement), _statement_seq: $ => choice( $._statement, seq($._simple_statement, $._newline, optional($._statement_seq)), seq($._compound_statement, $._statement_seq) ), _simple_statement: $ => choice( alias($.default_import, $.import), alias($.named_import, $.import), alias($.external_import, $.import), alias($.single_named_export, $.export), alias($.named_export, $.export), $._simple_expression ), _compound_statement: $ => choice( alias($.compound_export, $.export), $._compound_expression ), type_pair_or_type_list: $ => commaSep1($._type_pair_or_type), type_hint_list: $ => commaSep1($.type_hint), default_import: $ => seq( 'import', field('type', $.type), optional(seq( 'from', field('source', $.string) )) ), named_import: $ => seq( 'import', '{', field('types', $.type_pair_or_type_list), '}', 'from', field('source', $.string) ), external_import: $ => seq( 'import', 'extern', '{', field('type_hints', $.type_hint_list), '}', 'from', field('source', $.string) ), single_named_export: $ => seq( 'export', optional(field('default', 'default')), field('type', $.type), optional(seq( 'from', field('source', $.string) )) ), named_export: $ => seq( 'export', '{', field('types', $.type_pair_or_type_list), '}', optional(seq( 'from', field('source', $.string) )) ), compound_export: $ => seq( 'export', optional(field('default', 'default')), field('declaration', $._compound_declaration) ), block: $ => seq($._indent, $._expression_seq, $._dedent), refinement_block: $ => seq( $._indent, repeat1($.refinement_clause), optional(field('default', $.refinement_else_clause)), $._dedent ), _refinement_condition: $ => seq('|', $._simple_expression), refinement_clause: $ => seq( field('condition', $._refinement_condition), '=>', field('body', choice($._simple_expression, $.block)) ), refinement_else_clause: $ => seq( '|', 'else', '=>', field('body', choice($._simple_expression, $.block)) ), type_pair: $ => seq( $.type, '=>', $.type ), _type_pair_or_type: $ => choice($.type_pair, $.type), type_arguments: $ => commaSep1($.generic_type), type_parameters: $ => commaSep1($.polymorphic_type), intersection_type: $ => prec.right(PREC.TYPE, intersectionType($.generic_type)), union_type: $ => prec.right(PREC.TYPE, unionType($.generic_type)), map_type: $ => prec(PREC.TYPE, mapType($.generic_type)), tuple_type: $ => prec(PREC.TYPE, tupleType($.generic_type)), list_type: $ => prec(PREC.TYPE, listType($.generic_type)), refinement_type: $ => prec(PREC.TYPE, refinementType($.generic_type, $.identifier, $._simple_expression)), type_constructor: $ => prec.right(PREC.TYPE, typeConstructor($.type, $.type_arguments, 'arguments')), _grouped_type: $ => prec(PREC.TYPE, groupedType($.generic_type)), _atomic_type: $ => prec(PREC.TYPE, choice( $.intersection_type, $.union_type, $.map_type, $.tuple_type, $.list_type, $.refinement_type, $.type_constructor, $._grouped_type )), _curried_type: $ => prec.left(curriedType($._curried_type, $._atomic_type)), generic_type: $ => prec(PREC.TYPE, $._curried_type), polymorphic_intersection_type: $ => prec.right(PREC.TYPE, intersectionType($.polymorphic_type)), polymorphic_union_type: $ => prec.right(PREC.TYPE, unionType($.polymorphic_type)), polymorphic_map_type: $ => prec(PREC.TYPE, mapType($.polymorphic_type)), polymorphic_tuple_type: $ => prec(PREC.TYPE, tupleType($.polymorphic_type)), polymorphic_list_type: $ => prec(PREC.TYPE, listType($.polymorphic_type)), polymorphic_refinement_type: $ => prec(PREC.TYPE, refinementType($.polymorphic_type, $.identifier, $._simple_expression)), polymorphic_type_constructor: $ => prec.right(PREC.TYPE, typeConstructor($.type, $.type_parameters, 'parameters')), _grouped_polymorphic_type: $ => prec(PREC.TYPE, groupedType($.polymorphic_type)), polymorphic_type_condition: $ => prec(PREC.TYPE, seq( field('interface', $.generic_type), field('variable', alias($.identifier, $.type_variable)) )), polymorphic_type_conditions: $ => prec(PREC.TYPE, commaSep1($.polymorphic_type_condition)), _atomic_polymorphic_type: $ => prec(PREC.TYPE, choice( $.polymorphic_intersection_type, $.polymorphic_union_type, $.polymorphic_map_type, $.polymorphic_tuple_type, $.polymorphic_list_type, $.polymorphic_refinement_type, $.polymorphic_type_constructor, $._grouped_polymorphic_type, alias($.identifier, $.type_variable) )), _polymorphic_curried_type: $ => prec.left(curriedType($._polymorphic_curried_type, $._atomic_polymorphic_type)), polymorphic_type: $ => prec(PREC.TYPE, choice( $._polymorphic_curried_type, seq( field('conditions', $.polymorphic_type_conditions), '=>', $._polymorphic_curried_type ) )), parameter: $ => prec(PREC.INLINE_EXPRESSION, choice( field('value', $._literal), seq( choice( field('name', $.identifier), field('pattern', $._destructuring_pattern) ), optional(seq( ':', field('type', $.polymorphic_type) )), optional(seq( '=', field('default', $._simple_expression) )) ) )), rest_parameter: $ => seq( '...', field('name', $.identifier), optional(seq( ':', field('type', $.polymorphic_type) )), optional(seq( '=', field('default', $._simple_expression) )) ), parameters: $ => choice( seq( commaSep1($.parameter), optional(seq( ',', field('rest', $.rest_parameter) )) ), field('rest', $.rest_parameter) ), argument: $ => choice($._expression, '?'), arguments: $ => commaSep1($.argument), _destructuring_pattern: $ => prec(PREC.DESTRUCTURING_PATTERN, choice( alias($.map, $.map_pattern), // alias($.tuple, $.tuple_pattern), alias($.list, $.list_pattern) )), _expression: $ => choice($._simple_expression, $._compound_expression), _expression_seq: $ => choice( $._expression, seq($._simple_expression, $._newline, optional($._expression_seq)), seq($._compound_expression, $._expression_seq) ), expression_pair: $ => seq($._expression, '=>', $._expression), _compound_expression: $ => choice( $._compound_declaration, alias($.compound_function, $.function), $.if, $.case, $.atomic, alias($.compound_return, $.return), alias($.compound_hold, $.hold), alias($.compound_do, $.do), alias($.compound_include, $.include), alias($.compound_assignment, $.assignment) ), _compound_declaration: $ => choice( alias($.compound_struct, $.struct), alias($.compound_module, $.module), alias($.compound_interface, $.interface) ), compound_struct: $ => seq( optional(field('mutable', 'mutable')), 'struct', field('name', $.polymorphic_type), field('body', $.block) ), compound_module: $ => seq( optional(field('mutable', 'mutable')), 'module', field('name', $.polymorphic_type), field('body', $.block) ), interface_block: $ => seq( $._indent, choice( $.pass, repeat1(choice( $.type_hint, alias($.compound_include, $.include), alias($.simple_include, $.include) )) ), $._dedent ), compound_interface: $ => seq( 'interface', field('name', $.polymorphic_type), field('body', $.interface_block) ), compound_function: $ => seq( '(', optional(field('parameters', $.parameters)), ')', choice( seq( '=>', field('body', $.block) ), field('clauses', $.refinement_block) ) ), else_clause: $ => seq( 'else', field('body', $.block) ), else_if_clause: $ => seq( 'else if', field('condition', $._simple_expression), field('body', $.block) ), else_if_clauses: $ => repeat1($.else_if_clause), if: $ => seq( 'if', field('condition', $._simple_expression), field('body', $.block), optional(field('else_if_clauses', $.else_if_clauses)), optional(field('else_clause', $.else_clause)) ), expression_list: $ => commaSep1($._expression), when_clause: $ => seq( 'when', field('values', $.expression_list), field('body', $.block) ), when_clauses: $ => repeat1($.when_clause), case: $ => seq( 'case', field('expression', $._simple_expression), $._newline, field('when_clauses', $.when_clauses), optional(field('else_clause', $.else_clause)) ), atomic: $ => seq('atomic', $.block), compound_return: $ => seq('return', $._compound_expression), compound_hold: $ => seq('hold', $._compound_expression), compound_do: $ => seq('do', $._compound_expression), compound_include: $ => seq( 'include', field('declaration', $._compound_declaration) ), compound_assignment: $ => seq( field('left', choice($.type_hint, $.identifier, $._destructuring_pattern, $.pipeline)), '=', field('right', $._compound_expression) ), _simple_expression: $ => prec(PREC.INLINE_EXPRESSION, choice( alias($.simple_return, $.return), $.pass, alias($.simple_hold, $.hold), alias($.simple_do, $.do), $.retry, alias($.simple_include, $.include), alias($.simple_function, $.function), $._grouped_expression, $.map, $.tuple, $.list, $.list_comprehension, $.call, $.prefix_call, $.infix_call, $.pipeline, alias($.simple_if, $.if), alias($.simple_assignment, $.assignment), $.type_assignment, $.type_hint, $._literal, $.generic_type, $.identifier )), simple_return: $ => prec.right(seq('return', optional($._simple_expression))), pass: $ => 'pass', simple_hold: $ => prec.right(seq('hold', $._simple_expression)), simple_do: $ => prec.right(seq('do', $._simple_expression)), retry: $ => 'retry', type_list: $ => prec.left(commaSep1($.polymorphic_type)), simple_include: $ => seq('include', field('types', $.type_list)), simple_function: $ => seq( '(', optional(field('parameters', $.parameters)), ')', choice( field('clause', $.refinement_clause), seq( '=>', field('body', $._simple_expression) ) ) ), _grouped_expression: $ => seq('(', $._simple_expression, ')'), map: $ => seq( '{', commaSep(optional(choice( $.expression_pair, alias($.identifier, $.shorthand_pair_identifier), $.spread ))), '}' ), tuple: $ => seq( '(', commaSep2(choice( $._expression, $.spread )), ')' ), list: $ => seq( '[', commaSep(choice( $._expression, $.spread, $.range )), ']' ), spread: $ => seq('...', $._simple_expression), range: $ => seq($._expression, '..', $._expression), list_comprehension: $ => seq( '[', field('expression', $._expression), '|', field('generators', $.generators), ']' ), generators: $ => commaSep1($.generator), generator: $ => seq( field('variable', $.identifier), 'in', field('expression', $._simple_expression), optional(field('condition', $.generator_condition)) ), generator_condition: $ => seq('if', $._simple_expression), call: $ => prec(PREC.CALL, seq( $._simple_expression, '(', optional(field('arguments', $.arguments)), ')' )), prefix_call: $ => prec.right(PREC.PREFIX, seq( field('operator', $.identifier), field('expression', $._simple_expression) )), infix_call_operator: $ => $._operator, infix_call: $ => choice( prec.left(PREC.NOT, seq(field('left', $._simple_expression), field('operator', alias('!', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.EXPONENTIATION, seq(field('left', $._simple_expression), field('operator', alias('^', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.PRODUCT, seq(field('left', $._simple_expression), field('operator', alias('*', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.PRODUCT, seq(field('left', $._simple_expression), field('operator', alias('/', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.PRODUCT, seq(field('left', $._simple_expression), field('operator', alias('%', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.SUM, seq(field('left', $._simple_expression), field('operator', alias('+', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.SUM, seq(field('left', $._simple_expression), field('operator', alias('-', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.ORDER, seq(field('left', $._simple_expression), field('operator', alias('<', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.ORDER, seq(field('left', $._simple_expression), field('operator', alias('<=', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.ORDER, seq(field('left', $._simple_expression), field('operator', alias('>', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.ORDER, seq(field('left', $._simple_expression), field('operator', alias('>=', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.EQUALITY, seq(field('left', $._simple_expression), field('operator', alias('==', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.EQUALITY, seq(field('left', $._simple_expression), field('operator', alias('!=', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.EQUALITY, seq(field('left', $._simple_expression), field('operator', alias('===', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.EQUALITY, seq(field('left', $._simple_expression), field('operator', alias('!==', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.AND, seq(field('left', $._simple_expression), field('operator', alias('&&', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.OR, seq(field('left', $._simple_expression), field('operator', alias('||', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.IMPLICATION, seq(field('left', $._simple_expression), field('operator', alias('==>', $.infix_call_operator)), field('right', $._simple_expression))), prec.left(PREC.OPERATOR_INFIX, seq( field('left', $._simple_expression), field('operator', $.infix_call_operator), field('right', $._simple_expression)) ), prec.left(PREC.NAMED_INFIX, seq( field('left', $._simple_expression), '`', field('operator', alias($._identifier_without_operators, $.infix_call_operator)), '`', field('right', $._simple_expression)) ) ), pipeline: $ => prec.left(PREC.PIPELINE, seq( $._simple_expression, optional(field('safe', '&')), '.', $._simple_expression )), simple_if: $ => prec.right(seq( 'if', field('condition', $._simple_expression), 'then', field('body', $._simple_expression), optional(seq( 'else', field('else_body', $._simple_expression) )) )), simple_assignment: $ => seq( field('left', choice($.type_hint, $.identifier, $._destructuring_pattern, $.pipeline)), '=', field('right', $._simple_expression) ), type_assignment: $ => seq( 'type', field('left', $.polymorphic_type), '=', field('right', $.polymorphic_type) ), type_hint: $ => prec.right(seq( field('identifier', $.identifier), ':', field('type', $.polymorphic_type) )), _literal: $ => choice( $.number, $.string, $.regex, $.boolean, $.nil ), hash_bang_line: $ => /#!.*/, nil: $ => 'nil', boolean: $ => choice('false', 'true'), _decimal: $ => { const digits = repeat1(/_?[0-9]+/) const exponent = seq(/e-?/, digits) return token(choice( seq(digits, '.', digits, optional(exponent)), seq(digits, exponent) )) }, _integer: $ => token(choice( seq('0x', repeat1(/_?[A-Fa-f0-9]+/)), seq('0o', repeat1(/_?[0-7]+/)), seq('0b', repeat1(/_?[0-1]+/)), seq(repeat1(/_?[0-9]+/)) )), number: $ => choice($._decimal, $._integer), type: $ => /_?[A-Z0-9][a-zA-Z0-9]*/, _identifier_without_operators: $ => /_?[a-z][a-z0-9_]*\??/, _operator: $ => /(==|[!@#$%^&*|<>~*\-+/]+)=*>?/, identifier: $ => choice($._operator, $._identifier_without_operators), string: $ => seq( $._string_start, repeat(choice($.interpolation, $.escape_sequence, $._string_content)), $._string_end ), interpolation: $ => seq( '{', field('expression', $._simple_expression), '}' ), escape_sequence: $ => token.immediate(seq( '\\', choice( /[^xu0-7]/, /[0-7]{1,3}/, /x[0-9a-fA-F]{2}/, /u[0-9a-fA-F]{4}/, /u{[0-9a-fA-F]+}/ ) )), regex: $ => seq( '/', $.regex_pattern, token.immediate('/'), optional($.regex_flags) ), regex_pattern: $ => token.immediate( repeat1(choice(seq( '[', repeat(choice(seq('\\', /./), /[^\]\n\\]/)), ']' ), seq('\\', /./), /[^/\\\[\n]/)) ), regex_flags: $ => token.immediate(/[a-z]+/), comment: $ => token(seq('#', /.*/)) } }); function intersectionType(type) { return seq(field('left', type), '&', field('right', type)); } function unionType(type) { return seq(field('left', type), '|', field('right', type)); } function mapType(type) { return seq('{', field('key', type), '=>', field('value', type), '}'); } function tupleType(type) { return seq('(', commaSep2(type), ')'); } function listType(type) { return seq('[', type, ']'); } function refinementType(type, variable, condition) { return seq( '[', field('variable', variable), 'in', field('type', type), '|', field('condition', condition), ']' ); } function typeConstructor(type, arguments, argumentsFieldName) { return seq( field('base', type), optional(seq( '<', field(argumentsFieldName, arguments), '>' )) ); } function groupedType(type) { return seq('(', type, ')'); } function curriedType(self, atomicType) { return choice( atomicType, seq(atomicType, '->', optional(self)) ); } function commaSep2(rule) { return seq(rule, repeat1(seq(',', rule))); } function commaSep1(rule) { return seq(rule, repeat(seq(',', rule))); } function commaSep(rule) { return optional(commaSep1(rule)); }