diff --git a/Canon.Core/GrammarParser/Expression.cs b/Canon.Core/GrammarParser/Expression.cs
index 096163f..980f0ef 100644
--- a/Canon.Core/GrammarParser/Expression.cs
+++ b/Canon.Core/GrammarParser/Expression.cs
@@ -1,17 +1,29 @@
namespace Canon.Core.GrammarParser;
///
-/// An expression in the LR, like 'program_struct -> ~program_head ; program_body.'
-/// The '~' is the shift position now.
+/// LR语法中的一个表达式,例如 'program_struct -> ~program_head ; program_body'
+/// 其中'~'标识当前移进到达的位置
///
public class Expression : IEquatable
{
+ ///
+ /// 表达式的左部
+ ///
public required NonTerminator Left { get; init; }
+ ///
+ /// 表达式的向前看字符串
+ ///
public required Terminator LookAhead { get; init; }
+ ///
+ /// 表达式的右部
+ ///
public required List Right { get; init; }
+ ///
+ /// 当前移进的位置
+ ///
public int Pos { get; set; }
public bool Equals(Expression? other)
diff --git a/Canon.Core/GrammarParser/LrState.cs b/Canon.Core/GrammarParser/LrState.cs
new file mode 100644
index 0000000..b5ffb65
--- /dev/null
+++ b/Canon.Core/GrammarParser/LrState.cs
@@ -0,0 +1,66 @@
+namespace Canon.Core.GrammarParser;
+
+///
+/// LR语法中的一个项目集规范族
+/// 也就是自动机中的一个状态
+///
+public class LrState : IEquatable
+{
+ ///
+ /// 项目集规范族
+ ///
+ public required HashSet Expressions { get; init; }
+
+ ///
+ /// 自动机的迁移规则
+ ///
+ public Dictionary Transformer { get; } = [];
+
+ public bool Equals(LrState? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ if (Expressions.Count != other.Expressions.Count)
+ {
+ return false;
+ }
+
+ // 如果两个集合的大小相等,且一个是另一个的子集,那么两个集合相等。
+ return Expressions.IsSubsetOf(other.Expressions);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is not LrState other)
+ {
+ return false;
+ }
+
+ return Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ int hash = 0;
+
+ foreach (Expression expression in Expressions)
+ {
+ hash ^= expression.GetHashCode();
+ }
+
+ return hash;
+ }
+
+ public static bool operator ==(LrState a, LrState b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(LrState a, LrState b)
+ {
+ return !a.Equals(b);
+ }
+}