//#define ASYNC_UNIFY using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using glue.Collections.XSpinLock; using glue.Tasks; using glue.Extensions.Enumerable; using glue.Debugging; using glue.Tokenization; namespace agree.Parse { abstract public partial class SubscriptionChart : TaskCancellationChart { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active chart edge: binds a sequence interlock to a passive edge notification closure /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract class ActiveEdge : AtomicSequenceObject, IDisposable { public ActiveEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce) : base(c.AtomicStamp) { this.m_chart = c; this.mother = m; this.rgce = rgce; this.f_span_only = m.IsSpanOnly; } readonly protected UnificationParseChart m_chart; protected IGrammarRule mother; readonly protected IParseChartEdge[] rgce; readonly protected bool f_span_only; public abstract int TryMatchEdge(IParseChartEdge ce); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Dispose() { //IParseChartRule m = Interlocked.CompareExchange(ref mother, null, mother); //if (m != null) // m.Dispose(); mother.Dispose(); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active right edge: a partial left edge. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class ActiveRightEdge : ActiveEdge { TfsEdge daughter; readonly int i_arg, i_chart_limit; readonly bool f_last; public ActiveRightEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce) : base(c, m, rgce) { IList<TfsEdge> rd = mother.RuleDaughters; this.i_arg = rgce.Length; this.i_chart_limit = m_chart.ColumnCount - (rd.Count - i_arg); this.daughter = rd[i_arg]; this.f_last = i_arg == mother.RuleDaughters.Count - 1; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// The active edge tries to match the given PassiveChart edge. Three cases are possible: /// 1.) the specified edge does not extend this active edge; or /// 2.) the rule is complete, create a new passive edge and insert it into the chart; or /// 3.) the rule is still not complete: create a new active edge which incorporates the new partial result. /// In any of these three cases, this rule continues to accept future edges as before. /// This function preserves the thread-safety of the user-supplied Unification callback, if any. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public override int TryMatchEdge(IParseChartEdge ce) { // if the candidate edge doesn't fit, don't bother unifying if (ce.ChartSpan.EndIndex > i_chart_limit) return 0; if (f_last && f_span_only && ce.ChartSpan.EndIndex < m_chart.ColumnCount - 1) return 0; if (ce.License is Rule && !mother.CheckDaughterCompatibility(ce.License, i_arg)) return 0; if (m_chart.QuickCheck(daughter, ce.Contents)) return 0; #if ASYNC_UNIFY m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t => { if (!t.Result.Equals(default(TfsEdge))) ProcessMatchedEdge(ce, t.Result); }, m_chart.CancelTok, TaskContinuationOptions.AttachedToParent, TaskScheduler.Default); #else int cu = 1; TfsEdge result; if (m_chart.UnifyPartial(mother, daughter, ce.Contents, out result, f_last)) { IParseChartEdge[] rgce_new = rgce.Append(ce).ToArray(); if (i_arg < mother.RuleDaughters.Count - 1) { IGrammarRule gr_result = m_chart.IncorporatePart(mother, result); cu += m_chart.SubscribeRightOf(ce.ChartSpan.EndIndex, new ActiveRightEdge(m_chart, gr_result, rgce_new)); } else cu += m_chart.FinishMatchedEdge(mother.License, result, new Span(rgce_new[0].ChartSpan.StartIndex, ce.ChartSpan.EndIndex), rgce_new); } return cu; #endif } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active left edge: a partial right edge. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class ActiveLeftEdge : ActiveEdge { TfsEdge daughter; readonly int i_arg; public ActiveLeftEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce) : base(c, m, rgce) { IList<TfsEdge> rd = m.RuleDaughters; this.i_arg = rd.Count - rgce.Length - 1; this.daughter = rd[i_arg]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// The active edge tries to match the given PassiveChart edge. Three cases are possible: /// 1.) the specified edge does not extend this active edge; or /// 2.) the rule is complete, create a new passive edge and insert it into the chart; or /// 3.) the rule is still not complete: create a new active edge which incorporates the new partial result. /// In any of these three cases, this rule continues to accept future edges as before. /// This function preserves the thread-safety of the user-supplied Unification callback, if any. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public override int TryMatchEdge(IParseChartEdge ce) { // if the candidate edge doesn't fit, don't bother unifying if (ce.ChartSpan.StartIndex < i_arg) return 0; if (i_arg == 0 && f_span_only && ce.ChartSpan.StartIndex > 0) return 0; if (ce.License is Rule && !mother.CheckDaughterCompatibility(ce.License, i_arg)) return 0; if (m_chart.QuickCheck(daughter, ce.Contents)) return 0; #if ASYNC_UNIFY m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, i_arg == 0).ContinueWith(t => { if (!t.Result.Equals(default(TfsEdge))) ProcessMatchedEdge(ce, t.Result); }, m_chart.CancelTok, TaskContinuationOptions.AttachedToParent, TaskScheduler.Default); #else int cu = 1; TfsEdge result; if (m_chart.UnifyPartial(mother, daughter, ce.Contents, out result, i_arg == 0)) { IParseChartEdge[] rgce_new = rgce.Prepend(ce).ToArray(); if (i_arg > 0) { IGrammarRule gr_result = m_chart.IncorporatePart(mother, result); cu += m_chart.SubscribeLeftOf(ce.ChartSpan.StartIndex, new ActiveLeftEdge(m_chart, gr_result, rgce_new)); } else cu += m_chart.FinishMatchedEdge(mother.License, result, new Span(ce.ChartSpan.StartIndex, rgce_new[rgce_new.Length - 1].ChartSpan.EndIndex), rgce_new); } return cu; #endif } }; }; }