BSD 2 development
[unix-history] / src / pi1 / proc.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"
/*
* The following arrays are used to determine which classes may be
* read and written to/from text files.
* They are indexed by the return types from classify.
*/
#define rdops(x) rdxxxx[(x)-(TFIRST)]
#define wrops(x) wrxxxx[(x)-(TFIRST)]
int rdxxxx[] {
0, /* -7 file types */
0, /* -6 record types */
0, /* -5 array types */
0, /* -4 scalar types */
0, /* -3 pointer types */
0, /* -2 set types */
0, /* -1 string types */
0, /* 0 nil - i.e. no type */
0, /* 1 booleans */
O_READC, /* 2 character */
O_READ4, /* 3 integer */
O_READ8 /* 4 real */
};
int wrxxxx[] {
0, /* -7 file types */
0, /* -6 record types */
0, /* -5 array types */
0, /* -4 scalar types */
0, /* -3 pointer types */
0, /* -2 set types */
O_WRITG, /* -1 string types */
0, /* 0 nil - i.e. no type */
O_WRITB, /* 1 booleans */
O_WRITC, /* 2 character */
O_WRIT4, /* 3 integer */
O_WRIT8, /* 4 real */
};
\f
/*
* Proc handles procedure calls.
* Non-builtin procedures are "buck-passed" to func (with a flag
* indicating that they are actually procedures.
* builtin procedures are handled here.
*/
proc(r)
int *r;
{
register struct nl *p;
register int *al, op;
struct nl *filetype, *ap;
int argc, *argv, c, two, oct, hex, *file;
int pu;
int *pua, *pui, *puz;
int i, j, k;
/*
* Verify that the name is
* defined and is that of a
* procedure.
*/
p = lookup(r[2]);
if (p == NIL) {
rvlist(r[3]);
return;
}
if (p->class != PROC) {
error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
rvlist(r[3]);
return;
}
argv = r[3];
/*
* Call handles user defined
* procedures and functions.
*/
if (bn != 0) {
call(p, argv, PROC, bn);
return;
}
/*
* Call to built-in procedure.
* Count the arguments.
*/
argc = 0;
for (al = argv; al != NIL; al = al[2])
argc++;
/*
* Switch on the operator
* associated with the built-in
* procedure in the namelist
*/
op = p->value[0] &~ NSTAND;
if (opt('s') && (p->value[0] & NSTAND)) {
standard();
error("%s is a nonstandard procedure", p->symbol);
}
switch (op) {
case O_NULL:
if (argc != 0)
error("null takes no arguments");
return;
case O_FLUSH:
if (argc == 0) {
put1(O_MESSAGE);
return;
}
if (argc != 1) {
error("flush takes at most one argument");
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (ap->class != FILE) {
error("flush's argument must be a file, not %s", nameof(ap));
return;
}
put1(op);
return;
case O_MESSAGE:
case O_WRIT2:
case O_WRITLN:
/*
* Set up default file "output"'s type
*/
file = NIL;
filetype = nl+T1CHAR;
/*
* Determine the file implied
* for the write and generate
* code to make it the active file.
*/
if (op == O_MESSAGE) {
/*
* For message, all that matters
* is that the filetype is
* a character file.
* Thus "output" will suit us fine.
*/
put1(O_MESSAGE);
} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
/*
* If there is a first argument which has
* no write widths, then it is potentially
* a file name.
*/
codeoff();
ap = rvalue(argv[1], NIL);
codeon();
if (ap == NIL)
argv = argv[2];
if (ap != NIL && ap->class == FILE) {
/*
* Got "write(f, ...", make
* f the active file, and save
* it and its type for use in
* processing the rest of the
* arguments to write.
*/
file = argv[1];
filetype = ap->type;
rvalue(argv[1], NIL);
put1(O_UNIT);
/*
* Skip over the first argument
*/
argv = argv[2];
argc--;
} else
/*
* Set up for writing on
* standard output.
*/
put1(O_UNITOUT);
} else
put1(O_UNITOUT);
/*
* Loop and process each
* of the arguments.
*/
for (; argv != NIL; argv = argv[2]) {
al = argv[1];
if (al == NIL)
continue;
/*
* Op will be used to
* accumulate width information,
* and two records the fact
* that we saw two write widths
*/
op = 0;
two = 0;
oct = 0;
hex = 0;
if (al[0] == T_WEXP) {
if (filetype != nl+T1CHAR) {
error("Write widths allowed only with text files");
continue;
}
/*
* Handle width expressions.
* The basic game here is that width
* expressions get evaluated and left
* on the stack and their width's get
* packed into the high byte of the
* affected opcode (subop).
*/
if (al[3] == OCT)
oct++;
else if (al[3] == HEX)
hex++;
else if (al[3] != NIL) {
two++;
/*
* Arrange for the write
* opcode that takes two widths
*/
op =| O_WRIT82-O_WRIT8;
ap = rvalue(al[3], NIL);
if (ap == NIL)
continue;
if (isnta(ap, "i")) {
error("Second write width must be integer, not %s", nameof(ap));
continue;
}
op =| even(width(ap)) << 11;
}
if (al[2] != NIL) {
ap = rvalue(al[2], NIL);
if (ap == NIL)
continue;
if (isnta(ap, "i")) {
error("First write width must be integer, not %s", nameof(ap));
continue;
}
op =| even(width(ap)) << 8;
}
al = al[1];
if (al == NIL)
continue;
}
if (filetype != nl+T1CHAR) {
if (oct || hex) {
error("Oct/hex allowed only on text files");
continue;
}
if (op) {
error("Write widths allowed only on text files");
continue;
}
/*
* Generalized write, i.e.
* to a non-textfile.
*/
rvalue(file, NIL);
put1(O_FNIL);
/*
* file^ := ...
*/
ap = rvalue(argv[1], NIL);
if (ap == NIL)
continue;
if (incompat(ap, filetype, argv[1])) {
cerror("Type mismatch in write to non-text file");
continue;
}
convert(ap, filetype);
put2(O_AS, width(filetype));
/*
* put(file)
*/
put1(O_PUT);
continue;
}
/*
* Write to a textfile
*
* Evaluate the expression
* to be written.
*/
ap = rvalue(al, NIL);
if (ap == NIL)
continue;
c = classify(ap);
if (two && c != TDOUBLE) {
if (isnta(ap, "i")) {
error("Only reals can have two write widths");
continue;
}
convert(ap, nl+TDOUBLE);
c = TDOUBLE;
}
if (oct || hex) {
if (opt('s')) {
standard();
error("Oct and hex are non-standard");
}
switch (c) {
case TREC:
case TARY:
case TFILE:
case TSTR:
case TSET:
case TDOUBLE:
error("Can't write %ss with oct/hex", clnames[c]);
continue;
}
put1(op | (oct ? O_WROCT2 : O_WRHEX2) | (width(ap) >> 2));
continue;
}
if (wrops(c) == NIL) {
error("Can't write %ss to a text file", clnames[c]);
continue;
}
if (c == TINT && width(ap) != 4)
op =| O_WRIT2;
else
op =| wrops(c);
if (c == TSTR)
put2(op, width(ap));
else
put1(op);
}
/*
* Done with arguments.
* Handle writeln and
* insufficent number of args.
*/
switch (p->value[0] &~ NSTAND) {
case O_WRIT2:
if (argc == 0)
error("Write requires an argument");
break;
case O_MESSAGE:
if (argc == 0)
error("Message requires an argument");
case O_WRITLN:
if (filetype != nl+T1CHAR)
error("Can't 'writeln' a non text file");
put1(O_WRITLN);
break;
}
return;
case O_READ4:
case O_READLN:
/*
* Set up default
* file "input".
*/
file = NIL;
filetype = nl+T1CHAR;
/*
* Determine the file implied
* for the read and generate
* code to make it the active file.
*/
if (argv != NIL) {
codeoff();
ap = rvalue(argv[1], NIL);
codeon();
if (ap == NIL)
argv = argv[2];
if (ap != NIL && ap->class == FILE) {
/*
* Got "read(f, ...", make
* f the active file, and save
* it and its type for use in
* processing the rest of the
* arguments to read.
*/
file = argv[1];
filetype = ap->type;
rvalue(argv[1], NIL);
put1(O_UNIT);
argv = argv[2];
argc--;
} else {
/*
* Default is read from
* standard input.
*/
put1(O_UNITINP);
input->nl_flags =| NUSED;
}
} else {
put1(O_UNITINP);
input->nl_flags =| NUSED;
}
/*
* Loop and process each
* of the arguments.
*/
for (; argv != NIL; argv = argv[2]) {
/*
* Get the address of the target
* on the stack.
*/
al = argv[1];
if (al == NIL)
continue;
if (al[0] != T_VAR) {
error("Arguments to %s must be variables, not expressions", p->symbol);
continue;
}
ap = lvalue(al, MOD|ASGN|NOUSE);
if (ap == NIL)
continue;
if (filetype != nl+T1CHAR) {
/*
* Generalized read, i.e.
* from a non-textfile.
*/
if (incompat(filetype, ap, NIL)) {
error("Type mismatch in read from non-text file");
continue;
}
/*
* var := file ^;
*/
if (file != NIL)
rvalue(file, NIL);
else /* Magic */
put2(O_RV2, input->value[0]);
put1(O_FNIL);
put2(O_IND, width(filetype));
convert(filetype, ap);
if (isa(ap, "bsci"))
rangechk(ap, ap);
put2(O_AS, width(ap));
/*
* get(file);
*/
put1(O_GET);
continue;
}
c = classify(ap);
op = rdops(c);
if (op == NIL) {
error("Can't read %ss from a text file", clnames[c]);
continue;
}
put1(op);
/*
* Data read is on the stack.
* Assign it.
*/
if (op != O_READ8)
rangechk(ap, op == O_READC ? ap : nl+T4INT);
gen(O_AS2, O_AS2, width(ap),
op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2);
}
/*
* Done with arguments.
* Handle readln and
* insufficient number of args.
*/
if (p->value[0] == O_READLN) {
if (filetype != nl+T1CHAR)
error("Can't 'readln' a non text file");
put1(O_READLN);
}
else if (argc == 0)
error("read requires an argument");
return;
case O_GET:
case O_PUT:
if (argc != 1) {
error("%s expects one argument", p->symbol);
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (ap->class != FILE) {
error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
return;
}
put1(O_UNIT);
put1(op);
return;
case O_RESET:
case O_REWRITE:
if (argc == 0 || argc > 2) {
error("%s expects one or two arguments", p->symbol);
return;
}
if (opt('s') && argc == 2) {
standard();
error("Two argument forms of reset and rewrite are non-standard");
}
ap = lvalue(argv[1], MOD|NOUSE);
if (ap == NIL)
return;
if (ap->class != FILE) {
error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
return;
}
if (argc == 2) {
/*
* Optional second argument
* is a string name of a
* UNIX (R) file to be associated.
*/
al = argv[2];
al = rvalue(al[1], NIL);
if (al == NIL)
return;
if (classify(al) != TSTR) {
error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
return;
}
c = width(al);
} else
c = 0;
if (c > 127) {
error("File name too long");
return;
}
put2(op | c << 8, text(ap) ? 0: width(ap->type));
return;
case O_NEW:
case O_DISPOSE:
if (argc == 0) {
error("%s expects at least one argument", p->symbol);
return;
}
ap = lvalue(argv[1], MOD|NOUSE);
if (ap == NIL)
return;
if (ap->class != PTR) {
error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
return;
}
ap = ap->type;
if (ap == NIL)
return;
argv = argv[2];
if (argv != NIL) {
if (ap->class != RECORD) {
error("Record required when specifying variant tags");
return;
}
for (; argv != NIL; argv = argv[2]) {
if (ap->value[NL_VARNT] == NIL) {
error("Too many tag fields");
return;
}
if (!isconst(argv[1])) {
error("Second and successive arguments to %s must be constants", p->symbol);
return;
}
gconst(argv[1]);
if (con.ctype == NIL)
return;
if (incompat(con.ctype, ap->value[NL_TAG]->type)) {
cerror("Specified tag constant type clashed with variant case selector type");
return;
}
for (ap = ap->value[NL_VARNT]; ap != NIL; ap = ap->chain)
if (ap->range[0] == con.crval)
break;
if (ap == NIL) {
error("No variant case label value equals specified constant value");
return;
}
ap = ap->value[NL_VTOREC];
}
}
put2(op, width(ap));
return;
case O_DATE:
case O_TIME:
if (argc != 1) {
error("%s expects one argument", p->symbol);
return;
}
ap = lvalue(argv[1], MOD|NOUSE);
if (ap == NIL)
return;
if (classify(ap) != TSTR || width(ap) != 10) {
error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
return;
}
put1(op);
return;
case O_HALT:
if (argc != 0) {
error("halt takes no arguments");
return;
}
put1(op);
noreach = 1;
return;
case O_ARGV:
if (argc != 2) {
error("argv takes two arguments");
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (isnta(ap, "i")) {
error("argv's first argument must be an integer, not %s", nameof(ap));
return;
}
convert(ap, nl+T2INT);
al = argv[2];
ap = lvalue(al[1], MOD|NOUSE);
if (ap == NIL)
return;
if (classify(ap) != TSTR) {
error("argv's second argument must be a string, not %s", nameof(ap));
return;
}
put2(op, width(ap));
return;
case O_STLIM:
if (argc != 1) {
error("stlimit requires one argument");
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (isnta(ap, "i")) {
error("stlimit's argument must be an integer, not %s", nameof(ap));
return;
}
if (width(ap) != 4)
put1(O_STOI);
put1(op);
return;
case O_REMOVE:
if (argc != 1) {
error("remove expects one argument");
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (classify(ap) != TSTR) {
error("remove's argument must be a string, not %s", nameof(ap));
return;
}
put2(op, width(ap));
return;
case O_LLIMIT:
if (argc != 2) {
error("linelimit expects two arguments");
return;
}
ap = lvalue(argv[1], NOMOD|NOUSE);
if (ap == NIL)
return;
if (!text(ap)) {
error("linelimit's first argument must be a text file, not %s", nameof(ap));
return;
}
al = argv[2];
ap = rvalue(al[1], NIL);
if (ap == NIL)
return;
if (isnta(ap, "i")) {
error("linelimit's second argument must be an integer, not %s", nameof(ap));
return;
}
convert(ap, nl+T2INT);
put1(op);
return;
case O_PAGE:
if (argc != 1) {
error("page expects one argument");
return;
}
ap = rvalue(argv[1], NIL);
if (ap == NIL)
return;
if (!text(ap)) {
error("Argument to page must be a text file, not %s", nameof(ap));
return;
}
put1(O_UNIT);
put1(op);
return;
case O_PACK:
if (argc != 3) {
error("pack expects three arguments");
return;
}
pu = "pack(a,i,z)";
pua = (al = argv)[1];
pui = (al = al[2])[1];
puz = (al = al[2])[1];
goto packunp;
case O_UNPACK:
if (argc != 3) {
error("unpack expects three arguments");
return;
}
pu = "unpack(z,a,i)";
puz = (al = argv)[1];
pua = (al = al[2])[1];
pui = (al = al[2])[1];
packunp:
ap = rvalue(pui, NIL);
if (ap == NIL)
return;
if (width(ap) == 4)
put1(O_ITOS);
ap = lvalue(pua, op == O_PACK ? NOMOD : MOD|NOUSE);
if (ap == NIL)
return;
if (ap->class != ARRAY) {
error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
return;
}
al = lvalue(puz, op == O_UNPACK ? NOMOD : MOD|NOUSE);
if (al->class != ARRAY) {
error("%s requires z to be a packed array, not %s", pu, nameof(ap));
return;
}
if (al->type == NIL || ap->type == NIL)
return;
if (al->type != ap->type) {
error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
return;
}
k = width(al);
ap = ap->chain;
al = al->chain;
if (ap->chain != NIL || al->chain != NIL) {
error("%s requires a and z to be single dimension arrays", pu);
return;
}
if (ap == NIL || al == NIL)
return;
/*
* al is the range for z i.e. u..v
* ap is the range for a i.e. m..n
* i will be n-m+1
* j will be v-u+1
*/
i = ap->range[1] - ap->range[0] + 1;
j = al->range[1] - al->range[0] + 1;
if (i < j) {
error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
return;
}
/*
* get n-m-(v-u) and m for the interpreter
*/
i =- j;
j = ap->range[0];
put(5, op, width(ap), j, i, k);
return;
case 0:
error("%s is an unimplemented 6400 extension", p->symbol);
return;
default:
panic("proc case");
}
}