使用nom

时间:2017-01-13 01:22:33

标签: parsing rust

tl; dr :我正在努力寻找需要使用nom进行前瞻的文本解析器的文档或示例。

长版

我使用nom来解析6502程序集。我正在努力创建一个可以解析各种寻址模式的解析器。任何给定的操作码都具有以下格式:

XXX AM

其中XXX是三字符助记符,AM是操作数。操作数可以采用多种形式,称为"寻址模式。"我已经定义了操作数的枚举,寻址模式的枚举,以及包含这些值的OpCode元组结构,这最终是解析时返回的结果。

寻址模式可以完全省略,在这种情况下寻址模式为Implied,它的文字值可以是A,这是Accumulator寻址模式。

许多寻址模式都是指存储器位置,而这些寻址模式是我努力解析的。特别是,如果寻址模式以$00的形式指定单个字节,则它是ZeroPage寻址模式,而指定$0000形式的两个字节的操作数是{ {1}}寻址模式。为了使问题复杂化,这些寻址模式的索引变体以Absolute$00,X$00,Y等形式出现。

现有的文本解析器是否有一些很好的例子可以说明解析所有类似值($0000,X)的值的正确方法,但它们是如何结束的? nom文档不是很全面,我发现的最好的例子是INI解析器,它没有做任何像我试图完成的那样复杂的事情。我也查看了syn源代码,但它使用了很多自定义宏,并且是一个非常复杂的野兽,这使得它很难学习。

1 个答案:

答案 0 :(得分:2)

执行此操作的一种方法是使用alt!()宏。

这个想法是有一个解析器,它按顺序尝试每个替代方案。因此,如果您已经分别为每种寻址模式分配了解析器,则可以将它们组合成任何解析器:

// The sub-parsers all return Operand too.
named!(parse_operand<&str, Operand>,
    alt!(parse_absolute_indexed |
         parse_absolute |
         parse_zeropage_indexed |
         parse_zeropage |
         parse_implied));

一些注意事项:

  • 订单可能很重要;我把parse_absolute放在parse_absolute_indexed之后,因为前者会与操作数的初始部分匹配并且返回得太早。
  • 变体将包括与每个子解析器匹配的行尾(包括注释,如果适用)。然后它无法提前匹配。
  • 如果您正在解析输入的末尾而没有终止该模式的字节/字符(例如换行符),那么您可能需要使用alt_complete!()而不是alt!()。这样做的原因是,如果您尝试匹配ADD $00,可能与ADD $0000匹配的解析器必须假设如果有更多输入到达它仍然匹配,并且alt!()将不会跳过到下一个案例。使用alt_complete!(),或者在complete!()中包装内部匹配器,就是说不完整匹配是不匹配的。

如果解析器非常复杂,那么可能意味着要做一些额外的工作(按顺序尝试每个解析),而不是像例如古老的yacc生成的解析器,但我不认为这是一个问题。