Bell 32V development
[unix-history] / usr / doc / yacc / ss4
index d77c636..7cac9b2 100644 (file)
 .SH
 .SH
-Section 4: Ambiguity, Conflicts, and Precedence
+4: How the Parser Works
 .PP
 .PP
-A set of grammar rules is
-.ul
-ambiguous
-if there is some input string which can be structured in two or more different ways.
-For example, the grammar rule
-.DS
-expr :  expr \'\-\' expr ;
-.DE
-is a natural way of expressing the fact that one way of forming an arithmetic expression
-is to put two other expressions together with a minus sign between them.
-Unfortunately, this grammar rule does not
-completely specify the way that all complex inputs
-should be structured.
-For example, if we have input of the form
-.DS
-expr \- expr \- expr
-.DE
-the rule would permit us to treat this input either as
-.DS
-( expr \- expr ) \- expr
-.DE
-or as
-.DS
-expr \- ( expr \- expr )
-.DE
-(We speak of the first as
-.ul
-left association
-of operators, and the second as
-.ul
-right association).
+Yacc turns the specification file into a C program, which
+parses the input according to the specification given.
+The algorithm used to go from the
+specification to the parser is complex, and will not be discussed
+here (see
+the references for more information).
+The parser itself, however, is relatively simple,
+and understanding how it works, while
+not strictly necessary, will nevertheless make
+treatment of error recovery and ambiguities much more
+comprehensible.
 .PP
 .PP
-Yacc detects such ambiguities when it is attempting to build the parser.
-It is instructive to consider the problem that confronts the parser when it is
-given an input such as
-.DS
-expr \- expr \- expr
-.DE
-When the parser has read the second expr, the input which it has seen:
-.DS
-expr \- expr
-.DE
-matches the right side of the grammar rule above.
-One valid thing for the parser to do is to
-.ul
-reduce
-the input it has seen by applying this rule;
-after applying the rule, it would have reduced the input it had already seen to expr (the left side of the rule).
-It could then read the final part of the input:
-.DS
-\- expr
-.DE
-and again reduce by the rule.
-We see that the effect of this is to take the left associative interpretation.
+The parser produced by Yacc consists
+of a finite state machine with a stack.
+The parser is also capable of reading and remembering the next
+input token (called the
+.I lookahead
+token).
+The
+.I "current state"
+is always the one on the top of the stack.
+The states of the finite state machine are given
+small integer labels; initially, the machine is in state 0,
+the stack contains only state 0, and no lookahead token has been read.
 .PP
 .PP
-Alternatively, when the parser has seen
-.DS
-expr \- expr
-.DE
-it could defer the immediate application of the rule, and continue reading
-(the technical term is
-.ul
-shifting)
-the input until it had seen
-.DS
-expr \- expr \- expr
-.DE
-It could then apply the grammar rule to the rightmost three symbols, reducing them to expr and leaving
-.DS
-expr \- expr
-.DE
-Now it can reduce by the rule again; the effect is to
-take the right associative interpretation.
-Thus, having read
-.DS
-expr \- expr
-.DE
-the parser can do two legal things, a shift or a reduction, and has no way of
-deciding between them.
-We refer to this as a
-.ul
-shift/reduce conflict.
-It may also happen that the parser has a choice of two legal reductions;
-this is called a
-.ul
-reduce/reduce conflict.
-.PP
-When there are shift/reduce or reduce/reduce conflicts, Yacc still produces a parser.
-It does this by selecting one of the valid steps wherever it has a choice.
-A rule which describes which choice to make in a given situation is called
-a
-.ul
-disambiguating rule.
-.PP
-Yacc has two disambiguating rules which are invoked by default,
-in the absence of any user directives to the contrary:
+The machine has only four actions available to it, called
+.I shift ,
+.I reduce ,
+.I accept ,
+and
+.I error .
+A move of the parser is done as follows:
 .IP 1.
 .IP 1.
-In a shift/reduce conflict, the default is to do the shift.
+Based on its current state, the parser decides
+whether it needs a lookahead token to decide
+what action should be done; if it needs one, and does
+not have one, it calls
+.I yylex
+to obtain the next token.
 .IP 2.
 .IP 2.
-In a reduce/reduce conflict, the default is to reduce by the
-.ul
-earlier
-grammar rule (in the input sequence).
+Using the current state, and the lookahead token
+if needed, the parser decides on its next action, and
+carries it out.
+This may result in states being pushed onto the stack, or popped off of
+the stack, and in the lookahead token being processed
+or left alone.
 .PP
 .PP
-Rule 1 implies that reductions are deferred whenever there is a choice,
-in favor of shifts.
-Rule 2 gives the user rather crude control over the behavior of the parser
-in this situation, but the proper use of reduce/reduce conflicts is still a black art, and is
-properly considered an advanced topic.
+The
+.I shift
+action is the most common action the parser takes.
+Whenever a shift action is taken, there is always
+a lookahead token.
+For example, in state 56 there may be
+an action:
+.DS
+       IF      shift 34
+.DE
+which says, in state 56, if the lookahead token is IF,
+the current state (56) is pushed down on the stack,
+and state 34 becomes the current state (on the
+top of the stack).
+The lookahead token is cleared.
 .PP
 .PP
-Conflicts may arise because of mistakes in input or logic, or because the grammar rules, while consistent,
-require a more complex parser than Yacc can construct.
-In these cases, the application of disambiguating rules is inappropriate,
-and leads to a parser which is in error.
-For this reason, Yacc
-always reports the number of shift/reduce and reduce/reduce conflicts which were resolved by Rule 1 and Rule 2.
+The
+.I reduce
+action keeps the stack from growing without
+bounds.
+Reduce actions are appropriate when the parser has seen
+the right hand side of a grammar rule,
+and is prepared to announce that it has seen
+an instance of the rule, replacing the right hand side
+by the left hand side.
+It may be necessary to consult the lookahead token
+to decide whether to reduce, but
+usually it is not; in fact, the default
+action (represented by a ``.'') is often a reduce action.
 .PP
 .PP
-In general, whenever it is possible to apply disambiguating rules to produce a correct parser, it is also
-possible to rewrite the grammar rules so that the same inputs are read, but there are no
-conflicts.
-For this reason, most previous systems like Yacc
-have considered conflicts to be fatal errors.
-Our experience has suggested that this rewriting is somewhat unnatural to do,
-and produces slower parsers; thus, Yacc will produce parsers even in the presence of conflicts.
-.PP
-As an example of the power of disambiguating rules, consider a fragment from a programming
-language involving an
-``if-then-else'' construction:
-.DS
-stat : IF \'(\' cond \')\' stat |
-       IF \'(\' cond \')\' stat ELSE stat ;
-.DE
-Here, we consider IF and ELSE to be tokens, cond to be a nonterminal symbol describing
-conditional (logical) expressions, and
-stat to be a nonterminal symbol describing statements.
-In the following, we shall refer to these two rules as the
-.ul
-simple-if
-rule and the
-.ul
-if-else
-rule, respectively.
-.PP
-These two rules form an ambiguous construction, since input of the form
-.DS
-IF ( C1 ) IF ( C2 ) S1 ELSE S2
-.DE
-can be structured according to these rules in two ways:
-.DS
-IF ( C1 ) {
-       IF ( C2 ) S1
-}
-ELSE S2
-.DE
-or
+Reduce actions are associated with individual grammar rules.
+Grammar rules are also given small integer
+numbers, leading to some confusion.
+The action
 .DS
 .DS
-IF ( C1 ) {
-       IF ( C2 ) S1
-       ELSE S2
-}
+       \fB.\fR reduce 18
 .DE
 .DE
-The second interpretation is the one given in most programming languages
-which have this construct.
-Each ELSE is associated with the last preceding ``un-ELSE'd'' IF.
-In this example, consider the situation where the parser has seen
+refers to
+.I "grammar rule"
+18, while the action
 .DS
 .DS
-IF ( C1 ) IF ( C2 ) S1
+       IF      shift 34
 .DE
 .DE
-and is looking at the ELSE.
-It can immediately
-.ul
-reduce
-by the simple-if rule to get
-.DS
-IF ( C1 ) stat
-.DE
-and then read the remaining input,
-.DS
-ELSE S2
-.DE
-and reduce
-.DS
-IF ( C1 ) stat ELSE S2
-.DE
-by the if-else rule.
-This leads to the first of the above groupings of the input.
+refers to
+.I state
+34.
 .PP
 .PP
-On the other hand, we may
-.ul
-shift
-the ELSE and read S2, and then reduce the right hand portion of
-.a
-.DS
-IF ( C1 ) IF ( C2 ) S1 ELSE S2
-.DE
-by the if-else rule to get
-.DS
-IF ( C1 ) stat
-.DE
-which can be reduced by the simple-if rule.
-This leads to the second of the above groupings of the input, which
-is usually desired.
+Suppose the rule being reduced is
+.DS
+A      \fB:\fR x  y  z    ;
+.DE
+The reduce action depends on the
+left hand symbol (A in this case), and the number of
+symbols on the right hand side (three in this case).
+To reduce, first pop off the top three states
+from the stack
+(In general, the number of states popped equals the number of symbols on the
+right side of the rule).
+In effect, these states were the ones
+put on the stack while recognizing
+.I x ,
+.I y ,
+and
+.I z ,
+and no longer serve any useful purpose.
+After popping these states, a state is uncovered
+which was the state the parser was in before beginning to
+process the rule.
+Using this uncovered state, and the symbol
+on the left side of the rule, perform what is in
+effect a shift of A.
+A new state is obtained, pushed onto the stack, and parsing continues.
+There are significant differences between the processing of
+the left hand symbol and an ordinary shift of a token,
+however, so this action is called a
+.I goto
+action.
+In particular, the lookahead token is cleared by a shift, and
+is not affected by a goto.
+In any case, the uncovered state contains an entry such as:
+.DS
+       A       goto 20
+.DE
+causing state 20 to be pushed
+onto the stack, and become the current state.
+.PP
+In effect, the reduce action ``turns back the clock'' in the parse,
+popping the states off the stack to go back to the
+state where the right hand side of the rule was first seen.
+The parser then behaves as if it had seen the left side at that time.
+If the right hand side of the rule is empty,
+no states are popped off of the stack: the uncovered state
+is in fact the current state.
+.PP
+The reduce action is also important in the treatment of user-supplied
+actions and values.
+When a rule is reduced, the code supplied with the rule is executed
+before the stack is adjusted.
+In addition to the stack holding the states, another stack,
+running in parallel with it, holds the values returned
+from the lexical analyzer and the actions.
+When a shift takes place, the external variable
+.I yylval
+is copied onto the value stack.
+After the return from the user code, the reduction is carried out.
+When the
+.I goto
+action is done, the external variable
+.I yyval
+is copied onto the value stack.
+The pseudo-variables $1, $2, etc., refer to the value stack.
 .PP
 .PP
-Once again the parser can do two valid things \- we have a shift/reduce conflict.
-The application of disambiguating rule 1 tells the parser to shift in this case,
-which leads to the desired grouping.
+The other two parser actions are conceptually much simpler.
+The
+.I accept
+action indicates that the entire input has been seen and
+that it matches the specification.
+This action appears only when the lookahead token is 
+the endmarker, and indicates that the parser has successfully
+done its job.
+The
+.I error
+action, on the other hand, represents a place where the parser
+can no longer continue parsing according to the specification.
+The input tokens it has seen, together with the lookahead token,
+cannot be followed by anything that would result
+in a legal input.
+The parser reports an error, and attempts to recover the situation and
+resume parsing: the error recovery (as opposed to the detection of error)
+will be covered in Section 7.
 .PP
 .PP
-Notice that this shift/reduce conflict arises only when there is a particular current input symbol,
-ELSE, and particular inputs already seen, such as
+It is time for an example!
+Consider the specification
 .DS
 .DS
-IF ( C1 ) IF ( C2 ) S1
+%token  DING  DONG  DELL
+%%
+rhyme  :       sound  place
+       ;
+sound  :       DING  DONG
+       ;
+place  :       DELL
+       ;
 .DE
 .DE
-In general, there may be many conflicts, and each one
-will be associated with an input symbol and
-a set of previously read inputs.
-The previously read inputs are characterized by the
-.ul
-state
-of the parser, which is assigned a nonnegative integer.
-The number of states in the parser is typically two to five times the number of grammar
-rules.
 .PP
 .PP
-When Yacc is invoked with the verbose
-(\-v) option (see Appendix B), it produces a file of user output
-which includes a description of the states in the parser.
-For example, the output corresponding to the above
-example might be:
-.DS L
-23: shift/reduce Conflict (Shift 45, Reduce 18) on ELSE
+When Yacc is invoked with the
+.B \-v
+option, a file called
+.I y.output
+is produced, with a human-readable description of the parser.
+The
+.I y.output
+file corresponding to the above grammar (with some statistics
+stripped off the end) is:
+.DS
+state 0
+       $accept  :  \_rhyme  $end 
 
 
-State 23
+       DING  shift 3
+       .  error
 
 
-       stat : IF ( cond ) stat\_
-       stat : IF ( cond ) stat\_ELSE stat
+       rhyme  goto 1
+       sound  goto 2
 
 
-       ELSE    shift 45
-       .               reduce 18
+state 1
+       $accept  :   rhyme\_$end 
 
 
-.DE
-The first line describes the conflict, giving the state and the input symbol.
-The state title follows, and a brief description of the grammar
-rules which are active in this state.
-The underline ``\_'' describes the
-portions of the grammar rules which have been seen.
-Thus in the example, in state 23 we have seen input which corresponds
-to
-.DS
-IF ( cond ) stat
-.DE
-and the two grammar rules shown are active at this time.
-The actions possible are, if the input symbol is ELSE, we may shift into state
-45.
-In this state, we should find as part of the description a line of the form
-.DS
-stat : IF ( cond ) stat ELSE\_stat
-.DE
-because in this state we will have read and shifted the ELSE.
-Back in state 23, the alternative action, described by ``.'',
-is to be done if the input symbol is not mentioned explicitly in the above actions; thus,
-in this case, if the input symbol is not ELSE, we should reduce by grammar rule 18,
-which is presumably
-.DS
-stat : IF \'(\' cond \')\' stat
-.DE
-Notice that the numbers following ``shift'' commands refer to other states,
-while the numbers following ``reduce'' commands refer to grammar
-rule numbers.
-In most states, there will be only one reduce action possible in the
-state, and this will always be the default command.
-The user who encounters unexpected shift/reduce conflicts will probably want to
-look at the verbose output to decide whether the default actions are appropriate.
-In really tough cases, the user might need to know more about
-the behavior and construction of the parser than can be covered here;
-in this case, a reference such as [1]
-might be consulted; the services of a local guru might also be appropriate.
-.PP
-There is one common situation
-where the rules given above for resolving conflicts are not sufficient;
-this is in the area of arithmetic expressions.
-Most of the commonly used constructions for arithmetic expressions can be naturally
-described by the notion of
-.ul
-precedence
-levels for operators, together with information about left
-or right associativity.
-It turns out that ambiguous grammars with appropriate disambiguating rules
-can be used to create parsers which are faster and easier to
-write than parsers constructed from unambiguous grammars.
-The basic notion is to write grammar rules
-of the form
-.DS
-expr : expr OP expr
-.DE
-and
-.DS
-expr : UNARY expr
-.DE
-for all binary and unary operators desired.
-This creates a very ambiguous grammar, with many parsing conflicts.
-As disambiguating rules, the user specifies the precedence, or binding
-strength, of all the operators, and the associativity
-of the binary operators.
-This information is sufficient to allow Yacc to resolve the parsing conflicts
-in accordance with these rules, and construct a parser which realizes the desired
-precedences and associativities.
-.PP
-The precedences and associativities are attached to tokens in the declarations section.
-This is done by a series of lines beginning with a Yacc keyword: %left, %right,
-or %nonassoc, followed by a list of tokens.
-All of the tokens on the same line are assumed to have the same precedence level
-and associativity; the lines are listed in
-order of increasing precedence or binding strength.
-Thus,
-.DS
-%left \'+\' \'\-\'
-%left \'*\' \'/\'
-.DE
-describes the precedence and associativity of the four arithmetic operators.
-Plus and minus are left associative, and have lower precedence than
-star and slash, which are also left associative.
-The keyword %right is used to describe right associative operators,
-and the keyword %nonassoc is used to describe operators, like
-the operator .LT. in Fortran, which may not associate with themselves; thus,
-.DS
-A .LT. B .LT. C
-.DE
-is illegal in Fortran, and such an operator would be described with the keyword
-%nonassoc in Yacc.
-As an example of the behavior of these declarations, the description
-.DS
-%right \'=\'
-%left \'+\' \'\-\'
-%left \'*\' \'/\'
+       $end  accept
+       .  error
 
 
-%%
+state 2
+       rhyme  :   sound\_place 
 
 
-expr :
-       expr \'=\' expr |
-       expr \'+\' expr |
-       expr \'\-\' expr |
-       expr \'*\' expr |
-       expr \'/\' expr |
-       NAME ;
-.DE
-might be used to structure the input
-.DS
-a = b = c*d \- e \- f*g
-.DE
-as follows:
-.DS
-a = ( b = ( ((c*d)\-e) \- (f*g) ) )
-.DE
-When this mechanism is used,
-unary operators must, in general, be given a precedence.
-An interesting situation arises when we have a unary operator and a binary operator
-which have the same symbolic representation, but different precedences.
-An example is unary and binary \'\-\'; frequently, unary minus is given the same
-strength as multiplication, or even higher, while binary minus has a lower strength than
-multiplication.
-We can indicate this situation by use of
-another keyword, %prec, to change the precedence level associated with a particular grammar rule.
-%prec appears immediately after the body of the grammar rule, before the action or closing semicolon,
-and is followed by a token name or literal; it
-causes the precedence of the grammar rule to become that of the token name or literal.
-Thus, to make unary minus have the same precedence as multiplication, we might write:
-.DS
-%left \'+\' \'\-\'
-%left \'*\' \'/\'
+       DELL  shift 5
+       .  error
 
 
-%%
+       place   goto 4
+
+state 3
+       sound   :   DING\_DONG 
 
 
-expr :
-       expr \'+\' expr |
-       expr \'\-\' expr |
-       expr \'*\' expr |
-       expr \'/\' expr |
-       \'\-\' expr %prec \'*\' |
-       NAME ;
+       DONG  shift 6
+       .  error
+
+state 4
+       rhyme  :   sound  place\_    (1)
+
+       .   reduce  1
+
+state 5
+       place  :   DELL\_    (3)
+
+       .   reduce  3
+
+state 6
+       sound   :   DING  DONG\_    (2)
+
+       .   reduce  2
+.DE
+Notice that, in addition to the actions for each state, there is a
+description of the parsing rules being processed in each
+state.  The \_ character is used to indicate
+what has been seen, and what is yet to come, in each rule.
+Suppose the input is
+.DS
+DING  DONG  DELL
 .DE
 .DE
+It is instructive to follow the steps of the parser while
+processing this input.
 .PP
 .PP
-Notice that the precedences which are described
-by %left, %right, and %nonassoc are independent of the declarations of token names
-by %token.
-A symbol can be declared by %token, and, later in the declarations section,
-be given a precedence and associativity by one of the above methods.
-It is true, however, that names which are given a precedence or associativity are
-also declared to be token names, and so in general do not need to be
-declared by %token, although it does not hurt to do so.
+Initially, the current state is state 0.
+The parser needs to refer to the input in order to decide
+between the actions available in state 0, so
+the first token,
+.I DING ,
+is read, becoming the lookahead token.
+The action in state 0 on
+.I DING
+is
+is ``shift 3'', so state 3 is pushed onto the stack,
+and the lookahead token is cleared.
+State 3 becomes the current state.
+The next token,
+.I DONG ,
+is read, becoming the lookahead token.
+The action in state 3 on the token
+.I DONG
+is ``shift 6'',
+so state 6 is pushed onto the stack, and the lookahead is cleared.
+The stack now contains 0, 3, and 6.
+In state 6, without even consulting the lookahead,
+the parser reduces by rule 2.
+.DS
+       sound  :   DING  DONG
+.DE
+This rule has two symbols on the right hand side, so
+two states, 6 and 3, are popped off of the stack, uncovering state 0.
+Consulting the description of state 0, looking for a goto on 
+.I sound ,
+.DS
+       sound   goto 2
+.DE
+is obtained; thus state 2 is pushed onto the stack,
+becoming the current state.
 .PP
 .PP
-As we mentioned above, the precedences and associativities are used by Yacc to
-resolve parsing conflicts; they give rise to disambiguating rules.
-Formally, the rules work as follows:
-.IP 1.
-The precedences and associativities are recorded for those tokens and literals
-which have them.
-.IP 2.
-A precedence and associativity is associated with each grammar rule; it is the precedence
-and associativity of the last token or literal in the body of the rule.
-If the %prec construction is used, it overrides this default.
-Notice that some grammar rules may have no precedence and associativity associated with them.
-.IP 3.
-When there is a reduce/reduce conflict, or there is a shift/reduce conflict
-and either the input symbol or the grammar rule, or both, has no precedence and associativity
-associated with it, then the two disambiguating rules given at the beginning of the section are used,
-and the conflicts are reported.
-.IP 4.
-If there is a shift/reduce conflict, and both the grammar rule and the input character
-have precedence and associativity associated with them, then the conflict is resolved
-in favor of the action (shift or reduce) associated with the higher precedence.
-If the precedences are the same, then the associativity is used; left
-associative implies reduce, right associative implies shift, and nonassociating
-implies error.
+In state 2, the next token,
+.I DELL ,
+must be read.
+The action is ``shift 5'', so state 5 is pushed onto the stack, which now has
+0, 2, and 5 on it, and the lookahead token is cleared.
+In state 5, the only action is to reduce by rule 3.
+This has one symbol on the right hand side, so one state, 5,
+is popped off, and state 2 is uncovered.
+The goto in state 2 on
+.I place ,
+the left side of rule 3,
+is state 4.
+Now, the stack contains 0, 2, and 4.
+In state 4, the only action is to reduce by rule 1.
+There are two symbols on the right, so the top two states are popped off,
+uncovering state 0 again.
+In state 0, there is a goto on
+.I rhyme
+causing the parser to enter state 1.
+In state 1, the input is read; the endmarker is obtained,
+indicated by ``$end'' in the
+.I y.output
+file.
+The action in state 1 when the endmarker is seen is to accept,
+successfully ending the parse.
 .PP
 .PP
-There are a number of points worth making
-about this use of disambiguation.
-There is no reporting of conflicts which are resolved by this mechanism,
-and these conflicts are not counted in the number of shift/reduce and reduce/reduce
-conflicts found in the grammar.
-This means that occasionally mistakes in the specification of precedences
-disguise errors in the input grammar; it is a good idea to be sparing
-with precedences, and use them in an essentially ``cookbook'' fashion,
-until some experience has been gained.
-Frequently, not enough operators or precedences have been specified;
-this leads to a number of messages about shift/reduce or reduce/reduce conflicts.
-The cure is usually to specify more precedences, or use the %prec mechanism, or both.
-It is generally good to examine the verbose output file to ensure that
-the conflicts which are being reported can be validly resolved by precedence.
+The reader is urged to consider how the parser works
+when confronted with such incorrect strings as
+.I "DING DONG DONG" ,
+.I "DING DONG" ,
+.I "DING DONG DELL DELL" ,
+etc.
+A few minutes spend with this and other simple examples will
+probably be repaid when problems arise in more complicated contexts.