youtube : https://www.youtube.com/watch?v=54bo1qaHAfk&list=PLkB3phqR3X43IRqPT0t1iBfmT5bvn198Z
參考網站 :https://www.ibm.com/developerworks/cn/linux/sdk/lex/
強烈推薦文章:如何愉快地寫個小parser (大神寫的真的好,和下面的處理SQL配置文件剛好是一樣的思想)
這是一個youtube上介紹lex/yacc的視頻,講的超級的好,對於CS143的任務1,我又有了信心。這一篇文章是上面視頻的總結和記錄,我儘量寫得詳細點,但是強烈推薦去看看。
如果你給我說不能翻牆,那我只能說,國內搜索引擎都什麼呀!有個B站UP主叫LEX,取名字之前不先搜搜嗎?不過主要還是搜索引擎做的不好,加上國內對於計算機底層方面沒有計算機應用方面火。所以還是推薦大家試着科學上網。
然後就是這個是linux/unix上的,雙系統走一波。
還有需要就是這個不單單對編譯原理有用,視頻裏面舉例判斷SQL配置文件的程序相當漂亮。
簡單介紹詞法分析
什麼是LEX?
lex is a scanner generator.
- 輸入: 正則表達式和用c語言寫的相關聯的行爲(actions)
- 輸出: a table-driven scanner (默認文件名爲lex.yy.c)
flex 是原始UNIX上lex工具的一個開源實現。
lex 三個組成部分:
FRIST PART // 可選的
%%
pattern action // 模式 行爲
....
%%
THIRD PART // 可選的
LEX如何運行?
lex 示例(文件名 ex1.l,傳說中的hello world):
%%
"hello world" printf("GOODBYE\n"); // 將匹配"hello world",並執行printf
. ; // 匹配任意字符,忽略其他情況
%%
測試上面的代碼:
% lex ex1.l // 處理ex1.l生成lex.yy.c
% cc lex.yy.c -ll // 編譯lex.yy.c,-ll搶佔main函數
% ./a.out // gcc/cc默認輸出可執行文件爲a.out
hello world
GOODBYE
%
lex 常見正則表達式
lex 如何處理文本配置文件以及集成到c語言?
假如我們有一個配置文件(config.in):
db_type : myset
db_name : testdate
db_table_prefix : test_
db_port : 1099
首先定義scanner.h
用來爲每個token進行標記
#define TYPE 1
#define NAME 2
#define TABLE_PREFIX 3
#define PORT 4
#define COLON 5 // 冒號
#define IDENTIFIER 6
#define INTEGER 7
然後定義scanner.l
,這裏有一個坑,%{ %}
不要寫成了%{ }%
。
%{
#include "scanner.h"
%}
%%
: return COLON;
"db_type" return TYPE;
"db_name" return NAME;
"db_table_prefix" return TABLE_PREFIX;
"db_port" return PORT;
[a-zA-Z][_a-zA-Z0-9]* return IDENTIFIER;
[1-9][0-9]* return INTEGER;
[ \t\n] ;
. printf("unexpected character\n");
%%
/*
注意這一段必須包括 yywrap() 函數。
Lex 有一套可供使用的函數和變量。 其中之一就是 yywrap。
這一函數在文件(或輸入)的末尾調用。 如果函數的返回值是1,就停止解析。
*/
int yywrap(void) {
return 1;
}
然後lex scanner.l
生成lex.yy.c
文件
然後編寫scanner.c
#include <stdio.h>
#include "scanner.h"
extern int yylex(); // 這一函數開始分析。 它由 Lex 自動生成。
extern int yylineno; // 提供當前的行數信息。 (lexer不一定支持。)
extern char* yytext; // 匹配模式的文本存儲在這一變量中(char*)。
char *names[] = {NULL,"db_type","db_name","db_table_prefix","db_port"};
int main(void) {
int ntoken, vtoken; // name & value
ntoken = yylex();
while(ntoken) { // 結束時返回0
printf("%d\n",ntoken);
if(yylex() != COLON) { // 下一個不爲分號
printf("Syntax error in line %d, Except a ':' but found %s\n",yylineno,yytext);
return 1;
}
vtoken = yylex();
switch(vtoken){
case TYPE:
case NAME:
case TABLE_PREFIX:
if(vtoken != IDENTIFIER) {
printf("Syntax error in line %d, Except an identifier but found %s\n",yylineno,yytext);
return 1;
}
printf("%s is set to %s\n",names[ntoken],yytext);
break;
case PORT:
if(vtoken != INTEGER) {
printf("Syntax error in line %d, Except an identifier but found %s\n",yylineno,yytext);
return 1;
}
printf("%s is set to %s\n",names[ntoken],yytext);
break;
default:
printf("Syntax error in line %d\n",yylineno);
}
ntoken = yylex();
}
return 0;
}
然後gcc scanner.c lex.yy.c -o scanner
運行代碼./scanner < config.in
最終結果:
1
db_type is set to myset
2
db_name is set to testdate
3
db_table_prefix is set to test_
4
db_port is set to 1099