Scala 当某些语法出现时,ANLTR不会遍历整个树
我正在做一个项目,它可以阅读各种语言的源代码。项目本身是用scala编写的,但是如果您了解antlr,那么我现在所做的应该很熟悉。我在github上使用了Scala 当某些语法出现时,ANLTR不会遍历整个树,scala,antlr4,Scala,Antlr4,我正在做一个项目,它可以阅读各种语言的源代码。项目本身是用scala编写的,但是如果您了解antlr,那么我现在所做的应该很熟悉。我在github上使用了scala.g4语法来为antlr4生成解析器、词法分析器等。我已经编写了ScalaBaseListener的一个子类,它只在重写的Enter方法上打印 乙二醇 在我的应用程序的main中,我尝试从文件源遍历整个树,如下所示: import ScalaLexer._ import org.antlr.v4.runtime._ import or
scala.g4
语法来为antlr4生成解析器、词法分析器等。我已经编写了ScalaBaseListener的一个子类,它只在重写的Enter方法上打印
乙二醇
在我的应用程序的main中,我尝试从文件源遍历整个树,如下所示:
import ScalaLexer._
import org.antlr.v4.runtime._
import org.antlr.v4.runtime.tree._
import scala.io.Source
object Main extends App {
val fileContents = Source.fromFile(args(0)).getLines.mkString
val charStream = new ANTLRInputStream(fileContents)
val lexer = new ScalaLexer(charStream)
val tokens = new CommonTokenStream(lexer)
val parser = new ScalaParser(tokens)
val tree = parser.compilationUnit
ParseTreeWalker.DEFAULT
.walk(new ScalaMySubclassListener(), tree)
}
我发现,如果源文件是,比如说,只有几个类:
class Foo {
def bar = {
1
}
def baz = 1
}
class Foo1 {
def bar = {
1
}
def baz = 1
}
我可以从我的程序输出中看到,树上的每一片叶子都是走的
但是,如果我要在文件的顶部添加一个import语句(就像scala源文件中经常出现的那样)
只有import
语句中的叶子被遍历。文件的其余部分将被忽略
当我使用antlr4 GUI解析源文件时,整个树都是可见的。当解析树似乎被切断时,要做的第一件事是检查是否存在语法错误,因为这是最常见的原因。由于代码中根本没有错误处理,这意味着任何语法错误都应该打印到stderr。因为没有,所以显然没有任何语法错误 但是,我们还不能放弃出现语法错误的想法。在ANTLR中出现语法错误时,一个常见的陷阱是您的开始规则没有以
EOF
结束。如果是这样的话,ANTLR将简单地尝试查找语法上有效的输入前缀,而忽略其余的前缀。也就是说,它将在出现第一个语法错误时停止,而不会实际生成错误消息(只要存在导致该错误的有效程序-因为许多语法都接受空程序,这是非常常见的情况)。果然:如果我们看一下Scala.g4
语法中没有EOF(无论如何,在撰写本文时)。因此,让我们在编译单元
规则的末尾添加EOF
。现在,如果我们重新编译所有内容并再次运行代码,我们最终会得到一个语法错误:
line 1:20 mismatched input 'Foo' expecting {<EOF>, '.', ',', 'implicit', 'lazy', 'case', '@', 'override', 'abstract', 'final', 'sealed', 'private', 'protected', 'import', 'class', 'object', 'trait', 'package'}
第1行:20不匹配的输入'Foo'应为{,,,,,,,,'隐式','惰性','大小写','@',重写','抽象','最终','密封','私有','保护','导入','类','对象','特征','包'}
现在有两件事可能会让你感到好奇:
EOF
,GUI仍将显示正确的树)Foo
出现在第1行的第20列,而实际上出现在第3行fileContents
。您将看到,所有输入都在一行中,从import Thing开始
发生这种情况的原因是getLines
提供了一个没有行结尾的行列表,并且mkString
不使用任何分隔符将它们连接在一起。快速修复方法是将“\n”
作为分隔符传递给mkString
,但更好的解决方案是根本不自己读取文件
相反,您可以通过使用CharStreams.fromFileName
创建输入流来让ANTLR完成这项工作。这也将消除关于AntlInputStream
被弃用的警告
import Thing._
class Foo {
def bar = {
1
}
def baz = 1
}
class Foo1 {
def bar = {
1
}
def baz = 1
}
line 1:20 mismatched input 'Foo' expecting {<EOF>, '.', ',', 'implicit', 'lazy', 'case', '@', 'override', 'abstract', 'final', 'sealed', 'private', 'protected', 'import', 'class', 'object', 'trait', 'package'}