我是编写语法的新手,我读过大约一半的Antlr4:权威指南,我想我会对我正在研究的语法有所了解。我被困在一些听起来很基本但事实证明比我想象的更难的事情上。
我正在尝试解析smali(android反汇编)类声明。它看起来像这样:
.class public Lcom/packageName/example;
smali中的规则是所有完全限定的类都以L
为前缀,然后包部分用/
分隔,然后类名是{{{}之前的最后一部分。 1}}。
所以我有一个普通;
在我的词法分析器部分,但是我在解析WS : [ \t\r\n]+ -> skip ;
时如何禁用它。我想要将其停用,以便在没有fullyQualifiedClass
的情况下返回第一个classPackageComponent
,但如果忘记L
,则会显示L
。包名称相同,包名称与其Error: 'L' expected'
分隔符之间不能有空格。我知道问题是我的Parser甚至没有看到WS角色,因为Lexer只是抛弃它们。我该如何处理这个问题?渠道是答案吗?我还没有完成这一章,但其他SO帖子表明它可能是。
我的语法代码不正确:
/
答案 0 :(得分:1)
首先要解决几个问题:
词法
Class: '.class' ;
Semi: ';' ;
Modifier: 'public' | 'private';
Slash: '/' ;
ClassPrefix : 'L' { isPrefix() }? ;
Identifier : Letter (Letter|JavaIDDigit)* ;
WS : [ \t\r\n]+ -> skip ;
...
Lexing .class public Lcom/packageName/example;
(如果isPrefix()
总是返回false)将生成令牌流:
Class, Modifier, Identifier, Slash, Identifier, Slash, Identifier, Semi
这就是Parser所看到的。因此,解析器规则变为
classDeclaration : Class Modifier fullyQualifiedClass ;
fullyQualifiedClass: ClassPrefix? (Identifier Slash)* Identifier Semi ;
ClassPrefix
的问题在于没有可用于将其分离的自然分隔符。但是,有很多方法可以解决这个问题。
也许最直接的方法是Lexer检查,每次Lexer看到一个' L'它是否处于类似名称的开头。那就是谓词' {isPrefix()}?打算这样做。查看here和here以获取谓词实现的示例。
另一种方法是完全从Lexer中删除ClassPrefix
规则,并在解析器规则操作中检测前缀,或者更好的是,在解析树的后续步骤中检测:
fullyQualifiedClass: (Identifier Slash)* Identifier Semi ;
标识符的第一个实例是一个包含令牌匹配的基础文本的令牌。可以调用生成的解析器类'YourParser'.FullyQualifiedClassContext.Identifier()
的每个实例,以按从左到右的顺序返回标识符标记的List。检查最左边的前缀并相应处理。