Flex&Bison

2020-08-29
2 min read

Flex

头文件检查

下面代码用于递归检查 C 头文件是否都存在

%option noyywrap warn nodefault

%x IFILE

%{
struct bufstack {
  struct bufstack *prev;  /* previous entry */
  YY_BUFFER_STATE bs;     /* saved buffer */
  int lineno;             /* saved line number */
  char *filename;         /* name of this file */
  FILE *f;                /* current file */
} *curbs = 0;

char *curfilename;        /* name of current input file */

int newfile(char *fn);
int popfile(void);
%}

%%

^"#"[ \t]*include[ \t]*[\"<] { BEGIN IFILE; }

<IFILE>[^ \t\n\">]+          { 
                                { int c; while((c = input()) && c != '\n') ; }
                                yylineno++;
                                if(!newfile(yytext)) yyterminate(); 
                                BEGIN INITIAL;
                             }

<IFILE>.|\n                  { 
                                fprintf(stderr, "%4d bad include line\n", yylineno);
                                yyterminate();
                             }

^.                           { fprintf(yyout, "%4d %s", yylineno, yytext); }
^\n                          { fprintf(yyout, "%4d %s", yylineno++, yytext); }
\n                           { ECHO; yylineno++; }
.                            { ECHO; }
<<EOF>>                      { if(!popfile()) yyterminate(); }

%%

int main(int argc, char **argv) {
  if(argc < 2) {
    fprintf(stderr, "need filename\n");
    return 1;
  }
  if(newfile(argv[1]))
    yylex();
}

int newfile(char *fn) {
  FILE *f = fopen(fn, "r");
  struct bufstack *bs = malloc(sizeof(struct bufstack));

  /* die if no file or no room */
  if(!f) { perror(fn); return 0; }
  if(!bs) { perror("malloc"); exit(1); }

  /* remember state */
  if(curbs) curbs->lineno = yylineno;
  bs->prev = curbs;

  /* set up current entry */
  bs->bs = yy_create_buffer(f, YY_BUF_SIZE);
  bs->f = f;
  bs->filename = fn;
  yy_switch_to_buffer(bs->bs);
  curbs = bs;
  yylineno = 1;
  curfilename = fn;
  return 1;
}

int popfile(void) {
  struct bufstack *bs = curbs;
  struct bufstack *prevbs;

  if(!bs) return 0;

  /* get rid of current entry */
  fclose(bs->f);
  yy_delete_buffer(bs->bs);

  /* switch back to previous */
  prevbs = bs->prev;
  free(bs);

  if(!prevbs) return 0;

  yy_switch_to_buffer(prevbs->bs);
  curbs = prevbs;
  yylineno = curbs->lineno;
  curfilename = curbs->filename;
  return 1; 
}

Flex&Bison

TinyC

本节参考 tinyc,flex 词法文件可以参考这里,Bison 文件也可以从官网查到

计算器

本小节代码参考自 《Flex and Bison》 第三章。一些 c 文件可以参考这里

Makefile

fb3-2: fb3-2.l fb3-2.y fb3-2.h fb3-2funcs.c
	bison -d fb3-2.y && \
	flex -o fb3-2.lex.c fb3-2.l && \
	gcc -g -o $@ fb3-2.tab.c fb3-2.lex.c fb3-2funcs.c -lm

clean:
	rm -f fb3-1 fb3-2 fb3-1.lex.c fb3-1.tab.h fb3-1.tab.c fb3-2.tab.c fb3-2.tab.h fb3-2.lex.c

Flex 文件

%option noyywrap nodefault yylineno

%{
#include "fb3-2.h"
#include "fb3-2.tab.h"
%}

EXP ([Ee][-+]?[0-9]+)

%%
"+" |
"-" |
"*" |
"/" |
"=" |
"|" |
"," |
";" |
"(" |
")"     { return yytext[0]; }

">"     { yylval.fn = 1; return CMP; }
"<"     { yylval.fn = 2; return CMP; }
"<>"    { yylval.fn = 3; return CMP; }
"=="    { yylval.fn = 4; return CMP; }
">="    { yylval.fn = 5; return CMP; }
"<="    { yylval.fn = 6; return CMP; }

"if"    { return IF; }
"then"  { return THEN; }
"else"  { return ELSE; }
"while" { return WHILE; }
"do"    { return DO; }
"let"   { return LET;}

"sqrt"  { yylval.fn = B_sqrt; return FUNC; }
"exp"   { yylval.fn = B_exp; return FUNC; }
"log"   { yylval.fn = B_log; return FUNC; }
"print" { yylval.fn = B_print; return FUNC; }

"debug"[0-9]+         { debug = atoi(&yytext[5]); printf("debug set to %d\n", debug); }

[a-zA-Z][a-zA-Z0-9]*  { yylval.s = lookup(yytext); return NAME; }

[0-9]+"."[0-9]*{EXP}? |
"."?[0-9]+{EXP}?      { yylval.d = atof(yytext); return NUMBER; }

"//".*  
[ \t]   /* ignore white space */ 

\\\n    printf("c> "); /* ignore line continuation */
"\n"    { return EOL; }

.       { yyerror("Mystery character %c\n", *yytext); }
%%

Bison 文件

%{
#include <stdio.h>
#include <stdlib.h>
#include "fb3-2.h"
int yylex ();
%}

%union {
  struct ast *a;
  double d;
  struct symbol *s;
  struct symlist *sl;
  int fn;
}

%token <d> NUMBER
%token <s> NAME
%token <fn> FUNC
%token EOL

%token IF THEN ELSE WHILE DO LET

%nonassoc <fn> CMP
%right '='
%left '+' '-'
%left '*' '/'
%nonassoc '|' UMINUS

%type <a> exp stmt list explist
%type <sl> symlist

%start calclist

%%

stmt: IF exp THEN list           { $$ = newflow('I', $2, $4, NULL); }
   | IF exp THEN list ELSE list  { $$ = newflow('I', $2, $4, $6); }
   | WHILE exp DO list           { $$ = newflow('W', $2, $4, NULL); }
   | exp
;

list: /* nothing */ { $$ = NULL; }
   | stmt ';' list  { 
                      if ($3 == NULL) 
                        $$ = $1;
                      else
                        $$ = newast('L', $1, $3);
                    }
   ;

exp: exp CMP exp          { $$ = newcmp($2, $1, $3); }
   | exp '+' exp          { $$ = newast('+', $1,$3); }
   | exp '-' exp          { $$ = newast('-', $1,$3);}
   | exp '*' exp          { $$ = newast('*', $1,$3); }
   | exp '/' exp          { $$ = newast('/', $1,$3); }
   | '|' exp              { $$ = newast('|', $2, NULL); }
   | '(' exp ')'          { $$ = $2; }
   | '-' exp %prec UMINUS { $$ = newast('M', $2, NULL); }
   | NUMBER               { $$ = newnum($1); }
   | FUNC '(' explist ')' { $$ = newfunc($1, $3); }
   | NAME                 { $$ = newref($1); }
   | NAME '=' exp         { $$ = newasgn($1, $3); }
   | NAME '(' explist ')' { $$ = newcall($1, $3); }
;

explist: exp
 | exp ',' explist        { $$ = newast('L', $1, $3); }
;

symlist: NAME             { $$ = newsymlist($1, NULL); }
 | NAME ',' symlist       { $$ = newsymlist($1, $3); }
;

calclist: /* nothing */
  | calclist stmt EOL     {
                            if(debug) dumpast($2, 0);
                            printf("= %4.4g\n> ", eval($2));
                            treefree($2);
                          }
  | calclist LET NAME '(' symlist ')' '=' list EOL {
                       dodef($3, $5, $8);
                       printf("Defined %s\n> ", $3->name); }

  | calclist error EOL { yyerrok; printf("> "); }
 ;
%%