Lemon Parser Generator

来源:互联网 发布:淘宝卖家怎么关闭店铺 编辑:程序博客网 时间:2024/04/28 06:23

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 <assert.h>         /* 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 <stdio.h>#include <stdlib.h>#include "parser.h"int main(int argc, char *argv[]){        void *pParser;        char *c;        int   value;        if (argc != 2) {                fprintf(stderr, "usage: %s <expression>\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


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







原创粉丝点击