# Grammar for simple C++ declarations in file scope. This is quite a # complex example, but it covers almost all the features of Basil. # # Upper case symbols are tokens. Here's a brief description of the # symbol attributes (see the Basil documentation for more # information). # # a < Sticky follow. Follow 'a' only if a valid token is # next. (Basil uses default reductions in states, like # Yacc.) Only has effect on nonterminals. # # a * Accept. Accept the parser when 'a' is followed. Only # pending trial parsers from start of 'a' are canceled. # Only has effect on nonterminals. # # a 1 A 1 Lexical state. Assign a lexical state of 1 (can be any # integer >= 0) to all the tokens in the first set of # 'a', or assign a lexical state of 1 to 'A'. Each parse # state has an associated lexical state. Used for # context dependent lexing. The default lex state is 0. # # a > A > Shift priority. Give priority to all tokens derived # from 'a', or give priority to 'A'. Can be used # multiple times. # # a >! A >! Exclusive shift priority. Give exclusive priority to # all tokens derived from 'a', or give exclusive priority # to 'A'. Exclusive priority is a flag, so it has no # effect if used multiple times. # # a + Reduce priority. Give priority to each token in the # follow set of 'a'. Can be used multiple times. Only # has effect on nonterminals. # # a +! Exclusive reduce priority. Give exclusive priority to # each token in the follow set of 'a'. Only has effect # on nonterminals. # # On a conflict, the action that leads to parsing the token with the # highest priority is tried first. Or, if a token has exclusive # priority, only the action that leads to parsing it is tried. If # more than one token on a conflict has exclusive priority, the one # with the highest priority is picked. See the Basil documentation # for examples. # # All actions on a conflict must be ordered. If the actions are not # ordered, Basil will display an error message that should help you # fix the problem. # # Basil encodes the follow symbol with a reduction. As a result, # rules of the form 'a -> b' that are not named are bypassed (see # examples below). # # The newline character ends a comment, it is treated as whitespace in # all other contexts. # # Basil takes about 3.5 minutes to generate a parser for this grammar # on my Pentium II 300. # # start rule, parse an optional sequence of simple declarations # start -> simple-decl-seq-opt # simple declaration sequence opt # simple-decl-seq-opt -> simple-decl-seq-opt -> simple-decl-seq # simple decaration sequence # # Since a node is not associated with the second rule below, the parse # tree will be pruned on its reduction. It's OK becuase the tree is # not meant to be traversed top-down when complete, semantic actions # are executed bottom-up instead. # simple-decl-seq -> simple-decl simple-decl-seq -> simple-decl-seq simple-decl # simple declaration # # The declaration rules here are a bit different from the rules given # in the C++ standard. For one, the rules here allow the declarator # to be declared as soon as it is parsed using only synthesized # attributes (Basil doesn't support inherited attributes). Also, the # rules here allow the first name in a declaration to be parsed in the # following order: first as a constructor declarator, then as a user # type name, and then as non-constructor declarator. A three-way # conflict is created, then priorities are assigned to symbols to # achieve the desired order. # # We'll permit constructors to have return types and initializers, # being that it is probably better to issue a semantic warning than to # issue a syntax error. # # This rule is named. Here, the name specifies the node that the # parser should constuct when this rule is reduced. (Basil will # generate this node.) A rule name can also be empty ('[]'), this # just forces the parser not to bypass the rule if it is of the form # 'a -> b'. # [basl_SimpleDeclNode] simple-decl -> dcl-decl SEMI # declarator declaration # # Below, both decl-spec-seq-opt-a and decl-spec-seq-opt-b go to # decl-spec-seq-opt. This is so that an identifier is tried next as a # typename if not a constructor declarator. And then, if not a # typename, as a normal declarator. # # ctor-dcl is given two shift priorities, the declaration sequence is # given one, and dcl is given none. This will produce the desired # order. Here, both declaration specifier sequences are given one # shift priority. Only one could have been given the shift priority, # it doesn't matter. # # All dcl-decl nodes share common attributes, so we'll give them a # common base node (Basil will not generate this node). You can give # an explicit base node by following the node name with a ':' and the # base node name. # # Because we're going to be doing the same semantic actions on these # two rules and because the rules are very similar (have the same # number of children, for one), we can instruct the parser to create # the same node on their reductions. Below, the second rule shares # the same node as the first rule. # # Also, make sure a valid token is next when these rules are reduced # since we'll declare the declarator on these reductions. To see the # problem, consider a class type in the declaration specifier # sequence. If we don't make sure a valid token is next, it will # first be parsed as constructor declarator with an empty declaration # specifier sequence. By ensuring a valid token is next (a comma or a # semicolon in this case), we'll be OK. # # Once we've reduced a dcl-decl we don't want to try the tokens as # something else, so accept on the rules. # [ basl_DclDecl1Node : basl_DclDeclNode] dcl-decl < * -> decl-spec-seq-opt-a > dcl [(basl_DclDecl1Node)] dcl-decl < * -> decl-spec-seq-opt-b > ctor-dcl >> # dcl declaration with copy initializer # # For clarity, we'll give the symbols below the same priorities, but # since we've already given the priorities above we don't have to # again. # # In a real parser, the initializer expression can't be parsed until # the declarator is declared (eg. 'int i = i' initializes i to its own # unitialized value). The rules could be modified slightly to make # this work. # # The initializer expression below has exclusive reduce priority so # that a comma is always treated as a declarator separator, never as # part of a comma-expr. # [ basl_DclDecl2Node : basl_DclDeclNode] dcl-decl < * -> decl-spec-seq-opt-a > dcl ASGN expr +! [(basl_DclDecl2Node)] dcl-decl < * -> decl-spec-seq-opt-b > ctor-dcl >> ASGN expr +! # dcl declaration with direct initializer # # In each rule below, LPAREN here causes a two-way conflict. LPAREN # can be parsed either as the start of a direct initializer, or the # start of a function parameter list. It must first be parsed as the # start of a function parameter list, so the LPAREN in that context is # given shift priority. # [ basl_DclDecl3Node : basl_DclDeclNode] dcl-decl < * -> decl-spec-seq-opt-a > dcl LPAREN expr-list RPAREN [(basl_DclDecl3Node)] dcl-decl < * -> decl-spec-seq-opt-b > ctor-dcl >> LPAREN expr-list RPAREN # ignore brace enclosed initialization # permit more than one declarator in a declaration # # Again, try a constructor declarator first, and in the case of a # single name reduce towards ctor-dcl first (all tokens after ctor-dcl # will have priority). # [ basl_DclDecl4Node : basl_DclDeclNode] dcl-decl < * -> dcl-decl COMMA dcl [(basl_DclDecl4Node)] dcl-decl < * -> dcl-decl COMMA ctor-dcl >+ # same with copy initializer # [ basl_DclDecl5Node : basl_DclDeclNode] dcl-decl < * -> dcl-decl COMMA dcl ASGN expr +! [(basl_DclDecl5Node)] dcl-decl < * -> dcl-decl COMMA ctor-dcl >+ ASGN expr +! # and same with direct initializer # # As the LPAREN caused a two-way conflict above, here it causes a # four-way conflict! Consider an declarator consisting of a single # name, it can be reduced to either a ctor-direct-dcl, a ctor-dcl, a # direct-dcl, or a dcl. Becuase ctor-dcl below is given a shift # priority of two, the effective shift priority of the LPAREN starting # a function parameter list is three (becuase that LPAREN has a shift # priority of one, and the shift priorities are added). ctor-dcl also # has a reduce priority of two, giving the LPAREN starting the # initializer a priority of two (LPAREN here could also have been # given a shift priority of two to get the same result, or, one to # each). So, first the name will be reduced as a ctor-direc-dcl, then # as a ctor-dcl. Then, becuase of the shift priority on the LPAREN # starting a function parameter list, the name will be reduced to a # direct-dcl (priority 1), then finally to a dcl (priority 0) if all # else fails. # # You can verify this order by looking in the 'cc_states.txt' file, a # human-readable listing of all the states. # [ basl_DclDecl6Node : basl_DclDeclNode] dcl-decl < * -> dcl-decl COMMA dcl LPAREN expr-list RPAREN [(basl_DclDecl6Node)] dcl-decl < * -> dcl-decl COMMA ctor-dcl >>++ LPAREN expr-list RPAREN # decl spec seq opt a # # Here's where we set both decl-spec-seq-opt-a and decl-spec-seq-opt-b # to decl-spec-seq-opt. Becuase these rules are not named, the parser # will never reduce these rules, instead they will be bypassed, # providing us with the three-way conflict involving the constructor # declarator. # decl-spec-seq-opt-a -> decl-spec-seq-opt # decl spec seq opt b # decl-spec-seq-opt-b -> decl-spec-seq-opt # decl spec seq opt # decl-spec-seq-opt -> decl-spec-seq decl-spec-seq-opt -> # decl spec seq # # decl-spec-seq-a is a declaration specifier sequence with builtin # types plus other non-type specifiers. # # decl-spec-seq-b is a declaration specifier sequence with one user # type plus other non-type specifiers. # # decl-spec-seq-c is a declaration specifier sequence with non-type # specifiers. # # Writing a declaration specifier sequence like this frees the parser # from checking if the declaration specifier sequence is valid (in # terms of types) after each decl-spec is reduced. # # No need to name these rules. # decl-spec-seq -> decl-spec-seq-a decl-spec-seq -> decl-spec-seq-b decl-spec-seq -> decl-spec-seq-c # decl spec seq a (declaration specifier sequence with built-in types # plus non-type specifiers) # # Name all decl spec seq nodes basl_DeclSpecSeqNode since they all do # semantically the same thing. # # By default, top-down navigators will navigate, from left to right, # the child nonterminal nodes of nodes associated with rules of the # form "a -> a b", "a -> a TOKEN b", or "a -> b". So, make sure # basl_DeclSpecSeqNode is associated with one of the sequences that # Basil recognizes (the last one below). This will just make things # easier on the semantic side. # decl-spec-seq-a -> decl-spec-a [(basl_DeclSpecSeqNode)] decl-spec-seq-a -> decl-spec-seq-c decl-spec-a [(basl_DeclSpecSeqNode)] decl-spec-seq-a -> decl-spec-seq-a decl-spec-a [ basl_DeclSpecSeqNode ] decl-spec-seq-a -> decl-spec-seq-a decl-spec-c # decl spec a (builtin types) # [basl_DeclSpecANode] decl-spec-a -> builtin-type-name # builtin type name # # Derive node from basl_TypeNameNode (which holds a type attribute). # [basl_BuiltinTypeName : basl_TypeNameNode] builtin-type-name -> builtin-type-spec # builtin type specifier # builtin-type-spec -> BOOL builtin-type-spec -> CHAR builtin-type-spec -> DOUBLE builtin-type-spec -> FLOAT builtin-type-spec -> INT builtin-type-spec -> LONG builtin-type-spec -> SHORT builtin-type-spec -> SIGNED builtin-type-spec -> UNSIGNED builtin-type-spec -> VOID builtin-type-spec -> WCHAR # decl spec seq b (declaration specifier sequence with one user type plus # non-type specifiers) # decl-spec-seq-b -> decl-spec-b [(basl_DeclSpecSeqNode)] decl-spec-seq-b -> decl-spec-seq-c decl-spec-b [(basl_DeclSpecSeqNode)] decl-spec-seq-b -> decl-spec-seq-b decl-spec-c # decl spec b (user types) # # ignore elaborated types, class specifiers, enums ... # [basl_DeclSpecBNode] decl-spec-b -> user-type-name # user type name # # Optionally qualified user type names and templates. A # user-type-name will be checked as a real type when the parser # reduces these rules. If a user-type-name is not a type, the parser # will fail and try another option or issue a syntax error. # # Since we have the type, we might as well make it an attribute of the # node. We attach it to basl_UserTypeNameNode. # [basl_UserTypeName1Node : basl_TypeNameNode] user-type-name -> nested-name-spec-opt IDENT [basl_UserTypeName2Node : basl_TypeNameNode] user-type-name -> template-name LT template-arg-list-opt GT # decl spec seq c (decl specifier seq with nontypes) # decl-spec-seq-c -> decl-spec-c [(basl_DeclSpecSeqNode)] decl-spec-seq-c -> decl-spec-seq-c decl-spec-c # decl spec c # [basl_DeclSpecC1Node] decl-spec-c -> cv-spec [basl_DeclSpecC2Node] decl-spec-c -> func-spec [basl_DeclSpecC3Node] decl-spec-c -> stor-spec # the kind of the declaration is also a specifier # [basl_DeclSpecC4Node] decl-spec-c -> TYPEDEF [basl_DeclSpecC5Node] decl-spec-c -> FRIEND # cv spec # cv-spec -> CONST cv-spec -> VOLATILE # function specifier # func-spec -> INLINE func-spec -> VIRTUAL func-spec -> EXPLICIT # storage specifier # stor-spec -> STATIC stor-spec -> EXTERN stor-spec -> MUTABLE stor-spec -> AUTO stor-spec -> REGISTER # nested name specifier opt # nested-name-spec-opt -> nested-name-spec nested-name-spec-opt -> # nested name specifier # # Let's assume the trailing :: is always part of nested-name-spec, not # start of qualified declarator (we can always enclose the declarator # in parens if it does start with ::). So give the trailing :: # exclusive shift priority. # # On reduction, the names will be checked if they are scope-able, if # not, the parser will fail (try another option). The scope is # attached as an attribute to basl_NestedNameSpecNode. # [basl_NestedNameSpec1Node : basl_NestedNameSpecNode] nested-name-spec -> nested-name-spec-opt IDENT DCOLON >! [basl_NestedNameSpec2Node : basl_NestedNameSpecNode] nested-name-spec -> template-name LT template-arg-list-opt GT DCOLON >! [basl_NestedNameSpec3Node : basl_NestedNameSpecNode] nested-name-spec -> DCOLON # template name # # Like user-type-name, the parser will check if a template-name is a # real template name on the reduction of this rule. If not, the # parser won't continue in this direction. We might as well make sure # a template name is followed by an LT before we check. # # Accept on this reduction as well, since, if a template name, the LT # is never a less-than sign. The parser will accept only if the # semantic actions do not cause the parser to fail, so, in this case, # it'll accept only if the name is really a template name. Note # though that only pending trial parsers from the start of # template-name will be canceled on this accept, so, if the parser # later backtracks, it will backtrack before this rule. # [basl_TemplateNameNode] template-name < * -> nested-name-spec-opt IDENT # destructor template name # # Used in a template-id destructor. BITNOT is ~. # # Has same semantics as template-name, and also accept. # [basl_DtorTemplateNameNode] dtor-template-name < * -> nested-name-spec-opt BITNOT IDENT # template arg list opt # template-arg-list-opt -> template-arg-list template-arg-list-opt -> # template arg list # template-arg-list -> template-arg [basl_TemplateArgListNode] template-arg-list -> template-arg-list COMMA template-arg # template arg # # Try a type-id before an expression. And, assign-expr needs reduce # priority so that a GT is treated as the end of a template arg list # and not as part of a relational expr. But, type-id should reduce # first so that a single identifier is first reduced as a possible # type-name, so give type-id higher reduce proirity. We can't give # assign-expr exclusive reduce priority even though GT is *always* the # end of the template list, becuase we'd have to give type-id # exclusive reduce priority as well so it would reduce first, but we # can't give type-id exclusive reduce priority becuase we want to # guess on the reduction. So, to prevent GT from possibly being tried # later as part of a relational expression we'll just accept on the # reductions. (Alternately, we could give all GTs in template-ids # exclusive shift priority.) We'll accept on type-id also, this will # still allow the parser to backtrack if a name is not a type-name, # the parser will accept *only* when type-id is followed. # # Ignore template-template arguments. # [basl_TemplateArg1Node] template-arg -> type-id > ++ * [basl_TemplateArg2Node] template-arg -> assign-expr + * # type id # # Try a name in the declaration specifier seq first as a # user-type-name, then as the start of a member pointer. We can # achieve this by giving the whole decl-spec-seq shift priority. # # On reduction, the type will be found and attached as an attribute to # the node. We can attach the attribute directly to this node (note # missing ':'). # [basl_TypeIdNode basl_TypeAttrib] type-id -> decl-spec-seq > abs-dcl-opt # declarator # [basl_DclNode] dcl -> ptr-operator dcl dcl -> direct-dcl # direct declarator # # The LPAREN causes a conflict with the LPAREN of a direct # initializer. The standard says we have to try a param-decl-clause # first (eg. If A is a type, in 'A a (A ())', a is a function # returning an A taking a pointer to a function returning an A taking # no arguments.) # # ignore exception specifications on function types # [basl_DirectDcl1Node] direct-dcl -> direct-dcl LPAREN > param-decl-clause RPAREN cv-spec-seq-opt [basl_DirectDcl2Node] direct-dcl -> direct-dcl LBRACK expr-opt RBRACK [basl_DirectDcl3Node] direct-dcl -> LPAREN dcl RPAREN direct-dcl -> dcl-id # normal declarator id # [basl_DclId1Node] dcl-id -> nested-name-spec-opt IDENT # template declarator id # [basl_DclId2Node] dcl-id -> template-name LT template-arg-list-opt GT # destructor declarator id # [basl_DclId3Node] dcl-id -> nested-name-spec-opt BITNOT IDENT # destructor template id # [basl_DclId4Node] dcl-id -> dtor-template-name LT template-arg-list-opt GT # Ignore operator and conversion function ids. # ctor declarator # # Since the rules here are semantically just like the ones above, why # not share the same node names? It'll save a bit of coding. # [(basl_DclNode)] ctor-dcl -> ptr-operator ctor-dcl ctor-dcl -> ctor-direct-dcl # ctor direct declarator # # The LPAREN causes the same conflict as in direct-dcl above. It # needs one shift priority. # # An empty rule name ('[]') is used on the last rule below to prevent # a name being checked as a ctor name twice if followed by an LPAREN # (the reduce-reduce conflict is delayed then until this reduction). # [(basl_DirectDcl1Node)] ctor-direct-dcl -> ctor-direct-dcl LPAREN > param-decl-clause RPAREN cv-spec-seq-opt [(basl_DirectDcl2Node)] ctor-direct-dcl -> ctor-direct-dcl LBRACK expr-opt RBRACK [(basl_DirectDcl3Node)] ctor-direct-dcl -> LPAREN ctor-dcl RPAREN [ ] ctor-direct-dcl -> ctor-dcl-id # constructor declarator id # # The parser will check if the name is a real constructor name on the # reduction of this rule, and fail otherwise. # [basl_CtorDclId1Node] ctor-dcl-id -> nested-name-spec-opt IDENT # constructor template id constructor # # Same semantics as above. # [basl_CtorDclId2Node] ctor-dcl-id -> template-name LT template-arg-list-opt GT # abstract declarator opt # abs-dcl-opt -> abs-dcl abs-dcl-opt -> # abstract declarator # [basl_AbsDclNode] abs-dcl -> ptr-operator abs-dcl-opt abs-dcl -> direct-abs-dcl # direct abstract declarator opt # direct-abs-dcl-opt -> direct-abs-dcl direct-abs-dcl-opt -> # direct abs declarator # # In a parameter declaration, try 'int (A)' first as a function taking # an A, returning an int, then as a declarator enclosed in parens. In # a template type argument, also fine to try an LPAREN as start of a # function first, then as start of a nested abs-dcl. # # Giving LPAREN shift priority here will give it an combined shift # priority of two when the parser is parsing both an expression and a # type. Two becuase the type in that context is given one shift # priority so it is tried before the expression. But the LPAREN in a # function style cast also has a one shift priority (for reasons # explained there), so this LPAREN still wins. # [basl_DirectAbsDcl1Node] direct-abs-dcl -> direct-abs-dcl-opt LPAREN > param-decl-clause RPAREN cv-spec-seq-opt [basl_DirectAbsDcl2Node] direct-abs-dcl -> direct-abs-dcl-opt LBRACK expr-opt RBRACK [basl_DirectAbsDcl3Node] direct-abs-dcl -> LPAREN abs-dcl RPAREN # cv spec seq opt # cv-spec-seq-opt -> cv-spec-seq cv-spec-seq-opt -> # cv spec seq # [basl_CvSpecSeq1Node] cv-spec-seq -> cv-spec [basl_CvSpecSeq2Node] cv-spec-seq -> cv-spec-seq cv-spec # pointer operator # # BITAND is & # [basl_PtrOperator1Node] ptr-operator -> TIMES cv-spec-seq-opt [basl_PtrOperator2Node] ptr-operator -> BITAND [basl_PtrOperator3Node] ptr-operator -> nested-user-type-name TIMES cv-spec-seq-opt # nested user type name # # On reduction, name is checked as a type name. Won't work if we # write 'user-type-name DCOLON' because the following DCOLON will # always be treated as part of a nested name specifier, since it is # given exclusive shift priority in that context. Here, it's OK # because the parser will parse nested-user-type-name at the same time # as it's parsing a nested-name-spec since they look the same. # # Here we're going to be really pushing it: we can share the same # user-type-name nodes, since semantically we're going to be doing the # same thing. Since the rules sizes differ though, the DCOLON will be # lost on the reductions. (Basil figures you know what you're doing # and doesn't warn if you share a node that has a different number of # children -- beware though if you share a node that has *more* # children!) Reference counted pointers are used, so we don't need to # worry about leaking memory if we do this. # [(basl_UserTypeName1Node)] nested-user-type-name -> nested-name-spec-opt IDENT DCOLON [(basl_UserTypeName2Node)] nested-user-type-name -> template-name LT template-arg-list-opt GT DCOLON # param decl clause # [basl_ParamDeclClause1Node] param-decl-clause -> param-decl-list-opt ellipse-opt [basl_ParamDeclClause2Node] param-decl-clause -> param-decl-list COMMA ELLIPSE # ellipse opt # ellipse-opt -> ELLIPSE ellipse-opt -> # param decl list opt # param-decl-list-opt -> param-decl-list param-decl-list-opt -> # param decl list # param-decl-list -> param-decl [basl_ParamDeclListNode] param-decl-list -> param-decl-list COMMA param-decl # param decl # # Default arg expression is assign-expr since COMMA is always # param-decl separator, not part of comma-expr. (Could just as easily # use expr+! instead.) # # Try a name in the declaration specifier sequence as a user type name # first then as a declarator or the start of a member pointer. # # Make sure a valid token is next when we reduce by these rules, and # accept. # [basl_ParamDecl1Node] param-decl < * -> decl-spec-seq > dcl [basl_ParamDecl2Node] param-decl < * -> decl-spec-seq > dcl ASGN assign-expr [basl_ParamDecl3Node] param-decl < * -> decl-spec-seq > abs-dcl-opt [basl_ParamDecl4Node] param-decl < * -> decl-spec-seq > abs-dcl-opt ASGN assign-expr # expr opt # expr-opt -> expr expr-opt -> # expr # [ ] expr -> comma-expr # comma expression # # In all the expressions, rules that indicate higher precedence (rules # like 'comma-epxr -> assign-expr') are given an empty rule name # ('[]'). This will force the parser to reduce these rules instead of # bypassing them as it would normally. Why do this? Becuase if we # don't Basil will take *very* (10x) much longer and use a lot more # memory. The parse tables will be about 10 times larger as well. If # you remove them, make sure you have lots of available memory (> # 60M). You will see a significant difference in the parser debug # output (if you have it enabled), it'll be much easier to follow. # # Each expr node derives from basl_Expr. # [basl_CommaExprNode : basl_ExprNode] comma-expr -> comma-expr COMMA assign-expr [ ] comma-expr -> assign-expr # assignment expression # [basl_AssignExprNode : basl_ExprNode] assign-expr -> log-or-expr assign-oper assign-expr [ ] assign-expr -> cond-expr # assignment operator # assign-oper -> ASGN assign-oper -> TIMES_ASGN assign-oper -> DIVIDE_ASGN assign-oper -> MOD_ASGN assign-oper -> PLUS_ASGN assign-oper -> MINUS_ASGN assign-oper -> RSHIFT_ASGN assign-oper -> LSHIFT_ASGN assign-oper -> BITAND_ASGN assign-oper -> XOR_ASGN assign-oper -> BITOR_ASGN # conditional expr # [basl_CondExprNode : basl_ExprNode] cond-expr -> log-or-expr QMARK cond-expr COLON cond-expr [ ] cond-expr -> log-or-expr # logical or expression # [basl_LogOrExprNode : basl_ExprNode] log-or-expr -> log-or-expr OR log-and-expr [ ] log-or-expr -> log-and-expr # logical and expression # [basl_LogAndExprNode : basl_ExprNode] log-and-expr -> log-and-expr AND incl-or-expr [ ] log-and-expr -> incl-or-expr # inclusive or expression # [basl_InclOrExprNode : basl_ExprNode] incl-or-expr -> incl-or-expr BITOR excl-or-expr [ ] incl-or-expr -> excl-or-expr # exclusive or expression # [basl_ExclOrExprNode : basl_ExprNode] excl-or-expr -> excl-or-expr XOR and-expr [ ] excl-or-expr -> and-expr # and expression # [basl_AndExprNode : basl_ExprNode] and-expr -> and-expr BITAND eq-expr [ ] and-expr -> eq-expr # eq expression # [basl_EqExpr1Node : basl_ExprNode] eq-expr -> eq-expr EQ rel-expr [basl_EqExpr2Node : basl_ExprNode] eq-expr -> eq-expr NOTEQ rel-expr [ ] eq-expr -> rel-expr # rel expression # [basl_RelExpr1Node : basl_ExprNode] rel-expr -> rel-expr LT shift-expr [basl_RelExpr2Node : basl_ExprNode] rel-expr -> rel-expr GT shift-expr [basl_RelExpr3Node : basl_ExprNode] rel-expr -> rel-expr LTEQ shift-expr [basl_RelExpr4Node : basl_ExprNode] rel-expr -> rel-expr GTEQ shift-expr [ ] rel-expr -> shift-expr # shift expression # [basl_ShiftExpr1Node : basl_ExprNode] shift-expr -> shift-expr LSHIFT add-expr [basl_ShiftExpr2Node : basl_ExprNode] shift-expr -> shift-expr RSHIFT add-expr [ ] shift-expr -> add-expr # additive expression # [basl_AddExpr1Node : basl_ExprNode] add-expr -> add-expr PLUS mult-expr [basl_AddExpr2Node : basl_ExprNode] add-expr -> add-expr MINUS mult-expr [ ] add-expr -> mult-expr # multipicative expression # [basl_MultExpr1Node : basl_ExprNode] mult-expr -> mult-expr TIMES pm-expr [basl_MultExpr2Node : basl_ExprNode] mult-expr -> mult-expr DIVIDE pm-expr [basl_MultExpr3Node : basl_ExprNode] mult-expr -> mult-expr MOD pm-expr [ ] mult-expr -> pm-expr # pm expression # [basl_PmExpr1Node : basl_ExprNode] pm-expr -> pm-expr DOT_TIMES cast-expr [basl_PmExpr2Node : basl_ExprNode] pm-expr -> pm-expr PTR_TIMES cast-expr [ ] pm-expr -> cast-expr # cast expr # # try type-id before nested expression, and reduce a name followed by # an RPAREN as type-id first, eg. try '(A)' first as start of c-style # cast expression then as nested id expression (can either give # type-id reduce priority or RPAREN shift priority) # [basl_CastExprNode : basl_ExprNode] cast-expr -> LPAREN type-id >+ RPAREN cast-expr [ ] cast-expr -> unary-expr # unary expression # # Like above, try type-id before nested expression after sizeof, and # reduce a name followed by an RPAREN as type-id first, eg. try # 'sizeof (A)' first as a sizeof type, then as a sizeof nested # expression # [basl_UnaryExpr1Node : basl_ExprNode] unary-expr -> unary-oper unary-expr [basl_UnaryExpr2Node : basl_ExprNode] unary-expr -> SIZEOF LPAREN type-id >+ RPAREN [basl_UnaryExpr3Node : basl_ExprNode] unary-expr -> SIZEOF unary-expr [ ] unary-expr -> postfix-expr # ignore new and delete expressions # unary operator # unary-oper -> TIMES unary-oper -> BITAND unary-oper -> PLUS unary-oper -> MINUS unary-oper -> NOT unary-oper -> BITNOT unary-oper -> INC unary-oper -> DEC # postfix expression # [basl_PostfixExpr1Node : basl_ExprNode] postfix-expr -> postfix-expr LBRACK expr RBRACK [basl_PostfixExpr2Node : basl_ExprNode] postfix-expr -> postfix-expr LPAREN expr-list-opt RPAREN # try a name followed by an LPAREN first as a simple-type-spec, then # as an id-expr as part of a function call expression (can either give # simple-type-spec reduce priority or LPAREN shift priority) # [basl_PostfixExpr3Node : basl_ExprNode] postfix-expr -> simple-type-spec LPAREN > expr-list-opt RPAREN [basl_PostfixExpr4Node : basl_ExprNode] postfix-expr -> postfix-expr INC [basl_PostfixExpr5Node : basl_ExprNode] postfix-expr -> postfix-expr DEC [basl_PostfixExpr6Node : basl_ExprNode] postfix-expr -> DYNAMIC_CAST LT type-id GT LPAREN expr RPAREN [basl_PostfixExpr7Node : basl_ExprNode] postfix-expr -> STATIC_CAST LT type-id GT LPAREN expr RPAREN [basl_PostfixExpr8Node : basl_ExprNode] postfix-expr -> REINTERPRET_CAST LT type-id GT LPAREN expr RPAREN [basl_PostfixExpr9Node : basl_ExprNode] postfix-expr -> CONST_CAST LT type-id GT LPAREN expr RPAREN # try typeid argument first as a type-id before an expression, and # reduce a name followed by an RPAREN as type-id first # [basl_PostfixExpr10Node : basl_ExprNode] postfix-expr -> TYPEID LPAREN type-id >+ RPAREN [basl_PostfixExpr11Node : basl_ExprNode] postfix-expr -> TYPEID LPAREN expr RPAREN [ ] postfix-expr -> primary-expr # ignore member qualified expressions (like 'a.b' and 'a->B::c') # simple type specifier # # Notice that user-type-name and builtin-type-name nodes (and hence # simple-type-spec nodes) all share a common base class. So, for # example, in basl_PostfixExpr3Node the getSimpleTypeSpec() method # will return the common class. # simple-type-spec -> user-type-name simple-type-spec -> builtin-type-name # primary expression # [basl_PrimaryExpr1Node : basl_ExprNode] primary-expr -> THIS [basl_PrimaryExpr2Node : basl_ExprNode] primary-expr -> LPAREN expr RPAREN [basl_PrimaryExpr3Node : basl_ExprNode] primary-expr -> id-expr [basl_PrimaryExpr4Node : basl_ExprNode] primary-expr -> literal-expr # id-expr # # Try a name preceeding an LT first as a template name, then as an # id-expr in a relational expression. Do not give it exclusive shift # priority because we want the parser to guess. Recall that the # parser accepts on a template name, so, if a template name, the # parser will never backtrack and try the LT as a less than sign. # [basl_IdExpr1Node : basl_ExprNode] id-expr -> nested-name-spec-opt IDENT [basl_IdExpr2Node : basl_ExprNode] id-expr -> template-name LT > template-arg-list-opt GT # literal expression # # No need to name these rules since we'll be treating an expression as # a string. # literal-expr -> LITERAL_INT literal-expr -> LITERAL_CHAR literal-expr -> LITERAL_FLOAT literal-expr -> LITERAL_STRING literal-expr -> FALSE literal-expr -> TRUE # expr list opt # expr-list-opt -> expr-list expr-list-opt -> # expr list # # Always assume comma is a separator, never the comma in a comma-expr. # So give it exclusive shift priority (or give expr-list before COMMA # exclusive reduce priority). # # By giving the first rule below a node as well it will make # collecting an expression list a lot easier (see the navigator # basl_GetExprListNav). # [basl_ExprList1Node] expr-list -> expr [basl_ExprList2Node] expr-list -> expr-list COMMA >! expr