tree-sitter-c-sharp
Advanced tools
Comparing version 0.13.0 to 0.15.0
1836
grammar.js
const PREC = { | ||
DOT: 17, | ||
SELECT: 16, | ||
POSTFIX: 16, | ||
@@ -21,4 +23,2 @@ PREFIX: 15, | ||
const BYTE_ORDER_MARK = '\xEF\xBB\xBF'; | ||
module.exports = grammar({ | ||
@@ -29,95 +29,66 @@ name: 'c_sharp', | ||
$.comment, | ||
/\s+/ | ||
/[\s\u00A0]+/, | ||
$.preprocessor_directive | ||
], | ||
conflicts: $ => [ | ||
[$.overloadable_unary_operator, $.overloadable_binary_operator], | ||
[$.generic_name, $._expression], | ||
[$.if_statement] | ||
[$.block, $.initializer_expression], | ||
[$.element_access_expression, $.enum_member_declaration], | ||
[$.event_declaration, $.variable_declarator], | ||
[$._expression, $.declaration_pattern], | ||
[$._expression, $._simple_name], | ||
[$._expression, $._simple_name, $.generic_name], | ||
[$._expression, $._simple_name, $.parameter], | ||
[$.from_clause, $._reserved_identifier], | ||
[$._simple_name, $.enum_member_declaration], | ||
[$._simple_name, $.type_parameter], | ||
[$._simple_name, $.generic_name], | ||
[$.qualified_name, $.explicit_interface_specifier], | ||
[$._type, $.array_creation_expression], | ||
[$._type, $.stack_alloc_array_creation_expression], | ||
[$._type, $.attribute], | ||
], | ||
inline: $ => [ | ||
$.class_type, | ||
$.return_type | ||
$.return_type, | ||
$._identifier_or_global, | ||
], | ||
word: $ => $.identifier_name, | ||
word: $ => $.identifier, | ||
rules: { | ||
compilation_unit: $ => seq( | ||
optional(BYTE_ORDER_MARK), | ||
repeat($.extern_alias_directive), | ||
repeat($.using_directive), | ||
repeat($._global_attributes), | ||
repeat(choice( | ||
$.namespace_declaration, | ||
$._type_declaration | ||
)) | ||
), | ||
// Intentionally deviates from spec so that we can syntax highlight fragments of code | ||
compilation_unit: $ => repeat($._declaration), | ||
// types | ||
_type_declaration: $ => choice( | ||
_declaration: $ => choice( | ||
$.global_attribute_list, | ||
$.class_declaration, | ||
$.constructor_declaration, | ||
$.conversion_operator_declaration, | ||
$.delegate_declaration, | ||
$.destructor_declaration, | ||
$.enum_declaration, | ||
$.event_declaration, | ||
$.extern_alias_directive, | ||
$.event_field_declaration, | ||
$.field_declaration, | ||
$.indexer_declaration, | ||
$.interface_declaration, | ||
$.method_declaration, | ||
$.namespace_declaration, | ||
$.operator_declaration, | ||
$.property_declaration, | ||
$.struct_declaration, | ||
$.enum_declaration, | ||
$.delegate_declaration, | ||
$.interface_declaration | ||
$.using_directive, | ||
), | ||
_type: $ => choice( | ||
$.predefined_type, | ||
$.identifier_name, | ||
$.generic_name | ||
), | ||
extern_alias_directive: $ => seq('extern', 'alias', $.identifier, ';'), | ||
predefined_type: $ => choice( | ||
'bool', | ||
'byte', | ||
'char', | ||
'decimal', | ||
'double', | ||
'float', | ||
'int', | ||
'long', | ||
'object', | ||
'sbyte', | ||
'short', | ||
'string', | ||
'uint', | ||
'ulong', | ||
'ushort' | ||
), | ||
type_parameter_list: $ => seq('<', commaSep1($.type_parameter), '>'), | ||
type_parameter: $ => $._type, | ||
// modifiers | ||
modifiers: $ => repeat1( | ||
choice( | ||
'abstract', | ||
'async', | ||
'extern', | ||
'internal', | ||
'new', | ||
'override', | ||
'private', | ||
'protected', | ||
'public', | ||
'readonly', | ||
'sealed', | ||
'static', | ||
'unsafe', | ||
'virtual', | ||
'volatile' | ||
) | ||
), | ||
// extern | ||
extern_alias_directive: $ => seq('extern', 'alias', $.identifier_name, ';'), | ||
// using | ||
using_directive: $ => seq( | ||
@@ -129,213 +100,264 @@ 'using', | ||
)), | ||
choice( | ||
$.qualified_name, | ||
$.identifier_name | ||
), | ||
$._name, | ||
';' | ||
), | ||
name_equals: $ => seq($.identifier_name, '='), | ||
name_equals: $ => prec(1, seq($._identifier_or_global, '=')), | ||
// namespace | ||
identifier: $ => token(seq(optional('@'), /[a-zA-Z_][a-zA-Z_0-9]*/)), // identifier_token in Roslyn | ||
global: $ => 'global', | ||
_identifier_or_global: $ => choice($.global, $.identifier), | ||
namespace_declaration: $ => seq( | ||
'namespace', | ||
_name: $ => choice( | ||
$.alias_qualified_name, | ||
$.qualified_name, | ||
$._simple_name | ||
), | ||
alias_qualified_name: $ => seq($._identifier_or_global, '::', $._simple_name), | ||
_simple_name: $ => choice( | ||
$.generic_name, | ||
$._identifier_or_global | ||
), | ||
generic_name: $ => seq($.identifier, $.type_argument_list), | ||
// Intentionally different from Roslyn to avoid non-matching | ||
// omitted_type_argument in a lot of unnecessary places. | ||
type_argument_list: $ => seq( | ||
'<', | ||
choice( | ||
$.qualified_name, | ||
$.identifier_name | ||
repeat(','), | ||
commaSep1($._type), | ||
), | ||
'{', | ||
repeat(choice( | ||
$.namespace_declaration, | ||
$.using_directive, | ||
$._type_declaration | ||
)), | ||
'}', | ||
optional(';') | ||
'>' | ||
), | ||
// properties | ||
qualified_name: $ => prec(PREC.DOT, seq($._name, '.', $._simple_name)), | ||
property_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
$._type, | ||
$.identifier_name, | ||
$._property_body | ||
attribute_list: $ => seq('[', optional($.attribute_target_specifier), commaSep1($.attribute), ']'), | ||
attribute_target_specifier: $ => seq( | ||
choice('field', 'event', 'method', 'param', 'property', 'return', 'type'), | ||
':' | ||
), | ||
attribute: $ => seq($._name, optional($.attribute_argument_list)), | ||
_property_body: $ => choice( | ||
seq('{', $._accessor_declarations, '}', optional($._property_initializer)), | ||
seq('=>', $._expression, ';') | ||
attribute_argument_list: $ => seq( | ||
'(', | ||
commaSep($.attribute_argument), | ||
')' | ||
), | ||
_property_initializer: $ => seq('=', $.variable_initializer, ';'), | ||
_accessor_declarations: $ => choice( | ||
seq($.get_accessor_declaration, optional($.set_accessor_declaration)), | ||
seq($.set_accessor_declaration, optional($.get_accessor_declaration)) | ||
attribute_argument: $ => seq( | ||
optional(choice($.name_equals,$.name_colon)), | ||
$._expression | ||
), | ||
get_accessor_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.accessor_modifier), | ||
'get', | ||
choice($.statement_block, ';') | ||
global_attribute_list: $ => seq( | ||
'[', | ||
choice('assembly', 'module'), | ||
':', | ||
commaSep($.attribute), | ||
']' | ||
), | ||
set_accessor_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.accessor_modifier), | ||
'set', | ||
choice($.statement_block, ';') | ||
name_colon: $ => seq($._identifier_or_global, ':'), | ||
event_field_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'event', | ||
$.variable_declaration, | ||
';' | ||
), | ||
accessor_modifier: $ => choice( | ||
'protected', | ||
modifier: $ => prec.right(choice( | ||
'abstract', | ||
'async', | ||
'const', | ||
'extern', | ||
'fixed', | ||
'internal', | ||
'new', | ||
'override', | ||
'partial', | ||
'private', | ||
seq('protected', 'internal'), | ||
seq('internal', 'protected') | ||
'protected', | ||
'public', | ||
'readonly', | ||
'ref', | ||
'sealed', | ||
'static', | ||
'unsafe', | ||
'virtual', | ||
'volatile' | ||
)), | ||
variable_declaration: $ => seq($._type, commaSep1($.variable_declarator)), | ||
variable_declarator: $ => seq( | ||
$.identifier, | ||
optional($.bracketed_argument_list), | ||
optional($.equals_value_clause) | ||
), | ||
// class | ||
bracketed_argument_list: $ => seq( | ||
'[', | ||
commaSep1($.argument), | ||
']' | ||
), | ||
class_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
optional('partial'), | ||
'class', | ||
$.identifier_name, | ||
optional($.type_parameter_list), | ||
optional($.class_base), | ||
repeat($.type_parameter_constraints_clause), | ||
'{', | ||
repeat( | ||
choice( | ||
$.constant_declaration, | ||
$.field_declaration, | ||
$.method_declaration, | ||
$.property_declaration, | ||
$.event_declaration, | ||
$.indexer_declaration, | ||
$.operator_declaration, | ||
$.constructor_declaration, | ||
$.destructor_declaration, | ||
$._type_declaration | ||
), | ||
argument: $ => prec(1, seq( | ||
optional($.name_colon), | ||
choice( | ||
seq( | ||
optional(choice('ref','out', 'in')), | ||
$._expression | ||
), | ||
'}', | ||
optional(';') | ||
), | ||
seq( | ||
'out', | ||
$._type, | ||
$.identifier | ||
) | ||
) | ||
)), | ||
class_base: $ => seq( | ||
':', | ||
$.class_type, | ||
optional(seq(', ', commaSep1($.identifier_name))) | ||
equals_value_clause: $ => seq('=', $._expression), | ||
field_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$.variable_declaration, | ||
';' | ||
), | ||
class_type: $ => choice( | ||
$.identifier_name, | ||
'object', | ||
'dynamic', | ||
'string' | ||
constructor_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$.identifier, | ||
$.parameter_list, | ||
optional($.constructor_initializer), | ||
$._function_body | ||
), | ||
type_parameter_constraints_clause: $ => seq( | ||
'where', $.identifier_name, ':', $.type_parameter_constraints | ||
// Params varies quite a lot from grammar.txt as that handles neither 'out' nor 'params' or arrays... | ||
parameter_list: $ => seq( | ||
'(', | ||
optional($._formal_parameter_list), | ||
')' | ||
), | ||
type_parameter_constraints: $ => choice( | ||
$.constructor_constraint, | ||
seq( | ||
choice( | ||
$.class_type, | ||
'class', | ||
'struct' | ||
), | ||
optional(seq(',', commaSep1($.identifier_name))), | ||
optional(seq(',', $.constructor_constraint)) | ||
) | ||
_formal_parameter_list: $ => commaSep1(choice( | ||
$.parameter, | ||
$.parameter_array | ||
)), | ||
parameter: $ => seq( | ||
repeat($.attribute_list), | ||
optional($.parameter_modifier), | ||
optional($._type), | ||
$.identifier, | ||
optional($.equals_value_clause) | ||
), | ||
constructor_constraint: $ => seq('new', '(', ')'), | ||
parameter_modifier: $ => prec.right(choice('ref', 'out', 'this', 'in')), | ||
// indexers | ||
parameter_array: $ => seq( | ||
repeat($.attribute_list), | ||
'params', | ||
$.array_type, | ||
$.identifier | ||
), | ||
indexer_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
$._indexer_declarator, | ||
$._indexer_body | ||
constructor_initializer: $ => seq( | ||
':', | ||
choice('base', 'this'), | ||
$.argument_list | ||
), | ||
_indexer_declarator: $ => choice( | ||
seq($._type, 'this', '[', $._formal_parameter_list, ']'), | ||
seq( | ||
$._type, | ||
$.identifier_name, | ||
'.', | ||
'this', | ||
'[', | ||
$._formal_parameter_list, | ||
']' | ||
argument_list: $ => seq('(', commaSep($.argument), ')'), | ||
block: $ => seq('{', repeat($._statement), '}'), | ||
arrow_expression_clause: $ => seq('=>', $._expression), | ||
conversion_operator_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
choice( | ||
'implicit', | ||
'explicit' | ||
), | ||
'operator', | ||
$._type, | ||
$.parameter_list, | ||
$._function_body, | ||
), | ||
_indexer_body: $ => choice( | ||
seq('{', $._accessor_declarations, '}'), | ||
seq('=>', $._expression, ';'), | ||
_function_body: $ => choice( | ||
$.block, | ||
seq($.arrow_expression_clause, ';'), | ||
';' // Only applies to interfaces | ||
), | ||
// events | ||
destructor_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
optional('extern'), | ||
'~', | ||
$.identifier, | ||
$.parameter_list, | ||
$._function_body | ||
), | ||
event_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
'event', | ||
$._type, | ||
$.identifier_name, | ||
'{', | ||
choice( | ||
seq($.add_accessor_declaration, $.remove_accessor_declaration), | ||
seq($.remove_accessor_declaration, $.add_accessor_declaration) | ||
), | ||
'}' | ||
), | ||
method_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$.return_type, | ||
optional($.explicit_interface_specifier), | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
$.parameter_list, | ||
repeat($.type_parameter_constraints_clause), | ||
$._function_body, | ||
), | ||
add_accessor_declaration: $ => seq(optional($._attributes), 'add', $.statement_block), | ||
remove_accessor_declaration: $ => seq(optional($._attributes), 'remove', $.statement_block), | ||
explicit_interface_specifier: $ => prec(PREC.DOT, seq($._name, '.')), | ||
// operator declarations | ||
type_parameter_list: $ => seq('<', commaSep1($.type_parameter), '>'), | ||
operator_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
$._operator_declarator, | ||
choice( | ||
$.statement_block, | ||
seq('=>', $._expression, ';'), | ||
';' | ||
) | ||
type_parameter: $ => seq( | ||
optional($.attribute_list), | ||
optional(choice('in', 'out')), | ||
$.identifier | ||
), | ||
_operator_declarator: $ => choice( | ||
$._unary_operator_declarator, | ||
$._binary_operator_declarator, | ||
$._conversion_operator_declarator | ||
type_parameter_constraints_clause: $ => seq( | ||
'where', $._identifier_or_global, ':', commaSep1($.type_parameter_constraint) | ||
), | ||
_unary_operator_declarator: $ => seq( | ||
type_parameter_constraint: $ => choice( | ||
'class', | ||
'struct', | ||
'unmanaged', | ||
$.constructor_constraint, | ||
$.type_constraint | ||
), | ||
constructor_constraint: $ => seq('new', '(', ')'), | ||
type_constraint: $ => $._type, | ||
operator_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$._type, | ||
'operator', | ||
$.overloadable_unary_operator, | ||
'(', | ||
$._type, | ||
$.identifier_name, | ||
')' | ||
$._overloadable_operator, | ||
$.parameter_list, | ||
$._function_body, | ||
), | ||
overloadable_unary_operator: $ => choice( | ||
'+', | ||
'-', | ||
_overloadable_operator: $ => choice( | ||
'!', | ||
@@ -346,19 +368,3 @@ '~', | ||
'true', | ||
'false' | ||
), | ||
_binary_operator_declarator: $ => seq( | ||
$._type, | ||
'operator', | ||
$.overloadable_binary_operator, | ||
'(', | ||
$._type, | ||
$.identifier_name, | ||
',', | ||
$._type, | ||
$.identifier_name, | ||
')' | ||
), | ||
overloadable_binary_operator: $=> choice( | ||
'false', | ||
'+', '-', | ||
@@ -374,677 +380,1015 @@ '*', '/', | ||
_conversion_operator_declarator: $=> seq( | ||
$.overloadable_conversion_operator, | ||
'operator', | ||
event_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'event', | ||
$._type, | ||
'(', | ||
optional($.explicit_interface_specifier), | ||
$.identifier, | ||
choice( | ||
$._accessor_list, | ||
';' | ||
) | ||
), | ||
_accessor_list: $ => seq( | ||
'{', | ||
repeat($.accessor_declaration), | ||
'}' | ||
), | ||
accessor_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
choice('get', 'set', 'add', 'remove', $.identifier), | ||
$._function_body | ||
), | ||
indexer_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$._type, | ||
$.identifier_name, | ||
')' | ||
optional($.explicit_interface_specifier), | ||
'this', | ||
$.bracketed_parameter_list, | ||
choice( | ||
$._accessor_list, | ||
seq($.arrow_expression_clause, ';') | ||
) | ||
), | ||
overloadable_conversion_operator: $ => choice( | ||
'implicit', | ||
'explicit' | ||
bracketed_parameter_list: $ => seq('[', commaSep1($.parameter), ']'), | ||
property_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
$._type, | ||
optional($.explicit_interface_specifier), | ||
$.identifier, | ||
choice( | ||
seq($._accessor_list, optional(seq('=', $._expression, ';'))), // grammar.txt does not allow bodyless properties. | ||
seq($.arrow_expression_clause, ';') | ||
), | ||
), | ||
// interface | ||
enum_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'enum', | ||
$.identifier, | ||
optional($.base_list), | ||
$.enum_member_declaration_list, | ||
optional(';') | ||
), | ||
interface_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
optional('partial'), | ||
'interface', | ||
$.identifier_name, | ||
optional($.type_parameter_list), | ||
optional($.interface_base), | ||
repeat($.type_parameter_constraints_clause), | ||
base_list: $ => seq(':', commaSep1($._type)), | ||
enum_member_declaration_list: $ => seq( | ||
'{', | ||
repeat( | ||
choice( | ||
$.interface_method_declaration, | ||
$.interface_event_declaration, | ||
$.interface_property_declaration, | ||
$.interface_indexer_declaration | ||
) | ||
), | ||
commaSep($.enum_member_declaration), | ||
optional(','), | ||
'}', | ||
optional(';') | ||
), | ||
interface_base: $ => seq( | ||
':', | ||
$.identifier_name, | ||
optional(seq(',', commaSep1($.identifier_name))) | ||
enum_member_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
$.identifier, | ||
optional(seq('=', $._expression)) | ||
), | ||
interface_method_declaration: $ => seq( | ||
optional($._attributes), | ||
optional('new'), | ||
$.return_type, | ||
$.identifier_name, | ||
class_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'class', | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
optional($.parameter_list), | ||
optional($.base_list), | ||
repeat($.type_parameter_constraints_clause), | ||
';' | ||
$.declaration_list, | ||
optional(';') | ||
), | ||
interface_event_declaration: $ => seq( | ||
optional($._attributes), | ||
optional('new'), | ||
'event', | ||
$._type, | ||
$.identifier_name, | ||
';' | ||
), | ||
interface_property_declaration: $ => seq( | ||
optional($._attributes), | ||
optional('new'), | ||
$._type, | ||
$.identifier_name, | ||
declaration_list: $ => seq( | ||
'{', | ||
repeat1($.interface_accessor), | ||
repeat($._declaration), | ||
'}' | ||
), | ||
interface_accessor: $ => seq(optional($._attributes), choice('get', 'set'), ';'), | ||
interface_indexer_declaration: $ => seq( | ||
optional($._attributes), | ||
optional('new'), | ||
$._type, | ||
'this', | ||
'[', | ||
$._formal_parameter_list, | ||
']', | ||
'{', | ||
repeat1($.interface_accessor), | ||
'}' | ||
interface_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'interface', | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
optional($.base_list), | ||
repeat($.type_parameter_constraints_clause), | ||
$.declaration_list, | ||
optional(';') | ||
), | ||
// struct | ||
struct_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
optional('partial'), | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'struct', | ||
$.identifier_name, | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
optional($.struct_interfaces), | ||
optional($.base_list), | ||
repeat($.type_parameter_constraints_clause), | ||
'{', | ||
repeat( | ||
choice( | ||
$.constant_declaration, | ||
$.field_declaration, | ||
$.method_declaration, | ||
$.property_declaration, | ||
$.event_declaration, | ||
$.indexer_declaration, | ||
$.operator_declaration, | ||
$.constructor_declaration, | ||
$._type_declaration | ||
) | ||
), | ||
'}', | ||
$.declaration_list, | ||
optional(';') | ||
), | ||
struct_interfaces: $ => seq(':', commaSep1($.identifier_name)), | ||
delegate_declaration: $ => seq( | ||
repeat($.attribute_list), | ||
repeat($.modifier), | ||
'delegate', | ||
$.return_type, | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
$.parameter_list, | ||
repeat($.type_parameter_constraints_clause), | ||
';' | ||
), | ||
// enum | ||
enum_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
'enum', | ||
$.identifier_name, | ||
optional(seq(':', $._integral_type)), | ||
'{', | ||
commaSep1($.enum_member_declaration), | ||
'}', | ||
namespace_declaration: $ => seq( | ||
'namespace', | ||
$._name, | ||
$.declaration_list, | ||
optional(';') | ||
), | ||
enum_member_declaration: $ => seq( | ||
optional($._attributes), | ||
$.identifier_name, | ||
optional(seq('=', $.constant_expression)) | ||
_type: $ => choice( | ||
$.implicit_type, | ||
$.array_type, | ||
$._name, | ||
$.nullable_type, | ||
$.pointer_type, | ||
$.predefined_type, | ||
// $.ref_type, // TODO: Conflicts with 'ref' modifier... | ||
// $.tuple_type, // TODO: Conflicts with everything | ||
), | ||
_integral_type: $ => choice( | ||
implicit_type: $ => 'var', | ||
array_type: $ => prec(PREC.POSTFIX, seq($._type, $.array_rank_specifier)), | ||
// grammar.txt marks this non-optional and includes omitted_array_size_expression in | ||
// expression but we can't match empty rules. | ||
array_rank_specifier: $ => seq('[', commaSep(optional($._expression)), ']'), | ||
nullable_type: $ => prec(1, seq($._type, '?')), | ||
pointer_type: $ => prec(1, seq($._type, '*')), | ||
predefined_type: $ => choice( | ||
'bool', | ||
'byte', | ||
'char', | ||
'decimal', | ||
'double', | ||
'float', | ||
'int', | ||
'long', | ||
'object', | ||
'sbyte', | ||
'byte', | ||
'short', | ||
'ushort', | ||
'int', | ||
'string', | ||
'uint', | ||
'long', | ||
'ulong', | ||
'char' | ||
'ushort', | ||
// void is handled in return_type for better matching | ||
), | ||
// delegate | ||
delegate_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
'delegate', | ||
$.return_type, | ||
$.identifier_name, | ||
// TODO: Variant type parameters | ||
$.parameter_list, | ||
';' | ||
ref_type: $ => seq( | ||
'ref', | ||
optional('readonly'), | ||
$._type | ||
), | ||
return_type: $ => choice($._type, $.void_keyword), | ||
void_keyword: $ => 'void', | ||
// parameters | ||
parameter_list: $ => seq( | ||
tuple_type: $ => seq( | ||
'(', | ||
optional($._formal_parameter_list), | ||
$.tuple_element, | ||
repeat1(seq( | ||
',', | ||
$.argument, | ||
)), | ||
')' | ||
), | ||
_formal_parameter_list: $ => choice( | ||
commaSep1($.parameter), | ||
seq(commaSep1($.parameter), $.parameter_array), | ||
$.parameter_array | ||
), | ||
tuple_element: $ => seq($._type, optional($.identifier)), | ||
parameter: $ => seq( | ||
optional($._attributes), | ||
optional($.parameter_modifier), | ||
$._type, | ||
$.identifier_name, | ||
optional($.default_argument) | ||
_statement: $ => choice( | ||
$.block, | ||
$.break_statement, | ||
$.checked_statement, | ||
$.continue_statement, | ||
$.do_statement, | ||
$.empty_statement, | ||
$.expression_statement, | ||
$.fixed_statement, | ||
$.for_each_statement, | ||
$.for_statement, | ||
$.goto_statement, | ||
$.if_statement, | ||
$.labeled_statement, | ||
$.local_declaration_statement, | ||
$.local_function_statement, | ||
$.lock_statement, | ||
$.return_statement, | ||
$.switch_statement, | ||
$.throw_statement, | ||
$.try_statement, | ||
$.unsafe_statement, | ||
$.using_statement, | ||
$.while_statement, | ||
$.yield_statement, | ||
), | ||
default_argument: $ => seq('=', $._expression), | ||
parameter_modifier: $ => choice('ref', 'out', 'this'), | ||
break_statement: $ => seq('break', ';'), | ||
parameter_array: $ => seq( | ||
optional($._attributes), | ||
'params', | ||
$.array_type, | ||
$.identifier_name | ||
), | ||
checked_statement: $ => seq(choice('checked', 'unchecked'), $.block), | ||
// arrays | ||
continue_statement: $ => seq('continue', ';'), | ||
array_type: $ => seq($._type, $.array_rank_specifier), | ||
array_rank_specifier: $ => seq('[', repeat(','), ']'), | ||
do_statement: $ => seq('do', $._statement, 'while', '(', $._expression, ')', ';'), | ||
// attributes | ||
empty_statement: $ => ';', | ||
_attributes: $ => repeat1($._attribute_section), | ||
_attribute_section: $ => seq('[', $.attribute_list, ']'), | ||
attribute_list: $ => commaSep1($.attribute), | ||
attribute: $ => seq($.identifier_name, optional($.attribute_argument_list)), | ||
expression_statement: $ => seq($._expression, ';'), | ||
attribute_argument_list: $ => seq( | ||
fixed_statement: $ => seq('fixed', '(', $.variable_declaration, ')', $._statement), | ||
for_statement: $ => seq( | ||
'for', | ||
'(', | ||
// TODO: attribute_arguments | ||
')' | ||
optional(choice($.variable_declaration, commaSep1($._expression))), | ||
';', | ||
optional($._expression), | ||
';', | ||
optional(commaSep1($._expression)), | ||
')', | ||
$._statement | ||
), | ||
_global_attributes: $ => seq( | ||
'[', | ||
choice('assembly', 'module'), | ||
':', | ||
$.attribute_list, | ||
']' | ||
// Combines for_each_statement and for_each_variable_statement from grammar.txt | ||
for_each_statement: $ => seq( | ||
optional('await'), | ||
'foreach', | ||
'(', | ||
choice( | ||
seq($._type, $.identifier), // for_each_statement | ||
$._expression, // for_each_variable_statement | ||
), | ||
'in', | ||
$._expression, | ||
')', | ||
$._statement | ||
), | ||
// fields | ||
field_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
$.variable_declaration, | ||
// grammar.txt one doesn't seem to make sense so we do this instead | ||
goto_statement: $ => seq( | ||
'goto', | ||
choice( | ||
alias($.identifier, $.label_name), | ||
seq('case', $._expression), | ||
'default' | ||
), | ||
';' | ||
), | ||
variable_declaration: $ => seq($._type, commaSep1($.variable_declarator)), | ||
generic_name: $ => seq($.identifier_name, $.type_parameter_list), | ||
variable_declarator: $ => seq($.identifier_name, optional($.equals_value_clause)), | ||
variable_initializer: $ => choice( | ||
if_statement: $ => prec.right(seq( | ||
'if', | ||
'(', | ||
$._expression, | ||
$.array_initalizer | ||
), | ||
')', | ||
$._statement, | ||
optional($.else_clause) | ||
)), | ||
array_initalizer: $ => seq('{', commaSep1($.variable_initializer), '}'), | ||
equals_value_clause: $ => seq('=', $._expression), | ||
else_clause: $ => seq('else', $._statement), | ||
// constants | ||
labeled_statement: $ => seq( | ||
alias($.identifier, $.label_name), | ||
':', | ||
$._statement | ||
), | ||
constant_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
'const', | ||
$._type, | ||
$._constant_declarators, | ||
local_declaration_statement: $ => seq( | ||
optional('await'), | ||
optional('using'), | ||
repeat($.modifier), | ||
$.variable_declaration, | ||
';' | ||
), | ||
_constant_declarators: $ => seq( | ||
$.constant_declarator, | ||
repeat(seq(',', $.constant_declarator)) | ||
local_function_statement: $ => seq( | ||
repeat($.modifier), | ||
$.return_type, | ||
$.identifier, | ||
optional($.type_parameter_list), | ||
$.parameter_list, | ||
repeat($.type_parameter_constraints_clause), | ||
$._function_body | ||
), | ||
constant_declarator: $ => seq($.identifier_name, '=', $.constant_expression), | ||
lock_statement: $ => seq('lock', '(', $._expression, ')', $._statement), | ||
// expressions | ||
return_statement: $ => seq('return', optional($._expression), ';'), | ||
_expression: $ => choice( | ||
$.identifier_name, | ||
$._literal, | ||
$.ternary_expression, | ||
$.binary_expression, | ||
$.unary_expression, | ||
$.parenthesized_expression | ||
switch_statement: $ => seq( | ||
'switch', | ||
'(', | ||
$._expression, | ||
')', | ||
'{', | ||
repeat($.switch_section), | ||
'}' | ||
), | ||
boolean_expression: $ => $._expression, | ||
constant_expression: $ => $._expression, | ||
parenthesized_expression: $ => seq('(', $._expression, ')'), | ||
ternary_expression: $ => prec.right(PREC.COND, seq( | ||
$._expression, '?', $._expression, ':', $._expression | ||
switch_section: $ => prec.left(seq( | ||
repeat1(choice( // switch_label | ||
$.case_switch_label, | ||
$.case_pattern_switch_label, | ||
$.default_switch_label | ||
)), | ||
repeat1($._statement) | ||
)), | ||
binary_expression: $ => choice( | ||
...[ | ||
['&&', PREC.LOGAND], | ||
['||', PREC.LOGOR], | ||
['>>', PREC.SHIFT], | ||
['<<', PREC.SHIFT], | ||
['&', PREC.AND], | ||
['^', PREC.OR], | ||
['|', PREC.OR], | ||
['+', PREC.ADD], | ||
['-', PREC.ADD], | ||
['*', PREC.MULT], | ||
['/', PREC.MULT], | ||
['%', PREC.MULT], | ||
['<', PREC.REL], | ||
['<=', PREC.REL], | ||
['==', PREC.EQUAL], | ||
['!=', PREC.EQUAL], | ||
['>=', PREC.REL], | ||
['>', PREC.REL], | ||
].map(([operator, precedence]) => | ||
prec.left(precedence, seq($._expression, operator, $._expression)) | ||
) | ||
case_pattern_switch_label: $ => seq( | ||
'case', | ||
$._pattern, | ||
optional($.when_clause), | ||
':' | ||
), | ||
unary_expression: $ => prec.right(PREC.UNARY, choice( | ||
...[ | ||
'!', | ||
'~', | ||
'-', | ||
'+', | ||
'--', | ||
'++', | ||
'typeof', | ||
'sizeof' | ||
].map(operator => seq(operator, $._expression)))), | ||
_pattern: $ => choice( | ||
$.constant_pattern, | ||
$.declaration_pattern, | ||
$.discard, | ||
// $.recursive_pattern, | ||
$.var_pattern | ||
), | ||
// TODO, hook this up and fix issues with it | ||
postfix_expression: $ => prec.left(PREC.POSTFIX, choice( | ||
seq($._expression, '++'), | ||
seq($._expression, '--'), | ||
)), | ||
constant_pattern: $ => prec.right($._expression), | ||
// literals | ||
declaration_pattern: $ => seq($._type, $._variable_designation), | ||
_literal: $ => choice( | ||
$.boolean_literal, | ||
$.character_literal, | ||
$.integer_literal, | ||
$.null_literal, | ||
$.real_literal, | ||
$.string_literal | ||
_variable_designation: $ => choice( | ||
$.discard, | ||
$.parenthesized_variable_designation, | ||
$.identifier | ||
), | ||
boolean_literal: $ => choice( | ||
'true', | ||
'false' | ||
discard: $ => '_', | ||
parenthesized_variable_designation: $ => seq( | ||
'(', | ||
commaSep($._variable_designation), | ||
')' | ||
), | ||
character_literal: $ => seq( | ||
"'", | ||
choice( | ||
/[^']/, | ||
$._simple_escape_sequence, | ||
$._hexadecimal_escape_sequence, | ||
$._unicode_escape_sequence | ||
), | ||
"'" | ||
// TODO: Matches everything as optional which won't work. | ||
// Figure out valid combinations with at least one item to remove ambiguity. | ||
recursive_pattern: $ => seq( | ||
optional($._type), | ||
optional($.positional_pattern_clause), | ||
optional($.property_pattern_clause), | ||
optional($._variable_designation) | ||
), | ||
_hexadecimal_escape_sequence: $ => | ||
(/\\x[0-9a-fA-F][0-9a-fA-F]?[0-9a-fA-F]?[0-9a-fA-F]?/), | ||
positional_pattern_clause: $ => seq('(', commaSep($.subpattern), ')'), | ||
_unicode_escape_sequence: $ => choice( | ||
(/\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]/), | ||
(/\\U[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]/) | ||
subpattern: $ => seq( | ||
optional($.name_colon), | ||
$._pattern | ||
), | ||
_simple_escape_sequence: $ => choice( | ||
"\\'", | ||
'\\"', | ||
'\\\\', | ||
'\\0', | ||
'\\a', | ||
'\\b', | ||
'\\f', | ||
'\\n', | ||
'\\r', | ||
'\\t', | ||
'\\v' | ||
property_pattern_clause: $ => seq( | ||
'{', | ||
commaSep($.subpattern), | ||
'}' | ||
), | ||
integer_literal: $ => seq( | ||
choice( | ||
(/[0-9]+/), | ||
(/0x[0-9a-fA-F]+/) | ||
), | ||
optional($._integer_type_suffix) | ||
var_pattern: $ => prec(1, seq('var', $._variable_designation)), | ||
when_clause: $ => seq('when', $._expression), | ||
case_switch_label: $ => prec.left(1, seq('case', $._expression, ':')), | ||
default_switch_label: $ => prec.left(1, seq('default', ':')), | ||
throw_statement: $ => seq('throw', optional($._expression), ';'), | ||
try_statement: $ => seq( | ||
'try', | ||
$.block, | ||
repeat($.catch_clause), | ||
optional($.finally_clause), | ||
), | ||
_integer_type_suffix: $ => (/u|U|l|L|ul|UL|uL|Ul|lu|LU|Lu|lU/), | ||
catch_clause: $ => seq( | ||
'catch', | ||
optional($.catch_declaration), | ||
optional($.catch_filter_clause), | ||
$.block | ||
), | ||
null_literal: $ => 'null', | ||
catch_declaration: $ => seq('(', $._type, optional($.identifier), ')'), | ||
real_literal: $ => choice( | ||
seq( | ||
(/[0-9]+/), | ||
'.', | ||
(/[0-9]+/), | ||
optional($._exponent_part), | ||
optional($._real_type_suffix) | ||
catch_filter_clause: $ => seq('when', '(', $._expression, ')'), | ||
finally_clause: $ => seq('finally', $.block), | ||
unsafe_statement: $ => seq('unsafe', $.block), | ||
using_statement: $ => seq( | ||
optional('await'), | ||
'using', | ||
'(', | ||
choice($.variable_declaration, $._expression), | ||
')', | ||
$._statement | ||
), | ||
while_statement: $ => seq('while', '(', $._expression, ')', $._statement), | ||
yield_statement: $ => seq( | ||
'yield', | ||
choice( // grammar.txt incorrectly allows "break expression", we do not. | ||
seq('return', $._expression), | ||
'break' | ||
), | ||
seq( | ||
'.', | ||
(/[0-9]+/), | ||
optional($._exponent_part), | ||
optional($._real_type_suffix) | ||
), | ||
seq( | ||
(/[0-9]+/), | ||
$._exponent_part, | ||
optional($._real_type_suffix) | ||
), | ||
seq( | ||
(/[0-9]+/), | ||
$._real_type_suffix | ||
) | ||
';' | ||
), | ||
_real_type_suffix: $ => (/[fFdDmm]/), | ||
anonymous_method_expression: $ => seq( | ||
optional('async'), | ||
'delegate', | ||
optional($.parameter_list), | ||
$.block | ||
), | ||
_exponent_part: $ => (/[eE][+-]?[0-9]+/), | ||
lambda_expression: $ => prec(-1, seq( | ||
optional('async'), | ||
choice($.parameter_list, $.identifier), | ||
'=>', | ||
choice($.block, $._expression) | ||
)), | ||
string_literal: $ => choice( | ||
$._regular_string_literal, | ||
$._verbatim_string_literal | ||
anonymous_object_creation_expression: $ => seq( | ||
'new', | ||
'{', | ||
commaSep($._anonymous_object_member_declarator), | ||
optional(','), | ||
'}' | ||
), | ||
_regular_string_literal: $ => seq( | ||
'"', | ||
repeat($._regular_string_literal_character), | ||
'"' | ||
_anonymous_object_member_declarator: $ => choice( | ||
prec.dynamic(PREC.ASSIGN, seq($.name_equals, $._expression)), | ||
$._expression | ||
), | ||
_regular_string_literal_character: $ => choice( | ||
/[^"\\\n]/, | ||
$._simple_escape_sequence, | ||
$._hexadecimal_escape_sequence, | ||
$._unicode_escape_sequence | ||
array_creation_expression: $ => seq( | ||
'new', | ||
$.array_type, | ||
optional($.initializer_expression) | ||
), | ||
_verbatim_string_literal: $ => seq( | ||
'@"', | ||
/[^"]*/, | ||
'"' | ||
initializer_expression: $ => seq( | ||
'{', | ||
commaSep($._expression), | ||
optional(','), | ||
'}' | ||
), | ||
// names | ||
assignment_expression: $ => prec.right(seq( | ||
$._expression, | ||
$.assignment_operator, | ||
$._expression | ||
)), | ||
qualified_name: $ => seq( | ||
choice( | ||
$.identifier_name, | ||
$.qualified_name, | ||
$.alias_qualified_name | ||
), | ||
'.', | ||
$.identifier_name | ||
assignment_operator: $ => choice('=', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '|=', '<<=', '>>=', '??='), | ||
await_expression: $ => prec.right(PREC.UNARY, seq('await', $._expression)), | ||
cast_expression: $ => prec.right(PREC.CAST, seq( | ||
'(', | ||
$._type, | ||
')', | ||
$._expression | ||
)), | ||
checked_expression: $ => choice( | ||
seq('checked', '(', $._expression, ')'), | ||
seq('unchecked', '(', $._expression, ')') | ||
), | ||
alias_qualified_name: $ => seq('global', '::', $.identifier_name), | ||
identifier_name: $ => /[a-zA-Z_][a-zA-Z_0-9]*/, | ||
conditional_access_expression: $ => prec.right(seq( | ||
$._expression, | ||
'?', | ||
$._expression | ||
)), | ||
// commments | ||
conditional_expression: $ => prec.right(PREC.COND, seq( | ||
$._expression, '?', $._expression, ':', $._expression | ||
)), | ||
comment: $ => token(choice( | ||
seq('//', /.*/), | ||
seq( | ||
'/*', | ||
repeat(choice( | ||
/[^*]/, | ||
/\*[^/]/ | ||
)), | ||
'*/' | ||
declaration_expression: $ => seq( | ||
$._type, | ||
$._variable_designation | ||
), | ||
default_expression: $ => prec.right(seq( | ||
'default', | ||
optional( | ||
seq( | ||
'(', | ||
$._type, | ||
')' | ||
) | ||
) | ||
)), | ||
// methods | ||
element_access_expression: $ => seq($._expression, $.bracketed_argument_list), | ||
constructor_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
$.identifier_name, | ||
optional($.type_parameter_list), | ||
$.parameter_list, | ||
$.statement_block | ||
element_binding_expression: $ => $.bracketed_argument_list, | ||
implicit_array_creation_expression: $ => seq( | ||
'new', | ||
'[', | ||
repeat(','), | ||
']', | ||
$.initializer_expression | ||
), | ||
destructor_declaration: $ => seq( | ||
optional($._attributes), | ||
optional('extern'), | ||
'~', | ||
$.identifier_name, | ||
$.parameter_list, | ||
$.statement_block | ||
implicit_stack_alloc_array_creation_expression: $ => seq( | ||
'stackalloc', | ||
'[', | ||
']', | ||
$.initializer_expression | ||
), | ||
method_declaration: $ => seq( | ||
optional($._attributes), | ||
optional($.modifiers), | ||
optional('partial'), | ||
$.return_type, | ||
$.identifier_name, | ||
optional($.type_parameter_list), | ||
$.parameter_list, | ||
repeat($.type_parameter_constraints_clause), | ||
$.statement_block | ||
base_expression: $ => 'base', | ||
this_expression: $ => 'this', | ||
interpolated_string_expression: $ => choice( | ||
seq('$"', repeat($._interpolated_string_content), '"'), | ||
seq('$@"', repeat($._interpolated_verbatim_string_content), '"'), | ||
), | ||
// Statements | ||
_interpolated_string_content: $ => choice( | ||
$.interpolated_string_text, | ||
$.interpolation | ||
), | ||
_statement: $ => choice( | ||
$._labeled_statement, | ||
$._embedded_statement, | ||
$._declaration_statement, | ||
$.variable_assignment_statement // TODO: Remove | ||
_interpolated_verbatim_string_content: $ => choice( | ||
$.interpolated_verbatim_string_text, | ||
$.interpolation | ||
), | ||
variable_assignment_statement: $ => seq($.identifier_name, $.equals_value_clause, ';'), | ||
interpolated_string_text: $ => choice( | ||
'{{', | ||
token.immediate(prec(1, /[^{"\\\n]+/)), | ||
$.escape_sequence | ||
), | ||
statement_block: $ => seq('{', optional($._statement_list), '}'), | ||
_statement_list: $ => repeat1($._statement), | ||
_labeled_statement: $ => seq( | ||
alias($.identifier_name, $.label_name), | ||
':', | ||
$._statement | ||
interpolated_verbatim_string_text: $ => choice(/[^{"]+/, '""'), | ||
interpolation: $ => seq( | ||
'{', | ||
$._expression, | ||
optional($.interpolation_alignment_clause), | ||
optional($.interpolation_format_clause), | ||
'}' | ||
), | ||
// Embedded statements | ||
interpolation_alignment_clause: $ => seq(',', $._expression), | ||
_embedded_statement: $ => choice( | ||
$.statement_block, | ||
$.empty_statement, | ||
$.expression_statement, | ||
$._selection_statement, | ||
$._iteration_statement, | ||
$._jump_statement, | ||
$.try_statement, | ||
$.checked_statement, | ||
$.unchecked_statement, | ||
$.lock_statement, | ||
$.yield_statement | ||
interpolation_format_clause: $ => seq(':', /[^}"]+/), | ||
invocation_expression: $ => seq( | ||
$._expression, | ||
$.argument_list | ||
), | ||
empty_statement: $ => ';', | ||
expression_statement: $ => seq($._expression, ';'), | ||
is_pattern_expression: $ => prec.left(PREC.EQUAL, seq( | ||
$._expression, | ||
'is', | ||
$._pattern | ||
)), | ||
_selection_statement: $ => choice( | ||
$.if_statement, | ||
$.switch_statement | ||
make_ref_expression: $ => seq( | ||
'__makeref', | ||
'(', | ||
$._expression, | ||
')' | ||
), | ||
_iteration_statement: $ => choice( | ||
$.while_statement, | ||
$.do_statement | ||
member_access_expression: $ => prec(PREC.DOT, seq( | ||
$._expression, | ||
choice('.', '->'), | ||
$._simple_name | ||
)), | ||
member_binding_expression: $ => seq( | ||
'.', | ||
$._simple_name, | ||
), | ||
_jump_statement: $ => choice( | ||
$.break_statement, | ||
$.continue_statement, | ||
$.goto_statement, | ||
$.return_statement, | ||
$.throw_statement | ||
object_creation_expression: $ => prec.right(seq( | ||
'new', | ||
$._type, | ||
optional($.argument_list), | ||
optional($.initializer_expression) | ||
)), | ||
parenthesized_expression: $ => seq('(', $._expression, ')'), | ||
postfix_unary_expression: $ => prec.left(PREC.POSTFIX, choice( | ||
seq($._expression, '++'), | ||
seq($._expression, '--'), | ||
seq($._expression, '!') | ||
)), | ||
prefix_unary_expression: $ => prec.right(PREC.UNARY, choice( | ||
...[ | ||
'!', | ||
'&', | ||
'*', | ||
'+', | ||
'++', | ||
'-', | ||
'--', | ||
'^', | ||
'~' | ||
].map(operator => seq(operator, $._expression)))), | ||
query_expression: $ => seq($.from_clause, $._query_body), | ||
from_clause: $ => seq( | ||
'from', | ||
optional($._type), | ||
$.identifier, | ||
'in', | ||
$._expression | ||
), | ||
try_statement: $ => seq( | ||
'try', | ||
$.statement_block, | ||
repeat($.catch_clause), | ||
optional($.finally_clause), | ||
_query_body: $ => prec.right(seq( | ||
repeat($._query_clause), // grammar.txt is incorrect with '+' | ||
$._select_or_group_clause, | ||
optional($.query_continuation) | ||
)), | ||
_query_clause: $ => choice( | ||
$.from_clause, | ||
$.join_clause, | ||
$.let_clause, | ||
$.order_by_clause, | ||
$.where_clause | ||
), | ||
catch_clause: $ => seq( | ||
'catch', | ||
optional($._exception_specifier), | ||
optional($._exception_filter), | ||
$.statement_block | ||
join_clause: $ => seq( | ||
'join', | ||
optional($._type), | ||
$.identifier, | ||
'in', | ||
$._expression, | ||
'on', | ||
$._expression, | ||
'equals', | ||
$._expression, | ||
optional($.join_into_clause) | ||
), | ||
_exception_specifier: $ => seq('(', $._type, optional($.identifier_name), ')'), | ||
_exception_filter: $ => seq('when', '(', $._expression, ')'), | ||
finally_clause: $ => seq('finally', $.statement_block), | ||
join_into_clause: $ => seq('into', $.identifier), | ||
checked_statement: $ => seq('checked', $.statement_block), | ||
unchecked_statement: $ => seq('unchecked', $.statement_block), | ||
lock_statement: $ => seq('lock', '(', $._expression, ')', $._embedded_statement), | ||
let_clause: $ => seq( | ||
'let', | ||
$.identifier, | ||
'=', | ||
$._expression | ||
), | ||
yield_statement: $ => seq( | ||
'yield', | ||
choice( | ||
seq('return', $._expression), | ||
'break' | ||
), | ||
';' | ||
order_by_clause: $ => seq( | ||
'orderby', | ||
commaSep1($._ordering) | ||
), | ||
if_statement: $ => seq( | ||
'if', | ||
_ordering: $ => seq( | ||
$._expression, | ||
optional(choice('ascending', 'descending')) | ||
), | ||
where_clause: $ => seq('where', $._expression), | ||
_select_or_group_clause: $ => choice( | ||
$.group_clause, | ||
$.select_clause | ||
), | ||
group_clause: $ => prec.left(PREC.SELECT, seq( | ||
'group', | ||
$._expression, | ||
'by', | ||
$._expression | ||
)), | ||
select_clause: $ => prec.left(PREC.SELECT, seq('select', $._expression)), | ||
query_continuation: $ => seq('into', $.identifier, $._query_body), | ||
range_expression: $ => prec.right(seq( | ||
optional($._expression), | ||
'..', | ||
optional($._expression) | ||
)), | ||
ref_expression: $ => prec.right(seq('ref', $._expression)), | ||
ref_type_expression: $ => seq( | ||
'__reftype', | ||
'(', | ||
$.boolean_expression, | ||
')', | ||
$._embedded_statement, | ||
optional( | ||
seq( | ||
'else', | ||
$._embedded_statement, | ||
) | ||
) | ||
$._expression, | ||
')' | ||
), | ||
switch_statement: $ => seq( | ||
'switch', | ||
ref_value_expression: $ => seq( | ||
'__refvalue', | ||
'(', | ||
$._expression, | ||
')', | ||
'{', | ||
repeat($.switch_section), | ||
'}' | ||
',', | ||
$._type, | ||
')' | ||
), | ||
switch_section: $ => seq(repeat1($.switch_label), $._statement_list), | ||
switch_label: $ => choice( | ||
seq('case', $.constant_expression, ':'), | ||
seq('default', ':') | ||
size_of_expression: $ => seq( | ||
'sizeof', | ||
'(', | ||
$._type, | ||
')' | ||
), | ||
while_statement: $ => seq('while', '(', $.boolean_expression, ')', $._embedded_statement), | ||
do_statement: $ => seq('do', $._embedded_statement, 'while', '(', $.boolean_expression, ')', ';'), | ||
stack_alloc_array_creation_expression: $ => seq( | ||
'stackalloc', | ||
$.array_type, | ||
optional($.initializer_expression) | ||
), | ||
break_statement: $ => seq('break', ';'), | ||
continue_statement: $ => seq('continue', ';'), | ||
return_statement: $ => seq('return', optional($._expression), ';'), | ||
throw_statement: $ => seq('throw', optional($._expression), ';'), | ||
switch_expression: $ => seq( | ||
$._expression, | ||
'switch', | ||
'{', | ||
commaSep($.switch_expression_arm), | ||
'}', | ||
), | ||
goto_statement: $ => seq( | ||
'goto', | ||
choice( | ||
alias($.identifier_name, $.label_name), | ||
seq('case', $.constant_expression), | ||
'default' | ||
), | ||
';' | ||
switch_expression_arm: $ => seq( | ||
$._pattern, | ||
optional($.when_clause), | ||
'=>', | ||
$._expression | ||
), | ||
// declaration statements | ||
throw_expression: $ => prec.right(seq('throw', $._expression)), | ||
_declaration_statement: $ => seq( | ||
choice( | ||
$.local_variable_declaration, | ||
$.local_constant_declaration | ||
), | ||
';' | ||
tuple_expression: $ => seq( | ||
'(', | ||
$.argument, | ||
repeat1(seq( | ||
',', | ||
$.argument, | ||
)), | ||
')' | ||
), | ||
local_variable_declaration: $ => seq( | ||
$._local_variable_type, | ||
$._local_variable_declarators | ||
type_of_expression: $ => seq('typeof', '(', $._type, ')'), | ||
// TODO: Expressions need work on precedence and conflicts. | ||
_expression: $ => choice( | ||
$.anonymous_method_expression, | ||
$.anonymous_object_creation_expression, | ||
$.array_creation_expression, | ||
$.assignment_expression, | ||
$.await_expression, | ||
$.base_expression, | ||
$.binary_expression, | ||
$.cast_expression, | ||
$.checked_expression, | ||
$.conditional_access_expression, | ||
$.conditional_expression, | ||
// $.declaration_expression, | ||
$.default_expression, | ||
$.element_access_expression, | ||
$.element_binding_expression, | ||
$.implicit_array_creation_expression, | ||
$.implicit_stack_alloc_array_creation_expression, | ||
$.initializer_expression, | ||
$.interpolated_string_expression, | ||
$.invocation_expression, | ||
$.is_pattern_expression, | ||
$.lambda_expression, | ||
$.make_ref_expression, | ||
$.member_access_expression, | ||
$.member_binding_expression, | ||
$.object_creation_expression, | ||
$.parenthesized_expression, | ||
$.postfix_unary_expression, | ||
$.prefix_unary_expression, | ||
$.query_expression, | ||
$.range_expression, | ||
$.ref_expression, | ||
$.ref_type_expression, | ||
$.ref_value_expression, | ||
$.size_of_expression, | ||
$.stack_alloc_array_creation_expression, | ||
$.switch_expression, | ||
$.this_expression, | ||
$.throw_expression, | ||
$.tuple_expression, | ||
$.type_of_expression, | ||
$._type, | ||
$.identifier, | ||
alias($._reserved_identifier, $.identifier), | ||
// Literals | ||
$.null_literal, | ||
$.boolean_literal, | ||
$.character_literal, | ||
$.real_literal, // Don't combine real and integer literals together | ||
$.integer_literal, | ||
$.string_literal, // Or strings and verbatim strings | ||
$.verbatim_string_literal | ||
), | ||
_local_variable_type: $ => choice( | ||
'var', | ||
$._type | ||
binary_expression: $ => choice( | ||
...[ | ||
['&&', PREC.LOGAND], | ||
['||', PREC.LOGOR], | ||
['>>', PREC.SHIFT], | ||
['<<', PREC.SHIFT], | ||
['&', PREC.AND], | ||
['^', PREC.OR], | ||
['|', PREC.OR], | ||
['+', PREC.ADD], | ||
['-', PREC.ADD], | ||
['*', PREC.MULT], | ||
['/', PREC.MULT], | ||
['%', PREC.MULT], | ||
['<', PREC.REL], | ||
['<=', PREC.REL], | ||
['==', PREC.EQUAL], | ||
['!=', PREC.EQUAL], | ||
['>=', PREC.REL], | ||
['>', PREC.REL], | ||
['??', PREC.EQUAL], | ||
['as', PREC.EQUAL], | ||
].map(([operator, precedence]) => | ||
prec.left(precedence, seq($._expression, operator, $._expression)) | ||
).concat( | ||
prec.left(PREC.EQUAL, seq($._expression, 'is', $._type)) | ||
) | ||
), | ||
_local_variable_declarators: $ => choice( | ||
$.local_variable_declarator, | ||
seq($._local_variable_declarators, ',', $.local_variable_declarator) | ||
// Literals - grammar.txt is useless here as it just refs to lexical specification | ||
boolean_literal: $ => choice( | ||
'true', | ||
'false' | ||
), | ||
local_variable_declarator: $ => seq( | ||
$.identifier_name, | ||
optional(seq('=', $.local_variable_initializer)) | ||
character_literal: $ => seq( | ||
"'", | ||
choice(token.immediate(/[^'\\]/), $.escape_sequence), | ||
"'" | ||
), | ||
local_variable_initializer: $ => choice( | ||
$._expression, | ||
$.array_initalizer | ||
escape_sequence: $ => token(choice( | ||
/\\x[0-9a-fA-F][0-9a-fA-F]?[0-9a-fA-F]?[0-9a-fA-F]?/, | ||
/\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]/, | ||
/\\U[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]/, | ||
/\\[^xuU]/, | ||
)), | ||
integer_literal: $ => token(seq( | ||
choice( | ||
(/[0-9_]+/), // Decimal | ||
(/0[xX][0-9a-fA-F_]+/), // Hex | ||
(/0[bB][01_]+/) // Binary | ||
), | ||
optional(/u|U|l|L|ul|UL|uL|Ul|lu|LU|Lu|lU/) | ||
)), | ||
null_literal: $ => 'null', | ||
real_literal: $ => { | ||
const suffix = /[fFdDmM]/; | ||
const exponent = /[eE][+-]?[0-9_]+/; | ||
return token(choice( | ||
seq( | ||
(/[0-9_]+/), | ||
'.', | ||
(/[0-9_]+/), | ||
optional(exponent), | ||
optional(suffix) | ||
), | ||
seq( | ||
'.', | ||
(/[0-9_]+/), | ||
optional(exponent), | ||
optional(suffix) | ||
), | ||
seq( | ||
(/[0-9_]+/), | ||
exponent, | ||
optional(suffix) | ||
), | ||
seq( | ||
(/[0-9_]+/), | ||
suffix | ||
) | ||
)) | ||
}, | ||
string_literal: $ => seq( | ||
'"', | ||
repeat(choice( | ||
token.immediate(prec(1, /[^"\\\n]+/)), | ||
$.escape_sequence | ||
)), | ||
'"' | ||
), | ||
local_constant_declaration: $ => seq('const', $._type, $._constant_declarators) | ||
verbatim_string_literal: $ => token(seq( | ||
'@"', | ||
repeat(choice( | ||
/[^"]/, | ||
'""', | ||
)), | ||
'"' | ||
)), | ||
// Comments | ||
comment: $ => token(choice( | ||
seq('//', /.*/), | ||
seq( | ||
'/*', | ||
repeat(choice( | ||
/[^*]/, | ||
/\*[^/]/ | ||
)), | ||
'*/' | ||
) | ||
)), | ||
// Custom non-Roslyn additions beyond this point that will not sync up with grammar.txt | ||
_reserved_identifier: $ => choice( | ||
'from' | ||
), | ||
// We use this instead of type so 'void' is only treated as type in the right contexts | ||
return_type: $ => choice($._type, $.void_keyword), | ||
void_keyword: $ => 'void', | ||
// We could line this up with grammar.txt *_trivia at some point. | ||
// Will need to understand how structured_trivia is implemented. | ||
preprocessor_directive: $ => token( | ||
seq( | ||
// TODO: Nothing should come before the # on a line except whitespace | ||
'#', | ||
choice( | ||
'if', | ||
'define', | ||
'endif', | ||
'undef', | ||
'warning', | ||
'error', | ||
'line', | ||
'region', | ||
'endregion', | ||
'pragma warning', | ||
'pragma checksum', | ||
'nullable', | ||
// Individiual code can be broken up by #if #else #elif etc. Parsing all the code ignoring the tokens can cause | ||
// syntax errors. What we do instead is always parse the #if block but then completely ignore the #else and #elif blocks. | ||
// It's not perfect as sometimes only valid combinations of names would compile or the defined names might be re-used. | ||
seq( | ||
choice('else', seq('elif', /.*/)), | ||
repeat(choice(/[^#]/, /#[\s\u00A0]*(else|elif|define|undef|warning|error|line|region|endregion|pragma|nullable)/)), // Consume "disabled" code | ||
/#[\s\u00A0]*endif/ | ||
), | ||
), | ||
/.*/ | ||
) | ||
), | ||
} | ||
}) | ||
function commaSep(rule) { | ||
return optional(commaSep1(rule)) | ||
} | ||
function commaSep1(rule) { | ||
@@ -1051,0 +1395,0 @@ return seq( |
14
index.js
@@ -1,1 +0,13 @@ | ||
module.exports = require("./build/Release/ts_language_c_sharp_binding"); | ||
try { | ||
module.exports = require("./build/Release/tree_sitter_c_sharp_binding"); | ||
} catch (error) { | ||
try { | ||
module.exports = require("./build/Debug/tree_sitter_c_sharp_binding"); | ||
} catch (_) { | ||
throw error | ||
} | ||
} | ||
try { | ||
module.exports.nodeTypeInfo = require("./src/node-types.json"); | ||
} catch (_) {} |
{ | ||
"name": "tree-sitter-c-sharp", | ||
"version": "0.13.0", | ||
"version": "0.15.0", | ||
"description": "C# grammar for tree-sitter", | ||
@@ -16,8 +16,7 @@ "main": "index.js", | ||
"devDependencies": { | ||
"tree-sitter-cli": "^0.13.1" | ||
"tree-sitter-cli": "^0.15.14" | ||
}, | ||
"scripts": { | ||
"build": "tree-sitter generate && node-gyp build", | ||
"test": "tree-sitter test" | ||
} | ||
} |
@@ -10,6 +10,70 @@ tree-sitter-c-sharp | ||
Currently incomplete, based on the C# 6.0 draft proposal. | ||
Based on an export of the Roslyn grammar export with various changes in order to: | ||
- Deal with differences between the parsing technologies | ||
- Work around some bugs in that grammar | ||
- Handle `#if`, `#else`, `#elif`, `#endif` blocks | ||
- Support syntax highlighting/parsing of fragments | ||
- Simplify the output tree | ||
### Detailed status | ||
Comprehensive support for C# exists with the following exceptions: | ||
- [ ] [Non-ASCII identifiers](#37) | ||
- [ ] [Contextual keywords](#47) | ||
- [ ] [Global statements](#12) | ||
#### C# 7.0 | ||
- [ ] [Tuples](#15) | ||
- [x] Discards | ||
- [x] Basic pattern matching | ||
- [ ] [Recursive pattern matching](#19) | ||
- [ ] [Ref locals and return](#14) | ||
- [x] Local function statements | ||
- [x] Expression bodied constructors | ||
- [x] Expression bodied destructors/finalizers | ||
- [x] Throw expressions | ||
- [x] Binary and `_` literal support | ||
#### C# 7.1 | ||
- [x] `async` main method | ||
- [x] Default literals (as `default_expression`) | ||
- [ ] Inferred tuple element names | ||
- [ ] Generic type pattern matching | ||
#### C# 7.2 | ||
- [x] `in` parameter modifiers | ||
- [x] `ref readonly` method returns | ||
- [x] `readonly struct` | ||
- [x] `ref struct` | ||
- [x] Non-trailing named arguments | ||
- [x] `_` leading binary and hex literals | ||
- [x] `private protected` modifier | ||
- [ ] Conditional `ref` expressions | ||
#### C# 7.3 | ||
- [x] `stackalloc` array initializers | ||
- [x] `unmanaged` generic type parameter constraint | ||
- [x] Attributes attached to auto property backing fields | ||
- [x] `out` support for parameters on initializers, constructors etc. | ||
#### C# 8.0 | ||
- [x] `readonly` members | ||
- [x] Default interface methods | ||
- [x] `switch` expressions | ||
- [ ] `switch` property patterns | ||
- [ ] `switch` tuple patterns | ||
- [x] `static` local functions | ||
- [x] nullable reference types | ||
- [x] null-forgiving operator | ||
### References | ||
* [Official C# Language Spec](https://github.com/dotnet/csharplang/blob/master/spec/) It provides chapters that formally define the language grammar. | ||
* [Official C# 6 Language Spec](https://github.com/dotnet/csharplang/blob/master/spec/) provides chapters that formally define the language grammar. | ||
* [Roslyn C# language grammar export](https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
5737953
17
27330
78
2