home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume23 / quranref / part03 < prev    next >
Encoding:
Text File  |  1991-10-19  |  32.2 KB  |  1,117 lines

  1. Newsgroups: comp.sources.misc
  2. From: goer@midway.uchicago.edu (Richard L. Goerwitz)
  3. Subject:  v23i069:  quranref - Holy Qur'an word and passage based retrievals, Part03/08
  4. Message-ID: <1991Oct19.022243.12852@sparky.imd.sterling.com>
  5. X-Md4-Signature: c3c0b2565f4fdb2b1b848a6961d6826c
  6. Date: Sat, 19 Oct 1991 02:22:43 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: goer@midway.uchicago.edu (Richard L. Goerwitz)
  10. Posting-number: Volume 23, Issue 69
  11. Archive-name: quranref/part03
  12. Environment: Icon
  13.  
  14. ---- Cut Here and feed the following to sh ----
  15. #!/bin/sh
  16. # this is quranref.03 (part 3 of a multipart archive)
  17. # do not concatenate these parts, unpack them in order with /bin/sh
  18. # file inbits.icn continued
  19. #
  20. if test ! -r _shar_seq_.tmp; then
  21.     echo 'Please unpack part 1 first!'
  22.     exit 1
  23. fi
  24. (read Scheck
  25.  if test "$Scheck" != 3; then
  26.     echo Please unpack part "$Scheck" next!
  27.     exit 1
  28.  else
  29.     exit 0
  30.  fi
  31. ) < _shar_seq_.tmp || exit 1
  32. if test ! -f _shar_wnt_.tmp; then
  33.     echo 'x - still skipping inbits.icn'
  34. else
  35. echo 'x - continuing file inbits.icn'
  36. sed 's/^X//' << 'SHAR_EOF' >> 'inbits.icn' &&
  37. X    byte_length := 8
  38. X    }
  39. X
  40. X    old_byte_mask := (0 < 2^old_len - 1) | 0
  41. X    old_byte := iand(old_byte, old_byte_mask)
  42. X    i := ishift(old_byte, len-old_len)
  43. X
  44. X    len -:= (len > old_len) | {
  45. X    old_len -:= len
  46. X    return i
  47. X    }
  48. X    
  49. X    while byte := ord(reads(f)) do {
  50. X    i := ior(i, ishift(byte, len-byte_length))
  51. X    len -:= (len > byte_length) | {
  52. X        old_len := byte_length-len
  53. X        old_byte := byte
  54. X        return i
  55. X    }
  56. X    }
  57. X
  58. Xend
  59. SHAR_EOF
  60. echo 'File inbits.icn is complete' &&
  61. true || echo 'restore of inbits.icn failed'
  62. rm -f _shar_wnt_.tmp
  63. fi
  64. # ============= rewrap.icn ==============
  65. if test -f 'rewrap.icn' -a X"$1" != X"-c"; then
  66.     echo 'x - skipping rewrap.icn (File already exists)'
  67.     rm -f _shar_wnt_.tmp
  68. else
  69. > _shar_wnt_.tmp
  70. echo 'x - extracting rewrap.icn (Text)'
  71. sed 's/^X//' << 'SHAR_EOF' > 'rewrap.icn' &&
  72. X############################################################################
  73. X#
  74. X#    Name:     rewrap.icn
  75. X#
  76. X#    Title:     advanced line rewrap utility
  77. X#
  78. X#    Author:     Richard L. Goerwitz
  79. X#
  80. X#    Version: 1.4
  81. X#
  82. X############################################################################
  83. X#
  84. X#  The procedure rewrap(s,i), included in this file, reformats text
  85. X#  fed to it into strings < i in length.  Rewrap utilizes a static
  86. X#  buffer, so it can be called repeatedly with different s arguments,
  87. X#  and still produce homogenous output.  This buffer is flushed by
  88. X#  calling rewrap with a null first argument.  The default for
  89. X#  argument 2 (i) is 70.
  90. X#
  91. X#  Here's a simple example of how rewrap could be used.  The following
  92. X#  program reads the standard input, producing fully rewrapped output.
  93. X#
  94. X#  procedure main()
  95. X#      every write(rewrap(!&input))
  96. X#      write(rewrap())
  97. X#  end
  98. X#
  99. X#  Naturally, in practice you would want to do things like check for in-
  100. X#  dentation or blank lines in order to wrap only on a paragraph-by para-
  101. X#  graph basis, as in
  102. X#
  103. X#  procedure main()
  104. X#      while line := read(&input) do {
  105. X#          if line == "" then {
  106. X#              write("" ~== rewrap())
  107. X#              write(line)
  108. X#          } else {
  109. X#              if match("\t", line) then {
  110. X#                  write(rewrap())
  111. X#                  write(rewrap(line))
  112. X#              } else {
  113. X#                  write(rewrap(line))
  114. X#              }
  115. X#          }
  116. X#      }
  117. X#  end
  118. X#
  119. X#  Fill-prefixes can be implemented simply by prepending them to the
  120. X#  output of rewrap:
  121. X#
  122. X#      i := 70; fill_prefix := " > "
  123. X#      while line := read(input_file) do {
  124. X#          line ?:= (f_bit := tab(many('> ')) | "", tab(0))
  125. X#          write(fill_prefix || f_bit || rewrap(line, i - *fill_prefix))
  126. X#          etc.
  127. X#
  128. X#  Obviously, these examples are fairly simplistic.  Putting them to
  129. X#  actual use would certainly require a few environment-specific
  130. X#  modifications and/or extensions.  Still, I hope they offer some
  131. X#  indication of the kinds of applications rewrap might be used in.
  132. X# 
  133. X#  Note:  If you want leading and trailing tabs removed, map them to
  134. X#  spaces first.  Rewrap only fools with spaces, leaving tabs intact.
  135. X#  This can be changed easily enough, by running its input through the
  136. X#  Icon detab() function.
  137. X#
  138. X############################################################################
  139. X#
  140. X#  See also:  wrap.icn
  141. X#
  142. X############################################################################
  143. X
  144. X
  145. Xprocedure rewrap(s,i)
  146. X
  147. X    local extra_bit, line
  148. X    static old_line
  149. X    initial old_line := ""
  150. X
  151. X    # Default column to wrap on is 70.
  152. X    /i := 70
  153. X    # Flush buffer on null first argument.
  154. X    if /s then {
  155. X    extra_bit := old_line
  156. X    old_line := ""
  157. X    return "" ~== extra_bit
  158. X    }
  159. X
  160. X    # Prepend to s anything that is in the buffer (leftovers from the last s).
  161. X    s ?:= { tab(many(' ')); old_line || trim(tab(0)) }
  162. X
  163. X    # If the line isn't long enough, just add everything to old_line.
  164. X    if *s < i then old_line := s || " " & fail
  165. X
  166. X    s ? {
  167. X
  168. X    # While it is possible to find places to break s, do so.
  169. X    while any(' -',line := EndToFront(i),-1) do {
  170. X        # Clean up and suspend the last piece of s tabbed over.
  171. X        line ?:= (tab(many(' ')), trim(tab(0)))
  172. X            if *&subject - &pos + *line > i
  173. X        then suspend line
  174. X        else {
  175. X        old_line := ""
  176. X        return line || tab(0)
  177. X        }
  178. X    }
  179. X
  180. X    # Keep the extra section of s in a buffer.
  181. X    old_line := tab(0)
  182. X
  183. X    # If the reason the remaining section of s was unrewrapable was
  184. X    # that it was too long, and couldn't be broken up, then just return
  185. X    # the thing as-is.
  186. X    if *old_line > i then {
  187. X        old_line ? {
  188. X        if extra_bit := tab(upto(' -')+1) || (tab(many(' ')) | "")
  189. X        then old_line := tab(0)
  190. X        else extra_bit := old_line & old_line := ""
  191. X        return trim(extra_bit)
  192. X        }
  193. X    }
  194. X    # Otherwise, clean up the buffer for prepending to the next s.
  195. X    else {
  196. X        # If old_line is blank, then don't mess with it.  Otherwise,
  197. X        # add whatever is needed in order to link it with the next s.
  198. X        if old_line ~== "" then {
  199. X        # If old_line ends in a dash, then there's no need to add a
  200. X        # space to it.
  201. X        if old_line[-1] ~== "-"
  202. X        then old_line ||:= " "
  203. X        }
  204. X    }
  205. X    }
  206. X    
  207. Xend
  208. X
  209. X
  210. X
  211. Xprocedure EndToFront(i)
  212. X    # Goes with rewrap(s,i)
  213. X    *&subject+1 - &pos >= i | fail
  214. X    suspend &subject[.&pos:&pos <- &pos+i to &pos by -1]
  215. Xend
  216. SHAR_EOF
  217. true || echo 'restore of rewrap.icn failed'
  218. rm -f _shar_wnt_.tmp
  219. fi
  220. # ============= findre.icn ==============
  221. if test -f 'findre.icn' -a X"$1" != X"-c"; then
  222.     echo 'x - skipping findre.icn (File already exists)'
  223.     rm -f _shar_wnt_.tmp
  224. else
  225. > _shar_wnt_.tmp
  226. echo 'x - extracting findre.icn (Text)'
  227. sed 's/^X//' << 'SHAR_EOF' > 'findre.icn' &&
  228. X########################################################################
  229. X#    
  230. X#    Name:    findre.icn
  231. X#    
  232. X#    Title:    "Find" Regular Expression
  233. X#    
  234. X#    Author:    Richard L. Goerwitz
  235. X#
  236. X#    Version: 1.17
  237. X#
  238. X########################################################################
  239. X#
  240. X#  I place this and any later versions in the public domain - RLG.
  241. X#
  242. X########################################################################
  243. X#
  244. X#  DESCRIPTION:  findre() is like the Icon builtin function find(),
  245. X#  except that it takes, as its first argument, a regular expression
  246. X#  pretty much like the ones the Unix egrep command uses (the few
  247. X#  minor differences are listed below).  Its syntax is the same as
  248. X#  find's (i.e. findre(s1,s2,i,j)), with the exception that a no-
  249. X#  argument invocation wipes out all static structures utilized by
  250. X#  findre, and then forces a garbage collection.
  251. X#
  252. X#  (For those not familiar with regular expressions and the Unix egrep
  253. X#  command: findre() offers a simple and compact wildcard-based search
  254. X#  system.  If you do a lot of searches through text files, or write
  255. X#  programs which do searches based on user input, then findre is a
  256. X#  utility you might want to look over.)
  257. X#
  258. X#  IMPORTANT DIFFERENCES between find and findre:  As noted above,
  259. X#  findre() is just a find() function that takes a regular expression
  260. X#  as its first argument.  One major problem with this setup is that
  261. X#  it leaves the user with no easy way to tab past a matched
  262. X#  substring, as with
  263. X# 
  264. X#    s ? write(tab(find("hello")+5))
  265. X#
  266. X#  In order to remedy this intrinsic deficiency, findre() sets the
  267. X#  global variable __endpoint to the first position after any given
  268. X#  match occurs.  Use this variable with great care, preferably
  269. X#  assigning its value to some other variable immediately after the
  270. X#  match (for example, findre("hello [.?!]*",s) & tmp := __endpoint).
  271. X#  Otherwise, you will certainly run into trouble.  (See the example
  272. X#  below for an illustration of how __endpoint is used).
  273. X#
  274. X#  IMPORTANT DIFFERENCES between egrep and findre:  findre utilizes
  275. X#  the same basic language as egrep.  The only big difference is that
  276. X#  findre uses intrinsic Icon data structures and escaping conven-
  277. X#  tions rather than those of any particular Unix variant.  Be care-
  278. X#  ful!  If you put findre("\(hello\)",s) into your source file,
  279. X#  findre will treat it just like findre("(hello)",s).  If, however,
  280. X#  you enter '\(hello\)' at run-time (via, say, findre(!&input,s)),
  281. X#  what Icon receives will depend on your operating system (most
  282. X#  likely, a trace will show "\\(hello\\)").
  283. X#
  284. X#  BUGS:  Space has essentially been conserved at the expense of time
  285. X#  in the automata produced by findre().  The algorithm, in other
  286. X#  words, will produce the equivalent of a pushdown automaton under
  287. X#  certain circumstances, rather than strive (at the expense of space)
  288. X#  for full determinism.  I tried to make up a nfa -> dfa converter
  289. X#  that would only create that portion of the dfa it needed to accept
  290. X#  or reject a string, but the resulting automaton was actually quite
  291. X#  slow (if anyone can think of a way to do this in Icon, and keep it
  292. X#  small and fast, please let us all know about it).  Note that under
  293. X#  version 8 of Icon, findre takes up negligible storage space, due to
  294. X#  the much improved hashing algorithm.  I have not tested it under
  295. X#  version 7, but I would expect it to use up quite a bit more space
  296. X#  in that environment.
  297. X#
  298. X#  IMPORTANT NOTE:  Findre takes a shortest-possible-match approach
  299. X#  to regular expressions.  In other words, if you look for "a*",
  300. X#  findre will not even bother looking for an "a."  It will just match
  301. X#  the empty string.  Without this feature, findre would perform a bit
  302. X#  more slowly.  The problem with such an approach is that often the
  303. X#  user will want to tab past the longest possible string of matched
  304. X#  characters (say tab((findre("a*|b*"), __endpoint)).  In circumstan-
  305. X#  ces like this, please just use something like:
  306. X#
  307. X#      s ? {
  308. X#          tab(find("a")) &  # or use Arb() from the IPL (patterns.icn)
  309. X#          tab(many('a'))
  310. X#          tab(many('b'))
  311. X#      }
  312. X#
  313. X#  or else use some combination of findre and the above.
  314. X#    
  315. X########################################################################
  316. X#
  317. X#  REGULAR EXPRESSION SYNTAX: Regular expression syntax is complex,
  318. X#  and yet simple.  It is simple in the sense that most of its power
  319. X#  is concentrated in about a dozen easy-to-learn symbols.  It is
  320. X#  complex in the sense that, by combining these symbols with
  321. X#  characters, you can represent very intricate patterns.
  322. X#
  323. X#  I make no pretense here of offering a full explanation of regular
  324. X#  expressions, their usage, and the deeper nuances of their syntax.
  325. X#  As noted above, this should be gleaned from a Unix manual.  For
  326. X#  quick reference, however, I have included a brief summary of all
  327. X#  the special symbols used, accompanied by an explanation of what
  328. X#  they mean, and, in some cases, of how they are used (most of this
  329. X#  is taken from the comments prepended to Jerry Nowlin's Icon-grep
  330. X#  command, as posted a couple of years ago):
  331. X#
  332. X#     ^   -  matches if the following pattern is at the beginning
  333. X#            of a line (i.e. ^# matches lines beginning with "#")
  334. X#     $   -  matches if the preceding pattern is at the end of a line
  335. X#     .   -  matches any single character
  336. X#     +   -  matches from 1 to any number of occurrences of the
  337. X#            previous expression (i.e. a character, or set of paren-
  338. X#            thesized/bracketed characters)
  339. X#     *   -  matches from 0 to any number of occurrences of the previous
  340. X#            expression
  341. X#     \   -  removes the special meaning of any special characters
  342. X#            recognized by this program (i.e if you want to match lines
  343. X#            beginning with a "[", write ^\[, and not ^[)
  344. X#     |   -  matches either the pattern before it, or the one after
  345. X#            it (i.e. abc|cde matches either abc or cde)
  346. X#     []  -  matches any member of the enclosed character set, or,
  347. X#            if ^ is the first character, any nonmember of the
  348. X#            enclosed character set (i.e. [^ab] matches any character
  349. X#         _except_ a and b).
  350. X#     ()  -  used for grouping (e.g. ^(abc|cde)$ matches lines consist-
  351. X#            ing of either "abc" or "cde," while ^abc|cde$ matches
  352. X#            lines either beginning with "abc" or ending in "cde")
  353. X#
  354. X#########################################################################
  355. X#
  356. X#  EXAMPLE program:
  357. X#
  358. X#  procedure main(a)
  359. X#      while line := !&input do {
  360. X#          token_list := tokenize_line(line,a[1])
  361. X#          every write(!token_list)
  362. X#      }
  363. X#  end
  364. X#
  365. X#  procedure tokenize_line(s,sep)
  366. X#      tmp_lst := []
  367. X#      s ? {
  368. X#          while field := tab(findre(sep)|0) &
  369. X#          mark := __endpoint
  370. X#          do {
  371. X#              put(tmp_lst,"" ~== field)
  372. X#              if pos(0) then break
  373. X#              else tab(mark)
  374. X#          }
  375. X#      }
  376. X#      return tmp_lst
  377. X#  end
  378. X#
  379. X#  The above program would be compiled with findre (e.g. "icont
  380. X#  test_prg.icn findre.icn") to produce a single executable which
  381. X#  tokenizes each line of input based on a user-specified delimiter.
  382. X#  Note how __endpoint is set soon after findre() succeeds.  Note
  383. X#  also how empty fields are excluded with "" ~==, etc.  Finally, note
  384. X#  that the temporary list, tmp_lst, is not needed.  It is included
  385. X#  here merely to illustrate one way in which tokens might be stored.
  386. X#
  387. X#  Tokenizing is, of course, only one of many uses one might put
  388. X#  findre to.  It is very helpful in allowing the user to construct
  389. X#  automata at run-time.  If, say, you want to write a program that
  390. X#  searches text files for patterns given by the user, findre would be
  391. X#  a perfect utility to use.  Findre in general permits more compact
  392. X#  expression of patterns than one can obtain using intrinsic Icon
  393. X#  scanning facilities.  Its near complete compatibility with the Unix
  394. X#  regexp library, moreover, makes for greater ease of porting,
  395. X#  especially in cases where Icon is being used to prototype C code.
  396. X#
  397. X#########################################################################
  398. X
  399. X
  400. Xglobal state_table, parends_present, slash_present
  401. Xglobal biggest_nonmeta_str, __endpoint
  402. Xrecord o_a_s(op,arg,state)
  403. X
  404. X
  405. Xprocedure findre(re, s, i, j)
  406. X
  407. X    local p, default_val, x, nonmeta_len, tokenized_re, tmp
  408. X    static FSTN_table, STRING_table
  409. X    initial {
  410. X    FSTN_table := table()
  411. X    STRING_table := table()
  412. X    }
  413. X
  414. X    if /re then {
  415. X    FSTN_table := table()
  416. X    STRING_table := table()
  417. X    collect()  # do it *now*
  418. X    return
  419. X    }
  420. X
  421. X    if /s := &subject
  422. X    then default_val := &pos
  423. X    else default_val := 1
  424. X
  425. X    if \i then {
  426. X    if i < 1 then
  427. X        i := *s + (i+1)
  428. X    }
  429. X    else i := default_val
  430. X    
  431. X    if \j then {
  432. X    if j < 1 then
  433. X        j := *s + (j+1)
  434. X    }
  435. X    else j := *s+1
  436. X
  437. X    if /FSTN_table[re] then {
  438. X    # If we haven't seen this re before, then...
  439. X    if \STRING_table[re] then {
  440. X        # ...if it's in the STRING_table, use plain find()
  441. X        every p := find(STRING_table[re],s,i,j)
  442. X        do { __endpoint := p + *STRING_table[re]; suspend p }
  443. X        fail
  444. X    }
  445. X    else {
  446. X        # However, if it's not in the string table, we have to
  447. X        # tokenize it and check for metacharacters.  If it has
  448. X        # metas, we create an FSTN, and put that into FSTN_table;
  449. X        # otherwise, we just put it into the STRING_table.
  450. X        tokenized_re := tokenize(re)
  451. X        if 0 > !tokenized_re then {
  452. X        # if at least one element is < 0, re has metas
  453. X        MakeFSTN(tokenized_re) | err_out(re,2)
  454. X        # both biggest_nonmeta_str and state_table are global
  455. X        /FSTN_table[re] := [.biggest_nonmeta_str, copy(state_table)]
  456. X        }
  457. X        else {
  458. X        # re has no metas; put the input string into STRING_table
  459. X        # for future reference, and execute find() at once
  460. X        tmp := ""; every tmp ||:= char(!tokenized_re)
  461. X        insert(STRING_table,re,tmp)
  462. X        every p := find(STRING_table[re],s,i,j)
  463. X        do { __endpoint := p + *STRING_table[re]; suspend p }
  464. X        fail
  465. X        }
  466. X    }
  467. X    }
  468. X
  469. X
  470. X    if nonmeta_len := (1 < *FSTN_table[re][1]) then {
  471. X    # If the biggest non-meta string in the original re
  472. X    # was more than 1, then put in a check for it...
  473. X    s[1:j] ? {
  474. X        tab(x := i to j - nonmeta_len) &
  475. X        (find(FSTN_table[re][1]) | fail) \ 1 &
  476. X        (__endpoint := apply_FSTN(&null,FSTN_table[re][2])) &
  477. X        (suspend x)
  478. X    }
  479. X    }
  480. X    else {
  481. X    #...otherwise it's not worth worrying about the biggest nonmeta str
  482. X    s[1:j] ? {
  483. X        tab(x := i to j) &
  484. X        (__endpoint := apply_FSTN(&null,FSTN_table[re][2])) &
  485. X        (suspend x)
  486. X    }
  487. X    }
  488. X
  489. Xend
  490. X
  491. X
  492. X
  493. Xprocedure apply_FSTN(ini,tbl)
  494. X
  495. X    local biggest_pos, POS, tmp, fin
  496. X    static s_tbl
  497. X
  498. X    /ini := 1 & s_tbl := tbl & biggest_pos := 1
  499. X    if ini = 0 then {
  500. X    return &pos
  501. X    }
  502. X    POS := &pos
  503. X    fin := 0
  504. X
  505. X    repeat {
  506. X    if tmp := !s_tbl[ini] &
  507. X        tab(tmp.op(tmp.arg))
  508. X    then {
  509. X        if tmp.state = fin
  510. X        then return &pos
  511. X        else ini := tmp.state
  512. X    }
  513. X    else (&pos := POS, fail)
  514. X    }
  515. X
  516. Xend
  517. X    
  518. X
  519. X
  520. Xprocedure tokenize(s)
  521. X
  522. X    local token_list, chr, tmp, b_loc, next_one, fixed_length_token_list, i
  523. X
  524. X    token_list := list()
  525. X    s ? {
  526. X    tab(many('*+?|'))
  527. X    while chr := move(1) do {
  528. X        if chr == "\\"
  529. X        # it can't be a metacharacter; remove the \ and "put"
  530. X        # the integer value of the next chr into token_list
  531. X        then put(token_list,ord(move(1))) | err_out(s,2,chr)
  532. X        else if any('*+()|?.$^',chr)
  533. X        then {
  534. X        # Yuck!  Egrep compatibility stuff.
  535. X        case chr of {
  536. X            "*"    : {
  537. X            tab(many('*+?'))
  538. X            put(token_list,-ord("*"))
  539. X            }
  540. X            "+"    : {
  541. X            tmp := tab(many('*?+')) | &null
  542. X            if upto('*?',\tmp)
  543. X            then put(token_list,-ord("*"))
  544. X            else put(token_list,-ord("+"))
  545. X            }
  546. X            "?"    : {
  547. X            tmp := tab(many('*?+')) | &null
  548. X            if upto('*+',\tmp)
  549. X            then put(token_list,-ord("*"))
  550. X            else put(token_list,-ord("?"))
  551. X            }
  552. X            "("    : {
  553. X            tab(many('*+?'))
  554. X            put(token_list,-ord("("))
  555. X            }
  556. X            default: {
  557. X            put(token_list,-ord(chr))
  558. X            }
  559. X        }
  560. X        }
  561. X        else {
  562. X        case chr of {
  563. X            # More egrep compatibility stuff.
  564. X            "["    : {
  565. X            b_loc := find("[") | *&subject+1
  566. X            every next_one := find("]",,,b_loc)
  567. X            \next_one ~= &pos | err_out(s,2,chr)
  568. X            put(token_list,-ord(chr))
  569. X            }
  570. X                    "]"    : {
  571. X            if &pos = (\next_one+1)
  572. X            then put(token_list,-ord(chr)) &
  573. X                 next_one := &null
  574. X            else put(token_list,ord(chr))
  575. X            }
  576. X            default: put(token_list,ord(chr))
  577. X        }
  578. X        }
  579. X    }
  580. X    }
  581. X
  582. X    token_list := UnMetaBrackets(token_list)
  583. X
  584. X    fixed_length_token_list := list(*token_list)
  585. X    every i := 1 to *token_list
  586. X    do fixed_length_token_list[i] := token_list[i]
  587. X    return fixed_length_token_list
  588. X
  589. Xend
  590. X
  591. X
  592. X
  593. Xprocedure UnMetaBrackets(l)
  594. X
  595. X    # Since brackets delineate a cset, it doesn't make
  596. X    # any sense to have metacharacters inside of them.
  597. X    # UnMetaBrackets makes sure there are no metacharac-
  598. X    # ters inside of the braces.
  599. X
  600. X    local tmplst, i, Lb, Rb
  601. X
  602. X    tmplst := list(); i := 0
  603. X    Lb := -ord("[")
  604. X    Rb := -ord("]")
  605. X
  606. X    while (i +:= 1) <= *l do {
  607. X    if l[i] = Lb then {
  608. X        put(tmplst,l[i])
  609. X        until l[i +:= 1] = Rb
  610. X        do put(tmplst,abs(l[i]))
  611. X        put(tmplst,l[i])
  612. X    }
  613. X    else put(tmplst,l[i])
  614. X    }
  615. X    return tmplst
  616. X
  617. Xend
  618. X
  619. X
  620. X
  621. Xprocedure MakeFSTN(l,INI,FIN)
  622. X
  623. X    # MakeFSTN recursively descends through the tree structure
  624. X    # implied by the tokenized string, l, recording in (global)
  625. X    # fstn_table a list of operations to be performed, and the
  626. X    # initial and final states which apply to them.
  627. X
  628. X    local i, inter, inter2, tmp, Op, Arg
  629. X    static Lp, Rp, Sl, Lb, Rb, Caret_inside, Dot, Dollar, Caret_outside
  630. X    # global biggest_nonmeta_str, slash_present, parends_present
  631. X    initial {
  632. X    Lp := -ord("("); Rp := -ord(")")
  633. X    Sl := -ord("|")
  634. X    Lb := -ord("["); Rb := -ord("]"); Caret_inside := ord("^")
  635. X    Dot := -ord("."); Dollar := -ord("$"); Caret_outside := -ord("^")
  636. X    }
  637. X
  638. X    /INI := 1 & state_table := table() &
  639. X    NextState("new") & biggest_nonmeta_str := ""
  640. X    /FIN := 0
  641. X
  642. X    # I haven't bothered to test for empty lists everywhere.
  643. X    if *l = 0 then {
  644. X    /state_table[INI] := []
  645. X    put(state_table[INI],o_a_s(zSucceed,&null,FIN))
  646. X    return
  647. X    }
  648. X
  649. X    # HUNT DOWN THE SLASH (ALTERNATION OPERATOR)
  650. X    every i := 1 to *l do {
  651. X    if l[i] = Sl & tab_bal(l,Lp,Rp) = i then {
  652. X        if i = 1 then err_out(l,2,char(abs(l[i]))) else {
  653. X        /slash_present := "yes"
  654. X        inter := NextState()
  655. X        inter2:= NextState()
  656. X        MakeFSTN(l[1:i],inter2,FIN)
  657. X        MakeFSTN(l[i+1:0],inter,FIN)
  658. X        /state_table[INI] := []
  659. X        put(state_table[INI],o_a_s(apply_FSTN,inter2,0))
  660. X        put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  661. X        return
  662. X        }
  663. X    }
  664. X    }
  665. X
  666. X    # HUNT DOWN PARENTHESES
  667. X    if l[1] = Lp then {
  668. X    i := tab_bal(l,Lp,Rp) | err_out(l,2,"(")
  669. X    inter := NextState()
  670. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  671. X        case l[i+1] of {
  672. X        -ord("*")   : {
  673. X            /state_table[INI] := []
  674. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  675. X            MakeFSTN(l[2:i],INI,INI)
  676. X            MakeFSTN(l[i+2:0],inter,FIN)
  677. X            return
  678. X        }
  679. X        -ord("+")   : {
  680. X            inter2 := NextState()
  681. X            /state_table[inter2] := []
  682. X            MakeFSTN(l[2:i],INI,inter2)
  683. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  684. X            MakeFSTN(l[2:i],inter2,inter2)
  685. X            MakeFSTN(l[i+2:0],inter,FIN)
  686. X            return
  687. X        }
  688. X        -ord("?")   : {
  689. X            /state_table[INI] := []
  690. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  691. X            MakeFSTN(l[2:i],INI,inter)
  692. X            MakeFSTN(l[i+2:0],inter,FIN)
  693. X            return
  694. X        }
  695. X        }
  696. X    }
  697. X    else {
  698. X        MakeFSTN(l[2:i],INI,inter)
  699. X        MakeFSTN(l[i+1:0],inter,FIN)
  700. X        return
  701. X    }
  702. X    }
  703. X    else {     # I.E. l[1] NOT = Lp (left parenthesis as -ord("("))
  704. X    every i := 1 to *l do {
  705. X        case l[i] of {
  706. X        Lp     : {
  707. X            inter := NextState()
  708. X            MakeFSTN(l[1:i],INI,inter)
  709. X            /parends_present := "yes"
  710. X            MakeFSTN(l[i:0],inter,FIN)
  711. X            return
  712. X        }
  713. X        Rp     : err_out(l,2,")")
  714. X        }
  715. X    }
  716. X    }
  717. X
  718. X    # NOW, HUNT DOWN BRACKETS
  719. X    if l[1] = Lb then {
  720. X    i := tab_bal(l,Lb,Rb) | err_out(l,2,"[")
  721. X    inter := NextState()
  722. X    tmp := ""; every tmp ||:= char(l[2 to i-1])
  723. X    if Caret_inside = l[2]
  724. X    then tmp := ~cset(Expand(tmp[2:0]))
  725. X    else tmp :=  cset(Expand(tmp))
  726. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  727. X        case l[i+1] of {
  728. X        -ord("*")   : {
  729. X            /state_table[INI] := []
  730. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  731. X            put(state_table[INI],o_a_s(any,tmp,INI))
  732. X            MakeFSTN(l[i+2:0],inter,FIN)
  733. X            return
  734. X        }
  735. X        -ord("+")   : {
  736. X            inter2 := NextState()
  737. X            /state_table[INI] := []
  738. X            put(state_table[INI],o_a_s(any,tmp,inter2))
  739. X            /state_table[inter2] := []
  740. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  741. X            put(state_table[inter2],o_a_s(any,tmp,inter2))
  742. X            MakeFSTN(l[i+2:0],inter,FIN)
  743. X            return
  744. X        }
  745. X        -ord("?")   : {
  746. X            /state_table[INI] := []
  747. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  748. X            put(state_table[INI],o_a_s(any,tmp,inter))
  749. X            MakeFSTN(l[i+2:0],inter,FIN)
  750. X            return
  751. X        }
  752. X        }
  753. X    }
  754. X    else {
  755. X        /state_table[INI] := []
  756. X        put(state_table[INI],o_a_s(any,tmp,inter))
  757. X        MakeFSTN(l[i+1:0],inter,FIN)
  758. X        return
  759. X    }
  760. X    }
  761. X    else {           # I.E. l[1] not = Lb
  762. X    every i := 1 to *l do {
  763. X        case l[i] of {
  764. X        Lb     : {
  765. X            inter := NextState()
  766. X            MakeFSTN(l[1:i],INI,inter)
  767. X            MakeFSTN(l[i:0],inter,FIN)
  768. X            return
  769. X        }
  770. X        Rb     : err_out(l,2,"]")
  771. X        }
  772. X    }
  773. X    }
  774. X
  775. X    # FIND INITIAL SEQUENCES OF POSITIVE INTEGERS, CONCATENATE THEM
  776. X    if i := match_positive_ints(l) then {
  777. X    inter := NextState()
  778. X    tmp := Ints2String(l[1:i])
  779. X    # if a slash has been encountered already, forget optimizing
  780. X        # in this way; if parends are present, too, then forget it,
  781. X        # unless we are at the beginning or end of the input string
  782. X    if  INI = 1 | FIN = 2 | /parends_present &
  783. X        /slash_present & *tmp > *biggest_nonmeta_str
  784. X    then biggest_nonmeta_str := tmp
  785. X    /state_table[INI] := []
  786. X    put(state_table[INI],o_a_s(match,tmp,inter))
  787. X    MakeFSTN(l[i:0],inter,FIN)
  788. X    return
  789. X    }
  790. X
  791. X    # OKAY, CLEAN UP ALL THE JUNK THAT'S LEFT
  792. X    i := 0
  793. X    while (i +:= 1) <= *l do {
  794. X    case l[i] of {
  795. X        Dot          : { Op := any;   Arg := &cset }
  796. X        Dollar       : { Op := pos;   Arg := 0     }
  797. X        Caret_outside: { Op := pos;   Arg := 1     }
  798. X        default      : { Op := match; Arg := char(0 < l[i]) }
  799. X    } | err_out(l,2,char(abs(l[i])))
  800. X    inter := NextState()
  801. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  802. X        case l[i+1] of {
  803. X        -ord("*")   : {
  804. X            /state_table[INI] := []
  805. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  806. X            put(state_table[INI],o_a_s(Op,Arg,INI))
  807. X            MakeFSTN(l[i+2:0],inter,FIN)
  808. X            return
  809. X        }
  810. X        -ord("+")   : {
  811. X            inter2 := NextState()
  812. X            /state_table[INI] := []
  813. X            put(state_table[INI],o_a_s(Op,Arg,inter2))
  814. X            /state_table[inter2] := []
  815. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  816. X            put(state_table[inter2],o_a_s(Op,Arg,inter2))
  817. X            MakeFSTN(l[i+2:0],inter,FIN)
  818. X            return
  819. X        }
  820. X        -ord("?")   : {
  821. X            /state_table[INI] := []
  822. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  823. X            put(state_table[INI],o_a_s(Op,Arg,inter))
  824. X            MakeFSTN(l[i+2:0],inter,FIN)
  825. X            return
  826. X        }
  827. X        }
  828. X    }
  829. X    else {
  830. X        /state_table[INI] := []
  831. X        put(state_table[INI],o_a_s(Op,Arg,inter))
  832. X        MakeFSTN(l[i+1:0],inter,FIN)
  833. X        return
  834. X    }
  835. X    }
  836. X
  837. X    # WE SHOULD NOW BE DONE INSERTING EVERYTHING INTO state_table
  838. X    # IF WE GET TO HERE, WE'VE PARSED INCORRECTLY!
  839. X    err_out(l,4)
  840. X
  841. Xend
  842. X
  843. X
  844. X
  845. Xprocedure NextState(new)
  846. X    static nextstate
  847. X    if \new then nextstate := 1
  848. X    else nextstate +:= 1
  849. X    return nextstate
  850. Xend
  851. X
  852. X
  853. X
  854. Xprocedure err_out(x,i,elem)
  855. X    writes(&errout,"Error number ",i," parsing ",image(x)," at ")
  856. X    if \elem 
  857. X    then write(&errout,image(elem),".")
  858. X    else write(&errout,"(?).")
  859. X    exit(i)
  860. Xend
  861. X
  862. X
  863. X
  864. Xprocedure zSucceed()
  865. X    return .&pos
  866. Xend
  867. X
  868. X
  869. X
  870. Xprocedure Expand(s)
  871. X
  872. X    local s2, c1, c2
  873. X
  874. X    s2 := ""
  875. X    s ? {
  876. X    s2 ||:= ="^"
  877. X    s2 ||:= ="-"
  878. X    while s2 ||:= tab(find("-")-1) do {
  879. X        if (c1 := move(1), ="-",
  880. X        c2 := move(1),
  881. X        c1 << c2)
  882. X        then every s2 ||:= char(ord(c1) to ord(c2))
  883. X        else s2 ||:= 1(move(2), not(pos(0))) | err_out(s,2,"-")
  884. X    }
  885. X    s2 ||:= tab(0)
  886. X    }
  887. X    return s2
  888. X
  889. Xend
  890. X
  891. X
  892. X
  893. Xprocedure tab_bal(l,i1,i2)
  894. X
  895. X    local i, i1_count, i2_count
  896. X
  897. X    i := 0
  898. X    i1_count := 0; i2_count := 0
  899. X    while (i +:= 1) <= *l do {
  900. X    case l[i] of {
  901. X        i1  : i1_count +:= 1
  902. X        i2  : i2_count +:= 1
  903. X    }
  904. X    if i1_count = i2_count
  905. X    then suspend i
  906. X    }
  907. X
  908. Xend
  909. X
  910. X
  911. Xprocedure match_positive_ints(l)
  912. X    
  913. X    # Matches the longest sequence of positive integers in l,
  914. X    # beginning at l[1], which neither contains, nor is fol-
  915. X    # lowed by a negative integer.  Returns the first position
  916. X    # after the match.  Hence, given [55, 55, 55, -42, 55],
  917. X    # match_positive_ints will return 3.  [55, -42] will cause
  918. X    # it to fail rather than return 1 (NOTE WELL!).
  919. X
  920. X    local i
  921. X
  922. X    every i := 1 to *l do {
  923. X    if l[i] < 0
  924. X    then return (3 < i) - 1 | fail
  925. X    }
  926. X    return *l + 1
  927. X
  928. Xend
  929. X
  930. X
  931. Xprocedure Ints2String(l)
  932. X
  933. X    local tmp
  934. X
  935. X    tmp := ""
  936. X    every tmp ||:= char(!l)
  937. X    return tmp
  938. X
  939. Xend
  940. X
  941. X
  942. Xprocedure StripChar(s,s2)
  943. X
  944. X    local tmp
  945. X
  946. X    if find(s2,s) then {
  947. X    tmp := ""
  948. X    s ? {
  949. X        while tmp ||:= tab(find("s2"))
  950. X        do tab(many(cset(s2)))
  951. X        tmp ||:= tab(0)
  952. X    }
  953. X    }
  954. X    return \tmp | s
  955. X
  956. Xend
  957. SHAR_EOF
  958. true || echo 'restore of findre.icn failed'
  959. rm -f _shar_wnt_.tmp
  960. fi
  961. # ============= huffcode.icn ==============
  962. if test -f 'huffcode.icn' -a X"$1" != X"-c"; then
  963.     echo 'x - skipping huffcode.icn (File already exists)'
  964.     rm -f _shar_wnt_.tmp
  965. else
  966. > _shar_wnt_.tmp
  967. echo 'x - extracting huffcode.icn (Text)'
  968. sed 's/^X//' << 'SHAR_EOF' > 'huffcode.icn' &&
  969. X############################################################################
  970. X#
  971. X#    Name:     huffcode.icn
  972. X#
  973. X#    Title:     huffman coding tools
  974. X#
  975. X#    Author:     Richard L. Goerwitz
  976. X#
  977. X#    Version: 1.4
  978. X#
  979. X############################################################################
  980. X#  
  981. X#  An odd assortment of tools that lets me compress text using an
  982. X#  Iconish version of a generic Huffman algorithm.  See block_encode().
  983. X#
  984. X############################################################################
  985. X#
  986. X#  Links: outbits.icn inbits.icn
  987. X#
  988. X#  See also: press.icn
  989. X#
  990. X############################################################################
  991. X
  992. Xrecord node(l,r,n)
  993. Xrecord _N(l,r)
  994. Xrecord leaf(c,n)
  995. Xrecord hcode(c,i,len)
  996. X
  997. X# For debugging purposes.
  998. X# link ximage
  999. X
  1000. Xprocedure count_chars(s, char_tbl)
  1001. X
  1002. X    #
  1003. X    # Count chars in s, placing stats in char_tbl (keys = chars in
  1004. X    # s, values = leaf records, with the counts for each chr in s
  1005. X    # contained in char_tbl[chr].n).
  1006. X    #
  1007. X    local chr
  1008. X    initial {
  1009. X    /char_tbl & stop("count_chars:  need 2 args - 1 string, 2 table")
  1010. X    *char_tbl ~= 0 & stop("count_chars:  start me with an empty table!")
  1011. X    }
  1012. X
  1013. X    s ? {
  1014. X    while chr := move(1) do {
  1015. X        /char_tbl[chr]   := leaf(chr,0)
  1016. X        char_tbl[chr].n +:= 1
  1017. X    }
  1018. X    }
  1019. X
  1020. X#    write(ximage(char_tbl))
  1021. X    return *char_tbl        # for lack of anything better
  1022. X
  1023. Xend
  1024. X
  1025. X
  1026. Xprocedure heap_init(char_tbl)
  1027. X
  1028. X    #
  1029. X    # Create heap data structure out of the table filled out by
  1030. X    # successive calls to count_chars(s,t).  The heap is just a
  1031. X    # list.  Naturally, it's size can be obtained via *heap.
  1032. X    #
  1033. X    local heap
  1034. X
  1035. X    heap := list()
  1036. X    every push(heap, !char_tbl) do {
  1037. X    resettle_heap(heap, 1)
  1038. X#    write(ximage(heap))
  1039. X    }
  1040. X
  1041. X    return heap
  1042. X
  1043. Xend
  1044. X
  1045. X
  1046. Xprocedure resettle_heap(h, k)
  1047. X
  1048. X    #
  1049. X    # Based loosely on Sedgewick (2nd. ed., 1988), p. 160.  Take k-th
  1050. X    # node on the heap, and walk down the heap, switching this node
  1051. X    # along the way with the child whose value is the least AND whose
  1052. X    # value is less than this node's.  Stop when you find no children
  1053. X    # whose value is less than that of the original node.  Elements on
  1054. X    # heap are records of type leaf, with the values contained in the
  1055. X    # "n" field.
  1056. X    #
  1057. X    local j
  1058. X
  1059. X    # While we haven't spilled off the end of the heap (the size of the
  1060. X    # heap is *h; *h / 2 is the biggest k we need to look at)...
  1061. X    while k <= (*h / 2) do {
  1062. X
  1063. X    # ...double k, assign the result to j.
  1064. X    j := k+k
  1065. X
  1066. X    # If we aren't at the end of the heap...
  1067. X    if j < *h then {
  1068. X        # ...check to see which of h[k]'s children is the smallest,
  1069. X        # and make j point to it.
  1070. X        if h[j].n > h[j+1].n then
  1071. X        # h[j] :=: h[j+1]
  1072. X        j +:= 1
  1073. X    }
  1074. X
  1075. X    # If the current parent (h[k]) has a value less than those of its
  1076. X    # children, then break; we're done.
  1077. X    if h[k].n <= h[j].n then break
  1078. X
  1079. X    # Otherwise, switch the parent for the child, and loop around
  1080. X        # again, with k (the pointer to the parent) now pointing to the
  1081. X    # new offset of the element we have been working on.
  1082. X    h[k] :=: h[j]
  1083. X    k := j
  1084. X
  1085. X    }
  1086. X
  1087. X    return k
  1088. X    
  1089. Xend
  1090. X
  1091. X
  1092. Xprocedure heap_2_huffman_tree(h)
  1093. X
  1094. X    #
  1095. X    # Construct the Huffman tree out of heap h.  Find the smallest
  1096. X    # element, pop it off the heap, then reshuffle the heap.  After
  1097. X    # reshuffling, replace the top record on the stack with a node()
  1098. X    # record whose n field equal to the sum of the n fields for the
  1099. X    # element popped off the stack originally, and the one that is
  1100. X    # now about to be replaced.  Link the new node record to the 2
  1101. X    # elements on the heap it is now replacing.  Reshuffle the heap
  1102. X    # again, then repeat.  You're done when the size of the heap is
  1103. SHAR_EOF
  1104. true || echo 'restore of huffcode.icn failed'
  1105. fi
  1106. echo 'End of  part 3'
  1107. echo 'File huffcode.icn is continued in part 4'
  1108. echo 4 > _shar_seq_.tmp
  1109. exit 0
  1110.  
  1111. exit 0 # Just in case...
  1112. -- 
  1113. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1114. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1115. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1116. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1117.