how-to: have multiple nonterminals without spaces between?

Dec 28, 2009 at 5:47 PM

Hi,

 

I just started using Irony, so sorry if this is a real stupid question.

 

I have a string that can l7ook like any of the following: (the cards in POKER!)

"hand = A" (only one ACE)

"hand=AA" (pair of aces)

"hand = T T" (Pair of tens, but with a space between the two)

"hand = 6 suited 7" (a 6 and a 7 of the same suit (heart, diamon, spades, club))

I managed to get everything working except case 2 ("hand=AA", the one without any spaces

 

I used the following code:

Rank.Rule = ToTerm("A") | "K" | "Q" | "J" | "T" | "9" | "8" | "7" | "6" | "5" | "4" | "3" | "2";
            Hand.Rule = ToTerm("hand") + EqualSign + Rank |
                        "hand" + EqualSign + Rank + Rank |
                        "hand" + EqualSign + Rank + "suited" | 
                        "hand" + EqualSign + Rank + "suited" + Rank |
                        "hand" + EqualSign + Rank + Rank + "suited";


how can I get it to work with case 2?

 

 

Coordinator
Dec 28, 2009 at 6:02 PM

To control whitespace explicitly you need to assign empty string to WhitespaceChars property:

this.WhitespaceChars = string.Empty;

But then you have to add explicit whitespace elements between the terms.

But I suspect this is not exactly your problem - can you please provide more info, what does it mean - it does not work for case 2? does the parser give you an error at thsi point?

Or parser builder reports some conflict?

 

Dec 29, 2009 at 7:35 AM
Edited Dec 29, 2009 at 7:36 AM

I looked in the GrammarExplorer on the "Test" tab and I had multiple lines of my script.

 

But let me rephrase this:

 

Let's take a C# code:

You could write:

if(xy<=3)

 

or: if( xy <= 3)

 

I need to get the "name" of the variable ('xy') and I need to have it restrained to a maxium of 2 letters and to a range of characters (A,K,Q,J,T,9,8,7,6,5,4,3,2)

Coordinator
Dec 29, 2009 at 8:37 AM

Use identifierTerminal, assign AllFirstChars and AllChars properties explicitly (list only allowed leters and digits), then handle ValidateToken event of the IdentifierTerminal, and reject those terminals that are invalid for your rules (longer than 2 chars for ex)

 

Dec 29, 2009 at 4:35 PM
Edited Dec 29, 2009 at 5:09 PM

This works great, thanks.

 

But there is still one combination, where I don't get the right results.

This time with poker syntax:

"hand=AA" (works fine)

"hand=AK" (works fine, too)

"hand=A K"(does not work) GrammarExplorer says: "Invalid Character: K"

***************************************************************************

Rank.AllChars = "AKQJT98765432dshc";
            Rank.AllFirstChars = Rank.AllChars;
            Rank.ValidateToken += new EventHandler<ParsingEventArgs>(rank_ValidateToken);

 

Hand.Rule = ToTerm("hand") + EqualSign + Rank |
                        "hand" + EqualSign + Rank + "suited";

***************************************************************************

 

If I change the code to:

Hand.Rule = ToTerm("hand") + EqualSign + Rank |
                        "hand" + EqualSign + Rank + "suited" |
                        "hand" + EqualSign + Rank + Rank |
                        "hand" + EqualSign + Rank + Rank + "suited" | /* */
                        "hand" + EqualSign + Rank + "suited" + Rank;

 

I get the following results:

"hand=AA" (works fine)

"hand=AK suited " (does not work) GrammarExplorer: "Invalid character: u" (the 'u' in 'suited')

"hand=A K"(works)

 

How can I make the "suited" more binding than the Rank?
This error happens, because Irony tries to parse 'suited' as Rank :-/

 

 

Jan 6, 2010 at 4:33 PM
Edited Jan 7, 2010 at 3:30 PM

Hey Medic,

Looking at your first code, the last scenario fails because the letter K in the string does not make up any part of the expression.  Is there a line break or some other type of indication that the expression is complete? If there is a line break that follows, then you may just need to add it to the expression and update rank_ValidateToken.  If there is not, you may have to get crafty with the MakePlusRule or find another way to let the parser know it's done with that expression.

Blackjack anyone! ;-)

-MindCore

Coordinator
Jan 8, 2010 at 3:57 PM

I'd like to add my guess. The scanner is at position after AK and before "suited". It tries to figure out which terminal to use, and it has a choice of 2: Rank and "suited" key term. Key terms always have lower priority, so scanner tries Rank first. The terminal succeeeds with first "s", fires validation event and fails at "u".

Try marking all keywords in your language as Reserved words - that will give them highest priority; this makes sense in your case as all these "words" are really reserved and cannot be used as identifiers or as Rank tokens. Use Grammar.MarkReservedWords method.

Jan 8, 2010 at 6:19 PM

Thanks Roman,

I just learned something new about the terminal priority.

 

Medic - I took a crack at your code and came up with the following. I'm sure there may be a better way to handle the Face term.

 

[Language("BlackJack", "1.0", "Black Jack grammar")]
  public class BlackJackGrammar : Grammar
  {
      internal FixedLengthLiteral Face = new FixedLengthLiteral("Face", 1, TypeCode.String);      

      internal NonTerminal Hand = new NonTerminal("Hand");
      internal NonTerminal EqualSign = new NonTerminal("Equal_Sign");
      internal NonTerminal Suited = new NonTerminal("Suited");

      internal const string valid_faces = "AKQJT98765432";

      public BlackJackGrammar() : base(false)
      {
          Face.ValidateToken += new EventHandler<ParsingEventArgs>(face_ValidateToken);

          Suited.Rule = ToTerm("suited") + Face + Face |
                        Face + ToTerm("suited") + Face |
                        Face + Face + ToTerm("suited");

          Hand.Rule = ToTerm("hand") + EqualSign + Face |
              "hand" + EqualSign + Face + Face |
              "hand" + EqualSign + Suited;
          
          EqualSign.Rule = ToTerm("=");

          this.MarkReservedWords("hand", "suited");

          this.Root = Hand;
      }

      internal void face_ValidateToken(object sender, ParsingEventArgs e)
      {
          if (!valid_faces.Contains(e.Context.CurrentToken.ValueString))
              e.Context.CurrentToken
                  = e.Context.Source.CreateErrorToken("Must specify a valid Card face. They are {0}", valid_faces);
      }
  }