From cb05d5a1df3d4b7f89ecf0d95d8b9fe0dc40ae3a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 16 Oct 2017 20:42:37 -0700 Subject: Fleshed out basic implementations --- build.sbt | 33 +++++++++++++++ src/main/scala/hbt/Implicits.scala | 71 ++++++++++++++++++++++++++++++++ src/main/scala/hbt/Main.scala | 84 +++++++++++++++++++++++++++++++++++++- 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/hbt/Implicits.scala diff --git a/build.sbt b/build.sbt index 6836cb4f..6d412fc5 100644 --- a/build.sbt +++ b/build.sbt @@ -3,3 +3,36 @@ scalaVersion := "2.12.3" name := "hbt" organization := "com.lihaoyi" + + +libraryDependencies += "com.lihaoyi" %% "sourcecode" % "0.1.4" + +sourceGenerators in Compile += Def.task { + val dir = (sourceManaged in Compile).value + val file = dir/"fasterparser"/"SequencerGen.scala" + // Only go up to 21, because adding the last element makes it 22 + val tuples = (2 to 21).map{ i => + val ts = (1 to i) map ("T" + _) + val chunks = (1 to i) map { n => + s"t._$n" + } + val tsD = (ts :+ "D").mkString(",") + s""" + implicit def Sequencer$i[$tsD]: Sequencer[(${ts.mkString(", ")}), D, ($tsD)] = + Sequencer0((t, d) => (${chunks.mkString(", ")}, d)) + """ + } + val output = s""" + package hbt + trait SequencerGen[Sequencer[_, _, _]] extends LowestPriSequencer[Sequencer]{ + protected[this] def Sequencer0[A, B, C](f: (A, B) => C): Sequencer[A, B, C] + ${tuples.mkString("\n")} + } + trait LowestPriSequencer[Sequencer[_, _, _]]{ + protected[this] def Sequencer0[A, B, C](f: (A, B) => C): Sequencer[A, B, C] + implicit def Sequencer1[T1, T2]: Sequencer[T1, T2, (T1, T2)] = Sequencer0{case (t1, t2) => (t1, t2)} + } + """.stripMargin + IO.write(file, output) + Seq(file) +} \ No newline at end of file diff --git a/src/main/scala/hbt/Implicits.scala b/src/main/scala/hbt/Implicits.scala new file mode 100644 index 00000000..b9b12905 --- /dev/null +++ b/src/main/scala/hbt/Implicits.scala @@ -0,0 +1,71 @@ +package hbt + + +import scala.collection.mutable + +/** + * Container for all the type-level logic around appending things + * to tuples or flattening `Seq[Unit]`s into `Unit`s. + * + * Some of these implicits make liberal use of mutable state, so as + * to minimize allocations while parsing. + */ +object Implicits { + trait Sequencer[-T, V, R]{ + def apply(t: T, v: V): R + } + object Sequencer extends LowPriSequencer{ + def apply[T, V, R](f: (T, V) => R) = new Sequencer[T, V, R]{ + def apply(t: T, v: V): R = f(t, v) + } + implicit def SingleSequencer[T]: Sequencer[Unit, T, T] = Sequencer( (_, t) => t ) + } + trait LowPriSequencer extends LowerPriSequencer{ + implicit def UnitSequencer[T]: Sequencer[T, Unit, T] = Sequencer( (t, _) => t ) + } + trait LowerPriSequencer extends SequencerGen[Sequencer]{ + protected[this] def Sequencer0[A, B, C](f: (A, B) => C) = Sequencer(f) + } + trait Repeater[-T, R]{ + type Acc + def initial: Acc + def accumulate(t: T, acc: Acc): Unit + def result(acc: Acc): R + } + object Repeater extends LowPriRepeater{ + implicit object UnitRepeater extends Repeater[Unit, Unit]{ + type Acc = Unit + def initial = () + def accumulate(t: Unit, acc: Unit) = acc + def result(acc: Unit) = () + } + } + trait LowPriRepeater{ + implicit def GenericRepeaterImplicit[T] = GenericRepeater[T]() + case class GenericRepeater[T]() extends Repeater[T, Seq[T]]{ + type Acc = mutable.Buffer[T] + def initial = mutable.Buffer.empty[T] + def accumulate(t: T, acc: mutable.Buffer[T]) = acc += t + def result(acc: mutable.Buffer[T]) = acc + } + } + + trait Optioner[-T, R]{ + def none: R + def some(value: T): R + } + + object Optioner extends LowPriOptioner{ + implicit object UnitOptioner extends Optioner[Unit, Unit]{ + def none = () + def some(value: Unit) = () + } + } + trait LowPriOptioner{ + implicit def GenericOptionerImplicit[T] = GenericOptioner[T]() + case class GenericOptioner[T]() extends Optioner[T, Option[T]]{ + def none = None + def some(value: T) = Some(value) + } + } +} \ No newline at end of file diff --git a/src/main/scala/hbt/Main.scala b/src/main/scala/hbt/Main.scala index a8009735..6dc934e4 100644 --- a/src/main/scala/hbt/Main.scala +++ b/src/main/scala/hbt/Main.scala @@ -1,6 +1,88 @@ package hbt +import java.io.FileOutputStream + +import collection.JavaConverters._ +import java.nio.file.{Path => JPath} +import java.util.jar.JarEntry +import sourcecode.Enclosing +sealed trait Target[T]{ + def path: String + def map[V](f: T => V)(implicit path: Enclosing) = { + Target.Mapped(this, f, path.value) + } + def zip[V](other: Target[V])(implicit path: Enclosing) = { + Target.Zipped(this, other, path.value) + } + def ~[V, R](other: Target[V]) + (implicit s: Implicits.Sequencer[T, V, R]): Target[R] = { + this.zip(other).map(s.apply _ tupled) + } + +} + +object Target{ + def traverse[T](source: Seq[Target[T]])(implicit path: Enclosing) = { + Traverse(source, path.value) + } + case class Traverse[T](source: Seq[Target[T]], path: String) extends Target[Seq[T]] + case class Mapped[T, V](source: Target[T], f: T => V, + path: String) extends Target[V] + case class Zipped[T, V](source: Target[T], + source2: Target[V], + path: String) extends Target[(T, V)] + case class Path(path: String) extends Target[JPath] + case class Command(inputs: Seq[Target[JPath]], + output: Seq[Target[JPath]], + path: String) extends Target[Command.Result] + object Command{ + case class Result(stdout: String, + stderr: String, + writtenFiles: Seq[JPath]) + } +} object Main{ + def compileAll(sources: Target[Seq[JPath]]) + (implicit path: Enclosing): Target[JPath] = { + for(sources0 <- sources) yield { + val output = java.nio.file.Paths.get(path.value) + java.nio.file.Files.createDirectories(output) + val command = + Seq("scalac") ++ + sources0.map(_.toString) ++ + Seq("-d", path.value) + + + + new java.lang.ProcessBuilder() + .command(command: _*) + .start() + .waitFor() + + output + } + } + + def list(root: Target[JPath]): Target[Seq[JPath]] = { + root.map(java.nio.file.Files.list(_).iterator().asScala.toArray[JPath]) + } + def jarUp(roots: Target[JPath]*)(implicit path: Enclosing): Target[JPath] = { + for(rootsValue <- Target.traverse(roots)) yield { + val output = new java.util.jar.JarOutputStream(new FileOutputStream(path.value)) + for{ + root <- rootsValue + path <- java.nio.file.Files.list(root).iterator().asScala + }{ + val relative = root.relativize(path) + output.putNextEntry(new JarEntry(relative.toString)) + output.write(java.nio.file.Files.readAllBytes(path)) + } + java.nio.file.Paths.get(path.value) + } + } def main(args: Array[String]): Unit = { - println("Hello") + val sourceRoot: Target[JPath] = ??? + val resourceRoot: Target[JPath] = ??? + val classFiles: Target[JPath] = compileAll(list(sourceRoot)) + val jar: Target[JPath] = jarUp(resourceRoot, classFiles) } } \ No newline at end of file -- cgit v1.2.3