lemon的简单使用

lemon概述

lemon是LALR(1)的用于c或c++的解析器与生成器,与大名鼎鼎的bison与yacc做类似的工作,但又不是简单复制它们的功能,lemon使用了设计了不同的语法格式来减少编码的错误,lemon还使用了比yacc和bison更快的更复杂的解析引擎,它既可重入又线程安全。

lemon的简述

官方文档可知,首先要编译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的官方文档继续了解。由于本人才疏学浅,如有错误请批评指正。

### Lemon 库简介 Lemon 是 SQLite 数据库引擎中的解析器生成工具,类似于 yacc 或 bison。它用于生成 SQL 解析器的核心部分[^1]。尽管 Lemon 并不是一个独立的库供开发者直接使用,但它作为 SQLite 的一部分被广泛研究和应用。 以下是有关 Lemon 库的下载、安装以及基本使用的教程: --- ### 下载 Lemon 工具 Lemon 可以通过获取 SQLite 源码包来获得。SQLite 官方网站提供了源码版本,其中包含了 Lemon 的实现文件 `lemon.c` 和头文件 `lempar.c`。可以通过以下方式下载 SQLite 源码并提取 Lemon 文件: ```bash wget https://www.sqlite.org/2023/sqlite-autoconf-3400100.tar.gz tar -xzvf sqlite-autoconf-3400100.tar.gz cd sqlite-autoconf-3400100/ ls | grep lemon ``` 上述命令会显示 `lemon.c` 和其他相关文件的存在。这些文件即为构建 Lemon 所需的内容。 --- ### 编译与安装 Lemon 编译 Lemon 需要标准 C 编译器(如 GCC)。可以按照如下步骤完成编译过程: #### 步骤说明 1. 使用 C 编译器编译 `lemon.c` 文件。 2. 将其转换为可执行程序以便后续调用。 具体操作如下所示: ```bash gcc -o lemon lemon.c ./lemon --help ``` 如果一切正常运行,则表明 Lemon 成功编译完毕,并能够打印帮助信息。 --- ### 使用 Lemon 进行语法分析 Lemon 主要用作 LALR(1) 文法解析器生成器。它的输入是一个描述语法规则的 `.y` 文件,输出则是对应的 C 代码形式的解析函数。下面展示了一个简单的例子: 假设有一个名为 `example.y` 的文法定义文件,内容如下: ```plaintext %token_type {int} %extra_argument {void *client_data} nonterminal ::= TERMINAL. TERMINAL ::= nonterminal. ``` 将其传递给 Lemon 命令行工具即可生成目标解析器代码: ```bash ./lemon example.y ``` 这一步完成后会在当前目录下生成两个主要文件: - **`parse.h`**: 头文件,包含解析器接口声明; - **`parse.c`**: 实现文件,提供实际逻辑功能。 随后可以根据需求进一步集成到自己的项目当中去。 --- ### 关于 Node.js 开发环境配置 (补充) 对于现代 JavaScript 项目而言,通常推荐将开发依赖项限定在本地范围内而非全局范围之内。例如,在涉及 ESLint、Prettier 等工具时,建议遵循最佳实践——仅限于特定项目的 node_modules/.bin 路径下调用它们[^2]。这一原则同样适用于任何可能需要用到 Lemon 的场景之中。 因此,当考虑如何管理此类外部资源时,请务必优先采用 npm scripts 或者 yarn workspaces 来封装必要的脚本指令集,从而减少不必要的冲突风险。 --- ### 总结 以上便是围绕 Lemon 库展开的一系列基础指导,涵盖了从官方渠道获取必要组件直至初步运用整个流程的关键环节。希望对你有所帮助! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值