aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/firrtl/Emitter.scala20
-rw-r--r--src/main/scala/firrtl/IR.scala4
-rw-r--r--src/main/scala/firrtl/Parser.scala2
-rw-r--r--src/main/scala/firrtl/Serialize.scala5
-rw-r--r--src/main/scala/firrtl/StringLit.scala119
-rw-r--r--src/main/scala/firrtl/Translator.scala24
-rw-r--r--src/main/scala/firrtl/Visitor.scala10
-rw-r--r--src/main/scala/firrtl/passes/Checks.scala17
-rw-r--r--src/main/scala/firrtl/passes/Passes.scala8
-rw-r--r--src/test/resources/features/Printf.fir16
-rw-r--r--src/test/resources/top.cpp (renamed from test/integration/top.cpp)0
-rw-r--r--src/test/scala/firrtlTests/FirrtlSpec.scala6
-rw-r--r--src/test/scala/firrtlTests/StringSpec.scala113
13 files changed, 307 insertions, 37 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 4b88f526..2aee699d 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -60,21 +60,6 @@ object VerilogEmitter extends Emitter {
var w:Option[Writer] = None
var mname = ""
def wref (n:String,t:Type) = WRef(n,t,ExpKind(),UNKNOWNGENDER)
- def escape (s:String) : String = {
- val sx = ArrayBuffer[String]()
- //sx += '"'.toString
- var percent:Boolean = false
- for (c <- s) {
- if (c == '\n') sx += "\\n"
- else if (c == '"') sx += '\\'.toString + '"'.toString
- else {
- if((c == 'x') && percent) sx += "h" else sx += c.toString
- }
- percent = (c == '%')
- }
- //sx += '"'.toString
- sx.reduce(_ + _)
- }
def remove_root (ex:Expression) : Expression = {
(ex.as[WSubField].get.exp) match {
case (e:WSubField) => remove_root(e)
@@ -367,9 +352,10 @@ object VerilogEmitter extends Emitter {
def stop (ret:Int) : Seq[Any] = {
Seq("$fdisplay(32'h80000002,\"",ret,"\");$finish;")
}
- def printf (str:String,args:Seq[Expression]) : Seq[Any] = {
+ def printf (str:StringLit,args:Seq[Expression]) : Seq[Any] = {
val q = '"'.toString
- val strx = (Seq(q + escape(str) + q) ++ args.flatMap(x => Seq(",",x)))
+ val strx = Seq(q + VerilogStringLitHandler.escape(str) + q) ++
+ args.flatMap(x => Seq(",",x))
Seq("$fwrite(32'h80000002,",strx,");")
}
def delay (e:Expression, n:Int, clk:Expression) : Expression = {
diff --git a/src/main/scala/firrtl/IR.scala b/src/main/scala/firrtl/IR.scala
index 17b16052..f79e70ab 100644
--- a/src/main/scala/firrtl/IR.scala
+++ b/src/main/scala/firrtl/IR.scala
@@ -48,6 +48,8 @@ trait AST {
def serialize: String = firrtl.Serialize.serialize(this)
}
+case class StringLit(array: Array[Byte]) extends AST
+
trait PrimOp extends AST
case object ADD_OP extends PrimOp
case object SUB_OP extends PrimOp
@@ -107,7 +109,7 @@ case class BulkConnect(info: Info, loc: Expression, exp: Expression) extends Stm
case class Connect(info: Info, loc: Expression, exp: Expression) extends Stmt
case class IsInvalid(info: Info, exp: Expression) extends Stmt
case class Stop(info: Info, ret: Int, clk: Expression, en: Expression) extends Stmt
-case class Print(info: Info, string: String, args: Seq[Expression], clk: Expression, en: Expression) extends Stmt
+case class Print(info: Info, string: StringLit, args: Seq[Expression], clk: Expression, en: Expression) extends Stmt
case class Empty() extends Stmt
trait Width extends AST {
diff --git a/src/main/scala/firrtl/Parser.scala b/src/main/scala/firrtl/Parser.scala
index 6e93e7e6..b7a74e84 100644
--- a/src/main/scala/firrtl/Parser.scala
+++ b/src/main/scala/firrtl/Parser.scala
@@ -39,6 +39,8 @@ import antlr._
class ParserException(message: String) extends Exception(message)
case class ParameterNotSpecifiedException(message: String) extends ParserException(message)
case class ParameterRedefinedException(message: String) extends ParserException(message)
+case class InvalidStringLitException(message: String) extends ParserException(message)
+case class InvalidEscapeCharException(message: String) extends ParserException(message)
object Parser extends LazyLogging
{
diff --git a/src/main/scala/firrtl/Serialize.scala b/src/main/scala/firrtl/Serialize.scala
index 6692063c..02b9ebc2 100644
--- a/src/main/scala/firrtl/Serialize.scala
+++ b/src/main/scala/firrtl/Serialize.scala
@@ -45,6 +45,7 @@ private object Serialize {
case r: Port => serialize(r)
case r: Module => serialize(r)
case r: Circuit => serialize(r)
+ case r: StringLit => serialize(r)
case _ => throw new Exception("serialize called on unknown AST node!")
}
}
@@ -55,6 +56,8 @@ private object Serialize {
def serialize(op: PrimOp): String = op.getString
+ def serialize(lit: StringLit): String = FIRRTLStringLitHandler.escape(lit)
+
def serialize(exp: Expression): String = {
exp match {
case v: UIntValue => s"UInt${serialize(v.width)}(${serialize(v.value)})"
@@ -131,7 +134,7 @@ private object Serialize {
case s: Stop => s"stop(${serialize(s.clk)}, ${serialize(s.en)}, ${s.ret})"
case p: Print => {
val q = '"'.toString
- s"printf(${serialize(p.clk)}, ${serialize(p.en)}, ${q}${p.string}${q}" +
+ s"printf(${serialize(p.clk)}, ${serialize(p.en)}, ${q}${serialize(p.string)}${q}" +
(if (p.args.nonEmpty) p.args.map(serialize).mkString(", ", ", ", "") else "") + ")"
}
case s:Empty => "skip"
diff --git a/src/main/scala/firrtl/StringLit.scala b/src/main/scala/firrtl/StringLit.scala
new file mode 100644
index 00000000..b3d67064
--- /dev/null
+++ b/src/main/scala/firrtl/StringLit.scala
@@ -0,0 +1,119 @@
+/*
+Copyright (c) 2014 - 2016 The Regents of the University of
+California (Regents). All Rights Reserved. Redistribution and use in
+source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the name of the Regents nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+*/
+
+package firrtl
+
+import java.nio.charset.StandardCharsets.UTF_8
+import scala.annotation.tailrec
+
+// Default implementations are for valid FIRRTL Strings
+trait StringLitHandler {
+
+ // Apply correct escaping for strings in a given language
+ def escape(lit: StringLit): String = {
+ @tailrec
+ def escape(in: Array[Byte], str: String): String = {
+ if (in.isEmpty) str
+ else {
+ val s = in.head match {
+ case 0x5c => "\\\\"
+ case 0x0a => "\\n"
+ case 0x09 => "\\t"
+ case 0x22 => "\\\""
+ case c =>
+ if (c >= 0x20 && c <= 0x7e) new String(Array(c), UTF_8)
+ else throw new FIRRTLException(s"Unsupported byte in StringLit: $c")
+ }
+ escape(in.tail, str + s)
+ }
+ }
+ escape(lit.array, "")
+ }
+
+ // Turn raw parsed strings into String Literals
+ def unescape(raw: String): StringLit = {
+ @tailrec
+ def unescape(in: Array[Byte], out: Array[Byte]): Array[Byte] = {
+ if (in.isEmpty) out
+ else {
+ val c = in.head
+ if ((c < 0x20) || (c > 0x7e)) { // Range from ' ' to '~'
+ val msg = "Unsupported unescaped UTF-8 Byte 0x%02x! ".format(c) +
+ "Valid range [0x20-0x7e]"
+ throw new InvalidStringLitException(msg)
+ }
+ // Handle escaped characters
+ if (c == 0x5c) { // backslash
+ val (n: Int, bytes: Array[Byte]) = in(1) match {
+ case 0x5c => (2, Array[Byte](0x5c)) // backslash
+ case 0x6e => (2, Array[Byte](0x0a)) // newline
+ case 0x74 => (2, Array[Byte](0x09)) // tab
+ case 0x27 => (2, Array[Byte](0x27)) // single quote
+ case 0x22 => (2, Array[Byte](0x22)) // double quote
+ // TODO Finalize supported escapes, implement hex2Bytes
+ //case 0x78 => (4, hex2Bytes(in.slice(2, 3)))) // hex escape
+ //case 0x75 => (6, hex2Bytes(in.slice(2, 5))) // unicode excape
+ case e => { // error
+ val msg = s"Invalid escape character ${e.toChar}! " +
+ "Valid characters [nt'\"\\]"
+ throw new InvalidEscapeCharException(msg)
+ }
+ }
+ unescape(in.drop(n), out ++ bytes) // consume n
+ } else {
+ unescape(in.tail, out :+ in.head)
+ }
+ }
+ }
+
+ val byteArray = raw.getBytes(java.nio.charset.StandardCharsets.UTF_8)
+ StringLit(unescape(byteArray, Array()))
+ }
+
+ // Translate from FIRRTL formatting to the specific language's formatting
+ def format(lit: StringLit): StringLit = ???
+ // Translate from the specific language's formatting to FIRRTL formatting
+ def unformat(lit: StringLit): StringLit = ???
+}
+
+object FIRRTLStringLitHandler extends StringLitHandler
+
+object VerilogStringLitHandler extends StringLitHandler {
+ // Change FIRRTL format string to Verilog format
+ override def format(lit: StringLit): StringLit = {
+ @tailrec
+ def format(in: Array[Byte], out: Array[Byte], percent: Boolean): Array[Byte] = {
+ if (in.isEmpty) out
+ else {
+ if (percent && in.head == 'x') format(in.tail, out :+ 'h'.toByte, false)
+ else format(in.tail, out :+ in.head, in.head == '%' && !percent)
+ }
+ }
+ StringLit(format(lit.array, Array(), false))
+ }
+}
+
diff --git a/src/main/scala/firrtl/Translator.scala b/src/main/scala/firrtl/Translator.scala
index 1ca20346..07dd1b42 100644
--- a/src/main/scala/firrtl/Translator.scala
+++ b/src/main/scala/firrtl/Translator.scala
@@ -26,11 +26,9 @@ MODIFICATIONS.
*/
/* TODO
- * - Add support for comments (that being said, current Scopers regex should ignore commented lines)
* - Add better error messages for illformed FIRRTL
* - Add support for files that do not have a circuit (like a module by itself in a file)
* - Improve performance? Replace regex?
- * - Add proper commnad-line arguments?
* - Wrap in Reader subclass. This would have less memory footprint than creating a large string
*/
@@ -47,7 +45,27 @@ object Translator
def addBrackets(inputIt: Iterator[String]): StringBuilder = {
def countSpaces(s: String): Int = s.prefixLength(_ == ' ')
- def stripComments(s: String): String = s takeWhile (!";".contains(_))
+ def stripComments(s: String): String = {
+ // Delete anything after first semicolon that's not in a comment
+ var done = false
+ var inComment = false
+ var escape = false
+ var i = 0
+ while (!done && i < s.length) {
+ val c = s(i)
+ if (c == ';') {
+ if (!inComment) {
+ done = true
+ i = i - 1 // remove semicolon as well as what follows
+ }
+ } else {
+ if (c == '"' && !escape) inComment = !inComment
+ escape = if (c == '\\' && !escape) true else false
+ }
+ i += 1
+ }
+ s.take(i)
+ }
val scopers = """(circuit|module|when|else|mem|with)"""
val MultiLineScope = ("""(.*""" + scopers + """)(.*:\s*)""").r
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
index 5204ce8e..222daf8a 100644
--- a/src/main/scala/firrtl/Visitor.scala
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -41,6 +41,7 @@ import scala.collection.JavaConversions._
import antlr._
import PrimOps._
import FIRRTLParser._
+import scala.annotation.tailrec
class Visitor(val fullFilename: String) extends FIRRTLBaseVisitor[AST]
{
@@ -166,6 +167,12 @@ class Visitor(val fullFilename: String) extends FIRRTLBaseVisitor[AST]
map.getOrElse("writer", Seq()).map(x => (x.getText)), map.getOrElse("readwriter", Seq()).map(x => (x.getText)))
}
+ // visitStringLit
+ private def visitStringLit[AST](node: TerminalNode): StringLit = {
+ val raw = node.getText.tail.init // Remove surrounding double quotes
+ FIRRTLStringLitHandler.unescape(raw)
+ }
+
// visitStmt
private def visitStmt[AST](ctx: FIRRTLParser.StmtContext): Stmt = {
val info = getInfo(ctx)
@@ -201,8 +208,7 @@ class Visitor(val fullFilename: String) extends FIRRTLBaseVisitor[AST]
Conditionally(info, visitExp(ctx.exp(0)), visitBlock(ctx.block(0)), alt)
}
case "stop(" => Stop(info, string2Int(ctx.IntLit(0).getText), visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
- // Stip first and last character of string since they are the surrounding double quotes
- case "printf(" => Print(info, ctx.StringLit.getText.tail.init, ctx.exp.drop(2).map(visitExp),
+ case "printf(" => Print(info, visitStringLit(ctx.StringLit), ctx.exp.drop(2).map(visitExp),
visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
case "skip" => Empty()
// If we don't match on the first child, try the next one
diff --git a/src/main/scala/firrtl/passes/Checks.scala b/src/main/scala/firrtl/passes/Checks.scala
index 9a4e5eb6..8e278120 100644
--- a/src/main/scala/firrtl/passes/Checks.scala
+++ b/src/main/scala/firrtl/passes/Checks.scala
@@ -59,7 +59,6 @@ object CheckHighForm extends Pass with LazyLogging {
class NegWidthException extends PassException(s"${sinfo}: [module ${mname}] Width cannot be negative or zero.")
class NegVecSizeException extends PassException(s"${sinfo}: [module ${mname}] Vector type size cannot be negative.")
class NegMemSizeException extends PassException(s"${sinfo}: [module ${mname}] Memory size cannot be negative or zero.")
- // Note the following awkward strings are due to an issue with Scala string interpolation and escaped double quotes
class BadPrintfException(x: Char) extends PassException(s"${sinfo}: [module ${mname}] Bad printf format: " + "\"%" + x + "\"")
class BadPrintfTrailingException extends PassException(s"${sinfo}: [module ${mname}] Bad printf format: trailing " + "\"%\"")
class BadPrintfIncorrectNumException extends PassException(s"${sinfo}: [module ${mname}] Bad printf format: incorrect number of arguments")
@@ -164,16 +163,16 @@ object CheckHighForm extends Pass with LazyLogging {
}
}
- def checkFstring(s: String, i: Int) = {
- val validFormats = "bedxs"
+ def checkFstring(s: StringLit, i: Int) = {
+ val validFormats = "bdx"
var percent = false
- var ret = true
var npercents = 0
- for (x <- s) {
- if (!validFormats.contains(x) && percent)
- errors.append(new BadPrintfException(x))
- if (x == '%') npercents = npercents + 1
- percent = (x == '%')
+ s.array.foreach { b =>
+ if (percent) {
+ if (validFormats.contains(b)) npercents += 1
+ else if (b != '%') errors.append(new BadPrintfException(b.toChar))
+ }
+ percent = if (b == '%') !percent else false // %% -> percent = false
}
if (percent) errors.append(new BadPrintfTrailingException)
if (npercents != i) errors.append(new BadPrintfIncorrectNumException)
diff --git a/src/main/scala/firrtl/passes/Passes.scala b/src/main/scala/firrtl/passes/Passes.scala
index 1e8ceae2..0bedcbeb 100644
--- a/src/main/scala/firrtl/passes/Passes.scala
+++ b/src/main/scala/firrtl/passes/Passes.scala
@@ -1260,7 +1260,13 @@ object VerilogWrap extends Pass {
case (e) => e
}
}
- def v_wrap_s (s:Stmt) : Stmt = s map (v_wrap_s) map (v_wrap_e)
+ def v_wrap_s (s:Stmt) : Stmt = {
+ s map (v_wrap_s) map (v_wrap_e) match {
+ case s: Print =>
+ Print(s.info, VerilogStringLitHandler.format(s.string), s.args, s.clk, s.en)
+ case s => s
+ }
+ }
def run (c:Circuit): Circuit = {
val modulesx = c.modules.map{ m => {
(m) match {
diff --git a/src/test/resources/features/Printf.fir b/src/test/resources/features/Printf.fir
new file mode 100644
index 00000000..d0c1c775
--- /dev/null
+++ b/src/test/resources/features/Printf.fir
@@ -0,0 +1,16 @@
+circuit Top :
+ module Top :
+ input clk : Clock
+ input reset : UInt<1>
+
+ reg count : UInt<10>, clk with :
+ reset => (reset, UInt<6>(0))
+ reg const : UInt<32> clk with :
+ reset => (reset, UInt(123456))
+
+ node notReset = not(reset)
+ count <= add(count, UInt(1))
+ printf(clk, notReset, "\tcount = %d 0x%x b%b\\\'%d%%\'\n", count, count, count, const)
+
+ when eq(count, UInt(255)) :
+ stop(clk, UInt(1), 0)
diff --git a/test/integration/top.cpp b/src/test/resources/top.cpp
index 8bfe2a99..8bfe2a99 100644
--- a/test/integration/top.cpp
+++ b/src/test/resources/top.cpp
diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala
index 438a5282..a8ccb0a9 100644
--- a/src/test/scala/firrtlTests/FirrtlSpec.scala
+++ b/src/test/scala/firrtlTests/FirrtlSpec.scala
@@ -90,7 +90,7 @@ trait BackendCompilationUtilities {
val e = Process(s"./V${prefix}", dir) !
ProcessLogger(line => {
triggered = triggered || line.contains(assertionMsg)
- System.out.println(line)
+ //System.out.println(line)
})
triggered
}
@@ -101,7 +101,7 @@ trait BackendCompilationUtilities {
}
trait FirrtlRunners extends BackendCompilationUtilities {
- lazy val cpp = new File(s"/integration/top.cpp")
+ lazy val cppHarness = new File(s"/top.cpp")
def compileFirrtlTest(prefix: String, srcDir: String): File = {
val testDir = createTempDirectory(prefix)
copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir"))
@@ -112,7 +112,7 @@ trait FirrtlRunners extends BackendCompilationUtilities {
def runFirrtlTest(prefix: String, srcDir: String) {
val testDir = compileFirrtlTest(prefix, srcDir)
val harness = new File(testDir, s"top.cpp")
- copyResourceToFile(cpp.toString, harness)
+ copyResourceToFile(cppHarness.toString, harness)
verilogToCpp(prefix, testDir, Seq(), harness).!
cppToExe(prefix, testDir).!
diff --git a/src/test/scala/firrtlTests/StringSpec.scala b/src/test/scala/firrtlTests/StringSpec.scala
new file mode 100644
index 00000000..2278a147
--- /dev/null
+++ b/src/test/scala/firrtlTests/StringSpec.scala
@@ -0,0 +1,113 @@
+
+package firrtlTests
+
+import firrtl._
+
+import java.io._
+
+import scala.sys.process._
+import org.scalatest._
+import org.scalatest.prop._
+import org.scalatest.Assertions._
+import org.scalacheck._
+
+class PrintfSpec extends FirrtlPropSpec {
+
+ property("Printf should correctly print values in each format %x, %d, %b") {
+ val prefix = "Printf"
+ val testDir = compileFirrtlTest(prefix, "/features")
+ val harness = new File(testDir, s"top.cpp")
+ copyResourceToFile(cppHarness.toString, harness)
+
+ verilogToCpp(prefix, testDir, Seq(), harness).!
+ cppToExe(prefix, testDir).!
+
+ // Check for correct Printf:
+ // Count up from 0, match decimal, hex, and binary
+ // see /features/Print.fir to see what we're matching
+ val regex = """\tcount\s+=\s+(\d+)\s+0x(\w+)\s+b([01]+).*""".r
+ var done = false
+ var expected = 0
+ var error = false
+ val ret = Process(s"./V${prefix}", testDir) !
+ ProcessLogger( line => {
+ line match {
+ case regex(dec, hex, bin) => {
+ if (!done) {
+ // Must mark error before assertion or sbt test will pass
+ if (Integer.parseInt(dec, 10) != expected) error = true
+ assert(Integer.parseInt(dec, 10) == expected)
+ if (Integer.parseInt(hex, 16) != expected) error = true
+ assert(Integer.parseInt(hex, 16) == expected)
+ if (Integer.parseInt(bin, 2) != expected) error = true
+ assert(Integer.parseInt(bin, 2) == expected)
+ expected += 1
+ }
+ }
+ case _ => // Do Nothing
+ }
+ })
+ if (error) fail()
+ }
+}
+
+class StringSpec extends FirrtlPropSpec {
+
+ // Whitelist is [0x20 - 0x7e]
+ val whitelist =
+ """ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ""" +
+ """[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
+ val whitelistBA: Array[Byte] = Array.range(0x20, 0x7e) map (_.toByte)
+
+ property(s"Character whitelist should be supported: [$whitelist] ") {
+ val lit = firrtl.FIRRTLStringLitHandler.unescape(whitelist)
+ // Check internal
+ lit.array zip whitelistBA foreach { case (b, e) =>
+ assert(b == e, s"(${b.toChar} did not equal expected ${e.toChar})")
+ }
+ // Check result
+ assert(lit.serialize == whitelist)
+ }
+
+ // Valid escapes = \n, \t, \\, \", \'
+ val esc = """\\\'\"\t\n"""
+ val validEsc = Seq('n', 't', '\\', '"', '\'')
+ property(s"Escape characters [$esc] should parse") {
+ val lit = firrtl.FIRRTLStringLitHandler.unescape(esc)
+ assert(lit.array(0) == 0x5c)
+ assert(lit.array(1) == 0x27)
+ assert(lit.array(2) == 0x22)
+ assert(lit.array(3) == 0x09)
+ assert(lit.array(4) == 0x0a)
+ assert(lit.array.length == 5)
+ }
+
+ // Generators for random testing
+ val validChar = for (c <- Gen.choose(0x20.toChar, 0x7e.toChar) if c != '\\') yield c
+ val validCharSeq = Gen.containerOf[Seq, Char](validChar)
+ val invalidChar = Gen.oneOf(Gen.choose(0x00.toChar, 0x1f.toChar),
+ Gen.choose(0x7f.toChar, 0xff.toChar))
+ val invalidEsc = for (
+ c <- Gen.choose(0x00.toChar, 0xff.toChar
+ ) if (!validEsc.contains(c))) yield c
+
+ property("Random invalid strings should fail") {
+ forAll(validCharSeq, invalidChar, validCharSeq) {
+ (head: Seq[Char], bad: Char, tail: Seq[Char]) =>
+ val str = ((head :+ bad) ++ tail).mkString
+ intercept[InvalidStringLitException] {
+ firrtl.FIRRTLStringLitHandler.unescape(str)
+ }
+ }
+ }
+
+ property(s"Invalid escape characters should fail") {
+ forAll(validCharSeq, invalidEsc, validCharSeq) {
+ (head: Seq[Char], badEsc: Char, tail: Seq[Char]) =>
+ val str = (head ++ Seq('\\', badEsc) ++ tail).mkString
+ intercept[InvalidEscapeCharException] {
+ firrtl.FIRRTLStringLitHandler.unescape(str)
+ }
+ }
+ }
+}