add: nfa2dfa算法
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
namespace CanonSharp.Common.LexicalAnalyzer;
|
||||
|
||||
public class DeterministicState : IEquatable<DeterministicState>
|
||||
{
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
|
||||
public Dictionary<char, DeterministicState> Transaction { get; } = [];
|
||||
|
||||
public bool Equals(DeterministicState? other) => other is not null && Id.Equals(other.Id);
|
||||
|
||||
public override bool Equals(object? obj) => obj is DeterministicState other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
}
|
||||
|
||||
public class DeterministicFiniteAutomation
|
||||
{
|
||||
public DeterministicState Start { get; }
|
||||
|
||||
public HashSet<DeterministicState> FinalStates { get; }
|
||||
|
||||
private DeterministicFiniteAutomation(DeterministicState start, HashSet<DeterministicState> finalStates)
|
||||
{
|
||||
Start = start;
|
||||
FinalStates = finalStates;
|
||||
}
|
||||
|
||||
private record Pair(HashSet<NondeterministicState> States, DeterministicState State);
|
||||
|
||||
public static DeterministicFiniteAutomation Create(NondeterministicFiniteAutomation nfa)
|
||||
{
|
||||
Dictionary<NondeterministicStateSet, DeterministicState> map = [];
|
||||
HashSet<DeterministicState> visited = [];
|
||||
Queue<Pair> queue = [];
|
||||
HashSet<DeterministicState> finalStates = [];
|
||||
|
||||
HashSet<NondeterministicState> startClosure = nfa.Start.CalculateEmptyClosure();
|
||||
DeterministicState start = new();
|
||||
map.Add(new NondeterministicStateSet(startClosure), start);
|
||||
queue.Enqueue(new Pair(startClosure, start));
|
||||
|
||||
while (queue.TryDequeue(out Pair? pair))
|
||||
{
|
||||
if (pair.States.Any(s => nfa.FinalStates.Contains(s)))
|
||||
{
|
||||
finalStates.Add(pair.State);
|
||||
}
|
||||
|
||||
Dictionary<char, HashSet<NondeterministicState>> next = [];
|
||||
|
||||
foreach (NondeterministicState state in pair.States)
|
||||
{
|
||||
foreach (KeyValuePair<EmptyChar,HashSet<NondeterministicState>> transaction in
|
||||
state.Transactions.Where(p => !p.Key.IsEmpty))
|
||||
{
|
||||
HashSet<NondeterministicState> closure = [];
|
||||
|
||||
foreach (NondeterministicState s in transaction.Value)
|
||||
{
|
||||
closure.UnionWith(s.CalculateEmptyClosure());
|
||||
}
|
||||
|
||||
if (next.TryGetValue(transaction.Key.Char, out HashSet<NondeterministicState>? n))
|
||||
{
|
||||
n.UnionWith(closure);
|
||||
}
|
||||
next.Add(transaction.Key.Char, closure);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<char,HashSet<NondeterministicState>> transaction in next)
|
||||
{
|
||||
NondeterministicStateSet set = new(transaction.Value);
|
||||
if (!map.TryGetValue(set, out DeterministicState? nextState))
|
||||
{
|
||||
nextState = new DeterministicState();
|
||||
map.Add(set, nextState);
|
||||
}
|
||||
|
||||
pair.State.Transaction.Add(transaction.Key, nextState);
|
||||
|
||||
if (visited.Add(nextState))
|
||||
{
|
||||
queue.Enqueue(new Pair(transaction.Value, nextState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DeterministicFiniteAutomation(start, finalStates);
|
||||
}
|
||||
}
|
49
CanonSharp.Common/LexicalAnalyzer/EmptyChar.cs
Normal file
49
CanonSharp.Common/LexicalAnalyzer/EmptyChar.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace CanonSharp.Common.LexicalAnalyzer;
|
||||
|
||||
public class EmptyChar : IEquatable<EmptyChar>
|
||||
{
|
||||
public bool IsEmpty { get; }
|
||||
|
||||
public char Char { get; }
|
||||
|
||||
public static EmptyChar Empty => new();
|
||||
|
||||
private EmptyChar()
|
||||
{
|
||||
IsEmpty = true;
|
||||
Char = char.MaxValue;
|
||||
}
|
||||
|
||||
public EmptyChar(char c)
|
||||
{
|
||||
IsEmpty = false;
|
||||
Char = c;
|
||||
}
|
||||
|
||||
public bool Equals(EmptyChar? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsEmpty)
|
||||
{
|
||||
return other.IsEmpty;
|
||||
}
|
||||
|
||||
return Char == other.Char;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => obj is EmptyChar other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return IsEmpty.GetHashCode() ^ Char.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IsEmpty ? "ε" : Char.ToString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
namespace CanonSharp.Common.LexicalAnalyzer;
|
||||
|
||||
public class NondeterministicState : IEquatable<NondeterministicState>
|
||||
{
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
|
||||
public Dictionary<EmptyChar, HashSet<NondeterministicState>> Transactions { get; } = [];
|
||||
|
||||
public bool Equals(NondeterministicState? other) => other is not null && Id.Equals(other.Id);
|
||||
|
||||
public void AddTransaction(EmptyChar c, NondeterministicState state)
|
||||
{
|
||||
if (Transactions.TryGetValue(c, out HashSet<NondeterministicState>? states))
|
||||
{
|
||||
states.Add(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
Transactions.Add(c, [state]);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => obj is NondeterministicState other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
|
||||
public HashSet<NondeterministicState> CalculateEmptyClosure()
|
||||
{
|
||||
HashSet<NondeterministicState> result = [];
|
||||
Queue<NondeterministicState> queue = [];
|
||||
queue.Enqueue(this);
|
||||
|
||||
while (queue.TryDequeue(out NondeterministicState? state))
|
||||
{
|
||||
result.Add(state);
|
||||
|
||||
if (!state.Transactions.TryGetValue(EmptyChar.Empty, out HashSet<NondeterministicState>? next))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (NondeterministicState s in next.Where(s => !result.Contains(s)))
|
||||
{
|
||||
queue.Enqueue(s);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class NondeterministicStateSet(HashSet<NondeterministicState> states) : IEquatable<NondeterministicStateSet>
|
||||
{
|
||||
private readonly HashSet<NondeterministicState> _states = states;
|
||||
|
||||
public bool Equals(NondeterministicStateSet? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _states.Count == other._states.Count && _states.All(s => other._states.Contains(s));
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => obj is NondeterministicStateSet other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => _states.Aggregate(0, (current, state) => current ^ state.GetHashCode());
|
||||
}
|
||||
|
||||
public class NondeterministicFiniteAutomation(NondeterministicState start, HashSet<NondeterministicState> finalStates)
|
||||
{
|
||||
public NondeterministicState Start { get; } = start;
|
||||
|
||||
public HashSet<NondeterministicState> FinalStates { get; } = finalStates;
|
||||
}
|
109
CanonSharp.Common/LexicalAnalyzer/RegularExpression.cs
Normal file
109
CanonSharp.Common/LexicalAnalyzer/RegularExpression.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
namespace CanonSharp.Common.LexicalAnalyzer;
|
||||
|
||||
public abstract class RegularExpression
|
||||
{
|
||||
public abstract NondeterministicFiniteAutomation Convert2Nfa();
|
||||
|
||||
public static RegularExpression Empty => new EmptyExpression();
|
||||
|
||||
public static RegularExpression Single(char c) => new SymbolExpression(c);
|
||||
|
||||
public static RegularExpression Alternate(RegularExpression left, RegularExpression right) =>
|
||||
new AlternationExpression(left, right);
|
||||
|
||||
public static RegularExpression Concatenate(RegularExpression first, RegularExpression second) =>
|
||||
new ConcatenationExpression(first, second);
|
||||
|
||||
public static RegularExpression Kleene(RegularExpression inner) => new KleeneExpression(inner);
|
||||
}
|
||||
|
||||
public class EmptyExpression : RegularExpression
|
||||
{
|
||||
public override NondeterministicFiniteAutomation Convert2Nfa()
|
||||
{
|
||||
NondeterministicState final = new();
|
||||
NondeterministicState start = new();
|
||||
start.AddTransaction(EmptyChar.Empty, final);
|
||||
|
||||
return new NondeterministicFiniteAutomation(start, [final]);
|
||||
}
|
||||
}
|
||||
|
||||
public class SymbolExpression(char symbol) : RegularExpression
|
||||
{
|
||||
public char Symbol { get; } = symbol;
|
||||
|
||||
public override NondeterministicFiniteAutomation Convert2Nfa()
|
||||
{
|
||||
NondeterministicState final = new();
|
||||
NondeterministicState start = new();
|
||||
start.AddTransaction(new EmptyChar(Symbol), final);
|
||||
|
||||
return new NondeterministicFiniteAutomation(start, [final]);
|
||||
}
|
||||
}
|
||||
|
||||
public class AlternationExpression(RegularExpression left, RegularExpression right) : RegularExpression
|
||||
{
|
||||
public RegularExpression Left { get; } = left;
|
||||
|
||||
public RegularExpression Right { get; } = right;
|
||||
|
||||
public override NondeterministicFiniteAutomation Convert2Nfa()
|
||||
{
|
||||
NondeterministicFiniteAutomation left = Left.Convert2Nfa();
|
||||
NondeterministicFiniteAutomation right = Right.Convert2Nfa();
|
||||
|
||||
NondeterministicState final = new();
|
||||
foreach (NondeterministicState state in left.FinalStates.Concat(right.FinalStates))
|
||||
{
|
||||
state.AddTransaction(EmptyChar.Empty, final);
|
||||
}
|
||||
|
||||
NondeterministicState start = new();
|
||||
start.AddTransaction(EmptyChar.Empty, left.Start);
|
||||
start.AddTransaction(EmptyChar.Empty, right.Start);
|
||||
|
||||
return new NondeterministicFiniteAutomation(start, [final]);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConcatenationExpression(RegularExpression first, RegularExpression second) : RegularExpression
|
||||
{
|
||||
public RegularExpression First { get; } = first;
|
||||
|
||||
public RegularExpression Second { get; } = second;
|
||||
|
||||
public override NondeterministicFiniteAutomation Convert2Nfa()
|
||||
{
|
||||
NondeterministicFiniteAutomation first = First.Convert2Nfa();
|
||||
NondeterministicFiniteAutomation second = Second.Convert2Nfa();
|
||||
|
||||
foreach (NondeterministicState state in first.FinalStates)
|
||||
{
|
||||
state.AddTransaction(EmptyChar.Empty, second.Start);
|
||||
}
|
||||
|
||||
return new NondeterministicFiniteAutomation(first.Start, second.FinalStates);
|
||||
}
|
||||
}
|
||||
|
||||
public class KleeneExpression(RegularExpression inner) : RegularExpression
|
||||
{
|
||||
public RegularExpression Inner { get; } = inner;
|
||||
|
||||
public override NondeterministicFiniteAutomation Convert2Nfa()
|
||||
{
|
||||
NondeterministicFiniteAutomation inner = Inner.Convert2Nfa();
|
||||
|
||||
NondeterministicState final = new();
|
||||
final.AddTransaction(EmptyChar.Empty, inner.Start);
|
||||
|
||||
foreach (NondeterministicState state in inner.FinalStates)
|
||||
{
|
||||
state.AddTransaction(EmptyChar.Empty, final);
|
||||
}
|
||||
|
||||
return new NondeterministicFiniteAutomation(final, [final]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user