{ const KEYWORDS = new Set([ "alignas", "continue", "friend", "register", "true", "alignof", "decltype", "goto", "reinterpret_cast", "try", "asm", "default", "if", "return", "typedef", "auto", "delete", "inline", "short", "typeid", "bool", "do", "int", "signed", "typename", "break", "double", "long", "sizeof", "union", "case", "dynamic_cast", "mutable", "static", "unsigned", "catch", "else", "namespace", "static_assert", "using", "char", "enum", "new", "static_cast", "virtual", "char16_t", "explicit", "noexcept", "struct", "void", "char32_t", "export", "nullptr", "switch", "volatile", "class", "extern", "operator", "template", "wchar_t", "const", "false", "private", "this", "while", "constexpr", "float", "protected", "thread_local", "const_cast", "for", "public", "throw" ]); function composeBinaryOp(first: any, rest: any, assoc: "left" | "right", exprType: string) : any { if(!rest || rest.length === 0){ return first; } if (assoc === "left"){ let right = rest.pop(); return { construct_type: exprType, operator: right[0], left: track(composeBinaryOp(first, rest, assoc, exprType), location(), text()), right: right[1], associativity: assoc } } else{ let left = rest.shift(); return { construct_type: exprType, operator: left[0], left: first, right: track(composeBinaryOp(left[1], rest, assoc, exprType), location(), text()), associativity: assoc } } } function absorb(eater: any, eaten: any){ if (!eaten){ return eater; } for (let attr in eaten){ if(attr != "start" && attr != "end"){ eater[attr] = eaten[attr]; } } return eater; } function track(obj: any, location: any, text: string) { for(let key in obj) { if (obj[key] === null) { obj[key] = undefined; } } obj.source = { location : location, start : location.start.offset, text : text, line : location.start.line, column : location.start.column, end : location.start.offset + text.length }; return obj; } function postfixExp(ops: any, sub: any){ if(ops.length == 0){ return sub; } else{ let op = ops.pop(); op.operand = postfixExp(ops, sub); return op; } } let _userTypeNames = { "initializer_list": true, "initializer_list": true, "initializer_list": true, "initializer_list": true }; function getUserTypeNames() : any { //if (typeof Types !== "undefined"){ // return Types.userTypeNames; //} //else{ return _userTypeNames; //} } function isUserTypeName(name: string) { return getUserTypeNames()[name]; } var _type_specifier_user_type_allowed_stack = true; var emptyDeclSpecs = { typeSpecs: [], storageSpecs: [], length: 0 }; var classNameStack: string[] = []; var prohibitOwnClassName: boolean = false; } start = ws decls:(d:decl_or_comment ws {return d})* {return {construct_type: "translation_unit", declarations: decls.filter((elem: any) => elem!=="comment")};} decl_or_comment = comment {return "comment";} / declaration comment = "//@className=" (name:class_head_name { getUserTypeNames()[name.identifier]=true; }) [^\n]* "\n" / "//" [^\n]* "\n" / "/*" (!"*/".)* "*/" statement = stmt:labeled_statement / stmt:block / stmt:selection_statement /*/ stmt:switch_statement*/ / stmt:iteration_statement / stmt:jump_statement / declaration_statement / expression_statement / null_statement / anything /*/ asm_statement / try_except_statement / try_finally_statement*/ labeled_statement = label:identifier ws ":" ws stmt:statement {return track({construct_type: "labeled_statement", label:label, statement: stmt}, location(), text()); } / "case" WS c:exp_const ws ":" ws stmt:statement { return track({construct_type: "labeled_statement", case:c, statement:stmt}, location(), text()); } / "default" ws ":" ws stmt:statement { return track({construct_type: "labeled_statement", default:true, stmt:stmt}, location(), text()); } expression_statement = expr:expression ws ";" { return track({construct_type: "expression_statement", expression:expr}, location(), text()); } null_statement = ws ";" { return track({construct_type: "null_statement"}, location(), text()); } block = "{" ws stmts:(stmt:statement ws {return stmt})* "}" { return track({construct_type: "block", statements: stmts}, location(), text()); } selection_statement = "if" ws "(" ws cond:expression ws ")" ws then:statement ws "else" ws e:statement {return track({construct_type:"if_statement", condition:cond, then:then, otherwise:e}, location(), text()); } / "if" ws "(" ws cond:expression ws ")" ws then:statement {return track({construct_type:"if_statement", condition:cond, then:then}, location(), text()); } /* switch_statement = "switch" ws "(" ws cond:expression ws ")" ws stmt:statement { return track({construct_type:"switch_statement", condition:cond, stmt:stmt}, location(), text()); }*/ iteration_statement = "while" ws "(" ws cond:expression ws ")" ws body:statement { return track({construct_type:"while_statement", condition:cond, body:body}, location(), text()); } / "do" !identifierChar ws body:statement ws "while" ws "(" ws cond:expression ws ")" ws ";" { return track({construct_type:"dowhile_statement", condition:cond, body:body}, location(), text()); } / "for" ws "(" ws init:for_init_statement ws cond:expression ws ";" ws post:expression? ws ")" ws body:statement { return track({construct_type:"for_statement", condition:cond, initial:init, post:post, body:body}, location(), text()); } for_init_statement = expression_statement / null_statement / declaration_statement jump_statement = "break" !identifierChar ws ";" { return track({construct_type:"break_statement"}, location(), text()); } / "continue" !identifierChar ws ";" {return track({construct_type:"continue_statement"}, location(), text()); } / "return" !identifierChar ws ret:expression? ws ";" { return track({construct_type:"return_statement", expression:ret}, location(), text()); } declaration_statement = d:declaration {return track({construct_type:"declaration_statement", declaration: d}, location(), text()); } //Declarations declaration = simple_declaration / class_definition / function_definition /* TODO: template declaration */ // The ! id_expression part below is to prevent e.g. int constblah; parsing as int const blah; // This wouldn't be necessary if we had a separate lexing phase simple_declaration = specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws declarators:declarator_list ws ";" { return track({construct_type: "simple_declaration", specs:specs, declarators:declarators}, location(), text()); } declarator_list = first:declarator_init rest:(ws "," ws d:declarator_init {return d;})* {rest.unshift(first); return rest; } class_definition = spec:class_specifier ws ";" { return spec; } function_definition /* constructors */ = specs:decl_specifiers_prohibit_class_name ! id_expression ws declarator:declarator ws init:ctor_initializer? ws body:function_body {return track({construct_type: "function_definition", specs:specs, declarator:declarator, ctor_initializer:init, body:body}, location(), text()); } / ws declarator:declarator ws init:ctor_initializer? ws body:function_body {return track({construct_type: "function_definition", specs:emptyDeclSpecs, declarator:declarator, ctor_initializer:init, body:body}, location(), text()); } /* regular functions */ / specs:decl_specifiers ! id_expression ws declarator:declarator ws init:ctor_initializer? ws body:function_body {return track({construct_type: "function_definition", specs:specs, declarator:declarator, ctor_initializer:init, body:body}, location(), text()); } / specs:decl_specifiers ! id_expression ws declarator:member_declarator ws init:ctor_initializer? ws body:function_body {return track({construct_type: "function_definition", specs:specs, declarator:declarator, ctor_initializer:init, body:body}, location(), text()); } /*constructor_definition = name:class_name ws "(" ws args:argument_declaration_list ws ")" ws init:ctor_initializer? ws body:function_body {return track({construct_type: "constructor_definition", name:name, body:body, ctorInitializer:init, args: args}, location(), text()); } */ /*destructor_definition = "~" name:class_name ws "(" ws ")" ws body:(function_body / ";" {return null;}) {return track({construct_type: "destructor_definition", name:{identifier: "~"+name.identifier}, body:body}, location(), text()); } / "virtual" WS "~" name:class_name ws "(" ws ")" ws body:(function_body / ";" {return null;}) {return track({construct_type: "destructor_definition", name:{identifier: "~"+name.identifier}, body:body, virtual:true}, location(), text()); }*/ ctor_initializer = ":" ws first:mem_initializer rest:(ws "," ws m:mem_initializer {return m;})* {rest.unshift(first); return track({construct_type: "ctor_initializer", initializers: rest}, location(), text());} mem_initializer = i:identifier ws "(" ws args:exp_list? ws ")" {return track({construct_type: "member_initializer", member: i, args:args || []}, location(), text()); } function_body = block decl_specifiers = nonTypeDeclSpecs:_decl_specifiers_non_type ws firstTypeSpec:decl_specifier? rest:(WS s:decl_specifier_non_user_type_name {return s;})* { firstTypeSpec && rest.unshift(firstTypeSpec); rest = nonTypeDeclSpecs.concat(rest); var declSpecs : any = { typeSpecs: [], storageSpecs: [], length: 0 }; for(var i = 0; i < rest.length; ++i){ let spec = rest[i]; if (spec.typeSpec){ declSpecs.typeSpecs.push(spec.typeSpec); } else if (spec.storageSpec){ declSpecs.storageSpecs.push(spec.storageSpec); } else if (spec === "friend"){ declSpecs.friend = true; } else if (spec === "typedef"){ declSpecs.typedef = true; } else if (spec === "inline"){ declSpecs.inline = true; } else if (spec === "explicit"){ declSpecs.explicit = true; } else if (spec === "virtual"){ declSpecs.virtual = true; } ++declSpecs.length; } return track(declSpecs, location(), text()); } _decl_specifiers_non_type = first:decl_specifier_non_type? rest:(WS s:decl_specifier_non_type ! id_expression {return s;})* {first && rest.unshift(first); return rest;} _decl_specifiers_non_user_type_name = first:decl_specifier_non_user_type_name? rest:(WS s:decl_specifier_non_user_type_name ! id_expression {return s;})* {first && rest.unshift(first); return rest;} decl_specifier = t:type_specifier { return {typeSpec:t}; } / s:storage_class_specifier { return {storageSpec:s}; } / function_specifier / "typedef" / "friend" decl_specifier_non_type = "const" {return {typeSpec: "const"};} / "volatile" {return {typeSpec: "volatile"};} / s:storage_class_specifier { return {storageSpec:s}; } / function_specifier / "typedef" / "friend" decl_specifier_non_user_type_name = t:simple_type_name_non_user { return {typeSpec:t}; } / t:elaborated_type_specifier { return {typeSpec:t}; } / "const" {return {typeSpec: "const"};} / "volatile" {return {typeSpec: "volatile"};} / class_specifier / enum_specifier / s:storage_class_specifier { return {storageSpec:s}; } / function_specifier / "typedef" / "friend" decl_specifiers_prohibit_class_name = & { prohibitOwnClassName = true; return true;} d: decl_specifiers { prohibitOwnClassName = false; return d;} / & { prohibitOwnClassName = false; return false;} "PEG_doesnt_realize_we_never_get_to_here" type_specifiers = first:type_specifier rest:(WS s:type_specifier {return s})* { rest.unshift(first); return track(rest, location(), text()); } type_specifier = trailing_type_specifier / class_specifier / enum_specifier trailing_type_specifier = t:simple_type_name ! { return prohibitOwnClassName && t === classNameStack[classNameStack.length - 1]; } { return t; } / elaborated_type_specifier / "const" / "volatile" elaborated_type_specifier = class_key WS name:class_name {track({construct_type: "elaborated_type_specifier", name:name}, location(), text());} storage_class_specifier = "register" / "static" / "thread_local" / "extern" / "mutable" function_specifier = "inline" / "explicit" / "virtual" simple_type_name "simple type name" = t:_simple_type_name !identifierChar {return t;} _simple_type_name /*= qualified_type_name*/ = "char" / "short" / "int" / "size_t" / "bool" / "long" / "signed" / "unsigned" / "float" / "double" / "void" / n:class_name {return n.identifier;} simple_type_name_non_user "simple type name" = t:_simple_type_name_non_user !identifierChar {return t;} _simple_type_name_non_user /*= qualified_type_name*/ = "char" / "short" / "int" / "bool" / "long" / "signed" / "unsigned" / "float" / "double" / "void" enum_specifier /*= "enum" WS name:identifier? ws "{" first:enumerator rest:(ws "," ws e:enumerator {return e;})* ws "}" { return {enum:name, values:composeBinaryOp(first, rest)}; }*/ = "enum" WS name:identifier? ws "{" first:enumerator rest:(ws "," ws e:enumerator {return e;})* ws "}" { return "enum"; } enumerator = name:identifier value:(ws "=" ws e:exp_const {return e;}) {return {name:name, value:value}; } //Declarators declarator_init = d:declarator ws "=" ws "@library_unsupported@" {return track(absorb(d, {library_unsupported: true}), location(), text());} / d:declarator ws i:initializer? {return track(absorb(d, {initializer: i}), location(), text());} initializer = brace_or_equal_initializer / "(" ws args:exp_list? ws ")" { return track({construct_type:"direct_initializer", args:args}, location(), text()); } brace_or_equal_initializer = "=" ws init:exp_assn {return track({construct_type:"copy_initializer", args:[init]}, location(), text());} // / "=" ws "{" ws "}" {return [];} / "=" ws "{" ws init:initializer_list ws "}" {return track({construct_type:"list_initializer", arg:init}, location(), text());} initializer_list = first:exp_assn rest:(ws op:"," ws e:exp_assn {return e;})* { rest.unshift(first); return track({construct_type:"initializer_list_expression", elements: rest}, location(), text()); } /// exp:expression {return exp;} declarator = d:_declarator { var d2 = track(absorb({construct_type:"declarator"}, d), location(), text()); return d2; } abstract_declarator = d:_abs_declarator { var d2 = track(absorb({construct_type:"declarator", abstract: true}, d), location(), text()); return d2; } _declarator = "*" ws cv:cv_qualifier? ws decl:_declarator { return absorb({pointer:decl}, cv); } / "&" ws cv:cv_qualifier? ws decl:_declarator { return absorb({reference:decl}, cv); } / decl:declarator_simple postfixes:declarator_postfixes { return absorb(decl, {postfixes: postfixes}); } / declarator_simple _abs_declarator = "*" ws cv:cv_qualifier? ws decl:_abs_declarator { return absorb({pointer:decl}, cv); } / "&" ws cv:cv_qualifier? ws decl:_abs_declarator { return absorb({reference:decl}, cv); } / decl:abs_declarator_simple postfixes:declarator_postfixes { return absorb(decl, {postfixes: postfixes}); } / abs_declarator_simple declarator_postfixes = (ws p:declarator_postfix {return p})+ declarator_postfix = "(" ws args:argument_declaration_list ws ")" ws cv:cv_qualifier? { return absorb({kind:"function", args:args}, cv); } / "[" ws size:exp_const? ws "]" { return {kind:"array", size:size}; } declarator_simple = d:dname { return {name:d}; } /* TODO ptr to member/ "::*" ws cv_qualifier? WS _declarator */ / "(" ws d:_declarator? ws ")" {return {sub:d};} abs_declarator_simple /* TODO ptr to member/ "::*" ws cv_qualifier? WS _declarator */ = "(" ws d:_abs_declarator? ws ")" {return {sub:d};} / "" {return {};} dname = id_expression /* / class_name / "~" ws class_name / typedef_name / qualified_type_name */ type_id = specs:type_specifiers ws decl:abstract_declarator { return track({specs:specs, declarator:decl}, location(), text()); } cv_qualifier = "const" WS "volatile" { return {const: true, volatile: true}; } / "volatile" WS "const" { return {const: true, volatile: true}; } / "const" { return {const: true}; } / "volatile" { return {volatile: true}; } argument_declaration_list /* TODO: ... args*/ = first:argument_declaration rest:(ws "," ws arg:argument_declaration {return arg;})* { rest.unshift(first); return rest; } / "" {return [];} argument_declaration = specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws decl:declarator ws "=" ws init:exp_assn {return track({construct_type:"parameter_declaration", declarator:decl, specs:specs, initializer:init}, location(), text()); } / specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws decl:declarator {return track({construct_type:"parameter_declaration", specs:specs, declarator:decl}, location(), text()); } / specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws decl:abstract_declarator ws "=" ws init:exp_assn {return track({construct_type:"parameter_declaration", declarator:decl, specs:specs, initializer:init}, location(), text()); } / specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws decl:abstract_declarator {return track({construct_type:"parameter_declaration", specs:specs, declarator:decl}, location(), text()); } /* TODO abstract arg declarations */ //Expressions expression = first:exp_assn rest:(ws op:"," ws e:exp_assn {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "comma_expression"), location(), text()); } / exp_assn exp_assn = lhs:exp_unary ws op:"=" ws rhs:exp_assn { return track({construct_type: "assignment_expression", lhs:lhs, operator:op, rhs:rhs}, location(), text()); } / lhs:exp_unary ws op:op_comp_assn ws rhs:exp_assn { return track({construct_type: "compound_assignment_expression", lhs:lhs, operator:op, rhs:rhs}, location(), text()); } / exp_cond op_comp_assn = "*=" / "/=" / "%=" / "+=" / "-=" / ">>=" / "<<=" / "&=" / "and_eq" !identifierChar {return "&=";} / "^=" / "xor_eq" !identifierChar {return "^=";} / "|=" / "or_eq" !identifierChar {return "|=";} exp_const = sub:exp_cond { sub.constant = true; return sub; } exp_cond = condition:exp_or ws "?" ws then:expression ws ":" ws otherwise:exp_assn { return track({construct_type: "ternary_expression", condition:condition, then:then, otherwise:otherwise}, location(), text()); } / exp_or exp_or = first:exp_and rest:(ws op:("||" / "or" !identifierChar) ws e:exp_and {return ["||",e];} )* { return track(composeBinaryOp(first, rest, "left", "logical_binary_operator_expression"), location(), text()); } exp_and = first:exp_or_bit rest:(ws op:("&&" / "and" !identifierChar) ws e:exp_or_bit {return ["&&",e];} )* { return track(composeBinaryOp(first, rest, "left", "logical_binary_operator_expression"), location(), text()); } exp_or_bit = first:exp_xor_bit rest:(ws op:("|" / "bitor" !identifierChar) ws e:exp_xor_bit {return ["|",e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } exp_xor_bit = first:exp_and_bit rest:(ws op:("^" / "xor") ws e:exp_and_bit {return ["^",e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } exp_and_bit = first:exp_eq rest:(ws op:("&" !"&" / "bitand" !identifierChar) ws e:exp_eq {return ["&",e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } exp_eq = first:exp_rel rest:(ws op:("==" / ("!=" / "not_eq" !identifierChar {return "!=";})) ws e:exp_rel {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "relational_binary_operator_expression"), location(), text()); } exp_rel = first:exp_shift rest:(ws op:op_rel ws e:exp_shift {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "relational_binary_operator_expression"), location(), text()); } op_rel = "<=" / ">=" / "<" / ">" exp_shift = first:exp_add rest:(ws op:op_shift ws e:exp_add {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } op_shift = "<<" / ">>" exp_add = first:exp_mult rest:(ws op:op_add ws e:exp_mult {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } op_add = "+" / "-" exp_mult = first:exp_pm rest:(ws op:op_mult ws e:exp_pm {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "arithmetic_binary_operator_expression"), location(), text()); } op_mult = "*" / "/" / "%" exp_pm = first:exp_cast rest:(ws op:op_pm ws e:exp_cast {return [op,e];} )* { return track(composeBinaryOp(first, rest, "left", "pointer_to_member_expression"), location(), text()); } op_pm = ".*" / "->*" exp_cast = "(" ws t:type_id ws ")" ws sub:exp_cast { return track({construct_type: "c_style_cast_expression", type:t, operand:sub}, location(), text()); } / exp_unary exp_unary = op:"++" ws sub:exp_unary { return track({construct_type: "prefix_increment_expression", operator:op, operand:sub}, location(), text()); } / op:"--" ws sub:exp_unary { return track({construct_type: "prefix_increment_expression", operator:op, operand:sub}, location(), text()); } / op:op_unary ws sub:exp_cast { return track(absorb({operand:sub}, op), location(), text()); } / op:"sizeof" ws sub:exp_unary { return track({construct_type: "sizeof_expression", operator:"sizeof", operand:sub}, location(), text()); } / op:"sizeof" ws "(" ws sub:type_name ws ")" { return track({construct_type: "sizeof_type_expression", operator:"sizeof", operand:sub}, location(), text()); } / exp_new / exp_delete op_unary = "*" {return {construct_type: "dereference_expression", operator: "*"};} / "&" {return {construct_type: "address_of_expression", operator: "&"};} / "+" {return {construct_type: "unary_plus_expression", operator: "+"};} / "-" {return {construct_type: "unary_minus_expression", operator: "-"};} / "!" {return {construct_type: "logical_not_expression", operator: "!"};} / "not" !identifierChar {return {construct_type: "logical_not_expression", operator: "!"};} / "~" {return {construct_type: "bitwise_not_expression", operator: "~"};} / "compl" {return {construct_type: "bitwise_not_expression", operator: "~"};} exp_new = "new" WS id:new_type_id ws init:new_initializer? { return track(absorb({construct_type: "new_expression", initializer: init}, id), location(), text()); } / "new" ws "(" ws id:type_id ws ")" ws init:new_initializer? { return track(absorb({construct_type: "new_expression", initializer: init}, id), location(), text()); } /* / exp_dealloc */ new_type_id = specs:type_specifiers decl:(ws id:new_declarator {return id;})? { return track({specs:specs, declarator:decl}, location(), text()); } new_declarator = d:_new_declarator { var d2 = track(absorb({construct_type:"declarator"}, d), location(), text()); return d2; } _new_declarator = "*" ws cv:cv_qualifier? ws decl:_new_declarator? { return absorb({pointer:decl}, cv); } / "&" ws cv:cv_qualifier? ws decl:_new_declarator? { return absorb({reference:decl}, cv); } / postfixes:new_declarator_postfixes { return {postfixes: postfixes}; } new_declarator_postfixes = (ws p:new_declarator_postfix {return p;})+ new_declarator_postfix = "[" ws size:exp_assn? ws "]" { return {kind:"array", size:size}; } new_initializer = "(" ws ")" {return {construct_type:"value_initializer"};} / "(" ws args:exp_list? ws ")" {return {construct_type:"direct_initializer", args:args || []};} exp_delete = "delete" WS t:exp_cast { return track({construct_type: "delete_expression", operand: t}, location(), text()); } / "delete" ws "[" ws "]" WS t:exp_cast { return track({construct_type: "delete_array_expression", operand: t}, location(), text()); } / exp_postfix exp_postfix = "static_cast" ws "<" ws t:type_name ws ">" ws "(" ws sub:exp_cast ws ")" { return track({construct_type: "static_cast_expression", type: t, operand:sub}, location(), text());} / "dynamic_cast" ws "<" ws t:type_name ws ">" ws "(" ws sub:exp_cast ws ")" { return track({construct_type: "dynamic_cast_expression", type: t, operand:sub}, location(), text());} / "reinterpret_cast" ws "<" ws t:type_name ws ">" ws "(" ws sub:exp_cast ws ")" { return track({construct_type: "reinterpret_cast_expression", type: t, operand:sub}, location(), text());} / "const_cast" ws "<" ws t:type_name ws ">" ws "(" ws sub:exp_cast ws ")" { return track({construct_type: "const_cast_expression", type: t, operand:sub}, location(), text());} / sub:(exp_construct / exp_primary) ops:(ws op:op_postfix {return op;} )* { return ops.length > 0 ? track(postfixExp(ops, sub), location(), text()) : sub;} op_postfix = "[" ws sub:expression ws "]" { return track({construct_type:"subscript_expression", offset:sub}, location(), text()); } / "(" ws args:exp_list? ws ")" { return track({construct_type: "function_call_expression", args: args || []}, location(), text()); } / "." ws mem:id_expression { return track({construct_type: "dot_expression", member: mem}, location(), text()); } / "->" ws mem:id_expression { return track({construct_type: "arrow_expression", member: mem}, location(), text()); } / "++" { return track({construct_type: "postfix_increment_expression", operator: "++"}, location(), text()); } / "--" { return track({construct_type: "postfix_increment_expression", operator: "--"}, location(), text()); } exp_construct = type:simple_type_name ws "(" ws args:exp_list? ws ")" { return track({construct_type: "construct_expression", type:type, args:args || []}, location(), text()); } type_name = simple_type_name exp_list = first:exp_assn rest:(ws "," ws e:exp_assn {return e;} )* { rest.unshift(first); return rest; } exp_primary = "(" ws sub:expression ws ")" {return track({construct_type: "parentheses_expression", subexpression:sub}, location(), text());} / literal / "this" !identifierChar {return track({construct_type: "this_expression"}, location(), text()); } / "nullptr" !identifierChar {return track({construct_type: "nullptr_expression"}, location(), text()); } / "NULL" !identifierChar {return track({construct_type: "nullptr_expression"}, location(), text()); } / id:id_expression { return track({construct_type: "identifier_expression", identifier: id}, location(), text()); } / anything / opaque_expression id_expression = qualified_id / n:unqualified_id ! { return KEYWORDS.has(n.identifier); } { return n; } unqualified_id = operator_function_id / "~" ws n:class_name { return {construct_type: "unqualified_identifier", identifier: "~"+n.identifier}; } / identifier qualified_id = n:nested_name_specifier ws u:unqualified_id { n.push(u); return {construct_type: "qualified_identifier", components: n}; } nested_name_specifier = (id:(type_name_id / namespace_name) ws "::" ws {return id;})+ / "::" ws { return [];} type_name_id = id:type_name {return track({identifier: id}, location(), text());} namespace_name = identifier name = operator_function_id / identifier operator_function_id = "operator" ws op:overloadable_op {return track({construct_type: "unqualified_identifier", identifier: "operator"+op, operator: op}, location(), text());} overloadable_op = "new[]" / "delete[]" / "new" / "delete" / "->*" / ">>=" / "<<=" / "+=" / "-=" / "*=" / "/=" / "%=" / "^=" / "&=" / "|=" / "<<" / ">>" / "==" / "!=" / "<=" / ">=" / "&&" / "||" / "++" / "--" / "->" / "()" / "[]" / "+" / "-" / "*" / "/" / "%" / "^" / "&" / "|" / "~" / "!" / "=" / "<" / ">" / "," unoverloadable_op = "." / ".*" / "::" / "?:" literal = lit:literal_float {return track({construct_type: "numeric_literal_expression", type:"float", value:lit}, location(), text());} / lit:literal_int {return track({construct_type: "numeric_literal_expression", type:"int", value:lit}, location(), text());} / lit:literal_char {return track({construct_type: "numeric_literal_expression", type:"char", value:lit}, location(), text());} / lit:literal_string {return track({construct_type: "string_literal_expression", type:"string", value:lit}, location(), text());} / lit:literal_boolean {return track({construct_type: "numeric_literal_expression", type:"bool", value:lit}, location(), text());} literal_int = neg:"-"? digits:[0-9]+ { return parseInt((neg ? neg : "") + digits.join("")); } literal_float = neg:"-"? digits:[0-9.]+ & {return digits.indexOf(".") == digits.lastIndexOf(".") && digits.indexOf(".") != -1;} { return parseFloat((neg ? neg : "") + digits.join("")); } literal_char = "'" char:([^\'\\\n] / escape) "'" { return char; } literal_string = '"' chars:([^\"\\\n] / escape)* '"' { return chars.join(""); } literal_boolean = "true" !identifierChar {return true;} / "false" !identifierChar {return false;} escape = simple_escape / octal_escape /*/ hexadecimal_escape*/ simple_escape = "\\\"" //{ return "\""; } / "\\'" //{ return "'"; } / "\\?" //{ return "\?"; } / "\\\\" //{ return "\\"; } / "\\a" //{ return "\a"; } / "\\b" //{ return "\b"; } / "\\f" //{ return "\f"; } / "\\n" //{ return "\n"; } / "\\r" //{ return "\r"; } / "\\t" //{ return "\t"; } / "\\v" //{ return "\v"; } / "\\0" //{ return "\0"; } octal_escape = [0-8] / [0-8][0-8] / [0-8][0-8][0-8] identifier "identifier" = first:identifierFirstChar rest:identifierChar* {return track({construct_type: "unqualified_identifier", identifier: first + rest.join("")}, location(), text());} identifierChar = [a-zA-Z0-9_] identifierFirstChar = [a-zA-Z_] template_identifier = id:identifier "<" t:identifier ">" { return {identifier: id.identifier + "<" + t.identifier + ">"}; } opaque_expression = "@" id:([a-zA-Z_0-9~]/"::"/overloadable_op)+ { return track({construct_type: "opaque_expression", id: id.join("")}, location(), text()); } ws "optional whitespace" = [ \t\n\r]* comment ws / [ \t\n\r]* WS "whitespace" = comment WS / [ \t\n\r]+ comment ws / [ \t\n\r]+ class_name = id:template_identifier & {return isUserTypeName(id.identifier);} {return id;} / id:identifier & {return isUserTypeName(id.identifier);} {return id;} // / simple_template_id class_specifier = head:class_head & { classNameStack.push(head.name.identifier); getUserTypeNames()[head.name.identifier]=true; return true; } ws "{" ws mems:member_specification* ws "}" { classNameStack.pop(); return track({construct_type: "class_definition", head: head, memberSpecs: mems}, location(), text()); } / head:class_head & { classNameStack.pop(); return false; } class_head = key:class_key WS name:class_head_name bases:(WS b:base_clause {return b;})? { return track({construct_type: "class_head", classKey:key, name:name, bases:bases ?? []}, location(), text()); } // = key:class_key WS base:base_clause? // { return track({key:key, base:base}, location(), text()); } class_head_name = template_identifier / /* nested_name_specifier? ws */ identifier class_key = "class" / "struct" / "union" member_specification = access:access_specifier ws ":" ws members:(m:member_declaration ws {return m})* { return track({construct_type: "member_specification", members: members, access: access}, location(), text()); } / members:(m:member_declaration ws {return m})+ { return track({construct_type: "member_specification", members: members}, location(), text()); } access_specifier = "private" / "protected" / "public" member_declaration /* constructor declarations */ = specs:decl_specifiers_prohibit_class_name ! id_expression ws declarators:member_declarator_list ws ";" { return track({construct_type: "simple_member_declaration", specs:specs, declarators:declarators}, location(), text()); } / ws declarators:member_declarator_list ws ";" { return track({construct_type: "simple_member_declaration", specs:emptyDeclSpecs, declarators:declarators}, location(), text()); } /* regular declaration */ / specs:decl_specifiers ! {return specs.length === 0;} ! id_expression ws declarators:member_declarator_list ws ";" { return track({construct_type: "simple_member_declaration", specs:specs, declarators:declarators}, location(), text()); } /* / c:constructor_definition ws ";"? { return c; } */ /* / d:destructor_definition ws ";"? { return d; } */ / f:function_definition ws ";"? { return f; } member_declarator_list = first:member_declarator rest:(ws "," ws d:member_declarator {return d;})* {rest.unshift(first); return rest; } member_declarator = d:declarator ws "=" ws "0" {d.pureVirtual = true; return d;} / d:declarator ws "=" ws "@library_unsupported@" {d.library_unsupported = true; return d;} / d:declarator ws i:brace_or_equal_initializer {d.initializer = i; return d;} / d:declarator ws v:(v:virt_specifier ws {return v;}) i:brace_or_equal_initializer { d[v] = true; d.initializer = i; return d; } / d:declarator ws v:virt_specifier {d[v] = true; return d;} / d:declarator virt_specifier = "override" base_clause = ":" ws b:base_specifier_list {return b}; base_specifier_list = first:base_specifier rest:(ws "," ws b:base_specifier {return b;})* {rest.unshift(first); return rest; } base_specifier = "virtual" WS a:(a:access_specifier WS {return a;}) c:qualified_class_name{ return track({construct_type: "base_specifier", name: c, virtual: true, access: a}, location(), text());} / a:(a:access_specifier WS {return a;}) "virtual" WS c:qualified_class_name{ return track({construct_type: "base_specifier", name: c, virtual: true, access: a}, location(), text());} / a:access_specifier WS c:qualified_class_name{ return track({construct_type: "base_specifier", name: c, access: a}, location(), text());} / c:qualified_class_name{ return track({construct_type: "base_specifier", name: c}, location(), text());} qualified_class_name = n:nested_name_specifier ws c:class_name{ n.push(c); return {construct_type: "qualified_identifier", components: n};} / class_name anything = "@anything@" { return track({construct_type: "anything_construct"}, location(), text()); }