diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-10-22 09:37:50 -0700 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-10-22 09:37:50 -0700 |
commit | d5b903d44cbd29f838db3ea475d1efa101ad810f (patch) | |
tree | 3cb29f86f7be07ab2aaf3cf74fc66e41e9d3ed94 /src/main/scala/forge | |
parent | 06afaf6d3cf65cc7b42fa5f31aa6c48421626b6e (diff) | |
download | mill-d5b903d44cbd29f838db3ea475d1efa101ad810f.tar.gz mill-d5b903d44cbd29f838db3ea475d1efa101ad810f.tar.bz2 mill-d5b903d44cbd29f838db3ea475d1efa101ad810f.zip |
rename hbt -> forge
Diffstat (limited to 'src/main/scala/forge')
-rw-r--r-- | src/main/scala/forge/Args.scala | 11 | ||||
-rw-r--r-- | src/main/scala/forge/DefCtx.scala | 35 | ||||
-rw-r--r-- | src/main/scala/forge/Evaluator.scala | 78 | ||||
-rw-r--r-- | src/main/scala/forge/Implicits.scala | 71 | ||||
-rw-r--r-- | src/main/scala/forge/ReadWrite.scala | 20 | ||||
-rw-r--r-- | src/main/scala/forge/Target.scala | 72 | ||||
-rw-r--r-- | src/main/scala/forge/Tarjans.scala | 61 | ||||
-rw-r--r-- | src/main/scala/forge/Util.scala | 52 |
8 files changed, 400 insertions, 0 deletions
diff --git a/src/main/scala/forge/Args.scala b/src/main/scala/forge/Args.scala new file mode 100644 index 00000000..c7cc455c --- /dev/null +++ b/src/main/scala/forge/Args.scala @@ -0,0 +1,11 @@ +package forge + +import java.nio.{file => jnio} + +class Args(val args: IndexedSeq[_], val dest: jnio.Path){ + def length = args.length + def apply[T](index: Int): T = { + if (index >= 0 && index < args.length) args(index).asInstanceOf[T] + else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") + } +} diff --git a/src/main/scala/forge/DefCtx.scala b/src/main/scala/forge/DefCtx.scala new file mode 100644 index 00000000..57f2e71e --- /dev/null +++ b/src/main/scala/forge/DefCtx.scala @@ -0,0 +1,35 @@ +package forge + + +import scala.language.experimental.macros +import scala.reflect.macros._ + + +case class DefCtx(staticEnclosing: Option[String]) +object DefCtx{ + case class StaticContext(value: Boolean) + object StaticContext { + implicit def default: StaticContext = macro applyImpl + def rec(c: Context)(expr: c.Symbol): Boolean = { + import c.universe._ + // Classes and traits and such + if(!expr.isModuleClass && expr.isClass) false + // Method contents + else if(expr.isMethod) false + else if(expr.owner == NoSymbol) true + else rec(c)(expr.owner) + } + + def applyImpl(c: Context): c.Expr[StaticContext] = { + import c.universe._ + val staticContext = rec(c)(c.internal.enclosingOwner) + c.Expr[StaticContext](q"hbt.DefCtx.StaticContext($staticContext)") + } + } + + implicit def default(implicit enc: sourcecode.Enclosing, + sc: StaticContext) = { + if (sc.value) DefCtx(Some(enc.value)) + else DefCtx(None) + } +}
\ No newline at end of file diff --git a/src/main/scala/forge/Evaluator.scala b/src/main/scala/forge/Evaluator.scala new file mode 100644 index 00000000..137943f7 --- /dev/null +++ b/src/main/scala/forge/Evaluator.scala @@ -0,0 +1,78 @@ +package forge + +import java.nio.{file => jnio} + +import sourcecode.Enclosing + +import scala.collection.mutable + +class Evaluator(workspacePath: jnio.Path, + enclosingBase: DefCtx){ + + /** + * Takes the given targets, finds + */ + def prepareTransitiveTargets(targets: Seq[Target[_]]) = { + + val targetIndices = targets.zipWithIndex.toMap + + val transitiveTargetSet = mutable.Set.empty[Target[_]] + def rec(t: Target[_]): Unit = { + if (transitiveTargetSet.contains(t)) () // do nothing + else { + transitiveTargetSet.add(t) + t.inputs.foreach(rec) + } + } + + targets.foreach(rec) + val transitiveTargets = transitiveTargetSet.toVector + val numberedEdges = + for(i <- transitiveTargets.indices) + yield targets(i).inputs.map(targetIndices) + + val sortedClusters = Tarjans(numberedEdges) + val nonTrivialClusters = sortedClusters.filter(_.length > 1) + assert(nonTrivialClusters.isEmpty, nonTrivialClusters) + (transitiveTargets, sortedClusters.flatten) + } + + def apply[T](t: Target[T]) + (implicit enclosing: Enclosing): T = { + jnio.Files.createDirectories(workspacePath) + + val (transitiveTargets, sortedTargetIndices) = prepareTransitiveTargets(Seq(t)) + val results = mutable.Map.empty[Target[_], Any] + for (index <- sortedTargetIndices){ + + val target = transitiveTargets(index) + val inputResults = target.inputs.map(results) + + for(enclosing <- target.defCtx.staticEnclosing){ + val targetDestPath = workspacePath.resolve( + jnio.Paths.get(enclosing.stripSuffix(enclosingBase.staticEnclosing.getOrElse(""))) + ) + deleteRec(targetDestPath) + } + + + + + + results(target) = target.evaluate( + new Args(inputResults.toIndexedSeq, targetDestPath) + ) + } + results(t).asInstanceOf[T] + } + def deleteRec(path: jnio.Path) = { + if (jnio.Files.exists(path)){ + import collection.JavaConverters._ + jnio.Files.walk(path).iterator() + .asScala + .toArray + .reverseIterator + .map(jnio.Files.deleteIfExists) + } + } +} diff --git a/src/main/scala/forge/Implicits.scala b/src/main/scala/forge/Implicits.scala new file mode 100644 index 00000000..3295ce38 --- /dev/null +++ b/src/main/scala/forge/Implicits.scala @@ -0,0 +1,71 @@ +package forge + + +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/forge/ReadWrite.scala b/src/main/scala/forge/ReadWrite.scala new file mode 100644 index 00000000..ee74f35f --- /dev/null +++ b/src/main/scala/forge/ReadWrite.scala @@ -0,0 +1,20 @@ +package forge +import java.nio.{file => jnio} + +trait ReadWrite[T] { + def write(t: T, p: jnio.Path): Unit + def read(p: jnio.Path): T +} + +object ReadWrite{ + implicit object String extends ReadWrite[java.lang.String]{ + def write(t: String, p: jnio.Path) = { + jnio.Files.createDirectories(p.getParent) + jnio.Files.deleteIfExists(p) + jnio.Files.write(p, t.getBytes) + } + def read(p: jnio.Path) = { + new String(jnio.Files.readAllBytes(p)) + } + } +} diff --git a/src/main/scala/forge/Target.scala b/src/main/scala/forge/Target.scala new file mode 100644 index 00000000..80ff98c4 --- /dev/null +++ b/src/main/scala/forge/Target.scala @@ -0,0 +1,72 @@ +package forge + +import java.nio.{file => jnio} + +trait Target[T]{ + val defCtx: DefCtx + + val inputs: Seq[Target[_]] + def evaluate(args: Args): T + + def map[V](f: T => V)(implicit defCtx: DefCtx) = { + Target.Mapped(this, f, defCtx) + } + def zip[V](other: Target[V])(implicit defCtx: DefCtx) = { + Target.Zipped(this, other, defCtx) + } + def ~[V, R](other: Target[V]) + (implicit s: Implicits.Sequencer[T, V, R], defCtx: DefCtx): Target[R] = { + this.zip(other).map(s.apply _ tupled) + } + +} + +object Target{ + def noop(inputs: Target[_]*)(implicit defCtx: DefCtx) = NoopTarget(inputs, defCtx) + case class NoopTarget(inputs: Seq[Target[_]], defCtx: DefCtx) extends Target[Unit]{ + def evaluate(args: Args) = () + } + def traverse[T](source: Seq[Target[T]])(implicit defCtx: DefCtx) = { + Traverse[T](source, defCtx) + } + case class Traverse[T](inputs: Seq[Target[T]], defCtx: DefCtx) extends Target[Seq[T]]{ + def evaluate(args: Args) = { + for (i <- 0 until args.length) + yield args(i).asInstanceOf[T] + } + + } + case class Mapped[T, V](source: Target[T], f: T => V, + defCtx: DefCtx) extends Target[V]{ + def evaluate(args: Args) = f(args(0)) + val inputs = List(source) + } + case class Zipped[T, V](source1: Target[T], + source2: Target[V], + defCtx: DefCtx) extends Target[(T, V)]{ + def evaluate(args: Args) = (args(0), args(0)) + val inputs = List(source1, source1) + } + + def path(path: jnio.Path)(implicit defCtx: DefCtx) = Path(path, defCtx) + case class Path(path: jnio.Path, defCtx: DefCtx) extends Target[jnio.Path]{ + def evaluate(args: Args) = path + val inputs = Nil + } + case class Subprocess(inputs: Seq[Target[_]], + command: Args => Seq[String], + defCtx: DefCtx) extends Target[Subprocess.Result] { + + def evaluate(args: Args) = { + jnio.Files.createDirectories(args.dest) + import ammonite.ops._ + implicit val path = ammonite.ops.Path(args.dest, pwd) + val output = %%(command(args)) + assert(output.exitCode == 0) + Subprocess.Result(output, args.dest) + } + } + object Subprocess{ + case class Result(result: ammonite.ops.CommandResult, dest: jnio.Path) + } +} diff --git a/src/main/scala/forge/Tarjans.scala b/src/main/scala/forge/Tarjans.scala new file mode 100644 index 00000000..78dea34a --- /dev/null +++ b/src/main/scala/forge/Tarjans.scala @@ -0,0 +1,61 @@ +package forge + +import collection.mutable +// Adapted from +// https://github.com/indy256/codelibrary/blob/c52247216258e84aac442a23273b7d8306ef757b/java/src/SCCTarjan.java +object Tarjans { + def main(args: Array[String]) = { + val components = Tarjans( + Vector( + Vector(1), + Vector(0), + Vector(0, 1) + ) + ) + println(components) + } + + def apply(graph0: Seq[Seq[Int]]): Seq[Seq[Int]] = { + val graph = graph0.map(_.toArray).toArray + val n = graph.length + val visited = new Array[Boolean](n) + val stack = mutable.ArrayBuffer.empty[Integer] + var time = 0 + val lowlink = new Array[Int](n) + val components = mutable.ArrayBuffer.empty[Seq[Int]] + + + for (u <- 0 until n) { + if (!visited(u)) dfs(u) + } + + def dfs(u: Int): Unit = { + lowlink(u) = time + time += 1 + visited(u) = true + stack.append(u) + var isComponentRoot = true + for (v <- graph(u)) { + if (!visited(v)) dfs(v) + if (lowlink(u) > lowlink(v)) { + lowlink(u) = lowlink(v) + isComponentRoot = false + } + } + if (isComponentRoot) { + val component = mutable.Buffer.empty[Int] + + var done = false + while (!done) { + val x = stack.last + stack.remove(stack.length - 1) + component.append(x) + lowlink(x) = Integer.MAX_VALUE + if (x == u) done = true + } + components.append(component) + } + } + components + } +}
\ No newline at end of file diff --git a/src/main/scala/forge/Util.scala b/src/main/scala/forge/Util.scala new file mode 100644 index 00000000..89a3eafe --- /dev/null +++ b/src/main/scala/forge/Util.scala @@ -0,0 +1,52 @@ +package forge + +import java.io.FileOutputStream +import java.nio.{file => jnio} +import java.util.jar.JarEntry + +import sourcecode.Enclosing + +import scala.collection.JavaConverters._ + +object Util{ + def compileAll(sources: Target[Seq[jnio.Path]]) + (implicit defCtx: DefCtx) = { + Target.Subprocess( + Seq(sources), + args => + Seq("javac") ++ + args[Seq[jnio.Path]](0).map(_.toAbsolutePath.toString) ++ + Seq("-d", args.dest.toAbsolutePath.toString), + defCtx + ).map(_.dest) + } + + def list(root: Target[jnio.Path])(implicit defCtx: DefCtx): Target[Seq[jnio.Path]] = { + root.map(jnio.Files.list(_).iterator().asScala.toArray[jnio.Path]) + } + case class jarUp(roots: Target[jnio.Path]*)(implicit val defCtx: DefCtx) extends Target[jnio.Path]{ + + val inputs = roots + def evaluate(args: Args): jnio.Path = { + + val output = new java.util.jar.JarOutputStream(new FileOutputStream(args.dest.toFile)) + for{ + root0 <- args.args + root = root0.asInstanceOf[jnio.Path] + + path <- jnio.Files.walk(root).iterator().asScala + if jnio.Files.isRegularFile(path) + }{ + val relative = root.relativize(path) + output.putNextEntry(new JarEntry(relative.toString)) + output.write(jnio.Files.readAllBytes(path)) + } + output.close() + args.dest + } + + + } + + +}
\ No newline at end of file |