BSD 2 development
[unix-history] / src / pi0 / rval.c
/* Copyright (c) 1979 Regents of the University of California */
#
/*
* pi - Pascal interpreter code translator
*
* Charles Haley, Bill Joy UCB
* Version 1.2 January 1979
*/
#include "0.h"
#include "tree.h"
#include "opcode.h"
extern char *opnames[];
/*
* 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.
*/
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->value[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);
}
put3(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;
put3(O_CON4, l);
} else
put(5, O_CON8, p->real);
#endif
break;
case 4:
put3(O_CON4, p->range[0]);
break;
case 2:
put2(O_CON2, p->value[1]);
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(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 <>");
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, NIL, 1);
else {
p1 = rvalue(r[3], NIL);
rt = NIL;
}
if (p1 == nl+TSET) {
warning();
error("... in [] makes little sense, since it is always false!");
put1(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, c=l);
return (nl+T2INT);
}
put3(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);
}