明论  
5.lemon柠檬牌代码生成器

  前面说了,词法的解析器最大的难点在与将语句中关键词与参数多样组合,与程序具体的处理逻辑一一对应。这是一个光想想就头大的事情,为此,我们找到了一个能很好的解决这个难题的方案,代码生成器。
     lemon代码生成器,简单的说就是将我们写好的context-free grammar (CFG) 语法脚本,用来生产具体的c代码的逻辑,这东西是编写词法解析器与编译工具的利器。
     以下来剖析下这种CFG语法脚本,怎么配置与lemon代码生成器中。
    lemon语法文件是有一段一段的语法规则段构成的,每个语法规则段都是通过"::="组合起来的,后面跟and/or等逻辑表达式,每个语法规则段都是以句号结尾的。语法段的右边可以为空。规则可以已任意的顺序出现在文件中,除了%start的关键词例外,这个下面会提到,语法段大概像这样:
  expr ::= expr PLUS expr.
  expr ::= expr TIMES expr.
  expr ::= LPAREN expr RPAREN.
  expr ::= VALUE.
  lemon语法文件另一个重要的功能,就是嵌入C语句,当满足当前的语法规则时执行{}内的C语句。
  如下:
  expr ::= expr PLUS expr.   { printf("Doing an addition...\n"); }
  另外,柠檬牌代码生成器还支持一下关键字,
    * %destructor
    * %extra_argument
    * %include
    * %left
    * %name
    * %nonassoc
    * %parse_accept
    * %parse_failure
    * %right
    * %stack_overflow
    * %stack_size
    * %start_symbol
    * %syntax_error
    * %token_destructor
    * %token_prefix
    * %token_type
    * %type

* %left * %right 定义运算符关键字
例子,
%left OR.
%left AND.
%right NOT.
%left IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
%left GT LE LT GE.
%right ESCAPE.
%left BITAND BITOR LSHIFT RSHIFT.
%left PLUS MINUS.
%left STAR SLASH REM.
%left CONCAT.
%left COLLATE.
%right UMINUS UPLUS BITNOT
是的,上面的就是定义sql运算符的例子,left为左运算符,right为右运算符.
%type           定义函数的类型 如void int 等
%token_type {Token}  定义语句字符的类型
%default_type {Token}  默认语句字符的类型

%destructor 构析标识
   %type nt {void*}
   %destructor nt { free($$); }
   nt(A) ::= ID NUM.   { A = malloc( 100 ); }
这里%type定义了类型为void的函数,%destructor定义了当函数nt出堆时,程序必须执行的动作。

其他关键字不一一介绍,有兴趣的同学请参照文档 http://www.hwaci.com/sw/lemon/lemon.html

那么到这里,我们将配置好的语法规则导入lemon中,lemon会为我们生成两个C文件,一个头文件,与一个.c文件,头文件中包括我们定义的关键字,.c就是语法与逻辑映射的代码,而lemon为我们什么了几个方便的接口,让我们调用解析器

   01 ParseTree *ParseFile(const char *zFilename){
   02    Tokenizer *pTokenizer;
   03    void *pParser;
   04    Token sToken;
   05    int hTokenId;
   06    ParserState sState;
   07
   08    pTokenizer = TokenizerCreate(zFilename);  ---解析词组
   09    pParser = ParseAlloc( malloc );           ---开辟内存
   10    InitParserState(&sState);                 ---初始化
   11    while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
   12       Parse(pParser, hTokenId, sToken, &sState);   ---解析
   13    }
   14    Parse(pParser, 0, sToken, &sState);        
   15    ParseFree(pParser, free );                      ---释放内存
   16    TokenizerFree(pTokenizer);
   17    return sState.treeRoot;                         ----返回语法树
   18 }
是的,配好逻辑,我们不用写一句代码,我们完成了语法解析最复杂的部分,目标越来越近了。
posted on 2009-07-01 11:06  konyel  阅读(1798)  评论(1编辑  收藏  举报