Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
osupport.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: osupport.cpp
11  *
12  * =======================================================================
13  * Calculate_support_for_instantiation_preferences() does run-time o-support
14  * calculations -- it fills in pref->o_supported in each pref. on the
15  * instantiation. Calculate_compile_time_o_support() does the compile-time
16  * version: it takes the LHS and RHS, and fills in the a->support field in
17  * each RHS action with either UNKNOWN_SUPPORT, O_SUPPORT, or I_SUPPORT.
18  * =======================================================================
19  */
20 
21 
22 /* =========================================================================
23  O Support calculation routines.
24  ========================================================================= */
25 
26 #include <stdlib.h>
27 
28 #include "osupport.h"
29 #include "symtab.h"
30 #include "wmem.h"
31 #include "gdatastructs.h"
32 #include "agent.h"
33 #include "kernel.h"
34 #include "production.h"
35 #include "instantiations.h"
36 #include "rhsfun.h"
37 #include "print.h"
38 #include "reorder.h"
39 #include "rete.h"
40 #include "xml.h"
41 
42 /* -----------------------------------------------------------------------
43  O-Support Transitive Closure Routines
44 
45  These routines are used by the o-support calculations to mark transitive
46  closures through TM (= WM+PM) plus (optionally) the RHS-generated pref's.
47 
48  The caller should first call begin_os_tc (rhs_prefs_or_nil). Then
49  add_to_os_tc (id) should be called any number of times to add stuff
50  to the TC. (Note that the rhs_prefs shouldn't be modified between the
51  begin_os_tc() call and the last add_to_os_tc() call.)
52 
53  Each identifier in the TC is marked with id.tc_num=o_support_tc; the
54  caller can check for TC membership by looking at id.tc_num on any id.
55 ----------------------------------------------------------------------- */
56 
57 /* This prototype is needed by the following macros. */
58 void add_to_os_tc (agent* thisAgent, Symbol *id, Bool isa_state);
59 
60 /*#define add_to_os_tc_if_needed(sym) \
61  { if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE) \
62  add_to_os_tc (sym,FALSE); }*/
63 inline void add_to_os_tc_if_needed(agent* thisAgent, Symbol * sym)
64 {
65  if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
66  add_to_os_tc (thisAgent, sym,FALSE);
67 }
68 
69 /*#define add_to_os_tc_if_id(sym,flag) \
70  { if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE) \
71  add_to_os_tc (sym,flag); }*/
72 inline void add_to_os_tc_if_id(agent* thisAgent, Symbol * sym, Bool flag)
73 {
74  if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE) \
75  add_to_os_tc (thisAgent, sym,flag);
76 }
77 
78 /* SBH 4/14/93
79  * For NNPSCM, we must exclude the operator slot from the transitive closure of a state.
80  * Do that by passing a boolean argument, "isa_state" to this routine.
81  * If it isa_state, check for the operator slot before the recursive call.
82  */
83 
84 void add_to_os_tc (agent* thisAgent, Symbol *id, Bool isa_state) {
85  slot *s;
86  preference *pref;
87  wme *w;
88 
89  /* --- if id is already in the TC, exit; else mark it as in the TC --- */
90  if (id->id.tc_num==thisAgent->o_support_tc) return;
91  id->id.tc_num = thisAgent->o_support_tc;
92 
93  /* --- scan through all preferences and wmes for all slots for this id --- */
94  for (w=id->id.input_wmes; w!=NIL; w=w->next)
95  add_to_os_tc_if_needed (thisAgent, w->value);
96  for (s=id->id.slots; s!=NIL; s=s->next) {
97  if ((!isa_state) || (s->attr != thisAgent->operator_symbol)) {
98  for (pref=s->all_preferences; pref!=NIL; pref=pref->all_of_slot_next) {
99  add_to_os_tc_if_needed (thisAgent, pref->value);
100  if (preference_is_binary(pref->type))
101  add_to_os_tc_if_needed (thisAgent, pref->referent);
102  }
103  for (w=s->wmes; w!=NIL; w=w->next)
104  add_to_os_tc_if_needed (thisAgent, w->value);
105  }
106  } /* end of for slots loop */
107  /* --- now scan through RHS prefs and look for any with this id --- */
108  for (pref=thisAgent->rhs_prefs_from_instantiation; pref!=NIL; pref=pref->inst_next) {
109  if (pref->id==id) {
110  if ((!isa_state) || (pref->attr != thisAgent->operator_symbol)) {
111  add_to_os_tc_if_needed (thisAgent, pref->value);
112  if (preference_is_binary(pref->type))
113  add_to_os_tc_if_needed (thisAgent, pref->referent);
114  }
115  }
116  }
117  /* We don't need to worry about goal/impasse wmes here, since o-support tc's
118  never start there and there's never a pointer to a goal or impasse from
119  something else. */
120 }
121 
122 void begin_os_tc (agent* thisAgent, preference *rhs_prefs_or_nil) {
123  thisAgent->o_support_tc = get_new_tc_number(thisAgent);
124  thisAgent->rhs_prefs_from_instantiation = rhs_prefs_or_nil;
125 }
126 
127 /* -----------------------------------------------------------------------
128  Utilities for Testing Inclusion in the O-Support TC
129 
130  After a TC has been marked with the above routine, these utility
131  routines are used for checking whether certain things are in the TC.
132  Test_has_id_in_os_tc() checks whether a given test contains an equality
133  test for any identifier in the TC, other than the identifier
134  "excluded_sym". Id_or_value_of_condition_list_is_in_os_tc() checks whether
135  any id or value test in the given condition list (including id/value tests
136  inside NCC's) has a test for an id in the TC. In the case of value tests,
137  the id is not allowed to be "sym_excluded_from_value".
138 ----------------------------------------------------------------------- */
139 
140 Bool test_has_id_in_os_tc (agent* thisAgent, test t, Symbol *excluded_sym) {
141  cons *c;
142  Symbol *referent;
143  complex_test *ct;
144 
145  if (test_is_blank_test(t)) return FALSE;
147  referent = referent_of_equality_test(t);
148  if (referent->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
149  if (referent->id.tc_num==thisAgent->o_support_tc)
150  if (referent!=excluded_sym)
151  return TRUE;
152  return FALSE;
153  }
154  ct = complex_test_from_test(t);
155  if (ct->type==CONJUNCTIVE_TEST) {
156  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
157  if (test_has_id_in_os_tc (thisAgent, static_cast<char *>(c->first), excluded_sym)) return TRUE;
158  return FALSE;
159  }
160  return FALSE;
161 }
162 
164  Symbol *sym_excluded_from_value,
165  Symbol *match_state_to_exclude_test_of_the_operator_off_of) {
166  /* RBD 8/19/94 Under NNPSCM, when we use this routine to look for "something
167  off the state", we want to exclude tests of (match_state ^operator _). */
168  for ( ; conds!=NIL; conds=conds->next) {
169  switch (conds->type) {
170  case POSITIVE_CONDITION:
171  case NEGATIVE_CONDITION:
173  match_state_to_exclude_test_of_the_operator_off_of) &&
175  thisAgent->operator_symbol))
176  break;
177  if (test_has_id_in_os_tc (thisAgent, conds->data.tests.id_test, NIL))
178  return TRUE;
179  if (test_has_id_in_os_tc (thisAgent, conds->data.tests.value_test,
180  sym_excluded_from_value))
181  return TRUE;
182  break;
184  if (id_or_value_of_condition_list_is_in_os_tc (thisAgent, conds->data.ncc.top,
185  sym_excluded_from_value
186  , match_state_to_exclude_test_of_the_operator_off_of
187  ))
188  return TRUE;
189  break;
190  }
191  }
192  return FALSE;
193 }
194 
195 /* -----------------------------------------------------------------------
196 
197  is_state_id
198 
199  GAP 10-6-94
200 
201  This routine checks to see if the identifier is one of the context
202  objects i.e. it is the state somewhere in the context stack.
203  This is used to ensure that O-support is not given to context objects
204  in super-states.
205 
206 ----------------------------------------------------------------------- */
207 Bool is_state_id(agent* thisAgent, Symbol *sym,Symbol *match_state)
208 {
209  Symbol *c;
210 
211  for(c = thisAgent->top_goal; c != match_state; c = c->id.lower_goal) {
212  if (sym == c)
213  return TRUE;
214  }
215 
216  if (sym == match_state)
217  return TRUE;
218  else
219  return FALSE;
220 }
221 
222 /* -----------------------------------------------------------------------
223  Run-Time O-Support Calculation
224 
225  This routine calculates o-support for each preference for the given
226  instantiation, filling in pref->o_supported (TRUE or FALSE) on each one.
227 
228  The following predicates are used for support calculations. In the
229  following, "lhs has some elt. ..." means the lhs has some id or value
230  at any nesting level.
231 
232  lhs_oa_support:
233  (1) does lhs test (match_goal ^operator match_operator NO) ?
234  (2) mark TC (match_operator) using TM;
235  does lhs has some elt. in TC but != match_operator ?
236  (3) mark TC (match_state) using TM;
237  does lhs has some elt. in TC ?
238  lhs_oc_support:
239  (1) mark TC (match_state) using TM;
240  does lhs has some elt. in TC but != match_state ?
241  lhs_om_support:
242  (1) does lhs tests (match_goal ^operator) ?
243  (2) mark TC (match_state) using TM;
244  does lhs has some elt. in TC but != match_state ?
245 
246  rhs_oa_support:
247  mark TC (match_state) using TM+RHS;
248  if pref.id is in TC, give support
249  rhs_oc_support:
250  mark TC (inst.rhsoperators) using TM+RHS;
251  if pref.id is in TC, give support
252  rhs_om_support:
253  mark TC (inst.lhsoperators) using TM+RHS;
254  if pref.id is in TC, give support
255 
256  BUGBUG the code does a check of whether the lhs tests the match state via
257  looking just at id and value fields of top-level positive cond's.
258  It doesn't look at the attr field, or at any negative or NCC's.
259  I'm not sure whether this is right or not. (It's a pretty
260  obscure case, though.)
261 ----------------------------------------------------------------------- */
262 
263 /* RBD 8/91/94 changed calls to add_to_os_tc() in this routine to use
264  add_to_os_tc_if_id() instead -- in case people use constant-symbols
265  (instead of objects) for states or operators */
266 
268  preference *pref;
269  wme *w;
270  condition *c;
271 
272 
273  /* RCHONG: begin 10.11 */
274 
275  action *act;
276  Bool o_support,op_elab;
277  Bool operator_proposal;
278  char action_attr[50];
279  int pass;
280  wme *lowest_goal_wme;
281 
282  /* RCHONG: end 10.11 */
283 
284 
285 
286  /* REW: begin 09.15.96 */
287  if (thisAgent->soar_verbose_flag == TRUE) {
288  printf("\n in calculate_support_for_instantiation_preferences:");
289  xml_generate_verbose(thisAgent, "in calculate_support_for_instantiation_preferences:");
290  }
291  o_support = FALSE;
292  op_elab = FALSE;
293 
295  o_support = TRUE;
296  else if (inst->prod->declared_support == DECLARED_I_SUPPORT)
297  o_support = FALSE;
298  else if (inst->prod->declared_support == UNDECLARED_SUPPORT) {
299 
300  /*
301  check if the instantiation is proposing an operator. if it
302  is, then this instantiation is i-supported.
303  */
304 
305  operator_proposal = FALSE;
306  for (act = inst->prod->action_list; act != NIL ; act = act->next) {
307  if ((act->type == MAKE_ACTION) &&
308  (rhs_value_is_symbol(act->attr))) {
309  if ((strcmp(rhs_value_to_string (thisAgent, act->attr, action_attr, 50),
310  "operator") == NIL) &&
312  /* REW: 09.30.96. Bug fix (next line was
313  operator_proposal == TRUE;) */
314  operator_proposal = TRUE;
315  o_support = FALSE;
316  break;
317  }
318  }
319  }
320 
321 
322  if (operator_proposal == FALSE) {
323 
324  /*
325  an operator wasn't being proposed, so now we need to test if
326  the operator is being tested on the LHS.
327 
328  i'll need to make two passes over the wmes that pertain to
329  this instantiation. the first pass looks for the lowest goal
330  identifier. the second pass looks for a wme of the form:
331 
332  (<lowest-goal-id> ^operator ...)
333 
334  if such a wme is found, then this o-support = TRUE; FALSE otherwise.
335 
336  this code is essentially identical to that in
337  p_node_left_addition() in rete.c.
338 
339  BUGBUG this check only looks at positive conditions. we
340  haven't really decided what testing the absence of the
341  operator will do. this code assumes that such a productions
342  (instantiation) would get i-support.
343  */
344 
345  lowest_goal_wme = NIL;
346 
347  for (pass = 0; pass != 2; pass++) {
348 
349  for (c=inst->top_of_instantiated_conditions; c!=NIL; c=c->next) {
350  if (c->type==POSITIVE_CONDITION) {
351  w = c->bt.wme_;
352 
353  if (pass == 0) {
354 
355  if (w->id->id.isa_goal == TRUE) {
356 
357  if (lowest_goal_wme == NIL)
358  lowest_goal_wme = w;
359 
360  else {
361  if (w->id->id.level > lowest_goal_wme->id->id.level)
362  lowest_goal_wme = w;
363  }
364  }
365 
366  }
367 
368  else {
369  if ((w->attr == thisAgent->operator_symbol) &&
370  (w->acceptable == FALSE) &&
371  (w->id == lowest_goal_wme->id)) {
372  if (thisAgent->o_support_calculation_type == 3 || thisAgent->o_support_calculation_type == 4) {
373 
374  /* iff RHS has only operator elaborations
375  then it's IE_PROD, otherwise PE_PROD, so
376  look for non-op-elabs in the actions KJC 1/00 */
377  for (act = inst->prod->action_list;
378  act != NIL ; act = act->next) {
379  if (act->type == MAKE_ACTION) {
380  if ((rhs_value_is_symbol(act->id)) &&
381  (rhs_value_to_symbol(act->id) == w->value)) {
382  op_elab = TRUE;
383  } else if ( thisAgent->o_support_calculation_type == 4
384  && (rhs_value_is_reteloc(act->id))
386  op_elab = TRUE;
387  } else {
388  /* this is not an operator elaboration */
389  o_support = TRUE;
390  }
391  }
392  }
393  } else {
394  o_support = TRUE;
395  break;
396  }
397  }
398  }
399 
400 
401 
402  }
403  }
404  }
405  }
406  }
407 
408 
409  /* KJC 01/00: Warn if operator elabs mixed w/ applications */
410  if ( (thisAgent->o_support_calculation_type == 3
411  || thisAgent->o_support_calculation_type == 4 )
412  && (o_support == TRUE)) {
413 
414  if (op_elab == TRUE ) {
415 
416  /* warn user about mixed actions */
417  if ( thisAgent->o_support_calculation_type == 3 ) {
418 
419  print_with_symbols(thisAgent, "\nWARNING: operator elaborations mixed with operator applications\nget o_support in prod %y", inst->prod->name);
420 
422  add_to_growable_string(thisAgent, &gs, "WARNING: operator elaborations mixed with operator applications\nget o_support in prod ");
423  add_to_growable_string(thisAgent, &gs, symbol_to_string(thisAgent, inst->prod->name, true, 0, 0));
425  free_growable_string(thisAgent, gs);
426 
427  o_support = TRUE;
428  } else if ( thisAgent->o_support_calculation_type == 4 ) {
429  print_with_symbols(thisAgent, "\nWARNING: operator elaborations mixed with operator applications\nget i_support in prod %y", inst->prod->name);
430 
432  add_to_growable_string(thisAgent, &gs, "WARNING: operator elaborations mixed with operator applications\nget i_support in prod ");
433  add_to_growable_string(thisAgent, &gs, symbol_to_string(thisAgent, inst->prod->name, true, 0, 0));
435  free_growable_string(thisAgent, gs);
436 
437  o_support = FALSE;
438  }
439  }
440  }
441 
442  /*
443  assign every preference the correct support
444  */
445 
446  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next)
447  pref->o_supported = o_support;
448 }
449 
450 /* -----------------------------------------------------------------------
451  Run-Time O-Support Calculation: Doug Pearson's Scheme
452 
453  This routine calculates o-support for each preference for the given
454  instantiation, filling in pref->o_supported (TRUE or FALSE) on each one.
455 
456  This is basically Doug's original scheme (from email August 16, 1994)
457  modified by John's response (August 17) points #2 (don't give o-c
458  support unless pref in TC of RHS op.) and #3 (all support calc's should
459  be local to a goal). In detail:
460 
461  For a particular preference p=(id ^attr ...) on the RHS of an
462  instantiation [LHS,RHS]:
463 
464  RULE #1 (Context pref's): If id is the match state and attr="operator",
465  then p does NOT get o-support. This rule overrides all other rules.
466 
467  RULE #2 (O-A support): If LHS includes (match-state ^operator ...),
468  then p gets o-support.
469 
470  RULE #3 (O-M support): If LHS includes (match-state ^operator ... +),
471  then p gets o-support.
472 
473  RULE #4 (O-C support): If RHS creates (match-state ^operator ... +/!),
474  and p is in TC(RHS-operators, RHS), then p gets o-support.
475 
476  Here "TC" means transitive closure; the starting points for the TC are
477  all operators the RHS creates an acceptable/require preference for (i.e.,
478  if the RHS includes (match-state ^operator such-and-such +/!), then
479  "such-and-such" is one of the starting points for the TC). The TC
480  is computed only through the preferences created by the RHS, not
481  through any other existing preferences or WMEs.
482 
483  If none of rules 1-4 apply, then p does NOT get o-support.
484 
485  Note that rules 1 through 3 can be handled in linear time (linear in
486  the size of the LHS and RHS); rule 4 can be handled in time quadratic
487  in the size of the RHS (and typical behavior will probably be linear).
488 ----------------------------------------------------------------------- */
489 
491  Symbol *match_state;
492  Bool rule_2_or_3, anything_added;
493  preference *rhs, *pref;
494  wme *w;
495  condition *lhs, *c;
496 
497  lhs = inst->top_of_instantiated_conditions;
498  rhs = inst->preferences_generated;
499  match_state = inst->match_goal;
500 
501  /* --- First, check whether rule 2 or 3 applies. --- */
502  rule_2_or_3 = FALSE;
503  for (c=lhs; c!=NIL; c=c->next) {
504  if (c->type!=POSITIVE_CONDITION) continue;
505  w = c->bt.wme_;
506  if ((w->id==match_state)&&(w->attr==thisAgent->operator_symbol)) {
507  rule_2_or_3 = TRUE;
508  break;
509  }
510  }
511 
512  /* --- Initialize all pref's according to rules 2 and 3 --- */
513  for (pref=rhs; pref!=NIL; pref=pref->inst_next)
514  pref->o_supported = rule_2_or_3;
515 
516  /* --- If they didn't apply, check rule 4 --- */
517  if (! rule_2_or_3) {
518  thisAgent->o_support_tc = get_new_tc_number(thisAgent);
519  /* BUGBUG With Doug's scheme, o_support_tc no longer needs to be a
520  global variable -- it could simply be local to this procedure */
521  anything_added = FALSE;
522  /* --- look for RHS operators, add 'em (starting points) to the TC --- */
523  for (pref=rhs; pref!=NIL; pref=pref->inst_next) {
524  if ((pref->id==match_state) &&
525  (pref->attr==thisAgent->operator_symbol) &&
526  ((pref->type==ACCEPTABLE_PREFERENCE_TYPE) ||
527  (pref->type==REQUIRE_PREFERENCE_TYPE)) &&
528  (pref->value->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)) {
529  pref->value->id.tc_num = thisAgent->o_support_tc;
530  anything_added = TRUE;
531  }
532  }
533  /* --- Keep adding stuff to the TC until nothing changes anymore --- */
534  while (anything_added) {
535  anything_added = FALSE;
536  for (pref=rhs; pref!=NIL; pref=pref->inst_next) {
537  if (pref->id->id.tc_num != thisAgent->o_support_tc) continue;
538  if (pref->o_supported) continue; /* already added this thing */
539  pref->o_supported = TRUE;
540  anything_added = TRUE;
541  if (pref->value->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
542  pref->value->id.tc_num = thisAgent->o_support_tc;
543  if ((preference_is_binary(pref->type)) &&
544  (pref->referent->common.symbol_type==IDENTIFIER_SYMBOL_TYPE))
545  pref->referent->id.tc_num = thisAgent->o_support_tc;
546  }
547  }
548  }
549 
550  /* --- Finally, use rule 1, which overrides all the other rules. --- */
551  for (pref=rhs; pref!=NIL; pref=pref->inst_next)
552  if ((pref->id==match_state) &&
553  (pref->attr==thisAgent->operator_symbol))
554  pref->o_supported = FALSE;
555 }
556 
557 /* *********************************************************************
558 
559  Compile-Time O-Support Calculations
560 
561 ********************************************************************* */
562 
563 /* ------------------------------------------------------------------
564  Test Is For Symbol
565 
566  This function determines whether a given symbol could be the match
567  for a given test. It returns YES if the symbol is the only symbol
568  that could pass the test (i.e., the test *forces* that symbol to be
569  present in WM), NO if the symbol couldn't possibly pass the test,
570  and MAYBE if it can't tell for sure. The symbol may be a variable;
571  the test may contain variables.
572 ------------------------------------------------------------------ */
573 
574 enum yes_no_maybe { YES, NO, MAYBE } ;
575 
577  cons *c;
578  yes_no_maybe temp;
579  Bool maybe_found;
580  complex_test *ct;
581  Symbol *referent;
582 
583  if (test_is_blank_test(t)) return MAYBE;
584 
586  referent = referent_of_equality_test(t);
587  if (referent==sym) return YES;
588  if (referent->common.symbol_type==VARIABLE_SYMBOL_TYPE) return MAYBE;
589  if (sym->common.symbol_type==VARIABLE_SYMBOL_TYPE) return MAYBE;
590  return NO;
591  }
592 
593  ct = complex_test_from_test(t);
594 
595  switch (ct->type) {
596  case DISJUNCTION_TEST:
597  if (sym->common.symbol_type==VARIABLE_SYMBOL_TYPE) return MAYBE;
598  if (member_of_list (sym, ct->data.disjunction_list)) return MAYBE;
599  return NO;
600  case CONJUNCTIVE_TEST:
601  maybe_found = FALSE;
602  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest) {
603  temp = test_is_for_symbol (static_cast<char *>(c->first), sym);
604  if (temp==YES) return YES;
605  if (temp==MAYBE) maybe_found = TRUE;
606  }
607  if (maybe_found) return MAYBE;
608  return NO;
609  default: /* goal/impasse tests, relational tests other than equality */
610  return MAYBE;
611  }
612 }
613 
614 /* ------------------------------------------------------------------
615  Find Known Goals
616 
617  This routine looks at the LHS and returns a list of variables that
618  are certain to be bound to goals.
619 
620  Note: this uses the TC routines and clobbers any existing TC.
621 
622  BUGBUG should follow ^object links up the goal stack if possible
623 ------------------------------------------------------------------ */
624 
625 list *find_known_goals (agent* thisAgent, condition *lhs) {
626  tc_number tc;
627  list *vars;
628  condition *c;
629 
630  tc = get_new_tc_number(thisAgent);
631  vars = NIL;
632  for (c=lhs; c!=NIL; c=c->next) {
633  if (c->type != POSITIVE_CONDITION) continue;
635  TRUE,
636  FALSE))
637  add_bound_variables_in_test (thisAgent, c->data.tests.id_test, tc, &vars);
638  }
639  return vars;
640 }
641 
642 /* ------------------------------------------------------------------
643  Find Compile Time Match Goal
644 
645  Given the LHS and a list of known goals (i.e., variables that must
646  be bound to goals at run-time), this routine tries to determine
647  which variable will be the match goal. If successful, it returns
648  that variable; if it can't tell which variable will be the match
649  goal, it returns NIL.
650 
651  Note: this uses the TC routines and clobbers any existing TC.
652 ------------------------------------------------------------------ */
653 
654 Symbol *find_compile_time_match_goal (agent* thisAgent, condition *lhs, list *known_goals) {
655  tc_number tc;
656  list *roots;
657  list *root_goals;
658  int num_root_goals;
659  cons *c, *prev_c, *next_c;
660  Symbol *result;
661  condition *cond;
662 
663  /* --- find root variables --- */
664  tc = get_new_tc_number(thisAgent);
665  roots = collect_root_variables (thisAgent, lhs, tc, FALSE);
666 
667  /* --- intersect roots with known_goals, producing root_goals --- */
668  root_goals = NIL;
669  num_root_goals = 0;
670  for (c=roots; c!=NIL; c=c->rest)
671  if (member_of_list (c->first, known_goals)) {
672  push (thisAgent, c->first, root_goals);
673  num_root_goals++;
674  }
675  free_list (thisAgent, roots);
676 
677  /* --- if more than one goal, remove any with "^object nil" --- */
678  if (num_root_goals > 1) {
679  for (cond=lhs; cond!=NIL; cond=cond->next) {
680  if ((cond->type==POSITIVE_CONDITION) &&
682  (test_is_for_symbol(cond->data.tests.value_test,thisAgent->nil_symbol)==YES)) {
683  prev_c = NIL;
684  for (c=root_goals; c!=NIL; c=next_c) {
685  next_c = c->rest;
686  if (test_is_for_symbol (cond->data.tests.id_test, static_cast<symbol_union *>(c->first))==YES) {
687  /* --- remove c from the root_goals list --- */
688  if (prev_c) prev_c->rest = next_c; else root_goals = next_c;
689  free_cons (thisAgent, c);
690  num_root_goals--;
691  if (num_root_goals==1) break; /* be sure not to remove them all */
692  } else {
693  prev_c = c;
694  }
695  } /* end of for (c) loop */
696  if (num_root_goals==1) break; /* be sure not to remove them all */
697  }
698  } /* end of for (cond) loop */
699  }
700 
701  /* --- if there's only one root goal, that's it! --- */
702  if (num_root_goals==1)
703  result = static_cast<symbol_union *>(root_goals->first);
704  else
705  result = NIL;
706 
707  /* --- clean up and return result --- */
708  free_list (thisAgent, root_goals);
709  return result;
710 }
711 
712 /* ------------------------------------------------------------------
713  Find Thing Off Goal
714 
715  Given the LHS and a the match goal variable, this routine looks
716  for a positive condition testing (goal ^attr) for the given attribute
717  "attr". If such a condition exists, and the value field contains
718  an equality test for a variable, then that variable is returned.
719  (If more than one such variable exists, one is chosen arbitrarily
720  and returned.) Otherwise the function returns NIL.
721 
722  Note: this uses the TC routines and clobbers any existing TC.
723 ------------------------------------------------------------------ */
724 
726  Symbol *goal, Symbol *attr) {
727  condition *c;
728  list *vars;
729  tc_number tc;
730  Symbol *result;
731 
732  for (c=lhs; c!=NIL; c=c->next) {
733  if (c->type != POSITIVE_CONDITION) continue;
734  if (test_is_for_symbol (c->data.tests.id_test, goal) != YES) continue;
735  if (test_is_for_symbol (c->data.tests.attr_test, attr) != YES) continue;
736  if (c->test_for_acceptable_preference) continue;
737  tc = get_new_tc_number(thisAgent);
738  vars = NIL;
739  add_bound_variables_in_test (thisAgent, c->data.tests.value_test, tc, &vars);
740  if (vars) {
741  result = static_cast<symbol_union *>(vars->first);
742  free_list (thisAgent, vars);
743  return result;
744  }
745  }
746  return NIL;
747 }
748 
749 /* ------------------------------------------------------------------
750  Condition List Has Id Test For Sym
751 
752  This checks whether a given condition list has an equality test for
753  a given symbol in the id field of any condition (at any nesting level
754  within NCC's).
755 ------------------------------------------------------------------ */
756 
758  for ( ; conds!=NIL; conds=conds->next) {
759  switch (conds->type) {
760  case POSITIVE_CONDITION:
761  case NEGATIVE_CONDITION:
763  sym))
764  return TRUE;
765  break;
768  return TRUE;
769  break;
770  }
771  }
772  return FALSE;
773 }
774 
775 
776 /* SBH 7/1/94 #2 */
777 
778 /* ------------------------------------------------------------------
779 
780 ------------------------------------------------------------------ */
781 
783  Symbol *match_state) {
784  yes_no_maybe ynm;
785 
786  for ( ; conds!=NIL; conds=conds->next) {
787  switch (conds->type) {
788  case POSITIVE_CONDITION:
789  case NEGATIVE_CONDITION:
791  match_state)) {
792  ynm = test_is_for_symbol (conds->data.tests.attr_test, thisAgent->operator_symbol);
793  if (ynm == NO) return TRUE;
794  }
795  break;
797  if (match_state_tests_non_operator_slot (thisAgent, conds->data.ncc.top,
798  match_state))
799  return TRUE;
800  break;
801  }
802  }
803  return FALSE;
804 }
805 
806 /* end SBH 7/1/94 #2 */
807 
808 /* ------------------------------------------------------------------
809  Add TC Through LHS and RHS
810 
811  This enlarges a given TC by adding to it any connected conditions
812  in the LHS or actions in the RHS.
813 ------------------------------------------------------------------ */
814 
815 void add_tc_through_lhs_and_rhs (agent* thisAgent, condition *lhs, action *rhs,
816  tc_number tc, list **id_list, list **var_list) {
817  condition *c;
818  action *a;
819  Bool anything_changed;
820 
821  for (c=lhs; c!=NIL; c=c->next) c->already_in_tc = FALSE;
822  for (a=rhs; a!=NIL; a=a->next) a->already_in_tc = FALSE;
823 
824  /* --- keep trying to add new stuff to the tc --- */
825  while (TRUE) {
826  anything_changed = FALSE;
827  for (c=lhs; c!=NIL; c=c->next)
828  if (! c->already_in_tc)
829  if (cond_is_in_tc (thisAgent, c, tc)) {
830  add_cond_to_tc (thisAgent, c, tc, id_list, var_list);
831  c->already_in_tc = TRUE;
832  anything_changed = TRUE;
833  }
834  for (a=rhs; a!=NIL; a=a->next)
835  if (! a->already_in_tc)
836  if (action_is_in_tc (a, tc)) {
837  add_action_to_tc (thisAgent, a, tc, id_list, var_list);
838  a->already_in_tc = TRUE;
839  anything_changed = TRUE;
840  }
841  if (! anything_changed) break;
842  }
843 }
844 
845 /* -----------------------------------------------------------------------
846  Calculate Compile Time O-Support
847 
848  This takes the LHS and RHS, and fills in the a->support field in each
849  RHS action with either UNKNOWN_SUPPORT, O_SUPPORT, or I_SUPPORT.
850  (Actually, it only does this for MAKE_ACTION's--for FUNCALL_ACTION's,
851  the support doesn't matter.)
852 ----------------------------------------------------------------------- */
853 
855  list *known_goals;
856  cons *c;
857  Symbol *match_state, *match_operator;
858  yes_no_maybe lhs_oa_support, lhs_oc_support, lhs_om_support;
859  action *a;
860  condition *cond;
861  yes_no_maybe ynm;
862  Bool operator_found, possible_operator_found;
863  tc_number tc;
864 
865  /* --- initialize: mark all rhs actions as "unknown" --- */
866  for (a=rhs; a!=NIL; a=a->next)
868 
869  /* --- if "operator" doesn't appear in any LHS attribute slot, and there
870  are no RHS +/! makes for "operator", then nothing gets support --- */
871  operator_found = FALSE;
872  possible_operator_found = FALSE;
873  for (cond=lhs; cond!=NIL; cond=cond->next) {
874  if (cond->type != POSITIVE_CONDITION) continue;
875  ynm = test_is_for_symbol (cond->data.tests.attr_test, thisAgent->operator_symbol);
876  if (ynm==YES) { operator_found = possible_operator_found = TRUE; break; }
877  if (ynm==MAYBE) possible_operator_found = TRUE;
878  }
879  if (! operator_found) {
880  for (a=rhs; a!=NIL; a=a->next) {
881  if (a->type != MAKE_ACTION) continue;
882  if (rhs_value_is_symbol(a->attr)) { /* RBD 3/29/95 general RHS attr's */
883  Symbol *attr;
884  attr = rhs_value_to_symbol(a->attr);
885  if (attr==thisAgent->operator_symbol)
886  { operator_found = possible_operator_found = TRUE; break; }
887  if (attr->common.symbol_type==VARIABLE_SYMBOL_TYPE)
888  possible_operator_found = TRUE;
889  } else {
890  possible_operator_found = TRUE; /* for funcall, must play it safe */
891  }
892  }
893  }
894  if (! possible_operator_found) {
895  for (a=rhs; a!=NIL; a=a->next) {
896  if (a->type == MAKE_ACTION) a->support=I_SUPPORT;
897  }
898  return;
899  }
900 
901  /* --- find known goals; RHS augmentations of goals get no support --- */
902  known_goals = find_known_goals (thisAgent, lhs);
903  /* SBH: In NNPSCM, the only RHS-goal augmentations that can't get support are
904  preferences for the "operator" slot. */
905  for (c=known_goals; c!=NIL; c=c->rest) {
906  for (a=rhs; a!=NIL; a=a->next) {
907  if (a->type != MAKE_ACTION) continue;
908  if (rhs_value_is_symbol(a->attr) && /* RBD 3/29/95 */
909  rhs_value_to_symbol(a->attr)==thisAgent->operator_symbol &&
910  (rhs_value_to_symbol(a->id) == c->first))
911  a->support = I_SUPPORT;
912  }
913  }
914 
915  /* --- find match goal, state, and operator --- */
916  match_state = find_compile_time_match_goal (thisAgent, lhs, known_goals);
917  free_list (thisAgent, known_goals);
918  if (!match_state) return;
919  match_operator = find_thing_off_goal (thisAgent, lhs, match_state, thisAgent->operator_symbol);
920  /* --- If when checking (above) for "operator" appearing anywhere, we
921  found a possible operator but not a definite operator, now go back and
922  see if the possible operator was actually the match goal or match state;
923  if so, it's not a possible operator. (Note: by "possible operator" I
924  mean something appearing in the *attribute* field that might get bound
925  to the symbol "operator".) --- */
926  if (possible_operator_found && !operator_found) {
927  possible_operator_found = FALSE;
928  for (cond=lhs; cond!=NIL; cond=cond->next) {
929  if (cond->type != POSITIVE_CONDITION) continue;
930  ynm = test_is_for_symbol (cond->data.tests.attr_test, thisAgent->operator_symbol);
931  if ((ynm!=NO) &&
932  (test_is_for_symbol (cond->data.tests.attr_test, match_state)!=YES))
933  { possible_operator_found = TRUE; break; }
934  }
935  if (! possible_operator_found) {
936  for (a=rhs; a!=NIL; a=a->next) {
937  if (a->type != MAKE_ACTION) continue;
938  /* we're looking for "operator" augs of goals only, and match_state
939  couldn't get bound to a goal */
940  if (rhs_value_to_symbol(a->id) == match_state) continue;
941  if (rhs_value_is_symbol(a->attr)) { /* RBD 3/29/95 */
942  Symbol *attr;
943  attr = rhs_value_to_symbol(a->attr);
944  if ((attr->common.symbol_type==VARIABLE_SYMBOL_TYPE) &&
945  (attr != match_state))
946  { possible_operator_found = TRUE; break; }
947  } else { /* RBD 3/29/95 */
948  possible_operator_found = TRUE; break;
949  }
950  }
951  }
952  if (! possible_operator_found) {
953  for (a=rhs; a!=NIL; a=a->next)
954  if (a->type == MAKE_ACTION) a->support=I_SUPPORT;
955  return;
956  }
957  }
958 
959  /* --- calculate LHS support predicates --- */
960  lhs_oa_support = MAYBE;
961  if (match_operator) {
962  /* SBH 7/1/94 #2 */
963  if ((condition_list_has_id_test_for_sym (lhs, match_operator)) &&
964  (match_state_tests_non_operator_slot(thisAgent, lhs,match_state))) {
965  /* end SBH 7/1/94 #2 */
966  lhs_oa_support = YES;
967  }
968  }
969 
970  lhs_oc_support = MAYBE;
971  lhs_om_support = MAYBE;
972 
973  /* SBH 7/1/94 #2 */
974  /* For NNPSCM, must test that there is a test of a non-operator slot off
975  of the match_state. */
976  if (match_state_tests_non_operator_slot(thisAgent, lhs,match_state))
977  {
978  /* end SBH 7/1/94 #2 */
979 
980  lhs_oc_support = YES;
981  for (cond=lhs; cond!=NIL; cond=cond->next) {
982  if (cond->type != POSITIVE_CONDITION) continue;
983  if (test_is_for_symbol (cond->data.tests.id_test, match_state) != YES) continue;
984  if (test_is_for_symbol (cond->data.tests.attr_test, thisAgent->operator_symbol)
985  != YES)
986  continue;
987  lhs_om_support = YES;
988  break;
989  }
990  }
991 
992  if (lhs_oa_support == YES) { /* --- look for RHS o-a support --- */
993  /* --- do TC(match_state) --- */
994  tc = get_new_tc_number(thisAgent);
995  add_symbol_to_tc (thisAgent, match_state, tc, NIL, NIL);
996  add_tc_through_lhs_and_rhs (thisAgent, lhs, rhs, tc, NIL, NIL);
997 
998  /* --- any action with id in the TC gets support --- */
999  for (a=rhs; a!=NIL; a=a->next) {
1000 
1001  if (action_is_in_tc (a, tc)) {
1002  /* SBH 7/1/94 Avoid resetting of support that was previously set to I_SUPPORT. */
1003  /* gap 10/6/94 If the action has an attribue of operator, then you
1004  don't know if it should get o-support until run time because of
1005  the vagaries of knowing when this is matching a context object
1006  or not. */
1007  if (rhs_value_is_symbol(a->attr) &&
1008  (rhs_value_to_symbol(a->attr)==thisAgent->operator_symbol)) {
1009  if (a->support != I_SUPPORT) a->support = UNKNOWN_SUPPORT;
1010  } else {
1011  if (a->support != I_SUPPORT) a->support = O_SUPPORT;
1012  }
1013  }
1014  /* end SBH 7/1/94 */
1015  }
1016  }
1017 
1018  if (lhs_oc_support == YES) { /* --- look for RHS o-c support --- */
1019  /* --- do TC(rhs operators) --- */
1020  tc = get_new_tc_number(thisAgent);
1021  for (a=rhs; a!=NIL; a=a->next) {
1022  if (a->type != MAKE_ACTION) continue;
1023  if (
1024  (rhs_value_to_symbol(a->id)==match_state) &&
1025  (rhs_value_is_symbol(a->attr)) &&
1026  (rhs_value_to_symbol(a->attr)==thisAgent->operator_symbol) &&
1029  if (rhs_value_is_symbol(a->value)) {
1030  add_symbol_to_tc (thisAgent, rhs_value_to_symbol(a->value), tc, NIL,NIL);
1031  }
1032  }
1033  }
1034  add_tc_through_lhs_and_rhs (thisAgent, lhs, rhs, tc, NIL, NIL);
1035 
1036  /* --- any action with id in the TC gets support --- */
1037  for (a=rhs; a!=NIL; a=a->next) {
1038 
1039 
1040  if (action_is_in_tc (a, tc)) {
1041 
1042  /* SBH 6/7/94:
1043  Make sure the action is not already marked as "I_SUPPORT". This
1044  avoids giving o-support in the case where the operator
1045  points back to the goal, thus adding the goal to the TC,
1046  thus adding the operator proposal itself to the TC; thus
1047  giving o-support to an operator proposal.
1048  */
1049  if (a->support != I_SUPPORT) a->support = O_SUPPORT;
1050  /* End SBH 6/7/94 */
1051 
1052 
1053  /* REW: begin 09.15.96 */
1054  /* in operand, operator proposals are now only i-supported.*/
1055  if (thisAgent->soar_verbose_flag == TRUE) {
1056  printf("\n operator creation: setting a->support to I_SUPPORT");
1057  xml_generate_verbose(thisAgent, "operator creation: setting a->support to I_SUPPORT");
1058  }
1059  a->support = I_SUPPORT;
1060  /* REW: end 09.15.96 */
1061 
1062  }
1063  }
1064  }
1065 
1066  if (lhs_om_support == YES) { /* --- look for RHS o-m support --- */
1067  /* --- do TC(lhs operators) --- */
1068  tc = get_new_tc_number(thisAgent);
1069  for (cond=lhs; cond!=NIL; cond=cond->next) {
1070  if (cond->type != POSITIVE_CONDITION) continue;
1071  if (test_is_for_symbol (cond->data.tests.id_test, match_state) == YES) {
1072  if (test_is_for_symbol (cond->data.tests.attr_test, thisAgent->operator_symbol) == YES) {
1073  add_bound_variables_in_test (thisAgent, cond->data.tests.value_test, tc, NIL);
1074  }
1075  }
1076  }
1077  add_tc_through_lhs_and_rhs (thisAgent, lhs, rhs, tc, NIL, NIL);
1078 
1079  /* --- any action with id in the TC gets support --- */
1080  for (a=rhs; a!=NIL; a=a->next) {
1081  if (action_is_in_tc (a, tc)) {
1082  /* SBH 7/1/94 Avoid resetting of support that was previously set to I_SUPPORT. */
1083  if (a->support != I_SUPPORT) a->support = O_SUPPORT;
1084  /* end SBH 7/1/94 */
1085  }
1086  }
1087  }
1088 }
1089