# Java tests for simple calculator.           -*- Autotest -*-

# Copyright (C) 2007-2015, 2018-2020 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 3 of the License, 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 <http://www.gnu.org/licenses/>.

AT_BANNER([[Java Calculator.]])


# ------------------------- #
# Java invalid directives.  #
# ------------------------- #

AT_SETUP([Java invalid directives])

AT_DATA([[YYParser.y]], [
%language "Java"
%defines
%destructor { /* Nothing. */ } exp
%%
exp:
])

AT_BISON_CHECK([[-fcaret YYParser.y]], [1], [],
[[YYParser.y: error: %defines does not make sense in Java
YYParser.y:4.13-30: error: %destructor does not make sense in Java
    4 | %destructor { /* Nothing. */ } exp
      |             ^~~~~~~~~~~~~~~~~~
]])

AT_CLEANUP


# ------------------------- #
# Helping Autotest macros.  #
# ------------------------- #


# AT_JAVA_POSITION_DEFINE_OLD
# -----------------------
m4_define([AT_JAVA_POSITION_DEFINE_OLD],
[[class Position {
  public int line;
  public int token;

  public Position ()
  {
    line = 0;
    token = 0;
  }

  public Position (int l, int t)
  {
    line = l;
    token = t;
  }

  public boolean equals (Position l)
  {
    return l.line == line && l.token == token;
  }

  public String toString ()
  {
    return Integer.toString (line) + "." + Integer.toString (token);
  }

  public int lineno ()
  {
    return line;
  }

  public int token ()
  {
    return token;
  }
}]])





# -----------------#
# Java Parameters. #
# -----------------#

AT_BANNER([Java Parameters.])


# AT_CHECK_JAVA_MINIMAL([DIRECTIVES], [PARSER_ACTION], [POSITION_CLASS])
# ----------------------------------------------------------------------
# Check that a minimal parser with DIRECTIVES compiles in Java.
# Put the Java code in YYParser.java.
m4_define([AT_CHECK_JAVA_MINIMAL],
[
AT_DATA([[YYParser.y]], [
%language "Java"
%locations
%debug
%define parse.error verbose
%token-table
%token END "end"
$1
%%
start: END {$2};
%%
class m4_default([$3], [Position]) {}
])
AT_BISON_CHECK([[-Wno-deprecated YYParser.y]])
AT_CHECK([[grep '[mb]4_' YYParser.y]], [1], [ignore])
AT_JAVA_COMPILE([[YYParser.java]])
])


# AT_CHECK_JAVA_MINIMAL_W_LEXER([1:DIRECTIVES], [2:LEX_THROWS],
#       [3:YYLEX_ACTION], [4:LEXER_BODY], [5:PARSER_ACTION], [6:VALUE_TYPE],
#       [7:POSITION_TYPE], [8:LOCATION_TYPE])
# ---------------------------------------------------------------------
# Check that a minimal parser with DIRECTIVES and a "%code lexer".
# YYLEX is the body of yylex () which throws LEX_THROW.
# compiles in Java.
m4_define([AT_CHECK_JAVA_MINIMAL_W_LEXER],
[AT_CHECK_JAVA_MINIMAL([$1

%code lexer
{
  m4_default([$6], [Object]) yylval;
  public m4_default([$6], [Object]) getLVal() { return yylval; }

  public m4_default([$7], [Position]) getStartPos() { return null; }
  public m4_default([$7], [Position]) getEndPos()   { return null; }

  public void yyerror (m4_default([$8], [Location]) loc, String s)
  {
    System.err.println (loc + ": " + s);
  }

  public int yylex ()$2
  {
    $3
  }

  $4
}], [$5], [$7])])


# AT_CHECK_JAVA_GREP([LINE], [COUNT=1])
# -------------------------------------
# Check that YYParser.java contains exactly COUNT lines matching ^LINE$
# with grep.
m4_define([AT_CHECK_JAVA_GREP],
[AT_CHECK([grep -c '^$1$' YYParser.java], [ignore], [m4_default([$2], [1])
])])


# ------------------------------------- #
# Java parser class and package names.  #
# ------------------------------------- #

AT_SETUP([Java parser class and package names])

AT_CHECK_JAVA_MINIMAL([])
AT_CHECK_JAVA_GREP([[class YYParser]])

AT_CHECK_JAVA_MINIMAL([[%name-prefix "Prefix"]])
AT_CHECK_JAVA_GREP([[class PrefixParser]])

AT_CHECK_JAVA_MINIMAL([[%define api.prefix {Prefix}]])
AT_CHECK_JAVA_GREP([[class PrefixParser]])

AT_CHECK_JAVA_MINIMAL([[%define api.token.prefix {TOK_}]])
AT_CHECK_JAVA_GREP([[.*TOK_END.*]])

AT_CHECK_JAVA_MINIMAL([[%define api.parser.class {ParserClassName}]])
AT_CHECK_JAVA_GREP([[class ParserClassName]])

AT_CHECK_JAVA_MINIMAL([[%define package {user_java_package}]])
AT_CHECK_JAVA_GREP([[package user_java_package;]])

AT_CLEANUP


# ----------------------------- #
# Java parser class modifiers.  #
# ----------------------------- #

AT_SETUP([Java parser class modifiers])

AT_CHECK_JAVA_MINIMAL([[%define abstract]])
AT_CHECK_JAVA_GREP([[abstract class YYParser]])

AT_CHECK_JAVA_MINIMAL([[%define final]])
AT_CHECK_JAVA_GREP([[final class YYParser]])

AT_CHECK_JAVA_MINIMAL([[%define strictfp]])
AT_CHECK_JAVA_GREP([[strictfp class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define abstract
%define strictfp]])
AT_CHECK_JAVA_GREP([[abstract strictfp class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define final
%define strictfp]])
AT_CHECK_JAVA_GREP([[final strictfp class YYParser]])

AT_CHECK_JAVA_MINIMAL([[%define public]])
AT_CHECK_JAVA_GREP([[public class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define public
%define abstract]])
AT_CHECK_JAVA_GREP([[public abstract class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define public
%define final]])
AT_CHECK_JAVA_GREP([[public final class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define public
%define strictfp]])
AT_CHECK_JAVA_GREP([[public strictfp class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define public
%define abstract
%define strictfp]])
AT_CHECK_JAVA_GREP([[public abstract strictfp class YYParser]])

AT_CHECK_JAVA_MINIMAL([[
%define public
%define final
%define strictfp]])
AT_CHECK_JAVA_GREP([[public final strictfp class YYParser]])

# FIXME: Can't do a Java compile because javacomp.sh is configured for 1.3
AT_CHECK_JAVA_MINIMAL([[
%define annotations {/*@Deprecated @SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "deprecation"}) @SuppressWarnings(value={"unchecked", "deprecation"})*/}
%define public]])
AT_CHECK_JAVA_GREP([[/\*@Deprecated @SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "deprecation"}) @SuppressWarnings(value={"unchecked", "deprecation"})\*/ public class YYParser]])

AT_CLEANUP


# ---------------------------------------- #
# Java parser class extends and implements #
# ---------------------------------------- #

AT_SETUP([Java parser class extends and implements])

AT_CHECK_JAVA_MINIMAL([[%define extends {Thread}]])
AT_CHECK_JAVA_GREP([[class YYParser extends Thread]])

AT_CHECK_JAVA_MINIMAL([[%define implements {Cloneable}]])
AT_CHECK_JAVA_GREP([[class YYParser implements Cloneable]])

AT_CHECK_JAVA_MINIMAL([[
%define extends {Thread}
%define implements {Cloneable}]])
AT_CHECK_JAVA_GREP([[class YYParser extends Thread implements Cloneable]])

AT_CLEANUP


# -------------------------------- #
# Java %parse-param and %lex-param #
# -------------------------------- #

AT_SETUP([Java %parse-param and %lex-param])

AT_CHECK_JAVA_MINIMAL([])
AT_CHECK_JAVA_GREP([[ *public YYParser (Lexer yylexer) *]])

AT_CHECK_JAVA_MINIMAL([[%parse-param {int parse_param1}]])
AT_CHECK_JAVA_GREP([[ *protected final int parse_param1;]])
AT_CHECK_JAVA_GREP([[ *public YYParser (Lexer yylexer, *int parse_param1) *]])
AT_CHECK_JAVA_GREP([[ *this.parse_param1 = parse_param1;]])

AT_CHECK_JAVA_MINIMAL([[
%parse-param {int parse_param1}
%parse-param {long parse_param2}]])
AT_CHECK_JAVA_GREP([[ *protected final int parse_param1;]])
AT_CHECK_JAVA_GREP([[ *protected final long parse_param2;]])
AT_CHECK_JAVA_GREP([[ *public YYParser (Lexer yylexer, *int parse_param1, *long parse_param2) *]])
AT_CHECK_JAVA_GREP([[ *this.parse_param1 = parse_param1;]])
AT_CHECK_JAVA_GREP([[ *this.parse_param2 = parse_param2;]])

AT_CHECK_JAVA_MINIMAL_W_LEXER([], [], [[return EOF;]])
AT_CHECK_JAVA_GREP([[ *public YYParser () *]])
AT_CHECK_JAVA_GREP([[ *protected YYParser (Lexer yylexer) *]])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[%parse-param {int parse_param1}]],
    [], [[return EOF;]])
AT_CHECK_JAVA_GREP([[ *protected final int parse_param1;]])
AT_CHECK_JAVA_GREP([[ *public YYParser (int parse_param1) *]])
AT_CHECK_JAVA_GREP([[ *protected YYParser (Lexer yylexer, *int parse_param1) *]])
AT_CHECK_JAVA_GREP([[ *this.parse_param1 = parse_param1;]], [2])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%parse-param {int parse_param1}
%parse-param {long parse_param2}]],
    [], [[return EOF;]])
AT_CHECK_JAVA_GREP([[ *protected final int parse_param1;]])
AT_CHECK_JAVA_GREP([[ *protected final long parse_param2;]])
AT_CHECK_JAVA_GREP([[ *public YYParser (int parse_param1, *long parse_param2) *]])
AT_CHECK_JAVA_GREP([[ *protected YYParser (Lexer yylexer, *int parse_param1, *long parse_param2) *]])
AT_CHECK_JAVA_GREP([[ *this.parse_param1 = parse_param1;]], [2])
AT_CHECK_JAVA_GREP([[ *this.parse_param2 = parse_param2;]], [2])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[%lex-param {char lex_param1}]],
    [], [[return EOF;]], [[YYLexer (char lex_param1) {}]])
AT_CHECK_JAVA_GREP([[ *public YYParser (char lex_param1) *]])
AT_CHECK_JAVA_GREP([[.* = new YYLexer *(lex_param1);]])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%lex-param {char lex_param1}
%lex-param {short lex_param2}]],
    [], [[return EOF;]], [[YYLexer (char lex_param1, short lex_param2) {}]])
AT_CHECK_JAVA_GREP([[ *public YYParser (char lex_param1, *short lex_param2) *]])
AT_CHECK_JAVA_GREP([[.* = new YYLexer *(lex_param1, *lex_param2);]])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%parse-param {int parse_param1}
%parse-param {long parse_param2}
%lex-param {char lex_param1}
%lex-param {short lex_param2}]],
    [], [[return EOF;]], [[YYLexer (char lex_param1, short lex_param2) {}]])
AT_CHECK_JAVA_GREP([[ *protected final int parse_param1;]])
AT_CHECK_JAVA_GREP([[ *protected final long parse_param2;]])
AT_CHECK_JAVA_GREP([[ *public YYParser (char lex_param1, *short lex_param2, *int parse_param1, *long parse_param2) *]])
AT_CHECK_JAVA_GREP([[.* = new YYLexer *(lex_param1, *lex_param2);]])
AT_CHECK_JAVA_GREP([[ *protected YYParser (Lexer yylexer, *int parse_param1, *long parse_param2) *]])
AT_CHECK_JAVA_GREP([[ *this.parse_param1 = parse_param1;]], [2])
AT_CHECK_JAVA_GREP([[ *this.parse_param2 = parse_param2;]], [2])

AT_CLEANUP


# ------------------------- #
# Java throw specifications #
# ------------------------- #

AT_SETUP([Java throws specifications])

# %define throws        - 0 1 2
# %define lex-throws    - 0 1 2
# %code lexer             0 1

m4_define([AT_JT_lex_throws_define], [m4_case(AT_JT_lex_throws,
        -1, [],
         0, [[%define lex_throws {}]],
         1, [[%define lex_throws {InterruptedException}]],
         2, [[%define lex_throws {InterruptedException, IllegalAccessException}]])])

m4_define([AT_JT_yylex_throws], [m4_case(AT_JT_lex_throws,
        -1, [[ throws java.io.IOException]],
         0, [],
         1, [[ throws InterruptedException]],
         2, [[ throws InterruptedException, IllegalAccessException]])])

m4_define([AT_JT_yylex_action], [m4_case(AT_JT_lex_throws,
        -1, [[throw new java.io.IOException();]],
         0, [[return EOF;]],
         1, [[throw new InterruptedException();]],
         2, [[throw new IllegalAccessException();]])])


m4_define([AT_JT_throws_define], [m4_case(AT_JT_throws,
        -1, [],
         0, [[%define throws {}]],
         1, [[%define throws {ClassNotFoundException}]],
         2, [[%define throws {ClassNotFoundException, InstantiationException}]])])

m4_define([AT_JT_yyaction_throws], [m4_case(AT_JT_throws,
        -1, [],
         0, [],
         1, [[ throws ClassNotFoundException]],
         2, [[ throws ClassNotFoundException, InstantiationException]])])

m4_define([AT_JT_parse_throws_2], [m4_case(AT_JT_throws,
        -1, [],
         0, [],
         1, [[, ClassNotFoundException]],
         2, [[, ClassNotFoundException, InstantiationException]])])

m4_define([AT_JT_parse_throws],
        [m4_if(m4_quote(AT_JT_yylex_throws), [],
                [AT_JT_yyaction_throws],
                [AT_JT_yylex_throws[]AT_JT_parse_throws_2])])

m4_define([AT_JT_initial_action], [m4_case(AT_JT_throws,
        -1, [],
         0, [],
         1, [[%initial-action {if (true) throw new ClassNotFoundException();}]],
         2, [[%initial-action {if (true) throw new InstantiationException();}]])])

m4_define([AT_JT_parse_action], [m4_case(AT_JT_throws,
        -1, [],
         0, [],
         1, [[throw new ClassNotFoundException();]],
         2, [[throw new ClassNotFoundException();]])])

m4_for([AT_JT_lexer], 0, 1, 1,
  [m4_for([AT_JT_lex_throws], -1, 2, 1,
    [m4_for([AT_JT_throws], -1, 2, 1,
      [m4_if(AT_JT_lexer, 0,
        [AT_CHECK_JAVA_MINIMAL([
AT_JT_throws_define
AT_JT_lex_throws_define
AT_JT_initial_action],
[AT_JT_parse_action])],
        [AT_CHECK_JAVA_MINIMAL_W_LEXER([
AT_JT_throws_define
AT_JT_lex_throws_define
AT_JT_initial_action],
[AT_JT_yylex_throws],
[AT_JT_yylex_action],
[],
[AT_JT_parse_action])])
AT_CHECK_JAVA_GREP([[ *int yylex ()]AT_JT_yylex_throws *[;]])
AT_CHECK_JAVA_GREP([[ *private int yyaction ([^)]*)]AT_JT_yyaction_throws[ *]])
AT_CHECK_JAVA_GREP([[ *public boolean parse ()]AT_JT_parse_throws[ *]])
])])])

AT_CLEANUP


# ------------------------------------- #
# Java constructor init and init_throws #
# ------------------------------------- #

AT_SETUP([Java constructor init and init_throws])

m4_pushdef([AT_Witness],
[super("Test Thread"); if (true) throw new InterruptedException();])

AT_CHECK_JAVA_MINIMAL([[
%define extends {Thread}
%code init { ]AT_Witness[ }
%define init_throws {InterruptedException}
%lex-param {int lex_param}]])
AT_CHECK([[grep ']AT_Witness[' YYParser.java]], 0, [ignore])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%define extends {Thread}
%code init { ]AT_Witness[ }
%define init_throws {InterruptedException}]], [], [[return EOF;]])
AT_CHECK([[grep ']AT_Witness[' YYParser.java]], 0, [ignore])

m4_popdef([AT_Witness])

AT_CLEANUP


# ------------------------------------------ #
# Java value, position, and location types.  #
# ------------------------------------------ #

AT_SETUP([Java value, position, and location types])

AT_CHECK_JAVA_MINIMAL([[
%define api.value.type {java.awt.Color}
%type<java.awt.Color> start;
%define api.location.type {MyLoc}
%define api.position.type {MyPos}
%code { class MyPos {} }]], [[$$ = $<java.awt.Color>1;]], [[MyPos]])
AT_CHECK([[grep 'java.awt.Color' YYParser.java]], [0], [ignore])
AT_CHECK([[$EGREP -v ' */?\*' YYParser.java | grep -w 'Position']], [1], [ignore])
AT_CHECK([[$EGREP -v ' */?\*' YYParser.java | grep -w 'Location']], [1], [ignore])

AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%define api.value.type {java.awt.Color}
%type<java.awt.Color> start;
%define api.location.type {MyLoc}
%define api.position.type {MyPos}
%code { class MyPos {} }]], [], [[return EOF;]], [],
[[$$ = $<java.awt.Color>1;]],
[[java.awt.Color]], [[MyPos]], [[MyLoc]])
AT_CHECK([[grep 'java.awt.Color' YYParser.java]], [0], [ignore])
AT_CHECK([[$EGREP -v ' */?\*' YYParser.java | grep -w 'Position']], [1], [ignore])
AT_CHECK([[$EGREP -v ' */?\*' YYParser.java | grep -w 'Location']], [1], [ignore])

AT_CLEANUP


# ----------------------------------------------- #
# Java syntax error handling without error token. #
# ----------------------------------------------- #

AT_SETUP([Java syntax error handling without error token])

AT_DATA([[YYParser.y]], [[%language "Java"

%lex-param { String s }

%code imports {
  import java.io.IOException;
}

%code lexer {
  String Input;
  int Position;

  public YYLexer (String s)
  {
    Input    = s;
    Position = 0;
  }

  public void yyerror (String s)
  {
    System.err.println (s);
  }

  public Object getLVal ()
  {
    return null;
  }

  public int yylex () throws IOException
  {
    if (Position >= Input.length ())
      return EOF;
    else
      return Input.charAt (Position++);
  }
}

%code {
  public static void main (String args []) throws IOException
  {
    YYParser p = new YYParser (args [0]);
    p.parse ();
  }
}
%%
input:
  'a' 'a'
;
]])
AT_BISON_CHECK([[YYParser.y]])
AT_JAVA_COMPILE([[YYParser.java]])
AT_JAVA_PARSER_CHECK([[YYParser aa]], [[0]], [[]], [[]])
AT_JAVA_PARSER_CHECK([[YYParser ab]], [[0]], [[]], [[syntax error
]])
AT_JAVA_PARSER_CHECK([[YYParser ba]], [[0]], [[]], [[syntax error
]])

AT_CLEANUP
