Dazu muss erst ein Regex in einen DFA umgewandelt werden. Der Algorithmus ist hier in Details beschrieben und es gibt bereits eine F#-Implementierung zur Kompilierung eines regulären Ausdrucks in einen nicht-deterministischen endlichen Automaten (NFA). Was fehlt, ist der Übergang zum DFA und darum geht es hier.
Subset Construction Algorithm (aka Powerset Construction)
Ich hoffe ich verletze keine Copyright-Bestimmungen, wenn ich oben genannte Implementierung nutze ( hier kann man das Projekt herunterladen).
Wie bei mir schon üblich ist, diente der Haskell-Code als Vorbild.
// Required RegExProcessor from
// http://stevehorsfield.wordpress.com/2009/08/05/download-the-regular-expression-processor/
open RegExCompiling
open RegExParsing.RegExProcessor
type Node = int
//Transition: fromNode * toNode * Label
type Transition = Transition of Node * Node * NdfaEdge
type ConvertContext = { nfa : RegExCompiling.NdfaGraph;
trans : Transition list; //DFA Transition list.
//mapping NFA sets of nodes to a single node in the DFA.
setMap : Map<Set<Node>, int>;
setStack : Set<Node> list;
finalNfa : Set<Node>; // set of NFA final states
accept : Set<Node>; // DFA accept states
nextNode : Node;
start : Node
alphabet : char list}
// Search the table of transitions to find all nodes you can reach given an initial set of nodes.
// Auto - epsilon transition.
let inline findToNodes startNode trans value fromNodes =
let matchNodes (from, _to, edge) nodes =
match from with
| from' when (from' = fromNodes) ->
match edge, value with
| AnyChar, Simple _ -> Set.add _to nodes
| Auto, Auto -> Set.add _to nodes
| CharacterTest criteria, Simple c when (testCharacter criteria c) ->
Set.add _to nodes
| CharacterTest criteria, Simple c when not (testCharacter criteria c) ->
Set.add startNode nodes
| Simple v, Simple c when v = c -> Set.add _to nodes
| Simple v, Simple c when v <> c -> Set.add startNode nodes
| other -> nodes
| other -> nodes
List.foldBack matchNodes trans Set.empty
// Check if we already added this transition if not add it
let inline checkTransition ts context =
match List.exists (fun x -> x = ts) context.trans with
| true -> context
| false -> {context with trans = ts :: context.trans }
// Check if a given node set contains a accept state
// if so add it to the dfa accept states
let inline updateAcceptStates nfaAccepts dfaAccepts nSet nSetIndex =
match Set.intersect nSet nfaAccepts |> Set.isEmpty with
| true -> dfaAccepts
| false -> Set.add nSetIndex dfaAccepts
let inline addNodeSet nSet context =
let newNodesStack = context.setStack @ [nSet]
let newNode = context.nextNode
let newNodesMap = Map.add nSet newNode context.setMap
let newAccepts = updateAcceptStates context.finalNfa context.accept nSet newNode
newNode, {context with setMap = newNodesMap;
nextNode = newNode + 1;
setStack = newNodesStack;
accept = newAccepts}
// Checks a NodeSet to see if it has a node number value
// If it doesnt we assign it one and add it to the nodeSet stack
let inline checkNodeSet nSet context =
match Map.containsKey nSet context.setMap with
| true -> context.setMap.[nSet], context
| false -> addNodeSet nSet context
// Given a node and a set of nodes, union orginal set with the set of nodes you can
// traverse to from node on the value
let inline closure startNode trans value oldSet nodes =
Set.union (findToNodes startNode trans value nodes) oldSet
// Given an initial set of nodes, find the set of all nodes you can reach by taking
// transitions on epsilon only
let inline epsilonClosure start trans =
let generator = Set.fold (closure start trans Auto) Set.empty
Set.unionMany
<< Seq.unfold (fun state ->
match Set.isEmpty state with
| true -> None
| false -> Some(state, generator state))
//Move takes a set of nodes and input character and returns all nodes you can reach by taking transitions on given input character.
let inline moveClosure start trans character =
epsilonClosure start trans << Set.fold (closure start trans character) Set.empty
let inline buildTransition oldTrans context value=
let nodes = List.head context.setStack
let newSet = moveClosure context.start oldTrans value nodes
match Set.isEmpty newSet with
| false ->
let fromNode, c1 = checkNodeSet nodes context
let toNode, c2 = checkNodeSet newSet c1
checkTransition (Transition (fromNode, toNode, value)) c2
| true -> context
let inline runConversion machine nodes finalNfa letters =
let context = { nfa = machine;
trans = [];
setMap = Map.empty;
setStack = [];
finalNfa = finalNfa;
accept = Set.empty;
nextNode = 0;
start = 0;
alphabet = letters}
let popSetStack context = {context with setStack = List.tail context.setStack}
let trans = Graph.toTable context.nfa
let edges = context.alphabet |> List.map Simple
let startSet = epsilonClosure context.start trans nodes
checkNodeSet startSet context
|> snd
|> Seq.unfold (fun ctx ->
match List.isEmpty ctx.setStack with
| true -> None
| false ->
let newCtx = List.fold (buildTransition trans) ctx edges |> popSetStack
Some(newCtx, newCtx))
|> Seq.tryFind (fun ctx -> List.isEmpty ctx.setStack)
let inline convert letters nfa =
let fstLetter = List.head letters
let final =
getClosureMap nfa
|>Array.mapi (fun node isFinal ->
match isFinal with
| true -> Some(node)
| false -> None)
|>Array.choose id
|>Set.ofArray
let initialStates =
getStartStates nfa
let startNodes = (List.map (fun (i,_,_,_,_) -> i) initialStates)
let context =
match runConversion nfa (startNodes |> Set.ofList) final letters with
| Some v -> v
| None -> failwith "Conversion is not possible."
context open RegExParsing
> let test () =
let regex = "aa|bb"
let letters =['a'..'c']
let context =
"aa|bb" |> RegExParsing.parseRegExp |> RegExCompiling.compile FullMatch
|> convert letters
printfn "Context: %A" context;;
> test();;
Context: {nfa =
((7, 6),
[((6, (Closure, null)), []); ((5, (Normal, null)), [(4, 1, 4, Simple 'b')]);
((4, (Normal, null)), [(5, 0, 6, Simple 'b')]); ((3, (Closure, null)), []);
((2, (Normal, null)), [(1, 0, 1, Simple 'a')]);
((1, (Normal, null)), [(2, 0, 3, Simple 'a')]);
((0, (Start, null)), [(3, 1, 5, Auto); (0, 0, 2, Auto)])]);
trans =
[Transition (4,0,Simple 'c'); Transition (4,4,Simple 'b');
Transition (4,1,Simple 'a'); Transition (3,0,Simple 'c');
Transition (3,2,Simple 'b'); Transition (3,3,Simple 'a');
Transition (2,0,Simple 'c'); Transition (2,4,Simple 'b');
Transition (2,1,Simple 'a'); Transition (1,0,Simple 'c');
Transition (1,2,Simple 'b'); Transition (1,3,Simple 'a');
Transition (0,0,Simple 'c'); Transition (0,2,Simple 'b');
Transition (0,1,Simple 'a')];
setMap =
map
[(set [0; 1; 2; 3; 5], 3); (set [0; 1; 2; 5], 1); (set [0; 2; 4; 5], 2);
(set [0; 2; 4; 5; 6], 4); (set [0; 2; 5], 0)];
setStack = [];
finalNfa = set [3; 6];
accept = set [3; 4];
nextNode = 5;
start = 0;
alphabet = ['a'; 'b'; 'c'];}
val it : unit = ()
Fortsetzung folgt.





