commaSep = (rule) -> optional(commaSep1(rule)) commaSep1 = (rule) -> seq(rule, repeat(seq(",", rule))) # Avoid conflict between optional trailing comma and the comma separator. # TODO: maybe abstract this in tree-sitter core somehow commaSepTrailing = (recurSymbol, rule) -> choice(rule, seq(recurSymbol, ',', rule)) 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 call: 12 cast: 13 unary: 14 field: 15 subscript: 16 module.exports = grammar name: 'c' ubiquitous: -> [@comment, /\s/] expectedConflicts: -> [ [@_type_specifier, @_expression] [@_type_specifier, @direct_declarator] [@sizeof_expression, @cast_expression] ] rules: translation_unit: -> repeat(choice( @_preproc_statement, @function_definition, @declaration)) # Section - Preprocesser _preproc_statement: -> choice( @preproc_ifdef, @preproc_include, @preproc_def, @preproc_function_def, @preproc_call) preproc_include: -> seq( "#include", choice(@string_literal, @system_lib_string)) preproc_def: -> seq( "#define", @identifier, optional(@preproc_arg), '\n') preproc_function_def: -> seq( "#define", @identifier, @preproc_params, optional(@preproc_arg), '\n') preproc_params: -> seq( '(', commaSep(choice(@identifier, '...')), ')') preproc_call: -> seq( @preproc_directive, @preproc_arg) preproc_arg: -> token(prec(-1, repeat1(choice(/./, "\\\n")))) preproc_ifdef: -> seq( choice("#ifdef", "#ifndef"), @identifier, err(repeat(choice( @_preproc_statement, @function_definition, @declaration))), optional(@preproc_else), "#endif") preproc_else: -> seq( "#else", repeat(choice( @_preproc_statement, @function_definition, @declaration))) preproc_directive: -> /#\a\w*/ # Section - Main Grammar function_definition: -> seq( optional(@_declaration_specifiers), @_type_specifier, @_declarator, optional(repeat(@declaration)), @compound_statement) declaration: -> seq( optional(@_declaration_specifiers), @_type_specifier, commaSep(choice( @_declarator @init_declarator)), ";") _declaration_specifiers: -> repeat1(choice( @storage_class_specifier, @type_qualifier, @function_specifier)) _declarator: -> choice( @pointer_declarator, @function_declarator, @array_declarator, seq('(', @_declarator, ')'), @identifier, ) _abstract_declarator: -> choice( @abstract_pointer_declarator, @abstract_function_declarator, @abstract_array_declarator, prec(1, seq('(', @_abstract_declarator, ')')), ) pointer_declarator: -> seq('*', @_declarator) abstract_pointer_declarator: -> seq('*', optional(@_abstract_declarator)) function_declarator: -> prec(1, seq( @_declarator, '(', optional(@parameter_type_list), ')')) abstract_function_declarator: -> prec(1, seq( @_abstract_declarator, '(', optional(@parameter_type_list), ')')) array_declarator: -> prec(1, seq( @_declarator, '[', choice( seq(optional("static"), repeat(@type_qualifier), optional(@_expression)), seq(repeat(@type_qualifier), "static", @_expression)) ']')) abstract_array_declarator: -> prec(1, seq( optional(@_abstract_declarator), '[', choice( seq(optional("static"), repeat(@type_qualifier), optional(@_expression)), seq(repeat(@type_qualifier), "static", @_expression)) ']')) init_declarator: -> seq( @_declarator, "=", choice(@initializer_list, @_expression)) compound_statement: -> seq( "{", err(repeat(choice(@declaration, @_statement))), "}") storage_class_specifier: -> choice( "typedef", "extern", "static" , "auto", "register") type_qualifier: -> choice( "const", "restrict", "volatile") function_specifier: -> "inline" _type_specifier: -> choice( @struct_specifier, @union_specifier, @enum_specifier, @sized_type_specifier, @identifier) sized_type_specifier: -> seq( repeat1(choice( "unsigned", "long", "short")), @identifier) enum_specifier: -> seq( "enum", choice( @identifier, seq( optional(@identifier), "{", @_enum_specifier_contents, optional(',') "}"))) _enum_specifier_contents: -> commaSepTrailing( @_enum_specifier_contents, @enumerator) struct_specifier: -> seq( "struct", choice( @identifier, seq( optional(@identifier), "{", repeat(@struct_declaration), "}"))) union_specifier: -> seq( "union", choice( @identifier, seq( optional(@identifier), "{", repeat(@struct_declaration), "}"))) struct_declaration: -> seq( repeat(@type_qualifier), @_type_specifier, commaSep(@_declarator), optional(seq(':', @_expression)) ";") enumerator: -> choice( @identifier, seq(@identifier, "=", @_expression)) parameter_type_list: -> commaSep1(@parameter_declaration) parameter_declaration: -> seq( optional(@_declaration_specifiers), @_type_specifier, 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( @identifier, ":", @_statement) expression_statement: -> seq( optional(@_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"), ":", @_statement) 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", @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, @char_literal, seq("(", err(@_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_name, ")", @_expression)) sizeof_expression: -> choice( prec(PREC.sizeof, seq("sizeof", @_expression)), prec(PREC.sizeof, seq("sizeof", "(", @type_name, ")"))) subscript_expression: -> prec(PREC.subscript, seq(@_expression, "[", @_expression, "]")) call_expression: -> prec(PREC.call, seq(@_expression, "(", commaSep(@_expression), ")")) field_expression: -> choice( prec.left(PREC.field, seq(@_expression, ".", @_expression)), prec.left(PREC.field, seq(@_expression, "->", @_expression))) compound_literal_expression: -> seq( prec(PREC.cast, seq("(", @type_name, ")")), @initializer_list) type_name: -> seq( repeat(@type_qualifier), @_type_specifier, optional(@_abstract_declarator)) initializer_list: -> seq( "{", @_initializer_list_contents, optional(",") "}") _initializer_list_contents: -> commaSepTrailing( @_initializer_list_contents, seq( optional(seq(repeat1(@designator), "=")), choice(@_expression, @initializer_list))) designator: -> choice( seq("[", @_expression, "]"), seq(".", @identifier)) initializer: -> choice( @_expression, @initializer_list) number_literal: -> token(choice( /\d+(\.\d+)?/, /\d+u/, seq("0x", /[0-9a-fA-f]+/), seq("0b", /[01]+/), )) char_literal: -> token(seq( "'", choice( seq(optional("\\"), /[^']/), seq("\\", "'")), "'")) string_literal: -> token(seq( '"', repeat(choice(/[^"]/, '\\"')), '"')) system_lib_string: -> token(seq( '<', repeat(choice(/[^>]/, '\\>')), '>')) identifier: -> /[\a_][\a\d_]*/ comment: -> token(choice( seq("//", /.*/), seq("/*", repeat(choice(/[^\*]/, /\*[^/]/)), "*/")))