using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using glue.Tasks; using glue.Collections.XSpinLock; namespace agree.Parse { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// S U B S C R I P T I O N C H A R T /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// abstract public partial class SubscriptionChart<T> : TaskCancellationChart<T> where T : ChartBase<T>.IMotherDaughter, IEquatable<T> { internal SubscriptionChart(ParserConfiguration config, ISysObj so_owner, String source_text, int c_cols) : base(config, so_owner, source_text, c_cols) { this.srcs_leftof = new LeftNotificationSource[c_cols - 1]; this.srcs_rightof = new RightNotificationSource[c_cols - 1]; for (int i=0; i < c_cols - 1; i++) { srcs_leftof[i] = new LeftNotificationSource(this, i + 1); srcs_rightof[i] = new RightNotificationSource(this, i); } } LeftNotificationSource[] srcs_leftof; RightNotificationSource[] srcs_rightof; internal void SubscribeRightOf(int i_col, ActiveEdge ace) { srcs_rightof[i_col].Subscribe(ace); } internal void SubscribeLeftOf(int i_col, ActiveEdge ace) { srcs_leftof[i_col - 1].Subscribe(ace); } protected abstract void GenerateRules(IParseChartEdge pce); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Accept new edges into the chart. Atomically with insertion into the chart, stamp the edge with an interlocking /// sequence barrier in order to separate interested parties into two groups: those who registered previously--and /// who will receive the edge now, versus those who register at any point afterwards, who will receive the edge via /// the retroactive edge delivery mechanism which is part of the subscription process. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if false internal Task AddEdgeAsync(PassiveEdge pce) { return StartAttachedChildTask(AddEdgeInner, pce); } #endif internal void AddEdgeInner(IParseChartEdge pce) { if (ParseTime.TotalSeconds > config.ItemTimeoutSeconds) throw new ParseTimeoutException(); // add edge to chart with its default inert sequence stamp. this stamps it with a sequence barrier AddEdge(pce); // now that there's a sequence ID, print out some stuff ChartEdgeReport(pce); // a task for generating new rules StartAttachedChildTask(GenerateRules, pce); // send the edge to interested parties: // 1. parties for which this item is to the right int i_start = pce.ChartSpan.StartIndex; if (i_start > 0) srcs_rightof[i_start - 1].SendToAllAsync(pce); // 2. parties for which this item is to the left int i_end = pce.ChartSpan.EndIndex; if (i_end < ColumnCount - 1) srcs_leftof[(i_end - 1) + 1].SendToAllAsync(pce); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Clearing subscribers allows references to active edges which were not productive during the parse to be released /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected void ClearSubscribers() { foreach (NotificationSource ns in srcs_leftof.AsEnumerable<NotificationSource>().Concat(srcs_rightof)) foreach (ActiveEdge ae in ns.EnumerateSeq(AtomicStamp.InertValue)) ae.Dispose(); srcs_leftof = null; srcs_rightof = null; } }; }