diff --git a/README.md b/README.md index 70194cc..a593db9 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ private static Parser CreateParser() ).Do((_, u, d) => u.HasValue ? -d : d); var product = unary.Fold( - Seq(S, OneOf("*/"), unary), - (v, _, op, d) => op == '*' ? v * d : v / d); + S.Then(OneOf("*/")), + (l, r, op) => op == '*' ? l * r : l / r); sum.Parser = product.Fold( - Seq(S, OneOf("+-"), product), - (v, _, op, d) => op == '+' ? v + d : v - d); + S.Then(OneOf("+-")), + (l, r, op) => op == '+' ? l + r : l - r); return sum.Before(Eof); } @@ -175,19 +175,19 @@ AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores DefaultJob : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2 -| Method | Mean | Error | StdDev | Op/s | Gen0 | Allocated | -|---------------------- |------------:|---------:|---------:|------------:|-------:|----------:| -| Ramstack:Large | 1,636.8 ns | 13.17 ns | 11.68 ns | 610,962.9 | - | - | -| Ramstack:Diag:Large | 1,772.6 ns | 4.14 ns | 3.67 ns | 564,132.8 | 0.0057 | 104 B | -| Parlot:Large | 3,332.9 ns | 9.38 ns | 8.31 ns | 300,039.9 | 0.1984 | 3344 B | -| Parlot:Compiled:Large | 3,408.1 ns | 14.63 ns | 13.68 ns | 293,415.4 | 0.1984 | 3344 B | -| Pidgin:Large | 38,503.3 ns | 90.20 ns | 79.96 ns | 25,971.8 | 0.2441 | 4464 B | -| | | | | | | | -| Ramstack:Small | 220.5 ns | 0.71 ns | 0.67 ns | 4,534,869.6 | - | - | -| Ramstack:Diag:Small | 251.9 ns | 1.06 ns | 0.83 ns | 3,969,360.7 | 0.0062 | 104 B | -| Parlot:Small | 407.6 ns | 1.17 ns | 0.98 ns | 2,453,264.6 | 0.0391 | 656 B | -| Parlot:Compiled:Small | 388.3 ns | 1.91 ns | 1.78 ns | 2,575,382.0 | 0.0391 | 656 B | -| Pidgin:Small | 4,285.5 ns | 23.64 ns | 22.11 ns | 233,342.9 | 0.0381 | 736 B | +| Method | Mean | Error | StdDev | Op/s | Gen0 | Allocated | +|---------------------- |------------:|----------:|----------:|------------:|-------:|----------:| +| Ramstack:Large | 1,600.2 ns | 9.54 ns | 7.96 ns | 624,925.9 | - | - | +| Ramstack:Diag:Large | 1,699.6 ns | 3.53 ns | 3.13 ns | 588,379.8 | 0.0057 | 104 B | +| Parlot:Large | 3,520.1 ns | 11.08 ns | 10.37 ns | 284,081.7 | 0.1984 | 3344 B | +| Parlot:Compiled:Large | 3,356.7 ns | 7.08 ns | 6.27 ns | 297,910.3 | 0.1984 | 3344 B | +| Pidgin:Large | 39,197.3 ns | 150.13 ns | 140.43 ns | 25,511.9 | 0.2441 | 4464 B | +| | | | | | | | +| Ramstack:Small | 209.5 ns | 0.28 ns | 0.25 ns | 4,772,894.8 | - | - | +| Ramstack:Diag:Small | 260.6 ns | 0.57 ns | 0.50 ns | 3,838,032.3 | 0.0062 | 104 B | +| Parlot:Small | 410.7 ns | 1.46 ns | 1.36 ns | 2,434,653.7 | 0.0391 | 656 B | +| Parlot:Compiled:Small | 385.1 ns | 1.22 ns | 0.95 ns | 2,596,891.9 | 0.0391 | 656 B | +| Pidgin:Small | 4,322.2 ns | 18.28 ns | 17.10 ns | 231,363.9 | 0.0381 | 736 B | ``` - `Ramstack` diagnostic messages disabled. diff --git a/samples/ExpressionParser.cs b/samples/ExpressionParser.cs index b5a15a3..66a643e 100644 --- a/samples/ExpressionParser.cs +++ b/samples/ExpressionParser.cs @@ -38,12 +38,12 @@ private static Parser CreateExpressionParser() ).Do((_, u, d) => u.HasValue ? -d : d); var product = unary.Fold( - Seq(S, OneOf("*/"), unary), - (v, _, op, d) => op == '*' ? v * d : v / d); + S.Then(OneOf("*/")), + (l, r, o) => o == '*' ? l * r : l / r); sum.Parser = product.Fold( - Seq(S, OneOf("+-"), product), - (v, _, op, d) => op == '+' ? v + d : v - d); + S.Then(OneOf("+-")), + (l, r, o) => o == '+' ? l + r : l - r); return sum.Before(Eof); } diff --git a/src/Ramstack.Parsing/Parser.Fold.Generated.cs b/src/Ramstack.Parsing/Parser.Fold.Generated.cs deleted file mode 100644 index 50724fb..0000000 --- a/src/Ramstack.Parsing/Parser.Fold.Generated.cs +++ /dev/null @@ -1,1050 +0,0 @@ -//----------------------------------------------------------------------------// -// // -// This code was generated by a tool. // -// // -// Changes to this file may cause incorrect behavior // -// and will be lost if the code is regenerated. // -// // -//----------------------------------------------------------------------------// - -#nullable enable - -namespace Ramstack.Parsing; - -partial class Parser -{ - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2)> item, Func reduce) => - new Fold2Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2)> item, Func reduce) => - new FoldR2Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3)> item, Func reduce) => - new Fold3Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3)> item, Func reduce) => - new FoldR3Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4)> item, Func reduce) => - new Fold4Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4)> item, Func reduce) => - new FoldR4Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5)> item, Func reduce) => - new Fold5Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5)> item, Func reduce) => - new FoldR5Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6)> item, Func reduce) => - new Fold6Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6)> item, Func reduce) => - new FoldR6Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7)> item, Func reduce) => - new Fold7Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7)> item, Func reduce) => - new FoldR7Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8)> item, Func reduce) => - new Fold8Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8)> item, Func reduce) => - new FoldR8Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> item, Func reduce) => - new Fold9Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> item, Func reduce) => - new FoldR9Parser(parser, item, reduce); - - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> item, Func reduce) => - new Fold10Parser(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR(this Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> item, Func reduce) => - new FoldR10Parser(parser, item, reduce); - - #region Inner type: Fold2Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold2Parser(Parser parser, Parser<(T1, T2)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR2Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR2Parser(Parser parser, Parser<(T1, T2)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold3Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold3Parser(Parser parser, Parser<(T1, T2, T3)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR3Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR3Parser(Parser parser, Parser<(T1, T2, T3)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold4Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold4Parser(Parser parser, Parser<(T1, T2, T3, T4)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR4Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR4Parser(Parser parser, Parser<(T1, T2, T3, T4)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold5Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold5Parser(Parser parser, Parser<(T1, T2, T3, T4, T5)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR5Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR5Parser(Parser parser, Parser<(T1, T2, T3, T4, T5)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold6Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold6Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR6Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR6Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5, T6)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold7Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold7Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR7Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR7Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5, T6, T7)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold8Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold8Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR8Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR8Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5, T6, T7, T8)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold9Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold9Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8, r.Item9); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR9Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR9Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5, T6, T7, T8, T9)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8, r.Item9); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: Fold10Parser - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold10Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8, r.Item9, r.Item10); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR10Parser - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR10Parser(Parser parser, Parser<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> item, Func reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, r.Item1, r.Item2, r.Item3, r.Item4, r.Item5, r.Item6, r.Item7, r.Item8, r.Item9, r.Item10); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion -} diff --git a/src/Ramstack.Parsing/Parser.Fold.cs b/src/Ramstack.Parsing/Parser.Fold.cs index 6635ab2..74f81dd 100644 --- a/src/Ramstack.Parsing/Parser.Fold.cs +++ b/src/Ramstack.Parsing/Parser.Fold.cs @@ -3,71 +3,85 @@ namespace Ramstack.Parsing; partial class Parser { /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. + /// Creates a left-associative parser. /// /// /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); + /// // Example: Number ([+-] Number)* + /// // 1 + 2 + 3 + 4 => (((1 + 2) + 3) + 4) + /// var sum = number.Fold(OneOf("+-"), (l, r, op) => op == '+' ? l + r : l - r); /// /// - /// The type of value produces by the initial parser. - /// The type of the values being accumulated. - /// The initial parser. - /// The parser for values to accumulate. + /// The type of the value produced by the main parser. + /// The type of the operator token produced by the parser. + /// The main parser that matches a value. + /// The parser that matches an operator token. /// A reduction function. /// - /// A parser that performs left-associative folding over parsed values. + /// A parser that performs left-associative folding. /// - public static Parser Fold(this Parser parser, Parser item, Func reduce) => - new FoldParser(parser, item, reduce); + public static Parser Fold(this Parser parser, Parser op, Func reduce) => + new FoldParser(parser, op, reduce); /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. + /// Creates a right-associative parser. /// /// /// + /// // 2 ^ 3 ^ 4 ^ 1 => (2 ^ (3 ^ (4 ^ 1))) + /// // a = b = c = d => (a = (b = (c = d))) + /// /// // Number ("^" Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); + /// var power = number.FoldR(L('^'), (l, r, op) => Math.Pow(l, r)); /// /// - /// The type of value produces by the initial parser. - /// The type of the values being accumulated. - /// The initial parser. - /// The parser for values to accumulate. + /// The type of value produces by the main parser. + /// The type of the operator token produced by the parser. + /// The main parser that matches a value. + /// The operator parser that matches an operator token. /// A reduction function. /// - /// A parser that performs right-associative folding over parsed values. + /// A parser that performs right-associative folding. /// - public static Parser FoldR(this Parser parser, Parser item, Func reduce) => - new FoldRParser(parser, item, reduce); + public static Parser FoldR(this Parser parser, Parser op, Func reduce) => + new FoldRParser(parser, op, reduce); #region Inner type: FoldParser /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. + /// Represents a left-associative parser. /// - /// The type of value produces by the initial parser. - /// The type of the values being accumulated. - /// The initial parser. - /// The parser for values to accumulate. + /// The type of value produces by the main parser. + /// The type of the operator token produced by the parser. + /// The main parser that matches a value. + /// The operator parser that matches an operator token. /// A reduction function. - private sealed class FoldParser(Parser parser, Parser item, Func reduce) : Parser + private sealed class FoldParser(Parser parser, Parser op, Func reduce) : Parser { /// public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) { - if (parser.TryParse(ref context, out var result)) + var bookmark = context.BookmarkPosition(); + + if (parser.TryParse(ref context, out var v)) { - while (item.TryParse(ref context, out var r)) - result = reduce(result, r); + var result = v; + + while (true) + { + var rollback = context.BookmarkPosition(); + + if (op.TryParse(ref context, out var o) && parser.TryParse(ref context, out v)) + { + result = reduce(result, v, o); + continue; + } + + context.RestorePosition(rollback); + break; + } + context.SetMatched(bookmark); value = result!; return true; } @@ -77,8 +91,11 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); + protected internal override Parser ToVoidParser() + { + var p = parser.Void(); + return Seq(p, Seq(op.Void(), p).ZeroOrMore()); + } } #endregion @@ -86,26 +103,44 @@ protected internal override Parser ToVoidParser() => #region Inner type: FoldRParser /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. + /// Represents a right-associative parser. /// - /// The type of value produces by the initial parser. - /// The type of the values being accumulated. - /// The initial parser. - /// The parser for values to accumulate. + /// The type of value produces by the main parser. + /// The type of the operator token produced by the parser. + /// The main parser that matches a value. + /// The operator parser that matches an operator token. /// A reduction function. - private sealed class FoldRParser(Parser parser, Parser item, Func reduce) : Parser + private sealed class FoldRParser(Parser parser, Parser op, Func reduce) : Parser { /// public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) { - if (parser.TryParse(ref context, out var result)) + var bookmark = context.BookmarkPosition(); + + if (parser.TryParse(ref context, out var v)) { - var values = new ArrayList(); - while (item.TryParse(ref context, out var v)) - values.Add(v); + var list = new ArrayBuilder<(TOperator op, T value)>(); + list.Add((default!, v)); - for (var i = values.Count - 1; i >= 0; i--) - result = reduce(result, values[i]); + while (true) + { + var rollback = context.BookmarkPosition(); + + if (op.TryParse(ref context, out var o) && parser.TryParse(ref context, out v)) + { + list.Add((o, v)); + continue; + } + + context.RestorePosition(rollback); + break; + } + + context.SetMatched(bookmark); + + var result = list[^1].value; + for (var i = list.Count - 1; i > 0; i--) + result = reduce(list[i - 1].value, result, list[i].op); value = result!; return true; @@ -116,8 +151,11 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); + protected internal override Parser ToVoidParser() + { + var p = parser.Void(); + return Seq(p, Seq(op.Void(), p).ZeroOrMore()); + } } #endregion diff --git a/src/Ramstack.Parsing/Parser.Fold.tt b/src/Ramstack.Parsing/Parser.Fold.tt deleted file mode 100644 index 9dfff6e..0000000 --- a/src/Ramstack.Parsing/Parser.Fold.tt +++ /dev/null @@ -1,139 +0,0 @@ -<#@ template debug="false" hostspecific="false" language="C#" #> -<#@ include file="Properties\Autogenerated.ttinclude" #> -#nullable enable - -namespace Ramstack.Parsing; - -partial class Parser -{ -<# - for (var n = 2; n <= 10; n++) - { - WriteSeparatorLine(n != 2); - GenerateMethods(n); - } - - for (var n = 2; n <= 10; n++) - { - WriteLine(); - GenerateClasses(n); - } -#> -} -<#+ private void GenerateMethods(int arity) { #> - /// - /// Creates a left-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number ([+-] Number)* - /// var sum = number.Fold( - /// Seq(OneOf("+-"), number), - /// (r, op, d) => op == '+' ? r + n : r - n); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs left-associative folding over parsed values. - /// - public static Parser Fold $"T{n}", from: 1)#>>(this Parser parser, Parser<(<#=GenerateList(arity, n => $"T{n}", from: 1)#>)> item, Func $"T{n}", from: 1)#>, T> reduce) => - new Fold<#=arity#>Parser $"T{n}", from: 1)#>>(parser, item, reduce); - - /// - /// Creates a right-associative parser that applies the function - /// to reduce values from the parsed items. - /// - /// - /// - /// // Number (^ Number)* - /// var power = number.FoldR( - /// Seq(L('^'), number), - /// (r, _, d) => Math.Pow(r, d)); - /// - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - /// - /// A parser that performs right-associative folding over parsed values. - /// - public static Parser FoldR $"T{n}", from: 1)#>>(this Parser parser, Parser<(<#=GenerateList(arity, n => $"T{n}", from: 1)#>)> item, Func $"T{n}", from: 1)#>, T> reduce) => - new FoldR<#=arity#>Parser $"T{n}", from: 1)#>>(parser, item, reduce); -<#+ } #> -<#+ private void GenerateClasses(int arity) { #> - #region Inner type: Fold<#=arity#>Parser $"T{n}", from: 1)#>> - - /// - /// Represents a left-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class Fold<#=arity#>Parser $"T{n}", from: 1)#>>(Parser parser, Parser<(<#=GenerateList(arity, n => $"T{n}", from: 1)#>)> item, Func $"T{n}", from: 1)#>, T> reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - while (item.TryParse(ref context, out var r)) - result = reduce(result, <#=GenerateList(arity, n => $"r.Item{n}", from: 1)#>); - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion - - #region Inner type: FoldR<#=arity#>Parser $"T{n}", from: 1)#>> - - /// - /// Represents a right-associative parser that accumulates parsed items using a specified reduction function. - /// - /// The initial parser. - /// The parser for values to accumulate. - /// A reduction function. - private sealed class FoldR<#=arity#>Parser $"T{n}", from: 1)#>>(Parser parser, Parser<(<#=GenerateList(arity, n => $"T{n}", from: 1)#>)> item, Func $"T{n}", from: 1)#>, T> reduce) : Parser - { - /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) - { - if (parser.TryParse(ref context, out var result)) - { - var values = new ArrayList<(<#=GenerateList(arity, n => $"T{n}", from: 1)#>)>(); - while (item.TryParse(ref context, out var v)) - values.Add(v); - - for (var i = values.Count - 1; i >= 0; i--) - { - ref var r = ref values[i]; - result = reduce(result, <#=GenerateList(arity, n => $"r.Item{n}", from: 1)#>); - } - - value = result!; - return true; - } - - value = default; - return false; - } - - /// - protected internal override Parser ToVoidParser() => - Seq(parser.Void(), item.Void().ZeroOrMore()); - } - - #endregion -<#+ } #> diff --git a/src/Ramstack.Parsing/Ramstack.Parsing.csproj b/src/Ramstack.Parsing/Ramstack.Parsing.csproj index d181d30..61b19cd 100644 --- a/src/Ramstack.Parsing/Ramstack.Parsing.csproj +++ b/src/Ramstack.Parsing/Ramstack.Parsing.csproj @@ -50,10 +50,6 @@ - - TextTemplatingFileGenerator - Parser.Fold.Generated.cs - TextTemplatingFileGenerator Parser.Seq.Generated.cs @@ -65,11 +61,6 @@ - - True - True - Parser.Fold.tt - True True @@ -91,11 +82,6 @@ true \ - - True - True - Parser.Fold.tt - diff --git a/tests/Ramstack.Parsing.Tests/ParsersTests.Fold.cs b/tests/Ramstack.Parsing.Tests/ParsersTests.Fold.cs new file mode 100644 index 0000000..2983342 --- /dev/null +++ b/tests/Ramstack.Parsing.Tests/ParsersTests.Fold.cs @@ -0,0 +1,40 @@ +using System.Numerics; + +using static Ramstack.Parsing.Parser; + +namespace Ramstack.Parsing; + +partial class ParsersTests +{ + [TestCase("1", 1, 1)] + [TestCase("1+", 1, 1)] + [TestCase("1+2$", 3, 3)] + [TestCase("1+2-", 3, 3)] + [TestCase("1+2+3+4-2", 8, 9)] + [TestCase("1+2+3+4-2$", 8, 9)] + public void FoldTest(string expr, int result, int length) + { + var number = Literal.Number(); + var parser = number.Fold(OneOf("+-"), (l, r, o) => o == '+' ? l + r : l - r); + + Assert.That(parser.Parse(expr).Success, Is.True); + Assert.That(parser.Parse(expr).Value, Is.EqualTo(result)); + Assert.That(parser.Map(m => (m.Index, m.Length)).Parse(expr).Value, Is.EqualTo((0, length))); + } + + [TestCase("2", "2", 1)] + [TestCase("2$", "2", 1)] + [TestCase("2**", "2", 1)] + [TestCase("2**3", "8", 4)] + [TestCase("2**3**", "8", 4)] + [TestCase("2**3**4**1$", "2417851639229258349412352", 10)] + public void FoldRTest(string expr, string result, int length) + { + var number = Literal.Number(); + var parser = number.FoldR(L("**"), (l, r, _) => BigInteger.Pow(l, (int)r)); + + Assert.That(parser.Parse(expr).Success, Is.True); + Assert.That(parser.Parse(expr).Value, Is.EqualTo(BigInteger.Parse(result))); + Assert.That(parser.Map(m => (m.Index, m.Length)).Parse(expr).Value, Is.EqualTo((0, length))); + } +}