lex yacc 入門教程
宣告:原創作品,轉載註明出處http://www.cnblogs.com/vestinfo/
一、簡介
推薦書籍《flex&bison》.
在UNIX下是flex和bison.網上介紹很多,大部分是寫給懂的人看的,初學者一頭霧水。這樣來理解lex和yacc可能容易些:在linux下,有很多系統配置檔案,一些linux下的軟體也有配置檔案,那麼程式是如何讀取配置檔案中的資訊的呢?
首先用到lex詞法分析器,讀取配置檔案中的關鍵詞(後面說到的token標記其實可看做關鍵詞)。然後把關鍵詞
遞交給yacc,yacc對一些關鍵詞進行匹配,看是否符合一定語法邏輯,如果符合就進行相應動作。
上面舉得例子是分析配置檔案內容的,當然可分析其他檔案內容。
二、一個簡單的lex檔案例子
1、來看flex&bison這本書開篇給出的例子:輸入幾行字串,輸出行數,單詞數和字元的個數。
關於yylex即lex中相關變數系列3文章介紹。
/* just like Unix wc */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+ { words++; chars += strlen(yytext); }
\n { chars++; lines++; }
. { chars++; }
%%
main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
2、按照下面過程編譯。
#flex test.l
#gcc lex.yy.c –lfl
#./a.out
3、分析這個簡單的lex檔案:
(1)%%把檔案分為3段,第一段是c和lex的全域性宣告,第二段是規則段,第三段是c程式碼。
(2)第一段的c程式碼要用%{和%}括起來,第三段的c程式碼不用。
(3)第二段規則段,[a-zA-Z]+ \n . 是正規表示式,{}內的是c編寫的動作。
關於正規表示式系列3文章介紹。
4、如果不用-lfl選項,程式碼可以為下面這樣(具體原因見lex的庫和函式分析):
int chars = 0;
int words = 0;
int lines = 0;
int yywrap();
%}
%%
[a-zA-Z]+ { words++; chars += strlen(yytext); }
\n { chars++; lines++; }
. { chars++; }
%%
main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
int yywrap()
{
return 1;
}
三、修改第一個例子,將正規表示式放在全域性宣告中%{
int chars = 0;
int words = 0;
int lines = 0;
%}
mywords [a-zA-Z]+
mylines \n
mychars .
%%
{mywords} { words++; chars += strlen(yytext); }
{mylines} { chars++; lines++; }
{mychars} { chars++; }
%%
main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
編譯一同上。
四、The Scanner as Coroutine(協同程式)
即怎樣將掃描到的標記給其他程式使用,下面的例子,希望掃描到+ 或 -時做一個特殊輸出。
當呼叫yylex時,若掃描到return對應的標記時,yylex返回,且值就為return後的值;
若沒掃描到return對應的標記,yylex繼續執行,不返回。
下次呼叫自動從前一次的掃描位置處開始。
%{
enum yytokentype {
ADD = 259,
SUB = 260,
};
%}
myadd "+"
mysub "-"
myother .
%%
{myadd} { return ADD; }
{mysub} { return SUB; }
{myother} { printf("Mystery character\n"); }
%%
main(int argc, char **argv)
{
int tok;
while(tok = yylex()) { //yylex的返回值只能是ADD 或 SUB.
if(tok == ADD || tok == SUB) {printf("meet + or -\n");}
else {printf("this else statement will not be printed, \
because if yylex return,the retrun value must be ADD or SUB.");}
}
}
五、yacc —— unix下是bison
1、yacc語法規則部分和BNF類同,先來看BNF巴克斯正規化。
(1)<> 內包含的內容為必選項;
(2)[] 內的包含的內容為可選項;
(3){ } 內包含的為可重複0至無數次的項;
(4) | 表示在其左右兩邊任選一項,相當於"OR"的意思;
(5)::= 是“被定義為”的意思;
(6)雙引號“”內的內容代表這些字元本身;而double _quote用來表示雙引號。
(7)BNF正規化舉例,下面的例子用來定義java中的for語句:
FOR_STATEMENT ::=
"for" "(" ( variable_declaration |
( expression ";" ) | ";" )
[ expression ] ";"
[ expression ]
")" statement
2、yacc語法。
result: components { /*
action to be taken in C */ }
;
(1)components是根據規則放在一起的終端和非終端符號,後面是{}括起來的執行的動作。
3、語法例子。
param : NAME EQ NAME {
printf("\tName:%s\tValue(name):%s\n", $1,$3); }
| NAME EQ VALUE {
printf("\tName:%s\tValue(value):%s\n",$1,$3);}
;
simple_sentence: subject verb object
| subject verb object prep_phrase ;
subject: NOUN
| PRONOUN
| ADJECTIVE subject ;
verb: VERB
| ADVERB VERB
| verb VERB ;
object: NOUN
| ADJECTIVE object ;
prep_phrase: PREPOSITION NOUN ;
(1)理解 | 的意思,|表示左右兩邊任選一項,如| subject verb object prep_phrase ;中|的左邊為空,
所以該句表示匹配空或者subject verb object prep_phrase ;而上面還有一句subject verb object ,
所以
simple_sentence: subject verb object
| subject verb object prep_phrase ;
的意思是匹配subject verb object 或 subject verb object prep_phrase ;
六、flex和bison相結合。
test.l
%{
#include "test.tab.h"
#include <stdio.h>
#include <stdlib.h>
%}
%%
a {return A_STATE;}
b {return B_STATE;}
c {return C_STATE;}
not {return NOT;}
%%
test.y
%{
#include <stdio.h>
#include <stdlib.h>
%}
%token A_STATE B_STATE C_STATE NOT
%%
program :
A_STATE B_STATE {
printf("1");
}
c_state_not_token {
printf("2");
}
| NOT {
printf("3");
}
c_state_not_token : C_STATE {}
%%
yyerror(const char *s)
{
fprintf(stderr, "error: %s\n", s);
}
int main()
{
yyparse();
return 0;
}
編譯:
七、檔案資訊分析。
tset.l分析test.txt檔案中的關鍵詞(即test.y中的token標記),遇到token返回給test.y,test.y判斷
是否符合一定語法,符合則進行相應動作。
test.l
%{
#include "test.tab.h"
#include <stdio.h>
#include <string.h>
%}
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;
}
test.y%{
#include <stdio.h>
#include <stdlib.h>
typedef char* string;
#define YYSTYPE string
%}
%token NAME EQ AGE
%%
file : record file
| record
;
record : NAME EQ AGE {
printf("%s is %s years old!!!\n", $1, $3); }
;
%%
int main()
{
extern FILE* yyin;
if(!(yyin = fopen("test.txt", "r")))
{
perror("cannot open parsefile:");
return -1;
}
yyparse();
fclose(yyin);
return 0;
}
int yyerror(char *msg)
{
printf("Error encountered: %s \n", msg);
}
test.txtZhangSan=23
LiSi=34
WangWu=43
編譯
token定義的標記的型別及union的使用。
token定義的標記的型別預設為int 且 預設賦值從258開始。如上面的例子,在生成的標頭檔案
test.tab.h中有如下預編譯,
/* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NAME = 258, EQ = 259, AGE = 260 }; #endif
如果想將token標記定義為其他型別呢?首先將型別定義在聯合中,
%union { char *str; int num; struct { int num1; int num2; } dnum; }
然後,如下定義,
%token <str> K_HOST K_ERROR %token <str> WORD PATH STRING %token <num> NUM %token <dnum> DNUM
補充 :$$ $1 $2….
Each symbol in a bison rule has a value; the value of the target symbol (the one to the
left of the colon) is called $$ in the action code, and the values on the right are numbered
$1, $2, and so forth, up to the number of symbols in the rule.
$$——表示冒號的左邊符號;$1——冒號右邊第一個;$2——冒號右邊第二個,依此類推。
如record : NAME EQ AGE { printf("%s is %s years old!!!\n", $1, $3); } ;
匹配NAME EQ AGE後,$1即NAME所表示的內容,$3即AGE所表示的內容。
lex yacc 入門教程(3)正規表示式和lex變數及函式
參考:http://www.ibm.com/developerworks/cn/linux/sdk/lex/#resources
相關文章
- lex yacc 學習
- C編譯器LEX 和 YACC輸入原始檔。 (轉)編譯
- Pisa-Proxy SQL 解析之 Lex & YaccSQL
- LEX與YACC學習資料連結
- 從lex&yacc說到編譯器(2.flex的使用) (轉)編譯Flex
- Iptables入門教程
- vue入門教程Vue
- Redux入門教程Redux
- Electron入門教程
- Aseprite入門教程
- Twisted 入門 教程
- flask入門教程Flask
- Maven入門教程Maven
- awk 入門教程
- HBase入門教程
- Jmeter入門教程JMeter
- Elasticsearch入門教程Elasticsearch
- SnapKit入門教程APK
- Tmux入門教程UX
- Docker 入門教程Docker
- Thrift 入門教程
- Vuex入門教程Vue
- CMake入門教程
- RabbitMQ入門教程MQ
- mybatis入門教程MyBatis
- Git入門教程Git
- docker入門教程Docker
- tcpdump教程入門TCP
- Circos入門教程
- Vim入門教程
- OpenStack 入門教程
- React 入門教程React
- Webpack 入門教程Web
- Guzzle 入門教程
- ZooKeeper 入門教程
- Ceph入門教程
- Dart 入門教程Dart
- 新手入門,webpack入門詳細教程Web