Lemon Parser Generator

lighttpd的配置文件需要用到lemon来分析,lemon是一个LALR(1)的语法分析生成器。

本来以为要用到LALR(1)的知识,刚好上个学期学了编译原理这门课,但是由于课时

不够,Bottom-Up Parsing只讲了LR(0)和SLR(1),LR(1)和LALR(1)没讲到,所以在

尝试使用lemon之前特意花了一个小时去看了LR(1)和LALR(1)的知识。好在基础的

知识还没有还给老师,所以理解起来没有什么困难。不过看完lemon的文档之后发现

只需要知道一点编译原理的知识就可以了,不熟悉也问题不大。


Prepare

先到这里把lemon.c和lempar.c下载下来,再打开Documentation来学习一下。

其中lemon.c就是整个lemon parser generator的源代码了,而lempar.c则是

lemon默认使用的代码生成模板(从源代码中看出来的),一般来说用这个就

可以了。编译的话很简单:

$gcc lemon.c -o lemon

The Parser Interface

其实要用到的接口只有下面三个:

 
  
void *pParser = ParseAlloc( malloc );
ParseFree(pParser, free);
Parse(pParser, hTokenID, sTokenData, pArg);
还有一个用来调试的接口:

ParseTrace(FILE *stream, char *zPrefix);
使用的一般套路是这样:

   ParseFile(){
      pParser = ParseAlloc( malloc );
      while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
         Parse(pParser, hTokenId, sToken);
      }
      Parse(pParser, 0, sToken);
      ParseFree(pParser, free );
   }
其中GetNextToken的方法有很多,简单的话可以自己手动切Token。


详细的内容可以自行参考lemon的Documentation,其实看例子比看文档

要直观,看完一个例子基本上自己可以模仿着用了。

Example

网上介绍lemon的文章基本上都以实现一个简单的计算器作为例子,那我

就照般吧,呵呵。

使用lemon的主要工作其实就是根据需要编写自己的rules,下面我们看一下

一个简单的计算器的rules (parser.y):

%include {
#include          /* lemon需要用到assert这个宏 */
 }

%token_type {int}           /* 变量的类型 */

%syntax_error {             /* 当出现语法错误的时候,{}里面的语句就会执行 */
        fprintf(stderr, "Syntax error\n");
 }

/* left是左结合的意思,
   PLUS和MINUS放在TIMES和DIVIDE之前表示PLUS和MINUS的优先级比TIMES和DIVIDE低 */
%left PLUS MINUS.
%left TIMES DIVIDE.

/* 下面的第一条rule的左边(program)默认是start symbol,
   每一条rule后面的{}里面的是C代码,这些C代码会在它们reduce的时候被执行。*/
program ::= expr(A). { printf("Result = %d\n", A); }
expr(A) ::= expr(B) PLUS expr(C). { A = B + C; }
expr(A) ::= expr(B) MINUS expr(C). { A = B - C; }
expr(A) ::= expr(B) TIMES expr(C). { A = B * C; }
expr(A) ::= expr(B) DIVIDE expr(C). {
        if (C != 0)
                A = B / C;
        else
                fprintf(stderr, "divide by zero\n");
}
expr(A) ::= LPAR expr(B) RPAR. { A = (B); }
expr(A) ::= INTEGER(B). { A = B; }

然后让lemon来生成我们需要的代码:

$./lemon parser.y
没有错误的话就会生成三个文件,parser.h, parser.c, parser.out

然后我们还需要自己编写一个main函数(calc.c)来调用上面提到的那些接口函数。

#include 
#include 
#include "parser.h"

int main(int argc, char *argv[])
{
        void *pParser;
        char *c;
        int   value;

        if (argc != 2) {
                fprintf(stderr, "usage: %s \n", argv[0]);
                exit(EXIT_FAILURE);
        }

        pParser = (void *)ParseAlloc(malloc);

        for (c = argv[1]; *c; c++) {
                switch(*c) {
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                        for (value = 0; *c && *c >= '0' && *c <= '9'; c++)
                                value = value * 10 + (*c - '0');
                        c--;
                        Parse(pParser, INTEGER, value);
                        break;
                case '+':
                        Parse(pParser, PLUS, 0);
                        break;
                case '-':
                        Parse(pParser, MINUS, 0);
                        break;
                case '*':
                        Parse(pParser, TIMES, 0);
                        break;
                case '/':
                        Parse(pParser, DIVIDE, 0);
                        break;
                case '(':
                        Parse(pParser, LPAR, 0);
                        break;
                case ')':
                        Parse(pParser, RPAR, 0);
                        break;
                case ' ':       /* ignore whitespace */
                        break;
                default:
                        fprintf(stderr, "Unknown symbol: %c\n", *c);
                        return -1;
                }
        }
        Parse(pParser, 0, 0);

        ParseFree(pParser, free);

        return 0;
}
main函数除了调用那些接口之外,主要就是要自己手动切Token了,也很简单。

最后我们就可以编译得到我们的计算器了哦:

$gcc calc.c parser.c -o calc
好的,如无意外我们的计算器就做好了哦。现在回想起我大一写计算器的时候,

面对那些表达式调试了好几天才做好呢,现在估计半个小时就可以了,^_^

试用一下看看:

$./calc "(123 + 456) * 10 + 9"
Result = 5799


好吧,简单的使用就介绍到这里了,更加详细的内容请阅读手册,写得很清楚了。







你可能感兴趣的:(Useful,Tools,Lighttpd)