fix: 求解FirstSet中没有考虑空产生式 (#36)

Reviewed-on: PostGuard/Canon#36
This commit is contained in:
jackfiled 2024-04-13 12:37:17 +08:00
parent 67deb0aa2c
commit 792c1d44f1
4 changed files with 704 additions and 632 deletions

View File

@ -19,6 +19,61 @@ public class GrammarBuilder
public HashSet<LrState> Automation { get; } = []; public HashSet<LrState> Automation { get; } = [];
/// <summary>
/// 向指定非终结符的FirstSet中添加指定符号的FirstSet
/// </summary>
/// <param name="target">指定的非终结符</param>
/// <param name="t">指定的符号</param>
/// <param name="changed">标记是否改变了FirstSet</param>
private void AddFirstSetOfTerminatorBase(NonTerminator target, TerminatorBase t, ref bool changed)
{
if (t.IsTerminated)
{
Terminator terminator = (Terminator)t;
if (FirstSet.TryGetValue(target, out HashSet<Terminator>? firstSet))
{
if (firstSet.Add(terminator))
{
changed = true;
}
}
else
{
FirstSet.Add(target, [terminator]);
changed = true;
}
}
else
{
NonTerminator nonTerminator = (NonTerminator)t;
if (!FirstSet.TryGetValue(nonTerminator, out HashSet<Terminator>? firstSet))
{
return;
}
if (!FirstSet.ContainsKey(target))
{
FirstSet.Add(target, []);
changed = true;
}
foreach (Terminator i in firstSet)
{
if (i == Terminator.EmptyTerminator)
{
continue;
}
if (FirstSet[target].Add(i))
{
changed = true;
}
}
}
}
/// <summary> /// <summary>
/// 构建文法中所有非终结符的First集合 /// 构建文法中所有非终结符的First集合
/// </summary> /// </summary>
@ -34,53 +89,44 @@ public class GrammarBuilder
{ {
foreach (List<TerminatorBase> expression in pair.Value) foreach (List<TerminatorBase> expression in pair.Value)
{ {
// TODO: 对于空产生式直接跳过处理是正确的吗 TerminatorBase expressionHead = expression.First();
TerminatorBase? expressionHead = expression.FirstOrDefault(); AddFirstSetOfTerminatorBase(pair.Key, expressionHead, ref changed);
if (expressionHead is null)
// 处理空产生式
for (int i = 0; i < expression.Count; i++)
{ {
continue; if (!expression[i].IsTerminated)
{
NonTerminator nonTerminator = (NonTerminator)expression[i];
// 可以推出空产生式
// 则将下一个符号的FirstSet加入该符号的集合中
if (!FirstSet.TryGetValue(nonTerminator, out HashSet<Terminator>? firstSet))
{
break;
} }
if (!firstSet.Contains(Terminator.EmptyTerminator))
if (expressionHead.IsTerminated)
{ {
// 产生式的第一个字符是终结符 break;
// 将这个终结符加入该非终结符的First集合 }
Terminator terminator = (Terminator)expressionHead;
if (FirstSet.TryAdd(pair.Key, [terminator])) if (i + 1 < expression.Count)
{ {
changed = true; // 还有下一个符号
// 就把下一个符号的FirstSet加入
AddFirstSetOfTerminatorBase(pair.Key, expression[i + 1], ref changed);
} }
else else
{ {
if (FirstSet[pair.Key].Add(terminator)) // 没有下一个符号了
{ // 就需要加入空串
changed = true; AddFirstSetOfTerminatorBase(pair.Key, Terminator.EmptyTerminator, ref changed);
}
} }
} }
else else
{ {
NonTerminator nonTerminator = (NonTerminator)expressionHead; break;
// 产生式的第一个字符是非终结符
// 将该非终结符的结果合并到该
if (FirstSet.TryGetValue(nonTerminator, out HashSet<Terminator>? value))
{
foreach (Terminator first in value)
{
if (FirstSet.TryAdd(pair.Key, [first]))
{
changed = true;
}
else
{
if (FirstSet[pair.Key].Add(first))
{
changed = true;
}
}
}
} }
} }
} }

View File

@ -59,4 +59,15 @@ public class GenerateParserTests
} }
} }
} }
[Fact]
public void SubprogramDeclarationsFirstSetTest()
{
Assert.True(_builder.FirstSet.TryGetValue(
new NonTerminator(NonTerminatorType.SubprogramDeclarations), out HashSet<Terminator>? firstSet));
Assert.NotNull(firstSet);
Assert.Contains(Terminator.EmptyTerminator, firstSet);
Assert.Contains(new Terminator(KeywordType.Procedure), firstSet);
Assert.Contains(new Terminator(KeywordType.Function), firstSet);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -68,14 +68,23 @@ public class PascalGrammarTests
Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue); Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
} }
private static IGrammarParser GenerateGrammarParser() [Fact]
public void SubprogramTest()
{ {
GrammarBuilder builder = new() const string program = """
{ program main;
Generators = PascalGrammar.Grammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator) procedure test;
}; begin
end;
begin
end.
""";
Grammar grammar = builder.Build(); Lexer lexer = new(program);
return grammar.ToGrammarParser(); List<SemanticToken> tokens = lexer.Tokenize();
tokens.Add(SemanticToken.End);
ProgramStruct root = _parser.Analyse(tokens);
Assert.Equal("main", root.Head.ProgramName.LiteralValue);
} }
} }