From 603224433a130ecc28cf426e4ad9151ba4d22183 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 17 Dec 2015 15:21:51 -0800 Subject: [PATCH] Use new javascript grammar API --- grammar.coffee | 496 ---------------------------------------- grammar.js | 609 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 609 insertions(+), 496 deletions(-) delete mode 100644 grammar.coffee create mode 100644 grammar.js diff --git a/grammar.coffee b/grammar.coffee deleted file mode 100644 index a384551..0000000 --- a/grammar.coffee +++ /dev/null @@ -1,496 +0,0 @@ -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, - @compound_statement) - - declaration: -> seq( - err(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(choice(@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(/[^\*]/, /\*[^/]/)), "*/"))) diff --git a/grammar.js b/grammar.js new file mode 100644 index 0000000..a758dfc --- /dev/null +++ b/grammar.js @@ -0,0 +1,609 @@ +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, + CALL: 12, + CAST: 13, + UNARY: 14, + FIELD: 15, + SUBSCRIPT: 16, +}; + +module.exports = grammar({ + name: 'c', + + extras: $ => [ + /\s/, + $.comment, + ], + + conflicts: $ => [ + [$._type_specifier, $._expression], + [$._type_specifier, $.direct_declarator], + [$.sizeof_expression, $.cast_expression], + ], + + rules: { + translation_unit: $ => repeat(choice( + $._preproc_statement, + $.function_definition, + $.declaration + )), + + // 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*/), + + // Main Grammar + + function_definition: $ => seq( + optional($._declaration_specifiers), + $._type_specifier, + $._declarator, + $.compound_statement + ), + + declaration: $ => seq( + err(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(choice( + $.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(/[^\*]/, /\*[^/]/)), + '*/' + ) + )), + } +}); + +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)) +}