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