// fraction = literal-literal/literal | literal literal/literal | literal/literal
        private ParserRule[] GetFractionRules() => new ParserRule[]
        {
            // Mixed number fraction rule e.g. 1 1/2
            ParserRuleBuilder
            .New()
            .Token <LiteralAmountToken>(Literal)
            .Condition(buffer => buffer.TryConsume(' ') || buffer.TryConsume('-'))
            .Token <LiteralAmountToken>(Literal)
            .Condition(buffer => buffer.TryConsume('/'))
            .Token <LiteralAmountToken>(Literal)
            .Map(tokens =>
            {
                var wholeNumber = tokens[0] as LiteralAmountToken;
                var numerator   = tokens[1] as LiteralAmountToken;
                var denominator = tokens[2] as LiteralAmountToken;

                return(ParserRuleResult.Success(AmountToken.Fraction(wholeNumber, numerator, denominator)));
            })
            .Build(),

            // Standard fraction rule e.g. 1/2
            ParserRuleBuilder
            .New()
            .Token <LiteralAmountToken>(Literal)
            .Condition(buffer => buffer.TryConsume('/'))
            .Token <LiteralAmountToken>(Literal)
            .Map(tokens =>
            {
                var numerator   = tokens[0] as LiteralAmountToken;
                var denominator = tokens[1] as LiteralAmountToken;

                return(ParserRuleResult.Success(AmountToken.Fraction(wholeNumber: null, numerator, denominator)));
            })
            .Build()
        };
        // amount = range | fraction | literal
        private ParserRule[] GetAmountRules() => new ParserRule[]
        {
            // Range e.g. 1-4
            ParserRuleBuilder
            .New()
            .Token <RangeAmountToken>(Range)
            // Ensure all input is consumed
            .Condition(buffer => !buffer.HasNext())
            .Map(tokens =>
            {
                var range = tokens[0] as RangeAmountToken;

                return(ParserRuleResult.Success(range));
            })
            .Build(),

            // Fraction e.g. 1/2
            ParserRuleBuilder
            .New()
            .Token <FractionalAmountToken>(Fraction)
            // Ensure all input is consumed
            .Condition(buffer => !buffer.HasNext())
            .Map(tokens =>
            {
                var fraction = tokens[0] as FractionalAmountToken;

                return(ParserRuleResult.Success(fraction));
            })
            .Build(),

            // Literal e.g. 2
            ParserRuleBuilder
            .New()
            .Token <LiteralAmountToken>(Literal)
            // Ensure all input is consumed
            .Condition(buffer => !buffer.HasNext())
            .Map(tokens =>
            {
                var literal = tokens[0] as LiteralAmountToken;

                return(ParserRuleResult.Success(literal));
            })
            .Build()
        };
        // range = fraction-fraction | literal-literal
        private ParserRule[] GetRangeRules() => new ParserRule[]
        {
            // Fractional range e.g. 1/4-1/3
            ParserRuleBuilder
            .New()
            .Token <FractionalAmountToken>(Fraction)
            .Condition(buffer => buffer.OptionallyConsume(' '))
            .Condition(buffer => buffer.TryConsume('-'))
            .Condition(buffer => buffer.OptionallyConsume(' '))
            .Token <FractionalAmountToken>(Fraction)
            .Map(tokens =>
            {
                var lowerBound = tokens[0] as FractionalAmountToken;
                var upperBound = tokens[1] as FractionalAmountToken;

                return(ParserRuleResult.Success(AmountToken.Range(lowerBound, upperBound)));
            })
            .Build(),

            // Literal range e.g. 1-2
            ParserRuleBuilder
            .New()
            .Token <LiteralAmountToken>(Literal)
            .Condition(buffer => buffer.OptionallyConsume(' '))
            .Condition(buffer => buffer.TryConsume('-'))
            .Condition(buffer => buffer.OptionallyConsume(' '))
            .Token <LiteralAmountToken>(Literal)
            .Map(tokens =>
            {
                var lowerBound = tokens[0] as LiteralAmountToken;
                var upperBound = tokens[1] as LiteralAmountToken;

                return(ParserRuleResult.Success(AmountToken.Range(lowerBound, upperBound)));
            })
            .Build()
        };