| File: Internal\SrgsCompiler\Rule.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.Diagnostics.CodeAnalysis; using System.Speech.Internal.SrgsParser; using System.Speech.Synthesis.TtsEngine; using System.Text; namespace System.Speech.Internal.SrgsCompiler { [DebuggerDisplay("{Name}")] internal sealed class Rule : ParseElementCollection, IRule, IComparable<Rule> { #region Constructors // Only used for the special transition internal Rule(int iSerialize) : base(null!, null!) { _iSerialize = iSerialize; _id = null!; } internal Rule(Backend backend, string name, CfgRule cfgRule, int iSerialize, GrammarOptions SemanticFormat, ref int cImportedRules) : base(backend, null! /* Cannot pass 'this' into base constructor - setting this on _rule instead */) { _rule = this; _id = name; _cfgRule = cfgRule; _firstState = null; _cfgRule.DirtyRule = true; _iSerialize = iSerialize; _fHasExitPath = false; _fHasDynamicRef = false; _fIsEpsilonRule = false; _fStaticRule = false; if (_cfgRule.Import) { cImportedRules++; } } internal Rule(Backend backend, string name, int offsetName, SPCFGRULEATTRIBUTES attributes, int id, int iSerialize, GrammarOptions SemanticFormat, ref int cImportedRules) : this(backend, name, new CfgRule(id, offsetName, attributes), iSerialize, SemanticFormat, ref cImportedRules) { } #endregion #region internal Methods #region IComparable<Rule> Interface implementation int IComparable<Rule>.CompareTo(Rule? rule2) { if (rule2 is null) { return 1; } Rule rule1 = this; if (rule1._cfgRule.Import) { return (rule2._cfgRule.Import) ? rule1._cfgRule._nameOffset - rule2._cfgRule._nameOffset : -1; } else if (rule1._cfgRule.Dynamic) { return (rule2._cfgRule.Dynamic) ? rule1._cfgRule._nameOffset - rule2._cfgRule._nameOffset : 1; } else { return (rule2._cfgRule.Import) ? 1 : (rule2._cfgRule.Dynamic) ? -1 : rule1._cfgRule._nameOffset - rule2._cfgRule._nameOffset; } } #endregion #if DEBUG internal void CheckForExitPath(ref int iRecursiveDepth) { if (!_fHasExitPath) { // This check allows empty rules. if (_firstState != null && _firstState.NumArcs != 0) { _firstState.CheckExitPath(ref iRecursiveDepth); } } } #endif internal void Validate() { if ((!_cfgRule.Dynamic) && (!_cfgRule.Import) && _id != "VOID" && _firstState!.NumArcs == 0) { XmlParser.ThrowSrgsException(SRID.EmptyRule); } else { _fHasDynamicRef = _cfgRule.Dynamic; } } internal void PopulateDynamicRef(ref int iRecursiveDepth) { if (iRecursiveDepth > CfgGrammar.MAX_TRANSITIONS_COUNT) { XmlParser.ThrowSrgsException((SRID.MaxTransitionsCount)); } foreach (Rule rule in _listRules) { if (!rule._fHasDynamicRef) { rule._fHasDynamicRef = true; rule.PopulateDynamicRef(ref iRecursiveDepth); } } } internal Rule Clone(StringBlob symbol, string ruleName) { Rule rule = new(_iSerialize); int idWord; int offsetName = symbol.Add(ruleName, out idWord); rule._id = ruleName; rule._cfgRule = new CfgRule(idWord, offsetName, _cfgRule._flag) { DirtyRule = true, FirstArcIndex = 0 }; return rule; } internal void Serialize(StreamMarshaler streamBuffer) { // Dynamic rules and imports have no arcs _cfgRule.FirstArcIndex = _firstState != null && !_firstState.OutArcs.IsEmpty ? (uint)_firstState.SerializeId : 0; _cfgRule.DirtyRule = true; streamBuffer.WriteStream(_cfgRule); } void IElement.PostParse(IElement? grammar) { // Empty rule if (_endArc == null) { System.Diagnostics.Debug.Assert(_startArc == null); _firstState = _backend.CreateNewState(this); } else { // The last arc may contain an epsilon value. Remove it. TrimEndEpsilons(_endArc, _backend); // If the first arc was an epsilon value then there is no need to create a new state if (_startArc!.IsEpsilonTransition && _startArc.End != null && Graph.MoveSemanticTagRight(_startArc)) { // Discard the arc and replace it with the startArc _firstState = _startArc.End; System.Diagnostics.Debug.Assert(_startArc.End == _startArc.End); _startArc.End = null; } else { // if _first has not be set, create it _firstState = _backend.CreateNewState(this); // Attach the start and end arc to the rule _startArc.Start = _firstState; } } } void IRule.CreateScript(IGrammar grammar, string rule, string method, RuleMethodScript type) { ((GrammarElement)grammar).CustomGrammar._scriptRefs.Add(new ScriptRef(rule, method, type)); } #endregion #region Internal Properties internal string Name { get { return _id; } } string? IRule.BaseClass { get { return _baseclass; } set { _baseclass = value; } } internal string? BaseClass { get { return _baseclass; } } internal StringBuilder Script { get { return _script; } } internal StringBuilder Constructors { get { return _constructors; } } #endregion #region Private Methods private static void TrimEndEpsilons(Arc end, Backend backend) { Arc endArc = end; State? endState = endArc.Start; if (endState != null) { // Remove the end arc if possible, check done by MoveSemanticTagRight if (endArc.IsEpsilonTransition && endState.OutArcs.CountIsOne && Graph.MoveSemanticTagLeft(endArc)) { // State has a single input epsilon transition // Delete the input epsilon transition and delete state. endArc.Start = null; // Remove all the in arcs duplicate the arcs first foreach (Arc inArc in endState.InArcs.ToList()) { inArc.End = null; TrimEndEpsilons(inArc, backend); } // Delete the input epsilon transition and delete state if appropriate. backend.DeleteState(endState); } } } #endregion #region Internal Fields internal CfgRule _cfgRule; internal State? _firstState; internal bool _fHasExitPath; internal bool _fHasDynamicRef; internal bool _fIsEpsilonRule; internal int _iSerialize; internal int _iSerialize2; #if DEBUG internal int _cStates; #endif internal List<Rule> _listRules = new(); // this is used to refer to a static rule from a dynamic rule internal bool _fStaticRule; #endregion #region Private Fields private string _id; // STG fields private string? _baseclass; private StringBuilder _script = new(); private StringBuilder _constructors = new(); #endregion } }