using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using miew.Enumerable; namespace agree { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract partial class Entry : Instance { public Entry(Type t, String name, List<BaseFeatConstraint> bfc) : base(t.tm, name, bfc) { this.t = t; this.m_flags = (t.m_flags & Flags.HasAnyFeatures); } readonly Type t; public override Type InstanceType { get { return t; } } protected TargetTfs PreExpand() { TargetTfs tt = Unification.UnifyForceExpand(Definition, InstanceType.Expanded); if (tt == null) throw new TfsException("Error expanding entry '{0}': could not unify with its instance type(s).", this.Name); tt.Name = Name + " - Expanded"; return tt; } /// to avoid expanding lexical a dynamic expanding entry if all you need is the hash long _exp_tfs_hash = -1; public long ExpandedTfsHash { get { if (_exp_tfs_hash == -1) { if (this is StaticExpandEntry) _exp_tfs_hash = ((StaticExpandEntry)this).Expanded.TfsHash(); else _exp_tfs_hash = ((DemandExpandEntry)this).GetExpanded().TfsHash(); } return _exp_tfs_hash; } } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract partial class StaticExpandEntry : Entry { public StaticExpandEntry(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } public override Tfs EnsureExpanded(out bool f_did) { f_did = false; Tfs _tmp; if ((_tmp = _expanded) != null) return _tmp; Stopwatch stopw = Stopwatch.StartNew(); TargetTfs tt = PreExpand(); if ((_tmp = _expanded) != null) return _tmp; Tfs _new; if (tt.EdgeCount == 0) _new = new BareTfs(tt.Type); else _new = tt.ToArrayTfs(); Interlocked.Add(ref tm.ms_expand, (int)stopw.ElapsedMilliseconds); if ((_tmp = Interlocked.CompareExchange(ref _expanded, _new, null)) != null) return _tmp; f_did = true; return _new; } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Expand-on-demand support /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract partial class DemandExpandEntry : Entry { public DemandExpandEntry(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { /// an ID for the expanded TFS that will remain the same after weak resurrection exp_tfs_id = Interlocked.Increment(ref Tfs.next_tfs_id); } public override Tfs EnsureExpanded(out bool f_did) { f_did = true; return GetExpanded(); //throw new NotImplementedException("use GetExpanded() for DynamicExpandEntry"); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Hook the expansion of lexical entries and keep a weak reference. The published reference to a lexical entry or /// entries can be released but a weak reference will remain in the object for possible future recovery. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WeakReference wr = null; int exp_tfs_id; public Tfs GetExpanded() { /// we have to count weak reference recoveries as unifications (and pay the penalty of re-installing /// them atomically) or else the regression test results for number of unifications won't exactly match Tfs _exp; if (wr != null && (_exp = (Tfs)wr.Target) != null) return _exp; _exp = PreExpand().ToArrayTfs(); _exp.id = exp_tfs_id; if (wr == null) wr = new WeakReference(_exp, false); else wr.Target = _exp; return _exp; } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerDisplay("{ToString(),nq} ({t._feature_info(),nq})")] public abstract class Rule : StaticExpandEntry { public Rule(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } public Rule(Type t, BinaryReader br) : base(t, br) { } HashSet<Rule> _key_mothers = null; public bool IsSpanOnly = false; private MotherDaughterTfs _mdtfs; public override Tfs EnsureExpanded(out bool f_did) { f_did = false; Tfs _tmp; if ((_tmp = _expanded) != null) return _tmp; Stopwatch stopw = Stopwatch.StartNew(); TargetTfs tt = PreExpand(); if ((_tmp = _expanded) != null) return _tmp; _mdtfs = tt.ToMotherDaughterTfs(null); Interlocked.Add(ref tm.ms_expand, (int)stopw.ElapsedMilliseconds); if ((_tmp = Interlocked.CompareExchange(ref _expanded, _mdtfs, null)) != null) return _tmp; f_did = true; return _mdtfs; } public MotherDaughterTfs RuleTfs { get { bool f_dont_care; return _mdtfs ?? (MotherDaughterTfs)EnsureExpanded(out f_dont_care); } } public TfsSection[] RuleDaughters { get { bool f_dont_care; return (_mdtfs ?? (MotherDaughterTfs)EnsureExpanded(out f_dont_care)).RuleDaughters; } } public int KeyDaughterIndex { get { bool f_dont_care; return (_mdtfs ?? (MotherDaughterTfs)EnsureExpanded(out f_dont_care)).KeyDaughterIndex; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Check rule compatibility. There are two cache directions: first, each rule pre-checks and caches the mother /// rules for which this rule is compatible with that mother's KEY daughter. This cache is used by both grammar /// rules--which build both upwards and downwards--and lexical rules--which build upwards only. GrammarRule /// subclasses this function to add the down cache for non-key-daughters. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public virtual void AnalyzeRuleCompatibility(IEnumerable<Rule> key_mother_check, IEnumerable<Rule> daughter_check) { /// find the mother rules for which this rule is compatible with its KEY daughter _key_mothers = new HashSet<Rule>(); foreach (Rule mother in key_mother_check) if (tm.da.UnifyCheck(Expanded, mother.RuleDaughters[mother.KeyDaughterIndex])) _key_mothers.Add(mother); } public HashSet<Rule> CompatibleKeyMothers { get { return _key_mothers; } } public override string ToString() { return String.Format("{0}", Name); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public partial class GrammarRule : Rule { public GrammarRule(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } public GrammarRule(Type t, BinaryReader br) : base(t, br) { } HashSet<Rule>[] _non_key_daughters = null; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public override void AnalyzeRuleCompatibility(IEnumerable<Rule> key_mother_check, IEnumerable<Rule> daughter_check) { base.AnalyzeRuleCompatibility(key_mother_check, daughter_check); /// find the rules which are compatible with each non-KEY daughter position of this rule _non_key_daughters = new HashSet<Rule>[RuleDaughters.Length]; for (int i = 0; i < RuleDaughters.Length; i++) { if (i != KeyDaughterIndex) { _non_key_daughters[i] = new HashSet<Rule>(); TfsSection rd = RuleDaughters[i]; foreach (Rule candidate in daughter_check) if (tm.da.UnifyCheck(candidate.Expanded, rd)) _non_key_daughters[i].Add(candidate); } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public bool CheckDaughterCompatibility(Rule rule, int i_arg) { return _non_key_daughters[i_arg].Contains(rule); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public partial class LexicalRule : Rule { public LexicalRule(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerDisplay("{ToString(),nq}")] public partial class MorphologicalRule : LexicalRule { MorphologySubrule[] morph_subrules = null; public MorphologicalRule(Type t, String name, List<BaseFeatConstraint> bfc, List<MorphologySubrule> morph_subrules) : base(t, name, bfc) { if (morph_subrules != null) this.morph_subrules = morph_subrules.ToArray(); } public IList<MorphologySubrule> Subrules { get { return morph_subrules; } } public override string ToString() { return base.ToString() + String.Format(" subrules: {0}", morph_subrules.Length); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class StartSymbol : StaticExpandEntry { public StartSymbol(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } public StartSymbol(Type t, BinaryReader br) : base(t, br) { } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// abstract public class NodeLabel : StaticExpandEntry { public NodeLabel(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { } public NodeLabel(Type t, BinaryReader br) : base(t, br) { } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerDisplay("{Name}")] public class NodeLabelTemplate : NodeLabel { String label; public Tfs effective; public TfsSection effective_local; public NodeLabelTemplate(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { TypeMgr tm = t.tm; Tfs exp = this.Definition;// this.Expanded; ConstraintRef cr_label; if (!exp.GetEdgeAtPath(exp.Edge, tm.config.nodeLabels.LabelPath, out cr_label)) throw new Exception(String.Format("Node label template '{0}' has no label at '{1}'", Name, tm.config.nodeLabels.LabelPath.StringJoin("."))); label = tm.GetStringValue(cr_label.Constraint.FlagsId); /// Sharing the Mark of 'Expanded' but with a more derived type (computed here) causes /// the 'label' feature to be ignored when unifying for node labels Type t_new = tm.GlbOfMany(exp.AllPools.Exclude(cr_label.i_feat)); effective = exp.GetSection(exp.tm.CreateEdge(t_new, exp.Edge.Mark, false), 0); //if (! effective_local = exp.GetSection(tm.config.nodeLabels.LocalPath); //effective_local = default(TfsEdge); } public NodeLabelTemplate(Type t, BinaryReader br) : base(t, br) { } public String Label { get { return label; } } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerDisplay("{Name}")] public class NodeMetaTemplate : NodeLabel { String meta_prefix; String meta_suffix; public TfsSection effective; public NodeMetaTemplate(Type t, String name, List<BaseFeatConstraint> bfc) : base(t, name, bfc) { TypeMgr tm = t.tm; Tfs exp = this.Definition;//.Expanded; ConstraintRef cref; if (!exp.GetEdgeAtPath(exp.Edge, tm.config.nodeLabels.PrefixPath, out cref)) throw new Exception(String.Format("Node meta template '{0}' has no prefix at '{1}'", Name, tm.config.nodeLabels.PrefixPath.StringJoin("."))); meta_prefix = tm.GetStringValue(cref.Constraint.FlagsId); if (!exp.GetEdgeAtPath(exp.Edge, tm.config.nodeLabels.SuffixPath, out cref)) throw new Exception(String.Format("Node meta template '{0}' has no suffix at '{1}'", Name, tm.config.nodeLabels.SuffixPath.StringJoin("."))); meta_suffix = tm.GetStringValue(cref.Constraint.FlagsId); /// Sharing the Mark of 'Expanded' but with a more derived type (computed here) causes /// the 'prefix' and 'suffix' features to be ignored when unifying /// for now, assuming that the prefix and suffix features are introduced by meta Type t_new = tm.GlbOfMany(exp.AllPools.Where(fix => tm.feat_arr[fix].maximal_type != tm.tt_meta)); effective = exp.GetSection(tm.config.nodeLabels.RecursivePath); if (effective == null) throw new Exception(); } public NodeMetaTemplate(Type t, BinaryReader br) : base(t, br) { } public String Prefix { get { return meta_prefix; } } public String Suffix { get { return meta_suffix; } } }; }