LEX 簡單入門

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章