[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/satya-das/cppparser/master/cppparser/src/parser.y [Back]  [Original]

// Copyright (C) 2022 Satya Das and CppParser contributors
// SPDX-License-Identifier: MIT

/*
  TODOs:
    (1) Parsing of following needs improvements/support:
      (a) Imp: Function pointer
      (b) Sup: Reference to array
      (c) Sup: Pointer to array
      Need to borrow ideas from https://www.nongnu.org/hcb/
        and may be http://www.computing.surrey.ac.uk/research/dsrg/fog/CxxGrammar.y too can help.
*/

// clang-format off

%{
#include "cpptoken.h"
#include "cpp_entity_builders.h"

#include "cppast/cppast.h"
#include "optional.h"
#include "parser.tab.h"
#include "parser.l.h"
#include "utils.h"

#include "memory_util.h"

#include 
#include 
#include 
#include 
#include 

//////////////////////////////////////////////////////////////////////////

#ifndef NDEBUG
#  define YYDEBUG 1
#else 
#  define YYDEBUG 0
#endif //#ifndef NDEBUG

#define YYERROR_DETAILED

#define YYDELETEPOSN(x, y)
#define YYDELETEVAL(x, y)

#ifndef TRUE // Need this to fix BtYacc compilation error.
#  define TRUE true
#endif

static int gParseLog = 0;

#define ZZLOG               \
  {                         \
  if (gParseLog)                 \
    printf("ZZLOG @line#%d, parsing stream line#%d\n", __LINE__, g.mLineNo); \
}

static int gDisableYyValid = 0;

#define ZZVALID   {         \
  if (gParseLog)                 \
    printf("ZZVALID: ");    \
  ZZLOG;                    \
  if (!gDisableYyValid)     \
    YYVALID;                \
  }

#define ZZERROR             \
  do {                      \
    if (gParseLog)               \
      printf("ZZERROR: ");  \
    ZZLOG;                  \
    YYERROR;                \
  } while(0)

#define ZZVALID_DISABLE     \
  ++gDisableYyValid;

#define ZZVALID_ENABLE      \
  --gDisableYyValid;


/** {Globals} */
/**
 * A program unit is the entire parse tree of a source/header file
 */
static cppast::CppCompound*  gProgUnit;

// FuncdeclHack:
// Following gets parsed as variable with initialization:
// Type Identifier(Type * Id);
// `Type * Id` gets parsed as expression involving multiplication and so `Identifier`
//  followed by expression in brackets becomes a call to constructor of `Type`.
// Actually there is an ambiguity in the grammer which compilers solve by using context.
// For purpose of this parser we cannot collect all required context to solve this ambiguity.
// So, we use a hack:
// We define a production rule for this case and flag it as error. But before flagging error
// we save the position of operator '*' (or '&', or "&&") and then we check for location of
// the same operator in other expression production rule before accepting that as valid expression.
// For us we always want to parse it as function declaration rather than call to constructor by passing an expression,
// and so the hack is expected to serve us well.
static const char* gParamModPos = nullptr;

// TemplateParamHack:
// Template parameter gets parsed as vardecl which then gets reduced as templateparam without name as used in forward declaration.
// We don't want that, so to avoid such templateparam getting reduced as vardecl we apply some hack.
static const char* gTemplateParamStart = nullptr;
static bool gInTemplateSpec = false;

/**
 * A stack to know where (i.e. how deep inside class defnition) the current parsing activity is taking place.
 */
using CppCompoundStack = std::stack;

static CppCompoundStack                     gCompoundStack;

/** {End of Globals} */

#define YYPOSN char*

extern int yylex();

// Yacc generated code causes warnings that need suppression.
// This pragma should be at the end.
#if defined(__clang__) || defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wwrite-strings"
#endif

using namespace cppast;

// FIXME: Improve template arg parsing.
// Template argument needs more robust support.
// As of now we are treating them just as string.
// But for parsing we need to have a type.
class CppTemplateArg;

%}

%union {
  struct CppToken                                  str;
  struct CppFunctionData                           funcDeclData;
  cppast::CppMemberInit*                           memInit;
  cppast::CppEntity*                               cppEntity;
  cppast::CppEntityAccessSpecifier*                accessSpecifier;
  cppast::CppTypeModifier                          typeModifier;
  cppast::CppVarType*                              cppVarType;
  cppast::CppVar*                                  cppVarObj;
  cppast::CppEnum*                                 cppEnum;
  cppast::CppEnumItem*                             enumItem;
  std::list*                  enumItemList;
  cppast::CppTypedefName*                          typedefName;
  cppast::CppTypedefList*                          typedefList;
  cppast::CppUsingDecl*                            usingDecl;
  cppast::CppUsingNamespaceDecl*                   usingNamespaceDecl;
  cppast::CppNamespaceAlias*                       namespaceAlias;
  cppast::CppCompound*                             cppCompundObj;
  cppast::CppTemplateParam*                        templateParam;
  std::vector*           templateParamList;
  cppast::CppDocumentationComment*                 docCommentObj;
  cppast::CppForwardClassDecl*                     fwdDeclObj;
  cppast::CppVarList*                              cppVarObjList;
  cppast::CppPreprocessorUnrecognized*             unRecogPreProObj;
  cppast::CppExpression*                           cppExprObj;
  cppast::CppCallArgs*                             exprList;
  cppast::CppLambda*                               cppLambda;
  cppast::CppFunction*                             cppFuncObj;
  cppast::CppFunctionPointer*                      cppFuncPointerObj;
  cppast::CppEntity*                               varOrFuncPtr;
  std::vector* paramList;
  cppast::CppConstructor*                          cppCtorObj;
  cppast::CppDestructor*                           cppDtorObj;
  cppast::CppTypeConverter*                        cppTypeConverter;
  cppast::CppMemberInits*                          memInitList;
  std::list*           inheritList;
  bool                                             inheritType;
  std::vector*                        identifierList;
  std::vector*                        funcThrowSpec;
  class CppTemplateArg*                            templateArg;
  cppast::CppAsmBlock*                             asmBlock;
  cppast::CppCompoundType                          compoundType;
  unsigned short                                   ptrLevel;
  cppast::CppRefType                               refType;
  unsigned int                                     attr;
  Optional                  objAccessType;
  cppast::CppCallArgs*                             attribSpecifiers;
  cppast::CppIfBlock*                              ifBlock;
  cppast::CppWhileBlock*                           whileBlock;
  cppast::CppDoWhileBlock*                         doWhileBlock;
  cppast::CppForBlock*                             forBlock;
  cppast::CppRangeForBlock*                        forRangeBlock;
  cppast::CppSwitchBlock*                          switchBlock;
  std::vector*                    switchBody;
  cppast::CppTryBlock*                             tryBlock;
  cppast::CppCatchBlock*                           catchBlock;
  cppast::CppPreprocessorDefine*                      hashDefine;
  cppast::CppPreprocessorUndef*                    hashUndef;
  cppast::CppPreprocessorInclude*                  hashInclude;
  cppast::CppPreprocessorImport*                   hashImport;
  cppast::CppPreprocessorConditional*              hashIf;
  cppast::CppPreprocessorError*                    hashError;
  cppast::CppPreprocessorWarning*                  hashWarning;
  cppast::CppPreprocessorPragma*                   hashPragma;
  cppast::CppReturnStatement*                      returnStmt;
  cppast::CppThrowStatement*                       throwStmt;
  cppast::CppGotoStatement*                        gotoStmt;
  cppast::CppBlob*                                 blob;
  cppast::CppLabel*                                label;
  cppast::CppVarInitInfo*                          cppVarInitInfo;
}

%token     tknName tknID tknStrLit tknCharLit tknNumber tknMacro tknApiDecor
%token     tknTypedef tknUsing
%token     tknInteger tknChar tknDouble tknFloat
%token     tknEnum
%token     tknAuto
%token     tknPreProDef
%token     tknClass tknStruct tknUnion tknNamespace
%token     tknTemplate tknTypename tknDecltype
%token     tknFreeStandingBlockComment tknSideBlockComment tknFreeStandingLineComment tknSideLineComment
%token     tknScopeResOp
%token     tknNumSignSpec // signed/unsigned
%token     tknPublic tknProtected tknPrivate
%token     tknExternC
%token     tknUnRecogPrePro
%token     tknStdHdrInclude
%token     tknPragma tknHashError tknHashWarning
%token     tknEllipsis
%token     tknConstCast tknStaticCast tknDynamicCast tknReinterpretCast
%token     tknTry tknCatch tknThrow tknSizeOf
%token     tknOperator tknPlusEq tknMinusEq tknMulEq tknDivEq tknPerEq tknXorEq tknAndEq tknOrEq
%token     tknLShift tknRShift tknLShiftEq tknRShiftEq tknCmpEq tknNotEq tknLessEq tknGreaterEq
%token     tkn3WayCmp tknAnd tknOr tknInc tknDec tknArrow tknArrowStar
%token     tknLT tknGT // We will need the position of these operators in stream when used for declaring template instance.
%token     '+' '-' '*' '/' '%' '^' '&' '|' '~' '!' '=' ',' '(' ')' '[' ']' ';' '.'
%token     tknNew tknDelete
%token     tknConst tknConstExpr
%token     tknVoid // For the cases when void is used as function parameter.
%token     tknOverride tknFinal // override, final are not a reserved keywords
%token     tknAsm
%token     tknBlob
%token     tknGoto

%token  tknStatic tknExtern tknVirtual tknInline tknExplicit tknFriend tknVolatile tknMutable tknNoExcept

%token  tknPreProHash /* When # is encountered for pre processor definition */
%token  tknDefine tknUndef
%token  tknInclude tknImport
%token  tknIf tknIfDef tknIfNDef tknElse tknElIf tknEndIf
%token  tknFor tknWhile tknDo tknSwitch tknCase tknDefault
%token  tknReturn

%token  tknBlankLine

%type                  strlit
%type                  optapidecor apidecor apidecortokensq
%type                  identifier optidentifier numbertype typeidentifier varidentifier optname id name designatedname operfuncname funcname
%type                  templidentifier templqualifiedid
%type                  doccommentstr optdoccommentstr
%type                  rshift
%type                  macrocall
%type            stmt
%type         opttypemodifier typemodifier
%type              enumdefn enumfwddecl enumdefnstmt
%type             enumitem
%type         enumitemlist
%type           fwddecl
%type           vartype
%type            vardecl varinit vardeclstmt
%type       varassign optvarassign
%type         param
%type                  funcobjstr /* Identify funcobjstr as str, at least for time being */
%type          templatearg templatearglist /* For time being. We may need to make it more robust in future. */
%type             asmblock
%type        vardecllist vardeclliststmt
%type            paramlist lambdaparams
%type          typedefname typedefnamestmt
%type          typedeflist typedefliststmt
%type   usingnamespacedecl
%type       namespacealias
%type            usingdecl
%type        stmtlist optstmtlist progunit classdefn namespacedefn classdefnstmt externcblock block
%type    templatespecifier templateparamlist
%type        templateparam
%type        doccomment
%type           expr exprstmt optexpr lambdacapture captureallbyref captureallbyval exprorlist optexprorlist desinatedinitialization
%type             exprlist optexprlist
%type           objcarg objcarglist
%type            lambda
%type              ifblock;
%type           whileblock;
%type         dowhileblock;
%type             forblock;
%type        forrangeblock;
%type          switchstmt;
%type           caselist;
%type             tryblock;
%type           catchblock;
%type    functionpointer functionptrtype funcpointerdecl funcptrtypedef funcptrortype funcobj
%type         funcdecldata
%type           funcdecl funcdeclstmt funcdefn
%type           ctordecl ctordeclstmt ctordefn
%type           dtordecl dtordeclstmt dtordefn
%type     typeconverter typeconverterstmt
%type          meminitlist
%type              meminit
%type         classspecifier
%type                 varattrib exptype optfuncattrib functype optfunctype optfinal
%type          optinheritlist
%type          optinherittype
%type        protlevel
%type      entityaccessspecifier
%type       identifierlist
%type        functhrowspec optfuncthrowspec
%type     attribs optattribs attribspecifier attribspecifiers optattribspecifiers
%type           define
%type            undef
%type          include
%type           import
%type               hashif
%type            hasherror
%type          hashwarning
%type           pragma
%type           returnstmt
%type            throwstmt
%type             gotostmt
%type            preprocessor

%type                 blob

%type                label

// precedence as mentioned at https://en.cppreference.com/w/cpp/language/operator_precedence
%left COMMA
// &=, ^=, |=, =, *=, /=, %=, +=, -=, =, throw, a?b:c
%right tknAndEq tknXorEq tknOrEq tknLShiftEq tknRShiftEq tknMulEq tknDivEq tknPerEq tknPlusEq tknMinusEq '=' tknThrow '?' TERNARYCOND tknReturn
%left tknOr
%left tknAnd
%left '|'
%left '^'
%left '&'
%left tknCmpEq tknNotEq // ==, !=
// tknLT and tknGT are used instead of '' because otherwise parsing template and template args is very difficult.
%left tknLT tknGT tknLessEq tknGreaterEq
%left tkn3WayCmp       // 
%left  tknLShift tknRShift RSHIFT

%left   '+' '-'
%left   '*' '/' '%'
%left tknArrowStar
%right PREINCR PREDECR UNARYMINUS '!' '~' CSTYLECAST DEREF ADDRESSOF tknSizeOf tknNew tknDelete
%left POSTINCR POSTDECR FUNCTIONALCAST FUNCCALL SUBSCRIPT '.' tknArrow
%left tknScopeResOp
%right GLOBAL
%left TEMPLATE

/*
These are required to remove following ambiguity in the grammer.
Consider the following example:
  x * y;
Now it can be parsed as:
  (1) y is a pointer to type-x.
  (2) Or, the expression is multiplication of x and y.
Same ambiguity exists for:
  x * y = z;
  x & y;
  x & y = z;
  x && y;
  x && y = z;
PTRDECL and REFDECL solve this problem by giving variable declaration higher precedence.
*/
%left PTRDECL REFDECL

/*
These are required to remove following ambiguity in the grammer.
Consider the following example:
  class A
  {
    A();  // ctor declaration
    ~A(); // dtor declaration
  };

Now A() can be parsed in two different ways:
  (1) As a constructor declaration.
  (2) Or, as a function call.
Also, ~A() can be parsed as:
  (1) As a destructor declaration.
  (2) Or, as an expression where bit toggle operation is done on a return value of function call.
CTORDECL and DTORDECL solve this problem by giving constructor and destructor declarations higher precedence.
*/
%left CTORDECL DTORDECL


%%

/* A program unit is a source file, be it header file or implementation file */
progunit
  : optstmtlist [ZZLOG;] {
    gProgUnit = $$ = $1;
    if (gProgUnit)
      gProgUnit->compoundType(CppCompoundType::FILE);
    }
  ;

optstmtlist
  : [ZZLOG;] {
    $$ = nullptr;
  }
  | stmtlist [ZZLOG;] {
    $$ = $1;
  }
  ;

stmtlist
  : stmt [ZZLOG;] {
    $$ = new cppast::CppCompound();
    if ($1)
    {
      $$->add(Ptr($1));
    } // Avoid 'comment-btyacc-constructs.sh' to act on this
  }
  | stmtlist stmt [ZZLOG;] {
    $$ = ($1 == 0) ? new cppast::CppCompound() : $1;
    if ($2)
    {
        $$->add(Ptr($2));
    } // Avoid 'comment-btyacc-constructs.sh' to act on this
  }
  ;

stmt
  : vardeclstmt         [ZZLOG;] { $$ = $1; }
  | vardeclliststmt     [ZZLOG;] { $$ = $1; }
  | enumdefnstmt        [ZZLOG;] { $$ = $1; }
  | enumfwddecl         [ZZLOG;] { $$ = $1; }
  | typedefnamestmt     [ZZLOG;] { $$ = $1; }
  | typedefliststmt     [ZZLOG;] { $$ = $1; }
  | classdefnstmt       [ZZLOG;] { $$ = $1; }
  | namespacedefn       [ZZLOG;] { $$ = $1; }
  | fwddecl             [ZZLOG;] { $$ = $1; }
  | doccomment          [ZZLOG;] { $$ = $1; }
  | exprstmt            [ZZLOG;] { $$ = $1; }
  | ifblock             [ZZLOG;] { $$ = $1; }
  | whileblock          [ZZLOG;] { $$ = $1; }
  | dowhileblock        [ZZLOG;] { $$ = $1; }
  | forblock            [ZZLOG;] { $$ = $1; }
  | forrangeblock       [ZZLOG;] { $$ = $1; }
  | funcpointerdecl     [ZZLOG;] { $$ = $1; }
  | funcdeclstmt        [ZZLOG;] { $$ = $1; }
  | funcdefn            [ZZLOG;] { $$ = $1; }
  | ctordeclstmt        [ZZLOG;] { $$ = $1; }
  | ctordefn            [ZZLOG;] { $$ = $1; }
  | dtordeclstmt        [ZZLOG;] { $$ = $1; }
  | dtordefn            [ZZLOG;] { $$ = $1; }
  | typeconverterstmt   [ZZLOG;] { $$ = $1; }
  | externcblock        [ZZLOG;] { $$ = $1; }
  | funcptrtypedef      [ZZLOG;] { $$ = $1; }
  | preprocessor        [ZZLOG;] { $$ = $1; }
  | block               [ZZLOG;] { $$ = $1; }
  | switchstmt          [ZZLOG;] { $$ = $1; }
  | tryblock            [ZZLOG;] { $$ = $1; }
  | usingdecl           [ZZLOG;] { $$ = $1; }
  | usingnamespacedecl  [ZZLOG;] { $$ = $1; }
  | namespacealias      [ZZLOG;] { $$ = $1; }
  | macrocall           [ZZLOG;] { $$ = new cppast::CppMacroCall($1); }
  | macrocall ';'       [ZZLOG;] { $$ = new cppast::CppMacroCall(MergeCppToken($1, $2)); }
  | apidecortokensq macrocall [ZZLOG;] { $$ = new cppast::CppMacroCall(MergeCppToken($1, $2)); }
  | ';'                 [ZZLOG;] { $$ = nullptr; }  /* blank statement */
  | asmblock            [ZZLOG;] { $$ = $1; }
  | blob                [ZZLOG;] { $$ = $1; }
  | label               [ZZLOG;] { $$ = $1; }
  | returnstmt          [ZZLOG;] { $$ = $1; }
  | throwstmt           [ZZLOG;] { $$ = $1; }
  | gotostmt            [ZZLOG;] { $$ = $1; }
  | entityaccessspecifier     [ZZLOG;] { $$ = $1; }
  ;

label
  : name ':'            [ZZLOG;] { $$ = new cppast::CppLabel($1); }
  ;

preprocessor
  : define              [ZZLOG;] { $$ = $1; }
  | undef               [ZZLOG;] { $$ = $1; }
  | include             [ZZLOG;] { $$ = $1; }
  | import              [ZZLOG;] { $$ = $1; }
  | hashif              [ZZLOG;] { $$ = $1; }
  | hasherror           [ZZLOG;] { $$ = $1; }
  | hashwarning         [ZZLOG;] { $$ = $1; }
  | pragma              [ZZLOG;] { $$ = $1; }
  ;

asmblock
  : tknAsm              [ZZLOG;] { $$ = new cppast::CppAsmBlock($1); }
  ;

macrocall
  : tknMacro [ZZLOG; $$ = $1;] {}
  | macrocall '(' ')' [ZZLOG; $$ = MergeCppToken($1, $3); ] {}
  | macrocall '(' expr ')' [
    ZZLOG;
    $$ = MergeCppToken($1, $4);
    delete $3;
  ] {}
  ;

switchstmt
  : tknSwitch '(' expr ')' '{' caselist '}' [ZZLOG;] {
    $$ = new cppast::CppSwitchBlock(Ptr($3), std::move(*Ptr($6)));
  }
  ;

caselist
  : [ZZLOG;] {
    $$ = new std::vector;
  }
  | caselist tknCase expr ':' optstmtlist [ZZLOG;] {
    $$ = $1;
    $$->emplace_back(Ptr($3), Ptr($5));
  }
  | caselist tknDefault ':' optstmtlist [ZZLOG;] {
    $$ = $1;
    $$->emplace_back(nullptr, Ptr($4));
  }
  | doccommentstr caselist [ZZLOG;] { $$ = $2; }
  | caselist doccommentstr [ZZLOG;] { $$ = $1; }
  ;

block
  : '{' optstmtlist '}' [ZZLOG;] {
    $$ = $2;
    if ($$ == nullptr)
      $$ = new cppast::CppCompound(CppCompoundType::BLOCK);
    else
      $$->compoundType(CppCompoundType::BLOCK);
  }
  | doccomment block [ZZLOG;] {
    $$ = $2;
  }
  ;

ifblock
  : tknIf '(' expr ')' stmt [ZZLOG;] {
    $$ = new cppast::CppIfBlock(Ptr($3), Ptr($5));
  }
  | tknIf '(' expr ')' stmt tknElse stmt [ZZLOG;] {
    $$ = new cppast::CppIfBlock(Ptr($3), Ptr($5), Ptr($7));
  }
  | tknIf '(' varinit ')' stmt [ZZLOG;] {
    $$ = new cppast::CppIfBlock(Ptr($3), Ptr($5));
  }
  | tknIf '(' varinit ')' stmt tknElse stmt [ZZLOG;] {
    $$ = new cppast::CppIfBlock(Ptr($3), Ptr($5), Ptr($7));
  }
  /* TODO: Add support for else-if: compare the if.cpp file and its output by e2e test. */
  ;

whileblock
  : tknWhile '(' expr ')' stmt [ZZLOG;] {
    $$ = new cppast::CppWhileBlock(Ptr($3), Ptr($5));
  }
  | tknWhile '(' varinit ')' stmt [ZZLOG;] {
    $$ = new cppast::CppWhileBlock(Ptr($3), Ptr($5));
  }
  ;

dowhileblock
  : tknDo stmt tknWhile '(' expr ')' [ZZLOG;] {
    $$ = new cppast::CppDoWhileBlock(Ptr($5), Ptr($2));
  }
  ;

forblock
  : tknFor '(' optexprorlist ';' optexprorlist ';' optexprorlist ')' stmt [ZZLOG;] {
    $$ = new cppast::CppForBlock(Ptr($3), Ptr($5), Ptr($7), Ptr($9));
  }
  | tknFor '(' varinit ';' optexprorlist ';' optexprorlist ')' stmt [ZZLOG;] {
    $$ = new cppast::CppForBlock(Ptr($3), Ptr($5), Ptr($7), Ptr($9));
  }
  | tknFor '(' vardecllist ';' optexprorlist ';' optexprorlist ')' stmt [ZZLOG;] {
    $$ = new cppast::CppForBlock(Ptr($3), Ptr($5), Ptr($7), Ptr($9));
  }
  ;

forrangeblock
  : tknFor '(' vardecl ':' expr ')' stmt [ZZLOG;] {
    $$ = new cppast::CppRangeForBlock(Ptr($3), Ptr($5), Ptr($7));
  }
  ;

tryblock
  : tknTry block catchblock [ZZLOG;] {
    $$ = new cppast::CppTryBlock(Ptr($2), Ptr($3));
  }
  | tryblock catchblock [ZZLOG;] {
    $$ = $1;
    $$->addCatchBlock(Ptr($2));
  }
  ;

catchblock
  : tknCatch '(' vartype optname ')' block [ZZLOG;] {
    $$ = new cppast::CppCatchBlock{Ptr($3), $4, Ptr($6)};
  }
  ;

optexpr
  : {
    $$ = nullptr;
  }
  | expr [ZZLOG;] {
    $$ = $1;
  }
  /* | exprlist [ZZLOG;] {
    $$ = $1;
  } */
  ;

define
  : tknPreProHash tknDefine name name [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::RENAME, $3, $4);
  }
  | tknPreProHash tknDefine name [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::RENAME, $3);
  }
  | tknPreProHash tknDefine name tknNumber [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::NUMBER, $3, $4);
  }
  | tknPreProHash tknDefine name tknStrLit [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::STRING, $3, $4);
  }
  | tknPreProHash tknDefine name tknCharLit [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::CHARACTER, $3, $4);
  }
  | tknPreProHash tknDefine name tknPreProDef [ZZLOG;] {
    $$ = new cppast::CppPreprocessorDefine(cppast::CppPreprocessorDefineType::COMPLEX_DEFN, $3, $4);
  }
  ;

undef
  : tknPreProHash tknUndef name                 [ZZLOG;]  { $$ = new cppast::CppPreprocessorUndef($3); }
  ;

include
  : tknPreProHash tknInclude tknStrLit          [ZZLOG;]  { $$ = new cppast::CppPreprocessorInclude($3); }
  | tknPreProHash tknInclude tknStdHdrInclude   [ZZLOG;]  { $$ = new cppast::CppPreprocessorInclude($3); }
  ;

import
  : tknPreProHash tknImport tknStrLit           [ZZLOG;]  { $$ = new cppast::CppPreprocessorImport($3); }
  | tknPreProHash tknImport tknStdHdrInclude    [ZZLOG;]  { $$ = new cppast::CppPreprocessorImport($3); }
  ;

hashif
  : tknPreProHash tknIf tknPreProDef            [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::IF,      $3); }
  | tknPreProHash tknIfDef name                 [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::IFDEF,   $3); }
  | tknPreProHash tknIfNDef name                [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::IFNDEF,  $3); }
  | tknPreProHash tknIfNDef tknApiDecor         [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::IFNDEF,  $3); }
  | tknPreProHash tknElse                       [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::ELSE       ); }
  | tknPreProHash tknElIf  tknPreProDef         [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::ELIF,    $3); }
  | tknPreProHash tknEndIf                      [ZZLOG;]  { $$ = new cppast::CppPreprocessorConditional(PreprocessorConditionalType::ENDIF      ); }
  ;

hasherror
  : tknPreProHash tknHashError                  [ZZLOG;]  { $$ = new cppast::CppPreprocessorError($2); }
  | tknPreProHash tknHashError strlit           [ZZLOG;]  { $$ = new cppast::CppPreprocessorError(MergeCppToken($2, $3)); }
  ;

hashwarning
  : tknPreProHash tknHashWarning                [ZZLOG;]  { $$ = new cppast::CppPreprocessorWarning($2); }
  | tknPreProHash tknHashWarning strlit         [ZZLOG;]  { $$ = new cppast::CppPreprocessorWarning(MergeCppToken($2, $3)); }
  ;

pragma
  : tknPreProHash tknPragma tknPreProDef        [ZZLOG;]  { $$ = new cppast::CppPreprocessorPragma($3); }
  ;

doccomment
  : doccommentstr                               [ZZLOG;]  { $$ = new cppast::CppDocumentationComment((std::string) $1); }
  ;

optdoccommentstr
  :               [ZZLOG;] { $$ = CppToken{nullptr, 0}; }
  | doccommentstr [ZZLOG;] { $$ = $1; }
  ;

doccommentstr
  : tknFreeStandingBlockComment                          [ZZLOG;]  { $$ = $1; }
  | tknFreeStandingLineComment                           [ZZLOG;]  { $$ = $1; }
  | doccommentstr tknFreeStandingBlockComment            [ZZLOG;]  { $$ = MergeCppToken($1, $2); }
  | doccommentstr tknFreeStandingLineComment             [ZZLOG;]  { $$ = MergeCppToken($1, $2); }
  ;

identifier
  : name                                          [ZZLOG; $$ = $1; ] {}
  | identifier tknScopeResOp identifier           [ZZLOG; $$ = MergeCppToken($1, $3); ] {}
  | id                                            [ZZLOG; $$ = $1; ] {}
  | templidentifier                               [ZZLOG; $$ = $1; ] {}
  | tknOverride                                   [ZZLOG; $$ = $1;] { /* override is not a reserved keyword */ }
  | identifier tknEllipsis                        [ZZLOG; $$ = MergeCppToken($1, $2); ] {}
  | macrocall                                     [ZZLOG; $$ = $1; ] {}
  | templqualifiedid                              [ZZLOG; $$ = $1; ] {}
  ;

numbertype
  : tknInteger                            [ZZLOG;] { $$ = $1; }
  | tknFloat                              [ZZLOG;] { $$ = $1; }
  | tknDouble                             [ZZLOG;] { $$ = $1; }
  | tknChar                               [ZZLOG;] { $$ = $1; }
  | tknNumSignSpec                        [ZZLOG;] { $$ = $1; }
  | tknNumSignSpec numbertype             [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  ;

typeidentifier
  : identifier                            [ZZLOG;] { $$ = $1; }
  | tknScopeResOp identifier %prec GLOBAL [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | typeidentifier tknScopeResOp typeidentifier [ZZLOG;] { $$ = MergeCppToken($1, $3); }
  | numbertype                            [ZZLOG;] { $$ = $1; }
  | typeidentifier '[' ']'                [ZZLOG;] { $$ = MergeCppToken($1, $3); }
  | tknAuto                               [ZZLOG;] { $$ = $1; }
  | tknVoid                               [ZZLOG;] { $$ = $1; }
  | tknEnum  identifier                   [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknTypename identifier  [
                    if (gTemplateParamStart == $1.sz)
                      ZZERROR;
                    else
                      ZZLOG;
                  ]                                       [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknEllipsis                           [ZZLOG;] { $$ = $1; }
  | tknTypename tknEllipsis               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknClass tknEllipsis                  [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | typeidentifier tknEllipsis            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknDecltype '(' expr ')'              [ZZLOG;] { $$ = MergeCppToken($1, $4); delete $3; }
  ;

templidentifier
  : identifier tknLT templatearglist tknGT    [ZZLOG; $$ = MergeCppToken($1, $4); ] {}
// The following rule is needed to parse an ambiguous input as template identifier,
// see the test "vardecl-or-expr-ambiguity".
  | identifier tknLT expr tknNotEq expr tknGT [ZZLOG; $$ = MergeCppToken($1, $6); ] {}
// The following rule is needed to parse a template identifier which otherwise fails to parse
// because of higher precedence of tknLT and tknGT,
// see the test "C x;".
  | identifier tknLT templatearglist ',' expr tknNotEq expr tknGT [ZZLOG; $$ = MergeCppToken($1, $8); ] {}
  ;

templqualifiedid
  : tknTemplate templidentifier               [ZZLOG; $$ = MergeCppToken($1, $2); ] {}
  ;

name
  : tknName  [ZZLOG; $$ = $1;] {}
  ;

designatedname
  : '.' name [ZZLOG;] {$$ = MergeCppToken($1, $2);}
  ;

id
  : tknID  [ZZLOG; $$ = $1; ] {}
  ;

optname
  :         [ZZLOG;] { $$ = MakeCppToken(nullptr, nullptr); }
  | name    [ZZLOG;] { $$ = $1; }

optidentifier
  :             [ZZLOG;] { $$ = MakeCppToken(nullptr, nullptr); }
  | identifier  [ZZLOG;] { $$ = $1; }
  ;

enumitem
  : name            [ZZLOG;]   { $$ = new cppast::CppEnumItem($1); }
  | name '=' expr   [ZZLOG;]   { $$ = new cppast::CppEnumItem($1, Ptr($3)); }
  | doccomment      [ZZLOG;]   { $$ = new cppast::CppEnumItem(Ptr($1)); }
  | preprocessor    [ZZLOG;]   { $$ = new cppast::CppEnumItem(Ptr($1)); }
  | macrocall       [ZZLOG;]   { $$ = new cppast::CppEnumItem(Ptr(new cppast::CppMacroCall($1))); }
  | blob            [ZZLOG;]   { $$ = new cppast::CppEnumItem(Ptr($1)); }
  ;

blob
  : tknBlob      [ZZLOG;]   { $$ = new cppast::CppBlob($1); }
  ;

enumitemlist
  :                           [ZZLOG;] { $$ = 0; }
  | enumitemlist enumitem [ZZLOG;] {
    $$ = $1 ? $1 : new std::list;
    $$->push_back(Obj($2));
  }
  | enumitemlist ',' enumitem [ZZLOG;] {
    $$ = $1 ? $1 : new std::list;
    $$->push_back(Obj($3));
  }
  | enumitemlist ',' [ZZLOG;] {
    $$ = $1;
  }
  ;

enumdefn
  : tknEnum optname '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum($2, Obj($4));
  }
  | tknEnum optapidecor name ':' typeidentifier '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum($3, Obj($7), false, $5);
  };
  | tknEnum ':' typeidentifier '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum("", Obj($5), false, $3);
  };
  | tknEnum optapidecor name '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum($3, Obj($5), false);
  };
  | tknEnum tknClass optapidecor name ':' typeidentifier '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum($4, Obj($8), true, $6);
  }
  | tknEnum tknClass optapidecor name '{' enumitemlist '}' [ZZVALID;] {
    $$ = new cppast::CppEnum($4, Obj($6), true);
  }
  | tknTypedef tknEnum optapidecor optname '{' enumitemlist '}' name [ZZVALID;] {
    $$ = new cppast::CppEnum($8, Obj($6));
  }
  ;

enumdefnstmt
  : enumdefn ';' [ZZLOG;] { $$ = $1; }
  ;

enumfwddecl
  : tknEnum name ':' typeidentifier ';' [ZZVALID;] {
    $$ = new cppast::CppEnum($2, {}, false, $4);
  }
  | tknEnum tknClass name ':' typeidentifier ';' [ZZVALID;] {
    $$ = new cppast::CppEnum($3, {}, true, $5);
  }
  | tknEnum tknClass name ';' [ZZVALID;] {
    $$ = new cppast::CppEnum($3, {}, true);
  }
  ;

funcptrtypedef
  : tknTypedef functionpointer ';' [ZZVALID;] {
    $2->addAttr(TYPEDEF);
    $$ = $2;
  }

typedefnamestmt
  : typedefname ';'     [ZZVALID;] { $$ = $1; }
  ;

typedefliststmt
  : typedeflist ';'     [ZZVALID;] { $$ = $1; }
  ;

typedeflist
  : tknTypedef vardecllist  [ZZLOG;] { $$ = new cppast::CppTypedefList(Ptr($2)); }
  ;

typedefname
  : tknTypedef vardecl      [ZZLOG;] { $$ = new cppast::CppTypedefName(Ptr($2)); }
  ;

usingdecl
  : tknUsing name '=' vartype ';' [ZZLOG;] {
    $$ = new cppast::CppUsingDecl($2, Ptr($4));
  }
  | tknUsing name '=' functionptrtype ';' [ZZLOG;] {
    $$ = new cppast::CppUsingDecl($2, Ptr($4));
  }
  | tknUsing name '=' funcobj ';' [ZZLOG;] {
    $$ = new cppast::CppUsingDecl($2, Ptr($4));
  }
  | tknUsing name '=' classdefn ';' [ZZLOG;] {
    $$ = new cppast::CppUsingDecl($2, Ptr($4));
  }
  | templatespecifier usingdecl [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | tknUsing identifier ';' [ZZLOG;] {
    $$ = new cppast::CppUsingDecl($2);
  }
  ;

namespacealias
  : tknNamespace name '=' identifier ';' [ZZLOG;] {
    $$ = new cppast::CppNamespaceAlias($2, $4);
  }
  ;

usingnamespacedecl
  : tknUsing tknNamespace identifier ';' [ZZLOG;] {
    $$ = new cppast::CppUsingNamespaceDecl($3);
  }
  ;

vardeclliststmt
  : vardecllist ';' [ZZVALID;] { $$ = $1; }
  | exptype vardecllist ';' [ZZVALID;] { $$ = $2; }
  ;

vardeclstmt
  : vardecl ';'                   [ZZVALID;] { $$ = $1; }
  | varinit ';'                   [ZZVALID;] { $$ = $1; }
  | apidecor vardeclstmt          [ZZVALID;] { $$ = $2; $$->apidecor($1); }
  | exptype vardeclstmt           [ZZVALID;] { $$ = $2; $$->addAttr($1); }
  | varattrib vardeclstmt         [ZZVALID;] { $$ = $2; $$->addAttr($1); }
  ;

vardecllist
  : optfunctype varinit ',' opttypemodifier name optvarassign [ZZLOG;] {
    $2->addAttr($1);
    $$ = new cppast::CppVarList($2, CppVarDeclInList($4, VarDecl($5, $6)));
  }
  | optfunctype vardecl ',' opttypemodifier name optvarassign [ZZLOG;] {
    $2->addAttr($1);
    $$ = new cppast::CppVarList($2, CppVarDeclInList($4, VarDecl($5, $6)));
  }
  | optfunctype vardecl ',' opttypemodifier name '[' expr ']' [ZZLOG;] {
    $2->addAttr($1);
    CppVarDecl var2($5);
    var2.addArraySize($7);
    $$ = new cppast::CppVarList($2, CppVarDeclInList($4, std::move(var2)));
  }
  | vardecllist ',' opttypemodifier name '[' expr ']' [ZZLOG;] {
    $$ = $1;
    CppVarDecl var2($4);
    var2.addArraySize($6);
    $$->addVarDecl(CppVarDeclInList($3, std::move(var2)));
  }
  | optfunctype vardecl ',' opttypemodifier name ':' expr [ZZLOG;] {
    $2->addAttr($1);
    $$ = new cppast::CppVarList($2, CppVarDeclInList($4, CppVarDecl{$5}));
    /* TODO: Use optvarassign as well */
  }
  | vardecllist ',' opttypemodifier name optvarassign [ZZLOG;] {
    $$ = $1;
    $$->addVarDecl(CppVarDeclInList($3, VarDecl($4, $5)));
  }
  | vardecllist ',' opttypemodifier name optvarassign ':' expr [ZZLOG;] {
    $$ = $1;
    $$->addVarDecl(CppVarDeclInList($3, VarDecl($4, $5)));
    /* TODO: Use optvarassign as well */
  }
  ;

varinit
  : vardecl '(' typeidentifier '*' name      [gParamModPos = $4.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' typeidentifier '*' '*' name  [gParamModPos = $4.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' typeidentifier '*' '&' name  [gParamModPos = $4.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' typeidentifier '&' name      [gParamModPos = $4.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' typeidentifier tknAnd name   [gParamModPos = $4.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' typeidentifier ')'           [gParamModPos = $3.sz; ZZERROR;] { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl '(' ')'                          [ZZERROR;]                       { /*FuncDeclHack*/ $$ = nullptr; }
  | vardecl varassign [ZZLOG;] {
    $$ = $1;
    $$->initialize(Obj($2));
  }
  | tknConstExpr varinit [ZZLOG;] {
    $$ = $2;
    $$->addAttr(CONST_EXPR);
  }
  ;

varassign
  : '=' expr [ZZLOG;] {
                    $$ = VarInitInfo($2);
  }
  | '(' exprlist ')' [ZZLOG;] {
                    $$ = VarInitInfo($2, CppConstructorCallStyle::USING_PARENTHESES);
  }
  | '{' optexprlist '}' [ZZLOG;] {
                    $$ = VarInitInfo($2, CppConstructorCallStyle::USING_BRACES);
  }
  ;

optvarassign
  :                     [ZZLOG;]  { $$ = nullptr; }
  | varassign           [ZZLOG;]  { $$ = $1; }
  ;

vardecl
  : vartype varidentifier       [ZZLOG;]         {
    $$ = new cppast::CppVar($1, $2.toString());
  }
  | vartype apidecor varidentifier       [ZZLOG;]         {
    $$ = new cppast::CppVar($1, $3.toString());
    $$->apidecor($2);
  }
  | functionpointer [ZZLOG;] {
    $$ = new cppast::CppVar($1, CppTypeModifier());
  }
  | vardecl '[' expr ']' [ZZLOG;] {
    $$ = $1;
    $$->addArraySize($3);
  }
  | vardecl '[' ']' [ZZLOG;] {
    $$ = $1;
    $$->addArraySize(nullptr);
  }
  | vardecl ':' expr [ZZLOG;] {
    $$ = $1;
    $$->bitField(Ptr($3));
  }
  | templatespecifier vardecl [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | varattrib vardecl [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  ;

vartype
  : attribspecifiers typeidentifier opttypemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType($2, $3);
    $$->attribSpecifierSequence(Obj($1));
  }
  | typeidentifier opttypemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType($1, $2);
  }
  | tknClass identifier opttypemodifier [
    if (gTemplateParamStart == $1.sz)
      ZZERROR;
    else
      ZZLOG;
  ] {
    $$ = new cppast::CppVarType(MergeCppToken($1, $2), $3);
  }
  | tknClass optapidecor identifier opttypemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType(MergeCppToken($1, $3), $4);
  }
  | tknStruct optapidecor identifier opttypemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType(MergeCppToken($1, $3), $4);
  }
  | tknUnion identifier opttypemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType(MergeCppToken($1, $2), $3);
  }
  | functionptrtype [ZZLOG;] {
    $$ = new cppast::CppVarType($1, CppTypeModifier());
  }
  | classdefn [ZZLOG;] {
    $$ = new cppast::CppVarType($1, CppTypeModifier());
  }
  | classdefn typemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType($1, $2);
  }
  | enumdefn [ZZLOG;] {
    $$ = new cppast::CppVarType($1, CppTypeModifier());
  }
  | enumdefn typemodifier [ZZLOG;] {
    $$ = new cppast::CppVarType($1, $2);
  }
  | varattrib vartype [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  | vartype tknEllipsis [ZZLOG;] {
    $$ = $1;
    $$->parameterPack(true);
  }
  | typeidentifier typeidentifier tknScopeResOp typemodifier [ZZLOG;] {
    // reference to member declrations. E.g.:
    // int GrCCStrokeGeometry::InstanceTallies::* InstanceType
    $$ = new cppast::CppVarType(MergeCppToken($1, $3), $4);
  }
  ;

varidentifier
  : identifier          [ZZLOG;]  { $$ = $1; }
  | tknFinal            [ZZLOG;]  { $$ = $1; /* final is not a reserved keyword */ }
  | '(' '&' name ')'      [ZZLOG;]  { $$ = MergeCppToken($1, $4); }
  | '(' '*' name ')'      [ZZLOG;]  { $$ = MergeCppToken($1, $4); }
  | '(' '*' '*' name ')'  [ZZLOG;]  { $$ = MergeCppToken($1, $5); }
  ;

opttypemodifier
  : [ZZLOG;] { $$ = CppTypeModifier(); }
  | typemodifier { $$ = $1; }
  | doccomment opttypemodifier { $$ = $2; }
  ;

typemodifier
  : tknConst [ZZLOG;] {
    $$ = CppTypeModifier();
    $$.constBits_ |= (1 addAttr(CONST);
  }
  | apidecor typeconverter [ZZLOG;] {
    $$ = $2;
    $$->decor1($1);
  }
  | templatespecifier typeconverter [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  ;

typeconverterstmt
  : typeconverter ';' [ZZVALID;] {
    $$ = $1;
  }
  | typeconverter block [ZZVALID;] {
    $$ = $1;
    $$->defn(Ptr($2));
  }
  ;

funcdeclstmt
  : funcdecl ';' [ZZVALID;] { $$ = $1; }
  ;

funcdefn
  : funcdecl block [ZZVALID;] {
    $$ = $1;
    $$->defn(Ptr($2 ? $2 : new cppast::CppCompound(CppCompoundType::BLOCK)));
  }
  ;

lambda
  : '[' lambdacapture ']' lambdaparams block {
    $$ = new cppast::CppLambda(Ptr($2), Obj($4), Ptr($5));
  }
  | '[' lambdacapture ']' lambdaparams tknArrow vartype block {
    $$ = new cppast::CppLambda(Ptr($2), Obj($4), Ptr($7), Ptr($6));
  }
  ;

lambdaparams
  :                   [ZZLOG;] { $$ = nullptr; }
  | '(' paramlist ')' [ZZLOG;] { $$ = $2; }
  ;

funcptrortype
  : functype vartype '(' optapidecor identifier tknScopeResOp '*' optname ')' '(' paramlist ')' [ZZVALID;] {
    $$ = new cppast::CppFunctionPointer($8, Ptr($2), Obj($11), $1, MergeCppToken($5, $6));
    $$->decor2($4);
  }
  | vartype '(' optapidecor identifier tknScopeResOp '*' optname ')' '(' paramlist ')' [ZZVALID;] {
    $$ = new cppast::CppFunctionPointer($7, Ptr($1), Obj($10), 0, MergeCppToken($4, $5));
    $$->decor2($3);
  }
  | functype vartype '(' optapidecor '*' optname ')' '(' paramlist ')' [ZZVALID;] {
    $$ = new cppast::CppFunctionPointer($6, Ptr($2), Obj($9), $1);
    $$->decor2($4);
  }
  | vartype '(' optapidecor '*' optname ')' '(' paramlist ')' [ZZVALID;] {
    $$ = new cppast::CppFunctionPointer($5, Ptr($1), Obj($8), 0);
    $$->decor2($3);
  }
  | vartype '(' '*'  apidecor optname ')' '(' paramlist ')' [ZZVALID;] {
    $$ = new cppast::CppFunctionPointer($5, Ptr($1), Obj($8), 0);
    $$->decor2($4);
  }
  | apidecor funcptrortype [ZZVALID;] {
    $$ = $2;
    $$->decor1($1);
  }
  | funcptrortype optfuncattrib [ZZVALID;] {
    $$ = $1;
    $$->addAttr($2);
  }
  ;

functionpointer
  : funcptrortype
  [
    if ($1->name().empty() == true) {
      ZZERROR;
    }
  ]
  ;

functionptrtype
  : funcptrortype
  [
    if ($1->name().empty() == false) {
      ZZERROR;
    }
  ]
  ;

funcobj
  : vartype optapidecor '(' paramlist ')' [ZZLOG;] {
    $$ = new cppast::CppFunctionPointer("", Ptr($1), Obj($4), 0);
  }
  ;

funcpointerdecl
  : functionpointer ';' [ZZVALID;] { $$ = $1; }
  ;

funcdecldata
  : funcname '(' paramlist ')' [ZZLOG;] {
    $$ = CppFunctionData{$1, $3, 0};
  }
  | funcname '(' paramlist ')' optfuncattrib [ZZLOG;] {
    $$ = CppFunctionData{$1, $3, $5};
  }
  ;

funcdecl
  : vartype apidecor funcdecldata [ZZVALID;] {
    $$ = new cppast::CppFunction($3.funcName, Ptr($1), Obj($3.paramList), $3.funcAttr);
    $$->decor2($2);
  }
  | vartype funcdecldata [ZZVALID;] {
    $$ = new cppast::CppFunction($2.funcName, Ptr($1), Obj($2.paramList), $2.funcAttr);
  }
  | vartype tknConstExpr funcdecldata [ZZVALID;] {
    $$ = new cppast::CppFunction($3.funcName, Ptr($1), Obj($3.paramList), $3.funcAttr | CONST_EXPR);
  }
  | tknAuto funcdecldata tknArrow vartype [ZZVALID;] {
    $$ = new cppast::CppFunction($2.funcName, Ptr($4), Obj($2.paramList), $2.funcAttr | TRAILING_RETURN);
  }
  | tknAuto tknConstExpr funcdecldata tknArrow vartype [ZZVALID;] {
    $$ = new cppast::CppFunction($3.funcName, Ptr($5), Obj($3.paramList), $3.funcAttr | TRAILING_RETURN | CONST_EXPR);
  }
  | tknConstExpr funcdecl [ZZLOG;] {
    $$ = $2;
    $$->addAttr(CONST_EXPR);
  }
  | apidecor funcdecl [ZZLOG;] {
    $$ = $2;
    if (!$$->decor1().empty())
      $$->decor2($$->decor1());
    $$->decor1($1);
  }
  | templatespecifier funcdecl [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | functype funcdecl [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  | funcdecl '=' tknDelete [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DELETE);
  }
  | funcdecl '=' tknDefault [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DEFAULT);
  }
  | funcdecl functhrowspec [ZZLOG;] {
    $$ = $1;
    $$->throwSpec(Obj($2));
  }
  ;

funcobjstr
  : typeidentifier optapidecor '(' paramlist ')' [ZZLOG;] {
    delete $4;
    $$ = MergeCppToken($1, $5);
  }
  ;

funcname
  : operfuncname                          [ZZLOG;] { $$ = $1; }
  | typeidentifier                        [ZZLOG;] { $$ = $1; }
  | tknScopeResOp operfuncname            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  /* final can be a function name too, see SkOpSpan.h :| */
  | tknFinal                              [ZZLOG;] { $$ = $1; }
  ;

rshift
  : tknGT tknGT %prec RSHIFT [ if ($2.sz != ($1.sz + 1)) ZZERROR; ] { $$ = MergeCppToken($1, $2); }
  ;

operfuncname
  : tknOperator '+'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '-'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '*'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '/'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '%'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '^'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '&'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '|'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '~'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '!'               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '='               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknLT             [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknGT             [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknPlusEq         [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknMinusEq        [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknMulEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknDivEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknPerEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknXorEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknAndEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknOrEq           [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknLShift         [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknRShift         [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknLShiftEq       [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknRShiftEq       [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknCmpEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknNotEq          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknLessEq         [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknGreaterEq      [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tkn3WayCmp        [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknAnd            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknOr             [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknInc            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknDec            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator ','               [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknArrow          [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknArrowStar      [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator '(' ')'           [ZZLOG;] { $$ = MergeCppToken($1, $3); }
  | tknOperator '[' ']'           [ZZLOG;] { $$ = MergeCppToken($1, $3); }
  | tknOperator tknNew            [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknNew '[' ']'    [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  | tknOperator tknDelete         [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator tknDelete '[' ']' [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  | tknOperator typeidentifier    [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknOperator typeidentifier '*'       [ZZLOG;] { $$ = MergeCppToken($1, $3); }
  | identifier tknScopeResOp operfuncname [ZZLOG;] { $$ = MergeCppToken($1, $3); }

  /* see https://en.cppreference.com/w/cpp/language/user_literal */
  | tknOperator tknStrLit name      [ZZLOG;] { $$ = MergeCppToken($1, $3); }

  /* To fix something like:
      friend void* operator new(size_t, GrTAllocator*);
  */
  | operfuncname tknLT templatearglist tknGT [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  ;

paramlist
  : [ZZLOG;] {
    $$ = new std::vector;
  }
  | param [ZZLOG;] {
    $$ = new std::vector;
    $$->emplace_back($1);
  }
  | paramlist ',' param [ZZLOG;] {
    $1->emplace_back($3);
    $$ = $1;
  }
  ;

param
  : varinit                        [ZZLOG;] { $$ = $1; $1->addAttr(FUNC_PARAM); }
  | vartype '=' expr [ZZLOG;] {
    auto var = new cppast::CppVar($1, std::string());
    var->addAttr(FUNC_PARAM);
    var->initialize(Ptr($3));
    $$ = var;
  }
  | vardecl                         [ZZLOG;] { $$ = $1; $1->addAttr(FUNC_PARAM); }
  | vartype [ZZLOG;] {
    auto var = new cppast::CppVar($1, std::string());
    var->addAttr(FUNC_PARAM);
    $$ = var;
  }
  | funcptrortype                   [ZZLOG;] { $$ = $1; $1->addAttr(FUNC_PARAM); }
  | doccomment param                [ZZLOG;] { $$ = $2; }
  | vartype '[' expr ']' [ZZLOG;] {
    auto var = new cppast::CppVar($1, std::string());
    var->addAttr(FUNC_PARAM);
    var->addArraySize($3);
    $$ = var;
  }
  | vartype '[' ']' [ZZLOG;] {
    auto var = new cppast::CppVar($1, std::string());
    var->addAttr(FUNC_PARAM);
    var->addArraySize(nullptr);
    $$ = var;
  }
  ;

templatearg
  :                 [ZZLOG; $$ = nullptr;] { /*$$ = MakeCppToken(nullptr, nullptr);*/ }
  | vartype         [ZZLOG; $$ = nullptr;] { /*$$ = MergeCppToken($1, $2);*/ }
  | funcobjstr      [ZZLOG; $$ = nullptr;] { /*$$ = $1;*/ }
  | expr            [ZZLOG; $$ = nullptr;] {}
  ;

templatearglist
  : templatearg                      [ZZLOG; $$ = $1; ] {}
  | templatearglist ',' templatearg   [ZZLOG; $$ = $1;] { /*$$ = MergeCppToken($1, $3);*/ }
  | templatearglist ',' doccomment templatearg   [ZZLOG; $$ = $1;] { /*$$ = MergeCppToken($1, $3);*/ }
  ;

functype
  : exptype        [ZZLOG;] { $$ = $1; }
  | tknVirtual     [ZZLOG;] { $$ = VIRTUAL; }
  | tknExplicit    [ZZLOG;] { $$ = EXPLICIT; }
  | tknFriend      [ZZLOG;] { $$ = FRIEND; }
  | tknConstExpr   [ZZLOG;] { $$ = CONST_EXPR; }
  ;

optfunctype
  :                [ZZLOG;] { $$ = 0; }
  | functype       [ZZLOG;] { $$ = $1; }
  ;

optfuncattrib
  : tknConst                      [ZZLOG;] { $$ = CONST; }
  | tknOverride                   [ZZLOG;] { $$ = OVERRIDE; }
  | tknFinal                      [ZZLOG;] { $$ = FINAL; }
  | tknNoExcept                   [ZZLOG;] { $$ = NO_EXCEPT; }
  | '=' tknNumber  [if($2.len != 1 || $2.sz[0] != '0') YYABORT; else ZZVALID;] [ZZLOG;]
  {
    $$ = PURE_VIRTUAL;
  }
  | optfuncattrib tknConst        [ZZLOG;] { $$ = $1 | CONST; }
  | optfuncattrib tknOverride     [ZZLOG;] { $$ = $1 | OVERRIDE; }
  | optfuncattrib tknFinal        [ZZLOG;] { $$ = $1 | FINAL; }
  | optfuncattrib tknNoExcept     [ZZLOG;] { $$ = $1 | NO_EXCEPT; }
  | optfuncattrib '=' tknNumber   [if($3.len != 1 || $3.sz[0] != '0') YYABORT; else ZZVALID;]
  {
    $$ = $1 | PURE_VIRTUAL;
  }
  | tknMacro                      [ZZLOG;] { $$ = 0; } /* Ignore macros used as function attributes */
  ;

optfuncthrowspec
  :               [ZZLOG;] { $$ = new std::vector; }
  | functhrowspec [ZZLOG;] { $$ = $1; }
  ;

functhrowspec
  : tknThrow '(' identifierlist ')' [ZZLOG;] {
    $$ = $3 ? $3 : new std::vector;
  }
  ;

identifierlist
  :                               [ZZLOG;] { $$ = nullptr; }
  | identifier [ZZLOG;] {
    $$ = new std::vector;
    $$->push_back($1);
  }
  | identifierlist ',' identifier [ZZLOG;] {
    $$ = $1;
    $$->push_back($3);
  }
  ;

ctordeclstmt
  : ctordecl';'                 [ZZVALID;] { $$ = $1; }
  ;

ctordefn
  : ctordecl meminitlist block  [ZZVALID;]
  {
    $$ = $1;
    $$->memberInits(Obj($2));
    $$->defn(Ptr($3));
  }
  | name tknScopeResOp name [if($1 != $3) ZZERROR; else ZZVALID;]
                    '(' paramlist ')' optfuncthrowspec meminitlist block [ZZVALID;]
  {
    $$ = new cppast::CppConstructor(MergeCppToken($1, $3), Obj($6), Obj($9), 0);
    $$->defn(Ptr($10));
    $$->throwSpec(Obj($8));
  }
  | identifier tknScopeResOp name tknScopeResOp name [if($3 != $5) ZZERROR; else ZZVALID;]
                    '(' paramlist ')' optfuncthrowspec meminitlist block [ZZVALID;]
  {
    $$ = new cppast::CppConstructor(MergeCppToken($1, $5), Obj($8), Obj($11), 0);
    $$->defn(Ptr($12));
    $$->throwSpec(Obj($10));
  }
  | name tknLT templatearglist tknGT tknScopeResOp name [if($1 != $6) ZZERROR; else ZZVALID;]
                    '(' paramlist ')' optfuncthrowspec meminitlist block [ZZVALID;]
  {
    $$ = new cppast::CppConstructor(MergeCppToken($1, $6), Obj($9), Obj($12), 0);
    $$->defn(Ptr($13));
    $$->throwSpec(Obj($11));
  }
  | functype ctordefn [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  | templatespecifier ctordefn [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  ;

ctordecl
  : identifier '(' paramlist ')' %prec CTORDECL
  [
    if(gCompoundStack.empty())
      ZZERROR;
    if((gCompoundStack.top() != $1) && (ClassNameFromIdentifier(gCompoundStack.top()) != $1))
      ZZERROR;
    else
      ZZVALID;
  ]
  {
    $$ = new cppast::CppConstructor($1, Obj($3), {}, 0);
  }
  | functype ctordecl [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  | templatespecifier ctordecl [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | ctordecl '=' tknDelete [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DELETE);
  }
  | ctordecl '=' tknDefault [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DEFAULT);
  }
  | ctordecl functhrowspec [ZZLOG;] {
    $$ = $1;
    $$->throwSpec(Obj($2));
  }
  | ctordecl tknNoExcept [ZZLOG;] {
    $$ = $1;
    $$->addAttr(NO_EXCEPT);
  }
  | apidecor ctordecl [ZZLOG;] {
    $$ = $2;
    $$->decor1($1);
  }
  ;

meminitlist
  : [ZZLOG;] {
    $$ = new cppast::CppMemberInits;
  }
  | ':' meminit [ZZLOG;] {
    $$ = new cppast::CppMemberInits;
    $$->push_back(Obj($2));
  }
/*| ':' blob                 [ZZLOG;] { $$ = new CppMemberInits{{}, Ptr($2)}; } */
  | meminitlist ',' meminit [ZZLOG;] {
    $$ = $1;
    $$->push_back(Obj($3));
  }
  ;

meminit
  : identifier '(' exprlist ')'    [ZZLOG;] { $$ = MemberInit($1, Obj($3), cppast::CppConstructorCallStyle::USING_PARENTHESES); }
  | identifier '(' ')'             [ZZLOG;] { $$ = MemberInit($1, cppast::CppCallArgs(), cppast::CppConstructorCallStyle::USING_PARENTHESES); }
  | identifier '{' exprlist '}'    [ZZLOG;] { $$ = MemberInit($1, Obj($3), cppast::CppConstructorCallStyle::USING_BRACES); }
  | identifier '{' '}'             [ZZLOG;] { $$ = MemberInit($1, cppast::CppCallArgs(), cppast::CppConstructorCallStyle::USING_BRACES); }
  ;

dtordeclstmt
  : dtordecl ';'    [ZZVALID;]     { $$ = $1; }
  ;

dtordefn
  : dtordecl block  [ZZVALID;]
  {
    $$ = $1;
    $$->defn(Ptr($2 ? $2 : new cppast::CppCompound(CppCompoundType::BLOCK)));
  }
  | name tknScopeResOp '~' name [if($1 != $4) ZZERROR; else ZZVALID;] '(' ')' block
  {
    $$ = new cppast::CppDestructor(MergeCppToken($1, $4), 0);
    $$->defn(Ptr($8 ? $8 : new cppast::CppCompound(CppCompoundType::BLOCK)));
  }
  | identifier tknScopeResOp name tknScopeResOp '~' name [if($3 != $6) ZZERROR; else ZZVALID;] '(' ')' block
  {
    $$ = new cppast::CppDestructor(MergeCppToken($1, $6), 0);
    $$->defn(Ptr($10 ? $10 : new cppast::CppCompound(CppCompoundType::BLOCK)));
  }
  | name tknLT templatearglist tknGT tknScopeResOp '~' name [if($1 != $7) ZZERROR; else ZZVALID;] '(' ')' block
  {
    $$ = new cppast::CppDestructor(MergeCppToken($1, $7), 0);
    $$->defn(Ptr($11 ? $11 : new cppast::CppCompound(CppCompoundType::BLOCK)));
  }
  | templatespecifier dtordefn [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | functype dtordefn [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  ;

dtordecl
  : '~' name '(' optvoid ')' %prec DTORDECL [ZZLOG;]
  [
    if(gCompoundStack.empty())
      ZZERROR;
    if(ClassNameFromIdentifier(gCompoundStack.top()) != $2)
      ZZERROR;
    else
      ZZVALID;
  ]
  {
    const char* tildaStartPos = $2.sz-1;
    while(*tildaStartPos != '~') --tildaStartPos;
    $$ = new cppast::CppDestructor(MakeCppToken(tildaStartPos, $2.sz+$2.len-tildaStartPos), 0);
  }
  | apidecor dtordecl [ZZLOG;] {
    $$ = $2;
    $$->decor1($1);
  }
  | functype dtordecl [ZZLOG;] {
    $$ = $2;
    $$->addAttr($1);
  }
  | dtordecl optfuncattrib    [ZZLOG;]
  {
    $$ = $1;
    $$->addAttr($2);
  }
  | dtordecl '=' tknNumber    [ZZLOG;]
  {
    $$ = $1;
    $$->addAttr(PURE_VIRTUAL);
  }
  | dtordecl '=' tknDelete [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DELETE);
  }
  | dtordecl '=' tknDefault [ZZLOG;] {
    $$ = $1;
    $$->addAttr(DEFAULT);
  }
  | dtordecl functhrowspec [ZZLOG;] {
    $$ = $1;
    $$->throwSpec(Obj($2));
  }
  ;

optvoid
  :          [ZZLOG;]
  | tknVoid  [ZZLOG;]
  ;

optcomment
  :            [ZZLOG;]{
  }
  | doccomment [ZZVALID;] {
  }
  ;

classdefnstmt
  : classdefn ';' [ZZVALID;] { $$ = $1; }
  ;

attribs
  : expr {
    $$ = new std::vector;
    $$->push_back(Ptr($1));
  }
  | attribs ',' expr {
    $$ = $1;
    $$->push_back(Ptr($3));
  }
  ;

optattribs
  : {
    $$ = new std::vector;
  }
  | attribs { $$ = $1; }
  ;

attribspecifier
  : '[' '[' optattribs ']' ']' {
    $$ = $3;
  }
  ;

optattribspecifiers
  : {
    $$ = new std::vector;
  }
  | attribspecifiers { $$ = $1; }
  ;

attribspecifiers
  : attribspecifier {
    $$ = new std::vector;
    $$->insert($$->end(), std::make_move_iterator($1->begin()), std::make_move_iterator($1->end()));
  }
  | attribspecifiers attribspecifier {
    $$ = $1;
    $$->insert($$->end(), std::make_move_iterator($2->begin()), std::make_move_iterator($2->end()));
  }
  ;

classdefn
  : classspecifier optapidecor optattribspecifiers identifier optfinal optinheritlist optcomment '{'
  [
    ZZVALID;
    gCompoundStack.push($4);
  ]
  optstmtlist '}'
  [
    ZZVALID;
    gCompoundStack.pop();
  ]
  {
    $$ = $10 ? $10 : new cppast::CppCompound();
    $$->compoundType($1);
    $$->apidecor($2);
    $$->attribSpecifierSequence(Obj($3));
    $$->name(PruneClassName($4));
    $$->inheritanceList(Obj($6));
    $$->addAttr($5);
  }
  | classspecifier optattribspecifiers optinheritlist optcomment
    '{'
      optstmtlist
    '}' [ZZVALID;]
  {
    $$ = $6 ? $6 : new cppast::CppCompound();
    $$->compoundType($1);
    $$->attribSpecifierSequence(Obj($2));
    $$->inheritanceList(Obj($3));
  }
  | templatespecifier classdefn [ZZLOG;]
  {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  ;

namespacedefn
  : tknNamespace optidentifier '{'
  [
    ZZVALID;
    gCompoundStack.push(ClassNameFromIdentifier($2));
  ]
  optstmtlist '}'
  [
    ZZVALID;
    gCompoundStack.pop();
  ]
  {
    $$ = $5 ? $5 : new cppast::CppCompound();
    $$->compoundType(CppCompoundType::NAMESPACE);
    $$->name($2);
  }
  ;

optfinal
  :          [ZZLOG;] { $$ = 0; }
  | tknFinal [ZZLOG;] { $$ = FINAL; }
  ;

optinheritlist
  : [ZZLOG;] {
    $$ = new std::list;
  }
  | ':' protlevel optinherittype typeidentifier [ZZVALID;] {
    $$ = new std::list; $$->push_back({(std::string) $4, $2, $3});
  }
  | optinheritlist ',' protlevel optinherittype typeidentifier [ZZVALID;] {
    $$ = $1; $$->push_back({(std::string) $5, $3, $4});
  }
  | ':' optinherittype protlevel typeidentifier [ZZVALID;] {
    $$ = new std::list; $$->push_back({(std::string) $4, $3, $2});
  }
  | optinheritlist ',' optinherittype protlevel typeidentifier [ZZVALID;] {
    $$ = $1; $$->push_back({(std::string) $5, $4, $3});
  }
  ;

protlevel
  : { Init($$); }
  | tknPublic     [ZZLOG;] { Init($$, CppAccessType::PUBLIC); }
  | tknProtected  [ZZLOG;] { Init($$, CppAccessType::PROTECTED); }
  | tknPrivate    [ZZLOG;] { Init($$, CppAccessType::PRIVATE); }
  ;

optinherittype
  :             [ZZLOG;] { $$ = false; }
  | tknVirtual  [ZZLOG;] { $$ = true; }
  ;

fwddecl
  : classspecifier typeidentifier ';'          [ZZVALID;] { $$ = new cppast::CppForwardClassDecl($2, $1); }
  | classspecifier optapidecor identifier ';'  [ZZVALID;] { $$ = new cppast::CppForwardClassDecl($3, $2, $1); }
  | templatespecifier fwddecl [ZZLOG;] {
    $$ = $2;
    $$->templateSpecification(Obj($1));
  }
  | tknFriend typeidentifier ';'  [ZZVALID;] { $$ = new cppast::CppForwardClassDecl($2); $$->addAttr(FRIEND); }
  | tknFriend fwddecl             [ZZVALID;] { $$ = $2; $$->addAttr(FRIEND); }
  ;

classspecifier
  : tknClass      [ZZLOG;] { $$ = CppCompoundType::CLASS; }
  | tknStruct     [ZZLOG;] { $$ = CppCompoundType::STRUCT; }
  | tknUnion      [ZZLOG;] { $$ = CppCompoundType::UNION; }
  ;

templatespecifier
  : tknTemplate tknLT       [gInTemplateSpec = true;  ZZLOG;   ]
    templateparamlist tknGT [gInTemplateSpec = false; ZZVALID; ]
  {
    $$ = $4;
  }
  ;

templateparamlist
  : [ZZLOG;] {
    $$ = new cppast::CppTemplateParams;
  }
  | templateparam [ZZLOG;] {
    $$ = new cppast::CppTemplateParams;
    $$->emplace_back(Obj($1));
  }
  | templateparamlist ',' templateparam [ZZLOG;] {
    $$ = $1;
    $$->emplace_back(Obj($3));
  }
  ;

templateparam
  : tknTypename optname [ZZLOG;] {
    $$ = new cppast::CppTemplateParam($2);
  }
  | tknTypename optname '=' vartype [ZZLOG;] {
    $$ = new cppast::CppTemplateParam($2, Ptr($4));
  }
  | tknClass optname [ZZLOG;] {
    $$ = new cppast::CppTemplateParam($2);
  }
  | tknClass optname '=' vartype [ZZLOG;] {
    $$ = new cppast::CppTemplateParam($2, Ptr($4));
  }
  | vartype name [ZZLOG;] {
    $$ = new cppast::CppTemplateParam(Ptr($1), $2);
  }
  | vartype name '=' expr  %prec TEMPLATE [ZZLOG;] {
    $$ = new cppast::CppTemplateParam(Ptr($1), $2, Ptr($4));
  }
  | functionpointer [ZZLOG;] {
    $$ = new cppast::CppTemplateParam(Ptr($1), std::string());
  }
  | functionpointer '=' expr  %prec TEMPLATE [ZZLOG;] {
    $$ = new cppast::CppTemplateParam(Ptr($1), std::string(), Ptr($3));
  }
  | vartype                       [ZZLOG;] { // Can happen when forward declaring
    $$ = new cppast::CppTemplateParam(Ptr($1), std::string());
  }
  | vartype '=' expr              [ZZLOG;] { // Can happen when forward declaring
    $$ = new cppast::CppTemplateParam(Ptr($1), std::string(), Ptr($3));
  }
// 
  | tknTypename name ',' [
    if (gInTemplateSpec)
      gTemplateParamStart = $1.sz;
    ZZERROR;
  ] { $$ = nullptr; }
  | tknTypename name '=' [
    if (gInTemplateSpec)
      gTemplateParamStart = $1.sz;
    ZZERROR;
  ] { $$ = nullptr; }
  | tknTypename name tknGT [
    if (gInTemplateSpec)
      gTemplateParamStart = $1.sz;
    ZZERROR;
  ] { $$ = nullptr; }
  | tknClass name ',' [
    if (gInTemplateSpec)
      gTemplateParamStart = $1.sz;
    ZZERROR;
  ] { $$ = nullptr; }
  | tknClass name tknGT [
    if (gInTemplateSpec)
      gTemplateParamStart = $1.sz;
    ZZERROR;
  ] { $$ = nullptr; }
// 
  ;

optapidecor
  :             [ZZLOG;] { $$ = MakeCppToken(nullptr, nullptr); }
  | apidecor    [ZZLOG;] { $$ = $1; }
  ;

apidecor
  : apidecortokensq                     [ZZLOG;] { $$ = $1; }
  | apidecortokensq '(' name ')'          [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  | apidecortokensq '(' tknNumber ')'   [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  | apidecortokensq '(' strlit ')'   [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  ;

apidecortokensq
  : tknApiDecor                  [ZZLOG;] { $$ = $1; }
  | apidecortokensq tknApiDecor  [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  | tknApiDecor '(' strlit ')'   [ZZLOG;] { $$ = MergeCppToken($1, $4); }
  ;

entityaccessspecifier
  : tknPublic     ':'  [ZZVALID;] { $$ = new cppast::CppEntityAccessSpecifier(CppAccessType::PUBLIC); }
  | tknProtected  ':'  [ZZVALID;] { $$ = new cppast::CppEntityAccessSpecifier(CppAccessType::PROTECTED); }
  | tknPrivate    ':'  [ZZVALID;] { $$ = new cppast::CppEntityAccessSpecifier(CppAccessType::PRIVATE); }
  ;

externcblock
  : tknExternC block   [ZZVALID;] {$$ = $2; $$->compoundType(CppCompoundType::EXTERN_C_BLOCK); }
  ;

strlit
                  : tknStrLit          [ZZLOG;] { $$ = $1; }
  | strlit tknStrLit   [ZZLOG;] { $$ = MergeCppToken($1, $2); }
  ;

expr
  : strlit                            [ZZLOG;] { $$ = new cppast::CppStringLiteralExpr($1); }
  | tknCharLit                        [ZZLOG;] { $$ = new cppast::CppCharLiteralExpr($1); }
  | tknNumber                         [ZZLOG;] { $$ = new cppast::CppNumberLiteralExpr($1); }
  | identifier
    [
      if ($1.sz == gParamModPos) {
        gParamModPos = nullptr;
        ZZERROR;
      } else {
        ZZLOG;
      }
    ]                                 [ZZLOG;] { $$ = NameExpr($1); }
  | '(' exprlist ')'                  [ZZLOG;] { $$ = ExpressionListExpr(Obj($2)); };
  | '{' optexprlist '}'               [ZZLOG;] { $$ = InitializerListExpr($2); }
  | '{' optexprlist ',' '}'           [ZZLOG;] { $$ = InitializerListExpr($2); }
  | '+' expr                          [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::UNARY_PLUS, $2); }
  | '-' expr %prec UNARYMINUS         [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::UNARY_MINUS, $2); }
  | '~' expr                          [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::BIT_TOGGLE, $2); }
  | '!' expr                          [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::LOGICAL_NOT, $2); }
  | '*' expr %prec DEREF              [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::DEREFER, $2); }
  | '&' expr %prec ADDRESSOF          [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::REFER, $2); }
  | '&' operfuncname %prec ADDRESSOF  [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::REFER, NameExpr($2)); }
  | tknInc expr  %prec PREINCR        [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::PREFIX_INCREMENT, $2); }
  | tknDec expr  %prec PREDECR        [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::PREFIX_DECREMENT, $2); }
  | expr tknInc  %prec POSTINCR       [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::POSTFIX_INCREMENT, $1); }
  | expr tknDec  %prec POSTDECR       [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::POSTFIX_DECREMENT, $1); }
  | expr '+' expr                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::PLUS, $1, $3); }
  | expr '-' expr                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::MINUS, $1, $3); }
  | expr '*' expr
    [
      if ($2.sz == gParamModPos) {
        gParamModPos = nullptr;
        ZZERROR;
      } else {
        ZZLOG;
      }
    ]                                         [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::MUL, $1, $3); }
  | expr '/' expr                             [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::DIV, $1, $3); }
  | expr '%' expr                             [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::PERCENT, $1, $3); }
  | expr '&' expr
    [
      if ($2.sz == gParamModPos) {
        gParamModPos = nullptr;
        ZZERROR;
      } else {
        ZZLOG;
      }
    ]                                         [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::AND, $1, $3); }
  | expr '|' expr                             [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::OR, $1, $3); }
  | expr '^' expr                             [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::XOR, $1, $3); }
  | expr '=' expr                             [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ASSIGN, $1, $3); }
  | expr tknLT expr                           [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LESS, $1, $3); }
  | expr tknGT expr                           [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::GREATER, $1, $3); }
  | expr tknPlusEq expr                       [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::PLUS_ASSIGN, $1, $3); }
  | expr tknMinusEq expr                      [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::MINUS_ASSIGN, $1, $3); }
  | expr tknMulEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::MUL_ASSIGN, $1, $3); }
  | expr tknDivEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::DIV_ASSIGN, $1, $3); }
  | expr tknPerEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::PERCENT_ASSIGN, $1, $3); }
  | expr tknXorEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::XOR_ASSIGN, $1, $3); }
  | expr tknAndEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::AND_ASSIGN, $1, $3); }
  | expr tknOrEq expr                         [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::OR_ASSIGN, $1, $3); }
  | expr tknLShift expr                       [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LEFT_SHIFT, $1, $3); }
  | expr rshift expr                          [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::RIGHT_SHIFT, $1, $3); }
  | expr tknLShiftEq expr                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LSHIFT_ASSIGN, $1, $3); }
  | expr tknRShiftEq expr                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::RSHIFT_ASSIGN, $1, $3); }
  | expr tknCmpEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::EQUAL, $1, $3); }
  | expr tknNotEq expr                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::NOT_EQUAL, $1, $3); }
  | expr tknLessEq expr                       [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LESS_EQUAL, $1, $3); }
  | expr tknGreaterEq expr                    [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::GREATER_EQUAL, $1, $3); }
  | expr tkn3WayCmp expr                      [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::THREE_WAY_CMP, $1, $3); }
  | expr tknAnd expr
    [
      if ($2.sz == gParamModPos) {
        gParamModPos = nullptr;
        ZZERROR;
      } else {
        ZZLOG;
      }
    ]                                                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LOGICAL_AND, $1, $3); }
  | expr tknOr expr                                       [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::LOGICAL_OR, $1, $3); }
  | expr '.' expr                                         [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::DOT, $1, $3); }
  | expr '.' '*' expr                                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::DOT, $1, MonomialExpr(cppast::CppUnaryOperator::DEREFER, $4)); }
  | expr tknArrow expr                                    [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ARROW, $1, $3); }
  | expr tknArrowStar expr                                [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ARROW_STAR, $1, $3); }
  | expr '.' '~' funcname                                 [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::DOT, $1, MergeCppToken($3, $4)); }
  | expr tknArrow '~' funcname                            [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ARROW, $1, MergeCppToken($3, $4)); }
  | expr '[' expr ']' %prec SUBSCRIPT                     [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ARRAY_INDEX, $1, $3); }
  /*| expr '[' ']' %prec SUBSCRIPT                        [ZZLOG;] { $$ = BinomialExpr($1, kArrayElem); }*/
  | expr '(' optexprlist ')' %prec FUNCCALL               [ZZLOG;] { $$ = FuncCallExpr($1, $3); }
  | funcname '(' optexprlist ')' %prec FUNCCALL           [ZZLOG;] { $$ = FuncCallExpr(NameExpr($1), $3); }
  | expr tknArrow '~' identifier '(' ')' %prec FUNCCALL   [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ARROW, $1, FuncCallExpr(NameExpr(MergeCppToken($3, $4)))); }
  | expr '?' expr ':' expr %prec TERNARYCOND              [ZZLOG;] { $$ = TrinomialExpr(cppast::CppTernaryOperator::CONDITIONAL, $1, $3, $5); }
  | identifier '{' optexprlist '}' %prec FUNCCALL         [ZZLOG;] { $$ = UniformInitExpr($1, $3); }
  | '(' vartype ')' expr %prec CSTYLECAST                 [ZZLOG;] { $$ = CStyleCastExpr($2, $4); }
  | tknConstCast tknLT vartype tknGT '(' expr ')'         [ZZLOG;] { $$ = ConstCastExpr($3, $6); }
  | tknStaticCast tknLT vartype tknGT '(' expr ')'        [ZZLOG;] { $$ = StaticCastExpr($3, $6); }
  | tknDynamicCast tknLT vartype tknGT '(' expr ')'       [ZZLOG;] { $$ = DynamiCastExpr($3, $6); }
  | tknReinterpretCast tknLT vartype tknGT '(' expr ')'   [ZZLOG;] { $$ = ReinterpretCastExpr($3, $6); }
  | '(' expr ')'                                          [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::PARENTHESIZE, $2); }
  | tknNew typeidentifier opttypemodifier                 [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::NEW, VarTypeExpr($2, $3)); }
  | tknNew expr                                           [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::NEW, $2); }
  | tknNew '(' expr ')' expr %prec tknNew                 [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::PLACEMENT_NEW, $3, $5); }
  | tknScopeResOp tknNew '(' expr ')' expr %prec tknNew   [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::GLOBAL_PLACEMENT_NEW, $4, $6); }
  | tknDelete  expr                                       [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::DELETE, $2); }
  | tknDelete  '[' ']' expr %prec tknDelete               [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::DELETE_AARAY, $4); }
  | tknSizeOf '(' vartype ')'                             [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::SIZE_OF, VarTypeExpr($3)); }
  | tknSizeOf '(' expr ')'                                [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::SIZE_OF, $3); }
  | tknSizeOf tknEllipsis '(' vartype ')'                 [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::VARIADIC_SIZE_OF, VarTypeExpr($4)); }
  | tknSizeOf tknEllipsis '(' expr ')'                    [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::VARIADIC_SIZE_OF, $4); }
  | expr tknEllipsis                                      [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::VARIADIC, $1); }
  | lambda                                                [ZZLOG;] { $$ = LambdaExpression($1); }
  /* This is to parse implementation of string user literal, see https://en.cppreference.com/w/cpp/language/user_literal */
  | tknNumber name                                        [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::USER_LITERAL, NumberLiteralExpr($1), NameExpr($2)); }
  /* Objective C expressions */
  /* This will need improvements, as of now the aim is just to mainly parse C++ content. */
  | '[' expr expr ']'                                     [ZZLOG;] { $$ = $2; }
  | '[' expr objcarglist ']'                              [ZZLOG;] { $$ = $2; }
  ;

objcarg
  : name ':' expr { $$ = $3; }
  ;

objcarglist
  : objcarg { $$ = $1; }
  | objcarglist objcarg { $$ = $1; }
  ;

exprlist
  : expr [ZZLOG;] {
     $$ = new std::vector;
     $$->emplace_back($1);
  }
  | desinatedinitialization [ZZLOG;] {
     $$ = new std::vector;
     $$->emplace_back($1);
  }
  | exprlist optdoccommentstr ',' optdoccommentstr expr %prec COMMA [ZZLOG;] { $1->emplace_back($5); $$ = $1; }
  | exprlist optdoccommentstr ',' optdoccommentstr desinatedinitialization %prec COMMA [ZZLOG;] { $1->emplace_back($5); $$ = $1; }
  | doccommentstr exprlist [ZZLOG;] { $$ = $2; }
  | exprlist doccommentstr [ZZLOG;] { $$ = $1; }
  ;

desinatedinitialization
  : designatedname '=' expr [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::ASSIGN, NameExpr($1), $3); }
  ;

optexprlist
  :           [ZZLOG;] { $$ = nullptr; }
  | exprlist  [ZZLOG;] { $$ = $1; }
  ;

exprorlist
  : expr                        [ZZLOG;] { $$ = $1; }
  | exprlist                    [ZZLOG;] { $$ = ExpressionListExpr(Obj($1)); }
  | doccommentstr exprorlist    [ZZLOG;] { $$ = $2; }
  ;

optexprorlist
  :             [ZZLOG;] { $$ = nullptr; }
  | exprorlist  [ZZLOG;] { $$ = $1; }
  ;

/* TODO: Improve lambda capture types. As of now use of use of MonomialExpr and  BinomialExpr are hacks. */
captureallbyref
  : '&'  [ZZLOG;] { $$ = MonomialExpr(cppast::CppUnaryOperator::REFER, NameExpr("")); }
  ;

captureallbyval
  : '='  [ZZLOG;] { $$ = BinomialExpr(cppast::CppBinaryOperator::EQUAL, NameExpr(""), NameExpr("")); }
  ;

lambdacapture
  : optexprlist        [ZZLOG;] { $$ = nullptr; }
  | captureallbyref    [ZZLOG;] { $$ = nullptr; }
  | captureallbyval    [ZZLOG;] { $$ = nullptr; }
  ;

exprstmt
  : expr ';'           [ZZLOG;] { $$ = $1; }
  ;

returnstmt
  : tknReturn  exprorlist ';' [ZZLOG;] { $$ = new cppast::CppReturnStatement(Ptr($2)); }
  | tknReturn  ';'            [ZZLOG;] { $$ = new cppast::CppReturnStatement(); }
  ;

throwstmt
  : tknThrow  expr         [ZZLOG;] { $$ = new cppast::CppThrowStatement(Ptr($2)); }
  | tknThrow               [ZZLOG;] { $$ = new cppast::CppThrowStatement(); }
  ;

gotostmt
  : tknGoto expr           [ZZLOG;] { $$ = new cppast::CppGotoStatement(Ptr($2)); }
  ;

%%

//////////////////////////////////////////////////////////////////////////

// clang-format on

#include "parser.h"

  extern LexerData g;

extern const char* contextNameFromState(int ctx);

enum class ParseStatus
{
  NotAvailable,
  Success,
  Failure
};

ParseStatus gParseStatus = ParseStatus::NotAvailable;

void defaultErrorHandler(const char* errLineText, size_t lineNum, size_t errorStartPos, int lexerContext)
{
  constexpr size_t bufsize             = 1024;
  char             spacechars[bufsize] = {0};
  for (size_t i = 0; i < errorStartPos; ++i)
    spacechars[i] = (errLineText[i] == '\t') ? '\t' : ' ';
  char        errmsg[bufsize];
  const auto* errText         = errLineText + errorStartPos;
  const auto* strLexerContext = contextNameFromState(lexerContext);

  std::sprintf(errmsg,
               "Error: Unexpected '%.*s', while in context=%.*s(%d), found at line#%ld\n"
               "%.*s\n"   // Line that contains the error.
               "%.*s^\n", // A ^ below the beginning of unexpected token.
               static_cast(strlen(errText)),
               errText,
               static_cast(strlen(strLexerContext)),
               strLexerContext,
               lexerContext,
               lineNum,
               static_cast(strlen(errLineText)),
               errLineText,
               static_cast(strlen(spacechars)),
               spacechars);

  printf("%s", errmsg);
}

ErrorHandler gErrorHandler = defaultErrorHandler;

/**
 * yyparser() invokes this function when it encounters unexpected token.
 */
void yyerror_detailed(char* text, int errt, YYSTYPE& errt_value, YYPOSN& errt_posn)
{
  extern int getLexerContext();

  const char* lineStart = errt_posn;
  const char* buffStart = g.mInputBuffer;
  while (lineStart > buffStart)
  {
    if (lineStart[-1] == '\n' || lineStart[-1] == '\r')
      break;
    --lineStart;
  }
  char* lineEnd        = errt_posn;
  char  endReplaceChar = 0;
  while (*lineEnd)
  {
    if (*lineEnd == '\r' || *lineEnd == '\n')
    {
      endReplaceChar = *lineEnd;
      *lineEnd       = '\0'; // So that printing of lineStart does not print things beyond current line.
    }
    else
    {
      ++lineEnd;
    }
  }
  gParseStatus = ParseStatus::Failure;
  gErrorHandler(lineStart, g.mLineNo, errt_posn - lineStart, getLexerContext());
  // Replace back the end char
  if (endReplaceChar)
    *lineEnd = endReplaceChar;
}

enum
{
  kNoLog    = 0x000,
  kLexLog   = 0x001,
  kParseLog = 0x002,
  kYaccLog  = 0x004
};

static void setupEnv()
{
#if YYDEBUG
  const char* yys = getenv("ZZDEBUG");
  if (yys)
  {
    const int yyn = *yys - '0';

    gParseLog = ((yyn & kParseLog) ? 1 : 0);
    g.mLexLog = ((yyn & kLexLog) ? 1 : 0);
    yydebug   = ((yyn & kYaccLog) ? 1 : 0);
  }
#endif
}

void SetErrorHandler(ErrorHandler errorHandler)
{
  gErrorHandler = errorHandler;
}

void ResetErrorHandler()
{
  gErrorHandler = defaultErrorHandler;
}

int GetKeywordId(const std::string& keyword)
{
  static const std::unordered_map keywordToIdMap = {{"virtual", tknVirtual},
                                                                      {"final", tknFinal},
                                                                      {"override", tknOverride},
                                                                      {"const", tknConst},
                                                                      {"noexcept", tknNoExcept},
                                                                      {"inline", tknInline},
                                                                      {"explicit", tknExplicit},
                                                                      {"friend", tknFriend},
                                                                      {"public", tknPublic},
                                                                      {"protected", tknProtected},
                                                                      {"private", tknPrivate},
                                                                      {"struct", tknStruct},
                                                                      {"class", tknClass},
                                                                      {"union", tknUnion},
                                                                      {"namespace", tknNamespace},
                                                                      {"typename", tknTypename},
                                                                      {"template", tknTemplate},
                                                                      {"extern", tknExtern},
                                                                      {"extern", tknExtern},
                                                                      {"static", tknStatic},
                                                                      {"auto", tknAuto},
                                                                      {"mutable", tknMutable},
                                                                      {"volatile", tknVolatile},
                                                                      {"constexpr", tknConstExpr},
                                                                      {"constexpr", tknConstExpr},
                                                                      {"typedef", tknTypedef}};

  auto itr = keywordToIdMap.find(keyword);
  return (itr != keywordToIdMap.end()) ? itr->second : -1;
}

std::unique_ptr ParseStream(char* stm, size_t stmSize)
{
  gProgUnit = nullptr;

  void setupScanBuffer(char* buf, size_t bufsize);
  void cleanupScanBuffer();
  setupScanBuffer(stm, stmSize);
  setupEnv();
  gTemplateParamStart = nullptr;
  gParamModPos        = nullptr;
  gInTemplateSpec     = false;
  gDisableYyValid     = 0;
  gParseStatus        = ParseStatus::NotAvailable;
  yyparse();
  cleanupScanBuffer();
  CppCompoundStack tmpStack;
  gCompoundStack.swap(tmpStack);

  std::unique_ptr ret(gProgUnit);
  gProgUnit = nullptr;

  return ret;
}

Web Proxy Viewer  |  New URL  |  Original Page