tags:

views:

85

answers:

3

I am using Scala's combinator parser as follows:

def a = b ~ c ^^ { case x ~ y => A(x,y) }
def b = ... { B() }
def c = ... { C() }

now I have a feature change that change within the parsing of the reference of previously parsed B to be a val in C. So C's constructor is something like:

C(ref:B)

I can imagine, the only way to achieve this is a dirty patch work by assigning the instance of parsed B object to def c in between the parsing of a. Something like following:

var temp:B = null

def a = ( b ^^ { case x => temp = x } )
        ~ c(temp) ^^ {case x ~ y => A(x,y} )

Is there a standard, clean way of doing this? The definition of a can't be broken, it is used in many places in rest of the code.

The other solution is to use var instead of val and have following:

 def a = (b ~ c ) ^^ { case x ~ y => y.ref = c ; A(x,y) }

But this is also not acceptable as it would "work" now, but it would involve extra effort and boiler-plate code in future development.

I've not tested this code, as this is a small part and all the changes require a lot of effort so want the expert opinion first.

+2  A: 

Without changing the definition of a, there is no way to do this cleanly. The ~ combinator produces a new Parser which applies b and c in sequence, then tuples (well, logically tuples) up the results and returns them as its result. The key point is that the application of c is not a function of the output of b, thus there is nothing you can do to get the results of b inside the application of c.

What I would do is add a new combinator which does what you want. I'm not feeling particularly creative name-wise, but I think this should give you a rough idea:

implicit def cleverParserSyntax[A](left: Parser[A]) = new {
  def ~%[B](right: A => Parser[B]): Parser[A ~ B] = for {
    lr <- left
    rr <- right(lr)
  } yield new ~(lr, rr)
}

def a = b ~% c ^^ { case x ~ y => A(x,y) }
def b = ... { B() }
def c(res: B) = ... { C(res) }
Daniel Spiewak
Except the `into` method, of course. :-) Which happens to have a very similar type signature... :-)
Daniel
Well, I *could* go that route, but it's so much more fun to just come up with something on my own! ;-)
Daniel Spiewak
+1  A: 

I'd do this:

case class B(x: String)
case class C(b: B, x: String)       
case class A(b: B, c: C)

class MyParser extends RegexParsers {               
  def a = b >> c ^^ { case x ~ y => A(x, y) }       
  def b = "\\w+".r ^^ B                             
  def c(b: B) = "\\d+".r ^^ (x => new ~(b, C(b, x)))
}

Now, if B happens much before C, things get more complicated. I suggest, if things get that hairy, to search for the paper about Scala parser, which goes into a lot of very advanced features.

Daniel
+1  A: 

I'm not sure if I understand the problem correctly but if C depends on B why not express this in functional way?

case class B(...)
case class C(b: B, ...)
case class A(b: B, c: C)

def b: Parser[B] = ... ^^ { B(...) }
def c: Parser[B => C] = ... ^^ { C(_, ...) }
def a: Parser[A] = b ~ c ^^ {  A(b, c(b)) }

This way your problem is solved and you have your dependencies expressed explicitly and in concise way.

Grzegorz Kossakowski
I haven't thought of that. I just tested it, and it works indeed perfectly. Very cool! :-)
Daniel
Thanks. Actually, I realized that this is a really nice example of thinking in functional way. :-)
Grzegorz Kossakowski