home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume38 / ibpag2 / part04 < prev    next >
Encoding:
Text File  |  1993-07-12  |  53.5 KB  |  1,775 lines

  1. Newsgroups: comp.sources.misc
  2. From: goer@midway.uchicago.edu (Richard L. Goerwitz)
  3. Subject: v38i048:  ibpag2 - Icon-Based Parser Generator, Part04/05
  4. Message-ID: <1993Jul13.044448.17225@sparky.sterling.com>
  5. X-Md4-Signature: f0ed77a9d76435c759a05df8a064b4ab
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: University of Chicago
  8. Date: Tue, 13 Jul 1993 04:44:48 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: goer@midway.uchicago.edu (Richard L. Goerwitz)
  12. Posting-number: Volume 38, Issue 48
  13. Archive-name: ibpag2/part04
  14. Environment: Icon
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then feed it
  18. # into a shell via "sh file" or similar.  To overwrite existing files,
  19. # type "sh file -c".
  20. # Contents:  follow.icn iacc.ibp ibpag2.icn ibutil.icn slritems.icn
  21. # Wrapped by kent@sparky on Sun Jul 11 18:51:51 1993
  22. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  23. echo If this archive is complete, you will see the following message:
  24. echo '          "shar: End of archive 4 (of 5)."'
  25. if test -f 'follow.icn' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'follow.icn'\"
  27. else
  28.   echo shar: Extracting \"'follow.icn'\" \(11190 characters\)
  29.   sed "s/^X//" >'follow.icn' <<'END_OF_FILE'
  30. X############################################################################
  31. X#
  32. X#    Name:     follow.icn
  33. X#
  34. X#    Title:     compute follow sets for grammar
  35. X#
  36. X#    Author:     Richard L. Goerwitz
  37. X#
  38. X#    Version: 1.15
  39. X#
  40. X############################################################################
  41. X#
  42. X#  This file contains FIRST(st, symbol...) and FOLLOW(start_symbol,
  43. X#  st, symbol).  For FIRST(), arg1 is a list of productions.  Arg 2 is
  44. X#  a string (nonterminal) or an integer (terminal).  FIRST may take
  45. X#  more than one symbol argument.  FOLLOW takes a string as its first
  46. X#  argument, a list of productions as its second, and a symbol as its
  47. X#  third.  There is never any need to call FOLLOW with any more than
  48. X#  one symbol.  The return values for FIRST() and FOLLOW() may be
  49. X#  described as follows:
  50. X#
  51. X#  FIRST returns the set of all terminal symbols that begin valid
  52. X#  prefixes of the first symbol argument, or, if this contains
  53. X#  epsilon, of the first symbol -- <epsilon> ++ the set of terminals
  54. X#  beginning valid prefixes of the second symbol, etc....  The first
  55. X#  argument, st, contains the production list over which FIRST is to
  56. X#  be computed.
  57. X#
  58. X#  FOLLOW is similar, except that it accepts only one symbol argument,
  59. X#  and returns the set of nonterminals that begin valid prefixes of
  60. X#  symbols that may follow symbol in the grammar defined by the
  61. X#  productions in st.
  62. X#
  63. X#  Both FIRST() and FOLLOW() are optimized.  When called for the first
  64. X#  time with a specific production list (st), both FIRST() and
  65. X#  FOLLOW() create the necessary data structures to calculate their
  66. X#  respective return values.  Once created, these data structures are
  67. X#  saved, and re-used for subsequent calls with the same st argument.
  68. X#  The implications for the user are two: 1) The first call to FOLLOW
  69. X#  or FIRST for a given production list will take a while to return,
  70. X#  but 2) subsequent calls will return much faster.  Naturally, you
  71. X#  can call both FIRST() and FOLLOW() with various st arguments
  72. X#  throughout the life of a given program.
  73. X#
  74. X############################################################################
  75. X#
  76. X#  Links: none
  77. X#
  78. X############################################################################
  79. X
  80. X
  81. X#
  82. X# FIRST:  list|set x string|integer...   -> set
  83. X#         (st, symbols...)               -> FIRST_set
  84. X#
  85. X#     Where symbols are strings or integers (nonterminal or terminal
  86. X#     symbols in a production in the list or set of productions, st),
  87. X#     and where FIRST_set is a set of integers corresponding to
  88. X#     terminal symbols that begin valid prefixes of symbols[1], or if
  89. X#     that derives epsilon, of symbols[1] -- epsilon ++ symbols[2],
  90. X#     unless that derives epsilon, etc...
  91. X#
  92. Xprocedure FIRST(st, symbols[])
  93. X
  94. X    local   i, result, FIRST_tbl
  95. X    static  FIRST_tbl_tbl
  96. X    initial FIRST_tbl_tbl := table()
  97. X
  98. X    /FIRST_tbl_tbl[st] := make_FIRST_sets(st)
  99. X    FIRST_tbl := FIRST_tbl_tbl[st]
  100. X
  101. X    result := set()
  102. X    i := 0
  103. X    while *symbols >= (i +:= 1) do {
  104. X    /FIRST_tbl[symbols[i]] & iohno(90, image(symbols[i]))
  105. X    if not member(FIRST_tbl[symbols[i]], -2) then {
  106. X        # We're done if no epsilons.
  107. X        result ++:= FIRST_tbl[symbols[i]]
  108. X        break
  109. X    } else {
  110. X        # Remove the epsilon & try the next symbol in p.RHS.
  111. X        result ++:= FIRST_tbl[symbols[i]] -- FIRST_tbl[-2]
  112. X    }
  113. X    }
  114. X    # If we get to here without finding a symbol that doesn't derive
  115. X    # epsilon, then give up and insert <epsilon> into result.
  116. X    if i > *symbols then
  117. X    result ++:= FIRST_tbl[-2]
  118. X
  119. X    return result
  120. X
  121. Xend
  122. X
  123. X
  124. X#
  125. X# FOLLOW: list|set x string|integer -> set
  126. X#         (st,       symbol)        -> FOLLOW_set
  127. X#
  128. Xprocedure FOLLOW(start_symbol, st, symbol)
  129. X
  130. X    static  FOLLOW_tbl_tbl
  131. X    initial FOLLOW_tbl_tbl := table()
  132. X
  133. X    /FOLLOW_tbl_tbl[st] := make_slr_FOLLOW_sets(start_symbol, st) 
  134. X    return FOLLOW_tbl_tbl[st][symbol]
  135. X
  136. Xend
  137. X
  138. X
  139. X#
  140. X#  Below is the procedure make_slr_FOLLOW_sets(start_symbol, st),
  141. X#  which accepts a string, a set, and a table as its arguments and
  142. X#  returns another table.  The first argument must contain the start
  143. X#  symbol for the set (or list) of productions contained in the second
  144. X#  argument.  Returns a table of FOLLOW sets, where keys = symbols and
  145. X#  values = follow sets for those symbols.
  146. X#
  147. X#  The algorithm - somewhat inefficiently implemented here - works out
  148. X#  as follows:
  149. X#
  150. X#      1. Place $ (internal 0) in FOLLOW_tbl[start_symbol].
  151. X#      2. Initialize FOLLOW_tbl[symbol] to { } for every other symbol.
  152. X#      3. For each production A -> aBb do FOLLOW_tbl[B] ++:= FIRST(b) --
  153. X#         FIRST(<epsilon>).
  154. X#      4. For each production A -> aBb where FIRST(b) contains
  155. X#         <epsilon> and for each production A -> aB, do FOLLOW_tbl[B] ++:=
  156. X#         FOLLOW_tbl[A].
  157. X#
  158. X#  Repeat steps 3 and 4 until no FOLLOW set can be expanded, at which
  159. X#  point return the FOLLOW table.
  160. X#
  161. X#  Note that <epsilon> is represented internally by -2.
  162. X#
  163. X
  164. X
  165. X#
  166. X# make_slr_FOLLOW_sets: string x set/list  -> table
  167. X#                       (start_symbol, st) -> FOLLOW_tbl
  168. X#
  169. X#     Where start_symbol is the start symbol for the grammar defined
  170. X#     by the set/list of productions in st, and where FOLLOW_tbl is a
  171. X#     table of follow sets (keys = symbols, values = follow sets for
  172. X#     the symbols).
  173. X#
  174. Xprocedure make_slr_FOLLOW_sets(start_symbol, st)
  175. X
  176. X    local FOLLOW_tbl, k, size, old_size, p, i, j
  177. X
  178. X    FOLLOW_tbl := table()
  179. X    # step 1 above; note that 0 = EOF
  180. X    FOLLOW_tbl[start_symbol] := set([0])
  181. X
  182. X    # step 2
  183. X    every k := (!st).LHS do
  184. X    /FOLLOW_tbl[k] := set()
  185. X
  186. X    # steps 3 and 4
  187. X    size := 0
  188. X    #
  189. X    # When the old size of the FOLLOW sets equals the new size, we are
  190. X    # done because nothing was added to the FOLLOW sets on the last
  191. X    # pass.
  192. X    #
  193. X    while old_size ~===:= size do {
  194. X    size := 0
  195. X    every p := !st do {
  196. X        every i := 1 to *p.RHS-1 do {
  197. X        type(p.RHS[i]) == "string" | next
  198. X        /FOLLOW_tbl[p.RHS[i]] & iohno(90, image(p.RHS[i]))
  199. X        # Go through every RHS symbol until we get a FIRST set
  200. X        # without an epsilon move.
  201. X        every j := i+1 to *p.RHS do {
  202. X            if member(FIRST(st, p.RHS[j]), -2) then {
  203. X            FOLLOW_tbl[p.RHS[i]] ++:=
  204. X                FIRST(st, p.RHS[j]) -- FIRST(st, -2)
  205. X            } else {
  206. X            FOLLOW_tbl[p.RHS[i]] ++:= FIRST(st, p.RHS[j])
  207. X            size +:= *FOLLOW_tbl[p.RHS[i]]
  208. X            break next
  209. X            }
  210. X        }
  211. X        # If we get past "break next" then b in A -> aBb =>*
  212. X        # <epsilon>; add FOLLOW_tbl[A] to FOLLOW_tbl[B].
  213. X        FOLLOW_tbl[p.RHS[i]] ++:= FOLLOW_tbl[p.LHS]
  214. X        size +:= *FOLLOW_tbl[p.RHS[i]]
  215. X        }
  216. X        # Add FOLLOW_tbl[A] to FOLLOW_tbl[B] for the last symbol in the
  217. X        # RHS of every rule.
  218. X        type(p.RHS[*p.RHS]) == "string" | next
  219. X        /FOLLOW_tbl[p.RHS[*p.RHS]] & iohno(90, image(p.RHS[*p.RHS]))
  220. X        FOLLOW_tbl[p.RHS[*p.RHS]] ++:= FOLLOW_tbl[p.LHS]
  221. X        size +:= *FOLLOW_tbl[p.RHS[*p.RHS]]
  222. X    }
  223. X    }
  224. X
  225. X    # Print human-readable version of FOLLOW_tbl if instructed to do so.
  226. X    if \DEBUG then
  227. X    print_follow_sets(FOLLOW_tbl)
  228. X
  229. X    # check for useless nonterminal symbols
  230. X    every k := (!st).LHS do
  231. X    *FOLLOW_tbl[k] = 0 & iohno(91, k)
  232. X
  233. X    return FOLLOW_tbl
  234. X
  235. Xend
  236. X
  237. X
  238. X#
  239. X#  Below is the routine make_FIRST_sets(st), which accepts as its one
  240. X#  argument a list or set of production records, and which returns a
  241. X#  table t, where t's keys are symbols from the grammar defined by the
  242. X#  productions in st, and where the values assocated with each of
  243. X#  these keys is the FIRST set for that key.
  244. X#
  245. X#  Production records are structures where the first two fields, LHS
  246. X#  and RHS, contain the left-hand and right-hand side of each rule in
  247. X#  a given grammar.  The right-hand side is a linked list of integers
  248. X#  (used for terminals) and strings (used for nonterminals). LHS must
  249. X#  contain a string.  Terminals below 1 are reserved.  Currently three
  250. X#  are actually used:
  251. X#
  252. X#      0    EOF
  253. X#      -1   error
  254. X#      -2   epsilon
  255. X#
  256. X#  For a description of the FIRST() construction algorithm, see Alfred
  257. X#  Aho, Ravi Sethi, and Jeffrey D. Ullman _Compilers_ (Reading,
  258. X#  Massachusetts: Addison & Wesley, 1986), section 4.4, page 189.
  259. X#  Their algorithm is not strictly suitable, as is, for use here.  I
  260. X#  thank Dave Schaumann of the University of Arizona at Tuscon for
  261. X#  explaining to me the iterative construction algorithm that in fact
  262. X#  *is* suitable.
  263. X#  
  264. X#  FIRST is computed on an iterative basis as follows:
  265. X#
  266. X#      1. For every terminal symbol a, FIRST(a) = { a }
  267. X#      2. For every non-terminal symbol A, initialize FIRST(A) = { }
  268. X#      3. For every production A -> <epsilon>, add <epsilon> to FIRST(A)
  269. X#      4. For each production of the grammar having the form X -> Y1
  270. X#         Y2 ... Yn, perform the following procedure:
  271. X#          i := 1
  272. X#          while i <= number-of-RHS-symbols do {
  273. X#              if <epsilon> is not in FIRST(Y[i]) then {
  274. X#                  FIRST(X) ++:= FIRST(Y[i])
  275. X#                  break
  276. X#              } else {
  277. X#                  FIRST(X) ++:= FIRST(Y[i]) -- FIRST[<epsilon>]
  278. X#                  i +:= 1
  279. X#              }
  280. X#          }
  281. X#          if i > number-of-RHS-symbols then
  282. X#              # <epsilon> is in FIRST(Y[i])
  283. X#              FIRST(X) ++:= FIRST[epsilon]
  284. X#      5. Repeat step 3 until no new symbols or <epsilon> can be added
  285. X#         to any FIRST set
  286. X#
  287. X
  288. X
  289. X#
  290. X# make_FIRST_sets:  set/list -> table
  291. X#                   st       -> t
  292. X#
  293. X#     Where st is a set or list of production records, and t is a
  294. X#     table of FIRST sets, where the keys = terminal or nonterminal
  295. X#     symbols and the values = sets of terminal symbols.
  296. X#
  297. X#     Epsilon move is -2; terminals are positive integers;
  298. X#     nonterminals are strings.  Error is -1; EOF is 0.
  299. X#
  300. Xprocedure make_FIRST_sets(st)
  301. X
  302. X    local FIRST_tbl, symbol, p, old_size, size, i
  303. X
  304. X    FIRST_tbl    := table()
  305. X    FIRST_tbl[0] := set([0])
  306. X
  307. X    # steps 1, 2, and 3 above
  308. X    every p := !st do {
  309. X    # check for empty RHS (an error)
  310. X    *p.RHS = 0 & iohno(11, production_2_string(p))
  311. X    # step 1
  312. X    every symbol := !p.RHS do {
  313. X        if type(symbol) == "integer"
  314. X        then FIRST_tbl[symbol] := set([symbol])
  315. X    }
  316. X    # step 2
  317. X    /FIRST_tbl[p.LHS] := set() &
  318. X    # step 3
  319. X    if *p.RHS = 1 then {
  320. X        if p.RHS[1] === -2    # -2 is epsilon
  321. X        then insert(FIRST_tbl[p.LHS], -2)
  322. X    }
  323. X    }
  324. X
  325. X    # steps 4 and 5 above
  326. X    size := 0
  327. X    #
  328. X    # When the old size of the FIRST sets equals the new size, we are
  329. X    # done.  As long as they're unequal, set old_size to size and try
  330. X    # to add to the FIRST sets.
  331. X    #
  332. X    while old_size ~===:= size do {
  333. X    size := 0
  334. X    every p := !st do {
  335. X        every i := 1 to *p.RHS do {
  336. X        \FIRST_tbl[p.RHS[i]] | iohno(90, image(p.RHS[i]))
  337. X        if not member(FIRST_tbl[p.RHS[i]], -2) then {
  338. X            # We're done with this pass if no epsilons.
  339. X            FIRST_tbl[p.LHS] ++:= FIRST_tbl[p.RHS[i]]
  340. X            size +:= *FIRST_tbl[p.LHS]
  341. X            break next
  342. X        } else {
  343. X            # Remove the epsilon & try the next symbol in p.RHS.
  344. X            FIRST_tbl[p.LHS] ++:= FIRST_tbl[p.RHS[i]] -- FIRST_tbl[-2]
  345. X        }
  346. X        }
  347. X        # If we get past the every...do structure without
  348. X        # break+next-ing, then we are still finding epsilons.  In
  349. X        # this case, add epsilon to FIRST_tbl[p.LHS].
  350. X        FIRST_tbl[p.LHS] ++:= FIRST_tbl[-2]
  351. X        size +:= *FIRST_tbl[p.LHS]
  352. X    }
  353. X    }
  354. X
  355. X    # Print human-readable version of FIRST_tbl if instructed to do so.
  356. X    if \DEBUG then
  357. X    print_first_sets(FIRST_tbl)
  358. X
  359. X    return FIRST_tbl
  360. X
  361. Xend
  362. END_OF_FILE
  363.   if test 11190 -ne `wc -c <'follow.icn'`; then
  364.     echo shar: \"'follow.icn'\" unpacked with wrong size!
  365.   fi
  366.   # end of 'follow.icn'
  367. fi
  368. if test -f 'iacc.ibp' -a "${1}" != "-c" ; then 
  369.   echo shar: Will not clobber existing file \"'iacc.ibp'\"
  370. else
  371.   echo shar: Extracting \"'iacc.ibp'\" \(10968 characters\)
  372.   sed "s/^X//" >'iacc.ibp' <<'END_OF_FILE'
  373. X############################################################################
  374. X#
  375. X#    Name:     iacc.ibp
  376. X#
  377. X#    Title:     YACC-like front-end for Ibpag2 (experimental)
  378. X#
  379. X#    Author:     Richard L. Goerwitz
  380. X#
  381. X#    Version: 1.5
  382. X#
  383. X############################################################################
  384. X#
  385. X#  Description:
  386. X#
  387. X#      YACC-like Ibpag2 preprocessor (very experimental).  Iacc simply
  388. X#  reads &input (assumed to be a YACC file, but with Icon code in the
  389. X#  action fields), and wites an Ibpag2 file to &output.
  390. X#
  391. X#      Basically, Iacc does six things to the input stream: 1) puts
  392. X#  commas between tokens and symbols in rules, 2) removes superfluous
  393. X#  union and type declarations/tags, 3) inserts "epsilon" into the RHS
  394. X#  of empty rules, 4) turns "$$ = x" into "return x", 5) rewrites
  395. X#  rules so that all actions appear at the end of a production, and 6)
  396. X#  strips all comments.
  397. X#
  398. X#      Although Iacc is really meant for grammars with Icon action
  399. X#  code, Iacc can, in fact, accept straight YACC files, with C action
  400. X#  code.  There isn't much point to using it this way, though, since
  401. X#  its output is not meant to be human readable.  Rather, it is to be
  402. X#  passed directly to Ibpag2 for processing.  Iacc is simply a YACCish
  403. X#  front end.
  404. X#
  405. X############################################################################
  406. X#
  407. X#  Installation:
  408. X#
  409. X#      This file is not an Icon file, but rather an Ibpag2 file.  You
  410. X#  must have Ibpag2 installed in order to run it.  To create the iacc
  411. X#  executable, first create iacc.icn by typing "ibpag2 -f iacc.ibp -o
  412. X#  iacc.icn," then compile iacc.icn as you would any other Icon file
  413. X#  to create iacc (or on systems without direct execution, iacc.icx).
  414. X#  Put more simply, iacc.ibp not only outputs Ibpag2 files, but is
  415. X#  itself generated using Ibpag2 + icon{t,c}.
  416. X#
  417. X############################################################################
  418. X#
  419. X#  Links: longstr, strings
  420. X#  See also: ibpag2
  421. X#
  422. X############################################################################
  423. X
  424. X%{
  425. X
  426. Xlink strings, longstr
  427. Xglobal newrules, lval, symbol_no
  428. X
  429. X%}
  430. X
  431. X# basic entities
  432. X%token C_IDENT, IDENT    # identifiers and literals
  433. X%token NUMBER            # [0-9]+
  434. X
  435. X# reserved words:  %type -> TYPE, %left -> LEFT, etc.
  436. X%token LEFT, RIGHT, NONASSOC, TOKEN, PREC, TYPE, START, UNION
  437. X
  438. X# miscellaneous
  439. X%token MARK   # %%
  440. X%token LCURL  # %{
  441. X%token RCURL  # dummy token used to start processing of C code
  442. X
  443. X%start yaccf
  444. X
  445. X%%
  446. X
  447. Xyaccf    : front, back
  448. Xfront    : defs, MARK        { write(arg2) }
  449. Xback    : rules, tail        {
  450. X                  every write(!\newrules)
  451. X                  if write(\arg2) then
  452. X                      every write(!&input)
  453. X                }
  454. Xtail    : epsilon        { return &null }
  455. X    | MARK            { return arg1 }
  456. X
  457. Xdefs    : epsilon
  458. X    | defs, def        { write(\arg2) }
  459. X    | defs, cdef        { write(\arg2) }
  460. X
  461. Xdef    : START, IDENT        { return arg1 || " " || arg2 }
  462. X    | rword, tag, nlist    {
  463. X                  if arg1 == "%type"
  464. X                  then return &null
  465. X                  else return arg1 || " " || arg3
  466. X                }
  467. Xcdef    : stuff, RCURL, RCURL    { return arg1 }
  468. Xstuff    : UNION            { get_icon_code("%}"); return &null }
  469. X    | LCURL            { return "%{ " || get_icon_code("%}") }
  470. X
  471. Xrword    : TOKEN    | LEFT | RIGHT | NONASSOC | TYPE
  472. X
  473. Xtag    : epsilon        { return &null }
  474. X    | '<', IDENT, '>'    { return "<" || arg2 || ">" }
  475. X
  476. Xnlist    : nmno            { return arg1 }
  477. X    | nlist, nmno        { return arg1 || ", " || arg2 }
  478. X    | nlist, ',', nmno    { return arg1 || ", " || arg3 }
  479. X
  480. Xnmno    : IDENT            { return arg1 }
  481. X    | IDENT, NUMBER        { return arg1 }
  482. X    
  483. Xrules    : LHS, ':', RHS        { write(arg1, "\t: ", arg3) }
  484. X    | rules, rule        { write(arg2) }
  485. X
  486. XRHS    : rbody, prec        { return arg1 || " " || arg2 }
  487. X
  488. Xrule    : LHS, '|', RHS        { return "\t| " || arg3 }
  489. X    | LHS, ':', RHS        { return arg1 || "\t: " || arg3 }
  490. X
  491. XLHS    : C_IDENT        { symbol_no := 0 ; return arg1 }
  492. X    | epsilon        { symbol_no := 0 }
  493. X
  494. Xrbody    : IDENT            { symbol_no +:= 1; return arg1 }
  495. X    | act            { return "epsilon " || arg1 }
  496. X    | middle, IDENT        { return arg1 || ", " || arg2 }
  497. X    | middle, act        { return arg1 || " "  || arg2 }
  498. X    | middle, ',', IDENT    { return arg1 || ", " || arg3 }
  499. X    | epsilon        { return "epsilon" }
  500. X
  501. Xmiddle    : IDENT            { symbol_no +:= 1; return arg1 }
  502. X    | act            { symbol_no +:= 1; return arg1 }
  503. X    | middle, IDENT        { symbol_no +:= 1; return arg1 || ", "||arg2 }
  504. X    | middle, ',', IDENT    { symbol_no +:= 1; return arg1 || ", "||arg3 }
  505. X    | middle, act        {
  506. X                  local i, l1, l2
  507. X                  static actno
  508. X                  initial { actno := 0; newrules := [] }
  509. X                  actno +:= 1
  510. X                  l1 := []; l2 := []
  511. X                  every i := 1 to symbol_no do {
  512. X                      every put(l1, ("arg"|"$") || i)
  513. X                      if symbol_no-i = 0 then i := "0"
  514. X                      else i := "-" || symbol_no - i
  515. X                      every put(l2, ("$"|"$") || i)
  516. X                  }
  517. X                  put(newrules, "ACT_"|| actno ||
  518. X                    "\t: epsilon "|| mapargs(arg2, l1, l2))
  519. X                  symbol_no +:= 1
  520. X                  return arg1 || ", " || "ACT_" || actno
  521. X                }
  522. X
  523. Xact    : '{', cstuff, '}', '}'    { return "{" || arg2 }
  524. Xcstuff    : epsilon        { return get_icon_code("}") }
  525. X
  526. Xprec    : epsilon        { return "" }
  527. X    | PREC, IDENT        { return arg1 || arg2 }
  528. X    | PREC, IDENT, act    { return arg1 || arg2 || arg3 }
  529. X
  530. X
  531. X%%
  532. X
  533. X
  534. Xprocedure iilex()
  535. X
  536. X    local t
  537. X    static last_token, last_lval, colon
  538. X    initial colon := ord(":")
  539. X
  540. X    every t := next_token() do {
  541. X    iilval := last_lval
  542. X    if \last_token then {
  543. X        if t = colon then {
  544. X        if last_token = IDENT
  545. X        then suspend C_IDENT
  546. X        else suspend last_token
  547. X        } else
  548. X        suspend last_token
  549. X    }
  550. X    last_token := t
  551. X    last_lval := lval
  552. X    }
  553. X    iilval := last_lval
  554. X    suspend \last_token
  555. X
  556. Xend
  557. X
  558. X
  559. Xprocedure next_token()
  560. X
  561. X    local reserveds, UNreserveds, c, idchars, marks
  562. X
  563. X    reserveds := ["break","by","case","create","default","do",
  564. X          "else","end","every","fail","global","if",
  565. X          "initial","invocable","link","local","next",
  566. X          "not","of","procedure","record","repeat",
  567. X          "return","static","suspend","then","to","until",
  568. X          "while"]
  569. X
  570. X    UNreserveds := ["break_","by_","case_","create_","default_","do_",
  571. X            "else_","end_","every_","fail_","global_","if_",
  572. X            "initial_","invocable_","link_","local_","next_",
  573. X            "not_","of_","procedure_","record_","repeat_",
  574. X            "return_","static_","suspend_","then_","to_",
  575. X            "until_","while_"]
  576. X
  577. X    idchars := &letters ++ '._'
  578. X    marks := 0
  579. X
  580. X    c := reads()
  581. X    repeat {
  582. X    lval := &null
  583. X    case c of {
  584. X        "#" : { do_icon_comment(); c := reads() | break }
  585. X        "<" : { suspend ord(c); c := reads() | break }
  586. X        ">" : { suspend ord(c); c := reads() | break }
  587. X        ":" : { suspend ord(c); c := reads() | break }
  588. X        "|" : { suspend ord(c); c := reads() | break }
  589. X        "," : { suspend ord(c); c := reads() | break }
  590. X        "{" : { suspend ord(c | "}" | "}"); c := reads() }
  591. X        "/" : {
  592. X        reads() == "*" | stop("unknown YACC operator, \"/\"")
  593. X        do_c_comment()
  594. X        c := reads() | break
  595. X        }
  596. X        "'" : {
  597. X        lval := "'"
  598. X        while lval ||:= (c := reads()) do {
  599. X            if c == "\\"
  600. X            then lval ||:= reads()
  601. X            else if c == "'" then {
  602. X            suspend IDENT
  603. X            break
  604. X            }
  605. X        }
  606. X        c := reads() | break
  607. X        }
  608. X        "%" : {
  609. X        lval := "%"
  610. X        while any(&letters, c := reads()) do 
  611. X            lval ||:= c
  612. X        if *lval = 1 then {
  613. X            if c == "%" then {
  614. X            lval := "%%"
  615. X            suspend MARK
  616. X            if (marks +:= 1) > 1 then
  617. X                fail
  618. X            } else {
  619. X            if c == "{" then {
  620. X                lval := "%{"
  621. X                suspend LCURL | RCURL | RCURL
  622. X            }
  623. X            else stop("malformed %declaration")
  624. X            }
  625. X            c := reads() | break
  626. X        } else {
  627. X            case lval of {
  628. X            "%prec"     : suspend PREC
  629. X            "%left"     : suspend LEFT
  630. X            "%token"    : suspend TOKEN
  631. X            "%right"    : suspend RIGHT
  632. X            "%type"     : suspend TYPE
  633. X            "%start"    : suspend START
  634. X            "%union"    : suspend UNION | RCURL | RCURL
  635. X            "%nonassoc" : suspend NONASSOC
  636. X            default    : stop("unknown % code in def section")
  637. X            }
  638. X        }
  639. X        }
  640. X        default : {
  641. X        if any(&digits, c) then {
  642. X            lval := c
  643. X            while any(&digits, c := reads()) do
  644. X            lval ||:= c
  645. X            suspend NUMBER
  646. X        }    
  647. X        else {
  648. X            if any(idchars, c) then {
  649. X            lval := c
  650. X            while any(&digits ++ idchars, c := reads()) do
  651. X                lval ||:= c
  652. X            lval := mapargs(lval, reserveds, UNreserveds)
  653. X            suspend IDENT
  654. X            }
  655. X            else {
  656. X            # whitespace
  657. X            c := reads() | break
  658. X            }
  659. X        }
  660. X        }
  661. X    }
  662. X    }
  663. X
  664. X
  665. Xend
  666. X
  667. X
  668. Xprocedure get_icon_code(endmark, comment)
  669. X
  670. X    local yaccwords, ibpagwords, count, c, c2, s
  671. X
  672. X    yaccwords := ["YYDEBUG", "YYACCEPT", "YYERROR", "yyclearin", "yyerrok"]
  673. X    ibpagwords := ["IIDEBUG", "IIACCEPT", "IIERROR", "iiclearin", "iierrok"]
  674. X
  675. X    s := ""
  676. X    count := 1
  677. X    c := reads()
  678. X
  679. X    repeat {
  680. X    case c of {
  681. X        "\""    :  s ||:= c || do_string()
  682. X        "'"     :  s ||:= c || do_charlit()
  683. X        "$"     :  {
  684. X        c2 := reads() | break
  685. X        if c2 == "$" then {
  686. X            until (c := reads()) == "="
  687. X            s ||:= "return "
  688. X        } else {
  689. X            s ||:= c
  690. X            c := c2
  691. X            next
  692. X        }
  693. X        }
  694. X        "#"     :  {
  695. X        if s[-1] == "\n"
  696. X        then s[-1] := ""
  697. X        do_icon_comment()
  698. X        }
  699. X        "/" : {
  700. X        c := reads() | break
  701. X        if c == "*" then
  702. X            do_c_comment()
  703. X        else {
  704. X            s ||:= c
  705. X            next
  706. X        }
  707. X        }
  708. X        "{"     :  {
  709. X        s ||:= c
  710. X        if endmark == "}" then
  711. X            count +:= 1
  712. X        }
  713. X        "}"     :  {
  714. X        s ||:= c
  715. X        if endmark == "}" then {
  716. X            count -:= 1
  717. X            count = 0 & (return mapargs(s, yaccwords, ibpagwords))
  718. X        }
  719. X        }
  720. X        "%"     :  {
  721. X        s ||:= c
  722. X        if endmark == "%}" then {
  723. X            if (c := reads()) == "}"
  724. X            then return mapargs(s || c, yaccwords, ibpagwords)
  725. X            else next
  726. X        }
  727. X        }
  728. X        default : s ||:= c
  729. X    }
  730. X    c := reads() | break
  731. X    }
  732. X
  733. X    # if there is no endmark, just go to EOF
  734. X    if \endmark
  735. X    then stop("input file has mis-braced { code }")
  736. X    else return mapargs(s, yaccwords, ibpagwords)
  737. X
  738. Xend
  739. X
  740. X
  741. Xprocedure do_string()
  742. X
  743. X    local c, s
  744. X
  745. X    s := ""
  746. X    while c := reads() do {
  747. X    case c of {
  748. X        "\\"    : s ||:= c || reads()
  749. X        "\""    : return s || c || reads()
  750. X        default : s ||:= c
  751. X    }
  752. X    }
  753. X
  754. X    stop("malformed string literal")
  755. X
  756. Xend
  757. X
  758. X
  759. Xprocedure do_charlit()
  760. X
  761. X    local c, s
  762. X
  763. X    s := ""
  764. X    while c := reads() do {
  765. X    case c of {
  766. X        "\\"    : s ||:= c || reads()
  767. X        "'"     : return s || c || reads()
  768. X        default : s ||:= c
  769. X    }
  770. X    }
  771. X
  772. X    stop("malformed character literal")
  773. X
  774. Xend
  775. X
  776. X
  777. Xprocedure do_c_comment()
  778. X
  779. X    local c, s
  780. X
  781. X    s := c := reads() |
  782. X    stop("malformed C-style /* comment */")
  783. X
  784. X    repeat {
  785. X    if c == "*" then {
  786. X        s ||:= (c := reads() | break)
  787. X        if c == "/" then
  788. X        return s
  789. X    }
  790. X    else s ||:= (c := reads() | break)
  791. X    }
  792. X
  793. X    return s            # EOF okay
  794. X
  795. Xend
  796. X
  797. X
  798. Xprocedure do_icon_comment()
  799. X
  800. X    local c, s
  801. X
  802. X    s := ""
  803. X    while c := reads() do {
  804. X    case c of {
  805. X        "\\"    : s ||:= c || (reads() | break)
  806. X        "\n"    : return s
  807. X        default : s ||:= c
  808. X    }
  809. X    }
  810. X
  811. X    return s            # EOF okay
  812. X
  813. Xend
  814. X
  815. X
  816. Xprocedure mapargs(s, l1, l2)
  817. X
  818. X    local i, s2
  819. X    static cs, tbl, last_l1, last_l2
  820. X
  821. X    if /l1 | *l1 = 0 then return s
  822. X
  823. X    if not (last_l1 === l1, last_l2 === l2) then {
  824. X    cs := ''
  825. X    every cs ++:= (!l1)[1]
  826. X    tbl := table()
  827. X    every i := 1 to *l1 do
  828. X        insert(tbl, l1[i], (\l2)[i] | "")
  829. X    }
  830. X
  831. X    s2 := ""
  832. X    s ? {
  833. X    while s2 ||:= tab(upto(cs)) do {
  834. X        (s2 <- (s2 || tbl[tab(longstr(l1))]),
  835. X            not any(&letters++&digits++'_')) |
  836. X            (s2 ||:= move(1))
  837. X    }
  838. X    s2 ||:= tab(0)
  839. X    }
  840. X
  841. X    return s2
  842. X
  843. Xend
  844. X
  845. X
  846. Xprocedure main()
  847. X    iiparse()
  848. Xend
  849. END_OF_FILE
  850.   if test 10968 -ne `wc -c <'iacc.ibp'`; then
  851.     echo shar: \"'iacc.ibp'\" unpacked with wrong size!
  852.   fi
  853.   # end of 'iacc.ibp'
  854. fi
  855. if test -f 'ibpag2.icn' -a "${1}" != "-c" ; then 
  856.   echo shar: Will not clobber existing file \"'ibpag2.icn'\"
  857. else
  858.   echo shar: Extracting \"'ibpag2.icn'\" \(11375 characters\)
  859.   sed "s/^X//" >'ibpag2.icn' <<'END_OF_FILE'
  860. X############################################################################
  861. X#
  862. X#    Name:     ibpag2.icn
  863. X#
  864. X#    Title:     Icon-based parser generator (version 2)
  865. X#
  866. X#    Author:     Richard L. Goerwitz
  867. X#
  868. X#    Version: 1.20
  869. X#
  870. X############################################################################
  871. X#
  872. X#  The Basics
  873. X#
  874. X#      Ibpag2 is a simple tool for generating parsers from grammar
  875. X#  specifications.  This may sound pretty arcane to those who have
  876. X#  never used a parser generator.  In fact, though, this kind of tool
  877. X#  forms the basis of most programming language implementations.
  878. X#  Parser generators are also used in preprocessors, transducers,
  879. X#  compilers, interpreters, calculators and in fact for just about any
  880. X#  situation where some form of structured input needs to be read into
  881. X#  an internal data structure and/or converted into some form of
  882. X#  structured output.  This might include something as mundane as
  883. X#  reading in recepts or mailing addresses from a file, or turning
  884. X#  dates of one type (e.g. "September 3, 1993") into another
  885. X#  ("9/3/93").  For more information on how to use it, see the README
  886. X#  file included with the Ibpag2 distribution.
  887. X#
  888. X############################################################################
  889. X#
  890. X#  Running Ibpag2:
  891. X#
  892. X#      Invoking Ibpag2 is very, very simple.  There are quite a few
  893. X#  command-line switches, but all are optional:
  894. X#
  895. X#      ibpag2 [-f infile] [-m module] [-o outfile] [-p iiparse.lib dir]
  896. X#              [-a] [-c] [-v] [-y]
  897. X#
  898. X#  Where infile is the Ibpag2 source file (default &input), outfile is
  899. X#  the output file (default &output), module is an optional string
  900. X#  appended to all global variables and all procedure calls (to allow
  901. X#  multiple running parsers), and where -v instructs Ibpag2 to write a
  902. X#  summary of its operations to ibpag2.output.  Normally all of these
  903. X#  arguments can be ignored.  Ibpag2 can usually be run using simple
  904. X#  shell redirection symbols (if your OS supports them).  See the next
  905. X#  paragraph for an explanation of the -p option.  The -c option is
  906. X#  for compressed tables, and -a is for non-LR or ambiguous grammars.
  907. X#  See the advanced sections of README file.  -y directs Ibpag2 to
  908. X#  resolve reduce/reduce conflicts by their order of occurrence in the
  909. X#  grammar, and to resolve shift/reduce conflicts in favor of shift -
  910. X#  just like YACC.  Invoking Ibpag with -h causes it to abort with a
  911. X#  brief help message.
  912. X#
  913. X#      Make sure that the iiparse.lib and iiglrpar.lib files are in
  914. X#  some path listed in your LPATH directory, or else in a data
  915. X#  directory adjacent to some IPL "procs" directory in your LPATH.
  916. X#  Basically, LPATH is just a space-separated list of places where
  917. X#  .icn library source files reside.  If your system does not support
  918. X#  environment variables, then there are two ways to tell Ibpag2 where
  919. X#  the .lib files are without using LPATH.  The first is to move into
  920. X#  the directory that contains these files.  The second is to supply
  921. X#  the files' location using Ibpag's -p option (e.g. ibpag2 -p
  922. X#  /usr/local/lib/icon/data).
  923. X#
  924. X############################################################################
  925. X#
  926. X#  More Technical Details
  927. X#
  928. X#      Technically speaking, Ibpag2 is a preprocessor that accepts a
  929. X#  YACC-like source file containing grammar productions and actions,
  930. X#  then 1) converts these into parse tables and associated code, 2)
  931. X#  adds to them an LR parser, and a few debugging tools, and 3) writes
  932. X#  the combination to the standard output, along with the necessary
  933. X#  action and goto table construction code.  The user must $include,
  934. X#  or hard-code into the Ibpag2 source file, a lexical analyzer that
  935. X#  returns integers via symbolic $defines generated by %token, %right,
  936. X#  etc. declarations in the Ibpag2 source file.
  937. X#
  938. X#      Cycles and epsilon moves are handled correctly (to my
  939. X#  knowledge).  Shift-reduce conflicts are handled in the normal way
  940. X#  (i.e. pick the rule with the highest priority, and, in cases where
  941. X#  the priority is the same, check the associativities) I decided to
  942. X#  flag reduce/reduce conflicts as errors by default, since these
  943. X#  often conceal deeper precedence problems.  They are easily enough
  944. X#  handled, if need be, via dummy precedences.  The -y command-line
  945. X#  switch turns off this behavior, causing Ibpag2 to resolve
  946. X#  reduce/reduce conflicts in a YACCish manner (i.e. favoring the rule
  947. X#  that occurs first in the grammar).  Ibpag2 normally aborts on
  948. X#  shift/reduce conflicts.  The -y switch makes Ibpag resolve these in
  949. X#  favor of shift, and to keep on processing - again, just like YACC.
  950. X#
  951. X#      For more information, see the README file.
  952. X#
  953. X############################################################################
  954. X#
  955. X#  Links: ibreader, ibwriter, slrtbls, ibutil, version, options
  956. X#
  957. X############################################################################
  958. X
  959. X# link ibreader, ibwriter, slrtbls, ibutil, version, options
  960. Xlink options
  961. X
  962. Xglobal DEBUG
  963. X
  964. Xprocedure main(a)
  965. X
  966. X    local infile, outfile, verbosefile, atbl, gtbl, grammar, opttbl,
  967. X    module, abort_on_conflict, paths, path, parser_name,
  968. X    iiparse_file 
  969. X
  970. X    # Get command-line options.
  971. X    opttbl := options(a, "f:o:vdm:p:hcay", bad_arg)
  972. X
  973. X    # Abort with help message if -h is supplied.
  974. X    if \opttbl["h"] then {
  975. X    write(&errout, ib_version())
  976. X    return ib_help_()
  977. X    }
  978. X
  979. X    # If an input file was specified, open it.  Otherwise use stdin.
  980. X    #
  981. X    if \opttbl["f"] then
  982. X    infile := open(opttbl["f"], "r") |
  983. X        bad_arg("can't open " || opttbl["f"])
  984. X    else infile := &input
  985. X
  986. X    # If an output file was specified, use it.  Otherwise use stdout.
  987. X    #
  988. X    if \opttbl["o"] then
  989. X    outfile := open(opttbl["o"], "w") |
  990. X        bad_arg("can't open " || opttbl["o"])
  991. X    else outfile := &output
  992. X
  993. X    # If a module name was specified (-m), then use it.
  994. X    #
  995. X    module := opttbl["m"] | ""
  996. X
  997. X    # If the debug option was specified, set all verbose output to go
  998. X    # to errout.
  999. X    #
  1000. X    if \opttbl["d"] then {
  1001. X    verbosefile := &errout
  1002. X    DEBUG := 1
  1003. X    }
  1004. X
  1005. X    # If the verbose option was specified, send all verbose output to
  1006. X    # "ibpag2.output" (a bit like YACC's yacc.output file).
  1007. X    #
  1008. X    else if \opttbl["v"] then
  1009. X    verbosefile := open("ibpag2.output", "w") |
  1010. X        bad_arg("can't open " || opttbl["v"])
  1011. X
  1012. X    # Output defines for YACC-like macros.  Output iiisolate and
  1013. X    # iiprune if -a option is specified.  Sorry for the ugly code.
  1014. X    #
  1015. X    write_defines(opttbl, outfile, module)
  1016. X
  1017. X    # Whew!  Now fetch the grammar from the input file.
  1018. X    #
  1019. X    # Emit line directives keyed to actual line numbers in the
  1020. X    # original file.  Pass its name as arg4.  If obttbl["f"] is
  1021. X    # null (and the input file is &input), ibreader will default
  1022. X    # to something else.
  1023. X    #
  1024. X    grammar := ibreader(infile, outfile, module, opttbl["f"])
  1025. X    if \verbosefile then
  1026. X    # grammar contains start symbol, rules, and terminal token table
  1027. X    print_grammar(grammar, verbosefile)
  1028. X
  1029. X    # Fill in parse tables, atbl and gtbl.  Abort if there is a
  1030. X    # conflict caused by an ambiguity in the grammar or by some
  1031. X    # precedence/associativity problem, unless the -a option is
  1032. X    # supplied (telling Ibpag2 that ambiguous tables are okay).
  1033. X    #
  1034. X    if /opttbl["a"] then
  1035. X    abort_on_conflict := "yes"
  1036. X    atbl := table(); gtbl := table()
  1037. X    make_slr_tables(grammar, atbl, gtbl, abort_on_conflict, opttbl["y"])
  1038. X    if \verbosefile then
  1039. X    # grammar.tbl maps integer terminal symbols to human-readable strings
  1040. X    print_action_goto_tables(atbl, gtbl, grammar.tbl, verbosefile)
  1041. X
  1042. X    # If -c was specified on the command line, compress the action and
  1043. X    # goto tables.
  1044. X    #
  1045. X    if \opttbl["c"] then {
  1046. X    write(outfile, "\n$define COMPRESSED_TABLES\n")
  1047. X    if \verbosefile then
  1048. X        write(verbosefile, "\nNote:  parse tables are compressed")
  1049. X    shrink_tables(grammar, atbl, gtbl)
  1050. X    }
  1051. X
  1052. X    # Try to find the .lib file using LPATH.
  1053. X    #
  1054. X    parser_name := {
  1055. X    if \opttbl["a"] then "iiglrpar.lib"
  1056. X    else "iiparse.lib"
  1057. X    }
  1058. X    
  1059. X    paths := []
  1060. X    put(paths, trim(\opttbl["p"], '/') || "/")
  1061. X    put(paths, "")
  1062. X    (\getenv)("LPATH") ? {
  1063. X    while path := trim(tab(find(" ") | 0), '/') || "/" do {
  1064. X        tab(many(' '))
  1065. X        if find("procs", path) then
  1066. X        put(paths, ibreplace(path, "procs", "data"))
  1067. X        put(paths, path)
  1068. X        pos(0) & break
  1069. X    }
  1070. X    }
  1071. X    iiparse_file := open(!paths || parser_name, "r") | iohno(2)
  1072. X
  1073. X    # Write .lib file (contains the iiparse() parser routine), along
  1074. X    # with the start symbol, action table, goto table, and a list of
  1075. X    # productions.
  1076. X    #
  1077. X    # grammar contains start symbol, rules, and terminal token table
  1078. X    #
  1079. X    ibwriter(iiparse_file, outfile, grammar, atbl, gtbl, module)
  1080. X
  1081. X    return exit(0)
  1082. X
  1083. Xend
  1084. X
  1085. X
  1086. X#
  1087. X# write_defines
  1088. X# 
  1089. Xprocedure write_defines(opttbl, outfile, module)
  1090. X
  1091. X    # Output defines for YACC-like macros.  Output iiisolate and
  1092. X    # iiprune if -a option is specified.  Sorry for the ugly code.
  1093. X    #
  1094. X    if \opttbl["a"] then {
  1095. X    write(outfile,
  1096. X          "$define iiisolate (iidirective", module, " := \"isolate\")")
  1097. X    write(outfile,
  1098. X          "$define iiprune   (iidirective", module, " := \"prune\")")
  1099. X    write(outfile,
  1100. X          "$define iierrok   (iidirective", module, " := \"errok\")")
  1101. X    } else {
  1102. X    write(outfile,
  1103. X          "$define iierrok   (recover_shifts", module, " := &null &",
  1104. X                        " discards",     module, " := 0)")
  1105. X    }
  1106. X    write(outfile,
  1107. X      "$define iiclearin (iidirective",    module, " := \"clearin\")")
  1108. X    write(outfile,
  1109. X      "$define IIERROR   (iidirective",    module, " := \"error\")")
  1110. X    write(outfile,
  1111. X      "$define IIACCEPT  (iidirective",    module, " := \"accept\")")
  1112. Xend
  1113. X
  1114. X
  1115. X#
  1116. X# bad_arg
  1117. X#
  1118. X#     Simple routine called if command-line arguments are bad.
  1119. X#
  1120. Xprocedure bad_arg(s)
  1121. X
  1122. X    write(&errout, "ibpag2:  ",s)
  1123. X    write(&errout,
  1124. X      "usage:  ibpag2 [-f inf] [-m str ] [-o outf] _
  1125. X              [-p dir] [-a] [-c] [-v] [-y]")
  1126. X    write(&errout, "        for help, type \"ibpag2 -h\"")
  1127. X    stop()
  1128. X
  1129. Xend
  1130. X
  1131. X
  1132. X#
  1133. X# ib_help_
  1134. X#
  1135. Xprocedure ib_help_()
  1136. X
  1137. X    write(&errout, "")
  1138. X    write(&errout,
  1139. X      "usage:  ibpag2 [-f inf] [-m str] [-o outf] [-p dir] _
  1140. X              [-a] [-c] [-v] [-y]")
  1141. X    write(&errout, "")
  1142. X    write(&errout, "  -f inf........where inf = Ibpag2's input file (default")
  1143. X    write(&errout, "                   &input)")
  1144. X    write(&errout, "  -m str........where str = a string to be appended to")
  1145. X    write(&errout, "                   global identifiers and procedures")
  1146. X    write(&errout, "  -o outf.......where outf = Ibpag2's input file (default")
  1147. X    write(&errout, "                   &output)")
  1148. X    write(&errout, "  -p dir........where dir = directory in which the")
  1149. X    write(&errout, "                   iiparse.lib file resides (mainly for")
  1150. X    write(&errout, "                   systems lacking LPATH support)")
  1151. X    write(&errout, "  -a............permits ambiguous grammars and multiple")
  1152. X    write(&errout, "                   parses (makes iiparse() a generator).")
  1153. X    write(&errout, "  -c............compresses action/goto tables (obstructs")
  1154. X    write(&errout, "                   debugging somewhat).")
  1155. X    write(&errout, "  -v............sends debugging info to ibpag2.output")
  1156. X    write(&errout, "  -y............tells Ibpag2 to resolve reduce/reduce")
  1157. X    write(&errout, "                   conflicts by order of occurrence in")
  1158. X    write(&errout, "                   the grammar, and to resolve shift/")
  1159. X    write(&errout, "                   reduce conflicts in favor of shift")
  1160. X    stop("")
  1161. X    
  1162. Xend
  1163. END_OF_FILE
  1164.   if test 11375 -ne `wc -c <'ibpag2.icn'`; then
  1165.     echo shar: \"'ibpag2.icn'\" unpacked with wrong size!
  1166.   fi
  1167.   # end of 'ibpag2.icn'
  1168. fi
  1169. if test -f 'ibutil.icn' -a "${1}" != "-c" ; then 
  1170.   echo shar: Will not clobber existing file \"'ibutil.icn'\"
  1171. else
  1172.   echo shar: Extracting \"'ibutil.icn'\" \(8021 characters\)
  1173.   sed "s/^X//" >'ibutil.icn' <<'END_OF_FILE'
  1174. X############################################################################
  1175. X#
  1176. X#    Name:     ibutil.icn
  1177. X#
  1178. X#    Title:     utilities for Ibpag2
  1179. X#
  1180. X#    Author:     Richard L. Goerwitz
  1181. X#
  1182. X#    Version: 1.20
  1183. X#
  1184. X############################################################################
  1185. X#  
  1186. X#  Contains:
  1187. X#
  1188. X#      production_2_string(p)        makes production or item p human-
  1189. X#                    readable 
  1190. X#
  1191. X#      print_item_list(C, i)        returns human-readable version of
  1192. X#                                   item list C
  1193. X#
  1194. X#      print_grammar(grammar, f)    sends to file f (default &output)
  1195. X#                                   a human-readable printout of a grammar,
  1196. X#                                   as recorded in an ib_grammar structure
  1197. X#
  1198. X#      print_action_goto_tables(atbl, gtbl, ibtoktbl, f)
  1199. X#                                   sends to file f (default (&output)
  1200. X#                                   a human-readable printout of action
  1201. X#                                   table atbl and goto table gtbl
  1202. X#
  1203. X#      print_follow_sets(FOLLOW_table)
  1204. X#                                   returns a human-readable version
  1205. X#                                   of a FOLLOW table (table of sets)
  1206. X#
  1207. X#      print_first_sets(FIRST_table)
  1208. X#                                   returns a human-readable version
  1209. X#                                   of a FIRST table (a table of sets)
  1210. X#
  1211. X#      ibreplace(s1, s2, s3)        replaces s2 with s3 in s1
  1212. X#
  1213. X#      equivalent_items(i1, i2)     succeeds if item i1 is structurally
  1214. X#                    identical to item i2
  1215. X#
  1216. X#      equivalent_item_lists(l1,l2) same as equivalent_items, but for
  1217. X#                                   lists of items, not individual items
  1218. X#
  1219. X#      sortff(struct, f1...fn)      sorts struct on fields1, then
  1220. X#                    sub-sorts on field f2, ...fn
  1221. X#
  1222. X############################################################################
  1223. X#
  1224. X#  Links: none
  1225. X#
  1226. X############################################################################
  1227. X
  1228. X
  1229. Xrecord production(LHS, RHS, POS, LOOK, no, prec, assoc)
  1230. X
  1231. X#
  1232. X# production_2_string:  production record -> string
  1233. X#                       p                 -> s
  1234. X#
  1235. X#     Stringizes an image of the LHS and RHS of production p in
  1236. X#     human-readable form.
  1237. X#
  1238. Xprocedure production_2_string(p, ibtoktbl)
  1239. X
  1240. X    local s, m, t
  1241. X
  1242. X    s := image(p.LHS) || " -> "
  1243. X    every m := !p.RHS do {
  1244. X    if t := \ (\ibtoktbl)[m]
  1245. X    then s ||:= t || " "
  1246. X    else s ||:= image(m) || " "
  1247. X    }
  1248. X    # if the POS field is nonnull, print it
  1249. X    s ||:= "(POS = " || image(\p.POS) || ") "
  1250. X    # if the LOOK field is nonnull, print it, too
  1251. X    s ||:= "lookahead = " || image(\p.LOOK)
  1252. X
  1253. X    return trim(s)
  1254. X
  1255. Xend
  1256. X
  1257. X
  1258. X#
  1259. X# print_item_list:  makes item list human readable
  1260. X#
  1261. Xprocedure print_item_list(C, i)
  1262. X
  1263. X    write(&errout, "Productions for item list ", i, ":")
  1264. X    every write(&errout, "\t", production_2_string(!C[i]))
  1265. X    write(&errout)
  1266. X    return
  1267. X
  1268. Xend
  1269. X
  1270. X
  1271. X#
  1272. X# print_grammar:  makes entire grammar human readable
  1273. X#
  1274. Xprocedure print_grammar(grammar, f)
  1275. X
  1276. X    local p, i, sl
  1277. X
  1278. X    /f := &errout
  1279. X
  1280. X    write(f, "Start symbol:")
  1281. X    write(f, "\t", grammar.start)
  1282. X    write(f)
  1283. X    write(f, "Rules:")
  1284. X    every p := !grammar.rules do {
  1285. X    writes(f, "\tRule ", right(p.no, 3, " "), "  ")
  1286. X    write(f, production_2_string(p, grammar.tbl))
  1287. X    }
  1288. X    write(f)
  1289. X    write(f, "Tokens:")
  1290. X    sl := sort(grammar.tbl, 3)
  1291. X    every i := 1 to *sl-1 by 2 do
  1292. X    write(f, "\t", left(sl[i], 5, "."), right(sl[i+1], 20, "."))
  1293. X    write(f)
  1294. X    return
  1295. X
  1296. Xend
  1297. X
  1298. X
  1299. X#
  1300. X# print_action_goto_tables
  1301. X#
  1302. X#     Makes action & goto tables human readable.  If a table mapping
  1303. X#     integer (i.e. char) literals to token names is supplied, the
  1304. X#     token names themselves are printed.
  1305. X#
  1306. Xprocedure print_action_goto_tables(atbl, gtbl, ibtoktbl, f)
  1307. X
  1308. X    local TAB, tbl, key_set, size, i, column, k
  1309. X
  1310. X    /f := &errout
  1311. X    TAB := "\t"
  1312. X
  1313. X    every tbl := atbl|gtbl do {
  1314. X
  1315. X    key_set := set(); every insert(key_set, key(tbl))
  1316. X    writes(f, TAB)
  1317. X    every k := !key_set do
  1318. X        writes(f, \(\ibtoktbl)[k] | k, TAB)
  1319. X    write(f)
  1320. X    
  1321. X    size := 0; every size <:= key(!tbl)
  1322. X    every i := 1 to size do {
  1323. X        writes(f, i, TAB)
  1324. X        every column := tbl[!key_set] do {
  1325. X        # action lists may have more than one element
  1326. X        if /column[i] then
  1327. X            writes(f, "  ", TAB) & next
  1328. X        \column[i] ? {
  1329. X            if any('asr') then {
  1330. X            while any('asr') do {
  1331. X                writes(f, ="a") & next
  1332. X                writes(f, tab(upto('.<')))
  1333. X                if ="<" then tab(find(">")+1) else ="."
  1334. X                tab(many(&digits))
  1335. X            }
  1336. X            writes(f, TAB)
  1337. X            }
  1338. X            else writes(f, tab(many(&digits)), TAB)
  1339. X        }
  1340. X        }
  1341. X        write(f)
  1342. X    }
  1343. X    write(f)
  1344. X    }
  1345. X
  1346. X    return
  1347. X
  1348. Xend
  1349. X
  1350. X
  1351. X#
  1352. X# print_follow_sets:  make FOLLOW table human readable
  1353. X#
  1354. Xprocedure print_follow_sets(FOLLOW_table)
  1355. X
  1356. X    local FOLLOW_sets, i
  1357. X
  1358. X    FOLLOW_sets := sort(FOLLOW_table, 3)
  1359. X    write(&errout, "FOLLOW sets are as follows:")
  1360. X    every i := 1 to *FOLLOW_sets-1 by 2 do {
  1361. X    writes(&errout, "\tFOLLOW(", image(FOLLOW_sets[i]), ") = ")
  1362. X    every writes(&errout, image(! FOLLOW_sets[i+1]), " ")
  1363. X    write(&errout)
  1364. X    }
  1365. X    write(&errout)
  1366. X    return
  1367. X
  1368. Xend
  1369. X
  1370. X
  1371. X#
  1372. X# print_first_sets:  make FIRST table human readable
  1373. X#
  1374. Xprocedure print_first_sets(FIRST_table)
  1375. X
  1376. X    local FIRST_sets, i
  1377. X
  1378. X    FIRST_sets := sort(FIRST_table, 3)
  1379. X    write(&errout, "FIRST sets are as follows:")
  1380. X    every i := 1 to *FIRST_sets-1 by 2 do {
  1381. X    writes(&errout, "\tFIRST(", image(FIRST_sets[i]), ") = ")
  1382. X    every writes(&errout, image(! FIRST_sets[i+1]), " ")
  1383. X    write(&errout)
  1384. X    }
  1385. X    write(&errout)
  1386. X    return
  1387. X
  1388. Xend
  1389. X
  1390. X
  1391. X#
  1392. X# ibreplace: string x string x string -> string
  1393. X#            (s1,     s2,      s3)    -> s4
  1394. X#
  1395. X#     Where s4 is s1, with every instance of s2 stripped out and
  1396. X#     replaced by s3.  E.g. replace("hello there; hello", "hello",
  1397. X#     "hi") yields "hi there; hi".  Taken straight from the IPL.
  1398. X#
  1399. Xprocedure ibreplace(s1,s2,s3)
  1400. X
  1401. X    local result, i
  1402. X
  1403. X    result := ""
  1404. X    i := *s2
  1405. X
  1406. X    s1 ? {
  1407. X    while result ||:= tab(find(s2)) do {
  1408. X        result ||:= s3
  1409. X        move(i)
  1410. X    }
  1411. X    return result || tab(0)
  1412. X    }
  1413. X
  1414. Xend
  1415. X
  1416. X    
  1417. X#
  1418. X# equivalent_items:  record x record -> record or failure
  1419. X#                    (item1,  item2) -> item1  or failure
  1420. X#
  1421. X#     Where item1 and item2 are records having LHS, RHS, POS, & LOOK
  1422. X#     fields (and possibly others, though they aren't used).  Returns
  1423. X#     item1 if item1 and item2 are structurally identical as far as
  1424. X#     their LHS, RHS, LOOK, and POS fields are concerned.  For SLR
  1425. X#     table generators, LOOK will always be null.
  1426. X#
  1427. Xprocedure equivalent_items(item1, item2)
  1428. X
  1429. X    local i
  1430. X
  1431. X    item1 === item2 & (return item1)
  1432. X
  1433. X    if item1.LHS == item2.LHS &
  1434. X    item1.POS = item2.POS &
  1435. X    #
  1436. X    # This comparison doesn't have to be recursive, since I take
  1437. X    # care never to alter RHS structures.  Identical RHSs should
  1438. X    # always be *the same underlying structure*.
  1439. X    #
  1440. X    item1.RHS === item2.RHS &
  1441. X    item1.LOOK === item2.LOOK
  1442. X    then
  1443. X    return item1
  1444. X
  1445. Xend
  1446. X
  1447. X
  1448. X#
  1449. X# equivalent_item_lists: list x list -> list or fail
  1450. X#                        (il1,  il2) -> il1
  1451. X#
  1452. X#     Where il1 is one sorted list-of-items (as returned by goto() or
  1453. X#     by closure()), where il2 is another such list.  Returns the
  1454. X#     first list if the LHS, RHS, and POS fields of the constituent
  1455. X#     items are all structurally identical, i.e. if the two lists
  1456. X#     contain the structurally identical items.
  1457. X#
  1458. Xprocedure equivalent_item_lists(il1, il2)
  1459. X
  1460. X    local i
  1461. X
  1462. X    il1 === il2 & (return il1)
  1463. X    if *il1 = *il2
  1464. X    then {
  1465. X    every i := 1 to *il1 do
  1466. X            equivalent_items(il1[i], il2[i]) | fail
  1467. X    }
  1468. X    else fail
  1469. X
  1470. X    return il1
  1471. X
  1472. Xend
  1473. X
  1474. X
  1475. X#
  1476. X# sortff:  like sortf() except takes unlimited no. of field args
  1477. X#
  1478. Xprocedure sortff(arglst[])
  1479. X
  1480. X    local sortfield, i, old_i
  1481. X
  1482. X    *arglst[1] <= 1 | *arglst = 1 & { return arglst[1] }
  1483. X    sortfield := arglst[2]        | { return sortf(arglst[1]) }
  1484. X    arglst[1] := sortf(arglst[1], sortfield)
  1485. X    
  1486. X    old_i := 1
  1487. X    every i := old_i+1 to *arglst[1] do {
  1488. X        if not (arglst[1][old_i][sortfield] === arglst[1][i][sortfield])
  1489. X    then {
  1490. X        return sortff!(push(arglst[3:0], arglst[1][old_i : i])) |||
  1491. X           sortff!(push(arglst[2:0], arglst[1][i     : 0]))
  1492. X    }
  1493. X    }
  1494. X    return sortff!(push(arglst[3:0], arglst[1]))
  1495. X
  1496. Xend
  1497. END_OF_FILE
  1498.   if test 8021 -ne `wc -c <'ibutil.icn'`; then
  1499.     echo shar: \"'ibutil.icn'\" unpacked with wrong size!
  1500.   fi
  1501.   # end of 'ibutil.icn'
  1502. fi
  1503. if test -f 'slritems.icn' -a "${1}" != "-c" ; then 
  1504.   echo shar: Will not clobber existing file \"'slritems.icn'\"
  1505. else
  1506.   echo shar: Extracting \"'slritems.icn'\" \(8228 characters\)
  1507.   sed "s/^X//" >'slritems.icn' <<'END_OF_FILE'
  1508. X############################################################################
  1509. X#
  1510. X#    Name:     slritems.icn
  1511. X#
  1512. X#    Title:     compute item sets for a grammar
  1513. X#
  1514. X#    Author:     Richard L. Goerwitz
  1515. X#
  1516. X#    Version: 1.10
  1517. X#
  1518. X############################################################################
  1519. X#
  1520. X#  Contains make_slr_item_sets(start_symbol, st), slr_goto(l, symbol,
  1521. X#  st), slr_closure(l, st).  The user need only worry about
  1522. X#  make_slr_item_sets() initially.  The slr_goto() routine may be
  1523. X#  useful later when constructing action and goto tables.
  1524. X#
  1525. X#  Slr_closure(l, st) accepts a list of items as its first argument, a
  1526. X#  list or set of the productions in the grammar as its second, and
  1527. X#  returns the closure of item list l, in the form of another item
  1528. X#  list.
  1529. X#
  1530. X#  Note also that the production record structure (LHS, RHS, POS,
  1531. X#  LOOK...) has a POS field, and therefore can serve also as an item.
  1532. X#  In fact, any structure can be used, as long as its first three
  1533. X#  fields are LHS, RHS, and POS.
  1534. X#
  1535. X#  See the "Dragon Book" (cited in first.icn) p. 222 ff.
  1536. X#
  1537. X#  Slr_goto(l, symbol, st) accepts a list as its first argument, a
  1538. X#  string or integer as its second (string = nonterminal, integer =
  1539. X#  terminal), and a list or set for its third, returning another list.
  1540. X#  Arg 1 must be an item list, as generated either by another call to
  1541. X#  slr_goto() or by closure of the start production of the augmented
  1542. X#  grammar.  Arg 2, symbol, is some terminal or nonterminal symbol.
  1543. X#  Arg 3 is the list or set of all productions in the current grammar.
  1544. X#  The return value is the closure of the set of all items [A -> aX.b]
  1545. X#  such that [A -> a.Xb] is in l (arg 1).
  1546. X#
  1547. X#  make_slr_item_sets(start_sym, st) takes a string, start_sym, as its
  1548. X#  first argument, and a list or set of productions as its second.
  1549. X#  Returns a list of canonical LR(0) item sets or states.  It returns,
  1550. X#  in other words, a list of lists of items.  Items can be any record
  1551. X#  type that has LHS, RHS, and POS as its first three fields.
  1552. X#
  1553. X#  See the "Dragon Book," example 4.35 (p. 224).
  1554. X#
  1555. X############################################################################
  1556. X#
  1557. X#  Links: ibutil
  1558. X#
  1559. X############################################################################
  1560. X
  1561. X# link ibutil
  1562. X
  1563. X#
  1564. X# slr_closure:  list x list/set -> list
  1565. X#               (l2,    st)      -> l2
  1566. X#
  1567. X#     Where l is a list of items, where st is a list/set of all
  1568. X#     productions in the grammar from which l was derived, and where
  1569. X#     l(2) is the SLR closure of l, as constructed using the standard
  1570. X#     SLR closure operation.
  1571. X#
  1572. X#     Ignore the third to fifth arguments, len to added.  They are
  1573. X#     used internally by recursive calls to slr_closure().
  1574. X#
  1575. Xprocedure slr_closure(l, st, len, LHS_tbl, added)
  1576. X
  1577. X    local   p, i, new_p, symbol
  1578. X    static  LHS_tbl_tbl
  1579. X    initial LHS_tbl_tbl := table()
  1580. X
  1581. X    if /LHS_tbl then {
  1582. X    if /LHS_tbl_tbl[st] := table() then {
  1583. X        # makes looking up all rules with a given LHS easier
  1584. X        every p := !st do {
  1585. X        /LHS_tbl_tbl[st][p.LHS] := list()
  1586. X        put(LHS_tbl_tbl[st][p.LHS], p)
  1587. X        }
  1588. X    }
  1589. X    LHS_tbl := LHS_tbl_tbl[st]
  1590. X    }
  1591. X
  1592. X    /len := 0
  1593. X    /added := set()
  1594. X
  1595. X    # Len tells us where the elements in l start that we haven't yet
  1596. X    # tried to generate more items from.  These elements are basically
  1597. X    # the items added on the last recursive call (or the "core," if
  1598. X    # there has not yet been a recursive call).
  1599. X    #
  1600. X    every i := len+1 to *l do {
  1601. X    /l[i].POS := 1
  1602. X    # Fails if dot (i.e. l[i].POS) is at the end of the RHS;
  1603. X    # also fails if the current symbol (i.e. l[i].RHS[l[i].POS])
  1604. X    # is a nonterminal.
  1605. X    symbol := l[i].RHS[l[i].POS]
  1606. X    # No need to add productions having symbol as their LHS if
  1607. X    # we've already done so for this particular l.
  1608. X    member(added, symbol) & next
  1609. X    every p := !\LHS_tbl[symbol] do {
  1610. X        # Make a copy of p, but with dot set to position 1.
  1611. X        new_p := copy(p)
  1612. X        # Set POS to 1 for non-epsilon productions; otherwise to 2.
  1613. X        if *new_p.RHS = 1 & new_p.RHS[1] === -2 then
  1614. X        new_p.POS := 2
  1615. X        else new_p.POS := 1
  1616. X        # if new_p isn't in l, add it to the end of l
  1617. X        if not equivalent_items(new_p, !l) then
  1618. X        put(l, new_p)
  1619. X    }
  1620. X    insert(added, symbol)
  1621. X    }
  1622. X    return {
  1623. X    # If nothing new has been added, sort the result and return...
  1624. X    if *l = i then sortff(l, 1, 2, 3)
  1625. X    # ...otherwise, try to add more items to l.
  1626. X    else slr_closure(l, st, i, LHS_tbl, added)
  1627. X    }
  1628. X    
  1629. Xend
  1630. X
  1631. X    
  1632. X#
  1633. X# slr_goto: list x string|integer x list|set -> list
  1634. X#           (l,    symbol,          st)      -> l2
  1635. X#
  1636. X#     Where l is an item set previously returned by slr_goto or (for
  1637. X#     the start symbol of the augmented grammar) by slr_closure(),
  1638. X#     where symbol is a string (nonterminal) or integer (terminal),
  1639. X#     where st is a list or set of all productions in the current
  1640. X#     grammar, and where l2 is the SLR closure of the set of all items
  1641. X#     [A -> aX.b] such that [A -> a.Xb] is in l.
  1642. X#
  1643. X#     The idea is just to move the dots for all productions where the
  1644. X#     dots precede "symbol," creating a new item list for the "moved"
  1645. X#     items, and then performing a slr_closure() on that new item list.
  1646. X#     Note that items can be represented by any structure where fields
  1647. X#     1, 2, and 3 are LHS, RHS, and POS.
  1648. X#
  1649. X#     Note that slr_goto(l, symbol, st) may yield a result that's
  1650. X#     structurally equivalent to one already in the sets of items thus
  1651. X#     far generated.  This won't normally happen, because slr_goto()
  1652. X#     saves old results, never re-calcing for the same l x symbol
  1653. X#     combination.  Still, a duplicate result could theoretically
  1654. X#     happen.
  1655. X#
  1656. Xprocedure slr_goto(l, symbol, st)
  1657. X
  1658. X    local    item, item2, l2, iteml_symbol_table
  1659. X    static   iteml_symbol_table_table
  1660. X    initial  iteml_symbol_table_table := table()
  1661. X
  1662. X    # Keep old results for this grammar (st) in a table of tables of
  1663. X    # tables!
  1664. X    #
  1665. X    /iteml_symbol_table_table[st] := table()
  1666. X    iteml_symbol_table := iteml_symbol_table_table[st]
  1667. X
  1668. X    # See if we've already performed this same calculation.
  1669. X    #
  1670. X    if l2 := \(\iteml_symbol_table[l])[symbol]
  1671. X    then return l2
  1672. X
  1673. X    l2 := list()
  1674. X    every item := !l do {
  1675. X    # Subscripting operation fails if the dot's at end.
  1676. X        if item.RHS[item.POS] === symbol
  1677. X    then {
  1678. X        item2 := copy(item)    # copy is nonrecursive
  1679. X        item2.POS +:= 1
  1680. X        put(l2, item2)
  1681. X    }
  1682. X    }
  1683. X    if *l2 = 0 then fail
  1684. X    else l2 := slr_closure(l2, st)
  1685. X    #
  1686. X    # Keep track of item lists and symbols we've already seen.
  1687. X    #
  1688. X    /iteml_symbol_table[l] := table()
  1689. X    /iteml_symbol_table[l][symbol] := l2
  1690. X
  1691. X    if *l2 > 0 then
  1692. X    return l2
  1693. X    else fail
  1694. X
  1695. Xend
  1696. X
  1697. X
  1698. X#
  1699. X# make_slr_item_sets: string x list|set -> list
  1700. X#                     (start_sym, st)   -> l
  1701. X#
  1702. X#     Where start_sym is the start symbol for the grammar defined by
  1703. X#     the productions contained in st, and where l is the list of item
  1704. X#     lists generated by the standard LR(0) set-of-items construction
  1705. X#     algorithm.
  1706. X#
  1707. X#     Ignore the third and fourth arguments.  They are used internally
  1708. X#     by recursive calls.
  1709. X#
  1710. Xprocedure make_slr_item_sets(start_sym, st, C, len)
  1711. X
  1712. X    local i, next_items, item_list, new_list, item, symbol
  1713. X
  1714. X    #
  1715. X    # First extend the old start symbol and use the result as the new
  1716. X    # start symbol for the augmented grammar to which the set-of-items
  1717. X    # construction will be applied.
  1718. X    #
  1719. X    # &trace := -1
  1720. X    /C := [slr_closure(
  1721. X        [production("`_" || start_sym || "_'", [start_sym], 1)],st)]
  1722. X    /len := 0
  1723. X
  1724. X    # Iterate through C (the list of item-lists), doing gotos, and adding
  1725. X    # new states, until no more states can be added to C.
  1726. X    #
  1727. X    every item_list := C[i := len+1 to *C] do {
  1728. X    if \DEBUG then
  1729. X        print_item_list(C, i)
  1730. X    # collect all symbols after the dot for the the items in C[i]...
  1731. X    next_items := set()
  1732. X    every item := !item_list do
  1733. X        insert(next_items, item.RHS[item.POS])
  1734. X    # ...now, try to do a slr_goto() for every collected symbol.
  1735. X    every symbol := !next_items do {
  1736. X        new_list := slr_goto(item_list, symbol, st) | next
  1737. X        if not equivalent_item_lists(new_list, !C)
  1738. X        then put(C, new_list)
  1739. X    }
  1740. X    }
  1741. X    # If nothing has been inserted, return C and quit; otherwise, call
  1742. X    # recursively and try again.
  1743. X    #
  1744. X    return {
  1745. X    if i = *C then C
  1746. X    else make_slr_item_sets(&null, st, C, i)
  1747. X    }
  1748. X
  1749. Xend
  1750. X
  1751. X
  1752. END_OF_FILE
  1753.   if test 8228 -ne `wc -c <'slritems.icn'`; then
  1754.     echo shar: \"'slritems.icn'\" unpacked with wrong size!
  1755.   fi
  1756.   # end of 'slritems.icn'
  1757. fi
  1758. echo shar: End of archive 4 \(of 5\).
  1759. cp /dev/null ark4isdone
  1760. MISSING=""
  1761. for I in 1 2 3 4 5 ; do
  1762.     if test ! -f ark${I}isdone ; then
  1763.     MISSING="${MISSING} ${I}"
  1764.     fi
  1765. done
  1766. if test "${MISSING}" = "" ; then
  1767.     echo You have unpacked all 5 archives.
  1768.     rm -f ark[1-9]isdone
  1769. else
  1770.     echo You still must unpack the following archives:
  1771.     echo "        " ${MISSING}
  1772. fi
  1773. exit 0
  1774. exit 0 # Just in case...
  1775.