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)
This commit is contained in:
rdb 2017-01-15 15:39:34 +01:00
parent e12420571b
commit e2771d39a9
10 changed files with 243 additions and 43 deletions

View File

@ -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
{

View File

@ -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(']');

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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;