Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Create a Simple Parser in C# With Sprache

DZone's Guide to

Create a Simple Parser in C# With Sprache

If you need something more powerful than RegEx, but you don't quite need ANTLR, Sprache could be a happy middle ground for parsing that you can incorporate in your code.

· Web Dev Zone ·
Free Resource

Bugsnag monitors application stability, so you can make data-driven decisions on whether you should be building new features, or fixing bugs. Learn more.

{
    "projects": [
        "src",
        "test"
    ]
}


"dependencies": {
    "Sprache": "2.1.0"
},


 "dependencies": {
    "System.Runtime.Serialization.Primitives": "4.3.0",
    "xunit": "2.1.0",
    "dotnet-test-xunit": "1.0.0-rc2-192208-24",
    "SpracheGame": {
      "target": "project"
    }
},


 "runtimes": {
        "win7-x64": {},
        "win8-x64": {},
        "win81-x64": {},
        "win10-x64": {}
    }


public class GameParser
{
    public static Parser<string> Number = Parse.Digit.AtLeastOnce().Text().Token();

    public static Parser<Command> Command = Parse.Char('<').Then(_ => Parse.Char('>'))
                                                .Return(SpracheGameCore.Command.Between)
                                            .Or(Parse.Char('<')
                                                .Return(SpracheGameCore.Command.Less))
                                            .Or(Parse.Char('>')
                                                .Return(SpracheGameCore.Command.Greater))
                                            .Or(Parse.Char('=')
                                                .Return(SpracheGameCore.Command.Equal));


    public static Parser<Play> Play =
        (from action in Command
        from value in Number
        select new Play(action, value, null))
    .Or(from firstValue in Number
        from action in Command
        from secondValue in Number
        select new Play(action, firstValue, secondValue));
}


public class Play
{
    readonly Command _command;
    readonly int _firstNumber;
    readonly int _secondNumber;

    public Play(Command command, string firstNumber, string secondNumber)
    {
        _command = command;

        if (!int.TryParse(firstNumber, out _firstNumber))
            throw new ArgumentNullException("firstNumber");

        if (secondNumber != null)
        {
            if (!int.TryParse(secondNumber, out _secondNumber))
                throw new ArgumentNullException("secondNumber");
        }
    }

    public Command Command { get { return _command; } }

    public int FirstNumber { get { return _firstNumber; } }

    public int SecondNumber { get { return _secondNumber; } }

    public bool Evaluate(int number)
    {
        bool result = false;

        switch (Command)
        {
            case Command.Greater:
                result = number > FirstNumber;
                break;

            case Command.Less:
                result = number < FirstNumber;
                break;

            case Command.Between:
                result = (number > FirstNumber) && (number < SecondNumber);
                break;

            case Command.Equal:
                result = number == FirstNumber;
                break;
        }

        return result;
    }
}


public class GameParserTests
{
    [Theory]
    [InlineData("1")]
    [InlineData("10")]
    [InlineData("100")]
    public void CanParseNumbers(string value)
    {
        var number = GameParser.Number.Parse(value);

        Assert.Equal(number, value);
    }

    [Fact]
    public void CanParseGreaterCommand()
    {
        Command command = GameParser.Command.Parse(">");

        Assert.Equal(command, Command.Greater);
    }

    [...]

    [Fact]
    public void FailParseWrongPlay()
    {            
        Assert.Throws<ParseException>(() => GameParser.Play.Parse("> Number"));
    }
}


static void Main(string[] args)
{
    Random rand = new Random();
    int numberToGuess = rand.Next(1, 100);
    bool finished = false;

    Console.WriteLine("******************************************************");
    Console.WriteLine("*                                                    *");
    Console.WriteLine("* Guess the number by asking questions               *");
    Console.WriteLine("* Use < X to ask if the number is less than X        *");
    Console.WriteLine("* Use > X to ask if the number is greater than X     *");
    Console.WriteLine("* Use X <> Y to ask if the number is between X and Y *");
    Console.WriteLine("* Use = X to guess the number                        *");
    Console.WriteLine("* Use q to quit                                      *");
    Console.WriteLine("*                                                    *");
    Console.WriteLine("******************************************************");

    while (!finished)
    {
        try
        {
            var input = Console.ReadLine();
            if (input.Trim() == "q")
                finished = true;
            else
            {
                Play play = GameParser.Play.Parse(input);
                bool result = play.Evaluate(numberToGuess);
                Console.WriteLine(result);

                if (play.Command == Command.Equal && result == true)
                {
                    Console.WriteLine("You guessed right.");
                    finished = true;
                }
            }
        }
        catch (ParseException ex)
        {
            Console.WriteLine("There was an error: {0}", ex.Message);
        }

        Console.WriteLine();
    }
}


Monitor application stability with Bugsnag to decide if your engineering team should be building new features on your roadmap or fixing bugs to stabilize your application.Try it free.

Topics:
c# ,sprache ,web dev ,tutorial ,parsing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}