lemon是LALR(1)的用于c或c++的解析器与生成器,与大名鼎鼎的bison与yacc做类似的工作,但又不是简单复制它们的功能,lemon使用了设计了不同的语法格式来减少编码的错误,lemon还使用了比yacc和bison更快的更复杂的解析引擎,它既可重入又线程安全。
官方文档可知,首先要编译lemon.c文件,并且需要配置同级目录下lempar.c文件,此时在终端中编译lemon.c文件
gcc -o lemon lemon.c
接着在终端中输入并检查lemon;
$ lemon -v
Command line syntax error: undefined option.
lemon -v
^-- here
Valid command line options for "lemon" are:
-b Print only the basis in report.
-c Don't compress the action table.
-d<string> Output directory. Default '.'
-D<string> Define an %ifdef macro.
-f<string> Ignored. (Placeholder for -f compiler options.)
-g Print grammar without actions.
-I<string> Ignored. (Placeholder for '-I' compiler options.)
-m Output a makeheaders compatible file.
-l Do not print #line statements.
-O<string> Ignored. (Placeholder for '-O' compiler options.)
-p Show conflicts resolved by precedence rules
-q (Quiet) Don't print the report file.
-r Do not sort or renumber states
-s Print parser stats to standard output.
-x Print the version number.
-T<string> Specify a template file.
-W<string> Ignored. (Placeholder for '-W' compiler options.)
至此,lemon就配置完成。
%include {
#include <assert.h>
}
%token_type {int}
%syntax_error {
fprintf(stderr, "Syntax error\n");
}
%left PLUS MINUS.
%left TIMES DIVIDE.
program ::= expr(A). { printf("Result = %d\n", A);}
expr(A) ::= expr(B) PLUS expr(C). {
A = B + C;
printf("PLUS : A : %d, B : %d, C : %d\n", A, B, C);
}
expr(A) ::= expr(B) MINUS expr(C). {
A = B - C;
printf("MINUS : A : %d, B : %d, C : %d\n", A, B, C);
}
expr(A) ::= expr(B) TIMES expr(C). {
A = B*C;
printf("TIMES : A : %d, B : %d, C : %d\n", A, B, C);
}
expr(A) ::= expr(B) DIVIDE expr(C). {
printf("DIVIDE : A : %d, B : %d\n", A, B);
if (C !=0 )
A = B/C;
else
fprintf(stderr, "divide by zero\n");
}
expr(A) ::= LPAR expr(B) RPAR. {
printf("LPAR and RPAR : A : %d, B : %d\n", A, B);
A = (B);
}
expr(A) ::= INTEGER(B). {
printf("INTERGER(B) : A : %d, B : %d\n", A, B);
A=B;
}
保存改文件命名为first_lemon.y,然后终端输入,
lemon first_lemon.y
此时就会生成三个文件first_lemon.c(生成的词法解析调用的函数),first_lemon.h(头部文件),first_lemon.out(生成的规则树信息)文件。
此时编写的c函数需要调用生成的first_lemon.c中的函数,此时调用函数如下;
#include <stdio.h>
#include <stdlib.h>
#include "first_lemon.h"
int main(int argc, char const *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 ' ':
break;
default:
fprintf(stderr, "Unknown symbol: %c\n", *c);
return -1;
}
}
Parse(pParser, 0, 0);
ParseFree(pParser, free);
return 0;
}
此时,保存为first_lemon_cal.c并在终端中编译运行,
gcc -o first_lemon_cal first_lemon_cal.c first_lemon.c
如果出现语法错误;
first_lemon_cal.c:15:21: warning: implicit declaration of function 'ParseAlloc' is invalid in C99
[-Wimplicit-function-declaration]
pParser = (void *) ParseAlloc(malloc);
由于c语言的规则为C99,此时需要手动讲定义的头文件添加到first_lemon.h文件中,最终first_lemon.h的文件内容如下;
#define PLUS 1
#define MINUS 2
#define TIMES 3
#define DIVIDE 4
#define LPAR 5
#define RPAR 6
#define INTEGER 7
#ifndef YYMALLOCARGTYPE
# define YYMALLOCARGTYPE size_t
#endif
#define ParseCTX_PDECL
#define ParseARG_PDECL
#define ParseTOKENTYPE int
void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL);
void ParseFree(
void *p, /* The parser to be deleted */
void (*freeProc)(void*) /* Function used to reclaim memory */
);
void Parse(
void *yyp, /* The parser */
int yymajor, /* The major token code number */
ParseTOKENTYPE yyminor /* The value for the token */
ParseARG_PDECL /* Optional %extra_argument parameter */
);
再继续在终端中编译。
$ ./first_lemon_cal "(1+2+3)*2"
INTERGER(yymsp[0].minor.yy0) : yylhsminor.yy0 : -289395680, yymsp[0].minor.yy0 : 1
INTERGER(yymsp[0].minor.yy0) : yylhsminor.yy0 : -289395680, yymsp[0].minor.yy0 : 2
PLUS : yylhsminor.yy0 : 3, yymsp[-2].minor.yy0 : 1, yymsp[0].minor.yy0 : 2
INTERGER(yymsp[0].minor.yy0) : yylhsminor.yy0 : -289395680, yymsp[0].minor.yy0 : 3
PLUS : yylhsminor.yy0 : 6, yymsp[-2].minor.yy0 : 3, yymsp[0].minor.yy0 : 3
LPAR and RPAR : yymsp[-2].minor.yy0 : 0, yymsp[-1].minor.yy0 : 6
INTERGER(yymsp[0].minor.yy0) : yylhsminor.yy0 : -289395680, yymsp[0].minor.yy0 : 2
TIMES : yylhsminor.yy0 : 12, yymsp[-2].minor.yy0 : 6, yymsp[0].minor.yy0 : 2
Result = 12
至此,最简单的使用方式就如上所示。
本文作为sqlite源码分析中有关lemon的使用的一个简单的展示,但是简单展示的内容还不足以完全了解到sqlite中的parse.y的内容,大家可查看lemon的官方文档继续了解。由于本人才疏学浅,如有错误请批评指正。