#!/usr/bin/awk -f
# "yygrowstack_yacc_hack.awk"
#	$Id: yygrowstack_yacc_hack.awk,v 1.3 2007/02/12 06:54:43 fang Exp $

# Yacc is a piece of shit.  
# Yacc is unable to produce re-entrant parsers because it uses exclusively
# global variables for maintaining the parser stack... UNTIL NOW!

# NOTE: the output of this file will contain the word "REFERENCE"
# where a C++ reference (&) is desired.  Please post-process this through
# sed 's/REFERENCE/\&/g' to get the desired output.  

# This script is intended to hack (and I use that word in its true meaning)
# the .cc source file generated by bison (or yacc), by changing the 
# prototype and function call for yygrowstack, adding stack pointers and 
# variables (by reference) to the formals, and passing them as arguments.  
# This script is only good for C++, since C doesn't support references.  

# note: thus far this script will only work on the symbol "yygrowstack"
# because variable cannot appear in patterns for awk.  
# yygrowstack also must initially have a prototype with one variable argument, 
# and void return (in one line), for the purposes of pattern matching.  

# NOTE: FreeBSD's byacc skeletons have evolved a little over time,
# in some cases the yygrowstack prototype appears *before* YYSTYPE
# is declared, which means we have to hold the prototype in a buffer
# and flush the buffer once we are certain we've seen YYSTYPE's definition.

# NOTE: In a recent version of the skeleton, yygrowstack's proto is wrapped
# in #if conditionals, which means we may have to pick one.
# One proto may have a 'void' argument, while the other may be empty.

BEGIN {
	prototype_regex = "int[ \t]+yygrowstack[ ]?[A-Za-z_]+\\(.*\\);";
	# prototype has the __P((void)) proto and a ;
	definition_regex = "int[ \t]+yygrowstack[ ]?\\(.*\\)";
	call_regex = "yygrowstack[ ]?\\([^()]*\\)";

	# const - no attempt to modify, just read
	new_formals = "short*REFERENCE yyss, short*REFERENCE yyssp, YYSTYPE*REFERENCE yyvs, " \
		"YYSTYPE*REFERENCE yyvsp, short*REFERENCE yysslim, intREFERENCE yystacksize)";
	new_actuals = "yyss, yyssp, yyvs, yyvsp, yysslim, yystacksize)";
	seen_yystype_begin = 0;
	seen_yystype_end = 0;
	proto_buffer = "";
}

# look for YYSTYPE union definition, 
# hope there are no more union definitions!
/typedef.*union/ {
	seen_yystype_begin = 1;
}

/}.*YYSTYPE;/ {
	seen_yystype_end = seen_yystype_begin;
	# print "/* found definition of YYSTYPE */";
}

/yygrowstack.*\(.*\)/ {
	str = $0;
	# is it a prototype or a function call? (check for void)
	ind = match(str, definition_regex);
	if (ind) {
		proto = substr(str, ind, RLENGTH);	# length of match
		# assuming there is only one ')' in proto:
		# insert new formal arguments
		if (match(proto, "\\(void\\)")) {
			gsub("void\\)", new_formals, proto);
		} else {
			gsub("\\)", new_formals, proto);
		}
		# just gimme a goddam '&' character!!! (doesn't work)
		# gsub("REFERENCE", "\\&", proto);
		gsub(definition_regex, proto, str);
		if (seen_yystype_end) {
			print str;
		} else {
			# save it for later
			proto_buffer = str;
		}
	} else if (match(str, prototype_regex)) {
		# just delete the prototype which may look something like:
		# static int yygrowstack __P((void));
	} else {
		# is a function call
		ind = match(str, call_regex);
		call = substr(str, ind, RLENGTH);	# length of match
		# assuming there is only one ')' in call:
		gsub("\\)", new_actuals, call);
		gsub(call_regex, call, str);
		print str;
	}
}

!/yygrowstack.*\(.*\)/ {
	print;			# echo back out
	if (seen_yystype_end && length(proto_buffer)) {
		print proto_buffer;
		proto_buffer = "";
		see_yystype_begin = 0;	# unnecessary
		see_yystype_end = 0;	# unnecessary
	}
}

