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

dotty.tools.scaladoc.Inkuire.scala Maven / Gradle / Ivy

There is a newer version: 3.6.0-RC1-bin-20240903-21a3d39-NIGHTLY
Show newest version
package dotty.tools.scaladoc

import dotty.tools.scaladoc.util._

object Inkuire {

  var db = InkuireDb(Seq.empty, Map.empty, Seq.empty, Map.empty)
  var implicitConversions: Seq[(Option[TypeLike], Type)] = Seq.empty

  def beforeSave(): Unit = {
    db = db.copy(
      functions = functions.sortBy(_.hashCode),
      types = db.types.toSeq.sortBy(_._1.uuid).toMap,
      implicitConversions = implicitConversions.collect {
        case (Some(from), to) => from -> to
      }.sortBy(_._1.hashCode)
    )
  }

  def generateInkuireConfig(externalMappings: Seq[String]): String = {
    val paths =
      List("../inkuire-db.json").map(jsonString)
        // TODO: #13243
        // ++ externalMappings.map(_ + "../inkuire-db.json")).map(jsonString)
    jsonObject(("inkuirePaths", jsonList(paths))).toString
  }

  def curry(e: Signature): Signature = {
    e.result.typ match
      case t: Type if t.name.name == s"Function${t.params.size-1}" =>
        curry(
          e.copy(
            arguments = e.arguments ++ t.params.init.map(_.typ).map(Contravariance(_)),
            result = Covariance(t.params.last.typ)
          )
        )
      case _ => e
  }

  def functions: Seq[ExternalSignature] = Inkuire.db.functions.flatMap { func =>
    val fromConversions = Inkuire.implicitConversions.filter { ic =>
      func.signature.receiver.nonEmpty && matchingICTypes(ic._2, func.signature.receiver.get.typ)
    }.map {
      case (Some(from), to) =>
        func.copy(
          signature = func.signature.copy(
            receiver = func.signature.receiver.map { rcvr =>
              Contravariance(
                newReceiver(rcvr.typ, to, from)
              )
            }
          )
        )
      case (None, to) =>
        func.copy(
          signature = func.signature.copy(
            receiver = None
          )
        )
    }
    Seq(func) ++ fromConversions
  }
  .distinct

  def matchingICTypes(a: TypeLike, b: TypeLike): Boolean = (a, b) match {
    case (a: Type, b: Type) if a.params.size == 0 && b.params.size == 0 && a.itid == b.itid => true
    case (a: Type, b: Type) if a.params.size == 1 && b.params.size == 1 && a.itid == b.itid =>
      a.params.head.typ.isInstanceOf[Type] && a.params.head.typ.asInstanceOf[Type].isVariable &&
        b.params.head.typ.isInstanceOf[Type] && b.params.head.typ.asInstanceOf[Type].isVariable
    case _ => false
  }

  def newReceiver(old: TypeLike, to: TypeLike, from: TypeLike): TypeLike = (old, to) match {
    case (a: Type, b: Type) if a.params.size == 0 && b.params.size == 0 && a.itid == b.itid => from
    case (a: Type, b: Type) if a.params.size == 1 && b.params.size == 1 && a.itid == b.itid
      && a.params.head.typ.isInstanceOf[Type] && a.params.head.typ.asInstanceOf[Type].isVariable &&
        b.params.head.typ.isInstanceOf[Type] && b.params.head.typ.asInstanceOf[Type].isVariable =>
      if from.isInstanceOf[Type] && from.asInstanceOf[Type].params.size == 1 && from.asInstanceOf[Type].params.head.typ.isInstanceOf[Type] && from.asInstanceOf[Type].params.head.typ.asInstanceOf[Type].isVariable then
        from.asInstanceOf[Type].copy(
          params = Seq(Contravariance(a.params.head.typ.asInstanceOf[Type]))
        )
      else if from.isInstanceOf[Type] && from.asInstanceOf[Type].isVariable then
        a.params.head.typ.asInstanceOf[Type]
      else
        from
    case _ => old
  }

  case class InkuireDb(
    functions:           Seq[ExternalSignature],
    types:               Map[ITID, (Type, Seq[Type])],
    implicitConversions: Seq[(TypeLike, Type)],
    typeAliases:         Map[ITID, TypeLike]
  )

  case class ITID(uuid: String, isParsed: Boolean)

  case class Signature(
    receiver:  Option[Contravariance],
    arguments: Seq[Contravariance],
    result:    Covariance,
    context:   SignatureContext
  ) {
    def typesWithVariances: Seq[Variance] = receiver.toSeq ++ arguments ++ Seq(result)
  }

  object Signature {
    def apply(receiver: Option[TypeLike], arguments: Seq[TypeLike], result: TypeLike, context: SignatureContext): Signature =
      Signature(receiver.map(Contravariance(_)), arguments.map(Contravariance(_)), Covariance(result), context)
  }

  case class ExternalSignature(
    signature:    Signature,
    name:         String,
    packageName:  String,
    uri:          String,
    isLocationExternal: Boolean,
    entryType:    String
  )

  sealed trait TypeLike

  case class Type(
    name:             TypeName,
    params:           Seq[Variance] = Seq.empty,
    nullable:         Boolean = false,
    itid:             Option[ITID] = None,
    isVariable:       Boolean = false,
    isStarProjection: Boolean = false,
    isUnresolved:     Boolean = false
  ) extends TypeLike

  case class AndType(left: TypeLike, right: TypeLike) extends TypeLike
  case class OrType(left: TypeLike, right: TypeLike) extends TypeLike

  case class TypeLambda(args: Seq[Type], result: TypeLike) extends TypeLike

  object TypeLambda {
    def argument(name: String): Type =
      val uuid = s"external-type-lambda-arg-$name"
      Inkuire.Type(
        name = Inkuire.TypeName(name),
        itid = Some(Inkuire.ITID(uuid, isParsed = false)),
        isVariable = true
      )
  }

  object Type {
    def unresolved: Type =
      Type(
        name = TypeName(""),
        itid = Some(
          ITID(
            uuid = "",
            isParsed = false
          )
        )
      )

    def StarProjection: Type =
      Type(
        name = TypeName("_"),
        itid = Some(ITID("_", isParsed = false)),
        isStarProjection = true
      )
  }

  case class TypeName(name: String) {
    override def hashCode: Int = name.toLowerCase.hashCode

    override def equals(obj: Any): Boolean = {
      obj match {
        case o: TypeName => this.name.toLowerCase == o.name.toLowerCase
        case _ => false
      }
    }

    override def toString: String = name
  }

  case class SignatureContext(
    vars:        Set[String],
    constraints: Map[String, Seq[TypeLike]]
  ) {
    override def hashCode: Int = vars.size.hashCode

    override def equals(obj: Any): Boolean =
      obj match {
        case other: SignatureContext if this.vars.size == other.vars.size => true
        case _ => false
      }
  }

  object SignatureContext {
    def empty: SignatureContext = SignatureContext(Set.empty, Map.empty)
  }

  sealed abstract class Variance {
    val typ: TypeLike
  }

  case class Covariance(typ: TypeLike) extends Variance

  case class Contravariance(typ: TypeLike) extends Variance

  case class Invariance(typ: TypeLike) extends Variance

  case class UnresolvedVariance(typ: TypeLike) extends Variance

  object EngineModelSerializers {
    def serialize(db: InkuireDb): JSON = {
      jsonObject(
        ("types", serialize(db.types)),
        ("functions", jsonList(db.functions.map(serialize))),
        ("implicitConversions", jsonList(db.implicitConversions.map(serializeConversion))),
        ("typeAliases", serializeTypeAliases(db.typeAliases))
      )
    }

    private def serializeConversion(conversion: (TypeLike, Type)): JSON = {
      jsonList(
        Seq(
          serialize(conversion._1),
          serialize(conversion._2)
        )
      )
    }

    private def serialize(types: Map[ITID, (Type, Seq[Type])]): JSON = {
      jsonObject((
        types.toList.map {
          case (itid, v) =>
            (serializeAsKey(itid), serialize(v))
        }
      )*)
    }

    private def serializeTypeAliases(types: Map[ITID, TypeLike]): JSON = {
      jsonObject((
        types.toList.map {
          case (itid, v) =>
            (serializeAsKey(itid), serialize(v))
        }
      )*)
    }

    private def serializeAsKey(itid: ITID): String = {
      s"""${itid.isParsed}=${itid.uuid}"""
    }

    private def serialize(v: (Type, Seq[Type])): JSON = {
      jsonList(
        Seq(
          serialize(v._1),
          jsonList(v._2.map(serialize))
        )
      )
    }

    private def serialize(t: TypeLike): JSON = t match {
      case t: Type =>
        jsonObject(
          ("name", serialize(t.name)),
          ("params", jsonList(t.params.map(serialize))),
          ("nullable", serialize(t.nullable)),
          ("itid", serialize(t.itid.get)),
          ("isVariable", serialize(t.isVariable)),
          ("isStarProjection", serialize(t.isStarProjection)),
          ("isUnresolved", serialize(t.isUnresolved)),
          ("typelikekind", serialize("type"))
        )
      case t: OrType =>
        jsonObject(
          ("left", serialize(t.left)),
          ("right", serialize(t.right)),
          ("typelikekind", serialize("ortype"))
        )
      case t: AndType =>
        jsonObject(
          ("left", serialize(t.left)),
          ("right", serialize(t.right)),
          ("typelikekind", serialize("andtype"))
        )
      case t: TypeLambda =>
        jsonObject(
          ("args", jsonList(t.args.map(serialize))),
          ("result", serialize(t.result)),
          ("typelikekind", serialize("typelambda"))
        )
    }

    private def serialize(b: Boolean): JSON = {
      if b then rawJSON("true") else rawJSON("false")
    }

    private def serialize(itid: ITID): JSON = {
      jsonObject(
        ("uuid", serialize(itid.uuid)),
        ("isParsed", serialize(itid.isParsed))
      )
    }

    private def serialize(s: TypeName): JSON = {
      jsonObject(
        ("name", serialize(s.name))
      )
    }

    private def serialize(v: Variance): JSON = v match {
      case _: Invariance =>
        jsonObject(
          ("typ", serialize(v.typ)),
          ("variancekind", serialize("invariance"))
        )
      case _: Covariance =>
        jsonObject(
          ("typ", serialize(v.typ)),
          ("variancekind", serialize("covariance"))
        )
      case _: Contravariance =>
        jsonObject(
          ("typ", serialize(v.typ)),
          ("variancekind", serialize("contravariance"))
        )
      case _: UnresolvedVariance =>
        jsonObject(
          ("typ", serialize(v.typ)),
          ("variancekind", serialize("unresolved"))
        )
    }

    private def serialize(e: ExternalSignature): JSON = {
      jsonObject(
        ("signature", serialize(e.signature)),
        ("name", serialize(e.name)),
        ("packageName", serialize(e.packageName)),
        ("uri", serialize((if e.isLocationExternal then "e" else "i") + e.uri)),
        ("entryType", serialize(e.entryType))
      )
    }

    private def serialize(s: String): JSON = {
      jsonString(s)
    }

    private def serialize(s: Signature): JSON = {
      jsonObject(
        ("receiver", serialize(s.receiver)),
        ("arguments", jsonList(s.arguments.map(serialize))),
        ("result", serialize(s.result)),
        ("context", serialize(s.context))
      )
    }

    private def serialize(o: Option[Contravariance]): JSON = {
      o.fold(rawJSON("null")) { v =>
        serialize(v)
      }
    }

    private def serialize(c: SignatureContext): JSON = {
      jsonObject(
        ("vars", jsonList(c.vars.toSeq.map(serialize))),
        ("constraints", serializeConstraints(c.constraints))
      )
    }

    private def serializeConstraints(constraints: Map[String, Seq[TypeLike]]): JSON = {
      jsonObject((
        constraints.toList.map {
          case (name, vs) =>
            (name, jsonList(vs.map(serialize)))
        }
      )*)
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy