【问题标题】:How to get Kotlin AST?如何获得 Kotlin AST?
【发布时间】:2015-09-19 06:04:12
【问题描述】:

我有一个带有 Kotlin 源代码的字符串。如何在运行时编译它并获取抽象语法树和类型信息以进行分析?

【问题讨论】:

标签: abstract-syntax-tree kotlin


【解决方案1】:

我对 Kotlin 编译器进行了一些调查。在我的GitHub repo 上可以看到一些获得 AST 的概念证明。

这只是一个草图,但可能会有所帮助:

class KotlinScriptParser {
    companion object {
        private val LOG = Logger.getLogger(KotlinScriptParser.javaClass.name)
        private val messageCollector = object : MessageCollector {
            override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
                val path = location.path
                val position = if (path == null) "" else "$path: (${location.line}, ${location.column}) "

                val text = position + message

                if (CompilerMessageSeverity.VERBOSE.contains(severity)) {
                    LOG.finest(text)
                } else if (CompilerMessageSeverity.ERRORS.contains(severity)) {
                    LOG.severe(text)
                } else if (severity == CompilerMessageSeverity.INFO) {
                    LOG.info(text)
                } else {
                    LOG.warning(text)
                }
            }
        }

        private val classPath: ArrayList<File> by lazy {
            val classpath = arrayListOf<File>()
            classpath += PathUtil.getResourcePathForClass(AnnotationTarget.CLASS.javaClass)
            classpath
        }
    }

    fun parse(vararg files: String): TopDownAnalysisContext {
        // The Kotlin compiler configuration
        val configuration = CompilerConfiguration()

        val groupingCollector = GroupingMessageCollector(messageCollector)
        val severityCollector = MessageSeverityCollector(groupingCollector)
        configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, severityCollector)


        configuration.addJvmClasspathRoots(PathUtil.getJdkClassesRoots())
        // The path to .kt files sources
        files.forEach { configuration.addKotlinSourceRoot(it) }
        // Configuring Kotlin class path
        configuration.addJvmClasspathRoots(classPath)
        configuration.put(JVMConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME)
        configuration.put<List<AnalyzerScriptParameter>>(JVMConfigurationKeys.SCRIPT_PARAMETERS, CommandLineScriptUtils.scriptParameters())

        val rootDisposable = Disposer.newDisposable()
        try {
            val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
            val ktFiles = environment.getSourceFiles()
            val sharedTrace = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
            val moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(environment.project,
                    environment.getModuleName())

            val project = moduleContext.project
            val allFiles = JvmAnalyzerFacade.getAllFilesToAnalyze(project, null, ktFiles)
            val providerFactory = FileBasedDeclarationProviderFactory(moduleContext.storageManager, allFiles)
            val lookupTracker = LookupTracker.DO_NOTHING
            val packagePartProvider = JvmPackagePartProvider(environment)
            val container = createContainerForTopDownAnalyzerForJvm(
                    moduleContext,
                    sharedTrace,
                    providerFactory,
                    GlobalSearchScope.allScope(project),
                    lookupTracker,
                    packagePartProvider)

            val additionalProviders = ArrayList<PackageFragmentProvider>()

            additionalProviders.add(container.javaDescriptorResolver.packageFragmentProvider)

            return container.lazyTopDownAnalyzerForTopLevel.analyzeFiles(TopDownAnalysisMode.LocalDeclarations, allFiles, additionalProviders)
        } finally {
            rootDisposable.dispose()
            if (severityCollector.anyReported(CompilerMessageSeverity.ERROR)) {
                throw RuntimeException("Compilation error")
            }
        }
    }
}


fun main(args: Array<String>) {
    val scriptFile = "/media/data/java/blackfern/kotlin-compile-test/test.kt"

    val parser = KotlinScriptParser()
    // Getting a root element of the AST
    val analyzeContext = parser.parse(scriptFile)
    // Sample AST investigation
    val function = analyzeContext.functions.keys.first()
    val body = function.bodyExpression as KtBlockExpression
}

【讨论】:

  • 请在答案本身中包含链接资源的基本部分,应避免所谓的“仅链接”答案。
【解决方案2】:

目前没有标准的 API 可以做到这一点。您可以使用 Kotlin 编译器和 REPL source code 来尝试实现这一目标。

【讨论】:

  • 任何起点?相关的单元测试,也许,类名?我知道没有 API,但如果能提供任何关于从哪里开始或查看哪些类的提示,我将不胜感激。
  • 嗯,老实说,这相当复杂。您可以查看我们的ExpressionTypingVisitor 类是如何工作的,还有 PSI 的概念——具体的语法树,以及 BindingContext——从 PSI 到类型信息和其他语义信息的映射。 PSI 类以Jet 为前缀
  • 现在是什么状态?是否有或将有用于此目的的 api?
  • @user3464741 没有真正的改变,我们可能会在某个时候公开这样的 API,但目前这不是一个高优先级任务
  • @Andrey 有什么地方可以让用户投票支持这样的功能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 2011-08-04
  • 1970-01-01
  • 2023-02-06
  • 2018-09-10
相关资源
最近更新 更多