aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/annotations/AnnotationUtils.scala
blob: 517cea26b1427ede26fe43f62bc6a30c79cf5d31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// See LICENSE for license details.

package firrtl
package annotations

import java.io.File

import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write, writePretty}

import net.jcazevedo.moultingyaml._
import firrtl.annotations.AnnotationYamlProtocol._

import firrtl.ir._
import firrtl.Utils.error

case class InvalidAnnotationFileException(file: File, cause: Throwable = null)
  extends FIRRTLException(s"$file, see cause below", cause)
case class InvalidAnnotationJSONException(msg: String) extends FIRRTLException(msg)
case class AnnotationFileNotFoundException(file: File) extends FIRRTLException(
  s"Annotation file $file not found!"
)
case class AnnotationClassNotFoundException(className: String) extends FIRRTLException(
  s"Annotation class $className not found! Please check spelling and classpath"
)

object AnnotationUtils {
  def toYaml(a: LegacyAnnotation): String = a.toYaml.prettyPrint
  def fromYaml(s: String): LegacyAnnotation = s.parseYaml.convertTo[LegacyAnnotation]

  /** 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)
  }

  def toNamed(s: String): Named = s.split("\\.", 3) match {
    case Array(n) => CircuitName(n)
    case Array(c, m) => ModuleName(m, CircuitName(c))
    case Array(c, m, x) => ComponentName(x, ModuleName(m, CircuitName(c)))
  }

  /** 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) {
          Utils.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 {
      Utils.error(s"Cannot convert $s into an expression.")
    }
  }
}