Scala 当某些语法出现时,ANLTR不会遍历整个树

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编写的,但是如果您了解antlr,那么我现在所做的应该很熟悉。我在github上使用了
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'应为{,,,,,,,,'隐式','惰性','大小写','@',重写','抽象','最终','密封','私有','保护','导入','类','对象','特征','包'}
现在有两件事可能会让你感到好奇:

  • 为什么ANTLR在从您的代码运行时检测到语法错误,而不是从TestRig GUI运行时检测到语法错误(即使添加了
    EOF
    ,GUI仍将显示正确的树)
  • 为什么错误消息声称
    Foo
    出现在第1行的第20列,而实际上出现在第3行
  • 这两个问题的答案是相同的:您输入的ANTLR不是测试文件中的内容。要验证这一点,请在读入后尝试打印
    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'}