1 /* Copyright (c) 1979 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)pcproc.c 1.11 08/29/82"; 4 5 #include "whoami.h" 6 #ifdef PC 7 /* 8 * and to the end of the file 9 */ 10 #include "0.h" 11 #include "tree.h" 12 #include "opcode.h" 13 #include "pc.h" 14 #include "pcops.h" 15 16 /* 17 * The following array is used to determine which classes may be read 18 * from textfiles. It is indexed by the return value from classify. 19 */ 20 #define rdops(x) rdxxxx[(x)-(TFIRST)] 21 22 int rdxxxx[] = { 23 0, /* -7 file types */ 24 0, /* -6 record types */ 25 0, /* -5 array types */ 26 O_READE, /* -4 scalar types */ 27 0, /* -3 pointer types */ 28 0, /* -2 set types */ 29 0, /* -1 string types */ 30 0, /* 0 nil, no type */ 31 O_READE, /* 1 boolean */ 32 O_READC, /* 2 character */ 33 O_READ4, /* 3 integer */ 34 O_READ8 /* 4 real */ 35 }; 36 37 /* 38 * Proc handles procedure calls. 39 * Non-builtin procedures are "buck-passed" to func (with a flag 40 * indicating that they are actually procedures. 41 * builtin procedures are handled here. 42 */ 43 pcproc(r) 44 int *r; 45 { 46 register struct nl *p; 47 register int *alv, *al, op; 48 struct nl *filetype, *ap; 49 int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file; 50 char fmt, format[20], *strptr, *cmd; 51 int prec, field, strnglen, fmtlen, fmtstart, pu; 52 int *pua, *pui, *puz; 53 int i, j, k; 54 int itemwidth; 55 char *readname; 56 struct nl *tempnlp; 57 long readtype; 58 struct tmps soffset; 59 60 #define CONPREC 4 61 #define VARPREC 8 62 #define CONWIDTH 1 63 #define VARWIDTH 2 64 #define SKIP 16 65 66 /* 67 * Verify that the name is 68 * defined and is that of a 69 * procedure. 70 */ 71 p = lookup(r[2]); 72 if (p == NIL) { 73 rvlist(r[3]); 74 return; 75 } 76 if (p->class != PROC && p->class != FPROC) { 77 error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]); 78 rvlist(r[3]); 79 return; 80 } 81 argv = r[3]; 82 83 /* 84 * Call handles user defined 85 * procedures and functions. 86 */ 87 if (bn != 0) { 88 call(p, argv, PROC, bn); 89 return; 90 } 91 92 /* 93 * Call to built-in procedure. 94 * Count the arguments. 95 */ 96 argc = 0; 97 for (al = argv; al != NIL; al = al[2]) 98 argc++; 99 100 /* 101 * Switch on the operator 102 * associated with the built-in 103 * procedure in the namelist 104 */ 105 op = p->value[0] &~ NSTAND; 106 if (opt('s') && (p->value[0] & NSTAND)) { 107 standard(); 108 error("%s is a nonstandard procedure", p->symbol); 109 } 110 switch (op) { 111 112 case O_ABORT: 113 if (argc != 0) 114 error("null takes no arguments"); 115 return; 116 117 case O_FLUSH: 118 if (argc == 0) { 119 putleaf( P2ICON , 0 , 0 , P2INT , "_PFLUSH" ); 120 putop( P2UNARY P2CALL , P2INT ); 121 putdot( filename , line ); 122 return; 123 } 124 if (argc != 1) { 125 error("flush takes at most one argument"); 126 return; 127 } 128 putleaf( P2ICON , 0 , 0 129 , ADDTYPE( P2FTN | P2INT , P2PTR ) 130 , "_FLUSH" ); 131 ap = stklval(argv[1], NOFLAGS); 132 if (ap == NIL) 133 return; 134 if (ap->class != FILET) { 135 error("flush's argument must be a file, not %s", nameof(ap)); 136 return; 137 } 138 putop( P2CALL , P2INT ); 139 putdot( filename , line ); 140 return; 141 142 case O_MESSAGE: 143 case O_WRITEF: 144 case O_WRITLN: 145 /* 146 * Set up default file "output"'s type 147 */ 148 file = NIL; 149 filetype = nl+T1CHAR; 150 /* 151 * Determine the file implied 152 * for the write and generate 153 * code to make it the active file. 154 */ 155 if (op == O_MESSAGE) { 156 /* 157 * For message, all that matters 158 * is that the filetype is 159 * a character file. 160 * Thus "output" will suit us fine. 161 */ 162 putleaf( P2ICON , 0 , 0 , P2INT , "_PFLUSH" ); 163 putop( P2UNARY P2CALL , P2INT ); 164 putdot( filename , line ); 165 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 166 P2PTR|P2STRTY ); 167 putLV( "__err" , 0 , 0 , NGLOBAL , P2PTR|P2STRTY ); 168 putop( P2ASSIGN , P2PTR|P2STRTY ); 169 putdot( filename , line ); 170 } else if (argv != NIL && (al = argv[1])[0] != T_WEXP) { 171 /* 172 * If there is a first argument which has 173 * no write widths, then it is potentially 174 * a file name. 175 */ 176 codeoff(); 177 ap = stkrval(argv[1], NIL , RREQ ); 178 codeon(); 179 if (ap == NIL) 180 argv = argv[2]; 181 if (ap != NIL && ap->class == FILET) { 182 /* 183 * Got "write(f, ...", make 184 * f the active file, and save 185 * it and its type for use in 186 * processing the rest of the 187 * arguments to write. 188 */ 189 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 190 P2PTR|P2STRTY ); 191 putleaf( P2ICON , 0 , 0 192 , ADDTYPE( P2FTN | P2INT , P2PTR ) 193 , "_UNIT" ); 194 file = argv[1]; 195 filetype = ap->type; 196 stklval(argv[1], NOFLAGS); 197 putop( P2CALL , P2INT ); 198 putop( P2ASSIGN , P2PTR|P2STRTY ); 199 putdot( filename , line ); 200 /* 201 * Skip over the first argument 202 */ 203 argv = argv[2]; 204 argc--; 205 } else { 206 /* 207 * Set up for writing on 208 * standard output. 209 */ 210 putRV( 0, cbn , CURFILEOFFSET , 211 NLOCAL , P2PTR|P2STRTY ); 212 putLV( "_output" , 0 , 0 , NGLOBAL , 213 P2PTR|P2STRTY ); 214 putop( P2ASSIGN , P2PTR|P2STRTY ); 215 putdot( filename , line ); 216 output->nl_flags |= NUSED; 217 } 218 } else { 219 putRV( 0, cbn , CURFILEOFFSET , NLOCAL , 220 P2PTR|P2STRTY ); 221 putLV( "_output" , 0 , 0 , NGLOBAL , P2PTR|P2STRTY ); 222 putop( P2ASSIGN , P2PTR|P2STRTY ); 223 putdot( filename , line ); 224 output->nl_flags |= NUSED; 225 } 226 /* 227 * Loop and process each 228 * of the arguments. 229 */ 230 for (; argv != NIL; argv = argv[2]) { 231 /* 232 * fmtspec indicates the type (CONstant or VARiable) 233 * and number (none, WIDTH, and/or PRECision) 234 * of the fields in the printf format for this 235 * output variable. 236 * stkcnt is the number of longs pushed on the stack 237 * fmt is the format output indicator (D, E, F, O, X, S) 238 * fmtstart = 0 for leading blank; = 1 for no blank 239 */ 240 fmtspec = NIL; 241 stkcnt = 0; 242 fmt = 'D'; 243 fmtstart = 1; 244 al = argv[1]; 245 if (al == NIL) 246 continue; 247 if (al[0] == T_WEXP) 248 alv = al[1]; 249 else 250 alv = al; 251 if (alv == NIL) 252 continue; 253 codeoff(); 254 ap = stkrval(alv, NIL , RREQ ); 255 codeon(); 256 if (ap == NIL) 257 continue; 258 typ = classify(ap); 259 if (al[0] == T_WEXP) { 260 /* 261 * Handle width expressions. 262 * The basic game here is that width 263 * expressions get evaluated. If they 264 * are constant, the value is placed 265 * directly in the format string. 266 * Otherwise the value is pushed onto 267 * the stack and an indirection is 268 * put into the format string. 269 */ 270 if (al[3] == OCT) 271 fmt = 'O'; 272 else if (al[3] == HEX) 273 fmt = 'X'; 274 else if (al[3] != NIL) { 275 /* 276 * Evaluate second format spec 277 */ 278 if ( constval(al[3]) 279 && isa( con.ctype , "i" ) ) { 280 fmtspec += CONPREC; 281 prec = con.crval; 282 } else { 283 fmtspec += VARPREC; 284 } 285 fmt = 'f'; 286 switch ( typ ) { 287 case TINT: 288 if ( opt( 's' ) ) { 289 standard(); 290 error("Writing %ss with two write widths is non-standard", clnames[typ]); 291 } 292 /* and fall through */ 293 case TDOUBLE: 294 break; 295 default: 296 error("Cannot write %ss with two write widths", clnames[typ]); 297 continue; 298 } 299 } 300 /* 301 * Evaluate first format spec 302 */ 303 if (al[2] != NIL) { 304 if ( constval(al[2]) 305 && isa( con.ctype , "i" ) ) { 306 fmtspec += CONWIDTH; 307 field = con.crval; 308 } else { 309 fmtspec += VARWIDTH; 310 } 311 } 312 if ((fmtspec & CONPREC) && prec < 0 || 313 (fmtspec & CONWIDTH) && field < 0) { 314 error("Negative widths are not allowed"); 315 continue; 316 } 317 if ( opt('s') && 318 ((fmtspec & CONPREC) && prec == 0 || 319 (fmtspec & CONWIDTH) && field == 0)) { 320 standard(); 321 error("Zero widths are non-standard"); 322 } 323 } 324 if (filetype != nl+T1CHAR) { 325 if (fmt == 'O' || fmt == 'X') { 326 error("Oct/hex allowed only on text files"); 327 continue; 328 } 329 if (fmtspec) { 330 error("Write widths allowed only on text files"); 331 continue; 332 } 333 /* 334 * Generalized write, i.e. 335 * to a non-textfile. 336 */ 337 putleaf( P2ICON , 0 , 0 338 , ADDTYPE( 339 ADDTYPE( 340 ADDTYPE( p2type( filetype ) 341 , P2PTR ) 342 , P2FTN ) 343 , P2PTR ) 344 , "_FNIL" ); 345 stklval(file, NOFLAGS); 346 putop( P2CALL 347 , ADDTYPE( p2type( filetype ) , P2PTR ) ); 348 putop( P2UNARY P2MUL , p2type( filetype ) ); 349 /* 350 * file^ := ... 351 */ 352 switch ( classify( filetype ) ) { 353 case TBOOL: 354 case TCHAR: 355 case TINT: 356 case TSCAL: 357 precheck( filetype , "_RANG4" , "_RSNG4" ); 358 /* and fall through */ 359 case TDOUBLE: 360 case TPTR: 361 ap = rvalue( argv[1] , filetype , RREQ ); 362 break; 363 default: 364 ap = rvalue( argv[1] , filetype , LREQ ); 365 break; 366 } 367 if (ap == NIL) 368 continue; 369 if (incompat(ap, filetype, argv[1])) { 370 cerror("Type mismatch in write to non-text file"); 371 continue; 372 } 373 switch ( classify( filetype ) ) { 374 case TBOOL: 375 case TCHAR: 376 case TINT: 377 case TSCAL: 378 postcheck( filetype ); 379 /* and fall through */ 380 case TDOUBLE: 381 case TPTR: 382 putop( P2ASSIGN , p2type( filetype ) ); 383 putdot( filename , line ); 384 break; 385 default: 386 putstrop( P2STASG 387 , p2type( filetype ) 388 , lwidth( filetype ) 389 , align( filetype ) ); 390 putdot( filename , line ); 391 break; 392 } 393 /* 394 * put(file) 395 */ 396 putleaf( P2ICON , 0 , 0 397 , ADDTYPE( P2FTN | P2INT , P2PTR ) 398 , "_PUT" ); 399 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 400 P2PTR|P2STRTY ); 401 putop( P2CALL , P2INT ); 402 putdot( filename , line ); 403 continue; 404 } 405 /* 406 * Write to a textfile 407 * 408 * Evaluate the expression 409 * to be written. 410 */ 411 if (fmt == 'O' || fmt == 'X') { 412 if (opt('s')) { 413 standard(); 414 error("Oct and hex are non-standard"); 415 } 416 if (typ == TSTR || typ == TDOUBLE) { 417 error("Can't write %ss with oct/hex", clnames[typ]); 418 continue; 419 } 420 if (typ == TCHAR || typ == TBOOL) 421 typ = TINT; 422 } 423 /* 424 * If there is no format specified by the programmer, 425 * implement the default. 426 */ 427 switch (typ) { 428 case TPTR: 429 warning(); 430 if (opt('s')) { 431 standard(); 432 } 433 error("Writing %ss to text files is non-standard", 434 clnames[typ]); 435 /* and fall through */ 436 case TINT: 437 if (fmt == 'f') { 438 typ = TDOUBLE; 439 goto tdouble; 440 } 441 if (fmtspec == NIL) { 442 if (fmt == 'D') 443 field = 10; 444 else if (fmt == 'X') 445 field = 8; 446 else if (fmt == 'O') 447 field = 11; 448 else 449 panic("fmt1"); 450 fmtspec = CONWIDTH; 451 } 452 break; 453 case TCHAR: 454 tchar: 455 fmt = 'c'; 456 break; 457 case TSCAL: 458 warning(); 459 if (opt('s')) { 460 standard(); 461 } 462 error("Writing %ss to text files is non-standard", 463 clnames[typ]); 464 case TBOOL: 465 fmt = 's'; 466 break; 467 case TDOUBLE: 468 tdouble: 469 switch (fmtspec) { 470 case NIL: 471 field = 21; 472 prec = 14; 473 fmt = 'e'; 474 fmtspec = CONWIDTH + CONPREC; 475 break; 476 case CONWIDTH: 477 if (--field < 1) 478 field = 1; 479 prec = field - 7; 480 if (prec < 1) 481 prec = 1; 482 fmtspec += CONPREC; 483 fmt = 'e'; 484 break; 485 case VARWIDTH: 486 fmtspec += VARPREC; 487 fmt = 'e'; 488 break; 489 case CONWIDTH + CONPREC: 490 case CONWIDTH + VARPREC: 491 if (--field < 1) 492 field = 1; 493 } 494 format[0] = ' '; 495 fmtstart = 0; 496 break; 497 case TSTR: 498 constval( alv ); 499 switch ( classify( con.ctype ) ) { 500 case TCHAR: 501 typ = TCHAR; 502 goto tchar; 503 case TSTR: 504 strptr = con.cpval; 505 for (strnglen = 0; *strptr++; strnglen++) /* void */; 506 strptr = con.cpval; 507 break; 508 default: 509 strnglen = width(ap); 510 break; 511 } 512 fmt = 's'; 513 strfmt = fmtspec; 514 if (fmtspec == NIL) { 515 fmtspec = SKIP; 516 break; 517 } 518 if (fmtspec & CONWIDTH) { 519 if (field <= strnglen) 520 fmtspec = SKIP; 521 else 522 field -= strnglen; 523 } 524 break; 525 default: 526 error("Can't write %ss to a text file", clnames[typ]); 527 continue; 528 } 529 /* 530 * Generate the format string 531 */ 532 switch (fmtspec) { 533 default: 534 panic("fmt2"); 535 case NIL: 536 if (fmt == 'c') { 537 if ( opt( 't' ) ) { 538 putleaf( P2ICON , 0 , 0 539 , ADDTYPE( P2FTN|P2INT , P2PTR ) 540 , "_WRITEC" ); 541 putRV( 0 , cbn , CURFILEOFFSET , 542 NLOCAL , P2PTR|P2STRTY ); 543 stkrval( alv , NIL , RREQ ); 544 putop( P2LISTOP , P2INT ); 545 } else { 546 putleaf( P2ICON , 0 , 0 547 , ADDTYPE( P2FTN|P2INT , P2PTR ) 548 , "_fputc" ); 549 stkrval( alv , NIL , RREQ ); 550 } 551 putleaf( P2ICON , 0 , 0 552 , ADDTYPE( P2FTN | P2INT , P2PTR ) 553 , "_ACTFILE" ); 554 putRV( 0, cbn , CURFILEOFFSET , 555 NLOCAL , P2PTR|P2STRTY ); 556 putop( P2CALL , P2INT ); 557 putop( P2LISTOP , P2INT ); 558 putop( P2CALL , P2INT ); 559 putdot( filename , line ); 560 } else { 561 sprintf(&format[1], "%%%c", fmt); 562 goto fmtgen; 563 } 564 case SKIP: 565 break; 566 case CONWIDTH: 567 sprintf(&format[1], "%%%1D%c", field, fmt); 568 goto fmtgen; 569 case VARWIDTH: 570 sprintf(&format[1], "%%*%c", fmt); 571 goto fmtgen; 572 case CONWIDTH + CONPREC: 573 sprintf(&format[1], "%%%1D.%1D%c", field, prec, fmt); 574 goto fmtgen; 575 case CONWIDTH + VARPREC: 576 sprintf(&format[1], "%%%1D.*%c", field, fmt); 577 goto fmtgen; 578 case VARWIDTH + CONPREC: 579 sprintf(&format[1], "%%*.%1D%c", prec, fmt); 580 goto fmtgen; 581 case VARWIDTH + VARPREC: 582 sprintf(&format[1], "%%*.*%c", fmt); 583 fmtgen: 584 if ( opt( 't' ) ) { 585 putleaf( P2ICON , 0 , 0 586 , ADDTYPE( P2FTN | P2INT , P2PTR ) 587 , "_WRITEF" ); 588 putRV( 0 , cbn , CURFILEOFFSET , 589 NLOCAL , P2PTR|P2STRTY ); 590 putleaf( P2ICON , 0 , 0 591 , ADDTYPE( P2FTN | P2INT , P2PTR ) 592 , "_ACTFILE" ); 593 putRV( 0 , cbn , CURFILEOFFSET , 594 NLOCAL , P2PTR|P2STRTY ); 595 putop( P2CALL , P2INT ); 596 putop( P2LISTOP , P2INT ); 597 } else { 598 putleaf( P2ICON , 0 , 0 599 , ADDTYPE( P2FTN | P2INT , P2PTR ) 600 , "_fprintf" ); 601 putleaf( P2ICON , 0 , 0 602 , ADDTYPE( P2FTN | P2INT , P2PTR ) 603 , "_ACTFILE" ); 604 putRV( 0 , cbn , CURFILEOFFSET , 605 NLOCAL , P2PTR|P2STRTY ); 606 putop( P2CALL , P2INT ); 607 } 608 putCONG( &format[ fmtstart ] 609 , strlen( &format[ fmtstart ] ) 610 , LREQ ); 611 putop( P2LISTOP , P2INT ); 612 if ( fmtspec & VARWIDTH ) { 613 /* 614 * either 615 * ,(temp=width,MAX(temp,...)), 616 * or 617 * , MAX( width , ... ) , 618 */ 619 if ( ( typ == TDOUBLE && al[3] == NIL ) 620 || typ == TSTR ) { 621 soffset = sizes[cbn].curtmps; 622 tempnlp = tmpalloc(sizeof(long), 623 nl+T4INT, REGOK); 624 putRV( 0 , cbn , 625 tempnlp -> value[ NL_OFFS ] , 626 tempnlp -> extra_flags , P2INT ); 627 ap = stkrval( al[2] , NIL , RREQ ); 628 putop( P2ASSIGN , P2INT ); 629 putleaf( P2ICON , 0 , 0 630 , ADDTYPE( P2FTN | P2INT , P2PTR ) 631 , "_MAX" ); 632 putRV( 0 , cbn , 633 tempnlp -> value[ NL_OFFS ] , 634 tempnlp -> extra_flags , P2INT ); 635 } else { 636 if (opt('t') 637 || typ == TSTR || typ == TDOUBLE) { 638 putleaf( P2ICON , 0 , 0 639 ,ADDTYPE( P2FTN | P2INT, P2PTR ) 640 ,"_MAX" ); 641 } 642 ap = stkrval( al[2] , NIL , RREQ ); 643 } 644 if (ap == NIL) 645 continue; 646 if (isnta(ap,"i")) { 647 error("First write width must be integer, not %s", nameof(ap)); 648 continue; 649 } 650 switch ( typ ) { 651 case TDOUBLE: 652 putleaf( P2ICON , 1 , 0 , P2INT , 0 ); 653 putop( P2LISTOP , P2INT ); 654 putleaf( P2ICON , 1 , 0 , P2INT , 0 ); 655 putop( P2LISTOP , P2INT ); 656 putop( P2CALL , P2INT ); 657 if ( al[3] == NIL ) { 658 /* 659 * finish up the comma op 660 */ 661 putop( P2COMOP , P2INT ); 662 fmtspec &= ~VARPREC; 663 putop( P2LISTOP , P2INT ); 664 putleaf( P2ICON , 0 , 0 665 , ADDTYPE( P2FTN | P2INT , P2PTR ) 666 , "_MAX" ); 667 putRV( 0 , cbn , 668 tempnlp -> value[ NL_OFFS ] , 669 tempnlp -> extra_flags , 670 P2INT ); 671 tmpfree(&soffset); 672 putleaf( P2ICON , 8 , 0 , P2INT , 0 ); 673 putop( P2LISTOP , P2INT ); 674 putleaf( P2ICON , 1 , 0 , P2INT , 0 ); 675 putop( P2LISTOP , P2INT ); 676 putop( P2CALL , P2INT ); 677 } 678 putop( P2LISTOP , P2INT ); 679 break; 680 case TSTR: 681 putleaf( P2ICON , strnglen , 0 , P2INT , 0 ); 682 putop( P2LISTOP , P2INT ); 683 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 684 putop( P2LISTOP , P2INT ); 685 putop( P2CALL , P2INT ); 686 putop( P2COMOP , P2INT ); 687 putop( P2LISTOP , P2INT ); 688 break; 689 default: 690 if (opt('t')) { 691 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 692 putop( P2LISTOP , P2INT ); 693 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 694 putop( P2LISTOP , P2INT ); 695 putop( P2CALL , P2INT ); 696 } 697 putop( P2LISTOP , P2INT ); 698 break; 699 } 700 } 701 /* 702 * If there is a variable precision, 703 * evaluate it 704 */ 705 if (fmtspec & VARPREC) { 706 if (opt('t')) { 707 putleaf( P2ICON , 0 , 0 708 , ADDTYPE( P2FTN | P2INT , P2PTR ) 709 , "_MAX" ); 710 } 711 ap = stkrval( al[3] , NIL , RREQ ); 712 if (ap == NIL) 713 continue; 714 if (isnta(ap,"i")) { 715 error("Second write width must be integer, not %s", nameof(ap)); 716 continue; 717 } 718 if (opt('t')) { 719 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 720 putop( P2LISTOP , P2INT ); 721 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 722 putop( P2LISTOP , P2INT ); 723 putop( P2CALL , P2INT ); 724 } 725 putop( P2LISTOP , P2INT ); 726 } 727 /* 728 * evaluate the thing we want printed. 729 */ 730 switch ( typ ) { 731 case TPTR: 732 case TCHAR: 733 case TINT: 734 stkrval( alv , NIL , RREQ ); 735 putop( P2LISTOP , P2INT ); 736 break; 737 case TDOUBLE: 738 ap = stkrval( alv , NIL , RREQ ); 739 if ( isnta( ap , "d" ) ) { 740 putop( P2SCONV , P2DOUBLE ); 741 } 742 putop( P2LISTOP , P2INT ); 743 break; 744 case TSCAL: 745 case TBOOL: 746 putleaf( P2ICON , 0 , 0 747 , ADDTYPE( P2FTN | P2INT , P2PTR ) 748 , "_NAM" ); 749 ap = stkrval( alv , NIL , RREQ ); 750 sprintf( format , PREFIXFORMAT , LABELPREFIX 751 , listnames( ap ) ); 752 putleaf( P2ICON , 0 , 0 , P2PTR | P2CHAR 753 , format ); 754 putop( P2LISTOP , P2INT ); 755 putop( P2CALL , P2INT ); 756 putop( P2LISTOP , P2INT ); 757 break; 758 case TSTR: 759 putCONG( "" , 0 , LREQ ); 760 putop( P2LISTOP , P2INT ); 761 break; 762 default: 763 panic("fmt3"); 764 break; 765 } 766 putop( P2CALL , P2INT ); 767 putdot( filename , line ); 768 } 769 /* 770 * Write the string after its blank padding 771 */ 772 if (typ == TSTR ) { 773 if ( opt( 't' ) ) { 774 putleaf( P2ICON , 0 , 0 775 , ADDTYPE( P2FTN | P2INT , P2PTR ) 776 , "_WRITES" ); 777 putRV( 0 , cbn , CURFILEOFFSET , 778 NLOCAL , P2PTR|P2STRTY ); 779 ap = stkrval(alv, NIL , RREQ ); 780 putop( P2LISTOP , P2INT ); 781 } else { 782 putleaf( P2ICON , 0 , 0 783 , ADDTYPE( P2FTN | P2INT , P2PTR ) 784 , "_fwrite" ); 785 ap = stkrval(alv, NIL , RREQ ); 786 } 787 if (strfmt & VARWIDTH) { 788 /* 789 * min, inline expanded as 790 * temp < len ? temp : len 791 */ 792 putRV( 0 , cbn , 793 tempnlp -> value[ NL_OFFS ] , 794 tempnlp -> extra_flags , P2INT ); 795 putleaf( P2ICON , strnglen , 0 , P2INT , 0 ); 796 putop( P2LT , P2INT ); 797 putRV( 0 , cbn , 798 tempnlp -> value[ NL_OFFS ] , 799 tempnlp -> extra_flags , P2INT ); 800 putleaf( P2ICON , strnglen , 0 , P2INT , 0 ); 801 putop( P2COLON , P2INT ); 802 putop( P2QUEST , P2INT ); 803 tmpfree(&soffset); 804 } else { 805 if ( ( fmtspec & SKIP ) 806 && ( strfmt & CONWIDTH ) ) { 807 strnglen = field; 808 } 809 putleaf( P2ICON , strnglen , 0 , P2INT , 0 ); 810 } 811 putop( P2LISTOP , P2INT ); 812 putleaf( P2ICON , 1 , 0 , P2INT , 0 ); 813 putop( P2LISTOP , P2INT ); 814 putleaf( P2ICON , 0 , 0 815 , ADDTYPE( P2FTN | P2INT , P2PTR ) 816 , "_ACTFILE" ); 817 putRV( 0, cbn , CURFILEOFFSET , NLOCAL , 818 P2PTR|P2STRTY ); 819 putop( P2CALL , P2INT ); 820 putop( P2LISTOP , P2INT ); 821 putop( P2CALL , P2INT ); 822 putdot( filename , line ); 823 } 824 } 825 /* 826 * Done with arguments. 827 * Handle writeln and 828 * insufficent number of args. 829 */ 830 switch (p->value[0] &~ NSTAND) { 831 case O_WRITEF: 832 if (argc == 0) 833 error("Write requires an argument"); 834 break; 835 case O_MESSAGE: 836 if (argc == 0) 837 error("Message requires an argument"); 838 case O_WRITLN: 839 if (filetype != nl+T1CHAR) 840 error("Can't 'writeln' a non text file"); 841 if ( opt( 't' ) ) { 842 putleaf( P2ICON , 0 , 0 843 , ADDTYPE( P2FTN | P2INT , P2PTR ) 844 , "_WRITLN" ); 845 putRV( 0 , cbn , CURFILEOFFSET , 846 NLOCAL , P2PTR|P2STRTY ); 847 } else { 848 putleaf( P2ICON , 0 , 0 849 , ADDTYPE( P2FTN | P2INT , P2PTR ) 850 , "_fputc" ); 851 putleaf( P2ICON , '\n' , 0 , P2CHAR , 0 ); 852 putleaf( P2ICON , 0 , 0 853 , ADDTYPE( P2FTN | P2INT , P2PTR ) 854 , "_ACTFILE" ); 855 putRV( 0 , cbn , CURFILEOFFSET , 856 NLOCAL , P2PTR|P2STRTY ); 857 putop( P2CALL , P2INT ); 858 putop( P2LISTOP , P2INT ); 859 } 860 putop( P2CALL , P2INT ); 861 putdot( filename , line ); 862 break; 863 } 864 return; 865 866 case O_READ4: 867 case O_READLN: 868 /* 869 * Set up default 870 * file "input". 871 */ 872 file = NIL; 873 filetype = nl+T1CHAR; 874 /* 875 * Determine the file implied 876 * for the read and generate 877 * code to make it the active file. 878 */ 879 if (argv != NIL) { 880 codeoff(); 881 ap = stkrval(argv[1], NIL , RREQ ); 882 codeon(); 883 if (ap == NIL) 884 argv = argv[2]; 885 if (ap != NIL && ap->class == FILET) { 886 /* 887 * Got "read(f, ...", make 888 * f the active file, and save 889 * it and its type for use in 890 * processing the rest of the 891 * arguments to read. 892 */ 893 file = argv[1]; 894 filetype = ap->type; 895 putRV( 0, cbn , CURFILEOFFSET , NLOCAL , 896 P2PTR|P2STRTY ); 897 putleaf( P2ICON , 0 , 0 898 , ADDTYPE( P2FTN | P2INT , P2PTR ) 899 , "_UNIT" ); 900 stklval(argv[1], NOFLAGS); 901 putop( P2CALL , P2INT ); 902 putop( P2ASSIGN , P2PTR|P2STRTY ); 903 putdot( filename , line ); 904 argv = argv[2]; 905 argc--; 906 } else { 907 /* 908 * Default is read from 909 * standard input. 910 */ 911 putRV( 0, cbn , CURFILEOFFSET , NLOCAL , 912 P2PTR|P2STRTY ); 913 putLV( "_input" , 0 , 0 , NGLOBAL , 914 P2PTR|P2STRTY ); 915 putop( P2ASSIGN , P2PTR|P2STRTY ); 916 putdot( filename , line ); 917 input->nl_flags |= NUSED; 918 } 919 } else { 920 putRV( 0, cbn , CURFILEOFFSET , NLOCAL , 921 P2PTR|P2STRTY ); 922 putLV( "_input" , 0 , 0 , NGLOBAL , P2PTR|P2STRTY ); 923 putop( P2ASSIGN , P2PTR|P2STRTY ); 924 putdot( filename , line ); 925 input->nl_flags |= NUSED; 926 } 927 /* 928 * Loop and process each 929 * of the arguments. 930 */ 931 for (; argv != NIL; argv = argv[2]) { 932 /* 933 * Get the address of the target 934 * on the stack. 935 */ 936 al = argv[1]; 937 if (al == NIL) 938 continue; 939 if (al[0] != T_VAR) { 940 error("Arguments to %s must be variables, not expressions", p->symbol); 941 continue; 942 } 943 codeoff(); 944 ap = stklval(al, MOD|ASGN|NOUSE); 945 codeon(); 946 if (ap == NIL) 947 continue; 948 if (filetype != nl+T1CHAR) { 949 /* 950 * Generalized read, i.e. 951 * from a non-textfile. 952 */ 953 if (incompat(filetype, ap, argv[1] )) { 954 error("Type mismatch in read from non-text file"); 955 continue; 956 } 957 /* 958 * var := file ^; 959 */ 960 ap = lvalue( al , MOD | ASGN | NOUSE , RREQ ); 961 if ( isa( ap , "bsci" ) ) { 962 precheck( ap , "_RANG4" , "_RSNG4" ); 963 } 964 putleaf( P2ICON , 0 , 0 965 , ADDTYPE( 966 ADDTYPE( 967 ADDTYPE( 968 p2type( filetype ) , P2PTR ) 969 , P2FTN ) 970 , P2PTR ) 971 , "_FNIL" ); 972 if (file != NIL) 973 stklval(file, NOFLAGS); 974 else /* Magic */ 975 putRV( "_input" , 0 , 0 , NGLOBAL , 976 P2PTR | P2STRTY ); 977 putop( P2CALL , P2INT ); 978 switch ( classify( filetype ) ) { 979 case TBOOL: 980 case TCHAR: 981 case TINT: 982 case TSCAL: 983 case TDOUBLE: 984 case TPTR: 985 putop( P2UNARY P2MUL 986 , p2type( filetype ) ); 987 } 988 switch ( classify( filetype ) ) { 989 case TBOOL: 990 case TCHAR: 991 case TINT: 992 case TSCAL: 993 postcheck( ap ); 994 /* and fall through */ 995 case TDOUBLE: 996 case TPTR: 997 putop( P2ASSIGN , p2type( ap ) ); 998 putdot( filename , line ); 999 break; 1000 default: 1001 putstrop( P2STASG 1002 , p2type( ap ) 1003 , lwidth( ap ) 1004 , align( ap ) ); 1005 putdot( filename , line ); 1006 break; 1007 } 1008 /* 1009 * get(file); 1010 */ 1011 putleaf( P2ICON , 0 , 0 1012 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1013 , "_GET" ); 1014 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 1015 P2PTR|P2STRTY ); 1016 putop( P2CALL , P2INT ); 1017 putdot( filename , line ); 1018 continue; 1019 } 1020 /* 1021 * if you get to here, you are reading from 1022 * a text file. only possiblities are: 1023 * character, integer, real, or scalar. 1024 * read( f , foo , ... ) is done as 1025 * foo := read( f ) with rangechecking 1026 * if appropriate. 1027 */ 1028 typ = classify(ap); 1029 op = rdops(typ); 1030 if (op == NIL) { 1031 error("Can't read %ss from a text file", clnames[typ]); 1032 continue; 1033 } 1034 /* 1035 * left hand side of foo := read( f ) 1036 */ 1037 ap = lvalue( al , MOD|ASGN|NOUSE , RREQ ); 1038 if ( isa( ap , "bsci" ) ) { 1039 precheck( ap , "_RANG4" , "_RSNG4" ); 1040 } 1041 switch ( op ) { 1042 case O_READC: 1043 readname = "_READC"; 1044 readtype = P2INT; 1045 break; 1046 case O_READ4: 1047 readname = "_READ4"; 1048 readtype = P2INT; 1049 break; 1050 case O_READ8: 1051 readname = "_READ8"; 1052 readtype = P2DOUBLE; 1053 break; 1054 case O_READE: 1055 readname = "_READE"; 1056 readtype = P2INT; 1057 break; 1058 } 1059 putleaf( P2ICON , 0 , 0 1060 , ADDTYPE( P2FTN | readtype , P2PTR ) 1061 , readname ); 1062 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 1063 P2PTR|P2STRTY ); 1064 if ( op == O_READE ) { 1065 sprintf( format , PREFIXFORMAT , LABELPREFIX 1066 , listnames( ap ) ); 1067 putleaf( P2ICON , 0 , 0 , P2PTR | P2CHAR 1068 , format ); 1069 putop( P2LISTOP , P2INT ); 1070 warning(); 1071 if (opt('s')) { 1072 standard(); 1073 } 1074 error("Reading scalars from text files is non-standard"); 1075 } 1076 putop( P2CALL , readtype ); 1077 if ( isa( ap , "bcsi" ) ) { 1078 postcheck( ap ); 1079 } 1080 putop( P2ASSIGN , p2type( ap ) ); 1081 putdot( filename , line ); 1082 } 1083 /* 1084 * Done with arguments. 1085 * Handle readln and 1086 * insufficient number of args. 1087 */ 1088 if (p->value[0] == O_READLN) { 1089 if (filetype != nl+T1CHAR) 1090 error("Can't 'readln' a non text file"); 1091 putleaf( P2ICON , 0 , 0 1092 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1093 , "_READLN" ); 1094 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , 1095 P2PTR|P2STRTY ); 1096 putop( P2CALL , P2INT ); 1097 putdot( filename , line ); 1098 } else if (argc == 0) 1099 error("read requires an argument"); 1100 return; 1101 1102 case O_GET: 1103 case O_PUT: 1104 if (argc != 1) { 1105 error("%s expects one argument", p->symbol); 1106 return; 1107 } 1108 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , P2PTR|P2STRTY ); 1109 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1110 , "_UNIT" ); 1111 ap = stklval(argv[1], NOFLAGS); 1112 if (ap == NIL) 1113 return; 1114 if (ap->class != FILET) { 1115 error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 1116 return; 1117 } 1118 putop( P2CALL , P2INT ); 1119 putop( P2ASSIGN , P2PTR|P2STRTY ); 1120 putdot( filename , line ); 1121 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1122 , op == O_GET ? "_GET" : "_PUT" ); 1123 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , P2PTR|P2STRTY ); 1124 putop( P2CALL , P2INT ); 1125 putdot( filename , line ); 1126 return; 1127 1128 case O_RESET: 1129 case O_REWRITE: 1130 if (argc == 0 || argc > 2) { 1131 error("%s expects one or two arguments", p->symbol); 1132 return; 1133 } 1134 if (opt('s') && argc == 2) { 1135 standard(); 1136 error("Two argument forms of reset and rewrite are non-standard"); 1137 } 1138 putleaf( P2ICON , 0 , 0 , P2INT 1139 , op == O_RESET ? "_RESET" : "_REWRITE" ); 1140 ap = stklval(argv[1], MOD|NOUSE); 1141 if (ap == NIL) 1142 return; 1143 if (ap->class != FILET) { 1144 error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 1145 return; 1146 } 1147 if (argc == 2) { 1148 /* 1149 * Optional second argument 1150 * is a string name of a 1151 * UNIX (R) file to be associated. 1152 */ 1153 al = argv[2]; 1154 al = stkrval(al[1], NOFLAGS , RREQ ); 1155 if (al == NIL) 1156 return; 1157 if (classify(al) != TSTR) { 1158 error("Second argument to %s must be a string, not %s", p->symbol, nameof(al)); 1159 return; 1160 } 1161 strnglen = width(al); 1162 } else { 1163 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 1164 strnglen = 0; 1165 } 1166 putop( P2LISTOP , P2INT ); 1167 putleaf( P2ICON , strnglen , 0 , P2INT , 0 ); 1168 putop( P2LISTOP , P2INT ); 1169 putleaf( P2ICON , text(ap) ? 0: width(ap->type) , 0 , P2INT , 0 ); 1170 putop( P2LISTOP , P2INT ); 1171 putop( P2CALL , P2INT ); 1172 putdot( filename , line ); 1173 return; 1174 1175 case O_NEW: 1176 case O_DISPOSE: 1177 if (argc == 0) { 1178 error("%s expects at least one argument", p->symbol); 1179 return; 1180 } 1181 codeoff(); 1182 ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD ); 1183 codeon(); 1184 if (ap == NIL) 1185 return; 1186 if (ap->class != PTR) { 1187 error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 1188 return; 1189 } 1190 ap = ap->type; 1191 if (ap == NIL) 1192 return; 1193 if (op == O_DISPOSE) 1194 if ((ap->nl_flags & NFILES) != 0) 1195 cmd = "_DFDISPOSE"; 1196 else 1197 cmd = "_DISPOSE"; 1198 else if (opt('t')) 1199 cmd = "_NEWZ"; 1200 else 1201 cmd = "_NEW"; 1202 putleaf( P2ICON, 0, 0, ADDTYPE( P2FTN | P2INT , P2PTR ), cmd); 1203 stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD ); 1204 argv = argv[2]; 1205 if (argv != NIL) { 1206 if (ap->class != RECORD) { 1207 error("Record required when specifying variant tags"); 1208 return; 1209 } 1210 for (; argv != NIL; argv = argv[2]) { 1211 if (ap->ptr[NL_VARNT] == NIL) { 1212 error("Too many tag fields"); 1213 return; 1214 } 1215 if (!isconst(argv[1])) { 1216 error("Second and successive arguments to %s must be constants", p->symbol); 1217 return; 1218 } 1219 gconst(argv[1]); 1220 if (con.ctype == NIL) 1221 return; 1222 if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) { 1223 cerror("Specified tag constant type clashed with variant case selector type"); 1224 return; 1225 } 1226 for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 1227 if (ap->range[0] == con.crval) 1228 break; 1229 if (ap == NIL) { 1230 error("No variant case label value equals specified constant value"); 1231 return; 1232 } 1233 ap = ap->ptr[NL_VTOREC]; 1234 } 1235 } 1236 putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 ); 1237 putop( P2LISTOP , P2INT ); 1238 putop( P2CALL , P2INT ); 1239 putdot( filename , line ); 1240 return; 1241 1242 case O_DATE: 1243 case O_TIME: 1244 if (argc != 1) { 1245 error("%s expects one argument", p->symbol); 1246 return; 1247 } 1248 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1249 , op == O_DATE ? "_DATE" : "_TIME" ); 1250 ap = stklval(argv[1], MOD|NOUSE); 1251 if (ap == NIL) 1252 return; 1253 if (classify(ap) != TSTR || width(ap) != 10) { 1254 error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 1255 return; 1256 } 1257 putop( P2CALL , P2INT ); 1258 putdot( filename , line ); 1259 return; 1260 1261 case O_HALT: 1262 if (argc != 0) { 1263 error("halt takes no arguments"); 1264 return; 1265 } 1266 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1267 , "_HALT" ); 1268 1269 putop( P2UNARY P2CALL , P2INT ); 1270 putdot( filename , line ); 1271 noreach = 1; 1272 return; 1273 1274 case O_ARGV: 1275 if (argc != 2) { 1276 error("argv takes two arguments"); 1277 return; 1278 } 1279 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1280 , "_ARGV" ); 1281 ap = stkrval(argv[1], NIL , RREQ ); 1282 if (ap == NIL) 1283 return; 1284 if (isnta(ap, "i")) { 1285 error("argv's first argument must be an integer, not %s", nameof(ap)); 1286 return; 1287 } 1288 al = argv[2]; 1289 ap = stklval(al[1], MOD|NOUSE); 1290 if (ap == NIL) 1291 return; 1292 if (classify(ap) != TSTR) { 1293 error("argv's second argument must be a string, not %s", nameof(ap)); 1294 return; 1295 } 1296 putop( P2LISTOP , P2INT ); 1297 putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 ); 1298 putop( P2LISTOP , P2INT ); 1299 putop( P2CALL , P2INT ); 1300 putdot( filename , line ); 1301 return; 1302 1303 case O_STLIM: 1304 if (argc != 1) { 1305 error("stlimit requires one argument"); 1306 return; 1307 } 1308 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1309 , "_STLIM" ); 1310 ap = stkrval(argv[1], NIL , RREQ ); 1311 if (ap == NIL) 1312 return; 1313 if (isnta(ap, "i")) { 1314 error("stlimit's argument must be an integer, not %s", nameof(ap)); 1315 return; 1316 } 1317 putop( P2CALL , P2INT ); 1318 putdot( filename , line ); 1319 return; 1320 1321 case O_REMOVE: 1322 if (argc != 1) { 1323 error("remove expects one argument"); 1324 return; 1325 } 1326 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1327 , "_REMOVE" ); 1328 ap = stkrval(argv[1], NOFLAGS , RREQ ); 1329 if (ap == NIL) 1330 return; 1331 if (classify(ap) != TSTR) { 1332 error("remove's argument must be a string, not %s", nameof(ap)); 1333 return; 1334 } 1335 putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 ); 1336 putop( P2LISTOP , P2INT ); 1337 putop( P2CALL , P2INT ); 1338 putdot( filename , line ); 1339 return; 1340 1341 case O_LLIMIT: 1342 if (argc != 2) { 1343 error("linelimit expects two arguments"); 1344 return; 1345 } 1346 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1347 , "_LLIMIT" ); 1348 ap = stklval(argv[1], NOFLAGS|NOUSE); 1349 if (ap == NIL) 1350 return; 1351 if (!text(ap)) { 1352 error("linelimit's first argument must be a text file, not %s", nameof(ap)); 1353 return; 1354 } 1355 al = argv[2]; 1356 ap = stkrval(al[1], NIL , RREQ ); 1357 if (ap == NIL) 1358 return; 1359 if (isnta(ap, "i")) { 1360 error("linelimit's second argument must be an integer, not %s", nameof(ap)); 1361 return; 1362 } 1363 putop( P2LISTOP , P2INT ); 1364 putop( P2CALL , P2INT ); 1365 putdot( filename , line ); 1366 return; 1367 case O_PAGE: 1368 if (argc != 1) { 1369 error("page expects one argument"); 1370 return; 1371 } 1372 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , P2PTR|P2STRTY ); 1373 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1374 , "_UNIT" ); 1375 ap = stklval(argv[1], NOFLAGS); 1376 if (ap == NIL) 1377 return; 1378 if (!text(ap)) { 1379 error("Argument to page must be a text file, not %s", nameof(ap)); 1380 return; 1381 } 1382 putop( P2CALL , P2INT ); 1383 putop( P2ASSIGN , P2PTR|P2STRTY ); 1384 putdot( filename , line ); 1385 if ( opt( 't' ) ) { 1386 putleaf( P2ICON , 0 , 0 1387 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1388 , "_PAGE" ); 1389 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , P2PTR|P2STRTY ); 1390 } else { 1391 putleaf( P2ICON , 0 , 0 1392 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1393 , "_fputc" ); 1394 putleaf( P2ICON , '\f' , 0 , P2CHAR , 0 ); 1395 putleaf( P2ICON , 0 , 0 1396 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1397 , "_ACTFILE" ); 1398 putRV( 0 , cbn , CURFILEOFFSET , NLOCAL , P2PTR|P2STRTY ); 1399 putop( P2CALL , P2INT ); 1400 putop( P2LISTOP , P2INT ); 1401 } 1402 putop( P2CALL , P2INT ); 1403 putdot( filename , line ); 1404 return; 1405 1406 case O_ASRT: 1407 if (!opt('t')) 1408 return; 1409 if (argc == 0 || argc > 2) { 1410 error("Assert expects one or two arguments"); 1411 return; 1412 } 1413 putleaf( P2ICON , 0 , 0 1414 , ADDTYPE( P2FTN | P2INT , P2PTR ) , "_ASRT" ); 1415 ap = stkrval(argv[1], NIL , RREQ ); 1416 if (ap == NIL) 1417 return; 1418 if (isnta(ap, "b")) 1419 error("Assert expression must be Boolean, not %ss", nameof(ap)); 1420 if (argc == 2) { 1421 /* 1422 * Optional second argument is a string specifying 1423 * why the assertion failed. 1424 */ 1425 al = argv[2]; 1426 al = stkrval(al[1], NIL , RREQ ); 1427 if (al == NIL) 1428 return; 1429 if (classify(al) != TSTR) { 1430 error("Second argument to assert must be a string, not %s", nameof(al)); 1431 return; 1432 } 1433 } else { 1434 putleaf( P2ICON , 0 , 0 , P2INT , 0 ); 1435 } 1436 putop( P2LISTOP , P2INT ); 1437 putop( P2CALL , P2INT ); 1438 putdot( filename , line ); 1439 return; 1440 1441 case O_PACK: 1442 if (argc != 3) { 1443 error("pack expects three arguments"); 1444 return; 1445 } 1446 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1447 , "_PACK" ); 1448 pu = "pack(a,i,z)"; 1449 pua = (al = argv)[1]; 1450 pui = (al = al[2])[1]; 1451 puz = (al = al[2])[1]; 1452 goto packunp; 1453 case O_UNPACK: 1454 if (argc != 3) { 1455 error("unpack expects three arguments"); 1456 return; 1457 } 1458 putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) 1459 , "_UNPACK" ); 1460 pu = "unpack(z,a,i)"; 1461 puz = (al = argv)[1]; 1462 pua = (al = al[2])[1]; 1463 pui = (al = al[2])[1]; 1464 packunp: 1465 ap = stkrval((int *) pui, NLNIL , RREQ ); 1466 if (ap == NIL) 1467 return; 1468 ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 1469 if (ap == NIL) 1470 return; 1471 if (ap->class != ARRAY) { 1472 error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1473 return; 1474 } 1475 putop( P2LISTOP , P2INT ); 1476 al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 1477 if (al->class != ARRAY) { 1478 error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1479 return; 1480 } 1481 if (al->type == NIL || ap->type == NIL) 1482 return; 1483 if (al->type != ap->type) { 1484 error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1485 return; 1486 } 1487 putop( P2LISTOP , P2INT ); 1488 k = width(al); 1489 itemwidth = width(ap->type); 1490 ap = ap->chain; 1491 al = al->chain; 1492 if (ap->chain != NIL || al->chain != NIL) { 1493 error("%s requires a and z to be single dimension arrays", pu); 1494 return; 1495 } 1496 if (ap == NIL || al == NIL) 1497 return; 1498 /* 1499 * al is the range for z i.e. u..v 1500 * ap is the range for a i.e. m..n 1501 * i will be n-m+1 1502 * j will be v-u+1 1503 */ 1504 i = ap->range[1] - ap->range[0] + 1; 1505 j = al->range[1] - al->range[0] + 1; 1506 if (i < j) { 1507 error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i); 1508 return; 1509 } 1510 /* 1511 * get n-m-(v-u) and m for the interpreter 1512 */ 1513 i -= j; 1514 j = ap->range[0]; 1515 putleaf( P2ICON , itemwidth , 0 , P2INT , 0 ); 1516 putop( P2LISTOP , P2INT ); 1517 putleaf( P2ICON , j , 0 , P2INT , 0 ); 1518 putop( P2LISTOP , P2INT ); 1519 putleaf( P2ICON , i , 0 , P2INT , 0 ); 1520 putop( P2LISTOP , P2INT ); 1521 putleaf( P2ICON , k , 0 , P2INT , 0 ); 1522 putop( P2LISTOP , P2INT ); 1523 putop( P2CALL , P2INT ); 1524 putdot( filename , line ); 1525 return; 1526 case 0: 1527 error("%s is an unimplemented extension", p->symbol); 1528 return; 1529 1530 default: 1531 panic("proc case"); 1532 } 1533 } 1534 #endif PC 1535