1106 lines
27 KiB
JavaScript
1106 lines
27 KiB
JavaScript
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)));
|
|
} |