/*
* Copyright (c) 2004 - 2007, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Expression parser
*/
#include "expr.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "token.h"
#include "error.h"
#include "misc.h"
#include "defs.h"
#include "decl.h"
#include "scope.h"
#include "type.h"
#include "icode.h"
#include "debug.h"
#include "cc1_main.h"
#include "subexpr.h"
#include "functions.h"
#include "analyze.h"
#include "typemap.h"
#include "symlist.h"
#include "backend.h"
#include "libnwcc.h"
#include "reg.h"
#include "n_libc.h"
void
append_expr(struct expr **head, struct expr **tail, struct expr *e) {
if (*head == NULL) {
*head = *tail = e;
} else {
(*tail)->next = e;
e->prev = *tail;
*tail = (*tail)->next;
}
}
struct expr *
alloc_expr(void) {
struct expr *ret = n_xmalloc(sizeof *ret);
static struct expr nullexpr;
*ret = nullexpr;
return ret;
}
void recover(struct token **tok, int delim, int delim2);
static int
ambig_to_binary(struct token *t) {
int op = *(int *)t->data;
switch (op) {
case TOK_OP_AMB_PLUS:
op = TOK_OP_PLUS;
break;
case TOK_OP_AMB_MINUS:
op = TOK_OP_MINUS;
break;
case TOK_OP_AMB_MULTI:
op = TOK_OP_MULTI;
break;
case TOK_OP_AMB_BAND:
op = TOK_OP_BAND;
break;
case TOK_OP_AMB_COND2:
op = TOK_OP_COND2;
break;
}
if (!IS_BINARY(op) && op != TOK_OP_COND2) {
errorfl(t, "Invalid operator ``%s'' - "
"binary or ternary operator expected", t->ascii);
return -1;
}
*(int *)t->data = op; /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX ok? */
return op;
}
int
expr_ends(struct token *t, int delim, int delim2) {
int type;
if (t->type == TOK_OPERATOR) {
type = *(int *)t->data;
} else {
type = t->type;
}
if (type == delim || type == delim2) {
return 1;
}
return 0;
}
/*
* Get lowest precedence operator in an expression. Note that the
* conditional operator requires special handling; In
* foo? bar: baz;
* ... ? and : act like parentheses, i.e. lower precedence operators
* such as the comma operator may occur between them.
*/
static struct operator *
get_lowest_prec(struct expr *ex, struct expr **endp, int want_condop) {
int lowest = 100;
int cond_expr = 0;
struct expr *lptr = NULL;
*endp = NULL;
for (; ex != NULL; ex = ex->next) {
struct operator *tmp;
if (ex->op == 0 || ex->used) {
continue;
}
tmp = &operators[LOOKUP_OP2(ex->op)];
/*
* 08/22/07: This flag had to be added because otherwise
* the search loop for the second part of the conditional
* operator didn't work right in
*
* 1? 0: 1? 1,1 : 2;
*/
if (want_condop
&& ex->op != TOK_OP_COND
&& ex->op != TOK_OP_COND2) {
continue;
}
if (!cond_expr) {
if (tmp->prec < lowest) {
lowest = tmp->prec;
lptr = ex;
} else if (tmp->prec == lowest) {
if (tmp->assoc != OP_ASSOC_RIGHT) {
lptr = ex;
}
}
}
if (ex->op == TOK_OP_COND) {
++cond_expr;
} else if (ex->op == TOK_OP_COND2) {
if (cond_expr == 0) {
if (want_condop) {
/*
* Added... Otherwise stuff breaks
* because ? is selected rather than
* :
*/
lptr = ex;
break;
}
}
--cond_expr;
}
}
*endp = lptr;
return lptr? (void *)&operators[LOOKUP_OP2(lptr->op)]: (void *)NULL;
}
static struct expr *
bind_operators(struct expr *ex) {
struct operator *op;
struct expr *endp;
struct expr *start = ex;
if (ex == NULL) return NULL;
if ((op = get_lowest_prec(ex, &endp, 0)) == NULL) {
/* No more operators left */
if (!ex->used) {
endp = ex;
endp->used = 1;
}
else endp = NULL;
return endp;
} else if (op->value == TOK_OP_COND) {
struct expr *endp2;
struct expr *ex2 = endp->next;
/*
* Need to get second part of conditional operator. This
* requires skipping intermediate conditional operators.
*
* 08/22/07: This loop is nonsense since we added the
* want_condop flag to get_lowest_prec()
*/
/* for (;;) {*/
if (ex2 == NULL
|| (op = get_lowest_prec(ex2, &endp2, 1))
== NULL) {
errorfl(ex2? ex2->tok: endp->tok,
"Parse error - missing second part"
" of conditional operator");
return NULL;
}
#if 0
} else if (op->value == TOK_OP_COND) {
++cond1;
} else if (op->value == TOK_OP_COND2) {
if (cond1 == 0) {
/* done! */
break;
} else {
--cond1;
}
}
#endif
/* ex2 = endp2->next;*/
/* } */
/*
* At this point, endp points to ``?'' and endp2 to ``:''. We
* bind them such that:
* Given x = ``?''
* ... x->left = cond-expr
* ... x->right = :
* ... x->right->left = expr-nonzero
* ... x->right->right = expr-zero
*/
endp->used = endp2->used = 1;
ex = endp->next;
endp->next = NULL;
endp->left = bind_operators(start);
endp->right = endp2;
ex2 = endp2->next;
endp2->next = NULL;
endp->right->left = bind_operators(ex);
endp->right->right = bind_operators(ex2);
return endp;
}
endp->used = 1;
ex = endp->next;
endp->next = NULL;
endp->left = bind_operators(start);
endp->right = bind_operators(ex);
return endp;
}
static void
append_init_list(
struct initializer **init,
struct initializer **init_tail,
struct initializer *i) {
if (i->type == 0) abort();
if (*init == NULL) {
*init = *init_tail = i;
} else {
(*init_tail)->next = i;
i->prev = *init_tail;
*init_tail = (*init_tail)->next;
}
}
struct initializer *
alloc_initializer(void) {
static struct initializer nullinit;
struct initializer *ret;
ret = n_xmalloc(sizeof *ret);
*ret = nullinit;
if (ret == (void *)0x80c46c0) abort();
return ret;
}
static void
conv_init(struct tyval *tv, struct type *toty, struct token *t) {
struct type *fromty = tv->type;
struct type_node *ltn;
struct type_node *rtn;
if (toty->tlist == NULL) {
/* Simple basic type conversion */
if (tv->address != NULL) {
/* Must be pointer to int cast... Dangerous!!! */
return;
}
if (toty->code != fromty->code) {
cross_do_conv(tv, toty->code);
}
return;
}
if (fromty->tlist == NULL ||
(toty->code != fromty->code
&& (toty->code != TY_VOID && fromty->code != TY_VOID))) {
/* this bullshit doesn't work */
return;
#if 0
#if 0
/* XXX */ && (!IS_CHAR(toty->code) || !IS_CHAR(fromty->code)))) {
#endif
if (!tv->is_nullptr_const) {
errorfl(t, "Initializer of incompatible type");
return;
} else {
/* Explains various mysteries - Don't check tlists */
return;
}
#endif
}
/*
* Must be assignment to pointer since this function is only
* called for scalar types
*/
if (tv->is_nullptr_const) {
return;
}
if (toty->code == TY_VOID
&& toty->tlist->type == TN_POINTER_TO
&& toty->tlist->next == NULL) {
/* Assignment to void pointer */
return;
}
ltn = toty->tlist;
rtn = fromty->tlist;
if (rtn && rtn->type == TN_FUNCTION) {
if (ltn->type == TN_POINTER_TO) {
/* Probably pointer to function */
ltn = ltn->next;
}
}
for (; ltn != NULL && rtn != NULL;
ltn = ltn->next, rtn = rtn->next) {
if (ltn->type != rtn->type) {
if (rtn->type == TN_ARRAY_OF
&& rtn == fromty->tlist) {
/* OK - assign array address to ptr */
;
} else {
errorfl(t,
"Initializer of incompatible type");
return;
}
}
}
if (ltn != rtn) {
/* One type list is longer */
errorfl(t, "Initializer of incompatible type");
return;
}
}
struct desig_init_data {
struct initializer **init_head;
struct initializer **init_tail;
struct initializer *cur_init_ptr;
struct sym_entry *highest_encountered_member;
int highest_encountered_index;
};
static void
replace_cur_init(struct desig_init_data *data, struct initializer *init) {
if (data->cur_init_ptr->prev) {
data->cur_init_ptr->prev->next = init;
} else {
*data->init_head = init;
}
if (data->cur_init_ptr->next) {
data->cur_init_ptr->next->prev = init;
} else {
*data->init_tail = init;
}
init->next = data->cur_init_ptr->next;
init->prev = data->cur_init_ptr->prev;
free/*initializer*/(data->cur_init_ptr);
data->cur_init_ptr = init->next;
}
/*
* Put an initializer into the designated initializer list. As soon as the
* first designated initializer is encountered, this function also has to
* be called for all subsequent initializers, since it may have trashed the
* initializer order.
*
* There are four cases to handle here:
*
* - The initializer is itself designated, and it jumps forward. If that
* happens we create a null initializer for every skipped member (this element
* may have to be replaced later if another designated initializer jumps back
* in the order)
*
* - The initializer is itself designated, and it jumps backward. In that
* case we have to jump back and OVERWRITE an existing explicit data or null
* initializer. Also, the current ``initializer pointer'' must be moved to
* that location
*
* - The initializer is not designated, but overwrites an existing
* initializer because the current initializer pointer points to one
*
* - The initializer is not designated and just appended at the end of
* the list
*/
static void
put_desig_init_struct(struct desig_init_data *data,
struct sym_entry *desig_se,
struct sym_entry *prev_se,
struct sym_entry *whole_struct,
struct initializer *init,
struct token *tok,
int *items_read) {
int skipped = 0;
if (desig_se != NULL
&& prev_se != NULL
&& desig_se->dec->offset == prev_se->dec->offset) {
/*
* Designating the initializer which would we done now
* anyway - ignore designation.
*/
desig_se = NULL;
}
if (desig_se == NULL) {
/* Not designated, thank heaven */
if (data->cur_init_ptr == NULL) {
/* At end too! */
append_init_list(data->init_head,
data->init_tail, init);
} else {
/*
* We have to trash an existing initializer
*/
if (data->cur_init_ptr->type != INIT_NULL) {
warningfl(tok, "Member `%s' already has an "
"initializer",
prev_se->dec->dtype->name);
}
replace_cur_init(data, init);
}
} else {
/*
* Designated! But is it forward or backward?
*/
if (prev_se == NULL) {
/*
* Brute-force search for the target
*/
struct initializer *tmpinit;
tmpinit = *data->init_head;
for (prev_se = whole_struct;
prev_se != NULL;
prev_se = prev_se->next) {
if (prev_se->dec->offset == desig_se->dec->offset) {
break;
}
if (tmpinit->next == NULL) {
/*
* Fill holes with null initializers
*/
struct initializer *nullb;
nullb = backend->make_null_block(NULL,
prev_se->next->dec->dtype,
NULL, 1);
nullb->prev = tmpinit;
tmpinit->next = nullb;
}
tmpinit = tmpinit->next;
++skipped;
}
if (tmpinit->prev) {
tmpinit->prev->next = init;
} else {
*data->init_head = init;
}
if (tmpinit->next) {
tmpinit->next->prev = init;
} else {
*data->init_tail = init;
}
init->prev = tmpinit->prev;
init->next = tmpinit->next;
data->cur_init_ptr = tmpinit->next;
free/*_initializer XXX */(tmpinit);
*items_read = skipped;
if (skipped == 0) {
*data->init_head = init;
} else if (skipped > *items_read) {
*data->init_tail = init;
}
} else if (desig_se->dec->offset > prev_se->dec->offset) {
int highest_offset =
data->highest_encountered_member?
(int)data->highest_encountered_member->dec->offset:
-1;
/*
* Forward - create a null initializer for every
* skipped member if necessary
*/
while (prev_se->dec->offset < desig_se->dec->offset) {
if ((signed long)prev_se->dec->offset
> highest_offset) {
struct initializer *nullb;
nullb = backend->make_null_block(NULL,
prev_se->dec->dtype, NULL, 1);
append_init_list(data->init_head,
data->init_tail, nullb);
}
if (data->cur_init_ptr) {
data->cur_init_ptr =
data->cur_init_ptr->next;
}
++skipped;
prev_se = prev_se->next;
}
if (data->cur_init_ptr) {
if (data->cur_init_ptr->type != INIT_NULL) {
warningfl(tok, "Member `%s' already "
"has an initializer",
desig_se->dec->dtype->name);
}
replace_cur_init(data, init);
} else {
append_init_list(data->init_head,
data->init_tail,
init);
}
*items_read += skipped;
} else {
/*
* Backward. First search the target initializer.
* We can exploit the fact that every member behind
* us has one initializer already (null if skipped.)
*/
struct initializer *tmp;
tmp = *data->init_tail;
prev_se = prev_se->prev;
while (prev_se->dec->offset > desig_se->dec->offset) {
prev_se = prev_se->prev;
tmp = tmp->prev;
++skipped;
}
*items_read -= skipped;
if (tmp->prev) {
tmp->prev->next = init;
} else {
*data->init_head = init;
}
if (tmp->next) {
tmp->next->prev = init;
} else {
*data->init_tail = init;
}
init->next = tmp->next;
init->prev = tmp->prev;
if (desig_se == whole_struct) {
/* Start */
*data->init_head = init;
}
free/*_initializer XXX */(tmp);
data->cur_init_ptr = init->next;
}
}
if (desig_se != NULL) {
if (data->highest_encountered_member == NULL
|| data->highest_encountered_member->dec->offset
< desig_se->dec->offset) {
data->highest_encountered_member = desig_se;
}
} else {
if (data->cur_init_ptr == NULL) {
/* Appending at end */
data->highest_encountered_member = prev_se;
}
}
}
static void
put_desig_init_array(struct desig_init_data *data,
int desig_elem,
int *items_read,
struct initializer *init,
struct type *elem_type,
struct token *tok) {
int skipped = 0;
if (desig_elem == *items_read) {
/*
* Designating the initializer which would we done now
* anyway - ignore designation.
*/
desig_elem = -1;
}
if (desig_elem == -1) {
/* Not designated, thank heaven */
if (data->cur_init_ptr == NULL) {
/* At end too! */
append_init_list(data->init_head,
data->init_tail, init);
} else {
/*
* We have to trash an existing initializer
*/
if (data->cur_init_ptr->type != INIT_NULL) {
warningfl(tok, "Element %d already has an "
"initializer",
items_read);
}
replace_cur_init(data, init);
}
} else {
/*
* Designated! But is it forward or backward?
*/
if (desig_elem > *items_read) {
/*
* Forward - create a null initializer for every
* skipped member
*/
while (*items_read + skipped < desig_elem) {
if (*items_read + skipped >
data->highest_encountered_index) {
struct initializer *nullb;
nullb = backend->make_null_block(NULL,
elem_type, NULL, 1);
append_init_list(data->init_head,
data->init_tail, nullb);
}
if (data->cur_init_ptr) {
data->cur_init_ptr =
data->cur_init_ptr->next;
}
++skipped;
}
if (data->cur_init_ptr) {
warningfl(tok, "Element %d already has an "
"initializer", desig_elem);
replace_cur_init(data, init);
} else {
append_init_list(data->init_head,
data->init_tail, init);
}
*items_read += skipped;
data->cur_init_ptr = NULL;
} else {
/*
* Backward. First search the target initializer.
* We can exploit the fact that every member behind
* us has one initializer already (null if skipped.)
*/
struct initializer *tmp;
tmp = *data->init_tail;
++skipped;
while (*items_read - skipped > desig_elem) {
tmp = tmp->prev;
++skipped;
}
*items_read -= skipped;
if (tmp->prev) {
tmp->prev->next = init;
} else {
*data->init_head = init;
}
if (tmp->next) {
tmp->next->prev = init;
} else {
*data->init_tail = init;
}
init->next = tmp->next;
init->prev = tmp->prev;
free/*_initializer XXX */(tmp);
data->cur_init_ptr = init->next;
}
}
if (desig_elem != -1) {
if (desig_elem > data->highest_encountered_index) {
data->highest_encountered_index = desig_elem;
}
} else {
if (data->cur_init_ptr == NULL) {
/* Appending at end */
data->highest_encountered_index = *items_read;
}
}
}
struct initializer *
get_init_expr(struct token **tok, int type, struct type *lvalue, int initial,
int complit) {
struct token *t = *tok;
struct expr *ex;
struct initializer *ret = NULL;
struct initializer *rettail = NULL;
struct initializer *init;
struct sym_entry *se = NULL;
struct type *curtype = NULL;
struct type arrtype;
struct desig_init_data init_data;
struct sym_entry *prevse = NULL;
int items_read = 0;
int items_ok = 0;
int is_aggregate = 0;
int is_array = 0;
int is_char_array = 0;
int is_braced_string = 0;
int warned = 0;
int needbrace = 0;
int have_desig = 0;
init_data.init_head = &ret;
init_data.init_tail = &rettail;
init_data.cur_init_ptr = NULL;
init_data.highest_encountered_member = NULL;
init_data.highest_encountered_index = -1;
if (lvalue->tlist != NULL
&& lvalue->tlist->type == TN_ARRAY_OF) {
is_aggregate = 1;
is_array = 1;
items_ok = lvalue->tlist->arrarg_const;
if (!initial && items_ok == 0) {
errorfl(t, "Array size for nested array dimension "
"unspecified");
return NULL;
}
arrtype = *lvalue;
curtype = &arrtype;
arrtype.tlist = arrtype.tlist->next;
if (IS_CHAR(lvalue->code) && lvalue->tlist->next == NULL) {
is_char_array = 1;
}
} else if (lvalue->code == TY_STRUCT
&& lvalue->tlist == NULL) {
is_aggregate = 1;
se = lvalue->tstruc->scope->slist;
curtype = se->dec->dtype;
/* XXX should set items_ok ?!?!? */
if (initial && t->type != TOK_COMP_OPEN) {
/*
* 09/07/07: This always used EXPR_INIT instead of
* type!! (breaks for constant initializers like
* compound literals)
*/
if (type == EXPR_OPTCONSTINIT) {
type = EXPR_INIT;
}
ex = parse_expr(&t, TOK_OP_COMMA, TOK_SEMICOLON,
type, 1);
if (ex == NULL) {
return NULL;
}
if (ex->const_value && ex->const_value->static_init) {
init = ex->const_value->static_init;
} else {
init = alloc_initializer();
init->type = INIT_STRUCTEXPR;
init->left_type = dup_type(curtype);
init->data = ex;
}
*tok = t;
return init;
}
} else if (lvalue->code == TY_UNION
&& lvalue->tlist == NULL) {
se = lvalue->tstruc->scope->slist;
curtype = se->dec->dtype;
items_ok = 1;
if (initial && t->type != TOK_COMP_OPEN) {
/* XXX duplicates stuff for TY_STRUCT above */
if (type == EXPR_OPTCONSTINIT) {
type = EXPR_INIT;
}
ex = parse_expr(&t, TOK_OP_COMMA, TOK_SEMICOLON,
type, 1);
if (ex == NULL) {
return NULL;
}
if (ex->const_value && ex->const_value->static_init) {
init = ex->const_value->static_init;
} else {
init = alloc_initializer();
init->left_type = dup_type(curtype);
init->type = INIT_STRUCTEXPR;
init->data = ex;
}
*tok = t;
return init;
}
} else {
struct initializer *was_nullblock = NULL;
int was_varinit = 0;
/* The following are for variable struct/array inits */
struct token *starttok;
int delim1;
int delim2;
/* Only one item */
if (t->type == TOK_COMP_OPEN) {
/*
* Compound initializers are also allowed for
* non-aggregate types;
* int x = { 0 };
*/
t = t->next;
starttok = t;
ex = parse_expr(&t, TOK_COMP_CLOSE, TOK_OP_COMMA,type,1);
delim1 = TOK_COMP_CLOSE;
delim2 = TOK_OP_COMMA;
if (t->type == TOK_OP_COMMA
&& t->next != NULL
&& t->next->type == TOK_COMP_CLOSE) {
/* Trailing comma permitted in compound init */
t = t->next->next;
} else if (t->type != TOK_COMP_CLOSE) {
/*
* There may be a trailing comma here. BitchX
* uses that
*/
if (t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_COMMA
&& t->next
&& t->next->type == TOK_COMP_CLOSE) {
t = t->next->next;
} else {
errorfl(t, "Invalid initializer");
return NULL;
}
} else {
t = t->next;
}
} else {
starttok = t;
delim1 = TOK_OP_COMMA;
delim2 = initial? TOK_SEMICOLON: TOK_COMP_CLOSE;
ex = parse_expr(&t, TOK_OP_COMMA,
initial? TOK_SEMICOLON: TOK_COMP_CLOSE, type,1);
}
if (ex == NULL) {
*tok = t;
return NULL;
}
if (type == EXPR_CONSTINIT || type == EXPR_OPTCONSTINIT) {
if (ex->const_value != NULL) {
if (ex->const_value->static_init != NULL) {
/*
* Initialized with value of other static
* variable
*/
struct initializer *tmp;
tmp = ex->const_value->static_init;
if (tmp->type == INIT_EXPR) {
struct expr *tmpex = tmp->data;
ex->const_value = tmpex->const_value;
} else {
/* INIT_NULL */
was_nullblock = tmp->data;
}
}
/*
* Convert constant initializer to destination
* type
*/
if (!was_nullblock) {
conv_init(ex->const_value, lvalue, t->prev);
/*
* 08/03/07: To make
* static long nonsense = (long)&addr;
* work
*/
if (ex->const_value->address != NULL
&& is_integral_type(lvalue)) {
;
} else {
ex->const_value->type = ex->type
= n_xmemdup(lvalue, sizeof *lvalue);
}
} else {
/* XXX */
unimpl();
}
} else {
if (type == EXPR_OPTCONSTINIT
&& !ex->is_const) {
/*
* This is a non-constant expression
* used as initializer for a struct
* or an array. We have to save the
* expression and evaluate it when
* the initializer assignment takes
* place
*/
was_varinit = 1;
ex = parse_expr(&starttok, delim1,
delim2, 0,1);
t = starttok;
if (ex == NULL) {
*tok = t;
return NULL;
}
/*
* Initialize value to 0 for now
*/
was_nullblock = backend->
make_null_block(NULL, lvalue,
NULL, 1);
was_nullblock->varinit = ex;
} else {
return NULL;
}
}
}
if (was_nullblock) {
ret = init = was_nullblock;
} else {
ret = init = alloc_initializer();
init->type = INIT_EXPR;
init->data = ex;
}
init->left_type = dup_type(lvalue);
*tok = t;
return init;
}
if (t->type == TOK_COMP_OPEN && is_char_array) {
if (t->next && t->next->type == TOK_STRING_LITERAL) {
/*
* This must be a braces-enclosed string constant;
* char buf[] = { "hello" };
* The GNU people love using unnecessary braces
* (and parentheses) like this
*/
t = t->next;
is_braced_string = 1;
}
}
/*
* 08/22/07: Null initializers for strings were completely ignored!
* E.g. in
*
* static char buf[10] = "hehe";
*
* ... only 5 bytes would be allocated for the buffer, since there
* were only 5 bytes of initialized data! Same thing with string
* initializers for nested array initializers. So now we resize the
* string and add any necessary null bytes
*/
if (t->type == TOK_STRING_LITERAL) {
struct ty_string *ts = t->data;
size_t newlen;
if (lvalue->tlist != NULL
&& lvalue->tlist->type == TN_ARRAY_OF
&& (newlen = lvalue->tlist->arrarg_const) > ts->size) {
ts->str = n_xrealloc(ts->str, newlen);
memset(ts->str + ts->size - 1, 0, newlen - ts->size);
ts->size = newlen;
ts->ty->tlist->arrarg_const = newlen;
}
}
if (t->type != TOK_COMP_OPEN) {
if (t->type == TOK_STRING_LITERAL
&& is_array) {
struct token *tmp = t;
if (lvalue->tlist->next != NULL
|| !IS_CHAR(lvalue->code)) {
errorfl(t, "Invalid initializer");
return NULL;
} else if (lvalue->sign == TOK_KEY_UNSIGNED) {
#if 0
warningfl(t,
"Assigning string constant to array of `unsigned char'");
#endif
}
/*
* 09/01/07: The extra } check was needed because
* othrwise
*
* char foo[1][2] = { "hello" };
* breaks, since "hello" is an un-braced array
* initializer (the braces belong to the outer
* array), and we ended up looking for a , or ;
*
* XXX MAybe this still isn't fully correct
*/
if (is_braced_string
|| (t->next && t->next->type == TOK_COMP_CLOSE)) {
/*
* Read up to closing brace
*/
ex = parse_expr(&t, TOK_COMP_CLOSE, 0, type, 1);
} else {
/*
* Read up to comma or semicolon
*/
ex = parse_expr(&t, TOK_OP_COMMA, TOK_SEMICOLON, type,
1);
}
if (ex == NULL) {
return NULL;
}
if (t != tmp->next) {
tmp = tmp->next? tmp->next: t;
errorfl(tmp, "Parse error at `%s'",
tmp->ascii);
return NULL;
}
if (is_braced_string && t != NULL) {
/* Skip brace */
t = t->next;
}
init = alloc_initializer();
init->left_type = dup_type(lvalue);
init->data = ex;
init->type = INIT_EXPR;
*tok = t;
if (lvalue->tlist->arrarg->const_value != NULL) {
(void) backend->get_sizeof_type(lvalue, NULL);
}
if (lvalue->tlist->arrarg_const == 0) {
lvalue->tlist->arrarg_const =
strlen(t->prev->ascii) + 1;
}
return init;
} else if (initial) {
/* Initial array initializer requires braces */
errorfl(t, "Parse error at `%s'", t->ascii);
return NULL;
} else if (is_aggregate) {
warningfl(t,
"Nested aggregate initializer not surrounded by braces");
needbrace = 0;
}
} else {
needbrace = 1;
if (next_token(&t) != 0) {
return NULL;
}
}
do {
struct token *starttok = t;
struct sym_entry *desig_se = NULL;
struct sym_entry *prev_se = se;
int desig_elem = -1;
if ((t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_STRUMEMB)
|| (t->type == TOK_IDENTIFIER
&& t->next
&& t->next->type == TOK_OPERATOR
&& *(int *)t->next->data == TOK_OP_AMB_COND2)) {
int is_c99_style = 0;
/*
* Designated initializer -
*
* .membername = expr
*
* ... as per C99 or
*
* membername: expr
*
* ... as per GNU C (deprecated)
*/
have_desig = 1;
if (is_array) {
/*
* Only allowed for structs! (Note that we
* can't check ``se'' because it may be
* NULL here;
*
* struct foo { int x, y; }
* f = { .y = 0, .x = 0 };
* ^ NULL here
*/
errorfl(t, "Invalid designated initializer");
return NULL;
}
if (t->type != TOK_IDENTIFIER) {
is_c99_style = 1;
/* Skip . */
if (next_token(&t) != 0) {
return NULL;
}
if (t->type != TOK_IDENTIFIER) {
errorfl(t, "Syntax error - "
"identifier expected");
return NULL;
}
} else {
warningfl(t, "GNU C designated initializiers "
"are deprecated, use the C99 way "
"instead (`.member = expr' instead "
"of `member: expr')");
}
desig_se = lookup_symbol_se(
lvalue->tstruc->scope, t->data, 0);
if (desig_se == NULL) {
errorfl(t, "Unknown member `%s'", t->data);
return NULL;
}
if (next_token(&t) != 0) {
return NULL;
}
if (is_c99_style) {
if (t->type != TOK_OPERATOR
|| *(int *)t->data != TOK_OP_ASSIGN) {
errorfl(t, "Syntax error at `%s' - "
"`=' expected", t->next->ascii);
return NULL;
}
}
/* Skip : (GNU C) or = (C99) */
if (next_token(&t) != 0) {
return NULL;
}
se = desig_se;
curtype = se->dec->dtype;
} else if (t->type == TOK_ARRAY_OPEN) {
struct expr *ex;
size_t elem;
have_desig = 1;
if (next_token(&t) != 0) {
return NULL;
}
ex = parse_expr(&t, TOK_ARRAY_CLOSE, 0, EXPR_CONST, 1);
if (ex == NULL) {
return NULL;
}
elem = cross_to_host_size_t(ex->const_value);
if ((ssize_t)elem < 0) {
errorfl(t, "Negative and very large designated "
"array elements are not allowed");
return NULL;
} else if (elem > items_ok && items_ok != 0) {
errorfl(t, "Array index %lu out of bounds",
(unsigned long)elem);
return NULL;
} else if (elem > INT_MAX) {
errorfl(t, "Array index %lu too large",
(unsigned long)elem);
return NULL;
}
if (next_token(&t) != 0
|| t->type != TOK_OPERATOR
|| *(int *)t->data != TOK_OP_ASSIGN) {
if (t != NULL) {
errorfl(t, "Syntax error at `%s' - ",
"`=' expected",
t->ascii);
}
return NULL;
}
if (next_token(&t) != 0) {
return NULL;
}
desig_elem = (int)elem;
}
init = get_init_expr(&t, type, curtype, 0, 0);
if (init == NULL) {
return NULL;
}
if (is_basic_agg_type(curtype)) {
struct initializer *init2;
init2 = alloc_initializer();
init2->left_type = dup_type(curtype);
init2->type = INIT_NESTED;
init2->data = init;
init = init2;
/*append_init_list(&ret, &rettail, init2);*/
} else {
/*append_init_list(&ret, &rettail, init);*/
}
if (is_array) {
/* append_init_list(&ret, &rettail, init);*/
put_desig_init_array(&init_data, desig_elem,
&items_read,
init,
curtype,
NULL);
} else {
put_desig_init_struct(&init_data, desig_se,
prev_se,
lvalue->tstruc->scope->slist,
init,
NULL, &items_read);
}
++items_read;
if (items_ok != 0) {
if (items_read > items_ok && !warned) {
if (!is_aggregate) {
errorfl(starttok,
"Invalid aggregate initializer");
} else {
errorfl(starttok,
"Initializer for aggregate type too big");
}
warned = 1;
} else if (!needbrace && items_read == items_ok) {
/* We're done */
t = t->prev;
break;
}
}
if (se != NULL) {
prevse = se;
se = se->next;
if (se != NULL) {
/* XXX error msg if se = NULL maybe? */
curtype = se->dec->dtype;
}
}
/*
* 08/05/07: The code below was previously BEFORE the se
* update above! Thus if there was a trailing comma, the
* make_null_block() call at the bottom would wrongly
* create a null initializer for this structure member
* even though it already has an initializer
*
* This broke some vim option structs
*/
if (t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_COMMA
&& t->next != NULL
&& t->next->type == TOK_COMP_CLOSE) {
/* Trailing comma permitted in compound init. */
t = t->next;
break;
}
if (t->type == TOK_SEMICOLON) break; /* XXXXXXXX */
} while (!expr_ends(t, TOK_COMP_CLOSE, 0) && (t = t->next) != NULL);
if (t->type != TOK_SEMICOLON) /* XXXXXXXXXXX */
t = t->next;
*tok = t;
if (is_array && items_ok == 0) {
lvalue->tlist->arrarg_const = items_ok = items_read;
}
if (initial
&& t->type != TOK_SEMICOLON
&& (t->type != TOK_OPERATOR
|| *(int *)t->data != TOK_OP_COMMA)) {
if (!complit) {
errorfl(t, "Parse error at `%s'- expected semicolon or comma",
t->ascii);
return NULL;
}
}
/*
* Remaining fields are initialized to 0. This is very straightforward
* for normal initializers - just initialize the rest of the struct
* or array - and requires brute-force for designated initializers
*/
if (have_desig) {
init = NULL;
if (is_array) {
if (items_ok != 0) {
int remaining =
items_ok -
(init_data.highest_encountered_index+1);
if (remaining < 0) {
puts("BUG: ??????");
abort();
} else if (remaining > 0) {
init = backend->
make_null_block(NULL, curtype,
NULL, remaining);
}
}
} else {
struct initializer *tmp;
se = lvalue->tstruc->scope->slist;
tmp = ret;
for (; se != NULL; se = se->next) {
if (tmp->next == NULL) {
break;
}
tmp = tmp->next;
}
se = se->next;
if (se != NULL) {
init = backend->make_null_block(se, NULL, lvalue, 0);
}
}
if (init != NULL) {
append_init_list(&ret, &rettail, init);
}
} else {
if (items_read < items_ok || se != NULL) {
/* Remaining fields are initialized to 0 */
if (se != NULL) {
/* Structure */
init = backend->
make_null_block(se, NULL, lvalue, 0);
} else {
/* Array */
int remaining = items_ok - items_read;
init = backend->
make_null_block(NULL, curtype,
NULL, remaining);
}
if (init != NULL) {
append_init_list(&ret, &rettail, init);
}
}
}
return ret;
}
/*
* Parses an expression and returns a pointer to the parse tree.
*
* 09/07/07: Finally the ``nesting'' variable is gone! Instead the caller
* passes an ``initial'' flag, which states whether this is the initial
* call to get a particular expression.
*
* Only if ``initial'' is set, will constant expression results be
* evaluated before returning
*/
struct expr *
parse_expr(struct token **tok, int delim, int delim2, int type, int initial) {
struct token *t;
struct token *tokstart = *tok;
struct expr *ret = NULL;
struct expr *rettail = NULL;
struct expr *ex = NULL;
struct s_expr *s_ex;
struct s_expr *last_s_ex = NULL;
#ifdef NO_EXPR
/* Don't use expression parser */
recover(tok, delim, delim2);
if (*tok == NULL) {
lexerror("Unterminated expression");
exit(1);
}
return NULL;
#endif
t = *tok;
if (t->type == TOK_SEMICOLON) {
/* Empty expression */
if ((delim != TOK_SEMICOLON && delim2 != TOK_SEMICOLON)
|| type == EXPR_INIT
|| type == EXPR_CONSTINIT
|| type == EXPR_OPTCONSTINIT
|| type == EXPR_OPTCONSTARRAYSIZE) {
errorfl(t, "Parse error at `%s'", t->ascii);
return NULL;
}
ret = alloc_expr();
ret->is_const = 1;
return ret;
} else if (t->type == TOK_ARRAY_CLOSE
&& (delim == TOK_ARRAY_CLOSE
|| delim2 == TOK_ARRAY_CLOSE)) {
ret = alloc_expr();
ret->is_const = 1;
return ret;
} else if (delim == TOK_ARRAY_CLOSE
&& type == EXPR_CONST_FUNCARRAYPARAM) {
if ((t->type == TOK_KEY_RESTRICT
|| t->type == TOK_KEY_CONST /* XXX honor this */
|| t->type == TOK_KEY_VOLATILE)
&& t->next != NULL
&& t->next->type == TOK_ARRAY_CLOSE) {
/*
* This is a construct of the form
*
* char buf[restrict]
*
* which is equivalent to
*
* char *restrict buf
*/
*tok = t->next;
ret = alloc_expr();
ret->is_const = 1;
return ret;
}
type = EXPR_CONST;
} else if (t->type == TOK_COMP_OPEN) {
struct scope *scope;
/* GNU C statement-as-expression */
if (ansiflag) {
warningfl(t,
"Statement-as-expression (`({ ... })') isn't "
"available in ISO C (don't use -ansi!)");
}
scope = new_scope(SCOPE_CODE);
if (analyze(&t) != 0) {
close_scope();
return NULL;
}
close_scope();
scope->code->type = ST_EXPRSTMT;
*tok = t->next;
ret = alloc_expr();
ret->stmt_as_expr = /*curscope->next*/scope;
return ret;
}
/*
* Strategy: An expression is a number of sub-expressions
* connected through binary and ternary operators, so we
* just always need to read a sub-expression, a connecting
* operator - if any - and then the next sub-expression.
*/
while ((s_ex = get_sub_expr(&t, delim, delim2, type)) != NULL) {
int op;
last_s_ex = s_ex;
ex = alloc_expr();
ex->op = 0;
ex->data = s_ex;
append_expr(&ret, &rettail, ex);
if (expr_ends(t, delim, delim2)) {
if ((ex->tok = s_ex->meat) == NULL) {
if ((ex->tok = s_ex->is_sizeof) == NULL) {
/* Must be parenthesized expr */
ex->tok = s_ex->is_expr->tok;
}
}
break;
} else if (t->type != TOK_OPERATOR) {
errorfl(t, "Parse error at `%s'(#2)", t->ascii);
ex = NULL;
break;
}
/* Must be binary or ternary operator */
ex = alloc_expr();
ex->data = NULL;
op = *(int *)t->data;
ex->tok = t;
if (op != TOK_OP_COND) {
op = ambig_to_binary(t);
}
if (type == EXPR_CONST || type == EXPR_CONSTINIT) {
int err = 0;
char *is_what =
type == EXPR_CONST? "expression": "initializer";
if (op == TOK_OP_COMMA) {
errorfl(t,
"The comma operator is not allowed "
"in constant %ss", is_what);
err = 1;
} else if (IS_ASSIGN_OP(op)) {
errorfl(t,
"Assignment operators are not allowed "
"in constant %ss", is_what);
err = 1;
}
if (err) {
free(ex);
ex = NULL;
break;
}
}
ex->op = op;
append_expr(&ret, &rettail, ex);
if (next_token(&t) != 0) {
return NULL;
}
if (expr_ends(t, delim, delim2)) {
errorfl(ex->tok, "Syntax error at `%s'",
ex->tok->ascii);
break;
}
if (op == TOK_OP_COND) {
/*
* 08/18/07: Allow GNU C's empty first conditional
* operator operand
*/
if (t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_AMB_COND2) {
warningfl(t, "Conditionals with omitted "
"operands are not ISO C (GNU C only)");
if (next_token(&t) != 0) {
return NULL;
}
if (expr_ends(t, delim, delim2)) {
errorfl(t, "Syntax error at `%s'",
t->ascii);
break;
}
ex = alloc_expr();
ex->tok = t;
ex->op = TOK_OP_COND2;
append_expr(&ret, &rettail, ex);
}
}
}
*tok = t;
if (ex == NULL || !expr_ends(t, delim, delim2)) {
/* Try to recover from parse errors */
if (initial) {
recover(&tokstart, delim, delim2);
*tok = tokstart;
}
return NULL;
}
debug_print_expr(ret);
fflush(stdout);
if ((ret = bind_operators(ret)) == NULL) {
puts("panic: cannot bind operators");
exit(1); /* XXX */
}
debug_print_tree(ret);
ret->extype = type;
if (initial
&& (type == EXPR_CONST
|| type == EXPR_CONSTINIT
|| type == EXPR_OPTCONSTINIT
|| type == EXPR_OPTCONSTARRAYSIZE)) {
int not_constant;
if (eval_const_expr(ret, type, ¬_constant) != 0) {
if ((type == EXPR_OPTCONSTINIT
|| type == EXPR_OPTCONSTARRAYSIZE)
&& not_constant) {
ret->is_const = 0;
} else {
return NULL;
}
} else {
ret->is_const = 1;
}
}
return ret;
}
void
recover(struct token **tok, int delim, int delim2) {
struct token *t;
int parens = 0;
int brackets = 0;
int braces = 0;
if (*tok == NULL) return;
if (delim == TOK_PAREN_CLOSE || delim2 == TOK_PAREN_CLOSE) {
++parens;
}
if (delim == TOK_ARRAY_CLOSE || delim2 == TOK_ARRAY_CLOSE) {
++brackets;
}
if (delim == TOK_COMP_CLOSE || delim2 == TOK_COMP_CLOSE) {
++braces;
}
for (t = *tok; t != NULL; t = t->next) {
int ty = -1; /* 0 compared even with unused delim :( */
#ifdef DEBUG2
printf(" lol %s\n", t->ascii);
#endif
if (t->type == TOK_OPERATOR) {
ty = *(int *)t->data;
} else if (t->type == TOK_PAREN_OPEN) {
++parens;
continue;
} else if (t->type == TOK_PAREN_CLOSE) {
/*
* The below was --parens < 0, but <=
* seems more permissive for parse
* errors - so use that!
*/
if (--parens <= 0
&& (delim == TOK_PAREN_CLOSE
|| delim2 == TOK_PAREN_CLOSE)) {
*tok = t;
return;
}
continue;
} else if (t->type == TOK_ARRAY_OPEN) {
++brackets;
continue;
} else if (t->type == TOK_ARRAY_CLOSE) {
if (--brackets <= 0
&& (delim == TOK_ARRAY_CLOSE
|| delim2 == TOK_ARRAY_CLOSE)) {
*tok = t;
return;
}
continue;
} else if (t->type == TOK_COMP_OPEN) {
++braces;
continue;
} else if (t->type == TOK_COMP_CLOSE) {
if (--braces <= 0
&& (delim == TOK_COMP_CLOSE
|| delim2 == TOK_COMP_CLOSE)) {
*tok = t;
return;
}
continue;
} else {
ty = t->type;
}
if (ty == delim || ty == delim2) {
if (ty == TOK_OP_COMMA) {
/*
* We must take into account that someone
* could write
* int foo = bar(x, y, z);
* ... in which case we do not want to exit
* after x!
*/
if (parens != 0) {
continue;
}
}
*tok = t;
return;
}
}
*tok = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1