From 9c94bdc92bde2a6d7f9c14ef7ed6f2666f2f031d Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 22 Jul 2021 12:42:37 +0200 Subject: [PATCH 01/12] Remove permutating params --- .../inkuire/engine/common/service/DefaultSignatureResolver.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala index f4a110a8..f8b3c710 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala @@ -17,7 +17,6 @@ class DefaultSignatureResolver(inkuireDb: InkuireDb) extends BaseSignatureResolv _.toList .map(moveToReceiverIfPossible) .flatMap { sgn => convertReceivers(sgn).toList } - .flatMap { sgn => permutateParams(sgn).toList } .distinct ) signatures match { From 6112aadd076e8c6e59db1b23c00e87d8375966c2 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 22 Jul 2021 16:40:49 +0200 Subject: [PATCH 02/12] Add entryType of functions --- .../inkuire/engine/common/model/ExternalSignature.scala | 3 ++- .../inkuire/engine/common/model/OutputFormat.scala | 3 ++- .../engine/common/service/DefaultSignatureResolver.scala | 8 ++++---- .../inkuire/engine/common/service/OutputFormatter.scala | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/ExternalSignature.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/ExternalSignature.scala index d3fb3bc3..638bbc90 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/ExternalSignature.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/ExternalSignature.scala @@ -4,5 +4,6 @@ case class ExternalSignature( signature: Signature, name: String, packageName: String, - uri: String + uri: String, + entryType: String ) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/OutputFormat.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/OutputFormat.scala index c85f6f79..53230481 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/OutputFormat.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/OutputFormat.scala @@ -4,7 +4,8 @@ case class Match( prettifiedSignature: String, functionName: String, packageLocation: String, - pageLocation: String + pageLocation: String, + entryType: String ) sealed trait OutputFormat diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala index f8b3c710..1e9d4784 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala @@ -153,18 +153,18 @@ class DefaultSignatureResolver(inkuireDb: InkuireDb) extends BaseSignatureResolv case t: Type if t.isGeneric => resolveMultipleTypes(t.params.map(_.typ)).flatMap { params => ancestryGraph.values.map(_._1).filter(_.name == t.name).toSeq match { - case _ :: _ => + case Nil => Left(t.name.name) + case _ => Right(for { generic <- ancestryGraph.values.map(_._1).filter(_.name == t.name).toSeq params <- params.map(_.zipVariances(generic.params)) } yield copyITID(t.modify(_.params).setTo(params), generic.itid)) - case _ => Left(t.name.name) } } case t: Type => ancestryGraph.values.map(_._1).filter(_.name == t.name).toSeq match { - case types @ (_ :: _) => Right(types) - case _ => Left(t.name.name) + case Nil => Left(t.name.name) + case types => Right(types) } case t => Right(Seq(t)) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/OutputFormatter.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/OutputFormatter.scala index edf57d30..6928100a 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/OutputFormatter.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/OutputFormatter.scala @@ -22,7 +22,8 @@ class OutputFormatter(prettifier: SignaturePrettifier) { pretty, sgn.name, sgn.packageName, - sgn.uri + sgn.uri, + sgn.entryType ) } } From e0622af90a6d7d61821499ecc23167dd5bb2f81e Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 22 Jul 2021 16:43:26 +0200 Subject: [PATCH 03/12] Change syntax for argument-less functions and vals --- .../inkuire/engine/common/parser/ScalaSignatureParser.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala index a9af0b70..5e1504c2 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala @@ -42,7 +42,7 @@ class ScalaSignatureParser extends BaseSignatureParser { (singleType <~ "=>") ~ singleType ^^ { case t1 ~ t2 => List(t1, t2) } def curriedFunctionTypes: Parser[Seq[Type]] = - "()" ~> ("=>" ~> singleType) ^^ { case t => List(t) } | + "=>" ~> singleType ^^ { case t => List(t) } | curriedTypes private def mapToGenericFunctionType(receiver: Option[Type], args: Seq[Type], result: Type): Type = { From bb750fde2dfb3ef109c3728ba5c9fd3f1820ec49 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 22 Jul 2021 21:05:35 +0200 Subject: [PATCH 04/12] Add parsed signature currying --- .../common/parser/ScalaSignatureParser.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala index 5e1504c2..17d96d97 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala @@ -119,7 +119,7 @@ class ScalaSignatureParserService extends BaseSignatureParserService { private val scalaSignatureParser = new ScalaSignatureParser override def parse(str: String): Either[String, Signature] = - doParse(str) >>= convert >>= validate + doParse(str) >>= convert >>= (s => Right(curry(s))) >>= validate val parsingErrorGenericMessage = "Could not parse provided signature. Example signature looks like this: List[Int] => (Int => Boolean) => Int" @@ -172,6 +172,19 @@ class ScalaSignatureParserService extends BaseSignatureParserService { } } + private 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 + } + } + private def validate(sgn: Signature): Either[String, Signature] = for { _ <- validateConstraintsForNonVariables(sgn) From a534b3480f9a0d574c63d4682422773fa8a52bb1 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 09:57:35 +0200 Subject: [PATCH 05/12] Add or/and types to frontend --- .../engine/common/model/Signature.scala | 2 +- .../common/parser/ScalaSignatureParser.scala | 48 +++++++++++-------- .../common/service/FluffMatchService.scala | 16 +++---- ...ver.scala => ScalaSignatureResolver.scala} | 20 ++++++++ 4 files changed, 58 insertions(+), 28 deletions(-) rename engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/{DefaultSignatureResolver.scala => ScalaSignatureResolver.scala} (93%) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala index f9b8a411..40be0a1d 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala @@ -10,6 +10,6 @@ case class Signature( } object Signature { - def apply(receiver: Option[Type], arguments: Seq[Type], result: Type, context: SignatureContext): Signature = + def apply(receiver: Option[TypeLike], arguments: Seq[TypeLike], result: TypeLike, context: SignatureContext): Signature = Signature(receiver.map(Contravariance), arguments.map(Contravariance), Covariance(result), context) } diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala index 17d96d97..485402e3 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala @@ -24,28 +24,38 @@ class ScalaSignatureParser extends BaseSignatureParser { tupleType | typ + def typeLike: Parser[TypeLike] = + singleType | + orType | + andType + + def orType: Parser[OrType] = + "(" ~> typeLike ~ ("|" ~> typeLike) <~ ")" ^^ { case t1 ~ t2 => OrType(t1, t2) } + + def andType: Parser[AndType] = + "(" ~> typeLike ~ ("&" ~> typeLike) <~ ")" ^^ { case t1 ~ t2 => AndType(t1, t2) } + def functionType: Parser[Type] = - "()" ~> "=>" ~> singleType | - "(" ~> curriedFunctionTypes <~ ")" ^^ { - case types => mapToGenericFunctionType(None, types.init, types.last) - } + "(" ~> curriedFunctionTypes <~ ")" ^^ { + case types => mapToGenericFunctionType(None, types.init, types.last) + } - def tupleTypes: Parser[Seq[Type]] = - (singleType <~ ",") ~ tupleTypes ^^ { case t1 ~ ts => t1 +: ts } | - (singleType <~ ",") ~ singleType ^^ { case t1 ~ t2 => List(t1, t2) } + def tupleTypes: Parser[Seq[TypeLike]] = + (typeLike <~ ",") ~ tupleTypes ^^ { case t1 ~ ts => t1 +: ts } | + (typeLike <~ ",") ~ typeLike ^^ { case t1 ~ t2 => List(t1, t2) } def tupleType: Parser[Type] = "(" ~> tupleTypes <~ ")" ^^ { case types => mapToTupleType(types) } - def curriedTypes: Parser[Seq[Type]] = - (singleType <~ "=>") ~ curriedTypes ^^ { case t1 ~ ts => t1 +: ts } | - (singleType <~ "=>") ~ singleType ^^ { case t1 ~ t2 => List(t1, t2) } + def curriedTypes: Parser[Seq[TypeLike]] = + (typeLike <~ "=>") ~ curriedTypes ^^ { case t1 ~ ts => t1 +: ts } | + (typeLike <~ "=>") ~ typeLike ^^ { case t1 ~ t2 => List(t1, t2) } - def curriedFunctionTypes: Parser[Seq[Type]] = - "=>" ~> singleType ^^ { case t => List(t) } | + def curriedFunctionTypes: Parser[Seq[TypeLike]] = + "=>" ~> typeLike ^^ { case t => List(t) } | curriedTypes - private def mapToGenericFunctionType(receiver: Option[Type], args: Seq[Type], result: Type): Type = { + private def mapToGenericFunctionType(receiver: Option[Type], args: Seq[TypeLike], result: TypeLike): Type = { val params = receiver.fold(args :+ result)(_ +: args :+ result) Type( s"Function${params.size - 1}", @@ -53,7 +63,7 @@ class ScalaSignatureParser extends BaseSignatureParser { ) } - private def mapToTupleType(args: Seq[Type]): Type = + private def mapToTupleType(args: Seq[TypeLike]): Type = Type( s"Tuple${args.size}", params = args.map(UnresolvedVariance) @@ -64,9 +74,9 @@ class ScalaSignatureParser extends BaseSignatureParser { case baseType ~ types => Type(baseType, types.map(UnresolvedVariance)) } - def types: Parser[Seq[Type]] = list(singleType) | empty[List[Type]] + def types: Parser[Seq[TypeLike]] = list(typeLike) | empty[List[TypeLike]] - def typeArguments: Parser[Seq[Type]] = list(singleType) | empty[List[Type]] + def typeArguments: Parser[Seq[TypeLike]] = list(typeLike) | empty[List[TypeLike]] def typeVariable: Parser[(String, Seq[Type])] = (identifier <~ "<:") ~ singleType ^^ { case typeVar ~ constraint => (typeVar, Seq(constraint)) } | @@ -94,9 +104,9 @@ class ScalaSignatureParser extends BaseSignatureParser { curriedSignature def mapToSignature( - rcvr: Option[Type], - args: Seq[Type], - result: Type, + rcvr: Option[TypeLike], + args: Seq[TypeLike], + result: TypeLike, typeVars: (Seq[String], Map[String, Seq[Type]]), where: Map[String, Seq[Type]] ): Signature = diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala index 6e84894e..c606f90a 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala @@ -127,23 +127,23 @@ case class AncestryGraph( case (_, s: Type) if s.isStarProjection => State.pure(true) case (AndType(left, right), supr) => left.isSubTypeOf(supr)(context).flatMap { res => - if (res) right.isSubTypeOf(supr)(context) - else State.pure(false) + if (res) State.pure(true) + else right.isSubTypeOf(supr)(context) } case (typ, AndType(left, right)) => typ.isSubTypeOf(left)(context).flatMap { res => - if (res) State.pure(true) - else typ.isSubTypeOf(right)(context) + if (res) typ.isSubTypeOf(right)(context) + else State.pure(false) } case (OrType(left, right), supr) => left.isSubTypeOf(supr)(context).flatMap { res => - if (res) State.pure(true) - else right.isSubTypeOf(supr)(context) + if (res) right.isSubTypeOf(supr)(context) + else State.pure(false) } case (typ, OrType(left, right)) => typ.isSubTypeOf(left)(context).flatMap { res => - if (res) typ.isSubTypeOf(right)(context) - else State.pure(false) + if (res) State.pure(true) + else typ.isSubTypeOf(right)(context) } case (typ: TypeLambda, supr: TypeLambda) => val dummyTypes = genDummyTypes(typ.args.size) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala similarity index 93% rename from engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala rename to engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala index 1e9d4784..be5f4487 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/DefaultSignatureResolver.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala @@ -166,6 +166,26 @@ class DefaultSignatureResolver(inkuireDb: InkuireDb) extends BaseSignatureResolv case Nil => Left(t.name.name) case types => Right(types) } + case OrType(left, right) => + for { + l <- resolvePossibleTypes(left) + r <- resolvePossibleTypes(right) + } yield { + for { + r <- r + l <- l + } yield OrType(l, r) + } + case AndType(left, right) => + for { + l <- resolvePossibleTypes(left) + r <- resolvePossibleTypes(right) + } yield { + for { + r <- r + l <- l + } yield AndType(l, r) + } case t => Right(Seq(t)) } From d15f91d73d0ca5a428b1475de7eed8266b3bcf55 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 11:43:26 +0200 Subject: [PATCH 06/12] Change inkuire directory hierarchy --- engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala index 3cad1ba1..49e16fef 100644 --- a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala +++ b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala @@ -23,7 +23,7 @@ object Main extends App { } def startApp(): Unit = { - val scriptPath = Globals.pathToRoot + "scripts/" - val worker = new Worker(scriptPath + "inkuire-worker.js") + val inkuirePath = Globals.pathToRoot + "inkuire/" + val worker = new Worker(inkuirePath + "inkuire-worker.js") } } From efde7abe7a72429610479257a8f2453aeec6aff7 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 13:20:47 +0200 Subject: [PATCH 07/12] Reorganize code structure --- .../engine/common/service/AncestryGraph.scala | 239 +++++++++++++++ .../common/service/FluffMatchService.scala | 280 ------------------ .../common/service/TypeVariablesGraph.scala | 54 ++++ 3 files changed, 293 insertions(+), 280 deletions(-) create mode 100644 engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala create mode 100644 engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala new file mode 100644 index 00000000..92f25fd4 --- /dev/null +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala @@ -0,0 +1,239 @@ +package org.virtuslab.inkuire.engine.common.service + +import org.virtuslab.inkuire.engine.common.model._ +import cats.data.State +import cats.implicits._ +import com.softwaremill.quicklens._ +import scala.util.Random + +case class AncestryGraph( + nodes: Map[ITID, (Type, Seq[Type])], + implicitConversions: Map[ITID, Seq[Type]], + typeAliases: Map[ITID, TypeLike] +) extends VarianceOps { + + implicit class TypeOps(typ: TypeLike) { + def isSubTypeOf(supr: TypeLike)(context: SignatureContext): State[VariableBindings, Boolean] = { + (typ, supr) match { + case (t: Type, _) if t.isStarProjection => State.pure(true) + case (_, s: Type) if s.isStarProjection => State.pure(true) + case (AndType(left, right), supr) => + left.isSubTypeOf(supr)(context).flatMap { res => + if (res) State.pure(true) + else right.isSubTypeOf(supr)(context) + } + case (typ, AndType(left, right)) => + typ.isSubTypeOf(left)(context).flatMap { res => + if (res) typ.isSubTypeOf(right)(context) + else State.pure(false) + } + case (OrType(left, right), supr) => + left.isSubTypeOf(supr)(context).flatMap { res => + if (res) right.isSubTypeOf(supr)(context) + else State.pure(false) + } + case (typ, OrType(left, right)) => + typ.isSubTypeOf(left)(context).flatMap { res => + if (res) State.pure(true) + else typ.isSubTypeOf(right)(context) + } + case (typ: TypeLambda, supr: TypeLambda) => + val dummyTypes = genDummyTypes(typ.args.size) + val typResult = substituteBindings(typ.result, typ.args.flatMap(_.itid).zip(dummyTypes).toMap) + val suprResult = substituteBindings(supr.result, supr.args.flatMap(_.itid).zip(dummyTypes).toMap) + if (typ.args.size == supr.args.size) typResult.isSubTypeOf(suprResult)(context) + else State.pure(false) + case (_: TypeLambda, _) => + State.pure(false) + case (_, _: TypeLambda) => + State.pure(false) + case (typ: Type, supr: Type) if typ.isVariable && typ.isGeneric => + State.modify[VariableBindings](_.add(typ.itid.get, supr.modify(_.params).setTo(Seq.empty))) >> + checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) if supr.isVariable && supr.isGeneric => + State.modify[VariableBindings](_.add(supr.itid.get, typ.modify(_.params).setTo(Seq.empty))) >> + checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) if typ.isVariable && supr.isVariable => + val typConstraints = context.constraints.get(typ.name.name).toSeq.flatten + val suprConstraints = context.constraints.get(supr.name.name).toSeq.flatten + State.modify[VariableBindings](_.add(typ.itid.get, supr).add(supr.itid.get, typ)) >> + State.pure(typConstraints == suprConstraints) // TODO #56 Better 'equality' between two TypeVariables + case (typ: Type, supr: Type) if supr.isVariable => + if (supr.itid.get.isParsed) { + State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> + State.pure(true) + } else { + val constraints = context.constraints.get(supr.name.name).toSeq.flatten.toList + State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> + constraints + .foldLeft(State.pure[VariableBindings, Boolean](true)) { + case (acc, t) => + acc.flatMap { cond => + if (cond) typ.isSubTypeOf(t)(context) + else State.pure(false) + } + } + } + case (typ: Type, supr: Type) if typ.isVariable => + if (typ.itid.get.isParsed) { + val constraints = context.constraints.get(typ.name.name).toSeq.flatten.toList + State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> { + if (constraints.nonEmpty) { + constraints + .foldLeft(State.pure[VariableBindings, Boolean](false)) { + case (acc, t) => + acc.flatMap { cond => + if (cond) State.pure[VariableBindings, Boolean](true) + else t.isSubTypeOf(supr)(context) + } + } + } else State.pure(true) + } + } else { + State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> + State.pure(true) + } + case (typ: Type, supr: Type) if typ.itid == supr.itid => checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) => + nodes + .get(typ.itid.get) + .toList + .flatMap(node => specializeParents(typ, node)) + .map(_ -> supr) + .++(typeAliases.get(typ.itid.get).toList.flatMap(alias => dealias(typ, alias)).map(_ -> supr)) + .++(typeAliases.get(supr.itid.get).toList.flatMap(alias => dealias(supr, alias)).map(typ -> _)) + .foldLeft(State.pure[VariableBindings, Boolean](false)) { + case (acc, (t, s)) => + acc.flatMap { cond => + if (cond) State.pure(true) + else t.isSubTypeOf(s)(context) + } + } + } + } + } + + def dealias(concreteType: Type, node: TypeLike): Option[TypeLike] = (concreteType, node) match { + case (t: Type, rhs: Type) if !t.isGeneric => + Some(rhs) + case (t: Type, rhs: TypeLambda) if t.params.size == rhs.args.size => + Some(substituteBindings(rhs.result, rhs.args.flatMap(_.itid).zip(t.params.map(_.typ)).toMap)) + case _ => + None + } + + private def specializeParents(concreteType: Type, node: (Type, Seq[TypeLike])): Seq[TypeLike] = { + val (declaration, parents) = node + def resITID(t: TypeLike): Option[ITID] = t match { + case t: Type => t.itid + case t: TypeLambda => resITID(t.result) + case _ => None + } + val bindings: Map[ITID, TypeLike] = + declaration.params + .map(_.typ) + .map(resITID) + .flatMap(identity) + .zip(concreteType.params.map(_.typ)) + .toMap + parents.map(substituteBindings(_, bindings)) + } + + private def substituteBindings(parent: TypeLike, bindings: Map[ITID, TypeLike]): TypeLike = parent match { + case t: Type if t.isVariable => + t.itid match { + case None => t.modify(_.params.each.typ).using(substituteBindings(_, bindings)) + case Some(itid) => bindings.get(itid).getOrElse(t) + } + case t: Type => + t.modify(_.params.each.typ).using(substituteBindings(_, bindings)) + case t: OrType => + t.modifyAll(_.left, _.right).using(substituteBindings(_, bindings)) + case t: AndType => + t.modifyAll(_.left, _.right).using(substituteBindings(_, bindings)) + case t: TypeLambda => + t.modify(_.result).using(substituteBindings(_, bindings)) + } + + private def genDummyTypes(n: Int) = + 1.to(n).map { i => + val name = s"dummy$i${Random.nextString(10)}" + Type( + name = TypeName(name), + itid = Some(ITID(name, isParsed = false)), + isVariable = true + ) + } + + def getAllParentsITIDs(tpe: Type): Seq[ITID] = { + tpe.itid.get +: nodes.get(tpe.itid.get).toSeq.flatMap(_._2).flatMap(getAllParentsITIDs(_)) + } + + def checkTypesWithVariances( + types: Seq[Variance], + suprs: Seq[Variance], + context: SignatureContext + ): State[VariableBindings, Boolean] = { + if (types.size == suprs.size) { + types + .zip(suprs) + .toList + .foldLeft(State.pure[VariableBindings, Boolean](true)) { + case (acc, (externalType, queryType)) => + acc.flatMap { cond => + if (cond) checkByVariance(externalType, queryType, context) + else State.pure[VariableBindings, Boolean](false) + } + } + } else State.pure(false) + } + + private def checkTypeParamsByVariance( + typ: Type, + supr: Type, + context: SignatureContext + ): State[VariableBindings, Boolean] = { + val typVariance = writeVariancesFromDRI(typ) + val suprVariance = writeVariancesFromDRI(supr) + checkTypesWithVariances(typVariance.params, suprVariance.params, context) + } + + def checkByVariance( + typ: Variance, + supr: Variance, + context: SignatureContext + ): State[VariableBindings, Boolean] = { + (typ, supr) match { + case (Covariance(typParam), Covariance(suprParam)) => + typParam.isSubTypeOf(suprParam)(context) + case (Contravariance(typParam), Contravariance(suprParam)) => + suprParam.isSubTypeOf(typParam)(context) + case (Invariance(typParam), Invariance(suprParam)) => + typParam.isSubTypeOf(suprParam)(context) >>= { res1 => + if (res1) suprParam.isSubTypeOf(typParam)(context) + else State.pure(false) + } + case (v1, v2) => // Treating not matching variances as invariant + val typParam = v1.typ + val suprParam = v2.typ + typParam.isSubTypeOf(suprParam)(context) >>= { res1 => + if (res1) suprParam.isSubTypeOf(typParam)(context) + else State.pure(false) + } + } + } + + private def writeVariancesFromDRI: Type => Type = { + case typ if typ.params.nonEmpty => + typ + .modify(_.params) + .using { params => + if (typ.itid.nonEmpty && nodes.contains(typ.itid.get)) { + params.zip(nodes(typ.itid.get)._1.params).map { + case (t, v) => t.typ.zipVariance(v) + } + } else params + } + case typ => typ + } +} \ No newline at end of file diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala index c606f90a..f66fa4ab 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala @@ -65,283 +65,3 @@ class FluffMatchService(val inkuireDb: InkuireDb) extends BaseMatchService with } && !TypeVariablesGraph(bindings).hasCyclicDependency } } - -case class TypeVariablesGraph(variableBindings: VariableBindings) { - val dependencyGraph: Map[ITID, Seq[ITID]] = variableBindings.bindings.view.mapValues { - _.flatMap { - case g: Type if g.params.nonEmpty => retrieveVariables(g) - case _ => Seq() - }.distinct - }.toMap - - private def retrieveVariables(t: TypeLike): Seq[ITID] = - t match { - case t: Type if t.isVariable => Seq(t.itid.get) - case g: Type => g.params.map(_.typ).flatMap(retrieveVariables) - case _ => Seq() - } - - def hasCyclicDependency: Boolean = { - case class DfsState(visited: Set[ITID] = Set.empty, stack: Set[ITID] = Set.empty) - - def loop(current: ITID): State[DfsState, Boolean] = - for { - dfsState <- State.get[DfsState] - cycle = dfsState.stack.contains(current) - visited = dfsState.visited.contains(current) - newState = dfsState.modifyAll(_.visited, _.stack).using(_ + current) - _ <- State.set[DfsState](newState) - f <- - if (!visited) - dependencyGraph - .getOrElse(current, Seq()) - .toList - .traverse(loop) - else State.pure[DfsState, List[Boolean]](List()) - _ <- State.modify[DfsState](s => s.modify(_.stack).using(_ - current)) - } yield cycle || f.exists(identity) - - dependencyGraph.keys.toList - .traverse { v => - for { - dfsState <- State.get[DfsState] - flag <- if (dfsState.visited.contains(v)) State.pure[DfsState, Boolean](false) else loop(v) - } yield flag - } - .map(_.exists(identity)) - .runA(DfsState()) - .value - } -} - -case class AncestryGraph( - nodes: Map[ITID, (Type, Seq[Type])], - implicitConversions: Map[ITID, Seq[Type]], - typeAliases: Map[ITID, TypeLike] -) extends VarianceOps { - - implicit class TypeOps(typ: TypeLike) { - def isSubTypeOf(supr: TypeLike)(context: SignatureContext): State[VariableBindings, Boolean] = { - (typ, supr) match { - case (t: Type, _) if t.isStarProjection => State.pure(true) - case (_, s: Type) if s.isStarProjection => State.pure(true) - case (AndType(left, right), supr) => - left.isSubTypeOf(supr)(context).flatMap { res => - if (res) State.pure(true) - else right.isSubTypeOf(supr)(context) - } - case (typ, AndType(left, right)) => - typ.isSubTypeOf(left)(context).flatMap { res => - if (res) typ.isSubTypeOf(right)(context) - else State.pure(false) - } - case (OrType(left, right), supr) => - left.isSubTypeOf(supr)(context).flatMap { res => - if (res) right.isSubTypeOf(supr)(context) - else State.pure(false) - } - case (typ, OrType(left, right)) => - typ.isSubTypeOf(left)(context).flatMap { res => - if (res) State.pure(true) - else typ.isSubTypeOf(right)(context) - } - case (typ: TypeLambda, supr: TypeLambda) => - val dummyTypes = genDummyTypes(typ.args.size) - val typResult = substituteBindings(typ.result, typ.args.flatMap(_.itid).zip(dummyTypes).toMap) - val suprResult = substituteBindings(supr.result, supr.args.flatMap(_.itid).zip(dummyTypes).toMap) - if (typ.args.size == supr.args.size) typResult.isSubTypeOf(suprResult)(context) - else State.pure(false) - case (_: TypeLambda, _) => - State.pure(false) - case (_, _: TypeLambda) => - State.pure(false) - case (typ: Type, supr: Type) if typ.isVariable && typ.isGeneric => - State.modify[VariableBindings](_.add(typ.itid.get, supr.modify(_.params).setTo(Seq.empty))) >> - checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) if supr.isVariable && supr.isGeneric => - State.modify[VariableBindings](_.add(supr.itid.get, typ.modify(_.params).setTo(Seq.empty))) >> - checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) if typ.isVariable && supr.isVariable => - val typConstraints = context.constraints.get(typ.name.name).toSeq.flatten - val suprConstraints = context.constraints.get(supr.name.name).toSeq.flatten - State.modify[VariableBindings](_.add(typ.itid.get, supr).add(supr.itid.get, typ)) >> - State.pure(typConstraints == suprConstraints) // TODO #56 Better 'equality' between two TypeVariables - case (typ: Type, supr: Type) if supr.isVariable => - if (supr.itid.get.isParsed) { - State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> - State.pure(true) - } else { - val constraints = context.constraints.get(supr.name.name).toSeq.flatten.toList - State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> - constraints - .foldLeft(State.pure[VariableBindings, Boolean](true)) { - case (acc, t) => - acc.flatMap { cond => - if (cond) typ.isSubTypeOf(t)(context) - else State.pure(false) - } - } - } - case (typ: Type, supr: Type) if typ.isVariable => - if (typ.itid.get.isParsed) { - val constraints = context.constraints.get(typ.name.name).toSeq.flatten.toList - State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> { - if (constraints.nonEmpty) { - constraints - .foldLeft(State.pure[VariableBindings, Boolean](false)) { - case (acc, t) => - acc.flatMap { cond => - if (cond) State.pure[VariableBindings, Boolean](true) - else t.isSubTypeOf(supr)(context) - } - } - } else State.pure(true) - } - } else { - State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> - State.pure(true) - } - case (typ: Type, supr: Type) if typ.itid == supr.itid => checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) => - nodes - .get(typ.itid.get) - .toList - .flatMap(node => specializeParents(typ, node)) - .map(_ -> supr) - .++(typeAliases.get(typ.itid.get).toList.flatMap(alias => dealias(typ, alias)).map(_ -> supr)) - .++(typeAliases.get(supr.itid.get).toList.flatMap(alias => dealias(supr, alias)).map(typ -> _)) - .foldLeft(State.pure[VariableBindings, Boolean](false)) { - case (acc, (t, s)) => - acc.flatMap { cond => - if (cond) State.pure(true) - else t.isSubTypeOf(s)(context) - } - } - } - } - } - - def dealias(concreteType: Type, node: TypeLike): Option[TypeLike] = (concreteType, node) match { - case (t: Type, rhs: Type) if !t.isGeneric => - Some(rhs) - case (t: Type, rhs: TypeLambda) if t.params.size == rhs.args.size => - Some(substituteBindings(rhs.result, rhs.args.flatMap(_.itid).zip(t.params.map(_.typ)).toMap)) - case _ => - None - } - - private def specializeParents(concreteType: Type, node: (Type, Seq[TypeLike])): Seq[TypeLike] = { - val (declaration, parents) = node - def resITID(t: TypeLike): Option[ITID] = t match { - case t: Type => t.itid - case t: TypeLambda => resITID(t.result) - case _ => None - } - val bindings: Map[ITID, TypeLike] = - declaration.params - .map(_.typ) - .map(resITID) - .flatMap(identity) - .zip(concreteType.params.map(_.typ)) - .toMap - parents.map(substituteBindings(_, bindings)) - } - - private def substituteBindings(parent: TypeLike, bindings: Map[ITID, TypeLike]): TypeLike = parent match { - case t: Type if t.isVariable => - t.itid match { - case None => t.modify(_.params.each.typ).using(substituteBindings(_, bindings)) - case Some(itid) => bindings.get(itid).getOrElse(t) - } - case t: Type => - t.modify(_.params.each.typ).using(substituteBindings(_, bindings)) - case t: OrType => - t.modifyAll(_.left, _.right).using(substituteBindings(_, bindings)) - case t: AndType => - t.modifyAll(_.left, _.right).using(substituteBindings(_, bindings)) - case t: TypeLambda => - t.modify(_.result).using(substituteBindings(_, bindings)) - } - - private def genDummyTypes(n: Int) = - 1.to(n).map { i => - val name = s"dummy$i${Random.nextString(10)}" - Type( - name = TypeName(name), - itid = Some(ITID(name, isParsed = false)), - isVariable = true - ) - } - - def getAllParentsITIDs(tpe: Type): Seq[ITID] = { - tpe.itid.get +: nodes.get(tpe.itid.get).toSeq.flatMap(_._2).flatMap(getAllParentsITIDs(_)) - } - - def checkTypesWithVariances( - types: Seq[Variance], - suprs: Seq[Variance], - context: SignatureContext - ): State[VariableBindings, Boolean] = { - if (types.size == suprs.size) { - types - .zip(suprs) - .toList - .foldLeft(State.pure[VariableBindings, Boolean](true)) { - case (acc, (externalType, queryType)) => - acc.flatMap { cond => - if (cond) checkByVariance(externalType, queryType, context) - else State.pure[VariableBindings, Boolean](false) - } - } - } else State.pure(false) - } - - private def checkTypeParamsByVariance( - typ: Type, - supr: Type, - context: SignatureContext - ): State[VariableBindings, Boolean] = { - val typVariance = writeVariancesFromDRI(typ) - val suprVariance = writeVariancesFromDRI(supr) - checkTypesWithVariances(typVariance.params, suprVariance.params, context) - } - - def checkByVariance( - typ: Variance, - supr: Variance, - context: SignatureContext - ): State[VariableBindings, Boolean] = { - (typ, supr) match { - case (Covariance(typParam), Covariance(suprParam)) => - typParam.isSubTypeOf(suprParam)(context) - case (Contravariance(typParam), Contravariance(suprParam)) => - suprParam.isSubTypeOf(typParam)(context) - case (Invariance(typParam), Invariance(suprParam)) => - typParam.isSubTypeOf(suprParam)(context) >>= { res1 => - if (res1) suprParam.isSubTypeOf(typParam)(context) - else State.pure(false) - } - case (v1, v2) => // Treating not matching variances as invariant - val typParam = v1.typ - val suprParam = v2.typ - typParam.isSubTypeOf(suprParam)(context) >>= { res1 => - if (res1) suprParam.isSubTypeOf(typParam)(context) - else State.pure(false) - } - } - } - - private def writeVariancesFromDRI: Type => Type = { - case typ if typ.params.nonEmpty => - typ - .modify(_.params) - .using { params => - if (typ.itid.nonEmpty && nodes.contains(typ.itid.get)) { - params.zip(nodes(typ.itid.get)._1.params).map { - case (t, v) => t.typ.zipVariance(v) - } - } else params - } - case typ => typ - } -} diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala new file mode 100644 index 00000000..bf639001 --- /dev/null +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala @@ -0,0 +1,54 @@ +package org.virtuslab.inkuire.engine.common.service + +import org.virtuslab.inkuire.engine.common.model._ +import cats.data.State +import cats.implicits._ +import com.softwaremill.quicklens._ + +case class TypeVariablesGraph(variableBindings: VariableBindings) { + val dependencyGraph: Map[ITID, Seq[ITID]] = variableBindings.bindings.view.mapValues { + _.flatMap { + case g: Type if g.params.nonEmpty => retrieveVariables(g) + case _ => Seq() + }.distinct + }.toMap + + private def retrieveVariables(t: TypeLike): Seq[ITID] = + t match { + case t: Type if t.isVariable => Seq(t.itid.get) + case g: Type => g.params.map(_.typ).flatMap(retrieveVariables) + case _ => Seq() + } + + def hasCyclicDependency: Boolean = { + case class DfsState(visited: Set[ITID] = Set.empty, stack: Set[ITID] = Set.empty) + + def loop(current: ITID): State[DfsState, Boolean] = + for { + dfsState <- State.get[DfsState] + cycle = dfsState.stack.contains(current) + visited = dfsState.visited.contains(current) + newState = dfsState.modifyAll(_.visited, _.stack).using(_ + current) + _ <- State.set[DfsState](newState) + f <- + if (!visited) + dependencyGraph + .getOrElse(current, Seq()) + .toList + .traverse(loop) + else State.pure[DfsState, List[Boolean]](List()) + _ <- State.modify[DfsState](s => s.modify(_.stack).using(_ - current)) + } yield cycle || f.exists(identity) + + dependencyGraph.keys.toList + .traverse { v => + for { + dfsState <- State.get[DfsState] + flag <- if (dfsState.visited.contains(v)) State.pure[DfsState, Boolean](false) else loop(v) + } yield flag + } + .map(_.exists(identity)) + .runA(DfsState()) + .value + } +} \ No newline at end of file From 9f803ebb4f50ef1b5d93d29c7e0502103099b2a7 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 15:26:19 +0200 Subject: [PATCH 08/12] Remove unfunny joke --- .../inkuire/engine/common/service/BaseMatchService.scala | 4 ++-- .../inkuire/engine/common/service/ExactMatchService.scala | 6 +++--- .../inkuire/engine/common/service/FluffMatchService.scala | 6 +++--- .../scala/org/virtuslab/inkuire/engine/http/cli/Cli.scala | 2 +- .../org/virtuslab/inkuire/engine/http/http/HttpServer.scala | 2 +- .../org/virtuslab/inkuire/js/handlers/JSOutputHandler.scala | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala index b01df425..5e0d5553 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala @@ -4,6 +4,6 @@ import org.virtuslab.inkuire.engine.common.model.{ExternalSignature, InkuireDb, trait BaseMatchService { def inkuireDb: InkuireDb - def |??|(resolveResult: ResolveResult): Seq[ExternalSignature] - def |?|(resolveResult: ResolveResult)(against: ExternalSignature): Boolean + def findMatches(resolveResult: ResolveResult): Seq[ExternalSignature] + def isMatch(resolveResult: ResolveResult)(against: ExternalSignature): Boolean } diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ExactMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ExactMatchService.scala index acc0a26e..b58c52ad 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ExactMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ExactMatchService.scala @@ -4,10 +4,10 @@ import org.virtuslab.inkuire.engine.common.model.{ExternalSignature, InkuireDb, class ExactMatchService(val inkuireDb: InkuireDb) extends BaseMatchService { - override def |??|(resolveResult: ResolveResult): Seq[ExternalSignature] = { - inkuireDb.functions.filter(|?|(resolveResult)) + override def findMatches(resolveResult: ResolveResult): Seq[ExternalSignature] = { + inkuireDb.functions.filter(isMatch(resolveResult)) } - override def |?|(resolveResult: ResolveResult)(against: ExternalSignature): Boolean = + override def isMatch(resolveResult: ResolveResult)(against: ExternalSignature): Boolean = resolveResult.signatures.contains(against.signature) } diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala index f66fa4ab..bbcdd69c 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/FluffMatchService.scala @@ -29,11 +29,11 @@ class FluffMatchService(val inkuireDb: InkuireDb) extends BaseMatchService with } } - override def |?|(resolveResult: ResolveResult)(against: ExternalSignature): Boolean = { + override def isMatch(resolveResult: ResolveResult)(against: ExternalSignature): Boolean = { resolveResult.signatures.exists(against.signature.canSubstituteFor(_)) } - override def |??|(resolveResult: ResolveResult): Seq[ExternalSignature] = { + override def findMatches(resolveResult: ResolveResult): Seq[ExternalSignature] = { val actualSignatures = resolveResult.signatures.foldLeft(resolveResult.signatures) { case (acc, against) => acc.filter { sgn => @@ -43,7 +43,7 @@ class FluffMatchService(val inkuireDb: InkuireDb) extends BaseMatchService with val actualSignaturesSize = actualSignatures.headOption.map(_.typesWithVariances.size) inkuireDb.functions .filter(fun => Some(fun.signature.typesWithVariances.size) == actualSignaturesSize) - .filter(|?|(resolveResult.modify(_.signatures).setTo(actualSignatures))) + .filter(isMatch(resolveResult.modify(_.signatures).setTo(actualSignatures))) } private def checkBindings(bindings: VariableBindings): Boolean = { diff --git a/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/cli/Cli.scala b/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/cli/Cli.scala index 6a83df80..fd509771 100644 --- a/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/cli/Cli.scala +++ b/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/cli/Cli.scala @@ -75,7 +75,7 @@ class Cli extends InputHandler with OutputHandler with ConfigReader with IOHelpe .resolve(s) .fold( handleResolveError, - r => putStrLn(env.prettifier.prettify(env.matcher |??| r)).liftApp + r => putStrLn(env.prettifier.prettify(env.matcher findMatches r)).liftApp ) } ) diff --git a/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/http/HttpServer.scala b/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/http/HttpServer.scala index 0c447089..f9da7ffe 100644 --- a/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/http/HttpServer.scala +++ b/engineHttp/src/main/scala/org/virtuslab/inkuire/engine/http/http/HttpServer.scala @@ -47,7 +47,7 @@ class HttpServer extends OutputHandler { logger.info(s"Parsed signature: " + signature) env.resolver.resolve(parsed) } - .map { resolved => env.matcher |??| resolved } + .map { resolved => env.matcher findMatches resolved } .map { results => formatter.createOutput(signature, results) } } diff --git a/engineJs/src/main/scala/org/virtuslab/inkuire/js/handlers/JSOutputHandler.scala b/engineJs/src/main/scala/org/virtuslab/inkuire/js/handlers/JSOutputHandler.scala index 5d22dec0..1f295eae 100644 --- a/engineJs/src/main/scala/org/virtuslab/inkuire/js/handlers/JSOutputHandler.scala +++ b/engineJs/src/main/scala/org/virtuslab/inkuire/js/handlers/JSOutputHandler.scala @@ -27,7 +27,7 @@ class JSOutputHandler(private val jsHandler: JSHandler) extends OutputHandler { .fromIterable(env.db.functions) .filterEvalF { eSgn => IO.async[Boolean] { f => - f(Right(env.matcher.|?|(r)(eSgn))) + f(Right(env.matcher.isMatch(r)(eSgn))) } } .map(eSgn => outputFormatter.createOutput(query, Seq(eSgn))) From c698cfa062eebe33a1123810a19594ef25260659 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 15:52:40 +0200 Subject: [PATCH 09/12] Revert to old structure --- engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala index 49e16fef..383181c3 100644 --- a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala +++ b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala @@ -23,7 +23,7 @@ object Main extends App { } def startApp(): Unit = { - val inkuirePath = Globals.pathToRoot + "inkuire/" + val inkuirePath = Globals.pathToRoot + "scripts/" val worker = new Worker(inkuirePath + "inkuire-worker.js") } } From 79b782c7a04f69b38bb2493e5efe82a5790797f0 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 15:52:50 +0200 Subject: [PATCH 10/12] Cache --- .../engine/common/service/AncestryGraph.scala | 187 ++++++++++-------- 1 file changed, 100 insertions(+), 87 deletions(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala index 92f25fd4..be6c3337 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala @@ -5,6 +5,8 @@ import cats.data.State import cats.implicits._ import com.softwaremill.quicklens._ import scala.util.Random +import scala.util.chaining._ +import scala.collection.mutable.{Set => MSet} case class AncestryGraph( nodes: Map[ITID, (Type, Seq[Type])], @@ -12,103 +14,114 @@ case class AncestryGraph( typeAliases: Map[ITID, TypeLike] ) extends VarianceOps { + val cacheNeg: MSet[(TypeLike, TypeLike)] = MSet.empty + implicit class TypeOps(typ: TypeLike) { def isSubTypeOf(supr: TypeLike)(context: SignatureContext): State[VariableBindings, Boolean] = { - (typ, supr) match { - case (t: Type, _) if t.isStarProjection => State.pure(true) - case (_, s: Type) if s.isStarProjection => State.pure(true) - case (AndType(left, right), supr) => - left.isSubTypeOf(supr)(context).flatMap { res => - if (res) State.pure(true) - else right.isSubTypeOf(supr)(context) - } - case (typ, AndType(left, right)) => - typ.isSubTypeOf(left)(context).flatMap { res => - if (res) typ.isSubTypeOf(right)(context) - else State.pure(false) - } - case (OrType(left, right), supr) => - left.isSubTypeOf(supr)(context).flatMap { res => - if (res) right.isSubTypeOf(supr)(context) + if (cacheNeg.contains((typ, supr))) State.pure(false) + else { + val res = (typ, supr) match { + case (t: Type, _) if t.isStarProjection => State.pure(true) + case (_, s: Type) if s.isStarProjection => State.pure(true) + case (AndType(left, right), supr) => + left.isSubTypeOf(supr)(context).flatMap { res => + if (res) State.pure(true) + else right.isSubTypeOf(supr)(context) + } + case (typ, AndType(left, right)) => + typ.isSubTypeOf(left)(context).flatMap { res => + if (res) typ.isSubTypeOf(right)(context) + else State.pure(false) + } + case (OrType(left, right), supr) => + left.isSubTypeOf(supr)(context).flatMap { res => + if (res) right.isSubTypeOf(supr)(context) + else State.pure(false) + } + case (typ, OrType(left, right)) => + typ.isSubTypeOf(left)(context).flatMap { res => + if (res) State.pure(true) + else typ.isSubTypeOf(right)(context) + } + case (typ: TypeLambda, supr: TypeLambda) => + val dummyTypes = genDummyTypes(typ.args.size) + val typResult = substituteBindings(typ.result, typ.args.flatMap(_.itid).zip(dummyTypes).toMap) + val suprResult = substituteBindings(supr.result, supr.args.flatMap(_.itid).zip(dummyTypes).toMap) + if (typ.args.size == supr.args.size) typResult.isSubTypeOf(suprResult)(context) else State.pure(false) - } - case (typ, OrType(left, right)) => - typ.isSubTypeOf(left)(context).flatMap { res => - if (res) State.pure(true) - else typ.isSubTypeOf(right)(context) - } - case (typ: TypeLambda, supr: TypeLambda) => - val dummyTypes = genDummyTypes(typ.args.size) - val typResult = substituteBindings(typ.result, typ.args.flatMap(_.itid).zip(dummyTypes).toMap) - val suprResult = substituteBindings(supr.result, supr.args.flatMap(_.itid).zip(dummyTypes).toMap) - if (typ.args.size == supr.args.size) typResult.isSubTypeOf(suprResult)(context) - else State.pure(false) - case (_: TypeLambda, _) => - State.pure(false) - case (_, _: TypeLambda) => - State.pure(false) - case (typ: Type, supr: Type) if typ.isVariable && typ.isGeneric => - State.modify[VariableBindings](_.add(typ.itid.get, supr.modify(_.params).setTo(Seq.empty))) >> - checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) if supr.isVariable && supr.isGeneric => - State.modify[VariableBindings](_.add(supr.itid.get, typ.modify(_.params).setTo(Seq.empty))) >> - checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) if typ.isVariable && supr.isVariable => - val typConstraints = context.constraints.get(typ.name.name).toSeq.flatten - val suprConstraints = context.constraints.get(supr.name.name).toSeq.flatten - State.modify[VariableBindings](_.add(typ.itid.get, supr).add(supr.itid.get, typ)) >> - State.pure(typConstraints == suprConstraints) // TODO #56 Better 'equality' between two TypeVariables - case (typ: Type, supr: Type) if supr.isVariable => - if (supr.itid.get.isParsed) { - State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> - State.pure(true) - } else { - val constraints = context.constraints.get(supr.name.name).toSeq.flatten.toList - State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> - constraints - .foldLeft(State.pure[VariableBindings, Boolean](true)) { - case (acc, t) => - acc.flatMap { cond => - if (cond) typ.isSubTypeOf(t)(context) - else State.pure(false) - } - } - } - case (typ: Type, supr: Type) if typ.isVariable => - if (typ.itid.get.isParsed) { - val constraints = context.constraints.get(typ.name.name).toSeq.flatten.toList - State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> { - if (constraints.nonEmpty) { + case (_: TypeLambda, _) => + State.pure(false) + case (_, _: TypeLambda) => + State.pure(false) + case (typ: Type, supr: Type) if typ.isVariable && typ.isGeneric => + State.modify[VariableBindings](_.add(typ.itid.get, supr.modify(_.params).setTo(Seq.empty))) >> + checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) if supr.isVariable && supr.isGeneric => + State.modify[VariableBindings](_.add(supr.itid.get, typ.modify(_.params).setTo(Seq.empty))) >> + checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) if typ.isVariable && supr.isVariable => + val typConstraints = context.constraints.get(typ.name.name).toSeq.flatten + val suprConstraints = context.constraints.get(supr.name.name).toSeq.flatten + State.modify[VariableBindings](_.add(typ.itid.get, supr).add(supr.itid.get, typ)) >> + State.pure(typConstraints == suprConstraints) // TODO #56 Better 'equality' between two TypeVariables + case (typ: Type, supr: Type) if supr.isVariable => + if (supr.itid.get.isParsed) { + State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> + State.pure(true) + } else { + val constraints = context.constraints.get(supr.name.name).toSeq.flatten.toList + State.modify[VariableBindings](_.add(supr.itid.get, typ)) >> constraints - .foldLeft(State.pure[VariableBindings, Boolean](false)) { + .foldLeft(State.pure[VariableBindings, Boolean](true)) { case (acc, t) => acc.flatMap { cond => - if (cond) State.pure[VariableBindings, Boolean](true) - else t.isSubTypeOf(supr)(context) + if (cond) typ.isSubTypeOf(t)(context) + else State.pure(false) } } - } else State.pure(true) } - } else { - State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> - State.pure(true) - } - case (typ: Type, supr: Type) if typ.itid == supr.itid => checkTypeParamsByVariance(typ, supr, context) - case (typ: Type, supr: Type) => - nodes - .get(typ.itid.get) - .toList - .flatMap(node => specializeParents(typ, node)) - .map(_ -> supr) - .++(typeAliases.get(typ.itid.get).toList.flatMap(alias => dealias(typ, alias)).map(_ -> supr)) - .++(typeAliases.get(supr.itid.get).toList.flatMap(alias => dealias(supr, alias)).map(typ -> _)) - .foldLeft(State.pure[VariableBindings, Boolean](false)) { - case (acc, (t, s)) => - acc.flatMap { cond => - if (cond) State.pure(true) - else t.isSubTypeOf(s)(context) - } + case (typ: Type, supr: Type) if typ.isVariable => + if (typ.itid.get.isParsed) { + val constraints = context.constraints.get(typ.name.name).toSeq.flatten.toList + State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> { + if (constraints.nonEmpty) { + constraints + .foldLeft(State.pure[VariableBindings, Boolean](false)) { + case (acc, t) => + acc.flatMap { cond => + if (cond) State.pure[VariableBindings, Boolean](true) + else t.isSubTypeOf(supr)(context) + } + } + } else State.pure(true) + } + } else { + State.modify[VariableBindings](_.add(typ.itid.get, supr)) >> + State.pure(true) } + case (typ: Type, supr: Type) if typ.itid == supr.itid => checkTypeParamsByVariance(typ, supr, context) + case (typ: Type, supr: Type) => + nodes + .get(typ.itid.get) + .toList + .flatMap(node => specializeParents(typ, node)) + .map(_ -> supr) + .++(typeAliases.get(typ.itid.get).toList.flatMap(alias => dealias(typ, alias)).map(_ -> supr)) + .++(typeAliases.get(supr.itid.get).toList.flatMap(alias => dealias(supr, alias)).map(typ -> _)) + .foldLeft(State.pure[VariableBindings, Boolean](false)) { + case (acc, (t, s)) => + acc.flatMap { cond => + if (cond) State.pure(true) + else t.isSubTypeOf(s)(context) + } + } + } + res + .asInstanceOf[State[VariableBindings, Boolean]] + .map { b => + if (!b) cacheNeg.add((typ, supr)) + b + } } } } From 3aa0e13b87378fd834158e2f48c7676876634267 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 23 Jul 2021 16:37:44 +0200 Subject: [PATCH 11/12] fmt --- .../virtuslab/inkuire/engine/common/model/Signature.scala | 7 ++++++- .../engine/common/parser/ScalaSignatureParser.scala | 2 +- .../inkuire/engine/common/service/AncestryGraph.scala | 2 +- .../inkuire/engine/common/service/BaseMatchService.scala | 2 +- .../engine/common/service/ScalaSignatureResolver.scala | 2 +- .../inkuire/engine/common/service/TypeVariablesGraph.scala | 2 +- .../src/main/scala/org/virtuslab/inkuire/js/Main.scala | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala index 40be0a1d..1b6114b3 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/model/Signature.scala @@ -10,6 +10,11 @@ case class Signature( } object Signature { - def apply(receiver: Option[TypeLike], arguments: Seq[TypeLike], result: TypeLike, context: SignatureContext): Signature = + def apply( + receiver: Option[TypeLike], + arguments: Seq[TypeLike], + result: TypeLike, + context: SignatureContext + ): Signature = Signature(receiver.map(Contravariance), arguments.map(Contravariance), Covariance(result), context) } diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala index 485402e3..72f0c6c7 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/parser/ScalaSignatureParser.scala @@ -184,7 +184,7 @@ class ScalaSignatureParserService extends BaseSignatureParserService { private def curry(e: Signature): Signature = { e.result.typ match { - case t: Type if t.name.name == s"Function${t.params.size-1}" => + 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(_)), diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala index be6c3337..4a85bdbb 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala @@ -249,4 +249,4 @@ case class AncestryGraph( } case typ => typ } -} \ No newline at end of file +} diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala index 5e0d5553..b136d37d 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/BaseMatchService.scala @@ -5,5 +5,5 @@ import org.virtuslab.inkuire.engine.common.model.{ExternalSignature, InkuireDb, trait BaseMatchService { def inkuireDb: InkuireDb def findMatches(resolveResult: ResolveResult): Seq[ExternalSignature] - def isMatch(resolveResult: ResolveResult)(against: ExternalSignature): Boolean + def isMatch(resolveResult: ResolveResult)(against: ExternalSignature): Boolean } diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala index be5f4487..2deb264d 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/ScalaSignatureResolver.scala @@ -154,7 +154,7 @@ class DefaultSignatureResolver(inkuireDb: InkuireDb) extends BaseSignatureResolv resolveMultipleTypes(t.params.map(_.typ)).flatMap { params => ancestryGraph.values.map(_._1).filter(_.name == t.name).toSeq match { case Nil => Left(t.name.name) - case _ => + case _ => Right(for { generic <- ancestryGraph.values.map(_._1).filter(_.name == t.name).toSeq params <- params.map(_.zipVariances(generic.params)) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala index bf639001..01cbdad9 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/TypeVariablesGraph.scala @@ -51,4 +51,4 @@ case class TypeVariablesGraph(variableBindings: VariableBindings) { .runA(DfsState()) .value } -} \ No newline at end of file +} diff --git a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala index 383181c3..61c88f13 100644 --- a/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala +++ b/engineJs/src/main/scala/org/virtuslab/inkuire/js/Main.scala @@ -24,6 +24,6 @@ object Main extends App { def startApp(): Unit = { val inkuirePath = Globals.pathToRoot + "scripts/" - val worker = new Worker(inkuirePath + "inkuire-worker.js") + val worker = new Worker(inkuirePath + "inkuire-worker.js") } } From 841571634306e8cbdf6ec3ff685fbf25cfce3e9f Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 26 Jul 2021 09:39:34 +0200 Subject: [PATCH 12/12] Remove unnecassary cast --- .../inkuire/engine/common/service/AncestryGraph.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala index 4a85bdbb..f0f01ea2 100644 --- a/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala +++ b/engineCommon/shared/src/main/scala/org/virtuslab/inkuire/engine/common/service/AncestryGraph.scala @@ -20,7 +20,7 @@ case class AncestryGraph( def isSubTypeOf(supr: TypeLike)(context: SignatureContext): State[VariableBindings, Boolean] = { if (cacheNeg.contains((typ, supr))) State.pure(false) else { - val res = (typ, supr) match { + val res: State[VariableBindings, Boolean] = (typ, supr) match { case (t: Type, _) if t.isStarProjection => State.pure(true) case (_, s: Type) if s.isStarProjection => State.pure(true) case (AndType(left, right), supr) => @@ -117,7 +117,6 @@ case class AncestryGraph( } } res - .asInstanceOf[State[VariableBindings, Boolean]] .map { b => if (!b) cacheNeg.add((typ, supr)) b