/* @(#)proc.c 2.3 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 November 1978 */ #include "whoami" #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; int itemwidth; /* * 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 != FILET) { 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 == FILET) { /* * 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 == FILET) { /* * 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 != FILET) { 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 != FILET) { 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->ptr[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->ptr[NL_TAG])->type)) { cerror("Specified tag constant type clashed with variant case selector type"); return; } for (ap = ap->ptr[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->ptr[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((int *) pui, NLNIL); 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 = (struct nl *) 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); itemwidth = width( ap -> type ); 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, itemwidth , j, i, k); return; case 0: error("%s is an unimplemented 6400 extension", p->symbol); return; default: panic("proc case"); } }