7
\$\begingroup\$

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:

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 from
 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 errors and handle sensibly ...
 }
 //then parse at compile time
 c.Expr[Pattern]( q" riteofwhey.ocd.regex.RegexRuntime.parse($raw) ")
 }
 // if there is more then 1 string chunck i.e. r"regex_${2 + 2}ex" 
 // fall back to runtime interpolation
 case _ => {
 c.Expr[Pattern]( q" riteofwhey.ocd.regex.RegexRuntime.parse(StringContext(..$rawParts), Seq[Any](..$args) ) ")
 }
 }
 }
}

I have marked the places I find particularly worrying with TODOs. But I would also be interested in feedback on

  • documentation
  • general macro stuff
  • variable names
  • macro automated testing strategies

(Made corrections based on Zim-Zam O'Pootertoot's comment to use quasiqoates)

asked Jun 9, 2015 at 3:08
\$\endgroup\$

1 Answer 1

2
+100
\$\begingroup\$

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.

answered Jun 16, 2015 at 20:52
\$\endgroup\$
3
  • \$\begingroup\$ I tried to rewrite those awful ast blocks with quasiquotes for that exact reason... But I couldn't figure it out. The thpes didn't seem to line up and the syntax I was trying to emulate is a little crazy. \$\endgroup\$ Commented Jun 16, 2015 at 21:04
  • \$\begingroup\$ @user833970 There's some sample quasiquote macro code that I wrote here that might help demystify it a bit \$\endgroup\$ Commented Jun 16, 2015 at 21:20
  • \$\begingroup\$ Oh! for testing, I meant automated testing, I see how that came across wrong. \$\endgroup\$ Commented Jun 17, 2015 at 2:33

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.