Flex and Bison: Beginning a sentence with a specific keyword

给你一囗甜甜゛ 提交于 2020-01-17 04:59:32

问题


I am working on a program using Flex and Bison. My task can be done using only Flex(using start conditions etc.), but I have understood that using Bison might make my life easier. My task is to design a program which recognizes a programming language declaration part. Its syntax and logic can be understood through my code below. My problem is that I want my program to recognize as an acceptable declaration part every part of code which begins only with the "var" keyword! Until now, I have not managed to do that. How can I succeed it?

I present below my .l (flex) and .y (bison) files.

exercise4.l

%{
#include <stdio.h>
%}
%%
[ \t\n]+                { /* Ignore all whitespaces */ }
";"         { /* Ignore all semicolons */ }
":"         { /* Ignore all colons */ }
var                     { printf("A keyword: %s\n",yytext); return VAR; }
real                    { printf("A variable type: %s\n",yytext); return REAL; }
boolean                 { printf("A variable type: %s\n",yytext); return BOOLEAN; }
integer                 { printf("A variable type: %s\n",yytext); return INTEGER; }
char                    { printf("A variable type: %s\n",yytext); return CHAR; }
[a-zA-Z][a-zA-Z0-9_]*   { printf("A variable name: %s\n",yytext); return VAR_NAME; }
.                       { printf("Unrecognized character!\n"); return yytext[0]; }
%%

exercise4.y

%{
#include <stdio.h>    
%}
%token VAR VAR_NAME REAL BOOLEAN INTEGER CHAR
%%
program : VAR typedecls ;
typedecls : typedecl | typedecls typedecl ;
typedecl : varlist ':' var_type ';' ;
varlist : VAR_NAME | varlist ',' VAR_NAME ;
var_type : REAL | BOOLEAN | INTEGER | CHAR ;
%%
main( argc, argv )
int argc;
char **argv;
{
extern FILE *yyin;
++argv, --argc; /* skip over program name */
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
//yylex();
yyparse();
}
yyerror(char *s)  
{  
 printf("\nError\n");  
}
#include "lex.yy.c"

回答1:


Your scanner never returns : or ; tokens (and complains about , being unrecognized). But your grammar includes those tokens.

Consequently,

typedecl : varlist ':' var_type ';' ;

can never match anything. It doesn't see the : it is expecting, and the var_type is thus unexpected. (And it wouldn't ever match the ; if it got that far.)


I don't know what ancient tutorial you're following, but the pre-ansi prototype for main suggests it was composed in the last century (and not in its final years, either). As a rough guide, I slightly updated your files.

Here's exercise4.l; the most important modification is that I removed the handbuilt trace logs, since I intend to use flex's built-in debugging feature. I also added some options to reduce compiler warnings, and enabled line number tracking for use in error messages. I left in the bug.

/* File: exercise4.l */
%{
  /* The bison-generated header file includes token declarations */
  #include "exercise4.tab.h"
%}

%option noinput nounput noyywrap nodefault
%option yylineno

%%
[ \t\n]+                { /* Ignore all whitespaces */ }
";"                     { /* Ignore all semicolons. BUG */ }
":"                     { /* Ignore all colons. BUG */ }
var                     { return VAR; }
real                    { return REAL; }
boolean                 { return BOOLEAN; }
integer                 { return INTEGER; }
char                    { return CHAR; }
[a-zA-Z][a-zA-Z0-9_]*   { return VAR_NAME; }
.                       { return yytext[0]; }

And here's the parser, with a few fixes (like using the standard C prototype syntax from the 1989 C standard):

/* File: exercise4.y */
%{
  /* Used in this file */
  #include <stdio.h>

  /* Forward and external declarations */
  extern int yylineno;
  int yylex();
  void yyerror(const char* msg);
%}

 /* These two options make error messages more informative */    
%define parse.lac full
%error-verbose

%token VAR VAR_NAME REAL BOOLEAN INTEGER CHAR

%%

program   : VAR typedecls
typedecls : typedecl
          | typedecls typedecl
typedecl  : varlist ':' var_type ';'
varlist   : VAR_NAME
          | varlist ',' VAR_NAME ;
var_type  : REAL | BOOLEAN | INTEGER | CHAR ;

%%

/* Welcome to 1990 */
int main( int argc, char** argv ) {
  extern FILE *yyin;
  if ( argc > 1 )
    yyin = fopen( argv[1], "r" );
  else
    yyin = stdin;
  if (!yyin) {
    /* If you don't check, you'll end up segfaulting when
     * yylex tries to read from NULL. Checking lets us print
     * a hopefully meaningful error message.
     */
    perror("Could not open file for reading");
    return 1;
  }
  return yyparse();
}

/* Now that error messages have some content, it's worthwhile
 * actually using the argument passed to yyerror */
void yyerror(const char* msg) {
  fprintf(stderr, "At line %d: %s\n", yylineno, msg);
}

Now, I run the files through flex, bison and gcc, in that order, to produce an executable:

# The `-d` option to flex causes it to insert debugging traces. That's
# a lot less effort and a lot more useful than rolling your own trace
# logs.
flex -d -o exercise4.scan.c exercise4.l
# The `-d` option to bison causes it to output a header file, whose name
# is the same as the output file name with `.c` changed to `.h`. That's
# the file which we need to `#include` in the scanner, so that token
# names are available to scanner actions. Bison also has a debugging
# feature, but you need to use `-t` to enable it, plus add a line at
# runtime. For now, we'll leave it out.
bison -d -o exercise4.tab.c exercise4.y
# Now compile and link an executable:
gcc -Wall -g -o exercise4 exercise4.tab.c exercise4.scan.c

Normally, the three shell commands would go into a Makefile so that you could just type make exercise4 to create the executable. But there's nothing really complicated there.

Now, we can try it out. Since I didn't fix the bugs, you'll easily see the problem:

var a, b: integer;
--accepting rule at line 14 ("var")
--accepting rule at line 11 (" ")
--accepting rule at line 19 ("a")
--accepting rule at line 20 (",")
--accepting rule at line 11 (" ")
--accepting rule at line 19 ("b")
--accepting rule at line 13 (":")
--accepting rule at line 11 (" ")
--accepting rule at line 17 ("integer")
At line 1: syntax error, unexpected INTEGER, expecting ':' or ','

The lines starting -- are flex's trace. You can see each token as it is recognized. So when a : is encountered in the input, the rule at line 13 is executed. That rule is { /* Ignore all colons */ }. And that's what happens.

The error message at the end was printed by yyerror. Thanks to the %errors-verbose directive, it tells us what it found (INTEGER) and what it was expecting.

Hope that all helps some.



来源:https://stackoverflow.com/questions/34501734/flex-and-bison-beginning-a-sentence-with-a-specific-keyword

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!