Unit testing (running) a Rule[I, O] which requires something on the value-stack

28 views
Skip to first unread message

Ian Hummel

unread,
Jul 11, 2016, 4:10:57 AM7/11/16
to parboiled2.org User List
Hi folks,

Really enjoying Parboiled2 so far, but have a question about how to unit test a rule which relies on the presence of an item in the value stack.

I'm trying to parse some XML-like messages with nested tags.  Take something like this for instance

<foo><bar>this is a bar</bar><baz>bazzz</baz></foo>

Foo is actually an Avro specific record and when I encounter a <bar> or <baz> tag (they can appear in any order) I want to be able to set the relevant properties on the Foo object.  Here's a slightly simplified example to illustrate how my parser is written:

def foo= rule {
  tagBegin("foo") ~ push(Foo.newBuilder()) ~ ws ~ zeroOrMore(bar | baz) ~ tagEnd("foo") ~>((f: Foo.Builder) => f.build())
}

def bar = rule { leafTag("bar") ~>((f: Foo.Builder, s: String) => f.setBar(s)) }
def baz = rule { leafTag("baz") ~>((f: Foo.Builder, s: String) => f.setBaz(s)) }


I can easily unit test my "foo" rule with:

it should "parse a Foo" in {
  val p = new FooParser("<foo><bar>this is a bar</bar><baz>bazzz</baz></foo>")
  p.foo.run() match {
    case Success(x) => println(x)
    case Failure(e: ParseError) => println(p.formatError(e))
    case Failure(e) => throw e
  }
}

But how do I unit test my "bar" or "baz" rules?  They rely on the presence of an existing value on the value-stack...  This fails to compile:

it should "parse a Foo" in {
  val p = new FooParser("<bar>this is a bar</bar>")
  p.bar.run() match {
    case Success(x) => println(x)
    case Failure(e: ParseError) => println(p.formatError(e))
    case Failure(e) => throw e
  }
}

I get

Error:(43, 11) value run is not a member of org.parboiled2.Rule[shapeless.::[com.example.Foo.Builder,shapeless.HNil],shapeless.::[com.example.Foo.Builder,shapeless.HNil]]
    p.bar.run() match {
          ^

How can I prepare the value-stack and then run the parser to get the output?  Is there a different pattern I should be following for setting properties of an object conditional on what was parsed?


Advice appreciated, thanks!

Mathias Doenitz

unread,
Jul 11, 2016, 4:16:00 AM7/11/16
to parboil...@googlegroups.com
Hi Ian,

one thing you could do is extend your `FooParser` class in your tests and give it two more rule methods:

def barWithBuilder(b: Foo.Builder) = rule { push(b) ~ bar }
def bazWithBuilder(b: Foo.Builder) = rule { push(b) ~ baz }

Then you can simply call the `...WithBuilder` methods in your test.

Cheers,
Mathias

---
mat...@parboiled.org
http://www.parboiled.org
> --
> You received this message because you are subscribed to the Google Groups "parboiled2.org User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to parboiled-use...@googlegroups.com.
> Visit this group at https://groups.google.com/group/parboiled-user.
> To view this discussion on the web visit https://groups.google.com/d/msgid/parboiled-user/83d67bf8-fd1d-4207-9f19-da31d8c2cd73%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages