home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / ICONPL8.ZIP / PROCS.PAK < prev    next >
Text File  |  1990-03-23  |  136KB  |  5,068 lines

  1. ##########
  2. allof.icn
  3. ############################################################################
  4. #
  5. #    Name:    allof.icn
  6. #
  7. #    Title:    Iterative Conjunction Control Operation
  8. #
  9. #    Author:    Robert J. Alexander
  10. #
  11. #    Date:    November 3, 1989
  12. #
  13. ############################################################################
  14. #
  15. #  allof{expr1,expr2} -- Control operation that performs iterative
  16. #             conjunction.
  17. #
  18. #     Expr1 works like the control expression of "every-do"; it controls
  19. #  iteration by being resumed to produce all of its possible results.
  20. #  The allof{} expression produces the outcome of conjunction of all
  21. #  of the resulting expr2s, one instance of expr2 created for each
  22. #  iteration.
  23. #
  24. #     For example:
  25. #
  26. #    global c
  27. #    ...
  28. #    pattern := "ab*"
  29. #    "abcdef" ? {
  30. #       allof { c := !pattern ,
  31. #          if c == "*" then move(0 to *&subject - &pos + 1) else =c
  32. #          } & pos(0)
  33. #       }
  34. #
  35. #  This example will perform a wild card match on "abcdef" against
  36. #  pattern "ab*", where "*" in a pattern matches 0 or more characters.
  37. #  Since pos(0) will fail the first time it is evaluated, the allof{}
  38. #  expression will be resumed just as a conjunction expression would,
  39. #  and backtracking will propagate through all of the expr2s; the
  40. #  expression will ultimately succeed (as its conjunctive equivalent
  41. #  would).
  42. #
  43. #     Note that, due to the scope of variables in co-expressions,
  44. #  communication between expr1 and expr2 must be via global variables,
  45. #  hence c in the above example must be global.
  46. #
  47. #     The allof{} procedure models Icon's expression evaluation
  48. #  mechanism in that it explicitly performs backtracking.  The author of
  49. #  this procedure knows of no way to use Icon's built-in goal directed
  50. #  evaluation to perform conjunction of a arbitrary number of computed
  51. #  expressions (suggestions welcome).
  52. #
  53.  
  54. procedure allof(expr)
  55.    local elist,i,x,v
  56.    #
  57.    #  Initialize
  58.    #
  59.    elist := []    # expression list
  60.    i := 1    # expression list pointer
  61.    #
  62.    #  Loop until backtracking over all expr[2]s has failed.
  63.    #
  64.    while i > 0 do {
  65.       if not (x := elist[i]) then
  66.      #
  67.      #  If we're at the end of the list of expressions, attempt an
  68.      #  iteration to produce another expression.
  69.      #
  70.          if @expr[1] then
  71.         put(elist,x := ^expr[2])
  72.      else {
  73.         #
  74.         #  If no further iterations, suspend a result.
  75.         #
  76.         suspend v
  77.         #
  78.         #  We've been backed into -- back up to last expr[2].
  79.         #
  80.         i -:= 1
  81.         }
  82.       #
  83.       #  Evaluate the expression.
  84.       #
  85.       if v := @x then {
  86.      #
  87.      #  If success, move on to the refreshed next expression.
  88.      #
  89.          i +:= 1
  90.      elist[i] := ^elist[i]
  91.      }
  92.       else
  93.      #
  94.      #  If failure, back up.
  95.      #
  96.          i -:= 1
  97.       }
  98. end
  99. ##########
  100. bincvt.icn
  101. ############################################################################
  102. #
  103. #    Name:    bincvt.icn
  104. #
  105. #    Title:    Convert binary data
  106. #
  107. #    Author:    Robert J. Alexander
  108. #
  109. #    Date:    December 5, 1989
  110. #
  111. ############################################################################
  112. #
  113. #  unsigned() -- Converts binary byte string into unsigned integer.
  114. #  Detects overflow if number is too large.
  115. #
  116. #  This procedure is normally used for processing of binary data
  117. #  read from a file.
  118. #
  119. procedure unsigned(s)
  120.    local i
  121.    i := 0
  122.    every i := ord(!s) + i * 256
  123.    return i
  124. end
  125.  
  126. #
  127. #  raw() -- Puts raw bits of characters of string s into an integer.  If
  128. #  the size of s is less than the size of an integer, the bytes are put
  129. #  into the low order part of the integer, with the remaining high order
  130. #  bytes filled with zero.  If the string is too large, the most
  131. #  significant bytes will be lost -- no overflow detection.
  132. #
  133. #  This procedure is normally used for processing of binary data
  134. #  read from a file.
  135. #
  136.  
  137. procedure raw(s)
  138.    local i
  139.    i := 0
  140.    every i := ior(ord(!s),ishift(i,8))
  141.    return i
  142. end
  143.  
  144. #
  145. #  rawstring() -- Creates a string consisting of the raw bits in the low
  146. #  order "size" bytes of integer i.
  147. #
  148. #  This procedure is normally used for processing of binary data
  149. #  to be written to a file.
  150. #
  151.  
  152. procedure rawstring(i,size)
  153.    local s
  154.    s := ""
  155.    every 1 to size do {
  156.       s := char(iand(i,16rFF)) || s
  157.       i := ishift(i,-8)
  158.       }
  159.    return s
  160. end
  161. ##########
  162. bold.icn
  163. ############################################################################
  164. #
  165. #    Name:    bold.icn
  166. #
  167. #    Title:    Procedures for enboldening and underscoring test
  168. #
  169. #    Author:    Ralph E. Griswold
  170. #
  171. #    Date:    June 10, 1988
  172. #
  173. ############################################################################
  174. #  
  175. #  These procedures produce text with interspersed characters suit-
  176. #  able for printing to produce the effect of boldface (by over-
  177. #  striking) and underscoring (using backspaces).
  178. #  
  179. #       bold(s)        bold version of s
  180. #  
  181. #       uscore(s)      underscored version of s
  182. #  
  183. ############################################################################
  184.  
  185. procedure bold(s)
  186.    local c
  187.    static labels, trans, max
  188.    initial {
  189.       labels := "1"
  190.       trans := repl("1\b",4) || "1"
  191.       max := *labels
  192.       trans := bold(string(&lcase))
  193.       labels := string(&lcase)
  194.       max := *labels
  195.       }
  196.    if *s <= max then
  197.       return map(left(trans,9 * *s),left(labels,*s),s)
  198.    else return bold(left(s,*s - max)) ||
  199.       map(trans,labels,right(s,max))
  200. end
  201.  
  202. procedure uscore(s)
  203.    static labels, trans, max
  204.    initial {
  205.       labels := "1"
  206.       trans := "_\b1"
  207.       max := *labels
  208.       trans := uscore(string(&lcase))
  209.       labels := string(&lcase)
  210.       max := *labels
  211.       }
  212.    if *s <= max then
  213.       return map(left(trans,3 * *s),left(labels,*s),s)
  214.    else return uscore(left(s,*s - max)) ||
  215.       map(trans,labels,right(s,max))
  216. end
  217. ##########
  218. codeobj.icn
  219. ############################################################################
  220. #
  221. #    Name:    codeobj.icn
  222. #
  223. #    Title:    Procedures to encode and decode Icon data
  224. #
  225. #    Author:    Ralph E. Griswold
  226. #
  227. #    Date:    November 16, 1988
  228. #
  229. ############################################################################
  230. #
  231. #     These procedures provide a way of storing Icon values as strings and
  232. #  retrieving them.  The procedure encode(x) converts x to a string s that
  233. #  can be converted back to x by decode(s). These procedures handle all
  234. #  kinds of values, including structures of arbitrary complexity and even
  235. #  loops.  For "scalar" types -- null, integer, real, cset, and string --
  236. #
  237. #    decode(encode(x)) === x
  238. #
  239. #     For structures types -- list, set, table, and record types --
  240. #  decode(encode(x)) is, for course, not identical to x, but it has the
  241. #  same "shape" and its elements bear the same relation to the original
  242. #  as if they were encoded and decode individually.
  243. #
  244. #     No much can be done with files, functions and procedures, and
  245. #  co-expressions except to preserve type and identification.
  246. #
  247. #     The encoding of strings and csets handles all characters in a way
  248. #  that it is safe to write the encoding to a file and read it back.
  249. #
  250. #     No particular effort was made to use an encoding of value that
  251. #  minimizes the length of the resulting string. Note, however, that
  252. #  as of Version 7 of Icon, there are no limits on the length of strings
  253. #  that can be written out or read in.
  254. #
  255. ############################################################################
  256. #
  257. #     The encoding of a value consists of four parts:  a tag, a length,
  258. #  a type code, and a string of the specified length that encodes the value
  259. #  itself.
  260. #
  261. #     The tag is omitted for scalar values that are self-defining.
  262. #  For other values, the tag serves as a unique identification. If such a
  263. #  value appears more than once, only its tag appears after the first encoding.
  264. #  There is, therefore, a type code that distinguishes a label for a previously
  265. #  encoded value from other encodings. Tags are strings of lowercase
  266. #  letters. Since the tag is followed by a digit that starts the length, the
  267. #  two can be distinguished.
  268. #
  269. #     The length is simply the length of the encoded value that follows.
  270. #
  271. #     The type codes consist of single letters taken from the first character
  272. #  of the type name, with lower- and uppercase used to avoid ambiguities.
  273. #
  274. #     Where a structure contains several elements, the encodings of the
  275. #  elements are concatenated. Note that the form of the encoding contains
  276. #  the information needed to separate consecutive elements.
  277. #
  278. #     Here are some examples of values and their encodings:
  279. #
  280. #    x                     encode(x)
  281. #  -------------------------------------------------------
  282. #
  283. #    1                     "1i1"
  284. #    2.0                   "3r2.0"
  285. #    &null                 "0n"
  286. #    "\377"                "4s\\377"
  287. #    '\376\377'            "8c\\376\\377"
  288. #    procedure main        "a4pmain"
  289. #    co-expression #1 (0)  "b0C"
  290. #    []                    "c0L"
  291. #    set()                 "d0S"
  292. #    table("a")            "e3T1sa"
  293. #    L1 := ["hi","there"]  "f11L2shi5sthere"
  294. #
  295. #  A loop is illsutrated by
  296. #
  297. #    L2 := []
  298. #    put(L2,L2)
  299. #
  300. #  for which
  301. #
  302. #    x                     encode(x)
  303. #  -------------------------------------------------------
  304. #
  305. #    L2                    "g3L1lg"
  306. #
  307. #     Of course, you don't have to know all this to use encode and decode.
  308. #
  309. ############################################################################
  310. #
  311. #  Links: escape, gener
  312. #
  313. #  Requires:  co-expressions
  314. #
  315. #  See also: object.icn
  316. #
  317. ############################################################################
  318.  
  319. link escape, gener
  320.  
  321. global outlab, inlab
  322.  
  323. record triple(type,value,tag)
  324.  
  325. #  Encode an arbitary value as a string.
  326. #
  327. procedure encode(x,level)
  328.    local str, tag, Type
  329.    static label
  330.    initial label := create star(string(&lcase))
  331.    if /level then outlab := table()    # table is global, but reset at
  332.                     # each root call.
  333.    tag := ""
  334.    Type := typecode(x)
  335.    if Type == !"ri" then str := string(x)    # first the scalars
  336.    else if Type == !"cs" then str := image(string(x))[2:-1]    # remove quotes
  337.    else if Type == "n" then str := ""
  338.    else if Type == !"LSRTfpC" then    # next the structures and other types
  339.       if str := \outlab[x] then        # if the object has been processed,
  340.          Type := "l"            # use its label and type it as label.
  341.       else {
  342.          tag := outlab[x] := @label    # else make a label for it.
  343.          str := ""
  344.          if Type == !"LSRT" then {    # structures
  345.             every str ||:= encode(    # generate, recurse, and concatenate
  346.                case Type of {
  347.                   !"LS":   !x        # elements
  348.                   "T":    x[[]] | !sort(x,3)    # default, then elements
  349.                   "R":    type(x) | !x        # type then elements
  350.                   }
  351.                ,1)            # indicate internal call
  352.             }
  353.             else str ||:= case Type of {    # other things
  354.                "f":   image(x)
  355.                "C":   ""
  356.                "p":   image(x) ? {    # watch out for record constructors
  357.                   tab(find("record constructor ") + *"record constructor ") |
  358.                   tab(upto(' ') + 1)
  359.                   tab(0)
  360.                   }
  361.                }
  362.          }
  363.    else stop("unsupported type in encode: ",image(x))
  364.    return tag || *str || Type || str
  365. end
  366.  
  367. #  Produce a one-letter code for the type.
  368. #
  369. procedure typecode(x)
  370.    local code
  371.                 # be careful of records and their constructors
  372.    if image(x) ? ="record constructor " then return "p"
  373.    if image(x) ? ="record" then return "R"
  374.    code := type(x)
  375.    if code == ("list" | "set" | "table" | "co-expression") then
  376.       code := map(code,&lcase,&ucase)
  377.    return code[1]
  378. end
  379.  
  380. #  Generate decoded results.  At the top level, there is only one,
  381. #  but for structures, it is called recursively and generates the
  382. #  the decoded elements. 
  383. #
  384. procedure decode(s,level)
  385.    local p
  386.    if /level then inlab := table()    # global but reset
  387.    every p := separ(s) do {
  388.       suspend case p.type of {
  389.          "l":  inlab[p.value]        # label for an object
  390.          "i":  integer(p.value)
  391.          "s":  escape(p.value)
  392.          "c":  cset(escape(p.value))
  393.          "r":  real(p.value)
  394.          "n":  &null
  395.          "L":  delist(p.value,p.tag)
  396.          "R":  derecord(p.value,p.tag)
  397.          "S":  deset(p.value,p.tag)
  398.          "T":  detable(p.value,p.tag)
  399.          "f":  defile(p.value)
  400.          "C":  create &fail    # can't hurt much to fail
  401.          "p":  (proc(p.value) | stop("encoded procedure not found")) \ 1
  402.          default:  stop("unexpected type in decode: ",p.type)
  403.          }
  404.       }
  405. end
  406.  
  407. #  Generate triples for the encoded values in concatenation.
  408. #
  409. procedure separ(s)
  410.    local p, size
  411.  
  412.    while *s ~= 0 do {
  413.       p := triple()
  414.       s ?:= {
  415.          p.tag := tab(many(&lcase))
  416.          size := tab(many(&digits)) | break
  417.          p.type := move(1)
  418.          p.value := move(size)
  419.          tab(0)
  420.          }
  421.       suspend p
  422.       }
  423. end
  424.  
  425. #  Decode a list. The newly constructed list is added to the table that
  426. #  relates tags to structure values.
  427. #
  428. procedure delist(s,tag)
  429.    local a
  430.    inlab[tag] := a := []    # insert object for label
  431.    every put(a,decode(s,1))
  432.    return a
  433. end
  434.  
  435. #  Decode a set. Compare to delist above.
  436. #
  437. procedure deset(s,tag)
  438.    local S
  439.    inlab[tag] := S := set()
  440.    every insert(S,decode(s,1))
  441.    return S
  442. end
  443.  
  444. #  Decode a record.
  445. #
  446. procedure derecord(s,tag)
  447.    local R, e
  448.    e := create decode(s,1)    # note use of co-expressions to control
  449.                 # generation, since record must be constructed
  450.                 # before fields are produced.
  451.    inlab[tag] := R := proc(@e)() | stop("error in decoding record")
  452.    every !R := @e
  453.    return R
  454. end
  455.  
  456. #  Decode  a table.
  457. #
  458. procedure detable(s,tag)
  459.    local t, e
  460.    e := create decode(s,1)    # see derecord above; here it's the default
  461.                 # value that motivates co-expressions.
  462.    inlab[tag] := t := table(@e)
  463.    while t[@e] := @e
  464.    return t
  465. end
  466.  
  467. #  Decode a file.
  468. #
  469. procedure defile(s)
  470.    s := decode(s,1)        # the result is an image of the original file.
  471.    return case s of {        # files aren't so simple ...
  472.       "&input":  &input
  473.       "&output": &output
  474.       "&errout": &errout
  475.       default: s ? {
  476.             ="file("        # open for reading to play it safe
  477.             open(tab(upto(')'))) | stop("cannot open encoded file")
  478.             }
  479.        }
  480. end
  481. ##########
  482. collate.icn
  483. ############################################################################
  484. #
  485. #    Name:    collate.icn
  486. #
  487. #    Title:    Collate and decollate strings
  488. #
  489. #    Author:    Ralph E. Griswold
  490. #
  491. #    Date:    June 10, 1988
  492. #
  493. ############################################################################
  494. #  
  495. #  These procedures collate (interleave) respective characters of
  496. #  two strings and decollate such strings by selecting every other
  497. #  character of a string.  produce a string consisting of inter-
  498. #  leaved characters of s1 and s2.
  499. #  
  500. #       collate(s1,s2) collate the characters of s1 and s2.  For
  501. #                      example,
  502. #
  503. #                           collate("abc","def")
  504. #
  505. #                      produces "adbecf".
  506. #  
  507. #       decollate(s,i) produce a string consisting of every other
  508. #                      character of s. If i is odd, the odd-numbered
  509. #                      characters are selected, while if i is even,
  510. #                      the even-numbered characters are selected.
  511. #  
  512. ############################################################################
  513.  
  514. procedure collate(s1,s2)
  515.    local length, ltemp, rtemp
  516.    static llabels, rlabels, clabels, blabels, half
  517.    initial {
  518.       llabels := "ab"
  519.       rlabels := "cd"
  520.       blabels := llabels || rlabels
  521.       clabels := "acbd"
  522.       half := 2
  523.       ltemp := left(&cset,*&cset / 2)
  524.       rtemp := right(&cset,*&cset / 2)
  525.       clabels := collate(ltemp,rtemp)
  526.       llabels := ltemp
  527.       rlabels := rtemp
  528.       blabels := string(&cset)
  529.       half := *llabels
  530.       }
  531.    length := *s1
  532.    if length <= half then
  533.       return map(left(clabels,2 * length),left(llabels,length) ||
  534.          left(rlabels,length),s1 || s2)
  535.    else return map(clabels,blabels,left(s1,half) || left(s2,half)) ||
  536.       collate(right(s1,length - half),right(s2,length - half))
  537. end
  538.  
  539. #  decollate s according to even or odd i
  540. #
  541. procedure decollate(s,i)
  542.    static dsize, image, object
  543.    local ssize
  544.    initial {
  545.       image := collate(left(&cset,*&cset / 2),left(&cset,*&cset / 2))
  546.       object := left(&cset,*&cset / 2)
  547.       dsize := *image
  548.       }
  549.    i %:= 2
  550.    ssize := *s
  551.    if ssize + i <= dsize then
  552.       return map(object[1+:(ssize + i) / 2],image[(i + 1)+:ssize],s)
  553.    else return map(object[1+:(dsize - 2) / 2],image[(i + 1)+:dsize - 2],
  554.       s[1+:(dsize - 2)]) || decollate(s[dsize - 1:0],i)
  555. end
  556.  
  557. ##########
  558. colmize.icn
  559. ############################################################################
  560. #
  561. #    Name:    colmize.icn
  562. #
  563. #    Title:    Arrange data into columns
  564. #
  565. #    Author:    Robert J. Alexander
  566. #
  567. #    Date:    December 5, 1989
  568. #
  569. ############################################################################
  570. #
  571. #  colmize() -- Arrange data into columns.
  572. #
  573. #  Procedure to arrange a number of data items into multiple columns.
  574. #  Items are arranged in column-wise order, that is, the sequence runs
  575. #  down the first column, then down the second, etc.
  576. #
  577. #  This procedure goes to great lengths to print the items in as few
  578. #  vertical lines as possible.
  579. #
  580.  
  581. procedure colmize(entries,maxcols,space,minwidth,rowwise,distribute)
  582.    local mean,cols,lines,width,i,x,wid,extra,t,j
  583.    #
  584.    #  Process arguments -- provide defaults.
  585.    #
  586.    # entries: a list of items to be columnized
  587.    /maxcols := 80                        # max width of output lines
  588.    /space := 2                           # min nbr of spaces between columns
  589.    /minwidth := 0                        # min column width
  590.    # rowwise: if nonnull, entries are listed in rowwise order rather than
  591.    # columnwise
  592.    #
  593.    #  Starting with a trial number-of-columns that is guaranteed
  594.    #  to be too wide, successively reduce the number until the
  595.    #  items can be packed into the allotted width.
  596.    #
  597.    mean := 0
  598.    every mean +:= *!entries
  599.    mean := mean / (0 ~= *entries) | 1
  600.    every cols := (maxcols + space) * 2 / (mean + space) to 1 by -1 do {
  601.       lines := (*entries + cols - 1) / cols
  602.       width := list(cols,minwidth)
  603.       i := 0
  604.       if /rowwise then {                  # if column-wise
  605.      every x := !entries do {
  606.         width[i / lines + 1] <:= *x + space
  607.         i +:= 1
  608.         }
  609.      }
  610.       else {                              # else row-wise
  611.      every x := !entries do {
  612.         width[i % cols + 1] <:= *x + space
  613.         i +:= 1
  614.         }
  615.      }
  616.       wid := 0
  617.       every x := !width do wid +:= x
  618.       if wid <= maxcols + space then break
  619.       }
  620.    #
  621.    #  Now output the data in columns.
  622.    #
  623.    extra := (\distribute & (maxcols - wid) / (0 < cols - 1)) | 0
  624.    if /rowwise then {            # if column-wise
  625.       every i := 1 to lines do {
  626.      t := ""
  627.      every j := 0 to cols - 1 do
  628.            t ||:= left(entries[i + j * lines],width[j + 1] + extra)
  629.      suspend trim(t)
  630.      }
  631.       }
  632.    else {                                # else row-wise
  633.       every i := 0 to lines - 1 do {
  634.      t := ""
  635.      every j := 1 to cols do
  636.            t ||:= left(entries[j + i * cols],width[j] + extra)
  637.      suspend trim(t)
  638.      }
  639.       }
  640. end
  641. ##########
  642. complex.icn
  643. ############################################################################
  644. #
  645. #    Name:    complex.icn
  646. #
  647. #    Title:    Perform complex arithmetic
  648. #
  649. #    Author:    Ralph E. Griswold
  650. #
  651. #    Date:    June 10, 1988
  652. #
  653. ############################################################################
  654. #  
  655. #  The following procedures perform operations on complex numbers.
  656. #  
  657. #       complex(r,i)   create complex number with real part r and
  658. #                      imaginary part i
  659. #  
  660. #       cpxadd(x1,x2)  add complex numbers x1 and x2
  661. #  
  662. #       cpxdiv(x1,x2)  divide complex number x1 by complex number x2
  663. #  
  664. #       cpxmul(x1,x2)  multiply complex number x1 by complex number
  665. #                      x2
  666. #  
  667. #       cpxsub(x1,x2)  subtract complex number x2 from complex
  668. #                      number x1
  669. #  
  670. #       cpxstr(x)      convert complex number x to string represen-
  671. #                      tation
  672. #  
  673. #       strcpx(s)      convert string representation s of complex
  674. #                      number to complex number
  675. #  
  676. ############################################################################
  677.  
  678. record complex(rpart,ipart)
  679.  
  680. procedure strcpx(s)
  681.    local i
  682.  
  683.    i := upto('+-',s,2)
  684.    return complex(+s[1:i],+s[i:-1])
  685. end
  686.  
  687. procedure cpxstr(x)
  688.    if x.ipart < 0 then return x.rpart || x.ipart || "i"
  689.    else return x.rpart || "+" || x.ipart || "i"
  690. end
  691.  
  692. procedure cpxadd(x1,x2)
  693.    return complex(x1.rpart + x2.rpart,x1.ipart + x2.ipart)
  694. end
  695.  
  696. procedure cpxsub(x1,x2)
  697.    return complex(x1.rpart - x2.rpart,x1.ipart - x2.ipart)
  698. end
  699.  
  700. procedure cpxmul(x1,x2)
  701.    return complex(x1.rpart * x2.rpart - x1.ipart * x2.ipart,
  702.       x1.rpart * x2.ipart + x1.ipart * x2.rpart)
  703. end
  704.  
  705. procedure cpxdiv(x1,x2)
  706.    local denom
  707.  
  708.    denom := x2.rpart ^ 2 + x2.ipart ^ 2
  709.    return complex((x1.rpart * x2.rpart + x1.ipart * x2.ipart) /
  710.       denom,(x1.ipart * x2.rpart - x1.rpart * x2.ipart) /
  711.       denom)
  712. end
  713. ##########
  714. compress.icn
  715. ############################################################################
  716. #
  717. #    Name:    compress.icn
  718. #
  719. #    Title:    LZW compression procedure
  720. #
  721. #    Author:    Robert J. Alexander
  722. #
  723. #    Date:    December 5, 1989
  724. #
  725. ############################################################################
  726. #
  727. #  compress() -- LZW compression
  728. #
  729. #  Arguments:
  730. #
  731. #    inproc    a procedure that returns a single character from
  732. #        the input stream.
  733. #
  734. #    outproc    a procedure that writes a single character (its
  735. #        argument) to the output stream.
  736. #
  737. #    maxTableSize    the maximum size to which the string table
  738. #        is allowed to grow before something is done about it.
  739. #        If the size is positive, the table is discarded and
  740. #        a new one started.  If negative, it is retained, but
  741. #        no new entries are added.
  742. #
  743.  
  744. procedure compress(inproc,outproc,maxTableSize)
  745.    local EOF,c,charTable,junk1,junk2,outcode,s,t,
  746.      tossTable,x
  747.    #
  748.    #  Initialize.
  749.    #
  750.    /maxTableSize := 1024    # 10 "bits"
  751.    every outproc(!string(maxTableSize))
  752.    outproc("\n")
  753.    tossTable := maxTableSize
  754.    /lzw_recycles := 0
  755.    if maxTableSize < 0 then maxTableSize := -maxTableSize
  756.    charTable := table()
  757.    every c := !&cset do charTable[c] := ord(c)
  758.    EOF := charTable[*charTable] := *charTable    # reserve code=256 for EOF
  759.    lzw_stringTable := copy(charTable)
  760.    #
  761.    #  Compress the input stream.
  762.    #
  763.    s := inproc() | return maxTableSize
  764.    if \lzw_trace then {
  765.       wr(&errout,"\nInput string\tOutput code\tNew table entry")
  766.       wrs(&errout,"\"",image(s)[2:-1])
  767.       }
  768.    while c := inproc() do {
  769.    if \lzw_trace then
  770.      wrs(&errout,image(c)[2:-1])
  771.       if \lzw_stringTable[t := s || c] then s := t
  772.       else {
  773.      compress_output(outproc,junk2 := lzw_stringTable[s],junk1 := *lzw_stringTable)
  774.      if *lzw_stringTable < maxTableSize then
  775.            lzw_stringTable[t] := *lzw_stringTable
  776.      else if tossTable >= 0 then {
  777.            lzw_stringTable := copy(charTable)
  778.            lzw_recycles +:= 1
  779.         }
  780.      if \lzw_trace then
  781.            wrs(&errout,"\"\t\t",
  782.              image(char(*&cset > junk2) | junk2),
  783.              "(",junk1,")\t\t",lzw_stringTable[t]," = ",image(t),"\n\"")
  784.      s := c
  785.      }
  786.       }
  787.    compress_output(outproc,lzw_stringTable[s],*lzw_stringTable)
  788.    if \lzw_trace then
  789.      wr(&errout,"\"\t\t",
  790.            image(char(*&cset > (x := \lzw_stringTable[s] | 0)) | x))
  791.    compress_output(outproc,EOF,*lzw_stringTable)
  792.    compress_output(outproc)
  793.    return maxTableSize
  794. end
  795.  
  796.  
  797. procedure compress_output(outproc,code,stringTableSize)
  798.    static max,bits,buffer,bufferbits,lastSize
  799.    #
  800.    #  Initialize.
  801.    #
  802.    initial {
  803.       lastSize := 1000000
  804.       buffer := bufferbits := 0
  805.       }
  806.    #
  807.    #  If this is "close" call, flush buffer and reinitialize.
  808.    #
  809.    if /code then {
  810.       outcode := &null
  811.       if bufferbits > 0 then
  812.         outproc(char(outcode := ishift(buffer,8 - bufferbits)))
  813.       lastSize := 1000000
  814.       buffer := bufferbits := 0
  815.       return outcode
  816.       }
  817.    #
  818.    #  Expand output code size if necessary.
  819.    #
  820.    if stringTableSize < lastSize then {
  821.       max := 1
  822.       bits := 0
  823.       }
  824.    while stringTableSize > max do {
  825.       max *:= 2
  826.       bits +:= 1
  827.       }
  828.    lastSize := stringTableSize
  829.    #
  830.    #  Merge new code into buffer.
  831.    #
  832.    buffer := ior(ishift(buffer,bits),code)
  833.    bufferbits +:= bits
  834.    #
  835.    #  Output bits.
  836.    #
  837.    while bufferbits >= 8 do {
  838.       outproc(char(outcode := ishift(buffer,8 - bufferbits)))
  839.       buffer := ixor(buffer,ishift(outcode,bufferbits - 8))
  840.       bufferbits -:= 8
  841.       }
  842.    return outcode
  843. end
  844. ##########
  845. currency.icn
  846. ############################################################################
  847. #
  848. #    Name:    currency.icn
  849. #
  850. #    Title:    Currency formatting procedure
  851. #
  852. #    Author:    Robert J. Alexander
  853. #
  854. #    Date:    December 5, 1989
  855. #
  856. ############################################################################
  857. #
  858. #  currency() -- Formats "amount" in standard American currency format.
  859. #  "amount" can be a real, integer, or numeric string.  "width" is the
  860. #  output field width, in which the amount is right adjusted.  The
  861. #  returned string will be longer than "width" if necessary to preserve
  862. #  significance.  "minus" is the character string to be used for
  863. #  negative amounts (default "-"), and is placed to the right of the
  864. #  amount.
  865. #
  866.  
  867. procedure currency(amount,width,minus)
  868.    local sign,p
  869.    /width := 0
  870.    /minus := "-"
  871.    amount := real(amount) | fail
  872.    if amount < 0 then {
  873.       sign := minus
  874.       amount := -amount
  875.       }
  876.    else sign := repl(" ",*minus)
  877.    amount := string(amount)
  878.    amount := if p := find(".",amount) then left(amount,p + 2,"0") else
  879.      amount || ".00"
  880.    if match("0.",amount) then amount[1:3] := "0."
  881.    amount := "$" || amount || sign
  882.    return if *amount >= width then amount else right(amount,width)
  883. end
  884. ##########
  885. decompr.icn
  886. ############################################################################
  887. #
  888. #    Name:    decompr.icn
  889. #
  890. #    Title:    LZW decompression of compressed stream created
  891. #                  by compress()
  892. #
  893. #    Author:    Robert J. Alexander
  894. #
  895. #    Date:    December 5, 1989
  896. #
  897. ############################################################################
  898. #
  899. #  decompress() -- LZW decompression of compressed stream created
  900. #                  by compress()
  901. #
  902. #  Arguments:
  903. #
  904. #    inproc    a procedure that returns a single character from
  905. #        the input stream.
  906. #
  907. #    outproc    a procedure that writes a single character (its
  908. #        argument) to the output stream.
  909. #
  910.  
  911. procedure decompress(inproc,outproc)
  912.    local EOF,c,charSize,code,i,maxTableSize,new_code,old_strg,
  913.      strg,tossTable
  914.    #
  915.    #  Initialize.
  916.    #
  917.    maxTableSize := ""
  918.    while (c := inproc()) ~== "\n" do maxTableSize ||:= c
  919.    maxTableSize := integer(maxTableSize) |
  920.      stop("Invalid file format -- max table size missing")
  921.    tossTable := maxTableSize
  922.    /lzw_recycles := 0
  923.    if maxTableSize < 0 then maxTableSize := -maxTableSize
  924.    maxTableSize -:= 1
  925.    lzw_stringTable := list(*&cset)
  926.    every i := 1 to *lzw_stringTable do lzw_stringTable[i] := char(i - 1)
  927.    put(lzw_stringTable,EOF := *lzw_stringTable)  # reserve code=256 for EOF
  928.    charSize := *lzw_stringTable
  929.    if \lzw_trace then
  930.      wr(&errout,"\nInput code\tOutput string\tNew table entry")
  931.    #
  932.    #  Decompress the input stream.
  933.    #
  934.    while old_strg :=
  935.      lzw_stringTable[decompress_read_code(inproc,*lzw_stringTable,EOF) + 1] do {
  936.       if \lzw_trace then
  937.         wr(&errout,image(old_strg),"(",*lzw_stringTable,")",
  938.           "\t",image(old_strg))
  939.       outproc(old_strg)
  940.       c := old_strg[1]
  941.       (while new_code := decompress_read_code(inproc,*lzw_stringTable + 1,EOF) do {
  942.      strg := lzw_stringTable[new_code + 1] | old_strg || c
  943.      outproc(strg)
  944.      c := strg[1]
  945.      if \lzw_trace then
  946.            wr(&errout,image(char(*&cset > new_code) \ 1 | new_code),
  947.              "(",*lzw_stringTable + 1,")","\t",
  948.              image(strg),"\t\t",
  949.              *lzw_stringTable," = ",image(old_strg || c))
  950.      if *lzw_stringTable < maxTableSize then
  951.            put(lzw_stringTable,old_strg || c)
  952.      else if tossTable >= 0 then {
  953.         lzw_stringTable := lzw_stringTable[1:charSize + 1]
  954.         lzw_recycles +:= 1
  955.         break
  956.         }
  957.      old_strg := strg
  958.      }) | break  # exit outer loop if this loop completed
  959.       }
  960.    decompress_read_code()
  961.    return maxTableSize
  962. end
  963.  
  964.  
  965. procedure decompress_read_code(inproc,stringTableSize,EOF)
  966.    static max,bits,buffer,bufferbits,lastSize
  967.    #
  968.    #  Initialize.
  969.    #
  970.    initial {
  971.       lastSize := 1000000
  972.       buffer := bufferbits := 0
  973.       }
  974.    #
  975.    #  Reinitialize if called with no arguments.
  976.    #
  977.    if /inproc then {
  978.       lastSize := 1000000
  979.       buffer := bufferbits := 0
  980.       return
  981.       }
  982.    #
  983.    #  Expand code size if necessary.
  984.    #
  985.    if stringTableSize < lastSize then {
  986.       max := 1
  987.       bits := 0
  988.       }
  989.    while stringTableSize > max do {
  990.       max *:= 2
  991.       bits +:= 1
  992.       }
  993.    #
  994.    #  Read in more data if necessary.
  995.    #
  996.    while bufferbits < bits do {
  997.       buffer := ior(ishift(buffer,8),ord(inproc())) |
  998.         stop("Premature end of file")
  999.       bufferbits +:= 8
  1000.       }
  1001.    #
  1002.    #  Extract code from buffer and return.
  1003.    #
  1004.    code := ishift(buffer,bits - bufferbits)
  1005.    buffer := ixor(buffer,ishift(code,bufferbits - bits))
  1006.    bufferbits -:= bits
  1007.    return EOF ~= code
  1008. end
  1009. ##########
  1010. dif.icn
  1011. ############################################################################
  1012. #
  1013. #    Name:    dif.icn
  1014. #
  1015. #    Title:    Diff engine
  1016. #
  1017. #    Author:    Robert J. Alexander
  1018. #
  1019. #    Date:    May 15, 1989
  1020. #
  1021. ############################################################################
  1022. #
  1023. #  The procedure dif() is a generator that produces a sequence of
  1024. #  differences between an arbitrary number of input streams.  Each result
  1025. #  is returned as a list of diff_recs, one for each input stream, with
  1026. #  each diff_rec containing a list of items that differ and their position
  1027. #  in the input stream.  The diff_rec type is declared as:
  1028. #
  1029. #        record diff_rec(pos,diffs)
  1030. #
  1031. #  Dif fails if there are no differences, i.e. it produces an empty
  1032. #  result sequence.
  1033. #
  1034. #  For example, if two input streams are:
  1035. #
  1036. #    a b c d e f g h
  1037. #    a b d e f i j
  1038. #
  1039. #  the output sequence would be:
  1040. #
  1041. #    [diff_rec(3,[c]),diff_rec(3,[])]
  1042. #    [diff_rec(7,[gh]),diff_rec(6,[i,j])
  1043. #
  1044. #  The arguments to dif() are:
  1045. #
  1046. #    stream        A list of data objects that represent input streams
  1047. #            from which dif will extract its input "records".
  1048. #            The elements can be of several different types which
  1049. #            result in different actions, as follows:
  1050. #
  1051. #               Type               Action
  1052. #            ===========    =============================
  1053. #            file        file is "read" to get records
  1054. #
  1055. #            co-expression    co-expression is activated to
  1056. #                    get records
  1057. #
  1058. #            list        records are "gotten" (get()) from
  1059. #                    the list
  1060. #
  1061. #            diff_proc    a record type defined in "dif" to
  1062. #                    allow a procedure (or procedures)
  1063. #                    suppled by dif's caller to be called
  1064. #                    to get records.  Diff_proc has two
  1065. #                    fields, the procedure to call and the
  1066. #                    argument to call it with.  Its
  1067. #                    definition looks like this:
  1068. #
  1069. #                       record diff_proc(proc,arg)
  1070. #            
  1071. #
  1072. #  Optional arguments:
  1073. #
  1074. #    compare        Item comparison procedure -- succeeds if
  1075. #            "equal", otherwise fails (default is the
  1076. #            identity "===" comparison).  The comparison
  1077. #            must allow for the fact that the eof object
  1078. #            (see next) might be an argument, and a pair of
  1079. #            eofs must compare equal.
  1080. #    eof        An object that is distinguishable from other
  1081. #            objects in the stream.  Default is &null.
  1082. #    group        A procedure that is called with the current number
  1083. #            of unmatched items as its argument.  It must
  1084. #            return the number of matching items required
  1085. #            for file synchronization to occur.  Default is
  1086. #            the formula Trunc((2.0 * Log(M)) + 2.0) where
  1087. #            M is the number of unmatched items.
  1088. #
  1089. ############################################################################
  1090.  
  1091. record diff_rec(pos,diffs)
  1092. record diff_proc(proc,arg)
  1093. record diff_file(stream,queue)
  1094.  
  1095. procedure dif(stream,compare,eof,group)
  1096.   local f,linenbr,line,difflist,gf,i,j,k,l,m,n,x,test,
  1097.     result,synclist,nsyncs,syncpoint
  1098.   /compare := proc("===",2); /group := groupfactor
  1099.   f := []; every put(f,diff_file(!stream,[]))
  1100.   linenbr := list(*stream,0); line := list(*stream); test := list(*stream)
  1101.   difflist := list(*stream); every !difflist := []
  1102.   repeat {
  1103.     repeat {
  1104.       every i := 1 to *stream do line[i] := diffread(f[i]) | eof
  1105.       if not (every x := !line do (x === eof) | break) then break break
  1106.       every !linenbr +:= 1
  1107.       if (every x := !line[2:0] do compare(x,line[1]) | break) then break
  1108.     }
  1109.     every i := 1 to *stream do difflist[i] := [line[i]]
  1110.     repeat {
  1111.       every i := 1 to *stream do put(difflist[i],diffread(f[i]) | eof)
  1112.       gf := group(*difflist[1])
  1113.       every i := 1 to *stream do test[i] := difflist[i][-gf:0]
  1114.       j := *difflist[1] - gf + 1
  1115.       synclist := list(*stream); every !synclist := list(*stream)
  1116.       every k := 1 to *stream do synclist[k][k] := j
  1117.       nsyncs := list(*stream,1)
  1118.       every i := 1 to j do {        # position to look at
  1119.         every k := 1 to *stream do {    # stream whose new stuff to compare
  1120.       every l := 1 to *stream do {    # streams comparing to at pos i
  1121.         if /synclist[k][l] then {
  1122.           m := i - 1
  1123.           if not every n := 1 to gf do {
  1124.             if not compare(test[k][n],difflist[l][m +:= 1]) then break
  1125.           } then {
  1126.             synclist[k][l] := i
  1127.             if (nsyncs[k] +:= 1) = *stream then break break break break
  1128.           }
  1129.         }
  1130.       }
  1131.     }
  1132.       }
  1133.     }
  1134.     synclist := synclist[k]; result := list(*stream)
  1135.     every i := 1 to *stream do {
  1136.       j := synclist[i]; while difflist[i][j -:= 1] === eof
  1137.       result[i] := diff_rec(linenbr[i],difflist[i][1:j + 1])
  1138.       f[i].queue := difflist[i][synclist[i] + gf:0] ||| f[i].queue
  1139.       linenbr[i] +:= synclist[i] + gf - 2
  1140.       difflist[i] := []
  1141.     }
  1142.     suspend result
  1143.   }
  1144. end
  1145.  
  1146. procedure diffread(f)
  1147.   local x
  1148.   return get(f.queue) | case type(x := f.stream) of {
  1149.     "file": read(x)
  1150.     "co-expression": @x
  1151.     "diff_proc": x.proc(x.arg)
  1152.     "list": get(x)
  1153.   }
  1154. end
  1155.  
  1156. procedure groupfactor(m)  # Compute: Trunc((2.0 * Log(m)) + 2.0)
  1157.   m := string(m)
  1158.   return 2 * *m + if m <<= "316227766"[1+:*m] then 0 else 1
  1159. end
  1160. ##########
  1161. escape.icn
  1162. ############################################################################
  1163. #
  1164. #    Name:    escape.icn
  1165. #
  1166. #    Title:    Interpret Icon literal escapes
  1167. #
  1168. #    Author:    William H. Mitchell, modified by Ralph E. Griswold
  1169. #
  1170. #    Date:    November 21, 1988
  1171. #
  1172. ############################################################################
  1173. #  
  1174. #  The procedure escape(s) produces a string in which Icon quoted
  1175. #  literal escape conventions in s are replaced by the corresponding
  1176. #  characters.  For example, escape("\\143\\141\\164") produces the
  1177. #  string "cat".
  1178. #  
  1179. ############################################################################
  1180.  
  1181. procedure escape(s)
  1182.    local ns, c
  1183.  
  1184.    ns := ""
  1185.    s ? {
  1186.       while ns ||:= tab(upto('\\')) do {
  1187.          move(1)
  1188.          ns ||:= case c := move(1 | 0) of {
  1189.             "b":  "\b"
  1190.             "d":  "\d"
  1191.             "e":  "\e"
  1192.             "f":  "\f"
  1193.             "l":  "\n"
  1194.             "n":  "\n"
  1195.             "r":  "\r"
  1196.             "t":  "\t"
  1197.             "v":  "\v"
  1198.             "'":  "'"
  1199.             "\"":  "\""
  1200.             "x":  hexcode()
  1201.             "^":  ctrlcode()
  1202.             !"01234567":  octcode()
  1203.             default:  c
  1204.             }
  1205.          }
  1206.       ns ||:= tab(0)
  1207.       }
  1208.    return ns
  1209. end
  1210.  
  1211. procedure hexcode()
  1212.    local i, s
  1213.    static cdigs
  1214.    initial cdigs := ~'0123456789ABCDEFabcdef'
  1215.    
  1216.    move(i := 2 | 1) ? s := tab(upto(cdigs) | 0)
  1217.    move(*s - i)
  1218.    return char("16r" || s)
  1219. end
  1220.  
  1221. procedure octcode()
  1222.    local i, s
  1223.    static cdigs
  1224.    initial cdigs := ~'01234567'
  1225.    
  1226.    move(-1)
  1227.    move(i := 3 | 2 | 1) ? s := tab(upto(cdigs) | 0)
  1228.    move(*s - i)
  1229.    return char("8r" || s)
  1230. end
  1231.  
  1232. procedure ctrlcode(s)
  1233.    return char(upto(map(move(1)),&lcase))
  1234. end
  1235.  
  1236. ##########
  1237. filename.icn
  1238. ############################################################################
  1239. #
  1240. #    Name:    filename.icn
  1241. #
  1242. #    Title:    Parse file names
  1243. #
  1244. #    Author:    Robert J. Alexander
  1245. #
  1246. #    Date:    December 5, 1989
  1247. #
  1248. ############################################################################
  1249. #
  1250. #  suffix() -- Parses a hierarchical file name, returning a 2-element
  1251. #  list:  [prefix,suffix].  E.g. suffix("/a/b/c.d") -> ["/a/b/c","d"]
  1252. #
  1253.  
  1254. procedure suffix(s,separator)
  1255.    local i
  1256.    /separator := "."
  1257.    i := *s + 1
  1258.    every i := find(separator,s)
  1259.    return [s[1:i],s[(*s >= i) + 1:0] | &null]
  1260. end
  1261.  
  1262. #
  1263. #  tail() -- Parses a hierarchical file name, returning a 2-element
  1264. #  list:  [head,tail].  E.g. tail("/a/b/c.d") -> ["/a/b","c.d"].
  1265. #
  1266.  
  1267. procedure tail(s,separator)
  1268.    local i
  1269.    /separator := "/"
  1270.    i := 0
  1271.    every i := find(separator,s)
  1272.    return [s[1:i + (i <= 1 | 0)],"" ~== s[i + 1:0] | &null]
  1273. end
  1274.  
  1275. #  components() -- Parses a hierarchical file name, returning a list of
  1276. #  all directory names in the file path, with the file name (tail) as
  1277. #  the last element.
  1278. #  E.g.  components("/a/b/c.d") -> ["/","a","b","c.d"].
  1279. #
  1280.  
  1281. procedure components(s,separator)
  1282.    local x,head
  1283.    /separator := "/"
  1284.    x := tail(s,separator)
  1285.    return case head := x[1] of {
  1286.       separator: [separator]
  1287.       "": []
  1288.       default: components(head)
  1289.       } ||| ([&null ~=== x[2]] | [])
  1290. end
  1291. ##########
  1292. fullimag.icn
  1293. ############################################################################
  1294. #
  1295. #    Name:    fullimage.icn
  1296. #
  1297. #    Title:    Produces complete image of structured data
  1298. #
  1299. #    Author:    Robert J. Alexander
  1300. #
  1301. #    Date:    December 5, 1989
  1302. #
  1303. ############################################################################
  1304. #
  1305. #  fullimage() -- enhanced image()-type procedure that outputs all data
  1306. #  contained in structured types.  The "level" argument tells it how far
  1307. #  to descend into nested structures (defaults to unlimited).
  1308. #
  1309.  
  1310. global fullimage_level,fullimage_maxlevel,fullimage_done,fullimage_used,
  1311.       fullimage_indent
  1312.  
  1313.  
  1314. procedure fullimage(x,indent,maxlevel)
  1315.    local tr,s,t
  1316.    #
  1317.    #  Initialize
  1318.    #
  1319.    tr := &trace ; &trace := 0    # turn off trace till we're done
  1320.    fullimage_level := 1
  1321.    fullimage_indent := indent
  1322.    fullimage_maxlevel := \maxlevel | 0
  1323.    fullimage_done := table()
  1324.    fullimage_used := set()
  1325.    #
  1326.    #  Call fullimage_() to do the work.
  1327.    #
  1328.    s := fullimage_(x)
  1329.    #
  1330.    #  Remove unreferenced tags from the result string, and even
  1331.    #  renumber them.
  1332.    #
  1333.    fullimage_done := table()
  1334.    s ? {
  1335.       s := ""
  1336.       while s ||:= tab(upto('\'"<')) do {
  1337.      case t := move(1) of {
  1338.         "\"" | "'": {
  1339.            s ||:= t
  1340.            while (s ||:= tab(find(t) + 1)) \ 1 & s[-2] ~== "\\"
  1341.            }
  1342.         "<": {
  1343.            t := +tab(find(">")) & move(1)
  1344.            if member(fullimage_used,t) then {
  1345.           /fullimage_done[t] := *fullimage_done + 1
  1346.           s ||:= "<" || fullimage_done[t] || ">"
  1347.           }
  1348.            }
  1349.         }
  1350.      }
  1351.       s ||:= tab(0)
  1352.       }
  1353.    #
  1354.    #  Clean up and return.
  1355.    #
  1356.    fullimage_done := fullimage_used := &null     # remove structures
  1357.    &trace := tr                  # restore &trace
  1358.    return s
  1359. end
  1360.  
  1361.  
  1362. procedure fullimage_(x,noindent)
  1363.    local s,t,tr
  1364.    t := type(x)
  1365.    s := case t of {
  1366.       "null" | "string" | "integer" | "real" | "co-expression" | "cset" |
  1367.       "file" | "procedure" | "external": image(x)
  1368.       default: fullimage_structure(x)
  1369.       }
  1370.    #
  1371.    #  Return the result.
  1372.    #
  1373.    return (
  1374.       if \fullimage_indent & not \noindent then
  1375.      "\n" || repl(fullimage_indent,fullimage_level - 1) || s
  1376.       else
  1377.         s
  1378.    )
  1379. end
  1380.  
  1381. procedure fullimage_structure(x)
  1382.    local sep,s,t,tag,y
  1383.    #
  1384.    #  If this structure has already been output, just output its tag.
  1385.    #
  1386.    if \(tag := fullimage_done[x]) then {
  1387.       insert(fullimage_used,tag)
  1388.       return "<" || tag || ">"
  1389.       }
  1390.    #
  1391.    #  If we've reached the max level, just output a normal image
  1392.    #  enclosed in braces to indicate end of the line.
  1393.    #
  1394.    if fullimage_level = fullimage_maxlevel then
  1395.      return "{" || image(x) || "}"
  1396.    #
  1397.    #  Output the structure in a style indicative of its type.
  1398.    #
  1399.    fullimage_level +:= 1
  1400.    fullimage_done[x] := tag := *fullimage_done + 1
  1401.    if (t := type(x)) == ("table" | "set") then x := sort(x)
  1402.    s := "<" || tag || ">" || if t == "list" then "[" else t || "("
  1403.    sep := ""
  1404.    if t == "table" then every y := !x do {
  1405.       s ||:= sep || fullimage_(y[1]) || "->" || fullimage_(y[2],"noindent")
  1406.       sep := ","
  1407.       }
  1408.    else every s ||:= sep || fullimage_(!x) do sep := ","
  1409.    fullimage_level -:= 1
  1410.    return s || if t == "list" then "]" else ")"
  1411. end
  1412. ##########
  1413. gcd.icn
  1414. ############################################################################
  1415. #
  1416. #    Name:    gcd.icn
  1417. #
  1418. #    Title:    Compute greatest cmmon denominator
  1419. #
  1420. #    Author:    Ralph E. Griswold
  1421. #
  1422. #    Date:    May 11, 1989
  1423. #
  1424. ############################################################################
  1425. #
  1426. #     This procedure computes the greatest common denominator of two
  1427. #  integers. If both are zero, it fails.
  1428. #
  1429. ############################################################################
  1430.  
  1431. procedure gcd(i,j)
  1432.    local r
  1433.  
  1434.    if i = j = 0 then fail
  1435.    if i = 0 then return j
  1436.    if j = 0 then return i
  1437.    i := abs(i)
  1438.    j := abs(j)
  1439.    repeat {
  1440.       r := i % j
  1441.       if r = 0 then return j
  1442.       i := j
  1443.       j := r
  1444.       }
  1445. end
  1446. ##########
  1447. gener.icn
  1448. ############################################################################
  1449. #
  1450. #    Name:    gener.icn
  1451. #
  1452. #    Title:    Generate miscellaneous sequences
  1453. #
  1454. #    Author:    Ralph E. Griswold
  1455. #
  1456. #    Date:    June 10, 1988
  1457. #
  1458. ############################################################################
  1459. #  
  1460. #  These procedures generate sequences of results.
  1461. #  
  1462. #       hex()          sequence of hexadecimal codes for numbers
  1463. #                      from 0 to 255
  1464. #  
  1465. #       label(s,i)     sequence of labels with prefix s starting at
  1466. #                      i
  1467. #  
  1468. #       octal()        sequence of octal codes for numbers from 0 to
  1469. #                      255
  1470. #  
  1471. #       star(s)        sequence consisting of the closure of s
  1472. #                      starting with the empty string and continuing
  1473. #                      in lexical order as given in s
  1474. #  
  1475. ############################################################################
  1476.  
  1477. procedure hex()
  1478.    suspend !"0123456789abcdef" || !"0123456789abcdef"
  1479. end
  1480.  
  1481. procedure label(s,i)
  1482.    suspend s || (i | (i +:= |1))
  1483. end
  1484.  
  1485. procedure octal()
  1486.    suspend (0 to 3) || (0 to 7) || (0 to 7)
  1487. end
  1488.  
  1489. procedure star(s)
  1490.    suspend "" | (star(s) || !s)
  1491. end
  1492. ##########
  1493. getopt.icn
  1494. ############################################################################
  1495. #
  1496. #    Name:    getopt.icn
  1497. #
  1498. #    Title:    Get command-line options
  1499. #
  1500. #    Author:    Robert J. Alexander
  1501. #
  1502. #    Date:    June 10, 1988
  1503. #
  1504. ############################################################################
  1505. #  
  1506. #     getopt(arg,optstring) -- Get command line options.
  1507. #  
  1508. #     This procedure analyzes the -options on the command line
  1509. #  invoking an Icon program. Its inputs are:
  1510. #  
  1511. #       arg         the argument list as passed to the main pro-
  1512. #                   cedure.
  1513. #  
  1514. #       optstring   a string of allowable option letters. If a
  1515. #                   letter is followed by ":" the corresponding
  1516. #                   option is assumed to be followed by a string of
  1517. #                   data, optionally separated from the letter by
  1518. #                   space. If instead of ":" the letter is followed
  1519. #                   by a "+", the parameter will converted to an
  1520. #                   integer; if a ".", converted to a real.  If opt-
  1521. #                   string is omitted any letter is assumed to be
  1522. #                   valid and require no data.
  1523. #  
  1524. #     It returns a list consisting of two items:
  1525. #  
  1526. #       [1]  a table of options specified. The entry values are the
  1527. #            specified option letters. The assigned values are the
  1528. #            data words following the options, if any, or 1 if the
  1529. #            option has no data. The table's default value is &null.
  1530. #  
  1531. #       [2]  a list of remaining parameters on the command line
  1532. #            (usually file names). A "-" which is not followed by a
  1533. #            letter is taken as a file name rather than an option.
  1534. #  
  1535. #     If an error is detected, stop() is called with an appropriate
  1536. #  error message. After calling getopt() the original argument list,
  1537. #  arg, is empty.
  1538. #  
  1539. ############################################################################
  1540.  
  1541. procedure getopt(arg,optstring)
  1542.    local x,i,c,otab,flist,o,p
  1543.    /optstring := string(&lcase ++ &ucase)
  1544.    otab := table()
  1545.    flist := []
  1546.    while x := get(arg) do
  1547.       x ? {
  1548.      if ="-"  & not pos(0) then
  1549.         while c := move(1) do
  1550.            if i := find(c,optstring) + 1 then
  1551.           otab[c] :=
  1552.              if any(':+.',o := optstring[i]) then {
  1553.             p := "" ~== tab(0) | get(arg) |
  1554.                   stop("No parameter following ",x)
  1555.             case o of {
  1556.                ":": p
  1557.                "+": integer(p) |
  1558.                      stop("-",c," needs numeric parameter")
  1559.                ".": real(p) |
  1560.                      stop("-",c," needs numeric parameter")
  1561.                }
  1562.             }
  1563.              else 1
  1564.            else stop("Unrecognized option: ",x)
  1565.      else put(flist,x)
  1566.       }
  1567.    return [otab,flist]
  1568. end
  1569.  
  1570. ##########
  1571. hexcvt.icn
  1572. ############################################################################
  1573. #
  1574. #    Name:    hexcvt.icn
  1575. #
  1576. #    Title:    Hexadecimal conversion
  1577. #
  1578. #    Author:    Robert J. Alexander
  1579. #
  1580. #    Date:    December 5, 1989
  1581. #
  1582. ############################################################################
  1583. #
  1584. #  hex() -- Converts string of hex digits into an integer.
  1585. #
  1586.  
  1587. procedure hex(s)
  1588.    local a,c
  1589.    a := 0
  1590.    every c := !map(s) do
  1591.      a := ior(find(c,"0123456789abcdef") - 1,ishift(a,4)) | fail
  1592.    return a
  1593. end
  1594.  
  1595. #
  1596. #  hexstring() -- Returns a string that is the hexadecimal
  1597. #  representation of the argument.
  1598. #
  1599.  
  1600. procedure hexstring(i,n)
  1601.    local s
  1602.    i := integer(i) | fail
  1603.    if i = 0 then s := "0"
  1604.    else {
  1605.       s := ""
  1606.       while i ~= 0 do {
  1607.      s := "0123456789ABCDEF"[iand(i,15) + 1] || s
  1608.            i := ishift(i,-4)
  1609.      }
  1610.       }
  1611.    s := right(s,\n,"0")
  1612.    return s
  1613. end
  1614.  
  1615.  
  1616. ##########
  1617. image.icn
  1618. ############################################################################
  1619. #
  1620. #    Name:    image.icn
  1621. #
  1622. #    Title:    Produce generalized image of Icon value
  1623. #
  1624. #    Author:    Michael Glass, Ralph E. Griswold, and David Yost
  1625. #
  1626. #    Date:    June 10, 1988
  1627. #
  1628. ############################################################################
  1629. #  
  1630. #  The procedure Image(x,style) produces a string image of the value x.
  1631. #  The value produced is a generalization of the value produced by
  1632. #  the Icon function image(x), providing detailed information about
  1633. #  structures. The value of style determines the formatting and
  1634. #  order of processing:
  1635. #
  1636. #     1   indented, with ] and ) at end of last item (default)
  1637. #     2   indented, with ] and ) on new line
  1638. #     3   puts the whole image on one line
  1639. #     4   as 3, but with structures expanded breadth-first instead of
  1640. #         depth-first as for other styles.
  1641. #  
  1642. ############################################################################
  1643. #
  1644. #     Tags are used to uniquely identify structures. A tag consists
  1645. #  of a letter identifying the type followed by an integer. The tag
  1646. #  letters are L for lists, R for records, S for sets, and T for
  1647. #  tables. The first time a structure is encountered, it is imaged
  1648. #  as the tag followed by a colon, followed by a representation of
  1649. #  the structure. If the same structure is encountered again, only
  1650. #  the tag is given.
  1651. #  
  1652. #     An example is
  1653. #  
  1654. #     a := ["x"]
  1655. #     push(a,a)
  1656. #     t := table()
  1657. #     push(a,t)
  1658. #     t[a] := t
  1659. #     t["x"] := []
  1660. #     t[t] := a
  1661. #     write(Image(t))
  1662. #  
  1663. #  which produces
  1664. #  
  1665. #  T1:[
  1666. #    "x"->L1:[],
  1667. #    L2:[
  1668. #      T1,
  1669. #      L2,
  1670. #      "x"]->T1,
  1671. #    T1->L2]
  1672. #
  1673. #  On the other hand, Image(t,3) produces
  1674. #
  1675. #     T1:["x"->L1:[],L2:[T1,L2,"x"]->T1,T1->L2]
  1676. #  
  1677. #  Note that a table is represented as a list of entry and assigned
  1678. #  values separated by ->.
  1679. #  
  1680. ############################################################################
  1681. #
  1682. #  Problem:
  1683. #
  1684. #     The procedure here really is a combination of an earlier version and
  1685. #  two modifications to it.  It should be re-organized to combine the
  1686. #  presentation style and order of expansion.
  1687. #
  1688. #  Bug:
  1689. #
  1690. #     Since the table of structures used in a call to Image is local to
  1691. #  that call, but the numbers used to generate unique tags are static to
  1692. #  the procedures that generate tags, the same structure gets different
  1693. #  tags in different calls of Image.
  1694. #
  1695. ############################################################################
  1696.  
  1697. procedure Image(x,style,done,depth,nonewline)
  1698.    local retval
  1699.  
  1700.    if style === 4 then return Imageb(x)    # breadth-first style
  1701.  
  1702.    /style := 1
  1703.    /done := table()
  1704.    if /depth then depth := 0
  1705.    else depth +:= 2
  1706.    if (style ~= 3 & depth > 0 & /nonewline) then
  1707.       retval := "\n" || repl(" ",depth)
  1708.    else retval := ""
  1709.    if match("record ",image(x)) then retval ||:= rimage(x,done,depth,style)
  1710.    else {
  1711.       retval ||:=
  1712.       case type(x) of {
  1713.      "list":  limage(x,done,depth,style)
  1714.      "table": timage(x,done,depth,style)
  1715.      "set":   simage(x,done,depth,style)
  1716.      default: image(x)
  1717.      }
  1718.    }
  1719.    depth -:= 2
  1720.    return retval
  1721. end
  1722.  
  1723. #  list image
  1724. #
  1725. procedure limage(a,done,depth,style)
  1726.    static i
  1727.    local s, tag
  1728.    initial i := 0
  1729.    if \done[a] then return done[a]
  1730.    done[a] := tag := "L" || (i +:= 1)
  1731.    if *a = 0 then s := tag || ":[]" else {
  1732.       s := tag || ":["
  1733.       every s ||:= Image(!a,style,done,depth) || ","
  1734.       s[-1] := endof("]",depth,style)
  1735.       }
  1736.    return s
  1737. end
  1738.  
  1739. #  record image
  1740. #
  1741. procedure rimage(x,done,depth,style)
  1742.    static i
  1743.    local s, tag
  1744.    initial i := 0
  1745.    s := image(x)
  1746.                     #  might be record constructor
  1747.    if match("record constructor ",s) then return s
  1748.    if \done[x] then return done[x]
  1749.    done[x] := tag := "R" || (i +:= 1)
  1750.    s ?:=  (="record " & (":" || (tab(upto('(') + 1))))
  1751.    if *x = 0 then s := tag || s || ")" else {
  1752.       s := tag || s
  1753.       every s ||:= Image(!x,style,done,depth) || ","
  1754.       s[-1] := endof(")",depth,style)
  1755.       }
  1756.    return s
  1757. end
  1758.  
  1759. # set image
  1760. #
  1761. procedure simage(S,done,depth,style)
  1762.    static i
  1763.    local s, tag
  1764.    initial i := 0
  1765.    if \done[S] then return done[S]
  1766.    done[S] := tag := "S" || (i +:= 1)
  1767.    if *S = 0 then s := tag || ":[]" else {
  1768.       s := tag || ":["
  1769.       every s ||:= Image(!S,style,done,depth) || ","
  1770.       s[-1] := endof("]",depth,style)
  1771.       }
  1772.    return s
  1773. end
  1774.  
  1775. #  table image
  1776. #
  1777. procedure timage(t,done,depth,style)
  1778.    static i
  1779.    local s, tag, a, a1
  1780.    initial i := 0
  1781.    if \done[t] then return done[t]
  1782.    done[t] := tag := "T" || (i +:= 1)
  1783.    if *t = 0 then s := tag || ":[]" else {
  1784.       a := sort(t,3)
  1785.       s := tag || ":["
  1786.       while s ||:= Image(get(a),style,done,depth) || "->" ||
  1787.            Image(get(a),style,done,depth,1) || ","
  1788.       s[-1] := endof("]",depth,style)
  1789.       }
  1790.    return s
  1791. end
  1792.  
  1793. procedure endof (s,depth,style)
  1794.    if style = 2 then return "\n" || repl(" ",depth) || "]"
  1795.    else return "]"
  1796. end
  1797.  
  1798. ############################################################################
  1799. #
  1800. #  What follows is the breadth-first expansion style
  1801. #
  1802.  
  1803. procedure Imageb(x, done, tags)
  1804.    local t
  1805.  
  1806.    if /done then {
  1807.       done := [set([])]  # done[1] actually done; done[2:0] pseudo-done
  1808.       tags := table()    # unique label for each structure
  1809.       }
  1810.  
  1811.    if member(!done, x) then return tags[x]
  1812.  
  1813.    t := tagit(x, tags)     # The tag for x if structure; image(x) if not
  1814.  
  1815.    if /tags[x] then
  1816.       return t                       # Wasn't a structure
  1817.    else {
  1818.       insert(done[1], x)             # Mark x as actually done
  1819.       return case t[1] of {
  1820.          "R":  rimageb(x, done, tags)     # record
  1821.          "L":  limageb(x, done, tags)     # list
  1822.          "T":  timageb(x, done, tags)     # table
  1823.          "S":  simageb(x, done, tags)     # set
  1824.          }
  1825.       }
  1826. end
  1827.  
  1828.  
  1829. #  Create and return a tag for a structure, and save it in tags[x].
  1830. #  Otherwise, if x is not a structure, return image(x).
  1831. #
  1832. procedure tagit(x, tags)
  1833.    local ximage, t, prefix
  1834.    static serial
  1835.    initial serial := table(0)
  1836.  
  1837.    if \tags[x] then return tags[x]
  1838.  
  1839.    if match("record constructor ", ximage := image(x)) then
  1840.       return ximage                # record constructor
  1841.  
  1842.    if match("record ", t := ximage) |
  1843.       ((t := type(x)) == ("list" | "table" | "set")) then {
  1844.          prefix := map(t[1], "rlts", "RLTS")
  1845.          return tags[x] := prefix || (serial[prefix] +:=1)
  1846.          }                        # structure
  1847.  
  1848.    else return ximage             # anything else
  1849. end
  1850.  
  1851.  
  1852. #  Every component sub-structure of the current structure gets tagged
  1853. #  and added to a pseudo-done set.
  1854. #
  1855. procedure defer_image(a, done, tags)
  1856.    local x, t
  1857.    t := set([])
  1858.    every x := !a do {
  1859.       tagit(x, tags)
  1860.       if \tags[x] then insert(t, x)  # if x actually is a sub-structure
  1861.       }
  1862.    put(done, t)
  1863.    return
  1864. end
  1865.  
  1866.  
  1867. #  Create the image of every component of the current structure.
  1868. #  Sub-structures get deleted from the local pseudo-done set before
  1869. #  we actually create their image.
  1870. #
  1871. procedure do_image(a, done, tags)
  1872.    local x, t
  1873.    t := done[-1]
  1874.    suspend (delete(t, x := !a), Imageb(x, done, tags))
  1875. end
  1876.  
  1877.  
  1878. #  list image
  1879. #
  1880. procedure limageb(a, done, tags)
  1881.    local s
  1882.    if *a = 0 then s := tags[a] || ":[]" else {
  1883.       defer_image(a, done, tags)
  1884.       s := tags[a] || ":["
  1885.       every s ||:= do_image(a, done, tags) || ","
  1886.       s[-1] := "]"
  1887.       pull(done)
  1888.       }
  1889.    return s
  1890. end
  1891.  
  1892. #  record image
  1893. #
  1894. procedure rimageb(x, done, tags)
  1895.    local s
  1896.    s := image(x)
  1897.    s ?:=  (="record " & (":" || (tab(upto('(') + 1))))
  1898.    if *x = 0 then s := tags[x] || s || ")" else {
  1899.       defer_image(x, done, tags)
  1900.       s := tags[x] || s
  1901.       every s ||:= do_image(x, done, tags) || ","
  1902.       s[-1] := ")"
  1903.       pull(done)
  1904.       }
  1905.    return s
  1906. end
  1907.  
  1908. # set image
  1909. #
  1910. procedure simageb(S, done, tags)
  1911.    local s
  1912.    if *S = 0 then s := tags[S] || ":[]" else {
  1913.       defer_image(S, done, tags)
  1914.       s := tags[S] || ":["
  1915.       every s ||:= do_image(S, done, tags) || ","
  1916.       s[-1] := "]"
  1917.       pull(done)
  1918.       }
  1919.    return s
  1920. end
  1921.  
  1922. #  table image
  1923. #
  1924. procedure timageb(t, done, tags)
  1925.    local s, a
  1926.    if *t = 0 then s := tags[t] || ":[]" else {
  1927.       a := sort(t,3)
  1928.       defer_image(a, done, tags)
  1929.       s := tags[t] || ":["
  1930.       while s ||:= do_image([get(a)], done, tags) || "->" ||
  1931.                    do_image([get(a)], done, tags) || ","
  1932.       s[-1] := "]"
  1933.       pull(done)
  1934.       }
  1935.    return s
  1936. end
  1937. ##########
  1938. largint.icn
  1939. ############################################################################
  1940. #
  1941. #    Name:    largint.icn
  1942. #
  1943. #    Title:    Large integer arithmetic
  1944. #
  1945. #    Author:    Paul Abrahams and Ralph E. Griswold
  1946. #
  1947. #    Date:    May 11, 1989
  1948. #
  1949. ############################################################################
  1950. #
  1951. #     These procedures perform addition, multiplication, and exponentiation
  1952. #  On integers given as strings of numerals:
  1953. #
  1954. #        add(i,j)      sum of i and j
  1955. #
  1956. #        mpy(i,j)      product of i and j
  1957. #
  1958. #        raise(i,j)    i to the power j
  1959. #
  1960. #  Note:
  1961. #
  1962. #     The techniques used by add and mpy are different from those used by
  1963. #  raise.  These procedures are combined here for organizational reasons.
  1964. #  The procedures add and mpy are adapted from the Icon language book.
  1965. #  The procedure raise was written by Paul Abrahams.
  1966. #
  1967. ############################################################################
  1968.  
  1969. record largint(coeff,nextl)
  1970.  
  1971. global base, segsize
  1972.  
  1973. # Add i and j
  1974. #
  1975. procedure add(i,j)
  1976.  
  1977.    return lstring(addl(large(i),large(j)))
  1978.  
  1979. end
  1980.  
  1981. # Multiply i and j
  1982. #
  1983. procedure mpy(i,j)
  1984.  
  1985.    return lstring(mpyl(large(i),large(j)))
  1986.  
  1987. end
  1988.  
  1989. # Raise i to power j
  1990. #
  1991. procedure raise(i,j)
  1992.  
  1993.      return rstring(ipower(i,binrep(j)))
  1994.  
  1995. end
  1996.  
  1997. procedure addl(g1,g2,carry)
  1998.    local sum
  1999.    /carry := largint(0)    # default carry
  2000.    if /g1 & /g2 then return if carry.coeff ~= 0 then carry
  2001.    else &null
  2002.    if /g1 then return addl(carry,g2)
  2003.    if /g2 then return addl(g1,carry)
  2004.    sum := g1.coeff + g2.coeff + carry.coeff
  2005.    carry := largint(sum / base)
  2006.    return largint(sum % base,addl(g1.nextl,g2.nextl,carry))
  2007. end
  2008.  
  2009. procedure large(s)
  2010.    initial {
  2011.       base := 10000
  2012.       segsize := *base - 1
  2013.       }
  2014.  
  2015.    if *s <= segsize then return largint(integer(s))
  2016.    else return largint(right(s,segsize),
  2017.       large(left(s,*s - segsize)))
  2018. end
  2019.  
  2020. procedure lstring(g)
  2021.    local s
  2022.  
  2023.    if /g.nextl then s := g.coeff
  2024.    else s := lstring(g.nextl) || right(g.coeff,segsize,"0")
  2025.    s ?:= (tab(upto(~'0') | -1) & tab(0))
  2026.    return s
  2027. end
  2028.  
  2029. procedure mpyl(g1,g2)
  2030.    local prod
  2031.    if /(g1 | g2) then return &null    # zero product
  2032.    prod := g1.coeff * g2.coeff
  2033.    return largint(prod % base,
  2034.       addl(mpyl(largint(g1.coeff),g2.nextl),mpyl(g1.nextl,g2),
  2035.       largint(prod / base)))
  2036. end
  2037.  
  2038. # Compute the binary representation of n (as a string)
  2039. #
  2040. procedure binrep(n)
  2041.     local retval
  2042.     retval := ""
  2043.     while n > 0 do {
  2044.         retval := n % 2 || retval
  2045.         n /:= 2
  2046.         }
  2047.     return retval
  2048. end
  2049.  
  2050. # Compute a to the ipower bbits, where bbits is a bit string.
  2051. # The result is a list of coefficients for the polynomial a(i)*k^i,
  2052. # least significant values first, with k=10000 and zero trailing coefficient
  2053. # deleted.
  2054. #
  2055. procedure ipower(a, bbits)
  2056.     local b, m1, retval
  2057.     m1 := (if a >= 10000 then [a % 10000, a / 10000] else [a])
  2058.     retval := [1]
  2059.     every b := !bbits do {
  2060.         (retval := product(retval, retval)) | fail
  2061.         if b == "1" then
  2062.             (retval := product(retval, m1)) | fail
  2063.         }
  2064.     return retval
  2065. end
  2066.  
  2067. # Compute a*b as a polynomial in the same form as for ipower.
  2068. # a and b are also polynomials in this form.
  2069. #
  2070. procedure product(a,b)
  2071.     local i, j, k, retval, x
  2072.     if *a + *b > 5001 then
  2073.         fail
  2074.     retval := list(*a + *b, 0)
  2075.     every i := 1 to *a do
  2076.         every j := 1 to *b do {
  2077.             k := i + j - 1
  2078.             retval[k] +:= a[i] * b[j]
  2079.             while (x := retval[k]) >= 10000 do {
  2080.                 retval[k + 1] +:= x / 10000
  2081.                 retval[k] %:= 10000
  2082.                 k +:= 1
  2083.             }   }
  2084.     every i := *retval to 1 by -1 do
  2085.         if retval[i] > 0 then
  2086.             return retval[1+:i]
  2087.     return retval[1+:i]
  2088. end
  2089.  
  2090. procedure rstring(n)
  2091.     local ds, i, j, k, result
  2092.  
  2093.     ds := ""
  2094.     every k := *n to 1 by -1 do
  2095.         ds ||:= right(n[k], 4, "0")
  2096.     ds ?:= (tab(many("0")), tab(0))
  2097.     ds := repl("0", 4 - (*ds - 1) % 5) || ds
  2098.  
  2099.     result := ""
  2100.     every i := 1 to *ds by 50 do {
  2101.         k := *ds > i + 45 | *ds
  2102.         every j := i to k by 5 do {
  2103.        ds
  2104.            result ||:= ds[j+:5]
  2105.        }
  2106.         }
  2107.    result ? {
  2108.       tab(many('0'))
  2109.       return tab(0)
  2110.       }
  2111. end
  2112. ##########
  2113. lmap.icn
  2114. ############################################################################
  2115. #
  2116. #    Name:    lmap.icn
  2117. #
  2118. #    Title:    Map list elements
  2119. #
  2120. #    Author:    Ralph E. Griswold
  2121. #
  2122. #    Date:    June 10, 1988
  2123. #
  2124. ############################################################################
  2125. #  
  2126. #  The procedure lmap(a1,a2,a3) maps elements of a1 according to a2
  2127. #  and a3.  This procedure is the analog for lists of the built-in
  2128. #  string-mapping function map(s1,s2,s3). Elements in a1 that are
  2129. #  the same as elements in a2 are mapped into the corresponding ele-
  2130. #  ments of a3. For example, given the lists
  2131. #  
  2132. #     a1 := [1,2,3,4]
  2133. #     a2 := [4,3,2,1]
  2134. #     a3 := ["a","b","c","d"]
  2135. #  
  2136. #  then
  2137. #  
  2138. #     lmap(a1,a2,a3)
  2139. #  
  2140. #  produces a new list
  2141. #  
  2142. #     ["d","c","b","a"]
  2143. #  
  2144. #     Lists that are mapped can have any kinds of elements. The
  2145. #  operation
  2146. #  
  2147. #     x === y
  2148. #  
  2149. #  is used to determine if elements x and y are equivalent.
  2150. #  
  2151. #     All cases in lmap are handled as they are in map, except that
  2152. #  no defaults are provided for omitted arguments. As with map, lmap
  2153. #  can be used for transposition as well as substitution.
  2154. #  
  2155. #  Warning:
  2156. #
  2157. #     If lmap is called with the same lists a2 and a3 as in
  2158. #  the immediately preceding call, the same mapping is performed,
  2159. #  even if the values in a2 and a3 have been changed. This improves
  2160. #  performance, but it may cause unexpected effects.
  2161. #  
  2162. #     This ``caching'' of the mapping table based on a2 and a3
  2163. #  can be easily removed to avoid this potential problem.
  2164. #  
  2165. ############################################################################
  2166.  
  2167. procedure lmap(a1,a2,a3)
  2168.    static lmem2, lmem3, lmaptbl, tdefault
  2169.    local i, a
  2170.  
  2171.    initial tdefault := []
  2172.  
  2173.    if type(a := a1 | a2 | a3) ~== "list" then runerr(108,a)
  2174.    if *a2 ~= *a3 then runerr(208,a2)
  2175.  
  2176.    a1 := copy(a1)
  2177.  
  2178.    if not(lmem2 === a2 & lmem3 === a3) then {    # if an argument is new, rebuild
  2179.       lmem2 := a2                # save for future reference
  2180.       lmem3 := a3
  2181.       lmaptbl := table(tdefault)        # new mapping table
  2182.       every i := 1 to *a2 do            # build the map
  2183.          lmaptbl[a2[i]] := a3[i]
  2184.       }
  2185.    every i := 1 to *a1 do            # map the values
  2186.       a1[i] := (tdefault ~=== lmaptbl[a1[i]])
  2187.    return a1
  2188. end
  2189. ##########
  2190. math.icn
  2191. ############################################################################
  2192. #
  2193. #    Name:    math.icn
  2194. #
  2195. #    Title:    Perform mathematical computations
  2196. #
  2197. #    Author:    George D. Yee
  2198. #
  2199. #    Date:    June 10, 1988
  2200. #
  2201. ############################################################################
  2202. #  
  2203. #  The following procedures compute standard trigonometric func-
  2204. #  tions.  The arguments are in radians.
  2205. #  
  2206. #       sin(x)      sine of x
  2207. #  
  2208. #       cos(x)      cosine of x
  2209. #  
  2210. #       tan(x)      tangent of x
  2211. #  
  2212. #       asin(x)     arc sine of x in the range -pi/2 to pi/2
  2213. #  
  2214. #       acos(x)     arc cosine of x in the range 0 to pi
  2215. #  
  2216. #       atan(x)     arc tangent of x in the range -pi/2 to pi/2
  2217. #  
  2218. #       atan2(y,x)  arc tangent of x/y in the range -pi to pi
  2219. #  
  2220. #  The following procedures convert from degrees to radians and con-
  2221. #  versely:
  2222. #  
  2223. #       dtor(d)     radian equivalent of d
  2224. #  
  2225. #       rtod(r)     degree equivalent of r
  2226. #  
  2227. #  The following additional procedures are available:
  2228. #  
  2229. #       sqrt(x)     square root of x
  2230. #  
  2231. #       exp(x)      exponential function of x
  2232. #  
  2233. #       log(x)      natural logarithm of x
  2234. #  
  2235. #       log10(x)    base-10 logarithm of x
  2236. #  
  2237. #       floor(x)    largest integer not greater than x
  2238. #  
  2239. #       ceil(x)     smallest integer nor less than x
  2240. #  
  2241. #  Failure Conditions: asin(x) and acos(x) fail if the absolute
  2242. #  value of x is greater than one. sqrt(x), log(x), and log10(x)
  2243. #  fail if x is less than zero.
  2244. #  
  2245. ############################################################################
  2246.  
  2247. procedure sin(x)
  2248.    return _sinus(numeric(x),0)
  2249. end
  2250.  
  2251. procedure cos(x)
  2252.    return _sinus(abs(numeric(x)),1)
  2253. end
  2254.  
  2255. procedure tan(x)
  2256.    return sin(x) / (0.0 ~= cos(x))
  2257. end
  2258.  
  2259. # atan returns the value of the arctangent of its
  2260. # argument in the range [-pi/2,pi/2].
  2261. procedure atan(x)
  2262.    if numeric(x) then
  2263.       return if x > 0.0 then _satan(x) else -_satan(-x)
  2264. end
  2265.  
  2266. # atan2 returns the arctangent of y/x
  2267. # in the range [-pi,pi].
  2268. procedure atan2(y,x)
  2269.    local r
  2270.    static pi
  2271.    initial pi := 3.141592653589793238462643
  2272.    return if numeric(y) & numeric(x) then {
  2273.       if x > 0.0 then
  2274.          atan(y/x)
  2275.       else if x < 0.0 then {
  2276.          r := pi - atan(abs(y/x))
  2277.          if y >= 0.0 then r else -r
  2278.          }
  2279.       else if x = y = 0.0 then
  2280.          0.0         # special value if both x and y are zero
  2281.       else
  2282.          if y >= 0.0 then pi/2.0 else -pi/2.0
  2283.       }
  2284. end
  2285.  
  2286. procedure asin(x)
  2287.    if abs(numeric(x)) <= 1.0 then
  2288.       return atan2(x, (1.0-(x^2))^0.5)
  2289. end
  2290.  
  2291. procedure acos(x)
  2292.    return 1.570796326794896619231e0 - asin(x)
  2293. end
  2294.  
  2295. procedure dtor(deg)
  2296.    return numeric(deg)/57.29577951308232
  2297. end
  2298.  
  2299. procedure rtod(rad)
  2300.    return numeric(rad)*57.29577951308232
  2301. end
  2302.  
  2303. procedure sqrt(x)
  2304.     return (0.0 <= numeric(x)) ^ 0.5
  2305. end
  2306.  
  2307. procedure floor(x)
  2308.    return if numeric(x) then
  2309.       if x>=0.0 | real(x)=integer(x) then integer(x) else -integer(-x+1)
  2310. end
  2311.  
  2312. procedure ceil(x)
  2313.    return -floor(-numeric(x))
  2314. end
  2315.  
  2316. procedure log(x)
  2317.    local z, zsq, ex
  2318.    static log2, sqrto2, p0, p1, p2, p3, q0, q1, q2
  2319.    initial {
  2320.       # The coefficients are #2705 from Hart & Cheney. (19.38D)
  2321.       log2   :=  0.693147180559945309e0
  2322.       sqrto2 :=  0.707106781186547524e0
  2323.       p0     := -0.240139179559210510e2
  2324.       p1     :=  0.309572928215376501e2
  2325.       p2     := -0.963769093368686593e1
  2326.       p3     :=  0.421087371217979714e0
  2327.       q0     := -0.120069589779605255e2
  2328.       q1     :=  0.194809660700889731e2
  2329.       q2     := -0.891110902798312337e1
  2330.       }
  2331.    if numeric(x) > 0.0 then {
  2332.       ex := 0
  2333.       while x >= 1.0 do {
  2334.          x /:= 2.0
  2335.          ex +:= 1
  2336.          }
  2337.       while x < 0.5 do {
  2338.          x *:= 2.0
  2339.          ex -:= 1
  2340.          }
  2341.       if x < sqrto2 then {
  2342.          x *:= 2.0
  2343.          ex -:= 1
  2344.          }
  2345.       return ((((p3*(zsq:=(z:=(x-1.0)/(x+1.0))^2)+p2)*zsq+p1)*zsq+p0)/
  2346.              (((1.0*zsq+q2)*zsq+q1)*zsq+q0))*z+ex*log2
  2347.       }
  2348. end
  2349.  
  2350. procedure exp(x)
  2351.    return 2.718281828459045235360287 ^ numeric(x)
  2352. end
  2353.  
  2354. procedure log10(x)
  2355.    return log(x)/2.30258509299404568402
  2356. end
  2357.  
  2358. procedure _sinus(x,quad)
  2359.    local ysq, y, k
  2360.    static twoopi, p0, p1, p2, p3, p4, q0, q1, q2, q3
  2361.    initial {
  2362.       # Coefficients are #3370 from Hart & Cheney (18.80D).
  2363.       twoopi :=  0.63661977236758134308
  2364.       p0     :=  0.1357884097877375669092680e8
  2365.       p1     := -0.4942908100902844161158627e7
  2366.       p2     :=  0.4401030535375266501944918e6
  2367.       p3     := -0.1384727249982452873054457e5
  2368.       p4     :=  0.1459688406665768722226959e3
  2369.       q0     :=  0.8644558652922534429915149e7
  2370.       q1     :=  0.4081792252343299749395779e6
  2371.       q2     :=  0.9463096101538208180571257e4
  2372.       q3     :=  0.1326534908786136358911494e3
  2373.       }
  2374.    if x < 0.0 then {
  2375.       x := -x
  2376.       quad +:= 2
  2377.       }
  2378.    y := (x *:= twoopi) - (k := integer(x))
  2379.    if (quad := (quad + k) % 4) = (1|3) then
  2380.       y := 1.0 - y
  2381.    if quad > 1 then
  2382.       y := -y
  2383.    return (((((p4*(ysq:=y^2)+p3)*ysq+p2)*ysq+p1)*ysq+p0)*y) /
  2384.            ((((ysq+q3)*ysq+q2)*ysq+q1)*ysq+q0)
  2385. end
  2386.  
  2387. procedure _satan(x)
  2388.    static sq2p1,sq2m1,pio2,pio4
  2389.    initial {
  2390.       sq2p1 := 2.414213562373095048802e0
  2391.       sq2m1 := 0.414213562373095048802e0
  2392.       pio2  := 1.570796326794896619231e0
  2393.       pio4  := 0.785398163397448309615e0
  2394.       }
  2395.    return if x < sq2m1 then
  2396.              _xatan(x)
  2397.           else if x > sq2p1 then
  2398.              pio2 - _xatan(1.0/x)
  2399.           else
  2400.              pio4 + _xatan((x-1.0)/(x+1.0))
  2401. end
  2402.  
  2403. procedure _xatan(x)
  2404.    local xsq
  2405.    static p4,p3,p2,p1,p0,q4,q3,q2,q1,q0
  2406.    initial {
  2407.       # coefficients are #5077 from Hart & Cheney. (19.56D)
  2408.       p4    := 0.161536412982230228262e2
  2409.       p3    := 0.26842548195503973794141e3
  2410.       p2    := 0.11530293515404850115428136e4
  2411.       p1    := 0.178040631643319697105464587e4
  2412.       p0    := 0.89678597403663861959987488e3
  2413.       q4    := 0.5895697050844462222791e2
  2414.       q3    := 0.536265374031215315104235e3
  2415.       q2    := 0.16667838148816337184521798e4
  2416.       q1    := 0.207933497444540981287275926e4
  2417.       q0    := 0.89678597403663861962481162e3
  2418.       }
  2419.    return x * ((((p4*(xsq:=x^2)+p3)*xsq+p2)*xsq+p1)*xsq+p0) /
  2420.           (((((xsq+q4)*xsq+q3)*xsq+q2)*xsq+q1)*xsq+q0)
  2421. end
  2422. ##########
  2423. morse.icn
  2424. ############################################################################
  2425. #
  2426. #    Name:    morse.icn
  2427. #
  2428. #    Title:    Convert string to Morse code
  2429. #
  2430. #    Author:    Ralph E. Griswold
  2431. #
  2432. #    Date:    June 10, 1988
  2433. #
  2434. ############################################################################
  2435. #
  2436. #     This procedure converts the string s to its Morse code equivalent.
  2437. #
  2438. ############################################################################
  2439.  
  2440. procedure morse(s)
  2441.    local i, t, c, x
  2442.    static morsemeander, morseindex
  2443.  
  2444.    initial {
  2445.       morsemeander := "....------.----..---.-.---...--.--._
  2446.          -..--..-.--....-.-.-...-..-....."
  2447.       morseindex :=   "TMOT09TTT1T8TT2GQTTTJTZ7T3NKYTTCTTT_
  2448.          TDXTTWPTB64EARTTLTVTIUFTSH5"
  2449.       }
  2450.  
  2451.    x := ""
  2452.    every c := !map(s,&lcase,&ucase) do
  2453.       if not(i := upto(c,morseindex)) then x := x || "    "
  2454.          else {
  2455.             t := morsemeander[i+:6]
  2456.             x := x || t[upto("-",t)+1:0] || " "
  2457.             }
  2458.    return x
  2459. end
  2460. ##########
  2461. ngrams.icn
  2462. ############################################################################
  2463. #
  2464. #    Name:    ngrams.icn
  2465. #
  2466. #    Title:    Generate n-grams
  2467. #
  2468. #    Author:    Ralph E. Griswold
  2469. #
  2470. #    Date:    June 10, 1988
  2471. #
  2472. ############################################################################
  2473. #
  2474. #     The procedure ngrams(file,n,c,t) generates a tabulation of the n-grams
  2475. #  in the specified file.  If c is non-null, it is used as the set of
  2476. #  characters from which n-grams are taken (other characters break n-grams).
  2477. #  The default for c is the upper- and lowercase letters.  If t is non-null,
  2478. #  the tabulation is given in order of frequency; otherwise in alphabetical
  2479. #  order of n-grams.
  2480. #
  2481. #  Note:
  2482. #
  2483. #     The n-grams are kept in a table within the procedure and all n-grams
  2484. #  are processed before the tabulation is generated. Consequently, this
  2485. #  procedure is unsuitable if there are very many different n-grams.
  2486. #
  2487. ############################################################################
  2488.  
  2489. procedure ngrams(f,i,c,t)
  2490.    local line, grams, a, count
  2491.  
  2492.    if not (integer(i) > 0) then stop("invalid ngrams specification")
  2493.    if type(f) ~== "file" then stop("invalid file specification")
  2494.    /c := &lcase || &ucase
  2495.    if not (c := cset(c)) then stop("invalid cset specification")
  2496.    grams := table(0)
  2497.    line := ""
  2498.    while line ||:= reads(f,1000) do
  2499.       line ? while tab(upto(c)) do
  2500.          (tab(many(c)) \ 1) ? while grams[move(i)] +:= 1 do
  2501.             move(-i + 1)
  2502.    if /t then {
  2503.       a := sort(grams,4)
  2504.       while count := pull(a) do
  2505.          suspend pull(a) || right(count,8)
  2506.          }
  2507.    else {
  2508.       a := sort(grams,3)
  2509.       suspend |(get(a) || right(get(a),8))
  2510.       }
  2511. end
  2512. ##########
  2513. numbers.icn
  2514. ############################################################################
  2515. #
  2516. #    Name:    numbers.icn
  2517. #
  2518. #    Title:    Format and convert numbers
  2519. #
  2520. #    Author:    Ralph E. Griswold and Tim Korb
  2521. #
  2522. #    Date:    June 10, 1988
  2523. #
  2524. ############################################################################
  2525. #
  2526. #     These procedures format numbers in various ways:
  2527. #
  2528. #     commas(s)          inserts commas in s to separate digits into groups of
  2529. #                        three.
  2530. #
  2531. #     roman(i)           converts s to Roman numerals.
  2532. #
  2533. #     spell(i)           spells out i in English.
  2534. #
  2535. #     fpform(i,j,m,l,d)  formats i / j as a real (floating-point) number.
  2536. #                        If m is non-null, the result is multiplied by m. The
  2537. #                        default for m is 100 (giving a percentage). If l
  2538. #                        is non-null, the resulting string is l characters
  2539. #                        long (minimum 6); otherwise is is 7 characters long.
  2540. #                        If d is non-null, d digits to the right of the
  2541. #                        decimal point are produced; otherwise 3.
  2542. #
  2543. ############################################################################
  2544. #
  2545. #  Bug:
  2546. #
  2547. #     The procedure fpform() is not well conceived. It produces bogus
  2548. #  results in some cases if the formatting specifications are not
  2549. #  appropriate.
  2550. #
  2551. ############################################################################
  2552.  
  2553. procedure commas(n)
  2554.    if *n < 4 then return n
  2555.    else return commas(left(n,*n - 3)) || map(",123","123",right(n,3))
  2556. end
  2557.  
  2558. #  This procedure is based on a SNOBOL4 function written by Jim Gimpel.
  2559. #
  2560. procedure roman(n)
  2561.    local arabic, result
  2562.    static equiv
  2563.    initial equiv := ["","I","II","III","IV","V","VI","VII","VIII","IX"]
  2564.    integer(n) > 0 | fail
  2565.    result := ""
  2566.    every arabic := !n do
  2567.       result := map(result,"IVXLCDM","XLCDM**") || equiv[arabic + 1]
  2568.    if find("*",result) then fail else return result
  2569. end
  2570.  
  2571. procedure spell(n)
  2572.    local m
  2573.    n := integer(n) | stop(image(n)," is not an integer")
  2574.    if n <= 12 then return {
  2575.       "0zero,1one,2two,3three,4four,5five,6six,7seven,8eight,_
  2576.          9nine,10ten,11eleven,12twelve," ? {
  2577.             tab(find(n))
  2578.             move(*n)
  2579.             tab(upto(","))
  2580.             }
  2581.       }
  2582.    else if n <= 19 then return {
  2583.       spell(n[2] || "0") ?
  2584.          (if ="for" then "four" else tab(find("ty"))) || "teen"
  2585.       }
  2586.    else if n <= 99 then return {
  2587.       "2twen,3thir,4for,5fif,6six,7seven,8eigh,9nine," ? {
  2588.          tab(upto(n[1]))
  2589.          move(1)
  2590.          tab(upto(",")) || "ty" ||
  2591.             if n[2] ~= 0 then "-" || spell(n[2])
  2592.          }
  2593.       }
  2594.    else if n <= 999 then return {
  2595.       spell(n[1]) || " hundred" ||
  2596.          (if (m := n[2:0]) ~= 0 then " and " || spell(m) else "")
  2597.       }
  2598.    else if n <= 999999 then return {
  2599.       spell(n[1:-3]) || " thousand" ||
  2600.          (if (m := n[2:0]) ~= 0 then " and " || spell(m) else "")
  2601.       }
  2602.    else if n <= 999999999 then return {
  2603.       spell(n[1:-6]) || " million" ||
  2604.          (if (m := n[2:0]) ~= 0 then " and " || spell(m) else "")
  2605.       }
  2606.    else fail
  2607. end
  2608.  
  2609. procedure fpform(i,j,m,l,d)
  2610.    local r, int, dec
  2611.  
  2612.    /l := 7
  2613.    /d := 3
  2614.  
  2615.    if (l < 6) | ((l - d) < 3) then
  2616.       stop("cannot format according to specifications")
  2617.    r := real(i) / j
  2618.    r *:= (\m | 100)
  2619.    if r < 0.001 then return repl(" ",l - 5) || "0.000"
  2620.    string(r) ? {
  2621.       int := tab(upto('.'))
  2622.       move(1)
  2623.       dec := tab(0)
  2624.       }
  2625.    return right(int,l - d - 1) || "." || left(dec,d,"0")
  2626. end
  2627. ##########
  2628. object.icn
  2629. ############################################################################
  2630. #
  2631. #    Name:    object.icn
  2632. #
  2633. #    Title:    Encode and decode Icon values
  2634. #
  2635. #    Author:    Kurt A. Welgehausen
  2636. #
  2637. #    Date:    June 10, 1988
  2638. #
  2639. ############################################################################
  2640. #
  2641. #     These procedures provide a way of storing Icon values as strings in
  2642. #  files and reconstructing them.
  2643. #
  2644. #     putobj(obj, f) stores the Icon data object obj in the file f; it returns
  2645. #  the object stored.  The returned value is usually not of interest, so a
  2646. #  typical call is putobj(x, f).
  2647. #  
  2648. #     The file f must be open for writing; if f is null, it defaults to &output.
  2649. #  
  2650. #     Strings are stored as single lines in the file, with unprintable
  2651. #  characters stored as the escape sequences produced by image().  
  2652. #  
  2653. #  Integers, reals, and csets are writen to the file as single lines of the
  2654. #  form "%"type(obj)string(obj), for example 
  2655. #  
  2656. #      123 is stored as "%integer123"
  2657. #      123.4 is stored as "%real123.4"
  2658. #      '123' is stored as "%cset123"
  2659. #  
  2660. #     As in strings, unprintable characters in csets are stored as the escape 
  2661. #  sequences produced by image().
  2662. #  
  2663. #     Procedures, functions,and record constructors are stored as strings of the
  2664. #  form #  "%proc"procedure-name.  For example, the function write() is stored
  2665. #  as "%procwrite".
  2666. #  
  2667. #     Files are stored as strings of the form "#file("file-name")".  For
  2668. #  example, if f is a file variable connected to the disk file example.fil,
  2669. #  then f is stored by putobj() as "#file(example.fil)".  
  2670. #  
  2671. #     Co-expressions are stored as the string "#co-expr".
  2672. #  
  2673. #     Null objects are stored as lines containing only "%".
  2674. #  
  2675. #     Structured objects are stored as single lines of the form
  2676. #  "%"type(obj)"("n")", where n is the size of obj, followed by the n
  2677. #  components of obj (tables are stored as their default assigned values
  2678. #  followed by sorted lists of index and #  assigned values).  putobj() calls
  2679. #  itself recursively to store the components.  For example,
  2680. #
  2681. #        ["aaa", ["bbb", 'edc'], 16rfff, open("somefile"), create write(1 to 3)]
  2682. #
  2683. #  is stored as 
  2684. #  
  2685. #          %list(5)
  2686. #          aaa
  2687. #          %list(2)
  2688. #          bbb
  2689. #          %csetcde
  2690. #          %integer4095
  2691. #          #file(somefile)
  2692. #          #co-expr
  2693. #  
  2694. #  
  2695. #     getobj(f) retrieves an Icon data object from the file f; it returns the 
  2696. #  object.  A typical call is "x := getobj(f)".
  2697. #  
  2698. #     The file f must be open for reading; if f is null, it defaults to &input.
  2699. #  
  2700. #     The object to be retrieved must have been stored in the format used by
  2701. #  putobj().
  2702. #  
  2703. #     No attempt is made to reconstruct file variables or co-expressions; only
  2704. #  the descriptive string is returned.  It is up to the programmer to open the
  2705. #  file or recreate the co-expression.  For all other types, the actual Icon
  2706. #  object is returned.  
  2707. #  
  2708. ############################################################################
  2709. #
  2710. #  Warning:
  2711. #
  2712. #     putobj(x) calls itself to process structures in x.  If there is a
  2713. #  loop in the structure, putobj(x) gets stack overflow due to excessive
  2714. #  recursion.
  2715. #
  2716. #     Objects stored with putobj() and then retrieved with getobj() may
  2717. #  not be identical to the original objects.  For example, if x is an Icon
  2718. #  structure and y := [x, x], then y[1] and y[2] are identical; but 
  2719. #  after storing and retrieving y, y[1] and y[2] will be copies of each 
  2720. #  other but will not be the same object.
  2721. #
  2722. #     To  avoid these problems, use codeobj.icn instead of object.icn.
  2723. #  
  2724. ############################################################################
  2725. #
  2726. #  Links: escape
  2727. #
  2728. #  See also: codeobj.icn
  2729. #
  2730. ############################################################################
  2731.  
  2732. link  escape
  2733.  
  2734. global  HDRSYM, ESCSYM
  2735.  
  2736. procedure getobj(f)
  2737.     local  line, buf, otype, size
  2738.     initial  { /HDRSYM:= "%"; /ESCSYM:= "@" }   # these defs must be the same as
  2739.                                                 # those in putobj()
  2740.     /f:= &input
  2741.     (line:= (read(f) | fail)) ? {
  2742.         case move(1) | "" of {
  2743.             ESCSYM: buf:= escape(tab(0))
  2744.             HDRSYM: {
  2745.              (otype:= tab(upto('(')), move(1), size:= integer(tab(upto(')')))) |
  2746.                (buf:=
  2747.                   (=("integer" | "real" | "cset" | "proc"))(escape(tab(0)))) |
  2748.                  &null    # must succeed
  2749.             }
  2750.             "&": buf:= case tab(0) of {
  2751.                     "input": &input ;  "output": &output ;  "errout": &errout
  2752.                     "cset": &cset ;  "ascii": &ascii
  2753.                     "lcase": &lcase ;  "ucase": &ucase
  2754.                  }
  2755.             default: buf:= escape(line)
  2756.         }
  2757.     }
  2758.     \size & {       # not-null size means a structured type
  2759.         ((otype == "table") & (buf:= getobj(f))) |
  2760.             ((otype == "set") & (buf:= []))
  2761.         buf:= otype(buf)
  2762.         case otype of {
  2763.             "list": every 1 to size do put(buf, getobj(f))
  2764.             "table": every 1 to size do buf[getobj(f)]:= getobj(f)
  2765.             "set": every 1 to size do insert(buf, getobj(f))
  2766.             default: every buf[1 to size]:= getobj(f)
  2767.         }
  2768.     }
  2769.     return  buf
  2770. end
  2771. # Put object <obj> on file <f>; <f> must be open for writing.
  2772. # If <f> is not specified, output goes to &output.
  2773.  
  2774. global  HDRSYM, ESCSYM
  2775.  
  2776. procedure putobj(obj, f)
  2777.     local  t, buf
  2778.     initial  { /HDRSYM:= "%"; /ESCSYM:= "@" }  # these defs must be the same as
  2779.                                                # those in getobj()
  2780.     /f:= &output
  2781.     case t:= type(obj) of {
  2782.         "string": {
  2783.             match(ESCSYM | HDRSYM | "&", obj) & (obj:= ESCSYM || obj)
  2784.             write(f, image(obj)[2:-1])
  2785.         }
  2786.         "integer" | "real": write(f, HDRSYM, t, obj)
  2787.         "cset": {
  2788.             buf:= image(obj)
  2789.             (match("&", buf) & write(f, buf)) | write(f, HDRSYM, t, buf[2:-1])
  2790.         }
  2791.         "null": write(f, HDRSYM)
  2792.         "procedure": image(obj) ? {
  2793.             =("procedure " | "function " | "record constructor ")
  2794.             write(f, HDRSYM, "proc", tab(0))
  2795.         }
  2796.         "file": image(obj) ? write(f, (="&" | "#") || tab(0))
  2797.         "co-expression": write(f, "#", t[1:8])
  2798.         default: {
  2799.             write(f, HDRSYM, t, "(", *obj, ")")
  2800.             (t == "table", putobj(obj[[]], f), buf:= sort(obj, 3)) | (buf:= obj)
  2801.             (*buf > 0) & every putobj(!buf, f)
  2802.         }
  2803.     }
  2804.     return  obj
  2805. end
  2806. ##########
  2807. options.icn
  2808. ############################################################################
  2809. #
  2810. #    Name:        options.icn
  2811. #
  2812. #    Title:        Get command-line options
  2813. #
  2814. #    Authors:    Robert J. Alexander, June 10, 1988
  2815. #            Gregg M. Townsend, November 9, 1989
  2816. #
  2817. ############################################################################
  2818. #  
  2819. #     options(arg,optstring) -- Get command line options.
  2820. #  
  2821. #     This procedure analyzes the -options on the command line
  2822. #  invoking an Icon program.  The inputs are:
  2823. #  
  2824. #       arg         the argument list as passed to the main procedure.
  2825. #
  2826. #       optstring   a string of allowable option letters. If a
  2827. #                   letter is followed by ":" the corresponding
  2828. #                   option is assumed to be followed by a string of
  2829. #                   data, optionally separated from the letter by
  2830. #                   space. If instead of ":" the letter is followed
  2831. #                   by a "+", the parameter will converted to an
  2832. #                   integer; if a ".", converted to a real.  If opt-
  2833. #                   string is omitted any letter is assumed to be
  2834. #                   valid and require no data.
  2835. #  
  2836. #     It returns a table containing the options that were specified.
  2837. #  The keys are the specified option letters. The assigned values are
  2838. #  the data words following the options, if any, or 1 if the option
  2839. #  has no data. The table's default value is &null.
  2840. #  
  2841. #     If an error is detected, stop() is called with an appropriate
  2842. #  error message.
  2843. #
  2844. #     Options may be freely interspersed with non-option arguments.
  2845. #  An argument of "-" is treated as a non-option.  The special argument
  2846. #  "--" terminates option processing.  Non-option arguments are returned
  2847. #  in the original argument list for interpretation by the caller.
  2848. #  
  2849. ############################################################################
  2850.  
  2851. procedure options(arg,optstring)
  2852.    local x,i,c,otab,flist,o,p
  2853.    /optstring := string(&lcase ++ &ucase)
  2854.    otab := table()
  2855.    flist := []
  2856.    while x := get(arg) do
  2857.       x ? {
  2858.          if ="-" & not pos(0) then {
  2859.             if ="-" & pos(0) then break
  2860.             while c := move(1) do
  2861.                if i := find(c,optstring) + 1 then
  2862.                   otab[c] :=
  2863.                      if any(':+.',o := optstring[i]) then {
  2864.                         p := "" ~== tab(0) | get(arg) |
  2865.                               stop("No parameter following -",c)
  2866.                         case o of {
  2867.                            ":": p
  2868.                            "+": integer(p) |
  2869.                                  stop("-",c," needs numeric parameter")
  2870.                            ".": real(p) |
  2871.                                  stop("-",c," needs numeric parameter")
  2872.                            }
  2873.                         }
  2874.                      else 1
  2875.                else stop("Unrecognized option: -",c)
  2876.          }
  2877.          else put(flist,x)
  2878.       }
  2879.    while push(arg,pull(flist))
  2880.    return otab
  2881. end
  2882. ##########
  2883. patterns.icn
  2884. ############################################################################
  2885. #
  2886. #    Name:    patterns.icn
  2887. #
  2888. #    Title:    Pattern matching in the style of SNOBOL4
  2889. #
  2890. #    Author:    Ralph E. Griswold
  2891. #
  2892. #    Date:    June 10, 1988
  2893. #
  2894. ############################################################################
  2895. #  
  2896. #  These procedures provide procedural equivalents for most SNOBOL4
  2897. #  patterns and some extensions. 
  2898. #
  2899. #  Procedures and their pattern equivalents are:
  2900. #  
  2901. #       Any(s)         ANY(S)
  2902. #  
  2903. #       Arb()          ARB
  2904. #  
  2905. #       Arbno(p)       ARBNO(P)
  2906. #  
  2907. #       Arbx(i)        ARB(I)
  2908. #  
  2909. #       Bal()          BAL
  2910. #  
  2911. #       Break(s)       BREAK(S)
  2912. #  
  2913. #       Breakx(s)      BREAKX(S)
  2914. #  
  2915. #       Cat(p1,p2)     P1 P2
  2916. #  
  2917. #       Discard(p)     /P
  2918. #  
  2919. #       Exog(s)        \S
  2920. #  
  2921. #       Find(s)        FIND(S)
  2922. #  
  2923. #       Len(i)         LEN(I)
  2924. #  
  2925. #       Limit(p,i)     P \ i
  2926. #  
  2927. #       Locate(p)      LOCATE(P)
  2928. #  
  2929. #       Marb()         longest-first ARB
  2930. #  
  2931. #       Notany(s)      NOTANY(S)
  2932. #  
  2933. #       Pos(i)         POS(I)
  2934. #  
  2935. #       Replace(p,s)   P   S
  2936. #  
  2937. #       Rpos(i)        RPOS(I)
  2938. #  
  2939. #       Rtab(i)        RTAB(I)
  2940. #  
  2941. #       Span(s)        SPAN(S)
  2942. #  
  2943. #       String(s)      S
  2944. #  
  2945. #       Succeed()      SUCCEED
  2946. #  
  2947. #       Tab(i)         TAB(I)
  2948. #  
  2949. #       Xform(f,p)     F(P)
  2950. #  
  2951. #     The following procedures relate to the application and control
  2952. #  of pattern matching:
  2953. #  
  2954. #       Apply(s,p)     S ? P
  2955. #  
  2956. #       Mode()         anchored or unanchored matching (see Anchor
  2957. #                      and Float)
  2958. #  
  2959. #       Anchor()       &ANCHOR = 1  if Mode := Anchor
  2960. #  
  2961. #       Float()        &ANCHOR = 0  if Mode := Float
  2962. #  
  2963. #  In addition to the procedures above, the following expressions
  2964. #  can be used:
  2965. #  
  2966. #       p1() | p2()    P1 | P2
  2967. #  
  2968. #       v <- p()       P . V  (approximate)
  2969. #  
  2970. #       v := p()       P $ V  (approximate)
  2971. #  
  2972. #       fail           FAIL
  2973. #  
  2974. #       =s             S  (in place of String(s))
  2975. #  
  2976. #       p1() || p2()   P1 P2  (in place of Cat(p1,p2))
  2977. #  
  2978. #  Using this system, most SNOBOL4 patterns can be satisfactorily
  2979. #  transliterated into Icon procedures and expressions. For example,
  2980. #  the pattern
  2981. #  
  2982. #          SPAN("0123456789") $ N "H" LEN(*N) $ LIT
  2983. #  
  2984. #  can be transliterated into
  2985. #  
  2986. #          (n <- Span('0123456789')) || ="H" ||
  2987. #             (lit <- Len(n))
  2988. #  
  2989. #  Concatenation of components is necessary to preserve the
  2990. #  pattern-matching properties of SNOBOL4.
  2991. #  
  2992. #  Caveats: Simulating SNOBOL4 pattern matching using the procedures
  2993. #  above is inefficient.
  2994. #  
  2995. ############################################################################
  2996.  
  2997. global Mode, Float
  2998.  
  2999. procedure Anchor()            #  &ANCHOR = 1
  3000.    suspend ""
  3001. end
  3002.  
  3003. procedure Any(s)            #  ANY(S)
  3004.    suspend tab(any(s))
  3005. end
  3006.  
  3007. procedure Apply(s,p)            #  S ? P
  3008.    local tsubject, tpos, value
  3009.    initial {
  3010.       Float := Arb
  3011.       /Mode := Float            #  &ANCHOR = 0 if not already set
  3012.       }
  3013.    suspend (
  3014.       (tsubject := &subject) &
  3015.       (tpos := &pos) &
  3016.       (&subject <- s) &
  3017.       (&pos <- 1) &
  3018.       (Mode() & (value := p())) &
  3019.       (&pos <- tpos) &            # to restore on backtracking
  3020.       (&subject <- tsubject) &        # note this sets &pos
  3021.       (&pos <- tpos) &            # to restore on evaluation
  3022.       value
  3023.       )
  3024. end
  3025.  
  3026. procedure Arb()                #  ARB
  3027.    suspend tab(&pos to *&subject + 1)
  3028. end
  3029.  
  3030. procedure Arbno(p)            #  ARBNO(P)
  3031.    suspend "" | (p() || Arbno(p))
  3032. end
  3033.  
  3034. procedure Arbx(i)            #  ARB(I)
  3035.    suspend tab(&pos to *&subject + 1 by i)
  3036. end
  3037.  
  3038. procedure Bal()                #  BAL
  3039.    suspend Bbal() || Arbno(Bbal)
  3040. end
  3041.  
  3042. procedure Bbal()            #  used by Bal()
  3043.    suspend (="(" || Arbno(Bbal) || =")") | Notany("()")
  3044. end
  3045.  
  3046. procedure Break(s)            #  BREAK(S)
  3047.    suspend tab(upto(s) \ 1)
  3048. end
  3049.  
  3050. procedure Breakx(s)            #  BREAKX(S)
  3051.    suspend tab(upto(s))
  3052. end
  3053.  
  3054. procedure Cat(p1,p2)            #  P1 P2
  3055.    suspend p1() || p2()
  3056. end
  3057.  
  3058. procedure Discard(p)            #  /P
  3059.    suspend p() & ""
  3060. end
  3061.  
  3062. procedure Exog(s)            #  \S
  3063.    suspend s
  3064. end
  3065.  
  3066. procedure Find(s)            #  FIND(S)
  3067.    suspend tab(find(s) + 1)
  3068. end
  3069.  
  3070. procedure Len(i)            #  LEN(I)
  3071.    suspend move(i)
  3072. end
  3073.  
  3074. procedure Limit(p,i)            #  P \ i
  3075.    local j
  3076.    j := &pos
  3077.    suspend p() \ i
  3078.    &pos := j
  3079. end
  3080.  
  3081. procedure Locate(p)            #  LOCATE(P)
  3082.    suspend tab(&pos to *&subject + 1) & p()
  3083. end
  3084.  
  3085. procedure Marb()            # max-first ARB
  3086.    suspend tab(*&subject + 1 to &pos by -1)
  3087. end
  3088.  
  3089. procedure Notany(s)            #  NOTANY(S)
  3090.    suspend tab(any(~s))
  3091. end
  3092.  
  3093. procedure Pos(i)            #  POS(I)
  3094.    suspend pos(i + 1) & ""
  3095. end
  3096.  
  3097. procedure Replace(p,s)            #  P = S
  3098.    suspend p() & s
  3099. end
  3100.  
  3101. procedure Rpos(i)            #  RPOS(I)
  3102.    suspend pos(-i) & ""
  3103. end
  3104.  
  3105. procedure Rtab(i)            #  RTAB(I)
  3106.    suspend tab(-i)
  3107. end
  3108.  
  3109. procedure Span(s)            #  SPAN(S)
  3110.    suspend tab(many(s))
  3111. end
  3112.  
  3113. procedure String(s)            #  S
  3114.    suspend =s
  3115. end
  3116.  
  3117. procedure Succeed()            #  SUCCEED
  3118.    suspend |""
  3119. end
  3120.  
  3121. procedure Tab(i)            #  TAB(I)
  3122.    suspend tab(i + 1)
  3123. end
  3124.  
  3125. procedure Xform(f,p)            #  F(P)
  3126.    suspend f(p())
  3127. end
  3128. ##########
  3129. patword.icn
  3130. ############################################################################
  3131. #
  3132. #    Name:    patword.icn
  3133. #
  3134. #    Title:    Letter patterns in words
  3135. #
  3136. #    Author:    Kenneth Walker
  3137. #
  3138. #    Date:    June 10, 1988
  3139. #
  3140. ############################################################################
  3141. #
  3142. #     The procedure patword(s) returns a letter pattern in which each
  3143. #  different character in s is assigned a letter.  For example,
  3144. #  patword("structural") returns "abcdedbcfg".
  3145. #
  3146. ############################################################################
  3147.  
  3148. procedure patword(s)
  3149.     local numbering, orderS, orderset, patlbls
  3150.     static labels, revnum
  3151.  
  3152.     initial {
  3153.     labels := &lcase || &lcase
  3154.     revnum := reverse(&cset)
  3155.     }
  3156.  
  3157. # First map each character of s into another character, such that the
  3158. # the new characters are in increasing order left to right (note that
  3159. # the map function chooses the rightmost character of its second
  3160. # argument, so things must be reversed.
  3161. #
  3162. # Next map each of these new characters into contiguous letters.
  3163.  
  3164.     numbering := revnum[1 : *s + 1] | stop("word too long")
  3165.     orderS := map(s, reverse(s), numbering)
  3166.     orderset := string(cset(orderS))
  3167.     patlbls := labels[1 : *orderset + 1] | stop("too many characters")
  3168.     return map(orderS, orderset, patlbls)
  3169. end
  3170. ##########
  3171. pdae.icn
  3172. ############################################################################
  3173. #
  3174. #    Name:    pdae.icn
  3175. #
  3176. #    Title:    Programmer-defined argument evaluation
  3177. #
  3178. #    Author:    Ralph E. Griswold
  3179. #
  3180. #    Date:    June 10, 1988
  3181. #
  3182. ############################################################################
  3183. #  
  3184. #  These procedures use co-expressions to model the built-in argu-
  3185. #  ment evaluation regime of Icon and also provide new ones.
  3186. #  
  3187. #       Allpar{e1,e2, ...}   parallel evaluation with last result
  3188. #                            used for short sequences
  3189. #  
  3190. #       Extract{e1,e2, ...}  extract results of even-numbered argu-
  3191. #                            ments according to odd-numbered values
  3192. #  
  3193. #       Lifo{e1,e2, ...}     models standard Icon ``lifo'' evalua-
  3194. #                            tion
  3195. #  
  3196. #       Parallel{e1,e2, ...} parallel evaluation terminating on
  3197. #                            shortest sequence
  3198. #  
  3199. #       Reverse{e1,e2, ...}  left-to-right reversal of lifo evalua-
  3200. #                            tion
  3201. #  
  3202. #       Rotate{e1,e2, ...}   parallel evaluation with shorter
  3203. #                            sequences re-evaluated
  3204. #  
  3205. #       Simple{e1,e2, ...}   simple evaluation with only success or
  3206. #                            failure
  3207. #
  3208. #  Comments:
  3209. #
  3210. #     Because of the handling of the scope of local identif-
  3211. #  iers in co-expressions, expressions in programmer-defined argu-
  3212. #  ment evaluation regimes cannot communicate through local identif-
  3213. #  iers.  Some constructions, such as break and return, cannot be
  3214. #  used in arguments to programmer-defined argument evaluation
  3215. #  regimes.
  3216. #  
  3217. #     At most 10 arguments can be used in the invocation of a
  3218. #  programmer-defined argument evaluation regime. This limit can be
  3219. #  increased by modifying Call, a utility procedure that is
  3220. #  included. (The variable-argument facility in Version 7 of Icon should
  3221. #  be used to overcome this restriction.)
  3222. #  
  3223. ############################################################################
  3224.  
  3225. procedure Allpar(a)
  3226.    local i, x, done
  3227.    x := list(*a)
  3228.    done := list(*a,1)
  3229.    every i := 1 to *a do x[i] := @a[i] | fail
  3230.    repeat {
  3231.       suspend Call(x)
  3232.       every i := 1 to *a do
  3233.          if done[i] = 1 then ((x[i] := @a[i]) | (done[i] := 0))
  3234.       if not(!done = 1) then fail
  3235.        }
  3236. end
  3237.  
  3238. procedure Call(a)
  3239.    suspend case *a of {
  3240.       1 : a[1]()
  3241.       2 : a[1](a[2])
  3242.       3 : a[1](a[2],a[3])
  3243.       4 : a[1](a[2],a[3],a[4])
  3244.       5 : a[1](a[2],a[3],a[4],a[5])
  3245.       6 : a[1](a[2],a[3],a[4],a[5],a[6])
  3246.       7 : a[1](a[2],a[3],a[4],a[5],a[6],a[7])
  3247.       8 : a[1](a[2],a[3],a[4],a[5],a[6],a[7],a[8])
  3248.       9 : a[1](a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9])
  3249.       10 : a[1](a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10])
  3250.       default :  stop("Call : too many args.")
  3251.       }
  3252. end
  3253.  
  3254. procedure Extract(a)
  3255.    local i, j, n, x
  3256.    x := list(*a/2)
  3257.    repeat {
  3258.       i := 1
  3259.       while i < *a do {
  3260.          n := @a[i] | fail
  3261.          every 1 to n do
  3262.             x[(i + 1)/2] := @a[i + 1] | fail
  3263.          a[i + 1] := ^a[i + 1]
  3264.          i +:= 2
  3265.          }
  3266.       suspend Call(x)
  3267.       }
  3268. end
  3269.  
  3270. procedure Lifo(a)
  3271.    local i, x, ptr
  3272.    x := list(*a)
  3273.    ptr := 1
  3274.    repeat {
  3275.       repeat
  3276.          if x[ptr] := @a[ptr]
  3277.          then {
  3278.             ptr +:= 1
  3279.             (a[ptr] := ^a[ptr]) |
  3280.             break
  3281.             }
  3282.          else if (ptr -:=  1) = 0
  3283.               then fail
  3284.       suspend Call(x)
  3285.       ptr := *a
  3286.       }
  3287. end
  3288.  
  3289. procedure Parallel(a)
  3290.    local i, x
  3291.    x := list(*a)
  3292.    repeat {
  3293.       every i := 1 to *a do
  3294.          x[i] := @a[i] | fail
  3295.       suspend Call(x)
  3296.       }
  3297. end
  3298.  
  3299. procedure Reverse(a)
  3300.    local i, x, ptr
  3301.    x := list(*a)
  3302.    ptr := *a
  3303.    repeat {
  3304.       repeat
  3305.          if x[ptr] := @a[ptr]
  3306.          then {
  3307.             ptr -:= 1
  3308.             (a[ptr] := ^a[ptr]) |
  3309.             break
  3310.             }
  3311.          else if (ptr +:= 1) > *a
  3312.               then fail
  3313.       suspend Call(x)
  3314.       ptr := 1
  3315.       }
  3316. end
  3317.  
  3318. procedure Rotate(a)
  3319.    local i, x, done
  3320.    x := list(*a)
  3321.    done := list(*a,1)
  3322.    every i := 1 to *a do x[i] := @a[i] | fail
  3323.    repeat {
  3324.       suspend Call(x)
  3325.       every i := 1 to *a do
  3326.          if not(x[i] := @a[i]) then {
  3327.             done[i] := 0
  3328.             if !done = 1 then {
  3329.                a[i] := ^a[i]
  3330.                x[i] := @a[i] | fail
  3331.                }
  3332.             else fail
  3333.             }
  3334.         }
  3335. end
  3336.  
  3337. procedure Simple(a)
  3338.    local i, x
  3339.    x := list(*a)
  3340.    every i := 1 to *a do
  3341.       x[i] := @a[i] | fail
  3342.    return Call(x)
  3343. end
  3344.  
  3345. ##########
  3346. pdco.icn
  3347. ############################################################################
  3348. #
  3349. #    Name:    pdco.icn
  3350. #
  3351. #    Title:    Programm-defined control operations
  3352. #
  3353. #    Author:    Ralph E. Griswold
  3354. #
  3355. #    Date:    November 16, 1989
  3356. #
  3357. ############################################################################
  3358. #  
  3359. #  These procedures use co-expressions to used to model the built-in
  3360. #  control structures of Icon and also provide new ones.
  3361. #  
  3362. #       Alt{e1,e2}         models e1 | e2
  3363. #  
  3364. #       Colseq{e1,e2, ...} produces results of e1, e2, ... alter-
  3365. #                          nately
  3366. #  
  3367. #       Comseq{e1,e2}      compares result sequences of e1 and e2
  3368. #  
  3369. #       Cond{e1,e2, ...}   models the generalized Lisp conditional
  3370. #  
  3371. #       Every{e1,e2}       models every e1 do e2
  3372. #  
  3373. #       Galt{e1,e2, ...}   models generalized alternation: e1 | e2 |
  3374. #                          ...
  3375. #  
  3376. #       Lcond{e1,e2, ...}  models the Lisp conditional
  3377. #  
  3378. #       Limit{e1,e2}       models e1 \ e2
  3379. #  
  3380. #       Ranseq{e1,e2, ...} produces results of e1, e2, ... at random
  3381. #  
  3382. #       Repalt{e}          models |e
  3383. #  
  3384. #       Resume{e1,e2,e3}   models every e1 \ e2 do e3
  3385. #  
  3386. #       Select{e1,e2}      produces results from e1 by position
  3387. #                          according to e2
  3388. #  
  3389. #  Comments:
  3390. #
  3391. #     Because of the handling of the scope of local identif-
  3392. #  iers in co-expressions, expressions in programmer-defined control
  3393. #  operations cannot communicate through local identifiers.  Some
  3394. #  constructions, such as break and return, cannot be used in argu-
  3395. #  ments to programmer-defined control operations.
  3396. #  
  3397. ############################################################################
  3398.  
  3399. procedure Alt(L)
  3400.    local x
  3401.    while x := @L[1] do suspend x
  3402.    while x := @L[2] do suspend x
  3403. end
  3404.  
  3405. procedure Colseq(L)
  3406.    suspend |@!L
  3407. end
  3408.  
  3409. procedure Comseq(L)
  3410.    local x1, x2
  3411.    while x1 := @L[1] do
  3412.       (x1 === @L[2]) | fail
  3413.    if @L[2] then fail else return *L[1]
  3414. end
  3415.  
  3416. procedure Cond(L)
  3417.    local i, x
  3418.    every i := 1 to *l do
  3419.       if x := @L[i] then {
  3420.          suspend x
  3421.          suspend |@L[i]
  3422.          fail
  3423.          }
  3424. end
  3425.  
  3426. procedure Every(L)
  3427.    while @L[1] do @^L[2]
  3428. end
  3429.  
  3430. procedure Galt(L)
  3431.    local C
  3432.    every C := !L do suspend |@C
  3433. end
  3434.  
  3435. procedure Lcond(L)
  3436.    local i
  3437.    every i := 1 to *L by 2 do
  3438.       if @L[i] then {
  3439.          suspend |@L[i + 1]
  3440.          fail
  3441.          }
  3442. end
  3443.  
  3444. procedure Limit(L)
  3445.    local i, x
  3446.    while i := @L[2] do {
  3447.       every 1 to i do
  3448.          if x := @L[1] then suspend x
  3449.          else break
  3450.       L[1] := ^L[1]
  3451.       }
  3452. end
  3453.  
  3454. procedure Ranseq(L)
  3455.    local x
  3456.    while x := @?L do suspend x
  3457. end
  3458.  
  3459. procedure Repalt(L)
  3460.    local x
  3461.    repeat {
  3462.       while x := @L[1] do suspend x
  3463.       if *L[1] = 0 then fail
  3464.       else L[1] := ^L[1]
  3465.       }
  3466. end
  3467.  
  3468. procedure Resume(L)
  3469.    local i
  3470.    while i := @L[2] do {
  3471.       L[1] := ^L[1]
  3472.       every 1 to i do if @L[1] then @^L[3] else break
  3473.       }
  3474. end
  3475.  
  3476. procedure Select(L)
  3477.    local i, j, x
  3478.    j := 0
  3479.    while i := @L[2] do {
  3480.       while j < i do
  3481.          if x := @L[1] then j +:= 1
  3482.          else fail
  3483.       if i = j then suspend x
  3484.       else stop("selection sequence error")
  3485.       }
  3486. end
  3487. ##########
  3488. permute.icn
  3489. ############################################################################
  3490. #
  3491. #    Name:    permute.icn
  3492. #
  3493. #    Title:    Permutations, combinations, and such
  3494. #
  3495. #    Author:    Ralph E. Griswold and Kurt A. Welgehausen
  3496. #
  3497. #    Date:    May 9, 1989
  3498. #
  3499. ############################################################################
  3500. #
  3501. #     These procedures produce various rearrangements of strings of
  3502. #  characters:
  3503. #
  3504. #     comb(s,i)       generates the combinations characters from s taken
  3505. #                     i at a time.
  3506. #
  3507. #     permute(s)      generates all the permutations of the string s.
  3508. #
  3509. #     menader(s,n)    produces a "meandering" string which contains all
  3510. #                     n-tuples of characters of s.
  3511. #
  3512. #     csort(s)        produces the characters of s in lexical order.
  3513. #
  3514. #     ochars(s)       produces the unique characters of s in the order they
  3515. #                     first appear in s.
  3516. #
  3517. #     schars(s)       produces the unique characters of s in lexical order.
  3518. #
  3519. ############################################################################
  3520.  
  3521. procedure comb(s,i)
  3522.    local j
  3523.  
  3524.    if i < 1 then fail
  3525.    suspend if i = 1 then !s
  3526.       else s[j := 1 to *s - i + 1] || comb(s[j + 1:0],i - 1)
  3527. end
  3528.  
  3529. procedure permute(s)
  3530.    local i
  3531.  
  3532.    if *s = 0 then return ""
  3533.    suspend s[i := 1 to *s] || permute(s[1:i] || s[i+1:0])
  3534. end
  3535.  
  3536. procedure meander(alpha,n)
  3537.    local result, t, i, c, k
  3538.  
  3539.    i := k := *alpha
  3540.    t := n - 1
  3541.    result := repl(alpha[1],t)
  3542.    while c := alpha[i] do {
  3543.       if find(result[-t:0] || c,result)
  3544.       then i -:= 1
  3545.       else {
  3546.          result ||:= c
  3547.          i := k
  3548.          }
  3549.       }
  3550.    return result
  3551. end
  3552.  
  3553. procedure csort(s)
  3554.    local c, s1
  3555.  
  3556.    s1 := ""
  3557.    every c := !cset(s) do
  3558.       every find(c,s) do
  3559.          s1 ||:= c
  3560.    return s1
  3561. end
  3562.  
  3563. procedure schars(s)
  3564.    return string(cset(s))
  3565. end
  3566.  
  3567. procedure ochars(w)
  3568.    local out, c
  3569.  
  3570.    out := ""
  3571.    every c := !w do
  3572.     if not find(c,out) then
  3573.         out ||:= c
  3574.  
  3575.    return out
  3576.  
  3577. end
  3578. ##########
  3579. phoname.icn
  3580. ############################################################################
  3581. #
  3582. #    Name:    phoname.icn
  3583. #
  3584. #    Title:    Generate letter combinations for phone numbers
  3585. #
  3586. #    Author:    Unknown
  3587. #
  3588. #    Date:    June 10, 1988
  3589. #
  3590. ############################################################################
  3591. #
  3592. #     This procedure generates the letter combinations corresponding to the
  3593. #  digits in a telephone number.
  3594. #
  3595. #  Warning:
  3596. #
  3597. #     The number of possibilities is very large. This procedure should be
  3598. #  used in a context that limits or filters its output.
  3599. #
  3600. ############################################################################
  3601.  
  3602. procedure phoname(number)
  3603.  
  3604.     local buttons, nondigits, pstr, t, x
  3605.  
  3606.  
  3607.     buttons := ["000","111","abc","def","ghi","jkl","mno", "prs","tuv","wxy"]
  3608.     nondigits := ~&digits
  3609.  
  3610.     pstr := stripstr(number,nondigits)
  3611.  
  3612.     if 7 ~= *pstr then fail
  3613.     t := []
  3614.     every x := !pstr do
  3615.     put(t,buttons[x+1])
  3616.     
  3617.     suspend !t[1] || !t[2] || !t[3] || !t[4] || !t[5] || !t[6] || !t[7]
  3618.  
  3619. end
  3620.  
  3621. procedure stripstr(str,delchs)
  3622.  
  3623.     local i
  3624.  
  3625.     i := 1
  3626.     while i <= *str do
  3627.     {
  3628.     if any(delchs,str,i) then
  3629.         str[i] := ""
  3630.     else
  3631.         i +:= 1
  3632.     }
  3633.  
  3634.     return str
  3635.  
  3636. end # stripstr
  3637. ##########
  3638. printcol.icn
  3639. ############################################################################
  3640. #
  3641. #    Name:    printcol.icn
  3642. #
  3643. #    Title:    Format columnar data
  3644. #
  3645. #    Author:    Robert J. Alexander
  3646. #
  3647. #    Date:    June 10, 1988
  3648. #
  3649. ############################################################################
  3650. #  
  3651. #     This procedure deals with with the problem of printing tabular
  3652. #  data where the total width of items to be printed is wider than
  3653. #  the page.  Simply allowing the data to wrap to additional lines
  3654. #  often produces marginally readable output.  This procedure facil-
  3655. #  itates printing such groups of data as vertical columns down the
  3656. #  page length, instead of as horizontal rows across the page.  That
  3657. #  way many, many fields can be printed neatly.  The programming of
  3658. #  such a transformation can be a nuisance.  This procedure does
  3659. #  much of the work for you, like deciding how many items can fit
  3660. #  across the page width and ensuring that entire items will be
  3661. #  printed on the same page without page breaks (if that service is
  3662. #  requested).
  3663. #  
  3664. #     For example, suppose we have a list of records we would like
  3665. #  to print.  The record is defined as:
  3666. #  
  3667. #          record rec(item1,item2,item3,...)
  3668. #  
  3669. #  Also suppose that lines such as
  3670. #  
  3671. #          Field 1   Field 2   Field 3     ...
  3672. #          -------   -------   -------     ---
  3673. #          Record 1    item1     item2     item3      ...
  3674. #          Record 2    item1     item2     item3      ...
  3675. #  
  3676. #  are too long to print across the page.  This procedure will print
  3677. #  them as:
  3678. #  
  3679. #          TITLE
  3680. #          =====
  3681. #          Record 1   Record 2     ...
  3682. #          --------   --------     ---
  3683. #          Field 1   item1      item1       ...
  3684. #          Field 2   item2      item2       ...
  3685. #          Field 3   item3      item3       ...
  3686. #  
  3687. #  The arguments are:
  3688. #  
  3689. #       items:       a co-expression that produces a sequence of
  3690. #                    items (usually structured data objects, but not
  3691. #                    necessarily) for which data is to be printed.
  3692. #  
  3693. #       fields:      a list of procedures to produce the field's
  3694. #                    data.  Each procedure takes two arguments.  The
  3695. #                    procedure's action depends upon what is passed
  3696. #                    in the first argument:
  3697. #  
  3698. #            header      Produces the row heading string to be used
  3699. #                        for that field (the field name).
  3700. #  
  3701. #            width       Produces the maximum field width that can
  3702. #                        be produced (including the column header).
  3703. #  
  3704. #            Other      Produces the field value string for the
  3705. #                        item passed as the argument.
  3706. #  
  3707. #          The second argument is arbitrary data from the procedures
  3708. #       with each invocation.  The data returned by the first func-
  3709. #       tion on the list is used as a column heading string (the
  3710. #       item name).
  3711. #  
  3712. #       title:       optional.
  3713. #  
  3714. #  
  3715. #       pagelength:  if null (omitted) page breaks are ignored.
  3716. #  
  3717. #       linelength:  default 80.
  3718. #  
  3719. #       auxdata:     auxiliary arbitrary data to be passed to the field
  3720. #                    procedures -- see `fields', above.
  3721. #  
  3722. ############################################################################
  3723.  
  3724. procedure printcol(items,fields,title,pagelength,linelength,auxdata)
  3725.   local maxwidth,maxhead,groups,columns,itemlist,cont,f,p,underline,
  3726.     hfield
  3727.   /linelength := 80
  3728.   /pagelength := 30000
  3729.   /title := ""
  3730. #
  3731. #  Compute the maximum field width (so we know the column spacing) and
  3732. #  the maximum header width (so we know how much space to leave on the
  3733. #  left for headings.
  3734. #
  3735.   maxwidth := maxhead := -1 
  3736.   cont := ""
  3737.   every maxwidth <:= (!fields)("width",auxdata)
  3738.   hfield := get(fields)
  3739.   every maxhead <:= *(!fields)("header",auxdata)
  3740.   columns := (linelength - maxhead) / (maxwidth + 1)
  3741.   groups := pagelength / (6 + *fields)
  3742. #
  3743. #  Loop to print groups of data.
  3744. #
  3745.   repeat {
  3746.     if pagelength < 30000 then writes("\f")
  3747. #
  3748. #  Loop to print data of a group (a page's worth).
  3749. #
  3750.     every 1 to groups do {
  3751. #
  3752. #  Collect the items to be output in this group.  A group is the number
  3753. #  of columns that can fit across the page.
  3754. #
  3755.       itemlist := []
  3756.       every 1 to columns do put(itemlist,@items) | break
  3757.       if *itemlist = 0 then break break
  3758. #
  3759. #  Print a title and the column headings.
  3760. #
  3761.       write(repl("=",*write("\n",title || cont)))
  3762.       cont := " (continued)"
  3763.       writes(underline := left("",maxhead))
  3764.       every f := hfield(!itemlist,auxdata) do {
  3765.     p := if *f < maxwidth then center else left
  3766.     writes(" ",p(f,maxwidth))
  3767.     underline ||:= " " || p(repl("-",*f),maxwidth)
  3768.       }
  3769.       write("\n",underline)
  3770. #
  3771. #  Print the fields.
  3772. #
  3773.       every f := !fields do {
  3774.     writes(right(f("header",auxdata),maxhead))
  3775.     every writes(" ",center(f(!itemlist,auxdata),maxwidth))
  3776.     write()
  3777.       }
  3778.     }    # End of loop to print groups.
  3779.   }    # End of loop to print all items.
  3780.   return
  3781. end
  3782. ##########
  3783. printf.icn
  3784. ############################################################################
  3785. #
  3786. #    Name:    printf.icn
  3787. #
  3788. #    Title:    Printf-style formatting
  3789. #
  3790. #    Author:    William H. Mitchell
  3791. #
  3792. #    Date:    June 10, 1988
  3793. #
  3794. ############################################################################
  3795. #  
  3796. #     This procedure behaves somewhat like the standard printf.
  3797. #  Supports d, s, o, and x formats like printf.  An "r" format
  3798. #  prints real numbers in a manner similar to that of printf's "f",
  3799. #  but will produce a result in an exponential format if the number
  3800. #  is larger than the largest integer plus one.
  3801. #  
  3802. #     Left or right justification and field width control are pro-
  3803. #  vided as in printf.  %s and %r handle precision specifications.
  3804. #  
  3805. #     The %r format is quite a bit of a hack, but it meets the
  3806. #  author's requirements for accuracy and speed.  Code contributions
  3807. #  for %f, %e, and %g formats that work like printf are welcome.
  3808. #  
  3809. #     Possible new formats:
  3810. #  
  3811. #          %t -- print a real number as a time in hh:mm
  3812. #          %R -- roman numerals
  3813. #          %w -- integers in english
  3814. #          %b -- binary
  3815. #  
  3816. #  
  3817. ############################################################################
  3818.  
  3819. procedure sprintf(format, a, b, c, d, e, f, g, h)
  3820.    local args
  3821.  
  3822.     args := [a,b,c,d,e,f,g,h]
  3823.     return _doprnt(format, args)
  3824. end
  3825.  
  3826. procedure fprintf(file, format, a, b, c, d, e, f, g, h)
  3827.    local args
  3828.  
  3829.     args := [a,b,c,d,e,f,g,h]
  3830.     writes(file, _doprnt(format, args))
  3831.     return
  3832. end
  3833.  
  3834. procedure printf(format, a, b, c, d, e, f, g, h)
  3835.    local args
  3836.  
  3837.     args := [a,b,c,d,e,f,g,h]
  3838.     writes(&output, _doprnt(format, args))
  3839.     return
  3840. end
  3841.  
  3842. procedure _doprnt(format, args)
  3843.    local out, v, just, width, conv, prec, pad
  3844.  
  3845.     out := ""
  3846.     format ? repeat {
  3847.         (out ||:= tab(upto('%'))) | (out ||:= tab(0) & break)
  3848.         v := get(args)
  3849.         move(1)
  3850.         just := right
  3851.         width := conv := prec := pad := &null
  3852.         ="-" & just := left
  3853.         width := tab(many(&digits))
  3854.         (\width)[1] == "0" & pad := "0"
  3855.         ="." & prec := tab(many(&digits))
  3856.         conv := move(1)
  3857.         #write("just: ",image(just),", width: ", width, ", prec: ",
  3858.         # prec, ", conv: ", conv)
  3859.         case conv of {
  3860.             "d": {
  3861.                 v := string(v)
  3862.             }
  3863.             "s": {
  3864.                 v := string(v[1:(\prec+1)|0])
  3865.             }
  3866.             "x": v := hexstr(v)
  3867.             "o": v := octstr(v)
  3868.             "i": v := image(v)
  3869.             "r": v := fixnum(v,prec)
  3870.             default: {
  3871.                 push(args, v)
  3872.                 v := conv
  3873.             }
  3874.             }
  3875.         if \width & *v < width then {
  3876.             v := just(v, width, pad)
  3877.             }
  3878.         out ||:= v
  3879.         }
  3880.  
  3881.     return out
  3882. end
  3883.  
  3884. procedure hexstr(n)
  3885.    local h, neg
  3886.    static BigNeg, hexdigs, hexfix
  3887.  
  3888.     initial {
  3889.         BigNeg := -2147483647-1
  3890.         hexdigs := "0123456789abcdef"
  3891.         hexfix := "89abcdef"
  3892.         }
  3893.  
  3894.     n := integer(n)
  3895.     if n = BigNeg then
  3896.         return "80000000"
  3897.     h := ""
  3898.     if n < 0 then {
  3899.         n := -(BigNeg - n)
  3900.         neg := 1
  3901.         }
  3902.     repeat {
  3903.         h := hexdigs[n%16+1]||h
  3904.         if (n /:= 16) = 0 then
  3905.             break
  3906.         }
  3907.     if \neg then {
  3908.         h := right(h,8,"0")
  3909.         h[1] := hexfix[h[1]+1]
  3910.         }
  3911.     return h
  3912. end
  3913. procedure octstr(n)
  3914.    local h, neg
  3915.    static BigNeg, octdigs, octfix
  3916.  
  3917.     initial {
  3918.         BigNeg := -2147483647-1
  3919.         octdigs := "01234567"
  3920.         octfix := "23"
  3921.         }
  3922.  
  3923.     n := integer(n)
  3924.     if n = BigNeg then
  3925.         return "20000000000"
  3926.     h := ""
  3927.     if n < 0 then {
  3928.         n := -(BigNeg - n)
  3929.         neg := 1
  3930.         }
  3931.     repeat {
  3932.         h := octdigs[n%8+1]||h
  3933.         if (n /:= 8) = 0 then
  3934.             break
  3935.         }
  3936.     if \neg then {
  3937.         h := right(h,11,"0")
  3938.         h[1] := octfix[h[1]+1]
  3939.         }
  3940.     return h
  3941. end
  3942.  
  3943. procedure fixnum(x, prec)
  3944.    local int, frac, f1, f2, p10
  3945.  
  3946.     /prec := 6
  3947.     int := integer(x) | return image(x)
  3948.     frac := image(x - int)
  3949.     if find("e", frac) then {
  3950.         frac ?:= {
  3951.             f1 := tab(upto('.')) &
  3952.             move(1) &
  3953.             f2 := tab(upto('e')) &
  3954.             move(1) &
  3955.             p10 := -integer(tab(0)) &
  3956.             repl("0",p10-1) || f1 || f2
  3957.             }
  3958.         }
  3959.     else
  3960.         frac ?:= (tab(upto('.')) & move(1) & tab(0))
  3961.     frac := left(frac, prec, "0")
  3962.     return int || "." || frac
  3963. end
  3964. ##########
  3965. radcon.icn
  3966. ############################################################################
  3967. #
  3968. #    Name:    radcon.icn
  3969. #
  3970. #    Title:    Radix conversion
  3971. #
  3972. #    Author:    Ralph E. Griswold
  3973. #
  3974. #    Date:    June 10, 1988
  3975. #
  3976. ############################################################################
  3977. #  
  3978. #  The following procedures convert numbers from one radix to
  3979. #  another. The letters from a to z are used for ``digits'' greater
  3980. #  than 9. All the conversion procedures fail if the conversion can-
  3981. #  not be made.
  3982. #  
  3983. #       exbase10(i,j)  convert base-10 integer i to base j
  3984. #  
  3985. #       inbase10(s,i)  convert base-i integer s to base 10
  3986. #  
  3987. #       radcon(s,i,j)  convert base-i integer s to base j
  3988. #  
  3989. #  Limitation:
  3990. #
  3991. #     The maximum base allowed is 36.
  3992. #  
  3993. ############################################################################
  3994.  
  3995. procedure exbase10(i,j)
  3996.    static digits
  3997.    local s, d, sign
  3998.    initial digits := &digits || &lcase
  3999.    if i = 0 then return 0
  4000.    if i < 0 then {
  4001.       sign := "-"
  4002.       i := -i
  4003.       }
  4004.    else sign := ""
  4005.    s := ""
  4006.    while i > 0 do {
  4007.       d := i % j
  4008.       if d > 9 then d := digits[d + 1]
  4009.       s := d || s
  4010.       i /:= j
  4011.       }
  4012.    return sign || s
  4013. end
  4014.  
  4015. procedure inbase10(s,i)
  4016.    if s[1] == "-" then return "-" || integer(i || "r" || s[2:0])
  4017.    else return integer(i || "r" || s)
  4018. end
  4019.  
  4020. procedure radcon(s,i,j)
  4021.    return exbase10(inbase10(s,i),j)
  4022. end
  4023. ##########
  4024. rational.icn
  4025. ############################################################################
  4026. #
  4027. #    Name:    rational.icn
  4028. #
  4029. #    Title:    Perform arithmetic on rational numbers
  4030. #
  4031. #    Author:    Ralph E. Griswold
  4032. #
  4033. #    Date:    May 11, 1989
  4034. #
  4035. ############################################################################
  4036. #
  4037. #     These procedures perform arithmetic on rational numbers (fractions):
  4038. #
  4039. #     str2rst(s)    Convert the string representation of a rational number
  4040. #                   (such as "3/2") to a rational number.
  4041. #
  4042. #     rat2str(r)    Convert the rational number r to its string
  4043. #                   representation.
  4044. #
  4045. #     addrat(r1,r2) Add rational numbers r1 and r2.
  4046. #
  4047. #     subrat(r1,r2) Subtract rational numbers r1 and r2.
  4048. #
  4049. #     mpyrat(r1,r2) Multiply rational numbers r1 and r2.
  4050. #
  4051. #     divrat(r1,r2) Divide rational numbers r1 and r2.
  4052. #
  4053. #     negrat(r)     Produce negative of rational number r.
  4054. #
  4055. #     reciprat(r)   Produce the reciprocal of rational number r.
  4056. #    
  4057. ############################################################################
  4058. #
  4059. #  Links: gcd
  4060. #
  4061. ############################################################################
  4062.  
  4063. link gcd
  4064.  
  4065. record rational(numer,denom,sign)
  4066.  
  4067. procedure str2rat(s)
  4068.    local div, numer, denom, sign
  4069.  
  4070.    s ? {
  4071.       ="[" &
  4072.       numer := integer(tab(upto('/'))) &
  4073.       move(1) &
  4074.       denom := integer(tab(upto(']'))) &
  4075.       pos(-1)
  4076.       } | fail
  4077.    div := gcd(numer,denom) | fail
  4078.    numer /:= div
  4079.    denom /:= div
  4080.    if numer * denom >= 0 then sign := 1    # dangerous -- potential overflow
  4081.       else sign := -1
  4082.    return rational(abs(numer),abs(denom),sign)
  4083. end
  4084.   
  4085. procedure rat2str(r)
  4086.    return "[" || r.numer * r.sign || "/" || r.denom || "]"
  4087. end
  4088.  
  4089. procedure mpyrat(r1,r2)
  4090.    local numer, denom, div
  4091.  
  4092.    numer := r1.numer * r2.numer
  4093.    denom := r1.denom * r2.denom
  4094.    div := gcd(numer,denom) | fail    # shouldn't fail
  4095.    return rational(numer / div,denom / div, r1.sign * r2.sign)
  4096. end
  4097.  
  4098. procedure divrat(r1,r2)
  4099.    return mpyrat(r1,reciprat(r2))    # may fail
  4100. end
  4101.  
  4102. procedure reciprat(r)
  4103.    if r.numer = 0 then fail
  4104.    else return rational(r.denom,r.numer,r.sign)
  4105. end
  4106.  
  4107. procedure negrat(r)
  4108.    return rational(r.numer,r.denom,-r.sign)
  4109. end
  4110.  
  4111. procedure addrat(r1,r2)
  4112.    local denom, numer, div, sign
  4113.  
  4114.    denom := r1.denom * r2.denom
  4115.    numer := r1.sign * r1.numer * r2.denom +
  4116.       r2.sign * r2.numer * r1.denom
  4117.    if numer >= 0 then sign := 1
  4118.       else sign := -1
  4119.    div := gcd(numer,denom) | fail
  4120.    return rational(abs(numer / div),abs(denom / div),sign)
  4121. end
  4122.  
  4123. procedure subrat(r1,r2)
  4124.    return addrat(r1,negrat(r2))
  4125. end
  4126. ##########
  4127. segment.icn
  4128. ############################################################################
  4129. #
  4130. #    Name:    segment.icn
  4131. #
  4132. #    Title:    Segment string
  4133. #
  4134. #    Author:    William H. Mitchell
  4135. #
  4136. #    Date:    June 10, 1988
  4137. #
  4138. ############################################################################
  4139. #  
  4140. #     These procedures segment a string s into consecutive substrings
  4141. #  consisting of characters that respectively do/do not occur in c.
  4142. #  segment(s,c) generates the substrings, while seglist produces a list
  4143. #  of the segments.  For example,
  4144. #  
  4145. #          segment("Not a sentence.",&lcase ++ &ucase)
  4146. #  
  4147. #  generates
  4148. #  
  4149. #          "Not"
  4150. #          " "
  4151. #          "a"
  4152. #          " "
  4153. #          "sentence"
  4154. #          "."
  4155. #  while
  4156. #          seglist("Not a sentence.",&lcase ++ &ucase)
  4157. #
  4158. #  produces
  4159. #
  4160. #          ["Not"," ","a","sentence","."]
  4161. #
  4162. ############################################################################
  4163.  
  4164. procedure segment(line,dlms)
  4165.    local ndlms
  4166.  
  4167.    dlms := (any(dlms,line[1]) & ~dlms)
  4168.    ndlms := ~dlms
  4169.    line ? repeat {
  4170.       suspend tab(many(ndlms)) \ 1
  4171.       suspend tab(many(dlms)) \ 1
  4172.       pos(0) & break
  4173.       }
  4174. end
  4175.  
  4176. procedure seglist(s,c)
  4177.    local a
  4178.  
  4179.    a := []
  4180.    c := (any(c,s[1]) & ~c)
  4181.    s ? while put(a,tab(many(c := ~c)))
  4182.    return a
  4183. end
  4184. ##########
  4185. seqimage.icn
  4186. ############################################################################
  4187. #
  4188. #    Name:    seqimage.icn
  4189. #
  4190. #    Title:    Produce string image of Icon result sequence
  4191. #
  4192. #    Author:    Ralph E. Griswold
  4193. #
  4194. #    Date:    June 10, 1988
  4195. #
  4196. ############################################################################
  4197. #  
  4198. #     The procedure Seqimage{e,i,j} produces a string image of the
  4199. #  result sequence for the expression e. The first i results are
  4200. #  printed. If i is omitted, there is no limit. If there are more
  4201. #  than i results for e, ellipses are provided in the image after
  4202. #  the first i.  If j is specified, at most j results from the end
  4203. #  of the sequence are printed after the ellipses.  If j is omitted,
  4204. #  only the first i results are produced.
  4205. #  
  4206. #     For example, the expressions
  4207. #  
  4208. #     Seqimage{1 to 12}
  4209. #     Seqimage{1 to 12,10}
  4210. #     Seqimage{1 to 12,6,3}
  4211. #  
  4212. #  produce, respectively,
  4213. #  
  4214. #     {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
  4215. #     {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...}
  4216. #     {1, 2, 3, 4, 5, 6, ..., 10, 11, 12}
  4217. #  
  4218. #  
  4219. #  Warning:
  4220. #
  4221. #     If j is not omitted and e has a infinite result
  4222. #  sequence, Seqimage does not terminate.
  4223. #  
  4224. ############################################################################
  4225.  
  4226. procedure Seqimage(a)
  4227.    local seq, result, i, j, resid
  4228.  
  4229.    seq := ""
  4230.    i := @a[2]
  4231.    j := @a[3]
  4232.    while result := image(@a[1]) do
  4233.       if *a[1] > \i then {
  4234.          if /j then {
  4235.             seq ||:= ", ..."
  4236.             break
  4237.             }
  4238.          else {
  4239.             resid := [", " || result]
  4240.             every put(resid,", " || image(|@a[1]))
  4241.             if *resid > j then seq ||:= ", ..."
  4242.             every seq ||:= resid[*resid -j + 1 to *resid]
  4243.             }
  4244.          }
  4245.       else seq ||:= ", " || result
  4246.    return "{" || seq[3:0] || "}" | "{}"
  4247. end
  4248. ##########
  4249. shquote.icn
  4250. ############################################################################
  4251. #
  4252. #    Name:    shquote.icn
  4253. #
  4254. #    Title:    Quote word for shells
  4255. #
  4256. #    Author:    Robert J. Alexander
  4257. #
  4258. #    Date:    December 5, 1989
  4259. #
  4260. ############################################################################
  4261. #
  4262. #  cshquote(s) -- Produces a version of s which is properly quoted
  4263. #  for the c-shell (csh).
  4264. #
  4265.  
  4266. procedure cshquote(s)
  4267.    local quotechar,q
  4268.    quotechar := '\t\n $"#&\'()*;<>?[\\`|~'
  4269.    if not upto(quotechar,s) then return s
  4270.    q := ""
  4271.    s ? {
  4272.       while q ||:= tab(upto('\'\n')) ||
  4273.         case move(1) of {
  4274.            "'": "'\\''"
  4275.            "\n": "\\\n"
  4276.            }
  4277.       q ||:= tab(0)
  4278.       }
  4279.    return "'" || q || "'"
  4280. end
  4281.  
  4282. #
  4283. #  shquote(s) -- Produces a version of s which is properly quoted
  4284. #  for the Bourne shell (sh).
  4285. #
  4286.  
  4287. procedure shquote(s)
  4288.    local quotechar,q
  4289.    quotechar := '\t\n\r $"#&\'()*;<>?\\^`|'
  4290.    if not upto(quotechar,s) then return s
  4291.    q := ""
  4292.    s ? {
  4293.       while q ||:= tab(upto('\'')) ||
  4294.         case move(1) of {
  4295.            "'": "'\\''"
  4296.            }
  4297.       q ||:= tab(0)
  4298.       }
  4299.    return "'" || q || "'"
  4300. end
  4301.  
  4302. #
  4303. #  mpwquote(s) -- Produces a version of s which is properly quoted
  4304. #  for the Macintosh Programmer's Workshop shell (MPW Shell).
  4305. #
  4306.  
  4307. procedure mpwquote(s)
  4308.    local quotechar,q
  4309.    quotechar := ' \t\n\r\0#;&|()k\'"/\\{}`?w[]+*./r<>r.d'
  4310.    if not upto(quotechar,s) then return s
  4311.    q := ""
  4312.    s ? {
  4313.       while (q ||:= tab(upto('\'')) || "'k''") & move(1)
  4314.       q ||:= tab(0)
  4315.       }
  4316.    return "'" || q || "'"
  4317. end
  4318. ##########
  4319. shuffle.icn
  4320. ############################################################################
  4321. #
  4322. #    Name:    shuffle.icn
  4323. #
  4324. #    Title:    Shuffle values
  4325. #
  4326. #    Author:    Ward Cunningham
  4327. #
  4328. #    Date:    June 10, 1988
  4329. #
  4330. ############################################################################
  4331. #  
  4332. #     The procedure shuffle(x) shuffles a string or list. In the case
  4333. #  that x is a string, a corresponding string with the characters
  4334. #  randomly rearranged is produced. In the case that x is a list,
  4335. #  the values in the list are randomly rearranged.
  4336. #  
  4337. ############################################################################
  4338.  
  4339. procedure shuffle(x)
  4340.    x := string(x)
  4341.    every !x :=: ?x
  4342.    return x
  4343. end
  4344. ##########
  4345. snapshot.icn
  4346. ############################################################################
  4347. #
  4348. #    Name:    snapshot.icn
  4349. #
  4350. #    Title:    Show snapshot of Icon string scanning
  4351. #
  4352. #    Author:    Ralph E. Griswold and Randal L. Schwartz
  4353. #
  4354. #    Date:    June 10, 1988
  4355. #
  4356. ############################################################################
  4357. #  
  4358. #     The procedure snapshot() writes a snapshot of the state of string
  4359. #  scanning, showing the value of &subject and &pos. For example,
  4360. #  
  4361. #     "((a+b)-delta)/(c*d))" ? {
  4362. #        tab(bal('+-/*'))
  4363. #        snapshot()
  4364. #        }
  4365. #  
  4366. #  produces
  4367. #
  4368. #       -------------------------------------
  4369. #       |                                   |
  4370. #       | &subject = "((a+b)-delta)/(c*d))" |
  4371. #       |                          |        |
  4372. #       -------------------------------------
  4373. #  
  4374. #     Note that the bar showing the &pos is positioned under the &posth
  4375. #  character (actual positions are between characters).  If &pos is
  4376. #  at the end of &subject, the bar is positioned under the quotation
  4377. #  mark delimiting the subject. For example,
  4378. #  
  4379. #     "abcdefgh" ? (tab(0) & snapshot())
  4380. #  
  4381. #  produces
  4382. #
  4383. #       -------------------------
  4384. #       |                       |
  4385. #       | &subject = "abcdefgh" |
  4386. #       |                     | |
  4387. #       -------------------------
  4388. #  
  4389. #     Escape sequences are handled properly. For example,
  4390. #  
  4391. #     "abc\tdef\nghi" ? (tab(upto('\n')) & snapshot())
  4392. #  
  4393. #  produces
  4394. #
  4395. #       ------------------------------
  4396. #       |                            |
  4397. #       | &subject = "abc\tdef\nghi" |
  4398. #       |                     |      |
  4399. #       ------------------------------
  4400. #  
  4401. ############################################################################
  4402.  
  4403. procedure snapshot()
  4404.    local bar, bar2, is, is0, prefix
  4405.  
  4406.    prefix := "&subject = "
  4407.    is := image(&subject)
  4408.    is0 := *image(&subject[1:&pos]) | fail # quick exit if bogus
  4409.  
  4410.    write(bar := repl("-", *is + *prefix + 4)) # 4 = two vbars/two spaces
  4411.    write(bar2 := ("|" || repl(" ", *is + *prefix + 2) || "|"))
  4412.    write("| ", prefix, is, " |")
  4413.    bar2[*prefix + is0 + 2] := "|" # 2 = "| " prefix
  4414.    write(bar2)
  4415.    write(bar)
  4416.    return ""
  4417. end
  4418. ##########
  4419. strings.icn
  4420. ############################################################################
  4421. #
  4422. #    Name:    strings.icn
  4423. #
  4424. #    Title:    String utilities
  4425. #
  4426. #    Author:    Ralph E. Griswold
  4427. #
  4428. #    Date:    May 26, 1989
  4429. #
  4430. ############################################################################
  4431. #  
  4432. #  These procedures perform simple operations on strings.
  4433. #  
  4434. #       compress(s,c)      Compress consecutive occurrences of charac-
  4435. #                          ters in c that occur in s.
  4436. #  
  4437. #       omit(s,c)          Omit all occurrences of characters in c
  4438. #                          that occur in s.
  4439. #
  4440. #       replace(s1,s2,s3)  In s1, replace all occurrences of s2 by s3.
  4441. #  
  4442. #       rotate(s,i)        Rotate s i characters to the left (negative i
  4443. #                          produces rotation to the right); the default
  4444. #                          value of i is 1.
  4445. #  
  4446. ############################################################################
  4447.  
  4448. procedure compress(s,c)
  4449.    local result, s1
  4450.  
  4451.    result := ""
  4452.  
  4453.    s ? {
  4454.       while result ||:= tab(upto(c)) do {
  4455.          result ||:= (s1 := move(1))
  4456.          tab(many(s1))
  4457.          }
  4458.       return result || tab(0)
  4459.       }
  4460. end
  4461.  
  4462. #  omit characters
  4463. #
  4464. procedure omit(s,c)
  4465.    local result, s1
  4466.  
  4467.    result := ""
  4468.  
  4469.    s ? {
  4470.       while result ||:= tab(upto(c)) do {
  4471.          s1 := move(1)
  4472.          tab(many(s1))
  4473.          }
  4474.       return result || tab(0)
  4475.       }
  4476. end
  4477.  
  4478. #  replace string
  4479. #
  4480. procedure replace(s1,s2,s3)
  4481.    local result, i
  4482.  
  4483.    result := ""
  4484.    i := *s2
  4485.  
  4486.    s1 ? {
  4487.       while result ||:= tab(find(s2)) do {
  4488.          result ||:= s3
  4489.          move(i)
  4490.          }
  4491.       return result || tab(0)
  4492.       }
  4493. end
  4494.  
  4495. #  rotate string
  4496. #
  4497. procedure rotate(s,i)
  4498.    /i := 1
  4499.    if i <= 0 then i +:= *s
  4500.    i %:= *s
  4501.    return s[i + 1:0] || s[1:i + 1]
  4502. end
  4503. ##########
  4504. structs.icn
  4505. ############################################################################
  4506. #
  4507. #    Name:    structs.icn
  4508. #
  4509. #    Title:    Structure operations
  4510. #
  4511. #    Author:    Ralph E. Griswold
  4512. #
  4513. #    Date:    June 10, 1988
  4514. #
  4515. ############################################################################
  4516. #  
  4517. #     These procedures manipulate structures.
  4518. #  
  4519. #       depth(t)    compute maximum depth of tree t
  4520. #  
  4521. #       eq(x,y)     compare list structures x and y
  4522. #
  4523. #       teq(t1,t2)  compare trees t1 and t2
  4524. #  
  4525. #       equiv(s,y)  compare arbitrary structures x and y
  4526. #  
  4527. #       ldag(s)     construct a dag from the string s
  4528. #  
  4529. #       ltree(s)    construct a tree from the string s
  4530. #  
  4531. #       stree(t)    construct a string from the tree t
  4532. #  
  4533. #       tcopy(t)    copy tree t
  4534. #  
  4535. #       visit(t)  visit, in preorder, the nodes of the tree t
  4536. #  
  4537. #     The procedure equiv() tests for the "equivalence" of two values. For types
  4538. #  other than structures, it does the same thing as x1 === x2.  For structures,
  4539. #  the test is for "shape".  For example,
  4540. #
  4541. #    equiv([],[])
  4542. #
  4543. #  succeeds.
  4544. #
  4545. #     It handles loops, but does not recognize them as such.  For example,
  4546. #  given
  4547. #
  4548. #    L1 := []
  4549. #    L2 := []
  4550. #    put(L1,L1)
  4551. #    put(L2,L1)
  4552. #
  4553. #    equiv(L1,L2)
  4554. #
  4555. #  succeeds.
  4556. #
  4557. #     The concept of equivalence for tables and sets is not quite right
  4558. #  if their elements are themselves structures.  The problem is that there
  4559. #  is no concept of order for tables and sets, yet it is impractical to
  4560. #  test for equivalence of their elements without imposing an order.  Since
  4561. #  structures sort by "age", there may be a mismatch between equivalent
  4562. #  structures in two tables or sets.
  4563. #
  4564. #  Note:
  4565. #     The procedures equiv and ldag have a trailing argument that is used on
  4566. #  internal recursive calls; a second argument must not be supplied
  4567. #  by the user.
  4568. #  
  4569. ############################################################################
  4570.  
  4571. procedure eq(x,y)
  4572.    local i
  4573.    if x === y then return y
  4574.    if type(x) == type(y) == "list" then {
  4575.       if *x ~= *y then fail
  4576.       every i := 1 to *x do
  4577.          if not eq(x[i],y[i]) then fail
  4578.       return y
  4579.      }
  4580. end
  4581.  
  4582. procedure depth(ltree)
  4583.    local count
  4584.    count := 0
  4585.    every count <:= 1 + depth(ltree[2 to *ltree])
  4586.    return count
  4587. end
  4588.  
  4589. procedure ldag(stree,done)
  4590.    local a
  4591.    /done := table()
  4592.    if a := \done[stree] then return a
  4593.    stree ?
  4594.       if a := [tab(upto('('))] then {
  4595.          move(1)
  4596.          while put(a,ldag(tab(bal(',)')),done)) do
  4597.             move(1)
  4598.          }
  4599.       else a := [tab(0)]
  4600.    return done[stree] := a
  4601. end
  4602.  
  4603. procedure ltree(stree)
  4604.    local a
  4605.    stree ?
  4606.       if a := [tab(upto('('))] then {
  4607.          move(1)
  4608.          while put(a,ltree(tab(bal(',)')))) do
  4609.             move(1)
  4610.          }
  4611.       else a := [tab(0)]
  4612.    return a
  4613. end
  4614.  
  4615. procedure stree(ltree)
  4616.    local s
  4617.    if *ltree = 1 then return ltree[1]
  4618.    s := ltree[1] || "("
  4619.    every s ||:= stree(ltree[2 to *ltree]) || ","
  4620.    return s[1:-1] || ")"
  4621. end
  4622.  
  4623. procedure tcopy(ltree)
  4624.    local a
  4625.    a := [ltree[1]]
  4626.    every put(a,tcopy(ltree[2 to *ltree]))
  4627.    return a
  4628. end
  4629.  
  4630. procedure teq(a1,a2)
  4631.    local i
  4632.    if *a1 ~= *a2 then fail
  4633.    if a1[1] ~== a2[1] then fail
  4634.    every i := 2 to *a1 do
  4635.       if not teq(a1[i],a2[i]) then fail
  4636.    return a2
  4637. end
  4638.  
  4639. procedure visit(ltree)
  4640.    suspend ltree | visit(ltree[2 to *ltree])
  4641. end
  4642.  
  4643. #
  4644.     
  4645. procedure equiv(x1,x2,done)
  4646.    local code, i
  4647.  
  4648.    if x1 === x2 then return x2        # Covers everything but structures.
  4649.  
  4650.    if type(x1) ~== type(x2) then fail    # Must be same type.
  4651.  
  4652.    if type(x1) == ("procedure" | "file")
  4653.       then fail                # Leave only those with sizes (null
  4654.                     # taken care of by first two tests).
  4655.  
  4656.    if *x1 ~= *x2 then fail        # Skip a lot of possibly useless work.
  4657.  
  4658.                     # Structures (and others) remain.
  4659.  
  4660.    /done := table()            # Basic call.
  4661.  
  4662.    (/done[x1] := set()) |        # Make set of equivalences if new.
  4663.       (if member(done[x1],x2) then return x2)
  4664.  
  4665.                     # Records complicate things.
  4666.    image(x1) ? (code := (="record" | type(x1)))
  4667.  
  4668.    case code of {
  4669.       "list" | "record": 
  4670.          every i := 1 to *x1 do
  4671.             if not equiv(x1[i],x2[i],done) then fail
  4672.       "table": if not equiv(sort(x1,3),sort(x2,3),done) then fail
  4673.       "set":   if not equiv(sort(x1),sort(x2),done) then fail
  4674.       default: fail            # Vaues of other types are different. 
  4675.       }
  4676.  
  4677.    insert(done[x1],x2)            # Equivalent; add to set.
  4678.    return x2
  4679.  
  4680. end
  4681. ##########
  4682. usage.icn
  4683. ############################################################################
  4684. #
  4685. #    Name:    usage.icn
  4686. #
  4687. #    Title:    Service procedures
  4688. #
  4689. #    Author:    Ralph E. Griswold
  4690. #
  4691. #    Date:    May 11, 1989
  4692. #
  4693. ############################################################################
  4694. #
  4695. #     These procedures provide various common services:
  4696. #
  4697. #     Usage(s)          stops executions with a message concerning the
  4698. #                       expected form of usage of a program.
  4699. #
  4700. #     ErrorCheck(l,f)    reports an error that has been converted to
  4701. #                       failure.
  4702. #
  4703. #     Feature(s)        succeeds if feature s is available in the running
  4704. #                       implementation of Icon.
  4705. #
  4706. #     Requires(s)    terminates execution is feature s is not available.
  4707. #
  4708. #     Signature()    writes the version, host, and features support in
  4709. #                       the running implementation of Icon.
  4710. #
  4711. ############################################################################
  4712.  
  4713. procedure Usage(s)
  4714.    stop("Usage: ",s)
  4715. end
  4716.  
  4717. procedure ErrorCheck(line,file)
  4718.    if &errortext == "" then fail    # No converted error
  4719.    write("\nError ",&errornumber," at line ",line, " in file ",file)
  4720.    write(&errortext)
  4721.    write("offending value: ",image(&errorvalue))
  4722.    return
  4723. end
  4724.  
  4725. procedure Feature(s)
  4726.    if s == &features then return else fail
  4727. end
  4728.  
  4729. procedure Requires(s)
  4730.    if not(Feature(s)) then stop(s," required")
  4731. end
  4732.  
  4733. procedure Signature()
  4734.    write(&version)
  4735.    write(&host)
  4736.    every write(&features)
  4737. end
  4738. ##########
  4739. wildcard.icn
  4740. ############################################################################
  4741. #
  4742. #    Name:    wildcard.icn
  4743. #
  4744. #    Title:    UNIX-like Wild Card Pattern Matching Function
  4745. #
  4746. #    Author:    Robert J. Alexander
  4747. #
  4748. #    Date:    November 27, 1989
  4749. #
  4750. ############################################################################
  4751. #
  4752. #  wildcard(s1,s2,i,j) -- Generates the sequence of integer positions in
  4753. #  string s2 after strings which satisfy pattern s1 in s2[i:j], but fails
  4754. #  if there is no such position.  s1 is a UNIX-like wild-card pattern
  4755. #  containing *, ?, and [...].
  4756. #
  4757.  
  4758. link allof
  4759.  
  4760. global wild_element
  4761.  
  4762. procedure wildcard(p,s,i,j)
  4763.    local plist,c,e,complement,chars
  4764.    if /s := &subject then /i := &pos else /i := 1 ; /j := 0
  4765.    #
  4766.    #  Create a list of pattern elements.  The list looks like:
  4767.    #
  4768.    #     * --> "*"
  4769.    #     ? --> "?"
  4770.    #     [abc] --> 'abc'
  4771.    #     abc --> "abc"
  4772.    #
  4773.    plist := []
  4774.    p ? {
  4775.       while not pos(0) do {
  4776.      c := &null
  4777.      #
  4778.      #  Put pattern element character(s) on list.
  4779.      #
  4780.      e := =("*" | "?") |
  4781.            (="[" & c := tab(find("]")) & move(1)) |
  4782.            tab(upto('*?[') | 0)
  4783.      #
  4784.      #  If it's [abc], create a cset.  Special notations:
  4785.      #
  4786.      #       A-Z means all characters from A to Z inclusive.
  4787.      #       ! (if first) means any character not among those specified.
  4788.      #       - (if first, or after initial !) means itself.
  4789.      #
  4790.      \c ? {
  4791.         complement := if match("!") then move(1) else &null
  4792.         e := cset(if match("-") then move(1) else "")
  4793.         while chars := tab(find("-")) do {
  4794.            move(1)
  4795.            e ++:= chars[1:-1] ++
  4796.                  &cset[ord(chars[-1]) + 1:ord(move(1)) + 2]
  4797.            }
  4798.         e ++:= tab(0)
  4799.         if \complement then e := ~e
  4800.         }
  4801.      put(plist,e)
  4802.      }
  4803.       }
  4804.    #
  4805.    #  Do the pattern match.
  4806.    #
  4807.    suspend s[i:j] ? (
  4808.       allof {wild_element := !plist, case wild_element of {
  4809.         "*": move(0 to (*&subject - &pos + 1))
  4810.         "?": move(1)
  4811.         default: {
  4812.            case type(wild_element) of {
  4813.           "cset": tab(any(wild_element))
  4814.           default: =(wild_element)
  4815.           }
  4816.            }
  4817.         }
  4818.      } & i + &pos - 1)
  4819. end
  4820. ##########
  4821. wrap.icn
  4822. ############################################################################
  4823. #
  4824. #    Name:    wrap.icn
  4825. #
  4826. #    Title:    Wrap lines of output for use with write()
  4827. #
  4828. #    Author:    Robert J. Alexander
  4829. #
  4830. #    Date:    December 5, 1989
  4831. #
  4832. ############################################################################
  4833. #
  4834. #  wrap(s,i) -- Facilitates accumulation of small strings into longer
  4835. #       output strings, outputting when the accumulated string would
  4836. #       exceed a specified length (e.g. outputting items in multiple
  4837. #       columns).
  4838. #
  4839. #       s -- string to accumulate
  4840. #       i -- width of desired output string
  4841. #
  4842. #  Wrap fails if the string s did not necessitate output of the buffered
  4843. #  output string; otherwise the output string is returned (which never
  4844. #  includes s).
  4845. #
  4846. #  s defaults to the empty string (""), causing nothing to be
  4847. #  accumulated; i defaults to 0, forcing output of any buffered string.
  4848. #  Note that calling wrap() with no arguments produces the buffer (if it
  4849. #  is not empty) and clears it.
  4850. #
  4851. #  Wrap does no output to files.
  4852. #
  4853. #
  4854. #  Here's how wrap is normally used:
  4855. #
  4856. #       wrap()                  # Initialize (not really necessary unless
  4857. #                               # a previous use might have left stuff in
  4858. #                               # the buffer).
  4859. #
  4860. #       every i := 1 to 100 do  # Loop to process strings to output --
  4861. #         write(wrap(x[i],80))  # only writes when 80-char line filled.
  4862. #
  4863. #       write(wrap())           # Output what's in buffer -- only outputs
  4864. #                               # if something to write.
  4865. #
  4866.  
  4867. procedure wrap(s,i)
  4868.    local t
  4869.    static line
  4870.    initial line := ""
  4871.    /s := "" ; /i := 0
  4872.    if *(t := line || s) > i then
  4873.      return "" ~== (s :=: line)
  4874.    line := t
  4875. end
  4876.  
  4877. #
  4878. #  wraps(s,i) -- Facilitates managing output of numerous small strings
  4879. #       so that they do not exceed a reasonable line length (e.g.
  4880. #       outputting items in multiple columns).
  4881. #
  4882. #       s -- string to accumulate
  4883. #       i -- maximum width of desired output string
  4884. #
  4885. #  If the string "s" did not necessitate a line-wrap, the string "s" is
  4886. #  returned.  If a line-wrap is needed, "s", preceded by a new-line
  4887. #  character ("\n"), is returned.
  4888. #
  4889. #  "s" defaults to the empty string (""), causing nothing to be
  4890. #  accumulated; i defaults to 0, forcing a new line if anything had been
  4891. #  output on the current line.  Thus calling wraps() with no arguments
  4892. #  reinitializes it.
  4893. #
  4894. #  Wraps does no output to files.
  4895. #
  4896. #
  4897. #  Here's how wraps is normally used:
  4898. #
  4899. #       wraps()                 # Initialize (not really necessary unless
  4900. #                               # a previous use might have left it in an
  4901. #                               # unknown condition).
  4902. #
  4903. #       every i := 1 to 100 do  # Loop to process strings to output --
  4904. #         writes(wraps(x[i],80))# only wraps when 80-char line filled.
  4905. #
  4906. #       writes(wraps())         # Only outputs "\n" if something written
  4907. #                               # on last line.
  4908. #
  4909.  
  4910. procedure wraps(s,i)
  4911.    local t
  4912.    static size
  4913.    initial size := 0
  4914.    /s := "" ; /i := 0
  4915.    t := size + *s
  4916.    if t > i & size > 0 then {
  4917.       size := *s
  4918.       return "\n" || s
  4919.       }
  4920.    size := t
  4921.    return s
  4922. end
  4923. ##########
  4924. ximage.icn
  4925. ############################################################################
  4926. #
  4927. #    Name:    ximage.icn
  4928. #
  4929. #    Title:    Produces "executable" image of structured data
  4930. #
  4931. #    Author:    Robert J. Alexander
  4932. #
  4933. #    Date:    December 5, 1989
  4934. #
  4935. ############################################################################
  4936. #
  4937. #  ximage() -- enhanced image()-type procedure that outputs all data
  4938. #  contained in structured types.  It is called as follows:
  4939. #
  4940. #       ximage(x)
  4941. #
  4942. #  just like image(x) (the other arguments in the "procedure"
  4943. #  declaration are used for passing data among recursive levels).  The
  4944. #  output has an "executable" appearance, which will look familiar to
  4945. #  any Icon programmer.  The returned string for complex data contains
  4946. #  newline characters and indentation, suitable for write()-ing,
  4947. #  providing a pleasing and useful visual representation of the
  4948. #  structures.
  4949. #
  4950.  
  4951.  
  4952. procedure ximage(x,indent,done)
  4953.    local i,s,ss,state,t,xtag
  4954.    static tag,tr
  4955.    #
  4956.    #  If this is the outer invocation, do some initialization.
  4957.    #
  4958.    if /(state := done) then {
  4959.       tr := &trace ; &trace := 0    # postpone tracing while in here
  4960.       indent := ""
  4961.       tag := 0
  4962.       done := table()
  4963.       }
  4964.    #
  4965.    #  Determine the type and process accordingly.
  4966.    #
  4967.    indent := (if indent == "" then "\n" else "") || indent || "   "
  4968.    ss := ""
  4969.    t := type(x)
  4970.    s := if xtag := \done[x] then xtag else case t of {
  4971.       #
  4972.       #  Unstructured types just return their image().
  4973.       #
  4974.       "null" | "string" | "integer" | "real" | "cset" |
  4975.         "co-expression" | "file" | "procedure" | "external": image(x)
  4976.       #
  4977.       #  List.
  4978.       #
  4979.       "list": {
  4980.      done[x] := xtag := "L" || (tag +:= 1)
  4981.      #
  4982.      #  Figure out if there is a predominance of any object in the
  4983.      #  list.  If so, make it the default object.
  4984.      #
  4985.      t := table(0)
  4986.      every t[!x] +:= 1
  4987.      s := [,0]
  4988.      every t := !sort(t) do if s[2] < t[2] then s := t
  4989.      if s[2] > *x / 3 & s[2] > 2 then {
  4990.         s := s[1]
  4991.         t := ximage(s,indent || "   ",done)
  4992.         if t ? (not any('\'"') & ss := tab(find(" :="))) then
  4993.           t := "{" || t || indent || "   " || ss || "}"
  4994.         }
  4995.      else t := &null
  4996.      #
  4997.      #  Output the non-defaulted elements of the list.
  4998.      #
  4999.      ss := ""
  5000.      every i := 1 to *x do if x[i] ~=== s then {
  5001.         ss ||:= indent || xtag || "[" || i || "] := " ||
  5002.           ximage(x[i],indent,done)
  5003.         }
  5004.      s := image(x)
  5005.      s[-1:-1] := "," || \t
  5006.      xtag || " := " || s || ss
  5007.      }
  5008.       #
  5009.       #  Set.
  5010.       #
  5011.       "set": {
  5012.      done[x] := xtag := "S" || (tag +:= 1)
  5013.      every i := !sort(x) do {
  5014.         ss ||:= indent || "insert(" || xtag || "," ||
  5015.           ximage(i,indent,done,) || ")"
  5016.         }
  5017.      xtag || " := " || "set([])" || ss
  5018.      }
  5019.       #
  5020.       #  Table.
  5021.       #
  5022.       "table": {
  5023.      done[x] := xtag := "T" || (tag +:= 1)
  5024.      #
  5025.      #  Output the table elements.  This is a bit tricky, since
  5026.      #  the subscripts might be structured, too.
  5027.      #
  5028.      every i := !sort(x) do {
  5029.         t := ximage(i[1],indent || "   ",done)
  5030.         if t ? (not any('\'"') & s := tab(find(" :="))) then
  5031.           t := "{" || t || indent || "   " || s || "}"
  5032.         ss ||:= indent || xtag || "[" ||
  5033.           t || "] := " ||
  5034.           ximage(i[2],indent,done)
  5035.         }
  5036.      #
  5037.      #  Output the table, including its default value (which might
  5038.      #  also be structured.
  5039.      #
  5040.      t := ximage(x[[]],indent || "   ",done)
  5041.      if t ? (not any('\'"') & s := tab(find(" :="))) then
  5042.            t := "{" || t || indent || "   " || s || "}"
  5043.      xtag || " := " || "table(" || t || ")" || ss
  5044.      }
  5045.       #
  5046.       #  Record.
  5047.       #
  5048.       default: {
  5049.      done[x] := xtag := "R" || (tag +:= 1)
  5050.      every i := 1 to *x do {
  5051.         ss ||:= indent || xtag || "[" || i || "] := " ||
  5052.           ximage(\x[i],indent,done)
  5053.         }
  5054.      xtag || " := " || t || "()" || ss
  5055.      }
  5056.       }
  5057.    #
  5058.    #  If this is the outer invocation, clean up before returning.
  5059.    #
  5060.    if /state then {
  5061.       &trace := tr                        # restore &trace
  5062.       }
  5063.    #
  5064.    #  Return the result.
  5065.    #
  5066.    return s
  5067. end
  5068.