1

I want to match strings where the first character is a letter, then it is followed by multiple characters which are either digits or letters, then finally ends with a letter. For example a11a11a is correct but a11aa11 is incorrect because it ends with a digit and not a letter.

I wrote the following code to do it:

var grammar =
    from first in Parse.Letter.Once()
    from rest in Parse.LetterOrDigit.Many()
    from end in Parse.Letter.Once()
    select new string(first.Concat(rest).Concat(end).ToArray());

var result = grammar.TryParse("a111a");

Unfortunately LetterOrDigit.Many() consumes the last letter too.

Any way to avoid this?

Tahir Hassan
  • 5,715
  • 6
  • 45
  • 65
  • 1
    You want to use a [Regex](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) – Richard Schneider Jul 08 '18 at 14:58
  • 1
    I think he wants to use Sprache, not a RegEx. Just because a Regex is simple for the given case doesn't mean it will be simple for other grammar rules.. – TaW Jul 08 '18 at 15:09

2 Answers2

1

Here is a solution:

Parser<IEnumerable<char>> A = null, B = null, C = null;

var letter = Parse.Letter.Once();
var digit = Parse.Digit.Once();

B =
    (
    from d in digit
    from cs in Parse.Ref(() => C)
    select d.Concat(cs)
    ).Or
    (
        from l in letter
        from bs in Parse.Ref(() => B)
        select l.Concat(bs)
    ).Or(letter);

C = (
    from d in digit
    from bs in Parse.Ref(() => B)
    select d.Concat(bs)
    ).Or(letter);

A = (
    from l in letter
    from bs in Parse.Ref(() => B)
    select l.Concat(bs)
    ).Or(letter);

var grammar =
    from _ in Parse.WhiteSpace.Many()
    from a in A
    from __ in Parse.WhiteSpace.Many()
    select a;

The clauses in the Or's need to be in the correct order.

A commenter recommended the use of Regular Expressions. You can use them within Sprache:

Parse.Regex("[a-z]([a-z0-9]*[a-z])?")
Tahir Hassan
  • 5,715
  • 6
  • 45
  • 65
0

An alternative Solution:

       var text = "a11a11a";
       var n = text.Length;
       var grammer =
           from open in Parse.Letter.Once()
           from content in Parse.LetterOrDigit.Repeat(n - 2)
           from close in Parse.Letter.Once()
           select open.Concat(content).Concat(close);       

Try it

M.Hassan
  • 10,282
  • 5
  • 65
  • 84