add: nfa2dfa算法
This commit is contained in:
parent
a8886bec33
commit
02c5690d97
|
@ -7,7 +7,6 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Abstractions\" />
|
||||
<None Include="../.gitignore" />
|
||||
<None Include="../.editorconfig" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -5,9 +5,4 @@
|
|||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="LexicalAnalyzer\RegularExpression.fs" />
|
||||
<Compile Include="LexicalAnalyzer\NondeterministicFiniteAutomation.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
module CanonSharp.Parser.LexicalAnalyzer.NondeterministicFiniteAutomation
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open CanonSharp.Parser.LexicalAnalyzer.RegularExpression
|
||||
|
||||
type NondeterministicState(id: Guid, transaction: Option<char> -> list<NondeterministicState>) =
|
||||
member val id = id
|
||||
member val transaction = transaction
|
||||
|
||||
override this.GetHashCode() = this.id.GetHashCode()
|
||||
|
||||
new() = NondeterministicState(Guid.NewGuid(), fun a -> list.Empty)
|
||||
|
||||
|
||||
type NondeterministicFiniteAutomation(states: HashSet<NondeterministicState>, entryTransaction: Option<char> -> list<NondeterministicState>) =
|
||||
member val states = states
|
||||
member val entryTransaction = entryTransaction
|
||||
|
||||
let convertEmptyToNonDeterministicFiniteAutomation (expression: EmptyExpression) =
|
||||
let final = NondeterministicState()
|
||||
let transaction (a: Option<char>) =
|
||||
match a with
|
||||
| Some _ -> list.Empty
|
||||
| None -> [final]
|
||||
|
||||
let states = HashSet()
|
||||
let _ = states.Add(final)
|
||||
|
||||
NondeterministicFiniteAutomation(states, transaction)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let convertToNondeterministicFiniteAutomation expression: RegularExpression =
|
||||
match expression with
|
||||
| EmptyExpression ->
|
|
@ -1,12 +0,0 @@
|
|||
module CanonSharp.Parser.LexicalAnalyzer.RegularExpression
|
||||
|
||||
type RegularExpression =
|
||||
| EmptyExpression
|
||||
| SymbolExpression of symbol: char
|
||||
| AlternationExpression of left: RegularExpression * right : RegularExpression
|
||||
| ConcatenationExpression of first: RegularExpression * second : RegularExpression
|
||||
| KleeneExpression of expression: RegularExpression
|
||||
|
||||
let convertToNondeterministicFiniteAutomation expression =
|
||||
match expression with
|
||||
| EmptyExpression emptyExpression ->
|
|
@ -0,0 +1,77 @@
|
|||
using CanonSharp.Common.LexicalAnalyzer;
|
||||
|
||||
namespace CanonSharp.Tests.LexicalAnalyzerTests;
|
||||
|
||||
public class RegularExpressionTests
|
||||
{
|
||||
[Fact]
|
||||
public void KleeneTest()
|
||||
{
|
||||
RegularExpression expression = RegularExpression.Concatenate(
|
||||
RegularExpression.Kleene(RegularExpression.Single('a')),
|
||||
RegularExpression.Single('b'));
|
||||
|
||||
NondeterministicFiniteAutomation automation = expression.Convert2Nfa();
|
||||
|
||||
automation.Start.Transactions.TryGetValue(EmptyChar.Empty, out HashSet<NondeterministicState>? next);
|
||||
Assert.NotNull(next);
|
||||
|
||||
Assert.Contains(next, s => s.Transactions.ContainsKey(new EmptyChar('a')));
|
||||
Assert.Contains(next, s => s.Transactions.ContainsKey(new EmptyChar('b')));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlternateTest()
|
||||
{
|
||||
RegularExpression expression = RegularExpression.Alternate(
|
||||
RegularExpression.Kleene(RegularExpression.Single('a')),
|
||||
RegularExpression.Single('b'));
|
||||
|
||||
NondeterministicFiniteAutomation automation = expression.Convert2Nfa();
|
||||
|
||||
automation.Start.Transactions.TryGetValue(EmptyChar.Empty, out HashSet<NondeterministicState>? next);
|
||||
Assert.NotNull(next);
|
||||
|
||||
Assert.Contains(next, s => s.Transactions.ContainsKey(new EmptyChar('b')));
|
||||
|
||||
NondeterministicState? state = (from item in next
|
||||
where item.Transactions[EmptyChar.Empty].Count == 2
|
||||
select item).FirstOrDefault();
|
||||
Assert.NotNull(state);
|
||||
|
||||
Assert.Contains(state.Transactions[EmptyChar.Empty],
|
||||
s => s.Transactions.ContainsKey(new EmptyChar('a')));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertTest()
|
||||
{
|
||||
RegularExpression expression = RegularExpression.Alternate(
|
||||
RegularExpression.Kleene(RegularExpression.Single('a')),
|
||||
RegularExpression.Single('b'));
|
||||
|
||||
NondeterministicFiniteAutomation automation = expression.Convert2Nfa();
|
||||
DeterministicFiniteAutomation dfa = DeterministicFiniteAutomation.Create(automation);
|
||||
|
||||
DeterministicState state2 = dfa.Start.Transaction['a'];
|
||||
Assert.Equal(state2, state2.Transaction['a']);
|
||||
|
||||
DeterministicState state3 = dfa.Start.Transaction['b'];
|
||||
Assert.Empty(state3.Transaction);
|
||||
|
||||
Assert.Equal(3, dfa.FinalStates.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NondeterministicStateSetTest()
|
||||
{
|
||||
Dictionary<NondeterministicStateSet, char> map = [];
|
||||
|
||||
NondeterministicState key1 = new();
|
||||
NondeterministicState key2 = new();
|
||||
|
||||
map.Add(new NondeterministicStateSet([key1, key2]), 'a');
|
||||
|
||||
Assert.Equal('a', map[new NondeterministicStateSet([key2, key1])]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user