1: /* 2: * Focal, 1981. 3: * Dedicated to the old times, 4: * when an 8k PDP-8 was considered 5: * a BIG machine. 6: * Driver and command handler. 7: */ 8: 9: /*)BUILD 10: $(PROGRAM) = focal 11: $(INCLUDE) = focal.h proto.h 12: $(FILES) = { focal0.c focal1.c focal2.c focal3.c } 13: $(ATOD) = 1 14: $(DTOA) = 1 15: */ 16: #ifdef DOCUMENTATION 17: 18: title focal Ancient Interpretive Language 19: index Ancient Interpretive Language 20: 21: synopsis 22: 23: focal 24: 25: description 26: 27: Focal is a block-structured interpretive language. 28: It is described in the PDP-8 "Introduction to 29: Programming." There is also a PDP-11 Focal manual. 30: 31: This version of focal runs on Vax/VMS and Unix. 32: 33: author 34: 35: Dave Conroy 36: 37: #endif 38: 39: #include "focal.h" 40: #ifdef vms 41: #include 42: #include 43: #define IO_SUCCESS SS$_NORMAL 44: #define IO_ERROR (STS$M_INHIB_MSG | SS$_ABORT) 45: #else 46: #ifndef IO_SUCCESS 47: #define IO_SUCCESS 0 48: #endif 49: #ifndef IO_ERROR 50: #define IO_ERROR 1 51: #endif 52: #endif 53: 54: char cbuf[128]; /* Command buffer */ 55: char abuf[128]; /* Ask buffer */ 56: char *ctp; /* Current text pointer */ 57: struct line *linev; /* Line list header */ 58: struct line *clp; /* Current line pointer */ 59: int mode; /* Current processing mode */ 60: struct sym *forsp; /* Symbol pointer (for) */ 61: float forlimit; /* Loop limit (for) */ 62: float forstep; /* Loop step (for) */ 63: struct control *controlv; /* Control stack */ 64: jmp_buf env; /* Saved state for errors */ 65: int intflag; /* Interrupt flag */ 66: 67: struct sym *symv[N_HASH + 1]; /* Symbol table */ 68: 69: main() 70: { 71: register int c; 72: 73: #ifdef FUNCTIONS 74: builtin("fsin", fsin); 75: builtin("fcos", fcos); 76: builtin("fexp", fexp); 77: builtin("flog", flog); 78: builtin("fatn", fatn); 79: builtin("fsqt", fsqt); 80: builtin("fabs", fabt); 81: builtin("fsgn", fsgn); 82: builtin("fitr", fitr); 83: builtin("fran", fran); 84: #endif 85: setjmp(env); 86: catchcc(); 87: for (;;) { 88: putchar('*'); 89: if (gets(cbuf) == NULL) { 90: putchar('\n'); 91: break; 92: } 93: mode = C_TOP; 94: clp = NULL; 95: ctp = cbuf; 96: if ((c=getnb()) != 0) { 97: if (isdigit(c)) 98: inject(c); 99: else { 100: --ctp; 101: process(); 102: } 103: } 104: } 105: return EXIT_SUCCESS; 106: } 107: 108: /* 109: * Handcraft the symbol table 110: * entry for a builtin function. Used 111: * at initialization time, to jam 112: * the functions into the table, and 113: * never used again! 114: */ 115: void builtin(cp, fp) 116: const char *cp; 117: double (*fp)(double); 118: { 119: register struct sym *sp = NULL; 120: int ix; 121: 122: sp = (struct sym *)malloc(sizeof(*sp)+strlen(cp)+1); 123: if (sp == NULL) { 124: fprintf(stderr, "No memory for %s\n", cp); 125: exit(IO_ERROR); 126: } 127: ix = hashname(cp); 128: sp->s_fp = symv[ix]; 129: symv[ix] = sp; 130: sp->s_type = S_FUNC; 131: sp->s_subs = 0; 132: sp->s_un.s_fp = fp; 133: strcpy(sp->s_id, cp); 134: } 135: 136: 137: 138: 139: #ifdef DEBUG 140: void dumpsyms() 141: { 142: register struct sym *sp; 143: int i; 144: 145: for (i = 0; i < N_HASH; ++i) { 146: printf("%3d:", i); 147: for (sp = symv[i]; sp != NULL; sp = sp->s_fp) { 148: if (sp->s_type == S_ARRAY) 149: printf(" %s(%d)", sp->s_id, sp->s_subs); 150: else 151: printf(" %s", sp->s_id); 152: } 153: printf(" $\n"); 154: } 155: } 156: #endif 157: 158: 159: /* 160: * This is the line evaluator. It runs 161: * the do/for/return stack, checks for interrupts 162: * and calls the required subfunctions to get 163: * the focal commands executed. 164: */ 165: void process() 166: { 167: float limit, step = 1.0; 168: float value; 169: register int c; 170: register struct sym *sp; 171: register struct line *lp; 172: struct lno lno; 173: struct line *lp1, *lp2, *lp3; 174: int grp = 0; 175: 176: loop: 177: if (intflag) { 178: intflag = 0; 179: diag("^C"); 180: } 181: while ((c=getnb()) == ';') 182: ; 183: if (c == 0) { 184: if (mode == C_FOR) { 185: forsp->s_un.s_value += forstep; 186: if ((forstep>0.0 && forsp->s_un.s_value<=forlimit) 187: || (forstep<0.0 && forsp->s_un.s_value>=forlimit)) { 188: clp = controlv->c_lp; 189: ctp = controlv->c_tp; 190: goto loop; 191: } 192: popfor(); 193: } 194: if (clp != NULL) { 195: grp = clp->l_gno; 196: clp = clp->l_fp; 197: ctp = clp->l_text; 198: } 199: if (clp == NULL) { 200: if (mode == C_TOP) 201: return; 202: popdo(); 203: } else if (mode == C_DLINE 204: || (mode==C_DGRP && grp!=clp->l_gno)) 205: popdo(); 206: goto loop; 207: } 208: while (isalpha(*ctp)) 209: ++ctp; 210: if (isupper(c)) 211: c = tolower(c); 212: switch (c) { 213: 214: case 'a': 215: ask(); 216: break; 217: 218: case 'c': 219: while (*ctp != 0) 220: ++ctp; 221: break; 222: 223: case 'd': 224: getlno(&lno, -1); 225: pushcntl(); 226: if (lno.ln_type==LN_NONE || lno.ln_type==LN_ALL) { 227: if ((clp=linev) == NULL) 228: diag("No program"); 229: ctp = clp->l_text; 230: mode = C_DALL; 231: goto loop; 232: } 233: if (lno.ln_type == LN_GRP) { 234: lp = linev; 235: while (lp!=NULL && lp->l_gnol_fp; 237: if (lp!=NULL && lp->l_gno==lno.ln_gno) { 238: clp = lp; 239: ctp = clp->l_text; 240: mode = C_DGRP; 241: goto loop; 242: } 243: badline(); 244: } 245: lp = linev; 246: while (lp != NULL 247: && (lp->l_gno != lno.ln_gno 248: || lp->l_lno != lno.ln_lno)) 249: lp = lp->l_fp; 250: if (lp != NULL) { 251: clp = lp; 252: ctp = clp->l_text; 253: mode = C_DLINE; 254: goto loop; 255: } 256: badline(); 257: 258: case 'e': 259: getlno(&lno, -1); 260: if (lno.ln_type == LN_NONE) { 261: erasesyms(); 262: break; 263: } 264: lp1 = NULL; 265: lp2 = linev; 266: while (lp2 != NULL) { 267: if (lno.ln_type == LN_ALL 268: || (lp2->l_gno == lno.ln_gno 269: && (lno.ln_type==LN_GRP || lp2->l_lno==lno.ln_lno))) { 270: if (lp2 == clp) 271: diag("Erasing current line"); 272: lp3 = lp2; 273: lp2 = lp2->l_fp; 274: if (lp1 == NULL) 275: linev = lp2; else 276: lp1->l_fp = lp2; 277: free((char *) lp3); 278: } else { 279: lp1 = lp2; 280: lp2 = lp2->l_fp; 281: } 282: } 283: break; 284: 285: case 'f': 286: sp = getsym(); 287: clearfors(sp); 288: if (getnb() != '=') 289: diag("Missing = sign"); 290: sp->s_un.s_value = eval(); 291: if (getnb() != ',') 292: diag("Missing comma"); 293: limit = eval(); 294: if ((c=getnb()) == ';') 295: step = 1.0; 296: else if (c == ',') { 297: step = eval(); 298: if (getnb() != ';') 299: diag("Missing semi"); 300: } else 301: diag("Bad for"); 302: pushcntl(); 303: forsp = sp; 304: forlimit = limit; 305: forstep = step; 306: mode = C_FOR; 307: break; 308: 309: case 'g': 310: getlno(&lno, -1); 311: if (lno.ln_type == LN_NONE) { 312: if ((clp=linev) == NULL) 313: diag("No program"); 314: ctp = clp->l_text; 315: goto loop; 316: } else if (lno.ln_type == LN_LINE) { 317: lp = linev; 318: while (lp != NULL 319: && (lp->l_gno != lno.ln_gno 320: || lp->l_lno != lno.ln_lno)) 321: lp = lp->l_fp; 322: if (lp != NULL) { 323: clp = lp; 324: ctp = clp->l_text; 325: goto loop; 326: } 327: } 328: badline(); 329: 330: case 'i': 331: value = eval(); 332: if (value >= 0.0) { 333: while ((c = *ctp)!=0 && c!=',' && c!=';') 334: ++ctp; 335: if (c != ',') 336: goto loop; 337: ++ctp; 338: if (value != 0.0) { 339: while ((c = *ctp)!=0 && c!=',' && c!=';') 340: ++ctp; 341: if (c != ',') 342: goto loop; 343: ++ctp; 344: } 345: } 346: getlno(&lno, -1); 347: if (lno.ln_type == LN_LINE) { 348: lp = linev; 349: while (lp != NULL 350: && (lp->l_gno != lno.ln_gno 351: || lp->l_lno != lno.ln_lno)) 352: lp = lp->l_fp; 353: if (lp != NULL) { 354: clp = lp; 355: ctp = clp->l_text; 356: goto loop; 357: } 358: } 359: badline(); 360: 361: case 'l': 362: library(); 363: break; 364: 365: case 'q': 366: if (clp == NULL) 367: exit(IO_SUCCESS); 368: return; 369: 370: case 't': 371: type(); 372: break; 373: 374: case 'r': 375: while (mode == C_FOR) 376: popfor(); 377: popdo(); 378: break; 379: 380: case 's': 381: sp = getsym(); 382: if (getnb() != '=') 383: diag("Missing = sign"); 384: sp->s_un.s_value = eval(); 385: break; 386: 387: case 'w': 388: getlno(&lno, -1); 389: save(&lno, stdout); 390: break; 391: 392: #ifdef DEBUG 393: case 'x': 394: dumpsyms(); 395: break; 396: #endif 397: 398: default: 399: diag("Illegal command"); 400: } 401: goto loop; 402: } 403: 404: /* 405: * Process the ask command. 406: * The "ask" has already been read in. 407: * The funny altmode thing, where a variable 408: * is left unchanged, is not implemented. 409: * The input expression must be a legal floating 410: * point number, although no checking is done. 411: */ 412: void ask() 413: { 414: register struct sym *sp; 415: register int c; 416: 417: while ((c=getnb())!=0 && c!=';') { 418: if (c == '"') { 419: while ((c = *ctp++)!=0 && c!='"') 420: putchar(c); 421: if (c != 0) 422: continue; 423: diag("Missing `\"' in ask"); 424: } 425: if (c == ',') 426: continue; 427: --ctp; 428: sp = getsym(); 429: printf(": "); 430: if (gets(abuf) == NULL) { 431: putchar('\n'); 432: diag("EOF in ask"); 433: } 434: sp->s_un.s_value = atof(abuf); 435: } 436: --ctp; 437: } 438: 439: /* 440: * Complain about bad line 441: * numbers. Used all over the place. 442: */ 443: void badline() 444: { 445: diag("Bad line number"); 446: } 447: 448: 449: 450: /* 451: * Effectively use control stack 452: */ 453: static struct control *ccb_free = NULL; 454: 455: 456: /* 457: * Obtain a control stack entry. 458: * Check free list firstly, then call malloc 459: * only when free list is empty. 460: * -- added by Akira Kida 461: */ 462: struct control * 463: newcontrol(void) 464: { 465: struct control *cp; 466: 467: if (ccb_free != NULL) { 468: cp = ccb_free; 469: ccb_free = cp->c_fp; 470: } else if ((cp = (struct control *) 471: malloc(sizeof(struct control))) == NULL) { 472: diag("Out of space (control stack)"); 473: } 474: return cp; 475: } 476: 477: /* 478: * Free control stack that is no longer needed. 479: * Just return one to the free list. 480: */ 481: void 482: freecontrol(struct control *cp) 483: { 484: cp->c_fp = ccb_free; 485: ccb_free = cp; 486: } 487: 488: 489: /* 490: * Push an entry onto the control stack 491: * 492: * There are two formats corresponding to 493: * "for" and "do" statmenets. "for" statiement 494: * format differs from "do" statement format in 495: * that the loop variables are saved. 496: * -- added by Akira Kida 497: */ 498: void pushcntl() 499: { 500: register struct control *cp; 501: 502: cp = newcontrol(); 503: cp->c_fp = controlv; 504: controlv = cp; 505: cp->c_mode = mode; 506: cp->c_tp = ctp; 507: cp->c_lp = clp; 508: if (mode == C_FOR) { 509: cp->c_sp = forsp; 510: cp->c_limit = forlimit; 511: cp->c_step = forstep; 512: } 513: } 514: 515: 516: 517: /* 518: * Pop an entry from the control stack 519: */ 520: void popcntl() 521: { 522: register struct control *cp; 523: 524: cp = controlv; 525: controlv = cp->c_fp; 526: ctp = cp->c_tp; 527: clp = cp->c_lp; 528: mode = cp->c_mode; 529: if (mode == C_FOR) { 530: forsp = cp->c_sp; 531: forlimit = cp->c_limit; 532: forstep = cp->c_step; 533: } 534: freecontrol(cp); 535: } 536: 537: 538: /* 539: * Pop a "do" format entry from 540: * the control stack, restoring all of the 541: * global variables. 542: */ 543: void popdo() 544: { 545: if (controlv == NULL) 546: diag("Return not in do"); 547: popcntl(); 548: } 549: 550: 551: /* 552: * Pop a "for" format item from the 553: * control stack, restoring all the global 554: * variables. 555: */ 556: void popfor() 557: { 558: if (controlv == NULL) 559: diag("For stack botch"); 560: popcntl(); 561: } 562: 563: 564: 565: /* 566: * Dig in the control stack, 567: * looking for "for" stack entries that 568: * are controlling the variable whose symbol 569: * table entry is pointed to by `sp'. Rip 570: * them out. This makes everything work out 571: * if you "go" out of a loop and then "for" 572: * on the same variable. 573: */ 574: void clearfors(sp) 575: register struct sym *sp; 576: { 577: register struct control *cp1; 578: register struct control *cp2; 579: 580: if (mode==C_FOR && forsp==sp) 581: popfor(); 582: else { 583: cp1 = NULL; 584: cp2 = controlv; 585: while (cp2 != NULL) { 586: if (cp2->c_mode==C_FOR && cp2->c_sp==sp) { 587: if (cp1 == NULL) 588: controlv = cp2->c_fp; 589: else 590: cp1->c_fp = cp2->c_fp; 591: freecontrol(cp2); 592: break; 593: } 594: cp1 = cp2; 595: cp2 = cp2->c_fp; 596: } 597: } 598: } 599: 600: /* 601: * Inject a line of text, stored in 602: * the normal command line buffer, into the 603: * saved indirect program. The argument `c' 604: * is the first character of the line 605: * number, which is assumed to be valid. 606: */ 607: void inject(c) 608: register int c; 609: { 610: register struct line *lp1; 611: register struct line *lp2; 612: register struct line *lp3; 613: struct lno lno; 614: 615: getlno(&lno, c); 616: if (lno.ln_type != LN_LINE) 617: diag("Illegal line number"); 618: lp1 = NULL; 619: lp2 = linev; 620: while (lp2 != NULL 621: && (lp2->l_gno < lno.ln_gno 622: || (lp2->l_gno==lno.ln_gno&&lp2->l_lno<=lno.ln_lno))) { 623: if (lp2->l_gno == lno.ln_gno 624: && lp2->l_lno == lno.ln_lno) { 625: lp3 = lp2; 626: lp2 = lp2->l_fp; 627: if (lp1 == NULL) 628: linev = lp2; 629: else 630: lp1->l_fp = lp2; 631: free((char *) lp3); 632: break; 633: } 634: lp1 = lp2; 635: lp2 = lp2->l_fp; 636: } 637: if ((c=getnb()) != 0) { 638: lp3 = alocline(--ctp); 639: lp3->l_fp = lp2; 640: lp3->l_gno = lno.ln_gno; 641: lp3->l_lno = lno.ln_lno; 642: strcpy(lp3->l_text, ctp); 643: if (lp1 == NULL) 644: linev = lp3; 645: else 646: lp1->l_fp = lp3; 647: } 648: } 649: 650: 651: int getline(char *cp, FILE *fp) 652: { 653: register int c; 654: 655: while ((c=getc(fp))!=EOF && c!='\n') 656: *cp++ = c; 657: if (c == EOF) 658: return (0); 659: *cp = 0; 660: return (1); 661: } 662: 663: 664: void type() 665: { 666: register char *fmt; 667: register int c; 668: static char fmtb[20]; 669: static int ifmtb = 1; 670: int x, y; 671: 672: if (ifmtb) { 673: strcpy(fmtb, "%9.4f"); 674: ifmtb--; 675: } 676: fmt = fmtb; 677: while ((c=getnb())!=0 && c!=';') { 678: if (c == '%') { 679: if ((c=getnb())==0 || c==';' || c==',') { 680: strcpy(fmtb, "%6e"); 681: fmt = fmtb; 682: --ctp; 683: continue; 684: } 685: x = getnum(c); 686: if (getnb() != '.') 687: diag("Missing . in format"); 688: y = getnum(getnb()); 689: sprintf(fmtb, "%%%d.%df", x, y); 690: fmt = fmtb; 691: continue; 692: } 693: if (c == ',') 694: continue; 695: if (c == '!') { 696: putchar('\n'); 697: continue; 698: } 699: if (c == '#') { 700: putchar('\r'); 701: continue; 702: } 703: if (c == '"') { 704: while ((c = *ctp++)!='\0' && c!='"') 705: putchar(c); 706: if (c == '\0') { 707: diag("Missing `\"' in type"); 708: break; 709: } 710: continue; 711: } 712: --ctp; 713: printf(fmt, eval()); 714: } 715: --ctp; 716: } 717: 718: void save(lnop, fp) 719: register struct lno *lnop; 720: FILE *fp; 721: { 722: struct lno lno; 723: register struct line *lp; 724: register int tgroup, lgroup; 725: 726: if (lnop == NULL) { 727: lno.ln_type = LN_ALL; 728: lnop = &lno; 729: } 730: lp = linev; 731: if (lnop->ln_type!=LN_NONE && lnop->ln_type!=LN_ALL) { 732: while (lp!=NULL && lp->l_gnoln_gno) 733: lp = lp->l_fp; 734: if (lp==NULL || lp->l_gno!=lnop->ln_gno) 735: diag("Line not found"); 736: if (lnop->ln_type == LN_LINE) { 737: while (lp!=NULL && lp->l_lno!=lnop->ln_lno) 738: lp = lp->l_fp; 739: if (lp == NULL) 740: diag("Line not found"); 741: } 742: } 743: while (lp != NULL) { 744: putline(lp, fp); 745: if (lnop->ln_type == LN_LINE) 746: break; 747: lgroup = lp->l_gno; 748: if ((lp = lp->l_fp) != NULL) { 749: tgroup = lp->l_gno; 750: if (lnop->ln_type==LN_GRP && tgroup!=lnop->ln_gno) 751: break; 752: if (tgroup != lgroup) 753: putc('\n', fp); 754: } 755: } 756: } 757: 758: void erasesyms() 759: { 760: register struct sym *sp1, *sp2; 761: int i; 762: 763: for (i = 0; i < N_HASH; ++i) { 764: sp1 = symv[i]; 765: symv[i] = NULL; 766: while (sp1 != NULL) { 767: sp2 = sp1->s_fp; 768: free((char *) sp1); 769: sp1 = sp2; 770: } 771: } 772: }