patternMinor
Validation macro
Viewed 0 times
macrovalidationstackoverflow
Problem
I would like a review of a Scala validation library I am writing. For now we can focus on the regular expression component.
The idea being, if compilation breaks spectacularly the user can change imports from the validated RegexValidator, to the unvalidated RegexRuntime. Like how you can switch between the mutable and immutable collections library. I'd be really interested better names if someone can come up with any.
Here is an abridged version of the macro code:
``
val parts = rawParts map { case t @ Literal(Constant(const: String)) => (const, t.pos) }
parts match {
// if there is only one string literal
case List((raw, pos)) => {
//compiletime validation here
try {
val p = Pattern.compile(raw)
} catch {
case ex: PatternSyntaxException => {
//fancyness with underlineing
//TODO: this seems a little iffy...
val rpos = pos.asInstanceOf[scala.reflect.internal.util.OffsetPosition]
//TODO: better class?
val outpos = new RangePosition(rpos.source, rpos.start + ex.getIndex, rpos.start + ex.getIndex, rpos.start + ex.getIndex)
c.error(outpos.asInstanceOf[c.universe.Position], ex.getDescription())
}
//... catch other er
- The usage is described in RegexExample.scala
- There is an outline of the interface in RegexRuntime.scala
- But the macro and most of the craziness lives in RegexValidator.scala
The idea being, if compilation breaks spectacularly the user can change imports from the validated RegexValidator, to the unvalidated RegexRuntime. Like how you can switch between the mutable and immutable collections library. I'd be really interested better names if someone can come up with any.
Here is an abridged version of the macro code:
``
object RegexValidator {
implicit class RegexHelper(val sc: StringContext) extends AnyVal {
def r(args: Any*): Pattern = macro RegexHelperimpl
}
def RegexHelperimpl(c: Context)(args: c.Expr[Any]*): c.Expr[Pattern] = {
import c.universe._
c.prefix.tree match {
// access data of string interpolation
case Apply(_, List(Apply(_, rawParts))) =>
// parts` contain the strings a string interpolation is built fromval parts = rawParts map { case t @ Literal(Constant(const: String)) => (const, t.pos) }
parts match {
// if there is only one string literal
case List((raw, pos)) => {
//compiletime validation here
try {
val p = Pattern.compile(raw)
} catch {
case ex: PatternSyntaxException => {
//fancyness with underlineing
//TODO: this seems a little iffy...
val rpos = pos.asInstanceOf[scala.reflect.internal.util.OffsetPosition]
//TODO: better class?
val outpos = new RangePosition(rpos.source, rpos.start + ex.getIndex, rpos.start + ex.getIndex, rpos.start + ex.getIndex)
c.error(outpos.asInstanceOf[c.universe.Position], ex.getDescription())
}
//... catch other er
Solution
I highly recommend using quasiquotes wherever possible - they make the macro much more readable/maintainable.
For debugging I found that the REPL was much more useful than my IDE, especially when I made heavy use of Context.abort commands to pinpoint errors. This also helps make the code somewhat self-documenting.
For debugging I found that the REPL was much more useful than my IDE, especially when I made heavy use of Context.abort commands to pinpoint errors. This also helps make the code somewhat self-documenting.
Context
StackExchange Code Review Q#93062, answer score: 2
Revisions (0)
No revisions yet.