diff --git a/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs
new file mode 100644
index 0000000..bbf58a1
--- /dev/null
+++ b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs
@@ -0,0 +1,129 @@
+using Canon.Core.Enums;
+using Canon.Core.GrammarParser;
+using Xunit.Abstractions;
+
+namespace Canon.Tests.GrammarParserTests;
+
+public class SimpleGrammarWithEmptyTests(ITestOutputHelper testOutputHelper)
+{
+ ///
+ /// 带有空产生式的简单语法(课后题4.18)
+ /// S -> A
+ /// A -> BA | ε
+ /// B -> aB | b
+ /// 为了方便测试指定
+ /// A ProgramStruct
+ /// B ProgramBody
+ /// a Identifier
+ /// b Identifier
+ ///
+ // private readonly ITestOutputHelper _testOutputHelper;
+
+ private readonly ITestOutputHelper _testOutputHelper = testOutputHelper;
+
+ private static readonly Dictionary>> s_simpleGrammar = new()
+ {
+ {
+ new NonTerminator(NonTerminatorType.StartNonTerminator), [
+ [new NonTerminator(NonTerminatorType.ProgramStruct)]
+ ]
+ },
+ {
+ new NonTerminator(NonTerminatorType.ProgramStruct), [
+ [
+ new NonTerminator(NonTerminatorType.ProgramBody),
+ new NonTerminator(NonTerminatorType.ProgramStruct)
+ ],
+ []
+ ]
+ },
+ {
+ new NonTerminator(NonTerminatorType.ProgramBody), [
+ [
+ Terminator.IdentifierTerminator,
+ new NonTerminator(NonTerminatorType.ProgramBody)
+ ],
+ [Terminator.IdentifierTerminator]
+ ]
+ }
+ };
+
+ [Fact]
+ public void FirstSetTest()
+ {
+ GrammarBuilder builder = new()
+ {
+ Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
+ };
+
+ builder.Build();
+
+ Assert.Contains(builder.FirstSet, pair =>
+ pair.Key == new NonTerminator(NonTerminatorType.StartNonTerminator));
+ Assert.Contains(builder.FirstSet, pair =>
+ pair.Key == new NonTerminator(NonTerminatorType.ProgramStruct));
+ Assert.Contains(builder.FirstSet, pair =>
+ pair.Key == new NonTerminator(NonTerminatorType.ProgramBody));
+
+ foreach (HashSet terminators in builder.FirstSet.Values)
+ {
+ Assert.Single(terminators);
+ Assert.Contains(Terminator.IdentifierTerminator, terminators);
+ }
+ }
+
+ [Fact]
+ public void StatsTest()
+ {
+ GrammarBuilder builder = new()
+ {
+ Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
+ };
+
+ Grammar grammar = builder.Build();
+
+ Assert.Equal(6, builder.Automation.Count);
+
+ Assert.Contains(new NonTerminator(NonTerminatorType.ProgramStruct), grammar.BeginState.Transformer.Keys);
+ Assert.Contains(new NonTerminator(NonTerminatorType.ProgramBody), grammar.BeginState.Transformer.Keys);
+ Assert.Contains(Terminator.IdentifierTerminator, grammar.BeginState.Transformer.Keys);
+ Assert.Equal(7, grammar.BeginState.Expressions.Count);
+ _testOutputHelper.WriteLine("--- 0 ---");
+ _testOutputHelper.WriteLine(grammar.BeginState.ToString());
+
+ LrState state1 =
+ grammar.BeginState.Transformer[new NonTerminator(NonTerminatorType.ProgramStruct)];
+ Assert.Single(state1.Expressions);
+ _testOutputHelper.WriteLine("--- 1 ---");
+ _testOutputHelper.WriteLine(state1.ToString());
+
+ LrState state2 =
+ grammar.BeginState.Transformer[new NonTerminator(NonTerminatorType.ProgramBody)];
+ Assert.Contains(new NonTerminator(NonTerminatorType.ProgramStruct), state2.Transformer.Keys);
+ Assert.Contains(new NonTerminator(NonTerminatorType.ProgramBody), state2.Transformer.Keys);
+ Assert.Contains(Terminator.IdentifierTerminator, state2.Transformer.Keys);
+ Assert.Equal(7, state2.Expressions.Count);
+ _testOutputHelper.WriteLine("--- 2 ---");
+ _testOutputHelper.WriteLine(state2.ToString());
+
+ LrState state3 =
+ grammar.BeginState.Transformer[Terminator.IdentifierTerminator];
+ Assert.Contains(new NonTerminator(NonTerminatorType.ProgramBody), state3.Transformer.Keys);
+ Assert.Contains(Terminator.IdentifierTerminator, state3.Transformer.Keys);
+ Assert.Equal(8, state3.Expressions.Count);
+ _testOutputHelper.WriteLine("--- 3 ---");
+ _testOutputHelper.WriteLine(state3.ToString());
+
+ LrState state4 = state2.Transformer[new NonTerminator(NonTerminatorType.ProgramStruct)];
+ Assert.Empty(state4.Transformer);
+ Assert.Single(state4.Expressions);
+ _testOutputHelper.WriteLine("--- 4 ---");
+ _testOutputHelper.WriteLine(state4.ToString());
+
+ LrState state5 = state3.Transformer[new NonTerminator(NonTerminatorType.ProgramBody)];
+ Assert.Empty(state5.Transformer);
+ Assert.Equal(2, state5.Expressions.Count);
+ _testOutputHelper.WriteLine("--- 5 ---");
+ _testOutputHelper.WriteLine(state5.ToString());
+ }
+}