|
|
BSD 4.3
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
static char rcsid[] = "$Header: /var/lib/cvsd/repos/CSRG/43BSD/contrib/B/src/bed/que2.c,v 1.1.1.1 2018/04/24 16:12:54 root Exp $";
/*
* B editor -- Manipulate queues of nodes, higher levels.
*/
#include <ctype.h>
#include "b.h"
#include "feat.h"
#include "bobj.h"
#include "node.h"
#include "supr.h"
#include "queu.h"
#include "gram.h"
#include "tabl.h"
extern bool lefttorite;
/* Set by edit() to signal we parse purely left-to-right */
extern bool dflag; /* Debug mode even if NDEBUG on */
/*
* Insert a queue of nodes at the focus
* (which had better be some kind of a hole).
* The nodes may also be a text, in which case the individual characters
* are inserted.
* Extensive changes to the parse tree may occur, and the node may be
* broken up in its constituent parts (texts and other nodes) which
* are then inserted individually.
*/
Visible bool
ins_queue(ep, pq, pq2)
register environ *ep;
register queue *pq;
register queue *pq2;
{
register bool ok = Yes;
register node n;
register queue oldq2;
environ saveenv;
int oldindentation = focindent(ep);
int indentation = oldindentation;
leftvhole(ep);
while (ok && !emptyqueue(*pq)) {
n = queuebehead(pq);
if (Type(n) == Tex) {
ok = ins_string(ep, Str((value) n), pq2, 0);
switch (Str((value) n)[Length((value) n) - 1]) { /* Last char */
case '\t':
++indentation;
break;
case '\b':
--indentation;
break;
case '\n':
while (focindent(ep) > indentation) {
if (!ins_newline(ep))
break;
}
break;
}
}
else {
Ecopy(*ep, saveenv);
oldq2 = qcopy(*pq2);
if (!ins_node(&saveenv, n, pq2)) {
Erelease(saveenv);
qrelease(*pq2);
*pq2 = oldq2;
if (symbol(n) == Hole)
ok = ins_string(ep, "?", pq2, 0);
else
splitnode(n, pq);
}
else {
Erelease(*ep);
Emove(saveenv, *ep);
qrelease(oldq2);
}
}
noderelease(n);
}
if (!ok)
qshow(*pq, "ins_queue");
qrelease(*pq);
for (indentation = focindent(ep);
indentation > oldindentation; --indentation)
stringtoqueue("\b", pq2); /* Pass on indentation to outer level */
return ok;
}
/*
* Subroutine to insert a queue to the right of the focus
* without affecting the focus position.
*/
Visible bool
app_queue(ep, pq)
environ *ep;
queue *pq;
{
int where;
static int markbit = 1; /* To properly handle recursive calls */
if (emptyqueue(*pq))
return Yes;
where = focoffset(ep);
markbit <<= 1;
markpath(&ep->focus, markbit);
if (!ins_queue(ep, pq, pq)) {
markbit >>= 1;
return No;
}
firstmarked(&ep->focus, markbit) || Abort();
unmkpath(&ep->focus, markbit);
markbit >>= 1;
ep->spflag = No;
fixfocus(ep, where);
return Yes;
}
/*
* Advance to next thing after current position.
*/
Visible bool
move_on(ep)
register environ *ep;
{
register node n;
register string *rp;
register int sym;
register int ich = ichild(ep->focus);
if (!up(&ep->focus))
return No;
higher(ep);
n = tree(ep->focus);
rp = noderepr(n);
if (Fw_positive(rp[ich])) {
ep->mode = FHOLE;
ep->s1 = 2*ich + 1;
ep->s2 = 0;
if (ep->spflag) {
ep->spflag = No;
if (rp[ich][0] == ' ') {
++ep->s2;
if (fwidth(rp[ich]) > 1)
return Yes;
}
else
return Yes;
}
else
return Yes;
}
if (ich < nchildren(n)) {
s_downi(ep, ich+1);
sym = symbol(tree(ep->focus));
if (sym == Hole || sym == Optional)
ep->mode = WHOLE;
else
ep->mode = ATBEGIN;
return Yes;
}
ep->mode = ATEND;
return Yes;
}
/*
* Like move_on but moves through fixed texts, skipping only spaces
* and empty strings.
* <<<<< This code is a dinosaur and should be revised. >>>>>
*/
Visible bool
fix_move(ep)
register environ *ep;
{
register int ich;
register int i;
register string *rp;
register string cp;
Assert(ep->mode == FHOLE);
ich = ep->s1/2;
rp = noderepr(tree(ep->focus));
cp = rp[ich];
if (cp) {
i = ep->s2;
Assert(i <= Fwidth(cp));
if (cp[i] == ' ') {
do {
++i;
} while (cp[i] == ' ');
}
if (cp[i] == '\b' || cp[i] == '\t') {
++i;
Assert(!cp[i]);
}
else if (cp[i]) {
if (i == ep->s2)
return No;
ep->s2 = i;
return Yes;
}
}
if (ich >= nchildren(tree(ep->focus)))
ep->mode = ATEND;
else {
s_downi(ep, ich+1);
if (symbol(tree(ep->focus)) == Hole
|| symbol(tree(ep->focus)) == Optional)
ep->mode = WHOLE;
else
ep->mode = ATBEGIN;
}
return Yes;
}
/*
* Insert a node in the parse tree.
*/
Hidden bool
ins_node(ep, n, pq)
register environ *ep;
register node n;
register queue *pq;
{
register int sym;
register node nn;
register markbits x;
string *rp;
if (symbol(n) == Optional)
return Yes;
for (;;) {
switch (ep->mode) {
case FHOLE:
if (ep->s2 < lenitem(ep) || !fix_move(ep))
return No;
continue;
case VHOLE:
if (ep->s2 < lenitem(ep) || !move_on(ep))
return No;
continue;
case ATBEGIN:
sym = symbol(tree(ep->focus));
if (sym == Optional || sym == Hole) {
ep->mode = WHOLE;
continue;
}
x = marks(tree(ep->focus));
if (joinnodes(&ep->focus, n, tree(ep->focus), No)) {
if (x) {
s_downi(ep, 2);
markpath(&ep->focus, x);
s_up(ep);
}
s_down(ep);
ep->mode = ATEND;
leftvhole(ep);
return Yes;
}
nn = tree(ep->focus);
rp = noderepr(nn);
if (nchildren(nn) >= 1 && Fw_zero(rp[0])) {
sym = symbol(firstchild(nn));
if (sym == Hole || sym == Optional) {
s_down(ep);
if (fitnode(&ep->focus, n)) {
ep->mode = ATEND;
leftvhole(ep);
return Yes;
}
s_up(ep);
}
}
nn = nodecopy(nn);
if (!fitnode(&ep->focus, n)) {
addtoqueue(pq, nn);
noderelease(nn);
delfocus(&ep->focus);
ep->mode = WHOLE;
continue;
}
if (downrite(&ep->focus)) {
if (Type(tree(ep->focus)) != Tex) {
sym = symbol(tree(ep->focus));
if (sym == Hole || sym == Optional) {
if (fitnode(&ep->focus, nn)) {
noderelease(nn);
nn = Nnil;
}
}
}
else
up(&ep->focus);
}
if (nn) {
addtoqueue(pq, nn);
noderelease(nn);
}
ep->mode = ATEND;
leftvhole(ep);
return Yes;
case WHOLE:
sym = symbol(tree(ep->focus));
Assert(sym == Optional || sym == Hole);
do {
higher(ep); /* Only for second time around */
if (fitnode(&ep->focus, n)) {
ep->mode = ATEND;
leftvhole(ep);
return Yes;
}
} while (resttoqueue(&ep->focus, pq));
ep->mode = ATEND;
/* Fall through */
case ATEND:
do {
higher(ep); /* Only for second time around */
if (joinnodes(&ep->focus, tree(ep->focus), n, ep->spflag)) {
ep->spflag = No;
leftvhole(ep);
return Yes;
}
} while (resttoqueue(&ep->focus, pq)
|| move_on(ep) && ep->mode == ATEND);
return No;
default:
return No;
}
}
}
/*
* Insert a string in the parse tree.
*/
#define NEXT (++str, alt_c = 0)
Visible bool
ins_string(ep, str, pq, alt_c)
register environ *ep;
/*auto*/ string str;
register queue *pq;
int alt_c;
{
register node nn;
auto value v;
char buf[1024];
register string repr;
string oldstr;
register int sym;
register int len;
bool interactive = alt_c != 0;
if (alt_c < 0)
alt_c = 0;
while (*str) {
switch (*str) {
case '\n':
if (!ins_newline(ep))
return No;
/* Fall through */
case '\t':
case '\b':
NEXT;
continue;
}
switch (ep->mode) {
case ATBEGIN:
nn = tree(ep->focus);
if (Type(nn) == Tex) {
ep->s1 = 2*ichild(ep->focus);
ep->s2 = 0;
ep->mode = VHOLE;
s_up(ep);
continue;
}
sym = symbol(nn);
if (sym != Optional && sym != Hole) {
if (fwidth(noderepr(nn)[0]) == 0) {
if (down(&ep->focus))
break;
}
addtoqueue(pq, nn);
delfocus(&ep->focus);
}
ep->mode = WHOLE;
/* Fall through */
case WHOLE:
nn = tree(ep->focus);
sym = symbol(nn);
Assert(sym == Hole || sym == Optional);
while ((len = fitstring(&ep->focus, str, alt_c)) == 0) {
if (sym == Optional) {
if (!move_on(ep)) {
if (*str == ' ')
NEXT;
else
return No;
}
break;
}
if (!interactive && *str == '?') {
NEXT;
ep->mode = ATEND;
break;
}
if (resttoqueue(&ep->focus, pq))
higher(ep);
else if (spacefix(ep))
break;
else if (*str == ' ') {
NEXT;
break;
}
else if (interactive)
return No;
else {
ep->mode = ATEND;
break;
}
}
if (len > 0) {
str += len;
alt_c = 0;
fixfocus(ep, len);
}
break;
case ATEND:
if (add_string(ep, &str, alt_c)) {
alt_c = 0;
break;
}
len = joinstring(&ep->focus, str, ep->spflag,
alt_c ? alt_c : interactive ? -1 : 0, Yes);
if (len > 0) {
s_downi(ep, 2);
ep->spflag = No;
fixfocus(ep, len);
}
else {
if (resttoqueue(&ep->focus, pq)) {
higher(ep);
break;
}
if (move_on(ep))
break;
if (*str == ' ') {
NEXT;
break;
}
return No;
}
str += len;
alt_c = 0;
break;
case FHOLE:
nn = tree(ep->focus);
repr = noderepr(nn)[ep->s1/2];
if (ep->s2 >= fwidth(repr)
&& (ep->s2 <= 0 || ep->spflag || !isalpha(repr[0])
|| repr[ep->s2-1] == ' ')) { /* At end */
if (ep->s1/2 < nchildren(nn)) {
s_downi(ep, ep->s1/2 + 1);
ep->mode = ATBEGIN; /* Of next child */
}
else
ep->mode = ATEND;
break;
}
if ((*str == ':' || *str == ' ') && *str == repr[ep->s2]) {
/*****
* Quick hack for insertion of test-suites and refinements:
*****/
++ep->s2;
NEXT;
continue;
}
if (!lefttorite)
nosuggtoqueue(ep, pq);
oldstr = str;
if (resuggest(ep, &str, alt_c) || soften(ep, &str, alt_c)) {
if (str > oldstr)
alt_c = 0;
continue;
}
if (fix_move(ep))
continue;
return No;
case VHOLE:
Assert(!(ep->s1&1));
nn = tree(ep->focus);
#ifdef USERSUGG
if (symbol(nn) == Suggestion) {
if (newsugg(ep, &str, alt_c))
alt_c = 0;
else
killsugg(ep);
continue;
}
#endif USERSUGG
s_downi(ep, ep->s1/2);
v = copy((value) tree(ep->focus));
len = 0;
if (!ep->spflag) {
for (; len < sizeof buf - 1 && str[len]
&& mayinsert(nn, ep->s1/2, !!(ep->s2 + len),
str[len]);
++len) {
buf[len] = str[len];
}
if (len <= 0 && alt_c
&& mayinsert(nn, ep->s1/2, !!(ep->s2 + len), alt_c)) {
buf[0] = alt_c;
len = 1;
}
}
if (len > 0) { /* Effectuate change */
str += len;
alt_c = 0;
Assert(Type(v) == Tex);
buf[len] = 0;
putintrim(&v, ep->s2, Length(v) - ep->s2, buf);
replace(&ep->focus, (node) v);
s_up(ep);
ep->spflag = No;
ep->s2 += len;
}
else { /* Nothing inserted */
if (ep->s2 == 0) { /* Whole string rejected */
addtoqueue(pq, (node)v);
release(v);
s_up(ep);
delfocus(&ep->focus);
ep->mode = WHOLE;
break;
}
if (ep->s2 < Length(v)) {
addstringtoqueue(pq, Str(v) + ep->s2);
putintrim(&v, ep->s2, 0, "");
replace(&ep->focus, (node) v);
}
else
release(v);
move_on(ep) || Abort(); /* ==> up, cancelling s_downi! */
}
break;
default:
Abort();
}
}
return Yes;
}
/*
* See if two nodes can be joined in a hole.
* 'Spflag' indicates whether a space must be present between the nodes
* (required or forbidden).
* Either of n1, n2 may actually be the current contents of the hole.
*/
Hidden bool
joinnodes(pp, n1, n2, spflag)
path *pp;
node n1;
node n2;
bool spflag;
{
path pa = parent(*pp);
int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
struct table *tp = &table[sympa];
struct classinfo *ci = tp->r_class[ichild(*pp) - 1];
classptr cp = ci->c_join;
int sym1 = symbol(n1);
int sym2 = symbol(n2);
int symcp;
int symfound = -1;
if (!cp)
return No;
for (; *cp; cp += 2) {
if (cp[0] != spflag + 1)
continue;
symcp = cp[1];
tp = &table[symcp];
if (isinclass(sym1, tp->r_class[0])
&& isinclass(sym2, tp->r_class[1])) {
symfound = symcp;
break;
}
}
if (symfound < 0)
return No;
n1 = nodecopy(n1);
n2 = nodecopy(n2); /* 'Cause one of them may overlap tree(*pp) */
replace(pp, table[symfound].r_node);
down(pp) || Abort();
replace(pp, n1);
rite(pp) || Abort();
replace(pp, n2);
up(pp) || Abort();
return Yes;
}
/*
* Try to join a node (implicit as tree(*pp)) with some text.
* That is, try to replace the node by one with it as first child,
* (some of) the text as second child, and nothing or a space in between.
*
* 'Spflag' indicates whether a space is desirable between the nodes
* (but if No it is only used as advice).
*
* Returns the number of characters consumed from str.
*/
Visible int
joinstring(pp, str, spflag, alt_c, mayindent)
path *pp;
register string str;
register bool spflag;
int alt_c;
bool mayindent;
{
register struct table *tp;
path pa = parent(*pp);
node n1;
struct classinfo *ci;
register classptr cp;
int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
register int sym1;
register int symcp;
int symfound;
int len;
char buf[2];
bool interactive = alt_c != 0;
if (alt_c < 0)
alt_c = 0;
ci = table[sympa].r_class[ichild(*pp) - 1];
Assert(ci);
cp = ci->c_join;
if (!cp)
return 0;
n1 = tree(*pp);
sym1 = symbol(n1);
symfound = -1;
for (; *cp; cp += 2) {
if (cp[0] < spflag + 1)
continue;
symcp = cp[1];
tp = &table[symcp];
if (!mayindent && tp->r_repr[1] && index(tp->r_repr[1], '\t'))
continue;
if (isinclass(sym1, tp->r_class[0])
&& ((canfitchar(str[0], tp->r_class[1]))
|| str[0] == '?' && !interactive)) {
if (cp[0] == spflag + 1) {
symfound = symcp;
break;
}
if (symfound < 0)
symfound = symcp;
}
}
if (symfound < 0) { /* 1-level recursion */
if (!alt_c)
return 0;
buf[0] = alt_c;
buf[1] = 0;
return joinstring(pp, buf, spflag, 0, mayindent);
}
n1 = nodecopy(n1); /* 'Cause it overlaps tree(*pp) */
replace(pp, table[symfound].r_node);
down(pp) || Abort();
replace(pp, n1);
rite(pp) || Abort();
len = fitstring(pp, str, 0);
if (len == 0 && str[0] == '?')
len = 1;
Assert(len > 0); /* Disagreement between canfitchar and fitstring */
up(pp) || Abort();
return len;
}
/*
* Similar to joinstring, but now the string must match the delimiter
* rather than being acceptable as second child.
* (Interface has changed to resemble resuggest/soften.)
*/
Hidden bool
add_string(ep, pstr, alt_c)
environ *ep;
string *pstr;
int alt_c; /* Yet unused */
{
register struct table *tp;
path pa = parent(ep->focus);
node n1;
struct classinfo *ci;
register classptr cp;
int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
register int sym1;
register int symcp;
register int c;
ci = table[sympa].r_class[ichild(ep->focus) - 1];
Assert(ci);
cp = ci->c_append;
if (!cp)
return No;
n1 = tree(ep->focus);
sym1 = symbol(n1);
c = **pstr;
for (; *cp; cp += 2) {
if ((*cp&0177) != c)
continue;
symcp = cp[1];
tp = &table[symcp];
if (isinclass(sym1, tp->r_class[0]))
break;
}
if (!*cp)
return No;
++*pstr;
if (c == ' ') {
ep->spflag = Yes;
return Yes;
}
n1 = nodecopy(n1); /* 'Cause it overlaps tree(ep->focus) */
replace(&ep->focus, table[symcp].r_node);
s_down(ep);
replace(&ep->focus, n1);
s_up(ep);
ep->mode = FHOLE;
ep->s1 = 3;
ep->s2 = (*cp&0200) ? 2 : 1;
ep->spflag = No;
return Yes;
}
/*
* See whether a character may start a new node in a hole with given class.
*/
Visible bool
canfitchar(c, ci)
int c;
struct classinfo *ci;
{
register classptr cp;
register int code = Code(c);
Assert(ci);
cp = ci->c_insert;
Assert(cp);
for (; *cp; cp += 2) {
if (cp[0] == code)
return Yes;
}
return No;
}
/*
* Debug routine to print a queue.
*/
Visible Procedure
qshow(q, where)
queue q;
string where;
{
#ifndef NDEBUG
node n;
char buf[256];
string cp;
string sp;
sprintf(buf, "%s:", where);
cp = buf + strlen(buf);
for (;q; q = q->q_link) {
n = q->q_data;
*cp++ = ' ';
if (Type(n) == Tex) {
*cp++ = '"';
for (sp = Str((value) n); *sp; ++sp) {
if (isprint(*sp) || *sp == ' ') {
*cp++ = *sp;
if (*sp == '"')
*cp++ = *sp;
}
else {
sprintf(cp, "\\%03o", *sp&0377);
cp += 4;
}
}
*cp++ = '"';
}
else {
strncpy(cp, table[symbol(n)].r_name, 80);
cp += strlen(cp);
}
if (cp >= buf+80) {
strcpy(buf+76, "...");
break;
}
}
*cp = 0;
debug(buf);
#endif NDEBUG
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.