CONTENTS:linux
[TOC]git
這篇文章的內容包括:正則表達式
lex語法格式shell
linux下flex的安裝和使用express
flex實例ide
flex源代碼的編譯和使用函數
Lex是LEXical compiler的縮寫,是Unix環境下很是著名的工具,主要功能是生成一個詞法分析器(scanner)的C源碼,描述規則採用正則表達式(regular expression)。描述詞法分析器的文件*.l,通過lex編譯後,生成一個lex.yy.c 的文件,而後由C編譯器編譯生成一個詞法分析器。詞法分析器,簡單來講,其任務就是將輸入的各類符號,轉化成相應的標識符(token),轉化後的標識符 很容易被後續階段處理。 —— [ 百度百科 ] 工具
在使用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
flex的語法被分爲三個部分:
{definitions} %% {rules} %% {user subroutines}
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;
%}`
規則部分的語法以下:
{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()函數將字符串轉換爲整數。
此處主要是放置用戶須要執行的c語言代碼。他們會被原封不動地加入到lex.yy.c文件的末尾。
這裏通常用來存放main函數,詳細會在後面說明。
下面經過一個實例來具體展現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