const C = require("tree-sitter-c/grammar") module.exports = grammar(C, { name: 'objc', extras: $ => [ /\u00A0|\s|\\\r?\n/, // non-breaking space $.comment, $.pragma, $._ifdef_if_retain, $._ifdef_elif_ignore, $._ifdef_else_ignore, $._ifdef_endif_retain, $._ifdef_undef_retain, ], conflicts: ($, superclass) => superclass.concat([ [$.struct_specifier], [$.union_specifier], [$.enum_specifier], [$.ns_enum_specifier], [$.function_declarator], [$.type_descriptor], [$.type_definition], // field attributes [$.superclass_reference], [$._expression, $.macro_type_specifier], [$._expression, $.generic_type_specifier], [$._declaration_specifiers], [$._declaration_specifiers, $.class_interface], [$._protocol_identifier, $._type_specifier], [$._protocol_identifier, $._parameterized_class_type_arguments], [$.declaration, $._argument_type_declarator], ]), supertypes: $ => [ $._expression, $._statement, $._declarator, $._abstract_declarator, $._field_declarator, $._type_declarator, ], rules: { identifier: $ => /[$a-zA-Z_](\w|\$)*/, _top_level_item: ($, superclass) => choice( ...superclass.members.filter(member => member.name !== 'preproc_call'), // FIX: #endif $._import, $.class_interface, $.class_implementation, $.category_interface, $.category_implementation, $.protocol_declaration, $.protocol_forward_declaration, $.class_forward_declaration, $._ns_assume_nonnull_declaration, $.compatibility_alias_declaration, ), _name: $ => field('name', $.identifier), // // Imports // _import: $ => choice( $.preproc_import, $.module_import ), preproc_import: $ => seq( '#import', field('path', choice( $.system_lib_string, $.string_literal ) ) ), module_import: $ => seq( '@import', field('module', $.module_string) ), module_string: $ => /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/, // // Declarations // _ns_assume_nonnull_declaration: $ => token.immediate(/NS_ASSUME_NONNULL_BEGIN|NS_ASSUME_NONNULL_END|CF_EXTERN_C_BEGIN|CF_EXTERN_C_END/), compatibility_alias_declaration: $ => seq( '@compatibility_alias', field('class_name', $.identifier), field('alias_name', $.identifier), ';' ), protocol_forward_declaration: $ => prec(6, seq( '@protocol', commaSep1($._name), ';' )), class_forward_declaration: $ => seq( '@class', commaSep1(seq($._name, optional(choice($.protocol_qualifiers, $.parameterized_class_type_arguments)))), ';' ), class_interface: $ => choice( seq( optional($.class_interface_attribute_sepcifier), optional($.swift_name_attribute_sepcifier), repeat($.attribute_specifier), '@interface', choice( seq($._name, optional($.parameterized_class_type_arguments)), $.generics_type_reference ), optional($.superclass_reference), optional($.parameterized_class_type_arguments), $.protocol_qualifiers, // protocol_qualifiers must precede parameterized_class_type_arguments optional($._instance_variables), optional($._interface_declaration), '@end', ), seq( optional($.class_interface_attribute_sepcifier), optional($.swift_name_attribute_sepcifier), repeat($.attribute_specifier), '@interface', choice( seq($._name, optional($.parameterized_class_type_arguments), optional($.protocol_qualifiers)), $.generics_type_reference ), optional($.superclass_reference), optional($._instance_variables), optional($._interface_declaration), '@end', ), ), category_interface: $ => seq( '@interface', choice( seq($._name, optional($.parameterized_class_type_arguments), optional($.protocol_qualifiers)), $.generics_type_reference ), '(', field('category', optional($.identifier)),')', optional($.protocol_qualifiers), optional($._instance_variables), optional($._interface_declaration), '@end', ), protocol_declaration: $ => seq( optional($.swift_name_attribute_sepcifier), optional($.attribute_specifier), '@protocol', $._name, optional($.protocol_qualifiers), optional($._interface_declaration), '@end' ), superclass_reference: $ => seq( ':', $._name, field('type_reference', optional($.generic_type_references)), ), protocol_qualifiers: $ => seq( '<', commaSep1($._protocol_identifier), '>' ), _protocol_identifier: $ => prec.dynamic(5, field('name', $.identifier)), parameterized_class_type_arguments: $ => seq( '<', commaSep1($._parameterized_class_type_arguments), '>' ), _parameterized_class_type_arguments: $ => seq( optional(choice('__covariant', '__contravariant')), field('type', $.identifier), optional(seq(':', $.type_descriptor)), // superclass ), generics_type_reference: $ => seq( '__GENERICS', '(', seq($._name, repeat(seq(',', $._type_specifier))), ')', ), _instance_variables: $ => seq( '{', repeat($._instance_variable_declaration) ,'}' ), _instance_variable_declaration: $ => choice( $._visibility_specification, $.field_declaration, ), _field_declarator: ($, superclass) => choice( superclass, $.block_declarator, ), _visibility_specification: $ => choice( $.private, $.public, $.protected, $.package, ), private: $ => '@private', public: $ => '@public', protected: $ => '@protected', package: $ => '@package', _interface_declaration: $ => repeat1( choice( $._ns_assume_nonnull_declaration, $._interface_declaration_specifier, $.declaration, $.method_declaration, $.property_declaration, $._empty_declaration, $.type_definition, $.preproc_def, $.preproc_function_def, ) ), _interface_declaration_specifier: $ => choice( $.optional, $.required, ), optional: $ => '@optional', required: $ => '@required', method_declaration: $ => seq( field('scope', $._class_member_scope), field('return_type', optional($._method_argument_type_specifier)), field('selector', $._method_selector), repeat(choice( $.attribute_specifier, $.swift_name_attribute_sepcifier, )), ';' ), _class_member_scope: $ => choice( $.class_scope, $.instance_scope ), class_scope: $ => '+', instance_scope: $ => '-', storage_class_specifier: ($, superclass) => choice( superclass, /FOUNDATION_EXPORT|FOUNDATION_EXTERN|FOUNDATION_STATIC_INLINE|NS_INLINE|UIKIT_EXTERN|CG_EXTERN|CG_INLINE/ ), property_declaration: $ => seq( '@property', optional($.property_attributes), seq( optional($.attribute_specifier), repeat($.type_qualifier), field('type', $._type_specifier), optional($.attribute_specifier), repeat($.type_qualifier), commaSep1(field('declarator', $._declarator)), ), repeat($.attribute_specifier), optional($.swift_name_attribute_sepcifier), ';' ), property_attributes: $ => seq( '(', commaSep($._property_attribute), ')' ), _property_attribute: $ => choice( $.getter, $.setter, $.nonnull, $.nullable, $.null_resettable, $.unsafe_unretained, $.null_unspecified, $.direct, $.readwrite, $.readonly, $.strong, $.weak, $.copy, $.assign, $.retain, $.atomic, $.nonatomic, $.class, $.NS_NONATOMIC_IOSONLY, $.DISPATCH_QUEUE_REFERENCE_TYPE, ), class_interface_attribute_sepcifier: $ => token.immediate(/IB_DESIGNABLE|NS_ROOT_CLASS/), attribute_specifier: ($, superclass) => prec.dynamic(1, prec.left(choice( seq( choice('__attribute', '__attribute__'), '(', $.argument_list, ')' ), $.method_attribute_specifier, $.method_variadic_arguments_attribute_specifier, $.availability_attribute_specifier, ))), method_attribute_specifier: $ => token.immediate(/NS_DESIGNATED_INITIALIZER|NS_REQUIRES_SUPER|CF_RETURNS_RETAINED|CF_RETURNS_NOT_RETAINED|NS_REQUIRES_NIL_TERMINATION|NS_DIRECT/), method_variadic_arguments_attribute_specifier: $=> seq( choice('NS_FORMAT_FUNCTION', 'CF_FORMAT_FUNCTION'), '(', $.number_literal, ',', $.number_literal, ')' ), availability_attribute_specifier: $ => prec(1, choice( 'NS_UNAVAILABLE', 'DEPRECATED_ATTRIBUTE', 'UI_APPEARANCE_SELECTOR', 'UNAVAILABLE_ATTRIBUTE', seq( choice('NS_AVAILABLE', '__IOS_AVAILABLE', 'NS_AVAILABLE_IOS', 'API_AVAILABLE', 'API_UNAVAILABLE', 'API_DEPRECATED', 'NS_ENUM_AVAILABLE_IOS', 'NS_DEPRECATED_IOS', 'NS_ENUM_DEPRECATED_IOS', 'DEPRECATED_MSG_ATTRIBUTE', '__deprecated_msg', '__deprecated_enum_msg', 'NS_SWIFT_UNAVAILABLE', 'NS_EXTENSION_UNAVAILABLE_IOS', 'NS_CLASS_AVAILABLE_IOS', 'NS_CLASS_DEPRECATED_IOS'), '(', commaSep1(choice(field('message', $.string_literal), $.platform_version)), ')' ), )), platform_version: $ => choice( field('version', /\d+_\d+/), // 8_0 field('version', $.number_literal), // 11.0 field('platform', $.platform), // ios, macos, tvos seq( field('platform', $.platform), '(', field('version', commaSep1(choice( /\d+_\d+/, // 8_0 $.number_literal, // 11.0 $.identifier, // API_TO_BE_DEPRECATED macro ))), ')' ) ), platform: $ => choice( 'ios', 'tvos', 'macos', 'macosx', 'watchos' ), swift_name_attribute_sepcifier: $ => choice( 'NS_REFINED_FOR_SWIFT', seq( 'NS_SWIFT_NAME', '(', field('class', optional($.identifier)), field('method', $.identifier), field('parameters', optional(seq( '(', repeat( seq(repeat($._type_specifier), ':'), ), ')', ))), ')' ), ), getter: $ => seq( 'getter', '=', $._name ), setter: $ => seq( 'setter', '=', seq($._name, optional(':')) ), nonnull: $ => 'nonnull', nullable: $ => 'nullable', null_resettable: $ => 'null_resettable', unsafe_unretained: $ => 'unsafe_unretained', null_unspecified: $ => 'null_unspecified', direct: $ => 'direct', readwrite: $ => 'readwrite', readonly: $ => 'readonly', strong: $ => 'strong', weak: $ => 'weak', copy: $ => 'copy', assign: $ => 'assign', retain: $ => 'retain', atomic: $ => 'atomic', nonatomic: $ => 'nonatomic', class: $ => 'class', NS_NONATOMIC_IOSONLY: $ => 'NS_NONATOMIC_IOSONLY', DISPATCH_QUEUE_REFERENCE_TYPE: $=> 'DISPATCH_QUEUE_REFERENCE_TYPE', // // Implementation // class_implementation: $ => seq( optional($.attribute_specifier), '@implementation', $._name, optional($.superclass_reference), optional('NS_UNAVAILABLE'), optional($._instance_variables), optional($._implementation_definition), '@end' ), category_implementation: $ => seq( optional($.attribute_specifier), '@implementation', $._name, '(', field('category', $.identifier),')', optional($._implementation_definition), '@end' ), _implementation_definition: $ => repeat1( choice( $._ns_assume_nonnull_declaration, $.function_definition, $.declaration, $.synthesize_definition, $.dynamic_definition, $.method_definition, $.type_definition, $.preproc_def, $.preproc_function_def, ) ), synthesize_definition: $ => seq( '@synthesize', commaSep1($.synthesize_property), ';' ), synthesize_property: $ => seq( field('property', $.identifier), optional(seq('=', field('instance_variable', $.identifier))) ), dynamic_definition: $ => seq( '@dynamic', commaSep1(field('property', $.identifier)), ';' ), method_definition: $ => seq( field('scope', $._class_member_scope), field('return_type', optional($._method_argument_type_specifier)), field('selector', $._method_selector), optional($.attribute_specifier), optional(';'), // compatible with: - (void)method; {} field('body', $.compound_statement), optional(';'), ), _method_selector: $ => choice( $._unary_selector, seq( $.keyword_selector, optional(seq(',', '...')), ), ), _unary_selector: $ => $.identifier, keyword_selector: $ => repeat1($.keyword_declarator), keyword_declarator: $ => prec.right(seq( field('keyword', optional($.identifier)), ':', field('type', optional($._method_argument_type_specifier)), $._name, optional('__unused') )), _method_argument_type_specifier: $ => prec(1, seq( '(', $._argument_type_declarator, ')', optional($.attribute_specifier), )), _argument_type_declarator: $ => prec(10, seq( optional(choice($.nullable, $.nonnull)), optional('NS_NOESCAPE'), $._declaration_specifiers, optional(field('declarator', choice( $._declarator, $._abstract_declarator ))), repeat($.attribute_specifier), )), // Type specifiers type_definition: ($, superclass) => prec(1, seq( optional($.attribute_specifier), optional($.swift_name_attribute_sepcifier), 'typedef', choice( seq( optional($.attribute_specifier), repeat($.type_qualifier), field('type', $._type_specifier), optional($.attribute_specifier), repeat($.type_qualifier), field('declarator', $._type_declarator), ), seq( optional($.attribute_specifier), repeat($.type_qualifier), field('type', $.ns_enum_specifier), optional($.attribute_specifier), repeat($.type_qualifier), field('declarator', optional($._type_declarator)), // NS_ENUM optional ) ), field('attributes', optional(choice($.identifier, $.attribute_specifier))), ';' )), _type_declarator: ($, superclass) => choice( superclass, $.block_declarator, ), _abstract_declarator: ($, superclass) => choice( superclass, $.block_abstract_declarator, ), enum_specifier: ($, superclass) => seq( 'enum', choice( seq( field('name', optional($._type_identifier)), field('superclass', optional(seq(':', $._type_specifier))), field('body', optional($.enumerator_list)) ), field('body', $.enumerator_list) ) ), ns_enum_specifier: ($, superclass) => seq( choice('NS_ENUM', 'NS_ERROR_ENUM', 'NS_OPTIONS'), '(', field('type', $._type_specifier), optional(seq(',', field('name', $._type_identifier))), ')', field('body', optional($.enumerator_list)), ), enumerator: ($, superclass) => seq( field('name', $.identifier), optional(seq('=', field('value', $._expression))), repeat(choice($.attribute_specifier, $.swift_name_attribute_sepcifier)), ), _type_specifier: ($, superclass) => choice( superclass, $.id, $.SEL, $.IMP, $.Class, $.BOOL, $.auto, $.instancetype, $.ns_enum_specifier, $.typeof_specifier, $.atomic_specifier, $.generic_type_specifier, ), typeof_specifier: $ => seq( field('operator', choice('typeof', '__typeof', '__typeof__')), '(', field('type', $._expression), ')', ), // https://en.cppreference.com/w/c/language/atomic // Note: fail to match macro_type_specifier rule, because _Atomic is also a storage_class_specifier atomic_specifier: $ => prec(-1, seq( field('operator', '_Atomic'), '(', field('type', $.type_descriptor), ')' )), generic_type_specifier: $ => prec.dynamic(1, choice( seq( field('class_name', choice($._type_identifier, $.id, $.Class)), field('type_reference', repeat1(choice($.protocol_qualifiers, $.generic_type_references))), ) )), generic_type_references: $ => seq( '<', commaSep1($.type_descriptor), '>' ), struct_specifier: ($, superclass) => choice( seq( 'struct', optional($.attribute_specifier), choice( seq( field('name', $._type_identifier), optional($.superclass_reference), field('body', optional($.field_declaration_list)) ), field('body', $.field_declaration_list) ), ), seq( 'struct', optional($.attribute_specifier), field('name', optional($.identifier)), field('body', seq('@defs', '(', field('class_name', $.identifier),')')) ) ), type_qualifier: ($, superclass) => choice( superclass, 'in', 'out', 'inout', 'bycopy', 'byref', 'oneway', '_Nullable', '_Nonnull', '_Nullable_result', '_Null_unspecified', '__autoreleasing', '__nullable', '__nonnull', '__strong', '__weak', '__bridge', '__bridge_transfer', '__bridge_retained', '__unsafe_unretained', '__block', '__kindof', '__unused', '_Complex', '__complex', 'IBOutlet', 'IBInspectable', 'NS_VALID_UNTIL_END_OF_SCOPE', ), // // Block // https://opensource.apple.com/source/clang/clang-703.0.31/src/tools/clang/docs/BlockLanguageSpec.rst.auto.html // How Do I Declare A Block in Objective-C? // https://stackoverflow.com/questions/9201514/block-declaration-syntax-list /** As a local variable: returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; As a property: @property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes); As a method parameter: - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName; As an argument to a method call: [someObject someMethodThatTakesABlock:^returnType (parameters) {...}]; As a parameter to a C function: void SomeFunctionThatTakesABlock(returnType (^blockName)(parameterTypes)); As a typedef: typedef returnType (^TypeName)(parameterTypes); TypeName blockName = ^returnType(parameters) {...}; As a cast type specifier: (returnType (^)(parameterTypes))anotherBlock; **/ declaration: $ => seq( optional($.swift_name_attribute_sepcifier), $._declaration_specifiers, commaSep1(field('declarator', choice( $._declarator, $.init_declarator ))), optional($.attribute_specifier), optional($.type_qualifier), ';' ), _declarator: ($, superclass) => choice( superclass, $.block_declarator, ), block_abstract_declarator: $ => seq( '(', optional('NS_NOESCAPE'), optional($.type_qualifier), '^', optional($.type_qualifier), ')', field('parameters', $.parameter_list), ), block_declarator: $ => seq( '(', '^', choice( seq( repeat($.type_qualifier), field('declarator', $.identifier), repeat($.type_qualifier), ), field('return_type', $.block_declarator), ), ')', field('parameters', $.parameter_list), ), block_expression: $ => seq( '^', field('declarator', optional(choice($.type_descriptor, $.type_qualifier))), field('parameters', optional($.parameter_list)), $.compound_statement ), // // Types // self: $ => 'self', super: $ => 'super', nil: $ => 'nil', id: $ => 'id', instancetype: $ => 'instancetype', Class: $ => 'Class', Method: $ => 'Method', SEL: $ => 'SEL', IMP: $ => 'IMP', BOOL: $ => 'BOOL', auto: $ => '__auto_type', // // Statements // _non_case_statement: ($, superclass) => choice( superclass, $.autoreleasepool_statement, $.synchronized_statement, $.for_in_statement, $.try_catch_statement, $.throw_statement, ), autoreleasepool_statement: $ => prec.right(seq( '@autoreleasepool', field('consequence', $._statement), )), synchronized_statement: $ => prec.right(seq( '@synchronized', field('condition', $.parenthesized_expression), field('consequence', $._statement), )), for_in_statement: $ => prec.dynamic(1, seq( 'for', '(', field('initializer', $._argument_type_declarator), 'in', field('loop', $._expression), ')', $._statement )), try_catch_statement: $ => prec.right(seq( '@try', field('body', $._statement), repeat(seq( '@catch', field('declaration', seq('(', choice($.parameter_declaration, '...'), ')')), field('catch', $._statement), )), optional(seq( '@finally', field('finally', $._statement) )) )), throw_statement: $ => seq( '@throw', optional(choice($._expression, $.comma_expression)), ';' ), // // Expressions // _expression: ($, superclass) => choice( superclass, $.self, $.super, $.nil, $.YES, $.NO, $.message_expression, $.selector_expression, $.protocol_expression, $.encode_expression, $.number_expression, $.string_expression, $.object_expression, $.dictionary_expression, $.array_expression, $.boolean_expression, $.block_expression, $.available_expression, $.statement_expression, $.va_arg_expression, ), _assignment_left_expression: ($, superclass) => choice( superclass, $.self, ), message_expression: $ => seq( '[', field('receiver', $._receiver), field('selector', $._message_selector), ']' ), _receiver: $ => choice( $._type_specifier, $._expression, ), _message_selector: $ => choice( field('selector', $.identifier), $.keyword_argument_list ), keyword_argument_list: $ => repeat1($.keyword_argument), keyword_argument: $ => seq( field('keyword', optional($.identifier)), ':', field('argument', choice( $._expression, $._variadic_arguments, )) ), _variadic_arguments: $ => prec(1, commaSep1($._expression)), selector_expression: $ => seq( '@selector', '(', $._selector_name, ')' ), _selector_name: $ => choice( $._name, repeat1($._keyword_name) ), _keyword_name: $ => choice( seq($._name, ':'), ':' ), // https://opensource.apple.com/source/clang/clang-703.0.31/src/tools/clang/docs/ObjectiveCLiterals.rst.auto.html // objc-at-expression : '@' (string-literal | encode-literal | selector-literal | protocol-literal | object-literal) protocol_expression: $ => seq( '@protocol', '(', $._name, ')' ), encode_expression: $ => seq( '@encode', '(', $.type_descriptor, ')' ), number_expression: $ => prec(1, seq( '@', choice( seq( '(', choice( alias($.number_literal, 'number_literal'), seq("'", /[A-Za-z0-9]/, "'"), ), ')', ), seq( choice( alias($.number_literal, 'number_literal'), seq("'", /[A-Za-z0-9]/, "'"), ), ) ) )), string_expression: $ => seq( seq('@', alias($.string_literal, 'string_literal')), repeat(seq(optional('@'), alias($.string_literal, 'string_literal'))), ), object_expression: $ => seq( '@', '(', optional($._expression) , ')' ), dictionary_expression: $ => seq( '@', '{', optional($._dictionary_key_value_list), '}' ), _dictionary_key_value_list: $ => seq( commaSep1($._dictionary_key_value_pair), optional(',') ), _dictionary_key_value_pair: $ => seq( field('key', $._expression), ':', field('value', $._expression), ), array_expression: $ => seq( '@', '[', optional(commaSep1($._expression)), optional(',') ,']' ), YES: $ => 'YES', NO: $ => 'NO', boolean_expression: $ => seq( '@', choice($.YES, $.NO, '__objc_no', '__objc_yes') ), available_expression: $ => seq( choice('__builtin_available', '@available'), '(', commaSep1(seq( field('platform', $.identifier), field('version', /[0-9]+(\.[0-9]+)*/), )), optional(seq(',', '*')), ')', ), statement_expression: $ => seq( '(', field('body', $._statement), ')', ), va_arg_expression: $ => seq( 'va_arg', '(', field('va_list', $._expression), ',', field('type', $.type_descriptor), ')' ), _preproc_expression: ($, superclass) => choice( $.preproc_has_include, superclass, ), preproc_has_include: $ => seq( '__has_include', '(', field('path', choice( $.string_literal, $.system_lib_string, $.module_string, )), ')' ), conditional_expression: ($, superclass) => prec.right(-2, choice( superclass, seq( field('condition', $._expression), '?', ':', field('alternative', $._expression) ), )), cast_expression: $ => prec(C.PREC.CAST, seq( '(', field('type', choice($.type_descriptor, $.block_abstract_declarator)), ')', field('value', $._expression) )), // // Lexical Structure // /#[ \t]*(import|include|pragma|if|elif|else|endif|define|undef|line|warning|error)[^\r\n]*\r?\n/ // pragma: $ => token( /#[ \t]*(pragma|warning|error)[^\r\n]*\r?\n/ ), _ifdef_if_retain: $ => token(choice( /#if[^\r\n]*\r?\n/, seq( /#if[^\r\\\n]*\\\r?\n/, repeat(/[^\r\\\n]*\\\r?\n/), /[^#\\\n]+\r?\n/ ), seq( /#if[^\r\n]*\r?\n/, /#else[ \t]*\r?\n/, ) )), _ifdef_elif_ignore: $ => token(seq( /#elif[^\r\n]*\r?\n/, repeat(choice( /([ \t]*[^#][^#]*)?\r?\n/, /[ \t]*#[ \t]*(elif|else|import|include|pragma|define|undef|line|warning|error).*\r?\n/, )), /[ \t]*#endif/, )), _ifdef_else_ignore: $ => token(choice( '#else', seq( /#else[ \t]*\r?\n/, repeat(choice( /([ \t]*[^#][^#]*)?\r?\n/, /[ \t]*#[ \t]*(import|include|pragma|define|undef|line|warning|error).*\r?\n/, )), /[ \t]*#endif/, ) )), _ifdef_endif_retain: $ => token( /#endif[^\r\n]*\r?\n?/, ), _ifdef_undef_retain: $ => token( /#undef[^\r\n]*\r?\n?/, ), } }); function commaSep (rule) { return optional(commaSep1(rule)) } function commaSep1(rule) { return seq(rule, repeat(seq(',', rule))); }