Contextual rules

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Contextual rules

Pierre-Yves Saumont
Hello,

First, let me say that Parboiled is a fantastic tool. I just discovered it while I was planning to rewrite in Scala a parser I had written previously from scratch in Java. I was able to write a nearly complete new version in a few hours in only about one hundred lines, i.e 1/10 of the times and 1/10 of the code lines that had been needed for the previous version.

However, I am blocked with contextual rules. A simplified version of what I need to do would be to parse "T44ABCD|CEFD|T34GDC|EFC|ECD|T44EGCF". The string represents a musical chart with bars delimited by "|". The first digit after the letter T indicates how many letters (representing chords) are to be found in each bar. The corresponding value is used for following bars until a new value is encountered. The parser as to produce an object representation of the chart.

I tried to use Var[Int] and was able to pass it from rule to rule, like this:

def chart = {val beats = new Var[Int]; oneOrMore(bar(beats)) ~~> Chart }

def timeSig(beats: Var[Int]) = "T" ~ ("0" - "9") ~% (x => { beats.set(x.toInt)}) ~ ("0" - "9")  ~~> TimeSig

def bar(beats: Var[Int]) = optional("|") ~ optional(timeSig(beats) ~ oneOrMore("A" - "G")  ~~> Bar

However, it seems the Var is ok when constructing the rules, but not when applying them. So the value used is the initial value, i.e. 0 in the above case or whatever value I may use at initialization.

While searching this forum, I found the following example:

def myRule = oneOrMore("0" - "9") ~> (Integer.parseInt(_)) ~ oneOrMore("A" - "G") ~> (s => s) ~~? (_ ==  _.length)

But I was not able to make this rule produce something (the ~~> Bar, at the end) nor make the computed value be used in the following bars (without digits)

Thanks for helping,

Pierre-Yves
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Contextual rules

mathias
Administrator
Pierre-Yves,

sorry for taking so long for replying (just returned from vacation).

> A simplified version of what I
> need to do would be to parse "T44ABCD|CEFD|T34GDC|EFC|ECD|T44EGCF". The
> string represents a musical chart with bars delimited by "|". The first
> digit after the letter T indicates how many letters (representing chords)
> are to be found in each bar. The corresponding value is used for following
> bars until a new value is encountered. The parser as to produce an object
> representation of the chart.


I assume the redundancy in this language was put in on purpose, since the first digit after the letter T (for indicating the number of following chords) can be simply left out, no?
So, why do you want to use it in the parser?
I assume the reason is verification, i.e. error checking.
In this case I would simply use the Value Stack for this, i.e. parse the digit, evaluate it and put it into an object that you then push onto the value stack.
Then, after having parsed a series of chords you simply verify that the number is equal to the one in your object.

Note that, if you upgrade to parboiled2 you have another and possibly easier-to-use option: simply add a mutable field to the parser class itself which holds the value of the last "T digit" you parsed…

HTH and cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 01.01.2014, at 23:17, Pierre-Yves Saumont [via parboiled users] <[hidden email]> wrote:

>
>
> Hello,
>
> First, let me say that Parboiled is a fantastic tool. I just discovered it
> while I was planning to rewrite in Scala a parser I had written previously
> from scratch in Java. I was able to write a nearly complete new version in a
> few hours in only about one hundred lines, i.e 1/10 of the times and 1/10 of
> the code lines that had been needed for the previous version.
>
> However, I am blocked with contextual rules. A simplified version of what I
> need to do would be to parse "T44ABCD|CEFD|T34GDC|EFC|ECD|T44EGCF". The
> string represents a musical chart with bars delimited by "|". The first
> digit after the letter T indicates how many letters (representing chords)
> are to be found in each bar. The corresponding value is used for following
> bars until a new value is encountered. The parser as to produce an object
> representation of the chart.
>
> I tried to use Var[Int] and was able to pass it from rule to rule, like
> this:
>
> def chart = {val beats = new Var[Int]; oneOrMore(bar(beats)) ~~> Chart }
>
> def timeSig(beats: Var[Int]) = "T" ~ ("0" - "9") ~% (x => {
> beats.set(x.toInt)}) ~ ("0" - "9")  ~~> TimeSig
>
> def bar(beats: Var[Int]) = optional("|") ~ optional(timeSig(beats) ~
> oneOrMore("A" - "G")  ~~> Bar
>
> However, it seems the Var is ok when constructing the rules, but not when
> applying them. So the value used is the initial value, i.e. 0 in the above
> case or whatever value I may use at initialization.
>
> While searching this forum, I found the following example:
>
> def myRule = oneOrMore("0" - "9") ~> (Integer.parseInt(_)) ~ oneOrMore("A" -
> "G") ~> (s => s) ~~? (_ ==  _.length)
>
> But I was not able to make this rule produce something (the ~~> Bar, at the
> end) nor make the computed value be used in the following bars (without
> digits)
>
> Thanks for helping,
>
> Pierre-Yves
>
>
>
>
> _______________________________________________
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/Contextual-rules-tp4024268.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, visit class="small">
signature.asc (858 bytes)
Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Contextual rules

Pierre-Yves
Hi Mathias,

Thank you for the advice. In the meantime, I have rewritten my application to use Parboiled2.

The extra digit is not redundant. The first one is the number of beats, and the second is the length of one beat.

I managed to create a version that is context free, but I had to drop several other features for this, so I guess I am going to write it again. In fact, I had implemented it the way you say (with a mutable field in the parser) with parboiled1 and it did not work. It took me some time to understand why, Then, I did not even imagine it would work in parboiled2, so I did not try!

Let me say again that Parboil is a fantastic tool, and Parboiled2 is even better. The only little problem is with the error messages that are very long and do not use the type aliases, Some messages are more than 30 lines long just to indicate that a rule type is not correct. I think this is due to the fact that type aliases are not used in the messages. I supposed there is nothing to do about it, but these messages are very difficult to understand.

BTW, the parser is so fast, that it made me think to a new use case: parsing lines of code while the user is typing (i.e. each time he stops typing for 1 second). There are two options for this: changing the rules in order to make an incomplete chart correct, or keep the same rules in order to have the parser indicate what character may be typed next. This would involve an HTML form in a client browser and the parser on a server, communicating through web socket. Do you think this is feasible with Parboiled2?

Thanks,

Pierre-Yves
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Contextual rules

mathias
Administrator
Pierre-Yves,

> The extra digit is not redundant. The first one is the number of beats, and
> the second is the length of one beat.

I just seemed to me that I know the number of beats by just parsing up to the next `|` delimiter in something like
"T44ABCD|CEFD|T34GDC|EFC|ECD|T44EGCF".

> I managed to create a version that is context free, but I had to drop
> several other features for this, so I guess I am going to write it again. In
> fact, I had implemented it the way you say (with a mutable field in the
> parser) with parboiled1 and it did not work. It took me some time to
> understand why, Then, I did not even imagine it would work in parboiled2, so
> I did not try!

In pb1 the parser class is created once and potentially reused many times.
In pb2 you create a new parser class instance for every input. Therefore the scope of one parser instance is limited to one run and therefore inherently single-threaded.

> Let me say again that Parboil is a fantastic tool, and Parboiled2 is even
> better. The only little problem is with the error messages that are very
> long and do not use the type aliases, Some messages are more than 30 lines
> long just to indicate that a rule type is not correct. I think this is due
> to the fact that type aliases are not used in the messages. I supposed there
> is nothing to do about it, but these messages are very difficult to
> understand.

Yes, we'll try to improve the compiler error messages by as much as possible.

> BTW, the parser is so fast, that it made me think to a new use case: parsing
> lines of code while the user is typing (i.e. each time he stops typing for 1
> second). There are two options for this: changing the rules in order to make
> an incomplete chart correct, or keep the same rules in order to have the
> parser indicate what character may be typed next. This would involve an HTML
> form in a client browser and the parser on a server, communicating through
> web socket. Do you think this is feasible with Parboiled2?

Absolutely!
The `ParseError` instance you get back from a failing parsing run should contain everything that is required for you to be able to provide the user with the information, which characters are allowed next.

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 14.01.2014, at 14:27, Pierre-Yves [via parboiled users] <[hidden email]> wrote:

>
>
> Hi Mathias,
>
> Thank you for the advice. In the meantime, I have rewritten my application
> to use Parboiled2.
>
> The extra digit is not redundant. The first one is the number of beats, and
> the second is the length of one beat.
>
> I managed to create a version that is context free, but I had to drop
> several other features for this, so I guess I am going to write it again. In
> fact, I had implemented it the way you say (with a mutable field in the
> parser) with parboiled1 and it did not work. It took me some time to
> understand why, Then, I did not even imagine it would work in parboiled2, so
> I did not try!
>
> Let me say again that Parboil is a fantastic tool, and Parboiled2 is even
> better. The only little problem is with the error messages that are very
> long and do not use the type aliases, Some messages are more than 30 lines
> long just to indicate that a rule type is not correct. I think this is due
> to the fact that type aliases are not used in the messages. I supposed there
> is nothing to do about it, but these messages are very difficult to
> understand.
>
> BTW, the parser is so fast, that it made me think to a new use case: parsing
> lines of code while the user is typing (i.e. each time he stops typing for 1
> second). There are two options for this: changing the rules in order to make
> an incomplete chart correct, or keep the same rules in order to have the
> parser indicate what character may be typed next. This would involve an HTML
> form in a client browser and the parser on a server, communicating through
> web socket. Do you think this is feasible with Parboiled2?
>
> Thanks,
>
> Pierre-Yves
>
>
>
> _______________________________________________
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/Contextual-rules-tp4024268p4024270.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, visit class="small">
signature.asc (858 bytes)
Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Contextual rules

Pierre-Yves Saumont
> I just seemed to me that I know the number of beats by just parsing up to the next `|` delimiter in something like
>"T44ABCD|CEFD|T34GDC|EFC|ECD|T44EGCF".

In fact, what I want to achieve is verifying that the number of chords entered by the user (here he number of letters) match the indicated number of beats. The rule should fail if the number of letters (chords) is more than indicated. If it is less it could either fail, or fill the missing beats with a special value meaning "repeat the previous one"

In any cases, I am going to try implement this. If I have other questions, I will put them in the Parboiled 2 forum.

Thanks four your help!

Pierre-Yves
Loading...