Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
b3b00 committed Nov 15, 2024
1 parent eb0af21 commit 65601d2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 7 deletions.
3 changes: 2 additions & 1 deletion src/samples/ParserExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ public static object Rec(List<object> args)
private static void TestIssue507()
{
var tests = new EBNFTests();
tests.TestIssue507TransitiveEmptyStarter();
//tests.TestIssue507TransitiveEmptyStarter();
tests.TestIssue507MoreTransitiveEmptyStarter();
}

private static void BenchSimpleExpression()
Expand Down
26 changes: 26 additions & 0 deletions src/sly/parser/generator/EBNFParserBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public override BuildResult<Parser<IN, OUT>> BuildParser(object parserInstance,
configuration.AutoCloseIndentations = autoCloseIndentations;
LeftRecursionChecker<IN, OUT> recursionChecker = new LeftRecursionChecker<IN, OUT>();

SetEmptyNonTerminals(configuration);
// check left recursion.
var (foundRecursion, recursions) = LeftRecursionChecker<IN, OUT>.CheckLeftRecursion(configuration);
if (foundRecursion)
Expand Down Expand Up @@ -118,6 +119,31 @@ protected override ISyntaxParser<IN, OUT> BuildSyntaxParser(ParserConfiguration<
return parser;
}

private void SetEmptyNonTerminals(ParserConfiguration<IN, OUT> conf)
{
bool stillSetting = true;
while (stillSetting)
{
stillSetting = false;
foreach (var nt in conf.NonTerminals)
{
foreach (var rule in nt.Value.Rules)
{
foreach (var clause in rule.Clauses)
{
if (clause is NonTerminalClause<IN> nonTerminalClause)
{
var rules = conf.GetRulesForNonTerminal(nonTerminalClause.NonTerminalName);
stillSetting |= nonTerminalClause.SetMayBeEmpty(rules.Exists(x => x.MayBeEmpty));
}
}
}
}
}
}




#region configuration

Expand Down
9 changes: 9 additions & 0 deletions src/sly/parser/generator/ParserConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ public List<TerminalClause<IN>> GetAllExplicitTokenClauses()
return clauses;
}

public List<Rule<IN>> GetRulesForNonTerminal(string nonTerminal)
{
if (NonTerminals.TryGetValue(nonTerminal, out NonTerminal<IN> nonTerminalConfig))
{
return nonTerminalConfig.Rules.ToList();
}
return new List<Rule<IN>>();
}

[ExcludeFromCodeCoverage]
public string Dump()
{
Expand Down
13 changes: 10 additions & 3 deletions src/sly/parser/syntax/grammar/NonTerminalClause.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace sly.parser.syntax.grammar
Expand All @@ -14,10 +13,18 @@ public NonTerminalClause(string name)

public bool IsGroup { get; set; } = false;

private bool _mayBeEmpty = false;

public bool MayBeEmpty()
{
// TODO : issue #507 non terminal may be empty if at least 1 rule may be empty
return false;
return _mayBeEmpty;
}

public bool SetMayBeEmpty(bool mayBeEmpty)
{
bool setted = mayBeEmpty && !_mayBeEmpty;
_mayBeEmpty = mayBeEmpty;
return setted;
}


Expand Down
56 changes: 53 additions & 3 deletions tests/ParserTests/EBNFTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,38 @@ public string Z(Token<OptionTestToken> a)
return a.Value;
}
}

public class Issue507MoreTransitiveEmptyStarterParser
{
[Production("x : w")]
public string X(string w)
{
return w;
}

[Production("w : y")]
public string W(string y)
{
return y;
}

[Production("y : z*")]
public string Y(List<string> zs)
{
if (zs.Any())
{
return string.Join(",", zs);
}

return "empty";
}

[Production("z : a")]
public string Z(Token<OptionTestToken> a)
{
return a.Value;
}
}

public class Bugfix104Test
{
Expand Down Expand Up @@ -1362,9 +1394,27 @@ public void TestIssue507TransitiveEmptyStarter()
Check.That(parserResultNotEmpty).IsOkParsing();
Check.That(parserResultNotEmpty.Result).IsEqualTo("a,a,a");

var parserResulttEmpty = parser.Parse("");
Check.That(parserResulttEmpty).IsOkParsing();
Check.That(parserResulttEmpty.Result).IsEqualTo("empty");
var parserResultEmpty = parser.Parse("");
Check.That(parserResultEmpty).IsOkParsing();
Check.That(parserResultEmpty.Result).IsEqualTo("empty");
}

[Fact]
public void TestIssue507MoreTransitiveEmptyStarter()
{
var startingRule = $"x";
var parserInstance = new Issue507MoreTransitiveEmptyStarterParser();
var builder = new ParserBuilder<OptionTestToken, string>();
var builtParser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule);
Check.That(builtParser).IsOk();
var parser = builtParser.Result;
var parserResultNotEmpty = parser.Parse("a a a");
Check.That(parserResultNotEmpty).IsOkParsing();
Check.That(parserResultNotEmpty.Result).IsEqualTo("a,a,a");

var parserResultEmpty = parser.Parse("");
Check.That(parserResultEmpty).IsOkParsing();
Check.That(parserResultEmpty.Result).IsEqualTo("empty");
}

[Fact]
Expand Down

0 comments on commit 65601d2

Please sign in to comment.