#!/bin/sh
#
# Copyright (c) 2026  Vladimir Makarov <vmakarov@gcc.gnu.org>
#
# Usage: compare.sh
#
# The script uses Bison, Yacc (byacc), gcc, lex, YAEP, ElkHound
#

SRCDIR=$(dirname $0)

GECKO=
ALL=
while [[ $# -gt 0 ]]; do
	case $1 in
	--gecko)
		GECKO=yes
		shift
		;;
	--all)
		ALL=yes
		shift
		;;
	*)
		echo "Usage: $0 [--gecko] [--all]"
		echo "  --gecko         Run only GECKO benchmarks"
		echo "  --all           Run also sieve benchmarks"
		exit 1
		;;
	esac
done

YACC=
if type yacc | grep -F /yacc; then
	YACC=yacc
elif type byacc | grep -F /byacc; then
	YACC=byacc
fi
BISON=
if type bison | grep -F /bison; then
	BISON=bison
fi

GCC='gcc -O3 -w -DNDEBUG'
GCCP='g++ -O3 -w -DNDEBUG'
TEST=sieve100K.i
GCC_FOR_GCC_TEST=$SRCDIR/gcc_for_gcc.i
GCC_FOR_CLANG_TEST=$SRCDIR/gcc_for_clang.i
GCC_TEST=$SRCDIR/gcc.i
outfile=./a.out

if test x$GECKO = x; then
	echo
	echo This script compares speed of parser generated by Yacc, Bison, YAEP, ElkHound with Gecko
fi
echo

if test x$ALL != x; then

	echo Now preparation of test file based on sieve: $TEST

	awk 'END{for (i=0;i<100000;i++)print "SIEVE(", i,")"}' </dev/null >test_sieve.h
	gcc -E -I. $SRCDIR/sieve.c | grep -E -v '^#' >test_sieve.i
	if type clang-format >/dev/null 2>&1; then
		clang-format test_sieve.i >$TEST
	else
		cp test_sieve.i $TEST
	fi

	rm -f test_sieve.h test_sieve.i
	echo
fi

if test x$GECKO = x; then
	if test -x /usr/bin/time; then
		export CCACHE_DISABLE=1 # don't use ccache

		echo ++++++++GCC:
		if test x$ALL != x; then
			echo GCC -- multiple Sieve parsing
			echo GCC: gcc -fsyntax-only
			/usr/bin/time gcc -fsyntax-only -w $TEST -o /dev/null 2>&1

			echo GCC: gcc -O0
			/usr/bin/time gcc -O0 -w $TEST -c -o /dev/null 2>&1
			echo
		fi

		echo GCC -- whole old GCC test "(lines $(wc -l $GCC_FOR_GCC_TEST))"

		echo GCC: gcc -fsyntax-only
		/usr/bin/time gcc -fsyntax-only -w $GCC_FOR_GCC_TEST -c -o /dev/null 2>&1

		echo GCC: gcc -O0
		/usr/bin/time gcc -O0 -w $GCC_FOR_GCC_TEST -c -o /dev/null 2>&1

		echo

		if type clang >/dev/null 2>&1; then
			echo ++++++++CLANG:
			if test x$ALL != x; then
				echo CLANG -- multiple Sieve parsing
				echo CLANG: clang -fsyntax-only
				/usr/bin/time clang -fsyntax-only -w $TEST -c -o /dev/null 2>&1

				echo CLANG: clang -O0
				/usr/bin/time clang -O0 -w $TEST -c -o /dev/null 2>&1
				echo
			fi
			echo CLANG -- whole old GCC test "(lines $(wc -l $GCC_FOR_CLANG_TEST))"

			echo CLANG: clang -fsyntax-only
			/usr/bin/time clang -fsyntax-only -w $GCC_FOR_CLANG_TEST -c -o /dev/null 2>&1

			echo CLANG: clang -O0
			/usr/bin/time clang -O0 -w $GCC_FOR_CLANG_TEST -c -o /dev/null 2>&1
			echo
		fi
	fi

	if test x$BISON != x; then
		echo ++++++++Bison:
		if test x$ALL != x; then
			echo Bison -- multiple Sieve parsing
			if $BISON -y $SRCDIR/ansic.y && $GCC -I$SRCDIR -I$SRCDIR/.. $SRCDIR/test_yyparse.c -o $outfile && $outfile <$TEST; then
				echo
			else
				echo Failure with $BISON
				exit 1
			fi
		fi
	fi

	if test x$BISON != x; then
		# BISON
		echo Bison -- whole old GCC test "(lines $(wc -l $GCC_TEST))"
		if $BISON -y $SRCDIR/ansic.y && $GCC -I$SRCDIR -I$SRCDIR/.. $SRCDIR/test_yyparse.c -o $outfile && $outfile <$GCC_TEST; then
			echo
		else
			echo Failure with $BISON on the big test
			exit 1
		fi
	fi

	echo ++++++++Yacc:
	if test x$ALL != x; then
		echo Yacc -- multiple Sieve parsing
		if $YACC -y $SRCDIR/ansic.y && $GCC -I$SRCDIR -I$SRCDIR/.. $SRCDIR/test_yyparse.c -o $outfile && $outfile <$TEST; then
			echo
		else
			echo Failure with $YACC
			exit 1
		fi
	fi

	if test x$YACC != x; then
		# YACC
		echo YACC -- whole old GCC test "(lines $(wc -l $GCC_TEST))"

		if $YACC -y $SRCDIR/ansic.y && $GCC -I$SRCDIR -I$SRCDIR/.. $SRCDIR/test_yyparse.c -o $outfile && $outfile <$GCC_TEST; then
			echo
		else
			echo Failure with $YACC on the big test
			exit 1
		fi
	fi

	YAEPDIR=
	if test -f /usr/local/include/yaep/yaep.h; then
		YAEPDIR=/usr/local/include/yaep
	elif test -f /usr/include/yaep/yaep.h; then
		YAEPDIR=/usr/include/yaep
	elif test -f /usr/local/include/yaep.h; then
		YAEPDIR=/usr/local/include/yaep.h
	elif test -f /usr/include/yaep.h; then
		YAEPDIR=/usr/include/yaep.h
	fi

	YAEPLIB=
	if test -f /usr/local/lib/libyaep.a; then
		YAEPLIB=/usr/local/lib/libyaep.a
	elif test -f /usr/lib/libyaep.a; then
		YAEPLIB=/usr/lib/libyaep.a
	elif test -f /usr/include/yaep.h; then
		YAEPDIR=
	fi

	if test x$YAEPDIR != x; then

		YAEPDIR=/usr/local/include/yaep
		YAEPLIB=/usr/local/lib/libyaep.a

		echo ++++++++YAEP '(Yet Another Earley Parser)' Static Lookahead:
		if test x$ALL != x; then
			# YAEP (Yet Another Earley Parser)
			echo YAEP -- multiple Sieve parsing
			if $GCC -DYAEP -I$YAEPDIR -I$SRCDIR/.. -I$SRCDIR $SRCDIR/test_gecko.c $YAEPLIB -o $outfile && $outfile 1 0 <$TEST; then
				echo
			else
				echo Failure with 'YAEP (Yet Another Earley Parser)'
				exit 1
			fi
		fi

		echo YAEP -- whole old GCC test "(lines $(wc -l $GCC_TEST))"
		if $GCC -DYAEP -I$YAEPDIR -I$SRCDIR/.. -I$SRCDIR $SRCDIR/test_gecko.c $YAEPLIB -o $outfile && $outfile 1 1 <$GCC_TEST; then
			echo
		else
			echo Failure with 'YAEP (Yet Another Earley Parser)'
			exit 1
		fi
		echo YAEP -- ambiguous 'E=E+E|a' and 200 operators
		if $GCC -DYAEP -I$YAEPDIR -I$SRCDIR/.. -I$SRCDIR $SRCDIR/ambig.c $YAEPLIB -o $outfile && $outfile 1; then
			echo
		else
			echo Failure with 'Gecko Parser (GLR Parser)'
			exit 1
		fi

	fi

	ELKHOUND_LIB=
	ELKHOUND_EXE=
	ELKHOUND_INCL=
	SMBASE_LIB=
	SMBASE_INCL=
	if test x$ELKHOUND_DIR != x; then
		ELKHOUND_LIB=$(find $ELKHOUND_DIR -name libelkhound.a)
	fi
	if test x$ELKHOUND_LIB != x; then
		ELKHOUND_EXE=$(echo $ELKHOUND_LIB | sed s/libelkhound.a/elkhound/)
		ELKHOUND_INCL=$(find $ELKHOUND_DIR -name glr.h)
		ELKHOUND_INCL=$(echo $ELKHOUND_INCL | sed s/glr.h//)
	fi
	if test x$ELKHOUND_LIB != x; then
		SMBASE_LIB=$(find $ELKHOUND_DIR -name libsmbase.a)
		SMBASE_INCL=$(find $ELKHOUND_DIR -name strobjdict.h)
		SMBASE_INCL=$(echo $SMBASE_INCL | sed s/strobjdict.h//)
	fi

	if test x$ELKHOUND_DIR != x && test x$ELKHOUND_LIB != x && test x$ELKHOUND_EXE != x && test x$ELKHOUND_INCL != x && test x$SMBASE_LIB != x && test x$SMBASE_INCL != x; then

		echo ++++++++ELKHOUND '(GLR Parser)':
		$ELKHOUND_EXE $SRCDIR/elkh-c.gr
		echo

		if test x$ALL != x; then
			echo ELKHOUND -- multiple Sieve parsing
			if $GCCP -I$SRCDIR/.. -I$SRCDIR -I$ELKHOUND_INCL -I$SMBASE_INCL $SRCDIR/test_elkh.cc $ELKHOUND_LIB $SMBASE_LIB -o $outfile && $outfile <$TEST; then
				echo
			else
				echo Failure with 'Elkhound'
				exit 1
			fi
		fi

		echo ELKHOUND '(GLR Parser)' -- whole old GCC test "(lines $(wc -l $GCC_TEST))"
		if $GCCP -I$SRCDIR/.. -I$SRCDIR -I$ELKHOUND_INCL -I$SMBASE_INCL $SRCDIR/test_elkh.cc $ELKHOUND_LIB $SMBASE_LIB -o $outfile && $outfile <$GCC_TEST; then
			echo
		else
			echo Failure with 'Elkhound'
			exit 1
		fi

		echo ELKHOUND '(GLR Parser)' -- ambiguous 'E=E+E|a' and 200 operators
		$ELKHOUND_EXE $SRCDIR/elkh-ambig.gr
		if $GCCP -I$SRCDIR/.. -I$SRCDIR -I$ELKHOUND_INCL -I$SMBASE_INCL $SRCDIR/test_ambig_elkh.cc $ELKHOUND_LIB $SMBASE_LIB -o $outfile && $outfile; then
			echo
		else
			echo Failure with 'Elkhound'
			exit 1
		fi
		rm -f $SRCDIR/elkh-c.cc $SRCDIR/elkh-c.h $SRCDIR/elkh-ambig.cc $SRCDIR/elkh-ambig.h
	fi
fi

# Gecko
echo ++++++++Gecko:
if test x$ALL != x; then
	echo Gecko -- multiple Sieve parsing
	if $GCC -I$SRCDIR/.. -I$SRCDIR $SRCDIR/test_gecko.c $SRCDIR/../gecko.c -o $outfile && $outfile 1 <$TEST; then
		echo
	else
		echo Failure with 'Gecko Parser (GLR Parser)'
		exit 1
	fi
fi

echo Gecko -- whole old GCC test "(lines $(wc -l $GCC_TEST))"
if $GCC -I$SRCDIR/.. -I$SRCDIR $SRCDIR/test_gecko.c $SRCDIR/../gecko.c -o $outfile && $outfile 1 <$GCC_TEST; then
	echo
else
	echo Failure with 'Gecko Parser (GLR Parser)'
	exit 1
fi

echo Gecko -- ambiguous 'E=E+E|a' and 200 operators
if $GCC -I$SRCDIR/.. -I$SRCDIR $SRCDIR/ambig.c $SRCDIR/../gecko.c -o $outfile && $outfile 1; then
	echo
else
	echo Failure with 'Gecko Parser (GLR Parser)'
	exit 1
fi

rm -f $outfile
rm -f y.tab.c $TEST
exit 0
