Advice on introducing actions into your yacc parser --------------------------------------------------- In addition to the advice given in the sample generator file, you should keep in mind the following, which is either not explained in the book, or is hidden. 0. If you use the $0, $-1, $-2 and etc. variables, you need to give them a type, because they are NON-LOCAL variables (they refer to the grammar symbols in another rule, and yacc can't figure out which rule---there may be more than one---where it will be used). So, you need to give a type, using the type names from your %union command: $0 $-1.place etc. You might also need to do this when using $1, $2, and etc. in embedded actions, because they turn into separate dummy erasing rules. See p. 184 for more about this. You will get a type error from yacc if you don't do this right. 1. When introducing embedded actions, you can introduce shift/reduce or reduce/reduce errors. The book talks a little bit about how to avoid these. The main problem I and others have found is that in the conditional, embedded actions in the first part of the RHS (the part common to two rules) causes a R/R error: %nonassoc THEN %nonassoc ELSE s : IF expr { alpha } THEN s | IF expr { alpha } THEN s ELSE s ; This turns into s : IF expr dum1 THEN s | IF expr dum2 THEN s ELSE s ; dum1 : { alpha } ; dum2 : { alpha } ; The problem is that after seeing "IF expr" the parser doesn't yet know what rule it is in, so it can't figure out which of the two dummy rules to use. SOLUTION: Fortunately, when the LHS are similar like this, usually the actions are the same; this is certainly true in the if-then and if-then-else. The FIRST thing to try when having a problem with embedded actions is to make the dummy rule explicit (write it yourself in stead of letting yacc do it for you), and here, you should use the same dummy, since the actions are the same: s : IF expr dum THEN s | IF expr dum THEN s ELSE s ; dum : { alpha } ; Remember that yacc can't figure out that your actions are the same, so it produces TWO DIFFERENT dummies, and that is the source of the R/R error. 2. Another source of errors is that shift/reduce errors can turn into reduce/reduce errors if an embedded action occurs at the location of the S/R error. If you didn't use the %nonassoc THEN %nonassoc ELSE trick you would get a S/R error at the end of the first rule: s : IF expr dum THEN s ^ | IF expr dum THEN s ELSE s ; ^ dum : { alpha } ; This is because it can't figure out whether to shift the ELSE or reduce (the correct answer is of course to shift). Now, if you introduce another embedded action at this point, e.g., s : IF expr dum THEN s { beta1 } | IF expr dum THEN s { beta2 } ELSE s ; dum : { alpha } ; Then the S/R turns into a R/R, because instead of worrying about whether to reduce (choosing the first rule) or to shift the ELSE (choosing the second rule), it has to worry about whether to reduce by the first dummy for { beta1 } (choosing the first rule) or by the second dummy for { beta2 } (choosing the second rule). You would think that using %nonassoc THEN %nonassoc ELSE would help. Without embedded actions, this is telling the parser that the "operator" ELSE (which looks like an operator in "IF expr THEN stmt ELSE stmt") binds more tightly then THEN, and so you should shift. However, with the embedded actions, it still can't figure out which dummy rules for the beta's (remember, there will be two) to reduce, because ELSE IS IN THE FOLLOW SET OF EACH OF THE DUMMIES. The solution is remarkably simply, however: simply move the second { beta2 } action to AFTER THE ELSE: %nonassoc THEN %nonassoc ELSE s : IF expr dum THEN s { beta1 } | IF expr dum THEN s ELSE { beta2 } s ; dum : { alpha } ; What this does is to let the "precedence" of ELSE do its work before seeing the dummy for the beta's. If you don't do this, the parser has to worry about the beta's before using the precedence of ELSE and THEN. This change also changes the follow set of the { beta2 } dummy rule so that it does not include ELSE. 3. My general advice if you have having problems with the embedded actions is to (i) make the dummy erasing rules for embedded actions explicit, as mentioned above, and (ii) use an explicit grammar instead of the ambiguous grammar with the precedence rules (%left and etc.). The reason for this latter piece of advice is that the precedence rules behave in a way that is hidden to you, and so it will be hard to figure out the interaction of the various components. Finally, the most important piece of advice is one that is generally good advice for any programming task, and that is to develop your program INCREMENTALLY. Add a little bit and test, and get that little bit working (test it thoroughly!) before adding anything else. This is crucial with yacc because it does so many hidden and unintuitive things with your source file, and you can literally spend days tracking down a simple bug. Note that in the class notes for code generation and so on, I did not follow this advice, in the interests of readability; you can try it the way it is in the notes, and then if you have problems, do the explicit method just discussed. Whatever works.....