I have a scanner, parser and a main from which I create an executable via
bison -d parser.y; flex scanner.l; gcc main.c parer.tab.c lex.yy.c
When I run ./a.out
it does what I want: If Ctrl+D
is pressed an EOF
is detected and main
can act accordingly. This means: if yyin
is stdin
then hitting Return
ends the parsing of that line and the main loop waits for the next input line. Pressing Ctrl+D
ends parsing input with a break
in the main loop and exits. If the input comes from a file, e,g, testFile
that file can contain 1 expression to be parsed until an EOF. In the file scenario new lines should be eaten up like spaces and tabs. All this should behave like an interpreter when input is from stdin
and like a script evaluator when the input is from a file. An example content of such a test file would be:test\n
. Here the EOF is not detected. And I have trouble understanding why that is the case. In other words I'd like an extension of the question here to additionally work with input files
parser.y:
%{
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* stuff from flex that bison needs to know about: */
int yylex();
int yyparse();
FILE *yyin;
static int parseValue;
void yyerror(const char *s);
%}
%token TWORD
%token TEOF
%token TJUNK
%start input
%%
input: word { printf("W"); parseValue = 1; }
| eof { printf("eof"); parseValue = -11;}
| /* empty */ { printf("_"); parseValue = -1; }
| error { printf("E"); parseValue = -2; }
;
eof: TEOF
;
word: TWORD
;
%%
void yyerror(const char *s) {
printf("nope...");
}
int getWord( FILE *file) {
int err;
if (file) {
yyin = file;
} else /* error */ {
printf("file not valid");
return -3;
}
err = yyparse();
if (!err) {
return parseValue;
} else /* error */ {
printf("parse error");
return -4;
}
}
scanner.l:
%{
#include <stdio.h>
#include "parser.tab.h"
#define YYSTYPE int
int yylex();
%}
/* avoid: implicit declaration of function ‘fileno’ */
/*%option always-interactive*/
%option noyywrap
/* to avoid warning: ‘yyunput’ defined but not used */
%option nounput
/* to avoid warning: ‘input’ defined but not used */
%option noinput
%%
<<EOF>> { return TEOF; }
[ \t] { }
[\n] { if (yyin == stdin) return 0; }
[a-zA-Z][a-zA-Z0-9]* { return TWORD; }
. { return TJUNK; }
%%
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
int main(int argc, char *argv[]) {
int result = 0;
FILE *fOut = stdout, *fIn = stdin;
/* skip over program name */
++argv, --argc;
if ( argc > 0 ) {
fIn = fopen( argv[0], "r" );
}
while (true) {
fprintf(fOut, "\nTEST : ", result);
result = getWord(fIn);
if (result == -11) {
printf(" %i ", result); printf("--> EOF");
break;
}
if (result < 0) {
printf(" %i ", result); printf("--> <0");
/*continue;*/
break;
}
fprintf(fOut, " => %i", result);
}
fprintf(fOut, "\n\n done \n ");
exit(EXIT_SUCCESS);
}
I have tried to rewrite the parse according to suggestions made here or here, without much success. What is correct way for main to become aware of an EOF when input is read from a file?
Update:
One suggestion was that the issue may be due to the return 0;
on the \n
. As a quick test, I only return 0 if yyin == stin
but calling ./a.out testFile
still does not catch the EOF
.
Update 2:
I got this to work via using yywrap
. I got rid of all the TEOF
stuff. The scanner has a part:
extern int eof;
and at the end:
int yywrap() {
eof = 1;
return 1;
}
In the parser there is a:
int eof = 0;
and further down in the file:
err = yyparse();
if (err != 0) return -4;
else if (eof) return -11;
else return parseValue;
If someone can show me a more elegant solution, I'd still appreciate that. This is probably a good way to make a clean version.