<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    迷途書童

    敏感、勤學、多思
    隨筆 - 77, 文章 - 4, 評論 - 86, 引用 - 0
    數據加載中……

    從lex&yacc說到編譯器(2.flex的使用)

    看了第一篇的關于正則表達式的說明后,下面我們就來通過它,使用flex這個詞法分析工具來構造我們的編譯器的詞法分析器.

    關于lex的教程應該是很多,這里我就簡單地介紹一下,然后著重后面的lex和yacc的配合使用以及其技巧.所以,如果你不看了后還是不太明白lex或者yacc的使用,請你自己上網去查查,這方面的教程是很多的.我知道的一篇常見的就是

    Yacc 與 Lex 快速入門
    Lex 與 Yacc 介紹

    它的作者就是Ashish Bansal.

    Flex就是fast lex的意思.而lex就是Lexical Analyzar的意思.flex可以在cygwin或者gnupro中找到.它是unix的一個工具,屬于GNU組織產品.網上也可以找到單獨可以在windows下用的版本.

    我們一般把我們的詞法掃描程序要掃描的一些單詞(token)用正則表達式寫好,然后作為lex的輸入文件,輸入命令flex xxx.l(xxx.l就是輸入文件),lex經過處理后,就能得到一個名字叫lex.yy.c的C源代碼.這個C源代碼文件,就是我們的詞法掃描程序.通常lex為我們生成的詞法分析器的C源代碼都是十分復雜而且龐大的,我們一般根本不會去查看里面的代碼(放心好了,flex這個東西不會出錯的)

    下面讓我們看看幾個我已經使用過的幾個lex輸入文件.

    這是一個前段時間我為GBA上的一個RPG游戲寫的腳本引擎所使用的lex輸入文件(部分)

    2.1

    %{

    /* need this for the call to atof() below */

    #i nclude <stdio.h>

    #i nclude <stdlib.h>

    #i nclude <math.h>

    #i nclude "globals.h"

    %}

    digit??????? [0-9]

    number?????? ("-"|"+")?{digit}+

    hexnumber??? "0x"({digit}|[a-fA-F])+

    letter?????? [a-zA-Z]

    identifier?? ({letter}|_)({number}|{letter}|_)*

    newline????? [\n]

    whitespace?? [ \t]+

    string?????? \"[^"]*\"

    comment????? "#"[^#]*"#"

    %%

    {string}???? { return VM_STRING;??????? }

    "Logo"?????? { return VMIN_LOGO; }

    "FaceIn"???? { return VMIN_FACEIN; }

    "FaceOut"??? { return VMIN_FACEOUT; }

    "LoadTile"?? { return VMIN_LOAD_TILE;?? }

    "CreateRole" { return VMIN_CREATE_ROLE; }

    "ReleaseRole" { return VMIN_RELEASE_ROLE;}

    "CreateMap"? { return VMIN_CREATE_MAP;? }

    "ReleaseMAP" { return VMIN_RELEASE_MAP;}

    "ShowBitmap" { return VMIN_SHOWBITMAP;? }

    "CreateDialog" { return VMIN_CREATE_DIALOG; }

    "ReleaseDialog" { return VMIN_RELEASE_DIALOG;}

    "Fight"????? { return VMIN_FIGHT;?????? }

    "Delay"????? { return VMIN_DELAY;?????? }

    "PressA"???? { return VMIN_PRESS_A;???? }

    "PressB"???? { return VMIN_PRESS_B;???? }

    "PressR"???? { return VMIN_PRESS_R;???? }

    "PressL"???? { return VMIN_PRESS_L;???? }

    "PressStart" { return VMIN_PRESS_START; }

    "PressSelect" { return VMIN_PRESS_SELECT;}

    {number}???? { return VM_NUMBER;??????? }

    {whitespace} { /* skip whitespace */??? }

    {identifier} { return VM_ID;??????????? }

    {newline}??? ;

    .??????????? ;

    %%

    int yywrap()

    {

    ????? return 1;

    }

    這里的lex輸入文件一共有三個部分,用%%分開.第一部分中的%{和}%中的內容就是直接放在lex輸出C代碼中的頂部.我們通過它可以來定義一些所需要的宏,函數和include一些頭文件等等.我的這個lex輸入文件中也沒什么特別的東西,就是常規的C源文件的include頭文件

    %{

    /* need this for the call to atof() below */

    #i nclude <stdio.h>

    #i nclude <stdlib.h>

    #i nclude <math.h>

    #i nclude "globals.h"

    %}

    第一部分中,除了前面的%{和}%包含的部分,下面的就是正則表達式的定義.

    看了第一篇的正則表達式,這樣你就能夠在這里派上用場了.

    讓我們來看看我這里定義的正則表達式:

    digit??????? [0-9]

    number?????? ("-"|"+")?{digit}+

    hexnumber??? "0x"({digit}|[a-fA-F])+

    letter?????? [a-zA-Z]

    identifier?? ({letter}|_)({number}|{letter}|_)*

    newline????? [\n]

    whitespace?? [ \t]+

    string?????? \"[^"]*\"

    comment????? "#"[^#]*"#"

    digit就不用說了,就是0-9的阿拉伯數字定義,第一篇文章中也舉了這個例子.number就是digit的1到無限次的重復,再在其前面加上”+”和”-“符號.

    注意:

    “a”: 即使a是元字符,它仍是字符a

    \a: 當a是元字符時候,為字符a

    a?: 一個可選的a,也就是說可以是a,也可以沒有a

    a|b: a或b

    (a): a本身

    [abc]: 字符a,b或c中的任一個

    [a-d]: a,b,d或者d中的任一個

    [^ab]: 除了a或b外的任何一個字符

    .: 除了新行之外的任一個字符

    {xxx}: 名字xxx表示的正則表達式

    這里需要特別說明的就是

    newline????? [\n]

    newline就是新行,這里我使用了[]把\n換行號括起來.因為如果我直接用\n表示的話,那么按照上面的規則,那就會看成\和n兩個字符,所以我使用了[\n].有些時候newline也被寫成[\n]|[\r\n].因為在文本文件中,一般換行一次,那么就是一個\n(0xA),可是在二進制文件中,換行有時候又是\r\n(0xD,0xA)一共兩個字符號.

    第二部分就是定義掃描到正則表達式的動作.

    這些動作其實就是C代碼,它們將會被鑲嵌在lex輸出的C文件中的yylex()函數中.

    上面的例子的動作其實十分平常,就是返回一個值.

    我們在外部使用這個lex為我們生成C代碼的時候,只需要使用它的int yylex()函數.當我們使用一次yylex(),那么就會自動去掃描一個匹配的正則表達式,然后完成它相應的動作.這里的動作都是返回一值,那么yylex就會返回這個值.通常默認yylex返回0時候,表示文件掃描結束,所以你的動作中最好不要返回0,以免發生沖突.當然,動作中也可以不返回一值,那么yylex就會完成這個動作后自動掃描下一個可以被匹配的字符串,一直到掃描到文件結束.

    當掃描到一個可以被匹配的字符串,那么這個時候,全局變量yytext就等于這個字符串

    請大家一定記住這些正則表達式的順序.

    如果出現一個字符串,可以同時匹配多個正則表達式,那么它將會被定義在前面的正則表達式匹配.所以我一般把字符串string定義在最前面.

    如果文件中的字符沒有被lex輸入文件中任何一個字符匹配,那么它會自動地被標準輸出.所以大家一定要記住在每個正則表達式處理完畢后,一定要加上{newline}和.這兩個正則表達式的動作.

    ,讓我們看看lex為我們輸出C文件中提供一些常量

    Lex 變量

    yyin

    FILE* 類型。 它指向 lexer 正在解析的當前文件。

    yyout

    FILE* 類型。 它指向記錄 lexer 輸出的位置。 缺省情況下,yyin 和 yyout 都指向標準輸入和輸出。

    yytext

    匹配模式的文本存儲在這一變量中(char*)。

    yyleng

    給出匹配模式的長度。

    yylineno

    提供當前的行數信息。(lexer不一定支持。)

    2.2

    這是<<編譯原理與實踐>>書中配套的源代碼的lex輸入文件.大家可以參考一下,作者為它自己定義的一個Tiny C編譯所做的詞法掃描器.

    /****************************************************/

    /* File: tiny.l???????????????????????????????????? */

    /* Lex specification for TINY?????????????????????? */

    /* Compiler Construction: Principles and Practice?? */

    /* Kenneth C. Louden??????????????????????????????? */

    /****************************************************/

    %{

    #i nclude "globals.h"

    #i nclude "util.h"

    #i nclude "scan.h"

    /* lexeme of identifier or reserved word */

    char tokenString[MAXTOKENLEN+1];

    %}

    digit?????? [0-9]

    number????? {digit}+

    letter????? [a-zA-Z]

    identifier? {letter}+

    newline???? \n

    whitespace? [ \t]+

    %%

    "if"??????????? {return IF;}

    "then"????????? {return THEN;}

    "else"????????? {return ELSE;}

    "end"?????????? {return END;}

    "repeat"??????? {return REPEAT;}

    "until"???????? {return UNTIL;}

    "read"????????? {return READ;}

    "write"???????? {return WRITE;}

    ":="??????????? {return ASSIGN;}

    "="???????????? {return EQ;}

    "<"???????????? {return LT;}

    "+"???????????? {return PLUS;}

    "-"???????????? {return MINUS;}

    "*"????? ???????{return TIMES;}

    "/"???????????? {return OVER;}

    "("???????????? {return LPAREN;}

    ")"???????????? {return RPAREN;}

    ";"???????????? {return SEMI;}

    {number}??????? {return NUM;}

    {identifier}??? {return ID;}

    {newline}?????? {lineno++;}

    {whitespace}??? {/* skip whitespace */}

    "{"???????????? { char c;

    ????????????????? do

    ????????????????? { c = input();

    ??????????????????? if (c == EOF) break;

    ??????????????????? if (c == '\n') lineno++;

    ????????????????? } while (c != '}');

    ??????????????? }

    .?????????? ????{return ERROR;}

    %%

    TokenType getToken(void)

    { static int firstTime = TRUE;

    ? TokenType currentToken;

    ? if (firstTime)

    ? { firstTime = FALSE;

    ??? lineno++;

    ??? yyin = source;

    ??? yyout = listing;

    ? }

    ? currentToken = yylex();

    ? strncpy(tokenString,yytext,MAXTOKENLEN);

    ? if (TraceScan) {

    ??? fprintf(listing,"\t%d: ",lineno);

    ??? printToken(currentToken,tokenString);

    ? }

    ? return currentToken;

    }

    這里有點不同的就是,作者用了另外一個getToken函數來代替yylex作為外部輸出函數.其中getToken里面也使用了lex默認的輸出函數yylex(),同時還做了一些其它的事情.不過我建議大家不要像作者那樣另外寫自己的結果輸出函數,因為在后面,需要和yacc搭配工作的時候,yacc生成的語法分析程序只認名字叫yylex()的詞法結果輸出函數.

    if (firstTime)

    ? { firstTime = FALSE;

    ??? lineno++;

    ??? yyin = source;

    ??? yyout = listing;

    ? }

    posted on 2006-05-06 16:15 迷途書童 閱讀(916) 評論(0)  編輯  收藏 所屬分類: 編譯原理

    主站蜘蛛池模板: 女人18一级毛片免费观看| 最近2019中文字幕mv免费看| 无码精品人妻一区二区三区免费看 | 亚洲黄色激情视频| 久久亚洲精品11p| 成人A毛片免费观看网站| 91香蕉国产线在线观看免费| 免费看的一级毛片| 亚洲日韩欧洲乱码AV夜夜摸| 亚洲冬月枫中文字幕在线看 | 爱情岛论坛免费视频| 日本人成在线视频免费播放| 成人毛片18女人毛片免费视频未| 亚洲AV日韩精品久久久久| 亚洲精品无码久久久久A片苍井空| a免费毛片在线播放| 91热久久免费精品99| 亚洲日韩欧洲乱码AV夜夜摸 | 中文字幕手机在线免费看电影 | 亚洲国产成人久久精品99 | 亚洲男人在线无码视频| 久久久久亚洲AV无码观看| 在线看亚洲十八禁网站| 亚洲视频在线观看免费| 相泽亚洲一区中文字幕| 亚洲色欲色欱wwW在线| 免费日本一区二区| 四虎影院永久免费观看| 久久久亚洲欧洲日产国码是AV| 两性色午夜视频免费网| 亚洲精品97久久中文字幕无码| 欧美激情综合亚洲一二区| 好吊妞视频免费视频| 国产成人精品日本亚洲网址| 永久黄色免费网站| 国产日韩亚洲大尺度高清| 毛片亚洲AV无码精品国产午夜| 亚洲黄色免费电影| 亚洲视频在线观看网址| 最近中文字幕高清免费中文字幕mv | 亚洲av永久无码精品网址|