Parsing flex/bison是否有可用于8位嵌入式系统的替代方案?

Parsing flex/bison是否有可用于8位嵌入式系统的替代方案?,parsing,embedded,bison,flex-lexer,avr-gcc,Parsing,Embedded,Bison,Flex Lexer,Avr Gcc,我正在为一种简单的BASIC语言编写一个小的解释器,作为使用AVR gcc工具链在C语言中的AVR微控制器上的练习。然而,我想知道是否有任何开源工具可以帮助我编写lexer和解析器 如果我写这个在我的Linux机器上运行,我可以使用flex/bison。现在,我将自己限制在一个8位平台上,我必须手工完成这一切,或者不手工完成?您可以使用Linux上的flex/bison及其本机gcc生成代码,然后使用AVR gcc对嵌入式目标进行交叉编译。gcc可以交叉编译到各种平台,但是您在运行编译器的平台上

我正在为一种简单的BASIC语言编写一个小的解释器,作为使用AVR gcc工具链在C语言中的AVR微控制器上的练习。然而,我想知道是否有任何开源工具可以帮助我编写lexer和解析器


如果我写这个在我的Linux机器上运行,我可以使用flex/bison。现在,我将自己限制在一个8位平台上,我必须手工完成这一切,或者不手工完成?

您可以使用Linux上的flex/bison及其本机gcc生成代码,然后使用AVR gcc对嵌入式目标进行交叉编译。

gcc可以交叉编译到各种平台,但是您在运行编译器的平台上运行flex和bison。他们只是吐出编译器随后构建的C代码。测试它,看看结果的可执行文件到底有多大。请注意,它们有运行时库(
libfl.a
等),您还必须交叉编译到您的目标。

如果您想要一种简单的方法来编写解析器,或者您的空间有限,您应该手工编写递归下降解析器;这些基本上是(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,您往往一次只得到一行) 只需前进或不前进缓冲区扫描指针。 这段代码本质上是解析器的lexer部分

如果您的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;
有时您会遇到列表形成规则。 这些都是左递归的,这种情况很容易处理。其基本思想是使用迭代而不是递归,这样可以避免以“明显”的方式进行无限递归。 例如:

您可以使用迭代将其编码为:

subroutine L()
    if ~(A()) then return false;
    while (A()) do { /* loop */ }
    return true;
end L;
通过这种方式,您可以在一两天内编写几百条语法规则。 有更多的细节需要填写,但这里的基础应该足够了

如果你在空间上真的很紧,你可以构建一个虚拟机来实现这些想法。这就是我在70年代所做的,当时8K 16位单词是你能得到的


如果不想手工编写代码,可以使用元编译器()将其自动化,该元编译器可以生成基本相同的内容。这些都是令人兴奋的技术乐趣,真的需要做这一切的工作,即使是大型语法

2014年8月:

我收到很多关于“如何使用解析器构建AST”的请求。关于这一点的详细信息,主要阐述了这个答案,请参阅我的另一个答案

2015年7月:

有很多人想写一个简单的表达式计算器。您可以通过执行上面“AST builder”链接建议的相同类型的操作来实现这一点;只需进行算术运算,而不是构建树节点。
以下是。

我已经为一个简单的命令语言实现了一个解析器,该语言针对。该芯片有32k ROM和2k RAM。RAM绝对是更重要的限制——如果您还没有绑定到特定的芯片,请选择一个具有尽可能多的RAM的芯片。这会让你的生活更轻松

起初我考虑使用flex/bison。我出于两个主要原因决定反对这一选择:

  • 默认情况下,Flex&Bison依赖于一些标准库函数(特别是对于I/O),这些函数在avr-libc中不可用或不起作用。我很确定有支持的解决方法,但这是您需要考虑的一些额外工作
  • AVR有一个新的功能。C不是用来解释这一点的,所以默认情况下,即使是常量变量也会加载到RAM中。必须使用特殊的宏/函数在和中存储和访问数据。Flex&Bison会创建一些相对较大的查找表,这些表会很快消耗掉您的RAM。除非我弄错了(这是很有可能的),否则您必须编辑输出源以利用特殊的闪存和EEPROM接口
在拒绝Flex&Bison之后,我开始寻找其他生成器工具。以下是我考虑过的几个问题:

您可能还想看看

最终,我手工编写了lexer和parser

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

对于我的lexer,我为所有终端编写了正则表达式,绘制了等效状态机的图表,并使用用于在状态之间跳跃的
goto
,将其实现为一个巨大的函数。这很乏味,但效果很好。顺便说一句,
goto
是实现状态机的一个很好的工具——您的所有状态都可以在相关代码旁边有清晰的标签,没有函数调用或状态变量开销
subroutine L()
    if ~(A()) then return false;
    while (A()) do { /* loop */ }
    return true;
end L;