# Checking Java Push Parsing. -*- Autotest -*- # Copyright (C) 2013 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 . # 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]) # Modify the grep tester from java.at # to ignore status so we can test for zero matching lines m4_define([AT_CHECK_JAVA_GREP], [AT_CHECK([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 parser_class_name {YYParser} %error-verbose %code imports { import java.io.*; import java.util.*; } %% start: 'a' 'b' 'c' ; %% ]) # Define comon code across to be includede 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","YYMORE"}; static void check(int teststate, int expected, String msg) { System.err.println("teststate="+teststatename[[teststate]] +"; expected="+teststatename[[expected]]); if (teststate == expected) return; 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[[]] argv) throws IOException { setup(); teststate = parser.push_parse('a', null); check(teststate,YYParser.YYMORE,"push_parse('a', null)"); setup(); teststate = parser.push_parse('a', null); check(teststate,YYParser.YYMORE,"push_parse('a', null)"); teststate = parser.push_parse('b', null); check(teststate,YYParser.YYMORE,"push_parse('b', null)"); teststate = parser.push_parse('c', null); check(teststate,YYParser.YYMORE,"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[[]] argv) throws IOException { setup(); teststate = parser.push_parse('a', null); check(teststate,YYParser.YYMORE,"push_parse('a', null)"); teststate = parser.push_parse('b', null); check(teststate,YYParser.YYMORE,"push_parse('b', null)"); teststate = parser.push_parse('c', null); check(teststate,YYParser.YYMORE,"push_parse('c', null)"); teststate = parser.push_parse('\0', null); check(teststate,YYParser.YYACCEPT,"push_parse('\\0', null)"); System.exit(0); } } ]) AT_SETUP([Trivial Push Parser with api.push-pull verification]) AT_BISON_OPTION_PUSHDEFS AT_DATA([[input.y]],[[%language "Java" ]AT_TRIVIAL_PARSER[ ]]) AT_BISON_OPTION_POPDEFS # 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]) AT_CHECK_JAVA_GREP( [[Main.java]], [[.*public int push_parse (int yylextoken, Object yylexval).*]], [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_CLEANUP 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 # Define a single copy of the Calculator grammar. m4_define([AT_CALC_BODY],[ %code imports { import java.io.*; } %code { static StringReader getinput(String filename) throws IOException { StringBuilder buf = new StringBuilder(); FileReader file = new FileReader(filename); int c; while ((c=file.read()) > 0) buf.append((char)c); file.close(); return new StringReader(buf.toString()); } } /* Bison Declarations */ %token NUM "number" %type exp %nonassoc '=' /* comparison */ %left '-' '+' %left '*' '/' %left NEG /* negation--unary minus */ %right '^' /* exponentiation */ /* Grammar follows */ %% input: line | input line ; line: '\n' | exp '\n' {System.out.println("total = "+$[]1);} | error '\n' ; exp: NUM { $[]$ = $[]1;} | exp '=' exp { if ($[]1.intValue() != $[]3.intValue()) yyerror (]AT_LOCATION_IF([[@$,]])[ "calc: error: " + $[]1 + " != " + $[]3); } | exp '+' exp { $[]$ = new Integer ($[]1.intValue () + $[]3.intValue ()); } | exp '-' exp { $[]$ = new Integer ($[]1.intValue () - $[]3.intValue ()); } | exp '*' exp { $[]$ = new Integer ($[]1.intValue () * $[]3.intValue ()); } | exp '/' exp { $[]$ = new Integer ($[]1.intValue () / $[]3.intValue ()); } | '-' exp %prec NEG { $[]$ = new Integer (-$[]2.intValue ()); } | exp '^' exp { $[]$ = new Integer ((int)Math.pow ($[]1.intValue (), $[]3.intValue ())); } | '(' exp ')' { $[]$ = $[]2;} | '(' error ')' { $[]$ = new Integer (1111);} | '!' { $[]$ = new Integer (0); return YYERROR;} | '-' error { $[]$ = new Integer (0); return YYERROR;} ; ]) # Test that the states transitioned by the push parser are the # same as for the pull parser. This test is assumed to work # if it produces the same partial trace of stack states as is # produced when using pull parsing. The output is verbose, # but seems essential for verifying push parsing. AT_SETUP([Calc parser with api.push-pull both]) AT_BISON_OPTION_PUSHDEFS # Define the calculator input. # Warning: if you changes the input file # then the locations test file position numbers # may be incorrect and you will have # to modify that file as well. AT_DATA([input],[[1 + 2 * 3 = 7 1 + 2 * -3 = -5 -1^2 = -1 (-1)^2 = 1 ---1 = -1 1 - 2 - 3 = -4 1 - (2 - 3) = 2 2^2^3 = 256 (2^2)^3 = 64 ]]) # Compose pieces to build the actual .y file. AT_DATA([Calc.y],[[/* Infix notation calculator--calc */ %language "Java" %name-prefix "Calc" %define parser_class_name {Calc} %code { static class UserLexer implements Calc.Lexer { StreamTokenizer st; StringReader rdr; public UserLexer(StringReader reader) { rdr = reader; st = new StreamTokenizer(rdr); st.resetSyntax(); st.eolIsSignificant(true); st.whitespaceChars(9, 9); st.whitespaceChars(32, 32); st.wordChars(48, 57); } Integer yylval; public Object getLVal() { return yylval; } public void yyerror(String msg) { System.err.println(msg); } public int yylex () throws IOException { switch (st.nextToken()) { case StreamTokenizer.TT_EOF: return EOF; case StreamTokenizer.TT_EOL: return (int) '\n'; case StreamTokenizer.TT_WORD: yylval = new Integer (st.sval); return NUM; default: return st.ttype; } } } } %code { public static void main (String[] argv) throws IOException { StringReader reader = getinput(argv[0]); UserLexer lexer = new UserLexer(reader); Calc calc = new Calc(lexer); calc.setDebugLevel(1); calc.parse(); }//main } ]AT_CALC_BODY[ ]]) # This data was captured from running a pull parser. AT_DATA([[expout]],[[Stack now 0 Stack now 0 2 Stack now 0 9 Stack now 0 9 19 Stack now 0 9 19 2 Stack now 0 9 19 28 Stack now 0 9 19 28 20 Stack now 0 9 19 28 20 2 Stack now 0 9 19 28 20 29 Stack now 0 9 19 28 Stack now 0 9 Stack now 0 9 17 Stack now 0 9 17 2 Stack now 0 9 17 26 Stack now 0 9 Stack now 0 9 23 Stack now 0 8 Stack now 0 7 Stack now 0 7 2 Stack now 0 7 9 Stack now 0 7 9 19 Stack now 0 7 9 19 2 Stack now 0 7 9 19 28 Stack now 0 7 9 19 28 20 Stack now 0 7 9 19 28 20 3 Stack now 0 7 9 19 28 20 3 2 Stack now 0 7 9 19 28 20 3 12 Stack now 0 7 9 19 28 20 29 Stack now 0 7 9 19 28 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 3 Stack now 0 7 9 17 3 2 Stack now 0 7 9 17 3 12 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 4 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 3 Stack now 0 7 3 2 Stack now 0 7 3 12 Stack now 0 7 3 12 22 Stack now 0 7 3 12 22 2 Stack now 0 7 3 12 22 31 Stack now 0 7 3 12 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 3 Stack now 0 7 9 17 3 2 Stack now 0 7 9 17 3 12 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 5 Stack now 0 7 5 3 Stack now 0 7 5 3 2 Stack now 0 7 5 3 12 Stack now 0 7 5 14 Stack now 0 7 5 14 25 Stack now 0 7 9 Stack now 0 7 9 22 Stack now 0 7 9 22 2 Stack now 0 7 9 22 31 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 2 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 4 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 3 Stack now 0 7 3 3 Stack now 0 7 3 3 3 Stack now 0 7 3 3 3 2 Stack now 0 7 3 3 3 12 Stack now 0 7 3 3 12 Stack now 0 7 3 12 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 3 Stack now 0 7 9 17 3 2 Stack now 0 7 9 17 3 12 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 4 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 2 Stack now 0 7 9 Stack now 0 7 9 18 Stack now 0 7 9 18 2 Stack now 0 7 9 18 27 Stack now 0 7 9 Stack now 0 7 9 18 Stack now 0 7 9 18 2 Stack now 0 7 9 18 27 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 3 Stack now 0 7 9 17 3 2 Stack now 0 7 9 17 3 12 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 2 Stack now 0 7 9 Stack now 0 7 9 18 Stack now 0 7 9 18 5 Stack now 0 7 9 18 5 2 Stack now 0 7 9 18 5 14 Stack now 0 7 9 18 5 14 18 Stack now 0 7 9 18 5 14 18 2 Stack now 0 7 9 18 5 14 18 27 Stack now 0 7 9 18 5 14 Stack now 0 7 9 18 5 14 25 Stack now 0 7 9 18 27 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 2 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 4 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 2 Stack now 0 7 9 Stack now 0 7 9 22 Stack now 0 7 9 22 2 Stack now 0 7 9 22 31 Stack now 0 7 9 22 31 22 Stack now 0 7 9 22 31 22 2 Stack now 0 7 9 22 31 22 31 Stack now 0 7 9 22 31 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 2 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 5 Stack now 0 7 5 2 Stack now 0 7 5 14 Stack now 0 7 5 14 22 Stack now 0 7 5 14 22 2 Stack now 0 7 5 14 22 31 Stack now 0 7 5 14 Stack now 0 7 5 14 25 Stack now 0 7 9 Stack now 0 7 9 22 Stack now 0 7 9 22 2 Stack now 0 7 9 22 31 Stack now 0 7 9 Stack now 0 7 9 17 Stack now 0 7 9 17 2 Stack now 0 7 9 17 26 Stack now 0 7 9 Stack now 0 7 9 23 Stack now 0 7 16 Stack now 0 7 Stack now 0 7 15 ]]) AT_BISON_CHECK([PUSHPULLFLAG [-o Calc.java Calc.y]]) AT_JAVA_COMPILE([[Calc.java]]) #Verify that this is a push parser. AT_CHECK_JAVA_GREP([[Calc.java]], [[.*public void push_parse_initialize().*]]) # Capture stderr output for comparison purposes. AT_JAVA_PARSER_CHECK([Calc input], 0, [ignore-nolog], [stderr-nolog]) # Extract the "Stack Now" lines from the error output, # send them to stdout (via the sed command) and compare to expout. # NOTE: because the target is "expout", this macro automatically # compares the output of the sed command with the contents of # the file "expout" (defined above). AT_CHECK([[sed -e '/^Stack now.*$/p' -e d ./stderr|tee jj]],[ignore],[expout],[ignore-nolog]) AT_BISON_OPTION_POPDEFS AT_CLEANUP # This test looks for location reporting by looking # at the lexer output with locations enabled. # It defines a lexer that reports location info. AT_SETUP([Calc parser with %locations %code lexer and api.push-pull both]) AT_BISON_OPTION_PUSHDEFS AT_DATA([Calc.y],[[/* Infix notation calculator--calc. */ %language "Java" %name-prefix "Calc" %define parser_class_name {Calc} %lex-param { Reader rdr } %locations %code imports { import java.io.*; } %code lexer { StreamTokenizer st; Integer yylval; public YYLexer(Reader rdr) { st = new StreamTokenizer(rdr); st.resetSyntax(); st.eolIsSignificant(true); st.whitespaceChars(9, 9); st.whitespaceChars(32, 32); st.wordChars(48, 57); } Position yypos = new Position (1, 0); public Position getStartPos() { return yypos; } public Position getEndPos() { return yypos; } public Object getLVal() { return yylval; } public void yyerror(Location loc, String msg) { System.err.println(loc+":"+msg); } public int yylex () throws IOException { yypos = new Position (yypos.lineno (),yypos.token () + 1); switch (st.nextToken()) { case StreamTokenizer.TT_EOF: return EOF; case StreamTokenizer.TT_EOL: yypos = new Position (yypos.lineno () + 1, 0); return (int) '\n'; case StreamTokenizer.TT_WORD: yylval = new Integer (st.sval); return NUM; default: return st.ttype; } } } %code { 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; } }//Class Position } %code { public static void main (String[] argv) throws IOException { StringReader reader = getinput(argv[0]); Calc calc = new Calc(reader); calc.setDebugLevel(1); calc.parse(); } } ]AT_CALC_BODY[ ]]) # Define the expected calculator output. # This should match the output from a pull parser. AT_DATA([output],[[total = 7 total = -5 total = -1 total = 1 total = -1 total = -4 total = 2 total = 256 total = 64 ]]) AT_DATA([locations],[[Next token is token "number" (1.1: 1) Next token is token '+' (1.2: 1) Next token is token "number" (1.3: 2) Next token is token '*' (1.4: 2) Next token is token "number" (1.5: 3) Next token is token '=' (1.6: 3) Next token is token '=' (1.6: 3) Next token is token '=' (1.6: 3) Next token is token "number" (1.7: 7) Next token is token '\n' (2.0: 7) Next token is token '\n' (2.0: 7) Next token is token "number" (2.1: 1) Next token is token '+' (2.2: 1) Next token is token "number" (2.3: 2) Next token is token '*' (2.4: 2) Next token is token '-' (2.5: 2) Next token is token "number" (2.6: 3) Next token is token '=' (2.7: 3) Next token is token '=' (2.7: 3) Next token is token '=' (2.7: 3) Next token is token '=' (2.7: 3) Next token is token '-' (2.8: 3) Next token is token "number" (2.9: 5) Next token is token '\n' (3.0: 5) Next token is token '\n' (3.0: 5) Next token is token '\n' (3.0: 5) Next token is token '\n' (4.0: 5) Next token is token '-' (4.1: 5) Next token is token "number" (4.2: 1) Next token is token '^' (4.3: 1) Next token is token "number" (4.4: 2) Next token is token '=' (4.5: 2) Next token is token '=' (4.5: 2) Next token is token '=' (4.5: 2) Next token is token '-' (4.6: 2) Next token is token "number" (4.7: 1) Next token is token '\n' (5.0: 1) Next token is token '\n' (5.0: 1) Next token is token '\n' (5.0: 1) Next token is token '(' (5.1: 1) Next token is token '-' (5.2: 1) Next token is token "number" (5.3: 1) Next token is token ')' (5.4: 1) Next token is token ')' (5.4: 1) Next token is token '^' (5.5: 1) Next token is token "number" (5.6: 2) Next token is token '=' (5.7: 2) Next token is token '=' (5.7: 2) Next token is token "number" (5.8: 1) Next token is token '\n' (6.0: 1) Next token is token '\n' (6.0: 1) Next token is token '\n' (7.0: 1) Next token is token '-' (7.1: 1) Next token is token '-' (7.2: 1) Next token is token '-' (7.3: 1) Next token is token "number" (7.4: 1) Next token is token '=' (7.5: 1) Next token is token '=' (7.5: 1) Next token is token '=' (7.5: 1) Next token is token '=' (7.5: 1) Next token is token '-' (7.6: 1) Next token is token "number" (7.7: 1) Next token is token '\n' (8.0: 1) Next token is token '\n' (8.0: 1) Next token is token '\n' (8.0: 1) Next token is token '\n' (9.0: 1) Next token is token "number" (9.1: 1) Next token is token '-' (9.2: 1) Next token is token "number" (9.3: 2) Next token is token '-' (9.4: 2) Next token is token '-' (9.4: 2) Next token is token "number" (9.5: 3) Next token is token '=' (9.6: 3) Next token is token '=' (9.6: 3) Next token is token '-' (9.7: 3) Next token is token "number" (9.8: 4) Next token is token '\n' (10.0: 4) Next token is token '\n' (10.0: 4) Next token is token '\n' (10.0: 4) Next token is token "number" (10.1: 1) Next token is token '-' (10.2: 1) Next token is token '(' (10.3: 1) Next token is token "number" (10.4: 2) Next token is token '-' (10.5: 2) Next token is token "number" (10.6: 3) Next token is token ')' (10.7: 3) Next token is token ')' (10.7: 3) Next token is token '=' (10.8: 3) Next token is token '=' (10.8: 3) Next token is token "number" (10.9: 2) Next token is token '\n' (11.0: 2) Next token is token '\n' (11.0: 2) Next token is token '\n' (12.0: 2) Next token is token "number" (12.1: 2) Next token is token '^' (12.2: 2) Next token is token "number" (12.3: 2) Next token is token '^' (12.4: 2) Next token is token "number" (12.5: 3) Next token is token '=' (12.6: 3) Next token is token '=' (12.6: 3) Next token is token '=' (12.6: 3) Next token is token "number" (12.7: 256) Next token is token '\n' (13.0: 256) Next token is token '\n' (13.0: 256) Next token is token '(' (13.1: 256) Next token is token "number" (13.2: 2) Next token is token '^' (13.3: 2) Next token is token "number" (13.4: 2) Next token is token ')' (13.5: 2) Next token is token ')' (13.5: 2) Next token is token '^' (13.6: 2) Next token is token "number" (13.7: 3) Next token is token '=' (13.8: 3) Next token is token '=' (13.8: 3) Next token is token "number" (13.9: 64) Next token is token '\n' (14.0: 64) Next token is token '\n' (14.0: 64) ]]) # Define the calculator input. # Warning: if you changes the input file # then the locations test file position numbers # may be incorrect and you will have # to modify that file as well. AT_DATA([input],[[1 + 2 * 3 = 7 1 + 2 * -3 = -5 -1^2 = -1 (-1)^2 = 1 ---1 = -1 1 - 2 - 3 = -4 1 - (2 - 3) = 2 2^2^3 = 256 (2^2)^3 = 64 ]]) AT_BISON_CHECK([PUSHPULLFLAG [-o Calc.java Calc.y]]) AT_JAVA_COMPILE([[Calc.java]]) # Verify that this is a push parser AT_CHECK_JAVA_GREP([[Calc.java]], [[.*public void push_parse_initialize().*]]) # Capture the stdout and stderr output for comparison purposes. AT_JAVA_PARSER_CHECK([Calc input], 0, [stdout-nolog], [stderr-nolog]) # 1. Check that the token locations are correct AT_CHECK([[cp -f ./locations ./expout]],[ignore],[ignore-nolog],[ignore-nolog]) AT_CHECK([[sed -e '/^Next token.*$/p' -e d ./stderr]],[ignore],[expout],[ignore-nolog]) # 2. Check that the calculator output matches that of a pull parser AT_CHECK([[rm -f ./expout; cp -f ./output ./expout]],[ignore],[ignore-nolog],[ignore-nolog]) AT_CHECK([[cat ./stdout]],[ignore],[expout],[ignore-nolog]) AT_CLEANUP