home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / ibpag2 / part01 next >
Encoding:
Internet Message Format  |  1994-09-25  |  65.5 KB

  1. From: goer@midway.uchicago.edu (Richard L. Goerwitz)
  2. Newsgroups: comp.sources.misc
  3. Subject: v44i094:  ibpag2 - Icon-Based Parser Generation System 2, v1.2, Part01/04
  4. Date: 25 Sep 1994 15:00:56 -0500
  5. Organization: University of Chicago
  6. Sender: kent@sparky.sterling.com
  7. Approved: kent@sparky.sterling.com
  8. Message-ID: <csm-v44i094=ibpag2.150037@sparky.sterling.com>
  9. Reply-To: goer@midway.uchicago.edu
  10. X-Md4-Signature: 451a3edb3dc22c0ddf2fa2005ecfdd61
  11.  
  12. Submitted-by: goer@midway.uchicago.edu (Richard L. Goerwitz)
  13. Posting-number: Volume 44, Issue 94
  14. Archive-name: ibpag2/part01
  15. Environment: Icon, UNIX
  16. Supersedes: ibpag2: Volume 38, Issue 45-49
  17.  
  18. Ibpag2 is a so-called "parser generator," i.e. a tool for automating the 
  19. process of generating a recognizer and/or parser from abstract structural 
  20. descriptions of an input language.  Put in more practical terms, Ibpag2 is 
  21. a piece of software that a) reads a source file containing a grammar that 
  22. defines an input language, and then b) outputs an automaton that recognizes 
  23. that language.  The user may, at his or her option, specify actions this 
  24. automaton should take when it sees various substructures within its input 
  25. language.  By default, however, the parser simply recognizes a given 
  26. sequence as belonging, or not, to that language.  This all may sound pretty 
  27. exotic, but once you get past the jargon you'll find that parser generators 
  28. have very practical (and often quite simple) uses.
  29.  
  30. Ibpag2 utilizes so-called "LR" table generation and parsing
  31. algorithms.  These algorithms facilitate construction of reasonably
  32. fast deterministic pushdown automata that are powerful enough to
  33. handle most commonly used programming language constructs.  LR-based
  34. systems come in three main flavors: SLR(1), LALR(1), and LR(1).  The
  35. LR(1) flavor is fairly easy to implement, but uses too many resources
  36. to be practical.  LALR(1) algorithms are harder to implement, but much
  37. faster, and the parse tables they construct use considerably less
  38. memory than do those of their LR(1) counterparts.  SLR(1) algorithms
  39. are the easiest to implement, compile the fastest, and use about as
  40. much memory as LALR(1)s.  SLR(1) is the least powerful of the three,
  41. though, so there is a tradeoff.  Ibpag2 is an "enhanced" SLR(1) parser
  42. generator.  It is enhanced in the sense that it can operate both in
  43. its native SLR(1) mode, and in a more powerful "quasi-GLR" mode.
  44.  
  45. As its full title ("Icon-Based Parser Generator 2") implies,
  46. Ibpag2 is written in Icon [2,3], as are the automata it creates.
  47. Ibpag2 has been tested with Icon version 8.10 and 9.0.  The only
  48. change from the one version to the other is that under Icon 9.0 Ibpag2
  49. has to be compiled with string invocation turned on (-f s).  So far I
  50. have run Ibpag2 on an i386 (Xenix 2.3.3), an i486 (Linux 1.0.9), and
  51. a Sun 4 (SunOS 4.1.1?).  I have reports, though, of it running under
  52. other UNIX variants.  It will probably also run under other operating
  53. systems, though modifications will in some instances be required.
  54. Using Ibpag2 under MS-DOS may not be possible, on account of the way
  55. DOS manages memory.
  56.  
  57. Any comments on the Ibpag2 system itself or its documentation
  58. will be gratefully received.  Please feel free to drop me, 
  59. Richard Goerwitz, an e-mail message at goer@midway.uchicago.edu, 
  60. or (via the post) at:
  61.  
  62.        5410 S. Ridgewood Ct., 2E
  63.        Chicago, IL   60615
  64.  
  65. ------------
  66. #! /bin/sh
  67. # This is a shell archive.  Remove anything before this line, then feed it
  68. # into a shell via "sh file" or similar.  To overwrite existing files,
  69. # type "sh file -c".
  70. # Contents:  README ibutil.icn
  71. # Wrapped by kent@sparky on Sun Sep 25 14:50:14 1994
  72. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  73. echo If this archive is complete, you will see the following message:
  74. echo '          "shar: End of archive 1 (of 4)."'
  75. if test -f 'README' -a "${1}" != "-c" ; then 
  76.   echo shar: Will not clobber existing file \"'README'\"
  77. else
  78.   echo shar: Extracting \"'README'\" \(53409 characters\)
  79.   sed "s/^X//" >'README' <<'END_OF_FILE'
  80. X
  81. X
  82. X
  83. X
  84. X
  85. X
  86. X               A User's Manual for Ibpag2
  87. X           (Icon-Based Parser Generation System 2)
  88. X                 Version 1.2
  89. X
  90. X                - or -
  91. X
  92. X           How to Use an LR-based Parser Generator
  93. X
  94. X
  95. X               Richard L. Goerwitz, III
  96. X            University of Chicago
  97. X
  98. X
  99. X
  100. X
  101. X
  102. X
  103. X1.__What_is_Ibpag2?
  104. X
  105. X    Ibpag2 is a so-called "parser generator," i.e. a tool for
  106. Xautomating the process of generating a recognizer and/or parser from
  107. Xabstract structural descriptions of an input language.  Put in more
  108. Xpractical terms, Ibpag2 is a piece of software that a) reads a source
  109. Xfile containing a grammar that defines an input language, and then b)
  110. Xoutputs an automaton that recognizes that language.  The user may, at
  111. Xhis or her option, specify actions this automaton should take when it
  112. Xsees various substructures within its input language.  By default,
  113. Xhowever, the parser simply recognizes a given sequence as belonging,
  114. Xor not, to that language.  This all may sound pretty exotic, but once
  115. Xyou get past the jargon you'll find that parser generators have very
  116. Xpractical (and often quite simple) uses.
  117. X
  118. X    Ibpag2 utilizes so-called "LR" table generation and parsing
  119. Xalgorithms.  These algorithms facilitate construction of reasonably
  120. Xfast deterministic pushdown automata that are powerful enough to
  121. Xhandle most commonly used programming language constructs.  LR-based
  122. Xsystems come in three main flavors: SLR(1), LALR(1), and LR(1).  The
  123. XLR(1) flavor is fairly easy to implement, but uses too many resources
  124. Xto be practical.  LALR(1) algorithms are harder to implement, but much
  125. Xfaster, and the parse tables they construct use considerably less
  126. Xmemory than do those of their LR(1) counterparts.  SLR(1) algorithms
  127. Xare the easiest to implement, compile the fastest, and use about as
  128. Xmuch memory as LALR(1)s.  SLR(1) is the least powerful of the three,
  129. Xthough, so there is a tradeoff.  Ibpag2 is an "enhanced" SLR(1) parser
  130. Xgenerator.  It is enhanced in the sense that it can operate both in
  131. Xits native SLR(1) mode, and in a more powerful "quasi-GLR" mode (on
  132. Xwhich, see section 5 below).
  133. X
  134. X    As its full title ("Icon-Based Parser Generator 2") implies,
  135. XIbpag2 is written in Icon [2,3], as are the automata it creates.
  136. XIbpag2 has been tested with Icon version 8.10 and 9.0.  The only
  137. Xchange from the one version to the other is that under Icon 9.0 Ibpag2
  138. Xhas to be compiled with string invocation turned on (-f s).  So far I
  139. Xhave run Ibpag2 on an i386 (Xenix 2.3.3), an i486 (Linux 1.0.9), and
  140. Xa Sun 4 (SunOS 4.1.1?).  I have reports, though, of it running under
  141. Xother UNIX variants.  It will probably also run under other operating
  142. Xsystems, though modifications will in some instances be required.
  143. XUsing Ibpag2 under MS-DOS may not be possible, on account of the way
  144. XDOS manages memory.
  145. X
  146. X    The Ibpag2 distribution adheres to de facto UNIX installation
  147. Xstandards: Just set the appropriate variables in the makefile, and
  148. Xthen "make install."  For those who are using a non-UNIX system, or
  149. Xwho have not installed such a package before, there is a section at
  150. Xthe end entitled "Installing Ibpag2" that details the installation
  151. Xprocedure (section 6).
  152. X
  153. X    Aside from the above-mentioned installation section (6), the
  154. Xremainder of this document aims to provide the reader a) with a
  155. Xsimple, practical explanation of what LR-family parser generators are
  156. Xand how they work (section 2), and b) with a set of directions
  157. Xspecifically on how to use Ibpag2 (section 3).  There is also an
  158. Xadvanced section on debugging (4), and one on using Ibpag2 with non-LR
  159. Xand/or ambiguous languages (5).  The discussion is geared for those
  160. Xthat have little or no experience in parsing or automaton theory.  For
  161. Xvery advanced reading, consult the bibliography.  For a brief summary
  162. Xof Ibpag's command-line options, see the main Ibpag2 source file,
  163. Xibpag2.icn, or invoke ibpag2 with the -h (help) option.
  164. X
  165. X    In general, be warned that Ibpag2 works best with small or
  166. Xmedium-sized grammars.  Its parse tables have to be reconstructed at
  167. Xrun-time, and the code for doing this can become a bit cumbersome for
  168. Xgrammars with more than 100 rules and fifty or so terminal symbols.  I
  169. Xmyself have processed grammars with as many as 300 terminals and 400
  170. Xrules.  Although the resulting automata run well enough, the output
  171. Xfiles are over 300k, and Ibpag2 takes a long time to create them.  If
  172. Xyou must use Ibpag2 with a very large grammar symbols, try the -c
  173. Xcommand-line option (which produces compressed parse tables).  This
  174. Xoption is discussed below, in section 4.  Compiling (rather than
  175. Xinterpreting) Ibpag2 may result in much faster processing, as will
  176. Xresetting your BLOCKSIZE and STRSIZE environment variables.  See the
  177. Xinstallation section (6) below on using the Icon compiler to create
  178. Xthe Ibpag2 executable.  Good starting values for BLOCKSIZE and STRSIZE
  179. Xare triple their default values (i.e. 3 x 65000).  These variables are
  180. Xdiscussed in the Icon manual page.
  181. X
  182. X    My ultimate aim in writing this document has been to make
  183. Xaccessible to the non-CS portion of the Icon community what for them
  184. Xmight seem an inaccessible branch of applied parsing and automaton
  185. Xtheory.  I am a philologist myself, and feel that there is a great
  186. Xdeal that can and ought to be done to make advanced tools accessible
  187. Xto people without much intrinsic interest in computer science other
  188. Xthan to use its tools and methods to get other work done.  Despite my
  189. Xgoal to serve a non-technical audience, this introduction must still
  190. Xget pretty technical at times.  Be patient, and concentrate on the
  191. Xpractical examples.
  192. X
  193. X    Any comments on the Ibpag2 system itself or its documentation
  194. Xwill be gratefully received.  Write to me at the address appended to
  195. Xthe final section (6).
  196. X
  197. X
  198. X2.__What_is_an_LR_Parser_Generator?
  199. X
  200. X    Back in the late 50s and 60s, linguists, mathematicians, and
  201. Xsoftware engineers all became intensely interested in the formal
  202. Xproperties of languages: Could they be described as a series of logical
  203. Xstructures and relations?  Could computers recognize and manipulate
  204. Xthese structures efficiently?  Linguists, in particular, quickly
  205. Xrealized that the amount of structural complexity, ambiguity, and pure
  206. Xnoise in natural language would render it computationally intractable,
  207. Xespecially given the limited memory/throughput of then available CPUs.
  208. XMathematicians and engineers, however, found that many of the
  209. Xformalized notations they dealt with could, in fact, be (re)designed
  210. Xin such a way that efficient computer processing could - at least in
  211. Xprinciple - be achieved.
  212. X
  213. X    Principle, in this case, did not squarely meet reality until
  214. Xviable parser generation tools came into being.  Parser generation
  215. Xtools map an abstract structural description of a formal notation or
  216. X"language" to working computer code.  Ideally, the designer simply
  217. Xmakes assertions like:
  218. X
  219. X    an expression is composed of either
  220. X        1) a term (e.g. 10), or
  221. X        2) an expression, a "+" or "-", and another expression
  222. X
  223. XParser generator systems translate these assertions (the "grammar")
  224. Xinto a machine, i.e. automaton, that can recognize and/or manipulate
  225. Xinput streams that conform to the "language" so described.
  226. X
  227. X    Let me dwell, for a moment, on the toy expression grammar
  228. Xoffered above.  Note that it describes a set of simple mathematical
  229. Xconstructs like:
  230. X
  231. X    9
  232. X    9 + 3
  233. X    9 + 3 - 8
  234. X
  235. XAccording to the specifications given above, the nine, three, and
  236. Xeight alone constitute terms - which are also expressions (via rule
  237. X1).  Because these terms are also expressions, "9 + 3" can be reduced
  238. Xto a larger expression by rule 2.  The same is true for "9 + 3 - 8,"
  239. Xexcept that there rule 2 must apply twice - once for "9 + 3," and then
  240. Xagain for that and the remainder of the line - in effect grouping the
  241. Xexpressions as ( ( (9) + (3) ) - (8) ).  It is also possible to group
  242. Xthe expression ( (9) + ( (3) - (8) ) ), although for the discussion
  243. Xthat immediately follows this second grouping will be ignored (see
  244. Xbelow on the terms "precedence" and "associativity").
  245. X
  246. X    If we add actions to the above grammar specification, we can
  247. Xcreate a calculator-like automaton.  Traditionally, LR-family automata
  248. X(like the ones Ibpag2 creates) contain a parser, one or more stacks,
  249. Xand a set of action tables.  The parser reads from an input stream
  250. Xsegmented into "tokens" (e.g. TERM, '+', '-'), and then manipulates
  251. Xits stacks according to directives contained in so-called "action" and
  252. X"goto" tables.  As it reads the input stream, the parser matches rules
  253. Xwith action code specified by the programmer, e.g. rule 2 above might
  254. Xbe matched with code that added/subtracted the expressions on either
  255. Xside of the '+'/'-' operator, and produced (in calculator style) the
  256. Xresult.  Alternatively, it might be matched with code that generated
  257. Xan equivalent construct in another language.
  258. X
  259. X    In the case of our toy expression grammar above, the
  260. Xcorresponding LR automaton operates as follows.  Omitting and/or
  261. Xsimplifying some of the inner details, it first looks at the input
  262. Xstream to see what the next token is.  If the next token is an
  263. Xoperator or end-of-input, it checks the top of its stack.  If the top
  264. Xof the stack has a term on it, that term is popped off, and pushed
  265. Xback on, this time renamed as an expression (rule 1 above).  The input
  266. Xtoken is then shifted from the input stream onto the stack, unless it
  267. Xis the end-of-input token, in which case the parser returns with a
  268. Xresult.  If the top of the stack has an expression on it (rather than
  269. Xa term), the parser pops the top three elements off of the stack, and
  270. Xthen pushes an expression back on in its place (2).
  271. X
  272. X    Even in this much-simplified form, the automaton's structure
  273. Xseems complex.  Let us take a more detailed look, therefore, at its
  274. Xactual workings.  For the present, let us assume that actual calcula-
  275. Xtions will be performed when expressions are encountered (e.g. if we
  276. Xfind an expression "9 + 3" we will at some point save "12" on a value
  277. Xstack).  The following summary describes what an LR automaton might
  278. Xdo, based on the above toy expression grammar, and given the input
  279. Xstring "9 + 3 + 8":
  280. X
  281. X    1) read the 9, and push it onto the stack as a term
  282. X    2) see a plus sign on the input stream
  283. X    3) pop the term (9) off of the stack and push it back on again
  284. X       (this time calling it an expression)
  285. X    4) push the plus sign onto the stack
  286. X    5) read the 3, and push it onto the stack as a term
  287. X    6) see a minus sign on the input stream
  288. X    7) pop the 3 off of the stack and push it back on again (this
  289. X       time calling it an expression)
  290. X    8) see a minus sign still waiting on the input stream
  291. X    9) pop 9, +, and 3 off of the stack, apply the plus operator
  292. X       to 9 and 3, then push the result onto the stack again a
  293. X       single expression (the stack now has 12 on top)
  294. X    10) read the minus sign, and push it onto the stack
  295. X    11) read the 8, and push it onto the stack as a term
  296. X    12) see the end of input coming up on the input stream
  297. X    13) pop the 8 off of the stack and push it back on again as an
  298. X       expression
  299. X    14) see the end-of-input token still sitting on the input
  300. X       stream 
  301. X    15) pop 12, -, and 8 off of the stack, apply the minus operator
  302. X       to 12 and 8, then push the result onto the stack again (the
  303. X       stack now has 4 on top)
  304. X    16) return the "answer" (i.e. 4)
  305. X
  306. X    This series of actions is hard to describe, and even more so
  307. Xto model as part of a hand-written computer program.  And, even if
  308. Xsuch a program were written by hand, this program would have to be
  309. Xmodified, at times radically, every time the grammar it assumes was
  310. Xaugmented or changed.  What I am leading up to is that, with a parser
  311. Xgenerator, the hand compilation stage can be eliminated by allowing
  312. Xthe programmer simply to declare his/her tokens and language specs,
  313. Xthen have the appropriate automaton constructed with little, or no,
  314. Xhuman intervention.  This is why parser generation tools were critical
  315. Xto the development of not just theoretically feasible, but truly
  316. X*practical*, LR-based computer language design systems.
  317. X
  318. X
  319. X3.__Using_Ibpag2
  320. X
  321. X    To recode the above toy expression grammar in Ibpag2-compatible
  322. Xformat is relatively simple, especially if we omit the actions initially,
  323. Xand concentrate on simple recognition.  We need only a set of token
  324. Xdeclarations and three rules.  Certain modifications will have to be
  325. Xmade to the token declarations later on.  For general illustration's
  326. Xsake, however, the following will suffice:
  327. X
  328. X    %token TERM, '+', '-'
  329. X    %%
  330. X    expression : TERM
  331. X    expression : expression, '+', expression
  332. X    expression : expression, '-', expression
  333. X
  334. XTERM, and the addition and subtraction operators, are the tokens (i.e.
  335. Xthe terminals symbols out of which the grammar is constructed - the
  336. Xthings that the input stream is segmented into).  Note the %token
  337. Xkeyword used to declare them.  The colon means "is composed of."  The
  338. Xdouble percent sign separates token declarations from the grammar
  339. Xproper.
  340. X
  341. X    Adding in our actions - which above were keyed to a complex
  342. Xset of decisions based on input tokens and stack conditions - requires
  343. Xjust a few extra lines of Ibpag2 action code, set off in curly braces:
  344. X
  345. X    %token TERM, '+', '-'
  346. X    %%
  347. X    expression : TERM { return arg1 }
  348. X    expression : expression, '+', expression { return arg1 + arg3 }
  349. X    expression : expression, '-', expression { return arg1 - arg3 }
  350. X
  351. XUsing a "|" shorthand for repeated left-hand sides of rules, we may
  352. Xreformat this as:
  353. X
  354. X    %token TERM, '+', '-'
  355. X    %%
  356. X    expression : TERM { return arg1 }
  357. X           | expression, '+', expression { return arg1 + arg3 }
  358. X           | expression, '-', expression { return arg1 - arg3 }
  359. X
  360. X    ArgX above refers to the Xth element of the right-hand side of
  361. Xthe preceding rule.  So, for example, arg1 in "{ return arg1 }" above
  362. Xrefers to TERM - the only right-hand side element of the first rule.
  363. XThe action "{ return arg1 }" means, "once you find a TERM and have
  364. Xrenamed it as an expression, use the value of TERM as the value for
  365. Xthat expression."  By way of contrast, the action "{ return arg1 +
  366. Xarg3 }" means, in conjunction with the rule it follows: "When you find
  367. Xan expression consisting of a sub-expression, a plus operator, and
  368. Xanother sub-expression, use the value of sub-expression 1 + the value
  369. Xof sub-expression 2 as the value for the expression as a whole."
  370. XTechnically, the action "{ return arg1 }" for expression : TERM is not
  371. Xnecessary, since the Ibpag2 parser, by default, pushes the value of
  372. Xthe last RHS arg onto the stack.  For epsilon productions (to be
  373. Xdiscussed below), it pushes &null.
  374. X
  375. X    One serious problem with this set of specifications is that
  376. Xthe operators '-' and '+' are left associative.  We humans take this
  377. Xfor granted, because correct algebraic grouping is something our
  378. Xhigh-school math teachers burned into us.  The computer, though, has
  379. Xto be told, pedantically, how to group addition and subtraction
  380. Xexpressions.  It has to be explicitly instructed, in other words, to
  381. Xgroup expressions like "9 + 32 - 4" as (9 + 32) - 4.  Without
  382. Xinstructions of this kind, the parser does not know, after it has read
  383. X"9 + 32" and is looking at a minus sign, whether to shift the minus
  384. Xsign onto the stack, and eventually try to group as 9 + (32 - 4), or
  385. Xto reduce "9 + 32" to an expression and group as (9 + 32) - 4.
  386. XAlthough in this case the grouping may not seem to matter, it
  387. Xsometimes does.  Some operators group right to left.  The unary minus
  388. Xsign, for example, is one such operator (--4 groups as (- (- 4))).  To
  389. Xinclude the unary minus sign in our grammar, we might append yet
  390. Xanother rule:
  391. X
  392. X    %token TERM
  393. X    %left '+', '-'
  394. X    %right '-'
  395. X    %%
  396. X    expression : TERM { return arg1 }
  397. X           | expression, '+', expression { return arg1 + arg3 }
  398. X           | expression, '-', expression { return arg1 - arg3 }
  399. X           | '-', expression { return - arg2 }
  400. X
  401. XThe trouble with this arrangement is that the minus sign was already
  402. Xdeclared as left associative.  To get around the conflict we use a
  403. X"dummy" token declaration, and a %prec declaration in the applicable
  404. Xrule:
  405. X
  406. X    %token TERM
  407. X    %left '+', '-'
  408. X    %right UMINUS
  409. X    %%
  410. X    expression : TERM { return arg1 }
  411. X           | expression, '+', expression { return arg1 + arg3 }
  412. X           | expression, '-', expression { return arg1 - arg3 }
  413. X           | '-', expression %prec UMINUS { return - arg2 }
  414. X
  415. XThe %prec declaration simply tells the parser that, even though the
  416. Xrule contains a '-' operator, the rule should be handled as if the
  417. Xoperator were UMINUS.  UMINUS is not actually used as a symbol in the
  418. Xright-hand side of any rule (hence the designation "dummy").  It is
  419. Xthere simply to make the last rule behave as if the minus sign in the
  420. Xlast rule were different than in the second-to-last rule.
  421. X
  422. X    Let us now add in multiplication and division operators to our
  423. Xcalculator specifications, and see what happens.  Let me reiterate
  424. Xhere that the action "{ return arg1 }" for rule 1 (expression : TERM)
  425. Xis not strictly necessary, since the default is to push the last RHS
  426. Xarg onto the value stack:
  427. X
  428. X    %token TERM
  429. X    %left '+', '-'
  430. X    %left '*', '/'
  431. X    %right UMINUS
  432. X    %%
  433. X    expression : TERM { return arg1 }
  434. X           | expression, '+', expression { return arg1 + arg3 }
  435. X           | expression, '-', expression { return arg1 - arg3 }
  436. X           | expression, '*', expression { return arg1 * arg3 }
  437. X           | expression, '/', expression { return arg1 / arg3 }
  438. X           | '-', expression %prec UMINUS { return - arg2 }
  439. X
  440. XNote that the multiplication and division operators were defined
  441. X*after* the addition and subtraction operators.  The reason for this
  442. Xis that, technically speaking, the grammar itself is ambiguous.  If we
  443. Xtreat all operators identically, the parser will not be able to tell
  444. Xwhether "9 + 1 * 3" should be parsed as (9 + 1) * 3 or as 9 + (1 * 3).
  445. XAs we all know from our high-school algebra, multiplication has a
  446. Xhigher precedence than addition.  You do the multiplications before
  447. Xthe additions, in other words, no matter where they occur.  To tell
  448. Xthe parser to behave in this same manner, we declare '*' after '+'.
  449. XNote that, despite their higher priority, the '*' and '/' operators
  450. Xare still left associative.  Hence, given "3 / 4 * 7," the parser will
  451. Xgroup its input as (3 / 4) * 7.  As a brain teaser, try to figure out
  452. Xhow the parser might group the input "9 + 3 / 4 * 7."  Remember that
  453. Xhigher-precedence rules get done first, but that same-precedence rules
  454. Xget done according to associativity.
  455. X
  456. X    The only fundamental problem remaining with the above grammar
  457. Xis that it assumes that the end of the input coincides with the end of
  458. Xthe line.  Is it possible to redefine the language described as
  459. Xconsisting of arbitrary many lines?  The answer to this question is
  460. X"yes."  One can simply add another set of productions to the grammar
  461. Xthat state, essentially, that the input language consists of lines
  462. Xmade up of an expression and a carriage return or of nothing.  Nothing
  463. Xis indicated by the keyword epsilon.  Note that only the first rule
  464. Xhas an action field:
  465. X
  466. X    lines    : lines, expression, '\n'    { write(arg2) }
  467. X        | lines, '\n'
  468. X        | epsilon
  469. X
  470. XThis rule-series may seem rather abstruse, but it becomes a bit
  471. Xclearer when you think about what happens on actual input.  If there
  472. Xis no input (epsilon), nothing gets printed, because lines : epsilon
  473. Xhas no action field.  If the parser sees an expression and a newline,
  474. Xthe parser takes this as an instance of epsilon, plus an expression,
  475. Xplus a newline.  This, then, becomes the first component of rule 1 if
  476. Xanother expression + newline follows, or of rule two if just a newline
  477. Xoccurs.  Every time an instance of rule 1 occurs, the action "{
  478. Xwrite(arg2) }" is executed, i.e. the value of the expression gets
  479. Xprinted.  If this still seems hard to fathom, try walking through
  480. Xstep-by-step.  Even experienced hands may find these sorts of rules
  481. Xdifficult to construct and debug.
  482. X
  483. X    Note that "lines" is now the so-called "start symbol" of our
  484. Xgrammar.  It is, in other words, the goal of every parse.  By default
  485. Xthe left-hand side symbol of the first rule is the start symbol.  This
  486. Xmay be overridden with a %start declaration in the tokens section (on
  487. Xwhich, see the sample Ibpag2 input file below).
  488. X
  489. X    With our new, multi-line start symbol in place, the only piece
  490. Xthat needs to be added, in order to make our calculator specification
  491. Xa full working input to Ibpag2, is a tokenizer.  A tokenizer is a
  492. Xroutine that reads input from a file or from some other stream (e.g.
  493. Xthe user's console), and then segments this input into tokens that its
  494. Xparser can understand.  In some cases, the tokens must be accompanied
  495. Xby a literal value.  For example, if we encounter a TERM, we return
  496. XTERM, just as it is listed in the %token declaration.  But what is the
  497. Xliteral value of a TERM token?  It could be, for example, 9, or 5, or
  498. X700.  The tokenizer returns the symbol TERM, in this case, but then
  499. Xrecords that TERM's actual value by setting some global variable.  In
  500. XIbpag2's parser, this variable is assumed to be "iilval."  In the
  501. Xtokenizer, therefore, one might write
  502. X
  503. X    iilval := (literal value)
  504. X    suspend TERM
  505. X
  506. XFor literal operators like '+' and '*', there is no need to set
  507. Xiilval, since their literal value is irrelevant.  One simply returns
  508. Xthese as integers (usually via "suspend ord(c)").
  509. X
  510. X    The tokenizer routine is normally appended to the grammar
  511. Xafter another double percent sign.  Everything after this second
  512. Xdouble percent sign is copied literally to the output file.
  513. XAlternatively, the tokenizer can be $included via Icon's preprocessor.
  514. XIbpag2 demands that the tokenizer be called iilex, and that it take a
  515. Xsingle file argument, that it be a generator, and that it fail when it
  516. Xreaches end-of-input.  Combined with our "lines" productions, the
  517. Xaddition of an iilex routine to our calculator grammar yields the
  518. Xfollowing Ibpag2 input file:
  519. X
  520. X    %token TERM
  521. X    %left '+', '-'
  522. X    %left '*', '/'
  523. X    %right UMINUS
  524. X
  525. X    %start lines
  526. X
  527. X    %%
  528. X
  529. X    expression : TERM { return arg1 }
  530. X           | expression, '+', expression { return arg1 + arg3 }
  531. X           | expression, '-', expression { return arg1 - arg3 }
  532. X           | expression, '*', expression { return arg1 * arg3 }
  533. X           | expression, '/', expression { return arg1 / arg3 }
  534. X           | '-', expression %prec UMINUS { return - arg2 }
  535. X
  536. X    lines       : lines, expression, '\n'    { write(arg2) }
  537. X           | lines, '\n'
  538. X           | epsilon
  539. X
  540. X    %%
  541. X
  542. X    procedure iilex(infile)
  543. X
  544. X        local nextchar, c, num
  545. X
  546. X        nextchar := create !(!infile || "\n" || "\n")
  547. X        c := @nextchar | fail
  548. X
  549. X        repeat {
  550. X        if any(&digits, c) then {
  551. X            if not (\num ||:= c) then
  552. X            num := c
  553. X        } else {
  554. X            if iilval := \num then {
  555. X            suspend TERM
  556. X            num := &null
  557. X            }
  558. X            if any('+-*/()\n', c) then {
  559. X            iilval := c
  560. X            suspend ord(c)
  561. X            } else {
  562. X            if not any(' \t', c) then {
  563. X                # deliberate error - will be handled later
  564. X                suspend &null
  565. X            }
  566. X            }
  567. X        }
  568. X        c := @nextchar | break
  569. X        }
  570. X        if iilval := \num then {
  571. X        return TERM
  572. X        num := &null
  573. X        }
  574. X
  575. X    end
  576. X
  577. X    procedure main()
  578. X        return iiparse(&input, 1)
  579. X    end
  580. X
  581. XAs noted above, the tokenizer (iilex) must be a generator.  It must
  582. Xsuspend integers either directly (e.g. ord(c)), or else via symbolic
  583. Xdefines like TERM, created by Ibpag2 on the basis of %token, %right,
  584. X%left, and %nonassoc declarations.  The tokenizer must fail on end of   
  585. Xinput.
  586. X
  587. X    If you like, cut the above code out, place it in a temporary file,
  588. Xtmp.ibp, and then feed this file to Ibpag2 by typing "ibpag2 -f tmp.ibp -o
  589. Xtmp.icn."  If your system supports input and output redirection, type:
  590. X"ibpag2 < tmp.ibp > tmp.icn."  Ibpag2 will turn your grammar specifications
  591. Xand actions into a routine called iiparse.  If you look above, you will see
  592. Xthat I appended a main procedure that, in fact, calls iiparse().  Iiparse()
  593. Xtakes two arguments: 1) an input stream, and 2) a switch that, if nonnull,
  594. Xtells the parser to fail rather than abort on unrecoverable errors.  When
  595. XIbpag2 is finished creating its output file (tmp.icn above), compile that
  596. Xfile the way you would compile any other Icon program, except that full
  597. Xstring invocation must be enabled (e.g. "icont -f s tmp").  Finally, run the
  598. Xexecutable.  You should be able to type in various simple arithmetic
  599. Xexpressions and have the program spit back answers each time you hit a
  600. Xreturn.  The only problem you might encounter is that the parser aborts on
  601. Xerroneous input.
  602. X
  603. X    The issue of erroneous input brings up yet another point of
  604. Xgeneral Ibpag2 usage.  Normally, if one is processing input, one does
  605. Xnot want to abort on errors, but rather just emit an error message,
  606. Xand to continue processing - if this is at all possible.  To do this,
  607. XIbpag2 provides a simple but fairly effective mechanism: A reserved
  608. X"error" token.
  609. X
  610. X    When Ibpag2 encounters an error, it will remove symbols from
  611. Xits stack until it has backtracked to a point where the error token is
  612. Xlegal.  It then shifts the error token onto the stack, and tries to
  613. Xre-start the token stream at the point where it left off, discarding
  614. Xtokens if necessary in order to get itself resynchronized.  The parser
  615. Xconsiders itself resynchronized when it has successfully read and
  616. Xshifted three tokens after shifting the error token.  Until then it
  617. Xremains in an error state, and will not output additional error
  618. Xmessages as it discards tokens.
  619. X
  620. X    This explanation may sound a bit abstruse, but in practice it
  621. Xis turns out to be quite simple.  To implement error handling for our
  622. Xcalculator, we really have to add only one production to the end of
  623. Xthe "lines" section:
  624. X
  625. X    lines       : lines, expression, '\n'    { write(arg2) }
  626. X           | lines, '\n'
  627. X           | epsilon
  628. X           | error, '\n'    {
  629. X                      write("syntax error; try again:")
  630. X                      iierrok
  631. X                    }
  632. X
  633. XGiven the above grammar, the parser will handle errors as follows: If
  634. Xan error occurs (say it has an expression then an operator on its
  635. Xstack and sees a newline on the input stream) the parser will throw
  636. Xout the operator, then check if the error token would be OK in this
  637. Xstate (which it would not).  Then it would throw out the expression.
  638. XAt this point, the stack is in the ready-to-read-a-lines state - the
  639. Xstate it was in before it read the last expression.  Since "lines" may
  640. Xconsist of error and '\n,' the error token is legal here, and so the
  641. Xparser pushes error onto the stack, then looks back at the input
  642. Xstream (where a newline is still waiting).  Since the newline now
  643. Xcompletes the rule lines : error, '\n', the parser pushes the newline
  644. Xonto its stack, then executes the action associated with this
  645. Xproduction, i.e. it writes "syntax error; try again:" to the console,
  646. Xprompting the user for additional input.
  647. X
  648. X    The keyword "iierrok" in the above error production's action
  649. Xfield is there for a subtle, but important, reason: It tells the
  650. Xparser to consider itself resynchronized, even if three tokens have
  651. Xnot yet been shifted.  If iierrok were not in the action code for this
  652. Xrule, and the user were to supply more bad input after the prompt,
  653. Xthen the parser would simply discard those tokens, without emitting
  654. Xanother error message.  Why?  Because, as you will recall, the parser
  655. Xdiscards tokens after an error, in efforts to resynchronize itself.
  656. XUntil it reads and shifts three tokens successfully, it considers
  657. Xitself in an error state, and will not emit additional error messages.
  658. XThe three-token resync rule is there to prevent a cascade of
  659. Xirrelevant error messages touched off by a single error.  In our
  660. Xcalculator's case above, though, we are smarter than the parser.  We
  661. Xknow that it is resynchronized as soon as it reduces error, '\n' to
  662. Xlines.  So if a syntax error occurs on the next token, it should be
  663. Xreported.  Adding "iierrok" to the action insures that the parser will
  664. Xdo just this.
  665. X
  666. X    In addition to iierrok, there are several other directives
  667. XIbpag2 accepts as part of the action code segments.  These are as
  668. Xfollows:
  669. X
  670. X    iiclearin        clear the current input token
  671. X    IIERROR            perform error recovery
  672. X    IIACCEPT        simulate an accept action
  673. X
  674. XThere are several other directives (all implemented as macros) that
  675. XIbpag2 accepts in GLR mode.  For a discussion of GLR mode, see below,
  676. Xsection 5.  IIERROR in particular, and error recovery in general, work
  677. Xa bit differently in that mode than they do in Ibpag2's normal (i.e.
  678. XLR) mode.
  679. X
  680. X    There are admittedly many other topics that might be covered
  681. Xhere.  This treatment, however, is intended as a general nontechnical
  682. Xintroduction, and not as a complete textbook on parser generation use.
  683. XIf you want to learn more about this topic, consult the bibliography.
  684. XAlso, check the UNIX manual pages on the YACC utility (Yet Another
  685. XCompiler Compiler).  Ibpag's input format is fairly close (too close,
  686. Xperhaps) to YACC's.  In fact, most of what is said about YACC in UNIX
  687. Xdocumentation can be carried directly over to Ibpag2.  Several salient
  688. Xdifferences, though, should be kept in mind:
  689. X
  690. X        1) YACC's "$$ = x" constructs are replaced by "return x" (e.g.
  691. X           "$$ = $1 + $3" -> "return $1 + $3" [$1 is a synonym for
  692. X       "arg1", $3 for "arg3", etc.])
  693. X
  694. X        2) all variables within a given action are, by default, local
  695. X           to that action; i.e. they cannot be accessed by other
  696. X           actions unless you declare them global elsewhere (e.g. in
  697. X           the pass-through part of the declarations section %{ ...
  698. X           %})
  699. X
  700. X        3) the %union and %type declarations/tags are not needed by
  701. X       Ibpag2 (both for better and for worse)
  702. X
  703. X        4) tokens and symbols are separated from each other by a comma
  704. X           in Ibpag2 files (e.g. %token '+', '-' and S : NP, VP)
  705. X
  706. X        5) epsilon is indicated by the keyword "epsilon" (e.g. REL :
  707. X           epsilon), and not by an empty RHS
  708. X
  709. X        6) both epsilon and error *may* be declared as %tokens for
  710. X           reasons of precedence, although they retain hard-coded
  711. X           internal values (-2 and -1, respectively)
  712. X
  713. X        7) all actions must follow the last RHS symbol of the rule
  714. X           they apply to (preceded by an optional %prec directive); to
  715. X           achieve S : NP { action1 }, VP { action2 }, insert a dummy
  716. X           rule: S : NP, dummy, VP { action2 }; dummy : epsilon {
  717. X           action1 } ;
  718. X
  719. X        8) YYERROR, YYACCEPT, yyclearin, and yyerrok are the same,
  720. X           except they are written IIERROR, IIACCEPT, iiclearin, and
  721. X           iierrok (i.e. "ii" replaces "yy")
  722. X
  723. X        9) Ibpag2's input files are tokenized as modified Icon files,
  724. X           and, as a consequence, Icon's reserved words must not be
  725. X           used as symbols (e.g. "if : if, then" is no go)
  726. X
  727. XI myself find YACC to be ugly.  As a result, Ibpag2 is not an exact
  728. XYACC clone.  I would like to underscore the fact that I have no
  729. Xintention to move in this direction, either.  It's as YACC-like as
  730. Xit's going to get!
  731. X
  732. X    Both YACC and non-YACC users should note number 9 in the above
  733. Xlist.  Don't use things like "while," "every," "do," etc. as symbols
  734. Xin your grammar!  Just use the same rules for Ibpag2 nonterminals as
  735. Xfor Icon variables, and you'll be OK.
  736. X
  737. X    For those that just can't bear using anything but a strictly
  738. XYACC-conformant system, I've included a preprocessor with the Ibpag2
  739. Xdistribution called (at one user's recommendation) "iacc."  Iacc reads
  740. X&input - assumed to be a YACCish grammar - and sends to &output an
  741. XIbpag2-conformant file.  I have not tested this file extensively, and
  742. Xthere are likely to be bugs in the way I've handled the necessary 2
  743. Xtoken lookaheads and value stack references.  Give it a whirl, though,
  744. Xif you are feeling adventurous.  The only reason I personally use Iacc
  745. Xis that some YACCs (e.g. BSD YACC) have particularly nice debugging
  746. Xmessages and help.  If my grammar is particularly complex, I just run
  747. Xit through YACC without action code first, then use Iacc to convert it
  748. Xto Ibpag2 format.  Iacc's output, as I noted, is not meant to be
  749. Xpretty, so I invariably end up doing a little editing - usually just
  750. Xrespacing a few rules, and re-inserting any comments that I might have
  751. Xput in the original YACC file.
  752. X
  753. X    In general, Ibpag2 (like YACC) handles epsilon moves and
  754. Xindirect cycles.  LR-mode shift-reduce conflicts are also handled in
  755. Xthe normal way (i.e. pick the rule with the highest priority, and, in
  756. Xcases where the priority is the same, check the associativities).  In
  757. Xcontrast to YACC, Ibpag2 flags reduce/reduce conflicts as errors
  758. X(since these often conceal deeper precedence problems and just plain
  759. Xkludges).  Reduce/reduce conflict errors are easily enough remedied,
  760. Xif need be, via (dummy) precedences.  One can convert these errors to
  761. Xwarnings by specifying -y on the command line.  With the -y option,
  762. Xreduce/reduce conflicts are resolved in favor of the rule that occurs
  763. Xfirst in the grammar.  The -y switch also prevents Ibpag2 from
  764. Xaborting on shift/reduce conflicts, telling it instead to resolve in
  765. Xfavor of shift.  Basically, -y is a partial YACC compatibility switch.
  766. XNormally (i.e. in SLR mode) Ibpag2 is much more finicky than YACC
  767. Xabout conflicts in its grammars.
  768. X
  769. X    Also in contrast to YACC, Ibpag2 supports multiple
  770. Xsimultaneous parsers.  Ibpag2 normally names its main parser routine
  771. Xiiparse().  By using the -m command-line option, however, you can
  772. Xoverride this default behavior, and force Ibpag2 to augment this name
  773. Xin some uniquely identifiable fashion.  For example, "ibpag2 -m _1 <
  774. Xtmp.ibp > tmp.icn" will force Ibpag2 to write a parser called
  775. X"iiparse_1" to tmp.icn.  Note that, instead of calling iilex, this
  776. Xiiparse_1() routine will now call iilex_1, and all necessary global
  777. Xvariables will have _1 appended to them (e.g. errors will become
  778. Xerrors_1).  I don't expect that many people will have occasion to use
  779. Xthis feature.  It is there, though, for those that want it.
  780. X
  781. X
  782. X4.__Debugging
  783. X
  784. X    Constructing and debugging LR(1) family parsers can sometimes
  785. Xbe hair raising, even with a parser generator.  Several precautions
  786. Xcan be taken, however, to minimize the agony.  The first is to declare
  787. Xall tokens initially as part of a single %token declaration, i.e. with
  788. Xno precedences, and with the same associativities.  Also, leave out
  789. Xaction code until the grammar seems to be working.  In this stage, you
  790. Xcan even run the grammar through (BSD)YACC or GNU Bison.  All you
  791. Xwould need to do is remove the commas between tokens and symbols, and
  792. Xplace a semicolon at the end of every rule.  During this and all
  793. Xdebugging stages, supply Ibpag2 with a -v command-line switch.  This
  794. Xwill cause Ibpag2 to write a summary of rules, tokens, and its two
  795. Xstate tables to "ibpag2.output" (a bit like GNU Bison, but with a
  796. Xhard-coded name).  If you get messages about conflicts in your parse
  797. Xtables (e.g.  "unresolvable reduce/reduce conflict, state 5, token
  798. X257, rules 4,5").  This file will tell you what rules these are, and
  799. Xwhat token number 257 is.  Use precedences and associativities to
  800. Xclear these problems up as they arise.  If you are comfortable having
  801. Xreduce/reduce errors resolved by the order in which the conflicting
  802. Xrules occur, then use the -y command-line switch.  With -y on the
  803. Xcommand line, Ibpag2 will always resolve in favor of the earlier rule.
  804. XThis option will also cause it to resolve all shift/reduce conflicts
  805. Xin favor of shift.
  806. X
  807. X    There are certain languages that are not ambiguous that SLR(1)
  808. Xparsers like Ibpag2 will fail to produce an unambiguous parse table
  809. Xfor.  The classic example is
  810. X
  811. X    expr : lval, '=', rval | rval
  812. X    lval : '*', rval       | ID
  813. X    rval : lval
  814. X
  815. XC programmers will recognize this as a toy expression grammar with
  816. Xcode for identifiers, assignments, and pointers.  The problem is that
  817. Xif we feed this grammar to Ibpag2, it will claim that there is a
  818. Xconflict on lookahead '='.  In truth, there is no ambiguity.  The SLR
  819. Xparser simply doesn't remember the pathway the parser used to get to
  820. Xthe state it is in when it sees '=' on the input stream.  Whether the
  821. Xparser gets into this state by seeing '*' plus and ID, or by seeing
  822. Xjust an ID, it knows to turn the ID into an lval.  Then it knows to
  823. Xturn lval into rval.  At this point, though, it doesn't know whether
  824. Xto shift the = sign via rule 1, or to turn rval and the preceding '*'
  825. Xinto an lval.  The parser has "forgotten" that the '*' is there
  826. Xwaiting on level down on the stack!
  827. X
  828. X    The solution to this problem is actually quite simple (at
  829. Xleast in concept).  Just provide a unique pathway in the grammar for
  830. Xthe conflicting rules.  In this case, they are rules 1 and 5 (the
  831. Xfirst and last):
  832. X
  833. X    expr : lval, '=', rval | rval
  834. X    lval : '*', pval | ID
  835. X    pval : lval
  836. X    rval : lval
  837. X
  838. XNow when the parser sees '*,' it can only have a pval after it.  Never
  839. Xmind that pval is composed of precisely the same things as rval.  The
  840. Xpoint is that the parser generator follows a different route after
  841. Xseeing '*' than if it starts with ID and no preceding '*'.  Hence it
  842. X"remembers" that that the '*' is back on the stack, waiting for the
  843. X"lval : '*', pval" rule to apply.  There is no more conflict.
  844. X
  845. X    Go ahead and run these grammars through Ibpag2 if you aren't
  846. Xsure what is going on.  Remember to declare ID as a token, and to
  847. Xplace "%%" in the appropriate spot!
  848. X
  849. X    If you get your parser up and running, but find that it is not
  850. Xfunctioning quite the way you expect, add the following line somewhere
  851. Xnear the start of Ibpag2's output file:
  852. X
  853. X    $define IIDEBUG
  854. X
  855. XIf you like, you can add it to the beginning of your Ibpag2 input
  856. Xfile.  Place it in the declarations section (before the first double
  857. Xpercent sign), and surround it by %{ and %}, e.g.:
  858. X
  859. X    %{
  860. X    $define IIDEBUG
  861. X    %}
  862. X
  863. XThis tells Ibpag2 to send $define IIDEBUG straight through to the
  864. Xoutput file.
  865. X
  866. X    What defining IIDEBUG does is tell iiparse, once compiled, to
  867. Xemit profuse debugging messages about the parser's actions, and about
  868. Xthe state of its stacks.  This display will not make a whole lot of
  869. Xsense to anyone who doesn't understand LR-family parsers, so those who
  870. Xwant to access this feature should perhaps go through a standard
  871. Xreference like Aho, Sethi, and Ullman [1].
  872. X
  873. X    If, after you are finished debugging your grammar, you find
  874. Xthat Ibpag2's output files are rather large, you may try saving space
  875. Xby compressing the action and goto tables.  This is accomplished by
  876. Xinvoking Ibpag2 with the -c (compress) option.  Using this option
  877. Xmakes debugging difficult, and makes the parser run a bit more slowly.
  878. XIt also only works for rather large grammars with long nonterminal
  879. Xsymbol names.  Don't even consider it until the grammar is thoroughly
  880. Xdebugged and you have determined that the output file's size is just
  881. Xtoo great for practical use.  Even then, compression may or may not
  882. Xhelp, depending on how long your nonterminal names are.  In general,
  883. XIbpag2 is best as a teaching tool, or as a production system for
  884. Xmedium or small grammars.
  885. X
  886. X
  887. X5.__Using_Ibpag2_with_Non-LR_Grammars
  888. X
  889. X    There may be times when you *want* to parse languages that no
  890. XLR-based algorithm can handle.  There may be times, that is, when the
  891. Xgrammar you want to use contains conflicts or ambiguities that are
  892. Xthere by design, and not by oversight.  For example, you may want to
  893. Xparse a natural language.  Full-blown natural languages involve many
  894. Xhighly ambiguous constructs, and are not LR-parsable.  By invoking it
  895. Xwith the -a option, Ibpag2 can parse or recognize certain natural
  896. Xlanguages, or, more practically speaking, certain NL subsets.  The
  897. Xletter "a" in -a is supposed to stand for "ambiguous," although what
  898. Xthis option really does is put Ibpag2 into a quasi-GLR mode - i.e.
  899. Xinto a kind of "generalized" LR mode in which it can accept non-LR
  900. Xgrammars [4,5].
  901. X
  902. X    User-visible changes to Ibpag2's operation in quasi-GLR mode
  903. X(i.e. with the -a option) are as follows:
  904. X
  905. X    1) iiparse() is now a generator
  906. X    2) action code can use suspend as well as return
  907. X    3) IIERROR places the current thread in an error state (i.e.
  908. X       it doesn't *necessarily* trigger error recovery; see below)
  909. X    4) there are two new action-code directives (iiprune and
  910. X           iiisolate) and a general define (AUTO_PRUNE)
  911. X    5) conflicts due to ambiguities in the grammar no longer
  912. X       result in aborted processing (so, e.g., if you do not
  913. X       specify the -y option on a grammar with reduce/reduce
  914. X       conflicts, Ibpag2 will simply generate a parser capable of
  915. X       producing multiple parses for the same input)
  916. X
  917. X    In quasi-GLR mode, iiparse() should be invoked in a way that
  918. Xwill render multiple results usable, if they are available (e.g.
  919. X"every result := iiparse(&input) do...".  Action code is also allowed
  920. Xto produce more than one value (i.e. to use suspend).  When it does
  921. Xso, iiparse() creates separate parse threads for each value.  So, for
  922. Xinstance, if your action code for some production suspends both of the
  923. Xfollowing lists,
  924. X
  925. X    ["noun", "will", "gloss: desire"]
  926. X    ["noun", "will", "gloss: legal document mandating how _
  927. X                      one's possessions are to be disposed _
  928. X              of after one's death"],
  929. X
  930. Xiiparse() would create two separate parse threads - one for each
  931. Xresult.  Note that in this case, the syntactic structure of each
  932. Xthread is the same.  It is their semantics (i.e. the stuff on the
  933. Xvalue stack) that differs.
  934. X
  935. X    If you use the iierrok and iiclearin macros in your action
  936. Xcode before suspending any result, their affect persists through all
  937. Xsubseqent suspensions and resulting parse threads.  If you use these
  938. Xmacros after suspending one or more times, however, they are valid
  939. Xonly for the parse thread generated by the next suspension.  By way of
  940. Xcontrast, the IIERROR macro *always* flags only the next parse thread
  941. Xas erroneous.  Likewise, IIACCEPT always simulates an accept action on
  942. Xthe next suspension only.  IIERROR and IIACCEPT, in other words, never
  943. Xhave any effect on subsequent suspensions and parse threads other than
  944. Xthe one that immediately follows them.  This is true of iierrok and
  945. Xiiclearin only when used after the first suspension.
  946. X
  947. X    In quasi-GLR mode, IIERROR (number three in the difference
  948. Xlist above) becomes a mechanism for placing the current parse thread
  949. Xin error mode.  This is similar to, but not quite identical to, how
  950. XIIERROR functions in straight LR mode.  In quasi-GLR mode, if other
  951. Xthreads can carry on the parse without error the erroneous parse
  952. Xthread is quietly clobbered.  Full-blown error recovery only occurs if
  953. Xall of the other parsers halt as well.  This makes sense if you think
  954. Xabout it.  Why keep erroneous threads around when there are threads
  955. Xstill continuing a valid parse?  For some large interactive systems,
  956. Xit might be necessary to keep bogus threads around longer, and weed
  957. Xthem out only after a lengthy grading process.  If you are
  958. Xconstructing a system such as this, you'll have to modify Ibpag2's
  959. Xiiglrpar.lib file.  In particular, you'll need to change the segment
  960. Xin iiparse() that takes out the trash, so to speak, in such a way that
  961. Xit does so only if the error count in a given parser either rises
  962. Xabove a specific threshhold or else exceeds the number of errors in
  963. Xthe "most correct" parser by a certain amount.  This is not that hard
  964. Xto do.  I just don't expect that most parsers people generate with
  965. XIbpag2 will use IIERROR or error recovery in general in so involved a
  966. Xfashion.
  967. X
  968. X    Iiprune and iiisolate (number 4 above) are used to control the
  969. Xgrowth of the parallel parser array.  In order to give straightforward
  970. X(read "implementationally trivial") support for action code, Ibpag2
  971. Xcannot create a parse "forest" in the sense that a standard GLR parser
  972. Xdoes.  Instead, it simply duplicates the current parser environment
  973. Xwhenever it encounters a conflict in its action table.  Even if the
  974. Xconflict turns out to reflect only a local ambiguity, the parsers, by
  975. Xdefault, remain separate.  Put differently, Ibpag2's quasi-GLR parser,
  976. Xby default, makes no direct effort to reduce the size of its parser
  977. Xarrays or to alter the essentially linear structure of their value and
  978. Xstate stacks.  Size reduction, where necessary and/or desirable, is up
  979. Xto the programmer.  What the iiprune macro is there to do is to give
  980. Xthe programmer a way of pruning a given thread out of the active
  981. Xparser list.  Iiisolate allows him or her to prune out every thread
  982. X*but* the current one.  AUTO_PRUNE makes the parser behave more like a
  983. Xstandard GLR parser, instructing it to prune parse threads that are
  984. Xessentially duplicating another parse thread's efforts.  The parser,
  985. Xthough, does not build a parse tree per se, the way most GLR parsers
  986. Xtypically do, but rather manipulates its value stack like a
  987. Xtraditional LR-family parser.
  988. X
  989. X    Iiprune is useful when, for example, the semantics (i.e. your
  990. X"action" code segments) determine that a given parse thread is no
  991. Xlonger viable, and you want to signal the syntactic analyzer not to
  992. Xcontinue pursuing it.  The difference between iiprune and IIERROR is
  993. Xthat iiprune clobbers the current parser immediately.  IIERROR only
  994. Xputs it into an error state.  If all active parsers end up in an error
  995. Xstate, and none can shift additional input symbols, then the IIERROR
  996. Xmacro induces error recovery.  Iiprune does not.  NB: iiprune, if used
  997. Xin action code that suspends multiple results, cancels the current and
  998. Xremaining results (i.e. it does not clobber parsers already spun off
  999. Xby previous suspensions by invocation of that same code; it merely
  1000. Xcuts the result sequence).  Iiprune essentially stands in for "fail"
  1001. Xin this situation.  Fail itself can be used in the code, but be warned
  1002. Xthat iiparse() will still push *at least one* value onto its value
  1003. Xstack, even if a given action code segment fails.  This keeps the
  1004. Xvalue stack in sync with the syntax.  To avoid confusion, I recommend
  1005. Xnot using "fail" in any action code.
  1006. X
  1007. X    Iiisolate is useful if, during error recovery, you prompt the
  1008. Xuser interactively, or do something else that cannot be elegantly done
  1009. Xin parallel for two or more distinct parse threads.  Iiisolate allows
  1010. Xyou to preserve only the the current parse thread, and to clobber the
  1011. Xrest.  Iiisolate can also be useful as a way of making sure that only
  1012. Xone thread carries on the parse in non-error situations.  Suppose that
  1013. Xwe have a series of productions:
  1014. X
  1015. X    sentences  :  sentences sentence '\n'
  1016. X            { print_parse(arg2) }
  1017. X           |  sentences '\n'
  1018. X           |  error '\n'
  1019. X           |  epsilon
  1020. X
  1021. XIf we get a sentence with more than one parse, all of the underlying
  1022. Xthreads that produced these parses will be active for the next
  1023. Xsentence as well.  In many situations this will not be what we want.
  1024. XIf our desire it to have only one active parse thread at the start of
  1025. Xeach sentence, we simply tell our lexical analyzer to suspend two
  1026. Xnewlines every time it sees a newline on the input stream.  This
  1027. Xinsures that the second rule will always apply right after the first.
  1028. XWe then insert iiisolate directives for both it and the one error
  1029. Xproduction:
  1030. X
  1031. X    sentences  :  sentences sentence '\n'
  1032. X            { print_parse(arg2) }
  1033. X           |  sentences '\n'
  1034. X            { iiisolate }
  1035. X           |  error '\n'
  1036. X            { iiisolate; iierrok }
  1037. X           |  epsilon
  1038. X
  1039. XThe effect here is to allow multiple parsers to be generated only
  1040. Xwhile parsing "sentence".  The iiisolate directive, in other words,
  1041. Xsees to it that no sentence parse will ever begin with multiple active
  1042. Xparsers.  As with LR mode, iierrok clears the error flag for the
  1043. X(current) parser.
  1044. X
  1045. X    Note that if you use iiisolate in action code that suspends
  1046. Xmultiple results, iiisolate will clobber all parsers but the one
  1047. Xgenerated by the next suspension.
  1048. X
  1049. X    If there is no need for close control over the details of the
  1050. Xparser array, and you wish only to clobber parsers that end up doing
  1051. Xthe same thing as some other parser (and hence returning identical
  1052. Xvalues), then just make sure you add "$define AUTO_PRUNE" to the
  1053. Xpass-through code section at the top of the file.  Put differently,
  1054. Xdefining AUTO_PRUNE instructs the quasi-GLR parser to weed out parsers
  1055. Xthat are in the same state, and which have identical value stacks.
  1056. XAUTO_PRUNE can often be used in place of iiisolate in situations like
  1057. Xthe one discussed just above.  Its only drawback is that it slows
  1058. Xthe parser a bit.
  1059. X
  1060. X    Other than these deviations (action code and iiparse becoming
  1061. Xgenerators, IIERROR's altered behavior, and the addition of iiprune,
  1062. Xiiisolate, and AUTO_PRUNE), Ibpag2's quasi-GLR mode - at least on the
  1063. Xsurface - works pretty much like its straight LR mode.  In fact, if
  1064. Xyou take one of your SLR(1) grammars, and run it through Ibpag2 using
  1065. Xthe -a option, you probably won't notice any difference in the
  1066. Xresulting automaton unless you do some debugging or perform some
  1067. Xtiming tests (the GLR parser is slower, though for straight SLR(1)
  1068. Xgrammars not by much).  Even with non-SLR(1) grammars, the quasi-GLR
  1069. Xparser will clip along merrily, using all the same sorts of rules,
  1070. Xaction code, and macros that you would typically use in LR mode!
  1071. X
  1072. X
  1073. X6.__Installing_Ibpag
  1074. X
  1075. X    If you are a UNIX user, or have a generic "make" utility, you
  1076. Xare in luck.  Just edit Makefile.dist according to the directions
  1077. Xgiven in that file, rename it as "makefile," then execute "make."
  1078. XIbpag2 should be created automatically.  If everything goes smoothly,
  1079. Xthen "make install" (su-ing root, if both possible and necessary for
  1080. Xcorrect installation of the iiparse.icn file).  Check with your system
  1081. Xadministrator if you are on a public system, and aren't sure what to
  1082. Xdo.
  1083. X
  1084. X    Please be sure to read the directions in the makefile
  1085. Xcarefully, and set DESTDIR and LIBDIR to the directory where you want
  1086. Xthe executable and parser file to reside.  Also, make sure the paths
  1087. Xyou specify are correct for your Icon executables.  Although Ibpag2
  1088. Xwill apparently compile using iconc, I would recommend using the
  1089. Xinterpreter, icont, first, unless you are planning on working with a
  1090. Xlarge grammar.
  1091. X
  1092. X    If you are using some other system - one that lacks "make" -
  1093. Xthen shame on your manufacturer :-).  You'll be a bit inconvenienced.
  1094. XTry typing:
  1095. X
  1096. X    icont -o ibpag2 follow.icn ibpag2.icn ibreader.icn \
  1097. X         ibtokens.icn ibutil.icn ibwriter.icn iohno.icn \
  1098. X         outbits.icn slritems.icn slrtbls.icn shrnktbl.icn \
  1099. X         version.icn slshupto.icn
  1100. X
  1101. XThe backslashes merely indicate that the next line is a continuation.
  1102. XThe whole thing should, in other words, be on a single line.  As noted
  1103. Xabove, you may compile rather than interpret - if your OS supports the
  1104. XIcon compiler.  Just replace "icont" above with "iconc."  The
  1105. Xresulting executable will run considerably faster than with "icont,"
  1106. Xalthough the time required to compile it may be large, and the (still
  1107. Xsomewhat experimental) compiler may not work smoothly in all
  1108. Xenvironments.
  1109. X
  1110. X    If your operating system support environment variables, and
  1111. Xyou have set up your LPATH according to the specifications in the Icon
  1112. Xdistribution (see below), then you may copy iiparse.lib and
  1113. Xiiglrpar.lib to some file in your LPATH.  If you do not do this, or if
  1114. Xyour OS does not support environment variables, then you must be in
  1115. Xthe directory where you keep your Ibpag2 files when you use it, or
  1116. Xelse invoke Ibpag2 with the -p dirname option (where dirname is the
  1117. Xdirectory that holds the iiparse.lib and iiglrpar.lib files that come
  1118. Xwith the Ibpag2 distribution).  The .lib files contain template
  1119. Xparsers that are critical to Ibpag2's operation.  Ibpag2 will abort if
  1120. Xit cannot find them.
  1121. X
  1122. X    If your operating system permits the creation of macros or
  1123. Xbatch files, it might be useful to create one that changes
  1124. Xautomatically to the Ibpag2 source directory, and runs the executable.
  1125. XThis has the side-benefit of making it easier for Ibapg2 to find the
  1126. Xparser library files, iiparse.lib and iiglrpar.lib.  Under DOS, for
  1127. Xinstance, one might create a batch file that says:
  1128. X
  1129. X    c:
  1130. X    cd c:\ibpag2
  1131. X    iconx ibpag2 %1 %2 %3 %4 %5 %6 %7 %8 %9
  1132. X
  1133. XDOS, it turns out, has to execute Icon files indirectly through iconx,
  1134. Xso this technique has yet another advantage in that it hides the
  1135. Xsecond level of indirection - although it prevents you from using
  1136. Xinput and output redirection.  Naturally, the above example assumes
  1137. Xthat Ibpag2 is in c:\ibpag2.
  1138. X
  1139. X    Ibpag2 assumes the existence on your system, not only of an
  1140. XIcon interpreter or compiler, but also of an up-to-date Icon Program
  1141. XLibrary.  There are several routines included in the IPL that Bibleref
  1142. Xuses.  Make sure you (or the local system administrators) have put the
  1143. XIPL online, and have translated the appropriate object modules.  Set
  1144. Xyour IPATH environment variable to point to the place where the object
  1145. Xmodules reside.  Set LPATH to point to the modules' source files.
  1146. XBoth IPATH and LPATH are documented in doc directory of the Icon
  1147. Xsource tree (ipd224.doc).  If your system does not support environment
  1148. Xvariables, copy ximage.icn, options.icn, ebcdic.icn, and escape.icn
  1149. Xfrom the IPL into the Ibpag2 source directory, and compile them in
  1150. Xwith the rest of the Ibpag2 source files, either by adding them to the
  1151. XSRC variable in the makefile, or by adding them manually to the "icont
  1152. X-o ..." command line given above.
  1153. X
  1154. X    If you have any problems installing or using Ibpag2, please
  1155. Xfeel free to drop me, Richard Goerwitz, an e-mail message at
  1156. Xgoer@midway.uchicago.edu, or (via the post) at:
  1157. X
  1158. X    5410 S. Ridgewood Ct., 2E
  1159. X    Chicago, IL   60615
  1160. X
  1161. X
  1162. X6.__Bibliography
  1163. X
  1164. X1.  Aho, Alfred V., Sethi, Ravi, and Ullman, Jeffrey D.  Compilers.
  1165. X    Addison-Wesley: Reading, Massachusetts, second printing, 1988.
  1166. X
  1167. X2.  Griswold, Ralph E. and Griswold, Madge T.  The Icon Programming
  1168. X    Language. Prentice-Hall, Inc.: Englewood Cliffs, New Jersey, USA,
  1169. X    second edition, 1990.
  1170. X
  1171. X3.  Griswold, Ralph E., Jeffery, Clinton L., and Townsend, Gregg M.
  1172. X    Version 8.10 of the Icon Programming Language.  Univ. of Arizona
  1173. X    Icon Project Document 212, 1993.  (obtain via anonymous FTP from
  1174. X    cs.arizona.edu ~ftp/icon/docs/ipd212.doc)
  1175. X
  1176. X4.  Tomita, Masaru.  Efficient Parsing for Natural Language.  Boston:
  1177. X    Kluwer Academic Publishers, c. 1985.
  1178. X
  1179. X5.  Tomita, Masaru editor.  Generalized LR Parsing.  Boston:  Kluwer
  1180. X    Academic Publishers, 1991.
  1181. END_OF_FILE
  1182.   if test 53409 -ne `wc -c <'README'`; then
  1183.     echo shar: \"'README'\" unpacked with wrong size!
  1184.   fi
  1185.   # end of 'README'
  1186. fi
  1187. if test -f 'ibutil.icn' -a "${1}" != "-c" ; then 
  1188.   echo shar: Will not clobber existing file \"'ibutil.icn'\"
  1189. else
  1190.   echo shar: Extracting \"'ibutil.icn'\" \(7298 characters\)
  1191.   sed "s/^X//" >'ibutil.icn' <<'END_OF_FILE'
  1192. X############################################################################
  1193. X#
  1194. X#    Name:     ibutil.icn
  1195. X#
  1196. X#    Title:     utilities for Ibpag2
  1197. X#
  1198. X#    Author:     Richard L. Goerwitz
  1199. X#
  1200. X#    $Revision: 1.21 $
  1201. X#
  1202. X############################################################################
  1203. X#  
  1204. X#  Contains:
  1205. X#
  1206. X#      production_2_string(p)        makes production or item p human-
  1207. X#                    readable 
  1208. X#
  1209. X#      print_item_list(C, i)        returns human-readable version of
  1210. X#                                   item list C
  1211. X#
  1212. X#      print_grammar(grammar, f)    sends to file f (default &output)
  1213. X#                                   a human-readable printout of a grammar,
  1214. X#                                   as recorded in an ib_grammar structure
  1215. X#
  1216. X#      print_action_goto_tables(atbl, gtbl, ibtoktbl, f)
  1217. X#                                   sends to file f (default (&output)
  1218. X#                                   a human-readable printout of action
  1219. X#                                   table atbl and goto table gtbl
  1220. X#
  1221. X#      print_follow_sets(FOLLOW_table)
  1222. X#                                   returns a human-readable version
  1223. X#                                   of a FOLLOW table (table of sets)
  1224. X#
  1225. X#      print_first_sets(FIRST_table)
  1226. X#                                   returns a human-readable version
  1227. X#                                   of a FIRST table (a table of sets)
  1228. X#
  1229. X#      ibreplace(s1, s2, s3)        replaces s2 with s3 in s1
  1230. X#
  1231. X#      equivalent_items(i1, i2)     succeeds if item i1 is structurally
  1232. X#                    identical to item i2
  1233. X#
  1234. X#      equivalent_item_lists(l1,l2) same as equivalent_items, but for
  1235. X#                                   lists of items, not individual items
  1236. X#
  1237. X############################################################################
  1238. X#
  1239. X#  Links: none
  1240. X#
  1241. X############################################################################
  1242. X
  1243. X
  1244. Xrecord production(LHS, RHS, POS, LOOK, no, prec, assoc)
  1245. X
  1246. X#
  1247. X# production_2_string:  production record -> string
  1248. X#                       p                 -> s
  1249. X#
  1250. X#     Stringizes an image of the LHS and RHS of production p in
  1251. X#     human-readable form.
  1252. X#
  1253. Xprocedure production_2_string(p, ibtoktbl)
  1254. X
  1255. X    local s, m, t
  1256. X
  1257. X    s := image(p.LHS) || " -> "
  1258. X    every m := !p.RHS do {
  1259. X    if t := \ (\ibtoktbl)[m]
  1260. X    then s ||:= t || " "
  1261. X    else s ||:= image(m) || " "
  1262. X    }
  1263. X    # if the POS field is nonnull, print it
  1264. X    s ||:= "(POS = " || image(\p.POS) || ") "
  1265. X    # if the LOOK field is nonnull, print it, too
  1266. X    s ||:= "lookahead = " || image(\p.LOOK)
  1267. X
  1268. X    return trim(s)
  1269. X
  1270. Xend
  1271. X
  1272. X
  1273. X#
  1274. X# print_item_list:  makes item list human readable
  1275. X#
  1276. Xprocedure print_item_list(C, i)
  1277. X
  1278. X    write(&errout, "Productions for item list ", i, ":")
  1279. X    every write(&errout, "\t", production_2_string(!C[i]))
  1280. X    write(&errout)
  1281. X    return
  1282. X
  1283. Xend
  1284. X
  1285. X
  1286. X#
  1287. X# print_grammar:  makes entire grammar human readable
  1288. X#
  1289. Xprocedure print_grammar(grammar, f)
  1290. X
  1291. X    local p, i, sl
  1292. X
  1293. X    /f := &errout
  1294. X
  1295. X    write(f, "Start symbol:")
  1296. X    write(f, "\t", grammar.start)
  1297. X    write(f)
  1298. X    write(f, "Rules:")
  1299. X    every p := !grammar.rules do {
  1300. X    writes(f, "\tRule ", right(p.no, 3, " "), "  ")
  1301. X    write(f, production_2_string(p, grammar.tbl))
  1302. X    }
  1303. X    write(f)
  1304. X    write(f, "Tokens:")
  1305. X    sl := sort(grammar.tbl, 3)
  1306. X    every i := 1 to *sl-1 by 2 do
  1307. X    write(f, "\t", left(sl[i], 5, "."), right(sl[i+1], 20, "."))
  1308. X    write(f)
  1309. X    return
  1310. X
  1311. Xend
  1312. X
  1313. X
  1314. X#
  1315. X# print_action_goto_tables
  1316. X#
  1317. X#     Makes action & goto tables human readable.  If a table mapping
  1318. X#     integer (i.e. char) literals to token names is supplied, the
  1319. X#     token names themselves are printed.
  1320. X#
  1321. Xprocedure print_action_goto_tables(atbl, gtbl, ibtoktbl, f)
  1322. X
  1323. X    local TAB, tbl, key_set, size, i, column, k
  1324. X
  1325. X    /f := &errout
  1326. X    TAB := "\t"
  1327. X
  1328. X    every tbl := atbl|gtbl do {
  1329. X
  1330. X    key_set := set(); every insert(key_set, key(tbl))
  1331. X    writes(f, TAB)
  1332. X    every k := !key_set do
  1333. X        writes(f, \(\ibtoktbl)[k] | k, TAB)
  1334. X    write(f)
  1335. X    
  1336. X    size := 0; every size <:= key(!tbl)
  1337. X    every i := 1 to size do {
  1338. X        writes(f, i, TAB)
  1339. X        every column := tbl[!key_set] do {
  1340. X        # action lists may have more than one element
  1341. X        if /column[i] then
  1342. X            writes(f, "  ", TAB) & next
  1343. X        \column[i] ? {
  1344. X            if any('asr') then {
  1345. X            while any('asr') do {
  1346. X                writes(f, ="a") & next
  1347. X                writes(f, tab(upto('.<')))
  1348. X                if ="<" then tab(find(">")+1) else ="."
  1349. X                tab(many(&digits))
  1350. X            }
  1351. X            writes(f, TAB)
  1352. X            }
  1353. X            else writes(f, tab(many(&digits)), TAB)
  1354. X        }
  1355. X        }
  1356. X        write(f)
  1357. X    }
  1358. X    write(f)
  1359. X    }
  1360. X
  1361. X    return
  1362. X
  1363. Xend
  1364. X
  1365. X
  1366. X#
  1367. X# print_follow_sets:  make FOLLOW table human readable
  1368. X#
  1369. Xprocedure print_follow_sets(FOLLOW_table)
  1370. X
  1371. X    local FOLLOW_sets, i
  1372. X
  1373. X    FOLLOW_sets := sort(FOLLOW_table, 3)
  1374. X    write(&errout, "FOLLOW sets are as follows:")
  1375. X    every i := 1 to *FOLLOW_sets-1 by 2 do {
  1376. X    writes(&errout, "\tFOLLOW(", image(FOLLOW_sets[i]), ") = ")
  1377. X    every writes(&errout, image(! FOLLOW_sets[i+1]), " ")
  1378. X    write(&errout)
  1379. X    }
  1380. X    write(&errout)
  1381. X    return
  1382. X
  1383. Xend
  1384. X
  1385. X
  1386. X#
  1387. X# print_first_sets:  make FIRST table human readable
  1388. X#
  1389. Xprocedure print_first_sets(FIRST_table)
  1390. X
  1391. X    local FIRST_sets, i
  1392. X
  1393. X    FIRST_sets := sort(FIRST_table, 3)
  1394. X    write(&errout, "FIRST sets are as follows:")
  1395. X    every i := 1 to *FIRST_sets-1 by 2 do {
  1396. X    writes(&errout, "\tFIRST(", image(FIRST_sets[i]), ") = ")
  1397. X    every writes(&errout, image(! FIRST_sets[i+1]), " ")
  1398. X    write(&errout)
  1399. X    }
  1400. X    write(&errout)
  1401. X    return
  1402. X
  1403. Xend
  1404. X
  1405. X
  1406. X#
  1407. X# ibreplace: string x string x string -> string
  1408. X#            (s1,     s2,      s3)    -> s4
  1409. X#
  1410. X#     Where s4 is s1, with every instance of s2 stripped out and
  1411. X#     replaced by s3.  E.g. replace("hello there; hello", "hello",
  1412. X#     "hi") yields "hi there; hi".  Taken straight from the IPL.
  1413. X#
  1414. Xprocedure ibreplace(s1,s2,s3)
  1415. X
  1416. X    local result, i
  1417. X
  1418. X    result := ""
  1419. X    i := *s2
  1420. X
  1421. X    s1 ? {
  1422. X    while result ||:= tab(find(s2)) do {
  1423. X        result ||:= s3
  1424. X        move(i)
  1425. X    }
  1426. X    return result || tab(0)
  1427. X    }
  1428. X
  1429. Xend
  1430. X
  1431. X    
  1432. X#
  1433. X# equivalent_items:  record x record -> record or failure
  1434. X#                    (item1,  item2) -> item1  or failure
  1435. X#
  1436. X#     Where item1 and item2 are records having LHS, RHS, POS, & LOOK
  1437. X#     fields (and possibly others, though they aren't used).  Returns
  1438. X#     item1 if item1 and item2 are structurally identical as far as
  1439. X#     their LHS, RHS, LOOK, and POS fields are concerned.  For SLR
  1440. X#     table generators, LOOK will always be null.
  1441. X#
  1442. Xprocedure equivalent_items(item1, item2)
  1443. X
  1444. X    local i
  1445. X
  1446. X    item1 === item2 & (return item1)
  1447. X
  1448. X    if item1.LHS == item2.LHS &
  1449. X    item1.POS = item2.POS &
  1450. X    #
  1451. X    # This comparison doesn't have to be recursive, since I take
  1452. X    # care never to alter RHS structures.  Identical RHSs should
  1453. X    # always be *the same underlying structure*.
  1454. X    #
  1455. X    item1.RHS === item2.RHS &
  1456. X    item1.LOOK === item2.LOOK
  1457. X    then
  1458. X    return item1
  1459. X
  1460. Xend
  1461. X
  1462. X
  1463. X#
  1464. X# equivalent_item_lists: list x list -> list or fail
  1465. X#                        (il1,  il2) -> il1
  1466. X#
  1467. X#     Where il1 is one sorted list-of-items (as returned by goto() or
  1468. X#     by closure()), where il2 is another such list.  Returns the
  1469. X#     first list if the LHS, RHS, and POS fields of the constituent
  1470. X#     items are all structurally identical, i.e. if the two lists
  1471. X#     contain the structurally identical items.
  1472. X#
  1473. Xprocedure equivalent_item_lists(il1, il2)
  1474. X
  1475. X    local i
  1476. X
  1477. X    il1 === il2 & (return il1)
  1478. X    if *il1 = *il2
  1479. X    then {
  1480. X    every i := 1 to *il1 do
  1481. X            equivalent_items(il1[i], il2[i]) | fail
  1482. X    }
  1483. X    else fail
  1484. X
  1485. X    return il1
  1486. X
  1487. Xend
  1488. END_OF_FILE
  1489.   if test 7298 -ne `wc -c <'ibutil.icn'`; then
  1490.     echo shar: \"'ibutil.icn'\" unpacked with wrong size!
  1491.   fi
  1492.   # end of 'ibutil.icn'
  1493. fi
  1494. echo shar: End of archive 1 \(of 4\).
  1495. cp /dev/null ark1isdone
  1496. MISSING=""
  1497. for I in 1 2 3 4 ; do
  1498.     if test ! -f ark${I}isdone ; then
  1499.     MISSING="${MISSING} ${I}"
  1500.     fi
  1501. done
  1502. if test "${MISSING}" = "" ; then
  1503.     echo You have unpacked all 4 archives.
  1504.     rm -f ark[1-9]isdone
  1505. else
  1506.     echo You still must unpack the following archives:
  1507.     echo "        " ${MISSING}
  1508. fi
  1509. exit 0
  1510. exit 0 # Just in case...
  1511.