Как получить Kotlin AST?

У меня есть строка с источником Kotlin . Как я могу скомпилировать его во время выполнения и получить абстрактное синтаксическое дерево и типы информации для анализа?

У меня есть некоторое исследование компилятора Kotlin. Некоторое доказательство концепции получения АСТ можно увидеть на моем репо GitHub .

Это только эскиз, но может быть полезным:

 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 } к 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 } 

На данный момент нет стандартного API. Вы можете играть с компилятором Kotlin и исходным кодом REPL, чтобы попытаться достичь этого.