From 667a26bddb6133e8b243061f8a5fc5fe586cc1ae Mon Sep 17 00:00:00 2001 From: ducky Date: Tue, 25 Oct 2016 13:46:44 -0700 Subject: Range macro initial impl --- .../scala/chisel3/internal/RangeTransform.scala | 97 ++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala (limited to 'coreMacros/src') diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala new file mode 100644 index 00000000..bbb36190 --- /dev/null +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -0,0 +1,97 @@ +// See LICENSE for license details. + +// Macro transforms that statically (at compile time) parse range specifiers and emit the raw +// (non-human-friendly) range constructor calls. + +package chisel3.internal + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context +import scala.reflect.macros.whitebox + +class RangeTransform(val c: Context) { + import c.universe._ + def apply(args: c.Tree*): c.Tree = { + val stringTrees = c.prefix.tree match { + case q"$_(scala.StringContext.apply(..$strings))" => strings + case _ => c.abort(c.enclosingPosition, s"Range macro unable to parse StringContext, got: ${showCode(c.prefix.tree)}") + } + val strings = stringTrees.map { tree => tree match { + case Literal(Constant(string: String)) => string + case _ => c.abort(c.enclosingPosition, s"Range macro unable to parse StringContext element, got: ${showRaw(tree)}") + } } + + var nextStringIndex: Int = 1 + var nextArgIndex: Int = 0 + var currString: String = strings(0) + + /** Mutably gets the next numeric value in the range specifier. + */ + def getNextValue(): c.Tree = { + currString = currString.dropWhile(_ == ' ') // allow whitespace + if (currString.isEmpty()) { + if (nextArgIndex >= args.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier, expected interpolated value") + } + val nextArg = args(nextArgIndex) + nextArgIndex += 1 + + if (nextStringIndex >= strings.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + currString = strings(nextStringIndex) + nextStringIndex += 1 + + nextArg + } else { + val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) + currString = currString.substring(nextStringVal.length) + if (currString.isEmpty()) { + if (nextStringIndex >= strings.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + currString = strings(nextStringIndex) + nextStringIndex += 1 + } + c.parse(nextStringVal) + } + } + + // Currently, not allowed to have the end stops (inclusive / exclusive) be interpolated. + currString = currString.dropWhile(_ == ' ') + val startInclusive = currString(0) match { + case '[' => true + case '(' => false + case _ => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '${currString(0)}'") + } + currString = currString.substring(1) // eat the inclusive/exclusive specifier + val minArg = getNextValue() + currString = currString.dropWhile(_ == ' ') + if (currString(0) != ',') { + c.abort(c.enclosingPosition, s"Incomplete range specifier, expected ','") + } + currString = currString.substring(1) // eat the comma + val maxArg = getNextValue() + currString = currString.dropWhile(_ == ' ') + val endInclusive = currString(0) match { + case ']' => true + case ')' => false + case _ => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '${currString(0)}'") + } + currString = currString.substring(1) // eat the inclusive/exclusive specifier + currString = currString.dropWhile(_ == ' ') + + if (nextArgIndex < args.length) { + val unused = args.mkString("") + c.abort(c.enclosingPosition, s"Unused interpolated values in range specifier: '$unused'") + } + if (!currString.isEmpty || nextStringIndex < strings.length) { + val unused = currString + strings.slice(nextStringIndex, strings.length).mkString(", ") + c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") + } + + c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") + + q"" + } +} -- cgit v1.2.3 From 4245c8c31749d9031da4915708bb23ec82a56da2 Mon Sep 17 00:00:00 2001 From: ducky Date: Tue, 25 Oct 2016 16:59:20 -0700 Subject: Better testing, better parsing --- .../src/main/scala/chisel3/internal/RangeTransform.scala | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'coreMacros/src') diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index bbb36190..20142d5d 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -31,7 +31,7 @@ class RangeTransform(val c: Context) { currString = currString.dropWhile(_ == ' ') // allow whitespace if (currString.isEmpty()) { if (nextArgIndex >= args.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier, expected interpolated value") + c.abort(c.enclosingPosition, s"Incomplete range specifier") } val nextArg = args(nextArgIndex) nextArgIndex += 1 @@ -47,11 +47,7 @@ class RangeTransform(val c: Context) { val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) currString = currString.substring(nextStringVal.length) if (currString.isEmpty()) { - if (nextStringIndex >= strings.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier") - } - currString = strings(nextStringIndex) - nextStringIndex += 1 + c.abort(c.enclosingPosition, s"Incomplete range specifier") } c.parse(nextStringVal) } @@ -62,7 +58,7 @@ class RangeTransform(val c: Context) { val startInclusive = currString(0) match { case '[' => true case '(' => false - case _ => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '${currString(0)}'") + case other => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '$other'") } currString = currString.substring(1) // eat the inclusive/exclusive specifier val minArg = getNextValue() @@ -76,7 +72,7 @@ class RangeTransform(val c: Context) { val endInclusive = currString(0) match { case ']' => true case ')' => false - case _ => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '${currString(0)}'") + case other => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '$other'") } currString = currString.substring(1) // eat the inclusive/exclusive specifier currString = currString.dropWhile(_ == ' ') @@ -90,6 +86,8 @@ class RangeTransform(val c: Context) { c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") } + // TODO: FINISH THIS! + c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") q"" -- cgit v1.2.3 From bb1cb894f6f1c88e0d60de1501e86d68de7c0f76 Mon Sep 17 00:00:00 2001 From: chick Date: Wed, 9 Nov 2016 15:57:06 -0800 Subject: first attack on creating a range api for chisel3 --- coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'coreMacros/src') diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index 20142d5d..ff5ba953 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -90,6 +90,7 @@ class RangeTransform(val c: Context) { c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") - q"" + q"_root_.chisel3.internal.firrtl" + } } -- cgit v1.2.3 From d46b9acd557d2fe6ffe27f43ee72cd9b2a22f65d Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 9 Nov 2016 16:03:21 -0800 Subject: Add bounds generation to range macro transform --- .../main/scala/chisel3/internal/RangeTransform.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'coreMacros/src') diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index ff5ba953..d90492cd 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -86,11 +86,17 @@ class RangeTransform(val c: Context) { c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") } - // TODO: FINISH THIS! - - c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") - - q"_root_.chisel3.internal.firrtl" - + val startBound = if (startInclusive) { + q"_root_.chisel3.internal.firrtl.Closed($minArg)" + } else { + q"_root_.chisel3.internal.firrtl.Open($minArg)" + } + val endBound = if (endInclusive) { + q"_root_.chisel3.internal.firrtl.Closed($maxArg)" + } else { + q"_root_.chisel3.internal.firrtl.Open($maxArg)" + } + + q"($startBound, $endBound)" } } -- cgit v1.2.3 From fffde2bfbffeacbe9cca68d539b199bd18e30294 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 16 Nov 2016 16:35:06 -0800 Subject: Address review comments --- coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'coreMacros/src') diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index d90492cd..f431341d 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -29,7 +29,7 @@ class RangeTransform(val c: Context) { */ def getNextValue(): c.Tree = { currString = currString.dropWhile(_ == ' ') // allow whitespace - if (currString.isEmpty()) { + if (currString.isEmpty) { if (nextArgIndex >= args.length) { c.abort(c.enclosingPosition, s"Incomplete range specifier") } @@ -46,7 +46,7 @@ class RangeTransform(val c: Context) { } else { val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) currString = currString.substring(nextStringVal.length) - if (currString.isEmpty()) { + if (currString.isEmpty) { c.abort(c.enclosingPosition, s"Incomplete range specifier") } c.parse(nextStringVal) @@ -96,7 +96,7 @@ class RangeTransform(val c: Context) { } else { q"_root_.chisel3.internal.firrtl.Open($maxArg)" } - + q"($startBound, $endBound)" } } -- cgit v1.2.3