提高语义谓语的表现

时间:2014-04-16 18:55:48

标签: c# antlr antlr4

我正在研究一种具有用户定义函数调用的语言。在解析时,这些标识符中的每一个都已知。我的目标是在词法分析阶段对用户定义的标识符的每个实例进行标记。为此,我使用了与this answer中类似的方法,并进行了以下更改:

// Lexer.g4
USER_FUNCTION : [a-zA-Z0-9_]+ {IsUserDefinedFunction()}?;


// Lexer.g4.cs
bool IsUserDefinedFunction()
{
    foreach (string function in listOfUserDefinedFunctions)
    {
        if (this.Text == function)
        {
            return true;
        }
    }
    return false;
}

然而,我发现只有语义谓词{IsUserDefinedFunction()}?才能使解析速度极慢(约1-20毫秒没有,约2秒)。将IsUserDefinedFunction()定义为始终返回false没有任何影响,所以我肯定问题在于解析器。无论如何都要加速解析这些关键字?

正在解析的语言的一个主要问题是它不会在令牌之间使用大量的空格,因此用户定义的函数可能以语言定义的关键字开头。

例如:鉴于语言定义的关键字GOTO和用户定义的函数GOTO20Something,典型的程序文本可能如下所示:

GOTO20
GOTO30
GOTO20Something
GOTO20GOTO20Something

并应标记为GOTO NUMBER GOTO NUMBER USER_FUNCTION GOTO NUMBER USER_FUNCTION

修改以澄清:

甚至将IsUserDefinedFunction()重写为:

bool IsUserDefinedFunction() { return false; }

我仍然得到同样缓慢的表现。

另外,为了澄清,我将性能基线与动态关键字“硬编码”到Lexer中进行比较,如下所示:

// Lexer.g4 - Poor Performance (2000 line input, ~ 2 seconds)
USER_FUNCTION : [a-zA-Z0-9_]+ {IsUserDefinedFunction()}?;

// Lexer.g4 - Good Performance (2000 line input, ~ 20 milliseconds)
USER_FUNCTION
    :   'ActualUserKeyword'
    |   'AnotherActualUserKeyword'
    |   'MoreKeywords'
    ...
    ;

使用语义谓词可以提供正确的行为,但速度非常慢,因为必须检查每个字母数字字符。还有另一种处理运行时添加的令牌的方法吗?

2 个答案:

答案 0 :(得分:0)

编辑:为了回应此语言中没有任何其他标识符,我会采用不同的方法。

  1. 使用原始语法,但完全删除语义谓词。这意味着有效和无效的用户定义函数标识符都将生成USER_FUNCTION令牌。
  2. 在解析完成后使用侦听器或访问者来验证解析树中USER_FUNCTION的实例,并在代码使用尚未定义的函数时报告错误。
  3. 这种策略可以产生更好的错误消息,极大地提高了词法分析器和解析器从这些类型的错误中恢复的能力,并从文件中生成一个可用的解析树(即使它不是完全语义上的)有效,它仍可用于分析,报告,并可能在未来支持IDE功能。)


    原始答案假设非USER_FUNCTION的标识符应生成IDENTIFIER令牌。

    问题是在lexing阶段,谓词在每个字母,数字和下划线之后执行。您可以通过将USER_FUNCTION声明为令牌(并从语法中删除USER_FUNCTION规则)来提高性能:

    tokens {
      USER_FUNCTION
    }
    

    然后,在Lexer.g4.cs文件中,覆盖Emit()方法以执行测试,并在必要时覆盖令牌类型。

    public override IToken Emit() {
      if (_type == IDENTIFIER && IsUserDefinedFunction())
        _type = USER_FUNCTION;
    
      return base.Emit();
    }
    

答案 1 :(得分:0)

我对这种特定语言的解决方案是使用System.Text.RegularExpressions.Regex在输入字符串中用特殊字符(我选择§(\u00A7)字符)包围用户定义函数的所有实例。

然后词法分析器定义:

USER_FUNCTION : '\u00A7' [a-zA_Z0-9_]+ '\u00A7';

在解析器监听器中,我从函数名中删除了周围的§。