Compiler construction 带ANTLR 3的两级语法

Compiler construction 带ANTLR 3的两级语法,compiler-construction,antlr,antlr3,Compiler Construction,Antlr,Antlr3,我有一个语法(如果你把它简化一点)如下: options { backtrack=true; } // parser text: (TEXT)+; and_level2_thing: text | '(' and_thing ')'; and_thing: and_level2_thing (OP_AND and_level2_thing)*; and_expression: and_thing (OP_AND and_thing)*; parse_starts_here: a

我有一个语法(如果你把它简化一点)如下:

options
{
    backtrack=true;
}

// parser
text: (TEXT)+;

and_level2_thing: text | '(' and_thing ')';

and_thing: and_level2_thing (OP_AND and_level2_thing)*;

and_expression: and_thing (OP_AND and_thing)*;

parse_starts_here: and_expression EOF;

// lexer
OP_AND : 'AND';
TEXT : ( '"' (~'"')* '"' );
它有两种类型的表达式组顶级(
和_-thing
)和内部级别(
和_-level2 _-thing
),适用不同的规则,但这两种级别都必须支持,例如
顶级类型表达式和顶级类型表达式
,以及
顶部类型表达式和(内部类型表达式和内部类型表达式)

当我具有以下形式的值时:

(TOP_TYPE_表达式和(TOP_TYPE_表达式和(TOP_TYPE_表达式和(TOP_TYPE_表达式)))

时间在嵌套级别上开始呈指数增长,这可能是因为AND是不明确的。此表达式立即计算:

TOP_-TYPE_表达式和TOP_-TYPE_表达式以及TOP_-TYPE_表达式和TOP_-TYPE_表达式

如果你说这不是一种设计良好的语言——我完全同意,但这就是我现在所拥有的:)。有没有办法避免这个问题?

添加memoize“修复”这个问题。但我相信有更好的解决方案或更有趣的讨论

options
{
    backtrack=true;
    memoize=true;
}

你的语法模棱两可:

"a" AND "b"
可匹配为

parse_starts_here
  and_expression
    and_thing
      and_level2_thing
        text
      OP_AND
      and_level2_thing
        text
或作为

通常,ANTLR会警告您这种模糊性,但通过声明
backtrack=true
,您可以有效地告诉ANTLR尝试所有备选方案,并首先使用匹配的

在明确的语法上,ANTLR在线性时间内运行。使用回溯会导致潜在的指数时间
memoize=true
用于减少恢复线性的时间,但会消耗更多内存

我建议删除
backtrack=true
选项。然后,ANTLR将告诉您语法不明确的地方。您可以消除歧义,或者如果不可能,则仅在需要时使用语法谓词,以使一种可能的匹配优于另一种<如果最终使用语法谓词,code>memoize=true仍然会有帮助


编辑-关于为什么即使两个备选方案匹配也会出现回溯:

它不会倒退,但时间仍将是指数级的

问题是,ANTLR直到实际尝试匹配它时才知道它可以匹配第一个备选方案(因为您没有给它任何提示)。因此,它将首先尝试匹配该规则,如果成功,它将实际匹配该规则并执行所有相关操作(
memoize
选项通过记住给定输入位置的特定成功规则,而不是重复整个匹配过程来避免这一点)

例如:

"a" AND ( "b" AND "c" )
为此,ANTLR必须:

  • 匹配
    “a”
  • 确定是否可以使用内部规则匹配
    • 为此,它尝试匹配内部规则
    • 匹配,
      表示转到
      和_thing
    • 要匹配
      和\u thing
      ,它必须:
      • 匹配
        “b”
      • 确定是否可以使用内部规则匹配
        • 为此,它尝试将内部规则与
          和“c”匹配
        • 谓词成功-
          且“c”
          与内部规则匹配
      • 将内部规则与
        和“c”匹配
      • 匹配
    • 谓词成功-
      和(“b”和“c”)
      匹配内部规则
  • 将内部规则与
    和(“b”和“c”)
    • 匹配,
      表示转到
      和_thing
    • 要匹配
      和\u thing
      ,它必须:
      • 匹配
        “b”
      • 确定是否可以使用内部规则匹配
        • 为此,它尝试将内部规则与
          和“c”匹配
        • 谓词成功-
          且“c”
          与内部规则匹配
      • 将内部规则与
        和“c”匹配
      • 匹配
正如流程中强调的部分所示,ANTLR需要将文本
和“c”
匹配四次才能匹配输入,而嵌套有一个级别。如果有另一个级别,整个流程将重复两次,因此ANTLR将解析最后一部分八次



一个相关的注释-如果您使用语法谓词而不是回溯选项,您可以微调谓词包含的内容-在某些情况下,它不需要包含整个谓词规则。在上面的示例中,您可以告诉ANLTR在遇到
OP\u和
时使用
OP\u和
级别2\u事物
规则无需检查
和_level2_thing
是否匹配。请注意,您只能这样做,因为您知道
和_level2_thing
将匹配,或者其他替代方案也将失败。如果您这样做错误,解析器将丢失并拒绝一个如果选择正确的替代方案则有效的输入tive.

非常感谢@Jiri tusek。“然后ANTLR会告诉您语法不明确的地方。”-您是说在编译时,对吗?这里有一些关于删除回溯的信息。“告诉ANTLR尝试所有替代方法并首先使用匹配的方法”生成语法时,它会报告这些情况(不是在程序的编译时,而是在生成Java解析器类时)。我编辑了这篇文章,回答“如果第一个选项匹配,为什么它仍然回溯”——它不会回溯,但时间仍然是指数级的。
"a" AND ( "b" AND "c" )