Lex詞法分析器

LEX/FLEX詞法分析器

CONTENTS:linux

[TOC]git

這篇文章的內容包括:正則表達式

  • lex語法格式shell

  • linux下flex的安裝和使用express

  • flex實例ide

  • flex源代碼的編譯和使用函數


Lex/Flex詞法分析器

Lex是LEXical compiler的縮寫,是Unix環境下很是著名的工具,主要功能是生成一個詞法分析器(scanner)的C源碼,描述規則採用正則表達式(regular expression)。描述詞法分析器的文件*.l,通過lex編譯後,生成一個lex.yy.c 的文件,而後由C編譯器編譯生成一個詞法分析器。詞法分析器,簡單來講,其任務就是將輸入的各類符號,轉化成相應的標識符(token),轉化後的標識符 很容易被後續階段處理。 —— [ 百度百科 ] 工具

Flex的安裝和使用

在使用apt軟件包管理器linux系統上咱們能夠很是方便地安裝並使用flex。在終端中輸入如下代碼安裝flex:flex

$> sudo apt-get install flex

flex代碼的源文件每每是以.l爲後綴名的。
.l文件經過如下命令編譯(以文件名爲scanner.l爲例):this

$> flex scanner.l

編譯後在源代碼相同目錄下會生成一個lex.yy.c,這就是生成的可以執行上述scanner.l功能的c語言代碼。使用gcc編譯便可生成詞法分析程序1

void yywrap() { return 1; }

$> gcc lex.yy.c -o scanner

而後將須要分析的文件(以input.txt爲例)做爲參數傳遞給scanner執行分析:

$> ./scanner input.txt

Lex語法格式

flex的語法被分爲三個部分:

{definitions}
%%
{rules}
%%
{user subroutines}

definitions:

LABEL REGULAR_EXPRESSION

LABEL是這裏類字符串的名稱,REGULAR_EXPRESSION則是匹配這種字符串的正則表達式。正則表達式的語法主要包括:

符號 含義
\
[] 括號中的字符取其一
\- a-z表示ascii碼中介於a-z包括a.z的字符
\\ 轉義(flex不能識別除字母外的字符)
* 0或多個字符
? 0或1個字符
+ 1或多個字符
^ 除此以外的其他字符
. 除\n外的全部字符,等價於^\n

示例:

1. INT [1-9][0-9]*|[0]    /*整數類型,0或不以0開頭的由0-9組成的字符串*/
2. FLOAT [0-9]*[.][0-9]+([eE][+-]?[0-9]*|[0])?f?    /*浮點數格式*/
3. LP \(    /*一個左圓括號*/

注:用%{ %}括起來的語句將被徹底寫入編譯後的c語言文件中。

例如
`%{

#include <stdio.h>
int num_id = 0;

%}`

rules:

規則部分的語法以下:

{LABEL1} |
{LABLE2} |
...
{ 
/*TODO*/
}

TODO部分是告訴編譯器在匹配到字符串以後程序須要作些什麼。
例如在匹配到整數後打印這個整數:

{INT} {
    printf("Pick up an integer, value is %d", atoi(yytext));
    printf("Pick up an integer, value is %s", yytext);
}

其中atoi()函數將字符串轉換爲整數。

user subroutines

此處主要是放置用戶須要執行的c語言代碼。他們會被原封不動地加入到lex.yy.c文件的末尾。
這裏通常用來存放main函數,詳細會在後面說明。

FLEX實例

下面經過一個實例來具體展現flex的使用方式,主要功能是掃描並匹配文件中的字符串,並回顯其類型和內容,代碼以下:

/************************
 * scanner.l
 * @author mist
 * 2015-9-21 23:08
 ************************/
%{
#include "stdio.h"
#include "stdlib.h"
%}

INT_DEX [1-9][0-9]*|[0]
INT_HEX [0][Xx]([1-9][0-9]*|[0])
INT_OCT [0][0-7]
FLOAT [0-9]*[.][0-9]+([eE][+-]?[0-9]*|[0])?f?
SEMI [;]
COMMA [,]
ASSIGNOP [=]
RELOP [>]|[<]|[>][=]|[<][=]|[=][=]|[!][=](^[=])
PLUS [+]
MINUS [-]
STAR [*]
DIV [/]
AND [&][&]
OR [|][|]
DOT [.]
NOT [!]
TYPE int|float
LP \(
RP \)
LB \[
RB \]
LC \{
RC \}
STRUCT struct
RETURN return
IF if
ELSE else 
WHILE while
SPACE [ \n\t]
ID [a-zA-Z_][a-zA-Z_0-9]*
/*end of definition*/

%%
{SEMI} {
    printf("get semmi : %s\n", yytext);

}

{COMMA} {
    printf("get comma : %s\n", yytext);
}
{ASSIGNOP} {
    printf("get assignop : %s\n", yytext);
}

{INT_DEX} |
{INT_HEX} |
{INT_OCT} {
    printf("get an integer: %s\n", yytext);
}

{FLOAT} {
    printf("get a float: %s\n", yytext);
}

{PLUS} | 
{MINUS} |
{DIV} |
{STAR} {
    printf("get an operator: %s\n", yytext);
}

{RELOP} {
    printf("get a relop: %s\n", yytext);
}

{AND} |
{OR} |
{NOT} {
    printf("get a logic operator: %s\n", yytext);
}

{DOT} {
    printf("get a dot: %s\n", yytext);
}
{STRUCT} |
{RETURN} |
{IF} |
{ELSE} |
{WHILE} {
    printf("get keyword: %s\n", yytext);
}

{TYPE} {
    printf("get type: %s\n", yytext);
}

{LP} |
{RP} |
{LB} |
{RB} |
{LC} |
{RC} {
    printf("get brackets : %s\n", yytext);
}

{SPACE} |
. {
/*ABANDON THESE CHARACTORS*/
}

{ID} {
    printf("get an ID: %s\n", yytext);
}
%%
int yywrap() {
  return 1;
}

int main(int argc, char** argv) {
   if (argc > 1) {
       if (!(yyin = fopen(argv[1], "r"))) {   
           perror(argv[1]);
           return 1;
       }
   }
   while (yylex());
   return 0;

咱們須要爲生成的分析程序編寫main函數。首先須要經過yyin來獲取指向被分析文件的文件FILE指針,通常文件的路徑經過控制檯的第二個參數得到。分析部分的實體在函數yylex()中。
yywrap()用於判斷是否已經掃描完了全部的文件。若是它在最後一個文件的末尾被調用,則返回值爲1。此時程序將中止分析,能夠用來掃描多個文件。

輸入文本:
`int float {}()[] 0
0x0 0x123
123.5
.3e-10f
= >= || && ! ; ,
this_is_an_id
id123
if then else
`
輸出:

get type: int
get type: float
get brackets : {
get brackets : }
get brackets : (
get brackets : )
get brackets : [
get brackets : ]
get an integer: 0
get an integer: 0x0
get an integer: 0x123
get a float: 123.5
get a float: .3e-10f
get assignop : =
get a relop: >=
get a logic operator: ||
get a logic operator: &&
get a logic operator: !
get semmi : ;
get comma : ,
get an ID: this_is_an_id
get an ID: id123
get keyword: if
get an ID: then
get keyword: else

另外附上詞法要求:
INT  / A sequence of digits without spaces1 /
FLOAT  /* A real number consisting of digits and one decimal point. The deci-
mal point must be surrounded by at least one digit2 */
ID  /* A character string consisting of 52 upper- or lower-case alphabetic, 10
numeric and one underscore characters. Besides, an identifier must not start
with a digit3 */
SEMI  ;
COMMA  ,
ASSIGNOP  =
RELOP  > | < | >= | <= | == | !=
PLUS  +
MINUS  -
STAR  *
DIV  /
AND  &&
OR  ||
DOT  .
NOT  !
TYPE  int | float
LP  (
RP  )
LB  [
RB  ]
LC  {
RC  }
STRUCT  struct
RETURN  return
IF  if
ELSE  else
WHILE  while

1) 詞法單元INT表示的是全部(無符號)整型常數。一個十進制整數由0~9十個數字組
成,數字與數字中間沒有如空格之類的分隔符。除「0」以外,十進制整數的首位數字
不爲0。例如,下面幾個串都表示十進制整數:0、23四、10000。爲方便起見,你能夠
假設(或者只接受)輸入的整數都在32bits位以內。

2) 整型常數還能夠以八進制或十六進制的形式出現。八進制整數由0~7八個數字組成並以
數字0開頭,十六進制整數由0~九、A~F(或a~f)十六個數字組成並以0x或者0X開頭。
例如,0237(表示十進制的159)、0xFF32(表示十進制的65330)。

3) 詞法單元FLOAT表示的是全部(無符號)浮點型常數。一個浮點數由一串數字與一個
小數點組成,小數點的先後必須有數字出現。例如,下面幾個串都是浮點數:0.七、
12.4三、9.00。爲方便起見,你能夠假設(或者只接受)輸入的浮點數都符合IEEE754單
精度標準(即均可以轉換成C語言中的float類型)。

4) 浮點型常數還能夠以指數形式(即科學記數法)表示。指數形式的浮點數必須包括基
數、指數符號和指數三個部分,且三部分依次出現。基數部分由一串數字(0~9)和一
個小數點組成,小數點能夠出如今數字串的任何位置;指數符號爲「E」或「e」;指
數部分由可帶「+」或「」(也可不帶)的一串數字(0~9)組成,「+」或「」(如
果有)必須出如今數字串以前。例如01.23E12(表示1.23  1012)、43.e-4(表示43.0 
10-4)、.5E03(表示0.5  103)。

5) 詞法單元ID表示的是除去保留字之外的全部標識符。標識符能夠由大小寫字母、數字
以及下劃線組成,但必須以字母或者下劃線開頭。爲方便起見,你能夠假設(或者只
接受)標識符的長度小於32個字符。

6) 除了INT、FLOAT和ID這三個詞法單元之外,其它產生式中箭頭右邊都表示具體的字
符串。例如,產生式TYPE  int | float表示:輸入文件中的字符串「int」和「float」都
將被識別爲詞法單元TYPE。
2.2
High-level Definitions


  1. 生成詞法分析程序 可能會發生yywrap未定義的錯誤。yywrap必須由用戶親自編寫,通常按以下形式便可
相關文章
相關標籤/搜索