evalTemporalAtomWithoutOperator looks up facts and optionally binds/filters by interval.
( atom ast.Atom, interval *ast.Interval, subst unionfind.UnionFind, )
| 81 | |
| 82 | // evalTemporalAtomWithoutOperator looks up facts and optionally binds/filters by interval. |
| 83 | func (te *TemporalEvaluator) evalTemporalAtomWithoutOperator( |
| 84 | atom ast.Atom, |
| 85 | interval *ast.Interval, |
| 86 | subst unionfind.UnionFind, |
| 87 | ) ([]unionfind.UnionFind, error) { |
| 88 | var solutions []unionfind.UnionFind |
| 89 | |
| 90 | // Resolve the query interval if provided |
| 91 | var queryInterval ast.Interval |
| 92 | var hasConcreteInterval bool |
| 93 | var err error |
| 94 | |
| 95 | if interval != nil { |
| 96 | // Try to resolve bounds to concrete timestamps |
| 97 | resolved, err := ResolveHeadTime(interval, subst, te.evaluationTime) |
| 98 | if err == nil && resolved != nil { |
| 99 | queryInterval = *resolved |
| 100 | hasConcreteInterval = true |
| 101 | } else { |
| 102 | // If we can't resolve (e.g. unbound variables), we'll filter after retrieval |
| 103 | // But for now, let's assume we fetch all and filter/bind |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | // Helper to process a fact |
| 108 | processFact := func(tf factstore.TemporalFact) error { |
| 109 | // Check if the fact is valid at current evaluation time (if no specific interval requested) |
| 110 | // OR if it satisfies the requested interval |
| 111 | if !hasConcreteInterval { |
| 112 | // Only filter by evaluation time if no interval was requested at all |
| 113 | if interval == nil && !tf.Interval.Contains(te.evaluationTime) { |
| 114 | return nil |
| 115 | } |
| 116 | } else { |
| 117 | // If we have a concrete query interval, what does it mean? |
| 118 | // foo(X)@[t1, t2] usually means we are looking for facts that cover this interval |
| 119 | // or are equal to it? |
| 120 | // DatalogMTL semantics usually imply: |
| 121 | // If interval is variable @[T], we bind T to the fact's interval. |
| 122 | // If interval is concrete @[t1, t2], we filter facts that COVER [t1, t2]. |
| 123 | // Let's implement containment check: fact.Interval MUST CONTAIN queryInterval. |
| 124 | if !te.intervalContains(tf.Interval, queryInterval) { |
| 125 | return nil |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | // Unify the fact with the query atom |
| 130 | newSubst, err := unionfind.UnifyTermsExtend(atom.Args, tf.Atom.Args, subst) |
| 131 | if err != nil { |
| 132 | return nil // No match, continue |
| 133 | } |
| 134 | |
| 135 | // Bind interval variables if present in the original query interval |
| 136 | if interval != nil { |
| 137 | newSubst = te.bindIntervalVariables(*interval, tf.Interval, newSubst) |
| 138 | } |
| 139 | |
| 140 | solutions = append(solutions, newSubst) |
no test coverage detected