创建一门新的编程语言-Flex&Bison教程-(1)-初探

之前一直想钻研一下这方面的东西,于是便花了一些精力研究这些相关的工具和技术,现在我把我总结的一些经验分享给大家
Flex & Bison 是比较有名而且易用的parser组合,今后的工作大体都用到了这两个工具。他们可以在gnu官网下载,windows用户要么去下载winflexbison,要么自己编译一份(推荐),这里不详细介绍获取方法
本教程所有代码都在Android平台和windows平台的g++编译通过(可能不支持vs,untested)

初探 1

什么是Flex和Bison?

Flex是一个词法分析器,是unix lex的gnu克隆,作用是把"词"抽象成符号,供程序识别
Bison则是一个文法分析器(也是unix yacc的gnu克隆),语法就是在这里定义,是语言设计的核心
这两巨头不但可以应付复杂的语法处理,也可以拿来制作简单的分析器,如配置文件等

初探Flex
习惯上,一般把flex input文件命名为xxxll.l
以下是一个简单的例子test1ll.l:


%{
  #include 
  using namespace std;
%}
%%
[0-9]+        { cout << "Number "; }
[a-zA-Z]+     { cout << "Word ";   }
[ \t]         ; //忽略空格
%%


我们来看一看这些东西
开头的{% %}包着的东西是辅助代码段
两个%%之间的则是一些flex所认识的定义:左边为匹配正则(这里假设你已经学习过正则),右边也是当匹配后应该执行的代码
最后的%%后面还可以写一些辅助代码,现在我们还不需要
我们这样用这个文件:

flex -otest1ll.c test1ll.l
g++ test1ll.c -otest1 -lfl
./test1


(加上-lfl参数是因为我们没有main等函数)
现在输入几个数字或者单词试试?是不是能准确处理了?
我们来考虑输入的这串内容:123 abc jkl 134
程序的输出如下:Number Word Word Number
看出什么了吗?假设我们有一种语言的语法是两个数字中间夹两个单词?实际上语法就是一些符号按顺序的组合!但flex能帮我们的只到这里了,现在你更明白flex的作用了吗?
flex还有很多功能,今后会慢慢补充


Bison初探
对比flex,我们一般命名bison input文件为xxxyy.y
例子test1yy.y:

%{
  #include 
  using namespace std;
  int yylex();  //只是一个声明
  int yyerror(const char *); //必须要有
%}
%token  Number
%token  Word
%union {
  int iv;
  char *sv;
}
%%


main : main Number Word Word Number '\n'   { cout << $2 + $5 << $3 << $4 << endl ; free($3) ; free($4) ; //因为我们使用strdup }
       |    //空
       ;
  
%%


int yyerror(const char *emseg)
{
  cout << "Error: " << emseg << endl;
}


int main()
{
  yyparse();
}


格式和flex的差不多就不再啰嗦了
在%union里我们定义了两个类型,数字和字符串
在%token处,我们定义两个符号,并且用我们定义的两个类型来声明他们,这里与flex有关联,等下再提
重点在两个%%之间的内容,如果看过 编译原理 的读者可能很快知道这是什么,没看过也没关系在这里我们定义了一条BNF范式,什么是BNF范式呢?
举个例子:
exp : exp + exp
    | exp - exp
    | int       //一个数字
它的意思是,表达式exp有三种可能可以是两个exp表达式相加或者相减,也可能就是一个数字,是一个递归,这样,我们就很清晰地给 5 5+3 5+3-6+1 这样的算式提供了规则

上面,我们给两个数字夹两个单词的语法定义了规则,输出则是两个数字相加然后再连接两个单词

在bison,BNF范式的写法是

表达式 : 表达式1 表达式2 ... {当匹配后执行的代码}
          | 表达式 .... { ... }
          | ....           { ... }
          ....
          ;


变量名$N代表了在这行的第N个符号的值
为了让它工作,我们还需要稍微改一下我们的text1ll.l文件:

%{
  #include 
  #include "test1yy.h"  //由bison生成
  extern int yyerror(const char *);
  using namespace std;
%}
%%
[0-9]+        { yylval.iv = strtol(yytext,0,10); return Number; }
[a-zA-Z]+     { yylval.sv = strdup(yytext); return Word;   }
[ \t]         ; //忽略空格  
\n            { return *yytext; } //直接返回换行符作为符号给bison
%%
int yywrap()
{
  return 1;
}



yylval用来传递当前符号的值到bison,yytext则是flex读取到的东西


好,让我们生成成品

bison -d -otest1yy.c test1yy.y
flex -otest1ll.c test1ll.l
g++ test1*.c -otest1
./test1

因为我们已经自己实现了yywrap和main函数,我们就不需要-lfl参数了
尝试输入一些东西
输入 12 ty uy 45
输出 57tyuy
输入 16 45 2 3
输出 Error: synax error
是不是能按预期进行了?现在你知道flex和bison配合的意义了吗?


归纳:flex提供符号和值,bison进行相应文法处理,一般使用顺序为先写好bison规则,再写flex规则


计算器的诞生
我们先从简单的例子入手,之后本教程主要围绕一个计算器的开发的例子,从一个简易的加减乘除计算器到一个能定义函数或甚至循环表达式的计算器


(1)初探 结束

你可能感兴趣的:(Flex&Bison教程)