diff --git a/src/Parlot/Fluent/Between.cs b/src/Parlot/Fluent/Between.cs index dcd4946..39f1e59 100644 --- a/src/Parlot/Fluent/Between.cs +++ b/src/Parlot/Fluent/Between.cs @@ -23,8 +23,6 @@ public Between(Parser before, Parser parser, Parser after) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"Between({before.Name},{parser.Name},{after.Name})"; } public bool CanSeek { get; } @@ -144,4 +142,7 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => Name ?? $"Between({_before},{_parser},{_after})"; + } diff --git a/src/Parlot/Fluent/Capture.cs b/src/Parlot/Fluent/Capture.cs index 983ab21..5a12080 100644 --- a/src/Parlot/Fluent/Capture.cs +++ b/src/Parlot/Fluent/Capture.cs @@ -10,7 +10,6 @@ public sealed class Capture : Parser, ICompilable public Capture(Parser parser) { _parser = parser; - Name = $"{parser.Name} (Capture)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -87,4 +86,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Capture)"; } diff --git a/src/Parlot/Fluent/CharLiteral.cs b/src/Parlot/Fluent/CharLiteral.cs index 9bd3cfc..90ea99f 100644 --- a/src/Parlot/Fluent/CharLiteral.cs +++ b/src/Parlot/Fluent/CharLiteral.cs @@ -9,8 +9,7 @@ public sealed class CharLiteral : Parser, ICompilable, ISeekable public CharLiteral(char c) { Char = c; - ExpectedChars = [c]; - Name = $"Char('{c}')"; + ExpectedChars = new[] { c }; } public char Char { get; } @@ -45,12 +44,6 @@ public CompilationResult Compile(CompilationContext context) { var result = context.CreateCompilationResult(); - // if (context.Scanner.ReadChar(Char)) - // { - // success = true; - // value = Char; - // } - result.Body.Add( Expression.IfThen( context.ReadChar(Char), @@ -65,4 +58,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"Char('{Char}')"; } diff --git a/src/Parlot/Fluent/Deferred.cs b/src/Parlot/Fluent/Deferred.cs index 5e3a719..98d1c0d 100644 --- a/src/Parlot/Fluent/Deferred.cs +++ b/src/Parlot/Fluent/Deferred.cs @@ -2,6 +2,7 @@ using Parlot.Compilation; using Parlot.Rewriting; using System; + #if NET using System.Linq; #endif @@ -19,7 +20,6 @@ public Parser? Parser set { _parser = value ?? throw new ArgumentNullException(nameof(value)); - Name = $"{_parser.Name} (Deferred)"; } } @@ -31,7 +31,6 @@ public Parser? Parser public Deferred() { - Name = "Deferred"; } public Deferred(Func, Parser> parser) : this() @@ -149,4 +148,28 @@ public CompilationResult Compile(CompilationContext context) return result; } + + private bool _toString; + + public override string ToString() + { + // Handle recursion + + lock (this) + { + if (!_toString) + { + _toString = true; + var result = Name == null + ? $"{Parser} (Deferred)" + : $"{Name} (Deferred)"; + _toString = false; + return result; + } + else + { + return "(Deferred)"; + } + } + } } diff --git a/src/Parlot/Fluent/Discard.cs b/src/Parlot/Fluent/Discard.cs index 6b4eebf..5e7120c 100644 --- a/src/Parlot/Fluent/Discard.cs +++ b/src/Parlot/Fluent/Discard.cs @@ -15,8 +15,6 @@ public Discard(Parser parser, U value) { _parser = parser; _value = value; - - Name = $"{parser.Name} (Discard)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -60,4 +58,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Discard)"; } diff --git a/src/Parlot/Fluent/Else.cs b/src/Parlot/Fluent/Else.cs index e4bdada..338bd6a 100644 --- a/src/Parlot/Fluent/Else.cs +++ b/src/Parlot/Fluent/Else.cs @@ -29,8 +29,6 @@ public Else(Parser parser, T value) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"{parser.Name} (Else)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -81,4 +79,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Else)"; } diff --git a/src/Parlot/Fluent/Eof.cs b/src/Parlot/Fluent/Eof.cs index ec4f01d..8891a51 100644 --- a/src/Parlot/Fluent/Eof.cs +++ b/src/Parlot/Fluent/Eof.cs @@ -13,7 +13,6 @@ public sealed class Eof : Parser, ICompilable public Eof(Parser parser) { _parser = parser; - Name = $"{parser.Name} (Eof)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -62,4 +61,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Eof)"; } diff --git a/src/Parlot/Fluent/Error.cs b/src/Parlot/Fluent/Error.cs index 673abf4..d5b0a19 100644 --- a/src/Parlot/Fluent/Error.cs +++ b/src/Parlot/Fluent/Error.cs @@ -30,8 +30,6 @@ public ElseError(Parser parser, string message) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"{parser.Name} (ElseError)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -90,6 +88,8 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (ElseError)"; } public sealed class Error : Parser, ICompilable @@ -101,8 +101,6 @@ public Error(Parser parser, string message) { _parser = parser ?? throw new ArgumentNullException(nameof(parser)); _message = message; - - Name = $"{parser.Name} (Error)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -149,6 +147,8 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Error)"; } public sealed class Error : Parser, ICompilable, ISeekable @@ -173,8 +173,6 @@ public Error(Parser parser, string message) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"{parser.Name} (Error)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -222,4 +220,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (Error)"; } diff --git a/src/Parlot/Fluent/If.cs b/src/Parlot/Fluent/If.cs index efef27a..28484f6 100644 --- a/src/Parlot/Fluent/If.cs +++ b/src/Parlot/Fluent/If.cs @@ -24,8 +24,6 @@ public If(Parser parser, Func predicate, S? state) _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); _state = state; _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - Name = $"{parser.Name} (If)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -107,4 +105,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (If)"; } diff --git a/src/Parlot/Fluent/ListOfCharsLiteral.cs b/src/Parlot/Fluent/ListOfCharsLiteral.cs index e01af89..e4ed654 100644 --- a/src/Parlot/Fluent/ListOfCharsLiteral.cs +++ b/src/Parlot/Fluent/ListOfCharsLiteral.cs @@ -32,8 +32,6 @@ public ListOfChars(string values, int minSize = 1, int maxSize = 0) ExpectedChars = values.ToCharArray(); _minSize = minSize; _maxSize = maxSize; - - Name = $"AnyOf({values})"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -77,5 +75,7 @@ public override bool Parse(ParseContext context, ref ParseResult resul context.ExitParser(this); return true; } + + public override string ToString() => $"AnyOf([{string.Join(", ", ExpectedChars)}])"; } #endif diff --git a/src/Parlot/Fluent/Not.cs b/src/Parlot/Fluent/Not.cs index eaa7996..9a08608 100644 --- a/src/Parlot/Fluent/Not.cs +++ b/src/Parlot/Fluent/Not.cs @@ -11,8 +11,6 @@ public sealed class Not : Parser, ICompilable public Not(Parser parser) { _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - Name = $"Not ({parser.Name}"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -71,4 +69,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"Not ({_parser})"; } diff --git a/src/Parlot/Fluent/OneOf.ABT.cs b/src/Parlot/Fluent/OneOf.ABT.cs index 951022d..83f5bfc 100644 --- a/src/Parlot/Fluent/OneOf.ABT.cs +++ b/src/Parlot/Fluent/OneOf.ABT.cs @@ -15,8 +15,6 @@ public OneOf(Parser parserA, Parser parserB) { _parserA = parserA ?? throw new ArgumentNullException(nameof(parserA)); _parserB = parserB ?? throw new ArgumentNullException(nameof(parserB)); - - Name = $"OneOf ({parserA.Name}, {parserB.Name})"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -105,4 +103,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parserA} | {_parserB}"; } diff --git a/src/Parlot/Fluent/OneOf.cs b/src/Parlot/Fluent/OneOf.cs index 41410e7..7d857cc 100644 --- a/src/Parlot/Fluent/OneOf.cs +++ b/src/Parlot/Fluent/OneOf.cs @@ -118,7 +118,6 @@ public OneOf(Parser[] parsers) lookupTable?.Remove(OtherSeekableChar); var expectedChars = string.Join(",", lookupTable?.Keys.ToArray() ?? []); - Name = $"OneOf ({string.Join(",", Parsers.Select(x => x.Name))}) on '{expectedChars}'"; if (lookupTable != null && lookupTable.Count > 0) { _map = new CharMap>>(lookupTable); @@ -217,354 +216,356 @@ public override bool Parse(ParseContext context, ref ParseResult result) return false; } -/* We don't use the ICompilable interface anymore since the generated code is still slower than the original one. - * Furthermore the current implementation is creating too many lambdas (there might be a bug in the code). - - public CompilationResult Compile(CompilationContext context) - { - var result = context.CreateCompilationResult(); - - // var reset = context.Scanner.Cursor.Position; + public override string ToString() => $"{string.Join(" | ", Parsers)}) on [{string.Join(" ", ExpectedChars)}]"; - ParameterExpression? reset = null; + /* We don't use the ICompilable interface anymore since the generated code is still slower than the original one. + * Furthermore the current implementation is creating too many lambdas (there might be a bug in the code). - if (SkipWhitespace) + public CompilationResult Compile(CompilationContext context) { - reset = context.DeclarePositionVariable(result); - result.Body.Add(context.ParserSkipWhiteSpace()); - } + var result = context.CreateCompilationResult(); - Expression block = Expression.Empty(); + // var reset = context.Scanner.Cursor.Position; - if (_map != null) - { - // Switch is too slow, even for 2 elements compared to CharMap - // Expression are also not optimized like the compiler can do. + ParameterExpression? reset = null; - // UseSwitch(); - UseLookup(); - - void UseLookup() + if (SkipWhitespace) { - //var seekableParsers = _map[cursor.Current] ?? _otherParsers; - - //if (seekableParsers != null) - //{ - // var length = seekableParsers.Count; + reset = context.DeclarePositionVariable(result); + result.Body.Add(context.ParserSkipWhiteSpace()); + } - // for (var i = 0; i < length; i++) - // { - // if (seekableParsers[i].Parse(context, ref result)) - // { - // context.ExitParser(this); - // return true; - // } - // } - //} + Expression block = Expression.Empty(); - var charMapSetMethodInfo = typeof(CharMap>>).GetMethod("Set")!; - var nullSeekableParser = Expression.Constant(null, typeof(Func>)); + if (_map != null) + { + // Switch is too slow, even for 2 elements compared to CharMap + // Expression are also not optimized like the compiler can do. - var lambdaCache = new List<(List> Parsers, Func> CompiledLambda)>(); + // UseSwitch(); + UseLookup(); - foreach (var key in _map!.ExpectedChars) + void UseLookup() { - Expression group = Expression.Empty(); + //var seekableParsers = _map[cursor.Current] ?? _otherParsers; - var parsers = _map[key]!; + //if (seekableParsers != null) + //{ + // var length = seekableParsers.Count; - // Search if the same parsers set was already generated and compiled + // for (var i = 0; i < length; i++) + // { + // if (seekableParsers[i].Parse(context, ref result)) + // { + // context.ExitParser(this); + // return true; + // } + // } + //} - Func>? cacheCompiledLambda = null; + var charMapSetMethodInfo = typeof(CharMap>>).GetMethod("Set")!; + var nullSeekableParser = Expression.Constant(null, typeof(Func>)); - for (var i = 0; i < lambdaCache.Count; i++) + var lambdaCache = new List<(List> Parsers, Func> CompiledLambda)>(); + + foreach (var key in _map!.ExpectedChars) { - var cacheEntry = lambdaCache[i]; - if (cacheEntry.Parsers.Count != parsers.Count) + Expression group = Expression.Empty(); + + var parsers = _map[key]!; + + // Search if the same parsers set was already generated and compiled + + Func>? cacheCompiledLambda = null; + + for (var i = 0; i < lambdaCache.Count; i++) { - continue; - } + var cacheEntry = lambdaCache[i]; + if (cacheEntry.Parsers.Count != parsers.Count) + { + continue; + } + + var match = true; - var match = true; + for (var j = 0; j < parsers.Count; j++) + { + if (cacheEntry.Parsers[j] != parsers[j]) + { + match = false; + break; + } + } - for (var j = 0; j < parsers.Count; j++) + if (match) + { + cacheCompiledLambda = cacheEntry.CompiledLambda; + } + } + + if (cacheCompiledLambda == null) { - if (cacheEntry.Parsers[j] != parsers[j]) + var lambdaSuccess = Expression.Variable(typeof(bool), $"successL{context.NextNumber}"); + var lambdaResult = Expression.Variable(typeof(T), $"resultL{context.NextNumber}"); + + // The list is reversed since the parsers are unwrapped + foreach (var parser in parsers.ToArray().Reverse()) { - match = false; - break; + var groupResult = parser.Build(context); + + // lambdaSuccess and lambdaResult will be registered at the top of the method + + group = Expression.Block( + groupResult.Variables, + Expression.Block(groupResult.Body), + Expression.IfThenElse( + groupResult.Success, + Expression.Block( + Expression.Assign(lambdaSuccess, Expression.Constant(true, typeof(bool))), + context.DiscardResult + ? Expression.Empty() + : Expression.Assign(lambdaResult, groupResult.Value) + ), + group + ) + ); } + + var resultExpression = Expression.Variable(typeof(ValueTuple), $"result{context.NextNumber}"); + var returnTarget = Expression.Label(typeof(ValueTuple)); + var returnExpression = Expression.Return(returnTarget, resultExpression, typeof(ValueTuple)); + var returnLabel = Expression.Label(returnTarget, defaultValue: Expression.New(typeof(ValueTuple))); + + var groupBlock = (BlockExpression)group; + + var lambda = Expression.Lambda>>( + body: Expression.Block( + type: typeof(ValueTuple), + variables: groupBlock.Variables + .Append(resultExpression) + .Append(lambdaSuccess) + .Append(lambdaResult), + group, + Expression.Assign( + resultExpression, + Expression.New( + typeof(ValueTuple).GetConstructor([typeof(bool), typeof(T)])!, + lambdaSuccess, + context.DiscardResult ? + Expression.Constant(default(T), typeof(T)) : + lambdaResult) + ), + returnExpression, + returnLabel), + name: $"_map_{key}_{context.NextNumber}", + parameters: [context.ParseContext] // Only the name is used, so it will match the ones inside each compiler + ); + + cacheCompiledLambda = lambda.CompileFast(ifFastFailedReturnNull: false, ExpressionHelper.CompilerFlags); + lambdaCache.Add((parsers, cacheCompiledLambda)); + + #if DEBUG + context.Lambdas.Add(lambda); + #endif } - if (match) + if (key == OtherSeekableChar) + { + _lambdaOtherParsers = cacheCompiledLambda; + } + else { - cacheCompiledLambda = cacheEntry.CompiledLambda; + _lambdaMap.Set(key, cacheCompiledLambda!); } } - if (cacheCompiledLambda == null) + var seekableParsers = result.DeclareVariable>>($"seekableParser{context.NextNumber}", nullSeekableParser); + + var tupleResult = result.DeclareVariable>($"tupleResult{context.NextNumber}"); + + result.Body.Add( + Expression.Block( + // seekableParser = mapValue[key]; + Expression.Assign(seekableParsers, Expression.Call(Expression.Constant(_lambdaMap), CharMap>>.IndexerMethodInfo, Expression.Convert(context.Current(), typeof(uint)))), + // seekableParser ??= _otherParsers) + Expression.IfThen( + Expression.Equal( + nullSeekableParser, + seekableParsers + ), + Expression.Assign(seekableParsers, Expression.Constant(_lambdaOtherParsers, typeof(Func>))) + ), + Expression.IfThen( + Expression.NotEqual( + nullSeekableParser, + seekableParsers + ), + Expression.Block( + Expression.Assign(tupleResult, Expression.Invoke(seekableParsers, context.ParseContext)), + Expression.Assign(result.Success, Expression.Field(tupleResult, "Item1")), + context.DiscardResult + ? Expression.Empty() + : Expression.Assign(result.Value, Expression.Field(tupleResult, "Item2")) + ) + ) + ) + ); + } + + #pragma warning disable CS8321 // Local function is declared but never used + void UseSwitch() + { + // Lookup table is converted to a switch expression + + // switch (Cursor.Current) + // { + // case 'a' : + // parse1 instructions + // + // if (parser1.Success) + // { + // success = true; + // value = parse1.Value; + // } + // + // break; // implicit in SwitchCase expression + // + // case 'b' : + // ... + // } + + var cases = _map!.ExpectedChars.Where(x => x != OtherSeekableChar).Select(key => { - var lambdaSuccess = Expression.Variable(typeof(bool), $"successL{context.NextNumber}"); - var lambdaResult = Expression.Variable(typeof(T), $"resultL{context.NextNumber}"); + Expression group = Expression.Empty(); + + var parsers = _map[key]; // The list is reversed since the parsers are unwrapped - foreach (var parser in parsers.ToArray().Reverse()) + foreach (var parser in parsers!.ToArray().Reverse()) { var groupResult = parser.Build(context); - // lambdaSuccess and lambdaResult will be registered at the top of the method - group = Expression.Block( groupResult.Variables, Expression.Block(groupResult.Body), Expression.IfThenElse( groupResult.Success, Expression.Block( - Expression.Assign(lambdaSuccess, Expression.Constant(true, typeof(bool))), + Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), context.DiscardResult ? Expression.Empty() - : Expression.Assign(lambdaResult, groupResult.Value) + : Expression.Assign(result.Value, groupResult.Value) ), group ) ); } - var resultExpression = Expression.Variable(typeof(ValueTuple), $"result{context.NextNumber}"); - var returnTarget = Expression.Label(typeof(ValueTuple)); - var returnExpression = Expression.Return(returnTarget, resultExpression, typeof(ValueTuple)); - var returnLabel = Expression.Label(returnTarget, defaultValue: Expression.New(typeof(ValueTuple))); - - var groupBlock = (BlockExpression)group; - - var lambda = Expression.Lambda>>( - body: Expression.Block( - type: typeof(ValueTuple), - variables: groupBlock.Variables - .Append(resultExpression) - .Append(lambdaSuccess) - .Append(lambdaResult), - group, - Expression.Assign( - resultExpression, - Expression.New( - typeof(ValueTuple).GetConstructor([typeof(bool), typeof(T)])!, - lambdaSuccess, - context.DiscardResult ? - Expression.Constant(default(T), typeof(T)) : - lambdaResult) - ), - returnExpression, - returnLabel), - name: $"_map_{key}_{context.NextNumber}", - parameters: [context.ParseContext] // Only the name is used, so it will match the ones inside each compiler - ); - - cacheCompiledLambda = lambda.CompileFast(ifFastFailedReturnNull: false, ExpressionHelper.CompilerFlags); - lambdaCache.Add((parsers, cacheCompiledLambda)); - -#if DEBUG - context.Lambdas.Add(lambda); -#endif - } + return (Key: (uint)key, Body: group); - if (key == OtherSeekableChar) - { - _lambdaOtherParsers = cacheCompiledLambda; - } - else + }).ToArray(); + + // Construct default case body + Expression defaultBody = Expression.Empty(); + + if (_otherParsers != null) { - _lambdaMap.Set(key, cacheCompiledLambda!); - } - } + foreach (var parser in _otherParsers.ToArray().Reverse()) + { + var defaultCompileResult = parser.Build(context); - var seekableParsers = result.DeclareVariable>>($"seekableParser{context.NextNumber}", nullSeekableParser); + defaultBody = Expression.Block( + defaultCompileResult.Variables, + Expression.Block(defaultCompileResult.Body), + Expression.IfThenElse( + defaultCompileResult.Success, + Expression.Block( + Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), + context.DiscardResult + ? Expression.Empty() + : Expression.Assign(result.Value, defaultCompileResult.Value) + ), + defaultBody + ) + ); + } + } - var tupleResult = result.DeclareVariable>($"tupleResult{context.NextNumber}"); + block = + Expression.Switch( + Expression.Convert(context.Current(), typeof(uint)), + defaultBody, + cases.Select(c => Expression.SwitchCase( + c.Body, + Expression.Constant(c.Key) + )).ToArray() + ); + } + #pragma warning restore CS8321 // Local function is declared but never used - result.Body.Add( - Expression.Block( - // seekableParser = mapValue[key]; - Expression.Assign(seekableParsers, Expression.Call(Expression.Constant(_lambdaMap), CharMap>>.IndexerMethodInfo, Expression.Convert(context.Current(), typeof(uint)))), - // seekableParser ??= _otherParsers) - Expression.IfThen( - Expression.Equal( - nullSeekableParser, - seekableParsers - ), - Expression.Assign(seekableParsers, Expression.Constant(_lambdaOtherParsers, typeof(Func>))) - ), - Expression.IfThen( - Expression.NotEqual( - nullSeekableParser, - seekableParsers - ), - Expression.Block( - Expression.Assign(tupleResult, Expression.Invoke(seekableParsers, context.ParseContext)), - Expression.Assign(result.Success, Expression.Field(tupleResult, "Item1")), - context.DiscardResult - ? Expression.Empty() - : Expression.Assign(result.Value, Expression.Field(tupleResult, "Item2")) - ) - ) - ) - ); } - -#pragma warning disable CS8321 // Local function is declared but never used - void UseSwitch() + else { - // Lookup table is converted to a switch expression - - // switch (Cursor.Current) - // { - // case 'a' : - // parse1 instructions - // - // if (parser1.Success) - // { - // success = true; - // value = parse1.Value; - // } + // parse1 instructions // - // break; // implicit in SwitchCase expression - // - // case 'b' : + // if (parser1.Success) + // { + // success = true; + // value = parse1.Value; + // } + // else + // { + // parse2 instructions + // + // if (parser2.Success) + // { + // success = true; + // value = parse2.Value + // } + // // ... // } - var cases = _map!.ExpectedChars.Where(x => x != OtherSeekableChar).Select(key => - { - Expression group = Expression.Empty(); - - var parsers = _map[key]; - - // The list is reversed since the parsers are unwrapped - foreach (var parser in parsers!.ToArray().Reverse()) - { - var groupResult = parser.Build(context); - - group = Expression.Block( - groupResult.Variables, - Expression.Block(groupResult.Body), - Expression.IfThenElse( - groupResult.Success, - Expression.Block( - Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), - context.DiscardResult - ? Expression.Empty() - : Expression.Assign(result.Value, groupResult.Value) - ), - group - ) - ); - } - - return (Key: (uint)key, Body: group); - - }).ToArray(); - - // Construct default case body - Expression defaultBody = Expression.Empty(); - - if (_otherParsers != null) + foreach (var parser in Parsers.Reverse()) { - foreach (var parser in _otherParsers.ToArray().Reverse()) - { - var defaultCompileResult = parser.Build(context); + var parserCompileResult = parser.Build(context); - defaultBody = Expression.Block( - defaultCompileResult.Variables, - Expression.Block(defaultCompileResult.Body), - Expression.IfThenElse( - defaultCompileResult.Success, - Expression.Block( - Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), - context.DiscardResult - ? Expression.Empty() - : Expression.Assign(result.Value, defaultCompileResult.Value) - ), - defaultBody - ) - ); - } + block = Expression.Block( + parserCompileResult.Variables, + Expression.Block(parserCompileResult.Body), + Expression.IfThenElse( + parserCompileResult.Success, + Expression.Block( + Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), + context.DiscardResult + ? Expression.Empty() + : Expression.Assign(result.Value, parserCompileResult.Value) + ), + block + ) + ); } - - block = - Expression.Switch( - Expression.Convert(context.Current(), typeof(uint)), - defaultBody, - cases.Select(c => Expression.SwitchCase( - c.Body, - Expression.Constant(c.Key) - )).ToArray() - ); } -#pragma warning restore CS8321 // Local function is declared but never used - } - else - { - // parse1 instructions - // - // if (parser1.Success) - // { - // success = true; - // value = parse1.Value; - // } - // else + result.Body.Add(block); + + // [if skipwhitespace] + // if (!success) // { - // parse2 instructions - // - // if (parser2.Success) - // { - // success = true; - // value = parse2.Value - // } - // - // ... + // context.Scanner.Cursor.ResetPosition(begin); // } - foreach (var parser in Parsers.Reverse()) + if (reset != null) { - var parserCompileResult = parser.Build(context); - - block = Expression.Block( - parserCompileResult.Variables, - Expression.Block(parserCompileResult.Body), - Expression.IfThenElse( - parserCompileResult.Success, - Expression.Block( - Expression.Assign(result.Success, Expression.Constant(true, typeof(bool))), - context.DiscardResult - ? Expression.Empty() - : Expression.Assign(result.Value, parserCompileResult.Value) - ), - block + result.Body.Add( + Expression.IfThen( + Expression.Not(result.Success), + context.ResetPosition(reset) ) ); } - } - - result.Body.Add(block); - - // [if skipwhitespace] - // if (!success) - // { - // context.Scanner.Cursor.ResetPosition(begin); - // } - if (reset != null) - { - result.Body.Add( - Expression.IfThen( - Expression.Not(result.Success), - context.ResetPosition(reset) - ) - ); + return result; } - - return result; - } -*/ + */ } diff --git a/src/Parlot/Fluent/OneOrMany.cs b/src/Parlot/Fluent/OneOrMany.cs index 66db6b0..ffd5d00 100644 --- a/src/Parlot/Fluent/OneOrMany.cs +++ b/src/Parlot/Fluent/OneOrMany.cs @@ -22,8 +22,6 @@ public OneOrMany(Parser parser) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"OneOrMany({parser.Name})"; } public bool CanSeek { get; } @@ -127,4 +125,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser}+"; } diff --git a/src/Parlot/Fluent/Parser.TryParse.cs b/src/Parlot/Fluent/Parser.TryParse.cs index adb6ad2..0112955 100644 --- a/src/Parlot/Fluent/Parser.TryParse.cs +++ b/src/Parlot/Fluent/Parser.TryParse.cs @@ -7,6 +7,9 @@ public abstract partial class Parser private int _invocations; private volatile Parser? _compiledParser; + /// + /// Gets or sets the text which is to render the textual representation of the parser. + /// public string? Name { get; set; } public T? Parse(string text) diff --git a/src/Parlot/Fluent/Parser.cs b/src/Parlot/Fluent/Parser.cs index b7963e0..55855ff 100644 --- a/src/Parlot/Fluent/Parser.cs +++ b/src/Parlot/Fluent/Parser.cs @@ -58,7 +58,7 @@ public abstract partial class Parser /// public Parser Named(string name) { - this.Name = name; + Name = name; return this; } diff --git a/src/Parlot/Fluent/SearchValuesCharLiteral.cs b/src/Parlot/Fluent/SearchValuesCharLiteral.cs index 4608185..b64abcd 100644 --- a/src/Parlot/Fluent/SearchValuesCharLiteral.cs +++ b/src/Parlot/Fluent/SearchValuesCharLiteral.cs @@ -22,7 +22,6 @@ public SearchValuesCharLiteral(SearchValues searchValues, int minSize = 1, _searchValues = searchValues ?? throw new ArgumentNullException(nameof(searchValues)); _minSize = minSize; _maxSize = maxSize; - Name = $"AnyOf({searchValues})"; } public SearchValuesCharLiteral(ReadOnlySpan searchValues, int minSize = 1, int maxSize = 0) @@ -33,7 +32,6 @@ public SearchValuesCharLiteral(ReadOnlySpan searchValues, int minSize = 1, CanSeek = true; ExpectedChars = searchValues.ToArray(); - Name = $"AnyOf('{searchValues}')"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -82,5 +80,7 @@ public override bool Parse(ParseContext context, ref ParseResult resul context.ExitParser(this); return true; } + + public override string ToString() => $"AnyOf({_searchValues})"; } #endif diff --git a/src/Parlot/Fluent/Seekable.cs b/src/Parlot/Fluent/Seekable.cs index b7511d9..8ec77fd 100644 --- a/src/Parlot/Fluent/Seekable.cs +++ b/src/Parlot/Fluent/Seekable.cs @@ -25,8 +25,6 @@ public Seekable(Parser parser, bool skipWhiteSpace, params ReadOnlySpan ExpectedChars = expectedChars.ToArray().Distinct().ToArray(); SkipWhitespace = skipWhiteSpace; CanSeek = true; - - Name = $"{parser.Name} (Seekable)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -54,5 +52,7 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{Parser} (Seekable)"; } diff --git a/src/Parlot/Fluent/Separated.cs b/src/Parlot/Fluent/Separated.cs index f3e1583..c463ad3 100644 --- a/src/Parlot/Fluent/Separated.cs +++ b/src/Parlot/Fluent/Separated.cs @@ -25,7 +25,6 @@ public Separated(Parser separator, Parser parser) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - Name = $"Separated({separator.Name}, {parser.Name})"; } public bool CanSeek { get; } @@ -188,4 +187,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"Separated({_separator}, {_parser})"; } diff --git a/src/Parlot/Fluent/Sequence.cs b/src/Parlot/Fluent/Sequence.cs index 5352d64..4947627 100644 --- a/src/Parlot/Fluent/Sequence.cs +++ b/src/Parlot/Fluent/Sequence.cs @@ -20,8 +20,6 @@ public Sequence(Parser parser1, Parser parser2) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser1.Name}, {parser2.Name})"; } public bool CanSeek { get; } @@ -70,6 +68,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser1} & {_parser2}"; } public sealed class Sequence : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -91,8 +91,6 @@ Parser lastParser ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -134,6 +132,8 @@ public override bool Parse(ParseContext context, ref ParseResult $"{_parser} & {_lastParser} "; + public SkippableCompilationResult[] BuildSkippableParsers(CompilationContext context) { if (_parser is not ISkippableSequenceParser sequenceParser) @@ -166,8 +166,6 @@ public Sequence(Parser> parser, Parser lastParser) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -224,6 +222,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} "; } public sealed class Sequence : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -242,8 +242,6 @@ public Sequence(Parser> parser, Parser lastParser ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -301,6 +299,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} "; } public sealed class Sequence : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -319,8 +319,6 @@ public Sequence(Parser> parser, Parser lastPa ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -380,6 +378,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} "; } public sealed class Sequence : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -398,8 +398,6 @@ public Sequence(Parser> parser, Parser la ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"And({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -460,4 +458,6 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} "; } diff --git a/src/Parlot/Fluent/SequenceAndSkip.cs b/src/Parlot/Fluent/SequenceAndSkip.cs index d109943..80f0261 100644 --- a/src/Parlot/Fluent/SequenceAndSkip.cs +++ b/src/Parlot/Fluent/SequenceAndSkip.cs @@ -22,8 +22,6 @@ public SequenceAndSkip(Parser parser1, Parser parser2) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser1.Name}, {parser2.Name})"; } public bool CanSeek { get; } @@ -129,6 +127,8 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser1} & (skip) {_parser2}"; } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -150,8 +150,6 @@ Parser lastParser ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -206,6 +204,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; + } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -224,8 +225,6 @@ public SequenceAndSkip(Parser> parser, Parser lastPar ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -281,6 +280,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -299,8 +300,6 @@ public SequenceAndSkip(Parser> parser, Parser las ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -357,6 +356,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; + } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -375,8 +377,6 @@ public SequenceAndSkip(Parser> parser, Parser ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -436,6 +436,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -454,8 +456,6 @@ public SequenceAndSkip(Parser> parser, Parser ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -464,7 +464,6 @@ public SequenceAndSkip(Parser> parser, Parser public bool SkipWhitespace { get; } - public override bool Parse(ParseContext context, ref ParseResult> result) { context.EnterParser(this); @@ -516,6 +515,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; + } public sealed class SequenceAndSkip : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -534,8 +536,6 @@ public SequenceAndSkip(Parser> parser, Pa ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"AndSkip({parser.Name}, {lastParser.Name})"; } public bool CanSeek { get; } @@ -544,7 +544,6 @@ public SequenceAndSkip(Parser> parser, Pa public bool SkipWhitespace { get; } - public override bool Parse(ParseContext context, ref ParseResult> result) { context.EnterParser(this); @@ -597,4 +596,7 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & (skip) {_lastParser}"; + } diff --git a/src/Parlot/Fluent/SequenceSkipAnd.cs b/src/Parlot/Fluent/SequenceSkipAnd.cs index d39eb50..89141a9 100644 --- a/src/Parlot/Fluent/SequenceSkipAnd.cs +++ b/src/Parlot/Fluent/SequenceSkipAnd.cs @@ -127,6 +127,8 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser1} & {_parser2} (skip)"; } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -205,6 +207,8 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -281,6 +285,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; + } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -358,6 +365,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; + } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -437,6 +447,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; + } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -517,6 +530,9 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; + } public sealed class SequenceSkipAnd : Parser>, ICompilable, ISkippableSequenceParser, ISeekable @@ -598,4 +614,7 @@ public CompilationResult Compile(CompilationContext context) { return SequenceCompileHelper.CreateSequenceCompileResult(BuildSkippableParsers(context), context); } + + public override string ToString() => $"{_parser} & {_lastParser} (skip)"; + } diff --git a/src/Parlot/Fluent/SkipWhiteSpace.cs b/src/Parlot/Fluent/SkipWhiteSpace.cs index a9f2d5b..46cbb62 100644 --- a/src/Parlot/Fluent/SkipWhiteSpace.cs +++ b/src/Parlot/Fluent/SkipWhiteSpace.cs @@ -13,8 +13,6 @@ public SkipWhiteSpace(Parser parser) { Parser = parser ?? throw new ArgumentNullException(nameof(parser)); - Name = $"{Name} (SkipWhiteSpace)"; - if (parser is ISeekable seekable) { CanSeek = seekable.CanSeek; @@ -88,4 +86,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{Parser} (Skip WS)"; } diff --git a/src/Parlot/Fluent/Switch.cs b/src/Parlot/Fluent/Switch.cs index 2460cd4..87123a7 100644 --- a/src/Parlot/Fluent/Switch.cs +++ b/src/Parlot/Fluent/Switch.cs @@ -20,8 +20,6 @@ public Switch(Parser previousParser, Func> action) { _previousParser = previousParser ?? throw new ArgumentNullException(nameof(previousParser)); _action = action ?? throw new ArgumentNullException(nameof(action)); - - Name = $"{previousParser.Name} (Switch)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -116,4 +114,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_previousParser} (Switch)"; } diff --git a/src/Parlot/Fluent/TextBefore.cs b/src/Parlot/Fluent/TextBefore.cs index 9098ca5..0f3600b 100644 --- a/src/Parlot/Fluent/TextBefore.cs +++ b/src/Parlot/Fluent/TextBefore.cs @@ -43,8 +43,6 @@ public TextBefore(Parser delimiter, bool canBeEmpty = false, bool failOnEof = #endif _canJumpToNextExpectedChar = true; } - - Name = $"TextBefore({delimiter.Name})"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -272,4 +270,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"TextBefore({_delimiter})"; } diff --git a/src/Parlot/Fluent/TextLiteral.cs b/src/Parlot/Fluent/TextLiteral.cs index 04b88c8..846ed59 100644 --- a/src/Parlot/Fluent/TextLiteral.cs +++ b/src/Parlot/Fluent/TextLiteral.cs @@ -14,8 +14,6 @@ public sealed class TextLiteral : Parser, ICompilable, ISeekable public TextLiteral(string text, StringComparison comparisonType) { - Name = $"TextLiteral(\"{text}\")"; - Text = text ?? throw new ArgumentNullException(nameof(text)); _comparisonType = comparisonType; _hasNewLines = text.Any(Character.IsNewLine); @@ -114,4 +112,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"Text(\"{Text}\")"; } diff --git a/src/Parlot/Fluent/Then.cs b/src/Parlot/Fluent/Then.cs index e3d7d8f..24b36f7 100644 --- a/src/Parlot/Fluent/Then.cs +++ b/src/Parlot/Fluent/Then.cs @@ -29,8 +29,6 @@ private Then(Parser parser) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"{parser.Name} (Then)"; } public Then(Parser parser, Func action) : this(parser) @@ -137,4 +135,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + override public string ToString() => $"{_parser} (Then)"; } diff --git a/src/Parlot/Fluent/When.cs b/src/Parlot/Fluent/When.cs index dfb2bf1..8472bdc 100644 --- a/src/Parlot/Fluent/When.cs +++ b/src/Parlot/Fluent/When.cs @@ -21,16 +21,12 @@ public When(Parser parser, Func action) { _action = action != null ? (c, t) => action(t) : throw new ArgumentNullException(nameof(action)); _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - Name = $"{parser.Name} (When)"; } public When(Parser parser, Func action) { _action = action ?? throw new ArgumentNullException(nameof(action)); _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - Name = $"{parser.Name} (When)"; } public override bool Parse(ParseContext context, ref ParseResult result) @@ -99,4 +95,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser} (When)"; } diff --git a/src/Parlot/Fluent/ZeroOrMany.cs b/src/Parlot/Fluent/ZeroOrMany.cs index 17612ac..087e798 100644 --- a/src/Parlot/Fluent/ZeroOrMany.cs +++ b/src/Parlot/Fluent/ZeroOrMany.cs @@ -23,8 +23,6 @@ public ZeroOrMany(Parser parser) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"ZeroOrMany({parser.Name})"; } public bool CanSeek { get; } @@ -143,4 +141,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser}*"; } diff --git a/src/Parlot/Fluent/ZeroOrOne.cs b/src/Parlot/Fluent/ZeroOrOne.cs index d66cf28..09d8ba1 100644 --- a/src/Parlot/Fluent/ZeroOrOne.cs +++ b/src/Parlot/Fluent/ZeroOrOne.cs @@ -20,8 +20,6 @@ public ZeroOrOne(Parser parser, T defaultValue) ExpectedChars = seekable.ExpectedChars; SkipWhitespace = seekable.SkipWhitespace; } - - Name = $"ZeroOrOne({parser.Name})"; } public bool CanSeek { get; } @@ -76,4 +74,6 @@ public CompilationResult Compile(CompilationContext context) return result; } + + public override string ToString() => $"{_parser}?"; }