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

  1. ##########
  2. animal.icn
  3. ############################################################################
  4. #
  5. #    Name:    animal.icn
  6. #
  7. #    Title:    Animal game
  8. #
  9. #    Author:    Robert J. Alexander
  10. #
  11. #    Date:    June 10, 1988
  12. #
  13. ############################################################################
  14. #  
  15. #     This is the familiar ``animal game'' written in Icon.  The
  16. #  program asks its human opponent questions in an attempt to guess
  17. #  what animal he is thinking of.  It is an ``expert system'' that
  18. #  starts out with limited knowledge, but gets smarter as it plays
  19. #  and learns from its opponents.  At the conclusion of a session,
  20. #  the program asks permission to remember for future sessions that
  21. #  which it learned.
  22. #  
  23. #     The game is not limited to guessing animals only.  By simply
  24. #  modifying the first two lines of procedure "main" it will happily
  25. #  guess things in other categories.  For example, the lines:
  26. #  
  27. #          GameObject := "president"
  28. #          Tree := Question("Has he ever been known as Bonzo",
  29. #             "Reagan","Lincoln")
  30. #  
  31. #  can be substituted and it works reasonably well.  The knowledge
  32. #  files will be kept separate, too.
  33. #  
  34. #     Typing list at any yes/no prompt will show an inventory of
  35. #  animals known, and there are some other commands (see procedure
  36. #  Confirm).
  37. #  
  38. ############################################################################
  39.  
  40. global GameObject,Tree,ShowLine,Learn
  41. record Question(question,yes,no)
  42.  
  43. procedure main()
  44.   GameObject := "animal"
  45.   Tree := Question("Does it live in water","goldfish","canary")
  46.   Get()        # Recall prior knowledge
  47.   Game()    # Play a game
  48.   return
  49. end
  50.  
  51. procedure Game()
  52.   while Confirm("Are you thinking of ",Article(GameObject)," ",
  53.       GameObject) do {
  54.     Ask(Tree)
  55.   }
  56.   write("Thanks for a great game.")
  57.   if \Learn &
  58.       Confirm("Want to save knowledge learned this session") then Save()
  59.   return
  60. end
  61.  
  62. procedure Confirm(q1,q2,q3,q4,q5,q6)
  63.   local answer,s
  64.   static ok
  65.   initial {
  66.     ok := table()
  67.     ok["y"] := ok["yes"] := ok["yeah"] := ok["uh huh"] := "yes"
  68.     ok["n"] := ok["no"] := ok["nope"] := ok["uh uh"] := "no"
  69.   }
  70.   while /answer do {
  71.     write(q1,q2,q3,q4,q5,q6,"?")
  72.     case s := read() | exit(1) of {
  73.       "save": Save()
  74.       "get": Get()
  75.       "list": List()
  76.       "dump": Output(Tree,&output)
  77.       default: {
  78.     (answer := \ok[map(s,&ucase,&lcase)]) |
  79.           write("This is a \"yes\" or \"no\" question.")
  80.       }
  81.     }
  82.   }
  83.   return answer == "yes"
  84. end
  85.  
  86. procedure Ask(node)
  87.   local guess,question
  88.   case type(node) of {
  89.     "string": {
  90.       if not Confirm("It must be ",Article(node)," ",node,", right") then {
  91.         Learn := "yes"
  92.         write("What were you thinking of?")
  93.     guess := read() | exit(1)
  94.     write("What question would distinguish ",Article(guess)," ",
  95.         guess," from ",Article(node)," ",node,"?")
  96.     question := read() | exit(1)
  97.     if question[-1] == "?" then question[-1] := ""
  98.     question[1] := map(question[1],&lcase,&ucase)
  99.     if Confirm("For ",Article(guess)," ",guess,", what would the _
  100.         answer be") then {
  101.       return Question(question,guess,node)
  102.     }
  103.     else {
  104.       return Question(question,node,guess)
  105.     }
  106.       }
  107.     }
  108.     "Question": {
  109.       if Confirm(node.question) then {
  110.         node.yes := Ask(node.yes)
  111.       }
  112.       else {
  113.         node.no := Ask(node.no)
  114.       }
  115.     }
  116.   }
  117. end
  118.  
  119. procedure Article(word)
  120.   return if any('aeiouAEIOU',word) then "an" else "a"
  121. end
  122.  
  123. procedure Save()
  124.   local f
  125.   f := open(GameObject || "s","w")
  126.   Output(Tree,f)
  127.   close(f)
  128.   return
  129. end
  130.  
  131. procedure Output(node,f,sense)
  132.   static indent
  133.   initial indent := 0
  134.   /sense := " "
  135.   case type(node) of {
  136.     "string":  write(f,repl(" ",indent),sense,"A: ",node)
  137.     "Question": {
  138.       write(f,repl(" ",indent),sense,"Q: ", node.question)
  139.       indent +:= 1
  140.       Output(node.yes,f,"y")
  141.       Output(node.no,f,"n")
  142.       indent -:= 1
  143.     }
  144.   }
  145.   return
  146. end
  147.  
  148. procedure Get()
  149.   local f
  150.   f := open(GameObject || "s","r") | fail
  151.   Tree := Input(f)
  152.   close(f)
  153.   return
  154. end
  155.  
  156. procedure Input(f)
  157.   local nodetype,s
  158.   read(f) ? (tab(upto(~' \t')) & =("y" | "n" | "") &
  159.       nodetype := move(1) & move(2) & s := tab(0))
  160.   if nodetype == "Q" then {
  161.     return Question(s,Input(f),Input(f))
  162.   }
  163.   else {
  164.     return s
  165.   }
  166. end
  167.  
  168. procedure List()
  169.   ShowLine := ""
  170.   Show(Tree)
  171.   write(trim(ShowLine))
  172.   return
  173. end
  174.  
  175. procedure Show(node)
  176.   if type(node) == "Question" then {
  177.     Show(node.yes)
  178.     Show(node.no)
  179.   }
  180.   else {
  181.     if *ShowLine + *node > 78 then {
  182.       write(trim(ShowLine))
  183.       ShowLine := ""
  184.     }
  185.     ShowLine ||:= node || "  "
  186.   }
  187.   return
  188. end
  189. ##########
  190. calc.icn
  191. ############################################################################
  192. #
  193. #    Name:    calc.icn
  194. #
  195. #    Title:    Desk calculator
  196. #
  197. #    Author:    Ralph E. Griswold
  198. #
  199. #    Date:    February 22, 1990
  200. #
  201. ############################################################################
  202. #
  203. #  This is a simple Polish "desk calculator".  It accepts as values Icon
  204. #  integers, reals, csets, and strings (as they would appear in an Icon
  205. #  program). Other lines of input are interpreted as operations. These
  206. #  may be Icon operators, functions, or the special instructions listed
  207. #  below.
  208. #
  209. #  In the case of operator symbols, such as +, that correspond to both unary
  210. #  and binary operations, the binary one is used.  Thus, the unary operation
  211. #  is not available.
  212. #
  213. #  In case of Icon functions like write() that take an arbitrary number of
  214. #  arguments, one argument is used.
  215. #
  216. #  The special instructions are:
  217. #
  218. #    clear    remove all values from the calculator's stack
  219. #    dump    write out the contents of the stack
  220. #    print    print the top value on the stack, but do not remove it
  221. #    quit    exit the calculator
  222. #
  223. #  Example: the input lines
  224. #
  225. #    "abc"
  226. #    3
  227. #    repl
  228. #    print
  229. #
  230. #  prints "abcabcabc" and leaves this the only value on the stack.
  231. #
  232. #  Failure and most errors are detected, but in these case, arguments are
  233. #  consumed and not restored to the stack.
  234. #
  235. ############################################################################
  236.  
  237. global stack
  238.  
  239. procedure main()
  240.    local line, p, n, arglist
  241.  
  242.    stack := []
  243.  
  244.    while line := read() do {
  245.       push(stack,value(line)) | {    # if it's a value, push it
  246.          case line of {    # else check special operations
  247.             "clear":   {stack := []; next}
  248.             "dump":    {every write(image(!stack)); next}
  249.             "print":   {write(image(stack[1])); next}
  250.             "quit":    exit()
  251.             }      
  252.          if p := proc(line,3 | 2 | 1) then {    # check for procedure
  253.             n := abs(args(p))
  254.             arglist := []
  255.             every 1 to n do
  256.                push(arglist,pop(stack)) | {
  257.                   write(&errout,"*** not enough arguments ***")
  258.                   break next
  259.                   }
  260.             &error := 1    # anticipate possible error
  261.             push(stack,p!arglist) | {
  262.                if &error = 0 then {
  263.                   write(&errout,"*** error performing ",line)
  264.                   }
  265.                else write(&errout,"*** failure performing ",line)
  266.                }
  267.             }
  268.          else write(&errout,"*** invalid input: ",line)
  269.          }
  270.       }
  271. end
  272.  
  273. #  Check input to see if it's a value
  274. #
  275. procedure value(s)
  276.    local n
  277.  
  278.    if n := numeric(s) then return n
  279.    else {
  280.       s ? {
  281.          if ="\"" & s := tab(-1) & ="\"" then return escape(s)
  282.          else if ="'" & s := tab(-1) & ="'" then return cset(escape(s))
  283.          else fail
  284.          }
  285.       }
  286. end
  287.  
  288. #  Handling escape sequences is no fun
  289. #
  290. procedure escape(s)
  291.    local ns, c
  292.  
  293.    ns := ""
  294.    s ? {
  295.       while ns ||:= tab(upto('\\')) do {
  296.          move(1)
  297.          ns ||:= case c := map(move(1 | 0)) of {    # can be either case
  298.             "b":  "\b"
  299.             "d":  "\d"
  300.             "e":  "\e"
  301.             "f":  "\f"
  302.             "l":  "\n"
  303.             "n":  "\n"
  304.             "r":  "\r"
  305.             "t":  "\t"
  306.             "v":  "\v"
  307.             "'":  "'"
  308.             "\"":  "\""
  309.             "x":  hexcode()
  310.             "^":  ctrlcode()
  311.             !"01234567":  octcode()
  312.             default:  c
  313.             }
  314.          }
  315.       ns ||:= tab(0)
  316.       }
  317.    return ns
  318. end
  319.  
  320. procedure hexcode()
  321.    local i, s
  322.    static cdigs
  323.    initial cdigs := ~'0123456789ABCDEFabcdef'
  324.    
  325.    move(i := 2 | 1) ? s := tab(upto(cdigs) | 0)
  326.    move(*s - i)
  327.    return char("16r" || s)
  328. end
  329.  
  330. procedure octcode()
  331.    local i, s
  332.    static cdigs
  333.    initial cdigs := ~'01234567'
  334.    
  335.    move(-1)
  336.    move(i := 3 | 2 | 1) ? s := tab(upto(cdigs) | 0)
  337.    move(*s - i)
  338.    if s > 377 then {    # back off if too large
  339.       s := s[1:3]
  340.       move(-1)
  341.       }
  342.    return char("8r" || s)
  343. end
  344.  
  345. procedure ctrlcode(s)
  346.    return char(upto(map(move(1)),&lcase))
  347. end
  348.  
  349. ##########
  350. colm.icn
  351. ############################################################################
  352. #
  353. #    Name:    colm.icn
  354. #
  355. #    Title:    Arrange data into columns
  356. #
  357. #    Author:    Robert J. Alexander
  358. #
  359. #    Date:    December 5, 1989
  360. #
  361. ############################################################################
  362. #
  363. #  Colm -- Arrange data into columns.
  364. #
  365. #  Program  to  arrange  a  number  of  data items,  one per  line, into
  366. #  multiple  columns.  Items are arranged in column-wise order, that is,
  367. #  the sequence runs down the first column, then down the second, etc.
  368. #
  369. #  If a  null line appears in the input stream, it  signifies a break in
  370. #  the list,  and  the following  line is  taken  as  a  title  for  the
  371. #  following  data items.  No  title precedes  the initial  sequence  of
  372. #  items.
  373. #
  374. #  Usage:
  375. #
  376. #       colm [-w line_width] [-s space_between] [-m min_width]
  377. #               [-t tab_width] [-x] [-d] [file ...]
  378. #
  379. #  The parameters are:
  380. #
  381. #       line_width:     the maximum width allowed for output lines
  382. #                       (default: 80).
  383. #       space_between:  minimum number of spaces between items
  384. #                       (default: 2).
  385. #       min_width:      minimum width to be printed for each entry
  386. #                       (default: no minimum).
  387. #       tab_width:      tab width used to entab output lines.
  388. #                       (default: no tabs).
  389. #       -x              print items in row-wise order rather than
  390. #                       column-wise.
  391. #       -d (distribute) distribute columns throughout available width.
  392. #
  393. #  The command "colm -h" generates "help" text.
  394. #
  395. #  This is a  general utility,  but  it  was  written and tailored for a
  396. #  specific purpose:
  397. #
  398. #  This  utility  was written  to rearrange the file name  list from the
  399. #  Macintosh  Programmer's   Workshop  "Files"  command  into   a   more
  400. #  convenient  format.  "Files" lists  file  names in a  single  column.
  401. #  This program  takes  the  list  produced by  "Files"  and  outputs  a
  402. #  multi-column  list.  The  names  are  listed  vertically within  each
  403. #  column, and  the column width is computed dynamically  depending upon
  404. #  the sizes  of the  names listed.  A  recommendation  is  to create  a
  405. #  command file "lc" (List in Columns) as follows:
  406. #
  407. #       Files {"Parameters"} | colm
  408. #
  409. #  The output from  the  Files command  is "piped" to the "colm" program
  410. #  (this program), which prints its list in the current window.
  411. #
  412. #  By  putting both  the "lc"  command  file and the "colm" program into
  413. #  your {MPW}Tools folder, "lc" can be conveniently issued  as a command
  414. #  at any time, using the same parameters as the "Files" command.
  415.  
  416. link options, colmize
  417.  
  418. procedure main(arg)
  419.    local usage, help, opt, rowwise, distribute, maxcols, space, minwidth
  420.    local tabwidth, f, entries, entry
  421.    #
  422.    #  Define usage and help strings.
  423.    #
  424.    usage := "_
  425.    Usage:\tcolm [-w line_width] [-s space_between] [-m min_width]\n_
  426.         \t\t[-t tab_width] [-x] [file ...]\n_
  427.         \tcolm -h  for help"
  428.    help := "_
  429.         \tline_width:\tthe maximum width allowed for output lines\n_
  430.                     \t\t\t(default: 80).\n_
  431.         \tspace_between:\tminimum number of spaces between items\n_
  432.                     \t\t\t(default: 2).\n_
  433.         \tmin_width:\tminimum width to be printed for each entry\n_
  434.                     \t\t\t(default: no minimum).\n_
  435.         \ttab_width:\ttab width used to print output lines.\n_
  436.                     \t\t\t(default: no tabs).\n_
  437.         \t-x\t\tprint items in row-wise order rather than\n_
  438.                     \t\t\tcolumn-wise.\n_
  439.         \t-d (distribute)\tdistribute columns throughout available width."
  440.    #
  441.    #  Process command line options.
  442.    #
  443.    opt := options(arg,"hxdw+s+m+t+")
  444.    if \opt["h"] then write(usage,"\n\n",help) & exit()
  445.    rowwise := opt["x"]
  446.    distribute := opt["d"]
  447.    maxcols := \opt["w"] | 80
  448.    space := \opt["s"] | 2
  449.    minwidth := \opt["m"] | 0
  450.    tabwidth := (\opt["t"] | 0) + 1
  451.    if tabwidth = 1 then entab := 1
  452.    if *arg = 0 then arg := [&input]
  453.    #
  454.    #  Loop to process input files.
  455.    #
  456.    while f := get(arg) do {
  457.       f := (&input === f) | open(f) | stop("Can't open ",f)
  458.       #
  459.       #  Loop to process input groups (separated by empty lines).
  460.       #
  461.       repeat {
  462.      entries := []
  463.      #
  464.      #  Loop to build a list of non-empty lines of an input file.
  465.      #
  466.      while entry := "" ~== read(f) do {
  467.         put(entries,entry)
  468.         }
  469.      #
  470.      #  Now write the data in columns.
  471.      #
  472.      every write(entab(colmize(entries,maxcols,space,minwidth,
  473.            rowwise,distribute),tabwidth))
  474.      write("\n",read(f)) | break       # print the title line, if any
  475.      }
  476.       close(f)
  477.       write()
  478.       }
  479. end
  480. ##########
  481. concord.icn
  482. ############################################################################
  483. #
  484. #    Name:    concord.icn
  485. #
  486. #    Title:    Produce concordance
  487. #
  488. #    Author:    Ralph E. Griswold
  489. #
  490. #    Date:    December 22, 1989
  491. #
  492. ############################################################################
  493. #
  494. #     This program produces a simple concordance from standard input to standard
  495. #  output. Words less than three characters long are ignored.
  496. #
  497. #     There are two options:
  498. #
  499. #    -l n    set maximum line length to n (default 72), starts new line
  500. #    -w n    set maximum width for word to n (default 15), truncates
  501. #
  502. #     There are lots of possibilities for improving this program and adding
  503. #  functionality to it. For example, a list of words to be ignored could be
  504. #  provided.  The formatting could be made more flexible, and so on.
  505. #
  506. ############################################################################
  507. #
  508. #     Note that the program is organized to make it easy (via item()) to
  509. #  handle other kinds of tabulations.
  510. #
  511. ############################################################################
  512. #
  513. #  Links: options
  514. #
  515. ############################################################################
  516.  
  517. link options
  518.  
  519. global uses, colmax, namewidth, lineno
  520.  
  521. procedure main(args)
  522.    local opts, uselist, name, line
  523.    opts := options(args, "l+w+")        # process options
  524.    colmax := \opts["l"] | 72
  525.    namewidth := \opts["w"] | 15
  526.    uses := table("")
  527.    lineno := 0
  528.    every tabulate(item(), lineno)        # tabulate all the citations
  529.    uselist := sort(uses, 3)            # sort by uses
  530.    while name := get(uselist) do
  531.       format(left(name, namewidth) || get(uselist))
  532. end
  533.  
  534. #  Add line number to citations for name. If it already has been cited, 
  535. #  add (or increment) the number of citations.
  536. #
  537. procedure tabulate(name, lineno)
  538.    local new, count, number
  539.    lineno := string(lineno)
  540.    new := ""
  541.    uses[name] ? {
  542.       while new ||:= tab(upto(&digits)) do {
  543.          number := tab(many(&digits))
  544.          new ||:= number
  545.          }
  546.       if /number | (number ~== lineno)
  547.          then uses[name] ||:= lineno || ", "        # new line number
  548.       else {
  549.          if ="(" then count := tab(upto(')')) else count := 1
  550.          uses[name] := new || "(" || count + 1 || "), "
  551.          }
  552.       }
  553. end
  554.  
  555. #  Format the output, breaking long lines as necessary.
  556. #
  557. procedure format(line)
  558.    local i
  559.    while *line > colmax + 2 do {
  560.       i := colmax + 2
  561.       until line[i -:= 1] == " "                # back off to break point
  562.       write(line[1:i])
  563.       line := repl(" ", namewidth) || line[i + 1:0]
  564.       }
  565.    write(line[1:-2])
  566. end
  567.  
  568. #  Get an item. Different kinds of concordances can be obtained by
  569. #  modifying this procedure.
  570. #
  571. procedure item()
  572.    local i, word, line
  573.    while line := read() do {
  574.       lineno +:= 1
  575.       write(right(lineno, 6), "  ", line)
  576.       line := map(line)                # fold to lowercase
  577.       i := 1
  578.       line ? {
  579.          while tab(upto(&letters)) do {
  580.             word := tab(many(&letters))
  581.             if *word >= 3 then suspend word        # skip short words
  582.             }
  583.          }
  584.       }
  585. end
  586. ##########
  587. cross.icn
  588. ############################################################################
  589. #
  590. #    Name:    cross.icn
  591. #
  592. #    Title:    Display intersection of words
  593. #
  594. #    Author:    William P. Malloy
  595. #
  596. #    Date:    June 10, 1988
  597. #
  598. ############################################################################
  599. #  
  600. #     This program takes a list of words and tries to arrange them
  601. #  in cross-word format so that they intersect. Uppercase letters
  602. #  are mapped into lowercase letters on input.  For example, the
  603. #  input
  604. #  
  605. #          and
  606. #          eggplants
  607. #          elephants
  608. #          purple
  609. #  
  610. #  produces the output
  611. #       +---------+
  612. #       | p       |
  613. #       | u e     |
  614. #       | r g     |
  615. #       | p g     |
  616. #       |elephants|
  617. #       | e l     |
  618. #       |   and   |
  619. #       |   n     |
  620. #       |   t     |
  621. #       |   s     |
  622. #       +---------+
  623. #  
  624. #  Diagnostics: The program objects if the input contains a nonal-
  625. #  phabetic character.
  626. #  
  627. #  Comments: This program produces only one possible intersection
  628. #  and it does not attempt to produce the most compact result.  The
  629. #  program is not very fast, either.  There is a lot of room for
  630. #  improvement here. In particular, it is natural for Icon to gen-
  631. #  erate a sequence of solutions.
  632. #  
  633. ############################################################################
  634.  
  635. global fast, place, array, csave, fsave, number
  636.  
  637. procedure main()
  638.    local words, nonletter, line
  639.    nonletter := ~&letters
  640.    words := []
  641.  
  642.    while line := map(read()) do
  643.       if upto(nonletter,line) then stop("input contains nonletter")
  644.       else put(words,line)
  645.    number := *words
  646.    kross(words)
  647.  
  648. end
  649.  
  650. procedure kross(words)
  651.    local one, tst, t
  652.    array := [get(words)]
  653.    t := 0
  654.    while one := get(words) do {
  655.       tst := *words
  656.       if fit(one,array,0 | 1) then
  657.      t := 0
  658.       else {
  659.      t +:= 1
  660.          put(words,one)
  661.      if t > tst then
  662.         break
  663.      }
  664.       }
  665.    if *words = 0 then Print(array)
  666.    else write(&errout,"cannot construct puzzle")
  667. end
  668.  
  669. procedure fit(word,matrix,where)
  670.    local i, j, k, l, one, test, t, s
  671.    s := *matrix
  672.    t := *matrix[1]
  673.    every k := gen(*word) do
  674.       every i := gen(s) do
  675.          every j := gen(t) do
  676.         if matrix[i][j] == word[k] then {
  677.                # test for vertical fit
  678.                if where = 0 then {
  679.                   test := 0
  680.                   every l := (i - k + 1) to (i + (*word - k)) do
  681.                      if tstv(matrix,i,j,l,s,t) then {
  682.                         test := 1
  683.                         break
  684.                         }
  685.                   if test = 0 then
  686.                      return putvert(matrix,word,i,j,k)
  687.                   }
  688.                if where = 1 then {
  689.                   test := 0
  690.                   every l := (j - k + 1) to (j + (*word - k)) do
  691.                      if tsth(matrix,i,j,l,s,t) then {
  692.                         test := 1
  693.                         break
  694.                         }
  695.                   if test = 0 then
  696.                      return puthoriz(matrix,word,i,j,k)
  697.                   }
  698.                }
  699. end
  700.  
  701. procedure tstv(matrix,i,j,l,s,t)
  702.    return ((matrix[(l ~= i) & (s >= l) & (0 < l)][0 < j-1] ~== " ") |
  703.       (matrix[(l ~= i) & (s >= l) & (0 < l)][t >= j + 1] ~== " ") |
  704.       (matrix[(i ~= l-1) & (s >= l-1) & (0 < l-1)][j] ~== " ") |
  705.       (matrix[(i ~= l + 1) & (s >= l+1) & (0 < l + 1)][j] ~== " ") |
  706.       (matrix[(l ~= i) & (s >= l) & (0 < l)][j] ~== " "))
  707. end
  708.  
  709. procedure tsth(matrix,i,j,l,s,t)
  710.    return ((matrix[0 < i-1][(l ~= j) & (t >= l) & (0 < l)] ~== " ") |
  711.       (matrix[s >= i + 1][(l ~= j) & (t >= l) & (0 < l)] ~== " ") |
  712.       (matrix[i][(j ~= l-1) & (t >= l-1) & (0 < l-1)] ~== " ") |
  713.       (matrix[i][(j ~= l + 1) & (t >= l + 1) & (0 < l + 1)] ~== " ") |
  714.       (matrix[i][(l ~= j) & (t >= l) & (0 < l)] ~== " "))
  715. end
  716.  
  717. procedure gen(i)
  718.    local tmp, up, down
  719.    tmp := i / 2
  720.    if (i % 2) = 1 then
  721.       tmp +:= 1
  722.    suspend tmp
  723.    up := tmp
  724.    down := tmp
  725.    while (up < i) do {
  726.       suspend up +:= 1
  727.       suspend (down > 1) & (down -:= 1)
  728.       }
  729. end
  730.  
  731. # put `word' in vertically at pos(i,j)
  732.  
  733. procedure putvert(matrix,word,i,j,k)
  734.    local hdim, vdim, up, down, l, m, n
  735.    vdim := *matrix
  736.    hdim := *matrix[1]
  737.    up := 0
  738.    down := 0
  739.    up := abs(0 > (i - k))
  740.    down := abs(0 > ((vdim - i) - (*word - k)))
  741.    every m := 1 to up do
  742.       push(matrix,repl(" ",hdim))
  743.    i +:= up
  744.    every m := 1 to down do
  745.       put(matrix,repl(" ",hdim))
  746.    every l := 1 to *word do
  747.       matrix[i + l - k][j] := word[l]
  748.    return matrix
  749. end
  750.  
  751. # put `word' in horizontally at position i,j in matrix
  752.  
  753. procedure puthoriz(matrix,word,i,j,k)
  754.    local hdim, vdim, left, right, l, m, n
  755.    vdim := *matrix
  756.    hdim := *matrix[1]
  757.    left := 0
  758.    right := 0
  759.    left := (abs(0 > (j - k))) | 0
  760.    right := (abs(0 > ((hdim - j) - (*word - k)))) | 0
  761.    every m := 1 to left do
  762.       every l := 1 to vdim do
  763.        matrix[l] := " " || matrix[l]
  764.    j +:= left
  765.    every m := 1 to right do
  766.       every l := 1 to vdim do
  767.       matrix[l] ||:= " "
  768.    every l := 1 to *word do
  769.       matrix[i][j + l - k] := word[l]
  770.    return matrix
  771. end
  772.  
  773. procedure Print(matrix)
  774.    local i
  775.    write("+",repl("-",*matrix[1]),"+")
  776.    every i := 1 to *matrix do
  777.       write("|",matrix[i],"|")
  778.    write("+",repl("-",*matrix[1]),"+")
  779. end
  780. ##########
  781. csgen.icn
  782. ############################################################################
  783. #
  784. #    Name:    csgen.icn
  785. #
  786. #    Title:    Generate instances of sentences from context-sensitive grammars
  787. #
  788. #    Author:    Ralph E. Griswold
  789. #
  790. #    Date:    June 10, 1988
  791. #
  792. ############################################################################
  793. #  
  794. #     This program accepts a context-sensitive production grammar
  795. #  and generates randomly selected sentences from the corresponding
  796. #  language.
  797. #  
  798. #     Uppercase letters stand for nonterminal symbols and -> indi-
  799. #  cates the lefthand side can be rewritten by the righthand side.
  800. #  Other characters are considered to be terminal symbols. Lines
  801. #  beginning with # are considered to be comments and are ignored.
  802. #  A line consisting of a nonterminal symbol followed by a colon and
  803. #  a nonnegative integer i is a generation specification for i
  804. #  instances of sentences for the language defined by the nontermi-
  805. #  nal (goal) symbol.  An example of input to csgen is:
  806. #  
  807. #          #   a(n)b(n)c(n)
  808. #          #   Salomaa, p. 11.
  809. #          #   Attributed to M. Soittola.
  810. #          #
  811. #          X->abc
  812. #          X->aYbc
  813. #          Yb->bY
  814. #          Yc->Zbcc
  815. #          bZ->Zb
  816. #          aZ->aaY
  817. #          aZ->aa
  818. #          X:10
  819. #  
  820. #  The output of csgen for this example is
  821. #  
  822. #          aaabbbccc
  823. #          aaaaaaaaabbbbbbbbbccccccccc
  824. #          abc
  825. #          aabbcc
  826. #          aabbcc
  827. #          aaabbbccc
  828. #          aabbcc
  829. #          abc
  830. #          aaaabbbbcccc
  831. #          aaabbbccc
  832. #  
  833. #  
  834. #     A positive integer followed by a colon can be prefixed to a
  835. #  production to replicate that production, making its selection
  836. #  more likely. For example,
  837. #  
  838. #          3:X->abc
  839. #  
  840. #  is equivalent to
  841. #  
  842. #          X->abc
  843. #          X->abc
  844. #          X->abc
  845. #  
  846. #  Option: The -t option writes a trace of the derivations to stan-
  847. #  dard error output.
  848. #  
  849. #  Limitations: Nonterminal symbols can only be represented by sin-
  850. #  gle uppercase letters, and there is no way to represent uppercase
  851. #  letters as terminal symbols.
  852. #  
  853. #     There can be only one generation specification and it must
  854. #  appear as the last line of input.
  855. #  
  856. #  Comments: Generation of context-sensitive strings is a slow pro-
  857. #  cess. It may not terminate, either because of a loop in the
  858. #  rewriting rules or because of the progressive accumulation of
  859. #  nonterminal symbols.  The program avoids deadlock, in which there
  860. #  are no possible rewrites for a string in the derivation.
  861. #  
  862. #     This program would be improved if the specification of nonter-
  863. #  minal symbols were more general, as in rsg.
  864. #  
  865. ############################################################################
  866. #
  867. #  Links: options
  868. #
  869. ############################################################################
  870.  
  871. link options
  872.  
  873. global xlist
  874.  
  875. procedure main(args)
  876.    local line, goal, count, s, opts, deadlock
  877.  
  878.    opts := options(args,"x") 
  879.    deadlock := \opts["x"]
  880.    while line := read() do        #  read in grammar
  881.       if line[1] == "#" then next
  882.       else if xpairs(line) then next
  883.       else {
  884.          line ? (goal := move(1),move(1),count := (0 < integer(tab(0))))
  885.          break
  886.          }
  887.    if /count then stop("no goal specification")
  888.    every 1 to count do {        #  generate sentences
  889.       s := goal
  890.       while upto(&ucase,s) do {        #  test for nonterminal
  891.          if \deadlock then write(&errout,s)
  892.                     #  quit on deadlock
  893.          if not(s ? replace(!xlist)) then break next
  894.          until s ?:= replace(?xlist)    #  make replacement
  895.          }
  896.       write(s)
  897.       }
  898. end
  899.  
  900. #  replace left hand side by right hand side
  901. #
  902. procedure replace(a)
  903.    suspend tab(find(a[1])) || (move(*a[1]),a[2]) || tab(0)
  904. end
  905.  
  906. #  enter rewriting rule
  907. #
  908. procedure xpairs(s)
  909.    local i, a
  910.    initial xlist := []
  911.    if s ? {
  912.                 #  handle optional replication factor
  913.       i := 1(0 < integer(tab(upto(':'))),move(1)) | 1 &
  914.       a := [tab(find("->")),(move(2),tab(0))]
  915.       }
  916.    then {
  917.       every 1 to i do put(xlist,a)
  918.       return
  919.       }
  920. end
  921. ##########
  922. deal.icn
  923. ############################################################################
  924. #
  925. #    Name:    deal.icn
  926. #
  927. #    Title:    Deal bridge hands
  928. #
  929. #    Author:    Ralph E. Griswold
  930. #
  931. #    Date:    June 10, 1988
  932. #
  933. ############################################################################
  934. #  
  935. #     This program shuffles, deals, and displays hands in the game
  936. #  of bridge.  An example of the output of deal is
  937. #       ---------------------------------
  938. #  
  939. #                 S: KQ987
  940. #                 H: 52
  941. #                 D: T94
  942. #                 C: T82
  943. #  
  944. #       S: 3                S: JT4
  945. #       H: T7               H: J9863
  946. #       D: AKQ762           D: J85
  947. #       C: QJ94             C: K7
  948. #  
  949. #                 S: A652
  950. #                 H: AKQ4
  951. #                 D: 3
  952. #                 C: A653
  953. #  
  954. #       ---------------------------------
  955. #  
  956. #  Options: The following options are available:
  957. #  
  958. #       -h n Produce n hands. The default is 1.
  959. #  
  960. #       -s n Set the seed for random generation to n.  Different
  961. #            seeds give different hands.  The default seed is 0.
  962. #  
  963. ############################################################################
  964. #
  965. #  Links: options, shuffle
  966. #
  967. ############################################################################
  968.  
  969. link options, shuffle
  970.  
  971. global deck, deckimage, handsize, suitsize, denom, rank, blanker
  972.  
  973. procedure main(args)
  974.    local hands, opts
  975.  
  976.    deck := deckimage := string(&letters)    # initialize global variables
  977.    handsize := suitsize := *deck / 4
  978.    rank := "AKQJT98765432"
  979.    blanker := repl(" ",suitsize)
  980.    denom := &lcase[1+:suitsize]
  981.  
  982.    opts := options(args,"h+s+")
  983.    hands := \opts["h"] | 1
  984.    &random := \opts["s"]
  985.  
  986.    every 1 to hands do
  987.       display()
  988.  
  989. end
  990.  
  991. #  Display the hands
  992. #
  993. procedure display()
  994.    local layout, i
  995.    static bar, offset
  996.  
  997.    initial {
  998.       bar := "\n" || repl("-",33)
  999.       offset := repl(" ",10)
  1000.       }
  1001.  
  1002.    deck := shuffle(deck)
  1003.    layout := []
  1004.    every push(layout,show(deck[(0 to 3) * handsize + 1 +: handsize]))
  1005.  
  1006.    write()
  1007.    every write(offset,!layout[1])
  1008.    write()
  1009.    every i := 1 to 4 do
  1010.       write(left(layout[4][i],20),layout[2][i])
  1011.    write()
  1012.    every write(offset,!layout[3])
  1013.    write(bar)
  1014. end
  1015.  
  1016. #  Put the hands in a form to display
  1017. #
  1018. procedure show(hand)
  1019.    static clubmap, diamondmap, heartmap, spademap
  1020.    initial {
  1021.       clubmap := denom || repl(blanker,3)
  1022.       diamondmap := blanker || denom || repl(blanker,2)
  1023.       heartmap := repl(blanker,2) || denom || blanker
  1024.       spademap := repl(blanker,3) || denom
  1025.       }
  1026.    return [
  1027.       "S: " || arrange(hand,spademap),
  1028.       "H: " || arrange(hand,heartmap),
  1029.       "D: " || arrange(hand,diamondmap),
  1030.       "C: " || arrange(hand,clubmap)
  1031.       ]
  1032. end
  1033.  
  1034. #  Arrange hands for presentation
  1035. #
  1036. procedure arrange(hand,suit)
  1037.    return map(map(hand,deckimage,suit) -- ' ',denom,rank)
  1038. end
  1039. ##########
  1040. delam.icn
  1041. ############################################################################
  1042. #
  1043. #    Name:    delam.icn
  1044. #
  1045. #    Title:    Delaminate file
  1046. #
  1047. #    Author:    Thomas R. Hicks
  1048. #
  1049. #    Date:    June 10, 1988
  1050. #
  1051. ############################################################################
  1052. #  
  1053. #     This program delaminates standard input into several output
  1054. #  files according to the specified fields.  It writes the fields in
  1055. #  each line to the corresponding output files as individual lines.
  1056. #  If no data occurs in the specified position for a given input
  1057. #  line an empty output line is written. This insures that all out-
  1058. #  put files contain the same number of lines as the input file.
  1059. #  
  1060. #     If - is used for the input file, the standard input is read.
  1061. #  If - is used as an output file name, the corresponding field is
  1062. #  written to the standard output.
  1063. #  
  1064. #     The fields are defined by a list of field specifications,
  1065. #  separated by commas or colons, of the following form:
  1066. #  
  1067. #          n    the character in column n
  1068. #          n-m  the characters in columns n through m
  1069. #          n+m  m characters beginning at column n
  1070. #  
  1071. #  where the columns in a line are numbered from 1 to the length of
  1072. #  the line.
  1073. #  
  1074. #     The use of delam is illustrated by the following examples.
  1075. #  The command
  1076. #  
  1077. #          delam 1-10,5 x.txt y.txt
  1078. #  
  1079. #  reads standard input and writes characters 1 through 10 to file
  1080. #  x.txt and character 5 to file y.txt.  The command
  1081. #  
  1082. #          delam 10+5:1-10:1-10:80 mid x1 x2 end
  1083. #  
  1084. #  writes characters 10 through 14 to mid, 1 through 10 to x1 and
  1085. #  x2, and character 80 to end.  The command
  1086. #  
  1087. #          delam 1-80,1-80 - -
  1088. #  
  1089. #  copies standard input to standard output, replicating the first
  1090. #  eighty columns of each line twice.
  1091. #  
  1092. ############################################################################
  1093. #
  1094. #  Links: usage
  1095. #
  1096. ############################################################################
  1097.  
  1098. link usage
  1099.  
  1100. procedure main(a)
  1101.    local fylist, ranges
  1102.    if any(&digits,a[1]) then
  1103.       ranges := fldecode(a[1])
  1104.    else
  1105.       {
  1106.       write(&errout,"Bad argument to delam: ",a[1])
  1107.       Usage("delam fieldlist {outputfile | -} ...")
  1108.       }
  1109.    if not a[2] then
  1110.       Usage("delam fieldlist {outputfile | -} ...")
  1111.    fylist := doutfyls(a,2)
  1112.    if *fylist ~= *ranges then
  1113.       stop("Unequal number of field args and output files")
  1114.    delamr(ranges,fylist)
  1115. end
  1116.  
  1117. # delamr - do actual division of input file
  1118. #
  1119. procedure delamr(ranges,fylist)
  1120.    local i, j, k, line
  1121.    while line := read() do
  1122.       {
  1123.       i := 1
  1124.       while i <= *fylist do
  1125.          {
  1126.          j := ranges[i][1]
  1127.          k := ranges[i][2]
  1128.          if k > 0 then
  1129.             write(fylist[i][2],line[j+:k] | line[j:0] | "")
  1130.          i +:= 1
  1131.          }
  1132.       }
  1133. end
  1134.  
  1135. # doutfyls - process the output file arguments; return list
  1136. #
  1137. procedure doutfyls(a,i)
  1138.    local lst, x
  1139.    lst := []
  1140.    while \a[i] do
  1141.       {
  1142.       if x := llu(a[i],lst) then        # already in list
  1143.          lst |||:= [[a[i],lst[x][2]]]
  1144.       else                    # not in list
  1145.          if a[i] == "-" then            # standard out
  1146.             lst |||:= [[a[i],&output]]
  1147.          else                    # new file
  1148.             if not (x := open(a[i],"w")) then
  1149.                stop("Cannot open ",a[i]," for output")
  1150.             else
  1151.                lst |||:= [[a[i],x]]
  1152.       i +:= 1
  1153.       }
  1154.    return lst
  1155.  
  1156. end
  1157.  
  1158. # fldecode - decode the fieldlist argument
  1159. #
  1160. procedure fldecode(fldlst)
  1161.    local fld, flst, poslst, m, n, x
  1162.    poslst := []
  1163.    flst := str2lst(fldlst,':,')
  1164.    every fld := !flst do
  1165.       {
  1166.       if x := upto('-+',fld) then
  1167.          {
  1168.          if not (m := integer(fld[1:x])) then
  1169.             stop("bad argument in field list; ",fld)
  1170.          if not (n := integer(fld[x+1:0])) then
  1171.             stop("bad argument in field list; ",fld)
  1172.          if upto('-',fld) then
  1173.             {
  1174.             if n < m then
  1175.                n := 0
  1176.             else
  1177.                n := (n - m) + 1
  1178.             }
  1179.          }
  1180.       else {
  1181.          if not (m := integer(fld)) then
  1182.             stop("bad argument in field list; ",fld)
  1183.          n := 1
  1184.          }
  1185.       poslst |||:= [[m,n]]
  1186.       }
  1187.    return poslst
  1188. end
  1189.  
  1190. # llu - lookup file name in output file list
  1191. #
  1192. procedure llu(str,lst)
  1193.    local i
  1194.    i := 1
  1195.    while \lst[i] do
  1196.       {
  1197.       if \lst[i][1] == str then
  1198.          return i
  1199.       i +:= 1
  1200.       }
  1201. end
  1202.  
  1203. # str2lst - create a list from a delimited string
  1204. #
  1205. procedure str2lst(str,delim)
  1206.    local lst, f
  1207.    lst := []
  1208.    str ? {
  1209.       while f := (tab(upto(delim))) do
  1210.       {
  1211.       lst |||:= [f]
  1212.       move(1)
  1213.       }
  1214.         if "" ~== (f := tab(0)) then
  1215.         lst |||:= [f]
  1216.         }
  1217.    return lst
  1218. end
  1219. ##########
  1220. delamc.icn
  1221. ############################################################################
  1222. #
  1223. #    Name:    delamc.icn
  1224. #
  1225. #    Title:    Delaminate file using tab characters
  1226. #
  1227. #    Author:    Thomas R. Hicks
  1228. #
  1229. #    Date:    May 28, 1989
  1230. #
  1231. ############################################################################
  1232. #  
  1233. #     This program delaminates standard input into several output
  1234. #  files according to the separator characters specified by the
  1235. #  string following the -t option.  It writes the fields in each
  1236. #  line to the corresponding output files as individual lines. If no
  1237. #  data occurs in the specified position for a given input line an
  1238. #  empty output line is written. This insures that all output files
  1239. #  contain the same number of lines as the input file.
  1240. #  
  1241. #     If - is used as an output file name, the corresponding field
  1242. #  is written to the standard output. If the -t option is not used,
  1243. #  an ascii horizontal tab character is assumed as the default field
  1244. #  separator.
  1245. #  
  1246. #     The use of delamc is illustrated by the following examples.
  1247. #  The command
  1248. #  
  1249. #          delamc labels opcodes operands
  1250. #  
  1251. #  writes the fields of standard input, each of which is separated
  1252. #  by a tab character, to the output files labels, opcodes, and
  1253. #  operands.  The command
  1254. #  
  1255. #          delamc -t: scores names matric ps1 ps2 ps3
  1256. #  
  1257. #  writes the fields of standard input, each of which are separated
  1258. #  by a colon, to the indicated output files.  The command
  1259. #  
  1260. #          delamc -t,: oldata f1 f2
  1261. #  
  1262. #  separates the fields using either a comma or a colon.
  1263. #  
  1264. ############################################################################
  1265. #
  1266. #  Links:  usage
  1267. #
  1268. ############################################################################
  1269.  
  1270. link usage
  1271.  
  1272. procedure main(a)
  1273.    local tabset, fylist, nxtarg
  1274.    if match("-t",a[1]) then {        # tab char given
  1275.       tabset := cset(a[1][3:0])
  1276.       pop(a)                # get rid of that argument
  1277.       }
  1278.     if 0 = *(fylist := doutfyls(a)) then
  1279.        Usage("delamc [-tc] {outputfile | -} ...")
  1280.     /tabset := cset(&ascii[10])            # tab is default separator
  1281.     delamrc(tabset,fylist)            # call main routine
  1282. end
  1283.  
  1284. # delamrc - do actual division of input file using tab chars
  1285. #
  1286. procedure delamrc(tabset,fylist)
  1287.     local i, flen, line
  1288.     while line := read() do
  1289.         {
  1290.         i := 1
  1291.         flen := *fylist
  1292.         line ? while (i <= flen) do
  1293.             {
  1294.             if i = flen then
  1295.                 write(fylist[i][2],tab(0) | "")
  1296.             else
  1297.                 write(fylist[i][2],tab(upto(tabset)) | tab(0) | "")
  1298.             move(1)
  1299.             i +:= 1
  1300.             }
  1301.         }
  1302. end
  1303.  
  1304. # doutfyls - process output file arguments; return list
  1305. #
  1306. procedure doutfyls(a)
  1307.    local lst, x, i
  1308.    lst := []
  1309.    i := 1
  1310.    while \a[i] do {
  1311.       if x := llu(a[i],lst) then        # already in list
  1312.          lst |||:= [[a[i],lst[x][2]]]
  1313.       else                    # not in list
  1314.          if a[i] == "-" then            # standard out
  1315.             lst |||:= [[a[i],&output]]
  1316.          else                # a new file
  1317.             if not (x := open(a[i],"w")) then
  1318.                stop("Cannot open ",a[i]," for output")
  1319.             else lst |||:= [[a[i],x]]
  1320.       i +:= 1
  1321.       }
  1322.    return lst
  1323. end
  1324.  
  1325. # llu - lookup file name in output file list
  1326. #
  1327. procedure llu(str,lst)
  1328.    local i
  1329.    i := 1
  1330.    while \lst[i] do {
  1331.       if \lst[i][1] == str then return i
  1332.       i +:= 1
  1333.       }
  1334. end
  1335. ##########
  1336. diffn.icn
  1337. ############################################################################
  1338. #
  1339. #    Name:    diffn.icn
  1340. #
  1341. #    Title:    Show differences files
  1342. #
  1343. #    Author:    Robert J. Alexander
  1344. #
  1345. #    Date:    May 15, 1989
  1346. #
  1347. ############################################################################
  1348. #  
  1349. #   This program shows the differences between n files. Is is invoked as
  1350. #
  1351. #        diffn file1 file2 ... filen
  1352. #  
  1353. ############################################################################
  1354. #
  1355. #  Links: dif
  1356. #
  1357. ############################################################################
  1358.  
  1359. link dif
  1360. global f1,f2
  1361. record dfile(file,linenbr)
  1362.  
  1363. procedure main(arg)
  1364.   local f, i, files, drec, status
  1365.  
  1366.   if *arg < 2 then stop("usage: diffn file file ...")
  1367.   f := list(*arg)
  1368.   every i := 1 to *arg do
  1369.         f[i] := dfile(open(arg[i]) | stop("Can't open ",arg[i]),0)
  1370.   files := list(*arg)
  1371.   every i := 1 to *arg do {
  1372.     write("File ",i,": ",arg[i])
  1373.     files[i] := diff_proc(myread,f[i])
  1374.   }
  1375.   every drec := dif(files) do {
  1376.     status := "diffs"
  1377.     write("==================================")
  1378.     every i := 1 to *drec do {
  1379.       write("---- File ",i,", ",
  1380.                (drec[i].pos > f[i].linenbr & "end of file") |
  1381.          "line " || drec[i].pos,
  1382.          " ---- (",arg[i],")")
  1383.       listrange(drec[i].diffs,drec[i].pos)
  1384.     }
  1385.   }
  1386.   if /status then write("==== Files match ====")
  1387.   return
  1388. end
  1389.  
  1390. procedure listrange(dlist,linenbr)
  1391.   local x
  1392.  
  1393.   every x := !dlist do {
  1394.     write(x); linenbr +:= 1
  1395.   }
  1396.   return
  1397. end
  1398.  
  1399. procedure myread(x)
  1400.   return x.linenbr <- x.linenbr + 1 & read(x.file)
  1401. end
  1402. ##########
  1403. diffword.icn
  1404. ############################################################################
  1405. #
  1406. #    Name:    diffword.icn
  1407. #
  1408. #    Title:    List different words
  1409. #
  1410. #    Author:    Ralph E. Griswold
  1411. #
  1412. #    Date:    May 9, 1989
  1413. #
  1414. ############################################################################
  1415. #
  1416. #  This program lists all the different words in the input text.
  1417. #  The definition of a "word" is naive.
  1418. #
  1419. ############################################################################
  1420.  
  1421. procedure main()
  1422.    local letter, words, text
  1423.  
  1424.    letter := &letters
  1425.    words := set()
  1426.    while text := read() do
  1427.       text ? while tab(upto(letter)) do
  1428.          insert(words,tab(many(letter)))
  1429.    every write(!sort(words))
  1430. end
  1431. ##########
  1432. edscript.icn
  1433. ############################################################################
  1434. #
  1435. #    Name:    edscript.icn
  1436. #
  1437. #    Title:    Produce script for the ed editor
  1438. #
  1439. #    Author:    Ralph E. Griswold
  1440. #
  1441. #    Date:    June 10, 1988
  1442. #
  1443. ############################################################################
  1444. #  
  1445. #     This program takes specifications for global edits from standard
  1446. #  input and outputs an edit script for the UNIX editor ed to standard output.
  1447. #  Edscript is primarily useful for making complicated literal sub-
  1448. #  stitutions that involve characters that have syntactic meaning to
  1449. #  ed and hence are difficult to enter in ed.
  1450. #  
  1451. #     Each specification begins with a delimiter, followed by a tar-
  1452. #  get string, followed by the delimiter, followed by the replace-
  1453. #  ment string, followed by the delimiter.  For example
  1454. #  
  1455. #          |...|**|
  1456. #          |****||
  1457. #  
  1458. #  specifies the replacement of all occurrences of three consecutive
  1459. #  periods by two asterisks, followed by the deletion of all
  1460. #  occurrences of four consecutive asterisks.  Any character may be
  1461. #  used for the delimiter, but the same character must be used in
  1462. #  all three positions in any specification, and the delimiter char-
  1463. #  acter cannot be used in the target or replacement strings.
  1464. #  
  1465. #  Diagnostic:
  1466. #  
  1467. #     Any line that does not have proper delimiter structure is noted
  1468. #  and does not contribute to the edit script.
  1469. #  
  1470. #  Reference:
  1471. #  
  1472. #     "A Tutorial Introduction to the UNIX Text Editor", Brian W. Kernighan.
  1473. #  AT&T Bell Laboratories.
  1474. #  
  1475. ############################################################################
  1476.  
  1477. procedure main()
  1478.    local line, image, object, char
  1479.    while line := read() do {
  1480.       line ? {
  1481.          char := move(1) | {error(line); next}
  1482.          image := tab(find(char)) | {error(line); next}
  1483.          move(1)
  1484.          object := tab(find(char)) | {error(line); next}
  1485.          }
  1486.       write("g/",xform(image),"/s//",xform(object),"/g")
  1487.    }
  1488.    write("w\nq")
  1489. end
  1490.  
  1491. #  process characters that have meaning to ed
  1492. #
  1493. procedure insert()
  1494.    static special
  1495.    initial special := '\\/^&*[.$'
  1496.    suspend {
  1497.       tab(upto(special)) ||
  1498.       "\\" ||
  1499.       move(1) ||
  1500.       (insert() | tab(0))
  1501.       }
  1502. end
  1503.  
  1504. procedure error(line)
  1505.    write(&errout,"*** erroneous input: ",line)
  1506. end
  1507.  
  1508. #  transform line
  1509. #
  1510. procedure xform(line)
  1511.    line ?:= insert()
  1512.    return line
  1513. end
  1514. ##########
  1515. empg.icn
  1516. ############################################################################
  1517. #
  1518. #    Name:    empg.icn
  1519. #
  1520. #    Title:    Expression Measurement Program Generator
  1521. #
  1522. #    Author:    Ralph E. Griswold
  1523. #
  1524. #    Date:    March 8, 1990
  1525. #
  1526. ############################################################################
  1527. #
  1528. #     This program reads Icon expressions, one per line, and writes out
  1529. #  and Icon program, which when run, times the expressions and reports
  1530. #  average evaluation time and storage allocation.
  1531. #
  1532. #     Lines beginning with a # are treated as comments and written to the
  1533. #  output program so as to be written as comments when the output program is
  1534. #  run.
  1535. #
  1536. #     Lines beginning with a : are passed to the output program to be
  1537. #  evaluated, but not timed.
  1538. #
  1539. #     Lines beginning with a $ are included at the end of the output
  1540. #  program as declarations.
  1541. #
  1542. #     All other lines are timed in loops.
  1543. #
  1544. #     An example of input is:
  1545. #
  1546. #    :T := table(0)
  1547. #    $record complex(r,i)
  1548. #    T[1]
  1549. #    complex(0.0,0.0)
  1550. #
  1551. #     The resulting output program evaluates the expressions on the last two
  1552. #  lines and reports their average time and storage allocation.
  1553. #
  1554. #     Loop overhead for timing is computed first. The default number of
  1555. #  iterations s 10000. A different number can be given on the command line
  1556. #  when empg is executed, as in
  1557. #
  1558. #    iconx empg 1000 <test.exp >test.icn
  1559. #
  1560. #  which takes expressions from test.exp, computes loop overhead using 1000
  1561. #  iterations, and writes the measurement program to test.icn.
  1562. #
  1563. #     The default number of iterations for timing expressions is 1000. A
  1564. #  different number can be given on the command line when the measurement
  1565. #  program is run, as in
  1566. #
  1567. #    icont test
  1568. #    iconx test 5000
  1569. #
  1570. #  which times the expressions in test.icn using 5000 iterations.
  1571. #
  1572. #     If a garbage collection occurs during timing, the average time is
  1573. #  likely to be significantly distorted and average allocation cannot be
  1574. #  computed.  In this case, the number of garbage collections is reported
  1575. #  instead.  To avoid misleading results as a consequence, measurement
  1576. #  programs should be run with Icon's region sizes set to as large values
  1577. #  as possible. To avoid residual effects of one timed expression on
  1578. #  another, expressions that allocate significant amounts of storage
  1579. #  should be measured in separate programs.
  1580. #
  1581. #     The number of iterations used to compute loop overhead im empg
  1582. #  and the number of iterations used to time expressions in measurement
  1583. #  programs should be chosen so that the effects of low clock resolution
  1584. #  are minimized.  In particular, systems with very fast CPUs but
  1585. #  low clock resolution (like 386 and 486 processors running under
  1586. #  MS-DOS) need large values.
  1587. #
  1588. ############################################################################
  1589. #
  1590. #  Links: numbers (in measurement programs, not in empg.icn)
  1591. #
  1592. ############################################################################
  1593.  
  1594. procedure main(argl)
  1595.    local i, decls, line, input
  1596.    i := integer(argl[1]) | 10000
  1597.    decls := []                # list for declarations
  1598.    write("link numbers")
  1599.    write("global _Count, _Coll, _Store, _Overhead, _Names")
  1600.    write("procedure main(argl)")
  1601.    write("   _Iter := argl[1] | 1000")
  1602.    write("   _Names := [\"static\",\"string\",\"block \"]")
  1603.    write("   write(\"iterations: \",_Iter)")
  1604.    write("   write(\"&version: \",&version)")
  1605.    write("   write(\"&host: \",&host)")
  1606.    write("   write(\"&dateline: \",&dateline)")
  1607.    write("   write(\"region sizes: \")")
  1608.    write("   _I := 1")
  1609.    write("   every _S := ®ions do {")
  1610.    write("      write(\"   \",_Names[_I],\"   \",_S)")
  1611.    write("      _I +:= 1")
  1612.    write("      }")
  1613.    write("   _Count := ",i)
  1614.    write("   _Itime := &time")
  1615.    write("   every 1 to _Count do { &null }")
  1616.    write("   _Overhead := real(&time - _Itime) / _Count")
  1617.    write("   _Itime := &time")
  1618.    write("   every 1 to _Count do { &null & &null }")
  1619.    write("   _Overhead := real(&time - _Itime) / _Count - _Overhead")
  1620.    write("   _Count := _Iter")
  1621.    while line := read(input) do 
  1622.       case line[1] of {
  1623.          ":": {            # evaluate but do not time
  1624.             write("   ",line[2:0])
  1625.             write("   write(",image(line[2:0]),")")
  1626.             }
  1627.          "$": {            # line of declaration
  1628.             put(decls,line[2:0])
  1629.             write("   write(",image(line[2:0]),")")
  1630.             }
  1631.          "#":            # comment
  1632.             write("   write(",image(line),")")
  1633.          default: {        # time in a loop
  1634.             write("   write(",image(line),")")
  1635.             write("   _Prologue()")
  1636.             write("   _Itime := &time")
  1637.             write("   every 1 to _Count do {")
  1638.             write("      &null & ", line)
  1639.             write("      }")
  1640.             write("   _Epilogue(&time - _Itime)")
  1641.             }
  1642.       }
  1643.    write("end")
  1644.    write("procedure _Prologue()")
  1645.    write("   _Store := []")
  1646.    write("   _Coll := []")
  1647.    write("   collect()")
  1648.    write("   every put(_Store,&storage)")
  1649.    write("   every put(_Coll,&collections)")
  1650.    write("end")
  1651.    write("procedure _Epilogue(_Time)")
  1652.    write("   every put(_Store,&storage)")
  1653.    write("   every put(_Coll,&collections)")
  1654.    write("   write(fix(real(_Time) / _Count - _Overhead,1,8),\" ms.\")")
  1655.    write("   if _Coll[1] = _Coll[5] then {")
  1656.    write("      write(\"average allocation:\",)")
  1657.    write("         every _I := 1 to 3 do")
  1658.    write("            write(\"   \",_Names[_I],fix(real(_Store[_I + 3] - _Store[_I]),_Count,12))")
  1659.    write("      }")
  1660.    write("   else {")
  1661.    write("   write(\"garbage collections:\")")
  1662.    write("   write(\"   total \",right(_Coll[5] - _Coll[1],4))")
  1663.    write("   every _I := 6 to 8 do write(\"   \",_Names[_I - 5],right(_Coll[_I] - _Coll[_I - 4],4))")
  1664.    write("      }")
  1665.    write("   write()")
  1666.    write("end")
  1667.    every write(!decls)        # write out declarations
  1668. end
  1669. ##########
  1670. farb.icn
  1671. ############################################################################
  1672. #
  1673. #    Name:    farb.icn
  1674. #
  1675. #    Title:    Generate Farberisms
  1676. #
  1677. #    Author:    Ralph E. Griswold
  1678. #
  1679. #    Date:    June 10, 1988
  1680. #
  1681. ############################################################################
  1682. #  
  1683. #     Dave Farber, co-author of the original SNOBOL programming
  1684. #  language, is noted for his creative use of the English language.
  1685. #  Hence the terms ``farberisms'' and ``to farberate''.  This pro-
  1686. #  gram produces a randomly selected farberism.
  1687. #  
  1688. #  Notes: Not all of the farberisms contained in this program were
  1689. #  uttered by the master himself; others have learned to emulate
  1690. #  him.  A few of the farberisms may be objectionable to some per-
  1691. #  sons.  ``I wouldn't marry her with a twenty-foot pole.''
  1692. #  
  1693. ############################################################################
  1694. #
  1695. #  Program note:
  1696. #
  1697. #     This program is organized into several procedures to avoid oveflowing
  1698. #  the default table sizes in the Icon translator and linker.
  1699. #
  1700. ############################################################################
  1701.  
  1702. procedure main(arg)
  1703.    local count
  1704.  
  1705.    &random := map(&clock,":","0")
  1706.    count := integer(arg[1]) | 1
  1707.  
  1708.    every write(|??[farb1(),farb2(),farb3(),farb4()]) \ count
  1709.  
  1710. end
  1711.  
  1712. procedure farb1()
  1713.    return [
  1714.       "I enjoy his smiling continence.",
  1715.       "Picasso wasn't born in a day.",
  1716.       "I'll be there with spades on.",
  1717.       "Beware a Trojan bearing a horse.",
  1718.       "A hand in the bush is worth two anywhere else.",
  1719.       "All the lemmings are going home to roost.",
  1720.       "Anybody who marries her would stand out like a sore thumb.",
  1721.       "Before they made him they broke the mold.",
  1722.       "He's casting a red herring on the face of the water.",
  1723.       "Clean up or fly right.",
  1724.       "Come down off your charlie horse.",
  1725.       "Don't burn your bridges until you come to them.",
  1726.       "Don't count your chickens until the barn door is closed.",
  1727.       "Don't do anything I wouldn't do standing up in a hammock.",
  1728.       "Don't get your eye out of joint.",
  1729.       "Don't just stand there like a sitting duck.",
  1730.       "Don't look a mixed bag in the mouth.",
  1731.       "Don't look at me in that tone of voice.",
  1732.       "Don't make a molehill out of a can of beans.",
  1733.       "Don't make a tempest out of a teapot."
  1734.       ]
  1735. end
  1736.  
  1737. procedure farb2()
  1738.    return [
  1739.       "Don't upset the apple pie.",
  1740.       "Every cloud has a blue horizon.",
  1741.       "She's faster than the naked eye.",
  1742.       "Feather your den with somebody else's nest.",
  1743.       "From here on up, it's down hill all the way.",
  1744.       "Go fly your little red wagon somewhere else.",
  1745.       "Half a worm is better than none.",
  1746.       "He doesn't know which side his head is buttered on.",
  1747.       "He has feet of molasses.",
  1748.       "He hit the nose right on the head.",
  1749.       "He knows which side his pocketbook is buttered on.",
  1750.       "He smokes like a fish.",
  1751.       "He was hoisted by a skyhook on his own petard!",
  1752.       "He was putrified with fright.",
  1753.       "He would forget his head if it weren't screwed up.",
  1754.       "He's as happy as a pig at high tide.",
  1755.       "He's been living off his laurels for years.",
  1756.       "He's got a rat's nest by the tail.",
  1757.       "He's got four sheets in the wind.",
  1758.       "He's letting ground grow under his feet.",
  1759.       "He's lying through his britches.",
  1760.       "He's procrastinating like a bandit.",
  1761.       "He's reached the crescent of his success.",
  1762.       "He's so far above me I can't reach his bootstraps.",
  1763.       "He's too smart for his own bootstraps.",
  1764.       "His foot is in his mouth up to his ear.",
  1765.       "History is just a repetition of the past.",
  1766.       "I apologize on cringed knees.",
  1767.       "I don't know which dagger to clothe it in.",
  1768.       "I hear the handwriting on the wall.",
  1769.       "I wouldn't marry her with a twenty-foot pole.",
  1770.       "I'll procrastinate when I get around to it.",
  1771.       "I'm going to throw myself into the teeth of the gamut.",
  1772.       "I'm parked somewhere in the boondoggles."
  1773.       ]
  1774. end
  1775.  
  1776. procedure farb3()
  1777.    return [
  1778.       "I'm walking on cloud nine.",
  1779.       "I've got to put my duff to the grindstone.",
  1780.       "I've had it up to the hilt.",
  1781.       "If Calvin Coolidge were alive today, he'd turn over in his grave.",
  1782.       "If the onus fits, wear it.",
  1783.       "Is he an Amazon!",
  1784.       "It fills a well-needed gap.",
  1785.       "It is better to have tried and failed than never to have failed at all.",
  1786.       "It looks like it's going to go on ad infinitum for a while.",
  1787.       "It sounds like roses to my ears.",
  1788.       "It's a caterpillar in pig's clothing.",
  1789.       "It's a fiat accompli.",
  1790.       "It's a fool's paradise wrapped in sheep's clothing.",
  1791.       "It's a monkey wrench in your ointment.",
  1792.       "It's a new high in lows.",
  1793.       "It's bouncing like a greased pig.",
  1794.       "It's enough to make you want to rot your socks.",
  1795.       "It's like talking to a needle in a haystack.",
  1796.       "It's like trying to light a fire under a lead camel.",
  1797.       "It's not his bag of tea.",
  1798.       "It's so unbelieveable you wouldn't believe it.",
  1799.       "Just because it's there, you don't have to mount it.",
  1800.       "Keep your ear peeled!",
  1801.       "Let's not drag any more dead herrings across the garden path.",
  1802.       "Let's skin another can of worms.",
  1803.       "Look at the camera and say `bird'.",
  1804.       "Look before you turn the other cheek.",
  1805.       "Men, women, and children first!",
  1806.       "Necessity is the mother of strange bedfellows.",
  1807.       "Never feed a hungry dog an empty loaf of bread.",
  1808.       "No rocks grow on Charlie.",
  1809.       "No sooner said, the better.",
  1810.       "Nobody could fill his socks.",
  1811.       "Nobody is going to give you the world in a saucer.",
  1812.       "Nobody marches with the same drummer.",
  1813.       "Not by the foggiest stretch of the imagination!",
  1814.       "Not in a cocked hat, you don't!",
  1815.       "People in glass houses shouldn't call the kettle black.",
  1816.       "Put it on the back of the stove and let it simper."
  1817.       ]
  1818. end
  1819.  
  1820. procedure farb4()
  1821.    return [
  1822.       "Put the onus on the other foot.",
  1823.       "Rome wasn't built on good intentions alone.",
  1824.       "She has eyes like two holes in a burnt blanket.",
  1825.       "She's a virgin who has never been defoliated.",
  1826.       "She's trying to feather her own bush.",
  1827.       "Somebody's flubbing his dub.",
  1828.       "It's steel wool and a yard wide.",
  1829.       "Straighten up or fly right.",
  1830.       "Strange bedfellows flock together.",
  1831.       "That's a bird of a different color.",
  1832.       "That's a horse of a different feather.",
  1833.       "That's a sight for deaf ears.",
  1834.       "That's the way the old ball game bounces.",
  1835.       "The die has been cast on the face of the waters.",
  1836.       "The early bird will find his can of worms.",
  1837.       "The foot that rocks the cradle is usually in the mouth.",
  1838.       "The onus is on the other foot.",
  1839.       "The whole thing is a hairy potpourri.",
  1840.       "There are enough cooks in the pot already.",
  1841.       "There's a dark cloud on every rainbow's horizon.",
  1842.       "There's a flaw in the ointment.",
  1843.       "There's going to be hell and high water to pay.",
  1844.       "They don't stand a teabag's chance in hell.",
  1845.       "They sure dipsied his doodle.",
  1846.       "This ivory tower we're living in is a glass house.",
  1847.       "Time and tide strike but once."
  1848.       ]
  1849. end
  1850. ##########
  1851. fileprnt.icn
  1852. ############################################################################
  1853. #
  1854. #    Name:    fileprnt.icn
  1855. #
  1856. #    Title:    Display representations of characters in file
  1857. #
  1858. #    Author:    Ralph E. Griswold
  1859. #
  1860. #    Date:    November 21, 1989
  1861. #
  1862. ############################################################################
  1863. #
  1864. #     This program reads the file specified as a command-line argument and
  1865. #  writes out a representation of each character in several forms:
  1866. #  hexadecimal, octal, decimal, symbolic, and ASCII code.
  1867. #
  1868. #     Inpupt is from a named file rather than standard input, so that it
  1869. #  can be opened in untranslated mode.  Otherwise, on some systems, input
  1870. #  is terminated for characters like ^Z.
  1871. #
  1872. #     Since this program is comparatively slow, it is not suitable
  1873. #  for processing very large files.
  1874. #
  1875. #     There are several useful extensions that could be added to this program,
  1876. #  including other character representations, an option to skip an initial
  1877. #  portion of the input file, and suppression of long ranges of identical
  1878. #  characters.
  1879. #
  1880. ############################################################################
  1881. #
  1882. #  Requires: co-expressions
  1883. #
  1884. ############################################################################
  1885. #
  1886. #  Program note:
  1887. #
  1888. #     This program illustrates a situation in which co-expressions can be
  1889. #  used to considerably simplify programming.  Try recasting it without
  1890. #  co-expressions.
  1891. #
  1892. ############################################################################
  1893.  
  1894. procedure main(arg)
  1895.    local width, chars, nonprint, prntc, asc, hex, sym, dec
  1896.    local oct, ascgen, hexgen, octgen, chrgen, prtgen, c
  1897.    local cnt, line, length, bar, input
  1898.  
  1899.    input := open(arg[1],"u") | stop("*** cannot open input file")
  1900.    width := 16
  1901.    chars := string(&cset)
  1902.    nonprint := chars[1:33] || chars[128:0]
  1903.    prntc := map(chars,nonprint,repl(" ",*nonprint))
  1904.  
  1905.    asc := table("   |")
  1906.    hex := table()
  1907.    sym := table()
  1908.    dec := table()
  1909.    oct := table()
  1910.    ascgen := create "NUL" | "SOH" | "STX" | "ETX" | "EOT" | "ENQ" | "ACK" |
  1911.       "BEL" | " BS" | " HT" | " LF" |  " VT" | " FF" | " CR" | " SO" | " SI" |
  1912.       "DLE" | "DC1" | "DC2" | "DC3" | "DC4" | "NAK" | "SYN" |  "ETB" | "CAN" |
  1913.       " EM" | "SUB" | "ESC" | " FS" | " GS" | " RS" | " US" | " SP"
  1914.    hexgen := create !"0123456789ABCDEF" || !"0123456789ABCDEF"
  1915.    octgen := create (0 to 3) || (0 to 7) || (0 to 7)
  1916.    chrgen := create !chars
  1917.    prtgen := create !prntc
  1918.    every c := !&cset do {
  1919.       asc[c] := @ascgen || "|"
  1920.       oct[c] := @octgen || "|"
  1921.       hex[c] := " " || @hexgen || "|"
  1922.       sym[c] := " " || @prtgen || " |"
  1923.       }
  1924.    asc[char(127)] := "DEL|"            # special case
  1925.  
  1926.    cnt := -1    # to handle zero-indexing of byte count
  1927.  
  1928.    while line := reads(input,width) do {    # read one line's worth
  1929.       length := *line    # may not have gotten that many
  1930.       bar := "\n" || repl("-",5 + length * 4)
  1931.       write()
  1932.       writes("BYTE|")
  1933.       every writes(right(cnt + (1 to length),3),"|")
  1934.       write(bar)
  1935.       writes(" HEX|")
  1936.       every writes(hex[!line])
  1937.       write(bar)
  1938.       writes(" OCT|")
  1939.       every writes(oct[!line])
  1940.       write(bar)
  1941.       writes(" DEC|")
  1942.       every writes(right(ord(!line),3),"|")
  1943.       write(bar)
  1944.       writes(" SYM|")
  1945.       every writes(sym[!line])
  1946.       write(bar)
  1947.       writes(" ASC|")
  1948.       every writes(asc[!line])
  1949.       write(bar)
  1950.       cnt +:= length
  1951.       }
  1952. end
  1953. ##########
  1954. filter.icn
  1955. ############################################################################
  1956. #
  1957. #    Name:    filter.icn
  1958. #
  1959. #    Title:    Generic filter skeleton in Icon
  1960. #
  1961. #    Author:    Robert J. Alexander
  1962. #
  1963. #    Date:    December 5, 1989
  1964. #
  1965. ############################################################################
  1966. #
  1967. #  Generic filter skeleton in Icon.
  1968. #
  1969. #  This program is not intended to be used as is -- it serves as a
  1970. #  starting point for creation of filter programs.  Command line
  1971. #  options, file names, and tabbing are handled by the skeleton.  You
  1972. #  need only provide the filtering code.
  1973. #
  1974. #  As it stands, filter.icn simply copies the input file(s) to
  1975. #  standard output.
  1976. #
  1977. #  Multiple files can be specified as arguments, and will be processed
  1978. #  in sequence.  A file name of "-" represents the standard input file.
  1979. #  If there are no arguments, standard input is processed.
  1980. #
  1981. ############################################################################
  1982. #
  1983. #  Links: options
  1984. #
  1985. ############################################################################
  1986.  
  1987. link options
  1988.  
  1989. procedure main(arg)
  1990.    local opt, tabs, Detab, fn, f, line
  1991.    #
  1992.    #  Process command line options and file names.
  1993.    #
  1994.    opt := options(arg,"t+")      # e.g. "fs:i+r." (flag, string, integer, real)
  1995.    if *arg = 0 then arg := ["-"] # if no arguments, standard input
  1996.    tabs := (\opt["t"] | 8) + 1   # tabs default to 8
  1997.    Detab := tabs = 1 | detab     # if -t 0, no detabbing
  1998.    #
  1999.    #  Loop to process files.
  2000.    #
  2001.    every fn := !arg do {
  2002.       f := if fn == "-" then &input else
  2003.         open(fn) | stop("Can't open input file \"",fn,"\"")
  2004.       #
  2005.       #  Loop to process lines of file (in string scanning mode).
  2006.       #
  2007.       while line := Detab(read(f)) do line ? {
  2008.      write(line)       # copy line to standard output
  2009.      }
  2010.       #
  2011.       #  Close this file.
  2012.       #
  2013.       close(f)
  2014.       }
  2015.    #
  2016.    #  End of program.
  2017.    #
  2018. end
  2019. ##########
  2020. format.icn
  2021. ############################################################################
  2022. #
  2023. #    Name:    format.icn
  2024. #
  2025. #    Title:    Filter to word wrap a range of text
  2026. #
  2027. #    Author:    Robert J. Alexander
  2028. #
  2029. #    Date:    December 5, 1989
  2030. #
  2031. ############################################################################
  2032. #
  2033. #  Filter to word wrap a range of text.
  2034. #
  2035. #  A number of options are available, including full justification (see
  2036. #  usage text, below).  All lines that have the same indentation as the
  2037. #  first line (or same comment leading character format if -c option)
  2038. #  are wrapped.  Other lines are left as is.
  2039. #
  2040. #  This program is useful in conjunction with editors that can invoke
  2041. #  filters on a range of selected text.
  2042. #
  2043. #  The -c option attemps to establish the form of a comment based on the
  2044. #  first line, then does its best to deal properly with the following
  2045. #  lines.  The types of comment lines that are handled are those in
  2046. #  which each line starts with a "comment" character string (possibly
  2047. #  preceded by spaces).  While formatting comment lines, text lines
  2048. #  following the prototype line that don't match the prototype but are
  2049. #  flush with the left margin are also formatted as comments.  This
  2050. #  feature simplifies initially entering lengthy comments or making
  2051. #  major modifications, since new text can be entered without concern
  2052. #  for comment formatting, which will be done automatically later.
  2053. #
  2054. ############################################################################
  2055. #
  2056. #  Links: options
  2057. #
  2058. ############################################################################
  2059.  
  2060. link options
  2061.  
  2062. global width
  2063.  
  2064. procedure main(arg)
  2065.    local usage, opts, tabs, comment, format, just1, space, nspace, wchar
  2066.    local line, pre, empty, outline, spaces, word, len
  2067.    #
  2068.    #  Process the options.
  2069.    #
  2070.    usage := 
  2071.      "usage: ifmt [-n] [-w N] [-t N]\n_
  2072.             \t-w N\tspecify line width (default 72)\n_
  2073.             \t-t N\tspecify tab width (default 8)\n_
  2074.             \t-j\tfully justify lines\n_
  2075.             \t-J\tfully justify last line\n_
  2076.             \t-c\tattemp to format program comments\n_
  2077.             \t-h\tprint help message"
  2078.    opts := options(arg,"ht+w+cjJ")
  2079.    if \opts["h"] then stop(usage)
  2080.    width := \opts["w"] | 72
  2081.    tabs := \opts["t"] | 8
  2082.    comment := opts["c"]
  2083.    format := if \opts["j"] then justify else 1
  2084.    just1 := opts["J"]
  2085.    #
  2086.    #  Initialize variables.
  2087.    #
  2088.    space := ' \t'
  2089.    nspace := ~space
  2090.    wchar := nspace
  2091.    #
  2092.    #  Read the first line to establish a prototype of comment format
  2093.    #  if -c option, or of leading spaces if normal formatting.
  2094.    #
  2095.    line := ((tabs >= 2,detab) | 1)(read(),tabs) | exit()
  2096.    line ?
  2097.       pre := (tab(many(space)) | "") ||
  2098.      if \comment then
  2099.         tab(many(nspace)) || tab(many(space)) |
  2100.             stop("### Can't establish comment pattern")
  2101.      else
  2102.         ""
  2103.    width -:= *pre
  2104.    empty := trim(pre)
  2105.    outline := spaces := ""
  2106.    repeat {
  2107.       line ? {
  2108.      #
  2109.      #  If this line indicates a formatting break...
  2110.      #
  2111.      if (=empty & pos(0)) | (=pre & any(space) | pos(0)) |
  2112.             (/comment & not match(pre)) then {
  2113.         write(pre,"" ~== outline)
  2114.         outline := spaces := ""
  2115.         write(line)
  2116.         }
  2117.      #
  2118.      #  Otherwise continue formatting.
  2119.      #
  2120.      else {
  2121.         =pre
  2122.         tab(0) ? {
  2123.            tab(many(space))
  2124.            while word := tab(many(wchar)) & (tab(many(space)) | "") do {
  2125.           if *outline + *spaces + *word > width then {
  2126.              write(pre,"" ~== format(outline))
  2127.              outline := spaces := ""
  2128.              }
  2129.           outline ||:= spaces || word
  2130.           spaces := if any('.:?!',word[-1]) then "  " else " "
  2131.           }
  2132.            }
  2133.         }
  2134.      }
  2135.       line := ((tabs >= 2,detab) | 1)(read(),tabs) | break
  2136.       }
  2137.    write(((tabs >= 2,entab) | 1)(pre,tabs),
  2138.      "" ~== (if \just1 then justify else 1)(outline))
  2139. end
  2140.  
  2141. #
  2142. #  justify() -- add spaces between words until the line length = "width".
  2143. #
  2144. procedure justify(s)
  2145.    local min, spaces, len
  2146.  
  2147.    while *s < width do {
  2148.       min := 10000
  2149.       s ? {
  2150.      while tab(find(" ")) do {
  2151.         len := *tab(many(' '))
  2152.         if min >:= len then spaces := []
  2153.         if len = min then put(spaces,&pos)
  2154.         }
  2155.      }
  2156.       if /spaces then break
  2157.       s[?spaces+:0] := " "
  2158.       }
  2159.    return s
  2160. end
  2161. ##########
  2162. gcomp.icn
  2163. ############################################################################
  2164. #
  2165. #    Name:    gcomp.icn
  2166. #
  2167. #    Title:    Produce complement of file specification
  2168. #
  2169. #    Author:    William H. Mitchell, modified by Ralph E. Griswold    
  2170. #
  2171. #    Date:    December 27, 1989
  2172. #
  2173. ############################################################################
  2174. #
  2175. #     This program produces a list of the files in the current directory
  2176. #  that do not appear among the arguments.  For example,
  2177. #  
  2178. #       gcomp *.c
  2179. #  
  2180. #  produces a list of files in the current directory that do
  2181. #  not end in .c.  As another example, to remove all the files
  2182. #  in the current directory that do not match Makefile, *.c, and *.h
  2183. #  the following can be used:
  2184. #  
  2185. #       rm `gcomp Makefile *.c *.h`
  2186. #  
  2187. #  The files . and .. are not included in the output, but other
  2188. #  `dot files' are.
  2189. #
  2190. ############################################################################
  2191.  
  2192. procedure main(args)
  2193.    local files
  2194.    files := set()
  2195.    read(open("echo * .*","rp")) ? while insert(files,tab(upto(' ') | 0)) do
  2196.       move(1) | break
  2197.    every delete(files,"." | ".." | !args)
  2198.    every write(!sort(files))
  2199. end
  2200. ##########
  2201. grpsort.icn
  2202. ############################################################################
  2203. #
  2204. #    Name:    grpsort.icn
  2205. #
  2206. #    Title:    Sort groups of lines
  2207. #
  2208. #    Author:    Thomas R. Hicks
  2209. #
  2210. #    Date:    June 10, 1988
  2211. #
  2212. ############################################################################
  2213. #  
  2214. #     This program sorts input containing ``records'' defined to be
  2215. #  groups of consecutive lines. Output is written to standard out-
  2216. #  put.  Each input record is separated by one or more repetitions
  2217. #  of a demarcation line (a line beginning with the separator
  2218. #  string).  The first line of each record is used as the key.
  2219. #  
  2220. #     If no separator string is specified on the command line, the
  2221. #  default is the empty string. Because all input lines are trimmed
  2222. #  of whitespace (blanks and tabs), empty lines are default demarca-
  2223. #  tion lines. The separator string specified can be an initial sub-
  2224. #  string of the string used to demarcate lines, in which case the
  2225. #  resulting partition of the input file may be different from a
  2226. #  partition created using the entire demarcation string.
  2227. #  
  2228. #     The -o option sorts the input file but does not produce the
  2229. #  sorted records.  Instead it lists the keys (in sorted order) and
  2230. #  line numbers defining the extent of the record associated with
  2231. #  each key.
  2232. #  
  2233. #     The use of grpsort is illustrated by the following examples.
  2234. #  The command
  2235. #  
  2236. #          grpsort "catscats" <x >y
  2237. #  
  2238. #  sorts the file x, whose records are separated by lines containing
  2239. #  the string "catscats", into the file y placing a single line of
  2240. #  "catscats" between each output record. Similarly, the command
  2241. #  
  2242. #          grpsort "cats" <x >y
  2243. #  
  2244. #  sorts the file x as before but assumes that any line beginning
  2245. #  with the string "cats" delimits a new record. This may or may not
  2246. #  divide the lines of the input file into a number of records dif-
  2247. #  ferent from the previous example.  In any case, the output
  2248. #  records will be separated by a single line of "cats".  Another
  2249. #  example is
  2250. #  
  2251. #          grpsort -o <bibliography >bibkeys
  2252. #  
  2253. #  which sorts the file bibliography and produces a sorted list of
  2254. #  the keys and the extents of the associated records in bibkeys.
  2255. #  Each output key line is of the form:
  2256. #  
  2257. #          [s-e] key
  2258. #  
  2259. #  where
  2260. #  
  2261. #          s     is the line number of the key line
  2262. #          e     is the line number of the last line
  2263. #          key   is the actual key of the record
  2264. #  
  2265. #  
  2266. ############################################################################
  2267. #
  2268. #  Links: usage
  2269. #
  2270. ############################################################################
  2271.  
  2272. link usage
  2273.  
  2274. global lcount, linelst, ordflag
  2275.  
  2276. procedure main(args)
  2277.    local division, keytable, keylist, line, info, nexthdr, null
  2278.    linelst := []
  2279.    keytable := table()
  2280.    lcount := 0
  2281.  
  2282.    if *args = 2 then
  2283.       if args[1] == "-o" then
  2284.           ordflag := pop(args)
  2285.       else
  2286.           Usage("groupsort [-o] [separator string] <file >sortedfile")
  2287.  
  2288.    if *args = 1 then {
  2289.       if args[1] == "?" then
  2290.           Usage("groupsort [-o] [separator string] <file >sortedfile")
  2291.       if args[1] == "-o" then
  2292.           ordflag := pop(args)
  2293.       else
  2294.           division := args[1]
  2295.       }
  2296.  
  2297.    if *args = 0 then
  2298.       division := ""
  2299.  
  2300.    nexthdr := lmany(division) | fail    # find at least one record or quit
  2301.    info := [nexthdr,[lcount]]
  2302.  
  2303.    # gather all data lines for this group/record
  2304.    while line := getline() do {
  2305.       if eorec(division,line) then {    # at end of this record
  2306.           # enter record info into sort key table
  2307.           put(info[2],lcount-1)
  2308.           enter(info,keytable)
  2309.           # look for header of next record
  2310.           if nexthdr := lmany(division) then
  2311.           info := [nexthdr,[lcount]] # begin next group/record
  2312.           else
  2313.           info := null
  2314.           }
  2315.       }
  2316.    # enter last line info into sort key table
  2317.    if \info then {
  2318.       put(info[2],lcount)
  2319.       enter(info,keytable)
  2320.       }
  2321.  
  2322.    keylist := sort(keytable,1)        # sort by record headers
  2323.    if \ordflag then
  2324.       printord(keylist)        # list sorted order of records
  2325.    else
  2326.       printrecs(keylist,division)    # print records in order
  2327. end
  2328.  
  2329. # enter - enter the group info into the sort key table
  2330. procedure enter(info,tbl)
  2331.    if /tbl[info[1]] then        # new key value
  2332.       tbl[info[1]] := [info[2]]
  2333.    else
  2334.       put(tbl[info[1]],info[2])    # add occurrance info
  2335. end
  2336.  
  2337. # eorec - suceed if a delimiter string has been found, fail otherwise
  2338. procedure eorec(div,str)
  2339.    if div == "" then            # If delimiter string is empty,
  2340.       if str == div then return    # then make exact match
  2341.       else
  2342.           fail
  2343.    if match(div,str) then return    # Otherwise match initial string.
  2344.    else
  2345.       fail
  2346. end
  2347.  
  2348. # getline - get the next line (or fail), trim off trailing tabs and blanks.
  2349. procedure getline()
  2350.    local line
  2351.    static trimset
  2352.    initial trimset := ' \t'
  2353.    if line := trim(read(),trimset) then {
  2354.       if /ordflag then    # save only if going to print later
  2355.           put(linelst,line)
  2356.       lcount +:= 1
  2357.       return line
  2358.       }
  2359. end
  2360.  
  2361. # lmany - skip over many lines matching string div.
  2362. procedure lmany(div)
  2363.    local line
  2364.    while line := getline() do {
  2365.       if eorec(div,line) then next    #skip over multiple dividing lines
  2366.       return line
  2367.       }
  2368. end
  2369.  
  2370. # printord - print only the selection order of the records.
  2371. procedure printord(slist)
  2372.    local x, y
  2373.    every x := !slist do
  2374.       every y := !x[2] do
  2375.           write(y[1],"-",y[2],"\t",x[1])
  2376. end
  2377.  
  2378. # printrecs - write the records in sorted order, separated by div string.
  2379. procedure printrecs(slist,div)
  2380.    local x, y, z
  2381.    every x := !slist do 
  2382.       every y := !x[2] do {
  2383.           every z := y[1] to y[2] do
  2384.           write(linelst[z])
  2385.           write(div)
  2386.           }
  2387. end
  2388. ##########
  2389. hufftab.icn
  2390. ############################################################################
  2391. #
  2392. #    Name:    hufftab.icn
  2393. #
  2394. #    Title:    Comnpute state transitions for Huffman decoding.
  2395. #
  2396. #    Author:    Gregg M. Townsend
  2397. #
  2398. #    Date:    December 1, 1984
  2399. #
  2400. ############################################################################
  2401. #
  2402. #      Each input line should be a string of 0s & 1s followed by a value
  2403. #   field.  Output is a list of items in a form suitable for inclusion
  2404. #   by a C program as initialization for an array.  Each pair of items
  2405. #   indicates the action to be taken on receipt of a 0 or 1 bit from the
  2406. #   corresponding state; this is either a state number if more decoding
  2407. #   is needed or the value field from the input if not.  State 0 is the
  2408. #   initial state;  0 is output only for undefined states.  States are
  2409. #   numbered by two to facilitate use of a one-dimensional array.
  2410. #
  2411. #   sample input:        corresponding output:
  2412. #    00 a                /*  0 */  2, c, a, 4, 0, b,
  2413. #    011 b
  2414. #    1 c            [new line started every 10 entries]
  2415. #
  2416. #   Interpretation:
  2417. #    from state 0,  input=0 => go to state 2,  input=1 => return c
  2418. #    from state 2,  input=0 => return a,  input=1 => go to state 4
  2419. #    from state 4,  input=0 => undefined,  input=1 => return b
  2420. #
  2421. ############################################################################
  2422.  
  2423. global curstate, sttab, line
  2424.  
  2425. procedure main()
  2426.     local code, val, n
  2427.  
  2428.     sttab := list()
  2429.     put(sttab)
  2430.     put(sttab)
  2431.     while line := read() do  {
  2432.     line ? {
  2433.         if ="#" | pos(0) then next
  2434.         (code := tab(many('01'))) | (write(&errout,"bad: ",line) & next)
  2435.         tab(many(' \t'))
  2436.         val := tab(0)
  2437.     }
  2438.     curstate := 1
  2439.     every bit(!code[1:-1])
  2440.     curstate +:= code[-1]
  2441.     if \sttab[curstate] then write(&errout,"dupl: ",line)
  2442.     sttab[curstate] := val
  2443.     }
  2444.     write("/* generated by machine -- do not edit! */")
  2445.     write()
  2446.     writes("/*  0 */")
  2447.     out(sttab[1])
  2448.     every n := 2 to *sttab do {
  2449.     if n % 10 = 1 then writes("\n/* ",n-1," */")
  2450.     out(sttab[n])
  2451.     }
  2452.     write()
  2453.     end
  2454.  
  2455.  
  2456. procedure bit (c)
  2457.     curstate +:= c
  2458.     if integer(sttab[curstate]) then {
  2459.     curstate := sttab[curstate]
  2460.     return
  2461.     }
  2462.     if type(sttab[curstate]) == "string" then write(&errout,"dupl: ",line)
  2463.     curstate := sttab[curstate] := *sttab + 1
  2464.     put(sttab)
  2465.     put(sttab)
  2466.     end
  2467.  
  2468.  
  2469. procedure out(v)
  2470.     if type(v) == "integer"
  2471.     then writes(right(v-1,6),",")
  2472.         else writes(right(\v | "0",6),",")
  2473.     end
  2474. ##########
  2475. ilnkxref.icn
  2476. ############################################################################
  2477. #
  2478. #    Name:    ilnkxref.icn
  2479. #
  2480. #    Title:    Icon "link" Cross Reference Utility
  2481. #
  2482. #    Author:    Robert J. Alexander
  2483. #
  2484. #    Date:    December 5, 1989
  2485. #
  2486. ############################################################################
  2487. #
  2488. #  Utility to create cross reference of library files used in Icon
  2489. #  programs (i.e., those files named in "link" declarations).
  2490. #
  2491. #    ilnkxref <icon source file>...
  2492. #
  2493. ############################################################################
  2494. #
  2495. #  Links: wrap
  2496. #
  2497. ############################################################################
  2498.  
  2499. link wrap
  2500.  
  2501. procedure main(arg)
  2502.    local p, spaces, sep, proctable, maxlib, maxfile, fn, f, i, root
  2503.    local comma, line, libname, x, head, fill
  2504.    #
  2505.    #  Initialize
  2506.    #
  2507.    if *arg = 0 then {
  2508.       p := open("ls *.icn","rp")
  2509.       while put(arg,read(p))
  2510.       close(p)
  2511.       }
  2512.    spaces := ' \t'
  2513.    sep := ' \t,'
  2514.    proctable := table()
  2515.    maxlib := maxfile := 0
  2516.    #
  2517.    # Gather information from files.
  2518.    #
  2519.    every fn := !arg do {
  2520.       write(&errout,"File: ",fn)
  2521.       f := open(fn) | stop("Can't open ",fn)
  2522.       i := 0
  2523.       every i := find("/",fn)
  2524.       root := fn[1:find(".",fn,i + 1) | 0]
  2525.       comma := &null
  2526.       while line := read(f) do {
  2527.      line ? {
  2528.         tab(many(spaces))
  2529.         if \comma | ="link " then {
  2530.            write(&errout,"    ",line)
  2531.            comma := &null
  2532.            tab(many(spaces))
  2533.            until pos(0) | match("#") do {
  2534.           libname := tab(upto(sep) | 0)
  2535.           put(\proctable[libname],root) | (proctable[libname] := [root])
  2536.           maxlib <:= *libname
  2537.           maxfile <:= *root
  2538.           tab(many(spaces))
  2539.           comma := &null
  2540.           if comma := ="," then tab(many(spaces))
  2541.           }
  2542.            }
  2543.         }
  2544.      }
  2545.       close(f)
  2546.       }
  2547.    #
  2548.    #  Print the cross reference table.
  2549.    #
  2550.    write()
  2551.    every x := !sort(proctable) do {
  2552.       head := left(x[1],maxlib + 3)
  2553.       fill := repl(" ",*head)
  2554.       every x := !sort(x[2]) do {
  2555.      write(head,wrap(left(x,maxfile + 2),78)) & head := fill
  2556.      }
  2557.       write(head,wrap())
  2558.       }
  2559. end
  2560. ##########
  2561. ipp.icn
  2562. ############################################################################
  2563. #
  2564. #    Name:    ipp.icn
  2565. #
  2566. #    Title:    Icon preprocessor
  2567. #
  2568. #    Author:    Robert C. Wieland
  2569. #
  2570. #    Date:    December 22, 1989
  2571. #
  2572. ############################################################################
  2573. #
  2574. #     Ipp is a preprocessor for the Icon language.  Ipp has many operations and
  2575. #  features that are unique to the Icon environment and should not be used as a
  2576. #  generic preprocessor (such as m4).  Ipp produces output which when written to
  2577. #  a file is designed to be the source for icont, the command processor for Icon
  2578. #  programs.
  2579. #  
  2580. #  Ipp may be invoked from the command line as:
  2581. #
  2582. #    ipp [option  ...] [ifile [ofile]]
  2583. #  
  2584. #     Two file names may be specified as arguments.  'ifile' and 'ofile' are 
  2585. #  respectively the input and output files for the preprocessor.  By default
  2586. #  these are standard input and standard output.  If the output file is to be
  2587. #  specified while the input file should remain standard input a dash ('-')
  2588. #  should be given as 'ifile'.  For example, 'ipp - test' makes test the output
  2589. #  file while retaining standard input as the input file.
  2590. #  
  2591. #     The following special names are predefined by ipp and may not be redefined
  2592. #  or undefined.  The name _LINE_ is defined as the line number (as an
  2593. #  integer) of the line of the source file currently processed.  The
  2594. #  name _FILE_ is defined as the name of the current source file (as a string).  
  2595. #     If the source is standard input then it has the value 'stdin'.
  2596. #  
  2597. #     Also predefined are names corresponding to the features supported by the
  2598. #  implementation of Icon at the location the preprocessor is run.  This allows
  2599. #  conditional translations using the 'if' commands, depending on what features
  2600. #  are available.  Given below is a list of the features on a 4.nbsd UNIX 
  2601. #  implementation and the corresponding predefined names:
  2602. #  
  2603. #      Feature                Name
  2604. #      -----------------------------------------------------
  2605. #      UNIX                UNIX
  2606. #      co-expressions            co_expressions
  2607. #      overflow checking        overflow_checking
  2608. #      direct execution        direct_execution
  2609. #      environment variables        environment_variables
  2610. #      error traceback            error_traceback
  2611. #      executable images        executable_images
  2612. #      string invocation        string_invocation
  2613. #      expandable regions        expandable_regions
  2614. #  
  2615. #  
  2616. #  Command-Line Options:
  2617. #  ---------------------
  2618. #  
  2619. #    The following options to ipp are recognized:
  2620. #  
  2621. #   -C        By default ipp strips Icon-style comments.  If this option
  2622. #         is specified all comments are passed along except those
  2623. #         found on ipp command lines (lines starting with  a '$' 
  2624. #         command).
  2625. #   -D name    
  2626. #   -D name=def    Allows the user to define a name on the command line instead
  2627. #         of using a $define command in a source file.  In the first
  2628. #         form the name is defined as '1'.  In the second form name is
  2629. #         defined as the text following the equal sign.  This is less
  2630. #         powerful than the $define command line since def can not
  2631. #         contain any white space (spaces or tabs).
  2632. #   -d depth    By default ipp allows include files to be nested to a depth
  2633. #         of ten.  This allows the preprocessor to detect infinitely
  2634. #         recursive include sequences.  If a different limit for the
  2635. #         nesting depth is needed it may changed by using this option
  2636. #         with an integer argument greater than zero. Also, if a file
  2637. #         is found to already be in a nested include sequence an
  2638. #         error message is written regardless of the limit.
  2639. #   -I dir    The following algorithm is normally used in searching for
  2640. #         $include files.  Names enclosed in <> are always expected to 
  2641. #         in the /usr/icon/src directory.  On a UNIX system names enclosed
  2642. #         in "" are searched for by trying in order the directories
  2643. #         specified by the PATH environment variable.  On other systems
  2644. #         only the current directory is searched.  If the -I option is
  2645. #         given the directory specified is searched before the 'standard'
  2646. #         directories.  If this option is specified more than once the
  2647. #         directories specified are tried in the order that they appear
  2648. #         on the command line, then followed by the 'standard' 
  2649. #          directories.
  2650. #  
  2651. #  
  2652. #  Preprocessor commands:
  2653. #  ----------------------
  2654. #  
  2655. #     All ipp commands start with lines beginning with a '$'.  The name of the
  2656. #  command must immediately follow the '$'.  Any line beginning with a '$'
  2657. #  and not followed by a valid name will cause an error message to be sent
  2658. #  to standard error and termination of the preprocessor.  If the command
  2659. #  requires an argument then it must be separated from the command name by
  2660. #  white space (any number of spaces or tabs) otherwise the argument will be
  2661. #  considered part of the name and the result will likely produce an error.
  2662. #  In processing the #  commands ipp responds to exceptional conditions in one
  2663. #  of two ways.  It may produce a warning and continue processing or produce an
  2664. #  error message and terminate.  In both cases the message is sent to standard
  2665. #  error.  With the exception of error conditions encountered during the
  2666. #  processing of the command line, the messages normally include the name and
  2667. #  line number of the source file at the point the condition was
  2668. #  encountered.  Ipp was designed so that most exception conditions
  2669. #  encountered will produce errors and terminate.  This protects the user since
  2670. #  warnings could simply be overlooked or misinterpreted.
  2671. #
  2672. #     Many ipp command require names as arguments.  Names must begin with a
  2673. #  letter or an underscore, which may be followed by any number of letters,
  2674. #  underscores, and digits.  Icon-style comments may appear on ipp command
  2675. #  lines, however they must be separated from the normal end of the command by
  2676. #  white_space.  If any extraneous characters appear on a command line a
  2677. #  warning is issued.  This occurs when characters other than white-space or a
  2678. #  comment follow the normal end of a command.
  2679. #  
  2680. #     The following commands are implemented:
  2681. #  
  2682. #    $define:  This command may be used in one of two forms.  The first form
  2683. #           only allows simple textual substitution.  It would be invoked as
  2684. #          '$define name text'.  Subsequent occurrencegs of name are replaced 
  2685. #          with text.  Name and text must be separated by one white space
  2686. #          character which is not considered to be part of the replacement
  2687. #          text.  Normally the replacement text ends at the end of the line.
  2688. #          The text however may be continued on the next line if the backslash
  2689. #          character '\' is the last character on the line.  If name occurs
  2690. #          in the replacement text an error message (recursive textual substi-
  2691. #          tution) is written.
  2692. #  
  2693. #          The second form is '$define name(arg,...,arg) text' which defines
  2694. #          a macro with arguments.  There may be no white space between the 
  2695. #          name and the '('.  Each occurrenceg of arg in the replacement text
  2696. #          is replaced by the formal arg specified when the macro is 
  2697. #          encountered.   When a macro with arguments is expanded the arguments
  2698. #          are placed into the expanded replacement text unchanged.  After the
  2699. #          entire replacement text is expanded, ipp restarts its scan for names
  2700. #          to expand at the beginning of the newly formed replacement text.  
  2701. #          As with the first form above, the replacement text may be continued
  2702. #          an following lines.  The replacement text starts immediately after
  2703. #          the ')'. 
  2704. #          The names of arguments must comply with the convention for regular 
  2705. #          names.  See the section below on Macro processing for more 
  2706. #          information on the replacement process.
  2707. #  
  2708. #    $undef:   Invoked as '$undef name'.   Removes the definition of name.  If
  2709. #          name is not a valid name or if name is one of the reserved names
  2710. #          _FILE_ or _LINE_ a message is issued.
  2711. #  
  2712. #    $include: Invoked as '$include <filename>' or '$include "filename"'.  This
  2713. #          causes the preprocessor to make filename the new source until
  2714. #          end of file is reached upon which input is again taken from the
  2715. #          original source.  See the -I option above for more detail.
  2716. #  
  2717. #    $dump:    This command, which has no arguments, causes the preprocessor to 
  2718. #          write to standard error all names which are currently defined.
  2719. #          See '$ifdef' below for a definition of 'defined'.
  2720. #  
  2721. #    $endif:   This command has no arguments and ends the section of lines begun
  2722. #          by a test command ($ifdef, $ifndef, or $if).  Each test command
  2723. #          must have a matching $endif.
  2724. #  
  2725. #    $ifdef:   Invoked as 'ifdef name'.  The lines following this command appear
  2726. #          in the output only if the name given is defined.  'Defined' means
  2727. #            1.  The name is a predefined name and was not undefined using
  2728. #            $undef, or
  2729. #            2.  The name was defined using $define and has not been undefined
  2730. #            by an intervening $undef.
  2731. #  
  2732. #    $ifndef:  Invoked as 'ifndef name'.  The lines following this command do not
  2733. #          appear in the ouput if the name is not defined.
  2734. #  
  2735. #    $if:      Invoked as 'if constant-expression'.  Lines following this command
  2736. #          are processed only if the constant-expression produces a result.
  2737. #          The following arithmetic operators may be applied to integer 
  2738. #          arguments: + - * / % ^
  2739. #
  2740. #          If an argument to one of the above operators is not an integer an
  2741. #          error is produced.
  2742. #  
  2743. #             The following functions are provided: def(name), ndef(name)
  2744. #          This allows the utility of $ifdef and $ifndef in a $if command.
  2745. #          def produces a result if name is defined and ndef produces a
  2746. #          result if name is not defined.  There must not be any white space
  2747. #          between the name of the function and the '(' and also between the
  2748. #          name and the surrounding parentheses.
  2749. #          
  2750. #             The following comparision operators may be used on integer
  2751. #           operands:
  2752. #
  2753. #          > >= = < <= ~=
  2754. #
  2755. #              Also provided are alternation (|) and conjunction(&).  The
  2756. #           following table lists all operators with regard to decreasing
  2757. #           precedence:
  2758. #  
  2759. #          ^ (associates right to left)
  2760. #          * / %
  2761. #          + -
  2762. #               > >= = < <= ~=
  2763. #          |
  2764. #          &
  2765. #  
  2766. #           The precedence of '|' and '&' are the same as the corresponding
  2767. #           Icon counterparts.  Parentheses may be used for grouping.
  2768. #  
  2769. #    $else     This command has no arguments and reverses the notion of the test
  2770. #          command which matches this directive.  If the lines preceding this
  2771. #          command where ignored the lines following are processed, and vice
  2772. #          versa.
  2773. #  
  2774. #  Macro Processing and Textual Substitution
  2775. #  -----------------------------------------
  2776. #     No substitution is performed on text inside single quotes (cset literals)
  2777. #  and double quotes (strings) when a line is processed.   The preprocessor will
  2778. #  detect unclosed cset literals or strings on a line and issue an error message
  2779. #  unless the underscore character is the last character on the line.  The
  2780. #  output from 
  2781. #  
  2782. #      $define foo bar
  2783. #      write("foo")
  2784. #  
  2785. #  is
  2786. #
  2787. #       write("foo")
  2788. #  
  2789. #     Unless the -C option is specified comments are stripped from the source.
  2790. #  Even if the option is given the text after the '#' is never expanded.
  2791. #  
  2792. #     Macro formal parameters are recognized in $define bodies even inside cset 
  2793. #  constants and strings.  The output from
  2794. #  
  2795. #      $define test(a)        "a"
  2796. #      test(processed)
  2797. #  
  2798. #  is the following sequence of characters: "processed".
  2799. #  
  2800. #     Macros are not expanded while processing a $define or $undef.  Thus:
  2801. #  
  2802. #      $define off invalid
  2803. #      $define bar off
  2804. #      $undef off
  2805. #      bar
  2806. #  
  2807. #  produces off.  The name argument to $ifdef or $ifndef is also not expanded.
  2808. #  
  2809. #     Mismatches between the number of formal and actual parameters in a macro
  2810. #  call are caught by ipp.  If the number of actual parameters is greater than
  2811. #  the number of formal parameters is error is produced.  If the number of
  2812. #  actual parameters is less than the number of formal parameters a warning is
  2813. #  issued and the missing actual parameters are turned into null strings.
  2814. #  
  2815. ############################################################################
  2816. #
  2817. #    The records and global variables used by ipp are described below:
  2818. #
  2819. #  Src_desc:        Record which holds the 'file descriptor' and name
  2820. #            of the corresponding file.  Used in a stack to keep
  2821. #                track of the source files when $includes are used.
  2822. #  Opt_rec         Record returned by the get_args() routine which returns
  2823. #            the options and arguments on the command line.  options
  2824. #            is a cset containing options that have no arguments.
  2825. #            pairs is a list of [option,  argument] pairs. ifile and
  2826. #            ofile are set if the input or output files have been
  2827. #            specified.
  2828. #  Defs_rec        Record stored in a table keyed by names.  Holds the
  2829. #            names of formal arguments, if any, and the replacement
  2830. #            text for that name.
  2831. #  Chars        Cset of all characters that may appear in the input.
  2832. #  Defs            The table holding the definition data for each name.
  2833. #  Depth        The maximum depth of the input source stack.
  2834. #  Ifile        Descriptor for the input file.
  2835. #  Ifile_name        Name of the input file.
  2836. #  Init_name_char     Cset of valid initial characters for names.
  2837. #  Line_no        The current line number.
  2838. #  Name_char        Cset of valid characters for names.
  2839. #  Non_name_char    The complement of the above cset.
  2840. #  Ofile        The descriptor of the output file.
  2841. #  Options        Cset of no-argument options specified on the command
  2842. #            line.
  2843. #  Path_list        List of directories to search in for "" include files.
  2844. #  Src_stack        The stack of input source records.
  2845. #  Std_include_paths    List of directories to search in for <> include files.
  2846. #  White_space        Cset for white-space characters.
  2847. #  TRUE            Defined as 1.
  2848. #
  2849. ############################################################################
  2850.  
  2851. record Src_desc(fd, fname)
  2852. record Opt_rec(options, pairs, ifile, ofile)
  2853. record Defs_rec(arg_list, text)
  2854.  
  2855. global Chars, Defs, Depth, Ifile, Ifile_name, Init_name_char, 
  2856.   Line_no, Name_char, Non_name_char, Ofile, Options, Path_list, 
  2857.   Src_stack, Std_include_paths, White_space, TRUE 
  2858.  
  2859. procedure main(arg_list)
  2860.   local cmd, line, source
  2861.  
  2862.   init(arg_list)
  2863.  
  2864.   repeat {
  2865.     while line := read(Ifile) do {
  2866.       Line_no +:= 1
  2867.       line ? { 
  2868.     if tab(any('$')) then
  2869.       if cmd := tab(many(Chars)) then
  2870.         process_cmd(cmd)
  2871.       else
  2872.         error("Missing command")
  2873.     else
  2874.       write(Ofile, process_text(line))
  2875.         }
  2876.       }
  2877.     # Get new source
  2878.     close(Ifile)
  2879.     if source := pop(Src_stack) then {
  2880.       Ifile := source.fd
  2881.       Ifile_name := source.fname
  2882.       Line_no := 0
  2883.       }
  2884.     else  break
  2885.   }
  2886. end
  2887.  
  2888. procedure process_cmd(cmd)
  2889.   case cmd of {
  2890.     "dump":        dump()
  2891.     "define":        define()
  2892.     "undef":        undefine()
  2893.     "include":        include()
  2894.     "if":        if_cond()
  2895.     "ifdef":        ifdef()
  2896.     "ifndef":        ifndef()
  2897.     "else" | "endif":    error("No previous 'if' expression")    
  2898.     "endif":        error("No previous 'if' expression")    
  2899.     default:        error("Undefined command")
  2900.     }
  2901.   return
  2902. end
  2903.  
  2904. procedure init(arg_list)
  2905.   local s
  2906.  
  2907.   TRUE := 1
  2908.   Defs := table()
  2909.   Init_name_char := &letters ++ '_'
  2910.   Name_char := Init_name_char ++ &digits
  2911.   Non_name_char := ~Name_char
  2912.   White_space := ' \t\b'
  2913.   Chars := &ascii -- White_space
  2914.   Line_no := 0
  2915.   Depth := 10
  2916.   Std_include_paths := ["/usr/icon/src"]
  2917.  
  2918.   # Predefine features
  2919.   every s:= &features do {
  2920.     s[upto('  -', s)] := "_"
  2921.     Defs[s] := Defs_rec([], "1")
  2922.     }
  2923.  
  2924.   # Set path list for $include files given in ""
  2925.   Path_list := []
  2926.   if \Defs["UNIX"] then 
  2927.     getenv("PATH") ? while put(Path_list, 1(tab(upto(':')), move(1)))
  2928.   else
  2929.     put(Path_list, "")
  2930.  
  2931.   process_options(arg_list)
  2932. end
  2933.  
  2934. procedure process_options(arg_list)
  2935.   local args, arg_opts, pair, simple_opts, tmp_list, value
  2936.  
  2937.   simple_opts := 'C'
  2938.   arg_opts := 'dDI'
  2939.   Src_stack := []
  2940.  
  2941.   args := get_args(arg_list, simple_opts, arg_opts)
  2942.   if \args.ifile then {
  2943.     (Ifile := open(args.ifile)) | stop("Can not open input file ", args.ifile)
  2944.     Ifile_name := args.ifile
  2945.     }
  2946.   else {
  2947.     Ifile := &input
  2948.     Ifile_name := "stdin"
  2949.     }
  2950.   if \args.ofile then 
  2951.     (Ofile := open(args.ofile, "w")) | stop("Can not open output file",
  2952.       args.ofile)
  2953.   else 
  2954.     Ofile := &output
  2955.  
  2956.   Options := args.options 
  2957.   tmp_list := []
  2958.   every pair := !args.pairs do
  2959.     case pair[1] of {
  2960.       "D":    def_opt(pair[2])
  2961.       "d":    if (value := integer(pair[2])) > 0 then
  2962.           Depth := value
  2963.         else
  2964.           stop("Invalid argument for depth")
  2965.       "I":    push(tmp_list, pair[2])
  2966.     }
  2967.   Path_list := tmp_list ||| Path_list
  2968. end
  2969.  
  2970. procedure get_args(arg_list, simple_opts, arg_opts)
  2971.   local arg, ch, get_ofile, i, opts, queue
  2972.   opts := Opt_rec('', [])
  2973.   queue := []
  2974.  
  2975.   every arg := arg_list[i := 1 to *arg_list] do
  2976.     if arg == "-" then         # Next argument should be output file
  2977.       get_ofile := (i = *arg_list - 1) | 
  2978.     stop("Invalid position of '-' argument")
  2979.     else if arg[1] == "-" then     # Get options
  2980.       every ch := !arg[2: 0] do
  2981.     if any(simple_opts, ch) then
  2982.       opts.options ++:= ch
  2983.     else if any(arg_opts, ch) then
  2984.       put(queue, ch)
  2985.     else
  2986.       stop("Invalid option - ", ch)
  2987.     else if ch := pop(queue) then     # Get argument for option
  2988.       push(opts.pairs, [ch, arg])
  2989.     else if \get_ofile then {     # Get output file
  2990.       opts.ofile := arg
  2991.       get_ofile := &null
  2992.       }
  2993.     else {            # Get input file
  2994.       opts.ifile := arg
  2995.       get_ofile := (i < *arg_list)
  2996.       }
  2997.  
  2998.   if \get_ofile | *queue ~= 0 then
  2999.     stop("Invalid number of arguments")
  3000.  
  3001.   return opts
  3002. end
  3003.  
  3004. # if_cond is the procedure for $if.  The procedure const_expr() which 
  3005. # evaluates the constant expression may be found in expr.icn
  3006. #
  3007. # Procedure true_cond is invoked if the evaluation of a previous $if, $ifdef, or
  3008. # $ifndef causes subsequent lines to be processed.  Lines will be processed
  3009. # upto a $endif or a $else.  If $else is encountered, lines are skipped until
  3010. # the $endif matching the $else is encountered.
  3011. #
  3012. # Procedure false_cond is invoked if the evaluation of a previous $if, $ifdef, 
  3013. # or $ifndef causes subsequent lines to be skipped.  Lines will be skipped 
  3014. # upto a $endif or a $else.  If $else is encountered, lines are processed until
  3015. # the $endif matching the $else is encountered.
  3016. #
  3017. # If called with a 1, procedure skip_to skips over lines until a $endif is 
  3018. # encountered.  If called with 2, it skips until either a $endif or $else is 
  3019. # encountered.
  3020.  
  3021. procedure if_cond()
  3022.   local expr 
  3023.  
  3024.   if expr := (tab(many(White_space)) & not pos(0) & tab(0)) then 
  3025.     conditional(const_expr(expr))
  3026.   else
  3027.     error("Constant expression argument to 'if' missing")
  3028. end
  3029.  
  3030. procedure ifdef()
  3031.   local name
  3032.  
  3033.   if name := 2(tab(many(White_space)), tab(any(Init_name_char)) ||
  3034.     (tab(many(Name_char)) | ""), any(White_space) | pos(0)) then  {
  3035.     tab(many(White_space))
  3036.     if not(pos(0) | any('#')) then
  3037.       warning("Extraneous characters after argument to 'ifdef'")
  3038.     conditional(Defs[name])
  3039.     }
  3040.   else
  3041.     error("Argument to 'ifdef' is not a valid name")
  3042. end
  3043.   
  3044. procedure ifndef()
  3045.   local name
  3046.  
  3047.   if name := 2(tab(many(White_space)), tab(any(Init_name_char)) ||
  3048.     (tab(many(Name_char)) | ""), any(White_space) | pos(0)) then {
  3049.     tab(many(White_space))
  3050.     if not(pos(0) | any('#')) then
  3051.       warning("Extraneous characters after argument to 'ifndef'")
  3052.     if \Defs[name] then
  3053.       conditional(&null)
  3054.     else
  3055.       conditional(TRUE)
  3056.     }
  3057.   else
  3058.     error("Argument to 'ifndef' is not a valid name")
  3059. end
  3060.   
  3061. procedure conditional(flag)
  3062.  
  3063.   if \flag then
  3064.     true_cond()
  3065.   else
  3066.     false_cond()
  3067. end
  3068.  
  3069. procedure true_cond()
  3070.   local line
  3071.  
  3072.   while line := read(Ifile) & (Line_no +:= 1) do
  3073.     line ? {
  3074.       if tab(any('$')) then
  3075.         if tab(match("if")) then
  3076.           eval_cond()
  3077.         else if check_cmd("else") then {
  3078.       # Skip only until a $endif
  3079.       skip_to(1) |
  3080.             error("'endif' not encountered before end of file")
  3081.       return
  3082.       }
  3083.         else if check_cmd("endif") then
  3084.       return
  3085.     else 
  3086.       process_cmd(tab(many(Chars))) | error("Undefined command")
  3087.       else
  3088.         write(Ofile, process_text(line))
  3089.       }
  3090.      
  3091.     error("'endif' not encountered before end of file")
  3092. end
  3093.  
  3094. procedure false_cond()
  3095.   local cmd, line
  3096.  
  3097.   # Skip to $else or $endif
  3098.   (cmd := skip_to(2)) | error("'endif' not encountered before end of file")
  3099.   if cmd == "endif" then
  3100.     return
  3101.  
  3102.   while line := read(Ifile) & (Line_no +:= 1) do
  3103.     line ? {
  3104.       if tab(any('$')) then
  3105.     if check_cmd("endif") then
  3106.       return
  3107.     else if tab(match("if")) then
  3108.       eval_cond()
  3109.     else 
  3110.       process_cmd(tab(many(Chars))) | error("Undefined command")
  3111.       else
  3112.         write(Ofile, process_text(line))
  3113.       }
  3114.   error("'endif' not encountered before end of file")
  3115. end
  3116.  
  3117. procedure eval_cond()
  3118.     if tab(match("def")) & (any(White_space) | pos(0)) then
  3119.       ifdef()
  3120.     else if tab(match("ndef")) & (any(White_space) | pos(0)) then 
  3121.       ifndef()
  3122.     else if any(White_space) | pos(0) then
  3123.       return const_expr(tab(0))
  3124.     else
  3125.       error("Undefined command")
  3126. end
  3127.  
  3128. procedure check_cmd(cmd)
  3129.   local s
  3130.  
  3131.   if (s := tab(match(cmd))) & (tab(many(White_space)) | pos(0)) then {
  3132.     if not(match("if", cmd) | pos(0) | any('#')) then
  3133.       warning("Extraneous characters after command")
  3134.     return s
  3135.     }
  3136.   else
  3137.     fail
  3138. end
  3139.  
  3140. procedure skip_to(n)
  3141.   local cmd, ifs, elses, line, s
  3142.  
  3143.   ifs := elses := 0
  3144.   while line := read(Ifile) & (Line_no +:= 1) do
  3145.     line ? {
  3146.       if tab(any('$')) then
  3147.     if cmd := (check_cmd("endif") | (n = 2 & check_cmd("else"))) then
  3148.       if ifs = elses = 0 then
  3149.         return cmd
  3150.       else if cmd == "endif" then {
  3151.         ifs -:= 1
  3152.         elses := 0
  3153.         }
  3154.       else if elses = 0 then
  3155.         if ifs > 0 then
  3156.           elses := 1
  3157.         else
  3158.           error("'$else' encountered before 'if'")
  3159.       else
  3160.         error("Previous '$else' not terminated by 'endif'")
  3161.     else if check_cmd("endif") then {
  3162.       ifs -:= 1
  3163.       elses := 0
  3164.       }
  3165.         else if check_cmd("if" | "ifdef" | "ifndef") then
  3166.           ifs +:= 1
  3167.         else         # $else
  3168.           if elses = 0 then
  3169.             if ifs > 0 then
  3170.               elses := 1
  3171.             else
  3172.               error("'$else' encountered before 'if'")
  3173.        else 
  3174.          error("Previous '$else' not terminated by 'endif'")
  3175.    }
  3176. end
  3177.  
  3178. procedure define()
  3179.   local args, name, text
  3180.  
  3181.   if name := 2(tab(many(White_space)), tab(any(Init_name_char)) ||
  3182.     (tab(many(Name_char)) | ""), any(White_space | '(') | pos(0)) then {
  3183.     if name == ("_LINE_" | "_FILE_") then
  3184.       error(name, " is a reserved name and can not be redefined")
  3185.  
  3186.     if tab(any('(')) then {         # A macro
  3187.       if not upto(')') then
  3188.     error("Missing ')' in macro definition")
  3189.       args := get_formals()
  3190.       text := get_text(TRUE)
  3191.       }
  3192.     else {
  3193.       args := []
  3194.       text := get_text()
  3195.       }
  3196.  
  3197.     if \Defs[name] then
  3198.       warning(name, " redefined")
  3199.     Defs[name] := Defs_rec(args, text)
  3200.     }  
  3201.   else
  3202.     error("Illegal or missing name in define")
  3203. end
  3204.  
  3205. procedure get_text(flag)
  3206.   local get_cont, text, line
  3207.  
  3208.   if \flag then
  3209.     text := (tab(many(White_space)) | "") || tab(0)
  3210.   else
  3211.     text := (tab(any(White_space)) & tab(0)) | ""
  3212.   if text[-1] == "\\" then {
  3213.     get_cont := TRUE
  3214.     text[-1] := ""
  3215.     while line := read(Ifile) do {
  3216.     Line_no +:= 1
  3217.       text ||:= line
  3218.       if text[-1] == "\\" then
  3219.         text[-1] := ""
  3220.       else {
  3221.         get_cont := &null
  3222.         break
  3223.         }
  3224.       }
  3225.     }
  3226.   if \get_cont then
  3227.     error("Continuation line not found before end of file")
  3228.   return text
  3229. end
  3230.  
  3231. procedure get_formals()
  3232.   local arg, args, ch, edited
  3233.  
  3234.   args := []
  3235.   while arg := 1(tab(upto(',)')), ch := move(1)) do {
  3236.     if edited := (arg ? 2(tab(many(White_space)) | TRUE, 
  3237.       tab(any(Init_name_char)) || (tab(many(Name_char)) | ""),
  3238.       tab(many(White_space)) | pos(0))) then
  3239.         put(args, edited)
  3240.     else if arg == "" then
  3241.       return [""] 
  3242.     else
  3243.       error("Invalid formal argument in macro definition")
  3244.     if ch == ")" then 
  3245.       break
  3246.     }
  3247.   return args
  3248. end
  3249.  
  3250. procedure undefine()
  3251.   local name
  3252.  
  3253.   if name := (tab(many(White_space)) & tab(many(Chars))) then {
  3254.     tab(many(White_space))
  3255.     if not(pos(0) | any('#')) then
  3256.       warning("Extraneous characters after argument to undef")
  3257.     if not(name ? (tab(any(Init_name_char)), (tab(many(Name_char)) | ""), 
  3258.       pos(0))) then
  3259.       warning("Argument to undef is not a valid name")
  3260.     if name == ("_LINE_" | "_FILE_") then
  3261.       error(name, " is a reserved name that can not be undefined")
  3262.     \Defs[name] := &null
  3263.     }
  3264.   else
  3265.     error("Name missing in undefine")
  3266. end
  3267.  
  3268. procedure process_text(line)
  3269.   local add, entry, new, position, s, token
  3270.   static in_string, in_cset
  3271.  
  3272.   new :=  ""
  3273.   while *line > 0 do {
  3274.     add := ""
  3275.     line ? {
  3276.       if \in_string then {
  3277.     if new ||:= (tab(upto('"')) || move(1)) then
  3278.       in_string := &null
  3279.     else {
  3280.       new ||:= tab(0)
  3281.       if line[-1] ~== "_" then {
  3282.         in_string := &null
  3283.         warning("Unclosed double quote")
  3284.         }
  3285.       }
  3286.         }        
  3287.       if \in_cset then {
  3288.     if new ||:= (tab(upto('\'')) || move(1)) then
  3289.       in_cset := &null
  3290.     else {
  3291.       new ||:= tab(0)
  3292.       if line[-1] ~== "_" then {
  3293.         in_cset := &null
  3294.         warning("Unclosed single quote")
  3295.         }
  3296.       }
  3297.     }   
  3298.  
  3299.       new ||:= tab(many(White_space))
  3300.       if token := tab(many(Name_char) | any(Non_name_char)) then {
  3301.     if token == "\"" then { # Process string
  3302.       new ||:= "\""
  3303.           if \in_string then 
  3304.         in_string := &null
  3305.       else {
  3306.         in_string := TRUE 
  3307.         if pos(0) then {
  3308.           warning("Unclosed double quote")
  3309.           in_string := &null
  3310.           }
  3311.         }
  3312.       add ||:= tab(0)
  3313.       }
  3314.     else if token == "'" then { # Process cset literal
  3315.       new ||:= "'"
  3316.           if \in_cset then 
  3317.         in_cset := &null
  3318.       else {
  3319.         in_cset := TRUE 
  3320.         if pos(0) then {
  3321.           warning("Unclosed single quote")
  3322.           in_cset := &null
  3323.           }
  3324.         }
  3325.       add ||:= tab(0)
  3326.       }
  3327.     else if token == "#" then {
  3328.           if any(Options, "C") then
  3329.             new ||:= token || tab(0) 
  3330.           else
  3331.         (new ||:= (token ? tab(upto('#')))) & tab(0)
  3332.       }
  3333.     else if token == "_LINE_" then
  3334.       new ||:= string(Line_no)
  3335.     else if token == "_FILE_" then
  3336.       new ||:= Ifile_name
  3337.         else if /(entry := Defs[token]) then
  3338.         new ||:= token
  3339.     else if *entry.arg_list = 0 then
  3340.       if in_text(token, entry.text) then
  3341.         error("Recursive textual substitution")
  3342.       else
  3343.         add := entry.text
  3344.     else if *entry.arg_list = 1 & entry.arg_list[1] == "" then {
  3345.        if move(2) == "()" then
  3346.          add := entry.text
  3347.        else
  3348.              error(token, ":  Invalid macro call")
  3349.        }
  3350.         else {  # Macro with arguments
  3351.       s := tab(bal(White_space, '(', ')') | 0)
  3352.       if not any('(', s) then
  3353.             error(token, ":  Incomplete macro call")
  3354.           add := process_macro(token, entry, s)
  3355.       }
  3356.         }
  3357.       position := &pos
  3358.       }
  3359.     line := add || line[position: 0]
  3360.     }
  3361.   return new
  3362. end
  3363.  
  3364. procedure process_macro(name, entry, s)
  3365.   local arg, args, new_entry, news, token
  3366.  
  3367.   s ? {
  3368.     args := []
  3369.     if tab(any('(')) then {
  3370.       repeat {
  3371.     arg := tab(many(White_space)) | ""
  3372.         if token := tab(many(Chars -- '(,)')) then {
  3373.           if /(new_entry := Defs[token]) then
  3374.           arg ||:= token
  3375.       else if *new_entry.arg_list = 0 then
  3376.         arg ||:= new_entry.text
  3377.           else {  # Macro with arguments
  3378.         if news := tab(bal(' \t\b,)', '(', ')')) then
  3379.               arg ||:= process_macro(token, new_entry, news)
  3380.         else
  3381.               error(token, ":  Error in arguments to macro call")
  3382.         }
  3383.       } # if
  3384.     else if not any(',)') then
  3385.           error(name, ":  Incomplete macro call")
  3386.     arg ||:= tab(many(White_space))
  3387.         put(args, arg)
  3388.     if any(')') then
  3389.       break
  3390.     move(1)
  3391.         } # repeat 
  3392.         if *args > *entry.arg_list then
  3393.           error(name, ":  Too many arguments in macro call")
  3394.     else if *args < *entry.arg_list then
  3395.           warning(name, ":  Missing arguments in macro call")
  3396.         return macro_call(entry, args)
  3397.       } # if
  3398.     }
  3399. end
  3400.  
  3401. procedure macro_call(entry, args)
  3402.   local i, map, result, token, x, y
  3403.  
  3404.   x := create !entry.arg_list
  3405.   y := create !args
  3406.   map := table()
  3407.   while map[@x] := @y | ""
  3408.  
  3409.   entry.text ? {
  3410.     result := tab(many(Non_name_char)) | ""
  3411.     while token := tab(many(Name_char)) do {
  3412.       result ||:= \map[token] | token
  3413.       result ||:= tab(many(Non_name_char))
  3414.       }
  3415.     }
  3416.   return result
  3417. end
  3418.  
  3419. procedure in_text(name, text)
  3420.   text ? 
  3421.     return (pos(1) & tab(match(name)) & (upto(Non_name_char) | pos(0))) |
  3422.       (tab(find(name)) & move(-1) & tab(any(Non_name_char)) & move(*name) &
  3423.     any(Non_name_char) | pos(0))
  3424. end
  3425.  
  3426. # In order to simplify the evaluation the three relational operators that
  3427. # are longer than one character (<= ~= >=) are replaced by one character
  3428. # 'aliases'.
  3429. #
  3430. # One problem with eval_expr() is that the idea of failure as opposed to
  3431. # returning some special value can not be used.  For example if def(UNIX)
  3432. # fails eval_expr() would try to convert it to an integer as its next step.
  3433. # We would only want func() to fail if the argument is not a valid function,
  3434. # not if the function is valid and the call fails.  'Failure' is therefore
  3435. # represented by &null.
  3436.  
  3437. procedure const_expr(expr)
  3438.   local new, temp
  3439.  
  3440.   new := ""
  3441.   every new ||:= (" " ~== !expr)
  3442.   while new[find(">=", new) +: 2] := "\200" 
  3443.   while new[find("<=", new) +: 2] := "\201" 
  3444.   while new[find("~=", new) +: 2] := "\202" 
  3445.   return \eval_expr(new) | &null
  3446.  
  3447. end
  3448.  
  3449. procedure eval_expr(expr)
  3450.   while expr ?:= 2(="(", tab(bal(')')), pos(-1))
  3451.   return lassoc(expr, '&') | lassoc(expr, '|') | 
  3452.     lassoc(expr, '<=>\200\201\202' | '+-' | '*/%') | rassoc(expr, '^') | 
  3453.     func(expr) | integer(process_text(expr)) | error(expr, " :  Integer expected")
  3454. end
  3455.  
  3456. procedure lassoc(expr, op)
  3457.   local j
  3458.  
  3459.   expr ? {
  3460.     every j := bal(op)
  3461.     return eval(tab(\j), move(1), tab(0))
  3462.     }
  3463. end
  3464.  
  3465. procedure rassoc(expr, op)
  3466.   return expr ? eval(tab(bal(op)), move(1), tab(0))
  3467. end
  3468.  
  3469. procedure func(expr)
  3470.   local name, arg
  3471.  
  3472.   expr ? {
  3473.     (name := tab(upto('(')),
  3474.     arg := (move(1) & tab(upto(')')))) | fail 
  3475.     }
  3476.   if \name == ("def" | "ndef") then
  3477.     return name(arg)
  3478.   else
  3479.     error("Invalid function name") 
  3480. end
  3481.  
  3482. procedure eval(arg1, op, arg2)
  3483.   arg1 := process_text(\eval_expr(arg1)) | &null
  3484.   arg2 := process_text(\eval_expr(arg2)) | &null
  3485.   if (op ~== "&") & (op ~== "|") then
  3486.     (integer(arg1) & integer(arg2)) |
  3487.       error(map(op), " :  Arguments must be integers")
  3488.   return case op of {
  3489.     "+":    arg1 + arg2
  3490.     "-":    arg1 - arg2
  3491.     "*":    arg1 * arg2
  3492.     "/":    arg1 / arg2
  3493.     "%":    arg1 % arg2
  3494.     "^":    arg1 ^ arg2
  3495.     ">":     arg1 > arg2
  3496.     "=":    arg1 = arg2
  3497.     "<":    arg1 < arg2
  3498.     "\200":    arg1 >= arg2
  3499.     "\201":    arg1 <= arg2    
  3500.     "\202":    arg1 ~= arg2
  3501.     "|":    alt(arg1, arg2)    
  3502.     "&":    conjunction(arg1, arg2)
  3503.     }
  3504. end
  3505.  
  3506. procedure def(name)
  3507.   if \Defs[name] then
  3508.     return ""
  3509.   else
  3510.     return &null
  3511. end
  3512.  
  3513. procedure ndef(name)
  3514.   if \Defs[name] then
  3515.     return &null
  3516.   else
  3517.     return "" 
  3518. end
  3519.  
  3520. procedure alt(x, y)
  3521.   if \x then
  3522.     return x
  3523.   else if \y then
  3524.     return y
  3525.   else
  3526.     return &null
  3527. end
  3528.  
  3529. procedure conjunction(x, y)
  3530.   if \x & \y then
  3531.     return y
  3532.   else
  3533.     return &null
  3534. end
  3535.  
  3536. procedure map(op)
  3537.   return case op of {
  3538.     "\200":     ">="
  3539.     "\201":     "<="
  3540.     "\202":     "~="
  3541.     default:     op
  3542.     }
  3543. end
  3544.  
  3545. procedure dump()
  3546.   tab(many(White_space))
  3547.   if not(pos(0) | any('#')) then
  3548.     warning("Extraneous characters after dump command")
  3549.   every write(&errout, (!sort(Defs))[1])
  3550. end
  3551.  
  3552. procedure include()
  3553.   local ch, fname 
  3554.   static fname_chars
  3555.  
  3556.   initial fname_chars := Chars -- '<>"'
  3557.  
  3558.   if fname := 3(tab(many(White_space)), (tab(any('"')) & (ch := "\"")) |
  3559.     (tab(any('<')) & (ch := ">")), tab(many(fname_chars)), 
  3560.     tab(any('>"')) == ch, tab(many(White_space)) | pos(0)) then {
  3561.     if not(pos(0) | any('#')) then
  3562.       warning("Extraneous characters after include file name")
  3563.     if ch == ">" then 
  3564.       find_file(fname, Std_include_paths)
  3565.     else
  3566.       find_file(fname, Path_list)
  3567.     }
  3568.   else
  3569.     error("Missing or invalid include file name")
  3570. end
  3571.     
  3572. procedure find_file(fname, path_list)
  3573.   local ifile, ifname, path 
  3574.  
  3575.   every path := !path_list do {
  3576.     if path == ("" | ".") then
  3577.       ifname := fname
  3578.     else
  3579.       ifname := path || "/" || fname
  3580.     if ifile := open(ifname) then {
  3581.       if *Src_stack >= Depth then {
  3582.     close(ifile)
  3583.         error("Possibly infinitely recursive file inclusion")
  3584.     }
  3585.       if ifname == (Ifile_name | (!Src_stack).fname) then
  3586.         error("Infinitely recursive file inclusion")
  3587.       push(Src_stack, Src_desc(Ifile, Ifile_name))
  3588.       Ifile := ifile
  3589.       Ifile_name := ifname
  3590.       Line_no := 0
  3591.       return
  3592.       }
  3593.     }
  3594.     error("Can not open include file ", fname)
  3595. end
  3596.  
  3597. procedure def_opt(s)
  3598.   local name, text, Name
  3599.  
  3600.   s ? {
  3601.     name := tab(upto('=')) | tab(0)
  3602.     text := (move(1) & tab(0)) | "1"
  3603.     }
  3604.   if name == ("_LINE_" | "_FILE_") then
  3605.     error(name, " is a reserved name and can not be redefined by the -D option")
  3606.   if name ~==:= (tab(any(Init_name_char)) & tab(many(Name_char)) & pos(0)) then
  3607.     error(name, " :  Illegal name argument to -D option")
  3608.   if \Defs[Name] then
  3609.     warning(name, " : redefined by -D option")
  3610.   Defs[name] := Defs_rec([], text)
  3611. end
  3612.  
  3613. procedure warning(s1, s2)
  3614.   s1 ||:= \s2
  3615.   write(&errout, Ifile_name, ":  ", Line_no, ":  ", "Warning  " || s1)
  3616. end
  3617.  
  3618. procedure error(s1, s2)
  3619.   s1 ||:= \s2
  3620.   stop(Ifile_name, ":  ", Line_no, ":  ", "Error  " || s1)
  3621. end
  3622. ##########
  3623. iprint.icn
  3624. ############################################################################
  3625. #
  3626. #    Name:    iprint.icn
  3627. #
  3628. #    Title:    Print Icon program
  3629. #
  3630. #    Author:    Robert J. Alexander
  3631. #
  3632. #    Date:    June 10, 1988
  3633. #
  3634. ############################################################################
  3635. #  
  3636. #     The defaults are set up for printing of Icon programs, but
  3637. #  through command line options it can be set up to print programs
  3638. #  in other languages, too (such as C). This program has several
  3639. #  features:
  3640. #  
  3641. #     If a program is written in a consistent style, this program
  3642. #  will attempt to keep whole procedures on the same page. The
  3643. #  default is to identify the end of a print group (i.e. a pro-
  3644. #  cedure) by looking for the string "end" at the beginning of a
  3645. #  line. Through the -g option, alternative strings can be used to
  3646. #  signal end of a group. Using "end" as the group delimiter
  3647. #  (inclusive), comments and declarations prior to the procedure are
  3648. #  grouped with the procedure. Specifying a null group delimiter
  3649. #  string (-g '') suppresses grouping.
  3650. #  
  3651. #     Page creases are skipped over, and form-feeds (^L) imbedded in
  3652. #  the file are handled properly. (Form-feeds are treated as spaces
  3653. #  by many C compilers, and signal page ejects in a listing). Page
  3654. #  headings (file name, date, time, page number) are normally
  3655. #  printed unless suppressed by the -h option.
  3656. #  
  3657. #     Options:
  3658. #  
  3659. #       -n   number lines.
  3660. #  
  3661. #       -pN  page length: number of lines per page (default: 60
  3662. #            lines).
  3663. #  
  3664. #       -tN   tab stop spacing (default: 8).
  3665. #  
  3666. #       -h   suppress page headings.
  3667. #  
  3668. #       -l   add three lines at top of each page for laser printer.
  3669. #  
  3670. #       -gS  end of group string (default: "end").
  3671. #  
  3672. #       -cS  start of comment string (default: "#").
  3673. #  
  3674. #       -xS  end of comment string (default: none).
  3675. #  
  3676. #       -i   ignore FF at start of line.
  3677. #  
  3678. #     Any number of file names specified will be printed, each
  3679. #  starting on a new page.
  3680. #  
  3681. #     For example, to print C source files such as the Icon source
  3682. #  code, use the following options:
  3683. #  
  3684. #     iprint -g ' }' -c '/*' -x '*/' file ...
  3685. #  
  3686. #     Control lines:
  3687. #  
  3688. #     Control lines are special character strings that occur at the
  3689. #  beginnings of lines that signal special action. Control lines
  3690. #  begin with the start of comment string (see options). The control
  3691. #  lines currently recognized are:
  3692. #  
  3693. #     <comment string>eject -- page eject (line containing "eject"
  3694. #  does not print).
  3695. #  
  3696. #     <comment string>title -- define a title line to print at top
  3697. #  of each page. Title text is separated from the <comment
  3698. #  string>title control string by one space and is terminated by
  3699. #  <end of comment string> or end of line, whichever comes first.
  3700. #  
  3701. #     <comment string>subtitle -- define a sub-title line to print
  3702. #  at top of each page. Format is parallel to the "title" control
  3703. #  line, above.
  3704. #  
  3705. #     If a page eject is forced by maximum lines per page being
  3706. #  exceeded (rather than intentional eject via control line, ff, or
  3707. #  grouping), printing of blank lines at the top of the new page is
  3708. #  suppressed. Line numbers will still be printed correctly.
  3709. #  
  3710. ############################################################################
  3711. #
  3712. #  Links: options
  3713. #
  3714. ############################################################################
  3715.  
  3716. global pagelines,tabsize,lines,page,datetime,title,subtitle,pagestatus,blanks,
  3717.     group,numbers,noheaders,hstuff,gpat,comment,comment_end,laser,
  3718.     ignore_ff
  3719.  
  3720. procedure main(arg)
  3721.   local files,x
  3722.   &dateline ? {tab(find(",")) ; move(2) ; datetime := tab(0)}
  3723.   files := []
  3724.   pagelines := 60
  3725.   tabsize := 8
  3726.   gpat := "end"
  3727.   comment := "#"
  3728.  
  3729.   while x := get(arg) do {
  3730.     if match("-",x) then {    # Arg is an option
  3731.       case x[2] of {
  3732.     "n": numbers := "yes"
  3733.     "p": {
  3734.       pagelines := ("" ~== x[3:0]) | get(arg)
  3735.       if not (pagelines := integer(pagelines)) then
  3736.         stop("Invalid -p parameter: ",pagelines)
  3737.     }
  3738.     "t": {
  3739.       tabsize := ("" ~== x[3:0]) | get(arg)
  3740.       if not (tabsize := integer(tabsize)) then
  3741.         stop("Invalid -t parameter: ",tabsize)
  3742.     }
  3743.     "h": noheaders := "yes"
  3744.     "l": laser := "yes"
  3745.     "g": {
  3746.       gpat := ("" ~== x[3:0]) | get(arg)
  3747.     }
  3748.     "c": {
  3749.       comment := ("" ~== x[3:0]) | get(arg)
  3750.     }
  3751.     "x": {
  3752.       comment_end := ("" ~== x[3:0]) | get(arg)
  3753.     }
  3754.     "i": ignore_ff := "yes"
  3755.     default: stop("Invalid option ",x)
  3756.       }
  3757.     }
  3758.     else put(files,x)
  3759.   }
  3760.   if *files = 0 then stop("usage: iprint -options file ...\n_
  3761.     options:\n_
  3762.     \t-n\tnumber the lines\n_
  3763.     \t-p N\tspecify lines per page (default 60)\n_
  3764.     \t-t N\tspecify tab width (default 8)\n_
  3765.     \t-h\tsuppress page headers\n_
  3766.     \t-l\tadd 3 blank lines at top of each page\n_
  3767.     \t-g S\tpattern for last line in group\n_
  3768.     \t-c S\t'start of comment' string\n_
  3769.     \t-x S\t'end of comment' string\n_
  3770.     \t-i\tignore FF")
  3771.   every x := !files do expand(x)
  3772. end
  3773.  
  3774. procedure expand(fn)
  3775.   local f,line,cmd,linenbr,fname
  3776.   f := open(fn) | stop("Can't open ",fn)
  3777.   fn ? {
  3778.     while tab(find("/")) & move(1)
  3779.     fname := tab(0)
  3780.   }
  3781.   hstuff := fname || "  " || datetime || "  page "
  3782.   title := subtitle := &null
  3783.   lines := pagelines
  3784.   page := 0 ; linenbr := 0
  3785.   group := []
  3786.   while line := trim(read(f)) do {
  3787.     if \ignore_ff then while match("\f",line) do line[1] := ""
  3788.     linenbr +:= 1
  3789.     if match("\f",line) then {
  3790.       dumpgroup()
  3791.       lines := pagelines
  3792.       repeat {
  3793.     line[1] := ""
  3794.     if not match("\f",line) then break
  3795.       }
  3796.     }
  3797.     line ? {
  3798.       if =comment & cmd := =("eject" | "title" | "subtitle") then {
  3799.     dumpgroup()
  3800.     case cmd of {        # Command line
  3801.       "title": (move(1) & title := trim(tab(find(comment_end)))) |
  3802.         (title := &null)
  3803.       "subtitle": (move(1) & subtitle := trim(tab(find(comment_end)))) |
  3804.         (subtitle := &null)
  3805.     }
  3806.     lines := pagelines
  3807.       }
  3808.       else {    # Ordinary (non-command) line
  3809.     if not (*group = 0 & *line = 0) then {
  3810.       put(group,line)
  3811.       if \numbers then put(group,linenbr)
  3812.     }
  3813.     if endgroup(line) then dumpgroup()
  3814.       }
  3815.     }
  3816.   }
  3817.   dumpgroup()
  3818.   close(f)
  3819.   lines := pagelines
  3820. end
  3821.  
  3822. procedure dumpgroup()
  3823.   local line,linenbr
  3824.   if *group > 0 then {
  3825.     if lines + *group / ((\numbers & 2) | 1) + 2 >= pagelines then
  3826.     lines := pagelines
  3827.     else {write("\n") ; lines +:= 2}
  3828.     while line := get(group) do {
  3829.       if \numbers then linenbr := get(group)
  3830.       if lines >= pagelines then {
  3831.     printhead()
  3832.       }
  3833.       if *line = 0 then {
  3834.     if pagestatus ~== "empty" then {blanks +:= 1 ; lines +:= 1}
  3835.     next
  3836.       }
  3837.       every 1 to blanks do write()
  3838.       blanks := 0
  3839.       pagestatus := "not empty"
  3840.       if \numbers then writes(right(linenbr,5)," ")
  3841.       write(detab(line))
  3842.       lines +:= 1
  3843.     }
  3844.   }
  3845.   return
  3846. end
  3847.  
  3848. procedure endgroup(s)
  3849.   return match("" ~== gpat,s)
  3850. end
  3851.  
  3852. procedure printhead()
  3853.   static ff,pg
  3854.   writes(ff) ; ff := "\f"
  3855.   lines := 0
  3856.   pg := string(page +:= 1)
  3857.   if /noheaders then {
  3858.     if \laser then write("\n\n")
  3859.     write(left(\title | "",79 - *hstuff - *pg),hstuff,pg)
  3860.     lines +:= 2
  3861.     write(\subtitle) & lines +:= 1
  3862.     write()
  3863.   }
  3864.   pagestatus := "empty"
  3865.   blanks := 0
  3866.   return
  3867. end
  3868.  
  3869. procedure detab(s)
  3870.   local t
  3871.   t := ""
  3872.   s ? {
  3873.     while t ||:= tab(find("\t")) do {
  3874.       t ||:= repl(" ",tabsize - *t % tabsize)
  3875.       move(1)
  3876.     }
  3877.     t ||:= tab(0)
  3878.   }
  3879.   return t
  3880. end
  3881.  
  3882. ##########
  3883. ipsort.icn
  3884. ############################################################################
  3885. #
  3886. #    Name:    ipsort.icn
  3887. #
  3888. #    Title:    Sort Icon procedures
  3889. #
  3890. #    Author:    Ralph E. Griswold
  3891. #
  3892. #    Date:    June 10, 1988
  3893. #
  3894. ############################################################################
  3895. #  
  3896. #     This program reads an Icon program and writes an equivalent
  3897. #  program with the procedures sorted alphabetically. Global, link,
  3898. #  and record declarations come first in the order they appear in
  3899. #  the original program.  The main procedure comes next followed by
  3900. #  the remaining procedures in alphabetical order.
  3901. #  
  3902. #     Comments and white space between declarations are attached to
  3903. #  the next following declaration.
  3904. #  
  3905. #  Limitations: This program only recognizes declarations that start
  3906. #  at the beginning of a line.
  3907. #  
  3908. #     Comments and interline white space between declarations may
  3909. #  not come out as intended.
  3910. #  
  3911. ############################################################################
  3912.  
  3913. procedure main()
  3914.    local line, x, i, proctable, proclist, comments, procname
  3915.  
  3916.    comments := []            # list of comment lines
  3917.    proctable := table()            # table of procedure declarations
  3918.  
  3919.    while line := read() do {
  3920.      line ? {
  3921.         if ="procedure" &        #  procedure declaration
  3922.            tab(many('\t ')) &
  3923.            procname := tab(upto('(')) | stop("*** bad syntax: ",line)
  3924.         then {                # if main, force sorting order
  3925.            if procname == "main" then procname := "\0main"
  3926.            proctable[procname] := x := []
  3927.            while put(x,get(comments))    #  save it
  3928.            put(x,line)
  3929.            while line := read() do {
  3930.               put(x,line)
  3931.               if line == "end" then break
  3932.               }
  3933.            }
  3934.                     #  other declarations
  3935.          else if =("global" | "record" | "link")
  3936.          then {
  3937.             while write(get(comments))
  3938.             write(line)
  3939.             }
  3940.          else put(comments,line)
  3941.          }
  3942.       }
  3943.  
  3944.    while write(get(comments))
  3945.  
  3946.    proclist := sort(proctable,3)        #  sort procedures
  3947.  
  3948.    while get(proclist) do
  3949.       every write(!get(proclist))
  3950.  
  3951. end
  3952. ##########
  3953. ipsplit.icn
  3954. ############################################################################
  3955. #
  3956. #    Name:    ipsplit.icn
  3957. #
  3958. #    Title:    Split Icon program into separate files
  3959. #
  3960. #    Author:    Ralph E. Griswold
  3961. #
  3962. #    Date:    June 10, 1988
  3963. #
  3964. ############################################################################
  3965. #  
  3966. #     This progam reads an Icon program and writes each procedure to
  3967. #  a separate file. The output file names consist of the procedure
  3968. #  name with .icn appended.  If the -g option is specified, any glo-
  3969. #  bal, link, and record declarations are written to that file. Oth-
  3970. #  erwise they are written in the file for the procedure that
  3971. #  immediately follows them.
  3972. #  
  3973. #     Comments and white space between declarations are attached to
  3974. #  the next following declaration.
  3975. #  
  3976. #  Notes:
  3977. #
  3978. #     The program only recognizes declarations that start at the
  3979. #  beginning of lines.  Comments and interline white space between
  3980. #  declarations may not come out as intended.
  3981. #  
  3982. #     If the -g option is not specified, any global, link, or record
  3983. #  declarations that follow the last procedure are discarded.
  3984. #  
  3985. ############################################################################
  3986. #
  3987. #  Links: options
  3988. #
  3989. ############################################################################
  3990.  
  3991. link options
  3992.  
  3993. procedure main(args)
  3994.    local line, x, i, proctable, proclist, comments, gfile, gname, ofile
  3995.    local opts
  3996.  
  3997.    comments := []
  3998.  
  3999.    opts := options(args,"g:")
  4000.    if gname := \opts["g"] then {
  4001.       gfile := open(gname,"w") | stop("*** cannot open ",gname)
  4002.       }
  4003.  
  4004.    proctable := table()
  4005.    while line := read() do {
  4006.       if line ? {
  4007.          ="procedure" &            #  procedure declaration
  4008.          tab(many(' ')) &
  4009.          proctable[tab(upto('('))] := x := []
  4010.          } then {
  4011.             while put(x,get(comments))    #  save it
  4012.             put(x,line)
  4013.             i := 1
  4014.             while line := read() do {
  4015.                put(x,line)
  4016.                if line == "end" then break
  4017.                }
  4018.             }
  4019.                     #  other declarations
  4020.          else if \gfile & line ? =("global" | "record" | "link")
  4021.          then {
  4022.             while write(gfile,get(comments))
  4023.             write(gfile,line)
  4024.             }
  4025.          else put(comments,line)
  4026.          }
  4027.    while write(\gfile,get(comments))
  4028.    proclist := sort(proctable,3)    #  sort procedures
  4029.    while x := get(proclist) do {    #  output procedures
  4030.       ofile := open(x || ".icn","w") | stop("cannot write ",x,".icn")
  4031.       every write(ofile,!get(proclist))
  4032.       close(ofile)
  4033.       }
  4034. end
  4035. ##########
  4036. ipxref.icn
  4037. ############################################################################
  4038. #
  4039. #    Name:    ipxref.icn
  4040. #
  4041. #    Title:    Produce cross reference for Icon program
  4042. #
  4043. #    Author:    Allan J. Anderson
  4044. #
  4045. #    Date:    June 10, 1988
  4046. #
  4047. ############################################################################
  4048. #  
  4049. #     This program cross-references Icon programs. It lists the
  4050. #  occurrences of each variable by line number. Variables are listed
  4051. #  by procedure or separately as globals.  The options specify the
  4052. #  formatting of the output and whether or not to cross-reference
  4053. #  quoted strings and non-alphanumerics. Variables that are followed
  4054. #  by a left parenthesis are listed with an asterisk following the
  4055. #  name.  If a file is not specified, then standard input is cross-
  4056. #  referenced.
  4057. #  
  4058. #  Options: The following options change the format defaults:
  4059. #  
  4060. #       -c n The column width per line number. The default is 4
  4061. #            columns wide.
  4062. #  
  4063. #       -l n The starting column (i.e. left margin) of the line
  4064. #            numbers.  The default is column 40.
  4065. #  
  4066. #       -w n The column width of the whole output line. The default
  4067. #            is 80 columns wide.
  4068. #  
  4069. #     Normally only alphanumerics are cross-referenced. These
  4070. #  options expand what is considered:
  4071. #  
  4072. #       -q   Include quoted strings.
  4073. #  
  4074. #       -x   Include all non-alphanumerics.
  4075. #  
  4076. #  Note: This program assumes the subject file is a valid Icon pro-
  4077. #  gram. For example, quotes are expected to be matched.
  4078. #  
  4079. ############################################################################
  4080. #
  4081. #  Bugs:
  4082. #
  4083. #     In some situations, the output is not properly formatted.
  4084. #
  4085. ############################################################################
  4086. #
  4087. #  Links: options
  4088. #
  4089. ############################################################################
  4090.  
  4091. link options
  4092.  
  4093. global resword, linenum, letters, alphas, var, buffer, qflag, infile, xflag
  4094. global inmaxcol, inlmarg, inchunk, localvar, lin
  4095.  
  4096. record procrec(pname,begline,lastline)
  4097.  
  4098. procedure main(args)
  4099.  
  4100.    local word, w2, p, prec, i, L, ln, switches, nfile
  4101.  
  4102.    resword := ["break","by","case","default","do","dynamic","else","end",
  4103.       "every","fail","global","if","initial","link", "local","next","not",
  4104.       "of","procedure", "record","repeat","return","static","suspend","then",
  4105.       "to","until","while"]
  4106.    linenum := 0
  4107.    var := table()        # var[variable[proc]] is list of line numbers
  4108.    prec := []            # list of procedure records
  4109.    localvar := []        # list of local variables of current routine
  4110.    buffer := []            # a put-back buffer for getword
  4111.    proc := "global"
  4112.    letters := &letters ++ '_'
  4113.    alphas := letters ++ &digits
  4114.  
  4115.    switches := options(args,"qxw+l+c+")
  4116.  
  4117.    if \switches["q"] then qflag := 1
  4118.    if \switches["x"] then xflag := 1
  4119.    inmaxcol := \switches["w"]
  4120.    inlmarg := \switches["l"]
  4121.    inchunk := \switches["c"]
  4122.    infile := open(args[1],"r")     # could use some checking
  4123.  
  4124.    while word := getword() do
  4125.       if word == "link" then {
  4126.          buffer := []
  4127.          lin := ""
  4128.          next
  4129.          }
  4130.       else if word == "procedure" then {
  4131.          put(prec,procrec("",linenum,0))
  4132.          proc := getword() | break
  4133.          p := pull(prec)
  4134.          p.pname := proc
  4135.          put(prec,p)
  4136.          }
  4137.       else if word == ("global" | "link" | "record") then {
  4138.          word := getword() | break
  4139.          addword(word,"global",linenum)
  4140.          while (w2 := getword()) == "," do {
  4141.             if word == !resword then break
  4142.             word := getword() | break
  4143.             addword(word,"global",linenum)
  4144.             }
  4145.          put(buffer,w2)
  4146.          }
  4147.       else if word == ("local" | "dynamic" | "static") then {
  4148.          word := getword() | break
  4149.          put(localvar,word)
  4150.          addword(word,proc,linenum)
  4151.          while (w2 := getword()) == "," do {
  4152.             if word == !resword then break
  4153.             word := getword() | break
  4154.             put(localvar,word)
  4155.             addword(word,proc,linenum)
  4156.             }
  4157.          put(buffer,w2)
  4158.          }
  4159.       else if word == "end" then {
  4160.          proc := "global"
  4161.          localvar := []
  4162.          p := pull(prec)
  4163.          p.lastline := linenum
  4164.          put(prec,p)
  4165.          }
  4166.       else if word == !resword then 
  4167.          next
  4168.       else {
  4169.          ln := linenum
  4170.          if (w2 := getword()) == "(" then
  4171.             word ||:= " *"            # special mark for procedures
  4172.          else
  4173.             put(buffer,w2)            # put back w2
  4174.          addword(word,proc,ln)
  4175.          }
  4176.    every write(!format(var))
  4177.    write("\n\nprocedures:\tlines:\n")
  4178.    L := []
  4179.    every p := !prec do
  4180.       put(L,left(p.pname,16," ") || p.begline || "-" || p.lastline)
  4181.    every write(!sort(L))
  4182. end
  4183.  
  4184. procedure addword(word,proc,lineno)
  4185.    if any(letters,word) | \xflag then {
  4186.       /var[word] := table()
  4187.       if /var[word]["global"] | (word == !\localvar) then {
  4188.          /(var[word])[proc] := [word,proc]
  4189.          put((var[word])[proc],lineno)
  4190.          }
  4191.       else {
  4192.          /var[word]["global"] := [word,"global"]
  4193.          put((var[word])["global"],lineno)
  4194.          }
  4195.       }
  4196. end
  4197.  
  4198. procedure getword()
  4199.    local j, c
  4200.    static i, nonwhite
  4201.    initial nonwhite := ~' \t\n'
  4202.  
  4203.    repeat {
  4204.       if *buffer > 0 then return get(buffer)
  4205.       if /lin | i = *lin + 1 then
  4206.          if lin := read(infile) then {
  4207.             i := 1
  4208.             linenum +:= 1
  4209.             }
  4210.          else fail
  4211.       if i := upto(nonwhite,lin,i) then {   # skip white space
  4212.          j := i
  4213.          if lin[i] == ("'" | "\"") then {   # don't xref quoted words
  4214.             if /qflag then {
  4215.                c := lin[i]
  4216.                i +:= 1
  4217.                repeat
  4218.                   if i := upto(c ++ '\\',lin,i) + 1 then
  4219.                      if lin[i - 1] == c then break
  4220.                      else i +:= 1
  4221.                   else {
  4222.                      i := 1
  4223.                      linenum +:= 1
  4224.                      lin := read(infile) | fail
  4225.                      }
  4226.                }
  4227.             else i +:= 1
  4228.             }
  4229.          else if lin[i] == "#" then {    # don't xref comments; get next line
  4230.             i := *lin + 1
  4231.             }
  4232.          else if i := many(alphas,lin,i) then
  4233.             return lin[j:i]
  4234.          else {
  4235.             i +:= 1
  4236.             return lin[i - 1]
  4237.             }
  4238.          }
  4239.       else
  4240.          i := *lin + 1
  4241.    }       # repeat
  4242. end
  4243.  
  4244. procedure format(T)
  4245.    local V, block, n, L, lin, maxcol, lmargin, chunk, col
  4246.    initial {
  4247.       maxcol := \inmaxcol | 80
  4248.       lmargin := \inlmarg | 40
  4249.       chunk := \inchunk | 4
  4250.       }
  4251.    L := []
  4252.    col := lmargin
  4253.    every V := !T do
  4254.       every block := !V do {
  4255.          lin := left(block[1],16," ") || left(block[2],lmargin - 16," ")
  4256.          every lin ||:= center(block[3 to *block],chunk," ") do {
  4257.             col +:= chunk
  4258.             if col >= maxcol - chunk then {
  4259.                lin ||:= "\n\t\t\t\t\t"
  4260.                col := lmargin
  4261.                }
  4262.             }
  4263.          if col = lmargin then lin := lin[1:-6] # came out exactly even
  4264.          put(L,lin)
  4265.          col := lmargin
  4266.          }
  4267.    L := sort(L)
  4268.    push(L,"variable\tprocedure\t\tline numbers\n")
  4269.    return L
  4270. end
  4271. ##########
  4272. itab.icn
  4273. ############################################################################
  4274. #
  4275. #    Name:    itab.icn
  4276. #
  4277. #    Title:    Entab an Icon program
  4278. #
  4279. #    Author:    Robert J. Alexander
  4280. #
  4281. #    Date:    December 5, 1989
  4282. #
  4283. ############################################################################
  4284. #
  4285. #  itab -- Entab an Icon program, leaving quoted strings alone.
  4286. #
  4287. #       itab [input-tab-spacing] [output-tab-spacing] 
  4288. #                       < source-program > entabbed-program
  4289. #
  4290. #  Observes Icon Programming Language conventions for escapes and
  4291. #  continuations in string constants.  Input and output tab spacing
  4292. #  defaults to 8.
  4293. #
  4294. ############################################################################
  4295.  
  4296. global mapchars,intabs
  4297.  
  4298. procedure main(arg)
  4299.    local outtabs, line, c, nonwhite, delim
  4300.  
  4301.    intabs := (arg[1] | 8) + 1
  4302.    outtabs := (arg[2] | 8) + 1
  4303.    line := ""
  4304.    while c := readx() do {
  4305.       if not any(' \t',c) then nonwhite := 1
  4306.       case c of {
  4307.      "\n": {
  4308.         write(map(entab(line,outtabs),\mapchars," \t") | line)
  4309.         line := ""
  4310.         nonwhite := &null
  4311.         }
  4312.      "'" | "\"": {
  4313.         (/delim := c) | (delim := &null)
  4314.         line ||:= c
  4315.         }
  4316.      "\\": line ||:= c || readx()
  4317.      default: {
  4318.         line ||:= if \delim & \nonwhite & \mapchars then
  4319.           map(c," \t",mapchars) else c
  4320.         }
  4321.      }
  4322.       }
  4323. end
  4324.  
  4325. procedure readx()
  4326.    static buf,printchars
  4327.    initial {
  4328.       buf := ""
  4329.       printchars := &cset[33:128]
  4330.       }
  4331.    if *buf = 0 then {
  4332.       buf := detab(read(),intabs) || "\n" | fail
  4333.       mapchars := (printchars -- buf)[1+:2] | &null
  4334.       }
  4335.    return 1(.buf[1],buf[1] := "")
  4336. end
  4337. ##########
  4338. iundecl.icn
  4339. ############################################################################
  4340. #
  4341. #    Name:    undeclared.icn
  4342. #
  4343. #    Title:    Utility to find undeclared variables in Icon source program.
  4344. #
  4345. #    Author:    Robert J. Alexander
  4346. #
  4347. #    Date:    March 11, 1990
  4348. #
  4349. ############################################################################
  4350. #
  4351. #  This program invokes icont to find undeclared variables in an Icon
  4352. #  source program.  The output is in the form of a "local" declaration,
  4353. #  preceded by a comment line that identifies that procedure and file
  4354. #  name from whence it arose.  Beware that undeclared variables aren't
  4355. #  necessarily local, so any which are intended to be global must be
  4356. #  removed from the generated list.
  4357. #
  4358. #  Multiple files can be specified as arguments, and will be processed
  4359. #  in sequence.  A file name of "-" represents the standard input file.
  4360. #  If there are no arguments, standard input is processed.
  4361. #
  4362. #  The program works only if procedures are formatted such that the
  4363. #  keywords "procedure" and "end" are the first words on their
  4364. #  respective lines.
  4365. #
  4366. #  Only for UNIX, since the "p" (pipe) option of open() is used.
  4367. #
  4368. ############################################################################
  4369. #
  4370. #  Requires: UNIX
  4371. #
  4372. ############################################################################
  4373.  
  4374. link filename
  4375.  
  4376. procedure main(arg)
  4377.    local f, fn, line, names, p, sep, t, argstring, undeclared, pn
  4378.    #
  4379.    #  Process command line file names.
  4380.    #
  4381.    if *arg = 0 then arg := ["-"] # if no arguments, standard input
  4382.    #
  4383.    #  Build a set of all the undeclared identifiers.
  4384.    #
  4385.    argstring := ""
  4386.    every argstring ||:= " " || !arg
  4387.    p := open("icont -s -u -o /dev/null 2>&1" || argstring,"p") |
  4388.        stop("popen failed")
  4389.    undeclared := set()
  4390.    while line := read(p) do line ?
  4391.      if find("undeclared identifier") then
  4392.            tab(find("\"") + 1) & insert(undeclared,tab(find("\"")))
  4393.    close(p)
  4394.    #
  4395.    #  Loop through files to process individual procedures.
  4396.    #
  4397.    every fn := !arg do {
  4398.       f := if fn == "-" then &input else {
  4399.      fn := \suffix(fn)[1] || ".icn"
  4400.      open(fn) | stop("Can't open input file \"",fn,"\"")
  4401.      }
  4402.       #
  4403.       #  Loop to process lines of file (in string scanning mode).
  4404.       #
  4405.       while line := read(f) do line ? {
  4406.      if tab(many(' \t')) | "" & ="procedure" & tab(many(' \t')) then {
  4407.         t := open("undeclared_tmp.icn","w") | stop("Can't open work file")
  4408.         write(t,line)
  4409.         while line := read(f) do line ? {
  4410.            write(t,line)
  4411.            if tab(many(' \t')) | "" & ="end" & many(' \t') | pos(0) then
  4412.              break
  4413.            }
  4414.         close(t)
  4415.         #
  4416.             #  Now we have an isolated Icon procedure -- invoke icont to
  4417.         #  determine its undeclared variables.
  4418.         #
  4419.         p := open("icont -s -u -o /dev/null 2>&1 undeclared_tmp.icn","p") |
  4420.           stop("popen failed")
  4421.         names := []
  4422.         while line := read(p) do line ?
  4423.           if find("undeclared identifier") then
  4424.             tab(find("\"") + 1) &
  4425.             put(names,member(undeclared,tab(find("\""))))
  4426.         close(p)
  4427.         #
  4428.         #  Output the declaration.
  4429.         #
  4430.         pn := "\"" || tab(upto(' \t(')) || "\"" ||
  4431.           if *arg > 1 then " (" || fn || ")" else ""
  4432.         if *names = 0 then write("# ",pn," is OK")
  4433.         else {
  4434.            write("# Local declarations for procedure ",pn)
  4435.            sep := "local "
  4436.            every writes(sep,!sort(names)) do sep := ","
  4437.            write()
  4438.            }
  4439.         }
  4440.      }
  4441.       #
  4442.       #  Close this input file.
  4443.       #
  4444.       close(f)
  4445.       }
  4446.    remove("undeclared_tmp.icn")
  4447. end
  4448.  
  4449.  
  4450. ##########
  4451. iwriter.icn
  4452. ############################################################################
  4453. #
  4454. #    Name:    iwriter.icn
  4455. #
  4456. #    Title:    Write Icon code to write input
  4457. #
  4458. #    Author:    Ralph E. Griswold
  4459. #
  4460. #    Date:    March 7, 1990
  4461. #
  4462. ############################################################################
  4463. #
  4464. #     Program that reads standard input and produces Icon expressions,
  4465. #  which when compiled and executed, write out the original input.
  4466. #
  4467. #     This is handy for incorporating, for example, message text in
  4468. #  Icon programs.  Or even for writing Icon programs that write Icon
  4469. #  programs that ... .
  4470.  
  4471. procedure main()
  4472.  
  4473.    while  write("write(",image(read()),")")
  4474.  
  4475. end
  4476. ##########
  4477. krieg.icn
  4478. ############################################################################
  4479. #
  4480. #    Name:    krieg.icn
  4481. #
  4482. #    Title:    Play kriegspiel
  4483. #
  4484. #    Author:    David J. Slate
  4485. #
  4486. #    Date:    July 25, 1989
  4487. #
  4488. ############################################################################
  4489. #
  4490. #   The game:
  4491. #   
  4492. #   Kriegspiel (German for "war game") implements a monitor and, if desired,
  4493. #   an automatic opponent for a variation of the game of chess which has the
  4494. #   same rules and goal as ordinary chess except that neither player sees
  4495. #   the other's moves or pieces.  Thus Kriegspiel combines the intricacies
  4496. #   and flavor of chess with additional elements of uncertainty, psychology,
  4497. #   subterfuge, etc., which characterize games of imperfect information such
  4498. #   as bridge or poker.
  4499. #   
  4500. #   The version of the game implemented here was learned by the author
  4501. #   informally many years ago.  There may be other variations, and perhaps
  4502. #   the rules are actually written down somewhere in some book of games.
  4503. #   
  4504. #   The game is usually played in a room with three chess boards set up on
  4505. #   separate tables.  The players sit at the two end tables facing away from
  4506. #   each other.  A third participant, the "monitor", acts as a referee and
  4507. #   scorekeeper and keeps track of the actual game on the middle board,
  4508. #   which is also out of sight of either player.  Since each player knows
  4509. #   only his own moves, he can only guess the position of the enemy pieces,
  4510. #   so he may place and move these pieces on his board wherever he likes.
  4511. #   
  4512. #   To start the game, the "White" player makes a move on his board.  If the
  4513. #   move is legal, the monitor plays it on his board and invites "Black" to
  4514. #   make his response.  If a move attempt is illegal (because it leaves the
  4515. #   king in check or tries to move through an enemy piece, etc.), the
  4516. #   monitor announces that fact to both players and the moving player must
  4517. #   try again until he finds a legal move.  Thus the game continues until it
  4518. #   ends by checkmate, draw, or agreement by the players.  Usually the
  4519. #   monitor keeps a record of the moves so that the players can play the
  4520. #   game over at its conclusion and see what actually happened, which is
  4521. #   often quite amusing.
  4522. #   
  4523. #   With no additional information provided by the monitor, the game is very
  4524. #   difficult but, surprisingly, still playable, with viable tactical and
  4525. #   strategic ideas.  Usually, however, the monitor gives some minimal
  4526. #   feedback to both players about certain events.  The locations of
  4527. #   captures are announced as well as the directions from which checks on
  4528. #   the kings originate.
  4529. #   
  4530. #   Even with the feedback about checks and captures, a newcomer to
  4531. #   Kriegspiel might still think that the players have so little information
  4532. #   that they could do little more than shuffle around randomly hoping to
  4533. #   accidentally capture enemy pieces or checkmate the enemy king.  But in
  4534. #   fact a skilled player can infer a lot about his opponent's position and
  4535. #   put together plans with a good chance of success.  Once he achieves a
  4536. #   substantial material and positional advantage, with proper technique he
  4537. #   can usually exploit it by mopping up the enemy pieces, promoting pawns,
  4538. #   and finally checkmating the enemy king as he would in an ordinary chess
  4539. #   game.  In the author's experience, a skilled Kriegspiel player will win
  4540. #   most games against a novice, even if both players are equally matched at
  4541. #   regular chess.
  4542. #   
  4543. #   The implementation:
  4544. #   
  4545. #   The functions of this program are to replace the human monitor, whose
  4546. #   job is actually fairly difficult to do without mistakes, to permit the
  4547. #   players to play from widely separate locations, to produce a machine-
  4548. #   readable record of the game, and to provide, if desired, a computer
  4549. #   opponent for a single player to practice and spar with.
  4550. #   
  4551. #   When two humans play, each logs in to the same computer from a separate
  4552. #   terminal and executes his own copy of the program.  This requires a
  4553. #   multi-tasking, multi-user operating system.  For various reasons, the
  4554. #   author chose to implement Kriegspiel under Unix, using named pipes for
  4555. #   inter-process communication.  The program has been tested successfully
  4556. #   under Icon Version 7.5 on a DecStation 3100 running Ultrix (a Berkeley-
  4557. #   style Unix) and also under Icon Version 7.0 on the ATT Unix-PC and
  4558. #   another System V machine, but unanticipated problems could be
  4559. #   encountered by the installer on other computers.  An ambitious user may
  4560. #   be able to port the program to non-Unix systems such as Vax-VMS.  It may
  4561. #   also be possible to implement Kriegspiel on a non-multi-tasking system
  4562. #   such as MS-DOS by using separate computers linked via serial port or
  4563. #   other network.  See the "init" procedure for much of the system-
  4564. #   dependent code for getting user name, setting up communication files,
  4565. #   etc.
  4566. #   
  4567. #   Two prospective opponents should agree on who is to play "white", make
  4568. #   sure they know each other's names, and then execute Kriegspiel from
  4569. #   their respective terminals.  The program will prompt each player for his
  4570. #   name (which defaults to his user or login name), his piece color, the
  4571. #   name of his opponent, whether he wishes to play in "totally blind" mode
  4572. #   (no capture or check information - not recommended for beginners), and
  4573. #   the name of the log file on which the program will leave a record of the
  4574. #   game (the program supplies a default in /tmp).  Each program will set up
  4575. #   some communication files and wait for the opponent's to show up.  Once
  4576. #   communication is established, each player will be prompted for moves and
  4577. #   given information as appropriate.  The online "help" facility documents
  4578. #   various additional commands and responses.
  4579. #   
  4580. #   A player who wants a computer opponent should select "auto" as his
  4581. #   opponent's name.  Play then proceeds as with a human opponent.  "Auto"
  4582. #   is currently not very strong, but probably requires more than novice
  4583. #   skill to defeat.
  4584. #
  4585. #   Known bugs and limitations:
  4586. #
  4587. #   No bugs are currently known in the areas of legal move generation,
  4588. #   board position updating, checkmate detection, etc., but it is still
  4589. #   possible that there are a few.
  4590. #
  4591. #   Some cases of insufficient checkmating material on both sides are
  4592. #   not detected as draws by the program.
  4593. #
  4594. #   In the current implementation, a player may not play two
  4595. #   simultaneous games under the same user name with the same piece color.
  4596. #
  4597. #   If the program is terminated abnormally it may leave a communication
  4598. #   pipe file in /tmp.
  4599.  
  4600.  
  4601. record board( pcs, cmv, cnm, caswq, caswk, casbq, casbk, fepp, ply)
  4602.  
  4603. global    Me, Yu, Mycname, Yrcname, Mycomm, Yrcomm, Logname, Logfile,
  4604.     Mycol, Yrcol, Blind, Bg, Frinclst, Lmv, Any, Tries, Remind
  4605.  
  4606.  
  4607. procedure automov( )
  4608.  
  4609. #   Returns a pseudo-randomly selected move type-in to be used in
  4610. #   "auto opponent" mode.  But if possible, try to recapture (unless in
  4611. #   blind mode):
  4612.  
  4613.     local    m, ms
  4614.     static    anyflag
  4615.  
  4616.     initial    anyflag := 0
  4617.  
  4618.     if anyflag = 0 then {
  4619.     anyflag := 1
  4620.     return "any"
  4621.     }
  4622.     anyflag := 0
  4623.  
  4624.     ms := set( )
  4625.     every insert( ms, movgen( Bg))
  4626.  
  4627.     if / Any then {
  4628.     if find( ":", \ Lmv) & not find( "ep", \ Lmv) & / Blind then {
  4629.         every m := ! ms do {
  4630.         if m[ 4:6] == Lmv[ 4:6]  & movlegal( Bg, m) then
  4631.             return m[ 2:6] || "Q"
  4632.         }
  4633.         }
  4634.     while * ms ~= 0 do {
  4635.         if movlegal( Bg, m := ? ms) then
  4636.         return m[ 2:6] || "Q"
  4637.         delete( ms, m)
  4638.         }
  4639.     return "end"
  4640.     }
  4641.     else {
  4642.     every m := ! ms do {
  4643.         if m[ 1] == "P" & m[ 6] == ":" & movlegal( Bg, m) then
  4644.         return m[ 2:6] || "Q"
  4645.         }
  4646.     return "end"
  4647.     }
  4648. end
  4649.  
  4650.  
  4651. procedure chksqrs( b)
  4652.  
  4653. #   Generates the set of squares of pieces giving check in board b;
  4654. #   fails if moving side's king not in check:
  4655.  
  4656.     local    sk
  4657.  
  4658.     sk := find( pc2p( "K", b.cmv), b.pcs)
  4659.     suspend sqratks( b.pcs, sk, b.cnm)
  4660. end
  4661.  
  4662.  
  4663. procedure fr2s( file, rank)
  4664.  
  4665. #   Returns the square number corresponding to "file" and "rank"
  4666. #   numbers; fails if invalid file and/or rank:
  4667.  
  4668.     return (0 < (9 > file)) + 8 * (0 < ( 9 > rank)) - 8
  4669. end
  4670.  
  4671.  
  4672. procedure gamend( b)
  4673.  
  4674. #   If the position b is at end of game,
  4675. #   return an ascii string giving the result; otherwise, fail:
  4676.  
  4677.     local    nbn, sk
  4678.  
  4679.     sk := find( pc2p( "K", b.cmv), b.pcs)
  4680.  
  4681.     if not movlegal( b, movgen( b, sk)) & not movlegal( b, movgen( b)) then {
  4682.     if chksqrs( b) then {
  4683.         if b.cnm[ 1] == "W" then
  4684.         return "1-0"
  4685.         else
  4686.         return "0-1"
  4687.         }
  4688.     else
  4689.         return "1/2-1/2"
  4690.     }
  4691.     else if not upto( 'PRQprq', b.pcs) then {
  4692.     nbn := 0
  4693.     every upto( 'NBnb', b.pcs) do
  4694.         nbn +:= 1
  4695.     if nbn < 2 then
  4696.         return "1/2-1/2"
  4697.     }
  4698. end
  4699.     
  4700.  
  4701. procedure init( )
  4702.  
  4703. #   init initializes the program:
  4704.  
  4705.     local    whopipe, line, namdelim
  4706.  
  4707. #   Setup a data table for move generation:
  4708.  
  4709.     Frinclst := table( )
  4710.     Frinclst[ "R"] := [ [1, 0],  [0, 1],  [-1, 0],  [0, -1] ]
  4711.     Frinclst[ "N"] := [ [2, 1], [1, 2], [-1, 2], [-2, 1],
  4712.             [-2, -1], [-1, -2], [1, -2], [2, -1] ]
  4713.     Frinclst[ "B"] := [ [1, 1],  [-1, 1],  [-1, -1],  [1, -1] ]
  4714.     Frinclst[ "Q"] := Frinclst[ "R"] ||| Frinclst[ "B"]
  4715.     Frinclst[ "K"] := Frinclst[ "Q"]
  4716.     Frinclst[ "r"] := Frinclst[ "R"]
  4717.     Frinclst[ "n"] := Frinclst[ "N"]
  4718.     Frinclst[ "b"] := Frinclst[ "B"]
  4719.     Frinclst[ "q"] := Frinclst[ "Q"]
  4720.     Frinclst[ "k"] := Frinclst[ "K"]
  4721.  
  4722. #   Setup a character set to delimit user names:
  4723.  
  4724.     namdelim := ~(&letters ++ &digits ++ '_.-')
  4725.  
  4726. #   Set reminder bell flag to off:
  4727.  
  4728.     Remind := ""
  4729.  
  4730. #   Set random number seed:
  4731.  
  4732.     &random := integer( map( "hxmysz", "hx:my:sz", &clock))
  4733.  
  4734. #   Get my name from user or "who am I" command and issue greeting:
  4735.  
  4736.     writes( "Your name (up to 8 letters & digits; default = user name)? ")
  4737.     line := read( ) | kstop( "can't read user name")
  4738.     Me := tokens( line, namdelim)
  4739.     if /Me then {
  4740.     whopipe := open( "who am i | awk '{print $1}' | sed 's/^.*!//'", "rp")
  4741.     Me := tokens( read( whopipe), namdelim)
  4742.     close( \whopipe)
  4743.     }
  4744.     if /Me then
  4745.     write( "Can't get user name from system.")
  4746.     while /Me do {
  4747.     writes( "Your name? ")
  4748.     line := read( ) | kstop( "can't get user name")
  4749.     Me := tokens( line, namdelim)
  4750.     }
  4751.     write( "Welcome, ", Me, ", to Kriegspiel (double blind chess).")
  4752.  
  4753. #   Prompt user to enter color:
  4754.  
  4755.     while writes( "Your color (w or b)? ") do {
  4756.     line := read( ) | kstop( "can't read color")
  4757.     if find( line[ 1], "WwBb") then
  4758.         break
  4759.     }
  4760.     Mycol := (find( line[ 1], "Ww"), "White") | "Black"
  4761.     Yrcol := map( Mycol, "WhiteBlack", "BlackWhite")
  4762.  
  4763. #   Prompt user to enter opponent name:
  4764.  
  4765.     writes( "Enter opponent's name (default = auto): ")
  4766.     Yu := tokens( read( ), namdelim) | "auto"
  4767.  
  4768. #   Prompt user to select "blind" mode, if desired:
  4769.  
  4770.     writes( "Totally blind mode (default is no)? ")
  4771.     Blind := find( (tokens( read( )) \ 1)[ 1], "Yy")
  4772.  
  4773. #   Set communication file names and create my communication file:
  4774.  
  4775.     if Yu == "auto" then {
  4776.     Mycname := "/dev/null"
  4777.     Yrcname := "/dev/null"
  4778.     }
  4779.     else {
  4780.     Mycname := "/tmp/krcom" || Mycol[ 1] || Me
  4781.     Yrcname := "/tmp/krcom" || Yrcol[ 1] || Yu
  4782.     remove( Mycname)
  4783.     system( "/etc/mknod " || Mycname || " p && chmod 644 " ||
  4784.         Mycname) = 0 | kstop( "can't create my comm file")
  4785.     }
  4786.  
  4787. #   Get name of my log file, open it, then remove from directory:
  4788.  
  4789.     Logname := "/tmp/krlog" || Mycol[ 1] || Me
  4790.     while /Logfile do {
  4791.     writes( "Log file name (defaults to ", Logname, ")? ")
  4792.     line := read( ) | kstop( "can't read log file name")
  4793.     Logname := tokens( line)
  4794.     Logfile := open( Logname, "cr")
  4795.     }
  4796.     remove( Logname)
  4797.  
  4798. #   Open our communication files, trying to avoid deadlock:
  4799.  
  4800.     write( "Attempting to establish communication with ", Yu)
  4801.     if Mycol == "White" then
  4802.     Mycomm := open( Mycname, "w") | kstop( "can't open my comm file")
  4803.     while not (Yrcomm := open( Yrcname)) do {
  4804.     write( "Still attempting to establish communication")
  4805.     if system( "sleep 3") ~= 0 then
  4806.         kstop( "gave up on establishing communications")
  4807.     }
  4808.     if Mycol == "Black" then
  4809.     Mycomm := open( Mycname, "w") | kstop( "can't open my comm file")
  4810.  
  4811. #   Initialize board and moves:
  4812.  
  4813.     Bg := board(
  4814.  
  4815.     "RNBQKBNRPPPPPPPP                                pppppppprnbqkbnr",
  4816.     "White", "Black", "W-Q", "W-K", "B-Q", "B-K", &null, 0)
  4817.  
  4818. #   Initialize set of move tries:
  4819.  
  4820.     Tries := set( )
  4821.  
  4822.     write( Logfile, "Kriegspiel game begins ", &dateline)
  4823.     write( Logfile, Me, " is ", Mycol, "; ", Yu, " is ", Yrcol)
  4824.     \ Blind & write( Logfile, Me, " is in 'totally blind' mode!")
  4825.  
  4826.     write( "You have the ", Mycol, " pieces against ", Yu)
  4827.     \ Blind & write( "You have chosen to play in 'totally blind' mode!")
  4828.     write( "At the \"Try\" prompt you may type help for assistance.")
  4829.     write( "Initialization complete; awaiting first white move.")
  4830.     return
  4831. end
  4832.  
  4833.  
  4834. procedure kstop( s)
  4835.  
  4836. #   Clean up and terminate execution with message s:
  4837.  
  4838.     local    logtemp
  4839.  
  4840.     close( \Mycomm)
  4841.     remove( \Mycname)
  4842.     write( \Logfile, "Kriegspiel game ends ", &dateline)
  4843.     logboard( \ Logfile, \ Bg)
  4844.     if seek( \Logfile) then {
  4845.     logtemp := open( Logname, "w") | kstop( "can't open my log file")
  4846.     every write( logtemp, ! Logfile)
  4847.     write( "Game log is on file ", Logname)
  4848.     }
  4849.     stop( "Kriegspiel stop: ", s)
  4850. end
  4851.  
  4852.  
  4853. procedure logboard( file, b)
  4854.  
  4855. #   Print the full board position in b to file:
  4856.  
  4857.     local    f, r, p
  4858.  
  4859.     write( file, "Current board position:")
  4860.     write( file, " a  b  c  d  e  f  g  h")
  4861.     every r := 8 to 1 by -1 do {
  4862.     write( file, "-------------------------")
  4863.     every writes( file, "|", p2c( p := b.pcs[ fr2s( 1 to 8, r)])[ 1],
  4864.         pc2p( p, "W"))
  4865.     write( file, "|", r)
  4866.     }
  4867.     write( file, "-------------------------")
  4868.     writes( file, b.cmv, " to move;")
  4869.     writes( file, " enp file: ", "abcdefgh"[ \ b.fepp], ";")
  4870.     writes( file, " castle mvs ", b.caswq || " " || b.caswk || " " ||
  4871.     b.casbq || " " || b.casbk, ";")
  4872.     write( file, " half-mvs played ", b.ply)
  4873.     write( file, "")
  4874. end
  4875.  
  4876.  
  4877. procedure main( )
  4878.  
  4879.     local    line
  4880.  
  4881. #   Initialize player names and colors and establish communications:
  4882.  
  4883.     init( )
  4884.  
  4885. #   Loop validating our moves and processing opponent responses:
  4886.  
  4887.     repeat {
  4888.     while Mycol == Bg.cmv do {
  4889.         writes( Remind, "Try your (", Me, "'s) move # ",
  4890.         Bg.ply / 2 + 1, ": ")
  4891.         line := read( ) | kstop( "player read fail")
  4892.         write( Mycomm, line)
  4893.         write( Logfile, Me, " typed: ", line)
  4894.         line := map( tokens( line)) | ""
  4895.         case line of {
  4896.         ""            : 0
  4897.         left( "any", *line)    : myany( )
  4898.         left( "board", *line)    : myboard( )
  4899.         "end"            : myend( )
  4900.         left( "help", *line)    : myhelp( )
  4901.         left( "message", *line)    : mymessage( )
  4902.         left( "remind", *line)    : myremind( )
  4903.         default            : mytry( line)
  4904.         }
  4905.         }
  4906.     while Yrcol == Bg.cmv do {
  4907.         if Yu == "auto" then
  4908.         line := automov( )
  4909.         else
  4910.         line := read( Yrcomm) | kstop( "opponent read fail")
  4911.         write( Logfile, Yu, " typed: ", line)
  4912.         line := map( tokens( line)) | ""
  4913.         case line of {
  4914.         ""            : 0
  4915.         left( "any", *line)    : yrany( )
  4916.         left( "board", *line)    : 0
  4917.         "end"            : yrend( )
  4918.         left( "help", *line)    : 0
  4919.         left( "message", *line)    : yrmessage( )
  4920.         left( "remind", *line)    : 0
  4921.         default            : yrtry( line)
  4922.         }
  4923.         }
  4924.     }
  4925. end
  4926.  
  4927.  
  4928. procedure movgen( b, s)
  4929.  
  4930. #   movgen generates the pseudo-legal moves in board position b from the
  4931. #   piece on square s; if s is unspecified all pieces are considered.
  4932. #   Note: pseudo-legal here means that the legality of the move has been
  4933. #   determined up to the question of whether it leaves the moving side's
  4934. #   king in check:
  4935.  
  4936.     local    r, f, p, snfr, m, fto, rto, sl, sh,
  4937.         sto, fril, rp, r2, r4, r5, r7, ps
  4938.  
  4939.     ps := b.pcs
  4940.  
  4941.     sl := (\s | 1)
  4942.     sh := (\s | 64)
  4943.  
  4944.     every s := sl to sh do {
  4945.     if p2c( p := ps[ s]) == b.cmv then {
  4946.         f := s2f( s)
  4947.         r := s2r( s)
  4948.         snfr := s2sn( s)
  4949.  
  4950. #   Pawn moves:
  4951.  
  4952.         if find( p, "Pp") then {
  4953.         if p == "P" then {
  4954.             rp :=  1; r2 := 2; r4 := 4; r5 := 5; r7 := 7
  4955.             }
  4956.         else {
  4957.             rp := -1; r2 := 7; r4 := 5; r5 := 4; r7 := 2
  4958.             }
  4959.         if ps[ sto := fr2s( f, r + rp)] == " " then {
  4960.             m := "P" || snfr || s2sn( sto)
  4961.             if r = r7 then
  4962.             suspend m || ! "RNBQ"
  4963.             else {
  4964.             suspend m
  4965.             if r = r2 & ps[ sto := fr2s( f, r4)] == " " then
  4966.                 suspend "P" || snfr || s2sn( sto)
  4967.             }
  4968.             }
  4969.         every fto := 0 < (9 > (f - 1 to f + 1 by 2)) do {
  4970.             m := "P" || snfr ||
  4971.             s2sn( sto := fr2s( fto, r + rp)) || ":"
  4972.             if p2c( ps[ sto]) == b.cnm then {
  4973.             if r = r7 then
  4974.                 every suspend m || ! "RNBQ"
  4975.             else
  4976.                 suspend m
  4977.             }
  4978.             if r = r5 & fto = \ b.fepp then
  4979.             suspend m || "ep"
  4980.             }
  4981.         }
  4982.  
  4983. #   Sweep piece (rook, bishop, queen) moves:
  4984.  
  4985.         else if find( p, "RBQrbq") then {
  4986.         every fril := ! Frinclst[ p] do {
  4987.             fto := f
  4988.             rto := r
  4989.             while sto := fr2s( fto +:= fril[ 1], rto +:= fril[ 2]) do {
  4990.             if ps[ sto] == " " then
  4991.                 suspend pc2p( p, "W") || snfr || s2sn( sto)
  4992.             else {
  4993.                 if p2c( ps[ sto]) == b.cnm then
  4994.                 suspend pc2p( p, "W") ||
  4995.                     snfr || s2sn( sto) || ":"
  4996.                 break
  4997.                 }
  4998.             }
  4999.             }
  5000.         }
  5001.  
  5002. #   Knight and king moves:
  5003.  
  5004.         else if find( p, "KNkn") then {
  5005.         every fril := ! Frinclst[ p] do {
  5006.             if sto := fr2s( f + fril[ 1], r + fril[ 2]) then {
  5007.             if p2c( ps[ sto]) == b.cnm then
  5008.                 suspend pc2p( p, "W") ||
  5009.                 snfr || s2sn( sto) || ":"
  5010.             else if ps[ sto] == " " then
  5011.                 suspend pc2p( p, "W") || snfr || s2sn( sto)
  5012.             }
  5013.             }
  5014.         if p == "K" then {
  5015.             if (b.caswq ~== "", ps[ sn2s( "b1") : sn2s( "e1")] == "   ",
  5016.             not sqratks( ps, sn2s( "d1"), "Black"),
  5017.             not sqratks( ps, sn2s( "e1"), "Black")) then
  5018.                 suspend "Ke1c1cas"
  5019.             if (b.caswk ~== "", ps[ sn2s( "f1") : sn2s( "h1")] == "  ",
  5020.             not sqratks( ps, sn2s( "f1"), "Black"),
  5021.             not sqratks( ps, sn2s( "e1"), "Black")) then
  5022.                 suspend "Ke1g1cas"
  5023.             }
  5024.         else if p == "k" then {
  5025.             if (b.casbq ~== "", ps[ sn2s( "b8") : sn2s( "e8")] == "   ",
  5026.             not sqratks( ps, sn2s( "d8"), "White"),
  5027.             not sqratks( ps, sn2s( "e8"), "White")) then
  5028.                 suspend "Ke8c8cas"
  5029.             if (b.casbk ~== "", ps[ sn2s( "f8") : sn2s( "h8")] == "  ",
  5030.             not sqratks( ps, sn2s( "f8"), "White"),
  5031.             not sqratks( ps, sn2s( "e8"), "White")) then
  5032.                 suspend "Ke8g8cas"
  5033.             }
  5034.         }
  5035.         }
  5036.     }
  5037. end
  5038.  
  5039.  
  5040. procedure movlegal( b, m)
  5041.  
  5042. #   Tests move m on board b and, if it does not leave the moving color in
  5043. #   check, returns m; fails otherwise:
  5044.  
  5045.     local    ps, sfr, sto, sk
  5046.  
  5047.     ps := b.pcs
  5048.     sfr := sn2s( m[ 2:4])
  5049.     sto := sn2s( m[ 4:6])
  5050.  
  5051. #   Castling move:
  5052.  
  5053.     if m[ 6:9] == "cas" then {
  5054.     if m == "Ke1c1cas" then
  5055.         return not sqratks( ps, sn2s( "c1"), "Black") & m
  5056.     if m == "Ke1g1cas" then
  5057.         return not sqratks( ps, sn2s( "g1"), "Black") & m
  5058.     if m == "Ke8c8cas" then
  5059.         return not sqratks( ps, sn2s( "c8"), "White") & m
  5060.     if m == "Ke8g8cas" then
  5061.         return not sqratks( ps, sn2s( "g8"), "White") & m
  5062.     }
  5063.  
  5064. #   Enpassant pawn capture:
  5065.  
  5066.     if m[ 6:9] == ":ep" then
  5067.     ps[ fr2s( s2f( sto), s2r( sfr))] := " "
  5068.  
  5069. #   All non-castling moves:
  5070.  
  5071.     ps[ sto] := ps[ sfr]
  5072.     ps[ sfr] := " "
  5073.     sk := find( pc2p( "K", b.cmv), ps)
  5074.     return not sqratks( ps, sk, b.cnm) & m
  5075.  
  5076. end
  5077.  
  5078.  
  5079. procedure movmake( b, m)
  5080.  
  5081. #   Makes move m on board b:
  5082.  
  5083.     local    sfr, sto
  5084.  
  5085.     if m == "Ke1c1cas" then {
  5086.     b.pcs[ sn2s( "a1")] := " "
  5087.     b.pcs[ sn2s( "d1")] := "R"
  5088.     }
  5089.     else if m == "Ke1g1cas" then {
  5090.     b.pcs[ sn2s( "h1")] := " "
  5091.     b.pcs[ sn2s( "f1")] := "R"
  5092.     }
  5093.     else if m == "Ke8c8cas" then {
  5094.     b.pcs[ sn2s( "a8")] := " "
  5095.     b.pcs[ sn2s( "d8")] := "r"
  5096.     }
  5097.     else if m == "Ke8g8cas" then {
  5098.     b.pcs[ sn2s( "h8")] := " "
  5099.     b.pcs[ sn2s( "f8")] := "r"
  5100.     }
  5101.  
  5102.     sfr := sn2s( m[ 2:4])
  5103.     sto := sn2s( m[ 4:6])
  5104.     b.pcs[ sto] := b.pcs[ sfr]
  5105.     b.pcs[ sfr] := " "
  5106.  
  5107.     if find( m[ -1], "rnbqRNBQ") then
  5108.     b.pcs[ sto] := pc2p( m[ -1], b.cmv)
  5109.  
  5110.     if sfr = sn2s( "e1") then    b.caswq := b.caswk := ""
  5111.     if sfr = sn2s( "e8") then    b.casbq := b.casbk := ""
  5112.  
  5113.     if (sfr | sto) = sn2s( "a1") then    b.caswq := ""
  5114.     if (sfr | sto) = sn2s( "h1") then    b.caswk := ""
  5115.     if (sfr | sto) = sn2s( "a8") then    b.casbq := ""
  5116.     if (sfr | sto) = sn2s( "h8") then    b.casbk := ""
  5117.  
  5118.     if m[ 6:9] == ":ep" then
  5119.     b.pcs[ fr2s( s2f( sto), s2r( sfr))] := " "
  5120.  
  5121.     b.fepp := &null
  5122.     if m[ 1] == "P" & abs( s2r( sfr) - s2r( sto)) = 2 then
  5123.     b.fepp := s2f( sto)
  5124.  
  5125.     b.ply +:= 1
  5126.     b.cmv :=: b.cnm
  5127. end
  5128.  
  5129.  
  5130. procedure movtry( m)
  5131.  
  5132. #   Tests whether the typed move m is legal in the global board Bg and, if so,
  5133. #   returns the corresponding move returned from movgen (which will be in a
  5134. #   different format with piece letter prefix, etc.).  Fails if m is not
  5135. #   legal.  Note that if the any flag is set, only captures by pawns are
  5136. #   allowed:
  5137.  
  5138.     local    ml, mt, sfr, sto
  5139.  
  5140.     mt := map( tokens( m)) | ""
  5141.     if mt == "o-o" then
  5142.     mt := (Bg.cmv == "White", "e1g1") | "e8g8"
  5143.     else if mt == "o-o-o" then
  5144.     mt := (Bg.cmv == "White", "e1c1") | "e8c8"
  5145.  
  5146.     sfr := sn2s( mt[ 1:3]) | fail
  5147.     sto := sn2s( mt[ 3:5]) | fail
  5148.  
  5149.     if find( mt[ 5], "rnbq") then
  5150.     mt[ 5] := map( mt[ 5], "rnbq", "RNBQ")
  5151.     else mt := mt[ 1:5] || "Q"
  5152.     
  5153.     if \ Any then {
  5154.     if Bg.pcs[ sfr] ~== pc2p( "P", Bg.cmv) then fail
  5155.     every ml := movgen( Bg, sfr) do {
  5156.         if ml[ 4:7] == mt[ 3:5] || ":" then {
  5157.         if find( ml[ -1], "RNBQ") then
  5158.             ml[ -1] := mt[ 5]
  5159.         return movlegal( Bg, ml)
  5160.         }
  5161.         }
  5162.     }
  5163.     else {
  5164.     every ml := movgen( Bg, sfr) do {
  5165.         if ml[ 4:6] == mt[ 3:5] then {
  5166.         if find( ml[ -1], "RNBQ") then
  5167.             ml[ -1] := mt[ 5]
  5168.         return movlegal( Bg, ml)
  5169.         }
  5170.         }
  5171.     }
  5172. end
  5173.  
  5174.  
  5175. procedure myany( )
  5176.  
  5177. #   Process my any command.
  5178. #   Check for captures by pawns and inform the player of any, and, if
  5179. #   at least one, set Any flag to require that player try only captures
  5180. #   by pawns:
  5181.  
  5182.     local    m, p, s
  5183.  
  5184.     if \ Any then {
  5185.     write( "You have already asked 'Any' and received yes answer!")
  5186.     fail
  5187.     }
  5188.  
  5189.     p := pc2p( "P", Bg.cmv)
  5190.     if movlegal( Bg, 1( m := movgen( Bg, 1(s := 9 to 56, Bg.pcs[ s] == p)),
  5191.         m[ 6] == ":")) then {
  5192.     write( "Yes; you must now make a legal capture by a pawn.")
  5193.     Any := "Yes"
  5194.     }
  5195.     else
  5196.     write( "No.")
  5197. end
  5198.  
  5199.  
  5200. procedure myboard( )
  5201.  
  5202. #   Process my board command by printing the board but omitting the
  5203. #   opponent's pieces and the enpassant status; a count of pieces of
  5204. #   both colors is printed:
  5205. #   Note: no board printed in blind mode.
  5206.  
  5207.     local    f, r, p, nw, nb
  5208.  
  5209.     \ Blind & write( "Sorry; no board printout in blind mode!") & fail
  5210.  
  5211.     write( "Current board position (your pieces only):")
  5212.     write( " a  b  c  d  e  f  g  h")
  5213.     every r := 8 to 1 by -1 do {
  5214.     write( "-------------------------")
  5215.     every f := 1 to 8 do {
  5216.         if (p2c( p := Bg.pcs[ fr2s( f, r)])) == Mycol then
  5217.         writes( "|", Mycol[ 1], pc2p( p, "W"))
  5218.         else
  5219.         writes( "|  ")
  5220.         }
  5221.     write( "|", r)
  5222.     }
  5223.     write( "-------------------------")
  5224.     writes( Bg.cmv, " to move; ")
  5225.     writes( "castle mvs ", (Mycol == "White", Bg.caswq || " " || Bg.caswk) |
  5226.     Bg.casbq || " " || Bg.casbk)
  5227.     write( "; half-mvs played ", Bg.ply)
  5228.     nw := nb := 0
  5229.     every upto( &ucase, Bg.pcs) do nw +:= 1
  5230.     every upto( &lcase, Bg.pcs) do nb +:= 1
  5231.     write( nw, " White pieces, ", nb, " Black.")
  5232.     write( "")
  5233. end
  5234.  
  5235.  
  5236. procedure myend( )
  5237.  
  5238. #   Process my end command:
  5239.  
  5240.     kstop( "by " || Me)
  5241. end
  5242.  
  5243.  
  5244. procedure myhelp( )
  5245.  
  5246. #   Process my help command:
  5247.  
  5248.     write( "")
  5249.     write( "This is \"Kriegspiel\" (war play), a game of chess between two")
  5250.     write( "opponents who do not see the location of each other's pieces.")
  5251.     write( "Note: the moves of the special opponent 'auto' are played by the")
  5252.     write( "program itself.  Currently, auto plays at a low novice level.")
  5253.     write( "When it is your turn to move, you will be prompted to type")
  5254.     write( "a move attempt or one of several commands.  To try a move,")
  5255.     write( "type the from and to squares in algebraic notation, as in: e2e4")
  5256.     write( "or b8c6.  Castling may be typed as o-o, o-o-o, or as the move")
  5257.     write( "of the king, as in: e8g8.  Pawn promotions should look like")
  5258.     write( "d7d8Q.  If omitted, the piece promoted to is assumed to be a")
  5259.     write( "queen.  Letters may be in upper or lower case.  If the move is")
  5260.     write( "legal, it stands, and the opponent's response is awaited.")
  5261.     write( "If the move is illegal, the program will prompt you to")
  5262.     write( "try again.  If the move is illegal because of the opponent's")
  5263.     write( "position but not impossible based on the position of your")
  5264.     write( "pieces, then your opponent will be informed that you tried")
  5265.     write( "an illegal move (note: this distinction between illegal and")
  5266.     write( "impossible is somewhat tricky and the program may, in some")
  5267.     write( "cases, not get it right).  The program will announce the")
  5268.     write( "result and terminate execution when the game is over.  You may")
  5269.     write( "then inspect the game log file which the program generated.")
  5270.     write( "")
  5271.  
  5272.     writes( "Type empty line for more or 'q' to return from help: ")
  5273.     if map( read( ))[ 1] == "q" then
  5274.     fail
  5275.  
  5276.     write( "")
  5277.     write( "The program will let you know of certain events that take place")
  5278.     write( "during the game.  For each capture move, both players will be")
  5279.     write( "informed of the location of the captured piece.  The opponent")
  5280.     write( "will be informed of a pawn promotion but not of the piece")
  5281.     write( "promoted to or the square on which the promotion takes place.")
  5282.     write( "When a player gives check, both players will be informed of the")
  5283.     write( "event and of some information about the direction from which the")
  5284.     write( "check arises, as in: check on the rank', 'check on the file',")
  5285.     write( "'check on the + diagonal', 'check on the - diagonal', or 'check")
  5286.     write( "by a knight'.  For a double check, both directions are given.")
  5287.     write( "(A + diagonal is one on which file letters and rank numbers")
  5288.     write( "increase together, like a1-h8, and a - diagonal is one in which")
  5289.     write( "file letters increase while rank numbers decrease, as in a8-h1).")
  5290.     write( "")
  5291.     write( "Note: if you have selected the 'blind' mode, then you will")
  5292.     write( "receive no information about checks, captures, or opponent")
  5293.     write( "'any' or illegal move tries; nor will you be able to print")
  5294.     write( "the board.  You will not even be told when your own pieces")
  5295.     write( "are captured.  Except for answers to 'any' commands, the")
  5296.     write( "program will inform you only of when you have moved, when")
  5297.     write( "your opponent has moved, and of the result at end of game.")
  5298.     write( "")
  5299.  
  5300.     writes( "Type empty line for more or 'q' to return from help: ")
  5301.     if map( read( ))[ 1] == "q" then
  5302.     fail
  5303.  
  5304.     write( "")
  5305.     write( "Description of commands; note: upper and lower case letters")
  5306.     write( "are not distinguished, and every command except 'end' may be") 
  5307.     write( "abbreviated.")
  5308.     write( "")
  5309.     write( "any")
  5310.     write( "")
  5311.     write( "The 'any' command is provided to speed up the process of trying")
  5312.     write( "captures by pawns.  Since pawns are the only pieces that capture")
  5313.     write( "in a different manner from the way they ordinarily move, it is")
  5314.     write( "often useful to try every possible capture, since such a move")
  5315.     write( "can only be legal if it in fact captures something.  Since the")
  5316.     write( "process of trying the captures can be time-consuming, the 'any'")
  5317.     write( "command is provided to signal your intent to try captures by")
  5318.     write( "pawns until you find a legal one.  The program will tell you if")
  5319.     write( "you have at least one.  If you do then you must try captures by")
  5320.     write( "pawns (in any order) until you find a legal one.  Note that the")
  5321.     write( "opponent will be informed of your plausible 'any' commands (that")
  5322.     write( "is, those that are not impossible because you have no pawns on")
  5323.     write( "the board).")
  5324.     write( "")
  5325.  
  5326.     writes( "Type empty line for more or 'q' to return from help: ")
  5327.     if map( read( ))[ 1] == "q" then
  5328.     fail
  5329.  
  5330.     write( "")
  5331.     write( "board")
  5332.     write( "")
  5333.     write( "The 'board' command prints the current position of your")
  5334.     write( "pieces only, but also prints a count of pieces of both sides.")
  5335.     write( "Note: 'board' is disallowed in blind mode.")
  5336.     write( "")
  5337.     write( "end")
  5338.     write( "")
  5339.     write( "Then 'end' command informs the program and your")
  5340.     write( "opponent of your decision to terminate the game")
  5341.     write( "immediately.")
  5342.     write( "")
  5343.     write( "help")
  5344.     write( "")
  5345.     write( "The 'help' command prints this information.")
  5346.     write( "")
  5347.  
  5348.     writes( "Type empty line for more or 'q' to return from help: ")
  5349.     if map( read( ))[ 1] == "q" then
  5350.     fail
  5351.  
  5352.     write( "")
  5353.     write( "message")
  5354.     write( "")
  5355.     write( "The 'message' command allows you to send a one-line")
  5356.     write( "message to your opponent.  Your opponent will be prompted")
  5357.     write( "for a one-line response.  'message' may be useful for such")
  5358.     write( "things as witty remarks, draw offers, etc.")
  5359.     write( "")
  5360.     write( "remind")
  5361.     write( "")
  5362.     write( "The 'remind' command turns on (if off) or off (if on) the")
  5363.     write( "bell that is rung when the program is ready to accept your")
  5364.     write( "move or command.  The bell is initially off.")
  5365.     write( "")
  5366.  
  5367. end
  5368.  
  5369.  
  5370. procedure mymessage( )
  5371.  
  5372. #   Process my message command:
  5373.  
  5374.     local    line
  5375.  
  5376.     write( "Please type a one-line message:")
  5377.     line := read( ) | kstop( "can't read message")
  5378.     write( Mycomm, line)
  5379.     write( Logfile, line)
  5380.     write( "Awaiting ", Yu, "'s response")
  5381.     if Yu == "auto" then
  5382.     line := "I'm just your auto opponent."
  5383.     else
  5384.     line := read( Yrcomm) | kstop( "can't read message response")
  5385.     write( Yu, " answers: ", line)
  5386.     write( Logfile, line)
  5387. end
  5388.  
  5389.  
  5390. procedure myremind( )
  5391.  
  5392. #   Process my remind command:
  5393.  
  5394.     if Remind == "" then
  5395.     Remind := "\^g"
  5396.     else
  5397.     Remind := ""
  5398. end
  5399.  
  5400.  
  5401. procedure mytry( mt)
  5402.  
  5403. #   Process my move try mt:
  5404.  
  5405.     local    ml, result
  5406.  
  5407.     if ml := movtry( mt) then {
  5408.     Lmv := ml
  5409.     write( Me, " (", Mycol, ") has moved.")
  5410.     write( Logfile, Me, "'s move ", Bg.ply / 2 + 1, " is ", ml)
  5411.     / Blind & write( Me, " captures on ", s2sn( sqrcap( Bg, ml)))
  5412.     movmake( Bg, ml)
  5413.     / Blind & saycheck( )
  5414.     Any := &null
  5415.     Tries := set( )
  5416.     if result := gamend( Bg) then {
  5417.         write( "Game ends; result: ", result)
  5418.         write( Logfile, "Result: ", result)
  5419.         kstop( "end of game")
  5420.         }
  5421.     }
  5422.     else
  5423.     write( "Illegal move, ", Me, "; try again:")
  5424. end
  5425.  
  5426.  
  5427. procedure p2c( p)
  5428.  
  5429. #   Returns "White" if p is white piece code ("PRNBQK"), "Black"
  5430. #   if p is black piece code ("prnbqk"), and " " if empty square
  5431. #   (" "):
  5432.  
  5433.     if find( p, "PRNBQK") then
  5434.     return "White"
  5435.     else if find( p, "prnbqk") then
  5436.     return "Black"
  5437.     else
  5438.     return " "
  5439. end
  5440.  
  5441.  
  5442. procedure pc2p( p, c)
  5443.  
  5444. #   Returns the piece letter for the piece of type p but color c;
  5445. #   returns " " if p == " ".  Thus pc2p( "R", "Black") == "r".
  5446. #   c may be abbreviated to "W" or "B":
  5447.  
  5448.     if c[ 1] == "W" then
  5449.     return map( p, "prnbqk", "PRNBQK")
  5450.     else
  5451.     return map( p, "PRNBQK", "prnbqk")
  5452. end
  5453.  
  5454.  
  5455. procedure s2f( square)
  5456.  
  5457. #   Returns the file number of the square number "square"; fails
  5458. #   if invalid square number:
  5459.  
  5460.     return ( (0 < ( 65 > integer( square))) - 1) % 8 + 1
  5461. end
  5462.  
  5463.  
  5464. procedure s2r( square)
  5465.  
  5466. #   Returns the rank number of the square number "square"; fails
  5467. #   if invalid square number:
  5468.  
  5469.     return ( (0 < ( 65 > integer( square))) - 1) / 8 + 1
  5470. end
  5471.  
  5472.  
  5473. procedure s2sn( square)
  5474.  
  5475. #   Returns the algebraic square name corresponding to square number
  5476. #   "square"; fails if invalid square number:
  5477.  
  5478.     return "abcdefgh"[ s2f( square)] || string( s2r( square))
  5479. end
  5480.  
  5481.  
  5482. procedure saycheck( )
  5483.  
  5484. #   Announce checks, if any, in global board Bg:
  5485.  
  5486.     local    s, sk
  5487.  
  5488.     sk := find( pc2p( "K", Bg.cmv), Bg.pcs)
  5489.  
  5490.     every s := chksqrs( Bg) do {
  5491.     writes( (Mycol == Bg.cnm, Me) | Yu, " checks ")
  5492.     if s2r( s) == s2r( sk) then
  5493.         write( "on the rank.")
  5494.     else if s2f( s) == s2f( sk) then
  5495.         write( "on the file.")
  5496.     else if ( s2f( s) - s2f( sk)) = ( s2r( s) - s2r( sk)) then
  5497.         write( "on the + diagonal.")
  5498.     else if ( s2f( s) - s2f( sk)) = ( s2r( sk) - s2r( s)) then
  5499.         write( "on the - diagonal.")
  5500.     else
  5501.         write( "by knight.")
  5502.     }
  5503. end
  5504.  
  5505.  
  5506. procedure sn2s( sn)
  5507.  
  5508. #   Returns the square number corresponding to the algebraic square
  5509. #   name sn; examples: sn2s( "a1") = 1, sn2s( "b1") = 2, sn2s( "h8") = 64.
  5510. #   Fails if invalid square name:
  5511.  
  5512.     return find( sn[ 1], "abcdefgh") + 8 * (0 < (9 > integer( sn[ 2]))) - 8
  5513. end
  5514.  
  5515.  
  5516. procedure sqratks( ps, s, c)
  5517.  
  5518. #   Generates the numbers of squares of pieces of color c that "attack"
  5519. #   square s in board piece array ps; fails if no such squares:
  5520.  
  5521.     local    file, rank, rfr, sfr, fril, p, ffr
  5522.  
  5523.     file := s2f( s)
  5524.     rank := s2r( s)
  5525.  
  5526. #   Check for attacks from pawns:
  5527.  
  5528.     rfr := (c == "White", rank - 1) | rank + 1
  5529.     every sfr := fr2s( file - 1 to file + 1 by 2, rfr) do {
  5530.     if ps[ sfr] == pc2p( "P", c) then
  5531.         suspend sfr
  5532.     }
  5533.  
  5534. #   Check for attack from king or knights:
  5535.  
  5536.     every fril := ! Frinclst[ p := ("K" | "N")] do {
  5537.     if sfr := fr2s( file + fril[ 1], rank + fril[ 2]) then {
  5538.         if ps[ sfr] == pc2p( p, c) then
  5539.         suspend sfr
  5540.         }
  5541.     }
  5542.  
  5543. #   Check for attacks from sweep (rook and bishop) directions:
  5544.  
  5545.     every fril := ! Frinclst[ p := ("R" | "B")] do {
  5546.     ffr := file
  5547.     rfr := rank
  5548.     while sfr := fr2s( ffr +:= fril[ 1], rfr +:= fril[ 2]) do {
  5549.         if ps[ sfr] ~== " " then {
  5550.         if ps[ sfr] == pc2p( p | "Q", c) then
  5551.             suspend sfr
  5552.         break
  5553.         }
  5554.         }
  5555.     }
  5556. end
  5557.  
  5558.  
  5559. procedure sqrcap( b, m)
  5560.  
  5561. #   Returns square of piece captured by move m in board b; fails if m
  5562. #   not a capture:
  5563.  
  5564.     local    fto, rfr
  5565.  
  5566.     if m[ 6:9] == ":ep" then {
  5567.     fto := find( m[ 4], "abcdefgh")
  5568.     rfr := integer( m[ 3])
  5569.     return fr2s( fto, rfr)
  5570.     }
  5571.     else if m[ 6] == ":" then
  5572.     return sn2s( m[ 4:6])
  5573. end
  5574.  
  5575.  
  5576. procedure tokens( s, d)
  5577.  
  5578. #   Generate tokens from left to right in string s given delimiters in cset
  5579. #   d, where a token is a contiguous string of 1 or more characters not in
  5580. #   d bounded by characters in d or the left or right end of s.
  5581. #   d defaults to ' \t'.
  5582.  
  5583.     s := string( s) | fail
  5584.     d := (cset( d) | ' \t')
  5585.  
  5586.     s ? while tab( upto( ~d)) do
  5587.     suspend( tab( many( ~d)) \ 1)
  5588. end
  5589.  
  5590.  
  5591. procedure yrany( )
  5592.  
  5593. #   Process opponent's any command:
  5594.  
  5595.     local    m, p, s
  5596.  
  5597.     if \ Any then fail
  5598.  
  5599.     p := pc2p( "P", Bg.cmv)
  5600.     if not find( p, Bg.pcs) then fail
  5601.  
  5602.     / Blind & writes( Yu, " asked 'any' and was told ")
  5603.  
  5604.     if movlegal( Bg, 1( m := movgen( Bg, 1(s := 9 to 56, Bg.pcs[ s] == p)),
  5605.         m[ 6] == ":")) then {
  5606.     / Blind & write( "yes.")
  5607.     Any := "Yes"
  5608.     }
  5609.     else
  5610.     / Blind & write( "no.")
  5611. end
  5612.  
  5613.  
  5614. procedure yrend( )
  5615.  
  5616. #   Process opponent's end command:
  5617.  
  5618.     write( "Game terminated by ", Yu, ".")
  5619.     kstop( "by " || Yu)
  5620. end
  5621.  
  5622.  
  5623. procedure yrmessage( )
  5624.  
  5625. #   Process opponent's message command:
  5626.  
  5627.     local    line
  5628.  
  5629.     line := read( Yrcomm) | kstop( "can't read opponent message")
  5630.     write( "Message from ", Yu, ": ", line)
  5631.     write( Logfile, line)
  5632.     write( "Please write a one-line response:")
  5633.     line := read( ) | kstop( "can't read response to opponent message")
  5634.     write( Mycomm, line)
  5635.     write( Logfile, line)
  5636. end
  5637.  
  5638.  
  5639. procedure yrtry( mt)
  5640.  
  5641. #   Process opponent move try (or other type-in!) mt:
  5642.  
  5643.     local    ml, result, s, mtr, b, po, sfr, sto
  5644.  
  5645.     if ml := movtry( mt) then {
  5646.     Lmv := ml
  5647.     write( Yu, " (", Yrcol, ") has moved.")
  5648.     write( Logfile, Yu, "'s move ", Bg.ply / 2 + 1, " is ", ml)
  5649.     / Blind & write( Yu, " captures on ", s2sn( sqrcap( Bg, ml)))
  5650.     if find( ml[ -1], "RNBQ") then
  5651.         / Blind & write( Yu, " promotes a pawn.")
  5652.     movmake( Bg, ml)
  5653.     / Blind & saycheck( )
  5654.     Any := &null
  5655.     Tries := set( )
  5656.     if result := gamend( Bg) then {
  5657.         write( "Game ends; result: ", result)
  5658.         write( Logfile, "Result: ", result)
  5659.         kstop( "end of game")
  5660.         }
  5661.     }
  5662.  
  5663. #   Inform Me if opponent move illegal but not impossible.  Don't inform
  5664. #   if illegal move already tried.  Note: distinction between "illegal"
  5665. #   and "impossible" is tricky and may not always be made properly.
  5666. #   Note: don't bother informing if in blind mode.
  5667.  
  5668.     else {
  5669.     \ Blind & fail
  5670.     mtr := map( tokens( mt)) | ""
  5671.     if mtr == "o-o" then
  5672.         mtr := (Bg.cmv == "White", "e1g1") | "e8g8"
  5673.     else if mtr == "o-o-o" then
  5674.         mtr := (Bg.cmv == "White", "e1c1") | "e8c8"
  5675.     mtr := mtr[ 1:5] | fail
  5676.     if member( Tries, mtr) then fail
  5677.     insert( Tries, mtr)
  5678.     b := copy( Bg)
  5679.     po := (b.cmv[ 1] == "W", "prnbqk") | "PRNBQK"
  5680.     b.pcs := map( b.pcs, po, "      ")
  5681.     sfr := sn2s( mtr[ 1:3]) | fail
  5682.     sto := sn2s( mtr[ 3:5]) | fail
  5683.     if sn2s( movgen( b, sfr)[ 4:6]) = sto then
  5684.         / Any & write( Yu, " tried illegal move.")
  5685.     else {
  5686.         b.pcs[ sto] := pc2p( "P", b.cnm)
  5687.         if sn2s( movgen( b, sfr)[ 4:6]) = sto then
  5688.         write( Yu, " tried illegal move.")
  5689.         }
  5690.     }
  5691. end
  5692. ##########
  5693. kross.icn
  5694. ############################################################################
  5695. #
  5696. #    Name:    kross.icn
  5697. #
  5698. #    Title:    Diagram character intersections of strings
  5699. #
  5700. #    Author:    Ralph E. Griswold
  5701. #
  5702. #    Date:    May 9, 1989
  5703. #
  5704. ############################################################################
  5705. #
  5706. #     This program procedure accepts pairs of strings on successive lines.
  5707. #  It diagrams all the intersections of the two strings in a common
  5708. #  character.
  5709. #
  5710. ############################################################################
  5711.  
  5712. procedure main()
  5713.    local line, j
  5714.    while line := read() do {
  5715.       kross(line,read())
  5716.       }
  5717. end
  5718.  
  5719. procedure kross(s1,s2)
  5720.    local j, k
  5721.    every j := upto(s2,s1) do
  5722.       every k := upto(s1[j],s2) do
  5723.          xprint(s1,s2,j,k)
  5724. end
  5725.  
  5726. procedure xprint(s1,s2,j,k)
  5727.    write()
  5728.    every write(right(s2[1 to k-1],j))
  5729.    write(s1)
  5730.    every write(right(s2[k+1 to *s2],j))
  5731. end
  5732. ##########
  5733. kwic.icn
  5734. ############################################################################
  5735. #
  5736. #    Name:    kwic.icn
  5737. #
  5738. #    Title:    Produce keywords in context
  5739. #
  5740. #    Author:    Stephen B. Wampler, modified by Ralph E. Griswold
  5741. #
  5742. #    Date:    October 11, 1988
  5743. #
  5744. ############################################################################
  5745. #
  5746. #     This is a simple keyword-in-context (KWIC) program. It reads from
  5747. #  standard input and writes to standard output. The "key" words are
  5748. #  aligned in column 40, with the text shifted as necessary. Text shifted
  5749. #  left is truncated at the left. Tabs and other characters whose "print width"
  5750. #  is less than one may not be handled properly.
  5751. #
  5752. #     Some noise words are omitted (see "exceptions" in the program text).
  5753. #  If a file named except.wrd is open and readable i nthe current directory,
  5754. #  the words in it are used instead.
  5755. #
  5756. #     This program is pretty simple.  Possible extensions include ways
  5757. #  of specifying words to be omitted, more flexible output formatting, and
  5758. #  so on.  Another "embellisher's delight".
  5759. #
  5760. ############################################################################
  5761.  
  5762. global line, loc, exceptions
  5763.  
  5764. procedure main()
  5765.    local exceptfile
  5766.  
  5767.    if exceptfile := open("except.wrd") then {
  5768.       exceptions := set()
  5769.       every insert(exceptions, lcword(exceptfile))
  5770.       close(exceptfile)
  5771.       }
  5772.    else
  5773.       exceptions := set(["or", "in", "the", "to", "of", "on", "a",
  5774.          "an", "at", "and", "i", "it"])
  5775.  
  5776.    every write(kwic(&input))
  5777.  
  5778. end
  5779.  
  5780. procedure kwic(file)
  5781.    local index, word
  5782.  
  5783. #  Each word, in lowercase form, is a key in the table "index".
  5784. #  The corresponding values are lists of the positioned lines
  5785. #  for that word.  This method may use an impractically large
  5786. #  amount of space for large input files.
  5787.  
  5788.    index := table()
  5789.    every word := lcword(file) do {
  5790.       if not member(exceptions,word) then {
  5791.          /index[word] := []
  5792.          index[word] := put(index[word],position())
  5793.          }
  5794.       }
  5795.  
  5796. #  Before the new sort options, it was done this way -- the code preserved
  5797. #  as an example of "generators in action".
  5798.  
  5799. #  suspend !((!sort(index,1))[2])
  5800.  
  5801.    index := sort(index,3)
  5802.    while get(index) do
  5803.       suspend !get(index)
  5804. end
  5805.  
  5806. procedure lcword(file)
  5807.    static chars
  5808.    initial chars := &ucase ++ &lcase ++ '\''
  5809.    every line := !file do
  5810.       line ? while tab(loc := upto(chars)) do
  5811.          suspend map(tab(many(chars)) \ 1)
  5812. end
  5813.  
  5814. procedure position()
  5815.    local offset
  5816.  
  5817. #  Note that "line" and ""loc" are global.
  5818.  
  5819.    offset := 40 - loc
  5820.    if offset >= 0 then return repl(" ",offset) || line
  5821.    else return line[-offset + 1:0]
  5822. end
  5823. ##########
  5824. labels.icn
  5825. ############################################################################
  5826. #
  5827. #    Name:    labels.icn
  5828. #
  5829. #    Title:    Format mailing labels
  5830. #
  5831. #    Author:    Ralph E. Griswold
  5832. #
  5833. #    Date:    June 10, 1988
  5834. #
  5835. ############################################################################
  5836. #  
  5837. #     This program produces labels using coded information taken
  5838. #  from the input file.  In the input file, a line beginning with #
  5839. #  is a label header.  Subsequent lines up to the next header or
  5840. #  end-of-file are accumulated and output so as to be centered hor-
  5841. #  izontally and vertically on label forms.  Lines beginning with *
  5842. #  are treated as comments and are ignored.
  5843. #  
  5844. #  Options: The following options are available:
  5845. #  
  5846. #       -c n Print n copies of each label.
  5847. #  
  5848. #       -s s Select only those labels whose headers contain a char-
  5849. #            acter in s.
  5850. #  
  5851. #       -t   Format for curved tape labels (the default is to format
  5852. #            for rectangular mailing labels).
  5853. #  
  5854. #       -w n Limit line width to n characters. The default width is
  5855. #            40.
  5856. #  
  5857. #       -l n Limit the number of printed lines per label to n. The
  5858. #            default is 8.
  5859. #  
  5860. #       -d n Limit the depth of the label to n. The default is 9 for
  5861. #            rectangular labels and 12 for tape labels (-t).
  5862. #  
  5863. #       -f   Print the first line of each selected entry instead of
  5864. #            labels.
  5865. #  
  5866. #     Options are processed from left to right.  If the number of
  5867. #  printed lines is set to a value that exceeds the depth of the
  5868. #  label, the depth is set to the number of lines.  If the depth is
  5869. #  set to a value that is less than the number of printed lines, the
  5870. #  number of printed lines is set to the depth. Note that the order
  5871. #  in which these options are specified may affect the results.
  5872. #  
  5873. #  Printing Labels: Label forms should be used with a pin-feed pla-
  5874. #  ten.  For mailing labels, the carriage should be adjusted so that
  5875. #  the first character is printed at the leftmost position on the
  5876. #  label and so that the first line of the output is printed on the
  5877. #  topmost line of the label.  For curved tape labels, some experi-
  5878. #  mentation may be required to get the text positioned properly.
  5879. #  
  5880. #  Diagnostics: If the limits on line width or the number of lines
  5881. #  per label are exceeded, a label with an error message is written
  5882. #  to standard error output.
  5883. #  
  5884. ############################################################################
  5885. #
  5886. #  Links: options
  5887. #
  5888. #  See also:  zipsort
  5889. #
  5890. ############################################################################
  5891.  
  5892. link options
  5893.  
  5894. global line, lsize, repet, llength, ldepth, first, opts
  5895.  
  5896. procedure main(args)
  5897.    local selectors, y, i
  5898.    line := ""
  5899.    selectors := '#'
  5900.    lsize := 9
  5901.    ldepth := 8
  5902.    llength := 40
  5903.    repet := 1
  5904.    i := 0
  5905.    opts := options(args,"cfd+l+s:tw+")
  5906.    if \opts["f"] then first := 1
  5907.    selectors := cset(\opts["s"])
  5908.    if \opts["t"] then {
  5909.       lsize := 12
  5910.       if ldepth > lsize then ldepth := lsize
  5911.       }
  5912.    llength := nonneg("w")
  5913.    if ldepth := nonneg("l") then {
  5914.       if lsize < ldepth then lsize := ldepth
  5915.       }
  5916.    if lsize := nonneg("d") then {
  5917.       if ldepth > lsize then ldepth := lsize
  5918.       }
  5919.    repet := nonneg("c")
  5920.  
  5921.    repeat {                # processing loop
  5922.       if line[1] == "#" & upto(selectors,line)
  5923.          then obtain() else {
  5924.             line := read() | break
  5925.             }
  5926.       }
  5927. end
  5928.  
  5929. #  Obtain next label
  5930. #
  5931. procedure obtain()
  5932.    local label, max
  5933.    label := []
  5934.    max := 0
  5935.    line := ""
  5936.    while line := read() do {
  5937.       if line[1] == "*" then next
  5938.       if line[1] == "#" then break
  5939.       if \first then {
  5940.          write(line)
  5941.          return
  5942.          }
  5943.       else put(label,line)
  5944.       max <:= *line
  5945.       if *label > ldepth then {
  5946.          error(label[1],1)
  5947.          return
  5948.          }
  5949.       if max > llength then {
  5950.          error(label[1],2)
  5951.          return
  5952.          }
  5953.       }
  5954.    every 1 to repet do format(label,max)
  5955. end
  5956.  
  5957. #  Format a label
  5958. #
  5959. procedure format(label,width)
  5960.    local j, indent
  5961.    indent := repl(" ",(llength - width) / 2)
  5962.    j := lsize - *label
  5963.    every 1 to j / 2 do write()
  5964.    every write(indent,!label)
  5965.    every 1 to (j + 1) / 2 do write()
  5966. end
  5967.  
  5968. #  Issue label for an error
  5969. #
  5970. procedure error(name,type)
  5971.    static badform
  5972.    initial badform := list(lsize)
  5973.    case type of {
  5974.       1:  badform[3] := "     **** too many lines"
  5975.       2:  badform[3] := "     **** line too long"
  5976.       }
  5977.    badform[1] := name
  5978.    every write(&errout,!badform)
  5979. end
  5980.  
  5981. procedure nonneg(s)
  5982.    s := \opts[s] | fail
  5983.    return 0 < integer(s) | stop("-",s," needs postive numeric parameter")
  5984. end
  5985. ##########
  5986. lam.icn
  5987. ############################################################################
  5988. #
  5989. #    Name:    lam.icn
  5990. #
  5991. #    Title:    Laminate files
  5992. #
  5993. #    Author:    Thomas R. Hicks
  5994. #
  5995. #    Date:    June 10, 1988
  5996. #
  5997. ############################################################################
  5998. #  
  5999. #     This program laminates files named on the command line onto
  6000. #  the standard output, producing a concatenation of corresponding
  6001. #  lines from each file named.  If the files are different lengths,
  6002. #  empty lines are substituted for missing lines in the shorter
  6003. #  files.  A command line argument of the form - s causes the string
  6004. #  s to be inserted between the concatenated file lines.
  6005. #  
  6006. #     Each command line argument is placed in the output line at the
  6007. #  point that it appears in the argument list.  For example, lines
  6008. #  from file1 and file2 can be laminated with a colon between each
  6009. #  line from file1 and the corresponding line from file2 by the com-
  6010. #  mand
  6011. #  
  6012. #          lam file1 -: file2
  6013. #  
  6014. #     File names and strings may appear in any order in the argument
  6015. #  list.  If - is given for a file name, standard input is read at
  6016. #  that point.  If a file is named more than once, each of its lines
  6017. #  will be duplicated on the output line, except that if standard
  6018. #  input is named more than once, its lines will be read alter-
  6019. #  nately.  For example, each pair of lines from standard input can
  6020. #  be joined onto one line with a space between them by the command
  6021. #  
  6022. #          lam - "- " -
  6023. #  
  6024. #  while the command
  6025. #  
  6026. #          lam file1 "- " file1
  6027. #  
  6028. #  replicates each line from file1.
  6029. #  
  6030. ############################################################################
  6031. #
  6032. #  Links: usage
  6033. #
  6034. ############################################################################
  6035.  
  6036. link usage
  6037.  
  6038. global fndxs
  6039.  
  6040. procedure main(a)
  6041.    local bufs, i
  6042.    bufs := list(*a)
  6043.    fndxs := []
  6044.    if (*a = 0) | a[1] == "?" then Usage("lam file [file | -string]...")
  6045.    every i := 1 to *a do {
  6046.       if a[i] == "-" then {
  6047.          a[i] := &input
  6048.             put(fndxs,i)
  6049.             }
  6050.       else if match("-",a[i]) then {
  6051.          bufs[i] := a[i][2:0]
  6052.          a[i] := &null
  6053.          }
  6054.       else {
  6055.          if not (a[i] := open(a[i])) then
  6056.             stop("Can't open ",a[i])
  6057.          else put(fndxs,i)
  6058.          }
  6059.      }
  6060.    if 0 ~= *fndxs then lamr(a,bufs) else Usage("lam file [file | -string]...")
  6061. end
  6062.  
  6063. procedure lamr(args,bufs)
  6064.    local i, j
  6065.    every i := !fndxs do
  6066.       bufs[i] := (read(args[i]) | &null)
  6067.    while \bufs[!fndxs] do {
  6068.       every j := 1 to *bufs do
  6069.          writes(\bufs[j])
  6070.       write()
  6071.       every i := !fndxs do
  6072.          bufs[i] := (read(args[i]) | &null)
  6073.      }
  6074. end
  6075. ##########
  6076. latexidx.icn
  6077. ############################################################################
  6078. #
  6079. #    Name:    latexidx.icn
  6080. #
  6081. #    Title:    Process LaTeX .idx file
  6082. #
  6083. #    Author:    David S. Cargo
  6084. #
  6085. #    Date:    April 19, 1989
  6086. #
  6087. ############################################################################
  6088. #
  6089. #  Input:
  6090. #
  6091. #     A latex .idx file containing the \indexentry lines.
  6092. #
  6093. #  Output:
  6094. #
  6095. #     \item lines sorted in order by entry value,
  6096. #  with page references put into sorted order.
  6097. #
  6098. # Processing:
  6099. #
  6100. #     While lines are available from standard input
  6101. #         Read a line containing an \indexentry
  6102. #         Form a sort key for the indexentry
  6103. #         If there is no table entry for it
  6104. #         Then create a subtable for it and assign it an initial value
  6105. #         If there is a table entry for it,
  6106. #         But not an subtable entry for the actual indexentry
  6107. #         Then create an initial page number set for it
  6108. #         Otherwise add the page number to the corresponding page number set
  6109. #    Sort the table of subtables by sort key value
  6110. #    For all subtables in the sorted list
  6111. #         Sort the subtables by indexentry values
  6112. #         For all the indexentries in the resulting list
  6113. #             Sort the set of page references
  6114. #             Write an \item entry for each indexentry and the page references
  6115. #
  6116. #  Limitations:
  6117. #
  6118. #     Length of index handled depends on implementation limits of memory alloc.
  6119. #  Page numbers must be integers (no roman numerals).  Sort key formed by
  6120. #  mapping to lower case and removing leading articles (a separate function
  6121. #  is used to produce the sort key, simplifying customization) -- otherwise
  6122. #  sorting is done in ASCII order.
  6123. #
  6124. ############################################################################
  6125.  
  6126. procedure main()                       # no parameters, reading from stdin
  6127.     local key_table, s, page_num, itemval, key, item_list, one_item
  6128.     local page_list, refs
  6129.  
  6130.     key_table := table()               # for items and tables of page sets
  6131.     while s := read() do               # read strings from standard input
  6132.         {
  6133.         # start with s = "\indexentry{item}{page}"
  6134.         # save what's between the opening brace and the closing brace,
  6135.         # and reverse it
  6136.         s := reverse(s[upto('{',s)+1:-1])
  6137.         # giving s = "egap{}meti"
  6138.  
  6139.         # reversing allows extracting the page number first, thereby allowing
  6140.         # ANYTHING to be in the item field
  6141.  
  6142.         # grab the "egap", reverse it, convert to integer, convert to set
  6143.         # in case of conversion failure, use 0 as the default page number
  6144.         page_num := set([integer(reverse(s[1:upto('{',s)])) | 0])
  6145.  
  6146.         # the reversed item starts after the first closing brace
  6147.         # grab the "meti", reverse it
  6148.         itemval := reverse(s[upto('}', s)+1:0])
  6149.  
  6150.         # allow the sort key to be different from the item
  6151.         # reform may be customized to produce different equivalence classes
  6152.         key := reform(itemval)
  6153.  
  6154.         # if the assigned value for the key is null
  6155.         if /key_table[key]
  6156.         then
  6157.             {
  6158.             # create a subtable for the key and give it its initial value
  6159.             key_table[key] := table()
  6160.             key_table[key][itemval] := page_num
  6161.             }
  6162.  
  6163.         # else if the assigned value for the itemval is null
  6164.         # (e. g., when the second itemval associated with a key is found)
  6165.         else if /key_table[key][itemval]
  6166.  
  6167.         # give it its initial value
  6168.         then key_table[key][itemval] := page_num
  6169.  
  6170.         # otherwise just add it to the existing page number set
  6171.         else key_table[key][itemval] ++:= page_num
  6172.         }
  6173.  
  6174.     # now that all the input has been read....
  6175.     # sort keys and subtables by key value
  6176.     key_table := sort(key_table, 3)
  6177.  
  6178.     # loop, discarding the sort keys
  6179.     while get(key_table) do
  6180.         {
  6181.         # dequeue and sort one subtable into a list
  6182.         # sort is strictly by ASCII order within the equivalence class
  6183.         item_list := sort(get(key_table), 3)
  6184.  
  6185.         # loop, consuming the item and the page number sets as we go
  6186.         while one_item := get(item_list) do
  6187.             {
  6188.             # convert the page number set into a sorted list
  6189.             page_list := sort(get(item_list))
  6190.  
  6191.             # dequeue first integer and convert to string
  6192.             refs := string(get(page_list))
  6193.  
  6194.             # dequeue rest of page nums and append
  6195.             while (refs ||:= ", " || string(get(page_list)))
  6196.  
  6197.             write("\\item ", one_item, " ", refs)
  6198.             }
  6199.         }
  6200.     return
  6201. end
  6202.  
  6203. # reform - modify the item to enforce sort order appropriately
  6204. # This could do much more. For example it could strip leading braces,
  6205. # control sequences, quotation marks, etc.  It doesn't.  Maybe later.
  6206. procedure reform(item)
  6207.    item := map(item)        # map to lowercase
  6208. # drop leading article if present
  6209.    if match("a ",   item) then return item[3:0]
  6210.    if match("an ",  item) then return item[4:0]
  6211.    if match("the ", item) then return item[5:0]
  6212.    return item
  6213. end
  6214. ##########
  6215. linden.icn
  6216. ############################################################################
  6217. #
  6218. #    Name:    linden.icn
  6219. #
  6220. #    Title:    Generate sentences in Lindenmayer system
  6221. #
  6222. #    Author:    Ralph E. Griswold
  6223. #
  6224. #    Date:    October 11, 1988
  6225. #
  6226. ############################################################################
  6227. #
  6228. #  This program reads in a 0L-system (Lindenmayer system) consisting of
  6229. #  rewriting rules in which a string is rewritten with every character
  6230. #  replaced simultaneously (conpectually) by a specified string of
  6231. #  symbols.
  6232. #
  6233. #  The last line of input consists of an initial string followed by a colon
  6234. #  (which cannot be a symbol in the initial string) and the number of times
  6235. #  the rewriting rules are to be applied.  An example is
  6236. #
  6237. #    1->2#3
  6238. #    2->2
  6239. #    3->2#4
  6240. #    4->504
  6241. #    5->6
  6242. #    6->7
  6243. #    7->8(1)
  6244. #    8->8
  6245. #    (->(
  6246. #    )->)
  6247. #    #->#
  6248. #    0->0
  6249. #    1:14
  6250. #
  6251. #  Here, the initial string is "1" and the rewriting rules are to be
  6252. #  applied 14 times.
  6253. #
  6254. #  If no rule is provided for a character, the character is not changed
  6255. #  by rewriting. Thus, the example above can be expressed more concisely
  6256. #  as
  6257. #
  6258. #    1->2#3
  6259. #    3->2#4
  6260. #    4->504
  6261. #    5->6
  6262. #    6->7
  6263. #    7->8(1)
  6264. #    1:14
  6265. #
  6266. #  If -a is given on the command line, each rewriting is written out.
  6267. #  Otherwise, only the final result is written out.
  6268. #
  6269. #  Reference:
  6270. #
  6271. #     Formal Languages, Arto Salomaa, Academic Press, 1973. pp. 234-252.
  6272. #
  6273. ############################################################################
  6274. #
  6275. #  Links: options
  6276. #
  6277. ############################################################################
  6278.  
  6279. link options
  6280.  
  6281. global rewrite
  6282.  
  6283. procedure main(args)
  6284.    local line, count, axiom, detail, opts, i, result, s
  6285.  
  6286.    rewrite := table()
  6287.  
  6288. #  What follows is a trick.  It takes advantage of the fact that Icon
  6289. #  functions are first-class data objects and that function invocation
  6290. #  and mutual evaluation have the same syntax.  If -a is specified,
  6291. #  the value of "detail" becomes the function for writing and the
  6292. #  value of "write" becomes 1.  See below.
  6293.  
  6294.    detail := 1
  6295.  
  6296.    opts := options(args,"a")
  6297.    if \opts["a"] then detail :=: write
  6298.  
  6299.    while line := read() do
  6300.       if line[2:4] == "->" then rewrite[line[1]] := line[4:0]
  6301.       else {
  6302.          i := upto(':',line)    # asssume last line
  6303.          result := line[1:i]
  6304.          count := line[i+1:0]
  6305.          break
  6306.          }
  6307.  
  6308.    detail(result)
  6309.    every result := detail(linden(result)) \ count
  6310.    write(result)    # write the last result if not already written
  6311.  
  6312. end
  6313.  
  6314. procedure linden(pstring)
  6315.    local c, s
  6316.  
  6317.    repeat {
  6318.       s := ""
  6319.       every c := !pstring do
  6320.          s ||:= (\rewrite[c] | c)
  6321.       suspend pstring := s
  6322.       }
  6323. end
  6324. ##########
  6325. lisp.icn
  6326. ############################################################################
  6327. #
  6328. #    Name:    lisp.icn
  6329. #
  6330. #    Title:    Lips interpreter
  6331. #
  6332. #    Author:    Stephen B. Wampler
  6333. #
  6334. #    Date:    August 7, 1989
  6335. #
  6336. ############################################################################
  6337. #
  6338. #     This program is a simple interpreter for pure Lisp.
  6339. #
  6340. #    The syntax and semantics are based on EV-LISP, as described in
  6341. #    Laurent Siklossy's "Let's Talk LISP" (Prentice-Hall, 1976, ISBN
  6342. #    0-13-532762-8).  Functions that have been predefined match those
  6343. #    described in Chapters 1-4 of the book.
  6344. #
  6345. #    No attempt at improving efficiency has been made, this is
  6346. #    rather an example of how a simple LISP interpreter might be
  6347. #    implemented in Icon.
  6348. #
  6349. #    The language implemented is case-insensitive.
  6350. #
  6351. #     It only reads enough input lines at one time to produce at least
  6352. #     one LISP-expression, but continues to read input until a valid
  6353. #     LISP-expression is found.
  6354. #  
  6355. #     Errors:
  6356. #
  6357. #        fails on EOF; fails with error message if current
  6358. #        input cannot be made into a valid LISP-expression (i.e. more
  6359. #        right than left parens).
  6360. #  
  6361. ############################################################################
  6362.  
  6363. global words,     # table of variable atoms
  6364.        T, NIL     # universal constants
  6365.  
  6366. global trace_set  # set of currently traced functions
  6367.  
  6368. record prop(v,f)  # abbreviated propery list
  6369.  
  6370. ### main interpretive loop
  6371. #
  6372. procedure main()
  6373. local sexpr
  6374.    initialize()
  6375.    every sexpr := bstol(getbs()) do
  6376.          PRINT([EVAL([sexpr])])
  6377. end
  6378.  
  6379. ## (EVAL e) - the actual LISP interpreter
  6380. #
  6381. procedure EVAL(l)
  6382. local fn, arglist, arg
  6383.    l := l[1]
  6384.    if T === ATOM([l]) then {                  # it's an atom
  6385.       if T === l then return .T
  6386.       if EQ([NIL,l]) === T then return .NIL
  6387.       return .((\words[l]).v | NIL)
  6388.       }
  6389.    if glist(l) then {                         # it's a list
  6390.       if T === ATOM([l[1]]) then
  6391.          case Map(l[1]) of {
  6392.          "QUOTE" : return .(l[2] | NIL)
  6393.          "COND"  : return COND(l[2:0])
  6394.          "SETQ"  : return SET([l[2]]|||evlis(l[3:0]))
  6395.          "ITRACEON"  : return (&trace := -1,T)
  6396.          "ITRACEOFF" : return (&trace := 0,NIL)
  6397.          default : return apply([l[1]]|||evlis(l[2:0])) | NIL
  6398.          }
  6399.       return apply([EVAL([l[1]])]|||evlis(l[2:0])) | NIL
  6400.       }
  6401.    return .NIL
  6402. end
  6403.  
  6404. ## apply(fn,args) - evaluate the function
  6405.  
  6406. procedure apply(l)
  6407. local fn, arglist, arg, value, fcn
  6408.    fn := l[1]
  6409.    if member(trace_set, Map(string(fn))) then {
  6410.       write("Arguments of ",fn)
  6411.       PRINT(l[2:0])
  6412.       }
  6413.    if value := case Map(string(fn)) of {
  6414.       "CAR"     : CAR([l[2]]) | NIL
  6415.       "CDR"     : CDR([l[2]]) | NIL
  6416.       "CONS"    : CONS(l[2:0]) | NIL
  6417.       "ATOM"    : ATOM([l[2]]) | NIL
  6418.       "NULL"    : NULL([l[2]]) | NIL
  6419.       "EQ"      : EQ([l[2],l[3]]) | NIL
  6420.       "PRINT"   : PRINT([l[2]]) | NIL
  6421.       "EVAL"    : EVAL([l[2]]) | NIL
  6422.       "DEFINE"  : DEFINE(l[2]) | NIL
  6423.       "TRACE"   : TRACE(l[2]) | NIL
  6424.       "UNTRACE" : UNTRACE(l[2]) | NIL
  6425.       } then {
  6426.          if member(trace_set, Map(string(fn))) then {
  6427.             write("value of ",fn)
  6428.             PRINT(value)
  6429.             }
  6430.          return value
  6431.          }
  6432.    fcn := (\words[Map(fn)]).f | return NIL
  6433.    if type(fcn) == "list" then
  6434.       if Map(fcn[1]) == "LAMBDA" then {
  6435.          value :=  lambda(l[2:0],fcn[2],fcn[3])
  6436.          if member(trace_set, Map(string(fn))) then {
  6437.             write("value of ",fn)
  6438.             PRINT(value)
  6439.             }
  6440.          return value
  6441.          }
  6442.       else
  6443.          return EVAL([fn])
  6444.    return NIL
  6445. end
  6446.  
  6447. ## evlis(l) - evaluate everything in a list
  6448. #
  6449. procedure evlis(l)
  6450. local arglist, arg
  6451.    arglist := []
  6452.    every arg := !l do
  6453.       put(arglist,EVAL([arg])) | fail
  6454.    return arglist
  6455. end
  6456.  
  6457.  
  6458. ### Initializations
  6459.  
  6460. ## initialize() - set up global values
  6461. #
  6462. procedure initialize()
  6463.    words := table()
  6464.    trace_set := set()
  6465.    T     := "T"
  6466.    NIL   := []
  6467. end
  6468.  
  6469. ### Primitive Functions
  6470.  
  6471. ## (CAR l)
  6472. #
  6473. procedure CAR(l)
  6474.    return glist(l[1])[1] | NIL
  6475. end
  6476.  
  6477. ## (CDR l)
  6478. #
  6479. procedure CDR(l)
  6480.    return glist(l[1])[2:0] | NIL
  6481. end
  6482.  
  6483. ## (CONS l)
  6484. #
  6485. procedure CONS(l)
  6486.    return ([l[1]]|||glist(l[2])) | NIL
  6487. end
  6488.  
  6489. ## (SET a l)
  6490. #
  6491. procedure SET(l)
  6492.    (T === ATOM([l[1]])& l[2]) | return NIL
  6493.    /words[l[1]] := prop()
  6494.    if type(l[2]) == "prop" then
  6495.       return .(words[l[1]].v := l[2].v)
  6496.    else
  6497.       return .(words[l[1]].v := l[2])
  6498. end
  6499.  
  6500. ## (ATOM a)
  6501. #
  6502. procedure ATOM(l)
  6503.    if type(l[1]) == "list" then
  6504.       return (*l[1] = 0 & T) | NIL
  6505.    return T
  6506. end
  6507.  
  6508. ## (NULL l)
  6509. #
  6510. procedure NULL(l)
  6511.    return EQ([NIL,l[1]])
  6512. end
  6513.  
  6514. ## (EQ a1 a2)
  6515. #
  6516. procedure EQ(l)
  6517.    if type(l[1]) == type(l[2]) == "list" then
  6518.       return (0 = *l[1] = *l[2] & T) | NIL
  6519.    return (l[1] === l[2] & T) | NIL
  6520. end
  6521.  
  6522. ## (PRINT l)
  6523. #
  6524. procedure PRINT(l)
  6525.    if type(l[1]) == "prop" then
  6526.       return PRINT([l[1].v])
  6527.    return write(strip(ltos(l)))
  6528. end
  6529.  
  6530. ## COND(l) - support routine to eval
  6531. #                 (for COND)
  6532. procedure COND(l)
  6533. local pair
  6534.    every pair := !l do {
  6535.       if type(pair) ~== "list" |
  6536.               *pair ~= 2 then {
  6537.          write(&errout,"COND: ill-formed pair list")
  6538.          return NIL
  6539.          }
  6540.       if T === EVAL([pair[1]]) then
  6541.          return EVAL([pair[2]])
  6542.       }
  6543.    return NIL
  6544. end
  6545.  
  6546. ## (TRACE l)
  6547. #
  6548. procedure TRACE(l)
  6549.    local fn
  6550.  
  6551.    every fn := !l do {
  6552.       insert(trace_set, Map(fn))
  6553.       }
  6554.    return NIL
  6555. end
  6556.  
  6557. ## (UNTRACE l)
  6558. #
  6559. procedure UNTRACE(l)
  6560.    local fn
  6561.  
  6562.    every fn := !l do {
  6563.       delete(trace_set, Map(fn))
  6564.       }
  6565.    return NIL
  6566. end
  6567.  
  6568. ## glist(l) - verify that l is a list
  6569. #
  6570. procedure glist(l)
  6571.    if type(l) == "list" then return l
  6572. end
  6573.  
  6574. ## (DEFINE fname definition)
  6575. #
  6576. # This has been considerable rewritten (and made more difficult to use!)
  6577. #    in order to match EV-LISP syntax.
  6578. procedure DEFINE(l)
  6579.    local fn_def, fn_list
  6580.  
  6581.    fn_list := []
  6582.    every fn_def := !l do {
  6583.       put(fn_list, define_fn(fn_def))
  6584.       }
  6585.  
  6586.    return fn_list
  6587. end
  6588.  
  6589. ## Define a single function (called by 'DEFINE')
  6590. #
  6591. procedure define_fn(fn_def)
  6592.    /words[Map(fn_def[1])] := prop(NIL)
  6593.    words[Map(fn_def[1])].f := fn_def[2]
  6594.    return Map(fn_def[1])
  6595. end
  6596.  
  6597. ## lambda(actuals,formals,def)
  6598. #
  6599. procedure lambda(actuals, formals, def)
  6600. local save, act, form, pair, result, arg, i
  6601.    save := table()
  6602.    every arg := !formals do
  6603.       save[arg] := \words[arg] | prop(NIL)
  6604.    i := 0
  6605.    every words[!formals] := (prop(actuals[i+:=1]|NIL)\1)
  6606.    result := EVAL([def])
  6607.    every pair := !sort(save) do
  6608.       words[pair[1]] := pair[2]
  6609.    return result
  6610. end
  6611.  
  6612. #    Date:    June 10, 1988
  6613. #
  6614. procedure getbs()
  6615. static tmp
  6616.    initial tmp := ("" ~== |read()) || " "
  6617.  
  6618.    repeat {
  6619.       while not checkbal(tmp) do {
  6620.          if more(')','(',tmp) then break
  6621.          tmp ||:= (("" ~== |read()) || " ") | break
  6622.          }
  6623.       suspend balstr(tmp)
  6624.       tmp := (("" ~== |read()) || " ") | fail
  6625.       }
  6626. end
  6627.  
  6628. ## checkbal(s) - quick check to see if s is
  6629. #       balanced w.r.t. parentheses
  6630. #
  6631. procedure checkbal(s)
  6632.    return (s ? 1(tab(bal()),pos(-1)))
  6633. end
  6634.  
  6635. ## more(c1,c2,s) - succeeds if any prefix of
  6636. #       s has more characters in c1 than
  6637. #       characters in c2, fails otherwise
  6638. #
  6639. procedure more(c1,c2,s)
  6640. local cnt
  6641.    cnt := 0
  6642.    s ? while (cnt <= 0) & not pos(0) do {
  6643.          (any(c1) & cnt +:= 1) |
  6644.          (any(c2) & cnt -:= 1)
  6645.          move(1)
  6646.          }
  6647.    return cnt >= 0
  6648. end
  6649.  
  6650. ## balstr(s) - generate the balanced disjoint substrings
  6651. #               in s, with blanks or tabs separating words
  6652. #
  6653. #       errors:
  6654. #          fails when next substring cannot be balanced
  6655. #
  6656. #
  6657. procedure balstr(s)
  6658. static blanks
  6659.    initial blanks := ' \t'
  6660.    (s||" ") ? repeat {
  6661.           tab(many(blanks))
  6662.           if pos(0) then break
  6663.           suspend (tab(bal(blanks))\1 |
  6664.                   {write(&errout,"ill-formed expression")
  6665.                     fail}
  6666.                   ) \ 1
  6667.           }
  6668. end
  6669.  
  6670. ## bstol(s) - convert a balanced string into equivalent
  6671. #       list representation.
  6672. #
  6673. procedure bstol(s)
  6674. static blanks
  6675. local l
  6676.    initial blanks := ' \t'
  6677.    (s||" ") ? {tab(many(blanks))
  6678.                l := if not ="(" then s else []
  6679.               }
  6680.    if not string(l) then
  6681.       every put(l,bstol(balstr(strip(s))))
  6682.    return l
  6683. end
  6684.  
  6685. ## ltos(l) - convert a list back into a string
  6686. #
  6687. #
  6688. procedure ltos(l)
  6689.    local tmp
  6690.  
  6691.    if type(l) ~== "list" then return l
  6692.    if *l = 0 then return "NIL"
  6693.    tmp := "("
  6694.    every tmp ||:= ltos(!l) || " "
  6695.    tmp[-1] := ")"
  6696.    return tmp
  6697. end
  6698.  
  6699. procedure strip(s)
  6700.    s ?:= 2(="(", tab(bal()), =")", pos(0))
  6701.    return s
  6702. end
  6703.  
  6704. procedure Map(s)
  6705.    return map(s, &lcase, &ucase)
  6706. end
  6707. ##########
  6708. loadmap.icn
  6709. ############################################################################
  6710. #
  6711. #    Name:    loadmap.icn
  6712. #
  6713. #    Title:    Produce load map of UNIX obect file
  6714. #
  6715. #    Author:    Stephen B. Wampler
  6716. #
  6717. #    Date:    December 13, 1985
  6718. #
  6719. ############################################################################
  6720. #  
  6721. #     This program produces a formatted listing of selected symbol classes
  6722. #  from a compiled file.  The listing is by class, and gives the
  6723. #  name, starting address, and length of the region associated with
  6724. #  each symbol.
  6725. #  
  6726. #     The options are:
  6727. #  
  6728. #      -a Display the absolute symbols.
  6729. #  
  6730. #      -b Display the BSS segment symbols.
  6731. #  
  6732. #      -c Display the common segment symbols.
  6733. #  
  6734. #      -d Display the data segment symbols.
  6735. #  
  6736. #      -t Display the text segment symbols.
  6737. #  
  6738. #      -u Display the undefined symbols.
  6739. #  
  6740. #  If no options are specified, -t is assumed.
  6741. #  
  6742. #  If the address of a symbol cannot be determined, ???? is given in
  6743. #  its place.
  6744. #  
  6745. ############################################################################
  6746. #  
  6747. #  Notes:
  6748. #
  6749. #     The size of the last region in a symbol class is suspect and is
  6750. #  usually given as rem.
  6751. #  
  6752. #     Output is not particularly exciting on a stripped file.
  6753. #  
  6754. ############################################################################
  6755. #
  6756. #  Requires: UNIX
  6757. #
  6758. ############################################################################
  6759.  
  6760. record entry(name,address)
  6761.  
  6762. procedure main(args)
  6763.    local maptype, arg, file, nm, ldmap, tname, line, text, data, bss
  6764.    local SPACE, COLON, DIGITS, HEXDIGITS, usize, address, name, nmtype
  6765.    initial {
  6766.       if *args = 0 then stop("usage: loadmap [-t -d -b -u -a -c -l] file")
  6767.       SPACE := '\t '
  6768.       COLON := ':'
  6769.       DIGITS := '0123456789'
  6770.       HEXDIGITS := DIGITS ++ 'abcdef'
  6771.       ldmap := table(6)
  6772.       ldmap["u"] := []
  6773.       ldmap["d"] := []
  6774.       ldmap["a"] := []
  6775.       ldmap["b"] := []
  6776.       ldmap["t"] := []
  6777.       ldmap["c"] := []
  6778.       tname := table(6)
  6779.       tname["u"] := "Undefined symbols"
  6780.       tname["a"] := "Absolute locations"
  6781.       tname["t"] := "Text segment symbols"
  6782.       tname["d"] := "Data segment symbols"
  6783.       tname["b"] := "BSS segment symbols"
  6784.       tname["c"] := "Common symbols"
  6785.       nmtype := "nm -gno "
  6786.       }
  6787.    maptype := ""
  6788.    every arg := !args do
  6789.       if arg[1] ~== "-" then file := arg
  6790.       else if arg == "-l" then nmtype := "nm -no "
  6791.       else if arg[1] == "-" then maptype ||:= (!"ltdbuac" == arg[2:0]) |
  6792.          stop("usage:  loadmap [-t -d -b -u -a -c -l] file")
  6793.    maptype := if *maptype = 0 then "t" else string(cset(maptype))
  6794.    write("\n",file,"\n")
  6795.    usize := open("size " || file,"rp") | stop("loadmap: cannot execute size")
  6796.    !usize ? {
  6797.       writes("Text space: ",right(text := tab(many(DIGITS)),6),"   ")
  6798.       move(1)
  6799.       writes("Initialized Data: ",right(data := tab(many(DIGITS)),6),"   ")
  6800.       move(1)
  6801.       write("Uninitialized Data: ",right(bss := tab(many(DIGITS)),6))
  6802.       }
  6803.    close(usize)
  6804.    nm := open(nmtype || file,"rp") | stop("loadmap: cannot execute nm")
  6805.    every line := !nm do
  6806.       line ? {
  6807.          tab(upto(COLON)) & move(1)
  6808.          address := integer("16r" || tab(many(HEXDIGITS))) | "????"
  6809.          tab(many(SPACE))
  6810.          type := map(move(1))
  6811.          tab(many(SPACE))
  6812.          name := tab(0)
  6813.          if find(type,maptype) then put(ldmap[type],entry(name,address))
  6814.          }
  6815.    every type := !maptype do {
  6816.       if *ldmap[type] > 0 then {
  6817.          write("\n\n\n")
  6818.          write(tname[type],":")
  6819.          write()
  6820.          show(ldmap[type],(type == "t" & text) |
  6821.             (type == "d" & data) | (type == "b" & bss) | &null,
  6822.             ldmap[type][1].address)
  6823.          }
  6824.       }
  6825. end
  6826.  
  6827. procedure show(l,ssize,base)
  6828.    local i1, i2, nrows
  6829.    static ncols
  6830.    initial ncols := 3
  6831.    write(repl(repl(" ",3) || left("name",9) || right("addr",7) ||
  6832.       right("size",6),ncols))
  6833.    write()
  6834.    nrows := (*l + (ncols - 1)) / ncols
  6835.    every i1 := 1 to nrows do {
  6836.       every i2 := i1 to *l by nrows do
  6837.          writes(repl(" ",3),left(l[i2].name,9),right(l[i2].address,7),
  6838.             right(area(l[i2 + 1].address,l[i2].address) |
  6839.             if /ssize then "rem" else base + ssize - l[i2].address,6))
  6840.          write()
  6841.          }
  6842.    return
  6843. end
  6844.  
  6845. procedure area(high,low)
  6846.    if integer(low) & integer(high) then return high - low
  6847.    else return "????"
  6848. end
  6849. ##########
  6850. memsum.icn
  6851. ############################################################################
  6852. #
  6853. #    Name:    memsum.icn
  6854. #
  6855. #    Title:    Summarize Icon memory management
  6856. #
  6857. #    Author:    Ralph E. Griswold
  6858. #
  6859. #    Date:    March 8, 1990
  6860. #
  6861. ############################################################################
  6862. #
  6863. #     This program is a filter for Icon allocation history files (see IPD113).
  6864. #  It tabulates the number of allocations by type and the total amount of
  6865. #  storage (in bytes) by type.
  6866. #
  6867. #     It takes an Icon allocation history file from standard input and writes to
  6868. #  standard output.
  6869. #
  6870. #     The command-line options are:
  6871. #
  6872. #    -t    produce tab-separated output for use in spreadsheets (the
  6873. #           default is a formatted report
  6874. #    -d    produce debugging output
  6875. #
  6876. #  Some assumptions are made about where newlines occur -- specifically
  6877. #  that verification commands are on single lines and that refresh and
  6878. #  garbage collection data are on multiple lines.
  6879. #
  6880. ############################################################################
  6881. #
  6882. #  Links: numbers, options
  6883. #
  6884. ############################################################################
  6885.  
  6886. global cmds, highlights, lastlen, alloccnt, alloctot, collections
  6887. global mmunits, diagnose, namemap
  6888.  
  6889. link numbers, options
  6890.  
  6891. procedure main(args)
  6892.    local line, region, s, skip, opts
  6893.  
  6894.    opts := options(args,"dt")
  6895.    diagnose := if \opts["d"] then write else 1
  6896.    display := if \opts["t"] then spread else report
  6897.  
  6898.    cmds := 'cefihLlRrSsTtux"XAF'        # command characters
  6899.    highlights := '%$Y'            # highlight commands
  6900.    mmunits := 4                # (for most systems)
  6901.    namemap := table("*** undefined ***")
  6902.    namemap["b"] := "large integer"
  6903.    namemap["c"] := "cset"
  6904.    namemap["e"] := "table element tv"
  6905.    namemap["f"] := "file"
  6906.    namemap["h"] := "hash block"
  6907.    namemap["i"] := "large integer"
  6908.    namemap["L"] := "list header"
  6909.    namemap["l"] := "list element"
  6910.    namemap["R"] := "record"
  6911.    namemap["r"] := "real number"
  6912.    namemap["S"] := "set header"
  6913.    namemap["s"] := "set element"
  6914.    namemap["T"] := "table header"
  6915.    namemap["t"] := "table element"
  6916.    namemap["u"] := "substring tv"
  6917.    namemap["x"] := "refresh block"
  6918.    namemap["\""] := "string"
  6919.    namemap["X"] := "co-expression"
  6920.    namemap["A"] := "alien block"
  6921.    namemap["F"] := "free space"
  6922.  
  6923.    lastlen := table()            # last size
  6924.    alloccnt := table(0)            # count of allocations
  6925.    alloctot := table(0)            # total allocation
  6926.    collections := list(4,0)        # garbage collection counts
  6927.  
  6928.    every alloccnt[!cmds] := 0
  6929.    every alloctot[!cmds] := 0
  6930.  
  6931.    cmds ++:= highlights
  6932.  
  6933.    while line := read() do {        # input from MemMon history file
  6934.       line ? {                # note: coded for extensions
  6935.          if region := tab(upto('{')) then {    # skip refresh sequence
  6936.             collections[region] +:= 1
  6937.             while line := read() | stop("**** premature eof") do
  6938.                line ? if upto('#!') then break next
  6939.             }
  6940.          case move(1) of {
  6941.  
  6942.             "=": next            # skip verification command
  6943.             "#": next            # skip comment
  6944.             ";": next            # skip pause command
  6945.             "!" | ">": next        # resynchronize (edited file)
  6946.  
  6947.             default: {            # data to process
  6948.                move(-1)            # back off from move(1) above
  6949.                if s := tab(upto('<')) then {
  6950.                   mmunits := integer(s)    # covers old case with no mmunits
  6951.                   while line := read() | stop("**** premature eof") do
  6952.                      line ? if upto('#>') then break next
  6953.                   }
  6954.                else {
  6955.                   repeat {            # process allocation
  6956.                      tab(many(' '))    # skip blanks (old files)
  6957.                      if pos(0) then break next
  6958.                      skip := process(tab(upto(cmds) + 1)) |
  6959.                         stop("*** unexpected data: ",line)
  6960.                      move(skip)
  6961.                      }
  6962.                   }
  6963.                }
  6964.             }
  6965.          }
  6966.       }
  6967.  
  6968.    display()
  6969.  
  6970. end
  6971.  
  6972. #  Display a table of allocation data
  6973. #
  6974. procedure report()
  6975.    local cnt, cnttotal, i, tot, totalcoll, tottotal
  6976.  
  6977.    static col1, col2, gutter        # column widths
  6978.  
  6979.    initial {
  6980.       col1 := 16            # name field
  6981.       col2 := 10            # number field
  6982.       gutter := repl(" ",6)
  6983.       }
  6984.  
  6985.    write(,                # write column headings
  6986.       "\n",
  6987.       left("type",col1),
  6988.       right("number",col2),
  6989.       gutter,
  6990.       right("bytes",col2),
  6991.       gutter,
  6992.       right("average",col2),
  6993.       gutter,
  6994.       right("% bytes",col2),
  6995.       "\n"
  6996.       )
  6997.  
  6998.    alloccnt := sort(alloccnt,3)                # get the data
  6999.    alloctot := sort(alloctot,3)
  7000.  
  7001.    cnttotal := 0
  7002.    tottotal := 0
  7003.  
  7004.    every i := 2 to *alloccnt by 2 do {
  7005.       cnttotal +:= alloccnt[i]
  7006.       tottotal +:= alloctot[i]
  7007.       }
  7008.  
  7009.    while write(                        # write the data
  7010.       left(namemap[get(alloccnt)],col1),        # name
  7011.       right(cnt := get(alloccnt),col2),            # number of allocations
  7012.       gutter,
  7013.       get(alloctot) & right(tot := get(alloctot),col2),    # space allocated
  7014.       gutter,
  7015.       fix(tot,cnt,col2) | repl(" ",col2),
  7016.       gutter,
  7017.       fix(100.0 * tot,tottotal,col2) | repl(" ",col2)
  7018.       )
  7019.  
  7020.    write(                        # write totals
  7021.       "\n",
  7022.       left("total:",col1),
  7023.       right(cnttotal,col2),
  7024.       gutter,
  7025.       right(tottotal,col2),
  7026.       gutter,
  7027.       fix(tottotal,cnttotal,col2) | repl(" ",col2)
  7028.       )
  7029.  
  7030.    totalcoll := 0                    # garbage collections
  7031.    every totalcoll +:= !collections
  7032.    write("\n",left("collections:",col1),right(totalcoll,col2))
  7033.    if totalcoll > 0 then {
  7034.       write(left("  static region:",col1),right(collections[1],col2))
  7035.       write(left("  string region:",col1),right(collections[2],col2))
  7036.       write(left("  block region:",col1),right(collections[3],col2))
  7037.       write(left("  no region:",col1),right(collections[4],col2))
  7038.       }
  7039.  
  7040.    return
  7041. end
  7042.  
  7043. #  Produce tab-separated output for a spreadsheet.
  7044. #
  7045. procedure spread()
  7046.  
  7047.    alloccnt := sort(alloccnt,3)                # get the data
  7048.    alloctot := sort(alloctot,3)
  7049.  
  7050.    write("*\nname    number    bytes")
  7051.  
  7052.    while write(                        # write the data
  7053.       namemap[get(alloccnt)],
  7054.       "\t",
  7055.       get(alloccnt),
  7056.       "\t",
  7057.       get(alloctot) & get(alloctot),
  7058.       )
  7059.  
  7060.    return
  7061. end
  7062.  
  7063. #  Process datm
  7064. #
  7065. procedure process(s)
  7066.    local cmd, len
  7067.  
  7068.    s ? {
  7069.       tab(upto('+') + 1)        # skip address
  7070.       len := tab(many(&digits)) | &null
  7071.       cmd := move(1)
  7072.  
  7073.       if cmd == !highlights then return 2 else {
  7074.                        # if given len is nonstring, scale
  7075.          if cmd ~== "\"" then \len *:= mmunits
  7076.          alloccnt[cmd] +:= 1
  7077.          (/len := lastlen[cmd]) | (lastlen[cmd] := len)
  7078.          diagnose(&errout,"cmd=",cmd,", len=",len)
  7079.          alloctot[cmd] +:= len
  7080.          return 0
  7081.          }
  7082.       }
  7083. end
  7084. ##########
  7085. miu.icn
  7086. ############################################################################
  7087. #
  7088. #    Name:    miu.icn
  7089. #
  7090. #    Title:    Generate strings from the MIU system
  7091. #
  7092. #    Author:    Cary A. Coutant, modified by Ralph E. Griswold
  7093. #
  7094. #    Date:    December 27, 1989
  7095. #
  7096. ############################################################################
  7097. #
  7098. #     This program generates strings from the MIU string system.
  7099. #
  7100. #     The number of generations is determined by the command-line argument.
  7101. #  The default is 7.
  7102. #
  7103. #  Reference:
  7104. #
  7105. #     Godel, Escher, and Bach: an Eternal Golden Braid, Douglas R.
  7106. #  Hofstadter, Basic Books, 1979. pp. 33-36.
  7107. #
  7108. ############################################################################
  7109.  
  7110. procedure main(arg)
  7111.    local count, gen, limit
  7112.  
  7113.    count := 0
  7114.    limit := integer(arg[1]) | 7
  7115.    gen := ["MI"]
  7116.    every count := 1 to limit do {
  7117.       show(count,gen)
  7118.       gen := nextgen(gen)
  7119.       }
  7120. end
  7121.  
  7122. # show - show a generation of strings
  7123.  
  7124. procedure show(count,gen)
  7125.    write("Generation #",count)
  7126.    every write("   ",image(\!gen))
  7127.    write()
  7128. end
  7129.  
  7130. # nextgen - given a generation of strings, compute the next generation
  7131.  
  7132. procedure nextgen(gen)
  7133.    local new, s
  7134.    new := set()
  7135.    every insert(new,apply(!gen))
  7136.    return sort(new)
  7137. end
  7138.  
  7139. # apply - produce all strings derivable from s in a single rule application
  7140.  
  7141. procedure apply(s)
  7142.    local i
  7143.    if s[-1] == "I" then suspend s || "U"
  7144.    if s[1] == "M" then suspend s || s[2:0]
  7145.    every i := find("III",s) do
  7146.       suspend s[1:i] || "U" || s[i+3:0]
  7147.    every i := find("UU",s) do
  7148.       suspend s[1:i] || s[i+2:0]
  7149. end
  7150. ##########
  7151. monkeys.icn
  7152. ############################################################################
  7153. #
  7154. #    Name:    monkeys.icn
  7155. #
  7156. #    Title:    Generate random text
  7157. #
  7158. #    Author:    Stephen B. Wampler, modified by Ralph E. Griswold
  7159. #
  7160. #    Date:    June 10, 1988
  7161. #
  7162. ############################################################################
  7163. #
  7164. #  The old monkeys at the typewriters anecdote ...
  7165. #  
  7166. #     This program uses ngram analysis to randomly generate text in
  7167. #  the same 'style' as the input text.  The arguments are:
  7168. #  
  7169. #     -s     show the input text
  7170. #     -n n   use n as the ngram size (default:3)
  7171. #     -l n   output at about n lines (default:10)
  7172. #     -r n   set random number seed to n
  7173. #  
  7174. ############################################################################
  7175. #
  7176. #  Links: options
  7177. #
  7178. ############################################################################
  7179.  
  7180. link options
  7181.  
  7182. procedure main(args)
  7183.    local switches, n, linecount, ngrams, preline
  7184.    local line, ngram, nextchar, firstngram, Show
  7185.  
  7186.    switches := options(args,"sn+l+r+")
  7187.    if \switches["s"] then Show := writes else Show := 1
  7188.    n := \switches["n"] | 3
  7189.    linecount := \switches["l"] | 10
  7190.  
  7191.    ngrams := table()
  7192.  
  7193.    Show("Orginal Text is: \n\n")
  7194.  
  7195.    preline := ""
  7196.    every line := preline || !&input do {
  7197.       Show(line)
  7198.       line ? {
  7199.             while ngram := move(n) & nextchar := move(1) do {
  7200.                /firstngram := ngram
  7201.                /ngrams[ngram] := ""
  7202.                ngrams[ngram] ||:= nextchar
  7203.                move(-n)
  7204.                }
  7205.             preline := tab(0) || "\n"
  7206.             }
  7207.       }
  7208.  
  7209.    Show("\n\nGenerating Sentences\n\n")
  7210.  
  7211.    ngram := writes(firstngram)
  7212.    while linecount > 0 do {
  7213.       if /ngrams[ngram] then
  7214.          exit()                 # if hit EOF ngram early
  7215.       ngram := ngram[2:0] || writes(nextchar := ?ngrams[ngram])
  7216.       if (nextchar == "\n") then
  7217.          linecount -:= 1
  7218.       }
  7219.  
  7220. end
  7221. ##########
  7222. pack.icn
  7223. ############################################################################
  7224. #
  7225. #    Name:    pack.icn
  7226. #
  7227. #    Title:    Package multiple files
  7228. #
  7229. #    Author:    Ralph E. Griswold
  7230. #
  7231. #    Date:    May 27, 1989
  7232. #
  7233. ############################################################################
  7234. #
  7235. #     This programs reads a list of file names from standard input and
  7236. #  packages the files into a single file, which is written to standard
  7237. #  output.
  7238. #
  7239. #     Files are separated by a header, ##########, followed by the file
  7240. #  name.  This simple scheme does not work if a file contains such a header
  7241. #  itself, and it's problematical for files of binary data.
  7242. #
  7243. ############################################################################
  7244. #
  7245. #  See also:  unpack.icn
  7246. #
  7247. ############################################################################
  7248.  
  7249. procedure main()
  7250.    local in
  7251.  
  7252.    while name := read() do {
  7253.       close(\in)
  7254.       in := open(name) | stop("cannot open input file: ",name)
  7255.       write("##########")
  7256.       write(name)
  7257.       while write(read(in))
  7258.       }
  7259. end
  7260. ##########
  7261. parens.icn
  7262. ############################################################################
  7263. #
  7264. #    Name:    parens.icn
  7265. #
  7266. #    Title:    Produce random parenthesis-balanced strings
  7267. #
  7268. #    Author:    Ralph E. Griswold
  7269. #
  7270. #    Date:    June 10, 1988
  7271. #
  7272. ############################################################################
  7273. #  
  7274. #     This program produces parenthesis-balanced strings in which
  7275. #  the parentheses are randomly distributed.
  7276. #  
  7277. #  Options: The following options are available:
  7278. #  
  7279. #       -b n Bound the length of the strings to n left and right
  7280. #            parentheses each. The default is 10.
  7281. #  
  7282. #       -n n Produce n strings. The default is 10.
  7283. #  
  7284. #       -l s Use the string s for the left parenthesis. The default
  7285. #            is ( .
  7286. #  
  7287. #       -r s Use the string s for the right parenthesis. The default
  7288. #            is ) .
  7289. #  
  7290. #       -v   Randomly vary the length of the strings between 0 and
  7291. #            the bound.  In the absence of this option, all strings
  7292. #            are the exactly as long as the specified bound.
  7293. #  
  7294. #     For example, the output for
  7295. #  
  7296. #          parens -v -b 4 -l "begin " -r "end "
  7297. #  
  7298. #  is
  7299. #  
  7300. #          begin end
  7301. #          begin end begin end
  7302. #          begin begin end end begin end
  7303. #          begin end begin begin end end
  7304. #          begin end
  7305. #          begin begin end end
  7306. #          begin begin begin end end end
  7307. #          begin end begin begin end end
  7308. #          begin end begin end
  7309. #          begin begin end begin end begin end end
  7310. #  
  7311. #  
  7312. #  Comments: This program was motivated by the need for test data
  7313. #  for error repair schemes for block-structured programming lan-
  7314. #  gauges. A useful extension to this program would be some
  7315. #  way of generating other text among the parentheses.  In addition
  7316. #  to the intended use of the program, it can produce a variety of
  7317. #  interesting patterns, depending on the strings specified by -l
  7318. #  and -r.
  7319. #  
  7320. ############################################################################
  7321. #
  7322. #  Links: options
  7323. #
  7324. ############################################################################
  7325.  
  7326. link options
  7327.  
  7328. global r, k, lp, rp
  7329.  
  7330. procedure main(args)
  7331.    local string, i, s, bound, limit, varying, opts
  7332.    
  7333.    bound := limit := 10            # default bound and limit
  7334.    lp := "("                # default left paren
  7335.    rp := ")"                # default right paren
  7336.  
  7337.    opts := options(args,"l:r:vb+n+")
  7338.    bound := \opts["b"] | 10
  7339.    limit := \opts["n"] | 10
  7340.    lp := \opts["l"] | "("
  7341.    rp := \opts["r"] | ")"
  7342.    varying := opts["v"]
  7343.    every 1 to limit do {
  7344.       if \varying then k := 2 * ?bound else k := 2 * bound
  7345.       string := ""
  7346.       r := 0
  7347.       while k ~= r do {
  7348.          if r = 0 then string ||:= Open()
  7349.          else if ?0 < probClose()
  7350.             then string ||:= Close() else string ||:= Open()
  7351.          }
  7352.       while k > 0 do string ||:= Close()
  7353.       write(string)
  7354.       }
  7355. end
  7356.  
  7357. procedure Open()
  7358.    r +:= 1
  7359.    k -:= 1
  7360.    return lp
  7361. end
  7362.  
  7363. procedure Close()
  7364.    r -:= 1
  7365.    k -:= 1
  7366.    return rp
  7367. end
  7368.  
  7369. procedure probClose()
  7370.    return ((r * (r + k + 2)) / (2.0 * k * (r + 1)))
  7371. end
  7372. ##########
  7373. parse.icn
  7374.  
  7375. ############################################################################
  7376. #
  7377. #    Name:    parse.icn
  7378. #
  7379. #    Title:    Parse simple statements
  7380. #
  7381. #    Author:    Kenneth Walker
  7382. #
  7383. #    Date:    December 22, 1989
  7384. #
  7385. ############################################################################
  7386. global lex    # co-expression for lexical analyzer
  7387. global next_tok    # next token from input
  7388.  
  7389. record token(type, string)
  7390.  
  7391. procedure main()
  7392.    lex := create ((!&input ? get_tok()) | |token("eof", "eof"))
  7393.    prog()
  7394. end
  7395.  
  7396. #
  7397. # get_tok is the main body of lexical analyzer
  7398. #
  7399. procedure get_tok()
  7400.    local tok
  7401.    repeat {    # skip white space and comments
  7402.       tab(many('     '))
  7403.       if ="#" | pos(0) then fail
  7404.  
  7405.       if any(&letters) then    # determine token type
  7406.          tok := token("id", tab(many(&letters ++ '_')))
  7407.       else if any(&digits) then
  7408.          tok := token("integer", tab(many(&digits)))
  7409.       else case move(1) of {
  7410.          ";"    :    tok := token("semi", ";")
  7411.          "("    :    tok := token("lparen", "(")
  7412.          ")"    :    tok := token("rparen", ")")
  7413.          ":"    :    if ="=" then tok := token("assign", ":=")
  7414.                        else tok := token("colon", ":")
  7415.          "+"    :    tok := token("add_op", "+")
  7416.          "-"    :    tok := token("add_op", "-")
  7417.          "*"    :    tok := token("mult_op", "*")
  7418.          "/"    :    tok := token("mult_op", "/")
  7419.          default    :    err("invalid character in input")
  7420.          }
  7421.       suspend tok
  7422.       }
  7423. end
  7424.  
  7425. #
  7426. # The procedures that follow make up the parser
  7427. #
  7428.  
  7429. procedure prog()
  7430.    next_tok := @lex
  7431.    stmt()
  7432.    while next_tok.type == "semi" do {
  7433.       next_tok := @lex
  7434.       stmt()
  7435.       }
  7436.    if next_tok.type ~== "eof" then
  7437.       err("eof expected")
  7438. end
  7439.  
  7440. procedure stmt()
  7441.    if next_tok.type ~== "id" then
  7442.       err("id expected")
  7443.    write(next_tok.string)
  7444.    if (@lex).type ~== "assign" then
  7445.       err(":= expected")
  7446.    next_tok := @lex
  7447.    expr()
  7448.    write(":=")
  7449. end
  7450.  
  7451. procedure expr()
  7452.    local op
  7453.  
  7454.    term()
  7455.    while next_tok.type == "add_op" do {
  7456.       op := next_tok.string
  7457.       next_tok := @lex
  7458.       term()
  7459.       write(op)
  7460.       }
  7461. end
  7462.  
  7463. procedure term()
  7464.    local op
  7465.  
  7466.    factor()
  7467.    while next_tok.type == "mult_op" do {
  7468.       op := next_tok.string
  7469.       next_tok := @lex
  7470.       factor()
  7471.       write(op)
  7472.       }
  7473. end
  7474.  
  7475. procedure factor()
  7476.    case next_tok.type of {
  7477.       "id" | "integer": {
  7478.          write(next_tok.string)
  7479.          next_tok := @lex
  7480.          }
  7481.       "lparen": {
  7482.          next_tok := @lex
  7483.          expr()
  7484.          if next_tok.type ~== "rparen" then
  7485.             err(") expected")
  7486.          else
  7487.             next_tok := @lex
  7488.          }
  7489.       default:
  7490.          err("id or integer expected")
  7491.       }
  7492. end
  7493.  
  7494. procedure err(s)
  7495.    stop(" ** error **  ", s)
  7496. end
  7497. ##########
  7498. parsex.icn
  7499. ############################################################################
  7500. #
  7501. #    Name:    parsex.icn
  7502. #
  7503. #    Title:    Parse arithmetic expressions
  7504. #
  7505. #    Author:    Cheyenne Wills
  7506. #
  7507. #    Date:    June 10, 1988
  7508. #
  7509. ############################################################################
  7510. #
  7511. #  Adapted from C code written by Allen I. Holub published in the
  7512. #  Feb 1987 issue of Dr. Dobb's Journal.
  7513. #
  7514. #  General purpose expression analyzer.  Can evaluate any expression
  7515. #  consisting of number and the following operators (listed according
  7516. #  to precedence level):
  7517. #
  7518. #  () - ! 'str'str'
  7519. #  * / &
  7520. #  + -
  7521. #  < <= > >= == !=
  7522. #  && ||
  7523. #
  7524. # All operators associate left to right unless () are present.
  7525. # The top - is a unary minus.
  7526. #
  7527. #
  7528. #  <expr>   ::= <term> <expr1>
  7529. #  <expr1>  ::= && <term> <expr1>
  7530. #        ::= || <term> <expr1>
  7531. #        ::= epsilon
  7532. #
  7533. #  <term>   ::= <fact> <term1>
  7534. #  <term1>  ::= <  <fact> <term1>
  7535. #        ::= <= <fact> <term1>
  7536. #        ::= >  <fact> <term1>
  7537. #        ::= >= <fact> <term1>
  7538. #        ::= == <fact> <term1>
  7539. #        ::= != <fact> <term1>
  7540. #        ::= epsilon
  7541. #
  7542. #  <fact>   ::= <part> <fact1>
  7543. #  <fact1>  ::= + <part> <fact1>
  7544. #        ::= - <part> <fact1>
  7545. #        ::= - <part> <fact1>
  7546. #        ::= epsilon
  7547. #
  7548. #  <part>   ::= <const> <part1>
  7549. #  <part1>  ::= * <const> <part1>
  7550. #        ::= / <const> <part1>
  7551. #        ::= % <const> <part1>
  7552. #        ::= epsilon
  7553. #
  7554. #  <const>  ::= ( <expr> )
  7555. #        ::= - ( <expr> )
  7556. #        ::= - <const>
  7557. #        ::= ! <const>
  7558. #        ::= 's1's2'    # compares s1 with s2  0 if ~= else 1
  7559. #        ::= NUMBER       # number is a lose term any('0123456789.Ee')
  7560. #
  7561. #############################################################################
  7562.  
  7563. procedure main()
  7564.    local line
  7565.  
  7566.    writes("->")
  7567.    while line := read() do {
  7568.        write(parse(line))
  7569.        writes("->")
  7570.        }
  7571. end
  7572.  
  7573. procedure parse(exp)
  7574.    return exp ? expr()
  7575. end
  7576.  
  7577. procedure expr(exp)
  7578.    local lvalue
  7579.  
  7580.    lvalue := term()
  7581.    repeat {
  7582.        tab(many(' \t'))
  7583.        if ="&&" then lvalue := iand(term(),lvalue)
  7584.        else if ="||" then lvalue := ior(term(),lvalue)
  7585.        else break
  7586.        }
  7587.    return lvalue
  7588. end
  7589.  
  7590. procedure term()
  7591.    local lvalue
  7592.  
  7593.    lvalue := fact()
  7594.    repeat {
  7595.        tab(many(' \t'))
  7596.        if      ="<=" then lvalue := if lvalue <= fact() then 1 else 0
  7597.        else if ="<"  then lvalue := if lvalue <  fact() then 1 else 0
  7598.        else if =">=" then lvalue := if lvalue >= fact() then 1 else 0
  7599.        else if =">"  then lvalue := if lvalue >  fact() then 1 else 0
  7600.        else if ="==" then lvalue := if lvalue =  fact() then 1 else 0
  7601.        else if ="!=" then lvalue := if lvalue ~= fact() then 1 else 0
  7602.        else break
  7603.        }
  7604.    return lvalue
  7605. end
  7606.  
  7607. procedure fact()
  7608.    local lvalue
  7609.  
  7610.    lvalue := part()
  7611.    repeat {
  7612.        tab(many(' \t'))
  7613.        if ="+" then lvalue +:= part()
  7614.        else if ="-" then lvalue -:= part()
  7615.        else break
  7616.        }
  7617.    return lvalue
  7618. end
  7619.  
  7620. procedure part()
  7621.    local lvalue
  7622.  
  7623.    lvalue := const()
  7624.    repeat {
  7625.        tab(many(' \t'))
  7626.        if ="*" then lvalue *:= part()
  7627.        else if ="%" then lvalue %:= part()
  7628.        else if ="/" then lvalue /:= part()
  7629.        else break
  7630.        }
  7631.    return lvalue
  7632. end
  7633.  
  7634. procedure const()
  7635.    local sign, logical, rval, s1, s2
  7636.  
  7637.    tab(many(' \t'))
  7638.  
  7639.    if ="-" then sign := -1 else sign := 1
  7640.    if ="!" then logical := 1 else logical := &null
  7641.    if ="(" then {
  7642.        rval := expr()
  7643.        if not match(")") then {
  7644.        write(&subject)
  7645.        write(right("",&pos-1,"_"),"^ Mis-matched parenthesis")
  7646.        }
  7647.        else move(1)
  7648.        }
  7649.    else if ="'" then {
  7650.        s1 := tab(upto('\''))
  7651.        move(1)
  7652.        s2 := tab(upto('\''))
  7653.        move(1)
  7654.        rval := if s1 === s2 then 1 else 0
  7655.        }
  7656.    else {
  7657.        rval := tab(many('0123456789.eE'))
  7658.        }
  7659.    if \logical then { return if rval = 0 then 1 else 0 }
  7660.    else return rval * sign
  7661. end
  7662. ##########
  7663. press.icn
  7664. ############################################################################
  7665. #
  7666. #    Name:    press.icn
  7667. #
  7668. #    Title:    LZW Compression and Decompression Utility
  7669. #
  7670. #    Author:    Robert J. Alexander
  7671. #
  7672. #    Date:    December 5, 1989
  7673. #
  7674. ############################################################################
  7675. #
  7676. #  Note:  This program is designed primarily to demonstrate the LZW
  7677. #         compression process.  It contains a lot of tracing toward
  7678. #         that end and is too slow for practical use.
  7679. #
  7680. ############################################################################
  7681. #
  7682. #  Usage: press [-t] -c [-s n] [-f <compressed file>] <file to compress>...
  7683. #         press [-t] -x <compressed file>...
  7684. #
  7685. #  -c  perform compression
  7686. #  -x  expand (decompress) compressed file
  7687. #  -f  output file for compression -- if missing standard output used
  7688. #  -s  maximum string table size
  7689. #       (for compression only -- default = 1024)
  7690. #  -t  output trace info to standard error file
  7691. #
  7692. #  If the specified maximum table size is positive, the string table is
  7693. #  discarded when the maximum size is reached and rebuilt (recommended).
  7694. #  If negative, the original table is not discarded, which might produce
  7695. #  better results in some circumstances.
  7696. #
  7697. ############################################################################
  7698. #
  7699. #  Features that might be nice to add someday:
  7700. #
  7701. #       Allow decompress output to standard output.
  7702. #
  7703. #       Handle heirarchies.
  7704. #
  7705. #       Way to list files in archive, and access individual files
  7706. #
  7707. ############################################################################
  7708. #
  7709. #  Links: options
  7710. #
  7711. ############################################################################
  7712.  
  7713. global inchars,outchars,tinchars,toutchars,lzw_recycles,
  7714.       lzw_stringTable,lzw_trace,wr,wrs,rf,wf
  7715.  
  7716. link options
  7717.  
  7718. procedure main(arg)
  7719.    local compr,expand,fn,maxT,maxTableSize,opt,outfile,wfn
  7720.  
  7721.    #
  7722.    #  Initialize.
  7723.    #
  7724.    opt := options(arg,"ts+f:cx")
  7725.    if *arg = 0 then arg := ["-"]
  7726.    lzw_trace := opt["t"]
  7727.    expand := opt["x"]
  7728.    compr := opt["c"]
  7729.    outfile := opt["f"]
  7730.    maxTableSize := \opt["s"]
  7731.    if (/expand & /compr) then Usage()
  7732.    wr := write ; wrs := writes
  7733.    inchars := outchars := tinchars := toutchars := lzw_recycles := 0
  7734.    #
  7735.    #  Process compression.
  7736.    #
  7737.    if \compr then {
  7738.       if \expand then Usage()
  7739.       if \outfile then
  7740.         wf := open(outfile,"w") | stop("Can't open output file ",outfile)
  7741.       #
  7742.       #  Loop to process files on command line.
  7743.       #
  7744.       every fn := !arg do {
  7745.      if fn === outfile then next
  7746.      wr(&errout,"\nFile \"",fn,"\"")
  7747.      rf := if fn ~== "-" then open(fn) | &null else &input
  7748.      if /rf then {
  7749.         write(&errout,"Can't open input file \"",fn,"\" -- skipped")
  7750.         next
  7751.         }
  7752.      write(wf,tail(fn))
  7753.      maxT := compress(r,w,maxTableSize)
  7754.      close(rf)
  7755.      stats(maxT)
  7756.      }
  7757.       }
  7758.    #
  7759.    #  Process decompression.
  7760.    #
  7761.    else if \expand then {
  7762.       if \(compr | outfile | maxTableSize) then Usage()
  7763.       #
  7764.       #  Loop to process files on command line.
  7765.       #
  7766.       every fn := !arg do {
  7767.      rf := if fn ~== "-" then open(fn) | &null else &input
  7768.      if /rf then {
  7769.         write(&errout,"Can't open input file \"",fn,"\" -- skipped")
  7770.         next
  7771.         }
  7772.      while wfn := read(rf) do {
  7773.         wr(&errout,"\nFile \"",wfn,"\"")
  7774.         wf := open(wfn,"w") | &null
  7775.         if /wf then {
  7776.            write(&errout,"Can't open output file \"",wfn,"\" -- quitting")
  7777.            exit(1)
  7778.            }
  7779.         maxT := decompress(r,w)
  7780.         close(wf)
  7781.         stats(maxT)
  7782.         }
  7783.      close(rf)
  7784.      }
  7785.       }
  7786.    else Usage()
  7787.    #
  7788.    #  Write statistics
  7789.    #
  7790.    wr(&errout,"\nTotals: ",
  7791.      "\n  input = ",tinchars,
  7792.      "\n  output = ",toutchars,
  7793.      "\n  compression factor = ",(real(toutchars) / real(0 < tinchars)) | "")
  7794. end
  7795.  
  7796.  
  7797. procedure stats(maxTableSize)
  7798.    #
  7799.    #  Write statistics
  7800.    #
  7801.    wr(&errout,
  7802.      "  input = ",inchars,
  7803.      "\n  output = ",outchars,
  7804.      "\n  compression factor = ",(real(outchars) / real(0 < inchars)) | "",
  7805.      "\n  table size = ",*lzw_stringTable,"/",maxTableSize,
  7806.      " (",lzw_recycles," recycles)")
  7807.    tinchars +:= inchars
  7808.    toutchars +:= outchars
  7809.    inchars := outchars := lzw_recycles := 0
  7810.    return
  7811. end
  7812.  
  7813.  
  7814. procedure r()
  7815.    return 1(reads(rf),inchars +:= 1)
  7816. end
  7817.  
  7818.  
  7819. procedure w(s)
  7820.    return 1(writes(wf,s),outchars +:= *s)
  7821. end
  7822.  
  7823.  
  7824. procedure Usage()
  7825.    stop("_
  7826. #  Usage: icompress [-t] -c [-s n] <file to compress>...\n_
  7827. #         icompress [-t] -x <compressed file>...\n_
  7828. #\n_
  7829. #  -c  perform compression\n_
  7830. #  -x  expand (decompress) compressed file\n_
  7831. #  -f  output file for compression -- if missing standard output used\n_
  7832. #  -s  maximum string table size\n_
  7833. #       (for compression only -- default = 1024)\n_
  7834. #  -t  output trace info to standard error file\n_
  7835. #")
  7836. end
  7837.  
  7838. procedure tail(fn)
  7839.    local i
  7840.    i := 0
  7841.    every i := find("/",fn)
  7842.    return fn[i + 1:0]
  7843. end
  7844.  
  7845. #
  7846. #  compress() -- LZW compression
  7847. #
  7848. #  Arguments:
  7849. #
  7850. #    inproc    a procedure that returns a single character from
  7851. #        the input stream.
  7852. #
  7853. #    outproc    a procedure that writes a single character (its
  7854. #        argument) to the output stream.
  7855. #
  7856. #    maxTableSize    the maximum size to which the string table
  7857. #        is allowed to grow before something is done about it.
  7858. #        If the size is positive, the table is discarded and
  7859. #        a new one started.  If negative, it is retained, but
  7860. #        no new entries are added.
  7861. #
  7862.  
  7863. procedure compress(inproc,outproc,maxTableSize)
  7864.    local EOF,c,charTable,junk1,junk2,outcode,s,t,
  7865.      tossTable,x
  7866.    #
  7867.    #  Initialize.
  7868.    #
  7869.    /maxTableSize := 1024    # 10 "bits"
  7870.    every outproc(!string(maxTableSize))
  7871.    outproc("\n")
  7872.    tossTable := maxTableSize
  7873.    /lzw_recycles := 0
  7874.    if maxTableSize < 0 then maxTableSize := -maxTableSize
  7875.    charTable := table()
  7876.    every c := !&cset do charTable[c] := ord(c)
  7877.    EOF := charTable[*charTable] := *charTable    # reserve code=256 for EOF
  7878.    lzw_stringTable := copy(charTable)
  7879.    #
  7880.    #  Compress the input stream.
  7881.    #
  7882.    s := inproc() | return maxTableSize
  7883.    if \lzw_trace then {
  7884.       wr(&errout,"\nInput string\tOutput code\tNew table entry")
  7885.       wrs(&errout,"\"",image(s)[2:-1])
  7886.       }
  7887.    while c := inproc() do {
  7888.    if \lzw_trace then
  7889.      wrs(&errout,image(c)[2:-1])
  7890.       if \lzw_stringTable[t := s || c] then s := t
  7891.       else {
  7892.      compress_output(outproc,junk2 := lzw_stringTable[s],junk1 := *lzw_stringTable)
  7893.      if *lzw_stringTable < maxTableSize then
  7894.            lzw_stringTable[t] := *lzw_stringTable
  7895.      else if tossTable >= 0 then {
  7896.            lzw_stringTable := copy(charTable)
  7897.            lzw_recycles +:= 1
  7898.         }
  7899.      if \lzw_trace then
  7900.            wrs(&errout,"\"\t\t",
  7901.              image(char(*&cset > junk2) | junk2),
  7902.              "(",junk1,")\t\t",lzw_stringTable[t]," = ",image(t),"\n\"")
  7903.      s := c
  7904.      }
  7905.       }
  7906.    compress_output(outproc,lzw_stringTable[s],*lzw_stringTable)
  7907.    if \lzw_trace then
  7908.      wr(&errout,"\"\t\t",
  7909.            image(char(*&cset > (x := \lzw_stringTable[s] | 0)) | x))
  7910.    compress_output(outproc,EOF,*lzw_stringTable)
  7911.    compress_output(outproc)
  7912.    return maxTableSize
  7913. end
  7914.  
  7915.  
  7916. procedure compress_output(outproc,code,stringTableSize)
  7917.    local outcode
  7918.    static max,bits,buffer,bufferbits,lastSize
  7919.    #
  7920.    #  Initialize.
  7921.    #
  7922.    initial {
  7923.       lastSize := 1000000
  7924.       buffer := bufferbits := 0
  7925.       }
  7926.    #
  7927.    #  If this is "close" call, flush buffer and reinitialize.
  7928.    #
  7929.    if /code then {
  7930.       outcode := &null
  7931.       if bufferbits > 0 then
  7932.         outproc(char(outcode := ishift(buffer,8 - bufferbits)))
  7933.       lastSize := 1000000
  7934.       buffer := bufferbits := 0
  7935.       return outcode
  7936.       }
  7937.    #
  7938.    #  Expand output code size if necessary.
  7939.    #
  7940.    if stringTableSize < lastSize then {
  7941.       max := 1
  7942.       bits := 0
  7943.       }
  7944.    while stringTableSize > max do {
  7945.       max *:= 2
  7946.       bits +:= 1
  7947.       }
  7948.    lastSize := stringTableSize
  7949.    #
  7950.    #  Merge new code into buffer.
  7951.    #
  7952.    buffer := ior(ishift(buffer,bits),code)
  7953.    bufferbits +:= bits
  7954.    #
  7955.    #  Output bits.
  7956.    #
  7957.    while bufferbits >= 8 do {
  7958.       outproc(char(outcode := ishift(buffer,8 - bufferbits)))
  7959.       buffer := ixor(buffer,ishift(outcode,bufferbits - 8))
  7960.       bufferbits -:= 8
  7961.       }
  7962.    return outcode
  7963. end
  7964.  
  7965. ############################################################################
  7966. #
  7967. #  decompress() -- LZW decompression of compressed stream created
  7968. #                  by compress()
  7969. #
  7970. #  Arguments:
  7971. #
  7972. #    inproc    a procedure that returns a single character from
  7973. #        the input stream.
  7974. #
  7975. #    outproc    a procedure that writes a single character (its
  7976. #        argument) to the output stream.
  7977. #
  7978.  
  7979. procedure decompress(inproc,outproc)
  7980.    local EOF,c,charSize,code,i,maxTableSize,new_code,old_strg,
  7981.      strg,tossTable
  7982.    #
  7983.    #  Initialize.
  7984.    #
  7985.    maxTableSize := ""
  7986.    while (c := inproc()) ~== "\n" do maxTableSize ||:= c
  7987.    maxTableSize := integer(maxTableSize) |
  7988.      stop("Invalid file format -- max table size missing")
  7989.    tossTable := maxTableSize
  7990.    /lzw_recycles := 0
  7991.    if maxTableSize < 0 then maxTableSize := -maxTableSize
  7992.    maxTableSize -:= 1
  7993.    lzw_stringTable := list(*&cset)
  7994.    every i := 1 to *lzw_stringTable do lzw_stringTable[i] := char(i - 1)
  7995.    put(lzw_stringTable,EOF := *lzw_stringTable)  # reserve code=256 for EOF
  7996.    charSize := *lzw_stringTable
  7997.    if \lzw_trace then
  7998.      wr(&errout,"\nInput code\tOutput string\tNew table entry")
  7999.    #
  8000.    #  Decompress the input stream.
  8001.    #
  8002.    while old_strg :=
  8003.      lzw_stringTable[decompress_read_code(inproc,*lzw_stringTable,EOF) + 1] do {
  8004.       if \lzw_trace then
  8005.         wr(&errout,image(old_strg),"(",*lzw_stringTable,")",
  8006.           "\t",image(old_strg))
  8007.       outproc(old_strg)
  8008.       c := old_strg[1]
  8009.       (while new_code := decompress_read_code(inproc,*lzw_stringTable + 1,EOF) do {
  8010.      strg := lzw_stringTable[new_code + 1] | old_strg || c
  8011.      outproc(strg)
  8012.      c := strg[1]
  8013.      if \lzw_trace then
  8014.            wr(&errout,image(char(*&cset > new_code) \ 1 | new_code),
  8015.              "(",*lzw_stringTable + 1,")","\t",
  8016.              image(strg),"\t\t",
  8017.              *lzw_stringTable," = ",image(old_strg || c))
  8018.      if *lzw_stringTable < maxTableSize then
  8019.            put(lzw_stringTable,old_strg || c)
  8020.      else if tossTable >= 0 then {
  8021.         lzw_stringTable := lzw_stringTable[1:charSize + 1]
  8022.         lzw_recycles +:= 1
  8023.         break
  8024.         }
  8025.      old_strg := strg
  8026.      }) | break  # exit outer loop if this loop completed
  8027.       }
  8028.    decompress_read_code()
  8029.    return maxTableSize
  8030. end
  8031.  
  8032.  
  8033. procedure decompress_read_code(inproc,stringTableSize,EOF)
  8034.    local code
  8035.    static max,bits,buffer,bufferbits,lastSize
  8036.  
  8037.    #
  8038.    #  Initialize.
  8039.    #
  8040.    initial {
  8041.       lastSize := 1000000
  8042.       buffer := bufferbits := 0
  8043.       }
  8044.    #
  8045.    #  Reinitialize if called with no arguments.
  8046.    #
  8047.    if /inproc then {
  8048.       lastSize := 1000000
  8049.       buffer := bufferbits := 0
  8050.       return
  8051.       }
  8052.    #
  8053.    #  Expand code size if necessary.
  8054.    #
  8055.    if stringTableSize < lastSize then {
  8056.       max := 1
  8057.       bits := 0
  8058.       }
  8059.    while stringTableSize > max do {
  8060.       max *:= 2
  8061.       bits +:= 1
  8062.       }
  8063.    #
  8064.    #  Read in more data if necessary.
  8065.    #
  8066.    while bufferbits < bits do {
  8067.       buffer := ior(ishift(buffer,8),ord(inproc())) |
  8068.         stop("Premature end of file")
  8069.       bufferbits +:= 8
  8070.       }
  8071.    #
  8072.    #  Extract code from buffer and return.
  8073.    #
  8074.    code := ishift(buffer,bits - bufferbits)
  8075.    buffer := ixor(buffer,ishift(code,bufferbits - bits))
  8076.    bufferbits -:= bits
  8077.    return EOF ~= code
  8078. end
  8079. ##########
  8080. proto.icn
  8081. ############################################################################
  8082. #
  8083. #    Name:    proto.icn
  8084. #
  8085. #    Title:    Instances of different syntactic forms in Icon
  8086. #
  8087. #    Author:    Ralph E. Griswold
  8088. #
  8089. #    Date:    June 10, 1988
  8090. #
  8091. ############################################################################
  8092. #
  8093. #     This program doesn't "do" anything.  It just contains an example of
  8094. #  every syntactic form in Version 7 of Icon (or close to it).  It might
  8095. #  be useful for checking programs that process Icon programs.  Note, however,
  8096. #  that it does not contain many combinations of different syntactic forms.
  8097. #
  8098. ############################################################################
  8099. #
  8100. #  Program note:
  8101. #
  8102. #     This program is divided into procedures to avoid overflow with
  8103. #  default values for Icon's translator and linker.
  8104. #
  8105. ############################################################################
  8106. #
  8107. #  Links: options
  8108. #
  8109. #  Requires:  co-expressions
  8110. #
  8111. ############################################################################
  8112.  
  8113. link options
  8114.  
  8115. record three(x,y,z)
  8116. record zero()
  8117. record one(z)
  8118.  
  8119. global line, count
  8120.  
  8121. procedure main()
  8122.    expr1()
  8123.    expr2()
  8124.    expr3()
  8125.    expr4(1,2)
  8126.    expr4{1,2}
  8127.    expr5(1,2,3,4)
  8128. end
  8129.  
  8130. procedure expr1()
  8131.    local x, y, z
  8132.    local i, j
  8133.    static e1
  8134.  
  8135.    initial e1 := 0
  8136.  
  8137.    exit()            # get out before there's trouble
  8138.  
  8139.    ()
  8140.    {}
  8141.    ();()
  8142.    []
  8143.    [,]
  8144.    x.y
  8145.    x[i]
  8146.    x[i:j]
  8147.    x[i+:j]
  8148.    x[i-:j]
  8149.    (,,,)
  8150.    x(,,,)
  8151.    not x
  8152.    |x
  8153.    !x
  8154.    *x
  8155.    +x
  8156.    -x
  8157. end
  8158.  
  8159. procedure expr2()
  8160.    local x, i, y, j, c1, c2, s1, s2, a2, k, a1
  8161.  
  8162.    .x
  8163.    /x
  8164.    =x
  8165.    ?x
  8166.    \x
  8167.    ~x
  8168.    @x
  8169.    ^x
  8170.    x \ i
  8171.    x @ y
  8172.    i ^ j
  8173.    i * j
  8174.    i / j
  8175.    i % j
  8176.    c1 ** c2
  8177.    i + j
  8178.    i - j
  8179.    c1 ++ c2
  8180.    c1 -- c2
  8181.    s1 || s2
  8182.    a1 ||| a2
  8183.    i < j
  8184.    i <= j
  8185.    i = j
  8186.    i >= j
  8187.    i > j
  8188.    i ~= j
  8189.    s1 << s2
  8190.    s1 == s2
  8191.    s1 >>= s2
  8192.    s1 >> s2
  8193.    s1 ~== s2
  8194.    x === y
  8195.    x ~=== y
  8196.    x | y
  8197.    i to j
  8198.    i to j by k
  8199.    x := y
  8200.    x <- y
  8201.    x :=: y
  8202.    x <-> y
  8203.    i +:= j
  8204.    i -:= j
  8205.    i *:= j
  8206. end
  8207.  
  8208. procedure expr3()
  8209.    local i, j, c1, c2, s1, s2, a1, a2, x, y, s
  8210.  
  8211.    i /:= j
  8212.    i %:= j
  8213.    i ^:= j
  8214.    i <:= j
  8215.    i <=:= j
  8216.    i =:= j
  8217.    i >=:= j
  8218.    i ~=:= j
  8219.    c1 ++:= c2
  8220.    c1 --:= c2
  8221.    c1 **:= c2
  8222.    s1 ||:= s2
  8223.    s1 <<:= s2
  8224.    s1 <<=:= s2
  8225.    s1 ==:= s2
  8226.    s1 >>=:= s2
  8227.    s1 >>:= s2
  8228.    s1 ~==:= s2
  8229.    s1 ?:= s2
  8230.    a1 |||:= a2
  8231.    x ===:= y
  8232.    x ~===:= y
  8233.    x &:= y
  8234.    x @:= y
  8235.    s ? x
  8236.    x & y
  8237.    create x
  8238.    return
  8239.    return x
  8240.    suspend x
  8241.    suspend x do y
  8242.    fail
  8243. end
  8244.  
  8245. procedure expr4()
  8246.    local e1, e2, e, x, i, j, size, s, e3, X_
  8247.  
  8248.    while e1 do break
  8249.    while e1 do break e2
  8250.    while e1 do next
  8251.    case e of {
  8252.      x:   fail
  8253.      (i > j) | 1    :  return
  8254.      }
  8255.    case size(s) of {
  8256.      1:   1
  8257.      default:  fail
  8258.      }
  8259.    if e1 then e2
  8260.    if e1 then e2 else e3
  8261.    repeat e
  8262.    while e1
  8263.    while e1 do e2
  8264.    until e1
  8265.    until e1 do e2
  8266.    every e1
  8267.    every e1 do e2
  8268.    x
  8269.    X_
  8270.    &cset
  8271.    &null
  8272.    "abc"
  8273.    "abc_
  8274.     cde"
  8275.    'abc'
  8276.    'abc_
  8277.     cde'
  8278.    "\n"
  8279.    "^a"
  8280.    "\001"
  8281.    "\x01"
  8282.    1
  8283.    999999
  8284.    36ra1
  8285.    3.5
  8286.    2.5e4
  8287.    4e-10
  8288. end
  8289.  
  8290. procedure expr5(a,b,c[])
  8291. end
  8292. ##########
  8293. queens.icn
  8294. ############################################################################
  8295. #
  8296. #    Name:    queens.icn
  8297. #
  8298. #    Title:    Generate solutions to the n-queens problem
  8299. #
  8300. #    Author:    Stephen B. Wampler
  8301. #
  8302. #    Date:    June 10, 1988
  8303. #
  8304. ############################################################################
  8305. #  
  8306. #     This program displays the solutions to the non-attacking n-
  8307. #  queens problem: the ways in which n queens can be placed on an
  8308. #  n-by-n chessboard so that no queen can attack another. A positive
  8309. #  integer can be given as a command line argument to specify the
  8310. #  number of queens. For example,
  8311. #  
  8312. #          iconx queens -n8
  8313. #  
  8314. #  displays the solutions for 8 queens on an 8-by-8 chessboard.  The
  8315. #  default value in the absence of an argument is 6.  One solution
  8316. #  for six queens is:
  8317. #  
  8318. #         -------------------------
  8319. #         |   | Q |   |   |   |   |
  8320. #         -------------------------
  8321. #         |   |   |   | Q |   |   |
  8322. #         -------------------------
  8323. #         |   |   |   |   |   | Q |
  8324. #         -------------------------
  8325. #         | Q |   |   |   |   |   |
  8326. #         -------------------------
  8327. #         |   |   | Q |   |   |   |
  8328. #         -------------------------
  8329. #         |   |   |   |   | Q |   |
  8330. #         -------------------------
  8331. #  
  8332. #  Comments: There are many approaches to programming solutions to
  8333. #  the n-queens problem.  This program is worth reading for
  8334. #  its programming techniques.
  8335. #  
  8336. ############################################################################
  8337. #
  8338. #  Links: options
  8339. #
  8340. ############################################################################
  8341.  
  8342. link options
  8343.  
  8344. global n, solution
  8345.  
  8346. procedure main(args)
  8347.    local i, opts
  8348.  
  8349.    opts := options(args,"n+")
  8350.    n := \opts["n"] | 6
  8351.    if n <= 0 then stop("-n needs a positive numeric parameter")
  8352.  
  8353.    solution := list(n)        # ... and a list of column solutions
  8354.    write(n,"-Queens:")
  8355.    every q(1)            # start by placing queen in first column
  8356. end
  8357.  
  8358. # q(c) - place a queen in column c.
  8359. #
  8360. procedure q(c)
  8361.    local r
  8362.    static up, down, rows
  8363.    initial {
  8364.       up := list(2*n-1,0)
  8365.       down := list(2*n-1,0)
  8366.       rows := list(n,0)
  8367.       }
  8368.    every 0 = rows[r := 1 to n] = up[n+r-c] = down[r+c-1] &
  8369.       rows[r] <- up[n+r-c] <- down[r+c-1] <- 1        do {
  8370.          solution[c] := r    # record placement.
  8371.          if c = n then show()
  8372.          else q(c + 1)        # try to place next queen.
  8373.          }
  8374. end
  8375.  
  8376. # show the solution on a chess board.
  8377. #
  8378. procedure show()
  8379.    static count, line, border
  8380.    initial {
  8381.       count := 0
  8382.       line := repl("|   ",n) || "|"
  8383.       border := repl("----",n) || "-"
  8384.       }
  8385.    write("solution: ", count+:=1)
  8386.    write("  ", border)
  8387.    every line[4*(!solution - 1) + 3] <- "Q" do {
  8388.       write("  ", line)
  8389.       write("  ", border)
  8390.       }
  8391.    write()
  8392. end
  8393. ##########
  8394. recgen.icn
  8395. ############################################################################
  8396. #
  8397. #    Name:    recgen.icn
  8398. #
  8399. #    Title:    Generate recognizer for sentences in a context-free language
  8400. #
  8401. #    Author:    Ralph E. Griswold
  8402. #
  8403. #    Date:    June 10, 1988
  8404. #
  8405. ############################################################################
  8406. #
  8407. #     This program reads a context-free grammar and produces an Icon
  8408. #  program that is a recognizer for the corresponding language.
  8409. #
  8410. #     Nonterminal symbols are represented by uppercase letters. Vertical
  8411. #  bars separate alternatives.  All other characters are considered to
  8412. #  be terminal symbols.  The nonterminal symbol on the last line is
  8413. #  taken to be the goal.
  8414. #
  8415. #     An example is:
  8416. #
  8417. #    X::=T|T+X
  8418. #    T::=E|E*T
  8419. #    E::=x|y|z|(X)
  8420. #
  8421. #  Limitations:
  8422. #
  8423. #     Left recursion in the grammar may cause the recognizer to loop.
  8424. #  There is no check that all nonterminal symbols that are referenced
  8425. #  are defined.
  8426. #
  8427. #  Reference:
  8428. #
  8429. #     The Icon Programming Language, Ralph E. and Madge T. Griswold,
  8430. #  Prentice-Hall, 1983. pp. 161-165.
  8431. #
  8432. ############################################################################
  8433.  
  8434. global goal
  8435.  
  8436. procedure main()
  8437.    local line, sym
  8438.  
  8439.    while line := read() do define(line)
  8440.    write("\nprocedure main()")
  8441.    write("   while line := read() do {")
  8442.    write("      writes(image(line))")
  8443.    write("      if line ? (",goal,"() & pos(0)) then _
  8444.       write(\": accepted\")\n      else write(\": rejected\")")
  8445.    write("      }")
  8446.    write("end")
  8447. end
  8448.  
  8449. procedure expand(s,x)
  8450.    local s1, sym
  8451.  
  8452.    s1 := ""
  8453.    s ? while sym := move(1) do
  8454.       if any(&ucase,sym) then s1 ||:= sym || "() || "
  8455.       else s1 ||:= "=\"" || sym || "\" || "
  8456.    return s1[1:-4]
  8457. end
  8458.  
  8459. procedure define(line)
  8460.    line ? (
  8461.       write("\nprocedure ",goal := move(1),"()"),
  8462.       ="::=",
  8463.       write("   suspend {"),
  8464.       (every write("      ",prodlist())) | "",
  8465.       write("      }"),
  8466.       write("end")
  8467.       )
  8468. end
  8469.  
  8470. procedure prodlist()
  8471.    local p
  8472.    while p := expand(tab(many(~'|')),"=") do {
  8473.       move(1) | return "(" || p || ")"  # last alternative
  8474.       suspend "(" || p || ") |"
  8475.       }
  8476. end
  8477.  
  8478. ##########
  8479. roffcmds.icn
  8480. ############################################################################
  8481. #
  8482. #    Name:    roffcmds.icn
  8483. #
  8484. #    Title:    List commands and macros in a roff document
  8485. #
  8486. #    Author:    Ralph E. Griswold
  8487. #
  8488. #    Date:    June 10, 1988
  8489. #
  8490. ############################################################################
  8491. #  
  8492. #     This progam processes standard input and writes a tabulation of
  8493. #  nroff/troff commands and defined strings to standard output.
  8494. #  
  8495. #  Limitations:
  8496. #  
  8497. #     This program only recognizes commands that appear at the beginning of
  8498. #  lines and does not attempt to unravel conditional constructions.
  8499. #  Similarly, defined strings buried in disguised form in definitions are
  8500. #  not recognized.
  8501. #  
  8502. #  Reference:
  8503. #  
  8504. #     Nroff/Troff User's Manual, Joseph F. Ossana, Bell Laboratories,
  8505. #  Murray Hill, New Jersey. October 11, 1976.
  8506. #  
  8507. ############################################################################
  8508.  
  8509. procedure main()
  8510.    local line, con, mac, y, nonpuncs, i, inname, infile, outname, outfile
  8511.  
  8512.    nonpuncs := ~'. \t\\'
  8513.  
  8514.    con := table(0)
  8515.    mac := table(0)
  8516.    while line := read() do {
  8517.       line ? if tab(any('.\'')) then
  8518.          con[tab(any(nonpuncs)) || (tab(upto(' ') | 0))] +:= 1
  8519.       line ? while tab((i := find("\\")) + 1) do {
  8520.       case move(1) of {
  8521.       "(":   move(2)
  8522.       "*" | "f" | "n":  if ="(" then move(2) else move(1)
  8523.       }
  8524.       mac[&subject[i:&pos]] +:= 1
  8525.       }
  8526.    }
  8527.    con := sort(con,3)
  8528.    write(,"Commands:\n")
  8529.    while write(,get(con),"\t",get(con))
  8530.    mac := sort(mac,3)
  8531.    write(,"\nControls:\n")
  8532.    while write(,get(mac),"\t",get(mac))
  8533.  
  8534. end
  8535. ##########
  8536. rsg.icn
  8537. ############################################################################
  8538. #
  8539. #    Name:    rsg.icn
  8540. #
  8541. #    Title:    Generate randomly selected sentences from a grammar
  8542. #
  8543. #    Author:    Ralph E. Griswold
  8544. #
  8545. #    Date:    June 10, 1988
  8546. #
  8547. ############################################################################
  8548. #  
  8549. #     This program generates randomly selected strings (``sen-
  8550. #  tences'') from a grammar specified by the user.  Grammars are
  8551. #  basically context-free and resemble BNF in form, although there
  8552. #  are a number of extensions.
  8553. #  
  8554. #     The program works interactively, allowing the user to build,
  8555. #  test, modify, and save grammars. Input to rsg consists of various
  8556. #  kinds of specifications, which can be intermixed:
  8557. #  
  8558. #     Productions define nonterminal symbols in a syntax similar to
  8559. #  the rewriting rules of BNF with various alternatives consisting
  8560. #  of the concatenation of nonterminal and terminal symbols.  Gen-
  8561. #  eration specifications cause the generation of a specified number
  8562. #  of sentences from the language defined by a given nonterminal
  8563. #  symbol.  Grammar output specifications cause the definition of a
  8564. #  specified nonterminal or the entire current grammar to be written
  8565. #  to a given file.  Source specifications cause subsequent input to
  8566. #  be read from a specified file.
  8567. #  
  8568. #     In addition, any line beginning with # is considered to be a
  8569. #  comment, while any line beginning with = causes the rest of that
  8570. #  line to be used subsequently as a prompt to the user whenever rsg
  8571. #  is ready for input (there normally is no prompt). A line consist-
  8572. #  ing of a single = stops prompting.
  8573. #  
  8574. #  Productions: Examples of productions are:
  8575. #  
  8576. #          <expr>::=<term>|<term>+<expr>
  8577. #          <term>::=<elem>|<elem>*<term>
  8578. #          <elem>::=x|y|z|(<expr>)
  8579. #  
  8580. #  Productions may occur in any order. The definition for a nonter-
  8581. #  minal symbol can be changed by specifying a new production for
  8582. #  it.
  8583. #  
  8584. #     There are a number of special devices to facilitate the defin-
  8585. #  ition of grammars, including eight predefined, built-in nontermi-
  8586. #  nal symbols:
  8587. #     symbol   definition
  8588. #     <lb>     <
  8589. #     <rb>     >
  8590. #     <vb>     |
  8591. #     <nl>     newline
  8592. #     <>       empty string
  8593. #     <&lcase> any single lowercase letter
  8594. #     <&ucase> any single uppercase letter
  8595. #     <&digit> any single digit
  8596. #  
  8597. #  In addition, if the string between a < and a > begins and ends
  8598. #  with a single quotation mark, it stands for any single character
  8599. #  between the quotation marks. For example,
  8600. #  
  8601. #          <'xyz'>
  8602. #  
  8603. #  is equivalent to
  8604. #  
  8605. #          x|y|z
  8606. #  
  8607. #  Generation Specifications: A generation specification consists of
  8608. #  a nonterminal symbol followed by a nonnegative integer. An exam-
  8609. #  ple is
  8610. #  
  8611. #          <expr>10
  8612. #  
  8613. #  which specifies the generation of 10 <expr>s. If the integer is
  8614. #  omitted, it is assumed to be 1. Generated sentences are written
  8615. #  to standard output.
  8616. #  
  8617. #  Grammar Output Specifications: A grammar output specification
  8618. #  consists of a nonterminal symbol, followed by ->, followed by a
  8619. #  file name. Such a specification causes the current definition of
  8620. #  the nonterminal symbol to be written to the given file. If the
  8621. #  file is omitted, standard output is assumed. If the nonterminal
  8622. #  symbol is omitted, the entire grammar is written out. Thus,
  8623. #  
  8624. #          ->
  8625. #  
  8626. #  causes the entire grammar to be written to standard output.
  8627. #  
  8628. #  Source Specifications: A source specification consists of @ fol-
  8629. #  lowed by a file name.  Subsequent input is read from that file.
  8630. #  When an end of file is encountered, input reverts to the previous
  8631. #  file. Input files can be nested.
  8632. #  
  8633. #  Options: The following options are available:
  8634. #  
  8635. #       -s n Set the seed for random generation to n.  The default
  8636. #            seed is 0.
  8637. #  
  8638. #       -l n Terminate generation if the number of symbols remaining
  8639. #            to be processed exceeds n. The default is limit is 1000.
  8640. #  
  8641. #       -t   Trace the generation of sentences. Trace output goes to
  8642. #            standard error output.
  8643. #  
  8644. #  Diagnostics: Syntactically erroneous input lines are noted but
  8645. #  are otherwise ignored.  Specifications for a file that cannot be
  8646. #  opened are noted and treated as erroneous.
  8647. #  
  8648. #     If an undefined nonterminal symbol is encountered during gen-
  8649. #  eration, an error message that identifies the undefined symbol is
  8650. #  produced, followed by the partial sentence generated to that
  8651. #  point. Exceeding the limit of symbols remaining to be generated
  8652. #  as specified by the -l option is handled similarly.
  8653. #  
  8654. #  Caveats: Generation may fail to terminate because of a loop in
  8655. #  the rewriting rules or, more seriously, because of the progres-
  8656. #  sive accumulation of nonterminal symbols. The latter problem can
  8657. #  be identified by using the -t option and controlled by using the
  8658. #  -l option. The problem often can be circumvented by duplicating
  8659. #  alternatives that lead to fewer rather than more nonterminal sym-
  8660. #  bols. For example, changing
  8661. #  
  8662. #          <term>::=<elem>|<elem>*<term>
  8663. #  
  8664. #  to
  8665. #  
  8666. #          <term>::=<elem>|<elem>|<elem>*<term>
  8667. #  
  8668. #  increases the probability of selecting <elem> from 1/2 to 2/3.
  8669. #  
  8670. #     There are many possible extensions to the program. One of the
  8671. #  most useful would be a way to specify the probability of select-
  8672. #  ing an alternative.
  8673. #  
  8674. ############################################################################
  8675. #
  8676. #  Links: options
  8677. #
  8678. ############################################################################
  8679.  
  8680. link options
  8681.  
  8682. global defs, ifile, in, limit, prompt, tswitch
  8683.  
  8684. record nonterm(name)
  8685. record charset(chars)
  8686.  
  8687. procedure main(args)
  8688.    local line, plist, s, opts
  8689.                     # procedures to try on input lines
  8690.    plist := [define,generate,grammar,source,comment,prompter,error]
  8691.    defs := table()            # table of definitions
  8692.    defs["lb"] := [["<"]]        # built-in definitions
  8693.    defs["rb"] := [[">"]]
  8694.    defs["vb"] := [["|"]]
  8695.    defs["nl"] := [["\n"]]
  8696.    defs[""] := [[""]]
  8697.    defs["&lcase"] := [[charset(&lcase)]]
  8698.    defs["&ucase"] := [[charset(&ucase)]]
  8699.    defs["&digit"] := [[charset(&digits)]]
  8700.  
  8701.    opts := options(args,"tl+s+")
  8702.    limit := \opts["l"] | 1000
  8703.    tswitch := \opts["t"]
  8704.    &random := \opts["s"]
  8705.  
  8706.    ifile := [&input]            # stack of input files
  8707.    prompt := ""
  8708.    while in := pop(ifile) do {        # process all files
  8709.       repeat {
  8710.          if *prompt ~= 0 then writes(prompt)
  8711.          line := read(in) | break
  8712.          while line[-1] == "\\" do line := line[1:-1] || read(in) | break
  8713.          (!plist)(line)
  8714.          }
  8715.       close(in)
  8716.       }
  8717. end
  8718.  
  8719. #  process alternatives
  8720. #
  8721. procedure alts(defn)
  8722.    local alist
  8723.    alist := []
  8724.    defn ? while put(alist,syms(tab(upto('|') | 0))) do move(1) | break
  8725.    return alist
  8726. end
  8727.  
  8728. #  look for comment
  8729. #
  8730. procedure comment(line)
  8731.    if line[1] == "#" then return
  8732. end
  8733.  
  8734. #  look for definition
  8735. #
  8736. procedure define(line)
  8737.    return line ?
  8738.       defs[(="<",tab(find(">::=")))] := (move(4),alts(tab(0)))
  8739. end
  8740.  
  8741. #  define nonterminal
  8742. #
  8743. procedure defnon(sym)
  8744.    local chars, name
  8745.    if sym ? {
  8746.       ="'" &
  8747.       chars := cset(tab(-1)) &
  8748.       ="'"
  8749.       }
  8750.    then return charset(chars)
  8751.    else return nonterm(sym)
  8752. end
  8753.  
  8754. #  note erroneous input line
  8755. #
  8756. procedure error(line)
  8757.    write("*** erroneous line:  ",line)
  8758.    return
  8759. end
  8760.  
  8761. #  generate sentences
  8762. #
  8763. procedure gener(goal)
  8764.    local pending, symbol
  8765.    pending := [nonterm(goal)]
  8766.    while symbol := get(pending) do {
  8767.       if \tswitch then
  8768.          write(&errout,symimage(symbol),listimage(pending))
  8769.       case type(symbol) of {
  8770.          "string":   writes(symbol)
  8771.          "charset":  writes(?symbol.chars)
  8772.          "nonterm":  {
  8773.             pending := ?\defs[symbol.name] ||| pending | {
  8774.                write(&errout,"*** undefined nonterminal:  <",symbol.name,">")
  8775.                break 
  8776.                }
  8777.             if *pending > \limit then {
  8778.                write(&errout,"*** excessive symbols remaining")
  8779.                break 
  8780.                }
  8781.             }
  8782.          }
  8783.       }
  8784.    write()
  8785. end
  8786.  
  8787. #  look for generation specification
  8788. #
  8789. procedure generate(line)
  8790.    local goal, count
  8791.    if line ? {
  8792.       ="<" &
  8793.       goal := tab(upto('>')) \ 1 &
  8794.       move(1) &
  8795.       count := (pos(0) & 1) | integer(tab(0))
  8796.       }
  8797.    then {
  8798.       every 1 to count do
  8799.          gener(goal)
  8800.       return
  8801.       }
  8802.    else fail
  8803. end
  8804.  
  8805. #  get right hand side of production
  8806. #
  8807. procedure getrhs(a)
  8808.    local rhs
  8809.    rhs := ""
  8810.    every rhs ||:= listimage(!a) || "|"
  8811.    return rhs[1:-1]
  8812. end
  8813.  
  8814. #  look for request to write out grammar
  8815. #
  8816. procedure grammar(line)
  8817.    local file, out, name
  8818.    if line ? {
  8819.       name := tab(find("->")) &
  8820.       move(2) &
  8821.       file := tab(0) &
  8822.       out := if *file = 0 then &output else {
  8823.          open(file,"w") | {
  8824.             write(&errout,"*** cannot open ",file)
  8825.             fail
  8826.             }
  8827.          }
  8828.       }
  8829.    then {
  8830.       (*name = 0) | (name[1] == "<" & name[-1] == ">") | fail
  8831.       pwrite(name,out)
  8832.       if *file ~= 0 then close(out)
  8833.       return
  8834.       }
  8835.    else fail
  8836. end
  8837.  
  8838. #  produce image of list of grammar symbols
  8839. #
  8840. procedure listimage(a)
  8841.    local s, x
  8842.    s := ""
  8843.    every x := !a do
  8844.       s ||:= symimage(x)
  8845.    return s
  8846. end
  8847.  
  8848. #  look for new prompt symbol
  8849. #
  8850. procedure prompter(line)
  8851.    if line[1] == "=" then {
  8852.       prompt := line[2:0]
  8853.       return
  8854.       }
  8855. end
  8856.  
  8857. #  write out grammar
  8858. #
  8859. procedure pwrite(name,ofile)
  8860.    local nt, a
  8861.    static builtin
  8862.    initial builtin := ["lb","rb","vb","nl","","&lcase","&ucase","&digit"]
  8863.    if *name = 0 then {
  8864.       a := sort(defs,3)
  8865.       while nt := get(a) do {
  8866.          if nt == !builtin then {
  8867.             get(a)
  8868.             next
  8869.             }
  8870.          write(ofile,"<",nt,">::=",getrhs(get(a)))
  8871.          }
  8872.       }
  8873.    else write(ofile,name,"::=",getrhs(\defs[name[2:-1]])) |
  8874.       write("*** undefined nonterminal:  ",name)
  8875. end
  8876.  
  8877. #  look for file with input
  8878. #
  8879. procedure source(line)
  8880.    local file, new
  8881.  
  8882.    return line ? {
  8883.       if ="@" then {
  8884.          new := open(file := tab(0)) | {
  8885.             write(&errout,"*** cannot open ",file)
  8886.             fail
  8887.             }
  8888.          push(ifile,in) &
  8889.          in := new
  8890.          return
  8891.          }
  8892.       }
  8893. end
  8894.  
  8895. #  produce string image of grammar symbol
  8896. #
  8897. procedure symimage(x)
  8898.    return case type(x) of {
  8899.       "string":   x
  8900.       "nonterm":  "<" || x.name || ">"
  8901.       "charset":  "<'" || x.chars || "'>"
  8902.       }
  8903. end
  8904.  
  8905. #  process the symbols in an alternative
  8906. #
  8907. procedure syms(alt)
  8908.    local slist
  8909.    static nonbrack
  8910.    initial nonbrack := ~'<'
  8911.    slist := []
  8912.    alt ? while put(slist,tab(many(nonbrack)) |
  8913.       defnon(2(="<",tab(upto('>')),move(1))))
  8914.    return slist
  8915. end
  8916. ##########
  8917. ruler.icn
  8918. ############################################################################
  8919. #
  8920. #    Name:    ruler.icn
  8921. #
  8922. #    Title:    Write a character ruler to standard output
  8923. #
  8924. #    Author:    Robert J. Alexander
  8925. #
  8926. #    Date:    December 5, 1989
  8927. #
  8928. ############################################################################
  8929. #
  8930. #  Write a character ruler to standard output.  The first optional
  8931. #  argument is the length of the ruler in characters (default 80).
  8932. #  The second is a number of lines to write, with a line number on
  8933. #  each line.
  8934. #
  8935.  
  8936. procedure main(arg)
  8937.    local length, ruler, lines, i
  8938.  
  8939.    length := "" ~== arg[1] | 80
  8940.    every writes(right(1 to length / 10,10))
  8941.    ruler := right("",length,"----+----|")
  8942.    if lines := arg[2] then {
  8943.       write()
  8944.       every i := 2 to lines do
  8945.      write(i,ruler[*i + 1:0])
  8946.       }
  8947.    else write("\n",ruler)
  8948. end
  8949. ##########
  8950. shuffile.icn
  8951. ############################################################################
  8952. #
  8953. #    Name:    shuffile.icn
  8954. #
  8955. #    Title:    Shuffle lines in a file
  8956. #
  8957. #    Author:    Ralph E. Griswold
  8958. #
  8959. #    Date:    June 10, 1988
  8960. #
  8961. ############################################################################
  8962. #  
  8963. #     This program writes a version of the input file with the lines
  8964. #  shuffled.  For example, the result of shuffling
  8965. #  
  8966. #                   On the Future!-how it tells
  8967. #                   Of the rapture that impells
  8968. #                  To the swinging and the ringing
  8969. #                   Of the bells, bells, bells-
  8970. #                Of the bells, bells, bells, bells,
  8971. #                          Bells, bells, bells-
  8972. #            To the rhyming and the chiming of the bells!
  8973. #  
  8974. #  is
  8975. #  
  8976. #            To the rhyming and the chiming of the bells!
  8977. #                  To the swinging and the ringing
  8978. #                          Bells, bells, bells-
  8979. #                   Of the bells, bells, bells-
  8980. #                   On the Future!-how it tells
  8981. #                Of the bells, bells, bells, bells,
  8982. #                   Of the rapture that impells
  8983. #  
  8984. #  Option: The option -s n sets the seed for random generation to n.
  8985. #  The default seed is 0.
  8986. #  
  8987. #  Limitation:
  8988. #
  8989. #     This program stores the input file in memory and
  8990. #  shuffles pointers to the lines; there must be enough memory
  8991. #  available to store the entire file.
  8992. #  
  8993. ############################################################################
  8994. #
  8995. #  Links: options, shuffle
  8996. #
  8997. ############################################################################
  8998.  
  8999. link options, shuffle
  9000.  
  9001. procedure main(args)
  9002.    local opts, a
  9003.  
  9004.    opts := options(args, "s+")
  9005.    &random := \opts["s"]
  9006.  
  9007.    a := []
  9008.    every put(a,!&input)
  9009.    every write(!shuffle(a))
  9010. end
  9011. ##########
  9012. solit.icn
  9013. ############################################################################
  9014. #
  9015. #    Name:    solit.icn
  9016. #
  9017. #    Title:    Play the game of solitaire
  9018. #
  9019. #    Author:    Jerry Nowlin
  9020. #
  9021. #    Date:    June 10, 1988
  9022. #
  9023. ############################################################################
  9024. #  
  9025. #     This program was inspired by a solitaire game that was written
  9026. #  by Allyn Wade and copyrighted by him in 1985.  His game was
  9027. #  designed for the IBM PC/XT/PCjr with a color or monochrome moni-
  9028. #  tor.
  9029. #  
  9030. #     I didn't follow his design exactly because I didn't want to
  9031. #  restrict myself to a specific machine.  This program has the
  9032. #  correct escape sequences programmed into it to handle several
  9033. #  common terminals and PC's.  It's commented well enough that most
  9034. #  people can modify the source to work for their hardware.
  9035. #  
  9036. #     These variables must be defined with the correct escape
  9037. #  sequences to:
  9038. #  
  9039. #          CLEAR  -  clear the screen
  9040. #          CLREOL -  clear to the end of line
  9041. #          NORMAL -  turn on normal video for foreground characters
  9042. #          RED    -  make the foreground color for characters red
  9043. #          BLACK  -  make the foreground color for characters black
  9044. #  
  9045. #  If there is no way to use red and black, the escape sequences
  9046. #  should at least make RED and BLACK have different video attri-
  9047. #  butes; for example red could have inverse video while black has
  9048. #  normal video.
  9049. #  
  9050. #     There are two other places where the code is device dependent.
  9051. #  One is in the face() procedure.  The characters used to display
  9052. #  the suites of cards can be modified there.  For example, the IBM
  9053. #  PC can display actual card face characters while all other
  9054. #  machines currently use HDSC for hearts, diamonds, spades and
  9055. #  clubs respectively.
  9056. #  
  9057. #     The last, and probably trickiest place is in the movecursor()
  9058. #  procedure.  This procedure must me modified to output the correct
  9059. #  escape sequence to directly position the cursor on the screen.
  9060. #  The comments and 3 examples already in the procedure will help.
  9061. #  
  9062. #     So as not to cast dispersions on Allyn Wade's program, I
  9063. #  incorporated the commands that will let you cheat.  They didn't
  9064. #  exist in his program.  I also incorporated the auto pilot command
  9065. #  that will let the game take over from you at your request and try
  9066. #  to win.  I've run some tests, and the auto pilot can win about
  9067. #  10% of the games it's started from scratch.  Not great but not
  9068. #  too bad.  I can't do much better myself without cheating.  This
  9069. #  program is about as totally commented as you can get so the logic
  9070. #  behind the auto pilot is fairly easy to understand and modify.
  9071. #  It's up to you to make the auto pilot smarter.
  9072. #  
  9073. ############################################################################
  9074. #
  9075. #  Note:
  9076. #
  9077. #     The command-line argument, which defaults to support for the VT100,
  9078. #  determines the screen driver.  For MS-DOS computers, the ANSI.SYS driver
  9079. #  is needed.
  9080. ############################################################################
  9081.  
  9082. global    VERSION, CLEAR, CLREOL, NORMAL, RED, BLACK
  9083.  
  9084. global    whitespace, amode, seed, deck, over, hidden, run, ace
  9085.  
  9086. procedure main(args)
  9087.    local a, p, c, r, s, cnt, cheat, cmd, act, from, dest
  9088.  
  9089.     VERSION := (!args == ("Atari ST" | "hp2621" | "IBM PC" | "vt100"))
  9090.  
  9091.     case VERSION of {
  9092.  
  9093.         "Atari ST": {
  9094.             CLEAR  := "\eE"
  9095.             CLREOL := "\eK"
  9096.             NORMAL := "\eb3"
  9097.             RED    := "\eb1"
  9098.             BLACK  := "\eb2"
  9099.         }
  9100.  
  9101.         "hp2621": {
  9102.             CLEAR  := "\eH\eJ"
  9103.             CLREOL := "\eK"
  9104.             NORMAL := "\e&d@"
  9105.             RED    := "\e&dJ"
  9106.             BLACK  := "\e&d@"
  9107.         }
  9108.  
  9109.         "IBM PC" | "vt100": {
  9110.             CLEAR  := "\e[H\e[2J"
  9111.             CLREOL := "\e[0K"
  9112.             NORMAL := "\e[0m"
  9113.             RED    := "\e[31m"
  9114.             BLACK  := "\e[34m"
  9115.         }
  9116.  
  9117.         default: {    # same as IBM PC and vt100
  9118.             CLEAR  := "\e[H\e[2J"
  9119.             CLREOL := "\e[0K"
  9120.             NORMAL := "\e[0m"
  9121.             RED    := "\e[31m"
  9122.             BLACK  := "\e[34m"
  9123.         }
  9124.     }
  9125.  
  9126.     # white space is blanks or tabs
  9127.     whitespace := ' \t'
  9128.  
  9129.     # clear the auto pilot mode flag
  9130.     amode := 0
  9131.  
  9132.     # if a command line argument started with "seed" use the rest of
  9133.     # the argument for the random number generator seed value
  9134.     if (a := !args)[1:5] == "seed" then seed := integer(a[5:0])
  9135.  
  9136.     # initialize the data structures
  9137.     deck   := shuffle()
  9138.     over   := []
  9139.     hidden := [[],[],[],[],[],[],[]]
  9140.     run    := [[],[],[],[],[],[],[]]
  9141.     ace    := [[],[],[],[]]
  9142.  
  9143.     # lay down the 7 piles of cards
  9144.     every p := 1 to 7 do every c := p to 7 do put(hidden[c],get(deck))
  9145.  
  9146.     # turn over the top of each pile to start a run
  9147.     every r := 1 to 7 do put(run[r],get(hidden[r]))
  9148.  
  9149.     # check for aces in the runs and move them to the ace piles
  9150.     every r := 1 to 7 do while getvalue(run[r][1]) = 1 do {
  9151.         s := getsuite(!run[r])
  9152.         push(ace[s],get(run[r]))
  9153.         put(run[r],get(hidden[r]))
  9154.     }
  9155.  
  9156.     # initialize the command and cheat counts
  9157.     cnt := cheat := 0
  9158.  
  9159.     # clear the screen and display the initial layout
  9160.     writes(CLEAR)
  9161.     display()
  9162.  
  9163.     # if a command line argument was "auto" let the auto pilot take over
  9164.     if !args == "auto" then autopilot()
  9165.  
  9166.     # loop reading commands
  9167.     repeat {
  9168.  
  9169.         # increment the command count
  9170.         cnt +:= 1
  9171.  
  9172.         # prompt for a command
  9173.         movecursor(15,0)
  9174.         writes("cmd:",cnt,"> ",CLREOL)
  9175.  
  9176.         # scan the command line
  9177.         (cmd := read() | exit()) ? {
  9178.  
  9179.             # parse the one character action
  9180.             tab(many(whitespace))
  9181.             act := (move(1) | "")
  9182.             tab(many(whitespace))
  9183.  
  9184.             # switch on the action
  9185.             case act of {
  9186.  
  9187.             # turn on the automatic pilot
  9188.             "a": autopilot()
  9189.  
  9190.             # move a card or run of cards
  9191.             "m": {
  9192.                 from := move(1) | whoops(cmd)
  9193.                 tab(many(whitespace))
  9194.                 dest := move(1) | whoops(cmd)
  9195.  
  9196.                 if not movecard(from,dest) then
  9197.                     whoops(cmd)
  9198.                 else if cardsleft() = 0 then
  9199.                     finish(cheat)
  9200.                                 else &null
  9201.             }
  9202.  
  9203.             # thumb the deck
  9204.             "t" | "": thumb()
  9205.  
  9206.             # print some help
  9207.             "h" | "?": disphelp()
  9208.  
  9209.             # print the rules of the game
  9210.             "r": disprules()
  9211.  
  9212.             # give up without winning
  9213.             "q": break
  9214.  
  9215.             # shuffle the deck (cheat!)
  9216.             "s": {
  9217.                 deck |||:= over
  9218.                 over := []
  9219.                 deck := shuffle(deck)
  9220.                 display(["deck"])
  9221.                 cheat +:= 1
  9222.             }
  9223.  
  9224.             # put hidden cards in the deck (cheat!)
  9225.             "p": {
  9226.                 from := move(1) | whoops(cmd)
  9227.                 if integer(from) &
  9228.                    from >= 2 & from <= 7 &
  9229.                    *hidden[from] > 0 then {
  9230.                     deck |||:= hidden[from]
  9231.                     hidden[from] := []
  9232.                     display(["hide","deck"])
  9233.                     cheat +:= 1
  9234.                 } else {
  9235.                     whoops(cmd)
  9236.                 }
  9237.             }
  9238.  
  9239.             # print the contents of the deck (cheat!)
  9240.             "d": {
  9241.                 movecursor(17,0)
  9242.                 write(*deck + *over," cards in deck:")
  9243.                 every writes(face(deck[*deck to 1 by -1])," ")
  9244.                 every writes(face(!over)," ")
  9245.                 writes("\nHit RETURN")
  9246.                 read()
  9247.                 movecursor(17,0)
  9248.                 every 1 to 4 do write(CLREOL)
  9249.                 cheat +:= 1
  9250.             }
  9251.  
  9252.             # print the contents of a hidden pile (cheat!)
  9253.             "2" | "3" | "4" | "5" | "6" | "7": {
  9254.                 movecursor(17,0)
  9255.                 write(*hidden[act]," cards hidden under run ",
  9256.                     act)
  9257.                 every writes(face(!hidden[act])," ")
  9258.                 writes("\nHit RETURN")
  9259.                 read()
  9260.                 movecursor(17,0)
  9261.                 every 1 to 4 do write(CLREOL)
  9262.                 cheat +:= 1
  9263.             }
  9264.  
  9265.             # they gave an invalid command
  9266.             default: whoops(cmd)
  9267.  
  9268.             } # end of action case
  9269.  
  9270.         } # end of scan line
  9271.  
  9272.     } # end of command loop
  9273.  
  9274.     # a quit command breaks the loop
  9275.     movecursor(16,0)
  9276.     writes(CLREOL,"I see you gave up")
  9277.     if cheat > 0 then
  9278.         write("...even after you cheated ",cheat," times!")
  9279.     else
  9280.         write("...but at least you didn't cheat...congratulations!")
  9281.  
  9282.     exit(1)
  9283.  
  9284. end
  9285.  
  9286. # this procedure moves cards from one place to another
  9287.  
  9288. procedure movecard(from,dest,limitmove)
  9289.  
  9290.     # if from and dest are the same fail
  9291.     if from == dest then fail
  9292.  
  9293.     # move a card from the deck
  9294.     if from == "d" then {
  9295.  
  9296.         # to one of the aces piles
  9297.         if dest == "a" then {
  9298.             return deck2ace()
  9299.  
  9300.         # to one of the 7 run piles
  9301.         } else if integer(dest) & dest >= 1 & dest <= 7 then {
  9302.             return deck2run(dest)
  9303.         }
  9304.  
  9305.     # from one of the 7 run piles
  9306.     } else if integer(from) & from >= 1 & from <= 7 then {
  9307.  
  9308.         # to one of the aces piles
  9309.         if dest == "a" then {
  9310.             return run2ace(from)
  9311.  
  9312.  
  9313.         # to another of the 7 run piles
  9314.         } else if integer(dest) & dest >= 1 & dest <= 7 then {
  9315.             return run2run(from,dest,limitmove)
  9316.         }
  9317.     }
  9318.  
  9319.     # if none of the correct move combinations were found fail
  9320.     fail
  9321.  
  9322. end
  9323.  
  9324. procedure deck2run(dest)
  9325.    local fcard, dcard, s
  9326.  
  9327.     # set fcard to the top of the overturned pile or fail
  9328.     fcard := (over[1] | fail)
  9329.  
  9330.     # set dcard to the low card of the run or to null if there are no
  9331.     # cards in the run
  9332.     dcard := (run[dest][-1] | &null)
  9333.  
  9334.     # check to see if the move is legal
  9335.     if chk2run(fcard,dcard) then {
  9336.  
  9337.         # move the card and update the display
  9338.         put(run[dest],get(over))
  9339.         display(["deck",dest])
  9340.  
  9341.         # while there are aces on the top of the overturned pile
  9342.         # move them to the aces piles
  9343.         while getvalue(over[1]) = 1 do {
  9344.             s := getsuite(over[1])
  9345.             push(ace[s],get(over))
  9346.             display(["deck","ace"])
  9347.         }
  9348.         return
  9349.     }
  9350.  
  9351. end
  9352.  
  9353. procedure deck2ace()
  9354.    local fcard, a, s
  9355.  
  9356.     # set fcard to the top of the overturned pile or fail
  9357.     fcard := (over[1] | fail)
  9358.  
  9359.     # for every ace pile
  9360.     every a := !ace do {
  9361.  
  9362.         # if the top of the ace pile is one less than the from card
  9363.         # they are in the same suit and in sequence
  9364.         if a[-1] + 1 = fcard then {
  9365.  
  9366.             # move the card and update the display
  9367.             put(a,get(over))
  9368.             display(["deck","ace"])
  9369.  
  9370.             # while there are aces on the top of the overturned
  9371.             # pile move them to the aces piles
  9372.             while getvalue(over[1]) = 1 do {
  9373.                 s := getsuite(!over)
  9374.                 push(ace[s],get(over))
  9375.                 display(["deck","ace"])
  9376.             }
  9377.             return
  9378.         }
  9379.     }
  9380.  
  9381. end
  9382.  
  9383. procedure run2ace(from)
  9384.    local fcard, a, s
  9385.  
  9386.     # set fcard to the low card of the run or fail if there are no
  9387.     # cards in the run
  9388.     fcard := (run[from][-1] | fail)
  9389.  
  9390.     # for every ace pile
  9391.     every a := !ace do {
  9392.  
  9393.         # if the top of the ace pile is one less than the from card
  9394.         # they are in the same suit and in sequence
  9395.         if a[-1] + 1 = fcard then {
  9396.  
  9397.             # move the card and update the display
  9398.             put(a,pull(run[from]))
  9399.             display([from,"ace"])
  9400.  
  9401.             # if the from run is now empty and there are hidden
  9402.             # cards to expose
  9403.             if *run[from] = 0 & *hidden[from] > 0 then {
  9404.  
  9405.                 # while there are aces on the top of the
  9406.                 # hidden pile move them to the aces piles
  9407.                 while getvalue(hidden[from][1]) = 1 do {
  9408.                     s := getsuite(hidden[from][1])
  9409.                     push(ace[s],get(hidden[from]))
  9410.                     display(["ace"])
  9411.                 }
  9412.  
  9413.                 # put the top hidden card in the empty run
  9414.                 # and display the hidden counts
  9415.                 put(run[from],get(hidden[from]))
  9416.                 display(["hide"])
  9417.             }
  9418.  
  9419.             # update the from run display
  9420.             display([from])
  9421.             return
  9422.         }
  9423.     }
  9424.  
  9425. end
  9426.  
  9427. procedure run2run(from,dest,limitmove)
  9428.    local fcard, dcard, s
  9429.  
  9430.     # set fcard to the high card of the run or fail if there are no
  9431.     # cards in the run
  9432.     fcard := (run[from][1] | fail)
  9433.  
  9434.     # set dcard to the low card of the run or null if there are no
  9435.     # cards in the run
  9436.     dcard := (run[dest][-1] | &null)
  9437.  
  9438.     # avoid king thrashing in automatic mode (there's no point in
  9439.     # moving a king high run to an empty run if there are no hidden
  9440.     # cards under the king high run to be exposed)
  9441.     if amode > 0 & /dcard & getvalue(fcard) = 13 & *hidden[from] = 0 then
  9442.         fail
  9443.  
  9444.     # avoid wasted movement if the limit move parameter was passed
  9445.     # (there's no point in moving a pile if there are no hidden cards
  9446.     # under it unless you have a king in the deck)
  9447.     if amode > 0 & \limitmove & *hidden[from] = 0 then fail
  9448.  
  9449.     # check to see if the move is legal
  9450.     if chk2run(fcard,dcard) then {
  9451.  
  9452.         # add the from run to the dest run
  9453.         run[dest] |||:= run[from]
  9454.  
  9455.         # empty the from run
  9456.         run[from] := []
  9457.  
  9458.         # display the updated runs
  9459.         display([from,dest])
  9460.  
  9461.         # if there are hidden cards to expose
  9462.         if *hidden[from] > 0 then {
  9463.  
  9464.             # while there are aces on the top of the hidden
  9465.             # pile move them to the aces piles
  9466.             while getvalue(hidden[from][1]) = 1 do {
  9467.                 s := getsuite(hidden[from][1])
  9468.                 push(ace[s],get(hidden[from]))
  9469.                 display(["ace"])
  9470.             }
  9471.  
  9472.             # put the top hidden card in the empty run and
  9473.             # display the hidden counts
  9474.             put(run[from],get(hidden[from]))
  9475.             display(["hide"])
  9476.         }
  9477.  
  9478.         # update the from run display
  9479.         display([from])
  9480.         return
  9481.     }
  9482.  
  9483. end
  9484.  
  9485. procedure chk2run(fcard,dcard)
  9486.  
  9487.     # if dcard is null the from card must be a king or
  9488.     if ( /dcard & (getvalue(fcard) = 13 | fail) ) |
  9489.  
  9490.     # if the value of dcard is one more than fcard and
  9491.        ( getvalue(dcard) - 1 = getvalue(fcard) &
  9492.  
  9493.     # their colors are different they can be moved
  9494.          getcolor(dcard) ~= getcolor(fcard) ) then return
  9495.  
  9496. end
  9497.  
  9498. # this procedure finishes a game where there are no hidden cards left and the
  9499. # deck is empty
  9500.  
  9501. procedure finish(cheat)
  9502.  
  9503.     movecursor(16,0)
  9504.     writes("\007I'll finish for you now\007")
  9505.  
  9506.     # finish moving the runs to the aces piles
  9507.     while movecard(!"7654321","a")
  9508.  
  9509.     movecursor(16,0)
  9510.     writes(CLREOL,"\007You WIN\007")
  9511.  
  9512.     if cheat > 0 then
  9513.         write("...but you cheated ",cheat," times!")
  9514.     else
  9515.         write("...and without cheating...congratulations!")
  9516.  
  9517.     exit(0)
  9518.  
  9519. end
  9520.  
  9521. # this procedure takes over and plays the game for you
  9522.  
  9523. procedure autopilot()
  9524.    local tseq, totdeck
  9525.  
  9526.     movecursor(16,0)
  9527.     writes("Going into automatic mode...")
  9528.  
  9529.     # set auto pilot mode
  9530.     amode := 1
  9531.  
  9532.     # while there are cards that aren't in runs or the aces piles
  9533.     while (cardsleft()) > 0 do {
  9534.  
  9535.         # try to make any run to run plays that will uncover
  9536.         # hidden cards
  9537.         while movecard(!"7654321",!"1234567","hidden")
  9538.  
  9539.         # try for a move that will leave an empty spot
  9540.         if movecard(!"7654321",!"1234567") then next
  9541.  
  9542.         # if there's no overturned card thumb the deck
  9543.         if *over = 0 then thumb()
  9544.  
  9545.         # initialize the thumbed sequence set
  9546.         tseq := set()
  9547.  
  9548.         # try thumbing the deck for a play
  9549.         totdeck := *deck + *over
  9550.         every 1 to totdeck do {
  9551.             if movecard("d",!"1234567a") then break
  9552.             insert(tseq,over[1])
  9553.             thumb()
  9554.         }
  9555.  
  9556.         # if we made a deck to somewhere move continue
  9557.         if totdeck > *deck + *over then next
  9558.  
  9559.         # try for a run to ace play
  9560.         if movecard(!"7654321","a") then next
  9561.  
  9562.         # if we got this far and couldn't play give up
  9563.         break
  9564.     }
  9565.  
  9566.     # position the cursor for the news
  9567.     movecursor(16,28)
  9568.  
  9569.     # if all the cards are in runs or the aces piles
  9570.     if cardsleft() = 0 then {
  9571.  
  9572.         writes("\007YEA...\007")
  9573.  
  9574.         # finish moving the runs to the aces piles
  9575.         while movecard(!"7654321","a")
  9576.  
  9577.         movecursor(16,34)
  9578.         write("I won!!!!!")
  9579.  
  9580.         exit(0)
  9581.  
  9582.     } else {
  9583.  
  9584.         writes("I couldn't win this time")
  9585.  
  9586.         # print the information needed to verify that the
  9587.         # program couldn't win
  9588.         movecursor(17,0)
  9589.         writes(*deck + *over," cards in deck")
  9590.         if *tseq > 0 then {
  9591.             write("...final thumbing sequence:")
  9592.             every writes(" ",face(!tseq))
  9593.         }
  9594.         write()
  9595.  
  9596.         exit(1)
  9597.  
  9598.     }
  9599.  
  9600. end
  9601.  
  9602. # this procedure updates the display
  9603.  
  9604. procedure display(parts)
  9605.    local r, a, h, c, part, l
  9606.  
  9607.     static    long    # a list with the length of each run
  9608.  
  9609.     initial {
  9610.         long := [1,1,1,1,1,1,1]
  9611.     }
  9612.  
  9613.     # if the argument list is empty or contains "all" update all parts
  9614.     # of the screen
  9615.     if /parts | !parts == "all" then {
  9616.         long  := [1,1,1,1,1,1,1]
  9617.         parts := [    "label","hide","ace","deck",
  9618.                 "1","2","3","4","5","6","7" ]
  9619.     }
  9620.  
  9621.     # for every part in the argument list
  9622.     every part := !parts do case part of {
  9623.  
  9624.         # display the run number, aces and deck labels
  9625.         "label" : {
  9626.             every r := 1 to 7 do {
  9627.                 movecursor(1,7+(r-1)*5)
  9628.                 writes(r)
  9629.             }
  9630.             movecursor(1,56)
  9631.             writes("ACES")
  9632.             movecursor(6,56)
  9633.             writes("DECK")
  9634.         }
  9635.  
  9636.         # display the hidden card counts
  9637.         "hide" : {
  9638.             every r := 1 to 7 do {
  9639.                 movecursor(1,9+(r-1)*5)
  9640.                 writes(0 < *hidden[r] | " ")
  9641.             }
  9642.         }
  9643.  
  9644.         # display the aces piles
  9645.         "ace" : {
  9646.             movecursor(3,49)
  9647.             every a := 1 to 4 do
  9648.                 writes(face(ace[a][-1]) | "---","  ")
  9649.         }
  9650.  
  9651.         # display the deck and overturned piles
  9652.         "deck" : {
  9653.             movecursor(8,54)
  9654.             writes((*deck > 0 , " # ") | "   ","  ")
  9655.             writes(face(!over) | "   ","  ")
  9656.         }
  9657.  
  9658.         # display the runs piles
  9659.         "1" | "2" | "3" | "4" | "5" | "6" | "7" : {
  9660.             l := ((long[part] > *run[part]) | long[part])
  9661.             h := ((long[part] < *run[part]) | long[part])
  9662.             l <:= 1
  9663.             every c := l to h do {
  9664.                 movecursor(c+1,7+(part-1)*5)
  9665.                 writes(face(run[part][c]) | "   ")
  9666.             }
  9667.             long[part] := *run[part]
  9668.         }
  9669.     }
  9670.  
  9671.     return
  9672.  
  9673. end
  9674.  
  9675. # this procedure thumbs the deck 3 cards at a time
  9676.  
  9677. procedure thumb()
  9678.    local s
  9679.  
  9680.     # if the deck is all thumbed
  9681.     if *deck = 0 then {
  9682.  
  9683.         # if there are no cards in the overturned pile either return
  9684.         if *over = 0 then return
  9685.  
  9686.         # turn the overturned pile back over
  9687.         while put(deck,pull(over))
  9688.     }
  9689.  
  9690.     # turn over 3 cards or at least what's left
  9691.     every 1 to 3 do if *deck > 0 then push(over,get(deck))
  9692.  
  9693.     display(["deck"])
  9694.  
  9695.     # while there are aces on top of the overturned pile move them to
  9696.     # the aces pile
  9697.     while getvalue(over[1]) = 1 do {
  9698.         s := getsuite(over[1])
  9699.         push(ace[s],get(over))
  9700.         display(["deck","ace"])
  9701.     }
  9702.  
  9703.     # if the overturned pile is empty again and there are still cards
  9704.     # in the deck thumb again (this will only happen if the top three
  9705.     # cards in the deck were aces...not likely but)
  9706.     if *over = 0 & *deck > 0 then thumb()
  9707.  
  9708.     return
  9709.  
  9710. end
  9711.  
  9712. # this procedure shuffles a deck of cards
  9713.  
  9714. procedure shuffle(cards)
  9715.  
  9716.     static    fulldeck    # the default shuffle is a full deck of cards
  9717.  
  9718.     initial {
  9719.         # set up a full deck of cards
  9720.         fulldeck := []
  9721.         every put(fulldeck,1 to 52)
  9722.  
  9723.         # if seed isn't already set use the time to set it
  9724.         if /seed then seed := integer(&clock[1:3] ||
  9725.                           &clock[4:6] ||
  9726.                           &clock[7:0])
  9727.  
  9728.         # seed the random number generator for the first time
  9729.         &random := seed
  9730.     }
  9731.  
  9732.     # if no cards were passed use the full deck
  9733.     /cards := fulldeck
  9734.  
  9735.     # copy the cards (shuffling is destructive)
  9736.     deck := copy(cards)
  9737.  
  9738.     # shuffle the deck
  9739.     every !deck :=: ?deck
  9740.  
  9741.     return deck
  9742.  
  9743. end
  9744.  
  9745. procedure face(card)
  9746.  
  9747.     static    cstr,    # the list of card color escape sequences
  9748.         vstr,    # the list of card value labels
  9749.         sstr    # the list of card suite labels
  9750.  
  9751.     initial {
  9752.         cstr := [RED,BLACK]
  9753.         vstr := ["A",2,3,4,5,6,7,8,9,10,"J","Q","K"]
  9754.         if \VERSION == "IBM PC" then
  9755.             sstr := ["\003","\004","\005","\006"]
  9756.         else
  9757.             sstr := ["H","D","S","C"]
  9758.     }
  9759.  
  9760.     # return a string containing the correct color change escape sequence,
  9761.     # the value and suite labels right justified in 3 characters,
  9762.     # and the back to normal escape sequence
  9763.     return    cstr[getcolor(card)] ||
  9764.         right(vstr[getvalue(card)] || sstr[getsuite(card)],3) ||
  9765.         NORMAL
  9766.  
  9767. end
  9768.  
  9769. # a deck of cards is made up of 4 suites of 13 values; 1-13, 14-26, etc.
  9770.  
  9771. procedure getvalue(card)
  9772.  
  9773.     return (card-1) % 13 + 1
  9774.  
  9775. end
  9776.  
  9777. # each suite of cards is made up of ace - king (1-13)
  9778.  
  9779. procedure getsuite(card)
  9780.  
  9781.     return (card-1) / 13 + 1
  9782.  
  9783. end
  9784.  
  9785. # the first two suites are hearts and diamonds so all cards 1-26 are red
  9786. # and all cards 27-52 are black.
  9787.  
  9788. procedure getcolor(card)
  9789.  
  9790.     return (card-1) / 26 + 1
  9791.  
  9792. end
  9793.  
  9794. # this procedure counts cards that aren't in runs or the aces piles
  9795.  
  9796. procedure cardsleft()
  9797.    local totleft
  9798.  
  9799.     # count the cards left in the deck and the overturned pile
  9800.     totleft := *deck + *over
  9801.  
  9802.     # add in the hidden cards
  9803.     every totleft +:= *!hidden
  9804.  
  9805.     return totleft
  9806.  
  9807. end
  9808.  
  9809. # this procedure implements a device dependent cursor positioning scheme
  9810.  
  9811. procedure movecursor(line,col)
  9812.  
  9813.     if \VERSION == "Atari ST" then
  9814.         writes("\eY",&ascii[33+line],&ascii[33+col])
  9815.  
  9816.     else if \VERSION == "hp2621" then
  9817.         writes("\e&a",col,"c",line,"Y")
  9818.  
  9819.     else
  9820.         writes("\e[",line,";",col,"H")
  9821.  
  9822. end
  9823.  
  9824. # all invalid commands call this procedure
  9825.  
  9826. procedure whoops(cmd)
  9827.    local i, j
  9828.  
  9829.     movecursor(15,0)
  9830.     writes("\007Invalid Command: '",cmd,"'\007")
  9831.  
  9832.     # this delay loop can be diddled for different machines
  9833.     every i := 1 to 500 do j := i
  9834.  
  9835.     movecursor(15,0)
  9836.     writes("\007",CLREOL,"\007")
  9837.  
  9838.     return
  9839.  
  9840. end
  9841.  
  9842. # display the help message
  9843.  
  9844. procedure disphelp()
  9845.  
  9846.     static    help
  9847.  
  9848.     initial {
  9849.         help := [
  9850. "Commands: t or RETURN     : thumb the deck 3 cards at a time",
  9851. "          m [d1-7] [1-7a] : move cards or runs",
  9852. "          a               : turn on the auto pilot (in case you get stuck)",
  9853. "          s               : shuffle the deck (cheat!)",
  9854. "          p [2-7]         : put a hidden pile into the deck (cheat!)",
  9855. "          d               : print the cards in the deck (cheat!)",
  9856. "          [2-7]           : print the cards in a hidden pile (cheat!)",
  9857. "          h or ?          : print this command summary",
  9858. "          r               : print the rules of the game",
  9859. "          q               : quit",
  9860. "",
  9861. "Moving:   1-7, 'd', or 'a' select the source and destination for a move. ",
  9862. "          Valid moves are from a run to a run, from the deck to a run,",
  9863. "          from a run to an ace pile, and from the deck to an ace pile.",
  9864. "",
  9865. "Cheating: Commands that allow cheating are available but they will count",
  9866. "          against you in your next life!"
  9867.         ]
  9868.     }
  9869.  
  9870.     writes(CLEAR)
  9871.     every write(!help)
  9872.     writes("Hit RETURN")
  9873.     read()
  9874.     writes(CLEAR)
  9875.     display()
  9876.     return
  9877.  
  9878. end
  9879.  
  9880. # display the rules message
  9881.  
  9882. procedure disprules()
  9883.  
  9884.     static    rules
  9885.  
  9886.     initial {
  9887.         rules := [
  9888. "Object:   The object of this game is to get all of the cards in each suit",
  9889. "          in order on the proper ace pile.",
  9890. "                                        ",
  9891. "Rules:    Cards are played on the ace piles in ascending order: A,2,...,K. ",
  9892. "          All aces are automatically placed in the correct aces pile as",
  9893. "          they're found in the deck or in a pile of hidden cards.  Once a",
  9894. "          card is placed in an ace pile it can't be removed.",
  9895. "",
  9896. "          Cards must be played in descending order: K,Q,..,2, on the seven",
  9897. "          runs which are initially dealt.  They must always be played on a",
  9898. "          card of the opposite color.  Runs must always be moved as a",
  9899. "          whole, unless you're moving the lowest card on a run to the",
  9900. "          correct ace pile.",
  9901. "",
  9902. "          Whenever a whole run is moved, the top hidden card is turned",
  9903. "          over, thus becoming the beginning of a new run.  If there are no",
  9904. "          hidden cards left, a space is created which can only be filled by",
  9905. "          a king.",
  9906. "",
  9907. "          The rest of the deck is thumbed 3 cards at a time, until you spot",
  9908. "          a valid move.  Whenever the bottom of the deck is reached, the",
  9909. "          cards are turned over and you can continue thumbing."
  9910.         ]
  9911.     }
  9912.  
  9913.     writes(CLEAR)
  9914.     every write(!rules)
  9915.     writes("Hit RETURN")
  9916.     read()
  9917.     writes(CLEAR)
  9918.     display()
  9919.     return
  9920.  
  9921. end
  9922. ##########
  9923. tablc.icn
  9924. ############################################################################
  9925. #
  9926. #    Name:    tablc.icn
  9927. #
  9928. #    Title:    Tabulate characters in a file
  9929. #
  9930. #    Author:    Ralph E. Griswold
  9931. #
  9932. #    Date:    June 10, 1988
  9933. #
  9934. ############################################################################
  9935. #  
  9936. #     This program tabulates characters and lists each character and
  9937. #  the number of times it occurs. Characters are written using
  9938. #  Icon's escape conventions.  Line termination characters and other
  9939. #  control characters are included in the tabulation.
  9940. #  
  9941. #  Options: The following options are available:
  9942. #  
  9943. #       -a   Write the summary in alphabetical order of the charac-
  9944. #            ters. This is the default.
  9945. #  
  9946. #       -n   Write the summary in numerical order of the counts.
  9947. #  
  9948. #       -u   Write only the characters that occur just once.
  9949. #  
  9950. ############################################################################
  9951. #
  9952. #  Links: options
  9953. #
  9954. ############################################################################
  9955.  
  9956. link options
  9957.  
  9958. procedure main(args)
  9959.    local ccount, unique, order, s, a, pair, rwidth, opts
  9960.    unique := 0                # switch to list unique usage only
  9961.    order := 3                # alphabetical ordering switch
  9962.  
  9963.    opts := options(args,"anu")
  9964.    if \opts["a"] then order := 3
  9965.    if \opts["n"] then order := 4
  9966.    if \opts["u"] then unique := 1
  9967.  
  9968.    ccount := table(0)            # table of characters
  9969.    while ccount[reads()] +:= 1
  9970.    a := sort(ccount,order)
  9971.    if unique = 1 then {
  9972.       while s := get(a) do
  9973.      if get(a) = 1 then write(s)
  9974.       }
  9975.    else {
  9976.       rwidth := 0
  9977.       every rwidth <:= *!a
  9978.       while s := get(a) do
  9979.          write(left(image(s),10),right(get(a),rwidth))
  9980.       }
  9981. end
  9982. ##########
  9983. tablw.icn
  9984. ############################################################################
  9985. #
  9986. #    Name:    tablw.icn
  9987. #
  9988. #    Title:    Tabulate words in a file
  9989. #
  9990. #    Author:    Ralph E. Griswold
  9991. #
  9992. #    Date:    December 27, 1989
  9993. #
  9994. ############################################################################
  9995. #  
  9996. #     This program tabulates words and lists number of times each
  9997. #  word occurs. A word is defined to be a string of consecutive
  9998. #  upper- and lowercase letters with at most one interior occurrence
  9999. #  of a dash or apostrophe.
  10000. #  
  10001. #  Options: The following options are available:
  10002. #  
  10003. #       -a   Write the summary in alphabetical order of the words.
  10004. #            This is the default.
  10005. #  
  10006. #       -i   Ignore case distinctions among letters; uppercase
  10007. #            letters are mapped into to corresponding lowercase
  10008. #            letters on input. The default is to maintain case dis-
  10009. #            tinctions.
  10010. #  
  10011. #       -n   Write the summary in numerical order of the counts.
  10012. #  
  10013. #       -l n Tabulate only words longer than n characters. The
  10014. #            default is to tabulate all words.
  10015. #  
  10016. #       -u   Write only the words that occur just once.
  10017. #  
  10018. ############################################################################
  10019. #
  10020. #  Links: options, usage
  10021. #
  10022. ############################################################################
  10023.  
  10024. link options, usage
  10025.  
  10026. global limit, icase
  10027.  
  10028. procedure main(args)
  10029.    local wcount, unique, order, s, pair, lwidth, rwidth, max, opts, l, i
  10030.  
  10031.    limit := 0                # lower limit on usage to list
  10032.    unique := 0                # switch to list unique usage only
  10033.    order := 3                # alphabetical ordering switch
  10034.  
  10035.    opts := options(args,"ail+nu")
  10036.    if \opts["a"] then order := 3
  10037.    if \opts["n"] then order := 4
  10038.    if \opts["u"] then unique := 1
  10039.    if \opts["i"] then icase := 1
  10040.    l := \opts["l"] | 1
  10041.    if l <= 0 then Usage("-l needs positive parameter")
  10042.  
  10043.    wcount := table(0)            # table of words
  10044.    every wcount[words()] +:= 1
  10045.    wcount := sort(wcount,order)
  10046.    if unique = 1 then {
  10047.       while s := get(wcount) do
  10048.          if get(wcount) = 1 then write(s)
  10049.       }
  10050.    else {
  10051.       max := 0
  10052.       rwidth := 0
  10053.       i := 1
  10054.       while i < *wcount do {
  10055.          max <:= *wcount[i]
  10056.          rwidth <:= *wcount[i +:= 1]
  10057.      }
  10058.       lwidth := max + 3
  10059.       while write(left(get(wcount),lwidth),right(get(wcount),rwidth))
  10060.       }
  10061. end
  10062.  
  10063. #  generate words
  10064. #
  10065. procedure words()
  10066.    local line, word
  10067.    while line := read() do {
  10068.       if \icase then line := map(line)
  10069.       line ? while tab(upto(&letters)) do {
  10070.          word := tab(many(&letters)) || ((tab(any('-\'')) ||
  10071.             tab(many(&letters))) | "")
  10072.          if *word > limit then suspend word
  10073.          }
  10074.       }
  10075. end
  10076. ##########
  10077. textcnt.icn
  10078. ############################################################################
  10079. #
  10080. #    Name:    textcnt.icn
  10081. #
  10082. #    Title:    Tabulate properties of text file
  10083. #
  10084. #    Author:    Ralph E. Griswold
  10085. #
  10086. #    Date:    December 27, 1989
  10087. #
  10088. ############################################################################
  10089. #
  10090. #     This program tabulates the number of characters, "words", and
  10091. #  lines in standard input and gives the maxium and minimum line length.
  10092. #  
  10093. ############################################################################
  10094.  
  10095. procedure main()
  10096.    local chars, words, lines, name, infile, max, min, line
  10097.  
  10098.    chars := words := lines := 0
  10099.    max := 0
  10100.    min := 2 ^ 30            # larger than possible line length
  10101.    
  10102.      while line := read(infile) do {
  10103.         max <:= *line
  10104.         min >:= *line
  10105.         lines +:= 1
  10106.         chars +:= *line + 1
  10107.         line ? while tab(upto(&letters)) do {
  10108.            words +:= 1
  10109.            tab(many(&letters))
  10110.            }
  10111.         }
  10112.    
  10113.      if min = 2 ^ 30 then
  10114.         write("empty file")
  10115.      else {
  10116.         write("number of lines:     ",right(lines,8))
  10117.         write("number of words:     ",right(words,8))
  10118.         write("number of characters:",right(chars,8))
  10119.         write()
  10120.         write("longest line:        ",right(max,8))
  10121.         write("shortest line:       ",right(min,8))
  10122.         }
  10123.  
  10124. end
  10125. ##########
  10126. trim.icn
  10127. ############################################################################
  10128. #
  10129. #    Name:    trim.icn
  10130. #
  10131. #    Title:    Trim lines in a file
  10132. #
  10133. #    Author:    Ralph E. Griswold
  10134. #
  10135. #    Date:    June 10, 1988
  10136. #
  10137. ############################################################################
  10138. #  
  10139. #     This program copies lines from standard input to standard out-
  10140. #  put, truncating the lines at n characters and removing any trail-
  10141. #  ing blanks. The default value for n is 80.  For example,
  10142. #  
  10143. #          trim 70 <grade.txt >grade.fix
  10144. #  
  10145. #  copies grade.txt to grade.fix, with lines longer than 70 charac-
  10146. #  ters truncated to 70 characters and the trailing blanks removed
  10147. #  from all lines.
  10148. #  
  10149. #     The -f option causes all lines to be n characters long by
  10150. #  adding blanks to short lines; otherwise, short lines are left as
  10151. #  is.
  10152. #
  10153. ############################################################################
  10154. #
  10155. #  Links: options
  10156. #
  10157. ############################################################################
  10158.  
  10159. link options
  10160.  
  10161. procedure main(args)
  10162.    local n, pad, line, opts
  10163.  
  10164.    opts := options(args,"f")
  10165.    if \opts["f"] then pad := 1 else pad := 0
  10166.    n := (0 <= integer(args[1])) | 80
  10167.  
  10168.    while line := read() do {
  10169.       line := line[1+:n]
  10170.       line := trim(line)
  10171.       if pad = 1 then line := left(line,n)
  10172.       write(line)
  10173.       }
  10174. end
  10175. ##########
  10176. turing.icn
  10177. ############################################################################
  10178. #
  10179. #    Name:    turing.icn
  10180. #
  10181. #    Title:    Simulate a Turing machine
  10182. #
  10183. #    Author:    Gregg M. Townsend
  10184. #
  10185. #    Date:    June 10, 1988
  10186. #
  10187. ############################################################################
  10188. #
  10189. #     This program simulates the operation of an n-state Turing machine,
  10190. #  tracing all actions.  The machine starts in state 1 with an empty tape.
  10191. #
  10192. #     A description of the Turing machine is read from the file given as a
  10193. #  comand-line argument, or from standard input if none is specified.
  10194. #  Comment lines beginning with '#' are allowed, as are empty lines.
  10195. #
  10196. #     The program states must be numbered from 1 and must appear in order.
  10197. #  Each appears on a single line in this form:
  10198. #
  10199. #      sss.  wdnnn  wdnnn
  10200. #
  10201. #  sss is the state number in decimal.  The wdnnn fields specify the
  10202. #  action to be taken on reading a 0 or 1 respectively:
  10203. #
  10204. #      w   is the digit to write (0 or 1)
  10205. #      d   is the direction to move (L/l/R/r, or H/h to halt)
  10206. #      nnn is the next state number (0 if halting)
  10207. #
  10208. #  Sample input file:
  10209. #
  10210. #      1. 1r2 1l3
  10211. #      2. 1l1 1r2
  10212. #      3. 1l2 1h0
  10213. #
  10214. #     One line is written for each cycle giving the cycle number, current
  10215. #  state, and an image of that portion of the tape that has been visited
  10216. #  so far.  The current position is indicated by reverse video (using
  10217. #  ANSI terminal escape sequences).
  10218. #
  10219. #     Input errors are reported to standard error output and inhibit
  10220. #  execution.
  10221. #
  10222. #     Bugs:
  10223. #
  10224. #     Transitions to nonexistent states are not detected.
  10225. #  Reverse video should be parameterizable or at least optional.
  10226. #  There is no way to limit the number of cycles.
  10227. #  Infinite loops are not detected.  (Left as an excercise... :-)
  10228. #
  10229. #  Reference:
  10230. #
  10231. #     Scientific American, August 1984, pp. 19-23.  A. K. Dewdney's
  10232. #  discussion of "busy beaver" turing machines in his "Computer
  10233. #  Recreations" column motivated this program.  The sample above
  10234. #  is the three-state busy beaver.
  10235. #
  10236. ############################################################################
  10237. #
  10238. #  Links: options
  10239. #
  10240. ############################################################################
  10241.  
  10242. link options
  10243.  
  10244. record action (wrt, mov, nxs)
  10245.  
  10246. global machine, lns, lno, errs
  10247. global cycle, tape, posn, state, video
  10248.  
  10249. procedure main(args)
  10250.    local opts
  10251.  
  10252.    opts := options(args,"v")
  10253.    video := \opts["v"]
  10254.  
  10255.    rdmach(&input)            # read machine description
  10256.    if errs > 0 then stop("[execution suppressed]")
  10257.    lns := **machine            # initialize turing machine
  10258.    tape := "0"
  10259.    posn := 1
  10260.    cycle := 0
  10261.    state := 1
  10262.    while state > 0 do {        # execute
  10263.       dumptape()
  10264.       transit(machine[state][tape[posn]+1])
  10265.       cycle +:= 1
  10266.    }
  10267.    dumptape()
  10268. end
  10269.  
  10270. #  dumptape - display current tape contents on screen
  10271.  
  10272. procedure dumptape()
  10273.    if cycle < 10 then writes(" ")
  10274.    writes(cycle,". [",right(state,lns),"] ",tape[1:posn])
  10275.    if \video then write("\e[7m",tape[posn],"\e[m",tape[posn + 1:0])
  10276.    else {
  10277.       write(tape[posn:0])
  10278.       write(repl(" ",6 + *state + posn),"^")
  10279.       }
  10280. end
  10281.  
  10282.  
  10283. #  transit (act) - transit to the next state peforming the given action
  10284.  
  10285. procedure transit(act)
  10286.    tape[posn] := act.wrt
  10287.    if act.mov == "R" then {
  10288.       posn +:= 1
  10289.       if posn > *tape then tape ||:= "0"
  10290.       }
  10291.    else if act.mov == "L" then {
  10292.       if posn = 1 then tape := "0" || tape
  10293.       else posn -:= 1
  10294.       }
  10295.    state := act.nxs
  10296.    return
  10297. end
  10298.  
  10299. #  rdmach (f) - read machine description from the given file
  10300.  
  10301. procedure rdmach(f)
  10302.    local nstates, line, a0, a1,n
  10303.  
  10304.    machine := list()
  10305.    nstates := 0
  10306.    lno := 0
  10307.    errs := 0
  10308.    while line := trim(read(f),' \t') do {
  10309.       lno +:= 1
  10310.       if *line > 0 & line[1] ~== "#"
  10311.          then line ? {
  10312.               tab(many(' \t'))
  10313.               n := tab(many(&digits)) | 0
  10314.               if n ~= nstates + 1 then warn("sequence error")
  10315.             nstates := n
  10316.             tab(many('. \t'))
  10317.               a0 := tab(many('01LRHlrh23456789')) | ""
  10318.               tab(many(' \t'))
  10319.               a1 := tab(many('01LRHlrh23456789')) | ""
  10320.               pos(0) | (warn("syntax error") & next)
  10321.               put(machine,[mkact(a0),mkact(a1)])
  10322.             }
  10323.    }
  10324.    lno := "<EOF>"
  10325.    if *machine = errs = 0 then warn("no machine!")
  10326.    return
  10327. end
  10328.  
  10329. #  mkact (a) - construct the action record specified by the given string
  10330.  
  10331. procedure mkact(a)
  10332.    local w, m, n
  10333.  
  10334.    w := a[1] | "9"
  10335.    m := map(a[2],&lcase,&ucase) | "X"
  10336.    (any('01',w) & any('LRH',m)) | warn("syntax error")
  10337.    n := integer(a[3:0]) | (warn("bad nextstate"), 0)
  10338.    return action (w, m, n)
  10339. end
  10340.  
  10341. #  warn (msg) - report an error in the machine description
  10342.  
  10343. procedure warn(msg)
  10344.    write(&errout, "line ", lno, ": ", msg)
  10345.    errs +:= 1
  10346.    return
  10347. end
  10348. ##########
  10349. unique.icn
  10350. ############################################################################
  10351. #
  10352. #    Name:    unique.icn
  10353. #
  10354. #    Title:    Filter out identical adjacent lines
  10355. #
  10356. #    Author:    Anthony Hewitt
  10357. #
  10358. #    Date:    December 22, 1989
  10359. #
  10360. ############################################################################
  10361. #
  10362. #     Filters out identical adjacent lines in a file.
  10363. #
  10364. ############################################################################
  10365.  
  10366. procedure main()
  10367.    local s
  10368.  
  10369.    write(s := !&input)
  10370.    every write(s ~==:= !&input)
  10371. end
  10372. ##########
  10373. unpack.icn
  10374. ############################################################################
  10375. #
  10376. #    Name:    unpack.icn
  10377. #
  10378. #    Title:    Unpackage files
  10379. #
  10380. #    Author:    Ralph E. Griswold
  10381. #
  10382. #    Date:    May 27, 1989
  10383. #
  10384. ############################################################################
  10385. #
  10386. #     This program unpackages files produced by pack.icn.  See that program
  10387. #  for information about limitations.
  10388. #
  10389. ############################################################################
  10390. #
  10391. #  See also:  pack.icn
  10392. #
  10393. ############################################################################
  10394.  
  10395. procedure main()
  10396.    local line, out
  10397.    while line := read() do {
  10398.       if line == "##########" then {
  10399.          close(\out)
  10400.          out := open(name := read(),"w") | stop("cannot open ",name)
  10401.          }
  10402.       else write(out,line)
  10403.       }
  10404. end
  10405. ##########
  10406. vnq.icn
  10407. ############################################################################
  10408. #
  10409. #    Name:    vnq.icn
  10410. #
  10411. #    Title:    Display solutions to n-queens problem
  10412. #
  10413. #    Author:    Stephen B. Wampler
  10414. #
  10415. #    Date:    December 12, 1989
  10416. #
  10417. ############################################################################
  10418. #
  10419. #  Links: options
  10420. #
  10421. ############################################################################
  10422.  
  10423. link options
  10424.  
  10425. global n, nthq, solution, goslow, showall, line, border
  10426.  
  10427. procedure main(args)
  10428. local i, opts
  10429.  
  10430.    opts := options(args, "sah")  
  10431.    n := integer(get(args)) | 8    # default is 8 queens
  10432.    if \opts["s"] then goslow := "yes"
  10433.    if \opts["a"] then showall := "yes"
  10434.    if \opts["h"] then helpmesg()
  10435.  
  10436.    line := repl("|   ", n) || "|"
  10437.    border := repl("----", n) || "-"
  10438.    clearscreen()
  10439.    movexy(1, 1)
  10440.    write()
  10441.    write("  ", border)
  10442.    every 1 to n do {
  10443.       write("  ", line)
  10444.       write("  ", border)
  10445.       }
  10446.  
  10447.    nthq := list(n+2)    # need list of queen placement routines
  10448.    solution := list(n)    # ... and a list of column solutions
  10449.  
  10450.    nthq[1] := &main    # 1st queen is main routine.
  10451.    every i := 1 to n do    # 2 to n+1 are real queen placement
  10452.       nthq[i+1] := create q(i)    #    routines, one per column.
  10453.    nthq[n+2] := create show()    # n+2nd queen is display routine.
  10454.  
  10455.    write(n, "-Queens:")
  10456.    @nthq[2]    # start by placing queen in first colm.
  10457.  
  10458.    movexy(1, 2 * n + 5)
  10459. end
  10460.  
  10461. # q(c) - place a queen in column c (this is c+1st routine).
  10462. procedure q(c)
  10463. local r 
  10464. static up, down, rows
  10465.  
  10466.    initial {
  10467.       up := list(2 * n -1, 0)
  10468.       down := list(2 * n -1, 0)
  10469.       rows := list(n, 0)
  10470.       }
  10471.  
  10472.    repeat {
  10473.       every (0 = rows[r := 1 to n] = up[n + r - c] = down[r + c -1] &
  10474.             rows[r] <- up[n + r - c] <- down[r + c -1] <- 1) do {
  10475.          solution[c] := r    # record placement.
  10476.          if \showall then {
  10477.             movexy(4 * (r - 1) + 5, 2 * c + 1)
  10478.             writes("@")
  10479.             }
  10480.          @nthq[c + 2]    # try to place next queen.
  10481.          if \showall then {
  10482.             movexy(4  * (r - 1) + 5, 2 * c + 1)
  10483.             writes(" ")
  10484.             }
  10485.          }
  10486.       @nthq[c]    # tell last queen placer 'try again'
  10487.       }
  10488.  
  10489. end
  10490.  
  10491. # show the solution on a chess board.
  10492.  
  10493. procedure show()
  10494.    local c
  10495.    static count, lastsol
  10496.  
  10497.    initial {
  10498.       count := 0
  10499.       }
  10500.  
  10501.    repeat {
  10502.       if /showall & \lastsol then {
  10503.          every c := 1 to n do {
  10504.             movexy(4 * (lastsol[c] - 1) + 5, 2 * c + 1)
  10505.             writes(" ")
  10506.             }
  10507.          }
  10508.       movexy(1, 1)
  10509.       write("solution: ", right(count +:= 1, 10))
  10510.       if /showall then {
  10511.          every c := 1 to n do {
  10512.             movexy(4 * (solution[c] - 1) + 5, 2 * c + 1)
  10513.             writes("Q")
  10514.             }
  10515.          lastsol := copy(solution)
  10516.          }
  10517.       if \goslow then {
  10518.          movexy(1, 2 * n + 4)
  10519.          writes("Press return to see next solution:")
  10520.          read() | {
  10521.             movexy(1, 2 * n + 5)
  10522.             stop("Aborted.")
  10523.          }
  10524.          movexy(1, 2 * n + 4)
  10525.          clearline()
  10526.          }
  10527.  
  10528.       @nthq[n+1]                          # tell last queen placer to try again
  10529.       }
  10530.  
  10531. end
  10532.  
  10533. procedure helpmesg()
  10534.    write(&errout, "Usage: vnq [-s] [-a] [n]")
  10535.    write(&errout, "    where -s means to stop after each solution, ")
  10536.    write(&errout, "          -a means to show placement of every queen")
  10537.    write(&errout, "              while trying to find a solution")
  10538.    write(&errout, "      and  n is the size of the board (defaults to 8)")
  10539.    stop()
  10540. end
  10541.  
  10542. # Move cursor to x, y
  10543. #
  10544. procedure movexy (x, y)
  10545.    writes("\^[[", y, ";", x, "H")
  10546.    return
  10547. end
  10548.  
  10549. #
  10550. # Clear the text screen
  10551. #
  10552. procedure clearscreen()
  10553.    writes("\^[[2J")
  10554.    return
  10555. end
  10556.  
  10557. #
  10558. # Clear the rest of the line
  10559. #
  10560. procedure clearline()
  10561.    writes("\^[[2K")
  10562.    return
  10563. end
  10564. ##########
  10565. zipsort.icn
  10566. ############################################################################
  10567. #
  10568. #    Name:    zipsort.icn
  10569. #
  10570. #    Title:    Sort mailing labels by ZIP code
  10571. #
  10572. #    Author:    Ralph E. Griswold
  10573. #
  10574. #    Date:    June 10, 1988
  10575. #
  10576. ############################################################################
  10577. #  
  10578. #     This program sorts labels produced by labels in ascending
  10579. #  order of their postal zip codes.
  10580. #  
  10581. #  Option:
  10582. #
  10583. #     The option -d n sets the number of lines per label to n.
  10584. #  The default is 9. This value must agree with the value used to
  10585. #  format the labels.
  10586. #  
  10587. #  Zip Codes:
  10588. #
  10589. #     The zip code must be the last nonblank string at the
  10590. #  end of the label.  It must consist of digits but may have an
  10591. #  embedded dash for extended zip codes.  If a label does not end
  10592. #  with a legal zip code, it is placed after all labels with legal
  10593. #  zip codes.  In such a case, an error messages also is written to
  10594. #  standard error output.
  10595. #  
  10596. ############################################################################
  10597. #
  10598. #  Links: options
  10599. #
  10600. #  See also: labels.icn
  10601. #
  10602. ############################################################################
  10603.  
  10604. link options
  10605.  
  10606. procedure main(args)
  10607.    local t, a, label, zip, y, lsize, opts
  10608.  
  10609.    opts := options(args,"d+")
  10610.    lsize := (0 > integer(opts["d"])) | 9
  10611.  
  10612.    t := table("")
  10613.    repeat {
  10614.       label := ""
  10615.       every 1 to lsize do
  10616.          label ||:= read() || "\n" | break break
  10617.       label ? {
  10618.          while tab(upto(' ')) do tab(many(' '))
  10619.          zip := tab(upto('-') | 0)
  10620.          zip := integer(zip) | write(&errout,"*** illegal zipcode:  ",label)
  10621.          }
  10622.       t[zip] ||:= label
  10623.       }
  10624.  
  10625.    a := sort(t,3)
  10626.    while get(a) do
  10627.       writes(get(a))
  10628.  
  10629. end
  10630.