/* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". */ /* $Header$ */ /* EXPRESSION TREE HANDLING */ #include "nofloat.h" #include "botch_free.h" #include #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 "noRoption.h" extern char *symbol2str(); extern char options[]; 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: 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*/ } #ifndef NOROPTION int rank_of_expression(ex) register struct expr *ex; { /* Returns the rank of the top node in the expression. */ if (!ex || (ex->ex_flags & EX_PARENS) || ex->ex_class != Oper) return 0; return rank_of(ex->OP_OPER); } check_conditional(expr, oper, pos_descr) register struct expr *expr; char *pos_descr; { /* Warn if restricted C is in effect and the expression expr, which occurs at the position pos_descr, is not lighter than the operator oper. */ if (options['R'] && rank_of_expression(expr) >= rank_of(oper)) expr_warning(expr, "%s %s is ungrammatical", symbol2str(expr->OP_OPER), pos_descr); } #endif 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 STRING: string2expr(ex); break; case INTEGER: int2expr(ex); break; #ifndef NOFLOAT case FLOATING: float2expr(ex); break; #endif NOFLOAT default: crash("bad conversion to expression"); break; } } 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 */ add_def(idf, IMPLICIT, funint_type, level); /* RM 13 */ 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 */ if (def->df_type->tp_fund == LABEL) { expr_error(expr, "illegal use of label %s", idf->id_text); expr->ex_type = error_type; } else { def->df_used = 1; 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; expr->ex_class = Value; if (def->df_sc == ENUM) { expr->VL_CLASS = Const; expr->VL_VALUE = def->df_address; } 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; } else { expr->VL_CLASS = Name; expr->VL_IDF = idf; expr->VL_VALUE = (arith)0; } } string2expr(expr) register struct expr *expr; { /* Dot contains a string which is turned into an expression. */ expr->ex_type = string_type; expr->ex_lvalue = 0; expr->ex_class = String; expr->SG_VALUE = dot.tk_bts; expr->SG_LEN = dot.tk_len; expr->SG_DATLAB = 0; } 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); } #ifndef NOFLOAT float2expr(expr) register struct expr *expr; { /* Dot contains a floating point constant which is turned into an expression. */ expr->ex_type = double_type; expr->ex_class = Float; expr->FL_VALUE = dot.tk_fval; expr->FL_DATLAB = 0; } #endif NOFLOAT 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 LONG: ex->ex_type = long_type; break; case UNSIGNED: /* We cannot make a test like ivalue <= max_unsigned because, if sizeof(long) == int_size holds, max_unsigned may be a negative long in which case the comparison results in an unexpected answer. We assume that the type "unsigned long" is not part of portable C ! */ ex->ex_type = (ivalue & ~max_unsigned) ? long_type : uint_type; break; case INTEGER: ex->ex_type = (ivalue <= max_int) ? int_type : long_type; break; default: crash("(intexpr) bad fund %s\n", symbol2str(fund)); } 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; } op = &expr->ex_object.ex_oper; op->op_type = tp; op->op_oper = oper; op->op_left = e1; op->op_right = e2; return expr; } chk_cst_expr(expp) register 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. This routine will give a warning for those operators not allowed by K&R, under the R-option only. The anomalies are cast, logical operators and the expression comma. Special problems (of which there is only one, sizeof in Preprocessor #if) have to be dealt with locally Note that according to K&R the negation ! is illegal in constant expressions and is indeed rejected by the Ritchie compiler. */ register struct expr *expr = *expp; register int fund = expr->ex_type->tp_fund; register int flags = expr->ex_flags; int err = 0; #ifdef DEBUG print_expr("constant_expression", expr); #endif DEBUG if ( fund != CHAR && fund != SHORT && fund != INT && fund != ENUM && fund != LONG ) expr_error(expr, "non-numerical constant expression"), err++; else if (!is_ld_cst(expr)) expr_error(expr, "expression is not constant"), err++; #ifndef NOROPTION if (options['R']) { if (flags & EX_CAST) expr_warning(expr, "cast in constant expression"); if (flags & EX_LOGICAL) expr_warning(expr, "logical operator in constant expression"); if (flags & EX_COMMA) expr_warning(expr, "expression comma in constant expression"); } #endif NOROPTION if (err) erroneous2int(expp); } init_expression(eppp, expr) register struct expr ***eppp, *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 +/- or . */ return expr->ex_lvalue == 0 && expr->ex_class == Value; } int is_cp_cst(expr) register 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; } #ifndef NOFLOAT int is_fp_cst(expr) register struct expr *expr; { /* An expression is a `floating-point constant' if it consists of the float only. */ return expr->ex_class == Float; } #endif NOFLOAT 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); } }