Parser Generator使用說明

Parser Generator的使用說明

最近1個星期,大致學習了一下lex,雖然在windows系統上它並沒有我所期望的強大,在調試和編寫代碼都遇到了不少困難,但是總體來說Parser Generator還是讓我體會到了lex編程的快捷, 爲了自己加深印象把一些參考資料和認識總結了一下.

1.     parser generator是什麼?

它是一款在windows下編寫lex/yacc程序的工具.可以到

http://www.bumblebeesoftware.com/downloads.htm

下載

2.     parser generator的使用方法

這裏只以vc6.0爲例,首先打開parser generator編輯器,選擇Project->LibBuilder

LibBuilder對話框中選中Visual C++(32-bit),按屬性鍵Properties後確以下設置

Script file name                        ./Cpp/Script/msvc32.lbs

Name                                    Visual C++(32-bit)

Directory                               msvc32

Compiler Version                         Version 6

Unicode                                  True

Treat wchar_t as Built-in Type            False

Compiler Bin Directory             安裝路徑/Microsoft Visual Studio/Vc98/bin

Compiler Bin Directory(2) 安裝路徑/Microsoft Visual Studio/Common/MSDev98/bin

Compiler Include Directory      安裝路徑/Microsoft Visual Studio/Vc98/include

Compiler Include Directory(2)               

Compiler Library Directory          安裝路徑/Microsoft Visual Studio/Vc98/lib

Compiler Library Directory(2)               

Libraries下的庫文件全部選中後ok

LibBuilder對話框->Build(編譯過程可能幾分鐘)

編譯完成後我們就可以使用parser generator編寫lex或是yacc程序了

Project->ParserWizard

Step1 工程設定(一點需要注意語言可以選擇c或是c++java)

Step2 工程設定(默認創建帶main函數的yacc文件和lex文件)

Step3 yacc文件設定

Step4 lex文件設定

 

Lexyacc的語法參考http://www.ibm.com/developerworks/cn/linux/sdk/lex/

編輯好代碼後Project->RebBuild All在你創建好的工程下自動生成Step1選定語言的文件(.h/..c/.cpp/.java)

之後在vc6.0加入如下設置

Tool->Option-> directory

Bin file :

安裝目錄/PARSER GENERATOR 2/BIN

Include file:

安裝目錄/PARSER GENERATOR 2/CPP/INCLUDE

Library file

安裝目錄/PARSER GENERATOR 2/CPP/LIB/MSVC32

Soure file

安裝目錄/PARSER GENERATOR 2/CPP/SOURCE

創建vc6.0工程

將生成文件複製到vc6.0創建工程下

Source filesHeader Files中加入生成文件(.h/.c/.cpp)

在工程設定中Project->Settings For box選中win32 debug

c/c++ ->Category選中General ->Preprocessor Definitions加入YYDEBUG

在工程Project設定Project->Settings For box中選中all

link -> Category選中General->Object/Library Modules中加入yld.lib

這裏需要注意的是yld.libparser generatorDUBUG單線程版本,對於vc的控制檯程序是可以的,如果使用了MFC或是Windows applications程序則需要對應下表追加

Library(DEBUG)

Run-time Library

Description

yld.lib

Debug Single-Threaded

單線程靜態鏈接庫(DEBUG版本)

ylmtd.lib

Debug Multithreaded

多線程靜態鏈接庫(DEBUG版本)

ylmtrd.lib

Debug Multithreaded DLL

多線程靜態鏈接庫當run time library 使用動態庫(DEBUG版本)

ylmtrid.lib    

Debug Multithreaded DLL

多線程動態鏈接庫當run time library 使用動態庫(DEBUG版本)

 

Library(RELEASE)

Run-time Library

Description

yl.lib

Single-Threaded

單線程靜態鏈接庫(RELEASE版本)

ylmt.lib

Multithreaded

多線程靜態鏈接庫(RELEASE版本)

ylmtr.lib

Multithreaded DLL      

多線程靜態鏈接庫當run time library 使用動態庫(RELEASE版本)

ylmtri.lib

Multithreaded DLL      

多線程動態鏈接庫當run time library 使用動態庫(RELEASE版本)

如果使用了動態庫版本需要在程序運行環境中追加DLL的地址

安裝目錄/PARSER GENERATOR 2/CPP/LIB/MSVC32

如果需要鏈接yacc或是lexdll.Preprocessor Definitions下加入YYDLL.

 

這樣就可以使用vc6.0lex生成文件進行編譯了

3PG2的默認輸FILE *,怎樣轉char *?
PG2
提供了兩方法,一是重定yyinput,二是重定yygetchar實際使用中後者方便,因yyinput除了調yygetchar之外需要負責lineno量的增加。
yygetchar通常這樣
int yygetchar(void)
{
     if (* inputstring=='/0')
     {
           return -1;
     }
     return (unsigned char) * inputstring++;
}
唯一要注意的就是char *的末尾要返回一個-1代表EOF,使得yyinput停止。(感luocong的提醒)。

4
、同理,yyoutput
FILE *char *
直接
yytext操作既可,yytext就是char[]

3,4說明轉自

http://blog.csdn.net/tankaiha/archive/2005/12/13/551457.aspx

總體來說lex/yacc比起自己編寫分析程序要快的多,但是不足點就是錯誤的調試會非常困難,需要把.lex/.l.yacc/.y文件加入工程調試,每次變更都需要重新編譯parser generator工程,然後再次粘貼,lex的語法熟練度可能決定了效率,新手基於模型爲基礎進行研究逐步認識parser generator是最佳的學習方法,

Lex的自帶例程在/Parser Generator 2/Cpp/Examples ,在這對其中的class(四則計算器)做下簡單說明

lexer.l文件

%{              //%{ %}中的c代碼將被複制到.cpp文件中

#include <stdlib.h>

#include <math.h>

#include <assert.h>

#include "parser.h"//自動生成頭文件必須包含

%}

 

// include file

%include {              //%include {}裏的代碼複製到對應的.h文件中,這裏聲明calc_parser

class calc_parser;   // symboltable是爲在下面calc_lexer::create函數能夠找到使用類型的提前

class symboltable;  //聲明

}

 

// lexical analyser name

%name calc_lexer         //%name指定繼承自lexer的類名{}中爲類成員聲明,類的聲明將被解//釋到.h文件中

// class definition

{

// Attributes

protected:

        symboltable* m_symboltable;             // the symbol table

 

// Operations

public:

        int create(calc_parser* parser, symboltable* symboltable);

       

        // attribute commands

        double number() const;

        symbol* id() const;

}

 

// constructor

{

        // do nothing

}

 

// macros

exponent        ([Ee][+-]?[0-9]+)  //科學計算方定義爲exponent

 

%%

 

%{

// extract yylval for use later on in actions

YYSTYPE& yylval = *(YYSTYPE*)yyparserptr->yylvalptr;//指定yylval值的類型

%}

 

// 對於數字的規則處理 |表示或,|下一個算式使用的規則與第一個相同{}中是規則執行代碼

[0-9]+"."[0-9]*{exponent}?      |

"."[0-9]+{exponent}?            |

[0-9]+{exponent}?                       { yylval.value = number(); return NUMBER; }

 

//對於3角函數的規則處理

"sin"                                           { return SIN; }

"cos"                                           { return COS; }

"tan"                                           { return TAN; }

 

// 對於變量的規則處理

[a-zA-Z_][a-zA-Z0-9_]*          { yylval.symbol = id(); return ID; }

 

//對於符號的規則處理

"="                                                     { return '='; }

"+"                                                     { return '+'; }

"-"                                                     { return '-'; }

"*"                                                     { return '*'; }

"/"                                                     { return '/'; }

"("                                                     { return '('; }

")"                                                     { return ')'; }

 

// 對於特殊符號的處理

[ /t]                                           { /* do nothing */ }

/n                                                      { return '/n'; }

 

.                                                       { printf("invalid character '0x%02x'/n", (unsigned int)yytext[0]); }

 

%%

 

/////////////////////////////////////////////////////////////////////////////

// 以下都是對成員函數的實現,將被複制到.cpp文件中

 

int calc_lexer::create(calc_parser* parser, symboltable* symboltable)

{

        assert(parser != NULL);

        assert(symboltable != NULL);

       

        m_symboltable = symboltable;

        return yycreate(parser);

}

 

/////////////////////////////////////////////////////////////////////////////

// calc_lexer attribute commands

 

double calc_lexer::number() const

{

        errno = 0;              // clear error flag

        char* endp;

        double d = strtod(yytext, &endp);

        if ((d == +HUGE_VAL || d == -HUGE_VAL) && errno == ERANGE) {

                printf("number too big/n");

        }

        return d;

}

 

symbol* calc_lexer::id() const

{

        symbol* p = m_symboltable->install(yytext);

        return p;

}//lexer.l end

 

parser.y文件

%{                      //%{ %}中的c代碼將被複制到.cpp文件中

#include <math.h>

%}

 

// include file

%include {              //%include {}裏的代碼複製到對應的.h文件中,這裏聲明symbol

 // 是爲在下面calc_parser:: assign函數能夠找到使用類型的提前

 //聲明

// forward references

class symbol;

}

 

//%union表示使用聯合體聲明yytest的類型可能有2,也就是lex返回的標記的值的類型(//一個標記都會有一個值)

%union {

        symbol* symbol;

        double value;

}

 

// nonterminals

%type <value> expr  //%type是聲明變量exprdouble類型,這裏要注意的是<>聲明的類型必//須是在%union中定義的變量

//這裏要注意下越是後聲明的越高

%right '='          //%right聲明=爲右結合

%left '+', '-'  //%left 聲明+ -爲左結合

%left '*', '/'        //%left 聲明* /爲左結合

%right UMINUS     //%right聲明UMINUS爲右結合,此優先級最高

 

%token <value> NUMBER //%token聲明標記NUMBER和其類型,%type注意點一樣

%token <symbol> ID     //聲明標記ID的類型,%type注意點一樣

 

// keywords

%token SIN           //聲明標記SIN,因爲本身的值沒有用途所以不對其類型特殊聲明

%token COS           //聲明標記COS,因爲本身的值沒有用途所以不對其類型特殊聲明

%token TAN           //聲明標記TAN,因爲本身的值沒有用途所以不對其類型特殊聲明

 

// include file

%include {

#include "symbol.h"  //%{ %}中的c代碼將被複制到.h文件中

#include "lexer.h"

}

 

// parser name

%name calc_parser   //%name指定繼承自lexer的類名{}中爲類成員聲明,類的聲明將被解

//釋到.h文件中

// class definition

{

// 類成員聲明

protected:

        symboltable m_symboltable;              // the symbol table

        calc_lexer m_lexer;                             // the lexical analyser

       

// Operations

public:

        int create();

       

        // attribute commands

        double assign(symbol* id, double value);

        double divide(double dividend, double divisor);

}

 

// constructor

{

        // do nothing

}

 

%%

lines

        : lines line

        | /* empty */

        ;

 

line

        : expr '/n'                                     { printf("%g/n", (double)$1); }

        | error '/n'                            { yyerrok(); }

        ;

//這裏只對expr進行說明$$是冒號左邊表達式的值   $1爲右邊第一個表達式的值

$2爲右邊第二個表達式的值  $3爲右邊第三個表達式的值,以此類推.

expr

        : ID '=' expr                           { $$ = assign($1, $3); }//變量保存

        | expr '+' expr                         { $$ = $1 + $3; }

        | expr '-' expr                         { $$ = $1 - $3; }

        | expr '*' expr                         { $$ = $1 * $3; }

        | expr '/' expr                         { $$ = divide($1, $3); }//除法判斷

        | '(' expr ')'                          { $$ = $2; }

        | '-' expr %prec UMINUS         { $$ = -$2; }

// %prec說明'-' expr表達式的優先級和UMINUS一樣.

        | NUMBER                                        { $$ = $1; }

        | ID                                            { $$ = $1->m_value; }

        | SIN '(' expr ')'                      { $$ = sin($3); }

        | COS '(' expr ')'                      { $$ = cos($3); }

        | TAN '(' expr ')'                      { $$ = tan($3); }

        ;

 

%%

 

/////////////////////////////////////////////////////////////////////////////

//以下都是對成員函數的實現,將被複制到.cpp文件中

 

 

int main(void)

{

        int n = YYEXIT_FAILURE;

       

        calc_parser parser;

        if (parser.create()) {

                n = parser.yyparse();

        }

        return n;

}

 

/////////////////////////////////////////////////////////////////////////////

// calc_parser commands

 

int calc_parser::create()

{

        if (!yycreate(&m_lexer)) {

                return 0;

        }

        if (!m_lexer.create(this, &m_symboltable)) {

                return 0;

        }

        return 1;       // success

}

 

/////////////////////////////////////////////////////////////////////////////

// calc_parser attribute commands

 

double calc_parser::assign(symbol* id, double value)

{

        assert(id != NULL);

 

        id->m_value = value;

        return id->m_value;

}

 

double calc_parser::divide(double a, double b)

{

        if (b == 0) {

                printf("division by zero/n");

                yythrowerror();         // causes a syntax error

                return 0;

        }

        else {

                return a / b;

        }

}

更多詳細參考可以查看Parser Generator Help幫助文檔

以上都是作者的辛苦勞動,轉載請註明出處.

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