const PREC = { LOGICAL_OR: 1, LOGICAL_AND: 2, BITWISE_OR: 3, BITWISE_XOR: 4, BITWISE_AND: 5, COMPARISON: 6, SHIFT: 7, ADD: 8, MULTIPLY: 9, UNARY: 10, POSTFIX: 11, }; module.exports = grammar({ name: 'qk', word: $ => $.identifier, extras: $ => [ /[\s\uFEFF\u2060\u200B]/, /\\\r?\n/, $.line_comment, $.block_comment, ], supertypes: $ => [ $.expression, $.statement, $.type, ], conflicts: $ => [ [$.binding_declaration, $.type_identifier], [$.function_name, $.type_identifier], [$.expression, $.type_identifier], [$.import_spec], [$.for_each_clause, $.expression], [$.for_each_clause, $.expression, $.type_identifier], [$.for_each_destructure, $.expression], [$.for_statement, $.expression], [$.sequence_literal, $.slice_type], [$.sequence_literal, $.array_type], [$.statement, $.when_expression_block], [$.expression_statement, $.when_expression_block], [$.named_type], [$.when_expression_block, $.when_type_block], [$.expression, $.struct_literal], ], rules: { source_file: $ => repeat($._top_level_item), _top_level_item: $ => choice( $.module_declaration, $.import_declaration, $.function_definition, $.type_definition, $.binding_declaration, $.multi_binding_declaration, $.compile_time_when, $.compiler_error_directive, $.compiler_assert_directive, $.attribute, ';', ), module_declaration: $ => prec.right(seq( 'module', field('name', $.module_path), repeat(field('attribute', $.attribute)), )), import_declaration: $ => seq( 'import', choice( $.import_spec, seq('(', repeat(seq($.import_spec, optional(','))), ')'), ), ), import_spec: $ => seq( field('path', $.module_path), optional(field('alias', $.identifier)), ), module_path: $ => prec.left(seq( $.identifier, repeat(seq('.', $.identifier)), )), function_definition: $ => prec.right(seq( optional('pub'), 'let', field('name', $.function_name), field('parameters', $.parameter_list), optional(seq(':', field('return_type', $.return_type))), repeat(field('attribute', $.attribute)), optional(field('body', choice( $.block, seq('=', $.expression), ))), )), function_name: $ => choice( field('function', $.identifier), seq( field('owner', choice( $.identifier, seq('(', $.type, ')'), )), '.', field('method', $.identifier), ), ), parameter_list: $ => seq( '(', optional(seq( commaSep1(choice( $.receiver_parameter, $.type_parameter, $.parameter, $.variadic_parameter, )), optional(','), )), ')', ), receiver_parameter: $ => choice( alias('self', $.identifier), seq('*', optional('mut'), alias('self', $.identifier)), ), type_parameter: $ => seq( '$', field('name', $.type_identifier), ':', 'type', optional(seq('(', field('constraint', $.type), ')')), ), type_parameter_list: $ => seq( '(', commaSep1($.type_parameter), optional(','), ')', ), parameter: $ => seq( commaSep1($.parameter_name), ':', optional('...'), field('type', $.type), optional(seq('=', field('default', $.expression))), ), parameter_name: $ => seq( optional('mut'), field('name', $.identifier), optional(seq('=', field('default', $.expression))), ), variadic_parameter: _ => '...', return_type: $ => choice( $.type, seq('(', commaSep1($.type), ')'), ), type_definition: $ => seq( optional('pub'), 'let', field('name', $.type_identifier), optional(field('type_parameters', $.type_parameter_list)), '=', 'type', optional('alias'), field('value', $.type), ), binding_declaration: $ => prec.right(seq( optional('pub'), 'let', optional('mut'), optional(field('compile_time', '$')), field('name', $.identifier), optional(seq(':', field('type', $.type))), choice( seq( '=', field('value', $.expression), repeat(field('attribute', $.attribute)), ), repeat1(field('attribute', $.attribute)), ), )), multi_binding_declaration: $ => seq( 'let', optional('mut'), field('name', $.identifier), ',', commaSep1(field('name', $.identifier)), '=', field('value', $.expression), ), block: $ => seq('{', repeat(seq($.statement, optional(';'))), '}'), statement: $ => choice( $.function_definition, $.type_definition, $.binding_declaration, $.multi_binding_declaration, $.assignment_statement, $.return_statement, $.break_statement, $.continue_statement, $.defer_statement, $.for_statement, $.compile_time_when, $.compiler_error_directive, $.compiler_assert_directive, $.attribute, $.expression_statement, ), assignment_statement: $ => seq( commaSep1(field('left', $.expression)), field('operator', choice( '=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', )), field('right', $.expression), ), return_statement: $ => prec.right(seq('return', optional(commaSep1($.expression)))), break_statement: _ => 'break', continue_statement: _ => 'continue', defer_statement: $ => seq('defer', $.expression), expression_statement: $ => $.expression, if_expression: $ => prec.right(seq( 'if', field('condition', $.expression), field('consequence', $.block), repeat(seq('else', 'if', field('condition', $.expression), field('consequence', $.block))), optional(seq('else', field('alternative', $.block))), )), match_expression: $ => seq( 'match', field('subject', $.expression), repeat(seq(',', field('subject', $.expression))), optional(seq('as', field('binding', $.identifier))), '{', repeat(choice( alias($._block_match_arm, $.match_arm), seq($.match_arm, ','), )), optional($.match_arm), '}', ), _block_match_arm: $ => prec(1, seq( field('pattern', $.match_pattern), repeat(seq(',', field('pattern', $.match_pattern))), optional(seq('if', field('guard', $.expression))), '=>', field('body', $.block), optional(','), )), match_arm: $ => seq( field('pattern', $.match_pattern), repeat(seq(',', field('pattern', $.match_pattern))), optional(seq('if', field('guard', $.expression))), '=>', field('body', $.expression), ), match_pattern: $ => prec.left(seq( $.match_pattern_term, repeat(seq('|', $.match_pattern_term)), )), match_pattern_term: $ => choice( $.match_pattern_atom, seq(field('start', $.literal), '..', field('end', $.literal)), ), match_pattern_atom: $ => choice( alias('_', $.wildcard), $.literal, $.variant_pattern, ), variant_pattern: $ => seq( '.', field('variant', $.identifier), optional(seq('(', optional(seq(commaSep1($.pattern_binding), optional(','))), ')')), ), pattern_binding: $ => seq( optional(seq(field('field', $.identifier), '=')), field('name', $.identifier), ), for_statement: $ => seq( 'for', optional(choice($.for_each_clause, $.for_clause, $.expression)), field('body', $.block), ), for_each_clause: $ => seq( choice( seq( field('element', $.identifier), optional(seq('.', '&', optional('mut'))), ), field('element', $.for_each_destructure), ), optional(seq(',', field('index', $.identifier))), 'in', field('iterable', $.expression), optional(seq('..', optional('='), field('end', $.expression))), repeat($.iteration_attribute), ), for_each_destructure: $ => seq( '(', commaSep1(field('binding', choice(alias('_', $.wildcard), $.identifier))), ')', ), for_clause: $ => prec.left(seq( optional(choice($.binding_declaration, $.assignment_statement, $.expression)), ';', optional($.expression), ';', optional(choice($.assignment_statement, $.expression)), )), iteration_attribute: $ => seq('@', alias('reversed', $.attribute_name)), compile_time_when: $ => prec.right(seq( 'when', field('condition', $.expression), field('consequence', $.compile_time_block), repeat(seq('else', 'when', field('condition', $.expression), field('consequence', $.compile_time_block))), optional(seq('else', field('alternative', $.compile_time_block))), )), compile_time_block: $ => seq( '{', repeat(choice($.module_declaration, $.import_declaration, $.statement, ';')), '}', ), compiler_error_directive: $ => seq( '@', alias('compiler_error', $.builtin_name), '(', $.string_literal, ')', ), compiler_assert_directive: $ => seq( '@', alias('compiler_assert', $.builtin_name), '(', field('condition', $.expression), ',', field('message', $.string_literal), ')', ), attribute: $ => choice($.link_attribute, $.ordinary_attribute), ordinary_attribute: $ => prec.right(seq( '@', field('name', $.attribute_name), optional(seq('(', optional(seq(commaSep1($.attribute_argument), optional(','))), ')')), )), attribute_argument: $ => choice( $.string_literal, seq(field('name', $.identifier), field('value', $.string_literal)), ), link_attribute: $ => seq( '@', alias('link', $.attribute_name), '(', repeat(seq($.link_item, optional(','))), ')', ), link_item: $ => choice( $.link_entry, $.link_when, $.compiler_error_directive, ), link_entry: $ => seq( field('kind', alias(choice('system', 'path', 'search', 'framework'), $.link_kind)), field('value', $.string_literal), ), link_when: $ => prec.right(seq( 'when', field('condition', $.expression), field('consequence', $.link_block), repeat(seq('else', 'when', field('condition', $.expression), field('consequence', $.link_block))), optional(seq('else', field('alternative', $.link_block))), )), link_block: $ => seq('{', repeat(seq($.link_item, optional(','))), '}'), expression: $ => choice( $.identifier, $.literal, $.no_initializer, $.parenthesized_expression, $.block, $.if_expression, $.match_expression, $.when_expression, $.lambda_expression, $.struct_literal, $.sequence_literal, $.builtin_expression, $.inline_assembly_expression, $.unary_expression, $.binary_expression, $.call_expression, $.field_expression, $.index_expression, $.slice_expression, $.reference_expression, $.dereference_expression, $.cast_expression, $.enum_literal, ), parenthesized_expression: $ => seq('(', $.expression, ')'), lambda_expression: $ => prec.right(seq( field('parameters', $.lambda_parameter_list), '=>', field('body', $.expression), )), lambda_parameter_list: $ => seq( '|', optional(seq(commaSep1($.lambda_parameter), optional(','))), '|', ), lambda_parameter: $ => prec.right(seq( commaSep1($.lambda_parameter_name), optional(seq(':', optional('...'), field('type', $.type))), )), lambda_parameter_name: $ => field('name', $.identifier), unary_expression: $ => prec.right(PREC.UNARY, seq( field('operator', choice('!', '-', '~')), field('operand', $.expression), )), binary_expression: $ => choice( binary($, PREC.LOGICAL_OR, '||'), binary($, PREC.LOGICAL_AND, '&&'), binary($, PREC.BITWISE_OR, '|'), binary($, PREC.BITWISE_XOR, '^'), binary($, PREC.BITWISE_AND, '&'), binary($, PREC.COMPARISON, choice('==', '!=', '<', '>', '<=', '>=')), binary($, PREC.SHIFT, choice(seq('<', '<'), seq('>', '>'))), binary($, PREC.ADD, choice('+', '-')), binary($, PREC.MULTIPLY, choice('*', '/', '%')), ), call_expression: $ => prec.left(PREC.POSTFIX, seq( field('function', $.expression), field('arguments', $.argument_list), )), argument_list: $ => seq( '(', optional(seq(commaSep1($.expression), optional('...'), optional(','))), ')', ), field_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '.', field('field', $.identifier), )), index_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '[', field('index', $.expression), ']', )), slice_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '[', optional(field('start', $.expression)), ':', optional(field('end', $.expression)), ']', )), reference_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '.', '&', optional('mut'), )), dereference_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '.', '*', )), cast_expression: $ => prec.left(PREC.POSTFIX, seq( field('value', $.expression), '.', '(', field('type', $.type), ')', )), enum_literal: $ => seq('.', field('variant', $.identifier)), when_expression: $ => prec.right(seq( 'when', field('condition', $.expression), field('consequence', $.when_expression_block), repeat(seq('else', 'when', field('condition', $.expression), field('consequence', $.when_expression_block))), optional(seq('else', field('alternative', $.when_expression_block))), )), when_expression_block: $ => seq('{', field('value', choice($.expression, $.compiler_error_directive)), '}'), struct_literal: $ => prec.dynamic(5, seq( choice( seq('.', '{'), seq(field('type', choice($.named_type, $.field_expression, $.call_expression)), '.', '{'), ), optional(choice( seq(commaSep1($.field_initializer), optional(',')), seq(commaSep1($.flag_initializer), optional(',')), )), '}', )), field_initializer: $ => choice( seq(field('field', $.identifier), '=', field('value', $.expression)), $.no_initializer, ), flag_initializer: $ => seq('.', field('member', $.identifier)), sequence_literal: $ => seq( '[', optional(choice( seq(commaSep1($.expression), optional(',')), seq(field('value', $.expression), ';', field('count', $.expression)), )), ']', ), builtin_expression: $ => choice( seq('@', field('name', alias(choice('len', 'repr'), $.builtin_name)), '(', $.expression, ')'), seq('@', field('name', alias(choice('sizeof', 'alignof'), $.builtin_name)), '(', choice($.type, $.expression), ')'), seq('@', field('name', alias('offsetof', $.builtin_name)), '(', $.type, ',', $.identifier, ')'), seq('@', field('name', alias('embed', $.builtin_name)), '(', $.string_literal, ')'), ), inline_assembly_expression: $ => seq( '@', alias('asm', $.builtin_name), '(', field('template', $.string_literal), repeat(seq(',', $.assembly_operand)), optional(','), ')', ), assembly_operand: $ => choice( seq(alias('out', $.assembly_keyword), field('type', $.type), field('constraint', $.string_literal)), seq(alias('in', $.assembly_keyword), field('value', $.expression), field('constraint', $.string_literal)), seq(alias('clobber', $.assembly_keyword), field('name', $.string_literal)), alias('volatile', $.assembly_keyword), ), type: $ => choice( $.named_type, $.captured_type, $.pointer_type, $.dynamic_trait_type, $.slice_type, $.array_type, $.function_type, $.struct_type, $.enum_type, $.flags_type, $.union_type, $.trait_type, $.opaque_type, $.representation_type, $.when_type, ), named_type: $ => seq($.type_path, optional($.type_arguments)), captured_type: $ => seq( '$', field('name', $.type_identifier), optional(seq(':', field('constraint', $.type))), ), type_path: $ => prec.left(seq($.type_identifier, repeat(seq('.', $.type_identifier)))), type_arguments: $ => seq('(', commaSep1($.type), optional(','), ')'), when_type: $ => prec.right(seq( 'when', field('condition', $.expression), field('consequence', $.when_type_block), repeat(seq('else', 'when', field('condition', $.expression), field('consequence', $.when_type_block))), optional(seq('else', field('alternative', $.when_type_block))), )), when_type_block: $ => seq('{', field('value', choice($.type, $.compiler_error_directive)), '}'), pointer_type: $ => seq('*', optional('mut'), $.type), dynamic_trait_type: $ => seq('*', optional('mut'), 'dyn', $.named_type), slice_type: $ => seq('[', ']', optional('mut'), $.type), array_type: $ => seq('[', field('length', $.expression), ']', field('element', $.type)), function_type: $ => seq( '*', '(', optional(seq(commaSep1(seq(optional('...'), $.type)), optional(','))), ')', ':', field('return_type', $.type), ), struct_type: $ => seq( 'struct', repeat($.attribute), '{', optional(seq(commaSep1($.struct_field), optional(','))), '}', ), struct_field: $ => choice( seq(field('name', $.identifier), ':', field('type', $.type)), $.union_type, ), enum_type: $ => seq( 'enum', '{', optional(seq(commaSep1($.enum_member), optional(','))), '}', ), enum_member: $ => seq(field('name', $.identifier), optional(seq('=', field('value', $.integer_literal)))), flags_type: $ => seq( alias('flags', $.type_keyword), '(', field('underlying', $.named_type), ')', '{', optional(seq(commaSep1($.flags_member), optional(','))), '}', ), flags_member: $ => seq(field('name', $.identifier), optional(seq('=', field('value', $.expression)))), union_type: $ => seq( 'union', optional(seq('(', field('tag', choice($.type, $.auto_attribute)), ')')), '{', optional(seq(commaSep1(choice($.union_field, $.union_variant)), optional(','))), '}', ), auto_attribute: $ => seq('@', alias('auto', $.attribute_name)), union_field: $ => seq(field('name', $.identifier), ':', field('type', $.type)), union_variant: $ => seq( field('name', $.identifier), optional(seq('(', optional(seq(commaSep1($.payload_field), optional(','))), ')')), ), payload_field: $ => choice( $.type, seq(field('name', $.identifier), ':', field('type', $.type)), ), trait_type: $ => seq( 'trait', '{', repeat(seq($.trait_method, optional(choice(',', ';')))), '}', ), trait_method: $ => seq( 'let', field('name', $.identifier), '(', field('receiver', $.receiver_parameter), repeat(seq(',', choice($.type_parameter, $.trait_parameter))), optional(','), ')', optional(seq(':', field('return_type', $.return_type))), optional(field('body', choice($.block, seq('=', $.expression)))), ), trait_parameter: $ => seq(field('name', $.identifier), ':', field('type', $.type)), opaque_type: _ => 'opaque', representation_type: $ => seq('@', alias('reprof', $.builtin_name), '(', $.type, ')'), literal: $ => choice( $.integer_literal, $.float_literal, $.string_literal, $.c_string_literal, $.character_literal, $.boolean_literal, $.nil_literal, ), integer_literal: _ => token(choice( /0[bB][01]+/, /0[oO][0-7]+/, /0[xX][0-9a-fA-F]+/, /[0-9]+/, )), float_literal: _ => token(/[0-9]+\.[0-9]+/), string_literal: $ => seq('"', repeat(choice($.escape_sequence, $.string_content)), '"'), c_string_literal: $ => seq('c"', repeat(choice($.escape_sequence, $.string_content)), '"'), character_literal: $ => seq("'", choice($.escape_sequence, /[^'\\\n]/), "'"), string_content: _ => token.immediate(prec(1, /[^"\\\n]+/)), escape_sequence: _ => token.immediate(/\\[\\"'nrtbfva0]/), boolean_literal: _ => choice('true', 'false'), nil_literal: _ => 'nil', no_initializer: _ => '---', identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/, type_identifier: $ => alias($.identifier, $.type_identifier), attribute_name: $ => alias($.identifier, $.attribute_name), line_comment: _ => token(seq('//', /[^\n]*/)), block_comment: _ => token(seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/')), }, }); function commaSep1(rule) { return seq(rule, repeat(seq(',', rule))); } function binary($, precedence, operator) { return prec.left(precedence, seq( field('left', $.expression), field('operator', operator), field('right', $.expression), )); }