res->base.kind = kind;
res->base.parent = current_parent;
- res->base.source_position = token.base.source_position;
+ res->base.source_position = *HERE;
return res;
}
res->base.kind = kind;
res->base.type = type_error_type;
- res->base.source_position = token.base.source_position;
+ res->base.source_position = *HERE;
return res;
}
#endif
}
+#define eat(token_kind) (assert(token.kind == (token_kind)), next_token())
+
static inline bool next_if(token_kind_t const type)
{
if (token.kind == type) {
- next_token();
+ eat(type);
return true;
} else {
return false;
next_if('}');
}
-#define eat(token_kind) (assert(token.kind == (token_kind)), next_token())
-
/**
* Report a parse error because an expected token was not found.
*/
assert(token.kind == T_STRING_LITERAL);
string_t result = token.string.string;
- next_token();
+ eat(T_STRING_LITERAL);
while (token.kind == T_STRING_LITERAL) {
- warn_string_concat(&token.base.source_position);
+ warn_string_concat(HERE);
result = concat_strings(&result, &token.string.string);
- next_token();
+ eat(T_STRING_LITERAL);
}
return result;
&& (look_ahead(1)->kind == ',' || look_ahead(1)->kind == ')')) {
argument->kind = ATTRIBUTE_ARGUMENT_SYMBOL;
argument->v.symbol = token.base.symbol;
- next_token();
+ eat(T_IDENTIFIER);
} else {
/* must be an expression */
expression_t *expression = parse_assignment_expression();
attribute_t **anchor = &first;
eat(T___attribute__);
+ add_anchor_token(')');
+ add_anchor_token(',');
expect('(');
expect('(');
- add_anchor_token(')');
- add_anchor_token(',');
if (token.kind != ')') do {
attribute_t *attribute = parse_attribute_gnu_single();
if (attribute) {
switch (token.kind) {
case '[':
designator = allocate_ast_zero(sizeof(designator[0]));
- designator->source_position = token.base.source_position;
- next_token();
+ designator->source_position = *HERE;
+ eat('[');
add_anchor_token(']');
designator->array_index = parse_constant_expression();
rem_anchor_token(']');
break;
case '.':
designator = allocate_ast_zero(sizeof(designator[0]));
- designator->source_position = token.base.source_position;
- next_token();
+ designator->source_position = *HERE;
+ eat('.');
designator->symbol = expect_identifier("while parsing designator", NULL);
if (!designator->symbol)
return NULL;
}
} else {
expression_t *array_index = designator->array_index;
- assert(designator->array_index != NULL);
+ if (is_constant_expression(array_index) != EXPR_CLASS_CONSTANT)
+ return true;
if (!is_type_array(type)) {
if (is_type_valid(type)) {
} else if (token.kind == T_IDENTIFIER && look_ahead(1)->kind == ':') {
/* GNU-style designator ("identifier: value") */
designator = allocate_ast_zero(sizeof(designator[0]));
- designator->source_position = token.base.source_position;
+ designator->source_position = *HERE;
designator->symbol = token.base.symbol;
eat(T_IDENTIFIER);
eat(':');
/* the compound has a name, check if we have seen it already */
symbol = token.base.symbol;
entity = get_tag(symbol, kind);
- next_token();
+ eat(T_IDENTIFIER);
if (entity != NULL) {
if (entity->base.parent_scope != current_scope &&
if (token.kind == '}') {
errorf(HERE, "empty enum not allowed");
- next_token();
+ eat('}');
return;
}
case T_IDENTIFIER:
symbol = token.base.symbol;
entity = get_tag(symbol, ENTITY_ENUM);
- next_token();
+ eat(T_IDENTIFIER);
if (entity != NULL) {
if (entity->base.parent_scope != current_scope &&
type_t *type;
- expect('(');
add_anchor_token(')');
+ expect('(');
expression_t *expression = NULL;
{
attribute_property_argument_t *const property = allocate_ast_zero(sizeof(*property));
- expect('(');
-
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
+
do {
add_anchor_token('=');
source_position_t pos;
{
eat(T__declspec);
+ add_anchor_token(')');
expect('(');
if (token.kind != ')') {
- add_anchor_token(')');
-
attribute_t **anchor = &first;
do {
while (*anchor != NULL)
*anchor = attribute;
anchor = &attribute->next;
} while (next_if(','));
-
- rem_anchor_token(')');
}
+ rem_anchor_token(')');
expect(')');
return first;
}
bool saw_error = false;
memset(specifiers, 0, sizeof(*specifiers));
- specifiers->source_position = token.base.source_position;
+ specifiers->source_position = *HERE;
while (true) {
specifiers->attributes = parse_attributes(specifiers->attributes);
specifiers->storage_class = class; \
if (specifiers->thread_local) \
goto check_thread_storage_class; \
- next_token(); \
+ eat(token); \
break;
MATCH_STORAGE_CLASS(T_typedef, STORAGE_CLASS_TYPEDEF)
#define MATCH_TYPE_QUALIFIER(token, qualifier) \
case token: \
qualifiers |= qualifier; \
- next_token(); \
+ eat(token); \
break
MATCH_TYPE_QUALIFIER(T_const, TYPE_QUALIFIER_CONST);
} else { \
type_specifiers |= specifier; \
} \
- next_token(); \
+ eat(token); \
break
MATCH_SPECIFIER(T__Bool, SPECIFIER_BOOL, "_Bool");
MATCH_SPECIFIER(T_wchar_t, SPECIFIER_WCHAR_T, "wchar_t");
case T_inline:
- next_token();
+ eat(T_inline);
specifiers->is_inline = true;
break;
#if 0
case T__forceinline:
- next_token();
+ eat(T__forceinline);
specifiers->modifiers |= DM_FORCEINLINE;
break;
#endif
} else {
type_specifiers |= SPECIFIER_LONG;
}
- next_token();
+ eat(T_long);
break;
#define CHECK_DOUBLE_TYPE() \
case T___builtin_va_list:
CHECK_DOUBLE_TYPE();
type = duplicate_type(type_valist);
- next_token();
+ eat(T___builtin_va_list);
break;
case T_IDENTIFIER: {
case '&':
case '*':
errorf(HERE, "discarding stray %K in declaration specifier", &token);
- next_token();
+ eat(T_IDENTIFIER);
continue;
default:
type = allocate_type_zero(TYPE_TYPEDEF);
type->typedeft.typedefe = &entity->typedefe;
- next_token();
+ eat(T_IDENTIFIER);
saw_error = true;
continue;
}
}
}
- next_token();
+ eat(T_IDENTIFIER);
type = typedef_type;
break;
}
do {
entity_t *const entity = allocate_entity_zero(ENTITY_PARAMETER, NAMESPACE_NORMAL, token.base.symbol, HERE);
/* a K&R parameter has no type, yet */
- next_token();
+ eat(T_IDENTIFIER);
if (scope != NULL)
append_entity(scope, entity);
*/
static void parse_parameters(function_type_t *type, scope_t *scope)
{
- eat('(');
add_anchor_token(')');
+ eat('(');
if (token.kind == T_IDENTIFIER &&
!is_typedef_symbol(token.base.symbol) &&
do {
switch (token.kind) {
case T_DOTDOTDOT:
- next_token();
+ eat(T_DOTDOTDOT);
type->variadic = true;
goto parameters_finished;
expression_t *size = NULL;
if (token.kind == '*' && look_ahead(1)->kind == ']') {
array->is_variable = true;
- next_token();
+ eat('*');
} else if (token.kind != ']') {
size = parse_assignment_expression();
errorf(HERE, "no identifier expected in typename");
} else {
env->symbol = token.base.symbol;
- env->source_position = token.base.source_position;
+ env->source_position = *HERE;
}
- next_token();
+ eat(T_IDENTIFIER);
break;
case '(': {
case '[':
case T___attribute__: /* FIXME __attribute__ might also introduce a parameter of a function declarator. */
/* Paranthesized declarator. */
- next_token();
+ eat('(');
add_anchor_token(')');
inner_types = parse_inner_declarator(env);
if (inner_types != NULL) {
*/
static expression_t *parse_string_literal(void)
{
- source_position_t begin = token.base.source_position;
+ source_position_t begin = *HERE;
string_t res = token.string.string;
bool is_wide = (token.kind == T_WIDE_STRING_LITERAL);
next_token();
while (token.kind == T_STRING_LITERAL
|| token.kind == T_WIDE_STRING_LITERAL) {
- warn_string_concat(&token.base.source_position);
+ warn_string_concat(HERE);
res = concat_strings(&res, &token.string.string);
next_token();
is_wide |= token.kind == T_WIDE_STRING_LITERAL;
literal->literal.value.begin = value ? "true" : "false";
literal->literal.value.size = value ? 4 : 5;
- next_token();
+ eat(value ? T_true : T_false);
return literal;
}
}
}
if (*c != '\0') {
- errorf(&token.base.source_position,
- "invalid suffix '%S' on integer constant", suffix);
+ errorf(HERE, "invalid suffix '%S' on integer constant", suffix);
} else if (not_traditional) {
warn_traditional_suffix();
}
type = type_long_double;
}
if (*c != '\0') {
- errorf(&token.base.source_position,
- "invalid suffix '%S' on floatingpoint constant", suffix);
+ errorf(HERE, "invalid suffix '%S' on floatingpoint constant", suffix);
} else if (not_traditional) {
warn_traditional_suffix();
}
}
}
- next_token();
+ eat(T_CHARACTER_CONSTANT);
return literal;
}
warningf(WARN_MULTICHAR, HERE, "multi-character character constant");
}
- next_token();
+ eat(T_WIDE_CHARACTER_CONSTANT);
return literal;
}
static expression_t *parse_reference(void)
{
- source_position_t const pos = token.base.source_position;
+ source_position_t const pos = *HERE;
entity_t *const entity = parse_qualified_identifier();
type_t *orig_type;
return result;
}
-static expression_t *parse_function_keyword(void)
+static expression_t *parse_function_keyword(funcname_kind_t const kind)
{
- /* TODO */
-
if (current_function == NULL) {
- errorf(HERE, "'__func__' used outside of a function");
+ errorf(HERE, "'%K' used outside of a function", &token);
}
expression_t *expression = allocate_expression_zero(EXPR_FUNCNAME);
expression->base.type = type_char_ptr;
- expression->funcname.kind = FUNCNAME_FUNCTION;
+ expression->funcname.kind = kind;
next_token();
return expression;
}
-static expression_t *parse_pretty_function_keyword(void)
-{
- if (current_function == NULL) {
- errorf(HERE, "'__PRETTY_FUNCTION__' used outside of a function");
- }
-
- expression_t *expression = allocate_expression_zero(EXPR_FUNCNAME);
- expression->base.type = type_char_ptr;
- expression->funcname.kind = FUNCNAME_PRETTY_FUNCTION;
-
- eat(T___PRETTY_FUNCTION__);
-
- return expression;
-}
-
-static expression_t *parse_funcsig_keyword(void)
-{
- if (current_function == NULL) {
- errorf(HERE, "'__FUNCSIG__' used outside of a function");
- }
-
- expression_t *expression = allocate_expression_zero(EXPR_FUNCNAME);
- expression->base.type = type_char_ptr;
- expression->funcname.kind = FUNCNAME_FUNCSIG;
-
- eat(T___FUNCSIG__);
-
- return expression;
-}
-
-static expression_t *parse_funcdname_keyword(void)
-{
- if (current_function == NULL) {
- errorf(HERE, "'__FUNCDNAME__' used outside of a function");
- }
-
- expression_t *expression = allocate_expression_zero(EXPR_FUNCNAME);
- expression->base.type = type_char_ptr;
- expression->funcname.kind = FUNCNAME_FUNCDNAME;
-
- eat(T___FUNCDNAME__);
-
- return expression;
-}
-
static designator_t *parse_designator(void)
{
designator_t *const result = allocate_ast_zero(sizeof(result[0]));
eat(T___builtin_offsetof);
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
type_t *type = parse_typename();
rem_anchor_token(',');
expect(',');
return expression;
}
+static bool is_last_parameter(expression_t *const param)
+{
+ if (param->kind == EXPR_REFERENCE) {
+ entity_t *const entity = param->reference.entity;
+ if (entity->kind == ENTITY_PARAMETER &&
+ !entity->base.next &&
+ entity->base.parent_scope == ¤t_function->parameters) {
+ return true;
+ }
+ }
+
+ if (!is_type_valid(skip_typeref(param->base.type)))
+ return true;
+
+ return false;
+}
+
/**
- * Parses a _builtin_va_start() expression.
+ * Parses a __builtin_va_start() expression.
*/
static expression_t *parse_va_start(void)
{
eat(T___builtin_va_start);
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
expression->va_starte.ap = parse_assignment_expression();
rem_anchor_token(',');
expect(',');
expression_t *const param = parse_assignment_expression();
+ expression->va_starte.parameter = param;
rem_anchor_token(')');
expect(')');
- if (param->kind == EXPR_REFERENCE) {
- entity_t *const entity = param->reference.entity;
- if (!current_function->base.type->function.variadic) {
- errorf(¶m->base.source_position,
- "'va_start' used in non-variadic function");
- } else if (entity->base.parent_scope != ¤t_function->parameters ||
- entity->base.next != NULL ||
- entity->kind != ENTITY_PARAMETER) {
- errorf(¶m->base.source_position,
- "second argument of 'va_start' must be last parameter of the current function");
- } else {
- expression->va_starte.parameter = &entity->variable;
- }
- } else {
- expression = create_error_expression();
+ if (!current_function) {
+ errorf(&expression->base.source_position, "'va_start' used outside of function");
+ } else if (!current_function->base.type->function.variadic) {
+ errorf(&expression->base.source_position, "'va_start' used in non-variadic function");
+ } else if (!is_last_parameter(param)) {
+ errorf(¶m->base.source_position, "second argument of 'va_start' must be last parameter of the current function");
}
return expression;
eat(T___builtin_va_arg);
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
call_argument_t ap;
ap.expression = parse_assignment_expression();
expression->va_arge.ap = ap.expression;
eat(T___builtin_va_copy);
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
expression_t *dst = parse_assignment_expression();
assign_error_t error = semantic_assign(type_valist, dst);
report_assign_error(error, type_valist, dst, "call argument 1",
eat(T___builtin_constant_p);
- expect('(');
add_anchor_token(')');
+ expect('(');
expression->builtin_constant.value = parse_assignment_expression();
rem_anchor_token(')');
expect(')');
eat(T___builtin_types_compatible_p);
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
expression->builtin_types_compatible.left = parse_typename();
rem_anchor_token(',');
expect(',');
*/
static expression_t *parse_compare_builtin(void)
{
- expression_t *expression;
-
+ expression_kind_t kind;
switch (token.kind) {
- case T___builtin_isgreater:
- expression = allocate_expression_zero(EXPR_BINARY_ISGREATER);
- break;
- case T___builtin_isgreaterequal:
- expression = allocate_expression_zero(EXPR_BINARY_ISGREATEREQUAL);
- break;
- case T___builtin_isless:
- expression = allocate_expression_zero(EXPR_BINARY_ISLESS);
- break;
- case T___builtin_islessequal:
- expression = allocate_expression_zero(EXPR_BINARY_ISLESSEQUAL);
- break;
- case T___builtin_islessgreater:
- expression = allocate_expression_zero(EXPR_BINARY_ISLESSGREATER);
- break;
- case T___builtin_isunordered:
- expression = allocate_expression_zero(EXPR_BINARY_ISUNORDERED);
- break;
- default:
- internal_errorf(HERE, "invalid compare builtin found");
- }
- expression->base.source_position = *HERE;
+ case T___builtin_isgreater: kind = EXPR_BINARY_ISGREATER; break;
+ case T___builtin_isgreaterequal: kind = EXPR_BINARY_ISGREATEREQUAL; break;
+ case T___builtin_isless: kind = EXPR_BINARY_ISLESS; break;
+ case T___builtin_islessequal: kind = EXPR_BINARY_ISLESSEQUAL; break;
+ case T___builtin_islessgreater: kind = EXPR_BINARY_ISLESSGREATER; break;
+ case T___builtin_isunordered: kind = EXPR_BINARY_ISUNORDERED; break;
+ default: internal_errorf(HERE, "invalid compare builtin found");
+ }
+ expression_t *const expression = allocate_expression_zero(kind);
next_token();
- expect('(');
add_anchor_token(')');
add_anchor_token(',');
+ expect('(');
expression->binary.left = parse_assignment_expression();
rem_anchor_token(',');
expect(',');
eat(T__assume);
- expect('(');
add_anchor_token(')');
+ expect('(');
expression->unary.value = parse_assignment_expression();
rem_anchor_token(')');
expect(')');
/**
* Return the label for the current symbol or create a new one.
*/
-static label_t *get_label(void)
+static label_t *get_label(char const *const context)
{
- assert(token.kind == T_IDENTIFIER);
assert(current_function != NULL);
- entity_t *label = get_entity(token.base.symbol, NAMESPACE_LABEL);
+ symbol_t *const sym = expect_identifier(context, NULL);
+ if (!sym)
+ return NULL;
+
+ entity_t *label = get_entity(sym, NAMESPACE_LABEL);
/* If we find a local label, we already created the declaration. */
if (label != NULL && label->kind == ENTITY_LOCAL_LABEL) {
if (label->base.parent_scope != current_scope) {
} else if (label == NULL || label->base.parent_scope != ¤t_function->parameters) {
/* There is no matching label in the same function, so create a new one. */
source_position_t const nowhere = { NULL, 0, 0, false };
- label = allocate_entity_zero(ENTITY_LABEL, NAMESPACE_LABEL, token.base.symbol, &nowhere);
+ label = allocate_entity_zero(ENTITY_LABEL, NAMESPACE_LABEL, sym, &nowhere);
label_push(label);
}
- eat(T_IDENTIFIER);
return &label->label;
}
*/
static expression_t *parse_label_address(void)
{
- source_position_t source_position = token.base.source_position;
+ source_position_t const source_position = *HERE;
eat(T_ANDAND);
- if (token.kind != T_IDENTIFIER) {
- parse_error_expected("while parsing label address", T_IDENTIFIER, NULL);
+
+ label_t *const label = get_label("while parsing label address");
+ if (!label)
return create_error_expression();
- }
- label_t *const label = get_label();
label->used = true;
label->address_taken = true;
case T_STRING_LITERAL:
case T_WIDE_STRING_LITERAL: return parse_string_literal();
case T___FUNCTION__:
- case T___func__: return parse_function_keyword();
- case T___PRETTY_FUNCTION__: return parse_pretty_function_keyword();
- case T___FUNCSIG__: return parse_funcsig_keyword();
- case T___FUNCDNAME__: return parse_funcdname_keyword();
+ case T___func__: return parse_function_keyword(FUNCNAME_FUNCTION);
+ case T___PRETTY_FUNCTION__: return parse_function_keyword(FUNCNAME_PRETTY_FUNCTION);
+ case T___FUNCSIG__: return parse_function_keyword(FUNCNAME_FUNCSIG);
+ case T___FUNCDNAME__: return parse_function_keyword(FUNCNAME_FUNCDNAME);
case T___builtin_offsetof: return parse_offsetof();
case T___builtin_va_start: return parse_va_start();
case T___builtin_va_arg: return parse_va_arg();
expression_t *expression;
if (token.kind == '(' && is_declaration_specifier(look_ahead(1))) {
source_position_t const pos = *HERE;
- next_token();
+ eat('(');
add_anchor_token(')');
orig_type = parse_typename();
rem_anchor_token(')');
eat(T___builtin_classify_type);
- expect('(');
add_anchor_token(')');
+ expect('(');
expression_t *expression = parse_expression();
rem_anchor_token(')');
expect(')');
}
argument->constraints = parse_string_literals();
- expect('(');
add_anchor_token(')');
+ expect('(');
expression_t *expression = parse_expression();
rem_anchor_token(')');
if (is_out) {
* statement after a label. label:; is commonly used to have a label
* before a closing brace. */
inner_stmt = create_empty_statement();
- next_token();
+ eat(';');
break;
}
/* FALLTHROUGH */
static statement_t *parse_label_statement(void)
{
statement_t *const statement = allocate_statement_zero(STATEMENT_LABEL);
- label_t *const label = get_label();
+ label_t *const label = get_label(NULL /* Cannot fail, token is T_IDENTIFIER. */);
statement->label.label = label;
PUSH_PARENT(statement);
*/
static expression_t *parse_condition(void)
{
- expect('(');
add_anchor_token(')');
+ expect('(');
expression_t *const expr = parse_expression();
mark_vars_read(expr, NULL);
rem_anchor_token(')');
PUSH_PARENT(statement);
PUSH_SCOPE_STATEMENT(&statement->fors.scope);
- expect('(');
add_anchor_token(')');
+ expect('(');
PUSH_EXTENSION();
} else {
statement = allocate_statement_zero(STATEMENT_GOTO);
eat(T_goto);
- if (token.kind == T_IDENTIFIER) {
- label_t *const label = get_label();
+
+ label_t *const label = get_label("while parsing goto");
+ if (label) {
label->used = true;
statement->gotos.label = label;
*goto_anchor = &statement->gotos;
goto_anchor = &statement->gotos.next;
} else {
- if (GNU_MODE)
- parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
- else
- parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
- eat_until_anchor();
statement->gotos.label = &allocate_entity_zero(ENTITY_LABEL, NAMESPACE_LABEL, sym_anonymous, &builtin_source_position)->label;
}
}
entity_t *begin = NULL;
entity_t *end = NULL;
entity_t **anchor = &begin;
+ add_anchor_token(';');
+ add_anchor_token(',');
do {
source_position_t pos;
symbol_t *const symbol = expect_identifier("while parsing local label declaration", &pos);
- if (!symbol)
- goto end_error;
-
- entity_t *entity = get_entity(symbol, NAMESPACE_LABEL);
- if (entity != NULL && entity->base.parent_scope == current_scope) {
- source_position_t const *const ppos = &entity->base.source_position;
- errorf(&pos, "multiple definitions of '%N' (previous definition %P)", entity, ppos);
- } else {
- entity = allocate_entity_zero(ENTITY_LOCAL_LABEL, NAMESPACE_LABEL, symbol, &pos);
- entity->base.parent_scope = current_scope;
+ if (symbol) {
+ entity_t *entity = get_entity(symbol, NAMESPACE_LABEL);
+ if (entity != NULL && entity->base.parent_scope == current_scope) {
+ source_position_t const *const ppos = &entity->base.source_position;
+ errorf(&pos, "multiple definitions of '%N' (previous definition %P)", entity, ppos);
+ } else {
+ entity = allocate_entity_zero(ENTITY_LOCAL_LABEL, NAMESPACE_LABEL, symbol, &pos);
+ entity->base.parent_scope = current_scope;
- *anchor = entity;
- anchor = &entity->base.next;
- end = entity;
+ *anchor = entity;
+ anchor = &entity->base.next;
+ end = entity;
- environment_push(entity);
+ environment_push(entity);
+ }
}
} while (next_if(','));
+ rem_anchor_token(',');
+ rem_anchor_token(';');
expect(';');
-end_error:
statement->declaration.declarations_begin = begin;
statement->declaration.declarations_end = end;
return statement;
if (token.kind == T_IDENTIFIER) {
symbol = token.base.symbol;
- next_token();
-
entity = get_entity(symbol, NAMESPACE_NORMAL);
- if (entity != NULL
- && entity->kind != ENTITY_NAMESPACE
- && entity->base.parent_scope == current_scope) {
- if (is_entity_valid(entity)) {
- error_redefined_as_different_kind(&token.base.source_position,
- entity, ENTITY_NAMESPACE);
- }
+ if (entity && entity->kind != ENTITY_NAMESPACE) {
entity = NULL;
+ if (entity->base.parent_scope == current_scope && is_entity_valid(entity)) {
+ error_redefined_as_different_kind(HERE, entity, ENTITY_NAMESPACE);
+ }
}
+ eat(T_IDENTIFIER);
}
if (entity == NULL) {
case ';':
if (!strict_mode) {
warningf(WARN_STRAY_SEMICOLON, HERE, "stray ';' outside of function");
- next_token();
+ eat(';');
return;
}
/* FALLTHROUGH */