1 /*
2
3 This file is part of the Maude 2 interpreter.
4
5 Copyright 1997-2003 SRI International, Menlo Park, CA 94025, USA.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20
21 */
22
23 //
24 // Implementation of match compilation for A, AUl, AUr and AU theories.
25 //
26
27 struct AU_Term::CP_Sequence
28 {
29 Vector<Bool> sequence; // true = take left term, false = take right term
30 NatSet bound;
31 int cardinality;
32 int firstFlex;
33 };
34
35 local_inline bool
unitVariable(VariableTerm * vt,int index) const36 AU_Term::unitVariable(VariableTerm* vt, int index) const
37 {
38 if (vt == 0)
39 return false;
40 AU_Symbol* s = symbol();
41 Sort* vs = vt->getSort();
42 //
43 // A unit variable has a bound of one imposed by its sort and it cannot take an
44 // identity either because a suitable left/right identity doesn't exist or
45 // its sort is too low.
46 //
47 return (s->sortBound(vs) == 1 && !(idPossible(index) && s->takeIdentity(vs)));
48 }
49
50 void
analyseConstraintPropagation(NatSet & boundUniquely) const51 AU_Term::analyseConstraintPropagation(NatSet& boundUniquely) const
52 {
53 CP_Sequence bestSequence;
54 findConstraintPropagationSequence(boundUniquely, bestSequence);
55 boundUniquely = bestSequence.bound; // deep copy
56 if (bestSequence.sequence.length() == argArray.length() - 1)
57 {
58 AU_Symbol* s = symbol();
59 //
60 // Lone variable or abstracted term in flex part. We
61 // can only guarentee unique binding if our top symbol
62 // does not have a one sided identity.
63 //
64 if (!(s->oneSidedId()))
65 argArray[bestSequence.firstFlex].term->analyseConstraintPropagation(boundUniquely);
66 }
67 }
68
69 LhsAutomaton*
compileLhs2(bool matchAtTop,const VariableInfo & variableInfo,NatSet & boundUniquely,bool & subproblemLikely)70 AU_Term::compileLhs2(bool matchAtTop,
71 const VariableInfo& variableInfo,
72 NatSet& boundUniquely,
73 bool& subproblemLikely)
74 {
75 AU_Symbol* s = symbol();
76 bool oneSidedIdentity = s->oneSidedId();
77 LhsAutomaton* uniqueCollapseAutomaton = 0;
78 if (uniqueCollapseSubtermIndex != NONE)
79 {
80 //
81 // Maybe we should add the variable(s) which will be bound to identity
82 // to this local bound uniquely.
83 //
84 NatSet local(boundUniquely);
85 bool spl;
86 uniqueCollapseAutomaton = argArray[uniqueCollapseSubtermIndex].term->
87 compileLhs(matchAtTop, variableInfo, local, spl);
88 }
89 AU_LhsAutomaton* a = new AU_LhsAutomaton(s,
90 matchAtTop,
91 !(collapseSymbols().empty()),
92 uniqueCollapseAutomaton,
93 variableInfo.getNrProtectedVariables());
94 subproblemLikely = false;
95 int leftPos = 0;
96 int rightPos = argArray.length() - 1;
97 if (!matchAtTop)
98 {
99 //
100 // Build rigid part.
101 //
102 CP_Sequence bestSequence;
103 findConstraintPropagationSequence(boundUniquely, bestSequence);
104 int nrRigid = bestSequence.sequence.length();
105 for (int i = 0; i < nrRigid; i++)
106 {
107 bool leftEnd = bestSequence.sequence[i];
108 int j = leftEnd ? leftPos++ : rightPos--;
109 Term* t = argArray[j].term;
110 VariableTerm* v = dynamic_cast<VariableTerm*>(t);
111 if (v != 0)
112 {
113 a->addRigidVariable(v, leftEnd, idPossible(j));
114 boundUniquely.insert(v->getIndex());
115 }
116 else if (t->ground())
117 a->addRigidGroundAlien(t, leftEnd);
118 else
119 {
120 bool spl;
121 LhsAutomaton* subAutomaton =
122 t->compileLhs(false, variableInfo, boundUniquely, spl);
123 a->addRigidNonGroundAlien(subAutomaton, leftEnd);
124 subproblemLikely = subproblemLikely || spl;
125 }
126 }
127 Assert(boundUniquely == bestSequence.bound, "bound clash");
128 //
129 // Build flex part (special cases).
130 //
131 int nrFlex = rightPos - leftPos + 1;
132 if (nrFlex == 0)
133 {
134 a->complete(AU_LhsAutomaton::GROUND_OUT);
135 return a;
136 }
137 if (nrFlex == 1 && !oneSidedIdentity)
138 {
139 Assert(!matchAtTop, "shouldn't match at top");
140 Tuple& t = argArray[leftPos];
141 if (t.abstractionVariableIndex == NONE)
142 {
143 VariableTerm* vt = dynamic_cast<VariableTerm*>(t.term);
144 a->addFlexVariable(vt, UNDEFINED, idPossible(leftPos));
145 boundUniquely.insert(vt->getIndex());
146 a->complete((s->sortStructure(vt->getSort()) ==
147 AssociativeSymbol::PURE_SORT) ?
148 AU_LhsAutomaton::FAST_LONE_VARIABLE :
149 AU_LhsAutomaton::LONE_VARIABLE);
150 }
151 else
152 {
153 bool matchOurIdentity = t.matchOurIdentity && idPossible(leftPos);
154 Assert(t.collapseToOurSymbol || matchOurIdentity,
155 "should not use abstraction variable");
156 bool spl;
157 a->addFlexAbstractionVariable
158 (t.abstractionVariableIndex,
159 t.term->getComponent()->sort(Sort::ERROR_SORT),
160 t.collapseToOurSymbol ? UNBOUNDED : 1,
161 matchOurIdentity,
162 false,
163 t.term->compileLhs(false, variableInfo, boundUniquely, spl));
164 subproblemLikely = subproblemLikely || spl;
165 a->complete(AU_LhsAutomaton::LONE_VARIABLE);
166 }
167 return a;
168 }
169 }
170 //
171 // Build flex part (general case).
172 //
173 // Greedy matcher:
174 // (1) does not check to see if matched portion is in error sort;
175 // (2) cannot handle the intricacies involved in one sided identity theories;
176 // (3) does not check that enough has been matched if extension present.
177 //
178 bool greedy = !oneSidedIdentity &&
179 (!matchAtTop || collapseSymbols().empty());
180 int fixedLengthBlockStart = NONE;
181 for (int i = leftPos; i <= rightPos; i++)
182 {
183 Tuple& t = argArray[i];
184 greedy = greedy && t.abstractionVariableIndex == NONE &&
185 t.term->greedySafe(variableInfo, boundUniquely);
186 //
187 // Check if thing we match will be of fixed size.
188 //
189 bool fixedSize = (t.abstractionVariableIndex == NONE);
190 VariableTerm* vt = dynamic_cast<VariableTerm*>(t.term);
191 if (vt != 0)
192 {
193 Sort* vs = vt->getSort();
194 bool ip = idPossible(i);
195 bool ti = s->takeIdentity(vs);
196 bool awkward = matchAtTop && !ip && ti;
197 greedy = greedy && !awkward;
198 if (((matchAtTop || ip) && ti) || s->sortBound(vs) > 1)
199 {
200 fixedSize = false;
201 greedy = greedy &&
202 (boundUniquely.contains(vt->getIndex()) ||
203 s->sortStructure(vs) >= AssociativeSymbol::LIMIT_SORT);
204 }
205 }
206 //
207 // If not fixed size, see if we ended a fixed size block; then add
208 // flex variable or flex abstraction variable. Otherwise see if we need
209 // to start a fixed length block.
210 //
211 if (!fixedSize)
212 {
213 if (fixedLengthBlockStart != NONE)
214 {
215 bool spl;
216 addFixedLengthBlock(a, fixedLengthBlockStart,
217 i - fixedLengthBlockStart,
218 variableInfo, boundUniquely, spl);
219 greedy = greedy && !spl;
220 fixedLengthBlockStart = NONE;
221 }
222 bool ip = idPossible(i);
223 if (t.abstractionVariableIndex == NONE)
224 a->addFlexVariable(vt, UNDEFINED, ip);
225 else
226 {
227 bool matchOurIdentity = ip && t.matchOurIdentity;
228 bool awkward = matchAtTop && !ip && t.matchOurIdentity;
229 Assert(t.collapseToOurSymbol || matchOurIdentity || awkward,
230 "should not use abstraction variable");
231 NatSet local(boundUniquely);
232 bool spl;
233 a->addFlexAbstractionVariable
234 (t.abstractionVariableIndex,
235 t.term->getComponent()->sort(Sort::ERROR_SORT),
236 t.collapseToOurSymbol ? UNBOUNDED : 1,
237 matchOurIdentity,
238 awkward,
239 t.term->compileLhs(false, variableInfo, local, spl));
240 }
241 }
242 else
243 {
244 if (fixedLengthBlockStart == NONE)
245 fixedLengthBlockStart = i;
246 }
247 }
248 //
249 // Handle any remaining fixed length block.
250 //
251 if (fixedLengthBlockStart != NONE)
252 {
253 Assert(matchAtTop, "ended flex part with fixed length block");
254 bool spl;
255 addFixedLengthBlock(a, fixedLengthBlockStart,
256 argArray.length() - fixedLengthBlockStart,
257 variableInfo, boundUniquely, spl);
258 greedy = greedy && !spl;
259 }
260 //
261 // Finish up by decideding on a match strategy.
262 //
263 if (!greedy)
264 subproblemLikely = true;
265 a->complete(greedy ? AU_LhsAutomaton::GREEDY : AU_LhsAutomaton::FULL);
266 return a;
267 }
268
269 void
addFixedLengthBlock(AU_LhsAutomaton * a,int blockStart,int blockLength,const VariableInfo & variableInfo,const NatSet & boundUniquely,bool & subproblemLikely)270 AU_Term::addFixedLengthBlock(AU_LhsAutomaton* a,
271 int blockStart,
272 int blockLength,
273 const VariableInfo& variableInfo,
274 const NatSet& boundUniquely,
275 bool& subproblemLikely)
276 {
277 //
278 // For each possible shift factor sh from 1 to blockLength - 1 we
279 // find the index of the rightmost pattern p such that if the p matches
280 // some subject s (possibly with a subproblem that may or not be soluble),
281 // the pattern q that is sh places to the left of p will fail early
282 // on s, thus ruling out a shift of sh during matching.
283 //
284 Vector<int> largestIndexThatFails(blockLength);
285 for (int shift = 1; shift < blockLength; shift++)
286 {
287 largestIndexThatFails[shift] = -1; // default; shift is never ruled out
288 for (int i = blockLength - 1; i >= shift; i--)
289 {
290 int b = blockStart + i;
291 Term* p = argArray[b].term; // assume p matched subject
292 Term* q = argArray[b - shift].term; // q will get p's subject after a shift
293 if (q->earlyMatchFailOnInstanceOf(p))
294 {
295 largestIndexThatFails[shift] = i;
296 break;
297 }
298 }
299 }
300 //
301 // For each pattern p we find the smallest shift that is not ruled out
302 // when a match for that pattern fails and matches for all the patterns
303 // to the right of it succeed (modulo a possible subproblem).
304 //
305 subproblemLikely = false;
306 for (int i = 0; i < blockLength; i++)
307 {
308 int b = blockStart + i;
309 Term* p = argArray[b].term;
310 int shift = 1;
311 for (; shift < blockLength; shift++)
312 {
313 //
314 // A shift can be ruled out because a match to the right
315 // of p;
316 //
317 if (i < largestIndexThatFails[shift])
318 continue;
319 //
320 // Or because the p is more general than the pattern
321 // that will get it's subject after the shift. Here we need
322 // to be careful because variables bound by an external agency
323 // can invalidate subsumption. We rely on the convention that
324 // the external agency adds any variables that it might bind
325 // to the set of condition variables.
326 //
327 if (i >= shift &&
328 p->occursBelow().disjoint(variableInfo.getConditionVariables()) &&
329 p->subsumes(argArray[b - shift].term, true))
330 continue;
331 else
332 break;
333 }
334 VariableTerm* v = dynamic_cast<VariableTerm*>(p);
335 if (v != 0)
336 a->addFlexVariable(v, shift, false);
337 else if (p->ground())
338 a->addFlexGroundAlien(p, shift);
339 else
340 {
341 NatSet local(boundUniquely);
342 bool spl;
343 LhsAutomaton* subAutomaton =
344 p->compileLhs(false, variableInfo, local, spl);
345 a->addFlexNonGroundAlien(subAutomaton, shift);
346 subproblemLikely = subproblemLikely || spl;
347 }
348 }
349 }
350
351 void
findConstraintPropagationSequence(const NatSet & boundUniquely,CP_Sequence & bestSequence) const352 AU_Term::findConstraintPropagationSequence(const NatSet& boundUniquely,
353 CP_Sequence& bestSequence) const
354 {
355 DebugAdvisory("toplevel findConstraintPropagationSequence() - array length = " << argArray.length() <<
356 " subterm = " << this);
357 Vector<Bool> currentSequence;
358 bestSequence.cardinality = -1;
359 findConstraintPropagationSequence(currentSequence, boundUniquely,
360 0, argArray.length() - 1, bestSequence);
361 }
362
363 void
findConstraintPropagationSequence(const Vector<Bool> & currentSequence,const NatSet & boundUniquely,int leftPos,int rightPos,CP_Sequence & bestSequence) const364 AU_Term::findConstraintPropagationSequence(const Vector<Bool>& currentSequence,
365 const NatSet& boundUniquely,
366 int leftPos,
367 int rightPos,
368 CP_Sequence& bestSequence) const
369 {
370 if (leftPos <= rightPos)
371 {
372 //
373 // Try to grow search tree.
374 //
375 // (1) If left or right term is not abstracted and grounds out
376 // match it next and don't consider other possibilities.
377 //
378 const Tuple& lt = argArray[leftPos];
379 bool leftBad = lt.collapseToOurSymbol || (lt.matchOurIdentity && idPossible(leftPos));
380 if (!leftBad && boundUniquely.contains(lt.term->occursBelow()))
381 {
382 DebugAdvisory("lower level findConstraintPropagationSequence() - ground out left " << leftPos);
383 Vector<Bool> newSequence(currentSequence);
384 newSequence.append(true);
385 findConstraintPropagationSequence(newSequence, boundUniquely, leftPos + 1,
386 rightPos, bestSequence);
387 return;
388 }
389 const Tuple& rt = argArray[rightPos];
390 bool rightBad = (leftPos == rightPos) || rt.collapseToOurSymbol ||
391 (rt.matchOurIdentity && idPossible(rightPos));
392 if (!rightBad && boundUniquely.contains(rt.term->occursBelow()))
393 {
394 DebugAdvisory("lower level findConstraintPropagationSequence() - ground out right " << rightPos);
395 Vector<Bool> newSequence(currentSequence);
396 newSequence.append(false);
397 findConstraintPropagationSequence(newSequence, boundUniquely, leftPos,
398 rightPos - 1, bestSequence);
399 return;
400 }
401 //
402 // (2) If left or right term is a variable of unit sort which cannot take
403 // identity match it next and don't consider other possibilities.
404 //
405 VariableTerm* ltVar = dynamic_cast<VariableTerm*>(lt.term);
406 if (unitVariable(ltVar, leftPos))
407 {
408 DebugAdvisory("lower level findConstraintPropagationSequence() - unit var left " << leftPos);
409 Vector<Bool> newSequence(currentSequence);
410 newSequence.append(true);
411 NatSet newBound(boundUniquely);
412 newBound.insert(ltVar->getIndex());
413 findConstraintPropagationSequence(newSequence, newBound, leftPos + 1,
414 rightPos, bestSequence);
415 return;
416 }
417 VariableTerm* rtVar = dynamic_cast<VariableTerm*>(rt.term);
418 if (leftPos < rightPos && unitVariable(rtVar, rightPos))
419 {
420 DebugAdvisory("lower level findConstraintPropagationSequence() - unit var right " << rightPos);
421 Vector<Bool> newSequence(currentSequence);
422 newSequence.append(false);
423 NatSet newBound(boundUniquely);
424 newBound.insert(rtVar->getIndex());
425 findConstraintPropagationSequence(newSequence, newBound, leftPos,
426 rightPos - 1, bestSequence);
427 return;
428 }
429 //
430 // If left or right term is a non-abstracted alien then consider
431 // matching it next but also consider other possibilities.
432 //
433 bool growth = false;
434 if (!leftBad && ltVar == 0)
435 {
436 DebugAdvisory("lower level findConstraintPropagationSequence() - alien left " << leftPos);
437 Vector<Bool> newSequence(currentSequence);
438 newSequence.append(true);
439 NatSet newBound(boundUniquely);
440 lt.term->analyseConstraintPropagation(newBound);
441 findConstraintPropagationSequence(newSequence, newBound, leftPos + 1,
442 rightPos, bestSequence);
443 growth = true;
444 }
445 if (bestSequence.sequence.length() >= argArray.length() - 1)
446 {
447 //
448 // All arguments or all arguments but one have a forced match (with the left over argument taking what is left).
449 // There will be no branching at match time, so considering an alternative order is a waste of time.
450 //
451 DebugAdvisory("lower level findConstraintPropagationSequence() - aborting right branch");
452 return;
453 }
454 if (!rightBad && rtVar == 0)
455 {
456 DebugAdvisory("lower level findConstraintPropagationSequence() - alien right " << rightPos);
457 Vector<Bool> newSequence(currentSequence);
458 newSequence.append(false);
459 NatSet newBound(boundUniquely);
460 rt.term->analyseConstraintPropagation(newBound);
461 findConstraintPropagationSequence(newSequence, newBound, leftPos,
462 rightPos - 1, bestSequence);
463 growth = true;
464 }
465 if (growth)
466 return;
467 }
468 int n = boundUniquely.cardinality();
469 if (n > bestSequence.cardinality ||
470 (n == bestSequence.cardinality &&
471 currentSequence.length() > bestSequence.sequence.length()))
472 {
473 bestSequence.sequence = currentSequence; // deep copy
474 bestSequence.bound = boundUniquely; // deep copy
475 bestSequence.cardinality = n;
476 bestSequence.firstFlex = leftPos <= rightPos ? leftPos : NONE;
477 }
478 }
479