带有ANTLR 3的两级语法

时间:2016-07-27 15:32:21

标签: 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: and_expression EOF;

// lexer
OP_AND : 'AND';
TEXT : ( '"' (~'"')* '"' );

它有两种类型的表达式顶层(and_thing)和内层(​​and_level2_thing),其中适用不同的规则,但两个级别都必须支持AND,例如TOP_TYPE_EXPRESSION AND TOP_TYPE_EXPRESSIONTOP_TYPE_EXPRESSION AND (INNER_TYPE_EXPRESSION AND INNER_TYPE_EXPRESSION)

当我有表格的值时:

(TOP_TYPE_EXPRESSION AND (TOP_TYPE_EXPRESSION AND (TOP_TYPE_EXPRESSION AND (TOP_TYPE_EXPRESSION))))

时间开始在嵌套级别上成指数,可能是因为AND是模糊的。该表达式立即进行评估:

TOP_TYPE_EXPRESSION AND TOP_TYPE_EXPRESSION AND TOP_TYPE_EXPRESSION AND TOP_TYPE_EXPRESSION

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

2 个答案:

答案 0 :(得分:1)

添加memoize"修复"问题。但我确信这是一个更好的解决方案或更有趣的讨论。

import java.lang.*;

public class HA7BinaryErr {
    public static void main(String[] argv) {
        Scanner input = new Scanner(System.in);
        int number = 0;
        int factorOfTwo = 0;
        // get number to convert from user
        do {
            System.out.println("Enter the number to convert (0-255): ");
            number = input.nextInt();

        } while (number < 0 || number > 255);
        System.out.println("The number " + number + " converted to binary is : ");
        // convert to binary by successively dividing by larger factors of 2
        for (factorOfTwo = 1; factorOfTwo <= 128; factorOfTwo *= 2) {
            if (number / factorOfTwo >= 1) {
                System.out.print("1");
                number -= factorOfTwo;
            } else
                System.out.print("0");
        }

    } // end of main
}// end of class

答案 1 :(得分:1)

你的语法含糊不清:

"a" AND "b"

可以匹配为

parse_starts_here
  and_expression
    and_thing
      and_level2_thing
        text
      OP_AND
      and_level2_thing
        text

parse_starts_here
  and_expression
    and_thing
      and_level2_thing
        text
    OP_AND
    and_thing
      and_level2_thing
        text

通常情况下,ANTLR会警告你这种歧义,但是通过声明backtrack = true,你有效地告诉ANTLR尝试所有替代方案并首先使用匹配。

在明确的语法上,ANTLR以线性时间运行。回溯的使用导致潜在的指数时间。 memoize=true用于将时间减少到线性,代价是使用更多内存。

我建议删除backtrack=true选项。然后ANTLR会告诉你语法的含糊不清。您可以删除歧义,或者如果不可能,则仅在需要时使用语法谓词 以优先选择另一个可能的匹配。如果您最终使用语法谓词,memoize=true仍然有用。

编辑 - 至于为什么即使两种选择都匹配也会有回溯:

它没有回溯,但时间仍然是指数级的。

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

示例:

"a" AND ( "b" AND "c" )

为了匹配这一点,ANTLR必须:

  • 匹配"a"
  • 决定是否可以使用内部规则匹配AND
    • 为此,它会尝试匹配内部规则
    • AND次匹配,(表示转到and_thing
    • 要匹配and_thing,必须:
      • 匹配("b"
      • 决定是否可以使用内部规则匹配AND
        • 要执行此操作,它会尝试将内部规则与AND "c" 匹配
        • 谓词成功 - AND "c"匹配内部规则
      • 将内部规则与AND "c"
      • 相匹配
      • 匹配)
    • 谓词成功 - AND ( "b" AND "c" )匹配内部规则
  • 将内部规则与AND ( "b" AND "c" )匹配
    • AND次匹配,(表示转到and_thing
    • 要匹配and_thing,必须:
      • 匹配("b"
      • 决定是否可以使用内部规则匹配AND
        • 要执行此操作,它会尝试将内部规则与AND "c" 匹配
        • 谓词成功 - AND "c"匹配内部规则
      • 将内部规则与AND "c"
      • 相匹配
      • 匹配)

如过程中强调的部分所示,ANTLR需要将文本AND "c"匹配四次以匹配输入,同时还有一个嵌套级别。如果有另一个级别,整个过程将重复两次,因此ANTLR将解析最后一个级别八次。

一个相关的注释 - 如果使用语法谓词而不是回溯选项,则可以微调谓词所包含的内容 - 在某些情况下,它不需要包含所谓的整个规则。在上面的示例中,您可以告诉ANLTR在遇到OP_AND and_level2_thing时使用OP_AND规则,而无需检查and_level2_thing是否匹配。请注意,您只能执行此操作,因为您知道and_level2_thing将匹配,或者其他选项也会失败。如果你做错了,你最终会得到解析器迷失并拒绝输入,如果它选择了正确的替代方案,那将是有效的。