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