# Checking Java Push Parsing.                            -*- Autotest -*-

# Copyright (C) 2013-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/>.

# The Java push parser tests are intended primarily
# to verify that the sequence of states that the parser
# traverses is the same as a pull parser would traverse.

##################################################
# Provide a way to generate data with and without push parsing
# so  it is possible to capture the output for comparison
# (except the "trivial" tests).
# Use "both" rather than "push" so we can also set it to "pull" to
# get the "experr" data.

m4_define([PUSHPULLFLAG],[-Dapi.push-pull=both])

# AT_CHECK_JAVA_GREP(FILE, [LINE], [COUNT=1])
# -------------------------------------------
# Check that FILE contains exactly COUNT lines matching ^LINE$
# with grep.  Unquoted so that COUNT can be a shell expression.
m4_define([AT_CHECK_JAVA_GREP],
[AT_CHECK_UNQUOTED([grep -c '^$2$' $1], [ignore], [m4_default([$3], [1])
])])


##################################################

AT_BANNER([[Java Push Parsing Tests]])

# Define a single copy of the trivial parser grammar.
# This is missing main(), so two versions
# are instantiated with different main() procedures.
m4_define([AT_TRIVIAL_GRAMMAR],
[[
%define api.parser.class {YYParser}
%define parse.error verbose
%define parse.trace

%code imports {
import java.io.*;
import java.util.*;
}

%%

start: 'a' 'b' 'c' ;

%%
]])

# Define common code across to be included in
# class Main for the trivial parser tests.
m4_define([AT_TRIVIAL_COMMON],[[
  static class YYerror implements YYParser.Lexer
  {
    public Object getLVal() {return null;}
    public int yylex () throws java.io.IOException { return 0; }
    public void yyerror (String msg) { System.err.println(msg); }
  }

  static YYParser parser = null;
  static YYerror yyerror = null;
  static int teststate = -1;

  static void setup()
    throws IOException
  {
      yyerror = new YYerror();
      parser = new YYParser(yyerror);
      parser.setDebugLevel(1);
      teststate = -1;
  }

  static String[] teststatename
    = new String[]{"YYACCEPT","YYABORT","YYERROR","UNKNOWN","YYPUSH_MORE"};

  static void check(int teststate, int expected, String msg)
  {
    System.err.println("teststate="+teststatename[teststate]
                       +"; expected="+teststatename[expected]);
    if (teststate != expected)
        {
            System.err.println("unexpected state: "+msg);
            System.exit(1);
        }
  }
]])

m4_define([AT_TRIVIAL_PARSER],[[
  ]AT_TRIVIAL_GRAMMAR[

  public class Main
  {

  ]AT_TRIVIAL_COMMON[

  static public void main (String[] args)
    throws IOException
  {
      setup();

      teststate = parser.push_parse('a', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('a', null)");

      setup();

      teststate = parser.push_parse('a', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('a', null)");
      teststate = parser.push_parse('b', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('b', null)");
      teststate = parser.push_parse('c', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('c', null)");
      teststate = parser.push_parse('\0', null);
      check(teststate,YYParser.YYACCEPT,"push_parse('\\0', null)");

      /* Reuse the parser instance and cause a failure */
      teststate = parser.push_parse('b', null);
      check(teststate,YYParser.YYABORT,"push_parse('b', null)");

      System.exit(0);
  }

}
]])

m4_define([AT_TRIVIAL_PARSER_INITIAL_ACTION],[[
  ]AT_TRIVIAL_GRAMMAR[

  public class Main
  {

  ]AT_TRIVIAL_COMMON[

  static public void main (String[] args)
    throws IOException
  {
      setup();

      teststate = parser.push_parse('a', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('a', null)");
      teststate = parser.push_parse('b', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('b', null)");
      teststate = parser.push_parse('c', null);
      check(teststate,YYParser.YYPUSH_MORE,"push_parse('c', null)");
      teststate = parser.push_parse('\0', null);
      check(teststate,YYParser.YYACCEPT,"push_parse('\\0', null)");

      System.exit(0);
  }

}
]])

## ----------------------------------------------------- ##
## Trivial Push Parser with api.push-pull verification.  ##
## ----------------------------------------------------- ##

AT_SETUP([Trivial Push Parser with api.push-pull verification])
AT_BISON_OPTION_PUSHDEFS

AT_DATA([[input.y]],
[[%language "Java"
]AT_TRIVIAL_PARSER[
]])

# Verify that the proper procedure(s) are generated for each case.
AT_BISON_CHECK([[-Dapi.push-pull=pull -o Main.java input.y]])
AT_CHECK_JAVA_GREP([[Main.java]],
                   [[.*public boolean parse ().*]],
                   [1])
# If BISON_USE_PUSH_FOR_PULL is set, then we have one occurrence of
# this function, otherwise it should not be there.
AT_CHECK_JAVA_GREP([[Main.java]],
        [[.*public int push_parse (int yylextoken, Object yylexval).*]],
        [${BISON_USE_PUSH_FOR_PULL-0}])

AT_BISON_CHECK([[-Dapi.push-pull=both -o Main.java input.y]])
AT_CHECK_JAVA_GREP([[Main.java]],
                   [[.*public boolean parse ().*]],
                   [1])
AT_CHECK_JAVA_GREP([[Main.java]],
        [[.*public int push_parse (int yylextoken, Object yylexval).*]],
        [1])

AT_BISON_CHECK([[-Dapi.push-pull=push -o Main.java input.y]])
AT_CHECK_JAVA_GREP([[Main.java]],
                   [[.*public boolean parse ().*]],
                   [0])
AT_CHECK_JAVA_GREP([[Main.java]],
        [[.*public int push_parse (int yylextoken, Object yylexval).*]],
        [1])

AT_JAVA_COMPILE([[Main.java]])
AT_JAVA_PARSER_CHECK([Main], 0, [], [stderr-nolog])
AT_BISON_OPTION_POPDEFS
AT_CLEANUP


## ------------------------------------------ ##
## Trivial Push Parser with %initial-action.  ##
## ------------------------------------------ ##

AT_SETUP([Trivial Push Parser with %initial-action])
AT_BISON_OPTION_PUSHDEFS
AT_DATA([[input.y]],
[[%language "Java"
%initial-action {
System.err.println("Initial action invoked");
}
]AT_TRIVIAL_PARSER_INITIAL_ACTION[
]])
AT_BISON_OPTION_POPDEFS
AT_BISON_CHECK([[-Dapi.push-pull=push -o Main.java input.y]])
AT_CHECK_JAVA_GREP([[Main.java]],
  [[System.err.println("Initial action invoked");]])
AT_JAVA_COMPILE([[Main.java]])
AT_JAVA_PARSER_CHECK([Main], 0, [], [stderr-nolog])
# Verify that initial action is called exactly once.
AT_CHECK_JAVA_GREP(
        [[stderr]],
        [[Initial action invoked]],
        [1])
AT_CLEANUP
