Parser Generator的使用说明

单于楚
2023-12-01

Parser Generator的使用说明

最近1个星期,大致学习了一下lex,虽然在windows系统上它并没有我所期望的强大,在调试和编写代码都遇到了不少困难,但是总体来说Parser Generator还是让我体会到了lex编程的快捷, 为了自己加深印象把一些参考资料和认识总结了一下.

<!--[if !supportLists]-->1.     <!--[endif]-->parser generator是什么?

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

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

下载

<!--[if !supportLists]-->2.     <!--[endif]-->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文件设定

 

Lex和yacc的语法参考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 files和Header 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.lib为parser generator的DUBUG单线程版本,对于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或是lex的dll.在Preprocessor Definitions下加入YYDLL.

 

这样就可以使用vc6.0对lex生成文件进行编译了

3、PG2的默认输入为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是声明变量expr为double类型,这里要注意的是<>声明的类型必//须是在%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;

        }

}

 类似资料: