2015-09-19 4 views
3

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

ответ

7

У меня есть некоторое расследование компилятора 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 
} 
+0

Пожалуйста, укажите основные части связанного ресурса в самом ответе, поэтому следует избегать так называемых «ссылок только». – plamut

5

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

+0

Любые стартовые точки? Соответствующие модульные тесты, может быть, имена классов? Я понимаю, что API отсутствует, но я буду признателен за любые подсказки о том, с чего начать или какие классы искать. – Iurii

+2

Ну, это довольно сложно, если честно. Вы можете посмотреть, как работают наши классы ExpressionTypingVisitor, также существует концепция PSI - конкретного дерева синтаксиса, а BindingContext - отображение из PSI для ввода информации другой семантической информации. Классы PSI имеют префикс 'Jet' –

+1

Каков статус сейчас? Есть ли или есть api для этой цели? – Michael