/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* * $Header: b1tex.c,v 1.4 85/08/22 16:52:36 timo Exp $ */ /* B texts */ #include "b.h" #include "b1obj.h" #ifndef INTEGRATION #include "b0con.h" #include "b1mem.h" #include "b1btr.h" #include "b1val.h" #endif #include "b1tlt.h" #include "b3err.h" #ifndef INTEGRATION /* * Operations on texts represented as B-trees. * * Comments: * - The functions with 'i' prepended (ibehead, etc.) do no argument * checking at all. They actually implement the planned behaviour * of | and @, where out-of-bounds numerical values are truncated * rather than causing errors ("abc"|100 = "abc"@-100 = "abc"). * - The 'size' field of all texts must fit in a C int. If the result of * ^ or ^^ would exceed Maxint in size, a user error is signalled. If * the size of the *input* value(s) of any operation is Bigsize, a syserr * is signalled. * - Argument checking: trims, concat and repeat must check their arguments * for user errors. * - t^^n is implemented with an algorithm similar to the 'square and * multiply' algorithm for x**n, using the binary representation of n, * but it uses straightforward 'concat' operations. A more efficient * scheme is possible [see IW219], but small code seems more important. * - Degenerated cases (e.g. t@1, t|0, t^'' or t^^n) are not optimized, * but produce the desired result by virtue of the algorithms used. * The extra checking does not seem worth the overhead for the * non-degenerate cases. * - The code for PUT v IN t@h|l is still there, but it is not compiled, * as the interpreter implements the same strategy directly. * - 'trim()' is only used by f_uname in "b3fil.c". * - Code for outputting texts has been added. This is called from wri() * to output a text, and has running time O(n), compared to O(n log n) * for the old code in wri(). * * *** WARNING *** * - The 'zip' routine and its subroutine 'copynptrs' assume that items and * pointers are stored contiguously, so that &Ptr(p, i+1) == &Ptr(p, i)+1 * and &[IB]char(p, i+1) == &[IB]char(p, i)+1. For pointers, the order * might be reversed in the future; then change the macro Incr(pp, n) below * to *decrement* the pointer! * - Mkbtext and bstrval make the same assumption about items (using strncpy * to move charaters to/from a bottom node). */ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #define IsInner(p) (Flag(p) == Inner) #define IsBottom(p) (Flag(p) == Bottom) #define Incr(pp, n) ((pp) += (n)) /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* make a B text out of a C char */ Visible value mkchar(c) char c; { char buf[2]; buf[0] = c; buf[1] = '\0'; return mk_text(buf); } Visible char charval(v) value v; { if (!Character(v)) syserr(MESS(1600, "charval on non-char")); return Bchar(Root(v), 0); } Visible bool character(v) value v; { return Character(v); } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Hidden btreeptr mkbtext(s, len) string s; int len; { btreeptr p; int chunk, i, n, nbig; /* * Determine level of tree. * This is done for each inner node anew, to avoid having * to keep an explicit stack. * Problem is: make sure that for each node at the same * level, the computation indeed finds the same level! * (Don't care about efficiency here; in practice the trees * built by mk_text rarely need more than two levels.) */ chunk = 0; i = Maxbottom; /* Next larger chunk size */ while (len > i) { chunk = i; i = (i+1) * Maxinner + Maxinner; } n = len / (chunk+1); /* Number of items at this level; n+1 subtrees */ chunk = len / (n+1); /* Use minimal chunk size for subtrees */ p = grabbtreenode(chunk ? Inner : Bottom, Ct); Size(p) = len; Lim(p) = n; if (!chunk) strncpy(&Bchar(p, 0), s, len); else { nbig = len+1 - (n+1)*chunk; /* There will be 'nbig' nodes of size 'chunk'. */ /* The remaining 'n-nbig' will have size 'chunk-1'. */ for (i = 0; i < n; ++i) { Ptr(p, i) = mkbtext(s, chunk); s += chunk; Ichar(p, i) = *s++; len -= chunk+1; if (--nbig == 0) --chunk; /* This was the last 'big' node */ } Ptr(p, i) = mkbtext(s, len); } return p; } Visible value mk_text(s) string s; { value v; int len = strlen(s); v = grab_tlt(Tex, Ct); if (len == 0) Root(v) = Bnil; else Root(v) = mkbtext(s, len); return v; } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Hidden string bstrval(buf, p) string buf; btreeptr p; { /* Returns *next* available position in buffer */ int i, n = Lim(p); if (IsInner(p)) { for (i = 0; i < n; ++i) { buf = bstrval(buf, Ptr(p, i)); *buf++ = Ichar(p, i); } return bstrval(buf, Ptr(p, i)); } strncpy(buf, &Bchar(p, 0), n); return buf+n; } Visible string strval(v) value v; { static char *buffer; int len = Tltsize(v); if (len == Bigsize) syserr(MESS(1601, "strval on big text")); if (len == 0) return ""; if (buffer != NULL) regetmem(&buffer, (unsigned) len+1); else buffer = getmem((unsigned) len+1); *bstrval(buffer, Root(v)) = '\0'; return buffer; } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ typedef struct stackelem { btreeptr s_ptr; int s_lim; } stackelem; typedef stackelem stack[Maxheight]; typedef stackelem *stackptr; #define Snil ((stackptr)0) #define Push(s, p, l) ((s)->s_ptr = (p), ((s)->s_lim = (l)), (s)++) #define Pop(s, p, l) (--(s), (p) = (s)->s_ptr, (l) = (s)->s_lim) extern stackptr unzip(); extern Procedure cpynptrs(); extern int movnptrs(); Hidden btreeptr zip(s1, sp1, s2, sp2) stackptr s1, sp1, s2, sp2; { btreeptr p1, p2, newptr[2]; int l1, l2, i, n, n2; #define q1 newptr[0] #define q2 newptr[1] char newitem; bool overflow, underflow, inner; char *cp; btreeptr *pp; char cbuf[2*Maxbottom]; btreeptr pbuf[2*Maxinner+2]; while (s1 < sp1 && s1->s_lim == 0) ++s1; while (s2 < sp2 && s2->s_lim == Lim(s2->s_ptr)) ++s2; inner = overflow = underflow = No; q1 = Bnil; while (s1 < sp1 || s2 < sp2) { if (s1 < sp1) Pop(sp1, p1, l1); else p1 = Bnil; if (s2 < sp2) Pop(sp2, p2, l2); else p2 = Bnil; cp = cbuf; if (p1 != Bnil) { strncpy(cp, (inner ? &Ichar(p1, 0) : &Bchar(p1, 0)), l1); cp += l1; } if (overflow) *cp++ = newitem; n = cp - cbuf; if (p2 != Bnil) { strncpy(cp, (inner ? &Ichar(p2, l2) : &Bchar(p2, l2)), Lim(p2)-l2); n += Lim(p2)-l2; } if (inner) { pp = pbuf; /***** Change if reverse direction! *****/ if (p1 != Bnil) { cpynptrs(pp, &Ptr(p1, 0), l1); Incr(pp, l1); } movnptrs(pp, newptr, 1+overflow); Incr(pp, 1+overflow); if (p2 != Bnil) { cpynptrs(pp, &Ptr(p2, l2+1), Lim(p2)-l2); Incr(pp, Lim(p2)-l2); } if (underflow) { underflow= No; n= uflow(n, p1 ? l1 : 0, cbuf, pbuf, Ct); } } overflow = No; if (n > (inner ? Maxinner : Maxbottom)) { overflow = Yes; n2 = (n-1)/2; n -= n2+1; } else if (n < (inner ? Mininner : Minbottom)) underflow = Yes; q1 = grabbtreenode(inner ? Inner : Bottom, Ct); Lim(q1) = n; cp = cbuf; strncpy((inner ? &Ichar(q1, 0) : &Bchar(q1, 0)), cp, n); cp += n; if (inner) { pp = pbuf; i = movnptrs(&Ptr(q1, 0), pp, n+1); Incr(pp, n+1); n += i; } Size(q1) = n; if (overflow) { newitem = *cp++; q2 = grabbtreenode(inner ? Inner : Bottom, Ct); Lim(q2) = n2; strncpy((inner ? &Ichar(q2, 0) : &Bchar(q2, 0)), cp, n2); if (inner) n2 += movnptrs(&Ptr(q2, 0), pp, n2+1); Size(q2) = n2; } inner = Yes; } if (overflow) q1 = mknewroot(q1, (itemptr)&newitem, q2, Ct); return q1; #undef q1 #undef q2 } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Hidden value ibehead(v, h) value v; int h; { /* v@h */ stack s; stackptr sp; sp = (stackptr) unzip(Root(v), h-1, s); v = grab_tlt(Tex, Ct); Root(v) = zip(Snil, Snil, s, sp); return v; } Hidden value icurtail(v, t) value v; int t; { /* v|t */ stack s; stackptr sp; sp = (stackptr) unzip(Root(v), t, s); v = grab_tlt(Tex, Ct); Root(v) = zip(s, sp, Snil, Snil); return v; } Hidden value iconcat(v, w) value v, w; { /* v^w */ stack s1, s2; stackptr sp1 = (stackptr) unzip(Root(v), Tltsize(v), s1); stackptr sp2 = (stackptr) unzip(Root(w), 0, s2); v = grab_tlt(Tex, Ct); Root(v) = zip(s1, sp1, s2, sp2); return v; } #define Odd(n) (((n)&1) != 0) Hidden value irepeat(v, n) value v; int n; { /* v^^n */ value x, w = grab_tlt(Tex, Ct); Root(w) = Bnil; v = copy(v); while (n > 0) { if (Odd(n)) { w = iconcat(x = w, v); release(x); } n /= 2; if (n == 0) break; v = iconcat(x = v, v); release(x); } release(v); return w; } #ifdef UNUSED_CODE Hidden value jrepeat(v, n) value v; int n; { /* v^^n, recursive solution */ value w, x; if (n <= 1) { if (n == 1) return copy(v); w = grab_tlt(Tex, Ct); Root(w) = Bnil; return w; } w = jrepeat(v, n/2); w = iconcat(x = w, w); release(x); if (Odd(n)) { w = iconcat(x = w, v); release(x); } return w; } #endif UNUSED_CODE /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Visible value curtail(t, after) value t, after; { int syzcurv, syztext; if (!Is_text(t)) { reqerr(MESS(1602, "in t|n, t is not a text")); return Vnil; } if (!Is_number(after)) { reqerr(MESS(1603, "in t|n, n is not a number")); return Vnil; } syztext = Tltsize(t); if (syztext == Bigsize) syserr(MESS(1604, "curtail on very big text")); if (large(after) || (syzcurv = intval(after)) < 0 || syztext < syzcurv) { reqerr(MESS(1605, "in t|n, n is out of bounds")); return Vnil; } return icurtail(t, syzcurv); } Visible value behead(t, before) value t, before; { int syzbehv, syztext; if (!Is_text(t)) { reqerr(MESS(1606, "in t@n, t is not a text")); return Vnil; } if (!Is_number(before)) { reqerr(MESS(1607, "in t@n, n is not a number")); return Vnil; } syztext = Tltsize(t); if (syztext == Bigsize) syserr(MESS(1608, "behead on very big text")); if (large(before) || (syzbehv = intval(before)) <= 0 || syztext < syzbehv-1) { reqerr(MESS(1609, "in t@n, n is out of bounds")); return Vnil; } return ibehead(t, syzbehv); } #ifdef NOT_USED Visible value trim(v, b, c) value v; intlet b, c; { /*temporary*/ /* Only used in f_uname */ int len= Tltsize(v); value r= ibehead(v, b+1), s; s= icurtail(r, len-b-c); release(r); return s; } #endif /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Visible value concat(tleft, tright) value tleft, tright; { int syzleft, syzright; if (!Is_text(tleft) || !Is_text(tright)) { reqerr(MESS(1610, "in t^u, t or u is not a text")); return Vnil; } syzleft = Tltsize(tleft); syzright = Tltsize(tright); if (syzleft == Bigsize || syzright == Bigsize) syserr(MESS(1611, "concat on very big text")); if (syzleft > Maxint-syzright || syzright > Maxint-syzleft) { reqerr(MESS(1612, "in t^u, the result is too long")); return Vnil; } return iconcat(tleft, tright); } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Visible value repeat(t, n) value t, n; { int tsize, k; if (!Is_text(t)) { reqerr(MESS(1613, "in t^^n, t is not a text")); return Vnil; } if (!Is_number(n)) { reqerr(MESS(1614, "in t^^n, n is not a number")); return Vnil; } if (numcomp(n, zero) < 0) { reqerr(MESS(1615, "in t^^n, n is negative")); return Vnil; } tsize = Tltsize(t); if (tsize == 0) return copy(t); if (large(n) || Maxint/tsize < (k = intval(n))) { reqerr(MESS(1616, "in t^^n, the result is too long")); return Vnil; } return irepeat(t, k); } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Visible Procedure wrtext(putch, v, quote) int (*putch)(); value v; char quote; { if (v == Vnil || !Is_text(v)) { (*putch)('?'); return; } if (quote) (*putch)(quote); if (Root(v) != Bnil) wrbtext(putch, Root(v), quote); if (quote) (*putch)(quote); } Hidden Procedure wrbtext(putch, p, quote) int (*putch)(); btreeptr p; char quote; { int i, n = Lim(p); char c; if (IsInner(p)) { for (i = 0; still_ok && i < n; ++i) { wrbtext(putch, Ptr(p, i), quote); c = Ichar(p, i); (*putch)(c); if (quote && (c == quote || c == '`')) (*putch)(c); } wrbtext(putch, Ptr(p, i), quote); } else if (quote) { for (i = 0; i < n; ++i) { c = Bchar(p, i); (*putch)(c); if (c == quote || c == '`') (*putch)(c); } } else { for (i = 0; i < n; ++i) (*putch)(Bchar(p, i)); } } #else INTEGRATION Visible value mk_text(m) string m; { value v; intlet len= strlen(m); v= grab_tex(len); strcpy(Str(v), m); return v; } Visible bool character(v) value v; { if (Is_text(v) && Length(v) == 1) return Yes; else return No; } Visible char charval(v) value v; { if (!Is_text(v) || Length(v) != 1) error(MESS(1617, "value not a character")); return *Str(v); } Visible string strval(v) value v; { return Str(v); } Visible value concat(s, t) value s, t; { if (Type(s) != Tex) error(MESS(1618, "in t^u, t is not a text")); else if (Type(t) != Tex) error(MESS(1619, "in t^u, t is a text, but u is not")); else { value c= grab_tex(Length(s)+Length(t)); strcpy(Str(c), Str(s)); strcpy(Str(c)+Length(s), Str(t)); return c; } return grab_tex(0); } #define VERSION2 Visible Procedure concato(s, t) value *s; string t; { if (Type(*s) != Tex) error(MESS(1620, "attempt to join text with non-text")); else { #ifdef VERSION1 xtndtex(s, strlen(t)); strcat(Str(*s), t); #endif #ifdef VERSION2 value v= mk_text(t); value w= concat(*s, v); release(*s); release(v); *s= w; #endif } } Visible value trim(v, B, C) value v; intlet B, C; { intlet len= Length(v), k; if (Type(v) != Tex) error(MESS(1621, "trim (@ or |) applied to non-text")); else if (B < 0 || C < 0 || B+C > len) error(MESS(1622, "trim (@ or |) out of bounds")); else { value w= grab_tex(len-=(B+C)); string vp= Str(v)+B, wp= Str(w); Overall *wp++= *vp++; *wp= '\0'; return w; } return grab_tex(0); } Visible Procedure putintrim(pn, head, tail, str) value *pn; intlet head, tail; string str; { value v = *pn; intlet len= Length(v); if (Type(v) != Tex) error(MESS(1623, "putintrim (@ or |) applied to non-text")); else if (head < 0 || tail < 0 || head+tail > len) error(MESS(1624, "putintrim (@ or |) out of bounds")); else { value w = head == 0 ? mk_text("") : head == len ? copy(v) : trim(v, 0, len - head); if (*str) concato(&w, str); if (tail > 0) concato(&w, Str(v)+(len - tail)); release(v); *pn = w; } } Visible value curtail(v, n) value v, n; { intlet c= intval(n); v= trim(v, 0, Length(v) - c); return v; } Visible value behead(v, n) value v, n; { intlet b= intval(n); v= trim(v, b-1, 0); return v; } Visible value repeat(x, y) value x, y; { intlet i= propintlet(intval(y)); if (Type(x) != Tex) error(MESS(1625, "in t^^n, t is not a text")); if (i < 0) error(MESS(1626, "in t^^n, n is negative")); else { value r; string xp, rp; intlet p, q, xl= Length(x); r= grab_tex(propintlet(i*xl)); rp= Str(r); for (p= 0; p < i; p++) { xp= Str(x); for (q= 0; q < xl; q++) *rp++= *xp++; } *rp= '\0'; return r; } return grab_tex(0); } #define Left 'L' #define Right 'R' #define Centre 'C' Hidden value adj(x, y, side) value x, y; literal side; { value r, v= convert(x, Yes, Yes); int i= intval(y); intlet lv= Length(v), la, k, ls, rs; string rp, vp; la= propintlet(i) - lv; if (la <= 0) return v; r= grab_tex(lv+la); rp= Str(r); vp= Str(v); if (side == Left) { ls= 0; rs= la; } else if (side == Centre) { ls= la/2; rs= (la+1)/2; } else { ls= la; rs= 0; } for (k= 0; k < ls; k++) *rp++= ' '; for (k= 0; k < lv; k++) *rp++= *vp++; for (k= 0; k < rs; k++) *rp++= ' '; *rp= 0; release(v); return r; } Visible value adjleft(x, y) value x, y; { return adj(x, y, Left); } Visible value centre(x, y) value x, y; { return adj(x, y, Centre); } Visible value adjright(x, y) value x, y; { return adj(x, y, Right); } /* For reasons of efficiency, wri does not always call convert but writes directly on the standard output. Modifications in convert should be mirrored by changes in wri and vice versa. */ Visible value convert(v, coll, outer) value v; bool coll, outer; { literal type= Type(v); intlet len= Length(v), k; value *vp= Ats(v); value t, cv; switch (type) { case Num: return mk_text(convnum(v)); case Tex: if (outer) return copy(v); else {string tp= (string) vp; char cs[2]; cs[1]= '\0'; t= mk_text("'"); Overall { cs[0]= *tp++; concato(&t, cs); if (cs[0] == '\'' || cs[0] == '`') concato(&t, cs); } concato(&t, "'"); return t; } case Com: outer&= coll; t= mk_text(coll ? "" : "("); Overall { concato(&t, Str(cv= convert(*vp++, No, outer))); release(cv); if (k != len-1) concato(&t, outer ? " " : ", "); } if (!coll) concato(&t, ")"); return t; case Lis: case ELT: t= mk_text("{"); Overall { concato(&t, Str(cv= convert(*vp++, No, No))); release(cv); if (k != len-1) concato(&t, "; "); } concato(&t, "}"); return t; case Tab: t= mk_text("{"); Overall { concato(&t, "["); concato(&t, Str(cv= convert(Cts(*vp), Yes, No))); release(cv); concato(&t, "]: "); concato(&t, Str(cv= convert(Dts(*vp++), No, No))); release(cv); if (k != len-1) concato(&t, "; "); } concato(&t, "}"); return t; default: syserr(MESS(1627, "converting value of unknown type")); return (value) Dummy; } } #endif INTEGRATION