/** * @file C++ grammar for tree-sitter * @author Max Brunsfeld * @license MIT */ /* eslint-disable arrow-parens */ /* eslint-disable camelcase */ /* eslint-disable-next-line spaced-comment */ /// // @ts-check const C = require('tree-sitter-c/grammar'); const PREC = Object.assign(C.PREC, { LAMBDA: 18, NEW: C.PREC.CALL + 1, STRUCTURED_BINDING: -1, THREE_WAY: C.PREC.RELATIONAL + 1, }); const FOLD_OPERATORS = [ '+', '-', '*', '/', '%', '^', '&', '|', '=', '<', '>', '<<', '>>', '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '>>=', '<<=', '==', '!=', '<=', '>=', '&&', '||', ',', '.*', '->*', 'or', 'and', 'bitor', 'xor', 'bitand', 'not_eq', ]; module.exports = grammar(C, { name: 'cpp', externals: $ => [ $.raw_string_delimiter, $.raw_string_content, ], conflicts: $ => [ // C [$._type_specifier, $._declarator], [$._type_specifier, $._expression_not_binary], [$.sized_type_specifier], [$.attributed_statement], [$._declaration_modifiers, $.attributed_statement], // C++ [$.template_function, $.template_type], [$.template_function, $.template_type, $._expression_not_binary], [$.template_function, $.template_type, $.qualified_identifier], [$.template_method, $.field_expression], [$.template_type, $.qualified_type_identifier], [$.qualified_type_identifier, $.qualified_identifier], [$.comma_expression, $.initializer_list], [$._expression_not_binary, $._declarator], [$._expression_not_binary, $.structured_binding_declarator], [$._expression_not_binary, $._declarator, $._type_specifier], [$.parameter_list, $.argument_list], [$._type_specifier, $.call_expression], [$._declaration_specifiers, $._constructor_specifiers], [$._binary_fold_operator, $._fold_operator], [$._function_declarator_seq], ], inline: ($, original) => original.concat([ $._namespace_identifier, ]), precedences: $ => [ [$.argument_list, $.type_qualifier], [$._expression_not_binary, $._class_name], ], rules: { _top_level_item: ($, original) => choice( ...original.members.filter((member) => member.content?.name != '_old_style_function_definition'), $.namespace_definition, $.concept_definition, $.namespace_alias_definition, $.using_declaration, $.alias_declaration, $.static_assert_declaration, $.template_declaration, $.template_instantiation, alias($.constructor_or_destructor_definition, $.function_definition), alias($.operator_cast_definition, $.function_definition), alias($.operator_cast_declaration, $.declaration), ), _block_item: ($, original) => choice( ...original.members.filter((member) => member.content?.name != '_old_style_function_definition'), $.namespace_definition, $.concept_definition, $.namespace_alias_definition, $.using_declaration, $.alias_declaration, $.static_assert_declaration, $.template_declaration, $.template_instantiation, alias($.constructor_or_destructor_definition, $.function_definition), alias($.operator_cast_definition, $.function_definition), alias($.operator_cast_declaration, $.declaration), ), // Types placeholder_type_specifier: $ => prec(1, seq( field('constraint', optional($._type_specifier)), choice($.auto, alias($.decltype_auto, $.decltype)), )), auto: _ => 'auto', decltype_auto: $ => seq( 'decltype', '(', $.auto, ')', ), decltype: $ => seq( 'decltype', '(', $._expression, ')', ), _type_specifier: $ => choice( $.struct_specifier, $.union_specifier, $.enum_specifier, $.class_specifier, $.sized_type_specifier, $.primitive_type, $.template_type, $.dependent_type, $.placeholder_type_specifier, $.decltype, prec.right(choice( alias($.qualified_type_identifier, $.qualified_identifier), $._type_identifier, )), ), type_qualifier: (_, original) => choice( original, 'mutable', 'constinit', 'consteval', ), type_descriptor: (_, original) => prec.right(original), // When used in a trailing return type, these specifiers can now occur immediately before // a compound statement. This introduces a shift/reduce conflict that needs to be resolved // with an associativity. _class_declaration: $ => seq( repeat(choice($.attribute_specifier, $.alignas_specifier)), optional($.ms_declspec_modifier), repeat($.attribute_declaration), $._class_declaration_item, ), _class_declaration_item: $ => prec.right(seq( choice( field('name', $._class_name), seq( optional(field('name', $._class_name)), optional($.virtual_specifier), optional($.base_class_clause), field('body', $.field_declaration_list), ), ), optional($.attribute_specifier), )), class_specifier: $ => seq( 'class', $._class_declaration, ), union_specifier: $ => seq( 'union', $._class_declaration, ), struct_specifier: $ => seq( 'struct', $._class_declaration, ), _class_name: $ => prec.right(choice( $._type_identifier, $.template_type, alias($.qualified_type_identifier, $.qualified_identifier), )), function_definition: ($, original) => ({ ...original, members: original.members.map( (e) => e.name !== 'body' ? e : field('body', choice(e.content, $.try_statement))), }), virtual_specifier: _ => choice( 'final', // the only legal value here for classes 'override', // legal for functions in addition to final, plus permutations. ), virtual: _ => choice('virtual'), alignas_specifier: $ => seq( 'alignas', '(', choice($._expression, $.primitive_type), ')', ), _declaration_modifiers: ($, original) => choice( original, $.virtual, $.alignas_specifier, ), explicit_function_specifier: $ => choice( 'explicit', prec(PREC.CALL, seq( 'explicit', '(', $._expression, ')', )), ), base_class_clause: $ => seq( ':', commaSep1(seq( repeat($.attribute_declaration), optional(choice( $.access_specifier, seq($.access_specifier, $.virtual), seq($.virtual, $.access_specifier), )), $._class_name, optional('...'), )), ), enum_specifier: $ => prec.right(seq( 'enum', optional(choice('class', 'struct')), choice( seq( field('name', $._class_name), optional($._enum_base_clause), optional(field('body', $.enumerator_list)), ), field('body', $.enumerator_list), ), optional($.attribute_specifier), )), _enum_base_clause: $ => prec.left(seq( ':', field('base', choice( alias($.qualified_type_identifier, $.qualified_identifier), $._type_identifier, $.sized_type_specifier, )), )), // The `auto` storage class is removed in C++0x in order to allow for the `auto` type. storage_class_specifier: (_, original) => choice( ...original.members.filter((member) => member.value !== 'auto'), 'thread_local', ), dependent_type: $ => prec.dynamic(-1, prec.right(seq( 'typename', $._type_specifier, ))), // Declarations template_declaration: $ => seq( 'template', field('parameters', $.template_parameter_list), optional($.requires_clause), choice( $._empty_declaration, $.alias_declaration, $.declaration, $.template_declaration, $.function_definition, $.concept_definition, $.friend_declaration, alias($.constructor_or_destructor_declaration, $.declaration), alias($.constructor_or_destructor_definition, $.function_definition), alias($.operator_cast_declaration, $.declaration), alias($.operator_cast_definition, $.function_definition), ), ), template_instantiation: $ => seq( 'template', optional($._declaration_specifiers), field('declarator', $._declarator), ';', ), template_parameter_list: $ => seq( '<', commaSep(choice( $.parameter_declaration, $.optional_parameter_declaration, $.type_parameter_declaration, $.variadic_parameter_declaration, $.variadic_type_parameter_declaration, $.optional_type_parameter_declaration, $.template_template_parameter_declaration, )), alias(token(prec(1, '>')), '>'), ), type_parameter_declaration: $ => prec(1, seq( choice('typename', 'class'), optional($._type_identifier), )), variadic_type_parameter_declaration: $ => prec(1, seq( choice('typename', 'class'), '...', optional($._type_identifier), )), optional_type_parameter_declaration: $ => seq( choice('typename', 'class'), optional(field('name', $._type_identifier)), '=', field('default_type', $._type_specifier), ), template_template_parameter_declaration: $ => seq( 'template', field('parameters', $.template_parameter_list), choice( $.type_parameter_declaration, $.variadic_type_parameter_declaration, $.optional_type_parameter_declaration, ), ), parameter_list: $ => seq( '(', commaSep(choice( $.parameter_declaration, $.optional_parameter_declaration, $.variadic_parameter_declaration, '...', )), ')', ), optional_parameter_declaration: $ => seq( $._declaration_specifiers, field('declarator', optional($._declarator)), '=', field('default_value', $._expression), ), variadic_parameter_declaration: $ => seq( $._declaration_specifiers, field('declarator', choice( $.variadic_declarator, alias($.variadic_reference_declarator, $.reference_declarator), )), ), variadic_declarator: $ => seq( '...', optional($.identifier), ), variadic_reference_declarator: $ => seq( choice('&&', '&'), $.variadic_declarator, ), init_declarator: ($, original) => choice( original, seq( field('declarator', $._declarator), field('value', choice( $.argument_list, $.initializer_list, )), ), ), operator_cast: $ => prec.right(1, seq( 'operator', $._declaration_specifiers, field('declarator', $._abstract_declarator), )), // Avoid ambiguity between compound statement and initializer list in a construct like: // A b {}; compound_statement: (_, original) => prec(-1, original), field_initializer_list: $ => seq( ':', commaSep1($.field_initializer), ), field_initializer: $ => prec(1, seq( choice( $._field_identifier, $.template_method, alias($.qualified_field_identifier, $.qualified_identifier), ), choice($.initializer_list, $.argument_list), optional('...'), )), _field_declaration_list_item: ($, original) => choice( original, $.template_declaration, alias($.inline_method_definition, $.function_definition), alias($.constructor_or_destructor_definition, $.function_definition), alias($.constructor_or_destructor_declaration, $.declaration), alias($.operator_cast_definition, $.function_definition), alias($.operator_cast_declaration, $.declaration), $.friend_declaration, seq($.access_specifier, ':'), $.alias_declaration, $.using_declaration, $.type_definition, $.static_assert_declaration, ), field_declaration: $ => seq( $._declaration_specifiers, commaSep(seq( field('declarator', $._field_declarator), optional(choice( $.bitfield_clause, field('default_value', $.initializer_list), seq('=', field('default_value', choice($._expression, $.initializer_list))), )), )), optional($.attribute_specifier), ';', ), inline_method_definition: $ => seq( $._declaration_specifiers, field('declarator', $._field_declarator), choice( field('body', choice($.compound_statement, $.try_statement)), $.default_method_clause, $.delete_method_clause, ), ), _constructor_specifiers: $ => choice( $._declaration_modifiers, $.explicit_function_specifier, ), operator_cast_definition: $ => seq( repeat($._constructor_specifiers), field('declarator', choice( $.operator_cast, alias($.qualified_operator_cast_identifier, $.qualified_identifier), )), field('body', choice($.compound_statement, $.try_statement)), ), operator_cast_declaration: $ => prec(1, seq( repeat($._constructor_specifiers), field('declarator', choice( $.operator_cast, alias($.qualified_operator_cast_identifier, $.qualified_identifier), )), optional(seq('=', field('default_value', $._expression))), ';', )), constructor_try_statement: $ => seq( 'try', optional($.field_initializer_list), field('body', $.compound_statement), repeat1($.catch_clause), ), constructor_or_destructor_definition: $ => seq( repeat($._constructor_specifiers), field('declarator', $.function_declarator), choice( seq( optional($.field_initializer_list), field('body', $.compound_statement), ), alias($.constructor_try_statement, $.try_statement), $.default_method_clause, $.delete_method_clause, ), ), constructor_or_destructor_declaration: $ => seq( repeat($._constructor_specifiers), field('declarator', $.function_declarator), ';', ), default_method_clause: _ => seq('=', 'default', ';'), delete_method_clause: _ => seq('=', 'delete', ';'), friend_declaration: $ => seq( 'friend', choice( $.declaration, $.function_definition, seq( optional(choice( 'class', 'struct', 'union', )), $._class_name, ';', ), ), ), access_specifier: _ => choice( 'public', 'private', 'protected', ), _declarator: ($, original) => choice( original, $.reference_declarator, $.qualified_identifier, $.template_function, $.operator_name, $.destructor_name, $.structured_binding_declarator, ), _field_declarator: ($, original) => choice( original, alias($.reference_field_declarator, $.reference_declarator), $.template_method, $.operator_name, ), _abstract_declarator: ($, original) => choice( original, $.abstract_reference_declarator, ), reference_declarator: $ => prec.dynamic(1, prec.right(seq(choice('&', '&&'), $._declarator))), reference_field_declarator: $ => prec.dynamic(1, prec.right(seq(choice('&', '&&'), $._field_declarator))), abstract_reference_declarator: $ => prec.right(seq(choice('&', '&&'), optional($._abstract_declarator))), structured_binding_declarator: $ => prec.dynamic(PREC.STRUCTURED_BINDING, seq( '[', commaSep1($.identifier), ']', )), ref_qualifier: _ => choice('&', '&&'), _function_declarator_seq: $ => seq( field('parameters', $.parameter_list), optional($._function_attributes_start), optional($.ref_qualifier), optional($._function_exception_specification), optional($._function_attributes_end), optional($.trailing_return_type), optional($._function_postfix), ), _function_attributes_start: $ => prec(1, choice( seq(repeat1($.attribute_specifier), repeat($.type_qualifier)), seq(repeat($.attribute_specifier), repeat1($.type_qualifier)), )), _function_exception_specification: $ => choice( $.noexcept, $.throw_specifier, ), _function_attributes_end: $ => prec.right(seq( optional($.gnu_asm_expression), choice( seq(repeat1($.attribute_specifier), repeat($.attribute_declaration)), seq(repeat($.attribute_specifier), repeat1($.attribute_declaration)), ), )), _function_postfix: $ => prec.right(choice( repeat1($.virtual_specifier), $.requires_clause, )), function_declarator: $ => prec.dynamic(1, seq( field('declarator', $._declarator), $._function_declarator_seq, )), function_field_declarator: $ => prec.dynamic(1, seq( field('declarator', $._field_declarator), $._function_declarator_seq, )), abstract_function_declarator: $ => seq( field('declarator', optional($._abstract_declarator)), $._function_declarator_seq, ), trailing_return_type: $ => seq('->', $.type_descriptor), noexcept: $ => prec.right(seq( 'noexcept', optional( seq( '(', optional($._expression), ')', ), ), )), throw_specifier: $ => seq( 'throw', seq( '(', commaSep($.type_descriptor), ')', ), ), template_type: $ => seq( field('name', $._type_identifier), field('arguments', $.template_argument_list), ), template_method: $ => seq( field('name', choice($._field_identifier, $.operator_name)), field('arguments', $.template_argument_list), ), template_function: $ => seq( field('name', $.identifier), field('arguments', $.template_argument_list), ), template_argument_list: $ => seq( '<', commaSep(choice( prec.dynamic(3, $.type_descriptor), prec.dynamic(2, alias($.type_parameter_pack_expansion, $.parameter_pack_expansion)), prec.dynamic(1, $._expression), )), alias(token(prec(1, '>')), '>'), ), namespace_definition: $ => seq( optional('inline'), 'namespace', field('name', optional( choice( $._namespace_identifier, $.nested_namespace_specifier, ))), field('body', $.declaration_list), ), namespace_alias_definition: $ => seq( 'namespace', field('name', $._namespace_identifier), '=', choice( $._namespace_identifier, $.nested_namespace_specifier, ), ';', ), _namespace_specifier: $ => seq( optional('inline'), $._namespace_identifier, ), nested_namespace_specifier: $ => prec(1, seq( optional($._namespace_specifier), '::', choice( $.nested_namespace_specifier, $._namespace_specifier, ), )), using_declaration: $ => seq( 'using', optional(choice('namespace', 'enum')), choice( $.identifier, $.qualified_identifier, ), ';', ), alias_declaration: $ => seq( 'using', field('name', $._type_identifier), repeat($.attribute_declaration), '=', field('type', $.type_descriptor), ';', ), static_assert_declaration: $ => seq( 'static_assert', '(', field('condition', $._expression), optional(seq( ',', field('message', choice( $.string_literal, $.raw_string_literal, $.concatenated_string, )), )), ')', ';', ), concept_definition: $ => seq( 'concept', field('name', $.identifier), '=', $._expression, ';', ), // Statements _top_level_statement: ($, original) => choice( original, $.co_return_statement, $.co_yield_statement, $.for_range_loop, $.try_statement, $.throw_statement, ), _non_case_statement: ($, original) => choice( original, $.co_return_statement, $.co_yield_statement, $.for_range_loop, $.try_statement, $.throw_statement, ), switch_statement: $ => seq( 'switch', field('condition', $.condition_clause), field('body', $.compound_statement), ), while_statement: $ => seq( 'while', field('condition', $.condition_clause), field('body', $._statement), ), if_statement: $ => prec.right(seq( 'if', optional('constexpr'), field('condition', $.condition_clause), field('consequence', $._statement), optional(field('alternative', $.else_clause)), )), _for_statement_body: ($, original) => prec(1, original), for_range_loop: $ => seq( 'for', '(', $._for_range_loop_body, ')', field('body', $._statement), ), _for_range_loop_body: $ => seq( field('initializer', optional($.init_statement)), $._declaration_specifiers, field('declarator', $._declarator), ':', field('right', choice( $._expression, $.initializer_list, )), ), init_statement: $ => choice( $.alias_declaration, $.type_definition, $.declaration, $.expression_statement, ), condition_clause: $ => seq( '(', field('initializer', optional($.init_statement)), field('value', choice( $._expression, $.comma_expression, alias($.condition_declaration, $.declaration), )), ')', ), condition_declaration: $ => seq( $._declaration_specifiers, field('declarator', $._declarator), choice( seq( '=', field('value', $._expression), ), field('value', $.initializer_list), ), ), return_statement: ($, original) => seq( choice( original, seq('return', $.initializer_list, ';'), ), ), co_return_statement: $ => seq( 'co_return', optional($._expression), ';', ), co_yield_statement: $ => seq( 'co_yield', $._expression, ';', ), throw_statement: $ => seq( 'throw', optional($._expression), ';', ), try_statement: $ => seq( 'try', field('body', $.compound_statement), repeat1($.catch_clause), ), catch_clause: $ => seq( 'catch', field('parameters', $.parameter_list), field('body', $.compound_statement), ), // Expressions _expression_not_binary: ($, original) => choice( original, $.co_await_expression, $.requires_expression, $.requires_clause, $.template_function, $.qualified_identifier, $.new_expression, $.delete_expression, $.lambda_expression, $.parameter_pack_expansion, $.this, $.raw_string_literal, $.user_defined_literal, $.fold_expression, ), raw_string_literal: $ => seq( choice('R"', 'LR"', 'uR"', 'UR"', 'u8R"'), choice( seq( field('delimiter', $.raw_string_delimiter), '(', $.raw_string_content, ')', $.raw_string_delimiter, ), seq( '(', $.raw_string_content, ')', )), '"', ), subscript_expression: $ => prec(PREC.SUBSCRIPT, seq( field('argument', $._expression), field('indices', $.subscript_argument_list), )), subscript_argument_list: $ => seq( '[', commaSep(choice($._expression, $.initializer_list)), ']', ), call_expression: ($, original) => choice(original, seq( field('function', $.primitive_type), field('arguments', $.argument_list), )), co_await_expression: $ => prec.left(PREC.UNARY, seq( field('operator', 'co_await'), field('argument', $._expression), )), new_expression: $ => prec.right(PREC.NEW, seq( optional('::'), 'new', field('placement', optional($.argument_list)), field('type', $._type_specifier), field('declarator', optional($.new_declarator)), field('arguments', optional(choice( $.argument_list, $.initializer_list, ))), )), new_declarator: $ => prec.right(seq( '[', field('length', $._expression), ']', optional($.new_declarator), )), delete_expression: $ => seq( optional('::'), 'delete', optional(seq('[', ']')), $._expression, ), field_expression: $ => seq( prec(PREC.FIELD, seq( field('argument', $._expression), field('operator', choice('.', '.*', '->')), )), field('field', choice( $._field_identifier, $.destructor_name, $.template_method, alias($.dependent_field_identifier, $.dependent_name), )), ), type_requirement: $ => seq('typename', $._class_name), compound_requirement: $ => seq( '{', $._expression, '}', optional('noexcept'), optional($.trailing_return_type), ';', ), _requirement: $ => choice( alias($.expression_statement, $.simple_requirement), $.type_requirement, $.compound_requirement, ), requirement_seq: $ => seq('{', repeat($._requirement), '}'), constraint_conjunction: $ => prec.left(PREC.LOGICAL_AND, seq( field('left', $._requirement_clause_constraint), field('operator', choice('&&', 'and')), field('right', $._requirement_clause_constraint)), ), constraint_disjunction: $ => prec.left(PREC.LOGICAL_OR, seq( field('left', $._requirement_clause_constraint), field('operator', choice('||', 'or')), field('right', $._requirement_clause_constraint)), ), _requirement_clause_constraint: $ => choice( // Primary expressions" $.true, $.false, $._class_name, $.fold_expression, $.lambda_expression, $.requires_expression, // Parenthesized expressions seq('(', $._expression, ')'), // conjunction or disjunction of the above $.constraint_conjunction, $.constraint_disjunction, ), requires_clause: $ => seq( 'requires', field('constraint', $._requirement_clause_constraint), ), requires_parameter_list: $ => seq( '(', commaSep(choice( $.parameter_declaration, $.optional_parameter_declaration, $.variadic_parameter_declaration, )), ')', ), requires_expression: $ => seq( 'requires', field('parameters', optional(alias($.requires_parameter_list, $.parameter_list))), field('requirements', $.requirement_seq), ), lambda_expression: $ => seq( field('captures', $.lambda_capture_specifier), optional(seq( field('template_parameters', $.template_parameter_list), optional(field('constraint', $.requires_clause)), )), optional(field('declarator', $.abstract_function_declarator)), field('body', $.compound_statement), ), lambda_capture_specifier: $ => prec(PREC.LAMBDA, seq( '[', choice( $.lambda_default_capture, commaSep($._expression), seq( $.lambda_default_capture, ',', commaSep1($._expression), ), ), ']', )), lambda_default_capture: _ => choice('=', '&'), _fold_operator: _ => choice(...FOLD_OPERATORS), _binary_fold_operator: _ => choice(...FOLD_OPERATORS.map((operator) => seq(field('operator', operator), '...', operator))), _unary_left_fold: $ => seq( field('left', '...'), field('operator', $._fold_operator), field('right', $._expression), ), _unary_right_fold: $ => seq( field('left', $._expression), field('operator', $._fold_operator), field('right', '...'), ), _binary_fold: $ => seq( field('left', $._expression), $._binary_fold_operator, field('right', $._expression), ), fold_expression: $ => seq( '(', choice( $._unary_right_fold, $._unary_left_fold, $._binary_fold, ), ')', ), parameter_pack_expansion: $ => prec(-1, seq( field('pattern', $._expression), '...', )), type_parameter_pack_expansion: $ => seq( field('pattern', $.type_descriptor), '...', ), sizeof_expression: ($, original) => prec.right(PREC.SIZEOF, choice( original, seq( 'sizeof', '...', '(', field('value', $.identifier), ')', ), )), unary_expression: ($, original) => choice( original, prec.left(PREC.UNARY, seq( field('operator', choice('not', 'compl')), field('argument', $._expression), )), ), binary_expression: ($, original) => { const table = [ ['<=>', PREC.THREE_WAY], ['or', PREC.LOGICAL_OR], ['and', PREC.LOGICAL_AND], ['bitor', PREC.INCLUSIVE_OR], ['xor', PREC.EXCLUSIVE_OR], ['bitand', PREC.BITWISE_AND], ['not_eq', PREC.EQUAL], ]; return choice( ...original.members, ...table.map(([operator, precedence]) => { return prec.left(precedence, seq( field('left', $._expression), // @ts-ignore field('operator', operator), field('right', $._expression), )); })); }, // The compound_statement is added to parse macros taking statements as arguments, e.g. MYFORLOOP(1, 10, i, { foo(i); bar(i); }) argument_list: $ => seq( '(', commaSep(choice(seq(optional('__extension__'), $._expression), $.initializer_list, $.compound_statement)), ')', ), destructor_name: $ => prec(1, seq('~', $.identifier)), compound_literal_expression: ($, original) => choice( original, seq( field('type', choice($._class_name, $.primitive_type)), field('value', $.initializer_list), ), ), dependent_identifier: $ => seq('template', $.template_function), dependent_field_identifier: $ => seq('template', $.template_method), dependent_type_identifier: $ => seq('template', $.template_type), _scope_resolution: $ => prec(1, seq( field('scope', optional(choice( $._namespace_identifier, $.template_type, $.decltype, alias($.dependent_type_identifier, $.dependent_name), ))), '::', )), qualified_field_identifier: $ => seq( $._scope_resolution, field('name', choice( alias($.dependent_field_identifier, $.dependent_name), alias($.qualified_field_identifier, $.qualified_identifier), $.template_method, $._field_identifier, )), ), qualified_identifier: $ => seq( $._scope_resolution, field('name', choice( alias($.dependent_identifier, $.dependent_name), $.qualified_identifier, $.template_function, seq(optional('template'), $.identifier), $.operator_name, $.destructor_name, $.pointer_type_declarator, )), ), qualified_type_identifier: $ => seq( $._scope_resolution, field('name', choice( alias($.dependent_type_identifier, $.dependent_name), alias($.qualified_type_identifier, $.qualified_identifier), $.template_type, $._type_identifier, )), ), qualified_operator_cast_identifier: $ => seq( $._scope_resolution, field('name', choice( alias($.qualified_operator_cast_identifier, $.qualified_identifier), $.operator_cast, )), ), _assignment_left_expression: ($, original) => choice( original, $.qualified_identifier, $.user_defined_literal, ), assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq( field('left', $._assignment_left_expression), field('operator', choice( '=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '&=', '^=', '|=', 'and_eq', 'or_eq', 'xor_eq', )), field('right', choice($._expression, $.initializer_list)), )), operator_name: $ => prec(1, seq( 'operator', choice( 'co_await', '+', '-', '*', '/', '%', '^', '&', '|', '~', '!', '=', '<', '>', '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<', '>>', '>>=', '<<=', '==', '!=', '<=', '>=', '<=>', '&&', '||', '++', '--', ',', '->*', '->', '()', '[]', 'xor', 'bitand', 'bitor', 'compl', 'not', 'xor_eq', 'and_eq', 'or_eq', 'not_eq', 'and', 'or', seq(choice('new', 'delete'), optional('[]')), seq('""', $.identifier), ), )), this: _ => 'this', concatenated_string: $ => seq( choice($.identifier, $.string_literal, $.raw_string_literal), choice($.string_literal, $.raw_string_literal), repeat(choice($.identifier, $.string_literal, $.raw_string_literal)), ), number_literal: $ => { const sign = /[-\+]/; const separator = '\''; const binary = /[01]/; const binaryDigits = seq(repeat1(binary), repeat(seq(separator, repeat1(binary)))); const decimal = /[0-9]/; const firstDecimal = /[1-9]/; const intDecimalDigits = seq(firstDecimal, repeat(decimal), repeat(seq(separator, repeat1(decimal)))); const floatDecimalDigits = seq(repeat1(decimal), repeat(seq(separator, repeat1(decimal)))); const hex = /[0-9a-fA-F]/; const hexDigits = seq(repeat1(hex), repeat(seq(separator, repeat1(hex)))); const octal = /[0-7]/; const octalDigits = seq('0', repeat(octal), repeat(seq(separator, repeat1(octal)))); const hexExponent = seq(/[pP]/, optional(sign), floatDecimalDigits); const decimalExponent = seq(/[eE]/, optional(sign), floatDecimalDigits); const intSuffix = /(ll|LL)[uU]?|[uU](ll|LL)?|[uU][lL]?|[uU][zZ]?|[lL][uU]?|[zZ][uU]?/; const floatSuffix = /([fF](16|32|64|128)?)|[lL]|(bf16|BF16)/; return token(seq( optional(sign), choice( seq( choice( seq(choice('0b', '0B'), binaryDigits), intDecimalDigits, seq(choice('0x', '0X'), hexDigits), octalDigits, ), optional(intSuffix), ), seq( choice( seq(floatDecimalDigits, decimalExponent), seq(floatDecimalDigits, '.', optional(floatDecimalDigits), optional(decimalExponent)), seq('.', floatDecimalDigits, optional(decimalExponent)), seq( choice('0x', '0X'), choice( hexDigits, seq(hexDigits, '.', optional(hexDigits)), seq('.', hexDigits)), hexExponent, ), ), optional(floatSuffix), ), ), )); }, literal_suffix: _ => token.immediate(/[a-zA-Z_]\w*/), user_defined_literal: $ => seq( choice( $.number_literal, $.char_literal, $.string_literal, $.raw_string_literal, $.concatenated_string, ), $.literal_suffix, ), _namespace_identifier: $ => alias($.identifier, $.namespace_identifier), }, }); /** * Creates a rule to optionally match one or more of the rules separated by a comma * * @param {Rule} rule * * @return {ChoiceRule} * */ function commaSep(rule) { return optional(commaSep1(rule)); } /** * Creates a rule to match one or more of the rules separated by a comma * * @param {Rule} rule * * @return {SeqRule} * */ function commaSep1(rule) { return seq(rule, repeat(seq(',', rule))); }