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