本文已参与「新人创作礼」活动,一起开启掘金创作之路。
flex 和 bison
Flex是快速词法分析器(The Fast Lexical Analyzer)的缩写。
GNU bison是一个自由软件,用于自动生成语法分析器程序,实际上可用于所有常见的操作系统。Bison把LALR形式的上下文无关文法描述转换为可做语法分析的C或C++程序。
要说它们的父辈,那大抵是 lex 和 yacc
下面是它们的下载地址
| flex | bison |
|---|---|
| github.com/westes/flex | www.gnu.org/software/bi… |
当然,讲道理,在Linux,大多数情况下的系统是有这两个东西的
flex
flex 是 一个词法分析器
为什么要这个玩意呢? 用来干什么呢?
我们知道,在编译原理中,我们可以通过 RE 转化为一个 NFA
再由我们的 NFA 构造为 一个 DFA
在这些步骤中,我们也可以采取自己手动的方式去构建我们的 DFA
但是,往往更加常见的方法是,我们借助一些工具,来帮我们生成 DFA
flex就是一个可以用来帮我们把我们定义的规则,来转化为实际的代码逻辑,然后实现我们的分词功能
有什么作用呢 ?
举个例子,我们可以非常方便的拿它来写出一个 word count 的 程序
代码如下:
%option noyywrap
%{
#include <bits/stdc++.h>
int chars = 0;
int word = 0;
int line = 0;
%}
%%
[^ \n\t\f\v\r]+ {word++; chars += strlen(yytext);}
\n {chars++; line++;}
. {chars++;}
%%
int main() {
yylex();
printf("行%d 词%d 字符%d\n", line, word, chars);
return 0;
}
解释
我们的 flex的语法是兼容 lex语法的,它大抵把整体分为三部分
%{
// 定义段
%}
%%
// 规则段
%%
// 用户程序代码
int main() {
yylex();
}
在规则段中 我们采取 regex {oper} 的形式
regex代码的是我们的规则
括号里面则是对应这一种东西,我们的操作
如何 使用呢 ?
- 使用
flex命令生成lex.yy.c
> flex wc.l
- 使用
gccorg++编译lex.yy.c即可用
> g++ lex.yy.c
bison
Bison是一种通用解析器生成器,它将带注释的上下文无关文法转换为使用LALR(1)解析器表的确定性LR或广义LR(GLR)解析器 。
Bison与Yacc向上兼容:所有正确编写的Yacc语法都应与Bison一起使用,而无需进行任何更改。
bison 结构
- 定义部分
- %%
- 规则部分
- %%
- 用户附加的C语言部分
通常可以和flex配合进行使用
它通常支持语法形式的匹配
类似于这种
expr ::= expr + expr
| expr - expr
| expr * expr
| expr / expr
| number
依次来递归推到规则匹配
下面来看一个例子
四则算术的计算器(不带括号)
首先,我们要解析出 "+ - * /" 运算符,以及数字
然后进行模式匹配
具体代码如下:
calc.l
%{
#include <bits/stdc++.h>
#include "calc.tab.h"
%}
%option noyywrap
%%
"+" {return ADD;}
"-" {return SUB;}
"*" {return MUL;}
"/" {return DIV;}
[0-9]+ {yylval = atoi(yytext); return NUMBER;}
\n {return EOL;}
[ \t] {}
. {std::runtime_error("parser error");}
%%
calc.y
%{
extern int yylex(void);
extern "C" {
int yyparse();
void yyerror(char *s){}
}
#include <bits/stdc++.h>
%}
%token NUMBER
%token ADD SUB MUL DIV ABS
%token EOL
%%
list: /* null */
| list expr EOL { std::cout << "val-> " << $2 << "\n";}
;
expr: factor {$$ = $1;}
| expr ADD factor {$$ = $1 + $3;}
| expr SUB factor {$$ = $1 - $3;}
;
factor: term {$$ = $1;}
| factor MUL term {$$ = $1 * $3;}
| factor DIV term {
if ($3) $$ = $1 / $3;
else {
std::runtime_error("error");
}
}
;
term: NUMBER {$$ = $1;}
;
%%
int main() {
yyparse();
std::cout << "ok" << std::endl;
return 0;
}
void yyerror(const char* str) {
fprintf(stderr, "error %s", str);
}
我们把规则分成了三部分
list -> ϵ | list expr
expr -> factor | expr ADD factor | expr SUB factor
factor -> term | factor MUl term | factor DIV term
term -> number
然后把 加减和乘除分开,因为优先级不同
然后,我们就实现了一个简单的四则运算解析器