8

天!

如何构建一个简单的 ANTLR 语法来处理多行表达式而不需要分号或反斜杠?

我正在尝试为表达式编写一个简单的 DSL:

# sh style comments
ThisValue = 1
ThatValue = ThisValue * 2
ThisOtherValue = (1 + 2 + ThisValue * ThatValue)
YetAnotherValue = MAX(ThisOtherValue, ThatValue)

总的来说,我希望我的应用程序为脚本提供一些初始命名值并提取最终结果。但是,我对语法很感兴趣。我想支持多行表达式,如下所示:

# Note: no backslashes required to continue expression, as we're in brackets
# Note: no semicolon required at end of expression, either
ThisValueWithAReallyLongName = (ThisOtherValueWithASimilarlyLongName
                               +AnotherValueWithAGratuitouslyLongName)

我从这样的 ANTLR 语法开始:

exprlist
    : ( assignment_statement | empty_line )* EOF!
    ;
assignment_statement
    : assignment NL!?
    ;
empty_line
    : NL;
assignment
    : ID '=' expr
    ;

// ... and so on

看起来很简单,但我已经遇到了换行的麻烦:

warning(200): StackOverflowQuestion.g:11:20: Decision can match input such as "NL" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input

以图形方式,在 org.antlr.works.IDE 中:

决策可以使用多种选择匹配 NL http://img.skitch.com/20090723-ghpss46833si9f9ebk48x28b82.png

我已经踢了语法,但总是以违反预期行为而告终:

  • 文件末尾不需要换行符
  • 空行是可以接受的
  • 从井号开始的一行中的所有内容都被丢弃为注释
  • 作业以换行符结尾,而不是分号
  • 如果用括号括起来,表达式可以跨越多行

我可以找到具有许多这些特征的示例 ANTLR 语法。我发现当我削减它们以将它们的表现力限制在我需要的范围内时,我最终会破坏某些东西。其他的太简单了,我在添加表现力的同时打破它们。

我应该从哪个角度来理解这个语法?你能指出任何不是琐碎或完整的图灵完备语言的例子吗?

4

3 回答 3

6

我会让你的分词器完成繁重的工作,而不是将你的换行规则混合到你的语法中:

  • 计算括号、方括号和大括号,并且在存在未闭合组时不要生成 NL 标记。这将免费为您提供续行,而您的语法不会变得更聪明。

  • 无论最后一行是否以字符结尾,始终在文件末尾生成一个 NL 标记'\n',那么您不必担心没有 NL 的语句的特殊情况。语句总是以 NL 结尾。

第二点可以让您将语法简化为如下所示:

exprlist
    : ( assignment_statement | empty_line )* EOF!
    ;
assignment_statement
    : assignment NL
    ;
empty_line
    : NL
    ;
assignment
    : ID '=' expr
    ;
于 2009-07-23T03:35:36.783 回答
0

这个怎么样?

exprlist
    : (expr)? (NL+ expr)* NL!? EOF!
    ;
expr 
    : assignment | ...
    ;
assignment
    : ID '=' expr
    ;
于 2009-07-23T03:39:43.543 回答
0

我假设您选择将 NL 设为可选,因为输入代码中的最后一条语句不必以换行符结尾。

虽然这很有意义,但你让解析器的生活变得更加困难。应该珍惜分隔符(如 NL),因为它们可以消除歧义并减少发生冲突的机会。

在您的情况下,解析器不知道它是否应该解析“assignment NL”或“assignment empty_line”。有很多方法可以解决它,但其中大多数只是不明智的设计选择的创可贴。

我的建议是一个无辜的 hack:强制使用 NL,并始终将 NL 附加到输入流的末尾!

这可能看起来有点令人讨厌,但实际上它会为您节省很多未来的麻烦。

于 2017-01-10T14:23:36.300 回答