diff --git a/src/samples/indented/IndentedParser2.cs b/src/samples/indented/IndentedParser2.cs index 07a92f1c..8bdcb337 100644 --- a/src/samples/indented/IndentedParser2.cs +++ b/src/samples/indented/IndentedParser2.cs @@ -30,7 +30,7 @@ public Ast integer(Token tok) [Production("statement: [set|ifthenelse]? EOL[d]")] public Ast Statement(ValueOption stat) { - return stat.Match( + return stat.Match( x => x as Statement, () => new EmptyLine() ); diff --git a/src/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs b/src/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs index d37e5c3d..9e0b228e 100644 --- a/src/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs +++ b/src/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs @@ -46,10 +46,6 @@ public OUT Visit(ISyntaxNode n) return Visit(node); case SyntaxNode node: return Visit(node); - case SyntaxEpsilon epsilon: - { - return Visitor.VisitEpsilon(); - } default: return Visitor.VisitLeaf(new Token() {TokenID = default(IN),SpanValue="NULL".ToCharArray()}); } diff --git a/src/sly/parser/generator/visitor/EBNFSyntaxTreeVisitor.cs b/src/sly/parser/generator/visitor/EBNFSyntaxTreeVisitor.cs index eb6856b6..8f0b264a 100644 --- a/src/sly/parser/generator/visitor/EBNFSyntaxTreeVisitor.cs +++ b/src/sly/parser/generator/visitor/EBNFSyntaxTreeVisitor.cs @@ -29,8 +29,6 @@ protected override SyntaxVisitorResult Visit(ISyntaxNode n, object return Visit(node, context); case SyntaxNode node: return Visit(node, context); - case SyntaxEpsilon epsilon: - return SyntaxVisitorResult.Epsilon(); default: return null; } @@ -74,8 +72,6 @@ private SyntaxVisitorResult Visit(OptionSyntaxNode node, object con var innerResult = Visit(child, context); switch (child) { - case SyntaxEpsilon epsilon: - return SyntaxVisitorResult.NewOptionNone(); case SyntaxLeaf leaf: return SyntaxVisitorResult.NewToken(leaf.Token); case GroupSyntaxNode group: @@ -131,10 +127,6 @@ private SyntaxVisitorResult Visit(SyntaxNode node, object context = { args.Add(v.GroupListResult); } - else if (v.IsEpsilon) - { - args.Add(default(OUT)); - } } if (node.IsByPassNode) diff --git a/src/sly/parser/generator/visitor/SyntaxTreeVisitor.cs b/src/sly/parser/generator/visitor/SyntaxTreeVisitor.cs index c3b7e242..e67fbe03 100644 --- a/src/sly/parser/generator/visitor/SyntaxTreeVisitor.cs +++ b/src/sly/parser/generator/visitor/SyntaxTreeVisitor.cs @@ -128,12 +128,6 @@ public static SyntaxVisitorResult NoneResult() return res; } - public static SyntaxVisitorResult Epsilon() - { - var res = new SyntaxVisitorResult(); - res.IsEpsilon = true; - return res; - } } public class SyntaxTreeVisitor where IN : struct diff --git a/src/sly/parser/parser/ValueOption.cs b/src/sly/parser/parser/ValueOption.cs index 9a6ae442..544f8936 100644 --- a/src/sly/parser/parser/ValueOption.cs +++ b/src/sly/parser/parser/ValueOption.cs @@ -44,7 +44,7 @@ public ValueOption(T value) public bool IsNone { get; } public bool IsSome => !IsNone; - public T Match(Func some, Func none) + public X Match(Func some, Func none) { if (IsSome) return some(Value); diff --git a/src/sly/parser/parser/llparser/ebnf/EBNFRecursiveDescentSyntaxParser.Option.cs b/src/sly/parser/parser/llparser/ebnf/EBNFRecursiveDescentSyntaxParser.Option.cs index e2efbe28..1a952226 100644 --- a/src/sly/parser/parser/llparser/ebnf/EBNFRecursiveDescentSyntaxParser.Option.cs +++ b/src/sly/parser/parser/llparser/ebnf/EBNFRecursiveDescentSyntaxParser.Option.cs @@ -61,10 +61,13 @@ public SyntaxParseResult ParseOption(IList> tokens, OptionClause(); - result.IsError = false; - result.Root = new SyntaxEpsilon(); - result.EndingPosition = position; + result = new SyntaxParseResult + { + IsError = false, + Root = new OptionSyntaxNode(rule.NonTerminalName, new List>(), + rule.GetVisitor()), + EndingPosition = position + }; } break; diff --git a/src/sly/parser/syntax/grammar/ChoiceClause.cs b/src/sly/parser/syntax/grammar/ChoiceClause.cs index 7aec0277..152c2adc 100644 --- a/src/sly/parser/syntax/grammar/ChoiceClause.cs +++ b/src/sly/parser/syntax/grammar/ChoiceClause.cs @@ -46,6 +46,7 @@ public string Dump() return ToString(); } + [ExcludeFromCodeCoverage] public bool Equals(IClause clause) { if (clause is ChoiceClause other) @@ -55,7 +56,8 @@ public bool Equals(IClause clause) return false; } - + + [ExcludeFromCodeCoverage] private bool Equals(ChoiceClause other) { if (other.Choices.Count != Choices.Count) @@ -71,6 +73,7 @@ private bool Equals(ChoiceClause other) return other.Choices.TrueForAll(x => Choices.Exists(y => y.Equals(x))); } + [ExcludeFromCodeCoverage] public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; @@ -79,6 +82,7 @@ public override bool Equals(object obj) return Equals((ChoiceClause)obj); } + [ExcludeFromCodeCoverage] public override int GetHashCode() { return Dump().GetHashCode(); diff --git a/src/sly/parser/syntax/grammar/ClauseSequence.cs b/src/sly/parser/syntax/grammar/ClauseSequence.cs index a8a1cdd6..b75f029f 100644 --- a/src/sly/parser/syntax/grammar/ClauseSequence.cs +++ b/src/sly/parser/syntax/grammar/ClauseSequence.cs @@ -36,6 +36,7 @@ public string Dump() return Clauses.Select(c => c.Dump()).Aggregate((d1, d2) => d1 + " " + d2); } + [ExcludeFromCodeCoverage] public bool Equals(IClause other) { if (other is ClauseSequence sequence && sequence.Clauses.Count == Clauses.Count) diff --git a/src/sly/parser/syntax/grammar/GroupClause.cs b/src/sly/parser/syntax/grammar/GroupClause.cs index b89ab01a..92caeb7d 100644 --- a/src/sly/parser/syntax/grammar/GroupClause.cs +++ b/src/sly/parser/syntax/grammar/GroupClause.cs @@ -43,6 +43,7 @@ public string Dump() } + [ExcludeFromCodeCoverage] public bool Equals(IClause other) { if (other is GroupClause group && group.Clauses.Count == Clauses.Count) diff --git a/src/sly/parser/syntax/grammar/NonTerminalClause.cs b/src/sly/parser/syntax/grammar/NonTerminalClause.cs index 32df3b77..013c2fab 100644 --- a/src/sly/parser/syntax/grammar/NonTerminalClause.cs +++ b/src/sly/parser/syntax/grammar/NonTerminalClause.cs @@ -32,6 +32,7 @@ public string Dump() return $"{NonTerminalName}(NT)"; } + [ExcludeFromCodeCoverage] public bool Equals(IClause clause) { if (clause is NonTerminalClause other) @@ -42,11 +43,13 @@ public bool Equals(IClause clause) return false; } + [ExcludeFromCodeCoverage] private bool Equals(NonTerminalClause other) { return NonTerminalName == other.NonTerminalName && IsGroup == other.IsGroup; } + [ExcludeFromCodeCoverage] public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; @@ -55,6 +58,7 @@ public override bool Equals(object obj) return Equals((NonTerminalClause)obj); } + [ExcludeFromCodeCoverage] public override int GetHashCode() { return Dump().GetHashCode(); diff --git a/src/sly/parser/syntax/grammar/OneOrMoreClause.cs b/src/sly/parser/syntax/grammar/OneOrMoreClause.cs index 4deee342..0b50f319 100644 --- a/src/sly/parser/syntax/grammar/OneOrMoreClause.cs +++ b/src/sly/parser/syntax/grammar/OneOrMoreClause.cs @@ -27,6 +27,7 @@ public override string Dump() return Clause.Dump()+"+"; } + [ExcludeFromCodeCoverage] public override bool Equals(IClause other) { if (other is OneOrMoreClause otherOneOrMore) diff --git a/src/sly/parser/syntax/grammar/OptionClause.cs b/src/sly/parser/syntax/grammar/OptionClause.cs index 4b8b372d..83d2740d 100644 --- a/src/sly/parser/syntax/grammar/OptionClause.cs +++ b/src/sly/parser/syntax/grammar/OptionClause.cs @@ -31,6 +31,7 @@ public string Dump() return $"{Clause}?"; } + [ExcludeFromCodeCoverage] public bool Equals(IClause clause) { if (clause is OptionClause other) @@ -39,12 +40,14 @@ public bool Equals(IClause clause) } return false; } - + + [ExcludeFromCodeCoverage] private bool Equals(OptionClause other) { return Equals(Clause, other.Clause); } + [ExcludeFromCodeCoverage] public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; @@ -53,6 +56,7 @@ public override bool Equals(object obj) return Equals((OptionClause)obj); } + [ExcludeFromCodeCoverage] public override int GetHashCode() { return (Clause != null ? Clause.GetHashCode() : 0); diff --git a/src/sly/parser/syntax/grammar/ZeroOrMoreClause.cs b/src/sly/parser/syntax/grammar/ZeroOrMoreClause.cs index 8011f75c..e48b0d70 100644 --- a/src/sly/parser/syntax/grammar/ZeroOrMoreClause.cs +++ b/src/sly/parser/syntax/grammar/ZeroOrMoreClause.cs @@ -28,6 +28,7 @@ public override string Dump() return t; } + [ExcludeFromCodeCoverage] public override bool Equals(IClause other) { if (other is OneOrMoreClause otherOneOrMore) diff --git a/src/sly/parser/syntax/tree/OptionSyntaxNode.cs b/src/sly/parser/syntax/tree/OptionSyntaxNode.cs index d7f80202..9d9c1e22 100644 --- a/src/sly/parser/syntax/tree/OptionSyntaxNode.cs +++ b/src/sly/parser/syntax/tree/OptionSyntaxNode.cs @@ -6,7 +6,7 @@ namespace sly.parser.syntax.tree public class OptionSyntaxNode : SyntaxNode where IN : struct { public bool IsGroupOption { get; set; } = false; - + public OptionSyntaxNode(string name, List> children = null, MethodInfo visitor = null) : base( name, children, visitor) { } diff --git a/src/sly/parser/syntax/tree/SyntaxEpsilon.cs b/src/sly/parser/syntax/tree/SyntaxEpsilon.cs deleted file mode 100644 index 7bb19846..00000000 --- a/src/sly/parser/syntax/tree/SyntaxEpsilon.cs +++ /dev/null @@ -1,26 +0,0 @@ -using sly.lexer; -using System.Diagnostics.CodeAnalysis; - -namespace sly.parser.syntax.tree -{ - public class SyntaxEpsilon : ISyntaxNode where IN : struct - { - public bool IsEpsilon => true; - public bool Discarded { get; } = false; - public string Name => "Epsilon"; - - public bool HasByPassNodes { get; set; } = false; - - [ExcludeFromCodeCoverage] - public string Dump(string tab) - { - return $"Epsilon"; - } - - [ExcludeFromCodeCoverage] - public string ToJson(int index = 0) - { - return $@"""{index}.Epsilon"":""e"""; - } - } -} diff --git a/tests/ParserTests/EBNFTests.cs b/tests/ParserTests/EBNFTests.cs index e3f67f12..fb81ccc1 100644 --- a/tests/ParserTests/EBNFTests.cs +++ b/tests/ParserTests/EBNFTests.cs @@ -313,12 +313,12 @@ public string rootOption(Token a, ValueOption { builder.Append($";"); - return null; + return builder.ToString(); }); builder.Append(")"); return builder.ToString(); @@ -413,6 +413,51 @@ public string Choice(Token first, Token next) } } + + public class AlternateChoiceTestOptionNonTerminal + { + [Production("choice : [ A | B | C] [ B | C]? F?")] + public string Choice(string first, ValueOption next, ValueOption final) + { + StringBuilder builder = new StringBuilder(); + builder.Append(first); + + + var v = next.Match(x => x, + () => ""); + builder.Append($",{v}"); + v = final.Match(x => x, + () => ""); + builder.Append($",{v}"); + + return builder.ToString(); + } + + [Production("A : a")] + public string A(Token a) + { + return a.Value; + } + + [Production("B : b")] + public string B(Token b) + { + return b.Value; + } + + [Production("C : c")] + public string C(Token c) + { + return c.Value; + } + + [Production("F : f")] + public string F(Token f) + { + return f.Value; + } + } + public class AlternateChoiceTestOptionDiscardedTerminal { [Production("choice : [ a | b | c] [ b | c] [d]")] @@ -1209,6 +1254,30 @@ public void TestAlternateChoiceOptionTerminal() Check.That(parseResult.Result).IsEqualTo("a,"); } + [Fact] + public void TestAlternateChoiceOptionNonTerminal() + { + var startingRule = $"choice"; + var parserInstance = new AlternateChoiceTestOptionNonTerminal(); + var builder = new ParserBuilder(); + var builtParser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); + Check.That(builtParser.IsError).IsFalse(); + Check.That(builtParser.Errors).IsEmpty(); + var parseResult = builtParser.Result.Parse("a b f", "choice"); + Check.That(parseResult.IsOk).IsTrue(); + Check.That(parseResult.Result).IsEqualTo("a,b,f"); + parseResult = builtParser.Result.Parse("a", "choice"); + Check.That(parseResult.IsOk).IsTrue(); + Check.That(parseResult.Result).IsEqualTo("a,,"); + parseResult = builtParser.Result.Parse("a b ", "choice"); + Check.That(parseResult.IsOk).IsTrue(); + Check.That(parseResult.Result).IsEqualTo("a,b,"); + parseResult = builtParser.Result.Parse("a f", "choice"); + Check.That(parseResult.IsOk).IsTrue(); + Check.That(parseResult.Result).IsEqualTo("a,,f"); + + } + [Fact] public void TestAlternateChoiceOptionDiscardedTerminal() {