using System; using System.Diagnostics; using System.Linq; using miew.Debugging; namespace agree { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// N-way unification testing /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if DEBUG public unsafe class NWayCheck #else public unsafe struct NWayCheck #endif { const sbyte TfsCheckBottom = -1; const sbyte TfsCheckOk = 0; const int CorefsMax = 500; const int ArgArityMax = 11; const int CorefArityMax = 20; const int MaxParticipants = 16; // (13) [DebuggerBrowsable(DebuggerBrowsableState.Never)] int[][] rgrgfix; [DebuggerBrowsable(DebuggerBrowsableState.Never)] TypeMgr tm; Tfs[] participants; [DebuggerBrowsable(DebuggerBrowsableState.Never)] byte c_participants; [DebuggerBrowsable(DebuggerBrowsableState.Never)] NwayArgs* _corefs, pc_next; [DebuggerBrowsable(DebuggerBrowsableState.Never)] NwayArgs** ppmb_next, pp_map_base; [DebuggerBrowsable(DebuggerBrowsableState.Never)] NwayArgs*** participant_bases; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public NWayCheck(TypeMgr tm) #if !DEBUG : this() #endif { this.tm = tm; this.rgrgfix = tm.rgrgfix_by_type; this.participants = new Tfs[MaxParticipants]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// n-way unification : locates and traverses a unilateral, type-compatible monotonic descent, if one exists, through /// any number of internally coreferenced typed feature structures (TFS). /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public bool _check(NwayArgs* pnwa_in) { NwayArgs nwa; foreach (int i_feat in rgrgfix[(int)(pnwa_in->f & Edge.Flag.MultiIdMask)]) { bool f_expand = false; NwayArgs* pc = null; NwayArgs* pnwa = &nwa; pnwa->c_args = 0; pnwa->f = 0; for (int nm, i = 0; i < pnwa_in->c_args; i++) { byte tfsix = pnwa_in->rg_tfsix[i]; Tfs tfs = participants[tfsix]; Edge.Flag nf = tfs.TryGetFlagsMark(i_feat, pnwa_in->marks[i], out nm); if (nf == 0) continue; nf = nf & ~(Edge.Flag.Coreference | Edge.Flag.PrunedDuringParsing); if (pnwa->f == 0 || pnwa->f == nf) { pnwa->f = nf; f_expand = false; } else if (nf != 0) { Edge.Flag prev = pnwa->f; pnwa->f = tm.UnifyTypesNoCoref(prev, nf); if (pnwa->f == Edge.Flag.Bottom) return false; if (pnwa->f == nf) f_expand = false; else if (pnwa->f != prev) f_expand = true; /* note: if (f == prev), retain previous f_expand state */ } if (nm < 0) { NwayArgs* pc_cur = participant_bases[tfsix][~nm]; if (pc_cur == null) { byte c_remain = tfs.coref_tally_map[~nm]; Debug.Assert(c_remain > 0); // orphan corefs (m<0) are not added to coref table. thus, it is not an error for // a negative mark to appear in the nwa if (c_remain <= 1) goto coref_bit_false_positive; if (pc == null) { // nwa can have orphan marks (m<0) Debug.Assert(tfsix < c_participants); pc = participant_bases[tfsix][~nm] = pc_next++; if (nwa.c_args == 0) pc->f = nwa.f; else *pc = nwa; pc->c_remain = c_remain; pnwa = pc; } else { pc->c_remain += c_remain; participant_bases[tfsix][~nm] = pc; } } else { if (!tm.UnifyInTypeNoCoref(ref pnwa->f, pc_cur->f)) return false; if (pc == null) { pc = pc_cur; pc->f = nwa.f; pnwa = pc; for (byte j = 0; j < nwa.c_args; j++) AddResumePoint(pnwa, nwa.rg_tfsix[j], nwa.marks[j]); } else if (pc != pc_cur) { pc->c_remain += pc_cur->c_remain; pc_cur->c_remain = 0; for (byte j = 0; j < pc_cur->c_args; j++) { byte _tfsix = pc_cur->rg_tfsix[j]; int _m = pc_cur->marks[j]; AddResumePoint(pnwa, _tfsix, _m); if (_m < 0) participant_bases[_tfsix][~_m] = pc; } } } Debug.Assert(pc->c_remain > 0); pc->c_remain--; } coref_bit_false_positive: if (nm != 0) { //CheckAddResumePoint(pnwa, tfsix, nm); for (byte j = 0; j < pnwa->c_args; j++) if (pnwa->marks[j] == nm && pnwa->rg_tfsix[j] == tfsix) goto ah3; byte ii = pnwa->c_args++; pnwa->rg_tfsix[ii] = tfsix; pnwa->marks[ii] = nm; ah3: ; } } if (f_expand && (pnwa->f & Edge.Flag.EtmNonBareType) != 0) { Tfs tfs_exp = tm.type_arr[(int)(pnwa->f & Edge.Flag.MultiIdMask)].Expanded; Debug.Assert(!(tfs_exp is BareTfs)); AddResumePoint(pnwa, AddTfs(tfs_exp), tfs_exp.Edge.Mark); } if (pnwa->c_args != 0 && (pc == null || pc->c_remain == 0) && (pnwa->f & Edge.Flag.EtmNonBareType) != 0) { if (!_check(pnwa)) return false; } } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AddResumePoint(NwayArgs* pnwa, byte tfsix, int m) { Debug.Assert(m != 0); Debug.Assert(tfsix < c_participants); if (pnwa->c_args >= ArgArityMax) throw new NotImplementedException(String.Format("need n-way arg arity {0}", pnwa->c_args + 1)); byte i = pnwa->c_args++; pnwa->rg_tfsix[i] = tfsix; pnwa->marks[i] = m; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// In general, coreferenced marks should not be added with this function, because it this does not do /// any re-pointing. However, orphan corefs are not entered into the coref table and thus can appear in the nwa /// prior to discovering a proper coref amongst the current args. They do not need repointing. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool CheckAddResumePoint(NwayArgs* pnwa, byte tfsix, int m) { Debug.Assert(m != 0); for (byte j = 0; j < pnwa->c_args; j++) if (pnwa->rg_tfsix[j] == tfsix && pnwa->marks[j] == m) return false; AddResumePoint(pnwa, tfsix, m); return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// byte AddTfs(Tfs tfs) { if (c_participants >= MaxParticipants) throw new InvalidOperationException("need more participant slots"); if (tfs.coref_tally_map == null) ((TfsSection)tfs)._load_coref_tally_map(); byte tfsix = c_participants++; participants[tfsix] = tfs; participant_bases[tfsix] = ppmb_next; ppmb_next += tfs.coref_tally_map.Length; if (ppmb_next - pp_map_base > CorefsMax) throw new InvalidOperationException("need more coref slots"); return tfsix; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool _check(Edge.Flag f, Tfs tfs1, Tfs tfs2) { NwayArgs* _p = stackalloc NwayArgs[CorefsMax]; pc_next = _corefs = _p; NwayArgs** _pp = stackalloc NwayArgs*[CorefsMax]; ppmb_next = pp_map_base = _pp; NwayArgs*** _ppp = stackalloc NwayArgs**[MaxParticipants]; participant_bases = _ppp; NwayArgs nwa; AddResumePoint(&nwa, AddTfs(tfs1), tfs1.Edge.Mark); AddResumePoint(&nwa, AddTfs(tfs2), tfs2.Edge.Mark); nwa.f = f; if (!_check(&nwa)) return false; /// Self-blocking structures will result in non-zero tally remainders. The presence of such structure implies /// unification failure. for (NwayArgs* pc = _corefs; pc < pc_next; pc++) if (pc->c_remain != 0) return false; return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static bool Check(Tfs tfs1, Tfs tfs2) { TypeMgr tm = tfs1.tm; Edge.Flag nf = tm.UnifyTypesNoCoref( tfs1.Edge.FlagsId & ~Edge.Flag.Coreference, tfs2.Edge.FlagsId & ~Edge.Flag.Coreference); if (nf == Edge.Flag.Bottom) return false; if ((nf & Edge.Flag.EtmNonBareType) == 0) return true; Debug.Assert(nf == tfs1.Edge.FlagsId || nf == tfs2.Edge.FlagsId); return new NWayCheck(tm)._check(nf, tfs1, tfs2); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public partial struct NwayArgs { [DebuggerBrowsable(DebuggerBrowsableState.Never)] public byte c_args; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public fixed byte rg_tfsix[ArgArityMax]; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public fixed int marks[ArgArityMax]; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Edge.Flag f; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public int c_remain; //public int TypeId { get { return (int)(f & Edge.Flag.MultiIdMask); } } }; }; }