У меня есть строка с Kotlin
источник в ней. Как я могу скомпилировать его во время выполнения и получить абстрактное синтаксическое дерево и типы информации для анализа?Как получить Kotlin AST?
ответ
У меня есть некоторое расследование компилятора Kotlin. Некоторое доказательство концепции получения АСТ можно увидеть на моем GitHub repo.
Это эскиз только, но может быть полезно:
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 source code, чтобы попытаться достичь этого.
Любые стартовые точки? Соответствующие модульные тесты, может быть, имена классов? Я понимаю, что API отсутствует, но я буду признателен за любые подсказки о том, с чего начать или какие классы искать. – Iurii
Ну, это довольно сложно, если честно. Вы можете посмотреть, как работают наши классы ExpressionTypingVisitor, также существует концепция PSI - конкретного дерева синтаксиса, а BindingContext - отображение из PSI для ввода информации другой семантической информации. Классы PSI имеют префикс 'Jet' –
Каков статус сейчас? Есть ли или есть api для этой цели? – Michael
Пожалуйста, укажите основные части связанного ресурса в самом ответе, поэтому следует избегать так называемых «ссылок только». – plamut