Lex和Yacc應用方法(一).初識Lex

來源:互聯網
上載者:User

 Lex和Yacc應用方法(一).初識Lex

草木瓜  20070301

Lex(Lexical Analyzar 詞法分析產生器),Yacc(Yet Another Compiler Compiler
編譯器代碼產生器)是Unix下十分重要的詞法分析,文法分析的工具。經常用於語言分
析,公式編譯等廣泛領域。遺憾的是網上中文資料介紹不是過於簡單,就是跳躍太大,
入門參考意義並不大。本文通過循序漸進的例子,從0開始瞭解掌握Lex和Yacc的用法。

<本系列文章的地址:http://blog.csdn.net/liwei_cmg/category/207528.aspx>

一.Lex(Lexical Analyzar) 初步樣本

先看簡單的例子(註:本文所有執行個體皆在RetHat Linux下完成):

一個簡單的Lex檔案 exfirst.l 內容:

%{
#include "stdio.h"
%}
%%
[/n]                  ;
[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]);
%%

在命令列下執行命令flex解析,會自動產生lex.yy.c檔案:
[root@localhost liweitest]flex exfirst.l

進行編譯產生parser可執行程式:
[root@localhost liweitest]cc -o parser lex.yy.c -ll

[注意:如果不加-ll鏈結選項,cc編譯時間會出現以下錯誤,後面會進一步說明。]

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o(.text+0x18): In function `_start':
../sysdeps/i386/elf/start.S:77: undefined reference to `main'
/tmp/cciACkbX.o(.text+0x37b): In function `yylex':
: undefined reference to `yywrap'
/tmp/cciACkbX.o(.text+0xabd): In function `input':
: undefined reference to `yywrap'
collect2: ld returned 1 exit status

建立待解析的檔案 file.txt:

title
i=1+3.9;
a3=909/6
bcd=4%9-333

通過已產生的可執行程式,進行檔案解析。

[root@localhost liweitest]# ./parser < file.txt
Var     : title
Var     : i
Unknown : =
Int     : 1
Op      : +
Float   : 3.9
Unknown : ;
Var     : a3
Unknown : =
Int     : 909
Op      : /
Int     : 6
Var     : bcd
Unknown : =
Int     : 4
Op      : %
Int     : 9
Op      : -
Int     : 333

到此Lex用法會有個直觀的瞭解:

1.定義Lex描述檔案
2.通過lex,flex工具解析成lex.yy.c檔案
3.使用cc編譯lex.yy.c產生可執行程式

再來看一個比較完整的Lex描述檔案  exsec.l  :

%{
#include "stdio.h"
int linenum;
%}
%%
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]);
%%
showtitle()
{
printf("----- Lex Example -----/n");
}

int main()
{
  linenum=0;
  yylex(); /* 進行分析 */
  printf("/nLine Count: %d/n",linenum);
  return 0;
}
int yywrap()
{
return 1;
}

進行解析編譯:
[root@localhost liweitest]flex exsec.l
[root@localhost liweitest]cc -o parser lex.yy.c
[root@localhost liweitest]./parser < file.txt

----- Lex Example -----
Var     : i
Unknown : =
Int     : 1
Op      : +
Float   : 3.9
Unknown : ;
Var     : a3
Unknown : =
Int     : 909
Op      : /
Int     : 6
Var     : bcd
Unknown : =
Int     : 4
Op      : %
Int     : 9
Op      : -
Int     : 333

Line Count: 4

這裡就沒有加-ll選項,但是可以編譯通過。下面開始著重整理下Lex描述檔案.l。

二.Lex(Lexical Analyzar) 描述檔案的結構介紹

Lex工具是一種詞法剖析器產生器,它可以根據詞法規則說明書的要求來產生單詞識
別程式,由該程式識別出輸入文本中的各個單詞。 一般可以分為<定義部分><規則部
分><使用者子程式部分>。其中規則部分是必須的,定義和使用者子程式部分是任選的。 

(1)定義部分
定義部分起始於 %{ 符號,終止於 %} 符號,其間可以是包括include語句、聲明語句
在內的C語句。這部分跟普通C程式開頭沒什麼區別。
%{
#include "stdio.h"
int linenum;
%}

(2) 規則部分
規則部分起始於"%%"符號,終止於"%%"符號,其間則是詞法規則。詞法規則由模式和
動作兩部分組成。模式部分可以由任意的Regex組成,動作部分是由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]);
%%

A.規則部分的Regex

規則部分是Lex描述檔案中最為複雜的一部分,下面列出一些模式部分的Regex字
符含義:

A-Z, 0-9, a-z         構成模式部分的字元和數字。

-                     指定範圍。例如:a-z 指從 a 到 z 之間的所有字元。

/                     轉義元字元。用來覆蓋字元在此運算式中定義的特殊意義,
                      只取字元的本身。
                     
[]                    表示一個字元集合。匹配括弧內的任一字元。如果第一個字
                      符是^那麼它表示否定模式。例如: [abC] 匹配 a, b, 和C
                      的任何一個。
                     
^                     表示否定。

*                     匹配0個或者多個上述模式。
+                     匹配1個或者多個上述模式。
?                     匹配0個或1個上述模式。

$                     作為模式的最後一個字元時匹配一行的結尾。

{ }                   表示一個模式可能出現的次數。 例如: A{1,3} 表示 A 可
                     能出現1次或3次。[a-z]{5} 表示長度為5的,由a-z組成的
                     字元。此外,還可以表示預定義的變數。
                    
.                     匹配任一字元,除了 /n。

( )                   將一系列常規運算式分組。如:{Letter}({Letter}|{Digit})*
|                     運算式間的邏輯或。

"一些符號"            字元的字面含義。元字元具有。如:"*" 相當於 [/*]。

/                     向前匹配。如果在匹配的模式中的"/"後跟有後續運算式,
                      只匹配模版中"/"前面的部分。如:模式為 ABC/D 輸入 ABCD,
                      時ABC會匹配ABC/D,而D會匹配相應的模式。輸入ABCE的話,
                      ABCE就不會去匹配ABC/D。

B.規則部分的優先順序

規則部分具有優先順序的概念,先舉個簡單的例子:

%{
#include "stdio.h"
%}
%%
[/n]                  ;
A                     {printf("ONE/n");};
AA                    {printf("TWO/n");};
AAAA                  {printf("THREE/n");};
%%

此時,如果輸入內容:

[root@localhost liweitest]# cat file1.txt
AAAAAAA

[root@localhost liweitest]# ./parser < file1.txt
THREE
TWO
ONE

Lex分析詞法時,是逐個字元進行讀取,自上而下進行規則匹配的,讀取到第一個A字元
時,遍曆後發現三個規則皆匹配成功,Lex會繼續分析下去,讀至第五個字元時,發現
"AAAA"只有一個規則可用,即按行為進行處理,以此類推。可見Lex會選擇最長的字元
匹配規則。

如果將規則
AAAA                  {printf("THREE/n");};
改為
AAAAA                 {printf("THREE/n");};

 ./parser < file1.txt 輸出結果為:
THREE
TWO

再來一個特殊的例子:

%%
title                 showtitle();
[a-zA-Z][a-zA-Z0-9]*  printf("Var     : %s/n",yytext);
%%

並輸入title,Lex解析完後發現,仍然存在兩個規則,這時Lex只會選擇第一個規則,下面
的則被忽略的。這裡就體現了Lex的順序優先順序。把這個例子稍微改一下:

%%
[a-zA-Z][a-zA-Z0-9]*  printf("Var     : %s/n",yytext);
title                 showtitle();
%%

Lex編譯時間會提示:warning, rule cannot be matched.這時處理title字元時,匹配
到第一個規則後,第二個規則就無效了。

再把剛才第一個例子修改下,加深下印象!

%{
#include "stdio.h"
%}
%%
[/n]                  ;
A                     {printf("ONE/n");};
AA                    {printf("TWO/n");};
AAAA                  {printf("THREE/n");};
AAAA                  {printf("Cannot be executed!");};

./parser < file1.txt 顯示效果是一樣的,最後一項規則肯定是會忽略掉的。

 

C.規則部分的使用變數

且看下面樣本:

%{
#include "stdio.h"
int linenum;
%}
int                   [0-9]+
float                 [0-9]*/.[0-9]+
%%
{int}                 printf("Int     : %s/n",yytext);
{float}               printf("Float   : %s/n",yytext);
.                     printf("Unknown : %c/n",yytext[0]);
%%

在%}和%%之間,加入了一些類似變數的東西,注意是沒有;的,這表示int,float分
別代指特定的含義,在兩個%%之間,可以通過{int}{float}進行直接引用,簡化模
式定義。

(3) 使用者子程式部分

最後一個%%後面的內容是使用者子程式部分,可以包含用C語言編寫的子程式,而這些子
程式可以用在前面的動作中,這樣就可以達到簡化編程的目的。這裡需要注意的是,
當編譯時間不帶-ll選項時,是必須加入main函數和yywrap(yywrap將下後面說明)。如:

...
%%
showtitle()
{
printf("----- Lex Example -----/n");
}

int main()
{
  linenum=0;
  yylex(); /* 進行Lex分析 */
  printf("/nLine Count: %d/n",linenum);
  return 0;
}
int yywrap()
{
return 1;
}

三.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,就停止解
                   析。 因此它可以用來解析多個檔案。代碼可以寫在第三段,這
                   樣可以解析多個檔案。 方法是使用 yyin 檔案指標指向不同的
                   檔案,直到所有的檔案都被解析。最後,yywrap() 可以返回1
                   來表示解析的結束。
           
           
lex和flex都是解析Lex檔案的工具,用法相近,flex意為fast lexical analyzer generator。
可以看成lex的升級版本。

相關更多內容就需要參考flex的man手冊了,十分詳盡。

 

四.關於Lex的一些綜述

Lex其實就是詞法分析器,通過設定檔*.l,依據Regex逐字元去順序解析檔案,
並動態更新記憶體的資料解析狀態。不過Lex只有狀態和狀態轉換能力。因為它沒有堆棧,
它不適合用於剖析外殼結構。而yacc增加了一個堆棧,並且能夠輕易處理像括弧這樣的
結構。Lex善長於模式比對,如果有更多的運算要求就需要yacc了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.