//#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<T> : TaskCancellationChart<T> where T : ChartBase<T>.IMotherDaughter, IEquatable<T> { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active chart edge: binds a sequence interlock to a passive edge notification closure /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract class ActiveEdge : AtomicSequenceObject, IDisposable { public ActiveEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only) : base(c.AtomicStamp) { this.m_chart = c; this.mother = m; this.rgce = rgce; this.f_span_only = f_span_only; } readonly protected UnificationParseChart<T> m_chart; protected IGrammarRule mother; readonly protected IParseChartEdge[] rgce; readonly protected bool f_span_only; public abstract void TryMatchEdge(IParseChartEdge ce); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Dispose() { IGrammarRule m = Interlocked.CompareExchange(ref mother, null, mother); if (m != null) m.Dispose(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected void FinishMatchedEdge(T result, Span span, IParseChartEdge[] rgce_new) { //if (new System.Text.RegularExpressions.Regex("FinishMatchedEdge").Matches(new StackTrace().ToString()).Count > 20) //{ // m_chart.DumpChart(@"d:\chart_dump.html"); // Environment.Exit(0); // throw new ParseException("cyclic"); //} DerivedPassiveEdge nce = null; // try to match the start symbol if (span == m_chart.EntireSpan) { foreach (ISelf start_sym in m_chart.start_symbols) { if (m_chart.CanUnify(start_sym.Self, result)) { nce = new CompletedParse(this.m_chart, span, result, rgce_new, start_sym); if (m_chart.debug_output != null) m_chart.debug_output.WriteLineColor("$darkgreen {0}$ matched start symbol $magenta {1}", nce, start_sym); break; } } } nce = nce ?? new DerivedPassiveEdge(m_chart, span, result, rgce_new); m_chart.StartAttachedChildTask(m_chart.AddEdgeInner, nce);/*.ContinueWith(t => { if (t.Exception != null) Debugger.Break(); });*/ } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active right edge: a partial left edge. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class ActiveRightEdge : ActiveEdge { readonly T daughter; readonly int i_arg, i_chart_limit; public ActiveRightEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only) : base(c, m, rgce, f_span_only) { IList<T> rd = mother.RuleDaughters; this.i_arg = rgce == null ? 0 : rgce.Length; this.i_chart_limit = m_chart.ColumnCount - (rd.Count - i_arg); 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 void TryMatchEdge(IParseChartEdge ce) { // if the candidate edge doesn't fit, don't bother unifying if (ce.ChartSpan.EndIndex > i_chart_limit) return; if (m_chart.QuickCheck(daughter, ce.Self) == UnificationParseChart<T>.QuickCheckResult.Failed) return; // allows client to (e.g.) delete rule daughters if the unification succeeds bool f_last = i_arg == mother.RuleDaughters.Count - 1; if (f_last && f_span_only && ce.ChartSpan.EndIndex < m_chart.ColumnCount - 1) return; #if ASYNC_UNIFY m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t => { if (!t.Result.Equals(default(T))) ProcessMatchedEdge(ce, t.Result); }, m_chart.CancelTok, TaskContinuationOptions.AttachedToParent, TaskScheduler.Default); #else T result; if (m_chart.UnifyPartial(mother, daughter, ce.Self, out result, f_last)) ProcessMatchedEdge(ce, result); #endif } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ProcessMatchedEdge(IParseChartEdge ce, T result) { IParseChartEdge[] rgce_new = rgce.Append(ce).ToArray(); if (i_arg < mother.RuleDaughters.Count - 1) { IGrammarRule gr_result = m_chart.IncorporatePart(mother, result); m_chart.SubscribeRightOf(ce.ChartSpan.EndIndex, new ActiveRightEdge(m_chart, gr_result, rgce_new, f_span_only)); } else FinishMatchedEdge(result, new Span(rgce_new[0].ChartSpan.StartIndex, ce.ChartSpan.EndIndex), rgce_new); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Active left edge: a partial right edge. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class ActiveLeftEdge : ActiveEdge { readonly T daughter; readonly int i_arg; public ActiveLeftEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only) : base(c, m, rgce, f_span_only) { IList<T> rd = m.RuleDaughters; this.i_arg = rd.Count - (rgce == null ? 0 : 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 void TryMatchEdge(IParseChartEdge ce) { // if the candidate edge doesn't fit, don't bother unifying if (ce.ChartSpan.StartIndex < i_arg) return; if (m_chart.QuickCheck(daughter, ce.Self) == UnificationParseChart<T>.QuickCheckResult.Failed) return; bool f_last = i_arg == 0; if (f_last && f_span_only && ce.ChartSpan.StartIndex > 0) return; #if ASYNC_UNIFY m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t => { if (!t.Result.Equals(default(T))) ProcessMatchedEdge(ce, t.Result); }, m_chart.CancelTok, TaskContinuationOptions.AttachedToParent, TaskScheduler.Default); #else T result; if (m_chart.UnifyPartial(mother, daughter, ce.Self, out result, f_last)) ProcessMatchedEdge(ce, result); #endif } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ProcessMatchedEdge(IParseChartEdge ce, T result) { IParseChartEdge[] rgce_new = rgce.Prepend(ce).ToArray(); if (i_arg > 0) { IGrammarRule gr_result = m_chart.IncorporatePart(mother, result); m_chart.SubscribeLeftOf(ce.ChartSpan.StartIndex, new ActiveLeftEdge(m_chart, gr_result, rgce_new, f_span_only)); } else FinishMatchedEdge(result, new Span(ce.ChartSpan.StartIndex, rgce_new[rgce_new.Length - 1].ChartSpan.EndIndex), rgce_new); } }; }; }