home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / ibpag2 / part02 / iiglrpar.lib next >
Encoding:
Text File  |  1994-09-25  |  28.3 KB  |  969 lines

  1. ############################################################################
  2. #
  3. #    Name:     iiglrpar.lib
  4. #
  5. #    Title:     Quasi-GLR parser code
  6. #
  7. #    Author:     Richard L. Goerwitz
  8. #
  9. #    $Revision: 1.24 $
  10. #
  11. ############################################################################
  12. #
  13. #  This file contains quasi-GLR parser code for use by Ibpag2's
  14. #  output.  See below on what I mean by "quasi-GLR."  Entry point is
  15. #  iiparse(infile, fail_on_error).  Infile is the stream from which
  16. #  input is to be taken.  Infile is passed as argument 1 to the
  17. #  user-supplied lexical analyzer, iilex_module() (where _module is
  18. #  the string supplied with the -m option to Ibpag2).  If
  19. #  fail_on_error is nonnull, the parser, iiparse, will fail on errors,
  20. #  rather than abort.  Iiparse() returns the top element on its value
  21. #  stack on a successful parse (which can be handy).
  22. #
  23. #  Iilex_module() must suspend integers for tokens and may also set
  24. #  iilval_module to the actual string values.  Tokens -2, -1, and 0
  25. #  are reserved.  -2 is epsilon, and -1 is error.  0 is EOF, and is
  26. #  automatically appended to the token stream when iilex_module, the
  27. #  tokenizer, fails.  These values should not normally be returned by
  28. #  the analyzer.  In general, it is a good idea to $include
  29. #  iilex_module from your Ibpag2 source files, so that it can use the
  30. #  symbolic %token names declared in the original Ibpag2 source file.
  31. #  As implied above ("suspend"), iilex_module must be a generator,
  32. #  failing on EOF.
  33. #
  34. #  If desired, you may include your own error-handling routine.  It
  35. #  must be called iiparse_module (where _module is once again the
  36. #  module name supplied to ibpag2 via the -m option).  The global
  37. #  variable line_number_module is automatically defined below, so a
  38. #  typical arrangement would be for the lexical analyzer to initialize
  39. #  line_number_module to 0, and increment by 1 for each line read.
  40. #  The error handler, iierror_module() can then display this variable.
  41. #  Note that the error handler should accept a single string argument
  42. #  (set by iiparse to describe the token on the input stream when the
  43. #  error was encountered).
  44. #
  45. #  I label this parser "GLR" because it does support multiple parallel
  46. #  parsers (like GLR parsers are supposed to).  I use the qualifier
  47. #  "quasi," though, because it does not use a graph-structured stack.
  48. #  Instead it copies both value and state stacks (in fact, the whole
  49. #  parser environment) when creating new automata to handle
  50. #  alternative parse paths.  Slower, yes.  But it enables the user to
  51. #  use almost precisely the action and input format that is used for
  52. #  the standard parser.
  53. #
  54. #  Note that iiparse(), as implemented here, may suspend multiple
  55. #  results.  So be sure to call it in some context where multiple
  56. #  results can be used (e.g. every parse := iiparse(&input, 1), or the
  57. #  like).  Note also that when new parser "edges" get created, a
  58. #  rather cumbersome recursive copy routine is used.  Sorry, but it's
  59. #  necessary to prevent unintended side-effects.
  60. #
  61. ############################################################################
  62. #
  63. #  The algorithm:
  64. #
  65. #      A = list of active parsers needing action lookup
  66. #      S = list of parsers to be shifted
  67. #      R = list of parsers to be reduced
  68. #      B = list of parsers that "choked"
  69. #
  70. #      for every token on the input stream
  71. #      begin
  72. #        until length of R = 0 and length of A = 0
  73. #        begin
  74. #          - pop successive parsers off of A, and placing them in S,
  75. #            R, or B, depending on parse table directives; suspend a 
  76. #            result for each parser that has reached an accepting
  77. #            state
  78. #         -  pop successive parsers off of R, reducing them, and
  79. #            placing them back in A; perform the action code
  80. #            associated with each reduction
  81. #        end
  82. #        - pop successive parsers off of S, shifting them, and placing
  83. #          them back in A; mark recovering parsers as recovered when
  84. #          they have successfully shifted three tokens
  85. #        if length of A = 0 and token not = EOF
  86. #        then
  87. #          - initiate error recovery on the parsers in B, i.e. for
  88. #            each parser in B that is not already recovering, pop its
  89. #            stack until error (-1) can legally be shifted, then shift
  90. #            error, mark the parser as recovering from an error, and
  91. #            place it back in A; if the parser is already recovering,
  92. #            discard the current token
  93. #        else
  94. #          - clobber the parsers in B
  95. #        end
  96. #      end
  97. #
  98. #  Note that when a given active parser in A is being classified
  99. #  as needing a reduction, shift, suspension, or entry into the error
  100. #  list (B), more than one action may apply due to ambiguity in the
  101. #  grammar.  At such points, the parser environment is duplicated,
  102. #  once for each alternative pathway, and each of the new parsers is
  103. #  then entered into the appropriate list (R or S; if accept is an
  104. #  alternative, the classification routine suspends).
  105. #
  106. #  Note also that when performing the action code associated with
  107. #  reductions, parsers may be reclassified as erroneous, accepting,
  108. #  etc. via "semantic" directives like IIERROR and IIACCEPT.  See the
  109. #  README file.  Multiple-result action code will cause new parser
  110. #  threads to be created, just as ambiguities in the grammar do within
  111. #  the classification routine above.
  112. #
  113. #############################################################################
  114. #
  115. #  See also: ibpag2.icn, iiparse.icn
  116. #
  117. ############################################################################
  118.  
  119. $$line 119 "iiglrpar.lib"
  120.  
  121. $$ifndef IIDEBUG
  122.     $$define $iidebug    1
  123.     $$define show_new_forest    1
  124. $$endif                # not IIDEBUG
  125.  
  126. # These defines are output by Ibpag2 ahead of time (with the module
  127. # name appended, if need be):
  128. #
  129. # IIERROR
  130. # IIACCEPT
  131. # iiprune     - GLR mode only
  132. # iiisolate   - GLR mode only
  133. # iierrok
  134. # iiclearin
  135.  
  136. # Parser environment + lookahead and pending action field.
  137. #
  138. record $ib_pe(state_stack, value_stack, action, errors, expecteds,
  139.           recover_shifts, discards, clearin)
  140.  
  141. # Warning!  If you change the name of the value stack, change it also
  142. # in ibreader.icn, procedure write_action_as_procedure().
  143. #
  144. global $iilval, $line_number, $state_stack, $value_stack,
  145.     $iidirective, $ttbl, $errors, $discard_token
  146.  
  147. #
  148. # iiparse: file x   anything        -> ?s (a generator)
  149. #          (stream, fail_on_error)  -> ?
  150. #
  151. #     Where stream is an open file, where fail_on_error is a switch
  152. #     that (if nonnull) tells the iiparse to fail, rather than abort,
  153. #     on error, and where ?s represent the user-defined results of a
  154. #     completed parse of file, from the current location up to the
  155. #     point where the parser executes an "accept" action.  Note that
  156. #     iiparse, as implemented here, is a generator.
  157. #
  158. procedure $iiparse(stream, fail_on_error)
  159.  
  160.     local token, next_token, actives, reducers, shifters, barfers
  161.     #global ttbl, errors
  162.     static atbl
  163.     initial {
  164.     $iidirective := ""
  165.     atbl  := $atbl_insertion_point
  166.     $ttbl := $ttbl_insertion_point
  167.     $$line 166 "iiglrpar.lib"
  168.     \$iilex | stop("no iilex tokenizer defined")
  169.     }
  170.  
  171.     actives  := [ $ib_pe([1], [], &null, 0) ]
  172.     $state_stack := actives[1].state_stack
  173.     $value_stack := actives[1].value_stack
  174.     $errors := actives[1].errors
  175.     reducers := list()
  176.     shifters := list()
  177.     # I get tired of bland error code.  We'll call the list of
  178.     # parsers in an error state "barfers" :-).
  179.     barfers  := list()
  180.  
  181.     next_token := create $iilex(stream, fail_on_error) | 0
  182.  
  183.     token := @next_token
  184.     #
  185.     # After this ^, new tokens are read in near the end of the repeat
  186.     # loop.  None is read in on an error, since then we will try again
  187.     # on the token that caused the error.
  188.     #
  189.     repeat {
  190.     until *actives = *reducers = 0
  191.     do {
  192.  
  193.         # Prune out parsers that are doing the same thing as some
  194.         # other parser.
  195.         #
  196.         $$ifdef AUTO_PRUNE
  197.         auto_prune(actives)
  198.         $$endif
  199.  
  200.         # Suspends $value_stack[1] on accept actions.  Otherwise,
  201.         # puts parsers that need shifting into the shifters list,
  202.         # parsers that need reducing into the reducers list, and
  203.         # error-state parsers into the barfers list.  Creates new
  204.         # parser environments as needed.
  205.         #
  206.         suspend $ib_action(atbl, token, actives, shifters,
  207.                    reducers, barfers)
  208.  
  209.         # Perform reductions.  If instructed via the iiaccept
  210.         # macro, simulate an accept action, and suspend with a
  211.         # result.
  212.         #
  213.         suspend $perform_reductions(token, actives, shifters,
  214.                     reducers, barfers)
  215.     }
  216.  
  217.     # Shift token for every parser in the shifters list.  This
  218.         # will create a bunch of new active parsers.
  219.     #
  220.     $perform_shifts(token, actives, shifters)
  221.     #
  222.     # If we get to here and have no actives, and we're not at the
  223.     # end of the input stream, then we are at an error impasse.
  224.     # Do formal error recovery.
  225.     #
  226.     if *actives = 0 & token ~=== 0 then {
  227.         suspend $perform_barfs(atbl, token, actives, barfers,fail_on_error)
  228.         #
  229.         # Perform_barfs sets discard_token if recovery was
  230.         # unsuccessful on the last token, and it needs discarding.
  231.         #
  232.         if \$discard_token := &null then
  233.         token := @next_token | break
  234.         #
  235.         # If there *still* aren't any active parsers, we've
  236.         # reached an impasse (or there are no error productions).
  237.         # Abort.
  238.         #
  239.         if *actives = 0 then {
  240.         if \fail_on_error then fail
  241.         else stop()
  242.         }
  243.     }
  244.     else {
  245.         #
  246.         # Parsers in an error state should be weeded out, since if
  247.         # we get to here, we have some valid parsers still going.
  248.         # I.e. only use them if there are *no* actives (see above).
  249.         #
  250.     $$ifdef IIDEBUG
  251.         write(&errout, "+++ pruning ", *barfers, " erroneous parsers")
  252.         while parser := pop(barfers)
  253.         do $iidebug("p", token, &null, parser)
  254.     $$else
  255.         while pop(barfers)
  256.     $$endif    #IIDEBUG
  257.         #
  258.         # Get the next token.  Only do this if we have active
  259.         # parsers not recovering from an error, i.e., if we're here.
  260.         #
  261.         token := @next_token | break
  262.         }
  263.     }
  264.  
  265. end
  266.  
  267.  
  268. #
  269. # ib_action
  270. #
  271. procedure $ib_action(atbl, token, actives, shifters, reducers,
  272.              barfers)
  273.  
  274.     local a, act, num, parser, new_parser
  275.  
  276.     # While there is an active parser, take it off the actives list,
  277.     # and...
  278.     while parser := pop(actives) do {
  279.  
  280.     # ...check for a valid action (if none, then there is an
  281.     # error; put it into the barfers list).
  282.     #
  283.     if a := \ (\atbl[token])[parser.state_stack[1]]
  284.     then {
  285.         a ? {
  286.         # Keep track of how many actions we've seen.
  287.         num := 0
  288.  
  289.         # Snip off successive actions.  If there's no
  290.         # ambiguity, there will be only one action, & no
  291.         # additional parser environments will be created.
  292.         #
  293.         while {
  294.         $$ifdef COMPRESSED_TABLES
  295.             # "\x80" is the accept action; uncompress_action
  296.             # does its own move()ing
  297.             act := $uncompress_action()
  298.         $$else
  299.             act := ="a" | {
  300.             tab(any('sr')) || tab(upto('.<')) ||
  301.                 ((="<" || tab(find(">")+1)) | =".") ||
  302.                 tab(many(&digits))
  303.             }
  304.         $$endif    #COMPRESSED TABLES
  305.         }
  306.         do {
  307.             # New parser environment only needed for num > 1.
  308.             #
  309.             if (num +:= 1) > 1 then {
  310.             new_parser := $fullcopy(parser)
  311.             show_new_forest("=== table conflict; new parser",
  312.                 actives, shifters, reducers, barfers, new_parser)
  313.             }
  314.             else new_parser := parser
  315.             new_parser.action := act
  316.  
  317.             # Classify the action as s, r, or a, and place i
  318.             # the appropriate list (or suspend a result if a).
  319.             #
  320.             case act[1] of {
  321.             "s"  : put(shifters, new_parser)
  322.             "r"  : put(reducers, new_parser)
  323.             "a"  : {
  324.                 $iidebug("a", token, ruleno, parser)
  325.                 suspend parser.value_stack[1]
  326.             }
  327.             }
  328.         }
  329.         }
  330.     }
  331.     else {
  332.         #
  333.         # Error.  Parser will get garbage collected before another
  334.         # token is read from iilex, unless the parsers all fail -
  335.         # in which case, error recovery will be tried.
  336.         #
  337.         $iidebug("e", token, &null, parser)
  338.         put(barfers, parser)
  339.     }
  340.     }
  341.  
  342. end
  343.  
  344.  
  345. #
  346. # perform_reductions
  347. #
  348. procedure $perform_reductions(token, actives, shifters, reducers, barfers)
  349.  
  350.     local parser, ruleno, newsym, rhsize, arglist, result, num,
  351.     new_parser, tmp, p
  352.     static gtbl
  353.     initial {
  354.     gtbl := $gtbl_insertion_point
  355.     $$line 336 "iiglrpar.lib"
  356.     }
  357.  
  358.     while parser := get(reducers)
  359.     do {
  360.  
  361.     # Set up global state and value stacks, so that the action
  362.     # code can access them.
  363.     #
  364.     $state_stack := parser.state_stack
  365.     $value_stack := parser.value_stack
  366.     $errors := parser.errors
  367.  
  368.     # Finally, perform the given action:
  369.     #
  370.     parser.action ? {
  371.         #
  372.         # Reduce action format, e.g. r1<S>2 = reduce by rule 1
  373.         # (LHS = S, RHS length = 2).
  374.         #
  375.         move(1)
  376.         ruleno := integer(1(tab(find("<")), move(1)))
  377.         newsym := 1(tab(find(">")), move(1))
  378.         rhsize := integer(tab(many(&digits)))
  379.         arglist := []
  380.         every 1 to rhsize do {
  381.         pop($state_stack)
  382.         push(arglist, pop($value_stack))
  383.         }
  384.         # Gtbl is "backwards," i.e. token first, state second.
  385.         # The value produced is the "goto" state.
  386.         #
  387.         push($state_stack, gtbl[newsym][$state_stack[1]])
  388.         #
  389.         # The actions are in procedures having the same name as
  390.         # the number of their rule, bracketed by underscores, &
  391.         # followed by the current module name.  If there is such a
  392.         # procedure associated with the current reduce action,
  393.         # call it.
  394.         #
  395.         if func := proc("_" || ruleno || "_" || $module)
  396.         then {
  397.         num := 0
  398.         #
  399.         # For every valid result from the action code for the
  400.         # current reduction, create a new parser if need be
  401.         # (i.e. if num > 1), and check iidirective.  Push the
  402.         # result onto the stack of the new parser & put the
  403.         # new parser into the actives list.
  404.         #
  405.         every result := func!arglist do {
  406.             # For all but the first result, create a new parser.
  407.             if (num +:= 1) > 1 then {
  408.             new_parser := $fullcopy(parser)
  409.             pop(new_parser.value_stack) # take off pushed result
  410.             show_new_forest("=== multi-result action; new parser",
  411.                 actives, shifters, reducers, barfers, new_parser)
  412.             }
  413.             else new_parser := parser
  414.  
  415.             #
  416.             # IIERROR, IIACCEPT, iierrok, iiisolate, and iiprune
  417.             # are all implemented using a search through a global
  418.             # iidirective variable; see the $defines described
  419.             # above.
  420.             #
  421.             tmp := $iidirective
  422.             $iidirective := ""
  423.             if *tmp > 0 then {
  424.             if find("errok", tmp) then {
  425.                 new_parser.expecteds := &null
  426.                 new_parser.recover_shifts := &null
  427.                 new_parser.discards := 0
  428.             }
  429.             if find("clearin", tmp) then {
  430.                 # see perform_shifts() below
  431.                 new_parser.clearin := 1
  432.             }
  433.             if find("error", tmp) then {
  434.                 $iidebug("e", token, ruleno, new_parser)
  435.                 /new_parser.expecteds :=
  436.                 [$ttbl[token] || " (INDUCED)"]
  437.                 put(barfers, new_parser)
  438.                 next
  439.             }
  440.             if find("prune", tmp) then {
  441.                 # Garden path.
  442.                 $iidebug("p", token, ruleno, new_parser)
  443.                 break next
  444.             }
  445.             if find("isolate", tmp) then {
  446.                 # Prune all but the current parser.
  447.             $$ifdef IIDEBUG
  448.                 write(&errout, "+++ isolating by pruning")
  449.                 while p := pop(actives) do
  450.                 $iidebug("p", token, ruleno, p)
  451.                 while p := pop(reducers) do
  452.                 $iidebug("p", token, ruleno, p)
  453.                 while p := pop(shifters) do
  454.                 $iidebug("p", token, ruleno, p)
  455.                 while p := pop(barfers) do
  456.                 $iidebug("p", token, ruleno, p)
  457.             $$else
  458.                 while pop(actives)
  459.                 while pop(reducers)
  460.                 while pop(shifters)
  461.                 while pop(barfers)
  462.             $$endif    #IIDEBUG
  463.                 push(new_parser.value_stack, result)
  464.                 $iidebug("r", token, ruleno, new_parser)
  465.                 put(actives, new_parser)
  466.                 break next
  467.             }
  468.             if find("accept", tmp) then {
  469.                 $iidebug("a", token, ruleno, new_parser)
  470.                 suspend result
  471.                 next
  472.             }
  473.             }
  474.             #
  475.             # Push result onto the new parser thread's value
  476.             # stack.
  477.             #
  478.             push(new_parser.value_stack, result)
  479.             $iidebug("r", token, ruleno, new_parser)
  480.             put(actives, new_parser)
  481.             #
  482.             # Action code must have the stack in its original
  483.             # form.  So restore the stack's old form before
  484.             # going back to the action code.
  485.             #
  486.             if num = 1 then
  487.             $value_stack := parser.value_stack[2:0]
  488.             }
  489.         #
  490.         # If the action code for this rule failed, push &null.
  491.         # But first check $iidirective.
  492.         #
  493.         if num = 0 then {
  494.             #
  495.             # Same $iidirective code as above repeated
  496.             # (inelegantly) because it accesses too many
  497.             # variables to be easily isolated.
  498.             #
  499.             tmp := $iidirective
  500.             $iidirective := ""
  501.             if *tmp > 0 then {
  502.             if find("clearin", tmp) then {
  503.                 # see perform_shifts() below
  504.                 parser.clearin := 1
  505.             }
  506.             if find("error", tmp) then {
  507.                 $iidebug("e", token, ruleno, parser)
  508.                 /parser.expecteds :=
  509.                 [$ttbl[token] || " (INDUCED)"]
  510.                 put(barfers, parser)
  511.                 next
  512.             }
  513.             if find("errok", tmp) then {
  514.                 parser.expecteds := &null
  515.                 parser.recover_shifts := &null
  516.                 parser.discards := 0
  517.             }
  518.             if find("prune", tmp) then {
  519.                 # Garden path.
  520.                 $iidebug("p", token, ruleno, parser)
  521.                 next # go back to enclosing while pop...
  522.             }
  523.             if find("isolate", tmp) then {
  524.                 # Prune all but the current parser.
  525.             $$ifdef IIDEBUG
  526.                 write(&errout, "+++ isolating by pruning")
  527.                 while p := pop(actives) do
  528.                 $iidebug("p", token, ruleno, p)
  529.                 while p := pop(reducers) do
  530.                 $iidebug("p", token, ruleno, p)
  531.                 while p := pop(shifters) do
  532.                 $iidebug("p", token, ruleno, p)
  533.                 while p := pop(barfers) do
  534.                 $iidebug("p", token, ruleno, p)
  535.             $$else
  536.                 while pop(actives)
  537.                 while pop(reducers)
  538.                 while pop(shifters)
  539.                 while pop(barfers)
  540.             $$endif    #IIDEBUG
  541.             }
  542.             if find("accept", tmp) then {
  543.                 $iidebug("a", token, ruleno, parser)
  544.                 suspend arglist[-1] | &null
  545.                 next
  546.             }
  547.             }
  548.             # Finally, push the result!
  549.             result := arglist[-1] | &null
  550.             push(parser.value_stack, result)
  551.             $iidebug("r", token, ruleno, parser)
  552.             put(actives, parser)
  553.         }
  554.         }
  555.         # If there is no action code for this rule...
  556.         else {
  557.         # ...push the value of the last RHS arg.
  558.         # For 0-length e-productions, push &null.
  559.         result := arglist[-1] | &null
  560.         push(parser.value_stack, result)
  561.         $iidebug("r", token, ruleno, parser)
  562.         put(actives, parser)
  563.         }
  564.     }
  565.     }
  566.  
  567. end
  568.  
  569.  
  570. #
  571. # perform_shifts
  572. #
  573. procedure $perform_shifts(token, actives, shifters)
  574.     
  575.     local parser, ruleno
  576.  
  577.     *shifters = 0 & fail
  578.  
  579.     while parser := pop(shifters) do {
  580.     #
  581.     # One of the iidirectives is iiclearin, i.e. clear the input
  582.     # token and try again on the next token.
  583.     #
  584.     \parser.clearin := &null & {
  585.         put(actives, parser)
  586.         next
  587.     }
  588.     parser.action ? {
  589.         #
  590.             # Shift action format, e.g. s2.1 = shift and go to state 2
  591.         # by rule 1.
  592.             #
  593.         move(1)
  594.         push(parser.state_stack, integer(tab(find("."))))
  595.         ="."; ruleno := integer(tab(many(&digits)))
  596.         pos(0) | stop("malformed action:  ", act)
  597.         #
  598.         # If token is error (-1), then place on the value stack a
  599.         # list of "expected" tokens...
  600.         #
  601.         if token = -1 then {
  602.         push(parser.value_stack, parser.expecteds)
  603.         } else {
  604.         # ...otherwise use the normal $iilval
  605.         push(parser.value_stack, $iilval)
  606.             #
  607.             # If, while recovering, we manage to shift 3 tokens,
  608.         # then we are resynchronized.  Don't count error (-1).
  609.             #
  610.         if \parser.recover_shifts +:= 1 then {
  611.             # 3 shifts make a successful recovery
  612.             if parser.recover_shifts > 4 then {
  613.             parser.expecteds := &null
  614.             parser.recover_shifts := &null
  615.             parser.discards := 0
  616.             }
  617.         }
  618.         }
  619.         $iidebug("s", token, ruleno, parser)
  620.     }
  621.     put(actives, parser)
  622.     }
  623.  
  624.     return
  625.     
  626. end
  627.  
  628.  
  629. #
  630. # perform_barfs
  631. #
  632. procedure $perform_barfs(atbl, token, actives, barfers, fail_on_error)
  633.  
  634.     #
  635.     # Note how this procedure has its own local reducers and shifters
  636.     # list.  These are *not* passed from the parent environment!
  637.     #
  638.     local parser, count, reducers, shifters, recoverers, k
  639.  
  640.     # To hold the list of parsers that need to shift error (-1).
  641.     recoverers := list()
  642.  
  643.     count := 0
  644.     while parser := pop(barfers) do {
  645.     count +:= 1
  646.     if \parser.recover_shifts := 0 then {
  647.         #
  648.         # If we're already in an error state, discard the
  649.         # current token, and increment the number of discards
  650.         # we have made.  500 is too many; abort.
  651.         #
  652.         if (parser.discards +:= 1) > 500 then {
  653.         if proc($iierror)
  654.         then $iierror("fatal error: can't resynchronize")
  655.         else write(&errout, "fatal error: can't resynchronize")
  656.         if \fail_on_error then fail
  657.         else stop()
  658.         }
  659.         # try again on this one with the next token
  660.         put(actives, parser)
  661.     } else {
  662.         parser.errors +:= 1 # error count for this parser
  663.         parser.discards := parser.recover_shifts := 0
  664.         # If this is our first erroneous parser, print a message.
  665.         if count = 1 then {
  666.         if proc($iierror)
  667.         then $iierror(image(\$ttbl[token]) | image(token))
  668.         else write(&errout, "parse error")
  669.         }
  670.         #
  671.         # If error appears in a RHS, pop states until we get to a
  672.         # spot where error (-1) is a valid lookahead token:
  673.         #
  674.         if \$ttbl[-1] then {
  675.         # form a list of "expected" tokens to push onto
  676.         # the stack as the value for the error token
  677.         if /parser.expecteds := [$ttbl[token]] then {
  678.             every k := -1 ~= key($ttbl) do {
  679.             if \ (\atbl[k])[parser.state_stack[1]]
  680.             then put(parser.expecteds, $ttbl[k])
  681.             }
  682.         }
  683.         until *parser.state_stack = 0 do {
  684.             if \atbl[-1][parser.state_stack[1]] then {
  685.             put(recoverers, parser)
  686.             break next
  687.             } else pop(parser.state_stack) & pop(parser.value_stack)
  688.         }
  689.         }
  690.         # If we get past here, the stack is now empty or there
  691.         # are no error productions.  Abandon this parser.
  692.         $iidebug("p", token, &null, parser)
  693.     }
  694.     }
  695.  
  696.     # Parsers still recovering are in the actives list; those that
  697.     # need to shift error (-1) are in the recoverers list.  The
  698.     # following turns recoverers into actives:
  699.     #
  700.     if *recoverers > 0 then {
  701.     reducers := list()    # a scratch list
  702.     shifters := list()    # ditto
  703.     until *recoverers = *reducers = 0 do {
  704.         $$ifdef AUTO_PRUNE
  705.         auto_prune(actives)
  706.         $$endif
  707.         suspend $ib_action(atbl, -1, recoverers, shifters,
  708.                    reducers, barfers)
  709.         suspend $perform_reductions(-1, recoverers, shifters,
  710.                     reducers, barfers)
  711.     }
  712.     $perform_shifts(-1, recoverers, shifters)
  713.     every put(actives, !recoverers)
  714.     }
  715.     #
  716.     # If there were no recoverers, we've already shifted the error
  717.     # token, and are discarding tokens from the input stream.  Note
  718.     # that if one parser was recovering, they *all* should be
  719.     # recovering, since if one was not recovering, it the erroneous
  720.     # parsers should all have been discarded by the calling proc.
  721.     #
  722.     else
  723.     $discard_token := 1
  724.  
  725. end
  726.  
  727.  
  728. $$ifdef IIDEBUG
  729.  
  730. record production(LHS, RHS, POS, LOOK, no, prec, assoc)
  731. #
  732. # iidebug
  733. #
  734. procedure $iidebug(action, token, ruleno, parser)
  735.  
  736.     local p, t, state
  737.     static rule_list
  738.     initial {
  739.     rule_list := $rule_list_insertion_point
  740.     $$line 693 "iiglrpar.lib"
  741.     }
  742.  
  743.     write(&errout, "---  In parser ", image(parser), ":")
  744.     case action of {
  745.     "a"     : writes(&errout, "accepting ")    &
  746.         state := parser.state_stack[1]
  747.     "e"     : writes(&errout, "***ERROR***\n") &
  748.           write(&errout, "recover shifts = ",
  749.              parser.recover_shifts) &
  750.           write(&errout, "discarded tokens = ",
  751.              parser.discards) &
  752.               writes(&errout, "error action ") &
  753.         state := parser.state_stack[1]
  754.     "p"     : writes(&errout, "***PRUNING***\n") &
  755.               writes(&errout, "prune action ") &
  756.         state := parser.state_stack[1]
  757.     "r"     : writes(&errout, "reducing ")     &
  758.         state := parser.state_stack[2]
  759.     "s"     : writes(&errout, "shifting ")     &
  760.         state := parser.state_stack[2]
  761.     default : stop("malformed action argument to iidebug")
  762.     }
  763.  
  764.     t := image(token) || (" (" || (\$ttbl[token] | "unknown") || ")")
  765.     writes(&errout, "on lookahead ", t, ", in state ", state)
  766.     if \ruleno then {
  767.     (p := !rule_list).no === ruleno &
  768.         write(&errout, "; rule ", $production_2_string(p, $ttbl))
  769.     }
  770.     # for errors, ruleno is null
  771.     else write(&errout)
  772.  
  773.     write(&errout, "    state stack now: ")
  774.     every write(&errout, "\t", image(!parser.state_stack))
  775.     write(&errout, "    value stack now: ")
  776.     if *parser.value_stack > 0
  777.     then every write(&errout, "\t", image(!parser.value_stack))
  778.     else write(&errout, "\t(empty)")
  779.  
  780.     return
  781.  
  782. end
  783.  
  784.  
  785. #
  786. # production_2_string:  production record -> string
  787. #                       p                 -> s
  788. #
  789. #     Stringizes an image of the LHS and RHS of production p in
  790. #     human-readable form.
  791. #
  792. procedure $production_2_string(p, ibtoktbl)
  793.  
  794.     local s, m, t
  795.  
  796.     s := image(p.LHS) || " -> "
  797.     every m := !p.RHS do {
  798.     if t := \ (\ibtoktbl)[m]
  799.     then s ||:= t || " "
  800.     else s ||:= image(m) || " "
  801.     }
  802.     # if the POS field is nonnull, print it
  803.     s ||:= "(POS = " || image(\p.POS) || ") "
  804.     # if the LOOK field is nonnull, print it, too
  805.     s ||:= "lookahead = " || image(\p.LOOK)
  806.  
  807.     return trim(s)
  808.  
  809. end
  810.  
  811. #
  812. # show_new_forest
  813. #
  814. procedure show_new_forest(msg, actives, shifters, reducers, barfers, parser)
  815.     write(&errout, msg)
  816.     write(&errout, "    List of active parsers:")
  817.     every write(&errout, "\t", image(!actives))
  818.     every write(&errout, "\t", image(!shifters))
  819.     every write(&errout, "\t", image(!reducers))
  820.     every write(&errout, "\t", image(!barfers), " (error)")
  821.     write(&errout, "\tnew -> ", image(parser))
  822. end
  823. $$endif                # IIDEBUG
  824.  
  825.  
  826. $$ifdef COMPRESSED_TABLES
  827.  
  828. #
  829. # uncompress_action
  830. #
  831. procedure $uncompress_action()
  832.  
  833.     local next_chunk, full_action
  834.  
  835.     next_chunk := create ord(!&subject[&pos:0])
  836.     case $in_ib_bits(next_chunk, 2) of {
  837.     0: {
  838.         full_action := "s"
  839.         full_action ||:= $in_ib_bits(next_chunk, 11)
  840.         full_action ||:= "."
  841.         full_action ||:= $in_ib_bits(next_chunk, 11)
  842.         move(3)
  843.     }
  844.     1: {
  845.         full_action := "r"
  846.         full_action ||:= $in_ib_bits(next_chunk, 11)
  847.         full_action ||:= "<"
  848.         full_action ||:= $in_ib_bits(next_chunk, 11)
  849.         full_action ||:= ">"
  850.         full_action ||:= $in_ib_bits(next_chunk, 8)
  851.         move(4)
  852.     }
  853.         2: {
  854.         full_action := "a"
  855.         move(1)
  856.     }
  857.     } | fail
  858.  
  859.     return full_action
  860.  
  861. end
  862.  
  863.  
  864. #
  865. # in_ib_bits:  like inbits (IPL), but with coexpression for file
  866. #
  867. procedure $in_ib_bits(next_chunk, len)
  868.  
  869.     local i, byte, old_byte_mask
  870.     static old_byte, old_len, byte_length
  871.     initial {
  872.     old_byte := old_len := 0
  873.     byte_length := 8
  874.     }
  875.  
  876.     old_byte_mask := (0 < 2^old_len - 1) | 0
  877.     old_byte := iand(old_byte, old_byte_mask)
  878.     i := ishift(old_byte, len-old_len)
  879.  
  880.     len -:= (len > old_len) | {
  881.     old_len -:= len
  882.     return i
  883.     }
  884.     
  885.     while byte := @next_chunk do {
  886.     i := ior(i, ishift(byte, len-byte_length))
  887.     len -:= (len > byte_length) | {
  888.         old_len := byte_length-len
  889.         old_byte := byte
  890.         return i
  891.     }
  892.     }
  893.  
  894. end
  895.  
  896. $$endif                # COMPRESSED_TABLES
  897.  
  898. #
  899. # fullcopy:  make full recursive copy of object obj
  900. #
  901. procedure $fullcopy(obj)
  902.  
  903.     local retval, i, k
  904.  
  905.     case type(obj) of {
  906.         "co-expression"  : return obj
  907.         "cset"           : return obj
  908.         "file"           : return obj
  909.         "integer"        : return obj
  910.         "list"           : {
  911.             retval := list(*obj)
  912.             every i := 1 to *obj do
  913.                 retval[i] := $fullcopy(obj[i])
  914.             return retval
  915.         }
  916.         "null"           :  return &null
  917.         "procedure"      :  return obj
  918.         "real"           :  return obj
  919.         "set"            :  {
  920.             retval := set()
  921.             every insert(retval, $fullcopy(!obj))
  922.             return retval
  923.         }
  924.         "string"         :  return obj
  925.         "table"          :  {
  926.             retval := table(obj[[]])
  927.             every k := key(obj) do
  928.                 insert(retval, $fullcopy(k), $fullcopy(obj[k]))
  929.             return retval
  930.         }
  931.         # probably a record; if not, we're dealing with a new
  932.         # version of Icon or a nonstandard implementation, and
  933.     # we're screwed
  934.         default          :  {
  935.             retval := copy(obj)
  936.             every i := 1 to *obj do
  937.                 retval[i] := $fullcopy(obj[i])
  938.             return retval
  939.         }
  940.     }
  941.  
  942. end
  943.  
  944.  
  945. $$ifdef AUTO_PRUNE
  946. procedure auto_prune(actives)
  947.  
  948.     new_actives := []
  949.     while parser1 := pop(actives) do {
  950.     every parser2 := actives[j := 1 to *actives] do {
  951.         parser1.state_stack[1] = parser2.state_stack[1] | next
  952.         *parser1.value_stack   = *parser2.value_stack   | next
  953.         every i := 1 to *parser1.value_stack do {
  954.         parser1.value_stack[i] === parser2.value_stack[i] | 
  955.             break next
  956.         }
  957.         if parser1.errors < parser2.errors then
  958.         actives[j] := parser1
  959.         break next
  960.     }
  961.     put(new_actives, parser1)
  962.     }
  963.  
  964.     every put(actives, !new_actives)
  965.     return &null
  966.  
  967. end
  968. $$endif                # AUTO_PRUNE
  969.