ack/lang/cem/cemcom.ansi/expr.c
David Given c1aca7dae5 First milestone of replacing the build system.
--HG--
branch : dtrg-buildsystem
rename : lang/cem/cpp.ansi/Parameters => lang/cem/cpp.ansi/parameters.h
2013-05-12 20:45:55 +01:00

501 lines
10 KiB
C

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*/
/* $Id$ */
/* EXPRESSION TREE HANDLING */
#include <stdlib.h>
#include "parameters.h"
#include "assert.h"
#include <alloc.h>
#include <flt_arith.h>
#include "idf.h"
#include "arith.h"
#include "def.h"
#include "type.h"
#include "label.h"
#include "expr.h"
#include "LLlex.h"
#include "Lpars.h"
#include "decspecs.h"
#include "declar.h"
#include "sizes.h"
#include "level.h"
extern char *symbol2str();
extern char options[];
extern int InSizeof;
int
rank_of(oper)
int oper;
{
/* The rank of the operator oper is returned.
*/
switch (oper) {
default:
return 0; /* INT2INT etc. */
case '[':
case '(':
case '.':
case ARROW:
case PARCOMMA:
return 1;
case '!':
case PLUSPLUS:
case MINMIN:
case CAST:
case SIZEOF:
case ADDRESSOF:
return 2; /* monadic */
case '*':
case '/':
case '%':
return 3;
case '+':
case '-':
return 4;
case LEFT:
case RIGHT:
return 5;
case '<':
case '>':
case LESSEQ:
case GREATEREQ:
return 6;
case EQUAL:
case NOTEQUAL:
return 7;
case '&':
return 8;
case '^':
return 9;
case '|':
return 10;
case AND:
return 11;
case OR:
return 12;
case '?':
case ':':
return 13;
case '=':
case PLUSAB:
case MINAB:
case TIMESAB:
case DIVAB:
case MODAB:
case RIGHTAB:
case LEFTAB:
case ANDAB:
case XORAB:
case ORAB:
return 14;
case ',':
return 15;
}
/*NOTREACHED*/
}
dot2expr(expp)
struct expr **expp;
{
/* The token in dot is converted into an expression, a
pointer to which is stored in *expp.
*/
register struct expr *ex = new_expr();
*expp = ex;
ex->ex_file = dot.tk_file;
ex->ex_line = dot.tk_line;
switch (DOT) {
case IDENTIFIER:
idf2expr(ex);
break;
case INTEGER:
int2expr(ex);
break;
case FLOATING:
float2expr(ex);
break;
default:
crash("bad conversion to expression");
/*NOTREACHED*/
}
}
idf2expr(expr)
register struct expr *expr;
{
/* Dot contains an identifier which is turned into an
expression.
Note that this constitutes an applied occurrence of
the identifier.
*/
register struct idf *idf = dot.tk_idf; /* != 0*/
register struct def *def = idf->id_def;
if (def == 0) {
if (AHEAD == '(') {
/* function call, declare name implicitly (3.3.2.2) */
if (!options['o'])
warning("implicit declaration of function %s"
, idf->id_text);
add_def(idf, EXTERN, funint_type, level);
} else {
if (!is_anon_idf(idf))
error("%s undefined", idf->id_text);
/* declare idf anyway */
add_def(idf, 0, error_type, level);
}
def = idf->id_def;
}
/* now def != 0 */
#ifndef LINT
if (!InSizeof) {
if (! def->df_used) {
#ifndef PREPEND_SCOPES
code_scope(idf->id_text, def);
#endif /* PREPEND_SCOPES */
def->df_used = 1;
}
}
#endif /* LINT */
expr->ex_type = def->df_type;
if (expr->ex_type == error_type) {
expr->ex_flags |= EX_ERROR;
}
expr->ex_lvalue =
( def->df_type->tp_fund == FUNCTION ||
def->df_type->tp_fund == ARRAY ||
def->df_sc == ENUM
) ? 0 : 1;
if (def->df_type->tp_typequal & TQ_CONST)
expr->ex_flags |= EX_READONLY;
if (def->df_type->tp_typequal & TQ_VOLATILE)
expr->ex_flags |= EX_VOLATILE;
expr->ex_class = Value;
if (def->df_sc == ENUM) {
expr->VL_CLASS = Const;
expr->VL_VALUE = def->df_address;
}
#ifndef LINT
else
if (def->df_sc == STATIC && def->df_level >= L_LOCAL) {
expr->VL_CLASS = Label;
expr->VL_LBL = def->df_address;
expr->VL_VALUE = (arith)0;
}
#endif /* LINT */
else {
expr->VL_CLASS = Name;
expr->VL_IDF = idf;
expr->VL_VALUE = (arith)0;
}
}
string2expr(expp, str, len)
register struct expr **expp;
int len;
char *str;
{
/* The string in the argument is converted into an expression,
a pointer to which is stored in *expp.
*/
register struct expr *ex = new_expr();
*expp = ex;
ex->ex_file = dot.tk_file;
ex->ex_line = dot.tk_line;
ex->ex_type = string_type;
/* ex->ex_type = qualifier_type(ex->ex_type, TQ_CONST); */
ex->ex_flags |= EX_READONLY;
/* ex->ex_lvalue = 0; */
ex->ex_class = String;
ex->SG_VALUE = str;
ex->SG_LEN = len;
}
int2expr(expr)
struct expr *expr;
{
/* Dot contains an integer constant which is turned
into an expression.
*/
fill_int_expr(expr, dot.tk_ival, dot.tk_fund);
}
float2expr(expr)
register struct expr *expr;
{
/* Dot contains a floating point constant which is turned
into an expression.
*/
register int fund;
fund = dot.tk_fund;
switch (fund) {
case FLOAT:
expr->ex_type = float_type;
break;
case DOUBLE:
expr->ex_type = double_type;
break;
case LNGDBL:
expr->ex_type = lngdbl_type;
break;
default:
crash("(float2expr) bad fund %s\n", symbol2str(fund));
}
expr->ex_class = Float;
flt_str2flt(dot.tk_fval, &(expr->FL_ARITH));
free(dot.tk_fval);
ASSERT(flt_status != FLT_NOFLT);
if (flt_status == FLT_OVFL)
expr_warning(expr,"internal floating point overflow");
}
struct expr*
intexpr(ivalue, fund)
arith ivalue;
int fund;
{
/* The value ivalue is turned into an integer expression of
the size indicated by fund.
*/
register struct expr *expr = new_expr();
expr->ex_file = dot.tk_file;
expr->ex_line = dot.tk_line;
fill_int_expr(expr, ivalue, fund);
return expr;
}
fill_int_expr(ex, ivalue, fund)
register struct expr *ex;
arith ivalue;
int fund;
{
/* Details derived from ivalue and fund are put into the
constant integer expression ex.
*/
switch (fund) {
case INT:
ex->ex_type = int_type;
break;
case UNSIGNED:
ex->ex_type = uint_type;
break;
case LONG:
ex->ex_type = long_type;
break;
case ULONG:
ex->ex_type = ulong_type;
break;
default:
crash("(fill_int_expr) bad fund %s\n", symbol2str(fund));
/*NOTREACHED*/
}
ex->ex_class = Value;
ex->VL_CLASS = Const;
ex->VL_VALUE = ivalue;
cut_size(ex);
}
struct expr *
new_oper(tp, e1, oper, e2)
struct type *tp;
register struct expr *e1, *e2;
{
/* A new expression is constructed which consists of the
operator oper which has e1 and e2 as operands; for a
monadic operator e1 == NILEXPR.
During the construction of the right recursive initialisation
tree it is possible for e2 to be NILEXPR.
*/
register struct expr *expr = new_expr();
register struct oper *op;
if (e2) {
register struct expr *e = e2;
while (e->ex_class == Oper && e->OP_LEFT)
e = e->OP_LEFT;
expr->ex_file = e->ex_file;
expr->ex_line = e->ex_line;
}
else
if (e1) {
register struct expr *e = e1;
while (e->ex_class == Oper && e->OP_RIGHT)
e = e->OP_RIGHT;
expr->ex_file = e->ex_file;
expr->ex_line = e->ex_line;
}
else {
expr->ex_file = dot.tk_file;
expr->ex_line = dot.tk_line;
}
expr->ex_type = tp;
expr->ex_class = Oper;
/* combine depths and flags of both expressions */
if (e2) {
int e1_depth = e1 ? e1->ex_depth : 0;
int e1_flags = e1 ? e1->ex_flags : 0;
expr->ex_depth =
(e1_depth > e2->ex_depth ? e1_depth : e2->ex_depth) + 1;
expr->ex_flags = (e1_flags | e2->ex_flags)
& ~(EX_PARENS | EX_READONLY | EX_VOLATILE );
}
/*
* A function call should be evaluated first when possible. Just say
* that the expression tree is very deep.
*/
if (oper == '(') {
expr->ex_depth = 50;
}
op = &expr->ex_object.ex_oper;
op->op_type = tp;
op->op_oper = oper;
op->op_left = e1;
op->op_right = e2;
#ifdef LINT
lint_new_oper(expr);
#endif /* LINT */
return expr;
}
chk_cst_expr(expp)
struct expr **expp;
{
/* The expression expr is checked for constancy.
There are 6 places where constant expressions occur in C:
1. after #if
2. in a global initialization
3. as size in an array declaration
4. as value in an enum declaration
5. as width in a bit field
6. as case value in a switch
The constant expression in a global initialization is
handled separately (by IVAL()).
There are various disparate restrictions on each of
the others in the various C compilers. I have tried some
hypotheses to unify them, but all have failed.
Special problems (of which there is only one, sizeof in
Preprocessor #if) have to be dealt with locally
*/
register struct expr *expr = *expp;
#ifdef DEBUG
print_expr("constant_expression", expr);
#endif /* DEBUG */
switch(expr->ex_type->tp_fund) {
case CHAR:
case SHORT:
case INT:
case ENUM:
case LONG:
if (is_ld_cst(expr)) {
return;
}
expr_error(expr, "expression is not constant");
break;
default:
expr_error(expr, "non-numerical constant expression");
break;
}
erroneous2int(expp);
}
init_expression(eppp, expr)
register struct expr ***eppp;
struct expr *expr;
{
/* The expression expr is added to the tree designated
indirectly by **eppp.
The natural form of a tree representing an
initial_value_list is right-recursive, ie. with the
left-most comma as main operator. The iterative grammar in
expression.g, however, tends to produce a left-recursive
tree, ie. one with the right-most comma as its main
operator.
To produce a right-recursive tree from the iterative
grammar, we keep track of the address of the pointer where
the next expression must be hooked in.
*/
**eppp = new_oper(void_type, expr, INITCOMMA, NILEXPR);
*eppp = &(**eppp)->OP_RIGHT;
}
int
is_ld_cst(expr)
register struct expr *expr;
{
/* An expression is a `load-time constant' if it is of the form
<idf> +/- <integral> or <integral>.
*/
#ifdef LINT
if (expr->ex_class == String)
return 1;
#endif /* LINT */
return expr->ex_lvalue == 0 && expr->ex_class == Value;
}
int
is_cp_cst(expr)
struct expr *expr;
{
/* An expression is a `compile-time constant' if it is a
load-time constant, and the idf is not there.
*/
return is_ld_cst(expr) && expr->VL_CLASS == Const;
}
int
is_fp_cst(expr)
struct expr *expr;
{
/* An expression is a `floating-point constant' if it consists
of the float only.
*/
return expr->ex_class == Float;
}
int
is_zero_cst(expr)
register struct expr *expr;
{
flt_arith var;
switch(expr->ex_class) {
case Value:
return expr->VL_VALUE == 0;
case Float:
flt_arith2flt((arith) 0, &var, 0);
return flt_cmp(&var, &(expr->FL_ARITH)) == 0;
}
/*NOTREACHED*/
}
free_expression(expr)
register struct expr *expr;
{
/* The expression expr is freed recursively.
*/
if (expr) {
if (expr->ex_class == Oper) {
free_expression(expr->OP_LEFT);
free_expression(expr->OP_RIGHT);
}
free_expr(expr);
}
}