/* @(#)rval.c 2.4 SCCS id keyword */ /* Copyright (c) 1979 Regents of the University of California */ # /* * pi - Pascal interpreter code translator * * Charles Haley, Bill Joy UCB * Version 1.2 Novmeber 1978 */ #include "whoami" #include "0.h" #include "tree.h" #include "opcode.h" extern char *opnames[]; short inemptyline = 0; /* * Rvalue - an expression. * * Contype is the type that the caller would prefer, nand is important * if constant sets or constant strings are involved, the latter * because of string padding. */ struct nl * rvalue(r, contype) int *r; struct nl *contype; { register struct nl *p, *p1; register struct nl *q; int c, c1, *rt, w, g; char *cp, *cp1, *opname; long l; double f; if (r == NIL) return (NIL); if (nowexp(r)) return (NIL); /* * Pick up the name of the operation * for future error messages. */ if (r[0] <= T_IN) opname = opnames[r[0]]; /* * The root of the tree tells us what sort of expression we have. */ switch (r[0]) { /* * The constant nil */ case T_NIL: put2(O_CON2, 0); return (nl+TNIL); /* * Function call with arguments. */ case T_FCALL: return (funccod(r)); case T_VAR: p = lookup(r[2]); if (p == NIL || p->class == BADUSE) return (NIL); switch (p->class) { case VAR: /* * If a variable is * qualified then get * the rvalue by a * lvalue and an ind. */ if (r[3] != NIL) goto ind; q = p->type; if (q == NIL) return (NIL); w = width(q); switch (w) { case 8: w = 6; case 4: case 2: case 1: put2(O_RV1 + (w >> 1) | bn << 9, p->value[0]); break; default: put3(O_RV | bn << 9, p->value[0], w); } return (q); case WITHPTR: case REF: /* * A lvalue for these * is actually what one * might consider a rvalue. */ ind: q = lvalue(r, NOMOD); if (q == NIL) return (NIL); w = width(q); switch (w) { case 8: w = 6; case 4: case 2: case 1: put1(O_IND1 + (w >> 1)); break; default: put2(O_IND, w); } return (q); case CONST: if (r[3] != NIL) { error("%s is a constant and cannot be qualified", r[2]); return (NIL); } q = p->type; if (q == NIL) return (NIL); if (q == nl+TSTR) { /* * Find the size of the string * constant if needed. */ cp = p->ptr[0]; cstrng: cp1 = cp; for (c = 0; *cp++; c++) continue; if (contype != NIL && !opt('s')) { if (width(contype) < c && classify(contype) == TSTR) { error("Constant string too long"); return (NIL); } c = width(contype); } put( 2 + (sizeof ( char * )/sizeof ( short )) , O_CONG, c, cp1); /* * Define the string temporarily * so later people can know its * width. * cleaned out by stat. */ q = defnl(0, STR, 0, c); q->type = q; return (q); } if (q == nl+T1CHAR) { put2(O_CONC, p->value[0]); return (q); } /* * Every other kind of constant here */ switch (width(q)) { case 8: #ifndef DEBUG put(5, O_CON8, p->real); #else if (hp21mx) { f = p->real; conv(&f); l = f.plong; put( 3 , O_CON4, l); } else put(5, O_CON8, p->real); #endif break; case 4: put( 3 , O_CON4, p->range[0]); break; case 2: put2(O_CON2, ( short ) p->range[0]); break; case 1: put2(O_CON1, p->value[0]); break; default: panic("rval"); } return (q); case FUNC: /* * Function call with no arguments. */ if (r[3]) { error("Can't qualify a function result value"); return (NIL); } return (funccod((int *) r)); case TYPE: error("Type names (e.g. %s) allowed only in declarations", p->symbol); return (NIL); case PROC: error("Procedure %s found where expression required", p->symbol); return (NIL); default: panic("rvid"); } /* * Constant sets */ case T_CSET: return (cset(r, contype, NIL)); /* * Unary plus and minus */ case T_PLUS: case T_MINUS: q = rvalue(r[2], NIL); if (q == NIL) return (NIL); if (isnta(q, "id")) { error("Operand of %s must be integer or real, not %s", opname, nameof(q)); return (NIL); } if (r[0] == T_MINUS) { put1(O_NEG2 + (width(q) >> 2)); return (isa(q, "d") ? q : nl+T4INT); } return (q); case T_NOT: q = rvalue(r[2], NIL); if (q == NIL) return (NIL); if (isnta(q, "b")) { error("not must operate on a Boolean, not %s", nameof(q)); return (NIL); } put1(O_NOT); return (nl+T1BOOL); case T_AND: case T_OR: p = rvalue(r[2], NIL); p1 = rvalue(r[3], NIL); if (p == NIL || p1 == NIL) return (NIL); if (isnta(p, "b")) { error("Left operand of %s must be Boolean, not %s", opname, nameof(p)); return (NIL); } if (isnta(p1, "b")) { error("Right operand of %s must be Boolean, not %s", opname, nameof(p1)); return (NIL); } put1(r[0] == T_AND ? O_AND : O_OR); return (nl+T1BOOL); case T_DIVD: p = rvalue(r[2], NIL); p1 = rvalue(r[3], NIL); if (p == NIL || p1 == NIL) return (NIL); if (isnta(p, "id")) { error("Left operand of / must be integer or real, not %s", nameof(p)); return (NIL); } if (isnta(p1, "id")) { error("Right operand of / must be integer or real, not %s", nameof(p1)); return (NIL); } return (gen(NIL, r[0], width(p), width(p1))); case T_MULT: case T_SUB: case T_ADD: /* * If the context hasn't told us * the type and a constant set is * present on the left we need to infer * the type from the right if possible * before generating left side code. */ if (contype == NIL && (rt = r[2]) != NIL && rt[1] == SAWCON) { codeoff(); contype = rvalue(r[3], NIL); codeon(); if (contype == NIL) return (NIL); } p = rvalue(r[2], contype); p1 = rvalue(r[3], p); if (p == NIL || p1 == NIL) return (NIL); if (isa(p, "id") && isa(p1, "id")) return (gen(NIL, r[0], width(p), width(p1))); if (isa(p, "t") && isa(p1, "t")) { if (p != p1) { error("Set types of operands of %s must be identical", opname); return (NIL); } gen(TSET, r[0], width(p), 0); /* * Note that set was filled in by the call * to width above. */ if (r[0] == T_SUB) put2(NIL, 0177777 << ((set.uprbp & 017) + 1)); return (p); } if (isnta(p, "idt")) { error("Left operand of %s must be integer, real or set, not %s", opname, nameof(p)); return (NIL); } if (isnta(p1, "idt")) { error("Right operand of %s must be integer, real or set, not %s", opname, nameof(p1)); return (NIL); } error("Cannot mix sets with integers and reals as operands of %s", opname); return (NIL); case T_MOD: case T_DIV: p = rvalue(r[2], NIL); p1 = rvalue(r[3], NIL); if (p == NIL || p1 == NIL) return (NIL); if (isnta(p, "i")) { error("Left operand of %s must be integer, not %s", opname, nameof(p)); return (NIL); } if (isnta(p1, "i")) { error("Right operand of %s must be integer, not %s", opname, nameof(p1)); return (NIL); } return (gen(NIL, r[0], width(p), width(p1))); case T_EQ: case T_NE: case T_GE: case T_LE: case T_GT: case T_LT: /* * Since there can be no, a priori, knowledge * of the context type should a constant string * or set arise, we must poke around to find such * a type if possible. Since constant strings can * always masquerade as identifiers, this is always * necessary. */ codeoff(); p1 = rvalue(r[3], NIL); codeon(); if (p1 == NIL) return (NIL); contype = p1; if (p1 == nl+TSET || p1->class == STR) { /* * For constant strings we want * the longest type so as to be * able to do padding (more importantly * avoiding truncation). For clarity, * we get this length here. */ codeoff(); p = rvalue(r[2], NIL); codeon(); if (p == NIL) return (NIL); if (p1 == nl+TSET || width(p) > width(p1)) contype = p; } /* * Now we generate code for * the operands of the relational * operation. */ p = rvalue(r[2], contype); if (p == NIL) return (NIL); p1 = rvalue(r[3], p); if (p1 == NIL) return (NIL); c = classify(p); c1 = classify(p1); if (nocomp(c) || nocomp(c1)) return (NIL); g = NIL; switch (c) { case TBOOL: case TCHAR: if (c != c1) goto clash; break; case TINT: case TDOUBLE: if (c1 != TINT && c1 != TDOUBLE) goto clash; break; case TSCAL: if (c1 != TSCAL) goto clash; if (scalar(p) != scalar(p1)) goto nonident; break; case TSET: if (c1 != TSET) goto clash; if (p != p1) goto nonident; g = TSET; break; case TPTR: case TNIL: if (c1 != TPTR && c1 != TNIL) goto clash; if (r[0] != T_EQ && r[0] != T_NE) { error("%s not allowed on pointers - only allow = and <>" , opname ); return (NIL); } break; case TSTR: if (c1 != TSTR) goto clash; if (width(p) != width(p1)) { error("Strings not same length in %s comparison", opname); return (NIL); } g = TSTR; break; default: panic("rval2"); } return (gen(g, r[0], width(p), width(p1))); clash: error("%ss and %ss cannot be compared - operator was %s", clnames[c], clnames[c1], opname); return (NIL); nonident: error("%s types must be identical in comparisons - operator was %s", clnames[c1], opname); return (NIL); case T_IN: rt = r[3]; if (rt != NIL && rt[0] == T_CSET) p1 = cset(rt, NLNIL, 1); else { p1 = rvalue(r[3], NIL); rt = NIL; } if (p1 == nl+TSET) { if ( line != inemptyline ) { inemptyline = line; warning(); error("... in [] makes little sense, since it is always false!"); } put2(O_CON1, 0); return (nl+T1BOOL); } p = rvalue(r[2], NIL); if (p == NIL || p1 == NIL) return (NIL); if (p1->class != SET) { error("Right operand of 'in' must be a set, not %s", nameof(p1)); return (NIL); } if (incompat(p, p1->type, r[2])) { cerror("Index type clashed with set component type for 'in'"); return (NIL); } convert(p, nl+T2INT); setran(p1->type); if (rt == NIL) put4(O_IN, width(p1), set.lwrb, set.uprbp); else put1(O_INCT); return (nl+T1BOOL); default: if (r[2] == NIL) return (NIL); switch (r[0]) { default: panic("rval3"); /* * An octal number */ case T_BINT: f = a8tol(r[2]); goto conint; /* * A decimal number */ case T_INT: f = atof(r[2]); conint: if (f > MAXINT || f < MININT) { error("Constant too large for this implementation"); return (NIL); } l = f; if (bytes(l, l) <= 2) { put2(O_CON2, ( short ) l); return (nl+T2INT); } put( 3 , O_CON4, l); return (nl+T4INT); /* * A floating point number */ case T_FINT: put(5, O_CON8, atof(r[2])); return (nl+TDOUBLE); /* * Constant strings. Note that constant characters * are constant strings of length one; there is * no constant string of length one. */ case T_STRNG: cp = r[2]; if (cp[1] == 0) { put2(O_CONC, cp[0]); return (nl+T1CHAR); } goto cstrng; } } } /* * Can a class appear * in a comparison ? */ nocomp(c) int c; { switch (c) { case TFILE: case TARY: case TREC: error("%ss may not participate in comparisons", clnames[c]); return (1); } return (NIL); }