All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.exoquery.terpal.plugin.trees.Extractors.kt Maven / Gradle / Ivy

@file:Suppress("NAME_SHADOWING", "NAME_SHADOWING")

package io.exoquery.terpal.plugin.trees

import io.decomat.*
import io.exoquery.terpal.plugin.logging.CompileLogger
import io.exoquery.terpal.parseError
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*

val IrCall.simpleValueArgsCount get() = this.valueArgumentsCount - this.contextReceiversCount
val IrCall.simpleValueArgs get() =
  if (this.contextReceiversCount > 0)
    this.valueArguments.drop(this.contextReceiversCount)
  else
    this.valueArguments

val IrFunction.simpleValueParamsCount get() = this.valueParameters.size - this.contextReceiverParametersCount
val IrFunction.simpleValueParams get() =
  if (this.contextReceiverParametersCount > 0)
    this.valueParameters.drop(this.contextReceiverParametersCount)
  else
    this.valueParameters

val IrType.simpleTypeArgs: List get() =
  when (this) {
    is IrSimpleType ->
      this.arguments.mapNotNull { it.typeOrNull }
    else ->
      listOf()
  }

object Ir {
  object StringConcatenation {
    // Can't do just get(components: Pattern) need to do:
    // >> get(components: AP) or it doesn't work because
    // it needs to have a concrete pattern instance
    context(CompileLogger) operator fun >> get(components: AP) =
      customPattern1(components) { it: IrExpression ->
        if (it is IrStringConcatenation) {
          Components1(it.arguments)
        } else {
          null
        }
      }
  }

  object Const {
    operator fun get(value: Pattern0>) =
      customPattern1(value) { it: IrConst<*> ->
        Components1(it)
      }
  }

  object Call {
    // not a function on an object or class i.e. top-level
    object FunctionUntethered1 {
      context (CompileLogger) operator fun , E: IrExpression> get(x: AP): Pattern1 =
        customPattern1(x) { it: IrCall ->
          val reciever = it.extensionReceiver ?: it.dispatchReceiver
          if (reciever == null && it.simpleValueArgs.size == 1 && it.simpleValueArgs.all { it != null }) {
            Components1(it.simpleValueArgs.first())
          } else {
            null
          }
        }
    }
  }




  object BlockBody {
    // Need to use type-parameter like this or matching (e.g. in SimpleBlockBody) won't type correctly
    operator fun , A: List, S: IrStatement> get(statements: AP) =
      customPattern1(statements) { it: IrBlockBody -> Components1(it.statements.toList()) }

    object ReturnOnly {
      operator fun , A: IrExpression> get(statements: AP) =
        customPattern1(statements) { it: IrBlockBody ->
          on(it).match(
            case(BlockBody[Is()]).then { (irReturn) -> Components1(irReturn) }
          )
        }
    }

    object Statements {
      operator fun , A: List> get(statements: AP) =
        customPattern1(statements) { it: IrBlockBody ->
          on(it).match(
            case(BlockBody[Is()]).then { statements -> Components1(statements) }
          )
        }
    }
  }

  object Return {
    operator fun , A: IrExpression> get(statements: AP) =
      customPattern1(statements) { it: IrReturn -> Components1(it.value) }
  }

  /** I.e. a Lambda! */
  object FunctionExpression {
    operator fun , A: IrSimpleFunction> get(body: AP)  /*: Pattern1*/ =
      // Note, each one of these in here needs to be it:IrExression, not it:IrFunctionExpression or they will never match arbitrary IrExpression instances
      customPattern1(body) { it: IrFunctionExpression ->
        Components1(it.function)
      }

    object withBlockStatements {
      operator fun >, BP: Pattern>> get(params: AP, body: BP) =
        customPattern2(params, body) { it: IrFunctionExpression ->
          on(it).match(
            case(FunctionExpression[SimpleFunction.withBlockStatements[Is(), Is()]])
              .then { (params, statements) -> Components2(params, statements) }
          )
        }
    }

    object withReturnOnlyBlock {
      operator fun , A: IrExpression> get(body: AP) =
        customPattern1(body) { it: IrFunctionExpression ->
          on(it).match(
            case(FunctionExpression[SimpleFunction.withReturnOnlyExpression[Is()]]).then { (expr) ->
              on(expr).match(
                // output the return-body
                case(Return[Is()]).then { returnExpr -> Components1(returnExpr) }
              )
            }
          )
        }
    }
  }

  object SimpleFunction {
    operator fun , BP: Pattern, A: List, B: IrBlockBody> get(args: AP, body: BP): Pattern2 =
      customPattern2(args, body) { it: IrSimpleFunction ->
        it.body?.let { bodyVal ->
          when (val body = it.body) {
            // Ignore context-parameters here
            is IrBlockBody -> Components2(it.simpleValueParams, body)
            else -> parseError("The function ${it.name} body was not a blockBody")
          }

        }
      }

    object withBlockStatements {
      operator fun >, BP: Pattern>> get(params: AP, body: BP) =
        customPattern2(params, body) { it: IrSimpleFunction ->
          on(it).match(
            case(SimpleFunction[Is(), BlockBody.Statements[Is()]]).then { params, (b) ->
              Components2(params, b)
            }
          )
        }
    }

    object withReturnOnlyExpression    {
      operator fun , A: IrExpression> get(body: AP) =
        customPattern1(body) { it: IrSimpleFunction ->
          on(it).match(
            case(SimpleFunction[Is(), BlockBody.ReturnOnly[Is()]]).then { _, (b) ->
              Components1(b)
            }
          )
        }
    }
  }

}