ANTLR4的类似Python的缩进标记
antlr-denter的Python项目详细描述
类Python标记的缩进
antlrv4中INDENT/DEDENT令牌的一个基本现成的解决方案。只要插上DenterHelper
就可以走了!关于这个项目背后的一些动机,请参见this blog post。在
antlr helper是在the MIT license下发布的,这基本上意味着你可以用它做任何你想做的事情。也就是说,如果你觉得这个项目有用的话,我会非常感谢你的来信!也许能成为这个项目的明星?在
用法(Java)
马文
<dependency>
<groupId>com.yuvalshavit</groupId>
<artifactId>antlr-denter</artifactId>
<version>1.1</version>
</dependency>
向lexer添加INDENT/DEDENT标记
- 在语法中定义缩进和删除标记
- 在
@lexer::members
部分中,实例化一个DenterHelper
,其pullToken
方法委托给lexer的super.nextToken()
- 重写lexer的
super.nextToken
方法以使用DenterHelper::nextToken
。在 - 修改您的
NL
标记以获取后面的任何空白(换句话说,让它以' '*
,'\t'*
或类似的结尾)。在
DenterHelper
是一个抽象类,它的构造函数也有三个参数:newline、INDENT和DEDENT的标记类型。将它实例化为匿名类可能是最容易的。整个事情应该是这样的:
Java builder 8也特别有用:
tokens { INDENT, DEDENT }
@lexer::header {
import com.yuvalshavit.antlr4.DenterHelper;
}
@lexer::members {
private final DenterHelper denter = DenterHelper.builder()
.nl(NL)
.indent(MyCoolParser.INDENT)
.dedent(MyCoolParser.DEDENT)
.pullToken(MyCoolLexer.super::nextToken);
@Override
public Token nextToken() {
return denter.nextToken();
}
}
NL: ('\r'? '\n' ' '*);
用法(Python3)
tokens { INDENT, DEDENT }
@lexer::header{
from DenterHelper import DenterHelper
from MyCoolParser import MyCoolParser
}
@lexer::members {
class MyCoolDenter(DenterHelper):
def __init__(self, lexer, nl_token, indent_token, dedent_token, ignore_eof):
super().__init__(nl_token, indent_token, dedent_token, ignore_eof)
self.lexer: MyCoolLexer = lexer
def pull_token(self):
return super(MyCoolLexer, self.lexer).nextToken()
denter = None
def nextToken(self):
if not self.denter:
self.denter = self.MyCoolDenter(self, self.NL, MyCoolParser.INDENT, MyCoolParser.DEDENT, ***Should Ignore EOF***)
return self.denter.next_token()
}
在解析器中使用标记
基本上,只要使用它们。有一点值得注意的是,当denter注入DEDENT标记时,它将用一个NL
作为前缀。如果没有要插入的DEDENTs,则在EOF标记之前还插入一个NL
。例如,给定以下输入:
hello
world
universe
dolly
。。。代币大概是:
"hello"
INDENT
"world"
INDENT
"universe"
NL
DEDENT
DEDENT
"dolly"
NL
<eof>
这样做是为了使简单表达式可以由NL
标记终止,而不必担心周围的上下文(即将发生的dedent或EOF)。在本例中,universe
和{statement: expr NL | helloBlock;
的内容。别紧张!在
“半个树根”
当你放弃一个从未建立的缩进水平时会发生什么?在
someStatement()
if foo():
if bar():
fooAndBar()
bogusLine()
请注意,bogusLine()
与任何缩进级别都不匹配:它比if foo()
缩进得多,但比它的第一个语句if bar()
少。在
这是python中的一个有缺陷的程序。如果你运行这样一个程序,你会得到:
IndentationError: unindent does not match any outer indentation level
DenterHelper
处理器通过插入两个令牌来处理这一问题:一个DEDENT
紧跟着一个INDENT
(这里的总序列实际上是两个DEDENT
,后面跟着一个INDENT
,因为bogusLine()
是从fooAndBar()
中提取两次的)。其基本原理是该行已向其父行倾斜,然后缩进。它与缩进标记一致,例如:
someStatement()
bogusLine()
如果您的缩进方案与python类似,那么您很可能希望这是一个编译错误。好消息是,只要解析器不允许“自发”缩进。也就是说,如果这一段前面的例子失败了,那么上面的半个例子也会失败。在这两种情况下,解析器规则都会遇到意外的INDENT
标记。在
回购布局
- ^{str1}$Java/core:真实的东西。这就是你感兴趣的。Maven工件
antlr-denter
。在 - Java/examples:包含一个使用
DenterHelper
的语言的真实示例,因此您可以看到完整的解决方案,包括pom、如何设置解析器(与通常的antlr内容相比没有什么额外的内容)以及如何定义使用这些INDENT/DEDENT标记的语言。语言本身是相当基础的,但它应该能让人理解。Maven工件antlr-denter-example-examples
。在 - Python3:Python3实现
maven运行就像mvn install
(或您最喜欢的目标)一样简单。在
评论?建议?漏洞?在
不要羞于打开一个i嘘!在
- 项目
标签: