Permalink
Browse files

= Implement capturePos rule

  • Loading branch information...
alexander-myltsev committed Jul 28, 2015
1 parent db55488 commit 3db444c3aec0e30de25a980941446d204c13f1c8
View
@@ -6,7 +6,7 @@ val commonSettings = Seq(
"org.json4s" %% "json4s-jackson" % "3.2.11",
"org.littleshoot" % "littleshoot-commons-id" % "1.0.3",
"commons-lang" % "commons-lang" % "2.6",
"org.globalnames" %% "parboiled" % "2.2.0-cca58cba7126660158f6aa45f1cb4e1bdb8ddfe6-SNAPSHOT",
"org.globalnames" %% "parboiled" % "2.2.0-cca58cba7126660158f6aa45f1cb4e1bdb8ddfe6-STAGE-SNAPSHOT",
"org.specs2" %% "specs2-core" % "3.6.3" % "test"
),
resolvers ++= Seq(
@@ -0,0 +1,129 @@
package org.globalnames.parser
import org.parboiled2._
import org.parboiled2.support._
import shapeless.HList
import scala.language.experimental.macros
object MacrosCustom {
import scala.reflect.macros.blackbox.Context
type Context1[A] = Context { type PrefixType = Parser#RuleCreator1[A] }
type Context2[A, B] = Context { type PrefixType = Parser#RuleCreator2[A, B] }
type Context3[A, B, C] = Context { type PrefixType = Parser#RuleCreator3[A, B, C] }
def rule0[Ctx: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](ctx: Context)(
r: ctx.Expr[Rule[Ctx, I, O]]): ctx.Tree = rule0Impl(ctx)(r, debug = false)
def debugRule0[Ctx: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](ctx: Context)(
r: ctx.Expr[Rule[Ctx, I, O]]): ctx.Tree = rule0Impl(ctx)(r, debug = true)
private def rule0Impl[Ctx: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](ctx: Context)(
r: ctx.Expr[Rule[Ctx, I, O]], debug: Boolean): ctx.Tree = {
import ctx.universe._
val opTreeCtx = new OpTreeContextCustom[ctx.type](ctx)
import opTreeCtx._
tpeCtx = weakTypeOf[Ctx]
val body = ResultExpression(r.tree) mapStatements StateAccessTransformer mapResultAndGet { tree
val opTree = RuleCall(OpTreeCall(OpTree(tree)), ruleName(ctx))
q"""
def wrapped: Boolean = ${opTree.render(wrapped = true)}
if (__psi.inErrorAnalysis) wrapped else ${opTree.render(wrapped = false)}"""
}
val tree = q"""
new $prefix.RuleImpl[$tpeCtx] {
def run(__psi: $prefix.ParserStateImpl[$tpeCtx]): Boolean = $body
}.asInstanceOf[$prefix.Rule[$tpeCtx, ${weakTypeOf[I]}, ${weakTypeOf[O]}]]"""
//println(tree)
tree
}
def rule1[Ctx: ctx.WeakTypeTag, A: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](
ctx: Context1[A])(r: ctx.Expr[A Rule[Ctx, I, O]]): ctx.Tree = {
import ctx.universe._
val opTreeCtx = new OpTreeContextCustom[ctx.type](ctx)
import opTreeCtx._
tpeCtx = weakTypeOf[Ctx]
ResultExpression(r.tree) mapResultAndGet { tree
ctx.untypecheck(tree) match {
case Function(List(ValDef(_, argName, _, _)), body)
val runBody = ResultExpression(body) mapStatements StateAccessTransformer mapResultAndGet { bodyTree
val opTree = RuleCall(OpTreeCall(OpTree(bodyTree)), ruleName(ctx))
q"""
def wrapped: Boolean = ${opTree.render(wrapped = true)}
if (__psi.inErrorAnalysis) wrapped else ${opTree.render(wrapped = false)}"""
}
val tpeCtx = weakTypeOf[Ctx]
val tpeA = weakTypeOf[A]
q"""
new $prefix.Rule1XImpl[$tpeCtx, $tpeA] {
def run($argName: $tpeA, __psi: $prefix.ParserStateImpl[$tpeCtx]): Boolean = $runBody
}.asInstanceOf[$prefix.Rule1X[$tpeCtx, $tpeA, ${weakTypeOf[I]}, ${weakTypeOf[O]}]]"""
case x ctx.abort(x.pos, "Expression must be a Function1 literal")
}
}
}
def rule2[Ctx: ctx.WeakTypeTag, A: ctx.WeakTypeTag, B: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](
ctx: Context2[A, B])(r: ctx.Expr[(A, B) Rule[Ctx, I, O]]): ctx.Tree = {
import ctx.universe._
val opTreeCtx = new OpTreeContextCustom[ctx.type](ctx)
import opTreeCtx._
tpeCtx = weakTypeOf[Ctx]
ResultExpression(r.tree) mapResultAndGet { tree
ctx.untypecheck(tree) match {
case Function(List(ValDef(_, arg1Name, _, _), ValDef(_, arg2Name, _, _)), body)
val runBody = ResultExpression(body) mapStatements StateAccessTransformer mapResultAndGet { bodyTree
val opTree = RuleCall(OpTreeCall(OpTree(bodyTree)), ruleName(ctx))
q"""
def wrapped: Boolean = ${opTree.render(wrapped = true)}
if (__psi.inErrorAnalysis) wrapped else ${opTree.render(wrapped = false)}"""
}
val tpeCtx = weakTypeOf[Ctx]
val tpeA = weakTypeOf[A]
val tpeB = weakTypeOf[B]
q"""
new $prefix.Rule2XImpl[$tpeCtx, $tpeA, $tpeB] {
def run($arg1Name: $tpeA, $arg2Name: $tpeB, __psi: $prefix.ParserStateImpl[$tpeCtx]): Boolean = $runBody
}.asInstanceOf[$prefix.Rule2X[$tpeCtx, $tpeA, $tpeB, ${weakTypeOf[I]}, ${weakTypeOf[O]}]]"""
case x ctx.abort(x.pos, "Expression must be a Function2 literal")
}
}
}
def rule3[Ctx: ctx.WeakTypeTag, A: ctx.WeakTypeTag, B: ctx.WeakTypeTag, C: ctx.WeakTypeTag, I <: HList: ctx.WeakTypeTag, O <: HList: ctx.WeakTypeTag](
ctx: Context3[A, B, C])(r: ctx.Expr[(A, B, C) Rule[Ctx, I, O]]): ctx.Tree = {
import ctx.universe._
val opTreeCtx = new OpTreeContextCustom[ctx.type](ctx)
import opTreeCtx._
tpeCtx = weakTypeOf[Ctx]
ResultExpression(r.tree) mapResultAndGet { tree
ctx.untypecheck(tree) match {
case Function(List(ValDef(_, arg1Name, _, _), ValDef(_, arg2Name, _, _), ValDef(_, arg3Name, _, _)), body)
val runBody = ResultExpression(body) mapStatements StateAccessTransformer mapResultAndGet { bodyTree
val opTree = RuleCall(OpTreeCall(OpTree(bodyTree)), ruleName(ctx))
q"""
def wrapped: Boolean = ${opTree.render(wrapped = true)}
if (__psi.inErrorAnalysis) wrapped else ${opTree.render(wrapped = false)}"""
}
val tpeCtx = weakTypeOf[Ctx]
val tpeA = weakTypeOf[A]
val tpeB = weakTypeOf[B]
val tpeC = weakTypeOf[C]
q"""
new $prefix.Rule3XImpl[$tpeCtx, $tpeA, $tpeB, $tpeC] {
def run($arg1Name: $tpeA, $arg2Name: $tpeB, $arg3Name: $tpeC,
__psi: $prefix.ParserStateImpl[$tpeCtx]): Boolean = $runBody
}.asInstanceOf[$prefix.Rule3X[$tpeCtx, $tpeA, $tpeB, $tpeC, ${weakTypeOf[I]}, ${weakTypeOf[O]}]]"""
case x ctx.abort(x.pos, "Expression must be a Function3 literal")
}
}
}
private def ruleName(ctx: Context): ctx.Tree = {
import ctx.universe._
q"""
val __name = ${ctx.prefix}.name
if (__name.isEmpty) ${ctx.internal.enclosingOwner.name.decodedName.toString.trim} else __name"""
}
}
@@ -0,0 +1,26 @@
package org.globalnames.parser
import org.parboiled2._
import org.parboiled2.support._
import shapeless.HList
import scala.language.experimental.macros
class OpTreeContextCustom[OpTreeCtx <: reflect.macros.blackbox.Context](val c1: OpTreeCtx) extends OpTreeContext[OpTreeCtx](c1) {
import c.universe._
override def opTreePF: PartialFunction[Tree, OpTree] = {
case q"$a.this.capturePos[..$b]($arg)($d)" => CapturePos(OpTree(arg))
case t => super.opTreePF(t)
}
case class CapturePos(op: OpTree) extends DefaultNonTerminalOpTree {
def ruleTraceNonTerminalKey = reify(RuleTrace.Sequence).tree // this is stub
def renderInner(wrapped: Boolean): Tree = q"""
${if (!wrapped) q"val start = __psi.cursor" else q"();"}
val matched = ${op.render(wrapped)}
if (matched) {
__psi.valueStack.push(org.globalnames.parser.CapturePos(start, __psi.cursor))
true
} else false"""
}
}
@@ -0,0 +1,52 @@
package org.globalnames.parser
import org.parboiled2.{RuleTypes, RuleDSLBasics, RuleDSLCombinators, RuleDSLActions, Rule, Rule1X, Rule2X, Rule3X}
import shapeless._
import shapeless.ops.hlist.Prepend
import scala.language.experimental.macros
import scala.annotation.compileTimeOnly
import org.parboiled2.support.`n/a`
case class CapturePos(start: Int, end: Int)
trait RuleDSLActionsCustom {
/** Pushes the input text cursor position matched by its inner rule onto the value stack
* after its inner rule has been run successfully (and only then).
*/
@compileTimeOnly("Calls to `capturePos` must be inside `rule` macro")
def capturePos[C, I <: HList, O <: HList](r: Rule[C, I, O])(
implicit p: Prepend[O, CapturePos :: HNil]): Rule[C, I, p.Out] = `n/a`
}
abstract class ParserCustom extends RuleTypes
with RuleDSLBasics
with RuleDSLCombinators
with RuleDSLActions
with RuleDSLActionsCustom {
def rule = ruleCreator0
def rule[A]() = new RuleCreator1[A]("")
def rule[A, B]() = new RuleCreator2[A, B]("")
def rule[A, B, C]() = new RuleCreator3[A, B, C]("")
def namedRule(name: String) = new RuleCreator0(name)
def namedRule1X[A](name: String) = new RuleCreator1[A](name)
def namedRule2X[A, B](name: String) = new RuleCreator2[A, B](name)
def namedRule3X[A, B, C](name: String) = new RuleCreator3[A, B, C](name)
private val ruleCreator0 = new RuleCreator0("")
class RuleCreator0(val name: String) {
def apply[Ctx <: Context, I <: HList, O <: HList](r: Rule[Ctx, I, O]): Rule[Ctx, I, O] = macro MacrosCustom.rule0[Ctx, I, O]
def debug[Ctx <: Context, I <: HList, O <: HList](r: Rule[Ctx, I, O]): Rule[Ctx, I, O] = macro MacrosCustom.debugRule0[Ctx, I, O]
}
class RuleCreator1[A](val name: String) {
def apply[Ctx <: Context, I <: HList, O <: HList](r: A Rule[Ctx, I, O]): Rule1X[Ctx, A, I, O] = macro MacrosCustom.rule1[Ctx, A, I, O]
}
class RuleCreator2[A, B](val name: String) {
def apply[Ctx <: Context, I <: HList, O <: HList](r: (A, B) Rule[Ctx, I, O]): Rule2X[Ctx, A, B, I, O] = macro MacrosCustom.rule2[Ctx, A, B, I, O]
}
class RuleCreator3[A, B, C](val name: String) {
def apply[Ctx <: Context, I <: HList, O <: HList](r: (A, B, C) Rule[Ctx, I, O]): Rule3X[Ctx, A, B, C, I, O] = macro MacrosCustom.rule3[Ctx, A, B, C, I, O]
}
}
@@ -0,0 +1,30 @@
import scala.annotation.tailrec
import scala.util.{ Success, Failure }
import scala.io.StdIn
import org.parboiled2.ParseError
import org.globalnames.parser.ParserCustom
import org.parboiled2.CharPredicate
object TestParser extends App {
object Parser extends ParserCustom {
type Context = Any
val InputLine = rule { "a" ~ capturePos(oneOrMore(CharPredicate.Digit)) ~ EOI }
}
repl()
@tailrec
def repl(): Unit =
StdIn.readLine("---\nEnter expression for abc-parser > ") match {
case "" // terminate
case line
Parser.InputLine.run(line) match {
case Success(x) println(s"Expression is valid: $x")
case Failure(e: ParseError) println("Expression is not valid: " + e.format(line))
case Failure(e) println("Unexpected error during parsing run: " + e)
}
repl()
}
}
@@ -4,7 +4,7 @@ import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
import org.apache.commons.id.uuid.UUID
import org.parboiled2._
import org.parboiled2.{ParseError, Parser}
import scala.collection._
import scala.util.{Success, Failure, Try}
import org.apache.commons.lang.StringEscapeUtils

0 comments on commit 3db444c

Please sign in to comment.