在开始编写xml解析器之前我们先来简单介绍一下lex ,yacc。
Lex:
Lex工具是一种词法分析程序生成器,它可以根据词法规则说明书的要求来生成单词识
别程序,由该程序识别出输入文本中的各个单词。一般可以分为<定义部分><规则部
分><用户子程序部分>。其中规则部分是必须的,定义和用户子程序部分是任选的。
(1)定义部分
定义部分起始于 %{ 符号,终止于 %} 符号,其间可以是包括include语句、声明语句
在内的C语句。这部分跟普通C程序开头没什么区别。
%{
#include "stdio.h"
int linenum;
%}
(2) 规则部分
规则部分起始于"%%"符号,终止于"%%"符号,其间则是词法规则。词法规则由模式和
动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组
成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单
词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容。
类似yytext这些预定义的变量函数会随着后面内容展开一一介绍。动作部分如果有多
行执行语句,也可以用{}括起来。
%%
title showtitle();
[\n] linenum++;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
lex规则部分具有优先级的概念:
1 Lex会选择最长的字符匹配规则。
2 当存在多个规则同事满足时,这时Lex只会选择第一个规则
(3) 用户子程序部分
最后一个%%后面的内容是用户子程序部分,可以包含用C语言编写的子程序,而这些子
程序可以用在前面的动作中,这样就可以达到简化编程的目的。
Lex(Lexical Analyzar) 一些的内部变量和函数
内部预定义变量:
yytext char * 当前匹配的字符串
yyleng int 当前匹配的字符串长度
yyin FILE * lex当前的解析文件,默认为标准输出
yyout FILE * lex解析后的输出文件,默认为标准输入
yylineno int 当前的行数信息
内部预定义宏:
ECHO #define ECHO fwrite(yytext, yyleng, 1, yyout) 也是未匹配字符的默认动作
内部预定义的函数:
int yylex(void) 调用Lex进行词法分析
int yywrap(void) 在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析。因此它可以用来解析多个文件。
注意lex库提供一些默认的函数,例如main,yywrap函数等。 当编译时不带-ll选项时,必须自行实现main函数和yywrap等函数。
在linux中假设现在有lex规则文件a.l。运行lex a.l 便自动生成lex.yy.c
cc -o test lex.yy.c -ll 便生成了可执行程序。注意当编译时不带-ll选项时,必须自行实现main函数和yywrap等函数。
Yacc
如同 Lex 一样, 一个 Yacc 程序也用双百分号分为三段。 它们是:声明、语法规则和 C 代码
用 Yacc 来创建一个编译器包括四个步骤:
1 编写一个 .y 的语法文件(同时说明 C 在这里要进行的动作)。
2 编写一个词法分析器来处理输入并将标记传递给解析器。 这可以使用 Lex 来完成。
3 编写一个函数,通过调用 yyparse() 来开始解析。
4 编写错误处理例程(如 yyerror())。
5 通过yacc工具对.y生代码并编译它。
yacc 必须与lex一起使用,而lex可以单独使用。因为yacc需要使用lex来返回标记。
在linux中假设现在有lex规则文件a.l, a.y。
运行lex a.l 便自动生成lex.yy.c
运行 yacc -d a.y 便自动生成了y.tab.c y.tab.h -d表示生成头文件
cc -o test lex.yy.c y.tab.c -ll -ld 便生成了可执行程序
同样 yacc库也提供了一些默认的函数 例如main,yyerror等,如果不连接yacc库,需要自行实现这些函数。
yacc的一些细节
1、嵌入式动作在规则内变成一个符号,所以它的值($$)像任何其他的符号一样对于规则末端的动作都是可以用的。
如:
thing: A {$$=17;} B C
{printf(%d"",$2);}
在使用嵌入式动作时,如果正在使用“%union”和有类型的符号值,则在引用动作值时就得将值放入尖括号内,例如将它放入嵌入式动作时的$<type>$,以及在规则末端动作中引用它的$&l