From 48f0f59a6cbd74fea11b1353ae39d969c641f41e Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Fri, 13 Dec 2024 14:25:36 -1000 Subject: [PATCH] refactor Package and PackageMap some (#1318) * refactor Package and PackageMap some * simplify * move more code to Package * fix typo in name --- .../main/scala/org/bykn/bosatsu/Package.scala | 84 +++++++++++++- .../scala/org/bykn/bosatsu/PackageMap.scala | 106 +++--------------- 2 files changed, 100 insertions(+), 90 deletions(-) diff --git a/core/src/main/scala/org/bykn/bosatsu/Package.scala b/core/src/main/scala/org/bykn/bosatsu/Package.scala index 8abad1e4a..104e50316 100644 --- a/core/src/main/scala/org/bykn/bosatsu/Package.scala +++ b/core/src/main/scala/org/bykn/bosatsu/Package.scala @@ -1,8 +1,8 @@ package org.bykn.bosatsu -import cats.{Functor, Order, Parallel} +import cats.{Functor, Order, Parallel, Applicative} import cats.data.{Ior, ValidatedNel, Validated, NonEmptyList} -import cats.implicits._ +import cats.syntax.all._ import cats.parse.{Parser0 => P0, Parser => P} import org.typelevel.paiges.{Doc, Document} import scala.util.hashing.MurmurHash3 @@ -49,6 +49,11 @@ final case class Package[A, B, C, +D]( newImports: List[Import[A1, B1]] ): Package[A1, B1, C, D] = Package(name, newImports, exports, program) + + def getExport[T](i: ImportedName[T]): Option[NonEmptyList[ExportedName[C]]] = { + val iname = i.originalName + NonEmptyList.fromList(exports.filter(_.name == iname)) + } } object Package { @@ -64,6 +69,9 @@ object Package { type Parsed = Package[PackageName, Unit, Unit, List[Statement]] type Resolved = FixPackage[Unit, Unit, (List[Statement], ImportMap[PackageName, Unit])] + type ResolvedPackage = + Package[Resolved, Unit, Unit, (List[Statement], ImportMap[PackageName, Unit])] + type TypedProgram[T] = ( Program[TypeEnv[Kind.Arg], TypedExpr[T], Any], ImportMap[Interface, NonEmptyList[Referant[Kind.Arg]]] @@ -535,6 +543,78 @@ object Package { val prog1 = prog.copy(lets = prog.lets.filter { case (b, _, _) => fn(b) }) pack.copy(program = (prog1, importMap)) } + + def getImport[B]( + inside: PackageName, + i: ImportedName[B] + ): Either[PackageError, ImportedName[NonEmptyList[Referant[Kind.Arg]]]] = { + pack.getExport(i) match { + case Some(exps) => + val bs = exps.map(_.tag) + Right(i.map(_ => bs)) + case None => + Left( + PackageError.UnknownImportName( + inside, + pack.name, + pack.program._1.lets.iterator.map { case (n, _, _) => + (n: Identifier, ()) + }.toMap, + i, + pack.exports + ) + ) + } + } + } + + implicit class IfaceMethods(private val iface: Interface) extends AnyVal { + def getImportIface[A]( + inside: PackageName, + i: ImportedName[A] + ): Either[PackageError, ImportedName[NonEmptyList[Referant[Kind.Arg]]]] = { + iface.getExport(i) match { + case Some(exps) => + val bs = exps.map(_.tag) + Right(i.map(_ => bs)) + case None => + Left( + PackageError.UnknownImportFromInterface( + inside, + iface.name, + iface.exports.map(_.name), + i, + iface.exports + ) + ) + } + } + } + + implicit class ResolvedMethods(private val resolved: Resolved) extends AnyVal { + def importName[F[_], A]( + fromPackage: PackageName, + item: ImportedName[Unit] + )(recurse: ResolvedPackage => F[Typed[A]])(implicit F: Applicative[F]): F[Either[PackageError, (Package.Interface, ImportedName[NonEmptyList[Referant[Kind.Arg]]])]] = + Package.unfix(resolved) match { + case Right(p) => + /* + * Here we have a source we need to fully resolve + */ + recurse(p) + .map { packF => + val packInterface = Package.interfaceOf(packF) + packF.getImport(fromPackage, item) + .map((packInterface, _)) + } + case Left(iface) => + /* + * this import is already an interface, we can stop here + */ + // this is very fast and does not need to be done in a thread + F.pure(iface.getImportIface(fromPackage, item) + .map((iface, _))) + } } def orderByName[A, B, C, D]: Order[Package[A, B, C, D]] = diff --git a/core/src/main/scala/org/bykn/bosatsu/PackageMap.scala b/core/src/main/scala/org/bykn/bosatsu/PackageMap.scala index d5d572073..fbd7781d6 100644 --- a/core/src/main/scala/org/bykn/bosatsu/PackageMap.scala +++ b/core/src/main/scala/org/bykn/bosatsu/PackageMap.scala @@ -145,8 +145,8 @@ object PackageMap { type Inferred = Typed[Declaration] - /** This builds a DAG of actual packages where names have been replaced by the - * fully resolved packages + /** This builds a DAG of actual packages where on Import the PackageName have been replaced by the + * Either a Package.Interface (which gives exports only) or this same recursive structure. */ def resolvePackages[A, B, C]( map: PackageMap[PackageName, A, B, C], @@ -328,7 +328,7 @@ object PackageMap { // we know all the package names are unique here def foldMap( - m: Map[PackageName, (A, Package.Parsed)] + m: SortedMap[PackageName, (A, Package.Parsed)] ): (List[PackageError], PackageMap.ParsedImp) = { val initPm = PackageMap .empty[ @@ -338,7 +338,8 @@ object PackageMap { (List[Statement], ImportMap[PackageName, Unit]) ] - m.iterator.foldLeft((List.empty[PackageError], initPm)) { + // since the map is sorted, this order is deteriministic + m.foldLeft((List.empty[PackageError], initPm)) { case ((errs, pm), (_, (_, pack))) => val (lerrs, pp) = toProg(pack) (lerrs.toList ::: errs, pm + pp) @@ -350,14 +351,14 @@ object PackageMap { // combine the import errors now: val check: Ior[NonEmptyList[PackageError], Unit] = errs match { - case Nil => - Ior.right(()) + case Nil => Ior.right(()) case h :: tail => Ior.left(NonEmptyList(h, tail)) } // keep all the errors val nuEr: Ior[NonEmptyList[PackageError], Unit] = NonEmptyMap.fromMap(nonUnique) match { + case None => Ior.right(()) case Some(nenu) => val paths = nenu.map { case ((a, _), rest) => (a.show, rest.map(_._1.show)) @@ -367,8 +368,6 @@ object PackageMap { PackageError.DuplicatedPackageError(paths) ) ) - case None => - Ior.right(()) } (nuEr, check, res.toIor).parMapN((_, _, r) => r) @@ -418,54 +417,6 @@ object PackageMap { (nameToRes(p), i) } - def getImport[A, B]( - packF: Package.Inferred, - exMap: Map[Identifier, NonEmptyList[ExportedName[A]]], - i: ImportedName[B] - ): Ior[NonEmptyList[PackageError], ImportedName[NonEmptyList[A]]] = - exMap.get(i.originalName) match { - case None => - Ior.left( - NonEmptyList.one( - PackageError.UnknownImportName( - nm, - packF.name, - packF.program._1.lets.iterator.map { case (n, _, _) => - (n: Identifier, ()) - }.toMap, - i, - exMap.iterator.flatMap(_._2.toList).toList - ) - ) - ) - case Some(exps) => - val bs = exps.map(_.tag) - Ior.right(i.map(_ => bs)) - } - - def getImportIface[A, B]( - packF: Package.Interface, - exMap: Map[Identifier, NonEmptyList[ExportedName[A]]], - i: ImportedName[B] - ): Ior[NonEmptyList[PackageError], ImportedName[NonEmptyList[A]]] = - exMap.get(i.originalName) match { - case None => - Ior.left( - NonEmptyList.one( - PackageError.UnknownImportFromInterface( - nm, - packF.name, - packF.exports.map(_.name), - i, - exMap.iterator.flatMap(_._2.toList).toList - ) - ) - ) - case Some(exps) => - val bs = exps.map(_.tag) - Ior.right(i.map(_ => bs)) - } - /* * This resolves imports from PackageNames into fully typed Packages * @@ -473,41 +424,20 @@ object PackageMap { * type can have the same name as a constructor. After this step, each * distinct object has its own entry in the list */ - type IName = NonEmptyList[Referant[Kind.Arg]] - - def stepImport( - fixpack: Package.Resolved, - item: ImportedName[Unit] - ): FutVal[(Package.Interface, ImportedName[IName])] = - Package.unfix(fixpack) match { - case Right(p) => - /* - * Here we have a source we need to fully resolve - */ - IorT(recurse(p)) - .flatMap { case (_, packF) => - val packInterface = Package.interfaceOf(packF) - val exMap = packF.exports.groupByNel(_.name) - val ior = getImport(packF, exMap, item) - .map((packInterface, _)) - IorT.fromIor(ior) - } - case Left(iface) => - /* - * this import is already an interface, we can stop here - */ - val exMap = iface.exports.groupByNel(_.name) - // this is very fast and does not need to be done in a thread - val ior = - getImportIface(iface, exMap, item) - .map((iface, _)) - IorT.fromIor(ior) - } val inferImports: FutVal[ ImportMap[Package.Interface, NonEmptyList[Referant[Kind.Arg]]] - ] = - resolvedImports.parTraverse(stepImport(_, _)) + ] = { + // here we just need the interface, not the TypeEnv + val rec1 = recurse.andThen { res => IorT(res).map(_._2) } + + resolvedImports.parTraverse { (fixpack: Package.Resolved, item: ImportedName[Unit]) => + fixpack.importName(nm, item)(rec1(_)) + .flatMap { either => + IorT.fromEither(either.left.map(NonEmptyList.one(_))) + } + } + } val inferBody = inferImports