Flex / Bison是否可以在8位嵌入式系统上使用?


83

我正在使用avr-gcc工具链针对C语言的AVR微控制器上的练习,为一种简单的BASIC语言编写小型解释器。但是,我想知道是否有任何开源工具可以帮助我编写词法分析器和解析器。

如果我将其编写为在Linux机器上运行,则可以使用flex / bison。现在,我将自己限制在一个8位平台上,我是否必须手工完成所有工作?


1
您打算使用特定的芯片吗?它有多少ROM / RAM?
史蒂夫·S

更新到@mre的链接。Embedded.com已删除其URL。(embedded.com/design/prototyping-and-development/4024523/...
pgvoorhees

似乎只有堆栈语言(forth&Co)才有机会使用2KB RAM,并带有内核闪存
Jacek Cz 2016年

Answers:


59

我已经针对ATmega328p的简单命令语言实现了解析器。该芯片具有32k ROM和仅2k RAM。RAM绝对是更重要的限制-如果您尚未绑定到特定芯片,则选择一个具有尽可能多RAM的芯片。这将使您的生活更加轻松。

一开始我考虑使用flex / bison。我决定拒绝此选项的原因有两个:

  • 默认情况下,Flex&Bison依赖于一些标准库函数(尤其是I / O),这些函数在avr-libc中不可用或无法正常工作。我很确定有受支持的解决方法,但这是您需要考虑的一些额外工作。
  • AVR具有哈佛架构。C不是为了解决这个问题而设计的,因此默认情况下即使常量变量也被加载到RAM中。您必须使用特殊的宏/功能来存储和访问FlashEEPROM中的数据。Flex&Bison创建了一些相对较大的查找表,这些表将很快耗尽您的RAM。除非我弄错了(这很可能),否则您必须编辑输出源才能利用特殊的Flash和EEPROM接口。

拒绝Flex&Bison之后,我去寻找其他生成器工具。我考虑了以下几点:

您可能还想看看Wikipedia的比较

最终,我最终对词法分析器和解析器进行了手工编码。

对于解析,我使用了递归下降解析器。我认为Ira Baxter已经做了足够的工作来涵盖这个主题,并且在线上有很多教程。

对于我的词法分析器,我为我所有的终端编写了正则表达式,绘制了等效的状态机,并将其实现为一个巨型函数,使用goto来在状态之间跳转。这很乏味,但结果效果很好。顺便说goto一句,这是一个用于实现状态机的好工具-您所有的状态都可以在相关代码的旁边带有清晰的标签,没有函数调用或状态变量的开销,并且它的速度最快。C确实没有更好的构造静态机器的构造。

需要考虑的事情:词法分析器实际上只是解析器的一种特殊化。最大的区别是常规语法通常足以进行词法分析,而大多数编程语言具有(大部分)上下文无关的语法。因此,实际上没有什么阻止您将词法分析器实现为递归下降解析器或使用解析器生成器编写词法分析器。它通常不如使用更专业的工具那样方便。


221

如果您想要一种简单的方法来编写解析器,或者空间有限,则应该手动编写递归下降解析器。这些本质上是LL(1)解析器。这对于像Basic这样“简单”的语言尤其有效。(我在70年代做过其中的几个!)。好消息是它们不包含任何库代码。就是你写的

如果您已经有语法,它们非常容易编码。首先,您必须摆脱左递归规则(例如X = XY)。通常这很容易做到,因此我将其保留为练习。(对于列表形成规则,您不必这样做;请参见下面的讨论)。

然后,如果您具有以下形式的BNF规则:

 X = A B C ;

为规则(X,A,B,C)中的每个项目创建一个子例程,该例程返回一个布尔值“我看到了相应的语法构造”。对于X,代码:

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;

如果您有BNF规则以及其他选择:

 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;

您可以在一两天内以这种方式编写数百个语法规则。有更多细节需要填写,但是这里的基础知识应该绰绰有余。

如果您真的很紧张,可以构建一个虚拟机来实现这些想法。这就是我70年代所做的,那时您可以获得8K 16位字。


如果您不想手工编写代码,则可以使用产生基本相同结果的元编译器Meta II)将其自动化。这些是令人兴奋的技术乐趣,甚至对于大型语法而言,确实消除了所有工作。

2014年8月:

我收到了许多有关“如何使用解析器构建AST”的请求。有关此问题的详细信息,本质上讲得很详细,请参阅我的其他SO回答https://stackoverflow.com/a/25106688/120163

2015年7月:

有很多人想要编写一个简单的表达式评估器。您可以通过执行上面的“ AST构建器”链接建议的相同操作来完成此操作。只是做算术而不是建立树节点。这是一个以此方式完成的表达式评估器


2
是的,为简单的语言手动递归递归下降解析器并不难。记住要尽可能优化尾调用-当只有几千字节的RAM时,堆栈空间非常重要。
史蒂夫·S 2010年

2
全部:是的,您可以进行尾部呼叫优化。除非您期望嵌套在解析的代码中变得更深,否则这无关紧要。对于BASIC代码行,很难找到超过10个parathenses的表达式,您总是可以输入深度限制计数来启动。的确,嵌入式系统往往具有较少的堆栈空间,因此至少要注意此处的选择。
艾拉·巴克斯特

2
@Mark:可能是2012年,但是我所引用的1965年技术论文现在还是当年,而且相当不错,特别是如果您不知道的话。
艾拉·巴克斯特

2
@Mark,啊,好的,谢谢!看起来日期神秘地固定了。谢谢,时间勋爵。
艾拉·巴克斯特

2
如何处理空字符串?
Dante

11

您可以在Linux及其本地gcc上使用flex / bison生成代码,然后将它们与AVR gcc交叉编译以用于嵌入式目标。


2

GCC可以交叉编译到各种平台,但是您可以在运行编译器的平台上运行flex和bison。他们只是吐出编译器然后生成的C代码。测试它以查看生成的可执行文件的实际大小。请注意,它们具有运行时库(libfl.a等),您还必须将它们交叉编译到目标。


我仍然必须调查这些库的大小,这就是为什么我首先问这个问题的原因。我想要一些专门针对小型MCU的产品。
约翰

-1

尝试Boost :: Spirit。这是一个仅标头的库,您可以插入该库并完全使用C ++构建一个非常快速,干净的解析器。使用C ++中的重载运算符代替特殊的语法文件。


您回答的问题是它不了解8位平台约束。很难获得同时支持boost和如此小的平台的工具链。
Waslap

-5

与其重新发明轮子,不如看看LUA:www.lua.org。它是一种解释性语言,旨在嵌入到其他软件中并在小型系统(例如嵌入式系统)中使用。内置的过程语法分析树,控制逻辑,数学和变量支持-无需重新发明成千上万已经调试和使用过的东西。它是可扩展的,这意味着您可以通过添加自己的C函数来添加语法。


2
像Lua一样小,我敢肯定它还是太大了。
icktoofay 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.