#!/bin/sh
# --------------------------------------------------------------------------
# Copyright 1992 by Forschungszentrum Informatik (FZI)
#
# You can use and distribute this software under the terms of the license
# version 1 you should have received along with this software.
# If not or if you want additional information, write to
# Forschungszentrum Informatik, "STONE", Haid-und-Neu-Strasse 10-14,
# D-76131 Karlsruhe, Germany.
# ------------------------------------------------------------------------
# obst-patch_lex_yacc - 3/12/92 - dietmar theobald
#     changed         -30/09/93 - Michael Pergande
#                    (added patches for use of bison/flex)
#
# obst-patch_lex_yacc <output-file> [[-commondefs] -prefix <p>]
#
# Modifies files generated by YACC, LEX according to the schema used in OBST
# makefiles. There are two reasons for these modifications:
#  - to make generated standard output processable by C++ compilers, and
#  - to modularize the output file such that it will be possible to link
#    several parse modules to the same program.
#    This second modification can be omitted by not using the '-prefix' option.
#  - to make it possible to use bison -y instead of yacc and flex instead of lex.
#
# <output-file> is either a (modified) LEX generated header file (*_lex.h) or
# a (modified) YACC generated code file (*_yacc.[Cc]/*_yacc.cc).
# The corresponding original files (lex.yy.c / y.tab.c) will then be taken
# for input and removed after processing them.
# The input files must reside in the current working directory.
#
# If no '-prefix' option is given, just the C++ adaptation of the sources is
# performed.
#
# The parsers need some global variables. Exactly one of the parser modules
# which will be linked together must define these, the others just declare
# them (as external).
# This distinguished module is denoted by the option '-commondefs'.
#
# The `official` entry points of the modified parser module are prefixed by <p>
# to make their names globally unique. These are:
#   <p>_yyparse()
#   <p>_yyin, <p>_yyout
#   <p>_yylineno
#   <p>_yyreds
# <p> is given using the '-prefix' option.
#
# BUGS: This is a very simplistic patch.
#
##############################################################################
# general hints for writing yacc and lex sources in OBST applications
##############################################################################
#
# It should be possible to use yacc or bison -y for compilation of yacc sources
# and lex or flex for compilation of lex sources.
# To make this possible, some points have to be regarded:
#
# - lex sources:
#    - the declaration of the function 'yywrap' has to be enclosed by
#      '#ifndef FLEX_SCANNER
#       #ifdef HAVE_YYWRAP
#       ....
#       #endif
#       #endif'
#    - if the variable 'yylval' is used in the lex source,
#      the declaration 'extern YYSTYPE yylval' has to be added at the
#      beginning of it.
#    - insert a declaration of struct yywork; before its first use.
#      (necessary for IBM AIX)
#
# - yacc sources:
#    - the safest place for the inclusion of the corresponding lex file
#      is at the end of the definitions section.
#    - if functions that are used in the lex or yacc source are defined
#      in the yacc source, this has to be done also at the end of the
#      definitions section, after the inclusion of the lex file.
#      (e.g. 'yyerrror' and 'yyecho' in cfe.y.)
#    - 'YYSTYPE' has to be defined as follows:
#       'typedef union {......} _YYSTYPE;
#        #define YYSTYPE _YYSTYPE'.
#
# additional information:
# - the variable 'yylineno' is not supported by flex.
#   Its use in yacc or lex sources does not cause an error,
#   but its value during runtime is always zero.
#   (in lex, it contains the number of the currently parsed line)
# - when using flex, it is not necessary to bind the lex library '-ll'.
# 
# This list might be not complete. In general, occuring errors during
# compilation have to do with any yacc or lex variables ('yy...').
# Make sure that they are defined before they are used.
########################################################################## 
##########################################################################

      self='obst-patch_lex_yacc'
     usage="*** usage: $self <output-file> [[-commondefs] -prefix <p>]"
commondefs=
   outfile="$1"
   tmpfile="/tmp/$self-$$"
 do_module=
	ws='[ ]*'
        nl='\n'

[ $# -ge 1 ] || { echo >&2 "$usage"; exit 1; }
shift

[ $# -ge 1 ] && {
   do_module='+'
   [ "$1" = '-commondefs'	    ] && { commondefs='@@@@'; shift;  }
   [ "$1" = '-prefix'  -a  $# -eq 2 ] || { echo >&2 "$usage"; exit 1; }
   }
prefix="$2_"

case "$outfile" in
   *_lex.h)	yacc=
		infile=./lex.yy.c	  ;;
   *_yacc.[Cc]|\
   *_yacc.cc)	yacc='+'
		infile=./y.tab.c	  ;;
   *)		echo >&2 "$usage"; exit 1 ;;
esac
[ -r "$infile" ] || { echo >&2 "*** $self: $infile not readable"; exit 1; }



# The following substitutions are performed in y.tab.c for C++:
#  - Deletion of the C-style declarations for malloc/realloc.
#  - Extending the bodies of the macros YYA* by providing there typecasts for
#    arguments of free.
if [ "$yacc" ]; then
   echo >> $tmpfile.sed "
/^extern.*\<malloc\>.*/d
/^extern.*\<realloc\>.*/d
/define${ws}YYA/s+free(+free((char*)+g"

# The following substitutions are performed in lex.yy.c for C++:
#  - Change function headers of yyback, yyoutput, and yyunput into C++ style.
#  - For gcc-2.2.2 (only?): change the line '*yylastch++ = yych = input();'
#    to 'yych=input(); *yylastch++=yych;'
#  - If defined by a numeric constant, YYLMAX is set to 1024.
#    (HPUX defines it as 200 which is too small, SunOS defines it as BUFSIZ)
else
   echo >> $tmpfile.sed "
s|^yylex()|extern \"C\" int yylex()|
/^yyback(p,${ws}m)/ { s|.*|int yyback (int *p, int m)|; n; s|${ws}int$ws\*p;||;}
/^yyoutput(c)/      { s|.*|void yyoutput (int c)|; n; s|${ws}int${ws}c;||;}
/^yyunput(c)/       { s|.*|void yyunput (int c)|; n; s|${ws}int${ws}c;||;}
s|^\($ws\)\(\*yylastch++\)$ws=$ws\(yych\)$ws=$ws\(input()\)$ws;$ws$|\1\3=\4; \2=\3;|
s|\(#${ws}define${ws}YYLMAX${ws}\)[0-9][0-9]*|\1 1024|"
fi



# The following substitutions are performed for modularization:
# (1) If the option '-commondefs' is not used, 
#	- make the following declarations external:
#	     'int yy<..>;', 'int *yy<..>;', and 'char yy<..>[YY<..>]'
#	- turn the following initialized definitions into external declarations:
#	     'int yyprevious = <...>', 'char *yysptr = <...>'
#	- delete the function definitions at the end of *_lex.h, starting with
#	  the definition of yyback().
# (2) Make the following kinds of definitions local:
#	 '<...> <..> [] =', 'struct <...> <..> [] =', 'struct <...>;',
#	 'YYSTYPE yylval, yyval;'
# (3) Make the definitions of yysvec, yybgin global again, but change the
#     names to '_<p>_<...>' to avoid name conflicts.
#     (Keeping them local was too painful to be worth the effort.)
# (4) Change the variable names yyrestart, yy_delete_buffer, yy_switch_to_buffer,
#     yy_init_buffer, yy_create_buffer, yy_load_buffer_state
#     also to '_<p>_<...>' (to avoid name conflicts when flex is used)
# (5) Declare the function yylook() as local.

[ "$do_module" ] && echo >> $tmpfile.sed "
s|^${commondefs}\(int$ws[ 	*]*yy[a-z]*;\)|extern \1|
s|^${commondefs}\(char${ws}yy[a-z]*\[\)YY[^]]*|extern \1|
s|^${commondefs}\(int${ws}yyprevious\)$ws=[^;]*;|extern \1;|
s|^${commondefs}\(char$ws\*${ws}yysptr\)$ws=[^;]*;|extern \1;|

s|^\([a-z][a-z]*$ws[a-z][a-z0-9]*$ws\[\]$ws=\)|\static \1|
s|^\(struct$ws[a-z][a-z]*$ws*[a-z][a-z0-9]* *\[\]$ws=\)|\static \1|
s|^\(struct$ws[a-z][a-z]*$ws.*;\)|static \1|
s|^\(YYSTYPE *yylval *, *yylval *;\)|static \1|
s|^static$ws\(struct${ws}yysvf$ws\*${ws}yybgin$ws=\)|\1|
s|^static$ws\(struct${ws}yysvf${ws}yysvec$ws\[\]$ws=\)|\1|

s|\(yybgin[^a-z]\)|_$prefix\1|g
s|\(yysvec[^a-z]\)|_$prefix\1|g
s|\(yysvec\)$|_$prefix\1|g
s|\(yyrestart\)|_$prefix\1|g
s|\(yy_delete_buffer\)|_$prefix\1|g
s|\(yy_switch_to_buffer\)|_$prefix\1|g
s|\(yy_init_buffer\)|_$prefix\1|g
s|\(yy_create_buffer\)|_$prefix\1|g
s|\(yy_load_buffer_state\)|_$prefix\1|g

s|^\(yylook()$ws{\)|static \1|g"


#echo '--------------'; cat $tmpfile.sed; echo '-----------'

# - In case of a yacc source and -prefix-option #define-commands 
#   for some yacc variables that need to have unique names are inserted 
#   at the beginning of the file (to add the prefix to the names of the variables)
#   additionally, the same is done for the variables yys, yyv, yypv. These variables
#   are local and should cause no problems, but there seem to exist yacc versions
#   which do not declare them static.
#
# - In case of a flex source, a variable declaration for yytext and yylineno
#   is inserted. (flex does not support yylineno. To avoid errors, it is declared
#   and given the constant value 0.)
#   Flex sources are recognized by the occurence of a function yyrestart.
#
# - In case of a lex source to be modularized which does not contain the
#   common definitions, delete the function definitions at the end of *_lex.h,
#   starting with the definition of yyback()
#
# - Add forward declaration for yywork to any lex source.

if [ "$yacc" ]; then
   if [ "$do_module" ]; then
   echo "
#define yylineno	${prefix}yylineno
#define yyin		${prefix}yyin
#define yyreds		${prefix}yyreds
#define yyout		${prefix}yyout
#define yyparse		${prefix}yyparse
#define yylex		${prefix}yylex
#define yyerror		${prefix}yyerror
#define yylval		${prefix}yylval
#define yyval		${prefix}yyval
#define yys		${prefix}yys
#define yyv		${prefix}yyv
#define yypv		${prefix}yypv" > $outfile
   fi

else		# lex source
   if egrep -s 'yyrestart' "$infile"; then
      echo "
extern char *yytext;
extern int yylineno;
int yylineno = 0;
" > $outfile
   fi

   echo 'struct yywork;' >> $outfile
fi

sed -f $tmpfile.sed $infile \
 | ( if [ -n "$do_module" -a -z "$yacc" -a -z "$commondefs" ]; then
	sed "/^int yyback$ws(int/,\$d" 
     else
	cat
     fi
   ) \
 >> $outfile

#
# cleanup
#
rm -f $tmpfile* $infile
