Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
recmem.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: recmem.cpp
11  *
12  * =======================================================================
13  *
14  * Recognition Memory (Firer and Chunker) Routines
15  * (Does not include the Rete net)
16  *
17  * Init_firer() and init_chunker() should be called at startup time, to
18  * do initialization.
19  *
20  * Do_preference_phase() runs the entire preference phase. This is called
21  * from the top-level control in main.c.
22  *
23  * Possibly_deallocate_instantiation() checks whether an instantiation
24  * can be deallocated yet, and does so if possible. This is used whenever
25  * the (implicit) reference count on the instantiation decreases.
26  * =======================================================================
27  */
28 
29 #include <stdlib.h>
30 
31 #include "gdatastructs.h"
32 #include "instantiations.h"
33 #include "kernel.h"
34 #include "mem.h"
35 #include "symtab.h"
36 #include "agent.h"
37 #include "prefmem.h"
38 #include "rhsfun.h"
39 #include "rete.h"
40 #include "print.h"
41 #include "production.h"
42 #include "wmem.h"
43 #include "osupport.h"
44 #include "recmem.h"
45 #include "tempmem.h"
46 #include "reinforcement_learning.h"
47 #include "wma.h"
48 #include "xml.h"
49 #include "utilities.h"
50 #include "soar_TraceNames.h"
51 #include "consistency.h"
52 #include "misc.h"
53 #include "soar_module.h"
54 
55 #include "assert.h"
56 #include <string> // SBW 8/4/08
57 #include <list>
58 
59 using namespace soar_TraceNames;
60 
61 #ifdef USE_MEM_POOL_ALLOCATORS
62 typedef std::list< instantiation*, soar_module::soar_memory_pool_allocator< instantiation* > > inst_mpool_list;
63 typedef std::list< condition*, soar_module::soar_memory_pool_allocator< condition* > > cond_mpool_list;
64 #else
65 typedef std::list< instantiation* > inst_mpool_list;
66 typedef std::list< condition* > cond_mpool_list;
67 #endif
68 
69 /* Uncomment the following line to get instantiation printouts */
70 /* #define DEBUG_INSTANTIATIONS */
71 
72 /* TEMPORARY HACK (Ideally this should be doable through
73  the external kernel interface but for now using a
74  couple of global STL lists to get this information
75  from the rhs function to this prefference adding code)*/
77 
78 /* mvp 5-17-94 */
79 /* --------------------------------------------------------------------------
80  Build Prohibit Preference List for Backtracing
81 --------------------------------------------------------------------------*/
82 
83 void build_prohibits_list (agent* thisAgent, instantiation *inst) {
84  condition *cond;
85  preference *pref, *new_pref;
86 
87  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next) {
88  cond->bt.prohibits = NIL;
89  if (cond->type==POSITIVE_CONDITION && cond->bt.trace) {
90  if (cond->bt.trace->slot) {
92  while (pref) {
93  new_pref = NIL;
94  if (pref->inst->match_goal_level == inst->match_goal_level && pref->in_tm) {
95  push (thisAgent, pref, cond->bt.prohibits);
96  preference_add_ref (pref);
97  } else {
98  new_pref = find_clone_for_level (pref, inst->match_goal_level);
99  if (new_pref) {
100  if (new_pref->in_tm) {
101  push (thisAgent, new_pref, cond->bt.prohibits);
102  preference_add_ref (new_pref);
103  }
104  }
105  }
106  pref = pref->next;
107  }
108  }
109  }
110  }
111 }
112 
113 /* -----------------------------------------------------------------------
114  Find Clone For Level
115 
116  This routines take a given preference and finds the clone of it whose
117  match goal is at the given goal_stack_level. (This is used to find the
118  proper preference to backtrace through.) If the given preference
119  itself is at the right level, it is returned. If there is no clone at
120  the right level, NIL is returned.
121 ----------------------------------------------------------------------- */
122 
124  preference *clone;
125 
126  if (! p) {
127  /* --- if the wme doesn't even have a preference on it, we can't backtrace
128  at all (this happens with I/O and some architecture-created wmes --- */
129  return NIL;
130  }
131 
132  /* --- look at pref and all of its clones, find one at the right level --- */
133 
134  if (p->inst->match_goal_level == level) return p;
135 
136  for (clone=p->next_clone; clone!=NIL; clone=clone->next_clone)
137  if (clone->inst->match_goal_level==level) return clone;
138 
139  for (clone=p->prev_clone; clone!=NIL; clone=clone->prev_clone)
140  if (clone->inst->match_goal_level==level) return clone;
141 
142  /* --- if none was at the right level, we can't backtrace at all --- */
143  return NIL;
144 }
145 
146 /* =======================================================================
147 
148  Firer Utilities
149 
150 ======================================================================= */
151 
152 /* -----------------------------------------------------------------------
153  Find Match Goal
154 
155  Given an instantiation, this routines looks at the instantiated
156  conditions to find its match goal. It fills in inst->match_goal and
157  inst->match_goal_level. If there is a match goal, match_goal is set
158  to point to the goal identifier. If no goal was matched, match_goal
159  is set to NIL and match_goal_level is set to ATTRIBUTE_IMPASSE_LEVEL.
160 ----------------------------------------------------------------------- */
161 
163  Symbol *lowest_goal_so_far;
164  goal_stack_level lowest_level_so_far;
165  condition *cond;
166  Symbol *id;
167 
168  lowest_goal_so_far = NIL;
169  lowest_level_so_far = -1;
170  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next)
171  if (cond->type==POSITIVE_CONDITION) {
172  id = cond->bt.wme_->id;
173  if (id->id.isa_goal)
174  if (cond->bt.level > lowest_level_so_far) {
175  lowest_goal_so_far = id;
176  lowest_level_so_far = cond->bt.level;
177  }
178  }
179 
180  inst->match_goal = lowest_goal_so_far;
181  if (lowest_goal_so_far)
182  inst->match_goal_level = lowest_level_so_far;
183  else
185 }
186 
187 /* -----------------------------------------------------------------------
188 
189  Executing the RHS Actions of an Instantiation
190 
191  Execute_action() executes a given RHS action. For MAKE_ACTION's, it
192  returns the created preference structure, or NIL if an error occurs.
193  For FUNCALL_ACTION's, it returns NIL.
194 
195  Instantiate_rhs_value() returns the (symbol) instantiation of an
196  rhs_value, or NIL if an error occurs. It takes a new_id_level
197  argument indicating what goal_stack_level a new id is to be created
198  at, in case a gensym is needed for the instantiation of a variable.
199  (although I'm not sure this is really needed.)
200 
201  As rhs unbound variables are encountered, they are instantiated with
202  new gensyms. These gensyms are then stored in the rhs_variable_bindings
203  array, so if the same unbound variable is encountered a second time
204  it will be instantiated with the same gensym.
205 ----------------------------------------------------------------------- */
206 
207 
209  goal_stack_level new_id_level,
210  char new_id_letter,
211  struct token_struct *tok, wme *w) {
212  list *fl;
213  list *arglist;
214  cons *c, *prev_c, *arg_cons;
215  rhs_function *rf;
216  Symbol *result;
217  Bool nil_arg_found;
218 
219  if (rhs_value_is_symbol(rv)) {
220 
221  result = rhs_value_to_symbol(rv);
222 
223  /*
224  Long-Winded Case-by-Case [Hopeful] Explanation
225 
226  This has to do with long-term identifiers (LTIs) that exist within productions (including chunks/justifications).
227  The real issue is that identifiers, upon creation, require a goal level (used for promotion/demotion/garbage collection).
228  At the time of parsing a rule, we don't have this information, so we give it an invalid "unknown" value.
229  This is OK on the condition side of a rule, since the rete (we think) will just consider it another symbol used for matching.
230  However, it becomes hairy when LTIs are on the action side of a rule, with respect to the state of the LTI in working memory and the rule LHS.
231  Consider the following cases:
232 
233  1. Identifier is LTI, does NOT exist as a LHS symbol
234  - we do NOT support this!!! bad things will likely happen due to potential for adding an identifier to working memory
235  with an unknown goal level.
236 
237  2. Attribute/Value is LTI, does NOT exist as a LHS symbol (!!!!!IMPORTANT CASE!!!!!)
238  - the caller of this function will supply new_id_level (probably based upon the level of the id).
239  - if this is valid (i.e. greater than 0), we use it. else, ignore.
240  - we have a huge assert on add_wme_to_wm that will kill soar if we try to add an identifier to working memory with an invalid level.
241 
242  3. Identifier/Attribute/Value is LTI, DOES exist as LHS symbol
243  - in this situation, we are *guaranteed* that the resulting LTI (since it is in WM) has a valid goal level.
244  - it should be noted that if a value, the level of the LTI may change during promotion/demotion/garbage collection,
245  but this is natural Soar behavior and outside our perview.
246 
247  */
249  ( result->id.smem_lti != NIL ) &&
250  ( result->id.level == SMEM_LTI_UNKNOWN_LEVEL ) &&
251  ( new_id_level > 0 ) )
252  {
253  result->id.level = new_id_level;
254  result->id.promotion_level = new_id_level;
255  }
256 
257  symbol_add_ref (result);
258  return result;
259  }
260 
261  if (rhs_value_is_unboundvar(rv)) {
262  int64_t index;
263  Symbol *sym;
264 
265  index = static_cast<int64_t>(rhs_value_to_unboundvar(rv));
266  if (thisAgent->firer_highest_rhs_unboundvar_index < index)
267  thisAgent->firer_highest_rhs_unboundvar_index = index;
268  sym = *(thisAgent->rhs_variable_bindings+index);
269 
270  if (!sym) {
271  sym = make_new_identifier (thisAgent, new_id_letter, new_id_level);
272  *(thisAgent->rhs_variable_bindings+index) = sym;
273  return sym;
274  } else if (sym->common.symbol_type==VARIABLE_SYMBOL_TYPE) {
275  new_id_letter = *(sym->var.name + 1);
276  sym = make_new_identifier (thisAgent, new_id_letter, new_id_level);
277  *(thisAgent->rhs_variable_bindings+index) = sym;
278  return sym;
279  } else {
280  symbol_add_ref (sym);
281  return sym;
282  }
283  }
284 
285  if (rhs_value_is_reteloc(rv)) {
288  tok, w);
289  symbol_add_ref (result);
290  return result;
291  }
292 
293  fl = rhs_value_to_funcall_list(rv);
294  rf = static_cast<rhs_function_struct *>(fl->first);
295 
296  /* --- build up a list of the argument values --- */
297  prev_c = NIL;
298  nil_arg_found = FALSE;
299  arglist = NIL; /* unnecessary, but gcc -Wall warns without it */
300  for (arg_cons=fl->rest; arg_cons!=NIL; arg_cons=arg_cons->rest) {
301  allocate_cons (thisAgent, &c);
302  c->first = instantiate_rhs_value (thisAgent,
303  static_cast<char *>(arg_cons->first),
304  new_id_level, new_id_letter, tok, w);
305  if (! c->first) nil_arg_found = TRUE;
306  if (prev_c) prev_c->rest = c; else arglist = c;
307  prev_c = c;
308  }
309  if (prev_c) prev_c->rest = NIL; else arglist = NIL;
310 
311  /* --- if all args were ok, call the function --- */
312 
313  if (!nil_arg_found) {
314  // stop the kernel timer while doing RHS funcalls KJC 11/04
315  // the total_cpu timer needs to be updated in case RHS fun is statsCmd
316  #ifndef NO_TIMING_STUFF
317  thisAgent->timers_kernel.stop();
318  thisAgent->timers_cpu.stop();
319  thisAgent->timers_total_kernel_time.update(thisAgent->timers_kernel);
320  thisAgent->timers_total_cpu_time.update(thisAgent->timers_cpu);
321  thisAgent->timers_cpu.start();
322  #endif
323 
324  result = (*(rf->f))(thisAgent, arglist, rf->user_data);
325 
326  #ifndef NO_TIMING_STUFF // restart the kernel timer
327  thisAgent->timers_kernel.start();
328  #endif
329 
330  } else
331  result = NIL;
332 
333  /* --- scan through arglist, dereference symbols and deallocate conses --- */
334  for (c=arglist; c!=NIL; c=c->rest)
335  if (c->first) symbol_remove_ref (thisAgent, static_cast<Symbol *>(c->first));
336  free_list (thisAgent, arglist);
337 
338  return result;
339 }
340 
341 preference *execute_action (agent* thisAgent, action *a, struct token_struct *tok, wme *w) {
342  Symbol *id, *attr, *value, *referent;
343  char first_letter;
344 
345  if (a->type==FUNCALL_ACTION) {
346  value = instantiate_rhs_value (thisAgent, a->value, -1, 'v', tok, w);
347  if (value) symbol_remove_ref (thisAgent, value);
348  return NIL;
349  }
350 
351  attr = NIL;
352  value = NIL;
353  referent = NIL;
354 
355  id = instantiate_rhs_value (thisAgent, a->id, -1, 's', tok, w);
356  if (!id) goto abort_execute_action;
357  if (id->common.symbol_type!=IDENTIFIER_SYMBOL_TYPE) {
358  print_with_symbols (thisAgent, "Error: RHS makes a preference for %y (not an identifier)\n", id);
359  goto abort_execute_action;
360  }
361 
362  attr = instantiate_rhs_value (thisAgent, a->attr, id->id.level, 'a', tok, w);
363  if (!attr) goto abort_execute_action;
364 
365  first_letter = first_letter_from_symbol (attr);
366 
367  value = instantiate_rhs_value (thisAgent, a->value, id->id.level,
368  first_letter, tok, w);
369  if (!value) goto abort_execute_action;
370 
372  referent = instantiate_rhs_value (thisAgent, a->referent, id->id.level,
373  first_letter, tok, w);
374  if (!referent) goto abort_execute_action;
375  }
376 
379  (! (id->id.isa_goal && (attr==thisAgent->operator_symbol)) )) {
380  print_with_symbols (thisAgent, "\nError: attribute preference other than +/- for %y ^%y -- ignoring it.", id, attr);
381  goto abort_execute_action;
382  }
383 
384  return make_preference (thisAgent, a->preference_type, id, attr, value, referent);
385 
386 abort_execute_action: /* control comes here when some error occurred */
387  if (id) symbol_remove_ref (thisAgent, id);
388  if (attr) symbol_remove_ref (thisAgent, attr);
389  if (value) symbol_remove_ref (thisAgent, value);
390  if (referent) symbol_remove_ref (thisAgent, referent);
391  return NIL;
392 }
393 
394 /* -----------------------------------------------------------------------
395  Fill In New Instantiation Stuff
396 
397  This routine fills in a newly created instantiation structure with
398  various information. At input, the instantiation should have:
399  - preferences_generated filled in;
400  - instantiated conditions filled in;
401  - top-level positive conditions should have bt.wme_, bt.level, and
402  bt.trace filled in, but bt.wme_ and bt.trace shouldn't have their
403  reference counts incremented yet.
404 
405  This routine does the following:
406  - increments reference count on production;
407  - fills in match_goal and match_goal_level;
408  - for each top-level positive cond:
409  replaces bt.trace with the preference for the correct level,
410  updates reference counts on bt.pref and bt.wmetraces and wmes
411  - for each preference_generated, adds that pref to the list of all
412  pref's for the match goal
413  - fills in backtrace_number;
414  - if "need_to_do_support_calculations" is TRUE, calculates o-support
415  for preferences_generated;
416 ----------------------------------------------------------------------- */
417 
419  Bool need_to_do_support_calculations) {
420  condition *cond;
421  preference *p;
422  goal_stack_level level;
423 
424  production_add_ref (inst->prod);
425 
426  find_match_goal (inst);
427 
428  level = inst->match_goal_level;
429 
430  /* Note: since we'll never backtrace through instantiations at the top
431  level, it might make sense to not increment the reference counts
432  on the wmes and preferences here if the instantiation is at the top
433  level. As it stands now, we could gradually accumulate garbage at
434  the top level if we have a never-ending sequence of production
435  firings at the top level that chain on each other's results. (E.g.,
436  incrementing a counter on every decision cycle.) I'm leaving it this
437  way for now, because if we go to S-Support, we'll (I think) need to
438  save these around (maybe). */
439 
440  /* KJC 6/00: maintaining all the top level ref cts does have a big
441  impact on memory pool usage and also performance (due to malloc).
442  (See tests done by Scott Wallace Fall 99.) Therefore added
443  preprocessor macro so that by unsetting macro the top level ref cts are not
444  incremented. It's possible that in some systems, these ref cts may
445  be desireable: they can be added by defining DO_TOP_LEVEL_REF_CTS
446  */
447 
448  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next)
449  if (cond->type==POSITIVE_CONDITION) {
450  #ifdef DO_TOP_LEVEL_REF_CTS
451  wme_add_ref (cond->bt.wme_);
452  #else
453  if (level > TOP_GOAL_LEVEL) wme_add_ref (cond->bt.wme_);
454  #endif
455  /* --- if trace is for a lower level, find one for this level --- */
456  if (cond->bt.trace) {
457  if (cond->bt.trace->inst->match_goal_level > level) {
458  cond->bt.trace = find_clone_for_level (cond->bt.trace, level);
459  }
460  #ifdef DO_TOP_LEVEL_REF_CTS
461  if (cond->bt.trace) preference_add_ref (cond->bt.trace);
462  #else
463  if ((cond->bt.trace) && (level > TOP_GOAL_LEVEL))
464  preference_add_ref (cond->bt.trace);
465  #endif
466  }
467  }
468 
469 
470 
471  if (inst->match_goal) {
472  for (p=inst->preferences_generated; p!=NIL; p=p->inst_next) {
474  all_of_goal_next, all_of_goal_prev);
475  p->on_goal_list = TRUE;
476  }
477  }
478  inst->backtrace_number = 0;
479 
480  if ((thisAgent->o_support_calculation_type == 0) ||
481  (thisAgent->o_support_calculation_type == 3) ||
482  (thisAgent->o_support_calculation_type == 4)) {
483  /* --- do calc's the normal Soar 6 way --- */
484  if (need_to_do_support_calculations)
486  } else if (thisAgent->o_support_calculation_type == 1) {
487  if (need_to_do_support_calculations)
489  /* --- do calc's both ways, warn on differences --- */
490  if ((inst->prod->declared_support!=DECLARED_O_SUPPORT) &&
492  /* --- At this point, we've done them the normal way. To look for
493  differences, save o-support flags on a list, then do Doug's
494  calculations, then compare and restore saved flags. --- */
495  list *saved_flags;
496  preference *pref;
497  Bool difference_found;
498  saved_flags = NIL;
499  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next)
500  push (thisAgent, (pref->o_supported ? pref : NIL), saved_flags);
501  saved_flags = destructively_reverse_list (saved_flags);
503  difference_found = FALSE;
504  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next){
505  cons *c; Bool b;
506  c = saved_flags; saved_flags = c->rest;
507  b = (c->first ? TRUE : FALSE); free_cons (thisAgent, c);
508  if (pref->o_supported != b) difference_found = TRUE;
509  pref->o_supported = b;
510  }
511  if (difference_found) {
512  print_with_symbols(thisAgent, "\n*** O-support difference found in production %y",
513  inst->prod->name);
514  }
515  }
516  }
517  else {
518  /* --- do calc's Doug's way --- */
519  if ((inst->prod->declared_support!=DECLARED_O_SUPPORT) &&
522  }
523  }
524 }
525 
526 /* =======================================================================
527 
528  Main Firer Routines
529 
530  Init_firer() should be called at startup time. Do_preference_phase()
531  is called from the top level to run the whole preference phase.
532 
533  Preference phase follows this sequence:
534 
535  (1) Productions are fired for new matches. As productions are fired,
536  their instantiations are stored on the list newly_created_instantiations,
537  linked via the "next" fields in the instantiation structure. No
538  preferences are actually asserted yet.
539 
540  (2) Instantiations are retracted; their preferences are retracted.
541 
542  (3) Preferences (except o-rejects) from newly_created_instantiations
543  are asserted, and these instantiations are removed from the
544  newly_created_instantiations list and moved over to the per-production
545  lists of instantiations of that production.
546 
547  (4) Finally, o-rejects are processed.
548 
549  Note: Using the O_REJECTS_FIRST flag, step (4) becomes step (2b)
550 ======================================================================= */
551 
552 void init_firer (agent* thisAgent) {
553  init_memory_pool (thisAgent, &thisAgent->instantiation_pool, sizeof(instantiation),
554  "instantiation");
555 }
556 
557 /* --- Macro returning TRUE iff we're supposed to trace firings for the
558  given instantiation, which should have the "prod" field filled in. --- */
559 #ifdef USE_MACROS
560 #define trace_firings_of_inst(thisAgent, inst) \
561  ((inst)->prod && \
562  (thisAgent->sysparams[TRACE_FIRINGS_OF_USER_PRODS_SYSPARAM+(inst)->prod->type] || \
563  ((inst)->prod->trace_firings)))
564 #else
565 inline Bool trace_firings_of_inst(agent* thisAgent, instantiation * inst)
566 {
567  return ((inst)->prod &&
568  (thisAgent->sysparams[TRACE_FIRINGS_OF_USER_PRODS_SYSPARAM+(inst)->prod->type] ||
569  ((inst)->prod->trace_firings)));
570 }
571 #endif
572 
573 /* -----------------------------------------------------------------------
574  Create Instantiation
575 
576  This builds the instantiation for a new match, and adds it to
577  newly_created_instantiations. It also calls chunk_instantiation() to
578  do any necessary chunk or justification building.
579 ----------------------------------------------------------------------- */
580 
581 void create_instantiation (agent* thisAgent, production *prod, struct token_struct *tok, wme *w) {
582  instantiation *inst;
583  condition *cond;
584  preference *pref;
585  action *a;
586  cons *c;
587  Bool need_to_do_support_calculations;
588  Bool trace_it;
589  int64_t index;
590  Symbol **cell;
591 
592 #ifdef BUG_139_WORKAROUND
593  /* New waterfall model: this is now checked for before we call this function */
594  assert(prod->type != JUSTIFICATION_PRODUCTION_TYPE);
595  /* RPM workaround for bug #139: don't fire justifications */
596  //if (prod->type == JUSTIFICATION_PRODUCTION_TYPE) {
597  // return;
598  //}
599 #endif
600 
601  allocate_with_pool (thisAgent, &thisAgent->instantiation_pool, &inst);
602  inst->next = thisAgent->newly_created_instantiations;
603  thisAgent->newly_created_instantiations = inst;
604  inst->prod = prod;
605  inst->rete_token = tok;
606  inst->rete_wme = w;
607  inst->reliable = true;
608  inst->in_ms = TRUE;
609 
610  /* REW: begin 09.15.96 */
611  /* We want to initialize the GDS_evaluated_already flag
612  * when a new instantiation is created.
613  */
614 
616 
617  if (thisAgent->soar_verbose_flag == TRUE) {
618  print_with_symbols(thisAgent, "\n in create_instantiation: %y",
619  inst->prod->name);
620  char buf[256];
621  SNPRINTF(buf, 254, "in create_instantiation: %s", symbol_to_string(thisAgent, inst->prod->name, true, 0, 0));
622  xml_generate_verbose(thisAgent, buf);
623  }
624  /* REW: end 09.15.96 */
625 
626 
627  thisAgent->production_being_fired = inst->prod;
628  prod->firing_count++;
629  thisAgent->production_firing_count++;
630 
631  /* --- build the instantiated conditions, and bind LHS variables --- */
632  p_node_to_conditions_and_nots (thisAgent, prod->p_node, tok, w,
635  &(inst->nots), NIL);
636 
637  /* --- record the level of each of the wmes that was positively tested --- */
638  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next) {
639  if (cond->type==POSITIVE_CONDITION) {
640  cond->bt.level = cond->bt.wme_->id->id.level;
641  cond->bt.trace = cond->bt.wme_->preference;
642  }
643  }
644 
645  /* --- print trace info --- */
646  trace_it = trace_firings_of_inst (thisAgent, inst);
647  if (trace_it) {
648  if (get_printer_output_column(thisAgent)!=1) print (thisAgent, "\n"); /* AGR 617/634 */
649  print (thisAgent, "Firing ");
651  (thisAgent, inst,
652  static_cast<wme_trace_type>(thisAgent->sysparams[TRACE_FIRINGS_WME_TRACE_TYPE_SYSPARAM]), 0);
653  }
654 
655  /* --- initialize rhs_variable_bindings array with names of variables
656  (if there are any stored on the production -- for chunks there won't
657  be any) --- */
658  index = 0;
659  cell = thisAgent->rhs_variable_bindings;
660  for (c=prod->rhs_unbound_variables; c!=NIL; c=c->rest) {
661  *(cell++) = static_cast<symbol_union *>(c->first);
662  index++;
663  }
664  thisAgent->firer_highest_rhs_unboundvar_index = index - 1;
665 
666  /* 7.1/8 merge: Not sure about this. This code in 704, but not in either 7.1 or 703/soar8 */
667  /* --- Before executing the RHS actions, tell the user that the -- */
668  /* --- phase has changed to output by printing the arrow --- */
669  if (trace_it && thisAgent->sysparams[TRACE_FIRINGS_PREFERENCES_SYSPARAM]) {
670  print (thisAgent, " -->\n");
671  xml_object( thisAgent, kTagActionSideMarker );
672  }
673 
674  /* --- execute the RHS actions, collect the results --- */
675  inst->preferences_generated = NIL;
676  need_to_do_support_calculations = FALSE;
677  for (a=prod->action_list; a!=NIL; a=a->next) {
678 
679  if ( prod->type != TEMPLATE_PRODUCTION_TYPE )
680  {
681  pref = execute_action (thisAgent, a, tok, w);
682  }
683  else
684  {
685  pref = NIL;
686  /*Symbol *result = */rl_build_template_instantiation( thisAgent, inst, tok, w );
687  }
688 
689  /* SoarTech changed from an IF stmt to a WHILE loop to support GlobalDeepCpy */
690  while (pref) {
691  /* The parser assumes that any rhs preference of the form
692  *
693  * (<s> ^operator <o> = <x>)
694  *
695  * is a binary indifferent preference, because it assumes <x> is an
696  * operator. However, it could be the case that <x> is actually bound to
697  * a number, which would make this a numeric indifferent preference. The
698  * parser had no way of easily figuring this out, but it's easy to check
699  * here.
700  *
701  * jzxu April 22, 2009
702  */
703  if ((pref->type == BINARY_INDIFFERENT_PREFERENCE_TYPE) &&
706  {
708  }
709 
710  pref->inst = inst;
712  inst_next, inst_prev);
714  pref->o_supported = TRUE;
715  else if (inst->prod->declared_support==DECLARED_I_SUPPORT)
716  {
717  pref->o_supported = FALSE;
718  }
719  else {
720 
721  pref->o_supported =
722  (thisAgent->FIRING_TYPE == PE_PRODS) ? TRUE : FALSE;
723  /* REW: end 09.15.96 */
724  }
725 
726  /* TEMPORARY HACK (Ideally this should be doable through
727  the external kernel interface but for now using a
728  couple of global STL lists to get this information
729  from the rhs function to this prefference adding code)
730 
731  Getting the next pref from the set of possible prefs
732  added by the deep copy rhs function */
733  if ( glbDeepCopyWMEs != 0 ) {
734  wme* tempwme = glbDeepCopyWMEs;
735  pref = make_preference(thisAgent,
736  a->preference_type,
737  tempwme->id,
738  tempwme->attr,
739  tempwme->value, 0);
740  glbDeepCopyWMEs = tempwme->next;
741  deallocate_wme(thisAgent, tempwme);
742  } else {
743  pref = 0;
744  }
745  }
746  }
747 
748  /* --- reset rhs_variable_bindings array to all zeros --- */
749  index = 0;
750  cell = thisAgent->rhs_variable_bindings;
751  while (index++ <= thisAgent->firer_highest_rhs_unboundvar_index) *(cell++) = NIL;
752 
753  /* --- fill in lots of other stuff --- */
754  fill_in_new_instantiation_stuff (thisAgent, inst, need_to_do_support_calculations);
755 
756  /* --- print trace info: printing preferences --- */
757  /* Note: can't move this up, since fill_in_new_instantiation_stuff gives
758  the o-support info for the preferences we're about to print */
759  if (trace_it && thisAgent->sysparams[TRACE_FIRINGS_PREFERENCES_SYSPARAM]) {
760  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next) {
761  print (thisAgent, " ");
762  print_preference (thisAgent, pref);
763  }
764  }
765 
766  /* mvp 5-17-94 */
767  build_prohibits_list (thisAgent, inst);
768 
769  thisAgent->production_being_fired = NIL;
770 
771  /* --- build chunks/justifications if necessary --- */
772  chunk_instantiation (thisAgent, inst, false, &(thisAgent->newly_created_instantiations));
773 
774  /* MVP 6-8-94 */
775  if (!thisAgent->system_halted) {
776  /* --- invoke callback function --- */
777  soar_invoke_callbacks(thisAgent,
779  static_cast<soar_call_data>(inst));
780 
781  }
782 }
783 
789 Bool shouldCreateInstantiation (agent* thisAgent, production *prod, struct token_struct *tok, wme *w)
790 {
791  if (thisAgent->active_level == thisAgent->highest_active_level) {
792  return TRUE;
793  }
794 
795  if (prod->type == TEMPLATE_PRODUCTION_TYPE) {
796  return TRUE;
797  }
798 
799  // Scan RHS identifiers for their levels, don't fire those at or higher than the change level
800  action* a = NIL;
801  for (a=prod->action_list; a!=NIL; a=a->next) {
802  if (a->type==FUNCALL_ACTION) {
803  continue;
804  }
805 
806  // skip unbound variables
807  if (rhs_value_is_unboundvar(a->id)) {
808  continue;
809  }
810 
811  // try to make a symbol
812  Symbol* sym = NIL;
813  if (rhs_value_is_symbol(a->id)) {
814  sym = rhs_value_to_symbol(a->id);
815  } else {
816  if (rhs_value_is_reteloc(a->id)) {
819  tok, w);
820  }
821  }
822  assert(sym != NIL);
823 
824  // check level for legal change
825  if (sym->id.level <= thisAgent->change_level) {
826  if (thisAgent->sysparams[TRACE_WATERFALL_SYSPARAM]) {
827  print_with_symbols(thisAgent, "*** Waterfall: aborting firing because (%y * *)", sym);
828  print(thisAgent, " level %d is on or higher (lower int) than change level %d\n", sym->id.level, thisAgent->change_level);
829  }
830  return FALSE;
831  }
832  }
833  return TRUE;
834 }
835 /* -----------------------------------------------------------------------
836  Deallocate Instantiation
837 
838  This deallocates the given instantiation. This should only be invoked
839  via the possibly_deallocate_instantiation() macro.
840 ----------------------------------------------------------------------- */
841 
843 {
844  condition *cond;
845 
846  /* mvp 5-17-94 */
847  list *c, *c_old;
848  preference *pref;
849  goal_stack_level level;
850 
851 #ifdef USE_MEM_POOL_ALLOCATORS
854 #else
855  cond_mpool_list cond_stack;
856  inst_mpool_list inst_list;
857 #endif
858 
859  inst_list.push_back(inst);
860  inst_mpool_list::iterator next_iter = inst_list.begin();
861 
862  while ( next_iter != inst_list.end() )
863  {
864  inst = *next_iter;
865  assert(inst);
866  ++next_iter;
867 
868 #ifdef DEBUG_INSTANTIATIONS
869  if (inst->prod)
870  print_with_symbols (thisAgent, "\nDeallocate instantiation of %y",inst->prod->name);
871 #endif
872 
873  level = inst->match_goal_level;
874 
875  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next)
876  {
877  if (cond->type==POSITIVE_CONDITION)
878  {
879 
880  /* mvp 6-22-94, modified 94.01.17 by AGR with lotsa help from GAP */
881  if (cond->bt.prohibits)
882  {
883  c_old = c = cond->bt.prohibits;
884  cond->bt.prohibits = NIL;
885  for (; c!=NIL; c=c->rest)
886  {
887  pref = static_cast<preference *>(c->first);
888 #ifdef DO_TOP_LEVEL_REF_CTS
889  if (level > TOP_GOAL_LEVEL)
890 #endif
891  {
892  preference_remove_ref (thisAgent, pref);
893  }
894  }
895  free_list (thisAgent, c_old);
896  }
897  /* mvp done */
898 
899  /* voigtjr, nlderbin:
900  We flattened out the following recursive loop in order to prevent a stack
901  overflow that happens when the chain of backtrace instantiations is very long:
902 
903  retract_instantiation
904  possibly_deallocate_instantiation
905  loop start:
906  deallocate_instantiation (here)
907  preference_remove_ref
908  possibly_deallocate_preferences_and_clones
909  deallocate_preference
910  possibly_deallocate_instantiation
911  goto loop start
912  */
913 #ifndef DO_TOP_LEVEL_REF_CTS
914  if (level > TOP_GOAL_LEVEL)
915 #endif
916  {
917  wme_remove_ref (thisAgent, cond->bt.wme_);
918  if (cond->bt.trace)
919  {
920  cond->bt.trace->reference_count--;
921  if (cond->bt.trace->reference_count == 0)
922  {
923  preference *clone;
924 
925  if (cond->bt.trace->reference_count)
926  {
927  continue;
928  }
929  bool has_active_clones = false;
930  for (clone=cond->bt.trace->next_clone; clone!=NIL; clone=clone->next_clone)
931  {
932  if ( clone->reference_count )
933  {
934  has_active_clones = true;
935  }
936  }
937  if ( has_active_clones )
938  {
939  continue;
940  }
941  for ( clone = cond->bt.trace->prev_clone; clone != NIL; clone = clone->prev_clone )
942  {
943  if ( clone->reference_count )
944  {
945  has_active_clones = true;
946  }
947  }
948  if ( has_active_clones )
949  {
950  continue;
951  }
952 
953  // The clones are hopefully a simple case so we just call deallocate_preference on them.
954  // Someone needs to create a test case to push this boundary...
955  {
956  preference* clone = cond->bt.trace->next_clone;
957  preference* next;
958  while (clone) {
959  next = clone->next_clone;
960  deallocate_preference (thisAgent, clone);
961  clone = next;
962  }
963  clone = cond->bt.trace->prev_clone;
964  while (clone) {
965  next = clone->prev_clone;
966  deallocate_preference (thisAgent, clone);
967  clone = next;
968  }
969  }
970 
971  /* --- deallocate pref --- */
972  /* --- remove it from the list of bt.trace's for its match goal --- */
973  if ( cond->bt.trace->on_goal_list )
974  {
977  cond->bt.trace, all_of_goal_next, all_of_goal_prev );
978  }
979 
980  /* --- remove it from the list of bt.trace's from that instantiation --- */
981  remove_from_dll( cond->bt.trace->inst->preferences_generated, cond->bt.trace, inst_next, inst_prev );
982  if ( ( !cond->bt.trace->inst->preferences_generated ) && ( !cond->bt.trace->inst->in_ms ) )
983  {
984  next_iter = inst_list.insert( next_iter, cond->bt.trace->inst );
985  }
986 
987  cond_stack.push_back( cond );
988  } // if
989  } // if
990  } // if
991  /* voigtjr, nlderbin end */
992  } // if
993  } // for
994  } // while
995 
996  // free condition symbols and pref
997  while( !cond_stack.empty() )
998  {
999  condition* temp = cond_stack.back();
1000  cond_stack.pop_back();
1001 
1002  /* --- dereference component symbols --- */
1003  symbol_remove_ref( thisAgent, temp->bt.trace->id );
1004  symbol_remove_ref( thisAgent, temp->bt.trace->attr );
1005  symbol_remove_ref( thisAgent, temp->bt.trace->value );
1006  if ( preference_is_binary( temp->bt.trace->type ) )
1007  {
1008  symbol_remove_ref( thisAgent, temp->bt.trace->referent );
1009  }
1010 
1011  if ( temp->bt.trace->wma_o_set )
1012  {
1013  wma_remove_pref_o_set( thisAgent, temp->bt.trace );
1014  }
1015 
1016  /* --- free the memory --- */
1017  free_with_pool( &thisAgent->preference_pool, temp->bt.trace );
1018  }
1019 
1020  // free instantiations in the reverse order
1021  inst_mpool_list::reverse_iterator riter = inst_list.rbegin();
1022  while( riter != inst_list.rend() )
1023  {
1024  instantiation* temp = *riter;
1025  ++riter;
1026 
1028  deallocate_list_of_nots( thisAgent, temp->nots );
1029  if ( temp->prod )
1030  {
1031  production_remove_ref( thisAgent, temp->prod );
1032  }
1033  free_with_pool( &thisAgent->instantiation_pool, temp );
1034  }
1035 }
1036 
1037 /* -----------------------------------------------------------------------
1038  Retract Instantiation
1039 
1040  This retracts the given instantiation.
1041 ----------------------------------------------------------------------- */
1042 
1043 void retract_instantiation (agent* thisAgent, instantiation *inst) {
1044  preference *pref, *next;
1045  Bool retracted_a_preference;
1046  Bool trace_it;
1047 
1048  /* --- invoke callback function --- */
1049  soar_invoke_callbacks(thisAgent,
1051  static_cast<soar_call_data>(inst));
1052 
1053  retracted_a_preference = FALSE;
1054 
1055  trace_it = trace_firings_of_inst (thisAgent, inst);
1056 
1057  /* --- retract any preferences that are in TM and aren't o-supported --- */
1058  pref = inst->preferences_generated;
1059 
1060  while (pref!=NIL) {
1061  next = pref->inst_next;
1062  if (pref->in_tm && (! pref->o_supported)) {
1063 
1064  if (trace_it) {
1065  if (!retracted_a_preference) {
1066  if (get_printer_output_column(thisAgent)!=1) print (thisAgent, "\n"); /* AGR 617/634 */
1067  print (thisAgent, "Retracting ");
1068  print_instantiation_with_wmes (thisAgent, inst,
1069  static_cast<wme_trace_type>(thisAgent->sysparams[TRACE_FIRINGS_WME_TRACE_TYPE_SYSPARAM]),1);
1071  print (thisAgent, " -->\n");
1072  xml_object( thisAgent, kTagActionSideMarker );
1073  }
1074  }
1076  print (thisAgent, " ");
1077  print_preference (thisAgent, pref);
1078  }
1079  }
1080 
1081  remove_preference_from_tm (thisAgent, pref);
1082  retracted_a_preference = TRUE;
1083  }
1084  pref = next;
1085  }
1086 
1087  /* --- remove inst from list of instantiations of this production --- */
1088  remove_from_dll (inst->prod->instantiations, inst, next, prev);
1089 
1090  /* --- if retracting a justification, excise it --- */
1091  /*
1092  * if the reference_count on the production is 1 (or less) then the
1093  * only thing supporting this justification is the instantiation, hence
1094  * it has already been excised, and doing it again is wrong.
1095  */
1096  production* prod = inst->prod;
1097  if ( prod->type==JUSTIFICATION_PRODUCTION_TYPE && prod->reference_count > 1 )
1098  {
1099  excise_production (thisAgent, prod, FALSE);
1100  }
1101  else if ( prod->type == CHUNK_PRODUCTION_TYPE )
1102  {
1104 
1105  // we care about production history of chunks if...
1106  // - we are dealing with a non-RL rule and all chunks are subject to apoptosis OR
1107  // - we are dealing with an RL rule that...
1108  // - has not been updated by RL AND
1109  // - is not in line to be updated by RL
1110  if ( apoptosis != rl_param_container::apoptosis_none )
1111  {
1112  if ( ( !prod->rl_rule && ( apoptosis == rl_param_container::apoptosis_chunks ) ) ||
1113  ( prod->rl_rule && ( static_cast<int64_t>( prod->rl_update_count ) == 0 ) && ( prod->rl_ref_count == 0 ) ) )
1114  {
1115  thisAgent->rl_prods->reference_object( prod, 1 );
1116  }
1117  }
1118  }
1119 
1120  /* --- mark as no longer in MS, and possibly deallocate --- */
1121  inst->in_ms = FALSE;
1122  possibly_deallocate_instantiation (thisAgent, inst);
1123 }
1124 
1125 /* -----------------------------------------------------------------------
1126  Assert New Preferences
1127 
1128  This routine scans through newly_created_instantiations, asserting
1129  each preference generated except for o-rejects. It also removes
1130  each instantiation from newly_created_instantiations, linking each
1131  onto the list of instantiations for that particular production.
1132  O-rejects are bufferred and handled after everything else.
1133 
1134  Note that some instantiations on newly_created_instantiations are not
1135  in the match set--for the initial instantiations of chunks/justifications,
1136  if they don't match WM, we have to assert the o-supported preferences
1137  and throw away the rest.
1138 ----------------------------------------------------------------------- */
1139 
1140 void assert_new_preferences (agent* thisAgent, pref_buffer_list& bufdeallo)
1141 {
1142  instantiation *inst, *next_inst;
1143  preference *pref, *next_pref;
1144  preference *o_rejects;
1145 
1146  o_rejects = NIL;
1147 
1148 
1149  /* REW: begin 09.15.96 */
1150  if (thisAgent->soar_verbose_flag == TRUE) {
1151  printf("\n in assert_new_preferences:");
1152  xml_generate_verbose(thisAgent, "in assert_new_preferences:");
1153  }
1154  /* REW: end 09.15.96 */
1155 
1156 #ifdef O_REJECTS_FIRST
1157  {
1158 
1159  //slot *s;
1160  //preference *p, *next_p;
1161 
1162  /* Do an initial loop to process o-rejects, then re-loop
1163  to process normal preferences.
1164  */
1165  for (inst = thisAgent->newly_created_instantiations; inst != NIL; inst = next_inst) {
1166  next_inst = inst->next;
1167 
1168  for (pref = inst->preferences_generated; pref != NIL; pref = next_pref) {
1169  next_pref = pref->inst_next;
1170  if ((pref->type == REJECT_PREFERENCE_TYPE) && (pref->o_supported)) {
1171  /* --- o-reject: just put it in the buffer for later --- */
1172  pref->next = o_rejects;
1173  o_rejects = pref;
1174  }
1175  }
1176  }
1177 
1178  if (o_rejects)
1179  process_o_rejects_and_deallocate_them (thisAgent, o_rejects, bufdeallo);
1180 
1181  // s = find_slot(pref->id, pref->attr);
1182  // if (s) {
1183  // /* --- remove all pref's in the slot that have the same value --- */
1184  // p = s->all_preferences;
1185  // while (p) {
1186  // next_p = p->all_of_slot_next;
1187  // if (p->value == pref->value)
1188  // remove_preference_from_tm(thisAgent, p);
1189  // p = next_p;
1190  // }
1191  // }
1193  // }
1194  // }
1195  // }
1196 
1197  }
1198 #endif
1199 
1200  for (inst=thisAgent->newly_created_instantiations;
1201  inst!=NIL;
1202  inst=next_inst)
1203  {
1204  next_inst = inst->next;
1205  if (inst->in_ms)
1206  insert_at_head_of_dll (inst->prod->instantiations, inst, next, prev);
1207 
1208  /* REW: begin 09.15.96 */
1209  if (thisAgent->soar_verbose_flag == TRUE) {
1210  print_with_symbols(thisAgent, "\n asserting instantiation: %y\n",
1211  inst->prod->name);
1212  char buf[256];
1213  SNPRINTF(buf, 254, "asserting instantiation: %s", symbol_to_string(thisAgent, inst->prod->name, true, 0, 0));
1214  xml_generate_verbose(thisAgent, buf);
1215  }
1216  /* REW: end 09.15.96 */
1217 
1218  for (pref=inst->preferences_generated; pref!=NIL; pref=next_pref)
1219  {
1220  next_pref = pref->inst_next;
1221  if ((pref->type==REJECT_PREFERENCE_TYPE) && (pref->o_supported))
1222  {
1223 #ifndef O_REJECTS_FIRST
1224  /* --- o-reject: just put it in the buffer for later --- */
1225  pref->next = o_rejects;
1226  o_rejects = pref;
1227 #endif
1228 
1229  /* REW: begin 09.15.96 */
1230  /* No knowledge retrieval necessary in Operand2 */
1231  /* REW: end 09.15.96 */
1232 
1233  }
1234  else if (inst->in_ms || pref->o_supported)
1235  {
1236  /* --- normal case --- */
1237  if ( add_preference_to_tm (thisAgent, pref) )
1238  {
1239  /* REW: begin 09.15.96 */
1240  /* No knowledge retrieval necessary in Operand2 */
1241  /* REW: end 09.15.96 */
1242 
1243 
1244  if ( wma_enabled( thisAgent ) )
1245  {
1246  wma_activate_wmes_in_pref( thisAgent, pref );
1247  }
1248  }
1249  else
1250  {
1251  // NLD: the preference was o-supported, at
1252  // the top state, and was asserting an acceptable
1253  // preference for a WME that was already
1254  // o-supported. hence unnecessary.
1255 
1256  preference_add_ref( pref );
1257  preference_remove_ref( thisAgent, pref );
1258  }
1259  }
1260  else
1261  {
1262  /* --- inst. is refracted chunk, and pref. is not o-supported:
1263  remove the preference --- */
1264 
1265  /* --- first splice it out of the clones list--otherwise we might
1266  accidentally deallocate some clone that happens to have refcount==0
1267  just because it hasn't been asserted yet --- */
1268 
1269  if (pref->next_clone)
1270  pref->next_clone->prev_clone = pref->prev_clone;
1271  if (pref->prev_clone)
1272  pref->prev_clone->next_clone = pref->next_clone;
1273  pref->next_clone = pref->prev_clone = NIL;
1274 
1275  /* --- now add then remove ref--this should result in deallocation */
1276  preference_add_ref (pref);
1277  preference_remove_ref (thisAgent, pref);
1278  }
1279  }
1280  }
1281 #ifndef O_REJECTS_FIRST
1282  if (o_rejects)
1283  process_o_rejects_and_deallocate_them (thisAgent, o_rejects, bufdeallo);
1284 #endif
1285 }
1286 
1287 /* -----------------------------------------------------------------------
1288  Do Preference Phase
1289 
1290  This routine is called from the top level to run the preference phase.
1291 ----------------------------------------------------------------------- */
1292 
1293 void do_preference_phase (agent* thisAgent) {
1294  instantiation *inst = 0;
1295 
1296 
1297 /* AGR 617/634: These are 2 bug reports that report the same problem,
1298  namely that when 2 chunk firings happen in succession, there is an
1299  extra newline printed out. The simple fix is to monitor
1300  get_printer_output_column and see if it's at the beginning of a line
1301  or not when we're ready to print a newline. 94.11.14 */
1302 
1303 
1304  if (thisAgent->sysparams[TRACE_PHASES_SYSPARAM]) {
1305  if (thisAgent->current_phase == APPLY_PHASE) { /* it's always IE for PROPOSE */
1306  xml_begin_tag( thisAgent, kTagSubphase );
1307  xml_att_val( thisAgent, kPhase_Name, kSubphaseName_FiringProductions );
1308  switch (thisAgent->FIRING_TYPE) {
1309  case PE_PRODS:
1310  print (thisAgent, "\t--- Firing Productions (PE) For State At Depth %d ---\n", thisAgent->active_level); // SBW 8/4/2008: added active_level
1311  xml_att_val( thisAgent, kPhase_FiringType, kPhaseFiringType_PE );
1312  break;
1313  case IE_PRODS:
1314  print (thisAgent, "\t--- Firing Productions (IE) For State At Depth %d ---\n", thisAgent->active_level); // SBW 8/4/2008: added active_level
1315  xml_att_val( thisAgent, kPhase_FiringType, kPhaseFiringType_IE );
1316  break;
1317  }
1318  std::string levelString;
1319  to_string(thisAgent->active_level, levelString);
1320  xml_att_val( thisAgent, kPhase_LevelNum, levelString.c_str()); // SBW 8/4/2008: active_level for XML output mode
1321  xml_end_tag( thisAgent, kTagSubphase );
1322  }
1323  }
1324 
1325  if ( wma_enabled( thisAgent ) )
1326  {
1327  wma_activate_wmes_tested_in_prods( thisAgent );
1328  }
1329 
1330  /* New waterfall model: */
1331  // Save previous active level for usage on next elaboration cycle.
1332  thisAgent->highest_active_level = thisAgent->active_level;
1333  thisAgent->highest_active_goal = thisAgent->active_goal;
1334 
1335  thisAgent->change_level = thisAgent->highest_active_level;
1336  thisAgent->next_change_level = thisAgent->highest_active_level;
1337 
1338  // Temporary list to buffer deallocation of some preferences until
1339  // the inner elaboration loop is over.
1340 #ifdef USE_MEM_POOL_ALLOCATORS
1342 #else
1343  pref_buffer_list bufdeallo;
1344 #endif
1345 
1346  // inner elaboration cycle
1347  for (;;) {
1348  thisAgent->change_level = thisAgent->next_change_level;
1349 
1350  if (thisAgent->sysparams[TRACE_WATERFALL_SYSPARAM]) {
1351  print (thisAgent, "\n--- Inner Elaboration Phase, active level %d", thisAgent->active_level);
1352  if (thisAgent->active_goal) {
1353  print_with_symbols (thisAgent, " (%y)", thisAgent->active_goal);
1354  }
1355  print (thisAgent, " ---\n");
1356  }
1357 
1358  thisAgent->newly_created_instantiations = NIL;
1359 
1360  Bool assertionsExist = FALSE;
1361  production *prod = 0;
1362  struct token_struct *tok = 0;
1363  wme *w = 0;
1364  Bool once = TRUE;
1365  while (postpone_assertion (thisAgent, &prod, &tok, &w)) {
1366  assertionsExist = TRUE;
1367 
1368  if (thisAgent->max_chunks_reached) {
1370  thisAgent->system_halted = TRUE;
1371  soar_invoke_callbacks(thisAgent,
1373  0);
1374  return;
1375  }
1376 
1377  if (prod->type == JUSTIFICATION_PRODUCTION_TYPE) {
1379 
1380  // don't fire justifications
1381  continue;
1382  }
1383 
1384  if (shouldCreateInstantiation(thisAgent, prod, tok, w)) {
1385  once = FALSE;
1387  create_instantiation (thisAgent, prod, tok, w);
1388  }
1389  }
1390 
1391  // New waterfall model: something fired or is pending to fire at this level,
1392  // so this active level becomes the next change level.
1393  if (assertionsExist) {
1394  if (thisAgent->active_level > thisAgent->next_change_level) {
1395  thisAgent->next_change_level = thisAgent->active_level;
1396  }
1397  }
1398 
1399  // New waterfall model: push unfired matches back on to the assertion lists
1400  restore_postponed_assertions(thisAgent);
1401 
1402  assert_new_preferences (thisAgent, bufdeallo);
1403 
1404  // Update accounting
1405  thisAgent->inner_e_cycle_count++;
1406 
1407  if (thisAgent->active_goal == NIL) {
1408  if (thisAgent->sysparams[TRACE_WATERFALL_SYSPARAM]) {
1409  print(thisAgent, " inner elaboration loop doesn't have active goal.\n");
1410  }
1411  break;
1412  }
1413 
1414  if (thisAgent->active_goal->id.lower_goal == NIL) {
1415  if (thisAgent->sysparams[TRACE_WATERFALL_SYSPARAM]) {
1416  print(thisAgent, " inner elaboration loop at bottom goal.\n");
1417  }
1418  break;
1419  }
1420 
1421  if (thisAgent->current_phase == APPLY_PHASE) {
1422  thisAgent->active_goal = highest_active_goal_apply(thisAgent, thisAgent->active_goal->id.lower_goal, TRUE);
1423  } else {
1424  assert(thisAgent->current_phase == PROPOSE_PHASE);
1425  thisAgent->active_goal = highest_active_goal_propose(thisAgent, thisAgent->active_goal->id.lower_goal, TRUE);
1426  }
1427 
1428  if (thisAgent->active_goal != NIL)
1429  {
1430  thisAgent->active_level = thisAgent->active_goal->id.level;
1431  } else {
1432  if (thisAgent->sysparams[TRACE_WATERFALL_SYSPARAM]) {
1433  print(thisAgent, " inner elaboration loop finished but not at quiescence.\n");
1434  }
1435  break;
1436  }
1437  } // end inner elaboration loop
1438 
1439  // Deallocate preferences delayed during inner elaboration loop.
1440  for ( pref_buffer_list::iterator iter=bufdeallo.begin(); iter!=bufdeallo.end(); ++iter )
1441  {
1442  preference_remove_ref( thisAgent, *iter );
1443  }
1444 
1445  // Restore previous active level
1446  thisAgent->active_level = thisAgent->highest_active_level;
1447  thisAgent->active_goal = thisAgent->highest_active_goal;
1448  /* End new waterfall model */
1449 
1450  while (get_next_retraction (thisAgent, &inst))
1451  retract_instantiation (thisAgent, inst);
1452 
1453  /* REW: begin 08.20.97 */
1454  /* In Waterfall, if there are nil goal retractions, then we want to
1455  retract them as well, even though they are not associated with any
1456  particular goal (because their goal has been deleted). The
1457  functionality of this separate routine could have been easily
1458  combined in get_next_retraction but I wanted to highlight the
1459  distinction between regualr retractions (those that can be
1460  mapped onto a goal) and nil goal retractions that require a
1461  special data strucutre (because they don't appear on any goal)
1462  REW. */
1463 
1464  if (thisAgent->nil_goal_retractions) {
1465  while (get_next_nil_goal_retraction (thisAgent, &inst))
1466  retract_instantiation (thisAgent, inst);
1467  }
1468  /* REW: end 08.20.97 */
1469 
1470 }
1471