/// <summary>Enumerates all the paths in this transition excluding paths to dead-ends (and unexplored states if any)</summary> internal IEnumerable <(S, SymbolicRegexNode <S>?, int)> EnumeratePaths(IBooleanAlgebra <S> solver, S pathCondition) { switch (_kind) { case TransitionRegexKind.Leaf: // Omit any path that leads to a deadend or is unexplored if (_leaf >= 0) { yield return(pathCondition, null, _leaf); } break; case TransitionRegexKind.Union: Debug.Assert(_first is not null && _second is not null); foreach ((S, SymbolicRegexNode <S>?, int)path in _first.EnumeratePaths(solver, pathCondition)) { yield return(path); } foreach ((S, SymbolicRegexNode <S>?, int)path in _second.EnumeratePaths(solver, pathCondition)) { yield return(path); } break; case TransitionRegexKind.Conditional: Debug.Assert(_test is not null && _first is not null && _second is not null); foreach ((S, SymbolicRegexNode <S>?, int)path in _first.EnumeratePaths(solver, solver.And(pathCondition, _test))) { yield return(path); } foreach ((S, SymbolicRegexNode <S>?, int)path in _second.EnumeratePaths(solver, solver.And(pathCondition, solver.Not(_test)))) { yield return(path); } break; default: Debug.Assert(_kind is TransitionRegexKind.Lookaround && _look is not null && _first is not null && _second is not null); foreach ((S, SymbolicRegexNode <S>?, int)path in _first.EnumeratePaths(solver, pathCondition)) { SymbolicRegexNode <S> nullabilityTest = path.Item2 is null ? _look : _look._builder.MkAnd(path.Item2, _look); yield return(path.Item1, nullabilityTest, path.Item3); } foreach ((S, SymbolicRegexNode <S>?, int)path in _second.EnumeratePaths(solver, pathCondition)) { // Complement the nullability test SymbolicRegexNode <S> nullabilityTest = path.Item2 is null?_look._builder.MkNot(_look) : _look._builder.MkAnd(path.Item2, _look._builder.MkNot(_look)); yield return(path.Item1, nullabilityTest, path.Item3); } break; } }
/// <summary>Enumerates all target states from the given source state</summary> /// <param name="sourceState">must be a an integer between 0 and StateCount-1</param> /// <param name="input">must be a value that acts as a minterm for the transitions emanating from the source state</param> /// <param name="context">reflects the immediate surrounding of the input and is used to determine nullability of anchors</param> public IEnumerable <int> EnumerateTargetStates(int sourceState, S input, uint context) { Debug.Assert(sourceState >= 0 && sourceState < _transitionFunction.Length); // First operate in a mode assuming no Union happens by finding the target leaf state if one exists Transition transition = _transitionFunction[sourceState]; while (transition._kind != TransitionRegexKind.Union) { switch (transition._kind) { case TransitionRegexKind.Leaf: // deadend and unexplored are negative if (transition._leaf >= 0) { Debug.Assert(transition._leaf < _transitionFunction.Length); yield return(transition._leaf); } // The single target (or no target) state was found, so exit the whole enumeration yield break; case TransitionRegexKind.Conditional: Debug.Assert(transition._test is not null && transition._first is not null && transition._second is not null); // Branch according to the input condition in relation to the test condition if (_solver.IsSatisfiable(_solver.And(input, transition._test))) { // in a conditional transition input must be exclusive Debug.Assert(!_solver.IsSatisfiable(_solver.And(input, _solver.Not(transition._test)))); transition = transition._first; } else { transition = transition._second; } break; default: Debug.Assert(transition._kind == TransitionRegexKind.Lookaround && transition._look is not null && transition._first is not null && transition._second is not null); // Branch according to nullability of the lookaround condition in the given context transition = transition._look.IsNullableFor(context) ? transition._first : transition._second; break; } } // Continue operating in a mode where several target states can be yielded Debug.Assert(transition._first is not null && transition._second is not null); Stack <Transition> todo = new(); todo.Push(transition._second); todo.Push(transition._first); while (todo.TryPop(out _)) { switch (transition._kind) { case TransitionRegexKind.Leaf: // dead-end if (transition._leaf >= 0) { Debug.Assert(transition._leaf < _transitionFunction.Length); yield return(transition._leaf); } break; case TransitionRegexKind.Conditional: Debug.Assert(transition._test is not null && transition._first is not null && transition._second is not null); // Branch according to the input condition in relation to the test condition if (_solver.IsSatisfiable(_solver.And(input, transition._test))) { // in a conditional transition input must be exclusive Debug.Assert(!_solver.IsSatisfiable(_solver.And(input, _solver.Not(transition._test)))); todo.Push(transition._first); } else { todo.Push(transition._second); } break; case TransitionRegexKind.Lookaround: Debug.Assert(transition._look is not null && transition._first is not null && transition._second is not null); // Branch according to nullability of the lookaround condition in the given context todo.Push(transition._look.IsNullableFor(context) ? transition._first : transition._second); break; default: Debug.Assert(transition._kind == TransitionRegexKind.Union && transition._first is not null && transition._second is not null); todo.Push(transition._second); todo.Push(transition._first); break; } } }