| File: Internal\SrgsCompiler\State.cs | Web Access |
| Project: src\runtime\src\libraries\System.Speech\src\System.Speech.csproj (System.Speech) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Speech.Internal.SrgsParser; using System.Text; namespace System.Speech.Internal.SrgsCompiler { /// <summary> /// Class representing a state in the grammar. Note that states are not stored in the binary format /// instead all the arcs are, with a flag to indicate the end arc out of a state */ /// </summary> #if DEBUG [DebuggerDisplay("{ToString()}")] #endif internal sealed class State : IComparable<State> { #region Constructors internal State(Rule rule, uint hState, int iSerialize) { _rule = rule; _iSerialize = iSerialize; _id = hState; } internal State(Rule rule, uint hState) : this(rule, hState, (int)hState) { } #endregion #region internal Methods #region IComparable<State> Interface implementation int IComparable<State>.CompareTo(State? state2) { if (state2 is null) { return 1; } return Compare(this, state2); } #endregion internal void SerializeStateEntries(StreamMarshaler streamBuffer, bool tagsCannotSpanOverMultipleArcs, float[] pWeights, ref uint iArcOffset, ref int iOffset) { // The arcs must be sorted before being written to disk. List<Arc> outArcs = _outArcs.ToList(); outArcs.Sort(); Arc? lastArc = outArcs.Count > 0 ? outArcs[outArcs.Count - 1] : null; IEnumerator<Arc> enumArcs = ((IEnumerable<Arc>)outArcs).GetEnumerator(); enumArcs.MoveNext(); uint nextAvailableArc = (uint)outArcs.Count + iArcOffset; uint saveNextAvailableArc = nextAvailableArc; // Write the arc of the first epsilon arc with an arc has more than one semantic tag foreach (Arc arc in outArcs) { // Create the first arc. int cSemantics = arc.SemanticTagCount; // Set the semantic property reference for the first arc if (cSemantics > 0) { arc.SetArcIndexForTag(0, iArcOffset, tagsCannotSpanOverMultipleArcs); } // Serialize the arc if (cSemantics <= 1) { pWeights[iOffset++] = arc.Serialize(streamBuffer, lastArc == arc, iArcOffset++); } else { // update the position of the current arc ++iArcOffset; // more than one arc, create an epsilon transition pWeights[iOffset++] = Arc.SerializeExtraEpsilonWithTag(streamBuffer, arc, lastArc == arc, nextAvailableArc); // reset the position of the next available slop for an arc nextAvailableArc += (uint)cSemantics - 1; } } enumArcs = ((IEnumerable<Arc>)outArcs).GetEnumerator(); enumArcs.MoveNext(); // revert the position for the new arc nextAvailableArc = saveNextAvailableArc; // write the additional arcs if we have more than one semantic tag foreach (Arc arc in outArcs) { int cSemantics = arc.SemanticTagCount; if (cSemantics > 1) { // If more than 2 arcs insert extra new epsilon states, one per semantic tag for (int i = 1; i < cSemantics - 1; i++) { // Set the semantic property reference arc.SetArcIndexForTag(i, iArcOffset, tagsCannotSpanOverMultipleArcs); // reset the position of the next available slop for an arc nextAvailableArc++; // create an epsilon transition pWeights[iOffset++] = Arc.SerializeExtraEpsilonWithTag(streamBuffer, arc, true, nextAvailableArc); // update the position of the current arc ++iArcOffset; } // Set the semantic property reference arc.SetArcIndexForTag(cSemantics - 1, iArcOffset, tagsCannotSpanOverMultipleArcs); // Add the real arc at the end pWeights[iOffset++] = arc.Serialize(streamBuffer, true, iArcOffset++); // reset the position of the next available slop for an arc nextAvailableArc++; } } } internal void SetEndArcIndexForTags() { foreach (Arc arc in _outArcs) { arc.SetEndArcIndexForTags(); } } #region State linked list // The pointers for 2 linked list are stored within each state. // When states are created, they added into a list, the '1' list. // The Members of the list are Set, Add, Remove, Prev and Next. internal void Init() { System.Diagnostics.Debug.Assert(_next == null && _prev == null); } internal State Add(State state) { _next = state; state._prev = this; return state; } internal void Remove() { _prev?._next = _next; _next?._prev = _prev; _next = _prev = null; } internal State? Next { get { return _next; } } internal State? Prev { get { return _prev; } } #endregion #if DEBUG internal void CheckExitPath(ref int iRecursiveDepth) { if (iRecursiveDepth > CfgGrammar.MAX_TRANSITIONS_COUNT) { XmlParser.ThrowSrgsException(SRID.MaxTransitionsCount); } foreach (Arc arc in _outArcs) { if (_rule._fHasExitPath) { break; } if (arc.CheckingForExitPath) { arc.CheckingForExitPath = true; if (arc.RuleRef != null) { arc.RuleRef.CheckForExitPath(ref iRecursiveDepth); if (arc.RuleRef._fHasExitPath) { if (arc.End == null) { _rule._fHasExitPath = true; } else { arc.End.CheckExitPath(ref iRecursiveDepth); } } } else { if (arc.End == null) { _rule._fHasExitPath = true; } else { arc.End.CheckExitPath(ref iRecursiveDepth); } } arc.CheckingForExitPath = false; } } } #endif internal void CheckLeftRecursion(out bool fReachedEndState) { fReachedEndState = false; if ((int)(_recurseFlag & RecurFlag.RF_IN_LEFT_RECUR_CHECK) != 0) { XmlParser.ThrowSrgsException(SRID.CircularRuleRef, _rule != null ? _rule._rule.Name : string.Empty); } else { if ((_recurseFlag & RecurFlag.RF_CHECKED_LEFT_RECURSION) == 0) { _recurseFlag |= RecurFlag.RF_CHECKED_LEFT_RECURSION | RecurFlag.RF_IN_LEFT_RECUR_CHECK; foreach (Arc arc in _outArcs) { bool fRuleReachedEndState = false; // Does the rule ref have epsilon path to the end? // Traverse any rule refs to check for circular rule reference. if (arc.RuleRef != null && arc.RuleRef._firstState != null) { State pRuleFirstNode = arc.RuleRef._firstState; if (((int)(pRuleFirstNode._recurseFlag & RecurFlag.RF_IN_LEFT_RECUR_CHECK) != 0) || // Circular RuleRef ((int)(pRuleFirstNode._recurseFlag & RecurFlag.RF_CHECKED_LEFT_RECURSION) == 0)) // Untraversed rule { pRuleFirstNode.CheckLeftRecursion(out fRuleReachedEndState); } else { fRuleReachedEndState = arc.RuleRef._fIsEpsilonRule; } } // Can transition be traversed by epsilon? if (fRuleReachedEndState || ((arc.RuleRef == null) && (arc.WordId == 0) && arc.WordId == 0)) { if (arc.End != null) { arc.End.CheckLeftRecursion(out fReachedEndState); } else { fReachedEndState = true; } } } _recurseFlag &= (~RecurFlag.RF_IN_LEFT_RECUR_CHECK); if ((_rule._firstState == this) && fReachedEndState) { _rule._fIsEpsilonRule = true; } } } } #endregion #region Internal Properties internal int NumArcs { get { // if the number of tags > 1 extra epsilon state needs to be inserted int cExtra = 0; foreach (Arc arc in _outArcs) { if (arc.SemanticTagCount > 0) { cExtra += arc.SemanticTagCount - 1; } } int cArcs = 0; foreach (Arc arc in _outArcs) { cArcs++; } return cArcs + cExtra; } } internal int NumSemanticTags { get { int c = 0; foreach (Arc arc in _outArcs) { c += arc.SemanticTagCount; } return c; } } internal Rule Rule { get { return _rule; } } internal uint Id { get { return _id; } } internal ArcList OutArcs { get { return _outArcs; } } internal ArcList InArcs { get { return _inArcs; } } internal int SerializeId { get { return _iSerialize; } set { _iSerialize = value; } } #endregion #region private Methods // Sort based on rule first, so all states, and arcs for a rule end up together. // Then sort on index. private static int Compare(State state1, State state2) { if (state1._rule._cfgRule._nameOffset != state2._rule._cfgRule._nameOffset) { return state1._rule._cfgRule._nameOffset - state2._rule._cfgRule._nameOffset; } else { // First state of a rule needs to be in front. int isNode1FirstNode = (state1._rule._firstState == state1) ? -1 : 0; int isNode2FirstNode = (state2._rule._firstState == state2) ? -1 : 0; if (isNode1FirstNode != isNode2FirstNode) { return isNode1FirstNode - isNode2FirstNode; } else { // First returns null on empty collections Arc? arc1 = state1._outArcs != null && !state1._outArcs.IsEmpty ? state1._outArcs.First : null; Arc? arc2 = state2._outArcs != null && !state2._outArcs.IsEmpty ? state2._outArcs.First : null; int diff = (arc1 != null ? (arc1.RuleRef != null ? 0x1000000 : 0) + arc1.WordId : state1._iSerialize) - (arc2 != null ? (arc2.RuleRef != null ? 0x1000000 : 0) + arc2.WordId : state2._iSerialize); diff = diff != 0 ? diff : state1._iSerialize - state2._iSerialize; //System.Diagnostics.Debug.Assert (diff != 0); return diff; } } } #if DEBUG public override string ToString() { StringBuilder sb = new("[#"); sb.Append(_id.ToString(CultureInfo.InvariantCulture)); if (_rule != null && _rule._firstState == this) { sb.Append(' '); sb.Append(_rule.Name); } sb.Append("] "); if (_inArcs != null) { bool first = true; foreach (Arc arc in _inArcs) { if (!first) { sb.Append("\x20\x25cf\x20"); } sb.Append('#'); sb.Append(arc.Start != null ? arc.Start._id.ToString(CultureInfo.InvariantCulture) : "S"); sb.Append(' '); sb.Append(arc.DebuggerDisplayTags()); first = false; } } sb.Append(" <--> "); if (_outArcs != null) { bool first = true; foreach (Arc arc in _outArcs) { if (!first) { sb.Append("\x20\x25cf\x20"); } sb.Append('#'); sb.Append(arc.End != null ? arc.End._id.ToString(CultureInfo.InvariantCulture) : "E"); sb.Append(' '); sb.Append(arc.DebuggerDisplayTags()); first = false; } } return sb.ToString(); } #endif #endregion #region internal Fields #pragma warning disable 56524 // Arclist does not hold on any resources // Collection of transitions leaving this state private ArcList _outArcs = new(); // Collection of transitions entering this state private ArcList _inArcs = new(); #pragma warning restore 56524 // Arclist does not hold on any resources // Index of the first arc in the state. Also used as the state handle in SR engine interfaces. private int _iSerialize; private uint _id; private Rule _rule; private State? _next; private State? _prev; // Flags used for recursive validation methods internal enum RecurFlag : uint { RF_CHECKED_EPSILON = (1 << 0), RF_CHECKED_EXIT_PATH = (1 << 1), RF_CHECKED_LEFT_RECURSION = (1 << 2), RF_IN_LEFT_RECUR_CHECK = (1 << 3) }; // Flags used by recursive algorithms private RecurFlag _recurseFlag; #endregion } }