Flex&Bison
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("> "); }
;
%%