module.exports =
|
|
({choice, err, repeat, seq, keyword, optional, token, prec, precedenceValues}) ->
|
|
|
|
precedenceValues = (names) ->
|
|
result = {}
|
|
startValue = -1 * names.indexOf('default')
|
|
for name in names
|
|
result[name] = startValue++
|
|
result
|
|
|
|
commaSep = (rule) ->
|
|
optional(commaSep1(rule))
|
|
|
|
commaSep1 = (rule) ->
|
|
seq(rule, repeat(seq(",", rule)))
|
|
|
|
prefix = (precedence, operand, operator) ->
|
|
prec(precedence, seq(operator, operand))
|
|
|
|
postfix = (precedence, operand, operator) ->
|
|
prec(precedence, seq(operand, operator))
|
|
|
|
infix = (precedence, operand, operator) ->
|
|
prec(precedence, seq(operand, operator, operand))
|
|
|
|
PREC = precedenceValues [
|
|
'assignment'
|
|
'conditional'
|
|
'default'
|
|
'logical_or'
|
|
'logical_and'
|
|
'inclusive_or'
|
|
'exclusive_or'
|
|
'bitwise_and'
|
|
'equal'
|
|
'relational'
|
|
'sizeof'
|
|
'shift'
|
|
'add'
|
|
'multiply'
|
|
'call'
|
|
'cast'
|
|
'unary'
|
|
'field'
|
|
'subscript'
|
|
]
|
|
|
|
{
|
|
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(
|
|
keyword("#include"),
|
|
choice(@string_literal, @system_lib_string))
|
|
|
|
preproc_define: -> seq(
|
|
keyword("#define"),
|
|
@identifier,
|
|
token(seq(/.+/, repeat(seq("\\\n", /.+/)))))
|
|
|
|
preproc_call: -> choice(
|
|
seq(@preproc_directive, /.*/))
|
|
|
|
preproc_ifdef: -> seq(keyword("#ifdef"), @identifier)
|
|
preproc_ifndef: -> seq(keyword("#ifndef"), @identifier)
|
|
preproc_else: -> keyword("#else")
|
|
preproc_endif: -> keyword("#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: -> repeat(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(
|
|
keyword("typedef"),
|
|
keyword("extern"),
|
|
keyword("static") ,
|
|
keyword("auto"),
|
|
keyword("register"))
|
|
|
|
type_qualifier: -> choice(
|
|
keyword("const"),
|
|
keyword("restrict"),
|
|
keyword("volatile"))
|
|
|
|
function_specifier: -> keyword("inline")
|
|
|
|
type_specifier: -> choice(
|
|
@struct_specifier,
|
|
@union_specifier,
|
|
@enum_specifier,
|
|
seq(
|
|
repeat(choice(
|
|
keyword("unsigned"),
|
|
keyword("long"),
|
|
keyword("short"))),
|
|
@identifier))
|
|
|
|
enum_specifier: -> seq(
|
|
keyword("enum"),
|
|
choice(
|
|
@identifier,
|
|
seq(
|
|
optional(@identifier),
|
|
"{", commaSep(@enumerator), "}")))
|
|
|
|
struct_specifier: -> seq(
|
|
keyword("struct"),
|
|
choice(
|
|
@identifier,
|
|
seq(
|
|
optional(@identifier),
|
|
"{", repeat(@struct_declaration), "}")))
|
|
|
|
union_specifier: -> seq(
|
|
keyword("union"),
|
|
choice(
|
|
@identifier,
|
|
seq(
|
|
optional(@identifier),
|
|
"{", repeat(@struct_declaration), "}")))
|
|
|
|
struct_declaration: -> seq(
|
|
repeat(choice(
|
|
@type_specifier,
|
|
@type_qualifier)),
|
|
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("[", keyword("static"), repeat(@type_qualifier), @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), keyword("static"), @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), "*", "]"),
|
|
seq("(", optional(@parameter_type_list), ")"),
|
|
seq("(", commaSep(@identifier), ")"))))
|
|
|
|
direct_abstract_declarator: -> choice(
|
|
seq("(", @abstract_declarator, ")"),
|
|
seq(
|
|
@direct_abstract_declarator,
|
|
choice(
|
|
seq("[", repeat(@type_qualifier), optional(@expression), "]"),
|
|
seq("[", keyword("static"), repeat(@type_qualifier), @expression, "]"),
|
|
seq("[", repeat(@type_qualifier), keyword("static"), @expression, "]"),
|
|
seq("[", "*", "]"),
|
|
seq("(", optional(@parameter_type_list), ")"))))
|
|
|
|
parameter_type_list: -> seq(
|
|
commaSep(@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(
|
|
keyword("if"),
|
|
"(", @expression, ")",
|
|
@statement,
|
|
optional(seq(
|
|
keyword("else"),
|
|
@statement)))
|
|
|
|
switch_statement: -> seq(
|
|
keyword("switch"),
|
|
"(", @expression, ")",
|
|
@statement)
|
|
|
|
case_statement: -> seq(
|
|
choice(
|
|
seq(keyword("case"), @expression),
|
|
keyword("default")),
|
|
":",
|
|
@statement)
|
|
|
|
while_statement: -> seq(
|
|
keyword("while"),
|
|
"(", @expression, ")",
|
|
@statement)
|
|
|
|
do_statement: -> seq(
|
|
keyword("do"),
|
|
@statement,
|
|
keyword("while"),
|
|
"(", @expression, ")")
|
|
|
|
for_statement: -> seq(
|
|
keyword("for"),
|
|
"(",
|
|
choice(
|
|
@declaration,
|
|
seq(optional(@expression), ";")),
|
|
optional(@expression), ";"
|
|
optional(@expression),
|
|
")",
|
|
@statement)
|
|
|
|
return_statement: -> seq(
|
|
keyword("return"),
|
|
optional(@expression)
|
|
";")
|
|
|
|
break_statement: -> seq(
|
|
keyword("break"), ";")
|
|
|
|
continue_statement: -> seq(
|
|
keyword("continue"), ";")
|
|
|
|
goto_statement: -> seq(
|
|
keyword("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("(", @expression, ")"))
|
|
|
|
conditional_expression: -> prec(PREC.conditional, seq(
|
|
@expression,
|
|
"?",
|
|
@expression,
|
|
":",
|
|
@expression))
|
|
|
|
assignment_expression: ->
|
|
infix(PREC.assignment, @expression, choice(
|
|
"=",
|
|
"*=",
|
|
"/=",
|
|
"%=",
|
|
"+=",
|
|
"-=",
|
|
"<<=",
|
|
">>=",
|
|
"&=",
|
|
"^=",
|
|
"|="))
|
|
|
|
pointer_expression: -> choice(
|
|
prefix(PREC.unary, @expression, "*"),
|
|
prefix(PREC.unary, @expression, "&"))
|
|
|
|
logical_expression: -> choice(
|
|
infix(PREC.logical_or, @expression, "||"),
|
|
infix(PREC.logical_and, @expression, "&&"),
|
|
prefix(PREC.unary, @expression, "!"))
|
|
|
|
bitwise_expression: -> choice(
|
|
infix(PREC.inclusive_or, @expression, "|"),
|
|
infix(PREC.exclusive_or, @expression, "^"),
|
|
infix(PREC.bitwise_and, @expression, "&"),
|
|
prefix(PREC.unary, @expression, "~"))
|
|
|
|
equality_expression: ->
|
|
infix(PREC.equal, @expression, choice("==", "!="))
|
|
|
|
relational_expression: ->
|
|
infix(PREC.relational, @expression, choice("<", ">", "<=", ">="))
|
|
|
|
shift_expression: ->
|
|
infix(PREC.shift, @expression, choice("<<", ">>"))
|
|
|
|
math_expression: -> choice(
|
|
infix(PREC.add, @expression, "+"),
|
|
infix(PREC.add, @expression, "-"),
|
|
infix(PREC.multiply, @expression, "*"),
|
|
infix(PREC.multiply, @expression, "/"),
|
|
infix(PREC.multiply, @expression, "%"),
|
|
prefix(PREC.unary, @expression, "-"),
|
|
prefix(PREC.unary, @expression, "+"),
|
|
prefix(PREC.unary, @expression, choice("++", "--")),
|
|
postfix(PREC.unary, @expression, choice("++", "--")))
|
|
|
|
cast_expression: ->
|
|
prec(PREC.cast, seq("(", @type_name, ")", @expression))
|
|
|
|
sizeof_expression: -> choice(
|
|
prefix(PREC.sizeof, @expression, keyword("sizeof"))
|
|
prefix(PREC.sizeof, seq("(", @type_name, ")"), keyword("sizeof")))
|
|
|
|
subscript_expression: ->
|
|
prec(PREC.subscript, seq(@expression, "[", @expression, "]"))
|
|
|
|
call_expression: ->
|
|
prec(PREC.call, seq(@expression, "(", commaSep(@expression), ")"))
|
|
|
|
field_expression: -> choice(
|
|
infix(PREC.field, @expression, "."),
|
|
infix(PREC.field, @expression, "->"))
|
|
|
|
compound_literal_expression: -> seq(
|
|
"(", @type_name, ")",
|
|
@initializer_list)
|
|
|
|
type_name: -> seq(
|
|
repeat(choice(
|
|
@type_specifier,
|
|
@type_qualifier)),
|
|
optional(@abstract_declarator))
|
|
|
|
initializer_list: -> seq(
|
|
"{",
|
|
commaSep(seq(
|
|
optional(seq(repeat(@designator), "=")),
|
|
@initializer)),
|
|
optional(",")
|
|
"}")
|
|
|
|
designator: -> choice(
|
|
seq("[", @expression, "]"),
|
|
seq(".", @identifier))
|
|
|
|
initializer: -> choice(
|
|
@expression,
|
|
@initializer_list)
|
|
|
|
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(/[^\*]/, /\*[^/]/)), "*/")))
|
|
}
|