My Profile Photo

y-code


Blog about things I am mostly interested in code: functional programming, parsing grammars, compilers


Capturing Positions in parboiled2

It is a common task to track positions of recognized AST nodes. parboiled2 has no built-in facilities to do that. The reason is that different applications have different needs with regard to position tracking. Still, there are ways to capture positions.

One way is to compose capturing rules with existing DSL rules (originally proposed at parboiled user group):

    var nodes = Map.empty[AstNode, Position]

    def someAstNode = rule {
      startAstNode ~ ... /* match AST node syntax */ ... ~> MyAstNode ~ endAstNode ~ restOfRule
    }

    def startAstNode = rule { push(cursor) }

    def endAstNode[A <: AstNode]: Rule[Int :: A :: HNil, A :: HNil] = rule {
      run { (start: Int, node: A) =>
        val pos = Position(start, cursor)
        nodes = nodes.updated(node, pos)
        node
      }
    }

The problem with approach is that nodes structure together with both rules do not have properties of value stack. For example, if restOfRule fails then something (not clear how to implement) should track all stored AST nodes in nodes to clean it up later.

It would be useful if parboiled2 has facilities to add custom rules to DSL. Alas, parboiled2 has no easy way to do it. I know only one way to make it effectively: to patch source code. Hopefully, there are few steps to do it that are as follows:

  1. add DSL entity to RuleDSLActions.scala
  2. add quasiquote AST match to OpTreeContext.scala
  3. implement code rendering at the same OpTreeContext.scala

Summary of changes to capture positions is publicly available.

comments powered by Disqus