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