我正在使用avr-gcc工具链,用C语言在AVR微控制器上作为练习编写一个类似BASIC的小型解释器。
如果我要在Linux上运行该程序,我可以使用flex/bison。既然我限定了自己使用8位平台,那我应该如何编写解析器呢?
我正在使用avr-gcc工具链,用C语言在AVR微控制器上作为练习编写一个类似BASIC的小型解释器。
如果我要在Linux上运行该程序,我可以使用flex/bison。既然我限定了自己使用8位平台,那我应该如何编写解析器呢?
X = A B C ;
subroutine X()
if ~(A()) return false;
if ~(B()) { error(); return false; }
if ~(C()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end X;
同样的,对于 A、B、C 也是如此。
如果一个令牌是终止符号,编写代码检查输入流中组成该终止符号的字符串。例如,对于数字,检查输入流是否包含数字,并将输入流游标移过数字。如果您正在从缓冲区解析(对于 BASIC,您倾向于一次获取一行),则可以通过简单地前进或不前进缓冲区扫描指针来轻松完成此操作。这段代码本质上是解析器的词法分析部分。
如果您的 BNF 规则是递归的... 不要担心。只需编写递归调用即可。这处理语法规则,例如:
T = '(' T ')' ;
subroutine T()
if ~(left_paren()) return false;
if ~(T()) { error(); return false; }
if ~(right_paren()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end T;
P = Q | R ;
然后使用备选项编写P代码:
subroutine P()
if ~(Q())
{if ~(R()) return false;
return true;
}
return true;
end P;
L = A | L A ;
您可以使用迭代来编写此代码:
subroutine L()
if ~(A()) then return false;
while (A()) do { /* loop */ }
return true;
end L;
我已经为ATmega328p实现了一个简单命令语言的解析器。该芯片具有32k ROM和仅2k RAM。RAM这个限制是更重要的,如果您还没有绑定到特定的芯片,请选择尽可能多的RAM芯片,这将使您的生活变得更轻松。
起初我考虑使用Flex/Bison。出于两个主要原因,我决定放弃这个选项:
在拒绝了Flex&Bison之后,我开始寻找其他生成器工具。以下是我考虑过的几个:
您可能还想看一下维基百科的比较。
最终,我选择手工编写词法分析器和语法分析器。
对于语法分析,我使用了递归下降分析器。我认为Ira Baxter已经对此进行了充分的讨论,并且有很多在线教程。
对于我的词法分析器,我为所有终端符号编写了正则表达式,绘制了等效的状态机图,并将其实现为一个巨大的函数,使用goto
在不同状态之间跳转。虽然这很繁琐,但结果非常好。另外,goto
是实现状态机的好工具--所有状态的标签都可以清晰地显示在相关代码旁边,没有函数调用或状态变量开销,速度也尽可能快。在构建静态状态机方面,C确实没有更好的构造方法。
需要考虑的一件事情:词法分析器实际上只是解析器的一种特殊形式。最大的区别在于,正则语法通常足以进行词法分析,而大多数编程语言具有(基本上)上下文无关的语法结构。因此,您可以使用递归下降解析器作为词法分析器,或者使用解析器生成器编写词法分析器。只是通常不像使用更专业的工具那么方便。
您可以在Linux上使用本地gcc编译器使用flex/bison生成代码,然后使用AVR gcc进行交叉编译以用于嵌入式目标。
malloc
和free
,因为它不支持完整的C语言。(嵌入式系统实际上没有足够的内存来支持这些高级功能。)这可能会使交叉编译变得具有挑战性。 - Piotr Siupamalloc
和 free
,因为它不支持完整的 C 语言。(嵌入式系统实际上没有足够的内存来支持这些高级功能。)这可能会使交叉编译变得具有挑战性。 - undefinedGCC可以进行跨平台编译,但是你需要在运行编译器的平台上运行flex和bison。它们只是生成C代码,然后由编译器构建。测试一下生成的可执行文件的大小。请注意,它们有运行时库(libfl.a
等),你也需要将其交叉编译到目标平台。