diff --git a/ParserTests/EBNFTests.cs b/ParserTests/EBNFTests.cs index ecd38ce6..9635ff41 100644 --- a/ParserTests/EBNFTests.cs +++ b/ParserTests/EBNFTests.cs @@ -220,7 +220,16 @@ public string Choice(List> list) return string.Join(",",list.Select(x => x.Value)); } } - + + public class AlternateChoiceTestOneOrMoreTerminal + { + [Production("choice : [ a | b | c]+")] + public string Choice(List> list) + { + return string.Join(",", list.Select(x => x.Value)); + } + } + public class AlternateChoiceTestOptionTerminal { [Production("choice : [ a | b | c] [ b | c]?")] @@ -310,7 +319,35 @@ public string A(Token t) } } - + + public class AlternateChoiceTestOneOrMoreNonTerminal + { + [Production("choice : [ A | B | C]+")] + public string Choice(List choices) + { + return string.Join(" ", choices); + } + + [Production("C : c")] + public string C(Token t) + { + return $"C({t.Value})"; + } + + [Production("B : b")] + public string B(Token t) + { + return $"B({t.Value})"; + } + + [Production("A : a")] + public string A(Token t) + { + return $"A({t.Value})"; + } + + } + public class Bugfix104Test { [Production("testNonTerm : sub (COMMA[d] unreachable)? ")] @@ -866,7 +903,29 @@ public void TestAlternateChoiceNonTerminal() parseResult = builtParser.Result.Parse("d", "choice"); Assert.False(parseResult.IsOk); } - + + [Fact] + public void TestAlternateChoiceOneOrMoreNonTerminal() + { + var startingRule = $"choice"; + var parserInstance = new AlternateChoiceTestOneOrMoreNonTerminal(); + var builder = new ParserBuilder(); + var builtParser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); + Assert.False(builtParser.IsError); + Assert.False(builtParser.Errors.Any()); + var parseResult = builtParser.Result.Parse("a b", "choice"); + Assert.True(parseResult.IsOk); + Assert.Equal("A(a) B(b)", parseResult.Result); + parseResult = builtParser.Result.Parse("b", "choice"); + Assert.True(parseResult.IsOk); + Assert.Equal("B(b)", parseResult.Result); + parseResult = builtParser.Result.Parse("c", "choice"); + Assert.True(parseResult.IsOk); + Assert.Equal("C(c)", parseResult.Result); + parseResult = builtParser.Result.Parse("d", "choice"); + Assert.False(parseResult.IsOk); + } + [Fact] public void TestAlternateChoiceZeroOrMoreTerminal() { @@ -882,7 +941,23 @@ public void TestAlternateChoiceZeroOrMoreTerminal() parseResult = builtParser.Result.Parse("b", "choice"); Assert.True(parseResult.IsOk); } - + + [Fact] + public void TestAlternateChoiceOneOrMoreTerminal() + { + var startingRule = $"choice"; + var parserInstance = new AlternateChoiceTestOneOrMoreTerminal(); + var builder = new ParserBuilder(); + var builtParser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); + Assert.False(builtParser.IsError); + Assert.False(builtParser.Errors.Any()); + var parseResult = builtParser.Result.Parse("a b c", "choice"); + Assert.True(parseResult.IsOk); + Assert.Equal("a,b,c", parseResult.Result); + parseResult = builtParser.Result.Parse("b", "choice"); + Assert.True(parseResult.IsOk); + } + [Fact] public void TestAlternateChoiceOptionTerminal() { diff --git a/sly/parser/generator/ParserBuilder.cs b/sly/parser/generator/ParserBuilder.cs index a0589ca9..439914d9 100644 --- a/sly/parser/generator/ParserBuilder.cs +++ b/sly/parser/generator/ParserBuilder.cs @@ -289,8 +289,10 @@ private static bool NonTerminalReferences(NonTerminal nonTerminal, string re } else if (clause is OneOrMoreClause oneOrMore) { - if (oneOrMore.Clause is NonTerminalClause inner) - found = inner.NonTerminalName == referenceName; + if (oneOrMore.Clause is NonTerminalClause innerNonTerminal) + found = innerNonTerminal.NonTerminalName == referenceName; + if (oneOrMore.Clause is ChoiceClause innerChoice && innerChoice.IsNonTerminalChoice) + found = innerChoice.Choices.Where(c => (c as NonTerminalClause).NonTerminalName == referenceName).Any(); } else if (clause is ChoiceClause choice) { diff --git a/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs b/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs index ba90bfa0..8b69f862 100644 --- a/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs +++ b/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs @@ -368,6 +368,12 @@ public SyntaxParseResult ParseOneOrMore(IList> tokens, OneOrMoreCl manyNode.IsManyValues = true; firstInnerResult = ParseNonTerminal(tokens, innerClause as NonTerminalClause, currentPosition); } + else if (innerClause is ChoiceClause choice) + { + manyNode.IsManyTokens = choice.IsTerminalChoice; + manyNode.IsManyValues = choice.IsNonTerminalChoice; + firstInnerResult = ParseChoice(tokens, choice, currentPosition); + } else { throw new InvalidOperationException("unable to apply repeater to " + innerClause.GetType().Name);