如何在ANTLR中构造一个干净的Python语法?

时间:2009-07-23 03:27:29

标签: antlr grammar

天儿真好!

如何构建一个简单的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中:

Decision Can Match NL Using Multiple Alternatives http://img.skitch.com/20090723-ghpss46833si9f9ebk48x28b82.png

我已经开始使用语法,但总是违反预期的行为:

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

我可以找到具有许多这些特征的示例ANTLR语法。我发现当我把它们剪下来以限制它们对我所需要的表现力时,我最终会破坏某些东西。其他人太简单了,当我增加表现力时,我会打破它们。

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

3 个答案:

答案 0 :(得分:6)

我会让你的标记器完成繁重的操作而不是将你的换行规则混合到你的语法中:

  • 计算括号,括号和大括号,并且在有未关闭的组时不生成NL令牌。这将免费为您提供续行,而您的语法则不会更明智。

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

第二点可以让你简化你的语法:

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

答案 1 :(得分:0)

这个怎么样?

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

答案 2 :(得分:0)

我假设您选择使NL成为可选项,因为输入代码中的最后一个语句不必以换行符结束。

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

在你的情况下,解析器不知道它是否应解析"赋值NL"或"赋值empty_line"。有很多方法可以解决这个问题,但大多数方法都只是一种不明智的设计选择。

我的建议是无辜的黑客攻击:强制使用NL,并且总是将NL附加到输入流的末尾!

这看起来有点令人讨厌,但实际上它会为你带来许多未来的麻烦。