add: nfa2dfa算法
This commit is contained in:
parent
a8886bec33
commit
02c5690d97
|
@ -7,7 +7,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Abstractions\" />
|
|
||||||
<None Include="../.gitignore" />
|
<None Include="../.gitignore" />
|
||||||
<None Include="../.editorconfig" />
|
<None Include="../.editorconfig" />
|
||||||
</ItemGroup>
|
</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>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="LexicalAnalyzer\RegularExpression.fs" />
|
|
||||||
<Compile Include="LexicalAnalyzer\NondeterministicFiniteAutomation.fs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</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