home *** CD-ROM | disk | FTP | other *** search
- /* 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 */
- };
-
- /*
- * 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");
- }
- }
-