diff options
| author | Adam Izraelevitz | 2016-11-23 11:57:02 -0800 |
|---|---|---|
| committer | Jack Koenig | 2016-11-23 11:57:02 -0800 |
| commit | 66d3ec0498a73319a914eeffcb4e0b1109b5f4c5 (patch) | |
| tree | 325066fd05cc72b544d3b4d78d646e1a864119f3 /src/main/scala/firrtl/annotations | |
| parent | 9a967a27aa8bb51f4b62969d2889f9a9caa48e31 (diff) | |
Stringified annotations (#367)
Restricts annotations to be string-based (and thus less typesafe)
Makes annotations more easily serializable and interact with Chisel
Diffstat (limited to 'src/main/scala/firrtl/annotations')
4 files changed, 163 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/annotations/Annotation.scala b/src/main/scala/firrtl/annotations/Annotation.scala new file mode 100644 index 00000000..5881001f --- /dev/null +++ b/src/main/scala/firrtl/annotations/Annotation.scala @@ -0,0 +1,21 @@ +// See LICENSE for license details. + +package firrtl +package annotations + +import firrtl.ir._ + +case class AnnotationException(message: String) extends Exception(message) + +final case class Annotation(target: Named, transform: Class[_ <: Transform], value: String) { + val targetString: String = target.serialize + val transformClass: String = transform.getName + def serialize: String = this.toString + def update(tos: Seq[Named]): Seq[Annotation] = { + check(target, tos, this) + propagate(target, tos, duplicate) + } + def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.map(dup(_)) + def check(from: Named, tos: Seq[Named], which: Annotation): Unit = {} + def duplicate(n: Named) = new Annotation(n, transform, value) +} diff --git a/src/main/scala/firrtl/annotations/AnnotationUtils.scala b/src/main/scala/firrtl/annotations/AnnotationUtils.scala new file mode 100644 index 00000000..6e6af81d --- /dev/null +++ b/src/main/scala/firrtl/annotations/AnnotationUtils.scala @@ -0,0 +1,74 @@ +// See LICENSE for license details. + +package firrtl +package annotations + +import firrtl.ir._ + +object AnnotationUtils { + /** Returns true if a valid Module name */ + val SerializedModuleName = """([a-zA-Z_][a-zA-Z_0-9~!@#$%^*\-+=?/]*)""".r + def validModuleName(s: String): Boolean = s match { + case SerializedModuleName(name) => true + case _ => false + } + + /** Returns true if a valid component/subcomponent name */ + val SerializedComponentName = """([a-zA-Z_][a-zA-Z_0-9\[\]\.~!@#$%^*\-+=?/]*)""".r + def validComponentName(s: String): Boolean = s match { + case SerializedComponentName(name) => true + case _ => false + } + + /** Tokenizes a string with '[', ']', '.' as tokens, e.g.: + * "foo.bar[boo.far]" becomes Seq("foo" "." "bar" "[" "boo" "." "far" "]") + */ + def tokenize(s: String): Seq[String] = s.find(c => "[].".contains(c)) match { + case Some(_) => + val i = s.indexWhere(c => "[].".contains(c)) + s.slice(0, i) match { + case "" => Seq(s(i).toString) ++ tokenize(s.drop(i + 1)) + case x => Seq(x, s(i).toString) ++ tokenize(s.drop(i + 1)) + } + case None if s == "" => Nil + case None => Seq(s) + } + + /** Given a serialized component/subcomponent reference, subindex, subaccess, + * or subfield, return the corresponding IR expression. + * E.g. "foo.bar" becomes SubField(Reference("foo", UnknownType), "bar", UnknownType) + */ + def toExp(s: String): Expression = { + def parse(tokens: Seq[String]): Expression = { + val DecPattern = """([1-9]\d*)""".r + def findClose(tokens: Seq[String], index: Int, nOpen: Int): Seq[String] = { + if(index >= tokens.size) { + error("Cannot find closing bracket ]") + } else tokens(index) match { + case "[" => findClose(tokens, index + 1, nOpen + 1) + case "]" if nOpen == 1 => tokens.slice(1, index) + case "]" => findClose(tokens, index + 1, nOpen - 1) + case _ => findClose(tokens, index + 1, nOpen) + } + } + def buildup(e: Expression, tokens: Seq[String]): Expression = tokens match { + case "[" :: tail => + val indexOrAccess = findClose(tokens, 0, 0) + val exp = indexOrAccess.head match { + case DecPattern(d) => SubIndex(e, d.toInt, UnknownType) + case _ => SubAccess(e, parse(indexOrAccess), UnknownType) + } + buildup(exp, tokens.drop(2 + indexOrAccess.size)) + case "." :: tail => + buildup(SubField(e, tokens(1), UnknownType), tokens.drop(2)) + case Nil => e + } + val root = Reference(tokens.head, UnknownType) + buildup(root, tokens.tail) + } + if(validComponentName(s)) { + parse(tokenize(s)) + } else error(s"Cannot convert $s into an expression.") + } +} + diff --git a/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala b/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala new file mode 100644 index 00000000..5da00282 --- /dev/null +++ b/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala @@ -0,0 +1,37 @@ +// See LICENSE for license details. + +package firrtl +package annotations + +import net.jcazevedo.moultingyaml._ + +object AnnotationYamlProtocol extends DefaultYamlProtocol { + // bottom depends on top + implicit object AnnotationYamlFormat extends YamlFormat[Annotation] { + def write(a: Annotation) = + YamlArray( + YamlString(a.targetString), + YamlString(a.transformClass), + YamlString(a.value)) + + def read(value: YamlValue) = { + value.asYamlObject.getFields( + YamlString("targetString"), + YamlString("transformClass"), + YamlString("value")) match { + case Seq( + YamlString(targetString), + YamlString(transformClass), + YamlString(value)) => + new Annotation(toTarget(targetString), Class.forName(transformClass).asInstanceOf[Class[_ <: Transform]], value) + case _ => deserializationError("Color expected") + } + } + def toTarget(string: String) = string.split('.').toSeq match { + case Seq(c) => CircuitName(c) + case Seq(c, m) => ModuleName(m, CircuitName(c)) + case Nil => error("BAD") + case s => ComponentName(s.drop(2).mkString("."), ModuleName(s(1), CircuitName(s(0)))) + } + } +} diff --git a/src/main/scala/firrtl/annotations/Named.scala b/src/main/scala/firrtl/annotations/Named.scala new file mode 100644 index 00000000..e9b89e75 --- /dev/null +++ b/src/main/scala/firrtl/annotations/Named.scala @@ -0,0 +1,31 @@ +// See LICENSE for license details. + +package firrtl +package annotations + +import firrtl.ir.Expression +import AnnotationUtils.{validModuleName, validComponentName, toExp} + +/** + * Named classes associate an annotation with a component in a Firrtl circuit + */ +sealed trait Named { + def name: String + def serialize: String +} + +final case class CircuitName(name: String) extends Named { + if(!validModuleName(name)) throw AnnotationException(s"Illegal circuit name: $name") + def serialize: String = name +} + +final case class ModuleName(name: String, circuit: CircuitName) extends Named { + if(!validModuleName(name)) throw AnnotationException(s"Illegal module name: $name") + def serialize: String = name + "." + circuit.serialize +} + +final case class ComponentName(name: String, module: ModuleName) extends Named { + if(!validComponentName(name)) throw AnnotationException(s"Illegal component name: $name") + def expr: Expression = toExp(name) + def serialize: String = name + "." + module.serialize +} |
