ack/lang/cem/cemcom.ansi/expr.c
carl 750a6bc684 Better ANSI C compatibility and portability:
+ Addition of function prototypes and include files.
+ Change function definitions to ANSI C style.
+ Initial support for CMake
+ Scripts to generate compiler header is now sed based.
2019-02-19 00:54:23 +08:00

497 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 <assert.h>
#include <stdlib.h>
#include "parameters.h"
#include <alloc.h>
#include <flt_arith.h>
#include "expr.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"
#include "cstoper.h"
#include "error.h"
extern char *symbol2str();
extern char options[];
extern int InSizeof;
int rank_of(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*/
}
void dot2expr(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*/
}
}
void idf2expr(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;
}
}
void string2expr(register struct expr **expp, char *str, int len)
{
/* 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;
}
void int2expr(struct expr *expr)
{
/* Dot contains an integer constant which is turned
into an expression.
*/
fill_int_expr(expr, dot.tk_ival, dot.tk_fund);
}
void float2expr(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(
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;
}
void fill_int_expr(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(struct type *tp, register struct expr *e1, int oper,
register struct expr *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;
}
void chk_cst_expr(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);
}
void init_expression(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(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(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(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(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*/
}
void free_expression(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);
}
}