#! /bin/sh
# Copyright (C) 2011-2025 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# Check that many different Yacc parsers (both C and C++) can co-exists
# in the same directory.

required='cc c++ yacc'
. test-init.sh

cat >> configure.ac << 'END'
AC_PROG_CC
AC_PROG_CXX
AC_PROG_YACC
AC_OUTPUT
END

cat > Makefile.am << 'END'
AM_LFLAGS = --never-interactive

bin_PROGRAMS = c1 c2 cxx1 cxx2 cxx3
AM_YFLAGS = -d

c1_SOURCES = p.y p.h 1.c
c2_SOURCES = p.y 2.c
c2_YFLAGS =

cxx1_SOURCES = parse.yy main1.cc parse.hh

cxx2_SOURCES = parse2.y++ main2.c++
cxx2_YFLAGS =

cxx3_SOURCES = parse3.yxx main3.cxx

BUILT_SOURCES = p.h parse.hh parse3.hxx
END

# The content of all the .c and .y files created below is valid C but
# deliberately invalid C++.
# Vice versa, the content of all the .c++, .cxx, .cc, .y++, .yxx and
# .yy files created below is valid C++ but deliberately invalid C.

cat > p.y <<'END'
%{
#if (defined __cplusplus) && ((!defined __sun) || (defined __EXTERN_C__))
extern "C" {
#endif
int yylex (void) { int new = 0; return new; }
void yyerror (const char *s) {}
#if (defined __cplusplus) && ((!defined __sun) || (defined __EXTERN_C__))
}
#endif
%}
%token ZARDOZ
%%
x : 'x' {};
%%
END

cat > 1.c <<'END'
#include "p.h"
extern int yyparse (void);
int main ()
{
    int new = ZARDOZ;
    return yyparse () + new;
}

END

cat > 2.c <<'END'
extern int yyparse (void);
int main ()
{
    int yyparse ();
    int new = 0;
    return yyparse () + new;
}
END

cat > parse.yy <<'END'
%{
// Include C header to provide global symbols that flex assumes.
// https://bugs.gnu.org/20031
#include <stdlib.h>
// Valid C++, but deliberately invalid C.
#include <cstdlib>
using std::exit;
using std::free;
using std::malloc;
#include "parse.hh"
#if (defined __cplusplus) && ((!defined __sun) || (defined __EXTERN_C__))
extern "C" {
#endif
int yylex (void) { return 0; }
void yyerror (const char *s) {}
#if (defined __cplusplus) && ((!defined __sun) || (defined __EXTERN_C__))
}
#endif
%}
%token FOOBAR
%%
x : 'x' {};
%%
END

cat > parse2.y++ <<'END'
%{
#include <cstdlib>
int yylex (void) { return 0; }
void yyerror (const char *s) {}
%}
%%
x : 'x' {};
%%
END

cat > main1.cc <<'END'
using namespace std;
#include "parse.hh"
int main (int argc, char **argv)
{
    int yyparse (void);
    return yyparse () + FOOBAR;
}
END

cat > main2.c++ <<'END'
using namespace std;
int main (int argc, char **argv)
{
    int yyparse (void);
    return yyparse ();
}
END

edit () { sed -e 's/FOOBAR/BAZQUUX/' -e 's/"parse\.hh"/"parse3.hxx"/'; }
edit <parse.yy >parse3.yxx
edit <main1.cc >main3.cxx

$ACLOCAL
$AUTOCONF
$AUTOMAKE -a

# Try a VPATH and by default serial build first, and then an in-tree
# and by default parallel build.

for try in 0 1; do

  if test $try -eq 0; then
    # VPATH serial build.
    mkdir build
    cd build
    srcdir=..
    debug_info="ls -l . $srcdir"
    run_make=$MAKE
  elif test $try -eq 1; then
    # In-tree parallel build.
    srcdir=.
    debug_info="ls -l"
    case $MAKE in
      *\ -j*)
        # Degree of parallelism already specified by the user: do
        # not override it.
        run_make=$MAKE;;
      *)
        # Some make implementations (e.g., HP-UX) don't grok '-j',
        # some require no space between '-j' and the number of jobs
        # (e.g., older GNU make versions), and some *do* require a
        # space between '-j' and the number of jobs (e.g., Solaris
        # dmake).  We need a runtime test to see what works.
        echo 'all:' > Makefile
        for run_make in "$MAKE -j3" "$MAKE -j 3" "$MAKE"; do
          $run_make && break
        done
        rm -f Makefile
    esac
  else
    echo "$me: invalid value of \$try '$try'" >&2
    exit 99
  fi

  $srcdir/configure

  $run_make
  $debug_info

  test -f p.c
  test -f p.h
  test -f c2-p.c
  test ! -e c2-p.h

  test -f parse.cc
  test -f parse.hh
  test -f parse3.cxx
  test -f parse3.hxx

  test -f cxx2-parse2.c++
  test ! -e parse2.h++
  test ! -e cxx2-parse2.h++

  # Minimal checks about recovering from header removal.
  rm -f p.h parse.hh parse3.hxx
  $run_make p.h parse.hh
  $debug_info
  test -f p.h
  test -f parse.hh
  test ! -e parse3.hxx
  $run_make
  $debug_info
  test -f parse3.hxx

  cd $srcdir

done

:
