/// <summary> /// Установить выходные связи frag на state /// </summary> /// <param name="frag"></param> /// <param name="state"></param> public static void Patch(NFAFragment frag, NFAState state) { var e = frag._outArrows.GetEnumerator(); for (int i = 0; i < frag._outArrows.Count; i++) { frag._outArrows[i].value = state; } }
/// <summary> /// Конструируем НКА по постфиксному выражению по алгоритму Томпсона /// </summary> /// <param name="re"></param> /// <returns></returns> private static NFA FromPostfixRe(string re) { /* * Так как выражение поступает в постфиксной нотации алгоритм работает на стеке. * Для построения НКА используются такие абстрации: * NFAState - состояние НКА с выходным(-и) узлом. Может быть четырех типов: * если c < 256 - это состояние с одним выходом, по которому можно перейти если очередной символ равен (char)c * если c = 256 - это состояние с двумя е-переходами (Split) * и если c = 257 - то это финальное состояние (оно одно такое) * NFAFragment - кусочек строящегося НКА, со ссылкой на стартовое состояние и указателями (NFAStatePointer) на * выходные узлы графа. * Таким образом соединение двух фрагментов f1, f2 например для случая конкатенации (последовательно) * осуществляется путем создания нового фрагмента f3, установки f3.Start = f1.Start, f3.OutArrows = f2.OutArrows * и установки всех указателей из f1.OutArrows на f2.Start (NFAFragment.Patch()) */ Stack <NFAFragment> stack = new Stack <NFAFragment>(); NFAFragment e, e1, e2; NFAState s; foreach (char ch in re) { switch (ch) { default: //Literal characters s = new NFAState(ch, null, null); stack.Push(new NFAFragment(s, new List <NFAStatePointer>() { s.OutArrowPtr })); break; case CONCAT: //Concatenation e2 = stack.Pop(); e1 = stack.Pop(); NFAFragment.Patch(e1, e2.Start); stack.Push(new NFAFragment(e1.Start, e2.OutArrows)); break; case ALT: //Alternation e2 = stack.Pop(); e1 = stack.Pop(); s = new NFAState(NFAStateName.Split, e1.Start, e2.Start); stack.Push(new NFAFragment(s, NFAFragment.Concat(e1, e2))); break; case QUEST: //Zero or one e = stack.Pop(); s = new NFAState(NFAStateName.Split, e.Start, null); stack.Push(new NFAFragment(s, NFAFragment.Concat(e.OutArrows, new List <NFAStatePointer>() { s.OutArrow1Ptr }))); break; case STAR: //Zero or more e = stack.Pop(); s = new NFAState(NFAStateName.Split, e.Start, null); NFAFragment.Patch(e, s); stack.Push(new NFAFragment(s, new List <NFAStatePointer>() { s.OutArrow1Ptr })); break; case PLUS: //One or more e = stack.Pop(); s = new NFAState(NFAStateName.Split, e.Start, null); NFAFragment.Patch(e, s); stack.Push(new NFAFragment(e.Start, new List <NFAStatePointer>() { s.OutArrow1Ptr })); break; } } //Осталось только взять верхний элемент стека, он и есть построенный НКА, //и все выходные связи установить в финальное состояние e = stack.Pop(); NFAFragment.Patch(e, NFAState.MatchState); return(new NFA(e.Start)); }
/// <summary> /// Склеить списки выходных связей двух фрагментов /// </summary> /// <param name="f1"></param> /// <param name="f2"></param> /// <returns></returns> public static IList <NFAStatePointer> Concat(NFAFragment f1, NFAFragment f2) { return(Concat(f1.OutArrows, f2.OutArrows)); }