Skip to content

Commit

Permalink
Initial check-in of Scala Hammer IR library
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardcwang committed May 11, 2019
1 parent ae17833 commit 14f70a1
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 0 deletions.
5 changes: 5 additions & 0 deletions hammer_ir/scalalib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
out/
*.jar
amm
mill
test/tmp_*
9 changes: 9 additions & 0 deletions hammer_ir/scalalib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Hammer IR Scala library
=======================

This API is experimental and subject to change at any time.

To get a JAR, run `./build.sh`.
You can find the output JAR in `out/hammer_ir/assembly/dest/out.jar`.

TODO(edwardw): write documentation for what this is and how to use it.
17 changes: 17 additions & 0 deletions hammer_ir/scalalib/build.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import mill._, scalalib._

object hammer_ir extends SbtModule {
def scalaVersion = "2.12.6"

// Unrooted submodule
override def millSourcePath = super.millSourcePath / ammonite.ops.up

def ivyDeps = Agg(
ivy"com.typesafe.play::play-json:2.6.10"
)

object test extends Tests {
def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.0.4")
def testFrameworks = Seq("org.scalatest.tools.Framework")
}
}
6 changes: 6 additions & 0 deletions hammer_ir/scalalib/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
if [ ! -f "./mill" ]; then
./get_mill.sh
fi
./mill hammer_ir.assembly
cp out/hammer_ir/assembly/dest/out.jar hammer_ir.jar
4 changes: 4 additions & 0 deletions hammer_ir/scalalib/get_ammonite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -ex
wget -nv https://github.com/lihaoyi/Ammonite/releases/download/1.6.6/2.12-1.6.6 -O amm
chmod +x ./amm
4 changes: 4 additions & 0 deletions hammer_ir/scalalib/get_mill.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -ex
wget -nv https://github.com/lihaoyi/mill/releases/download/0.3.6/0.3.6 -O mill
chmod +x ./mill
2 changes: 2 additions & 0 deletions hammer_ir/scalalib/scalatest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
./mill hammer_ir.test
62 changes: 62 additions & 0 deletions hammer_ir/scalalib/src/main/scala/HammerObject.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// See LICENSE for licence details.

package hammer_ir

import scala.reflect.ClassTag

import play.api.libs.json.{Json, JsObject, JsValue}

/**
* All Hammer IR types that can be converted to a JsValue.
*/
trait JSONConvertible {
/**
* Turn this object into a Play JSON object.
*/
private[hammer_ir] def toJSON: JsValue
}

/**
* All Hammer IR objects implement this trait which allows them to be
* serialized to/deserialized from JSON.
*/
trait HammerObject {
/**
* Turn this object into a Play JSON object.
*/
private[hammer_ir] def toJSON: JsObject

/**
* Turn this object into a JSON string.
*/
override def toString = Json.prettyPrint(toJSON)

/**
* Write this object as a JSON string into the given file.
*/
def toFile(filename: String) = reflect.io.File(filename).writeAll(toString)
}

/**
* Abstract class mixed into companion classes of Hammer IR objects.
*/
abstract class HammerObjectCompanion[T <: HammerObject : ClassTag] {
/**
* Create this object from a Play JSON object.
*/
private[hammer_ir] def fromJSON(json: JsObject): T

/**
* Create this object from a JSON string.
*/
def fromString(json: String): T = fromJSON(Json.parse(json).as[JsObject])

/**
* Create this object from a JSON file.
*/
def fromFile(filename: String): T = {
val source = scala.io.Source.fromFile(filename)
val lines = try source.mkString finally source.close()
fromString(lines)
}
}
136 changes: 136 additions & 0 deletions hammer_ir/scalalib/src/main/scala/PlacementConstraint.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// See LICENSE for licence details.

package hammer_ir

import play.api.libs.json.{JsNumber, JsObject, JsString}

sealed abstract class PlacementConstraintType extends JSONConvertible {
override def toJSON = JsString(stringVal)
// Force objects to implement this since toString is defined by
// default.
def stringVal: String
override def toString = stringVal
}
object PlacementConstraintType {
def fromString(input: String): PlacementConstraintType = {
values.foreach { i =>
if (i.stringVal == input) return i
}
throw new IllegalArgumentException(s"Illegal PlacementConstraintType $input")
}

case object Dummy extends PlacementConstraintType {
override def stringVal = "dummy"
}
case object Placement extends PlacementConstraintType {
override def stringVal = "placement"
}
case object TopLevel extends PlacementConstraintType {
override def stringVal = "toplevel"
}
case object HardMacro extends PlacementConstraintType {
override def stringVal = "hardmacro"
}
case object Hierarchical extends PlacementConstraintType {
override def stringVal = "hierarchical"
}
case object Obstruction extends PlacementConstraintType {
override def stringVal = "obstruction"
}

def values = Seq(
Dummy, Placement, TopLevel, HardMacro, Hierarchical, Obstruction
)
}

sealed abstract class ObstructionType
object ObstructionType {
case object Place extends ObstructionType
case object Route extends ObstructionType
case object Power extends ObstructionType
}

case class Margins(
left: Double,
bottom: Double,
right: Double,
top: Double
)

case class PlacementConstraint(
path: String,
`type`: PlacementConstraintType,
x: Double,
y: Double,
width: Double,
height: Double,
orientation: Option[String],
margins: Option[Margins],
top_layer: Option[String],
layers: Option[Seq[String]],
obs_types: Option[Seq[ObstructionType]]
) extends HammerObject {
override def toJSON = {
JsObject(Seq(
"path" -> JsString(path),
"type" -> `type`.toJSON,
"x" -> JsNumber(x),
"y" -> JsNumber(y),
"width" -> JsNumber(width),
"height" -> JsNumber(height)
// TODO(edwardw): FIXME
))
}
}

object PlacementConstraint extends HammerObjectCompanion[PlacementConstraint] {
def apply(
path: String,
`type`: PlacementConstraintType,
x: Double,
y: Double,
width: Double,
height: Double
): PlacementConstraint = {
new PlacementConstraint(
path,
`type`,
x,
y,
width,
height,
None, // TODO(edwardw): FIXME
None,
None,
None,
None
)
}

/* Helper function used to convert strings to Double
* for forwards-compatibility with Decimal type. */
def doubleOrString(value: play.api.libs.json.JsLookupResult): Double = {
try {
value.as[Double]
} catch {
case _: play.api.libs.json.JsResultException =>
value.as[String].toDouble
}
}

override def fromJSON(json: JsObject): PlacementConstraint = {
new PlacementConstraint(
(json \ "path").as[String],
PlacementConstraintType.fromString((json \ "type").as[String]),
doubleOrString(json \ "x"),
doubleOrString(json \ "y"),
doubleOrString(json \ "width"),
doubleOrString(json \ "height"),
None, // TODO(edwardw): FIXME
None,
None,
None,
None
)
}
}
34 changes: 34 additions & 0 deletions hammer_ir/scalalib/src/test/scala/HammerObjectSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// See LICENSE for licence details.

package hammer_ir.test

import org.scalatest.{FlatSpec, Matchers}
import play.api.libs.json.{JsObject, JsString}

import hammer_ir._

class HammerObjectSpec extends FlatSpec with Matchers {
behavior of "HammerObject"

case class Test(foobar: String) extends HammerObject {
override def toJSON = JsObject(Seq(
"foobar" -> JsString(foobar)
))
}
object Test extends HammerObjectCompanion[Test] {
override def fromJSON(json: JsObject): Test = {
new Test(
(json \ "foobar").as[String]
)
}
}

it should "serialize and deserialize correctly" in {
val t = Test("helloworld")
// Need these replaces to be robust to spacing variations
assert(t.toString
.replaceAll("\n", "").replaceAll(" ", "")
=== """{"foobar":"helloworld"}""")
assert(Test.fromJSON(t.toJSON) == t)
}
}
15 changes: 15 additions & 0 deletions hammer_ir/scalalib/test/placement_test/script.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// See LICENSE for licence details.

import $cp.`hammer_ir.jar`

import hammer_ir._

// Read and write back
val p = PlacementConstraint.fromFile("c1.json")

p.`type` match {
case PlacementConstraintType.Placement => println("Am placement")
case _ => println("Am not placement")
}

p.toFile("c1-out.json")
24 changes: 24 additions & 0 deletions hammer_ir/scalalib/test/prep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
# Test preparation script.
# Ensures that ammonite and the Hammer IR JAR are built.

set -e
set -euo pipefail

script_dir=$(dirname $0)
cd $script_dir

# Build Hammer IR
pushd ..
./build.sh

# Ensure ammonite exists
if [ ! -f "amm" ]; then
./get_ammonite.sh
fi

popd

# Create ammonite wrapper to import hammer_ir JAR.
echo "../amm --predef-code 'import ammonite.ops._; interp.load.cp(pwd/os.up/\"hammer_ir.jar\")' \$1" > amm
chmod +x amm
4 changes: 4 additions & 0 deletions hammer_ir/scalalib/test/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -ex
cd $(dirname $0)
./test_*
Loading

0 comments on commit 14f70a1

Please sign in to comment.