1 /* Copyright (c) 1979 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)proc.c 1.14 09/03/82"; 4 5 #include "whoami.h" 6 #ifdef OBJ 7 /* 8 * and the rest of the file 9 */ 10 #include "0.h" 11 #include "tree.h" 12 #include "opcode.h" 13 #include "objfmt.h" 14 15 /* 16 * The following array is used to determine which classes may be read 17 * from textfiles. It is indexed by the return value from classify. 18 */ 19 #define rdops(x) rdxxxx[(x)-(TFIRST)] 20 21 int rdxxxx[] = { 22 0, /* -7 file types */ 23 0, /* -6 record types */ 24 0, /* -5 array types */ 25 O_READE, /* -4 scalar types */ 26 0, /* -3 pointer types */ 27 0, /* -2 set types */ 28 0, /* -1 string types */ 29 0, /* 0 nil, no type */ 30 O_READE, /* 1 boolean */ 31 O_READC, /* 2 character */ 32 O_READ4, /* 3 integer */ 33 O_READ8 /* 4 real */ 34 }; 35 36 /* 37 * Proc handles procedure calls. 38 * Non-builtin procedures are "buck-passed" to func (with a flag 39 * indicating that they are actually procedures. 40 * builtin procedures are handled here. 41 */ 42 proc(r) 43 int *r; 44 { 45 register struct nl *p; 46 register int *alv, *al, op; 47 struct nl *filetype, *ap; 48 int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file; 49 char fmt, format[20], *strptr; 50 int prec, field, strnglen, fmtlen, fmtstart, pu; 51 int *pua, *pui, *puz; 52 int i, j, k; 53 int itemwidth; 54 struct tmps soffset; 55 struct nl *tempnlp; 56 57 #define CONPREC 4 58 #define VARPREC 8 59 #define CONWIDTH 1 60 #define VARWIDTH 2 61 #define SKIP 16 62 63 /* 64 * Verify that the name is 65 * defined and is that of a 66 * procedure. 67 */ 68 p = lookup(r[2]); 69 if (p == NIL) { 70 rvlist(r[3]); 71 return; 72 } 73 if (p->class != PROC && p->class != FPROC) { 74 error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]); 75 rvlist(r[3]); 76 return; 77 } 78 argv = r[3]; 79 80 /* 81 * Call handles user defined 82 * procedures and functions. 83 */ 84 if (bn != 0) { 85 call(p, argv, PROC, bn); 86 return; 87 } 88 89 /* 90 * Call to built-in procedure. 91 * Count the arguments. 92 */ 93 argc = 0; 94 for (al = argv; al != NIL; al = al[2]) 95 argc++; 96 97 /* 98 * Switch on the operator 99 * associated with the built-in 100 * procedure in the namelist 101 */ 102 op = p->value[0] &~ NSTAND; 103 if (opt('s') && (p->value[0] & NSTAND)) { 104 standard(); 105 error("%s is a nonstandard procedure", p->symbol); 106 } 107 switch (op) { 108 109 case O_ABORT: 110 if (argc != 0) 111 error("null takes no arguments"); 112 return; 113 114 case O_FLUSH: 115 if (argc == 0) { 116 put(1, O_MESSAGE); 117 return; 118 } 119 if (argc != 1) { 120 error("flush takes at most one argument"); 121 return; 122 } 123 ap = stklval(argv[1], NIL , LREQ ); 124 if (ap == NIL) 125 return; 126 if (ap->class != FILET) { 127 error("flush's argument must be a file, not %s", nameof(ap)); 128 return; 129 } 130 put(1, op); 131 return; 132 133 case O_MESSAGE: 134 case O_WRITEF: 135 case O_WRITLN: 136 /* 137 * Set up default file "output"'s type 138 */ 139 file = NIL; 140 filetype = nl+T1CHAR; 141 /* 142 * Determine the file implied 143 * for the write and generate 144 * code to make it the active file. 145 */ 146 if (op == O_MESSAGE) { 147 /* 148 * For message, all that matters 149 * is that the filetype is 150 * a character file. 151 * Thus "output" will suit us fine. 152 */ 153 put(1, O_MESSAGE); 154 } else if (argv != NIL && (al = argv[1])[0] != T_WEXP) { 155 /* 156 * If there is a first argument which has 157 * no write widths, then it is potentially 158 * a file name. 159 */ 160 codeoff(); 161 ap = stkrval(argv[1], NIL , RREQ ); 162 codeon(); 163 if (ap == NIL) 164 argv = argv[2]; 165 if (ap != NIL && ap->class == FILET) { 166 /* 167 * Got "write(f, ...", make 168 * f the active file, and save 169 * it and its type for use in 170 * processing the rest of the 171 * arguments to write. 172 */ 173 file = argv[1]; 174 filetype = ap->type; 175 stklval(argv[1], NIL , LREQ ); 176 put(1, O_UNIT); 177 /* 178 * Skip over the first argument 179 */ 180 argv = argv[2]; 181 argc--; 182 } else 183 /* 184 * Set up for writing on 185 * standard output. 186 */ 187 put(1, O_UNITOUT); 188 output->nl_flags |= NUSED; 189 } else 190 put(1, O_UNITOUT); 191 output->nl_flags |= NUSED; 192 /* 193 * Loop and process each 194 * of the arguments. 195 */ 196 for (; argv != NIL; argv = argv[2]) { 197 /* 198 * fmtspec indicates the type (CONstant or VARiable) 199 * and number (none, WIDTH, and/or PRECision) 200 * of the fields in the printf format for this 201 * output variable. 202 * stkcnt is the number of bytes pushed on the stack 203 * fmt is the format output indicator (D, E, F, O, X, S) 204 * fmtstart = 0 for leading blank; = 1 for no blank 205 */ 206 fmtspec = NIL; 207 stkcnt = 0; 208 fmt = 'D'; 209 fmtstart = 1; 210 al = argv[1]; 211 if (al == NIL) 212 continue; 213 if (al[0] == T_WEXP) 214 alv = al[1]; 215 else 216 alv = al; 217 if (alv == NIL) 218 continue; 219 codeoff(); 220 ap = stkrval(alv, NIL , RREQ ); 221 codeon(); 222 if (ap == NIL) 223 continue; 224 typ = classify(ap); 225 if (al[0] == T_WEXP) { 226 /* 227 * Handle width expressions. 228 * The basic game here is that width 229 * expressions get evaluated. If they 230 * are constant, the value is placed 231 * directly in the format string. 232 * Otherwise the value is pushed onto 233 * the stack and an indirection is 234 * put into the format string. 235 */ 236 if (al[3] == OCT) 237 fmt = 'O'; 238 else if (al[3] == HEX) 239 fmt = 'X'; 240 else if (al[3] != NIL) { 241 /* 242 * Evaluate second format spec 243 */ 244 if ( constval(al[3]) 245 && isa( con.ctype , "i" ) ) { 246 fmtspec += CONPREC; 247 prec = con.crval; 248 } else { 249 fmtspec += VARPREC; 250 } 251 fmt = 'f'; 252 switch ( typ ) { 253 case TINT: 254 if ( opt( 's' ) ) { 255 standard(); 256 error("Writing %ss with two write widths is non-standard", clnames[typ]); 257 } 258 /* and fall through */ 259 case TDOUBLE: 260 break; 261 default: 262 error("Cannot write %ss with two write widths", clnames[typ]); 263 continue; 264 } 265 } 266 /* 267 * Evaluate first format spec 268 */ 269 if (al[2] != NIL) { 270 if ( constval(al[2]) 271 && isa( con.ctype , "i" ) ) { 272 fmtspec += CONWIDTH; 273 field = con.crval; 274 } else { 275 fmtspec += VARWIDTH; 276 } 277 } 278 if ((fmtspec & CONPREC) && prec < 0 || 279 (fmtspec & CONWIDTH) && field < 0) { 280 error("Negative widths are not allowed"); 281 continue; 282 } 283 if ( opt('s') && 284 ((fmtspec & CONPREC) && prec == 0 || 285 (fmtspec & CONWIDTH) && field == 0)) { 286 standard(); 287 error("Zero widths are non-standard"); 288 } 289 } 290 if (filetype != nl+T1CHAR) { 291 if (fmt == 'O' || fmt == 'X') { 292 error("Oct/hex allowed only on text files"); 293 continue; 294 } 295 if (fmtspec) { 296 error("Write widths allowed only on text files"); 297 continue; 298 } 299 /* 300 * Generalized write, i.e. 301 * to a non-textfile. 302 */ 303 stklval(file, NIL , LREQ ); 304 put(1, O_FNIL); 305 /* 306 * file^ := ... 307 */ 308 ap = rvalue(argv[1], NIL); 309 if (ap == NIL) 310 continue; 311 if (incompat(ap, filetype, argv[1])) { 312 cerror("Type mismatch in write to non-text file"); 313 continue; 314 } 315 convert(ap, filetype); 316 put(2, O_AS, width(filetype)); 317 /* 318 * put(file) 319 */ 320 put(1, O_PUT); 321 continue; 322 } 323 /* 324 * Write to a textfile 325 * 326 * Evaluate the expression 327 * to be written. 328 */ 329 if (fmt == 'O' || fmt == 'X') { 330 if (opt('s')) { 331 standard(); 332 error("Oct and hex are non-standard"); 333 } 334 if (typ == TSTR || typ == TDOUBLE) { 335 error("Can't write %ss with oct/hex", clnames[typ]); 336 continue; 337 } 338 if (typ == TCHAR || typ == TBOOL) 339 typ = TINT; 340 } 341 /* 342 * Place the arguement on the stack. If there is 343 * no format specified by the programmer, implement 344 * the default. 345 */ 346 switch (typ) { 347 case TPTR: 348 warning(); 349 if (opt('s')) { 350 standard(); 351 } 352 error("Writing %ss to text files is non-standard", 353 clnames[typ]); 354 /* and fall through */ 355 case TINT: 356 if (fmt != 'f') { 357 ap = stkrval(alv, NIL , RREQ ); 358 stkcnt += sizeof(long); 359 } else { 360 ap = stkrval(alv, NIL , RREQ ); 361 put(1, O_ITOD); 362 stkcnt += sizeof(double); 363 typ = TDOUBLE; 364 goto tdouble; 365 } 366 if (fmtspec == NIL) { 367 if (fmt == 'D') 368 field = 10; 369 else if (fmt == 'X') 370 field = 8; 371 else if (fmt == 'O') 372 field = 11; 373 else 374 panic("fmt1"); 375 fmtspec = CONWIDTH; 376 } 377 break; 378 case TCHAR: 379 tchar: 380 if (fmtspec == NIL) { 381 put(1, O_FILE); 382 ap = stkrval(alv, NIL , RREQ ); 383 convert(nl + T4INT, INT_TYP); 384 put(2, O_WRITEC, 385 sizeof(char *) + sizeof(int)); 386 fmtspec = SKIP; 387 break; 388 } 389 ap = stkrval(alv, NIL , RREQ ); 390 convert(nl + T4INT, INT_TYP); 391 stkcnt += sizeof(int); 392 fmt = 'c'; 393 break; 394 case TSCAL: 395 warning(); 396 if (opt('s')) { 397 standard(); 398 } 399 error("Writing %ss to text files is non-standard", 400 clnames[typ]); 401 /* and fall through */ 402 case TBOOL: 403 stkrval(alv, NIL , RREQ ); 404 put(2, O_NAM, (long)listnames(ap)); 405 stkcnt += sizeof(char *); 406 fmt = 's'; 407 break; 408 case TDOUBLE: 409 ap = stkrval(alv, TDOUBLE , RREQ ); 410 stkcnt += sizeof(double); 411 tdouble: 412 switch (fmtspec) { 413 case NIL: 414 # ifdef DEC11 415 field = 21; 416 # else 417 field = 22; 418 # endif DEC11 419 prec = 14; 420 fmt = 'e'; 421 fmtspec = CONWIDTH + CONPREC; 422 break; 423 case CONWIDTH: 424 if (--field < 1) 425 field = 1; 426 # ifdef DEC11 427 prec = field - 7; 428 # else 429 prec = field - 8; 430 # endif DEC11 431 if (prec < 1) 432 prec = 1; 433 fmtspec += CONPREC; 434 fmt = 'e'; 435 break; 436 case CONWIDTH + CONPREC: 437 case CONWIDTH + VARPREC: 438 if (--field < 1) 439 field = 1; 440 } 441 format[0] = ' '; 442 fmtstart = 1; 443 break; 444 case TSTR: 445 constval( alv ); 446 switch ( classify( con.ctype ) ) { 447 case TCHAR: 448 typ = TCHAR; 449 goto tchar; 450 case TSTR: 451 strptr = con.cpval; 452 for (strnglen = 0; *strptr++; strnglen++) /* void */; 453 strptr = con.cpval; 454 break; 455 default: 456 strnglen = width(ap); 457 break; 458 } 459 fmt = 's'; 460 strfmt = fmtspec; 461 if (fmtspec == NIL) { 462 fmtspec = SKIP; 463 break; 464 } 465 if (fmtspec & CONWIDTH) { 466 if (field <= strnglen) { 467 fmtspec = SKIP; 468 break; 469 } else 470 field -= strnglen; 471 } 472 /* 473 * push string to implement leading blank padding 474 */ 475 put(2, O_LVCON, 2); 476 putstr("", 0); 477 stkcnt += sizeof(char *); 478 break; 479 default: 480 error("Can't write %ss to a text file", clnames[typ]); 481 continue; 482 } 483 /* 484 * If there is a variable precision, evaluate it onto 485 * the stack 486 */ 487 if (fmtspec & VARPREC) { 488 ap = stkrval(al[3], NIL , RREQ ); 489 if (ap == NIL) 490 continue; 491 if (isnta(ap,"i")) { 492 error("Second write width must be integer, not %s", nameof(ap)); 493 continue; 494 } 495 if ( opt( 't' ) ) { 496 put(3, O_MAX, 0, 0); 497 } 498 convert(nl+T4INT, INT_TYP); 499 stkcnt += sizeof(int); 500 } 501 /* 502 * If there is a variable width, evaluate it onto 503 * the stack 504 */ 505 if (fmtspec & VARWIDTH) { 506 if ( ( typ == TDOUBLE && fmtspec == VARWIDTH ) 507 || typ == TSTR ) { 508 soffset = sizes[cbn].curtmps; 509 tempnlp = tmpalloc(sizeof(long), 510 nl+T4INT, REGOK); 511 put(2, O_LV | cbn << 8 + INDX, 512 tempnlp -> value[ NL_OFFS ] ); 513 } 514 ap = stkrval(al[2], NIL , RREQ ); 515 if (ap == NIL) 516 continue; 517 if (isnta(ap,"i")) { 518 error("First write width must be integer, not %s", nameof(ap)); 519 continue; 520 } 521 /* 522 * Perform special processing on widths based 523 * on data type 524 */ 525 switch (typ) { 526 case TDOUBLE: 527 if (fmtspec == VARWIDTH) { 528 fmt = 'e'; 529 put(1, O_AS4); 530 put(2, O_RV4 | cbn << 8 + INDX, 531 tempnlp -> value[NL_OFFS] ); 532 # ifdef DEC11 533 put(3, O_MAX, 8, 1); 534 # else 535 put(3, O_MAX, 9, 1); 536 # endif DEC11 537 convert(nl+T4INT, INT_TYP); 538 stkcnt += sizeof(int); 539 put(2, O_RV4 | cbn << 8 + INDX, 540 tempnlp->value[NL_OFFS] ); 541 fmtspec += VARPREC; 542 tmpfree(&soffset); 543 } 544 put(3, O_MAX, 1, 1); 545 break; 546 case TSTR: 547 put(1, O_AS4); 548 put(2, O_RV4 | cbn << 8 + INDX, 549 tempnlp -> value[ NL_OFFS ] ); 550 put(3, O_MAX, strnglen, 0); 551 break; 552 default: 553 if ( opt( 't' ) ) { 554 put(3, O_MAX, 0, 0); 555 } 556 break; 557 } 558 convert(nl+T4INT, INT_TYP); 559 stkcnt += sizeof(int); 560 } 561 /* 562 * Generate the format string 563 */ 564 switch (fmtspec) { 565 default: 566 panic("fmt2"); 567 case SKIP: 568 break; 569 case NIL: 570 sprintf(&format[1], "%%%c", fmt); 571 goto fmtgen; 572 case CONWIDTH: 573 sprintf(&format[1], "%%%d%c", field, fmt); 574 goto fmtgen; 575 case VARWIDTH: 576 sprintf(&format[1], "%%*%c", fmt); 577 goto fmtgen; 578 case CONWIDTH + CONPREC: 579 sprintf(&format[1], "%%%d.%d%c", field, prec, fmt); 580 goto fmtgen; 581 case CONWIDTH + VARPREC: 582 sprintf(&format[1], "%%%d.*%c", field, fmt); 583 goto fmtgen; 584 case VARWIDTH + CONPREC: 585 sprintf(&format[1], "%%*.%d%c", prec, fmt); 586 goto fmtgen; 587 case VARWIDTH + VARPREC: 588 sprintf(&format[1], "%%*.*%c", fmt); 589 fmtgen: 590 fmtlen = lenstr(&format[fmtstart], 0); 591 put(2, O_LVCON, fmtlen); 592 putstr(&format[fmtstart], 0); 593 put(1, O_FILE); 594 stkcnt += 2 * sizeof(char *); 595 put(2, O_WRITEF, stkcnt); 596 } 597 /* 598 * Write the string after its blank padding 599 */ 600 if (typ == TSTR) { 601 put(1, O_FILE); 602 put(2, CON_INT, 1); 603 if (strfmt & VARWIDTH) { 604 put(2, O_RV4 | cbn << 8 + INDX , 605 tempnlp -> value[ NL_OFFS ] ); 606 put(2, O_MIN, strnglen); 607 convert(nl+T4INT, INT_TYP); 608 tmpfree(&soffset); 609 } else { 610 if ((fmtspec & SKIP) && 611 (strfmt & CONWIDTH)) { 612 strnglen = field; 613 } 614 put(2, CON_INT, strnglen); 615 } 616 ap = stkrval(alv, NIL , RREQ ); 617 put(2, O_WRITES, 618 2 * sizeof(char *) + 2 * sizeof(int)); 619 } 620 } 621 /* 622 * Done with arguments. 623 * Handle writeln and 624 * insufficent number of args. 625 */ 626 switch (p->value[0] &~ NSTAND) { 627 case O_WRITEF: 628 if (argc == 0) 629 error("Write requires an argument"); 630 break; 631 case O_MESSAGE: 632 if (argc == 0) 633 error("Message requires an argument"); 634 case O_WRITLN: 635 if (filetype != nl+T1CHAR) 636 error("Can't 'writeln' a non text file"); 637 put(1, O_WRITLN); 638 break; 639 } 640 return; 641 642 case O_READ4: 643 case O_READLN: 644 /* 645 * Set up default 646 * file "input". 647 */ 648 file = NIL; 649 filetype = nl+T1CHAR; 650 /* 651 * Determine the file implied 652 * for the read and generate 653 * code to make it the active file. 654 */ 655 if (argv != NIL) { 656 codeoff(); 657 ap = stkrval(argv[1], NIL , RREQ ); 658 codeon(); 659 if (ap == NIL) 660 argv = argv[2]; 661 if (ap != NIL && ap->class == FILET) { 662 /* 663 * Got "read(f, ...", make 664 * f the active file, and save 665 * it and its type for use in 666 * processing the rest of the 667 * arguments to read. 668 */ 669 file = argv[1]; 670 filetype = ap->type; 671 stklval(argv[1], NIL , LREQ ); 672 put(1, O_UNIT); 673 argv = argv[2]; 674 argc--; 675 } else { 676 /* 677 * Default is read from 678 * standard input. 679 */ 680 put(1, O_UNITINP); 681 input->nl_flags |= NUSED; 682 } 683 } else { 684 put(1, O_UNITINP); 685 input->nl_flags |= NUSED; 686 } 687 /* 688 * Loop and process each 689 * of the arguments. 690 */ 691 for (; argv != NIL; argv = argv[2]) { 692 /* 693 * Get the address of the target 694 * on the stack. 695 */ 696 al = argv[1]; 697 if (al == NIL) 698 continue; 699 if (al[0] != T_VAR) { 700 error("Arguments to %s must be variables, not expressions", p->symbol); 701 continue; 702 } 703 ap = stklval(al, MOD|ASGN|NOUSE); 704 if (ap == NIL) 705 continue; 706 if (filetype != nl+T1CHAR) { 707 /* 708 * Generalized read, i.e. 709 * from a non-textfile. 710 */ 711 if (incompat(filetype, ap, argv[1] )) { 712 error("Type mismatch in read from non-text file"); 713 continue; 714 } 715 /* 716 * var := file ^; 717 */ 718 if (file != NIL) 719 stklval(file, NIL , LREQ ); 720 else /* Magic */ 721 put(2, PTR_RV, (int)input->value[0]); 722 put(1, O_FNIL); 723 put(2, O_IND, width(filetype)); 724 convert(filetype, ap); 725 if (isa(ap, "bsci")) 726 rangechk(ap, ap); 727 put(2, O_AS, width(ap)); 728 /* 729 * get(file); 730 */ 731 put(1, O_GET); 732 continue; 733 } 734 typ = classify(ap); 735 op = rdops(typ); 736 if (op == NIL) { 737 error("Can't read %ss from a text file", clnames[typ]); 738 continue; 739 } 740 if (op != O_READE) 741 put(1, op); 742 else { 743 put(2, op, (long)listnames(ap)); 744 warning(); 745 if (opt('s')) { 746 standard(); 747 } 748 error("Reading scalars from text files is non-standard"); 749 } 750 /* 751 * Data read is on the stack. 752 * Assign it. 753 */ 754 if (op != O_READ8 && op != O_READE) 755 rangechk(ap, op == O_READC ? ap : nl+T4INT); 756 gen(O_AS2, O_AS2, width(ap), 757 op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2); 758 } 759 /* 760 * Done with arguments. 761 * Handle readln and 762 * insufficient number of args. 763 */ 764 if (p->value[0] == O_READLN) { 765 if (filetype != nl+T1CHAR) 766 error("Can't 'readln' a non text file"); 767 put(1, O_READLN); 768 } 769 else if (argc == 0) 770 error("read requires an argument"); 771 return; 772 773 case O_GET: 774 case O_PUT: 775 if (argc != 1) { 776 error("%s expects one argument", p->symbol); 777 return; 778 } 779 ap = stklval(argv[1], NIL , LREQ ); 780 if (ap == NIL) 781 return; 782 if (ap->class != FILET) { 783 error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 784 return; 785 } 786 put(1, O_UNIT); 787 put(1, op); 788 return; 789 790 case O_RESET: 791 case O_REWRITE: 792 if (argc == 0 || argc > 2) { 793 error("%s expects one or two arguments", p->symbol); 794 return; 795 } 796 if (opt('s') && argc == 2) { 797 standard(); 798 error("Two argument forms of reset and rewrite are non-standard"); 799 } 800 codeoff(); 801 ap = stklval(argv[1], MOD|NOUSE); 802 codeon(); 803 if (ap == NIL) 804 return; 805 if (ap->class != FILET) { 806 error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 807 return; 808 } 809 put(2, O_CON24, text(ap) ? 0: width(ap->type)); 810 if (argc == 2) { 811 /* 812 * Optional second argument 813 * is a string name of a 814 * UNIX (R) file to be associated. 815 */ 816 al = argv[2]; 817 codeoff(); 818 al = stkrval(al[1], NOFLAGS , RREQ ); 819 codeon(); 820 if (al == NIL) 821 return; 822 if (classify(al) != TSTR) { 823 error("Second argument to %s must be a string, not %s", p->symbol, nameof(al)); 824 return; 825 } 826 put(2, O_CON24, width(al)); 827 al = argv[2]; 828 al = stkrval(al[1], NOFLAGS , RREQ ); 829 } else { 830 put(2, O_CON24, 0); 831 put(2, PTR_CON, NIL); 832 } 833 ap = stklval(argv[1], MOD|NOUSE); 834 put(1, op); 835 return; 836 837 case O_NEW: 838 case O_DISPOSE: 839 if (argc == 0) { 840 error("%s expects at least one argument", p->symbol); 841 return; 842 } 843 ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD ); 844 if (ap == NIL) 845 return; 846 if (ap->class != PTR) { 847 error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 848 return; 849 } 850 ap = ap->type; 851 if (ap == NIL) 852 return; 853 if ((ap->nl_flags & NFILES) && op == O_DISPOSE) 854 op = O_DFDISP; 855 argv = argv[2]; 856 if (argv != NIL) { 857 if (ap->class != RECORD) { 858 error("Record required when specifying variant tags"); 859 return; 860 } 861 for (; argv != NIL; argv = argv[2]) { 862 if (ap->ptr[NL_VARNT] == NIL) { 863 error("Too many tag fields"); 864 return; 865 } 866 if (!isconst(argv[1])) { 867 error("Second and successive arguments to %s must be constants", p->symbol); 868 return; 869 } 870 gconst(argv[1]); 871 if (con.ctype == NIL) 872 return; 873 if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) { 874 cerror("Specified tag constant type clashed with variant case selector type"); 875 return; 876 } 877 for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 878 if (ap->range[0] == con.crval) 879 break; 880 if (ap == NIL) { 881 error("No variant case label value equals specified constant value"); 882 return; 883 } 884 ap = ap->ptr[NL_VTOREC]; 885 } 886 } 887 put(2, op, width(ap)); 888 return; 889 890 case O_DATE: 891 case O_TIME: 892 if (argc != 1) { 893 error("%s expects one argument", p->symbol); 894 return; 895 } 896 ap = stklval(argv[1], MOD|NOUSE); 897 if (ap == NIL) 898 return; 899 if (classify(ap) != TSTR || width(ap) != 10) { 900 error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 901 return; 902 } 903 put(1, op); 904 return; 905 906 case O_HALT: 907 if (argc != 0) { 908 error("halt takes no arguments"); 909 return; 910 } 911 put(1, op); 912 noreach = 1; 913 return; 914 915 case O_ARGV: 916 if (argc != 2) { 917 error("argv takes two arguments"); 918 return; 919 } 920 ap = stkrval(argv[1], NIL , RREQ ); 921 if (ap == NIL) 922 return; 923 if (isnta(ap, "i")) { 924 error("argv's first argument must be an integer, not %s", nameof(ap)); 925 return; 926 } 927 al = argv[2]; 928 ap = stklval(al[1], MOD|NOUSE); 929 if (ap == NIL) 930 return; 931 if (classify(ap) != TSTR) { 932 error("argv's second argument must be a string, not %s", nameof(ap)); 933 return; 934 } 935 put(2, op, width(ap)); 936 return; 937 938 case O_STLIM: 939 if (argc != 1) { 940 error("stlimit requires one argument"); 941 return; 942 } 943 ap = stkrval(argv[1], NIL , RREQ ); 944 if (ap == NIL) 945 return; 946 if (isnta(ap, "i")) { 947 error("stlimit's argument must be an integer, not %s", nameof(ap)); 948 return; 949 } 950 if (width(ap) != 4) 951 put(1, O_STOI); 952 put(1, op); 953 return; 954 955 case O_REMOVE: 956 if (argc != 1) { 957 error("remove expects one argument"); 958 return; 959 } 960 codeoff(); 961 ap = stkrval(argv[1], NOFLAGS , RREQ ); 962 codeon(); 963 if (ap == NIL) 964 return; 965 if (classify(ap) != TSTR) { 966 error("remove's argument must be a string, not %s", nameof(ap)); 967 return; 968 } 969 put(2, O_CON24, width(ap)); 970 ap = stkrval(argv[1], NOFLAGS , RREQ ); 971 put(1, op); 972 return; 973 974 case O_LLIMIT: 975 if (argc != 2) { 976 error("linelimit expects two arguments"); 977 return; 978 } 979 al = argv[2]; 980 ap = stkrval(al[1], NIL , RREQ ); 981 if (ap == NIL) 982 return; 983 if (isnta(ap, "i")) { 984 error("linelimit's second argument must be an integer, not %s", nameof(ap)); 985 return; 986 } 987 ap = stklval(argv[1], NOFLAGS|NOUSE); 988 if (ap == NIL) 989 return; 990 if (!text(ap)) { 991 error("linelimit's first argument must be a text file, not %s", nameof(ap)); 992 return; 993 } 994 put(1, op); 995 return; 996 case O_PAGE: 997 if (argc != 1) { 998 error("page expects one argument"); 999 return; 1000 } 1001 ap = stklval(argv[1], NIL , LREQ ); 1002 if (ap == NIL) 1003 return; 1004 if (!text(ap)) { 1005 error("Argument to page must be a text file, not %s", nameof(ap)); 1006 return; 1007 } 1008 put(1, O_UNIT); 1009 put(1, op); 1010 return; 1011 1012 case O_ASRT: 1013 if (!opt('t')) 1014 return; 1015 if (argc == 0 || argc > 2) { 1016 error("Assert expects one or two arguments"); 1017 return; 1018 } 1019 if (argc == 2) { 1020 /* 1021 * Optional second argument is a string specifying 1022 * why the assertion failed. 1023 */ 1024 al = argv[2]; 1025 al = stkrval(al[1], NIL , RREQ ); 1026 if (al == NIL) 1027 return; 1028 if (classify(al) != TSTR) { 1029 error("Second argument to assert must be a string, not %s", nameof(al)); 1030 return; 1031 } 1032 } else { 1033 put(2, PTR_CON, NIL); 1034 } 1035 ap = stkrval(argv[1], NIL , RREQ ); 1036 if (ap == NIL) 1037 return; 1038 if (isnta(ap, "b")) 1039 error("Assert expression must be Boolean, not %ss", nameof(ap)); 1040 put(1, O_ASRT); 1041 return; 1042 1043 case O_PACK: 1044 if (argc != 3) { 1045 error("pack expects three arguments"); 1046 return; 1047 } 1048 pu = "pack(a,i,z)"; 1049 pua = argv[1]; 1050 al = argv[2]; 1051 pui = al[1]; 1052 alv = al[2]; 1053 puz = alv[1]; 1054 goto packunp; 1055 case O_UNPACK: 1056 if (argc != 3) { 1057 error("unpack expects three arguments"); 1058 return; 1059 } 1060 pu = "unpack(z,a,i)"; 1061 puz = argv[1]; 1062 al = argv[2]; 1063 pua = al[1]; 1064 alv = al[2]; 1065 pui = alv[1]; 1066 packunp: 1067 codeoff(); 1068 ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 1069 al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 1070 codeon(); 1071 if (ap == NIL) 1072 return; 1073 if (ap->class != ARRAY) { 1074 error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1075 return; 1076 } 1077 if (al->class != ARRAY) { 1078 error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1079 return; 1080 } 1081 if (al->type == NIL || ap->type == NIL) 1082 return; 1083 if (al->type != ap->type) { 1084 error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1085 return; 1086 } 1087 k = width(al); 1088 itemwidth = width(ap->type); 1089 ap = ap->chain; 1090 al = al->chain; 1091 if (ap->chain != NIL || al->chain != NIL) { 1092 error("%s requires a and z to be single dimension arrays", pu); 1093 return; 1094 } 1095 if (ap == NIL || al == NIL) 1096 return; 1097 /* 1098 * al is the range for z i.e. u..v 1099 * ap is the range for a i.e. m..n 1100 * i will be n-m+1 1101 * j will be v-u+1 1102 */ 1103 i = ap->range[1] - ap->range[0] + 1; 1104 j = al->range[1] - al->range[0] + 1; 1105 if (i < j) { 1106 error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i); 1107 return; 1108 } 1109 /* 1110 * get n-m-(v-u) and m for the interpreter 1111 */ 1112 i -= j; 1113 j = ap->range[0]; 1114 put(2, O_CON24, k); 1115 put(2, O_CON24, i); 1116 put(2, O_CON24, j); 1117 put(2, O_CON24, itemwidth); 1118 al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 1119 ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 1120 ap = stkrval((int *) pui, NLNIL , RREQ ); 1121 if (ap == NIL) 1122 return; 1123 put(1, op); 1124 return; 1125 case 0: 1126 error("%s is an unimplemented extension", p->symbol); 1127 return; 1128 1129 default: 1130 panic("proc case"); 1131 } 1132 } 1133 #endif OBJ 1134