天天看點

Lex/Yacc Lex結合Yacc

這次就來嘗試lex結合yacc。 簡單lex 先寫個簡單lex,如下:

%{
#include "name.tab.h"

#include <stdio.h>
#include <string.h>
extern char* yylval;
%}
char [A-Za-z]
num [0-9]
eq [=]
name {char}+
age {num}+
%%
{name} { yylval = strdup(yytext);
return NAME;}
{eq} {return EQ;}
{age} {yylval = strdup(yytext);
return AGE;}
%%
int yywrap()
{
return 1;
}
           

這個lex的意思是說, 1. 提取符合name規則的内容,也就是A-Za-z組成的單詞 2. 提取eq,也就是= 3. 提取age,也就是0-9組成的數字。 然後把提取出來的内容放到一個變量yylval裡面。 編譯使用名flex name.lex,産生一個.c檔案,就是lex.yy.c,如截圖所示:

Lex/Yacc Lex結合Yacc

簡單yacc例子 接下來先寫個yacc檔案,如:

%{
typedef char* string;
#define YYSTYPE string
#include "lex.yy.c"
%}
%token NAME EQ AGE
%%
file : record file
| record
;
record : NAME EQ AGE {
printf("%s is %s years old!!!", $1, $3); }
;
%%
int main()
{
yyparse();
return 0;
}
int yyerror(char* msg)
{
printf("Error : %s \n", msg);
}
           

這裡有幾個東西需要說明一下: 1. #define YYSTYPE string, YYSTYPE是指yylval變量的類型,yylval是用來在lex裡面把内容填充進去,然後yyac裡面通路的。 2. NAME EQ AGE這3個token也是在yacc裡面定義,然後lex裡面使用的。 3. record : NAME EQ AGE意思是說,符合NAME EQ AGE這種類型的内容提出來。看lex的定義可以知道,這種内容應該是 “NAME=AGE"的形式,NAME是指A-Za-z組成的單詞,AGE是0-9組成的數字。 建立一個yacc檔案name.y,把上面的内容丢進去,同樣編譯一下: bison -d name.y 看一下截圖:

Lex/Yacc Lex結合Yacc

多了兩個檔案,lex會include ”name.tab.h",因為lex需要使用yacc定義的一些東西。 接下來,編譯name.tab.c,如: gcc name.tab.c 看截圖:

Lex/Yacc Lex結合Yacc

多了一個a.out可執行檔案。 使用lex+yacc 直接跑一下吧: ./a.out 然後看一下截圖:

Lex/Yacc Lex結合Yacc

第一次輸入兩個符合的内容,被捕獲兩次,并且打出了log,沒問題。 第二次輸入一個内容,也沒問題。 第三次就有問題了,應輸入的name是kevin2016,而我們在lex裡面定義的name隻能是A-Za-z組成的單詞,是以報錯。 有關報錯,是我們在yacc裡面定義的yyerror()。 這個文章裡面的例子,隻要有一條不符合規則,就報錯了。 看如下:

Lex/Yacc Lex結合Yacc

第一次測試一條符合的都沒找到,第二條第一個還是符合的,第二個不符合,就報錯退出了。

總結: lex:就是用來提取符合一定規則的内容 yacc:分析lex提取過來的内容,然後做進一步操作。