瀏覽代碼

cppparser: support for various C++11/C++14/C++17 features:
- decltype(auto)
- attributes (ie. [[deprecated]]), incl. with C++17 "using"
- extern template class (parses)
- sizeof struct members
- aggregate initialization
- initializers in capture lists
- alignas (parses)

rdb 9 年之前
父節點
當前提交
e2771d39a9

+ 105 - 33
dtool/src/cppparser/cppBison.yxx

@@ -249,6 +249,8 @@ pop_struct() {
 %token XOREQUAL
 %token LSHIFTEQUAL
 %token RSHIFTEQUAL
+%token ATTR_LEFT
+%token ATTR_RIGHT
 
 %token KW_ALIGNAS
 %token KW_ALIGNOF
@@ -874,10 +876,18 @@ storage_class:
 {
   $$ = $2 | (int)CPPInstance::SC_thread_local;
 }
-        | '[' '[' attribute_specifiers ']' ']' storage_class
+        | ATTR_LEFT attribute_specifiers ATTR_RIGHT storage_class
 {
   // Ignore attribute specifiers for now.
-  $$ = $6;
+  $$ = $4;
+}
+        | KW_ALIGNAS '(' const_expr ')' storage_class
+{
+  $$ = $5;
+}
+        | KW_ALIGNAS '(' type_decl ')' storage_class
+{
+  $$ = $5;
 }
         ;
 
@@ -889,6 +899,7 @@ attribute_specifiers:
 attribute_specifier:
         name
         | name '(' formal_parameter_list ')'
+        | KW_USING name ':' attribute_specifier
         ;
 
 type_like_declaration:
@@ -1272,10 +1283,10 @@ function_post:
 {
   $$ = $1;
 }
-/*        | function_post '[' '[' attribute_specifiers ']' ']'
+        | function_post ATTR_LEFT attribute_specifiers ATTR_RIGHT
 {
   $$ = $1;
-}*/
+}
         ;
 
 function_operator:
@@ -1443,7 +1454,8 @@ more_template_declaration:
         ;
 
 template_declaration:
-        KW_TEMPLATE
+        KW_EXTERN template_declaration
+        | KW_TEMPLATE
 {
   push_scope(new CPPTemplateScope(current_scope));
 }
@@ -1451,7 +1463,7 @@ template_declaration:
 {
   pop_scope();
 }
-	| KW_TEMPLATE type_like_declaration
+        | KW_TEMPLATE type_like_declaration
         ;
 
 template_formal_parameters:
@@ -1903,6 +1915,10 @@ function_parameter:
         | KW_REGISTER function_parameter
 {
   $$ = $2;
+}
+        | ATTR_LEFT attribute_specifiers ATTR_RIGHT function_parameter
+{
+  $$ = $4;
 }
         ;
 
@@ -2246,16 +2262,16 @@ type:
 {
   $$ = CPPType::new_type($1);
 }
-        | struct_keyword name
+        | struct_keyword struct_attributes name
 {
-  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
+  CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer);
   if (type != NULL) {
     $$ = type;
   } else {
     CPPExtensionType *et =
-      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
+      CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file))
       ->as_extension_type();
-    CPPScope *scope = $2->get_scope(current_scope, global_scope);
+    CPPScope *scope = $3->get_scope(current_scope, global_scope);
     if (scope != NULL) {
       scope->define_extension_type(et);
     }
@@ -2286,6 +2302,10 @@ type:
     str << *$3;
     yyerror("could not determine type of " + str.str(), @3);
   }
+}
+        | KW_DECLTYPE '(' KW_AUTO ')'
+{
+  $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto));
 }
         | KW_UNDERLYING_TYPE '(' full_type ')'
 {
@@ -2343,16 +2363,16 @@ type_decl:
 {
   $$ = new CPPTypeDeclaration(CPPType::new_type($1));
 }
-        | struct_keyword name
+        | struct_keyword struct_attributes name
 {
-  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
+  CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer);
   if (type != NULL) {
     $$ = type;
   } else {
     CPPExtensionType *et =
-      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
+      CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file))
       ->as_extension_type();
-    CPPScope *scope = $2->get_scope(current_scope, global_scope);
+    CPPScope *scope = $3->get_scope(current_scope, global_scope);
     if (scope != NULL) {
       scope->define_extension_type(et);
     }
@@ -2401,6 +2421,10 @@ type_decl:
     str << *$3;
     yyerror("could not determine type of " + str.str(), @3);
   }
+}
+        | KW_DECLTYPE '(' KW_AUTO ')'
+{
+  $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto));
 }
         | KW_UNDERLYING_TYPE '(' full_type ')'
 {
@@ -2435,16 +2459,16 @@ predefined_type:
 {
   $$ = CPPType::new_type(new CPPTBDType($2));
 }
-        | struct_keyword name
+        | struct_keyword struct_attributes name
 {
-  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
+  CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer);
   if (type != NULL) {
     $$ = type;
   } else {
     CPPExtensionType *et =
-      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
+      CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file))
       ->as_extension_type();
-    CPPScope *scope = $2->get_scope(current_scope, global_scope);
+    CPPScope *scope = $3->get_scope(current_scope, global_scope);
     if (scope != NULL) {
       scope->define_extension_type(et);
     }
@@ -2525,8 +2549,15 @@ full_type:
 }
         ;
 
+struct_attributes:
+        empty
+        | struct_attributes ATTR_LEFT attribute_specifiers ATTR_RIGHT
+        | struct_attributes KW_ALIGNAS '(' const_expr ')'
+        | struct_attributes KW_ALIGNAS '(' type_decl ')'
+        ;
+
 anonymous_struct:
-        struct_keyword '{'
+        struct_keyword struct_attributes '{'
 {
   CPPVisibility starting_vis =
   ($1 == CPPExtensionType::T_class) ? V_private : V_public;
@@ -2550,19 +2581,19 @@ anonymous_struct:
         ;
 
 named_struct:
-        struct_keyword name_no_final
+        struct_keyword struct_attributes name_no_final
 {
   CPPVisibility starting_vis =
   ($1 == CPPExtensionType::T_class) ? V_private : V_public;
 
-  CPPScope *scope = $2->get_scope(current_scope, global_scope, current_lexer);
+  CPPScope *scope = $3->get_scope(current_scope, global_scope, current_lexer);
   if (scope == NULL) {
     scope = current_scope;
   }
-  CPPScope *new_scope = new CPPScope(scope, $2->_names.back(),
+  CPPScope *new_scope = new CPPScope(scope, $3->_names.back(),
                                      starting_vis);
 
-  CPPStructType *st = new CPPStructType($1, $2, current_scope,
+  CPPStructType *st = new CPPStructType($1, $3, current_scope,
                                         new_scope, @1.file);
   new_scope->set_struct_type(st);
   current_scope->define_extension_type(st);
@@ -2945,6 +2976,7 @@ element:
         | SCOPE | PLUSPLUS | MINUSMINUS
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
+        | ATTR_LEFT | ATTR_RIGHT
         | KW_ALIGNAS | KW_ALIGNOF | KW_AUTO | KW_BOOL | KW_CATCH
         | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T | KW_CLASS | KW_CONST
         | KW_CONSTEXPR | KW_CONST_CAST | KW_DECLTYPE | KW_DEFAULT
@@ -3028,6 +3060,18 @@ no_angle_bracket_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+{
+  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
+  if (arg == (CPPDeclaration *)NULL) {
+    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
+  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
+    CPPInstance *inst = arg->as_instance();
+    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
+  } else {
+    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
+  }
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -3190,6 +3234,16 @@ const_expr:
   }
   assert(type != NULL);
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
+}
+        | TYPENAME_IDENTIFIER '{' optional_const_expr_comma '}'
+{
+  // Aggregate initialization.
+  CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
+  assert(type != NULL);
+  $$ = new CPPExpression(CPPExpression::aggregate_init_op(type, $3));
 }
         | KW_INT '(' optional_const_expr_comma ')'
 {
@@ -3270,6 +3324,18 @@ const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+{
+  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
+  if (arg == (CPPDeclaration *)NULL) {
+    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
+  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
+    CPPInstance *inst = arg->as_instance();
+    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
+  } else {
+    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
+  }
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -3602,6 +3668,18 @@ formal_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+{
+  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
+  if (arg == (CPPDeclaration *)NULL) {
+    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
+  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
+    CPPInstance *inst = arg->as_instance();
+    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
+  } else {
+    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
+  }
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -3828,15 +3906,17 @@ capture_list:
 {
   $$ = new CPPClosureType(CPPClosureType::CT_by_reference);
 }
-        | capture
+        | capture maybe_initialize
 {
   $$ = new CPPClosureType();
+  $1->_initializer = $2;
   $$->_captures.push_back(*$1);
   delete $1;
 }
-        | capture_list ',' capture
+        | capture_list ',' capture maybe_initialize
 {
   $$ = $1;
+  $3->_initializer = $4;
   $$->_captures.push_back(*$3);
   delete $3;
 }
@@ -3884,14 +3964,6 @@ class_derivation_name:
     type = CPPType::new_type(new CPPTBDType($1));
   }
   $$ = type;
-}
-        | struct_keyword name
-{
-  CPPType *type = $2->find_type(current_scope, global_scope, true, current_lexer);
-  if (type == NULL) {
-    type = CPPType::new_type(new CPPTBDType($2));
-  }
-  $$ = type;
 }
         | KW_TYPENAME name
 {

+ 13 - 6
dtool/src/cppparser/cppClosureType.cxx

@@ -12,6 +12,7 @@
  */
 
 #include "cppClosureType.h"
+#include "cppExpression.h"
 
 /**
  *
@@ -47,7 +48,7 @@ operator = (const CPPClosureType &copy) {
  * Adds a new capture to the beginning of the capture list.
  */
 void CPPClosureType::
-add_capture(string name, CaptureType type) {
+add_capture(string name, CaptureType type, CPPExpression *initializer) {
   if (type == CT_none) {
     if (name == "this") {
       type = CT_by_reference;
@@ -56,7 +57,7 @@ add_capture(string name, CaptureType type) {
     }
   }
 
-  Capture capture = {move(name), type};
+  Capture capture = {move(name), type, initializer};
   _captures.insert(_captures.begin(), move(capture));
 }
 
@@ -117,19 +118,25 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete) const {
 
   Captures::const_iterator it;
   for (it = _captures.begin(); it != _captures.end(); ++it) {
+    const Capture &capture = *it;
     if (have_capture) {
       out << ", ";
     }
-    if ((*it)._name == "this") {
-      if ((*it)._type == CT_by_value) {
+    if (capture._name == "this") {
+      if (capture._type == CT_by_value) {
         out.put('*');
       }
     } else {
-      if ((*it)._type == CT_by_reference) {
+      if (capture._type == CT_by_reference) {
         out.put('&');
       }
     }
-    out << (*it)._name;
+    out << capture._name;
+
+    if (capture._initializer != NULL) {
+      out << " = " << *capture._initializer;
+    }
+
     have_capture = true;
   }
   out.put(']');

+ 2 - 1
dtool/src/cppparser/cppClosureType.h

@@ -37,13 +37,14 @@ public:
   struct Capture {
     string _name;
     CaptureType _type;
+    CPPExpression *_initializer;
   };
   typedef vector<Capture> Captures;
   Captures _captures;
 
   CaptureType _default_capture;
 
-  void add_capture(string name, CaptureType type);
+  void add_capture(string name, CaptureType type, CPPExpression *initializer = NULL);
 
   virtual bool is_fully_specified() const;
 

+ 62 - 3
dtool/src/cppparser/cppExpression.cxx

@@ -257,13 +257,12 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
       _u._variable = inst;
       return;
     }
-    // Actually, we can't scope function groups.
-    /*CPPFunctionGroup *fgroup = decl->as_function_group();
+    CPPFunctionGroup *fgroup = decl->as_function_group();
     if (fgroup != NULL) {
       _type = T_function;
       _u._fgroup = fgroup;
       return;
-    }*/
+    }
   }
 
   _type = T_unknown_ident;
@@ -347,6 +346,22 @@ construct_op(CPPType *type, CPPExpression *op1) {
   return expr;
 }
 
+/**
+ * Creates an expression that represents an aggregate initialization.
+ */
+CPPExpression CPPExpression::
+aggregate_init_op(CPPType *type, CPPExpression *op1) {
+  CPPExpression expr(0);
+  if (op1 == NULL) {
+    expr._type = T_empty_aggregate_init;
+  } else {
+    expr._type = T_aggregate_init;
+  }
+  expr._u._typecast._to = type;
+  expr._u._typecast._op1 = op1;
+  return expr;
+}
+
 /**
  * Creates an expression that represents a use of the new operator.
  */
@@ -606,6 +621,8 @@ evaluate() const {
 
   case T_construct:
   case T_default_construct:
+  case T_aggregate_init:
+  case T_empty_aggregate_init:
   case T_new:
   case T_default_new:
   case T_sizeof:
@@ -1029,6 +1046,8 @@ determine_type() const {
   case T_reinterpret_cast:
   case T_construct:
   case T_default_construct:
+  case T_aggregate_init:
+  case T_empty_aggregate_init:
     return _u._typecast._to;
 
   case T_new:
@@ -1147,10 +1166,28 @@ determine_type() const {
 
     case 'f': // Function evaluation
       if (t1 != NULL) {
+        // Easy case, function with only a single overload.
         CPPFunctionType *ftype = t1->as_function_type();
         if (ftype != (CPPFunctionType *)NULL) {
           return ftype->_return_type;
         }
+      } else if (_u._op._op1->_type == T_function) {
+        CPPFunctionGroup *fgroup = _u._op._op1->_u._fgroup;
+        if (_u._op._op2 == NULL) {
+          // If we are passing no args, look for an overload that has takes no
+          // args.
+          for (auto it = fgroup->_instances.begin(); it != fgroup->_instances.end(); ++it) {
+            CPPInstance *inst = *it;
+            if (inst != NULL && inst->_type != NULL) {
+              CPPFunctionType *type = inst->_type->as_function_type();
+              if (type != NULL && type->accepts_num_parameters(0)) {
+                return type->_return_type;
+              }
+            }
+          }
+        } else {
+          //TODO
+        }
       }
       return NULL;
 
@@ -1230,11 +1267,13 @@ is_fully_specified() const {
   case T_const_cast:
   case T_reinterpret_cast:
   case T_construct:
+  case T_aggregate_init:
   case T_new:
     return (_u._typecast._to->is_fully_specified() &&
             _u._typecast._op1->is_fully_specified());
 
   case T_default_construct:
+  case T_empty_aggregate_init:
   case T_default_new:
   case T_sizeof:
   case T_alignof:
@@ -1360,6 +1399,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
   case T_const_cast:
   case T_reinterpret_cast:
   case T_construct:
+  case T_aggregate_init:
   case T_new:
     rep->_u._typecast._op1 =
       _u._typecast._op1->substitute_decl(subst, current_scope, global_scope)
@@ -1368,6 +1408,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
     // fall through
 
   case T_default_construct:
+  case T_empty_aggregate_init:
   case T_default_new:
   case T_sizeof:
   case T_alignof:
@@ -1462,6 +1503,8 @@ is_tbd() const {
   case T_const_cast:
   case T_reinterpret_cast:
   case T_construct:
+  case T_aggregate_init:
+  case T_empty_aggregate_init:
   case T_new:
   case T_default_construct:
   case T_default_new:
@@ -1674,6 +1717,18 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << "()";
     break;
 
+  case T_aggregate_init:
+    _u._typecast._to->output(out, indent_level, scope, false);
+    out << "{";
+    _u._typecast._op1->output(out, indent_level, scope, false);
+    out << "}";
+    break;
+
+  case T_empty_aggregate_init:
+    _u._typecast._to->output(out, indent_level, scope, false);
+    out << "{}";
+    break;
+
   case T_new:
     out << "(new ";
     _u._typecast._to->output(out, indent_level, scope, false);
@@ -2095,11 +2150,13 @@ is_equal(const CPPDeclaration *other) const {
   case T_const_cast:
   case T_reinterpret_cast:
   case T_construct:
+  case T_aggregate_init:
   case T_new:
     return _u._typecast._to == ot->_u._typecast._to &&
       *_u._typecast._op1 == *ot->_u._typecast._op1;
 
   case T_default_construct:
+  case T_empty_aggregate_init:
   case T_default_new:
   case T_sizeof:
   case T_alignof:
@@ -2193,6 +2250,7 @@ is_less(const CPPDeclaration *other) const {
   case T_const_cast:
   case T_reinterpret_cast:
   case T_construct:
+  case T_aggregate_init:
   case T_new:
     if (_u._typecast._to != ot->_u._typecast._to) {
       return _u._typecast._to < ot->_u._typecast._to;
@@ -2200,6 +2258,7 @@ is_less(const CPPDeclaration *other) const {
     return *_u._typecast._op1 < *ot->_u._typecast._op1;
 
   case T_default_construct:
+  case T_empty_aggregate_init:
   case T_default_new:
   case T_sizeof:
   case T_alignof:

+ 3 - 0
dtool/src/cppparser/cppExpression.h

@@ -48,6 +48,8 @@ public:
     T_reinterpret_cast,
     T_construct,
     T_default_construct,
+    T_aggregate_init,
+    T_empty_aggregate_init,
     T_new,
     T_default_new,
     T_sizeof,
@@ -81,6 +83,7 @@ public:
 
   static CPPExpression typecast_op(CPPType *type, CPPExpression *op1, Type cast_type = T_typecast);
   static CPPExpression construct_op(CPPType *type, CPPExpression *op1);
+  static CPPExpression aggregate_init_op(CPPType *type, CPPExpression *op1);
   static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL);
   static CPPExpression typeid_op(CPPType *type, CPPType *std_type_info);
   static CPPExpression typeid_op(CPPExpression *op1, CPPType *std_type_info);

+ 25 - 0
dtool/src/cppparser/cppFunctionType.cxx

@@ -66,6 +66,31 @@ operator = (const CPPFunctionType &copy) {
   _class_owner = copy._class_owner;
 }
 
+/**
+ * Returns true if the function accepts the given number of parameters.
+ */
+bool CPPFunctionType::
+accepts_num_parameters(int num_parameters) {
+  if (_parameters == NULL) {
+    return (num_parameters == 0);
+  }
+  size_t actual_num_parameters = _parameters->_parameters.size();
+  // If we passed too many parameters, it must have an ellipsis.
+  if (num_parameters > actual_num_parameters) {
+    return _parameters->_includes_ellipsis;
+  }
+
+  // Make sure all superfluous parameters have a default value.
+  for (size_t i = num_parameters; i < actual_num_parameters; ++i) {
+    CPPInstance *param = _parameters->_parameters[i];
+    if (param->_initializer == NULL) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 /**
  * Returns true if this declaration is an actual, factual declaration, or
  * false if some part of the declaration depends on a template parameter which

+ 2 - 0
dtool/src/cppparser/cppFunctionType.h

@@ -50,6 +50,8 @@ public:
   CPPFunctionType(const CPPFunctionType &copy);
   void operator = (const CPPFunctionType &copy);
 
+  bool accepts_num_parameters(int num_parameters);
+
   CPPType *_return_type;
   CPPParameterList *_parameters;
   int _flags;

+ 22 - 0
dtool/src/cppparser/cppPreprocessor.cxx

@@ -209,6 +209,7 @@ CPPPreprocessor() {
   _state = S_eof;
   _paren_nesting = 0;
   _parsing_template_params = false;
+  _parsing_attribute = false;
   _unget = '\0';
   _last_c = '\0';
   _start_of_line = true;
@@ -986,6 +987,13 @@ internal_get_next_token() {
         return CPPToken(0, loc);
       }
     }
+  } else if (_parsing_attribute) {
+    // If we're parsing an attribute, also keep track of the paren nesting.
+    if (c == '[' || c == '(') {
+      ++_paren_nesting;
+    } else if (c == ']' || c == ')') {
+      --_paren_nesting;
+    }
   }
 
   // Look for an end-of-line comment, and parse it before we finish this
@@ -1090,6 +1098,20 @@ check_digraph(int c) {
     if (next_c == '=') return MODEQUAL;
     if (next_c == '>') return '}';
     break;
+
+  case '[':
+    if (next_c == '[' && !_parsing_attribute) {
+      _parsing_attribute = true;
+      return ATTR_LEFT;
+    }
+    break;
+
+  case ']':
+    if (next_c == ']' && _parsing_attribute && _paren_nesting == 0) {
+      _parsing_attribute = false;
+      return ATTR_RIGHT;
+    }
+    break;
   }
 
   return 0;

+ 1 - 0
dtool/src/cppparser/cppPreprocessor.h

@@ -213,6 +213,7 @@ private:
   State _state;
   int _paren_nesting;
   bool _parsing_template_params;
+  bool _parsing_attribute;
 
   bool _start_of_line;
   int _unget;

+ 8 - 0
dtool/src/cppparser/cppToken.cxx

@@ -252,6 +252,14 @@ output(ostream &out) const {
     out << "RSHIFTEQUAL";
     break;
 
+  case ATTR_LEFT:
+    out << "ATTR_LEFT";
+    break;
+
+  case ATTR_RIGHT:
+    out << "ATTR_RIGHT";
+    break;
+
   case KW_BOOL:
     out << "KW_BOOL";
     break;