【问题标题】:Parsing text that requires lookahead using nom使用 nom 解析需要前瞻的文本
【发布时间】:2017-01-13 01:22:32
【问题描述】:

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

加长版

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

XXX AM

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

寻址方式可以完全省略,此时寻址方式为Implied,可以有一个字面值A,即Accumulator寻址方式。

许多寻址模式都涉及内存位置,而我正在努力解析这些寻址模式。特别是,如果寻址模式以$00 的形式指定单个字节,则它是ZeroPage 寻址模式,而以$0000 的形式指定两个字节的操作数是Absolute 寻址模式。更复杂的是,这些寻址模式的索引变体以$00,X$00,Y$0000,X 等形式存在。

是否有任何现有文本解析器的好例子可以说明解析值的正确方法,这些值都以相似的方式开始 ($00...),但它们的结束方式有所不同? nom 文档不是很全面,我发现的最好的例子是 INI 解析器,它并没有像我试图完成的那样复杂。我也看过 syn 源代码,但它使用了很多自定义宏,而且是一个相当复杂的野兽,因此很难学习。

【问题讨论】:

  • 虽然不使用 nom ... the 6502 emulator, assembler and disassembler crate that I wrote 手动滚动这个。它可能不是 Rust 中解析的最佳示例,但它确实设法做到了(注意:我仍处于清理阶段 - 但现在假期结束了,它的进展要慢得多!)。 assembler 文件夹是这一切发生的地方——它非常明确且非常嵌套,但可能以 some 方式有所帮助?值得注意的是——我不往前看。我只是标记它并在我去的时候弄清楚。所以我不确定你追求的是什么?
  • 我想我假设我正在尝试使用 nom 做的事情需要前瞻,或者至少是我目前正在接近它的方式。出于好奇,您的汇编程序是独立的吗?我真的只是在寻找一些简单的东西,我可以用它来为我正在编写的 NES 模拟器创建漂亮且可读的单元测试。
  • 在这种情况下,您必须为我定义“自包含”。它是自包含的,因为它都是用 Rust 编写的。它...不是独立的,因为它不在 1 个文件中 - 它依赖于 lexer.rsparser.rstoken.rsassembler.rs 文件中的类型......等等。它应该很简单足以让您将其包含为依赖项和使用-尽管我在不知道您的具体用例的情况下这么说。将 6502 助记符解析为输入时,汇编器(目前)非常有限。例如,它不像某些汇编程序那样具有花哨的宏或条件构造。
  • 虽然在 crate 中有很多示例用法 - 我为该 crate 的每个部分编写了很多单元测试 - 直到单个操作码。所以你应该能够看到它的简单用法。
  • 一种选择是使用alt!(),并以正确的顺序使用替代方案 - 因此在较短的模式之前包括索引模式。

标签: parsing rust


【解决方案1】:

一种方法是使用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 之后,因为前者会匹配操作数的初始部分并且返回得太早。
  • 一种变体是将行尾匹配(如果适用,包括 cmets)包含到每个子解析器中。然后就不能早点匹配了。
  • 如果您解析到输入的末尾没有终止模式的字节/字符(例如换行符),那么您可能需要使用alt_complete!() 而不是alt!()。这样做的原因是,如果您尝试匹配ADD $00,可能匹配ADD $0000 的解析器必须假设如果有更多输入到达它可能仍然匹配,并且alt!() 不会跳到下一个案例。使用alt_complete!(),或者将内部匹配器包装在complete!() 中,就是说不完整的匹配是不匹配的。

如果解析器非常复杂,与由例如古老的 yacc 生成的解析器相比,它可能意味着要做额外的工作(按顺序尝试每个解析),但我认为在这种情况下这不是问题。

【讨论】:

  • 我应该注意的一点是,我必须使用alt_complete!,同时按照您的说明正确排序解析器。如果没有alt_complete,在IResult::Incomplete 的某些内存寻址模式下解析会失败。
  • 谢谢;我添加了一个注释。如果看起来不对,请告诉我!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
相关资源
最近更新 更多