1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: que2.c,v 2.3 84/07/23 13:02:38 guido Exp $"; 3: 4: /* 5: * B editor -- Manipulate queues of nodes, higher levels. 6: */ 7: 8: #include <ctype.h> 9: 10: #include "b.h" 11: #include "feat.h" 12: #include "bobj.h" 13: #include "node.h" 14: #include "supr.h" 15: #include "queu.h" 16: #include "gram.h" 17: #include "tabl.h" 18: 19: 20: extern bool lefttorite; 21: /* Set by edit() to signal we parse purely left-to-right */ 22: extern bool dflag; /* Debug mode even if NDEBUG on */ 23: 24: 25: /* 26: * Insert a queue of nodes at the focus 27: * (which had better be some kind of a hole). 28: * The nodes may also be a text, in which case the individual characters 29: * are inserted. 30: * Extensive changes to the parse tree may occur, and the node may be 31: * broken up in its constituent parts (texts and other nodes) which 32: * are then inserted individually. 33: */ 34: 35: Visible bool 36: ins_queue(ep, pq, pq2) 37: register environ *ep; 38: register queue *pq; 39: register queue *pq2; 40: { 41: register bool ok = Yes; 42: register node n; 43: register queue oldq2; 44: environ saveenv; 45: int oldindentation = focindent(ep); 46: int indentation = oldindentation; 47: 48: leftvhole(ep); 49: while (ok && !emptyqueue(*pq)) { 50: n = queuebehead(pq); 51: if (Type(n) == Tex) { 52: ok = ins_string(ep, Str((value) n), pq2, 0); 53: switch (Str((value) n)[Length((value) n) - 1]) { /* Last char */ 54: case '\t': 55: ++indentation; 56: break; 57: case '\b': 58: --indentation; 59: break; 60: case '\n': 61: while (focindent(ep) > indentation) { 62: if (!ins_newline(ep)) 63: break; 64: } 65: break; 66: } 67: } 68: else { 69: Ecopy(*ep, saveenv); 70: oldq2 = qcopy(*pq2); 71: if (!ins_node(&saveenv, n, pq2)) { 72: Erelease(saveenv); 73: qrelease(*pq2); 74: *pq2 = oldq2; 75: if (symbol(n) == Hole) 76: ok = ins_string(ep, "?", pq2, 0); 77: else 78: splitnode(n, pq); 79: } 80: else { 81: Erelease(*ep); 82: Emove(saveenv, *ep); 83: qrelease(oldq2); 84: } 85: } 86: noderelease(n); 87: } 88: if (!ok) 89: qshow(*pq, "ins_queue"); 90: qrelease(*pq); 91: for (indentation = focindent(ep); 92: indentation > oldindentation; --indentation) 93: stringtoqueue("\b", pq2); /* Pass on indentation to outer level */ 94: return ok; 95: } 96: 97: 98: /* 99: * Subroutine to insert a queue to the right of the focus 100: * without affecting the focus position. 101: */ 102: 103: Visible bool 104: app_queue(ep, pq) 105: environ *ep; 106: queue *pq; 107: { 108: int where; 109: static int markbit = 1; /* To properly handle recursive calls */ 110: 111: if (emptyqueue(*pq)) 112: return Yes; 113: where = focoffset(ep); 114: markbit <<= 1; 115: markpath(&ep->focus, markbit); 116: if (!ins_queue(ep, pq, pq)) { 117: markbit >>= 1; 118: return No; 119: } 120: firstmarked(&ep->focus, markbit) || Abort(); 121: unmkpath(&ep->focus, markbit); 122: markbit >>= 1; 123: ep->spflag = No; 124: fixfocus(ep, where); 125: return Yes; 126: } 127: 128: 129: /* 130: * Advance to next thing after current position. 131: */ 132: 133: Visible bool 134: move_on(ep) 135: register environ *ep; 136: { 137: register node n; 138: register string *rp; 139: register int sym; 140: register int ich = ichild(ep->focus); 141: 142: if (!up(&ep->focus)) 143: return No; 144: higher(ep); 145: n = tree(ep->focus); 146: rp = noderepr(n); 147: if (Fw_positive(rp[ich])) { 148: ep->mode = FHOLE; 149: ep->s1 = 2*ich + 1; 150: ep->s2 = 0; 151: if (ep->spflag) { 152: ep->spflag = No; 153: if (rp[ich][0] == ' ') { 154: ++ep->s2; 155: if (fwidth(rp[ich]) > 1) 156: return Yes; 157: } 158: else 159: return Yes; 160: } 161: else 162: return Yes; 163: } 164: if (ich < nchildren(n)) { 165: s_downi(ep, ich+1); 166: sym = symbol(tree(ep->focus)); 167: if (sym == Hole || sym == Optional) 168: ep->mode = WHOLE; 169: else 170: ep->mode = ATBEGIN; 171: return Yes; 172: } 173: ep->mode = ATEND; 174: return Yes; 175: } 176: 177: 178: /* 179: * Like move_on but moves through fixed texts, skipping only spaces 180: * and empty strings. 181: * <<<<< This code is a dinosaur and should be revised. >>>>> 182: */ 183: 184: Visible bool 185: fix_move(ep) 186: register environ *ep; 187: { 188: register int ich; 189: register int i; 190: register string *rp; 191: register string cp; 192: 193: Assert(ep->mode == FHOLE); 194: 195: ich = ep->s1/2; 196: rp = noderepr(tree(ep->focus)); 197: cp = rp[ich]; 198: if (cp) { 199: i = ep->s2; 200: Assert(i <= Fwidth(cp)); 201: if (cp[i] == ' ') { 202: do { 203: ++i; 204: } while (cp[i] == ' '); 205: } 206: if (cp[i] == '\b' || cp[i] == '\t') { 207: ++i; 208: Assert(!cp[i]); 209: } 210: else if (cp[i]) { 211: if (i == ep->s2) 212: return No; 213: ep->s2 = i; 214: return Yes; 215: } 216: } 217: 218: if (ich >= nchildren(tree(ep->focus))) 219: ep->mode = ATEND; 220: else { 221: s_downi(ep, ich+1); 222: if (symbol(tree(ep->focus)) == Hole 223: || symbol(tree(ep->focus)) == Optional) 224: ep->mode = WHOLE; 225: else 226: ep->mode = ATBEGIN; 227: } 228: return Yes; 229: } 230: 231: 232: /* 233: * Insert a node in the parse tree. 234: */ 235: 236: Hidden bool 237: ins_node(ep, n, pq) 238: register environ *ep; 239: register node n; 240: register queue *pq; 241: { 242: register int sym; 243: register node nn; 244: register markbits x; 245: string *rp; 246: 247: if (symbol(n) == Optional) 248: return Yes; 249: 250: for (;;) { 251: switch (ep->mode) { 252: 253: case FHOLE: 254: if (ep->s2 < lenitem(ep) || !fix_move(ep)) 255: return No; 256: continue; 257: 258: case VHOLE: 259: if (ep->s2 < lenitem(ep) || !move_on(ep)) 260: return No; 261: continue; 262: 263: case ATBEGIN: 264: sym = symbol(tree(ep->focus)); 265: if (sym == Optional || sym == Hole) { 266: ep->mode = WHOLE; 267: continue; 268: } 269: x = marks(tree(ep->focus)); 270: if (joinnodes(&ep->focus, n, tree(ep->focus), No)) { 271: if (x) { 272: s_downi(ep, 2); 273: markpath(&ep->focus, x); 274: s_up(ep); 275: } 276: s_down(ep); 277: ep->mode = ATEND; 278: leftvhole(ep); 279: return Yes; 280: } 281: nn = tree(ep->focus); 282: rp = noderepr(nn); 283: if (nchildren(nn) >= 1 && Fw_zero(rp[0])) { 284: sym = symbol(firstchild(nn)); 285: if (sym == Hole || sym == Optional) { 286: s_down(ep); 287: if (fitnode(&ep->focus, n)) { 288: ep->mode = ATEND; 289: leftvhole(ep); 290: return Yes; 291: } 292: s_up(ep); 293: } 294: } 295: nn = nodecopy(nn); 296: if (!fitnode(&ep->focus, n)) { 297: addtoqueue(pq, nn); 298: noderelease(nn); 299: delfocus(&ep->focus); 300: ep->mode = WHOLE; 301: continue; 302: } 303: if (downrite(&ep->focus)) { 304: if (Type(tree(ep->focus)) != Tex) { 305: sym = symbol(tree(ep->focus)); 306: if (sym == Hole || sym == Optional) { 307: if (fitnode(&ep->focus, nn)) { 308: noderelease(nn); 309: nn = Nnil; 310: } 311: } 312: } 313: else 314: up(&ep->focus); 315: } 316: if (nn) { 317: addtoqueue(pq, nn); 318: noderelease(nn); 319: } 320: ep->mode = ATEND; 321: leftvhole(ep); 322: return Yes; 323: 324: case WHOLE: 325: sym = symbol(tree(ep->focus)); 326: Assert(sym == Optional || sym == Hole); 327: do { 328: higher(ep); /* Only for second time around */ 329: if (fitnode(&ep->focus, n)) { 330: ep->mode = ATEND; 331: leftvhole(ep); 332: return Yes; 333: } 334: } while (resttoqueue(&ep->focus, pq)); 335: ep->mode = ATEND; 336: /* Fall through */ 337: case ATEND: 338: do { 339: higher(ep); /* Only for second time around */ 340: if (joinnodes(&ep->focus, tree(ep->focus), n, ep->spflag)) { 341: ep->spflag = No; 342: leftvhole(ep); 343: return Yes; 344: } 345: } while (resttoqueue(&ep->focus, pq) 346: || move_on(ep) && ep->mode == ATEND); 347: return No; 348: 349: default: 350: return No; 351: 352: } 353: } 354: } 355: 356: 357: /* 358: * Insert a string in the parse tree. 359: */ 360: 361: #define NEXT (++str, alt_c = 0) 362: 363: Visible bool 364: ins_string(ep, str, pq, alt_c) 365: register environ *ep; 366: /*auto*/ string str; 367: register queue *pq; 368: int alt_c; 369: { 370: register node nn; 371: auto value v; 372: char buf[1024]; 373: register string repr; 374: string oldstr; 375: register int sym; 376: register int len; 377: bool interactive = alt_c != 0; 378: 379: if (alt_c < 0) 380: alt_c = 0; 381: while (*str) { 382: switch (*str) { 383: 384: case '\n': 385: if (!ins_newline(ep)) 386: return No; 387: /* Fall through */ 388: case '\t': 389: case '\b': 390: NEXT; 391: continue; 392: 393: } 394: switch (ep->mode) { 395: 396: case ATBEGIN: 397: nn = tree(ep->focus); 398: if (Type(nn) == Tex) { 399: ep->s1 = 2*ichild(ep->focus); 400: ep->s2 = 0; 401: ep->mode = VHOLE; 402: s_up(ep); 403: continue; 404: } 405: sym = symbol(nn); 406: if (sym != Optional && sym != Hole) { 407: if (fwidth(noderepr(nn)[0]) == 0) { 408: if (down(&ep->focus)) 409: break; 410: } 411: addtoqueue(pq, nn); 412: delfocus(&ep->focus); 413: } 414: ep->mode = WHOLE; 415: /* Fall through */ 416: case WHOLE: 417: nn = tree(ep->focus); 418: sym = symbol(nn); 419: Assert(sym == Hole || sym == Optional); 420: while ((len = fitstring(&ep->focus, str, alt_c)) == 0) { 421: if (sym == Optional) { 422: if (!move_on(ep)) { 423: if (*str == ' ') 424: NEXT; 425: else 426: return No; 427: } 428: break; 429: } 430: if (!interactive && *str == '?') { 431: NEXT; 432: ep->mode = ATEND; 433: break; 434: } 435: if (resttoqueue(&ep->focus, pq)) 436: higher(ep); 437: else if (spacefix(ep)) 438: break; 439: else if (*str == ' ') { 440: NEXT; 441: break; 442: } 443: else if (interactive) 444: return No; 445: else { 446: ep->mode = ATEND; 447: break; 448: } 449: } 450: if (len > 0) { 451: str += len; 452: alt_c = 0; 453: fixfocus(ep, len); 454: } 455: break; 456: 457: case ATEND: 458: if (add_string(ep, &str, alt_c)) { 459: alt_c = 0; 460: break; 461: } 462: len = joinstring(&ep->focus, str, ep->spflag, 463: alt_c ? alt_c : interactive ? -1 : 0, Yes); 464: if (len > 0) { 465: s_downi(ep, 2); 466: ep->spflag = No; 467: fixfocus(ep, len); 468: } 469: else { 470: if (resttoqueue(&ep->focus, pq)) { 471: higher(ep); 472: break; 473: } 474: if (move_on(ep)) 475: break; 476: if (*str == ' ') { 477: NEXT; 478: break; 479: } 480: return No; 481: } 482: str += len; 483: alt_c = 0; 484: break; 485: 486: case FHOLE: 487: nn = tree(ep->focus); 488: repr = noderepr(nn)[ep->s1/2]; 489: if (ep->s2 >= fwidth(repr) 490: && (ep->s2 <= 0 || ep->spflag || !isalpha(repr[0]) 491: || repr[ep->s2-1] == ' ')) { /* At end */ 492: if (ep->s1/2 < nchildren(nn)) { 493: s_downi(ep, ep->s1/2 + 1); 494: ep->mode = ATBEGIN; /* Of next child */ 495: } 496: else 497: ep->mode = ATEND; 498: break; 499: } 500: if ((*str == ':' || *str == ' ') && *str == repr[ep->s2]) { 501: /***** 502: * Quick hack for insertion of test-suites and refinements: 503: *****/ 504: ++ep->s2; 505: NEXT; 506: continue; 507: } 508: if (!lefttorite) 509: nosuggtoqueue(ep, pq); 510: oldstr = str; 511: if (resuggest(ep, &str, alt_c) || soften(ep, &str, alt_c)) { 512: if (str > oldstr) 513: alt_c = 0; 514: continue; 515: } 516: if (fix_move(ep)) 517: continue; 518: return No; 519: 520: case VHOLE: 521: Assert(!(ep->s1&1)); 522: nn = tree(ep->focus); 523: #ifdef USERSUGG 524: if (symbol(nn) == Suggestion) { 525: if (newsugg(ep, &str, alt_c)) 526: alt_c = 0; 527: else 528: killsugg(ep); 529: continue; 530: } 531: #endif USERSUGG 532: s_downi(ep, ep->s1/2); 533: v = copy((value) tree(ep->focus)); 534: len = 0; 535: if (!ep->spflag) { 536: for (; len < sizeof buf - 1 && str[len] 537: && mayinsert(nn, ep->s1/2, !!(ep->s2 + len), 538: str[len]); 539: ++len) { 540: buf[len] = str[len]; 541: } 542: if (len <= 0 && alt_c 543: && mayinsert(nn, ep->s1/2, !!(ep->s2 + len), alt_c)) { 544: buf[0] = alt_c; 545: len = 1; 546: } 547: } 548: if (len > 0) { /* Effectuate change */ 549: str += len; 550: alt_c = 0; 551: Assert(Type(v) == Tex); 552: buf[len] = 0; 553: putintrim(&v, ep->s2, Length(v) - ep->s2, buf); 554: replace(&ep->focus, (node) v); 555: s_up(ep); 556: ep->spflag = No; 557: ep->s2 += len; 558: } 559: else { /* Nothing inserted */ 560: if (ep->s2 == 0) { /* Whole string rejected */ 561: addtoqueue(pq, (node)v); 562: release(v); 563: s_up(ep); 564: delfocus(&ep->focus); 565: ep->mode = WHOLE; 566: break; 567: } 568: if (ep->s2 < Length(v)) { 569: addstringtoqueue(pq, Str(v) + ep->s2); 570: putintrim(&v, ep->s2, 0, ""); 571: replace(&ep->focus, (node) v); 572: } 573: else 574: release(v); 575: move_on(ep) || Abort(); /* ==> up, cancelling s_downi! */ 576: } 577: break; 578: 579: default: 580: Abort(); 581: 582: } 583: } 584: 585: return Yes; 586: } 587: 588: 589: /* 590: * See if two nodes can be joined in a hole. 591: * 'Spflag' indicates whether a space must be present between the nodes 592: * (required or forbidden). 593: * Either of n1, n2 may actually be the current contents of the hole. 594: */ 595: 596: Hidden bool 597: joinnodes(pp, n1, n2, spflag) 598: path *pp; 599: node n1; 600: node n2; 601: bool spflag; 602: { 603: path pa = parent(*pp); 604: int sympa = pa ? symbol(tree(pa)) : Rootsymbol; 605: struct table *tp = &table[sympa]; 606: struct classinfo *ci = tp->r_class[ichild(*pp) - 1]; 607: classptr cp = ci->c_join; 608: int sym1 = symbol(n1); 609: int sym2 = symbol(n2); 610: int symcp; 611: int symfound = -1; 612: 613: if (!cp) 614: return No; 615: for (; *cp; cp += 2) { 616: if (cp[0] != spflag + 1) 617: continue; 618: symcp = cp[1]; 619: tp = &table[symcp]; 620: if (isinclass(sym1, tp->r_class[0]) 621: && isinclass(sym2, tp->r_class[1])) { 622: symfound = symcp; 623: break; 624: } 625: } 626: 627: if (symfound < 0) 628: return No; 629: n1 = nodecopy(n1); 630: n2 = nodecopy(n2); /* 'Cause one of them may overlap tree(*pp) */ 631: replace(pp, table[symfound].r_node); 632: down(pp) || Abort(); 633: replace(pp, n1); 634: rite(pp) || Abort(); 635: replace(pp, n2); 636: up(pp) || Abort(); 637: return Yes; 638: } 639: 640: 641: /* 642: * Try to join a node (implicit as tree(*pp)) with some text. 643: * That is, try to replace the node by one with it as first child, 644: * (some of) the text as second child, and nothing or a space in between. 645: * 646: * 'Spflag' indicates whether a space is desirable between the nodes 647: * (but if No it is only used as advice). 648: * 649: * Returns the number of characters consumed from str. 650: */ 651: 652: Visible int 653: joinstring(pp, str, spflag, alt_c, mayindent) 654: path *pp; 655: register string str; 656: register bool spflag; 657: int alt_c; 658: bool mayindent; 659: { 660: register struct table *tp; 661: path pa = parent(*pp); 662: node n1; 663: struct classinfo *ci; 664: register classptr cp; 665: int sympa = pa ? symbol(tree(pa)) : Rootsymbol; 666: register int sym1; 667: register int symcp; 668: int symfound; 669: int len; 670: char buf[2]; 671: bool interactive = alt_c != 0; 672: 673: if (alt_c < 0) 674: alt_c = 0; 675: ci = table[sympa].r_class[ichild(*pp) - 1]; 676: Assert(ci); 677: cp = ci->c_join; 678: if (!cp) 679: return 0; 680: 681: n1 = tree(*pp); 682: sym1 = symbol(n1); 683: symfound = -1; 684: for (; *cp; cp += 2) { 685: if (cp[0] < spflag + 1) 686: continue; 687: symcp = cp[1]; 688: tp = &table[symcp]; 689: if (!mayindent && tp->r_repr[1] && index(tp->r_repr[1], '\t')) 690: continue; 691: if (isinclass(sym1, tp->r_class[0]) 692: && ((canfitchar(str[0], tp->r_class[1])) 693: || str[0] == '?' && !interactive)) { 694: if (cp[0] == spflag + 1) { 695: symfound = symcp; 696: break; 697: } 698: if (symfound < 0) 699: symfound = symcp; 700: } 701: } 702: 703: if (symfound < 0) { /* 1-level recursion */ 704: if (!alt_c) 705: return 0; 706: buf[0] = alt_c; 707: buf[1] = 0; 708: return joinstring(pp, buf, spflag, 0, mayindent); 709: } 710: n1 = nodecopy(n1); /* 'Cause it overlaps tree(*pp) */ 711: replace(pp, table[symfound].r_node); 712: down(pp) || Abort(); 713: replace(pp, n1); 714: rite(pp) || Abort(); 715: len = fitstring(pp, str, 0); 716: if (len == 0 && str[0] == '?') 717: len = 1; 718: Assert(len > 0); /* Disagreement between canfitchar and fitstring */ 719: up(pp) || Abort(); 720: return len; 721: } 722: 723: 724: /* 725: * Similar to joinstring, but now the string must match the delimiter 726: * rather than being acceptable as second child. 727: * (Interface has changed to resemble resuggest/soften.) 728: */ 729: 730: Hidden bool 731: add_string(ep, pstr, alt_c) 732: environ *ep; 733: string *pstr; 734: int alt_c; /* Yet unused */ 735: { 736: register struct table *tp; 737: path pa = parent(ep->focus); 738: node n1; 739: struct classinfo *ci; 740: register classptr cp; 741: int sympa = pa ? symbol(tree(pa)) : Rootsymbol; 742: register int sym1; 743: register int symcp; 744: register int c; 745: 746: ci = table[sympa].r_class[ichild(ep->focus) - 1]; 747: Assert(ci); 748: cp = ci->c_append; 749: if (!cp) 750: return No; 751: n1 = tree(ep->focus); 752: sym1 = symbol(n1); 753: c = **pstr; 754: for (; *cp; cp += 2) { 755: if ((*cp&0177) != c) 756: continue; 757: symcp = cp[1]; 758: tp = &table[symcp]; 759: if (isinclass(sym1, tp->r_class[0])) 760: break; 761: } 762: if (!*cp) 763: return No; 764: ++*pstr; 765: if (c == ' ') { 766: ep->spflag = Yes; 767: return Yes; 768: } 769: n1 = nodecopy(n1); /* 'Cause it overlaps tree(ep->focus) */ 770: replace(&ep->focus, table[symcp].r_node); 771: s_down(ep); 772: replace(&ep->focus, n1); 773: s_up(ep); 774: ep->mode = FHOLE; 775: ep->s1 = 3; 776: ep->s2 = (*cp&0200) ? 2 : 1; 777: ep->spflag = No; 778: return Yes; 779: } 780: 781: 782: /* 783: * See whether a character may start a new node in a hole with given class. 784: */ 785: 786: Visible bool 787: canfitchar(c, ci) 788: int c; 789: struct classinfo *ci; 790: { 791: register classptr cp; 792: register int code = Code(c); 793: 794: Assert(ci); 795: cp = ci->c_insert; 796: Assert(cp); 797: for (; *cp; cp += 2) { 798: if (cp[0] == code) 799: return Yes; 800: } 801: return No; 802: } 803: 804: 805: /* 806: * Debug routine to print a queue. 807: */ 808: 809: Visible Procedure 810: qshow(q, where) 811: queue q; 812: string where; 813: { 814: #ifndef NDEBUG 815: node n; 816: char buf[256]; 817: string cp; 818: string sp; 819: 820: sprintf(buf, "%s:", where); 821: cp = buf + strlen(buf); 822: for (;q; q = q->q_link) { 823: n = q->q_data; 824: *cp++ = ' '; 825: if (Type(n) == Tex) { 826: *cp++ = '"'; 827: for (sp = Str((value) n); *sp; ++sp) { 828: if (isprint(*sp) || *sp == ' ') { 829: *cp++ = *sp; 830: if (*sp == '"') 831: *cp++ = *sp; 832: } 833: else { 834: sprintf(cp, "\\%03o", *sp&0377); 835: cp += 4; 836: } 837: } 838: *cp++ = '"'; 839: } 840: else { 841: strncpy(cp, table[symbol(n)].r_name, 80); 842: cp += strlen(cp); 843: } 844: if (cp >= buf+80) { 845: strcpy(buf+76, "..."); 846: break; 847: } 848: } 849: *cp = 0; 850: debug(buf); 851: #endif NDEBUG 852: }