#!/usr/bin/awk -f
# "lexer/flex_common_marker.awk"
#	$Id: flex_common_marker.awk,v 1.4 2008/12/03 05:32:16 fang Exp $
# annotate lex.yy.cc source into sections for consumption
# to be made suitable for extracting common functions.

# Rationale: a diff-style patch was not sufficient to cover multiple versions 
# of flex, so a more intelligent patch method was required.  
# validated against flex versions:
# 2.5.4 (works, supported)
# 2.5.31 (works, supported)
# 2.5.33 (patch works, but runtime craches!)
# 2.5.35 (patch works, but runtime craches!)

# this actually does some fake C-preprocessing
# by keeping track of levels of nesting.

BEGIN {
	cpp_nest_level = 0;
	curly_brace_level = 0;
	buffer_count = 0;
	patch_count = 0;
	insert_mark_level = 0;

# strings for marking the output
	begin_keep_header = "#if\tdefined(KEEP_FLEX_COMMON) || 1";
	end_keep_header = "#endif\t// defined(KEEP_FLEX_COMMON) || 1";
	begin_buffer_state = "#ifndef\tFLEX_STRUCT_BUFFER_STATE";
	end_buffer_state = "#endif\t// FLEX_STRUCT_BUFFER_STATE";
	begin_keep_body = "#if\tdefined(KEEP_FLEX_COMMON)";
	end_keep_body = "#endif\t// KEEP_FLEX_COMMON";
}

function save_line(str) {
	saved_lines[buffer_count] = str;
	++buffer_count;
}

# function save_endif_line(str) {
#	saved_lines[buffer_count] = saved_lines[buffer_count -1];
#	saved_lines[buffer_count -1] = str;
#	++buffer_count;
# }

function print_or_save(str) {
	if (cpp_nest_level || curly_brace_level) {
		save_line(str);
	} else {
		print str;
	}
}

# only after cpp #ifs are balanced, print out lines
function flush_saved_lines( \
	i) {
	for (i=0; i<buffer_count; ++i) {
		print saved_lines[i];
		delete saved_lines[i];
	}
	buffer_count = 0;
	flush_me = 0;
}

# patch section 1 begin:
/lexical scanner generated by flex/ {
	print begin_keep_header;
	# flex 2.5.33 requires the following
	print "#if\tdefined(__cplusplus) && !defined(__STDC_VERSION__)";
	print "#define\t__STDC_VERSION__\t1L";
	# really wants to be defined >= 199901L
	# but <inttypes.h> can be screwy
	print "#endif";
	++patch_count;
	++insert_mark_level;
}

# patch section 2a begin:
/define EOB_ACT_CONTINUE_SCAN/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# patch section 2b begin:
/cannot portably get our hands on size_t/ {
	print begin_buffer_state;
	++patch_count;
	++insert_mark_level;
}

# patch section 2c begin: for flex-2.5.35
/#ifndef.*YY_TYPEDEF_YY_SIZE_T/ {
	print begin_buffer_state;
	++patch_count;
	++insert_mark_level;
}

# patch section 3a, 3b begin:
/void yyrestart.*;$/ {
	print begin_keep_header;
	print "#define\tstatic\textern";
	patch_count += 2;
	++insert_mark_level;
}

# patch section 4 (1-line):
/typedef.*yy_state_type/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# patch section 5 (1-line):
/#define yytext_ptr/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# patch section 6:
/static int yy_get_next_buffer.*;$/ {
	print begin_keep_header;
	print "#define\tstatic\textern";
	++patch_count;
	++insert_mark_level;
}

# patch section 7 begin: define REJECT
/The intent behind this definition is that/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# patch section 8 begin:
/#define INITIAL/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# patch section 8.1 begin:
# /#ifndef YY_FATAL_ERROR/ {
# if (!insert_mark_level) {
#	print begin_keep_header;
#	++patch_count;
# }
#	++insert_mark_level;
# }

# patch section 8.1 begin:
# /#ifndef.*YY_NO_UNPUT/ || /static void yyunput.*\(.*\)$/
/Macros after this point can all be overridden by user definitions/ {
# if (!patched_section_10) {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
#	patched_section_10 = 1;
# }
}

# patch section 9 begin:
/yy_get_next_buffer - try to read in a new buffer/ {
	print begin_keep_body;
	++patch_count;
	++insert_mark_level;
}

# patch section 10 begin:
/return yy_is_jam.*yy_current_state/ {
	print_or_save(begin_keep_body);
	++patch_count;
	++insert_mark_level;
}

# patch section 11 begin (flex 2.5.33):
/#define YY_CURRENT_BUFFER / {
	print begin_keep_header;
	print begin_buffer_state;
	++patch_count;
	++insert_mark_level;
}

# patch section 11 end (flex 2.5.33):
# /#define YY_CURRENT_BUFFER_LVALUE/
/yy_hold_char holds the character/ {
	print end_buffer_state;
	print end_keep_header;
	++patch_count;
	--insert_mark_level;
}

# patch section 12 begin:
/#define YY_NEVER_INTERACTIVE/ {
	print begin_keep_header;
	++patch_count;
	++insert_mark_level;
}

# cpp #if matching and balancing
/^#[ \t]*if/ { ++cpp_nest_level; }

# track brace-nesting
/[{]/ {
	tmp = $0;
	gsub("[^{]+", "", tmp);
	curly_brace_level += length(tmp);
}

# by default, print all lines, like sed would
{
	if (!cpp_nest_level && !curly_brace_level) {
		flush_saved_lines();
	}
	print;
}

/^#[ \t]*endif/ { --cpp_nest_level; }

# track brace-nesting
/[}]/ {
	tmp = $0;
	gsub("[^}]+", "", tmp);
	curly_brace_level -= length(tmp);
}

# patch section 1 end:
/typedef struct yy_buffer_state/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 2a, 2b end:
/define YY_BUFFER_EOF_PENDING/ {
	print_or_save(end_buffer_state);
	print_or_save(end_keep_header);
	patch_count += 2;
	insert_mark_level -= 2;
}

# patch section 3a end:
/void yyfree.*;$/ {
	print_or_save("#undef\tstatic");
	++patch_count;
}

# patch section 3b end:
/typedef.*YY_CHAR;$/ {
	print_or_save("#undef\tstatic");
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 4 (1-line):
/typedef.*yy_state_type/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 5 (1-line):
/#define yytext_ptr/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 6 end:
/static void yy_fatal_error.*;$/ {
	print_or_save("#undef\tstatic");
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 7 end:
/#define YY_RESTORE_YY_MORE_OFFSET/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 8 end:
/#define INITIAL/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}

# patch section 8.1 end:
/#define YY_FATAL_ERROR/ {
# if (insert_mark_level == 2) {
	print_or_save(end_keep_header);
	++patch_count;
# }
	--insert_mark_level;
}

# patch section 9 end:
/return ret_val/ {
	print_or_save(end_keep_body);
	++patch_count;
	--insert_mark_level;
}

# patch section 10 end:
/free.*(.*ptr.*);/ {
	print_or_save(end_keep_body);
	++patch_count;
	--insert_mark_level;
}

# patch section 12 end:
/#define YY_STACK_USED/ {
	print_or_save(end_keep_header);
	++patch_count;
	--insert_mark_level;
}


# sanity checks
END {
	if (cpp_nest_level) {
		print "ERROR: cpp conditionals not balanced!";
		exit(1);
	}
	if (curly_brace_level) {
		print "ERROR: curly braces not balanced!";
		exit(1);
	}
	if (insert_mark_level) {
		print "ERROR: mark insertions were not balanced: " \
			insert_mark_level;
		exit(1);
	}
	if (patch_count != 29 && patch_count != 28) {
		# && patch_count != 28
		# not a perfect guarantee, just one safety catch
		print "Expecting patch_count 29 (or 28) but got " patch_count;
		exit (1);
	}
}

