You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

805 lines
18 KiB

const PREC = {
ASSIGNMENT: -1,
CONDITIONAL: -2,
DEFAULT: 0,
LOGICAL_OR: 1,
LOGICAL_AND: 2,
INCLUSIVE_OR: 3,
EXCLUSIVE_OR: 4,
BITWISE_AND: 5,
EQUAL: 6,
RELATIONAL: 7,
SIZEOF: 8,
SHIFT: 9,
ADD: 10,
MULTIPLY: 11,
CAST: 12,
UNARY: 13,
CALL: 14,
FIELD: 15,
SUBSCRIPT: 16
};
module.exports = grammar({
name: 'c',
extras: $ => [
/\s/,
$.comment,
],
inline: $ => [
$._statement,
$._top_level_item,
$._compound_statement_item,
$._type_identifier,
$._field_identifier,
$._statement_identifier,
],
conflicts: $ => [
[$._type_specifier, $._declarator],
[$._type_specifier, $._declarator, $.macro_type_specifier],
[$._type_specifier, $._expression],
[$._type_specifier, $._expression, $.macro_type_specifier],
[$._type_specifier, $.macro_type_specifier],
[$.sized_type_specifier],
],
rules: {
translation_unit: $ => repeat($._top_level_item),
_top_level_item: $ => choice(
$.function_definition,
$.linkage_specification,
$.declaration,
$.type_definition,
$._empty_declaration,
$.preproc_if,
$.preproc_ifdef,
$.preproc_include,
$.preproc_def,
$.preproc_function_def,
$.preproc_call
),
_compound_statement_item: $ => choice(
$._statement,
$.declaration,
$.type_definition,
$._empty_declaration,
alias($.preproc_if_in_compound_statement, $.preproc_if),
alias($.preproc_ifdef_in_compound_statement, $.preproc_ifdef),
$.preproc_include,
$.preproc_def,
$.preproc_function_def,
$.preproc_call
),
// Preprocesser
preproc_include: $ => seq(
preprocessor('include'),
choice($.string_literal, $.system_lib_string)
),
preproc_def: $ => seq(
preprocessor('define'),
$.identifier,
optional(seq(/[ \t]+/, $.preproc_arg)),
'\n'
),
preproc_function_def: $ => seq(
preprocessor('define'),
$.identifier,
$.preproc_params,
optional($.preproc_arg),
'\n'
),
preproc_params: $ => seq(
'(', commaSep(choice($.identifier, '...')), ')'
),
preproc_call: $ => seq(
$.preproc_directive,
optional(seq(/[ \t]+/, $.preproc_arg)),
'\n'
),
preproc_if: $ => seq(
preprocessor('if'),
$.preproc_arg,
repeat($._top_level_item),
optional(choice($.preproc_else, $.preproc_elif)),
preprocessor('endif')
),
preproc_if_in_compound_statement: $ => seq(
preprocessor('if'),
$.preproc_arg,
repeat($._compound_statement_item),
optional(choice(
alias($.preproc_else_in_compound_statement, $.preproc_else),
alias($.preproc_elif_in_compound_statement, $.preproc_elif)
)),
preprocessor('endif')
),
preproc_ifdef: $ => seq(
choice(preprocessor('ifdef'), preprocessor('ifndef')),
$.identifier,
repeat($._top_level_item),
optional(choice($.preproc_else, $.preproc_elif)),
preprocessor('endif')
),
preproc_ifdef_in_compound_statement: $ => seq(
choice(preprocessor('ifdef'), preprocessor('ifndef')),
$.identifier,
repeat($._compound_statement_item),
optional(choice(
alias($.preproc_else_in_compound_statement, $.preproc_else),
alias($.preproc_elif_in_compound_statement, $.preproc_elif)
)),
preprocessor('endif')
),
preproc_elif: $ => seq(
preprocessor('elif'),
$.preproc_arg,
repeat($._top_level_item),
optional(choice($.preproc_elif, $.preproc_else))
),
preproc_elif_in_compound_statement: $ => seq(
preprocessor('elif'),
$.preproc_arg,
repeat($._compound_statement_item),
optional(choice(
alias($.preproc_elif_in_compound_statement, $.preproc_elif),
alias($.preproc_else_in_compound_statement, $.preproc_else)
))
),
preproc_else: $ => seq(
preprocessor('else'),
repeat($._top_level_item)
),
preproc_else_in_compound_statement: $ => seq(
preprocessor('else'),
repeat($._compound_statement_item)
),
preproc_directive: $ => /#[ \t]*[a-zA-Z]\w*/,
preproc_arg: $ => token(prec(-1, repeat1(choice(/./, '\\\n')))),
// Main Grammar
function_definition: $ => seq(
$._declaration_specifiers,
$._declarator,
$.compound_statement
),
declaration: $ => seq(
$._declaration_specifiers,
commaSep1(choice(
$._declarator,
$.init_declarator
)),
';'
),
type_definition: $ => seq(
'typedef',
repeat($.type_qualifier),
$._type_specifier,
$._type_declarator,
';'
),
_declaration_specifiers: $ => seq(
repeat(choice(
$.storage_class_specifier,
$.type_qualifier
)),
$._type_specifier,
repeat(choice(
$.storage_class_specifier,
$.type_qualifier
))
),
linkage_specification: $ => seq(
'extern',
$.string_literal,
choice(
$.function_definition,
$.declaration,
$.declaration_list
)
),
declaration_list: $ => seq(
'{',
repeat($._top_level_item),
'}'
),
_declarator: $ => choice(
$.pointer_declarator,
$.function_declarator,
$.array_declarator,
seq('(', $._declarator, ')'),
$.identifier
),
_field_declarator: $ => choice(
alias($.pointer_field_declarator, $.pointer_declarator),
alias($.function_field_declarator, $.function_declarator),
alias($.array_field_declarator, $.array_declarator),
seq('(', $._field_declarator, ')'),
$._field_identifier
),
_type_declarator: $ => choice(
alias($.pointer_type_declarator, $.pointer_declarator),
alias($.function_type_declarator, $.function_declarator),
alias($.array_type_declarator, $.array_declarator),
seq('(', $._type_declarator, ')'),
$._type_identifier
),
_abstract_declarator: $ => choice(
$.abstract_pointer_declarator,
$.abstract_function_declarator,
$.abstract_array_declarator,
prec(1, seq('(', $._abstract_declarator, ')'))
),
pointer_declarator: $ => prec.right(seq('*', $._declarator)),
pointer_field_declarator: $ => prec.right(seq('*', $._field_declarator)),
pointer_type_declarator: $ => prec.right(seq('*', $._type_declarator)),
abstract_pointer_declarator: $ => prec.right(seq('*', optional($._abstract_declarator))),
function_declarator: $ => prec(1, seq($._declarator, $.parameter_list)),
function_field_declarator: $ => prec(1, seq($._field_declarator, $.parameter_list)),
function_type_declarator: $ => prec(1, seq($._type_declarator, $.parameter_list)),
abstract_function_declarator: $ => prec(1, seq(optional($._abstract_declarator), $.parameter_list)),
array_declarator: $ => prec(1, seq(
$._declarator,
'[',
optional($._declaration_specifiers),
optional($._expression),
']'
)),
array_field_declarator: $ => prec(1, seq(
$._field_declarator,
'[',
optional($._declaration_specifiers),
optional($._expression),
']'
)),
array_type_declarator: $ => prec(1, seq(
$._type_declarator,
'[',
optional($._declaration_specifiers),
optional($._expression),
']'
)),
abstract_array_declarator: $ => prec(1, seq(
optional($._abstract_declarator),
'[',
optional($._declaration_specifiers),
optional($._expression),
']'
)),
init_declarator: $ => seq(
$._declarator,
'=',
choice($.initializer_list, $._expression)
),
compound_statement: $ => seq(
'{',
repeat($._compound_statement_item),
'}'
),
storage_class_specifier: $ => choice(
'extern',
'static' ,
'auto',
'register',
'inline'
),
type_qualifier: $ => choice(
'const',
'restrict',
'volatile',
'restrict',
'_Atomic'
),
_type_specifier: $ => choice(
$.struct_specifier,
$.union_specifier,
$.enum_specifier,
$.macro_type_specifier,
$.sized_type_specifier,
$.primitive_type,
$._type_identifier
),
sized_type_specifier: $ => seq(
repeat1(choice(
'unsigned',
'long',
'short'
)),
optional(choice(
prec.dynamic(-1, $._type_identifier),
$.primitive_type
))
),
primitive_type: $ => token(choice(
'bool',
'char',
'int',
'float',
'double',
'void',
'size_t',
'ssize_t',
'intptr_t',
'uintptr_t',
'charptr_t',
...[8, 16, 32, 64].map(n => `int${n}_t`),
...[8, 16, 32, 64].map(n => `uint${n}_t`),
...[8, 16, 32, 64].map(n => `char${n}_t`)
)),
enum_specifier: $ => seq(
'enum',
choice(
seq(
$._type_identifier,
optional($.enumerator_list)
),
$.enumerator_list
)
),
enumerator_list: $ => seq(
'{',
commaSep($.enumerator),
optional(','),
'}'
),
struct_specifier: $ => seq(
'struct',
choice(
seq(
$._type_identifier,
optional($.field_declaration_list)
),
$.field_declaration_list
)
),
union_specifier: $ => seq(
'union',
choice(
seq(
$._type_identifier,
optional($.field_declaration_list)
),
$.field_declaration_list
)
),
field_declaration_list: $ => seq(
'{',
repeat($.field_declaration),
'}'
),
field_declaration: $ => seq(
$._declaration_specifiers,
commaSep($._field_declarator),
optional(seq(':', $._expression)),
';'
),
enumerator: $ => seq(
$.identifier,
optional(seq('=', $._expression))
),
parameter_list: $ => prec.dynamic(1, seq(
'(',
commaSep(choice($.parameter_declaration, '...')),
')'
)),
parameter_declaration: $ => seq(
$._declaration_specifiers,
optional(choice($._declarator, $._abstract_declarator))
),
// Statements
_statement: $ => choice(
$.labeled_statement,
$.compound_statement,
$.expression_statement,
$.if_statement,
$.switch_statement,
$.case_statement,
$.do_statement,
$.while_statement,
$.for_statement,
$.return_statement,
$.break_statement,
$.continue_statement,
$.goto_statement
),
labeled_statement: $ => seq(
$._statement_identifier,
':',
$._statement
),
expression_statement: $ => seq(
optional(choice(
$._expression,
$.comma_expression
)),
';'
),
if_statement: $ => prec.right(seq(
'if',
'(', $._expression, ')',
$._statement,
optional(seq(
'else',
$._statement
))
)),
switch_statement: $ => seq(
'switch',
'(', $._expression, ')',
$._statement
),
case_statement: $ => seq(
choice(
seq('case', $._expression),
'default'
),
':',
choice(
$._statement,
$.declaration,
$.type_definition
)
),
while_statement: $ => seq(
'while',
'(', $._expression, ')',
$._statement
),
do_statement: $ => seq(
'do',
$._statement,
'while',
'(', $._expression, ')'
),
for_statement: $ => seq(
'for',
'(',
choice(
$.declaration,
seq(optional($._expression), ';')
),
optional($._expression), ';',
commaSep($._expression),
')',
$._statement
),
return_statement: $ => seq(
'return',
optional($._expression),
';'
),
break_statement: $ => seq(
'break', ';'
),
continue_statement: $ => seq(
'continue', ';'
),
goto_statement: $ => seq(
'goto',
$._statement_identifier,
';'
),
// Expressions
_expression: $ => choice(
$.conditional_expression,
$.assignment_expression,
$.logical_expression,
$.bitwise_expression,
$.equality_expression,
$.relational_expression,
$.shift_expression,
$.math_expression,
$.cast_expression,
$.pointer_expression,
$.sizeof_expression,
$.subscript_expression,
$.call_expression,
$.field_expression,
$.compound_literal_expression,
$.identifier,
$.number_literal,
$.string_literal,
$.true,
$.false,
$.null,
$.concatenated_string,
$.char_literal,
$.parenthesized_expression
),
comma_expression: $ => seq(
$._expression,
',',
choice($._expression, $.comma_expression)
),
conditional_expression: $ => prec.right(PREC.CONDITIONAL, seq(
$._expression,
'?',
$._expression,
':',
$._expression
)),
assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
$._expression,
choice(
'=',
'*=',
'/=',
'%=',
'+=',
'-=',
'<<=',
'>>=',
'&=',
'^=',
'|='
),
$._expression
)),
pointer_expression: $ => choice(
prec.left(PREC.UNARY, seq('*', $._expression)),
prec.left(PREC.UNARY, seq('&', $._expression))
),
logical_expression: $ => choice(
prec.left(PREC.LOGICAL_OR, seq($._expression, '||', $._expression)),
prec.left(PREC.LOGICAL_AND, seq($._expression, '&&', $._expression)),
prec.left(PREC.UNARY, seq('!', $._expression))
),
bitwise_expression: $ => choice(
prec.left(PREC.INCLUSIVE_OR, seq($._expression, '|', $._expression)),
prec.left(PREC.EXCLUSIVE_OR, seq($._expression, '^', $._expression)),
prec.left(PREC.BITWISE_AND, seq($._expression, '&', $._expression)),
prec.left(PREC.UNARY, seq('~', $._expression))
),
equality_expression: $ => prec.left(PREC.EQUAL, seq(
$._expression, choice('==', '!='), $._expression
)),
relational_expression: $ => prec.left(PREC.RELATIONAL, seq(
$._expression, choice('<', '>', '<=', '>='), $._expression
)),
shift_expression: $ => prec.left(PREC.SHIFT, seq(
$._expression, choice('<<', '>>'), $._expression)
),
math_expression: $ => choice(
prec.left(PREC.ADD, seq($._expression, '+', $._expression)),
prec.left(PREC.ADD, seq($._expression, '-', $._expression)),
prec.left(PREC.MULTIPLY, seq($._expression, '*', $._expression)),
prec.left(PREC.MULTIPLY, seq($._expression, '/', $._expression)),
prec.left(PREC.MULTIPLY, seq($._expression, '%', $._expression)),
prec.right(PREC.UNARY, seq('-', $._expression)),
prec.right(PREC.UNARY, seq('+', $._expression)),
prec.right(PREC.UNARY, seq(choice('--', '++'), $._expression)),
prec.right(PREC.UNARY, seq($._expression, choice('++', '--')))
),
cast_expression: $ => prec(PREC.CAST, seq(
'(',
$.type_descriptor,
')',
$._expression
)),
type_descriptor: $ => seq(
repeat($.type_qualifier),
$._type_specifier,
optional($._abstract_declarator)
),
sizeof_expression: $ => prec(PREC.SIZEOF, seq(
'sizeof',
choice(
$._expression,
seq('(', $.type_descriptor, ')')
)
)),
subscript_expression: $ => prec(PREC.SUBSCRIPT, seq(
$._expression, '[', $._expression, ']')
),
call_expression: $ => prec(PREC.CALL, seq($._expression, $.argument_list)),
argument_list: $ => prec.dynamic(1, seq('(', commaSep($._expression), ')')),
field_expression: $ => seq(
prec(PREC.FIELD, seq(
$._expression,
choice('.', '->')
)),
$._field_identifier
),
compound_literal_expression: $ => seq(
'(',
$.type_descriptor,
')',
$.initializer_list
),
parenthesized_expression: $ => seq(
'(',
choice($._expression, $.comma_expression),
')'
),
initializer_list: $ => seq(
'{',
commaSep(choice(
$.initializer_pair,
$._expression,
$.initializer_list
)),
optional(','),
'}'
),
initializer_pair: $ => seq(
repeat1(choice($.subscript_designator, $.field_designator)),
'=',
choice($._expression, $.initializer_list)
),
subscript_designator: $ => seq('[', $._expression, ']'),
field_designator: $ => seq('.', $._field_identifier),
number_literal: $ => token(seq(
choice(
/\d+(\.\d+)?/,
/\d+u/,
seq('0x', /[0-9a-fA-f]+/),
seq('0b', /[01]+/)
),
repeat(choice('u', 'l', 'U', 'L'))
)),
char_literal: $ => token(seq(
'\'',
choice(
seq(optional('\\'), /[^']/),
seq('\\', '\'')
),
'\''
)),
concatenated_string: $ => seq(
$.string_literal,
repeat1($.string_literal)
),
string_literal: $ => token(seq(
'"',
repeat(choice(/[^\\"\n]/, /\\./)),
'"')
),
system_lib_string: $ => token(seq(
'<',
repeat(choice(/[^>\n]/, '\\>')),
'>'
)),
true: $ => token(choice('TRUE', 'true')),
false: $ => token(choice('FALSE', 'false')),
null: $ => 'NULL',
identifier: $ => /[a-zA-Z_]\w*/,
_type_identifier: $ => alias($.identifier, $.type_identifier),
_field_identifier: $ => alias($.identifier, $.field_identifier),
_statement_identifier: $ => alias($.identifier, $.statement_identifier),
_empty_declaration: $ => seq(
$._declaration_specifiers,
';'
),
macro_type_specifier: $ => seq(
$.identifier,
'(',
$.type_descriptor,
')'
),
// http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890
comment: $ => token(choice(
seq('//', /.*/),
seq(
'/*',
/[^*]*\*+([^/*][^*]*\*+)*/,
'/'
)
)),
}
});
module.exports.PREC = PREC
function preprocessor (command) {
return alias(new RegExp('#[ \t]*' + command), '#' + command)
}
function commaSep (rule) {
return optional(commaSep1(rule))
}
function commaSep1 (rule) {
return seq(rule, repeat(seq(',', rule)))
}
function commaSepTrailing (recurSymbol, rule) {
return choice(rule, seq(recurSymbol, ',', rule))
}