From 3b5464a25f269382cab6b494fdfd97d73b346cd8 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sat, 21 Dec 2024 16:20:46 -1000 Subject: [PATCH 1/3] Remove optional ops from PlatformIO --- .../scala/org/bykn/bosatsu/IOPlatformIO.scala | 48 ++-- .../org/bykn/bosatsu/Fs2PlatformIO.scala | 38 +-- .../scala/org/bykn/bosatsu/MainModule.scala | 88 ++++--- .../scala/org/bykn/bosatsu/MemoryMain.scala | 221 +++++++++++++----- .../scala/org/bykn/bosatsu/PlatformIO.scala | 32 ++- .../scala/org/bykn/bosatsu/TestUtils.scala | 25 +- 6 files changed, 287 insertions(+), 165 deletions(-) diff --git a/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala b/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala index 47f637b8d..10abff34a 100644 --- a/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala +++ b/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala @@ -26,29 +26,23 @@ object IOPlatformIO extends PlatformIO[IO, JPath] { override def pathArg: Argument[Path] = Argument.readPath - def resolve(base: JPath, p: List[String]): JPath = - p.foldLeft(base)(_.resolve(_)) - override def moduleIOMonad: MonadError[IO, Throwable] = cats.effect.IO.asyncForIO - def readPath(path: Path): IO[String] = + def readUtf8(path: Path): IO[String] = IO.blocking(new String(java.nio.file.Files.readAllBytes(path), "utf-8")) - val resolvePath: Some[(Path, PackageName) => IO[Option[Path]]] = - Some( - { (root: Path, pack: PackageName) => - val dir = pack.parts.init.foldLeft(root)(_.resolve(_)) - val filePath = dir.resolve(pack.parts.last + ".bosatsu") - IO.blocking { - // this is a side-effect since file is mutable - // and talks to the file system - val file = filePath.toFile - if (file.exists()) Some(filePath) - else None - } + def fsDataType(p: Path): IO[Option[PlatformIO.FSDataType]] = + IO.blocking { + val f = p.toFile() + if (f.exists()) Some { + if (f.isDirectory()) PlatformIO.FSDataType.Dir + else PlatformIO.FSDataType.File } - ) + else None + } + + def resolve(p: Path, c: String): Path = p.resolve(c) def read[A <: GeneratedMessage]( path: Path @@ -106,18 +100,16 @@ object IOPlatformIO extends PlatformIO[IO, JPath] { IO.fromTry(ProtoConverter.packagesToProto(packages)) .flatMap(write(_, path)) - def unfoldDir: Option[Path => IO[Option[IO[List[Path]]]]] = Some { - (path: Path) => - IO.blocking { - val f = path.toFile + def unfoldDir(path: Path): IO[Option[IO[List[Path]]]] = + IO.blocking { + val f = path.toFile - if (f.isDirectory()) { - Some(IO.blocking { - f.listFiles.iterator.map(_.toPath).toList - }) - } else None - } - } + if (f.isDirectory()) { + Some(IO.blocking { + f.listFiles.iterator.map(_.toPath).toList + }) + } else None + } def hasExtension(str: String): Path => Boolean = { (path: Path) => path.toString.endsWith(str) diff --git a/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala b/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala index 64c741806..9c1aa4e2a 100644 --- a/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala +++ b/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala @@ -32,9 +32,21 @@ object Fs2PlatformIO extends PlatformIO[IO, Path] { private val FilesIO = Files.forIO - def readPath(p: Path): IO[String] = + def readUtf8(p: Path): IO[String] = FilesIO.readUtf8(p).compile.string + def fsDataType(p: Path): IO[Option[PlatformIO.FSDataType]] = + FilesIO.exists(p, followLinks = true) + .flatMap { + case false => IO.pure(None) + case true => + FilesIO.isDirectory(p, followLinks = true) + .map { + case true => Some(PlatformIO.FSDataType.Dir) + case false => Some(PlatformIO.FSDataType.File) + } + } + def readPackages(paths: List[Path]): IO[List[Package.Typed[Unit]]] = paths.parTraverse { path => for { @@ -66,6 +78,8 @@ object Fs2PlatformIO extends PlatformIO[IO, Path] { else None } + def resolve(p: Path, c: String): Path = p.resolve(c) + /** Modules optionally have the capability to combine paths into a tree */ val resolvePath: Option[(Path, PackageName) => IO[Option[Path]]] = Some { @@ -84,18 +98,15 @@ object Fs2PlatformIO extends PlatformIO[IO, Path] { * * if the given path is a directory, return Some and all the first children. */ - def unfoldDir: Option[Path => IO[Option[IO[List[Path]]]]] = Some { - (path: Path) => { - FilesIO.isDirectory(path, followLinks = true) - .map { - case true => Some { - // create a list of children - FilesIO.list(path).compile.toList - } - case false => None + def unfoldDir(path: Path): IO[Option[IO[List[Path]]]] = + FilesIO.isDirectory(path, followLinks = true) + .map { + case true => Some { + // create a list of children + FilesIO.list(path).compile.toList } - } - } + case false => None + } def hasExtension(str: String): Path => Boolean = { (path: Path) => path.extName == str } @@ -114,9 +125,6 @@ object Fs2PlatformIO extends PlatformIO[IO, Path] { .compile .drain - def resolve(base: Path, p: List[String]): Path = - p.foldLeft(base)(_.resolve(_)) - // this is println actually def print(str: String): IO[Unit] = IO.println(str) diff --git a/core/src/main/scala/org/bykn/bosatsu/MainModule.scala b/core/src/main/scala/org/bykn/bosatsu/MainModule.scala index 1818db316..6d118c02d 100644 --- a/core/src/main/scala/org/bykn/bosatsu/MainModule.scala +++ b/core/src/main/scala/org/bykn/bosatsu/MainModule.scala @@ -137,7 +137,7 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { paths .traverse { path => val defaultPack = packRes.packageNameFor(path) - readPath(path).map { str => + readUtf8(path).map { str => parseStart(Package.headerParser(defaultPack), path, str) .map { case (_, pp) => (path, pp) } } @@ -189,7 +189,7 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { packRes .pathFor(search) .flatMap(_.traverse { path => - readPath(path).map((path, _)) + readUtf8(path).map((path, _)) }) } @@ -299,7 +299,7 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { p: P0[A], path: Path ): IO[Either[Throwable, ValidatedNel[ParseError, (LocationMap, A)]]] = - readPath(path).attempt + readUtf8(path).attempt .map(_.map(parseString(p, path, _))) /** like typecheck, but a no-op for empty lists @@ -520,7 +520,7 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { def read = moduleIOMonad.pure(asString) } case class FromPath(path: Path) extends JsonInput { - def read = readPath(path) + def read = readUtf8(path) } } @@ -666,35 +666,31 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { .orEmpty .map(paths => paths.foldMap(PathGen.Direct[IO, Path](_): PathGen)) - unfoldDir match { - case None => direct - case Some(unfold) => - val select = hasExtension(ext) - val child1 = Opts - .options[Path](arg + "_dir", help = s"all $help in directory") - .orEmpty - .map { paths => - paths.foldMap( - PathGen - .ChildrenOfDir[IO, Path](_, select, false, unfold): PathGen - ) - } - val childMany = Opts - .options[Path]( - arg + "_all_subdir", - help = s"all $help recursively in all directories" - ) - .orEmpty - .map { paths => - paths.foldMap( - PathGen - .ChildrenOfDir[IO, Path](_, select, true, unfold): PathGen - ) - } + val select = hasExtension(ext) + val child1 = Opts + .options[Path](arg + "_dir", help = s"all $help in directory") + .orEmpty + .map { paths => + paths.foldMap( + PathGen + .ChildrenOfDir[IO, Path](_, select, false, unfoldDir(_)): PathGen + ) + } + val childMany = Opts + .options[Path]( + arg + "_all_subdir", + help = s"all $help recursively in all directories" + ) + .orEmpty + .map { paths => + paths.foldMap( + PathGen + .ChildrenOfDir[IO, Path](_, select, true, unfoldDir(_)): PathGen + ) + } - (direct, child1, childMany).mapN { (a, b, c) => - (a :: b :: c :: Nil).combineAll - } + (direct, child1, childMany).mapN { (a, b, c) => + (a :: b :: c :: Nil).combineAll } } private val srcs = @@ -712,21 +708,17 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { help = "for implicit package names, consider these paths as roots" ) private val packSearch = - resolvePath match { - case None => Opts(None) - case some @ Some(_) => - Opts - .flag( - "search", - help = - "if set, we search the package_roots for imports not explicitly given" - ) - .orFalse - .map { - case true => some - case false => None - } - } + Opts + .flag( + "search", + help = + "if set, we search the package_roots for imports not explicitly given" + ) + .orFalse + .map { + case true => Some((p, pn) => resolveFile(p, pn)) + case false => None + } private val packRes: Opts[PackageResolver] = (packRoot @@ -774,7 +766,7 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { for { pn <- inputs.packMap(this, Nil, errColor) (packs, names) = pn - optString <- generator.traverse(readPath) + optString <- generator.traverse(readUtf8) dataTry = optString.transpiler.renderAll(packs, optString.args) data <- moduleIOMonad.fromTry(dataTry) } yield Output.TranspileOut(data, outDir) diff --git a/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala b/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala index a66069e13..3e82ceedd 100644 --- a/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala +++ b/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala @@ -1,17 +1,18 @@ package org.bykn.bosatsu import cats.MonadError -import cats.data.Kleisli +import cats.data.{Chain, Kleisli, Validated} import com.monovore.decline.Argument import scala.collection.immutable.SortedMap import org.bykn.bosatsu.tool.Output import org.typelevel.paiges.Doc import cats.syntax.all._ +import cats.data.ValidatedNel -class MemoryMain[G[_], K]( - platform: PlatformIO[Kleisli[G, MemoryMain.State[K], *], K]) extends - MainModule[Kleisli[G, MemoryMain.State[K], *], K](platform) { +class MemoryMain[G[_]]( + platform: PlatformIO[Kleisli[G, MemoryMain.State, *], Chain[String]]) extends + MainModule[Kleisli[G, MemoryMain.State, *], Chain[String]](platform) { import platformIO._ @@ -29,31 +30,49 @@ class MemoryMain[G[_], K]( } def runWith( - files: Iterable[(K, String)], - packages: Iterable[(K, List[Package.Typed[Unit]])] = Nil, - interfaces: Iterable[(K, List[Package.Interface])] = Nil - )(cmd: List[String]): G[Output[K]] = + files: Iterable[(Chain[String], String)], + packages: Iterable[(Chain[String], List[Package.Typed[Unit]])] = Nil, + interfaces: Iterable[(Chain[String], List[Package.Interface])] = Nil + )(cmd: List[String])(implicit G: MonadError[G, Throwable]): G[Output[Chain[String]]] = run(cmd) match { case Left(msg) => - moduleIOMonad.raiseError[Output[K]]( + G.raiseError[Output[Chain[String]]]( new Exception(s"got the help message for: $cmd: $msg") ) - .run(SortedMap.empty[K, MemoryMain.FileContent]) case Right(io) => - val state0 = - files.foldLeft(SortedMap.empty[K, MemoryMain.FileContent]) { - case (st, (k, str)) => - st.updated(k, MemoryMain.FileContent.Str(str)) + for { + state0 <- + files.toList.foldM(MemoryMain.State.empty) { + case (st, (k, str)) => + st.withFile(k, MemoryMain.FileContent.Str(str)) match { + case Some(s1) => G.pure(s1) + case None => + G.raiseError[MemoryMain.State]( + new Exception(s"couldn't add file: $k to state = $st") + ) + } + } + state1 <- packages.toList.foldM(state0) { case (st, (k, packs)) => + st.withFile(k, MemoryMain.FileContent.Packages(packs)) match { + case Some(s1) => G.pure(s1) + case None => + G.raiseError[MemoryMain.State]( + new Exception(s"couldn't add file: $k to state = $st") + ) + } } - val state1 = packages.foldLeft(state0) { case (st, (k, packs)) => - st.updated(k, MemoryMain.FileContent.Packages(packs)) - } - val state2 = interfaces.foldLeft(state1) { case (st, (k, ifs)) => - st.updated(k, MemoryMain.FileContent.Interfaces(ifs)) - } - io.run(state2) + state2 <- interfaces.toList.foldM(state1) { case (st, (k, ifs)) => + st.withFile(k, MemoryMain.FileContent.Interfaces(ifs)) match { + case Some(s1) => G.pure(s1) + case None => + G.raiseError[MemoryMain.State]( + new Exception(s"couldn't add file: $k to state = $st") + ) + } + } + res <- io.run(state2) + } yield res } - } object MemoryMain { @@ -64,31 +83,98 @@ object MemoryMain { case class Interfaces(ifs: List[Package.Interface]) extends FileContent } - type State[K] = SortedMap[K, FileContent] + case class State(children: SortedMap[String, Either[State, FileContent]]) { + def get(path: Chain[String]): Option[Either[State, FileContent]] = + path.uncons match { + case None => Some(Left(this)) + case Some((head, tail)) => + children.get(head).flatMap { + case Left(s) => s.get(tail) + case Right(fc) => + if (tail.isEmpty) Some(Right(fc)) + else None + } + } + + def withDir(path: Chain[String]): Option[State] = + path.uncons match { + case None => Some(this) + case Some((h, t)) => + children.get(h) match { + case Some(Right(_)) => + // this is a file + None + case Some(Left(s)) => + s.withDir(t).map { s1 => + copy(children = children.updated(h, Left(s1))) + } + case None => + State.empty.withDir(t).map { s1 => + copy(children = children.updated(h, Left(s1))) + } + } + } + + def withFile(path: Chain[String], fc: FileContent): Option[State] = + path.uncons.flatMap { case (h, t) => + if (t.isEmpty) { + children.get(h) match { + case Some(Right(_)) | None => + // a file, or no file + Some(copy(children = children.updated(h, Right(fc)))) + case Some(Left(_)) => + // already a directory here + None + } + } + else children.get(h) match { + // we need to find a directory + case Some(Right(_)) => + // this is a file + None + case Some(Left(s)) => + s.withFile(t, fc).map { s1 => + copy(children = children.updated(h, Left(s1))) + } + case None => + State.empty.withFile(t, fc).map { s1 => + copy(children = children.updated(h, Left(s1))) + } + } + } + } + + object State { + val empty: State = State(SortedMap.empty) + } - def apply[G[_], K: Argument: Ordering](split: K => List[String])(implicit innerMonad: MonadError[G, Throwable]): MemoryMain[G, K] = - new MemoryMain(memoryPlatformIO[G, K](split)) + def apply[G[_]](implicit innerMonad: MonadError[G, Throwable]): MemoryMain[G] = + new MemoryMain(memoryPlatformIO[G]) - def memoryPlatformIO[G[_], K](split: K => List[String])(implicit - pathArg0: Argument[K], - pathOrd: Ordering[K], - innerMonad: MonadError[G, Throwable]): PlatformIO[Kleisli[G, State[K], *], K] = { + def memoryPlatformIO[G[_]](implicit + innerMonad: MonadError[G, Throwable]): PlatformIO[Kleisli[G, State, *], Chain[String]] = { - val catsDefaultME = implicitly[MonadError[Kleisli[G, State[K], *], Throwable]] + val catsDefaultME = implicitly[MonadError[Kleisli[G, State, *], Throwable]] - new PlatformIO[Kleisli[G, State[K], *], K] { - type F[A] = Kleisli[G, State[K], A] - type Path = K + new PlatformIO[Kleisli[G, State, *], Chain[String]] { + type F[A] = Kleisli[G, State, A] + type Path = Chain[String] def moduleIOMonad: MonadError[F, Throwable] = catsDefaultME - def pathOrdering = pathOrd - def pathArg: Argument[K] = pathArg0 + def pathOrdering = Chain.catsDataOrderForChain[String].toOrdering + val pathArg: Argument[Path] = + new Argument[Path] { + def defaultMetavar: String = "path" + def read(string: String): ValidatedNel[String,Path] = + if (string == "") Validated.valid(Chain.empty) + else Validated.valid(Chain.fromSeq(string.split("/", -1).toIndexedSeq)) + } - def readPath(p: Path): F[String] = + def readUtf8(p: Path): F[String] = Kleisli - .ask[G, State[K]] + .ask[G, State] .flatMap { files => files.get(p) match { - case Some(MemoryMain.FileContent.Str(res)) => moduleIOMonad.pure(res) + case Some(Right(MemoryMain.FileContent.Str(res))) => moduleIOMonad.pure(res) case other => moduleIOMonad.raiseError( new Exception(s"expect String content, found: $other") @@ -96,16 +182,28 @@ object MemoryMain { } } - def resolvePath: Option[(Path, PackageName) => F[Option[Path]]] = None + def fsDataType(p: Path): Kleisli[G,State,Option[PlatformIO.FSDataType]] = + Kleisli + .ask[G, State] + .map { files => + files.get(p) match { + case Some(Right(_)) => Some(PlatformIO.FSDataType.File) + case Some(Left(_)) => Some(PlatformIO.FSDataType.Dir) + case None => None + } + } + + def resolve(p: Chain[String], child: String): Chain[String] = + p :+ child def readPackages(paths: List[Path]): F[List[Package.Typed[Unit]]] = Kleisli - .ask[G, MemoryMain.State[K]] + .ask[G, MemoryMain.State] .flatMap { files => paths .traverse { path => files.get(path) match { - case Some(MemoryMain.FileContent.Packages(res)) => + case Some(Right(MemoryMain.FileContent.Packages(res))) => moduleIOMonad.pure(res) case other => moduleIOMonad.raiseError[List[Package.Typed[Unit]]]( @@ -118,12 +216,12 @@ object MemoryMain { def readInterfaces(paths: List[Path]): F[List[Package.Interface]] = Kleisli - .ask[G, MemoryMain.State[K]] + .ask[G, MemoryMain.State] .flatMap { files => paths .traverse { path => files.get(path) match { - case Some(MemoryMain.FileContent.Interfaces(res)) => + case Some(Right(MemoryMain.FileContent.Interfaces(res))) => moduleIOMonad.pure(res) case other => moduleIOMonad.raiseError[List[Package.Interface]]( @@ -134,32 +232,43 @@ object MemoryMain { .map(_.flatten) } - def unfoldDir: Option[Path => F[Option[F[List[Path]]]]] = None + def unfoldDir(path: Path): F[Option[F[List[Path]]]] = + Kleisli + .ask[G, MemoryMain.State] + .map { files => + files.get(path).flatMap { + case Left(state) => + Some(moduleIOMonad.pure(state.children.iterator.collect { + case (k, Right(_)) => + path :+ k + } + .toList)) + case Right(_) => None + } + } def hasExtension(str: String): (Path => Boolean) = Function.const(false)(_) - def pathPackage(roots: List[K], packFile: K): Option[PackageName] = { - val fparts = split(packFile) - + def pathPackage(roots: List[Path], packFile: Path): Option[PackageName] = { + val parts = packFile.toList def getP(p: Path): Option[PackageName] = { - val splitP = split(p) - if (fparts.startsWith(splitP)) { - val parts = fparts.drop(splitP.length) - PackageName.parse(parts.mkString("/")) + if (parts.startsWith(p.toList)) { + val parts1 = parts.drop(p.length.toInt) + PackageName.parse(parts1.mkString("/")) } else None } roots.collectFirstSome(getP) } - def writeDoc(p: K, d: Doc): F[Unit] = + def writeDoc(p: Path, d: Doc): F[Unit] = catsDefaultME.raiseError(new Exception(s"writeDoc($p, $d) is unimplemented on a read only platform")) - def writeInterfaces(ifaces: List[Package.Interface], path: K): F[Unit] = + def writeInterfaces(ifaces: List[Package.Interface], path: Path): F[Unit] = catsDefaultME.raiseError(new Exception(s"writeInterfaces($ifaces, $path) is unimplemented on a read only platform")) - def writePackages[A](packs: List[Package.Typed[A]], path: K): F[Unit] = + def writePackages[A](packs: List[Package.Typed[A]], path: Path): F[Unit] = catsDefaultME.raiseError(new Exception(s"writePackages($packs, $path) is unimplemented on a read only platform")) def writeStdout(doc: Doc): F[Unit] = @@ -168,8 +277,8 @@ object MemoryMain { def print(str: String): F[Unit] = catsDefaultME.raiseError(new Exception(s"print($str) is unimplemented on a read only platform")) - // TODO: this is broken - def resolve(base: K, parts: List[String]): K = base + override def resolve(base: Path, parts: List[String]): Path = + base ++ Chain.fromSeq(parts) } } } diff --git a/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala b/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala index 1657e9e0b..1a8ef3a3b 100644 --- a/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala +++ b/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala @@ -1,6 +1,7 @@ package org.bykn.bosatsu import cats.MonadError +import cats.data.ValidatedNel import com.monovore.decline.Argument import org.typelevel.paiges.Doc @@ -11,9 +12,12 @@ trait PlatformIO[F[_], Path] { implicit def pathArg: Argument[Path] implicit def pathOrdering: Ordering[Path] - def readPath(p: Path): F[String] - def readPackages(paths: List[Path]): F[List[Package.Typed[Unit]]] + final def path(str: String): ValidatedNel[String, Path] = + pathArg.read(str) + + def readUtf8(p: Path): F[String] + def readPackages(paths: List[Path]): F[List[Package.Typed[Unit]]] def readInterfaces(paths: List[Path]): F[List[Package.Interface]] /** given an ordered list of prefered roots, if a packFile starts with one of @@ -21,15 +25,23 @@ trait PlatformIO[F[_], Path] { */ def pathPackage(roots: List[Path], packFile: Path): Option[PackageName] - /** Modules optionally have the capability to combine paths into a tree - */ - def resolvePath: Option[(Path, PackageName) => F[Option[Path]]] + def fsDataType(p: Path): F[Option[PlatformIO.FSDataType]] + + def resolve(p: Path, child: String): Path + def resolveFile(root: Path, pack: PackageName): F[Option[Path]] = { + val dir = pack.parts.init.foldLeft(root)(resolve(_, _)) + val filePath = resolve(dir, pack.parts.last + ".bosatsu") + fsDataType(filePath).map { + case Some(PlatformIO.FSDataType.File) => Some(filePath) + case _ => None + } + } /** some modules have paths that form directory trees * * if the given path is a directory, return Some and all the first children. */ - def unfoldDir: Option[Path => F[Option[F[List[Path]]]]] + def unfoldDir(path: Path): F[Option[F[List[Path]]]] def hasExtension(str: String): Path => Boolean @@ -43,7 +55,8 @@ trait PlatformIO[F[_], Path] { } - def resolve(base: Path, p: List[String]): Path + def resolve(base: Path, p: List[String]): Path = + p.foldLeft(base)(resolve(_, _)) // this is println actually def print(str: String): F[Unit] @@ -79,4 +92,9 @@ object PlatformIO { else roots.collectFirstSome(getP) } + sealed abstract class FSDataType + object FSDataType { + case object Dir extends FSDataType + case object File extends FSDataType + } } \ No newline at end of file diff --git a/core/src/test/scala/org/bykn/bosatsu/TestUtils.scala b/core/src/test/scala/org/bykn/bosatsu/TestUtils.scala index 9a448124d..ddeee4e84 100644 --- a/core/src/test/scala/org/bykn/bosatsu/TestUtils.scala +++ b/core/src/test/scala/org/bykn/bosatsu/TestUtils.scala @@ -1,6 +1,6 @@ package org.bykn.bosatsu -import cats.data.{Ior, Validated, NonEmptyList} +import cats.data.{Chain, Ior, Validated, NonEmptyList} import java.nio.file.{Files, Paths} import org.bykn.bosatsu.rankn._ import org.bykn.bosatsu.tool.Output @@ -153,18 +153,17 @@ object TestUtils { } - def makeInputArgs(files: List[(Int, Any)]): List[String] = - ("--package_root" :: Int.MaxValue.toString :: Nil) ::: files.flatMap { - case (idx, _) => "--input" :: idx.toString :: Nil + def makeInputArgs(files: List[(Chain[String], Any)]): List[String] = + ("--package_root" :: "" :: Nil) ::: files.flatMap { + case (idx, _) => "--input" :: idx.iterator.mkString("/") :: Nil } - private val module = MemoryMain[Either[Throwable, *], Int]({ idx => - if (idx == Int.MaxValue) Nil - else List(s"Package$idx") - }) + private val module = MemoryMain[Either[Throwable, *]] def evalTest(packages: List[String], mainPackS: String, expected: Value) = { - val files = packages.zipWithIndex.map(_.swap) + val files = packages.zipWithIndex.map(_.swap).map { case (idx, content) => + Chain.one(s"Package$idx") -> content + } module.runWith(files)( "eval" :: "--main" :: mainPackS :: makeInputArgs(files) @@ -190,7 +189,9 @@ object TestUtils { mainPackS: String, expected: Json ) = { - val files = packages.zipWithIndex.map(_.swap) + val files = packages.zipWithIndex.map(_.swap).map { case (idx, content) => + Chain.one(s"Package$idx") -> content + } module.runWith(files)( "json" :: "write" :: "--main" :: mainPackS :: "--output" :: "-1" :: makeInputArgs( @@ -211,7 +212,9 @@ object TestUtils { mainPackS: String, assertionCount: Int ) = { - val files = packages.zipWithIndex.map(_.swap) + val files = packages.zipWithIndex.map(_.swap).map { case (idx, content) => + Chain.one(s"Package$idx") -> content + } module.runWith(files)( "test" :: "--test_package" :: mainPackS :: makeInputArgs(files) From 88ff2fa41811ac8216172974e541f74654d86849 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sat, 21 Dec 2024 16:34:57 -1000 Subject: [PATCH 2/3] fix some js code --- .../scala/org/bykn/bosatsu/jsapi/JsApi.scala | 24 +++++++++++++++---- .../scala/org/bykn/bosatsu/jsui/Store.scala | 7 +++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/jsapi/src/main/scala/org/bykn/bosatsu/jsapi/JsApi.scala b/jsapi/src/main/scala/org/bykn/bosatsu/jsapi/JsApi.scala index 143a1524e..5c20bd312 100644 --- a/jsapi/src/main/scala/org/bykn/bosatsu/jsapi/JsApi.scala +++ b/jsapi/src/main/scala/org/bykn/bosatsu/jsapi/JsApi.scala @@ -1,6 +1,7 @@ package org.bykn.bosatsu package jsapi +import cats.data.Chain import org.typelevel.paiges.Doc import org.bykn.bosatsu.tool.Output import scala.scalajs.js @@ -16,10 +17,13 @@ object JsApi { private def splitPath(p: String): List[String] = p.split("/", -1).toList.map(_.toLowerCase.capitalize) - private val module = MemoryMain[Either[Throwable, *], String](splitPath) + def normalize(s: String): String = + splitPath(s).mkString("/") + + private val module = MemoryMain[Either[Throwable, *]] private def makeInputArgs(keys: Iterable[String]): List[String] = - keys.iterator.flatMap(key => "--input" :: key :: Nil).toList + keys.iterator.flatMap(key => "--input" :: normalize(key) :: Nil).toList class Error(val error_message: String) extends js.Object @@ -37,7 +41,13 @@ object JsApi { val main = if (mainPackage != null) "--main" :: mainPackage :: baseArgs else "--main_file" :: mainFile :: baseArgs - module.runWith(files)("eval" :: main ::: makeInputArgs(files.keys)) match { + + val filePaths = files.iterator.map { case (k, data) => + Chain.fromSeq(splitPath(k)) -> data + } + .toMap + + module.runWith(filePaths)("eval" :: main ::: makeInputArgs(files.keys)) match { case Left(err) => new Error(s"error: ${err.getMessage}") case Right(Output.EvaluationResult(_, tpe, resDoc)) => @@ -86,7 +96,13 @@ object JsApi { val main = if (mainPackage != null) "--main" :: mainPackage :: baseArgs else "--main_file" :: mainFile :: baseArgs - module.runWith(files)( + + val filePaths = files.iterator.map { case (k, data) => + Chain.fromSeq(splitPath(k)) -> data + } + .toMap + + module.runWith(filePaths)( "json" :: "write" :: "--output" :: "" :: main ::: makeInputArgs( files.keys ) diff --git a/jsui/src/main/scala/org/bykn/bosatsu/jsui/Store.scala b/jsui/src/main/scala/org/bykn/bosatsu/jsui/Store.scala index d1eba3349..39f00c1aa 100644 --- a/jsui/src/main/scala/org/bykn/bosatsu/jsui/Store.scala +++ b/jsui/src/main/scala/org/bykn/bosatsu/jsui/Store.scala @@ -1,5 +1,6 @@ package org.bykn.bosatsu.jsui +import cats.data.Chain import cats.effect.{IO, Resource} import org.bykn.bosatsu.{MemoryMain, Package, Test, rankn} import org.bykn.bosatsu.tool.Output @@ -9,9 +10,9 @@ import org.scalajs.dom.window.localStorage import Action.Cmd object Store { - val memoryMain = MemoryMain[Either[Throwable, *], String](_.split("/", -1).toList) + val memoryMain = MemoryMain[Either[Throwable, *]] - type HandlerFn = Output[String] => String + type HandlerFn = Output[Chain[String]] => String def cmdHandler(cmd: Cmd): (List[String], HandlerFn) = cmd match { case Cmd.Eval => @@ -93,7 +94,7 @@ object Store { val (args, handler) = cmdHandler(cmd) val res = memoryMain.runWith( - files = Map("root/WebDemo" -> str) + files = Map(Chain("root", "WebDemo") -> str) )(args) match { case Right(good) => handler(good) case Left(err) => From 8879bf19fd2e7691987cbdaf4a10c934d57cc7d1 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sun, 22 Dec 2024 07:40:16 -1000 Subject: [PATCH 3/3] minor fixes --- cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala | 2 +- cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala | 3 +-- core/src/main/scala/org/bykn/bosatsu/MainModule.scala | 4 ++-- core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala | 2 +- core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala | 5 ++--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala b/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala index 10abff34a..d2ff3faea 100644 --- a/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala +++ b/cli/src/main/scala/org/bykn/bosatsu/IOPlatformIO.scala @@ -134,7 +134,7 @@ object IOPlatformIO extends PlatformIO[IO, JPath] { } } - def print(str: String): IO[Unit] = + def println(str: String): IO[Unit] = IO.println(str) def writeStdout(doc: Doc): IO[Unit] = diff --git a/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala b/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala index 9c1aa4e2a..3fc1d48cd 100644 --- a/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala +++ b/cliJS/src/main/scala/org/bykn/bosatsu/Fs2PlatformIO.scala @@ -125,8 +125,7 @@ object Fs2PlatformIO extends PlatformIO[IO, Path] { .compile .drain - // this is println actually - def print(str: String): IO[Unit] = + def println(str: String): IO[Unit] = IO.println(str) def writeInterfaces( diff --git a/core/src/main/scala/org/bykn/bosatsu/MainModule.scala b/core/src/main/scala/org/bykn/bosatsu/MainModule.scala index 6d118c02d..948ae9da0 100644 --- a/core/src/main/scala/org/bykn/bosatsu/MainModule.scala +++ b/core/src/main/scala/org/bykn/bosatsu/MainModule.scala @@ -1326,12 +1326,12 @@ abstract class MainModule[IO[_], Path](val platformIO: PlatformIO[IO, Path]) { val testReport = Test.outputFor(evalTest, color) val success = !hasMissing && (testReport.fails == 0) val code = if (success) ExitCode.Success else ExitCode.Error - print(testReport.doc.render(80)).as(code) + println(testReport.doc.render(80)).as(code) case Output.EvaluationResult(_, tpe, resDoc) => val tDoc = rankn.Type.fullyResolvedDocument.document(tpe) val doc = resDoc.value + (Doc.lineOrEmpty + Doc.text(": ") + tDoc).nested(4) - print(doc.render(100)).as(ExitCode.Success) + println(doc.render(100)).as(ExitCode.Success) case Output.JsonOutput(json, pathOpt) => writeOut(json.toDoc, pathOpt) .as(ExitCode.Success) diff --git a/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala b/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala index 3e82ceedd..7ae5c0e47 100644 --- a/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala +++ b/core/src/main/scala/org/bykn/bosatsu/MemoryMain.scala @@ -274,7 +274,7 @@ object MemoryMain { def writeStdout(doc: Doc): F[Unit] = catsDefaultME.raiseError(new Exception(s"writeStdout($doc) is unimplemented on a read only platform")) - def print(str: String): F[Unit] = + def println(str: String): F[Unit] = catsDefaultME.raiseError(new Exception(s"print($str) is unimplemented on a read only platform")) override def resolve(base: Path, parts: List[String]): Path = diff --git a/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala b/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala index 1a8ef3a3b..e3a1f67d1 100644 --- a/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala +++ b/core/src/main/scala/org/bykn/bosatsu/PlatformIO.scala @@ -29,7 +29,7 @@ trait PlatformIO[F[_], Path] { def resolve(p: Path, child: String): Path def resolveFile(root: Path, pack: PackageName): F[Option[Path]] = { - val dir = pack.parts.init.foldLeft(root)(resolve(_, _)) + val dir = resolve(root, pack.parts.init) val filePath = resolve(dir, pack.parts.last + ".bosatsu") fsDataType(filePath).map { case Some(PlatformIO.FSDataType.File) => Some(filePath) @@ -58,8 +58,7 @@ trait PlatformIO[F[_], Path] { def resolve(base: Path, p: List[String]): Path = p.foldLeft(base)(resolve(_, _)) - // this is println actually - def print(str: String): F[Unit] + def println(str: String): F[Unit] def writeInterfaces( interfaces: List[Package.Interface],