From: mcglk@cpac.washington.edu (Ken McGlothlen)
Newsgroups: comp.unix.programmer
Subject: `yacc' problems.
Date: 7 Apr 92 22:11:40 GMT
Article-I.D.: bailey.MCGLK.92Apr7141142
Distribution: na
Organization: Dubious.

Up until recently, I resisted using `lex' and `yacc' for doing programming, on
the (admittedly petulant and silly) principal that I always wanted to decipher
the C code of anything I wrote.  Fear of the unknown, I guess.

So I finally succumbed and started diddling around with them.  I've decided I
*really* like `lex'.  A lot.  I'm having more problems with `yacc'.
Specifically, elegant syntax-error handling.  The following "shar" file is a
small, pared-down, minimalistic example of the things that I'm trying to do.

Could someone help me out here?  I'm 'way confused.

				---Ken McGlothlen
				   mcglk@u.washington.edu
				   mcglk@cpac.bitnet

#------------------------------CUT HERE----------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Makefile
#	README
#	inputfile
#	output.current
#	output.diffs
#	output.ideal
#	test.c
#	test.h
#	testlex.lex
#	testyacc.y
# This archive created: Tue Apr  7 14:11:07 1992
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'Makefile'" '(611 characters)'
if test -f 'Makefile'
then
    mv Makefile Makefile~old; echo shar: "moved Makefile to Makefile~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'Makefile'
XCC=gcc
XYACC=yacc -d -t
XLEX=lex
XCCOPTS=-Wall -g
XNOWARNCCOPTS=-g
X
XSRCS=test.c testyacc.c testlex.c
XHS=test.h testlex.h
XOBJS=test.o testyacc.o testlex.o
X
Xtestyacc: $(OBJS)
X	$(CC) $(CCOPTS) -o $@ $(OBJS) -ll -ly
X
Xtestyacc.o: testyacc.c
X	$(CC) $(NOWARNCCOPTS) -c testyacc.c
X
Xtestlex.o: testlex.c
X	$(CC) $(NOWARNCCOPTS) -c testlex.c
X
Xtestyacc.c: testyacc.y test.h
X	$(YACC) testyacc.y
X	mv *.tab.c $@
X	mv *.tab.h testlex.h
X
Xtestlex.c: testlex.lex testlex.h
X	$(LEX) testlex.lex
X	mv *.yy.c $@
X
Xclean:
X	rm -f *.o testyacc testyacc.c testlex.c testlex.h
X	rm -f *.yy.c *.tab.c *~ core y.output
X
X.c.o:
X	$(CC) $(CCOPTS) -c $<
OogaBooga
if test 611 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 611 characters)'
fi
chmod 600 'Makefile'
fi
echo shar: "extracting 'README'" '(1605 characters)'
if test -f 'README'
then
    mv README README~old; echo shar: "moved README to README~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'README'
XYou should be able to type "make", and have this compile on any Unix system
Xequipped with gcc.
X
XThe final program is called `testyacc'.  Run it like this:
X
X	% testyacc < inputfile
X
XWhat I'm *trying* to get is listed in "output.ideal", but you'll note that the
Xactual output has some problems.  The current output is "output.current", and
Xthe diffs are in "output.diffs".
X
XIdeally, I'm trying to figure out three things:
X
X   1.	Where it says
X
X		cmd1_s matched 4 1992 3 4 on line 5
X
X	it *should* read
X
X		cmd1_s matched commanda 1992 3 4 on line 6
X
X	For some reason, $1 (which should be the string "commanda") is being
X	replaced with $4 (in this case, the *string* "4").  This error shows up
X	again in the error message on line 7.
X
X   2.	Where it says
X
X		booga.txt:8: syntax error
X
X	*should* read
X
X		booga.txt:8: "5" is insufficient!
X
X	Essentially, we're supplying an additional input to commanda, which
X	goes
X
X		commanda y m d q
X
X	where q is the extra parameter.  This *should* get parsed as "commanda
X	y m d" as one expression, and then gag on the lone "q", which *should*
X	get caught as an error with the appropriate "insufficient" message.
X	Instead, yyerror is getting called with "syntax error," which is not
X	what I want.
X
X   3.	Where it says
X
X		booga.txt:10: "
X		 1992 3" requires three parameters!
X
X	*should* read
X
X		booga.txt:9: "commanda 1992 3" requires three parameters!
X
XI'm not so concerned about the line-number off-by-one errors I'm getting---I
Xthink I know why those are occurring.  I'm more concerned because I don't
Xunderstand how the error-handling mechanisms work.  Help?
X
X							---Ken
OogaBooga
if test 1605 -ne "`wc -c < 'README'`"
then
	echo shar: "error transmitting 'README'" '(should have been 1605 characters)'
fi
chmod 644 'README'
fi
echo shar: "extracting 'inputfile'" '(168 characters)'
if test -f 'inputfile'
then
    mv inputfile inputfile~old; echo shar: "moved inputfile to inputfile~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'inputfile'
X(2 3 4 ^ 3 4 5 & ! 5 6 7) ^ ! 7 8 9
X
Xcommandc | 0 0 31
X1992 1 1
X1992 0 2
Xcommanda 1992 3 4
Xcommanda 1992 0 4
Xcommanda 1992 3 4 5
Xcommanda 1992 3
Xcommanda 1992
Xcommanda
OogaBooga
if test 168 -ne "`wc -c < 'inputfile'`"
then
	echo shar: "error transmitting 'inputfile'" '(should have been 168 characters)'
fi
chmod 600 'inputfile'
fi
echo shar: "extracting 'output.current'" '(552 characters)'
if test -f 'output.current'
then
    mv output.current output.current~old; echo shar: "moved output.current to output.current~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'output.current'
X>line 1: `CMD2_c 2 3 4 CMD2_c 3 4 5 CMD2_c 5 6 7 ! & ^ CMD2_c 7 8 9 ! ^'
X>line 3: `CMD3_C CMD2_c 0 0 31 |'
X>line 4: `CMD2_c 1992 1 1'
X>line 5: `CMD2_c 1992 0 2'
Xcmd1_s matched 4 1992 3 4 on line 5
X>line 6: `CMD1_S 1992 3 4'
Xcmd1_s matched 4 1992 0 4 on line 6
Xbooga.txt:7: `0' illegal in "4 1992 0 4"
X>line 7: `'
Xcmd1_s matched 4 1992 3 4 on line 7
Xbooga.txt:8: syntax error
Xbooga.txt:10: "
X 1992 3" requires three parameters!
X>line 9: `'
Xbooga.txt:11: "
X 1992" requires three parameters!
X>line 10: `'
Xbooga.txt:12: "
X" has no parameters!
X>line 11: `'
OogaBooga
if test 552 -ne "`wc -c < 'output.current'`"
then
	echo shar: "error transmitting 'output.current'" '(should have been 552 characters)'
fi
chmod 644 'output.current'
fi
echo shar: "extracting 'output.diffs'" '(798 characters)'
if test -f 'output.diffs'
then
    mv output.diffs output.diffs~old; echo shar: "moved output.diffs to output.diffs~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'output.diffs'
X5c5
X< cmd1_s matched commanda 1992 3 4 on line 6
X---
X> cmd1_s matched 4 1992 3 4 on line 5
X7,8c7,8
X< cmd1_s matched commanda 1992 0 4 on line 7
X< booga.txt:7: `0' illegal in "commanda 1992 0 4"
X---
X> cmd1_s matched 4 1992 0 4 on line 6
X> booga.txt:7: `0' illegal in "4 1992 0 4"
X10,13c10,13
X< cmd1_s matched commanda 1992 3 4 on line 8
X< booga.txt:8: "5" is insufficient!
X< >line 8: `'
X< booga.txt:9: "commanda 1992 3" requires three parameters!
X---
X> cmd1_s matched 4 1992 3 4 on line 7
X> booga.txt:8: syntax error
X> booga.txt:10: "
X>  1992 3" requires three parameters!
X15c15,16
X< booga.txt:10: "commanda 1992" requires three parameters!
X---
X> booga.txt:11: "
X>  1992" requires three parameters!
X17c18,19
X< booga.txt:11: "commanda" has no parameters!
X---
X> booga.txt:12: "
X> " has no parameters!
OogaBooga
if test 798 -ne "`wc -c < 'output.diffs'`"
then
	echo shar: "error transmitting 'output.diffs'" '(should have been 798 characters)'
fi
chmod 644 'output.diffs'
fi
echo shar: "extracting 'output.ideal'" '(620 characters)'
if test -f 'output.ideal'
then
    mv output.ideal output.ideal~old; echo shar: "moved output.ideal to output.ideal~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'output.ideal'
X>line 1: `CMD2_c 2 3 4 CMD2_c 3 4 5 CMD2_c 5 6 7 ! & ^ CMD2_c 7 8 9 ! ^'
X>line 3: `CMD3_C CMD2_c 0 0 31 |'
X>line 4: `CMD2_c 1992 1 1'
X>line 5: `CMD2_c 1992 0 2'
Xcmd1_s matched commanda 1992 3 4 on line 6
X>line 6: `CMD1_S 1992 3 4'
Xcmd1_s matched commanda 1992 0 4 on line 7
Xbooga.txt:7: `0' illegal in "commanda 1992 0 4"
X>line 7: `'
Xcmd1_s matched commanda 1992 3 4 on line 8
Xbooga.txt:8: "5" is insufficient!
X>line 8: `'
Xbooga.txt:9: "commanda 1992 3" requires three parameters!
X>line 9: `'
Xbooga.txt:10: "commanda 1992" requires three parameters!
X>line 10: `'
Xbooga.txt:11: "commanda" has no parameters!
X>line 11: `'
OogaBooga
if test 620 -ne "`wc -c < 'output.ideal'`"
then
	echo shar: "error transmitting 'output.ideal'" '(should have been 620 characters)'
fi
chmod 644 'output.ideal'
fi
echo shar: "extracting 'test.c'" '(409 characters)'
if test -f 'test.c'
then
    mv test.c test.c~old; echo shar: "moved test.c to test.c~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'test.c'
X#define MAIN_PROGRAM (1)
X
X#include "test.h"
X
Xint  yyparse();
X
Xint  yyerror( s )
X    char  *s;
X{
X    printf( "%s:%d: %s\n", fname, linum + 1, s );
X    return( 0 );
X}
X
Xvoid  result( s )
X    char  *s;
X{
X    printf( ">line %d: `%s'\n", linum, s );
X}
X
Xint main()
X{
X    extern int yydebug;
X    yydebug = 0;
X    strcpy( fname, "booga.txt" );
X    linum = 0;
X    while( !feof( stdin ) )
X	yyparse();
X    return( 0 );
X}
OogaBooga
if test 409 -ne "`wc -c < 'test.c'`"
then
	echo shar: "error transmitting 'test.c'" '(should have been 409 characters)'
fi
chmod 600 'test.c'
fi
echo shar: "extracting 'test.h'" '(225 characters)'
if test -f 'test.h'
then
    mv test.h test.h~old; echo shar: "moved test.h to test.h~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'test.h'
X#include <string.h>
X#include <stdio.h>
X
X#ifdef MAIN_PROGRAM
X#define GLOBAL
X#else
X#define GLOBAL extern
X#endif
X
XGLOBAL char	fname[128];
XGLOBAL int	linum;
X
Xint	yyparse(), yylex();
Xint	yyerror( char * );
Xvoid	result( char * );
X
OogaBooga
if test 225 -ne "`wc -c < 'test.h'`"
then
	echo shar: "error transmitting 'test.h'" '(should have been 225 characters)'
fi
chmod 600 'test.h'
fi
echo shar: "extracting 'testlex.lex'" '(863 characters)'
if test -f 'testlex.lex'
then
    mv testlex.lex testlex.lex~old; echo shar: "moved testlex.lex to testlex.lex~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'testlex.lex'
X%{
X
X#include "testlex.h"
X#include "test.h"
X
Xchar	lexerr[128];
X
X%}
X
X%%
X
X[!^&|()]	\
X	{						/* operators */
X		return( *yytext );
X	}
X
X[\n]		\
X	{						/* newline */
X		++linum;
X		return( *yytext );
X	}
X
X[Cc][Oo][Mm][Mm][Aa][Nn][Dd][Aa]	\
X	{
X		yylval.sval = yytext;
X		return( CMD1_S );
X	}
X
X[Cc][Oo][Mm][Mm][Aa][Nn][Dd][Bb]	\
X	{
X		yylval.sval = yytext;
X		return( CMD2_C );
X	}
X
X[Cc][Oo][Mm][Mm][Aa][Nn][Dd][Cc]	\
X	{
X		yylval.sval = yytext;
X		return( CMD3_C );
X	}
X
X[-+]?[0-9]+	\
X	{						/* integer */
X		sscanf( yytext, "%ld", &yylval.lval );
X		return( INUM );
X	}
X
X[A-Za-z]+[0-9A-Za-z_]*	\
X	{						/* name */
X		yylval.sval = yytext;
X		sprintf( lexerr, "What the heck is \"%s\"?!", yytext );
X		yyerror( lexerr );
X		return( NAME );
X	}
X
X[ \t]+		\
X	{						/* whitespace */
X	}
X
X.		\
X	{						/* anything else */
X		printf( "LEX found a weird character: \"%s\"\n", yytext );
X	}
X
X%%
OogaBooga
if test 863 -ne "`wc -c < 'testlex.lex'`"
then
	echo shar: "error transmitting 'testlex.lex'" '(should have been 863 characters)'
fi
chmod 600 'testlex.lex'
fi
echo shar: "extracting 'testyacc.y'" '(8647 characters)'
if test -f 'testyacc.y'
then
    mv testyacc.y testyacc.y~old; echo shar: "moved testyacc.y to testyacc.y~old"
fi; if test -d . ; then
sed 's/^X//' << \OogaBooga > 'testyacc.y'
X/*=============================================================================
X    C language declaration section.
X=============================================================================*/
X
X%{
X
X/*  Include function declarations.  */
X
X#include "test.h"
X
X/*  Set up a couple of temporary strings for error-handling.  */
X
Xstatic char	ohaerr[128], t[128];
X
X%}
X
X/*=============================================================================
X    Parser declarations.
X=============================================================================*/
X
X/*-----------------------------------------------------------------------------
X    This parser can currently handle strings, longs, and floats.
X-----------------------------------------------------------------------------*/
X
X%union {
X	char	*sval;
X	long	*lval;
X	char	array[1024];
X}
X
X/*-----------------------------------------------------------------------------
X    Token declarations.
X-----------------------------------------------------------------------------*/
X
X/*  Basic tokens.  */
X
X%token	<lval>	INUM		/*  Integers.  */
X%token	<sval>	NAME		/*  Random alphanumeric identifiers.  */
X
X/*  Commands.  */
X
X%token	<sval>	CMD1_S		/* standalone command */
X%token	<sval>	CMD2_C		/* combination command */
X%token  <sval>  CMD3_C		/* combination command */
X
X/*  Operators.  */
X
X%left	<sval>	'|'		/*  OR  */
X%left	<sval>	'^'		/*  XOR  */
X%left	<sval>	'&'		/*  AND  */
X%left	<sval>	'!'		/*  NOT  */
X
X/*-----------------------------------------------------------------------------
X    Essential expressions.  "expr" stands for a combinatorial expression.
X    "standalone" stands for expressions which cannot combine with others---for
X    instance, ANNIVERSARY.  "integer" is self-evident.  All the "cmd_*" are for
X    commands.
X-----------------------------------------------------------------------------*/
X
X%type	<array>	expr
X%type	<array>	standalone
X%type	<lval>	integer
X%type	<sval>	cmd1_s
X%type	<sval>	cmd2_c
X%type	<sval>	cmd3_c
X%type	<sval>	name
X
X%%
X
X/*=============================================================================
X    Parser section
X=============================================================================*/
X
X/*-----------------------------------------------------------------------------
X    "lines" is a recursive defintion of either a collection of lines, or no
X    lines at all.  "line" is either a newline (take no action), or a standalone
X    expression (in which case, we print the result), or a combination
X    expression (in which case, we print the result).
X-----------------------------------------------------------------------------*/
X
Xlines:		/* empty */
X	|	lines line
X	;
X
Xline:		'\n'
X	|	error '\n'		{ yyerrok; }
X	|	expr '\n'		{ result( $1 ); }
X	|	standalone '\n'		{ result( $1 ); }
X	;
X
X/*-----------------------------------------------------------------------------
X    Fundamental elements:  integers and commands.  Note that all we're doing in
X    this test parser is building up a postfix string, so all the commands do is
X    copy the string which lex snarfed into "$$", the result string.
X-----------------------------------------------------------------------------*/
X
Xinteger:	INUM		{ $$ = $1; } ;
Xcmd1_s:		CMD1_S		{ strcpy( $$, $1 ); } ;
Xcmd2_c:		CMD2_C		{ strcpy( $$, $1 ); } ;
Xcmd3_c:		CMD3_C		{ strcpy( $$, $1 ); } ;
Xname:		NAME		{ strcpy( $$, $1 ); } ;
X
X/*-----------------------------------------------------------------------------
X    Standalone commands.  These are commands that can't be combined with
X    logical operators.
X-----------------------------------------------------------------------------*/
X
Xstandalone:
X    /*-------------------------------------------------------------------------
X	cmd1_s takes three integer parameters.  If any of the integers are "0",
X	the function returns an appropriate error message (`0' illegal in
X	"command1 1992 * 31").  If all three parameters are okay, "CMD1_S y m
X	d" is written to the output string (where y, m, and d correspond to the
X	three integer parameters).  If only two, one or no parameters follow
X	the cmd1_s string, yyerror() is called appropriately.
X
X	BUGS:  "command1 1992 3 4" is strange.  The first grammar/action pair
X	has a printf statement at the top of the action which just prints the
X	entire command to the output; yet it prints "CMD1_S matched 4 1992 3 4"
X	when it should read "CMD1_S matched command1 1992 3 4".  In addition,
X	the other illegal-number-of-parameters rules don't print out well,
X	either; $1 seems to be a newline of some sort.  In addition, when
X	there's an error, I'd like it to just write an error message and not
X	try to parse the rest of the line.
X
X	So why doesn't $1 in the first grammar/action pair result in the string
X	"command1" or some variant thereof?
X    -------------------------------------------------------------------------*/
X	cmd1_s integer integer integer
X		{
X			printf( "cmd1_s matched %s %d %d %d on line %d\n", $1,
X			       $2, $3, $4, linum );
X			if( $2 == 0 || $3 == 0 || $4 == 0 )
X			{
X			    sprintf( ohaerr,
X				"`0' illegal in \"%s %d %d %d\"",
X				$1, $2, $3, $4 );
X			    yyerror( ohaerr );
X			}
X			else				
X			    sprintf( $$, "CMD1_S %d %d %d", $2, $3, $4 );
X		}
X    |	cmd1_s integer integer
X		{
X			sprintf( ohaerr, "\"%s %d %d\" ", $1, $2, $3 );
X			strcat( ohaerr, "requires three parameters!" );
X			yyerror( ohaerr );
X			yyerrok;
X		}
X    |	cmd1_s integer
X		{
X			sprintf( ohaerr, "\"%s %d\" ", $1, $2 );
X			strcat( ohaerr, "requires three parameters!" );
X			yyerror( ohaerr );
X		}
X    |	cmd1_s
X		{
X			sprintf( ohaerr, "\"%s\" has no parameters!", $1 );
X			yyerror( ohaerr );
X		}
X	;
X
Xexpr:	
X    /*-------------------------------------------------------------------------
X	These are all combinatorial expressions; that is, they can be combined
X	by logical operators.  cmd2_c takes three integers; cmd3_c takes none.
X    -------------------------------------------------------------------------*/
X	cmd2_c integer integer integer
X		{
X			sprintf( $$, "%s %d %d %d", $1, $2, $3, $4 );
X		}
X    |	cmd2_c integer integer
X		{
X			sprintf( ohaerr, "\"%s %d %d\" ", $1, $2, $3 );
X			strcat( ohaerr, "requires three parameters!" );
X			yyerror( ohaerr );
X		}
X    |	cmd2_c integer
X		{
X			sprintf( ohaerr, "\"%s %d\" ", $1, $2 );
X			strcat( ohaerr, "requires three parameters!" );
X			yyerror( ohaerr );
X		}
X    |	cmd2_c
X		{
X			sprintf( ohaerr, "\"%s\" has no parameters!", $1 );
X			yyerror( ohaerr );
X		}
X    |	cmd3_c
X		{
X			strcpy( $$, "CMD3_C" );
X		}
X    /*-------------------------------------------------------------------------
X        After all the explicit commands are taken care of, we assume that three
X	numbers in a row implies CMD2_C.  Two numbers in a row, or one
X	number standing alone, are errors, and we print appropriate error
X	messages.
X    -------------------------------------------------------------------------*/
X    |	integer integer integer
X		{
X			sprintf( $$, "CMD2_c %d %d %d", $1, $2, $3 );
X		}
X    |	integer integer
X		{
X			strcpy( ohaerr, "\"%d %d\" is insufficient!",
X				$1, $2 );
X			yyerror( ohaerr );
X		}
X    |	integer
X		{
X			strcpy( ohaerr, "\"%d\" is insufficient!", $1 );
X			yyerror( ohaerr );
X		}
X    /*-------------------------------------------------------------------------
X        And now we get to *how* to combine these expressions.  ()-grouping,
X	NOT (!), AND (&), XOR (^), and OR (|), in decreasing precedence.
X    -------------------------------------------------------------------------*/
X    |	'(' expr ')'
X		{ strcpy($$, $2); }
X    |	'!' expr
X		{ strcpy($$, $2); strcat($$, " !"); }
X    |	expr '&' expr	
X		{
X			strcpy($$, $1); strcat($$, " ");
X			strcat($$, $3); strcat($$, " &");
X		}
X    |	expr '^' expr
X		{
X			strcpy($$, $1); strcat($$, " ");
X			strcat($$, $3); strcat($$, " ^");
X		}
X    |	expr '|' expr
X		{
X			strcpy($$, $1); strcat($$, " ");
X			strcat($$, $3); strcat($$, " |");
X		}
X    ;
X
Xname:
X    /*-------------------------------------------------------------------------
X	At this point, we should have no random names, so print an error
X	message calling attention to the problem string.
X
X	BUGS:  This was intended to catch misspelled words and print the
X	unrecognized word out, and error out, discarding the rest of the line.
X	Unfortunately, when presented with the word "anniverary," which should
X	result in a NAME being returned by yylex(), the following action
X	doesn't occur.
X
X	In addition, I still get the "syntax error" from somewhere, but I don't
X	know where it's coming from.
X    -------------------------------------------------------------------------*/
X	name	{
X			printf( "BOOGA\n" );
X			sprintf( ohaerr, "Unrecognized command `%s'!", $1 );
X			yyerror( ohaerr );
X		}
X	;
X
X%%
OogaBooga
if test 8647 -ne "`wc -c < 'testyacc.y'`"
then
	echo shar: "error transmitting 'testyacc.y'" '(should have been 8647 characters)'
fi
chmod 600 'testyacc.y'
fi
exit 0
#	End of shell archive
#------------------------------CUT HERE----------------------------------------