mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 15:22:04 +00:00
[qfcc] Create a compound initializer expression type
This fixes the problem of using the return value of a function as an element in a compound initializer. The cause of the problem is that compound initializers were represented by block expressions, but function calls are contained within block expressions, so def initialization saw the block expression and thought it was a nested compound initializer. Technically, it was a bug in the nested element parsing code in that it wasn't checking the result value of the block expression, but using a whole new expression type makes things much cleaner and the work done paves the way for labeled initializers and compound assignments.
This commit is contained in:
parent
f10f9e157d
commit
d1e83b9d48
5 changed files with 109 additions and 44 deletions
|
@ -55,6 +55,7 @@ typedef enum {
|
|||
|
||||
ex_nil, ///< umm, nil, null. nuff said (0 of any type)
|
||||
ex_value, ///< constant value (::ex_value_t)
|
||||
ex_compound, ///< compound initializer
|
||||
} expr_type;
|
||||
|
||||
/** Binary and unary expressions.
|
||||
|
@ -84,6 +85,17 @@ typedef struct {
|
|||
ex_label_t *label;
|
||||
} ex_labelref_t;
|
||||
|
||||
typedef struct ex_initele_s {
|
||||
struct ex_initele_s *next; ///< next in chain
|
||||
struct symbol_s *symbol; ///< for labeled initializers
|
||||
struct expr_s *expr; ///< initializer expression
|
||||
} ex_initele_t;
|
||||
|
||||
typedef struct ex_cmpinit_s {
|
||||
ex_initele_t *head;
|
||||
ex_initele_t **tail;
|
||||
} ex_cmpinit_t;
|
||||
|
||||
typedef struct {
|
||||
struct expr_s *head; ///< the first expression in the block
|
||||
struct expr_s **tail; ///< last expression in the block, for appending
|
||||
|
@ -210,6 +222,7 @@ typedef struct expr_s {
|
|||
ex_temp_t temp; ///< temporary variable expression
|
||||
ex_vector_t vector; ///< vector expression list
|
||||
ex_value_t *value; ///< constant value
|
||||
ex_cmpinit_t compound; ///< compound initializer
|
||||
} e;
|
||||
} expr_t;
|
||||
|
||||
|
@ -348,6 +361,10 @@ expr_t *new_block_expr (void);
|
|||
*/
|
||||
expr_t *build_block_expr (expr_t *expr_list);
|
||||
|
||||
ex_initele_t *new_initele (expr_t *expr, struct symbol_s *symbol);
|
||||
expr_t *new_compound_init (void);
|
||||
expr_t *append_element (expr_t *compound, ex_initele_t *element);
|
||||
|
||||
/** Create a new binary expression node node.
|
||||
|
||||
If either \a e1 or \a e2 are error expressions, then that expression will
|
||||
|
|
|
@ -86,7 +86,7 @@ new_element (void)
|
|||
}
|
||||
|
||||
static element_t *
|
||||
append_element (element_chain_t *element_chain, element_t *element)
|
||||
append_init_element (element_chain_t *element_chain, element_t *element)
|
||||
{
|
||||
element->next = 0;
|
||||
*element_chain->tail = element;
|
||||
|
@ -371,7 +371,7 @@ static void
|
|||
build_element_chain (element_chain_t *element_chain, type_t *type,
|
||||
expr_t *eles, int base_offset)
|
||||
{
|
||||
expr_t *e = eles->e.block.head;
|
||||
ex_initele_t *ele = eles->e.compound.head;
|
||||
|
||||
if (is_array (type)) {
|
||||
type_t *array_type = type->t.array.type;
|
||||
|
@ -380,17 +380,18 @@ build_element_chain (element_chain_t *element_chain, type_t *type,
|
|||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
int offset = base_offset + i * type_size (array_type);
|
||||
if (e && e->type == ex_block) {
|
||||
build_element_chain (element_chain, array_type, e, offset);
|
||||
if (ele->expr && ele->expr->type == ex_compound) {
|
||||
build_element_chain (element_chain, array_type,
|
||||
ele->expr, offset);
|
||||
} else {
|
||||
element_t *element = new_element ();
|
||||
element->type = array_type;
|
||||
element->offset = offset;
|
||||
element->expr = e; // null will be treated as nil
|
||||
append_element (element_chain, element);
|
||||
element->expr = ele->expr; // null will be treated as nil
|
||||
append_init_element (element_chain, element);
|
||||
}
|
||||
if (e) {
|
||||
e = e->next;
|
||||
if (ele) {
|
||||
ele = ele->next;
|
||||
}
|
||||
}
|
||||
} else if (is_struct (type) || is_vector (type) || is_quaternion (type)) {
|
||||
|
@ -403,23 +404,24 @@ build_element_chain (element_chain_t *element_chain, type_t *type,
|
|||
|| field->visibility == vis_anonymous) {
|
||||
continue;
|
||||
}
|
||||
if (e && e->type == ex_block) {
|
||||
build_element_chain (element_chain, field->type, e, offset);
|
||||
if (ele->expr && ele->expr->type == ex_compound) {
|
||||
build_element_chain (element_chain, field->type,
|
||||
ele->expr, offset);
|
||||
} else {
|
||||
element_t *element = new_element ();
|
||||
element->type = field->type;
|
||||
element->offset = offset;
|
||||
element->expr = e; // null will be treated as nil
|
||||
append_element (element_chain, element);
|
||||
element->expr = ele->expr; // null will be treated as nil
|
||||
append_init_element (element_chain, element);
|
||||
}
|
||||
if (e) {
|
||||
e = e->next;
|
||||
if (ele) {
|
||||
ele = ele->next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error (eles, "invalid initializer");
|
||||
}
|
||||
if (e && e->next && options.warnings.initializer) {
|
||||
if (ele && ele->next && options.warnings.initializer) {
|
||||
warning (eles, "excessive elements in initializer");
|
||||
}
|
||||
}
|
||||
|
@ -652,7 +654,7 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
|
|||
}
|
||||
if (!sym->s.def) {
|
||||
if (is_array (sym->type) && !type_size (sym->type)
|
||||
&& init->type == ex_block && !init->e.block.result) {
|
||||
&& init->type == ex_compound) {
|
||||
sym->type = array_type (sym->type->t.array.type,
|
||||
num_elements (init));
|
||||
}
|
||||
|
@ -676,7 +678,7 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
|
|||
return;
|
||||
if ((is_array (sym->type) || is_struct (sym->type)
|
||||
|| sym->type == &type_vector || sym->type == &type_quaternion)
|
||||
&& ((init->type == ex_block && !init->e.block.result)
|
||||
&& ((init->type == ex_compound)
|
||||
|| init->type == ex_nil)) {
|
||||
init_elements (sym->s.def, init);
|
||||
sym->s.def->initialized = 1;
|
||||
|
|
|
@ -217,6 +217,7 @@ get_type (expr_t *e)
|
|||
return &type_void;
|
||||
case ex_label:
|
||||
case ex_error:
|
||||
case ex_compound:
|
||||
return 0; // something went very wrong
|
||||
case ex_bool:
|
||||
if (options.code.progsversion == PROG_ID_VERSION)
|
||||
|
@ -420,14 +421,21 @@ copy_expr (expr_t *e)
|
|||
n = new_expr ();
|
||||
n->e.vector.type = e->e.vector.type;
|
||||
n->e.vector.list = copy_expr (e->e.vector.list);
|
||||
n = n->e.vector.list;
|
||||
t = e->e.vector.list;
|
||||
e = n->e.vector.list;
|
||||
while (t->next) {
|
||||
n->next = copy_expr (t->next);
|
||||
n = n->next;
|
||||
e->next = copy_expr (t->next);
|
||||
e = e->next;
|
||||
t = t->next;
|
||||
}
|
||||
return n;
|
||||
case ex_compound:
|
||||
n = new_expr ();
|
||||
*n = *e;
|
||||
for (ex_initele_t *i = e->e.compound.head; i; i = i->next) {
|
||||
append_element (n, new_initele (i->expr, i->symbol));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
internal_error (e, "invalid expression");
|
||||
}
|
||||
|
@ -543,6 +551,46 @@ new_block_expr (void)
|
|||
return b;
|
||||
}
|
||||
|
||||
ex_initele_t *
|
||||
new_initele (expr_t *expr, symbol_t *symbol)
|
||||
{
|
||||
ex_initele_t *i = calloc (1, sizeof (*i));
|
||||
i->expr = expr;
|
||||
i->symbol = symbol;
|
||||
return i;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_compound_init (void)
|
||||
{
|
||||
expr_t *c = new_expr ();
|
||||
c->type = ex_compound;
|
||||
c->e.compound.head = 0;
|
||||
c->e.compound.tail = &c->e.compound.head;
|
||||
return c;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
append_element (expr_t *compound, ex_initele_t *element)
|
||||
{
|
||||
if (compound->type != ex_compound) {
|
||||
internal_error (compound, "not a compound expression");
|
||||
}
|
||||
|
||||
if (!element || (element->expr && element->expr->type == ex_error)) {
|
||||
return compound;
|
||||
}
|
||||
|
||||
if (element->next) {
|
||||
internal_error (compound, "append_element: element loop detected");
|
||||
}
|
||||
|
||||
*compound->e.compound.tail = element;
|
||||
compound->e.compound.tail = &element->next;
|
||||
|
||||
return compound;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_binary_expr (int op, expr_t *e1, expr_t *e2)
|
||||
{
|
||||
|
@ -1499,6 +1547,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_label:
|
||||
case ex_labelref:
|
||||
case ex_state:
|
||||
case ex_compound:
|
||||
internal_error (e, 0);
|
||||
case ex_uexpr:
|
||||
if (e->e.expr.op == '-')
|
||||
|
@ -1565,6 +1614,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_label:
|
||||
case ex_labelref:
|
||||
case ex_state:
|
||||
case ex_compound:
|
||||
internal_error (e, 0);
|
||||
case ex_bool:
|
||||
return new_bool_expr (e->e.bool.false_list,
|
||||
|
@ -1630,6 +1680,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_label:
|
||||
case ex_labelref:
|
||||
case ex_state:
|
||||
case ex_compound:
|
||||
internal_error (e, 0);
|
||||
case ex_uexpr:
|
||||
if (e->e.expr.op == '~')
|
||||
|
|
|
@ -123,6 +123,7 @@ check_valid_lvalue (expr_t *expr)
|
|||
return check_valid_lvalue (expr->e.expr.e1);
|
||||
}
|
||||
break;
|
||||
case ex_compound:
|
||||
case ex_state:
|
||||
case ex_bool:
|
||||
case ex_label:
|
||||
|
|
|
@ -98,6 +98,7 @@ int yylex (void);
|
|||
void *pointer; // for ensuring pointer values are null
|
||||
struct type_s *type;
|
||||
struct expr_s *expr;
|
||||
struct ex_initele_s *element;
|
||||
struct function_s *function;
|
||||
struct switch_block_s *switch_block;
|
||||
struct param_s *param;
|
||||
|
@ -181,7 +182,9 @@ int yylex (void);
|
|||
%type <symbol> methoddef
|
||||
%type <expr> opt_initializer var_initializer local_def
|
||||
|
||||
%type <expr> opt_init opt_expr cexpr expr element_list element
|
||||
%type <expr> opt_init opt_expr cexpr expr
|
||||
%type <expr> compound_init element_list
|
||||
%type <element> element
|
||||
%type <expr> optional_state_expr texpr vector_expr
|
||||
%type <expr> statement statements compound_statement
|
||||
%type <expr> else bool_label break_label continue_label
|
||||
|
@ -1148,17 +1151,20 @@ opt_initializer
|
|||
|
||||
var_initializer
|
||||
: '=' expr { $$ = $2; }
|
||||
| '=' '{' { $<spec>$ = $<spec>-1; }
|
||||
element_list optional_comma '}' { $$ = $4; }
|
||||
| '=' '{' '}'
|
||||
| '=' compound_init
|
||||
{
|
||||
if (is_scalar ($<spec>-1.type)) {
|
||||
if (!$2 && is_scalar ($<spec>-1.type)) {
|
||||
error (0, "empty scalar initializer");
|
||||
}
|
||||
$$ = new_nil_expr ();
|
||||
$$ = $2 ? $2 : new_nil_expr ();
|
||||
}
|
||||
;
|
||||
|
||||
compound_init
|
||||
: '{' element_list optional_comma '}' { $$ = $2; }
|
||||
| '{' '}' { $$ = 0; }
|
||||
;
|
||||
|
||||
optional_state_expr
|
||||
: /* emtpy */ { $$ = 0; }
|
||||
| vector_expr { $$ = build_state_expr ($1); }
|
||||
|
@ -1167,30 +1173,18 @@ optional_state_expr
|
|||
element_list
|
||||
: element
|
||||
{
|
||||
$$ = new_block_expr ();
|
||||
append_expr ($$, $1);
|
||||
$$ = new_compound_init ();
|
||||
append_element ($$, $1);
|
||||
}
|
||||
| element_list ',' {$<spec>$ = $<spec>0; } element
|
||||
| element_list ',' element
|
||||
{
|
||||
append_expr ($$, $4);
|
||||
append_element ($$, $3);
|
||||
}
|
||||
;
|
||||
|
||||
element
|
||||
: '{' { $<spec>$ = $<spec>0; }
|
||||
element_list optional_comma '}' { $$ = $3; }
|
||||
| '{' '}'
|
||||
{
|
||||
// FIXME doesn't check the right type (does prove the inherited
|
||||
// attributes have been passed down correctly though). The problem
|
||||
// is that the type of the sub elements needs to be extracted if
|
||||
// possible
|
||||
if (is_scalar ($<spec>0.type)) {
|
||||
error (0, "empty scalar initializer");
|
||||
}
|
||||
$$ = new_nil_expr ();
|
||||
}
|
||||
| expr { $$ = $1; }
|
||||
: compound_init { $$ = new_initele ($1, 0); }
|
||||
| expr { $$ = new_initele ($1, 0); }
|
||||
;
|
||||
|
||||
optional_comma
|
||||
|
|
Loading…
Reference in a new issue