module.exports =
|
|
({choice, err, repeat, seq, optional, token, prec}) ->
|
|
|
|
repeat1 = (rule) ->
|
|
seq(rule, repeat(rule))
|
|
|
|
commaSep = (rule) ->
|
|
optional(commaSep1(rule))
|
|
|
|
commaSep1 = (rule) ->
|
|
seq(rule, repeat(prec.left(0, seq(",", 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
|
|
|
|
{
|
|
name: 'c'
|
|
|
|
ubiquitous: -> [@comment, /\s/]
|
|
|
|
rules:
|
|
translation_unit: -> repeat(choice(
|
|
@preproc_statement,
|
|
@function_definition,
|
|
@declaration))
|
|
|
|
# Section - Preprocesser
|
|
|
|
preproc_statement: -> choice(
|
|
@preproc_ifdef,
|
|
@preproc_ifndef,
|
|
@preproc_endif,
|
|
@preproc_else,
|
|
@preproc_include,
|
|
@preproc_define,
|
|
@preproc_call)
|
|
|
|
preproc_include: -> seq(
|
|
"#include",
|
|
choice(@string_literal, @system_lib_string))
|
|
|
|
preproc_define: -> seq(
|
|
"#define",
|
|
@identifier,
|
|
token(seq(/.+/, repeat(seq("\\\n", /.+/)))))
|
|
|
|
preproc_call: -> choice(
|
|
seq(@preproc_directive, /.*/))
|
|
|
|
preproc_ifdef: -> seq("#ifdef", @identifier)
|
|
preproc_ifndef: -> seq("#ifndef", @identifier)
|
|
preproc_else: -> "#else"
|
|
preproc_endif: -> "#endif"
|
|
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(@init_declarator),
|
|
";")
|
|
|
|
declaration_specifiers: -> repeat1(choice(
|
|
@storage_class_specifier,
|
|
@type_qualifier,
|
|
@function_specifier))
|
|
|
|
declarator: ->
|
|
seq(repeat(@pointer), @direct_declarator)
|
|
|
|
abstract_declarator: -> choice(
|
|
@pointer,
|
|
seq(repeat(@pointer), @direct_abstract_declarator))
|
|
|
|
init_declarator: -> choice(
|
|
@declarator,
|
|
seq(@declarator, "=", @initializer))
|
|
|
|
compound_statement: -> seq(
|
|
"{",
|
|
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,
|
|
seq(
|
|
repeat(choice(
|
|
"unsigned",
|
|
"long",
|
|
"short")),
|
|
prec(20, @identifier)))
|
|
|
|
enum_specifier: -> seq(
|
|
"enum",
|
|
choice(
|
|
@identifier,
|
|
seq(
|
|
optional(@identifier),
|
|
"{", commaSep(@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(@struct_declarator),
|
|
";")
|
|
|
|
enumerator: -> choice(
|
|
@identifier,
|
|
seq(@identifier, "=", @expression))
|
|
|
|
struct_declarator: -> choice(
|
|
@declarator,
|
|
seq(optional(@declarator), ":", @expression))
|
|
|
|
pointer: -> seq(
|
|
"*",
|
|
repeat(@type_qualifier))
|
|
|
|
direct_declarator: -> choice(
|
|
@identifier,
|
|
|
|
# TODO - is this needed? Currently, it creates ambiguity w/
|
|
# call expressions.
|
|
#
|
|
# seq("(", @declarator, ")"),
|
|
|
|
seq(
|
|
@direct_declarator,
|
|
choice(
|
|
seq("[", repeat(@type_qualifier), optional(@expression), "]"),
|
|
seq("[", "static", repeat(@type_qualifier), @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), "static", @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), "*", "]"),
|
|
seq("(", optional(@parameter_type_list), ")"),
|
|
seq("(", commaSep(@identifier), ")"))))
|
|
|
|
direct_abstract_declarator: -> choice(
|
|
|
|
# TODO - is this needed? Currently, it creates ambiguity.
|
|
#
|
|
# seq("(", @abstract_declarator, ")"),
|
|
|
|
seq(
|
|
@direct_abstract_declarator,
|
|
choice(
|
|
seq("[", repeat(@type_qualifier), optional(@expression), "]"),
|
|
seq("[", "static", repeat(@type_qualifier), @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), "static", @expression, "]"),
|
|
seq("[", "*", "]"),
|
|
seq("(", @parameter_type_list, ")"))))
|
|
|
|
parameter_type_list: -> seq(
|
|
commaSep1(@parameter_declaration),
|
|
optional(seq(",", "...")))
|
|
|
|
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: -> seq(
|
|
"if",
|
|
"(", @expression, ")",
|
|
prec.left(0, @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), ";"
|
|
optional(@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,
|
|
prec(PREC.cast, seq("(", @expression, ")")))
|
|
|
|
conditional_expression: -> prec(PREC.conditional, seq(
|
|
@expression,
|
|
"?",
|
|
@expression,
|
|
":",
|
|
@expression))
|
|
|
|
assignment_expression: ->
|
|
prec.right(PREC.assignment, seq(
|
|
@expression,
|
|
choice(
|
|
"=",
|
|
"*=",
|
|
"/=",
|
|
"%=",
|
|
"+=",
|
|
"-=",
|
|
"<<=",
|
|
">>=",
|
|
"&=",
|
|
"^=",
|
|
"|="),
|
|
@expression))
|
|
|
|
pointer_expression: -> choice(
|
|
prec(PREC.unary, seq("*", @expression)),
|
|
prec(PREC.unary, seq("&", @expression)))
|
|
|
|
logical_expression: -> choice(
|
|
prec(PREC.logical_or, seq(@expression, "||", @expression)),
|
|
prec(PREC.logical_and, seq(@expression, "&&", @expression)),
|
|
prec(PREC.unary, seq("!", @expression)))
|
|
|
|
bitwise_expression: -> choice(
|
|
prec(PREC.inclusive_or, seq(@expression, "|", @expression)),
|
|
prec(PREC.exclusive_or, seq(@expression, "^", @expression)),
|
|
prec(PREC.bitwise_and, seq(@expression, "&", @expression)),
|
|
prec(PREC.unary, seq("~", @expression)))
|
|
|
|
equality_expression: ->
|
|
prec(PREC.equal, seq(@expression, choice("==", "!="), @expression))
|
|
|
|
relational_expression: ->
|
|
prec(PREC.relational, seq(@expression, choice("<", ">", "<=", ">="), @expression))
|
|
|
|
shift_expression: ->
|
|
prec(PREC.shift, seq(@expression, choice("<<", ">>"), @expression))
|
|
|
|
math_expression: -> choice(
|
|
prec(PREC.add, seq(@expression, "+", @expression)),
|
|
prec(PREC.add, seq(@expression, "-", @expression)),
|
|
prec(PREC.multiply, seq(@expression, "*", @expression)),
|
|
prec(PREC.multiply, seq(@expression, "/", @expression)),
|
|
prec(PREC.multiply, seq(@expression, "%", @expression)),
|
|
prec(PREC.unary, seq("-", @expression)),
|
|
prec(PREC.unary, seq("+", @expression)),
|
|
prec(PREC.unary, seq(choice("--", "++"), @expression)),
|
|
prec(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(PREC.field, seq(@expression, ".", @expression)),
|
|
prec(PREC.field, seq(@expression, "->", @expression)))
|
|
|
|
compound_literal_expression: -> seq(
|
|
prec(PREC.cast, seq("(", @type_name, ")")),
|
|
"{",
|
|
@_initializer_list,
|
|
optional(",")
|
|
"}")
|
|
|
|
type_name: -> seq(
|
|
repeat(@type_qualifier),
|
|
@type_specifier,
|
|
optional(@abstract_declarator))
|
|
|
|
_initializer_list: -> choice(
|
|
seq(
|
|
optional(seq(repeat1(@designator), "=")),
|
|
@initializer),
|
|
seq(
|
|
@_initializer_list,
|
|
","
|
|
seq(
|
|
optional(seq(repeat1(@designator), "=")),
|
|
@initializer)))
|
|
|
|
designator: -> choice(
|
|
seq("[", @expression, "]"),
|
|
seq(".", @identifier))
|
|
|
|
initializer: -> choice(
|
|
@expression,
|
|
seq(
|
|
"{"
|
|
@_initializer_list
|
|
optional(",")
|
|
"}"))
|
|
|
|
number_literal: -> /\d+(\.\d+)?/
|
|
|
|
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(/[^\*]/, /\*[^/]/)), "*/")))
|
|
}
|