How can I rewrite the programs, so that I don't have to call `flex` but only call `bison` and `cc`?

做~自己de王妃 提交于 2019-12-11 23:23:48

问题


I already have a calculator program based on bison and flex which takes input from command line arguments.

Now how can I rewrite the programs, so that I don't have to call flex but only call bison and cc during building process? (Achieve something similar to https://unix.stackexchange.com/questions/499190/where-is-the-official-documentation-debian-package-iproute-doc#comment919875_499225).

$ ./fb1-5 '1+3'
= 4

Makefile:

fb1-5:  fb1-5.l fb1-5.y
    bison -d fb1-5.y
    flex fb1-5.l
    cc -o $@ fb1-5.tab.c lex.yy.c -lfl

fb1-5.y

/* simplest version of calculator */

%{
#  include <stdio.h>
%}

/* declare tokens */
%token NUMBER
%token ADD SUB MUL DIV ABS
%token OP CP

%%

calclist: /* nothing */
 | calclist exp { printf("= %d\n> ", $2); }
 ;

exp: factor
 | exp ADD exp { $$ = $1 + $3; }
 | exp SUB factor { $$ = $1 - $3; }
 | exp ABS factor { $$ = $1 | $3; }
 ;

factor: term
 | factor MUL term { $$ = $1 * $3; }
 | factor DIV term { $$ = $1 / $3; }
 ;

term: NUMBER
 | ABS term { $$ = $2 >= 0? $2 : - $2; }
 | OP exp CP { $$ = $2; }
 ;
%%
int main(int argc, char** argv)
{
  // printf("> ");
  if(argc > 1) {
    if(argv[1]){
      yy_scan_string(argv[1]);
    }
  }

  yyparse();
  return 0;
}

yyerror(char *s)
{
  fprintf(stderr, "error: %s\n", s);
}

fb1-5.l:

/* recognize tokens for the calculator and print them out */

%{
# include "fb1-5.tab.h"
%}

%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"|"     { return ABS; }
"("     { return OP; }
")"     { return CP; }
[0-9]+  { yylval = atoi(yytext); return NUMBER; }

"//".*  
[ \t]   { /* ignore white space */ }
.   { yyerror("Mystery character %c\n", *yytext); }
%%

Update:

I tried to follow the advice in the reply, see the modified code below. in main(), why is yyerror() called before printf("argv[%d]: %s ", n, argv[n])? Isn't yyerror() called only by yyparse(), and isn't yyparse only called after printf("argv[%d]: %s ", n, argv[n]) in main() in main().

$ ./fb1-5  2*4
2*4error: �
= 8

fb1-5.y:

/* simplest version of calculator */

%{
#  include <stdio.h>
  FILE * fin;
  int yylex (void);
  void yyerror(char *s);  
  %}

/* declare tokens */
%token NUMBER
%token ADD SUB MUL DIV ABS
%token OP CP

%%

calclist: /* nothing */
 | calclist exp { printf("= %d\n", $2); }
 ;

exp: factor
 | exp ADD exp { $$ = $1 + $3; }
 | exp SUB factor { $$ = $1 - $3; }
 | exp ABS factor { $$ = $1 | $3; }
 ;

factor: term
 | factor MUL term { $$ = $1 * $3; }
 | factor DIV term { $$ = $1 / $3; }
 ;

term: NUMBER
 | ABS term { $$ = $2 >= 0? $2 : - $2; }
 | OP exp CP { $$ = $2; }
 ;
%%




/* The lexical analyzer returns a double floating point
   number on the stack and the token NUM, or the numeric code
   of the character read if not a number.  It skips all blanks
   and tabs, and returns 0 for end-of-input.  */

#include <ctype.h>
#include <string.h>

int yylex (void)
{
  char c;

/* Skip white space.  */
  while ((c = getc(fin)) == ' ' || c == '\t'){
    continue;
  }

  // printf("%s", &c);

  /* Process numbers.  */
  if (c == '.' || isdigit (c))
    {
      ungetc(c, fin);
      fscanf (fin, "%d", &yylval);
      return NUMBER;
    }

  /* Process addition.  */
  if (c == '+')
    {
      return ADD;
    }

  /* Process sub.  */
  if (c == '-')
    {
      return SUB;
    }

  /* Process mult.  */
  if (c == '*')
    {
      return MUL;
    }

  /* Process division.  */
  if (c == '/')
    {
      return DIV;
    }

  /* Process absolute.  */
  if (c == '|')
    {
      return ABS;
    }

   /* Process left paren.  */
   if (c == '(')
    {
      return OP;
    }

  /* Process right paren.  */
  if (c == ')')
    {
      return CP;
    }

  /* Return a single char.  */
  yyerror(&c);
  return c;
}


int main(int argc, char** argv)
{
  // evaluate each command line arg as an arithmetic expression
  int n=1;
  while (n < argc) {
    if(argv[n]){
      // yy_scan_string(argv[n]);
      // fin = stdin;
      fin = fmemopen(argv[n], strlen (argv[n]), "r");
      printf("%s ",argv[n]);
      fflush(stdout);
      yyparse();
    }
    n++;
  }

  return 0;
}

void yyerror(char *s)
{
  fprintf(stderr, "error: %s\n", s);
}

回答1:


There is a basic implementation of a lexical scanner in the examples section of the bison manual. (Slightly less basic versions are later in the manual.)

That won't help you directly because it is based on fscanf, which means that it works on an input stream. Most C libraries contain functions which let you treat a character string as a FILE* (see, for example, the Posix standard fmemopen). Failing that, you'd have to replace the getc and scanf calls with string based alternatives, which means you will need to keep track of a buffer and input pointer somewhere. strtoul (or strtod) will prove useful because the second argument helps you keep track of how much of the string was used by the number.



来源:https://stackoverflow.com/questions/54600702/how-can-i-rewrite-the-programs-so-that-i-dont-have-to-call-flex-but-only-cal

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