Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
rete.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: rete.cpp
11  *
12  * =======================================================================
13  *
14  * All_wmes_in_rete is the header for a dll of all the wmes currently
15  * in the rete. (This is normally equal to all of WM, except at times
16  * when WM changes have been buffered but not yet done.) The wmes
17  * are linked via their "rete_next" and "rete_prev" fields.
18  * Num_wmes_in_rete counts how many wmes there are in the rete.
19  *
20  * Init_rete() initializes the rete. It should be called at startup time.
21  *
22  * Any_assertions_or_retractions_ready() returns TRUE iff there are any
23  * pending changes to the match set. This is used to test for quiescence.
24  * Get_next_assertion() retrieves a pending assertion (returning TRUE) or
25  * returns FALSE is no more are available. Get_next_retraction() is
26  * similar.
27  *
28  * Add_production_to_rete() adds a given production, with a given LHS,
29  * to the rete. If "refracted_inst" is non-NIL, it should point to an
30  * initial instantiation of the production. This routine returns one
31  * of NO_REFRACTED_INST, REFRACTED_INST_MATCHED, etc. (see below).
32  * Excise_production_from_rete() removes the given production from the
33  * rete, and enqueues all its existing instantiations as pending
34  * retractions.
35  *
36  * Add_wme_to_rete() and remove_wme_from_rete() inform the rete of changes
37  * to WM.
38  *
39  * P_node_to_conditions_and_nots() takes a p_node and (optionally) a
40  * token/wme pair, and reconstructs the (optionally instantiated) LHS
41  * for the production. The firer uses this to build the instantiated
42  * conditions; the printer uses it to reconstruct the LHS for printing.
43  * Get_symbol_from_rete_loc() takes a token/wme pair and a location
44  * specification (levels_up/field_num), examines the match (token/wme),
45  * and returns the symbol at that location. The firer uses this for
46  * resolving references in RHS actions to variables bound on the LHS.
47  *
48  * Count_rete_tokens_for_production() returns a count of the number of
49  * tokens currently in use for the given production.
50  *
51  * Print_partial_match_information(), print_match_set(), and
52  * print_rete_statistics() do printouts for various interface routines.
53  *
54  * Save_rete_net() and load_rete_net() are used for the fastsave/load
55  * commands. They save/load everything to/from the given (already open)
56  * files. They return TRUE if successful, FALSE if any error occurred.
57  *
58  * =======================================================================
59  */
60 
61 /* ======================================================================
62 
63  Rete Net Routines for Soar 6
64 
65  TABLE OF CONTENTS (each part is labeled "SECTION" in the code)
66 
67  1: Rete Net Structures and Declarations
68  2: Match Set Changes
69  3: Alpha Portion of the Rete Net
70  4: Beta Net Initialization and Primitive Construction Routines
71  5: Beta Net Primitive Destruction Routines
72  6: Variable Bindings and Locations
73  7: Varnames and Node_Varnames
74  8: Building the Rete Net: Condition-To-Node Converstion
75  9: Production Addition and Excising
76  10: Building Conditions (instantiated or not) from the Rete Net
77  11: Rete Test Evaluation Routines
78  12: Beta Node Interpreter Routines: Mem, Pos, and MP Nodes
79  13: Beta Node Interpreter Routines: Negative Nodes
80  14: Beta Node Interpreter Routines: CN and CN_PARTNER Nodes
81  15: Beta Node Interpreter Routines: Production Nodes
82  16: Beta Node Interpreter Routines: Tree-Based Removal
83  17: Fast, Compact Save/Reload of the Whole Rete Net
84  18: Statistics and User Interface Utilities
85  19: Rete Initialization
86 
87 ====================================================================== */
88 
89 #include <stdlib.h>
90 
91 #include "rete.h"
92 #include "kernel.h"
93 #include "mem.h"
94 #include "wmem.h"
95 #include "gdatastructs.h"
96 #include "explain.h"
97 #include "symtab.h"
98 #include "agent.h"
99 #include "print.h"
100 #include "production.h"
101 #include "init_soar.h"
102 #include "instantiations.h"
103 #include "rhsfun.h"
104 #include "lexer.h"
105 #include "xml.h"
106 #include "soar_TraceNames.h"
107 
108 #include "reinforcement_learning.h"
109 #include "episodic_memory.h"
110 #include "semantic_memory.h"
111 #include "utilities.h"
112 
113 #include "assert.h"
114 
115 #include <sstream>
116 
117 /* ----------- basic functionality switches ----------- */
118 
119 /* Set to FALSE to preserve variable names in chunks (takes extra space) */
120 #define discard_chunk_varnames TRUE
121 
122 /* ----------- debugging switches ----------- */
123 
124 /* Uncomment the following line to get pnode printouts */
125 /* #define DEBUG_RETE_PNODES */
126 
127 /* REW: begin 08.20.97 */
128 /* For information on the Waterfall processing in rete.cpp */
129 /* #define DEBUG_WATERFALL */
130 /* REW: end 08.20.97 */
131 
132 /* ----------- statistics switches ----------- */
133 
134 /* Uncomment the following line to get statistics on token counts with and
135  without sharing */
136 /* #define TOKEN_SHARING_STATS */
137 
138 /* Uncomment the following line to gather statistics on null activations */
139 /* #define NULL_ACTIVATION_STATS */
140 
141 /* Uncomment the following line to gather statistics on beta node sharing */
142 /* #define SHARING_FACTORS */
143 
144 /* ----------- handle inter-switch dependencies ----------- */
145 
146 /* --- TOKEN_SHARING_STATS requires SHARING_FACTORS --- */
147 #ifdef TOKEN_SHARING_STATS
148 #ifndef SHARING_FACTORS
149 #define SHARING_FACTORS
150 #endif
151 #endif
152 
153 /* --- Calculate DO_ACTIVATION_STATS_ON_REMOVALS --- */
154 #ifdef NULL_ACTIVATION_STATS
155 #ifndef DO_ACTIVATION_STATS_ON_REMOVALS
156 #define DO_ACTIVATION_STATS_ON_REMOVALS
157 #endif
158 #endif
159 
160 
161 
162 using namespace soar_TraceNames;
163 
164 
165 
166 
167 
168 /* **********************************************************************
169 
170  SECTION 1: Rete Net Structures and Declarations
171 
172 ********************************************************************** */
173 
174 /* ----------------------------------------------------------------------
175 
176  Structures and Declarations: Alpha Portion of the Rete Net
177 
178 ---------------------------------------------------------------------- */
179 
180 /* --- dll of all wmes currently in the rete: this is needed to
181  initialize newly created alpha memories --- */
182 /* wme *all_wmes_in_rete; (moved to glob_vars.h) */
183 
184 /* --- structure of each alpha memory --- */
185 typedef struct alpha_mem_struct {
186  struct alpha_mem_struct *next_in_hash_table; /* next mem in hash bucket */
187  struct right_mem_struct *right_mems; /* dll of right_mem structures */
188  struct rete_node_struct *beta_nodes; /* list of attached beta nodes */
189  struct rete_node_struct *last_beta_node; /* tail of above dll */
190  Symbol *id; /* constants tested by this alpha mem */
191  Symbol *attr; /* (NIL if this alpha mem ignores that field) */
193  Bool acceptable; /* does it test for acceptable pref? */
194  uint32_t am_id; /* id for hashing */
195  uint64_t reference_count; /* number of beta nodes using this mem */
197 } alpha_mem;
198 
199 /* --- the entry for one WME in one alpha memory --- */
200 typedef struct right_mem_struct {
201  wme *w; /* the wme */
202  alpha_mem *am; /* the alpha memory */
203  struct right_mem_struct *next_in_bucket, *prev_in_bucket; /*hash bucket dll*/
204  struct right_mem_struct *next_in_am, *prev_in_am; /*rm's in this amem*/
205  struct right_mem_struct *next_from_wme, *prev_from_wme; /*tree-based remove*/
206 } right_mem;
207 
208 /* Note: right_mem's are stored in hash table thisAgent->right_ht */
209 
210 /* ----------------------------------------------------------------------
211 
212  Structures and Declarations: Beta Portion of the Rete Net
213 
214 ---------------------------------------------------------------------- */
215 
216 /* --- types of tests found at beta nodes --- */
217 #define CONSTANT_RELATIONAL_RETE_TEST 0x00
218 #define VARIABLE_RELATIONAL_RETE_TEST 0x10
219 #define DISJUNCTION_RETE_TEST 0x20
220 #define ID_IS_GOAL_RETE_TEST 0x30
221 #define ID_IS_IMPASSE_RETE_TEST 0x31
222 //#define test_is_constant_relational_test(x) (((x) & 0xF0)==0x00)
223 //#define test_is_variable_relational_test(x) (((x) & 0xF0)==0x10)
224 
226 {
227  return (((x) & 0xF0)==CONSTANT_RELATIONAL_RETE_TEST);
228 }
229 
231 {
232  return (((x) & 0xF0)==VARIABLE_RELATIONAL_RETE_TEST);
233 }
234 
235 /* --- for the last two (i.e., the relational tests), we add in one of
236  the following, to specifiy the kind of relation --- */
237 #define RELATIONAL_EQUAL_RETE_TEST 0x00
238 #define RELATIONAL_NOT_EQUAL_RETE_TEST 0x01
239 #define RELATIONAL_LESS_RETE_TEST 0x02
240 #define RELATIONAL_GREATER_RETE_TEST 0x03
241 #define RELATIONAL_LESS_OR_EQUAL_RETE_TEST 0x04
242 #define RELATIONAL_GREATER_OR_EQUAL_RETE_TEST 0x05
243 #define RELATIONAL_SAME_TYPE_RETE_TEST 0x06
244 //#define kind_of_relational_test(x) ((x) & 0x0F)
245 //#define test_is_not_equal_test(x) (((x)==0x01) || ((x)==0x11))
246 
248 {
249  return ((x) & 0x0F);
250 }
251 
253 {
256 }
257 
258 /* --- tells where to find a variable --- */
259 typedef unsigned short rete_node_level;
260 
261 typedef struct var_location_struct {
262  rete_node_level levels_up; /* 0=current node's alphamem, 1=parent's, etc. */
263  byte field_num; /* 0=id, 1=attr, 2=value */
264 } var_location;
265 
266 /* define an equality predicate for var_location structures */
267 /*#define var_locations_equal(v1,v2) \
268  ( ((v1).levels_up==(v2).levels_up) && ((v1).field_num==(v2).field_num) )*/
270 {
271  return ( ((v1).levels_up==(v2).levels_up) && ((v1).field_num==(v2).field_num) );
272 }
273 
274 /* --- extract field (id/attr/value) from wme --- */
275 /* WARNING: this relies on the id/attr/value fields being consecutive in
276  the wme structure (defined in soarkernel.h) */
277 /*#define field_from_wme(wme,field_num) \
278  ( (&((wme)->id))[(field_num)] )*/
279 
280 /* The semantics of this function is the same as
281  * inline Symbol * field_from_wme(wme * _wme, byte field_num) {
282  * switch (field_num) {
283  * case 0:
284  * return _wme->id;
285  * case 1:
286  * return _wme->attr;
287  * case 2:
288  * return _wme->value;
289  * }
290  * }
291  */
292 inline Symbol * field_from_wme(wme * _wme, byte field_num)
293 {
294  return ( (&((_wme)->id))[(field_num)] );
295 }
296 
297 /* --- gives data for a test that must be applied at a node --- */
298 typedef struct rete_test_struct {
299  byte right_field_num; /* field (0, 1, or 2) from wme */
300  byte type; /* test type (ID_IS_GOAL_RETE_TEST, etc.) */
302  var_location variable_referent; /* for relational tests to a variable */
303  Symbol *constant_referent; /* for relational tests to a constant */
304  list *disjunction_list; /* list of symbols in disjunction test */
305  } data;
306  struct rete_test_struct *next; /* next in list of tests at the node */
307 } rete_test;
308 
309 /* --- types and structure of beta nodes --- */
310 /* key: bit 0 --> hashed */
311 /* bit 1 --> memory */
312 /* bit 2 --> positive join */
313 /* bit 3 --> negative join */
314 /* bit 4 --> split from beta memory */
315 /* bit 6 --> various special types */
316 
317 /* Warning: If you change any of these or add ones, be sure to update the
318  bit-twiddling macros just below */
319 #define UNHASHED_MEMORY_BNODE 0x02
320 #define MEMORY_BNODE 0x03
321 #define UNHASHED_MP_BNODE 0x06
322 #define MP_BNODE 0x07
323 #define UNHASHED_POSITIVE_BNODE 0x14
324 #define POSITIVE_BNODE 0x15
325 #define UNHASHED_NEGATIVE_BNODE 0x08
326 #define NEGATIVE_BNODE 0x09
327 #define DUMMY_TOP_BNODE 0x40
328 #define DUMMY_MATCHES_BNODE 0x41
329 #define CN_BNODE 0x42
330 #define CN_PARTNER_BNODE 0x43
331 #define P_BNODE 0x44
332 
333 /*
334 #define bnode_is_hashed(x) ((x) & 0x01)
335 #define bnode_is_memory(x) ((x) & 0x02)
336 #define bnode_is_positive(x) ((x) & 0x04)
337 #define bnode_is_negative(x) ((x) & 0x08)
338 #define bnode_is_posneg(x) ((x) & 0x0C)
339 #define bnode_is_bottom_of_split_mp(x) ((x) & 0x10)
340 #define real_parent_node(x) ( bnode_is_bottom_of_split_mp((x)->node_type) ? (x)->parent->parent : (x)->parent )
341 */
342 
343 inline byte bnode_is_hashed(byte x) { return ((x) & 0x01); }
344 inline byte bnode_is_memory(byte x) { return ((x) & 0x02); }
345 inline byte bnode_is_positive(byte x) { return ((x) & 0x04); }
346 inline byte bnode_is_negative(byte x) { return ((x) & 0x08); }
347 inline byte bnode_is_posneg(byte x) { return ((x) & 0x0C); }
348 inline byte bnode_is_bottom_of_split_mp(byte x) { return ((x) & 0x10); }
349 
350 /* This function cannot be defined before struct rete_node_struct */
351 inline rete_node * real_parent_node(rete_node * x);
352 
353 /*
354  * Initialize the list with all empty strings.
355  */
356 const char *bnode_type_names[256] =
357 {
358  "","","","","","","","","","","","","","","","",
359  "","","","","","","","","","","","","","","","",
360  "","","","","","","","","","","","","","","","",
361  "","","","","","","","","","","","","","","","",
362  "","","","","","","","","","","","","","","","",
363  "","","","","","","","","","","","","","","","",
364  "","","","","","","","","","","","","","","","",
365  "","","","","","","","","","","","","","","","",
366  "","","","","","","","","","","","","","","","",
367  "","","","","","","","","","","","","","","","",
368  "","","","","","","","","","","","","","","","",
369  "","","","","","","","","","","","","","","","",
370  "","","","","","","","","","","","","","","","",
371  "","","","","","","","","","","","","","","","",
372  "","","","","","","","","","","","","","","","",
373  "","","","","","","","","","","","","","","",""
374 };
375 
376 /* --- data for positive nodes only --- */
377 typedef struct pos_node_data_struct {
378  /* --- dll of left-linked pos nodes from the parent beta memory --- */
379  struct rete_node_struct *next_from_beta_mem, *prev_from_beta_mem;
380 } pos_node_data;
381 
382 /* --- data for both positive and negative nodes --- */
383 typedef struct posneg_node_data_struct {
384  rete_test *other_tests; /* tests other than the hashed test */
385  alpha_mem *alpha_mem_; /* the alpha memory this node uses */
386  struct rete_node_struct *next_from_alpha_mem; /* dll of nodes using that */
387  struct rete_node_struct *prev_from_alpha_mem; /* ... alpha memory */
390 
391 /* --- data for beta memory nodes only --- */
393  /* --- first pos node child that is left-linked --- */
396 
397 /* --- data for cn and cn_partner nodes only --- */
398 typedef struct cn_node_data_struct {
399  struct rete_node_struct *partner; /* cn, cn_partner point to each other */
400 } cn_node_data;
401 
402 /* --- data for production nodes only --- */
403 typedef struct p_node_data_struct {
404  struct production_struct *prod; /* the production */
405  struct node_varnames_struct *parents_nvn; /* records variable names */
406  struct ms_change_struct *tentative_assertions; /* pending MS changes */
408 } p_node_data;
409 
410 #define O_LIST 0 /* moved here from soarkernel.h. only used in rete.cpp */
411 #define I_LIST 1 /* values for prod->OPERAND_which_assert_list */
412 
413 /* --- data for all except positive nodes --- */
414 typedef struct non_pos_node_data_struct {
415  struct token_struct *tokens; /* dll of tokens at this node */
416  unsigned is_left_unlinked:1; /* used on mp nodes only */
418 
419 /* --- structure of a rete beta node --- */
420 typedef struct rete_node_struct {
421  byte node_type; /* tells what kind of node this is */
422 
423  /* -- used only on hashed nodes -- */
424  /* field_num: 0=id, 1=attr, 2=value */
426  /* left_hash_loc_levels_up: 0=current node's alphamem, 1=parent's, etc. */
428  /* node_id: used for hash function */
430 
431 #ifdef SHARING_FACTORS
432  uint64_t sharing_factor;
433 #endif
434 
435  struct rete_node_struct *parent; /* points to parent node */
436  struct rete_node_struct *first_child; /* used for dll of all children, */
437  struct rete_node_struct *next_sibling; /* regardless of unlinking status */
439  pos_node_data pos; /* for pos. nodes */
440  non_pos_node_data np; /* for all other nodes */
441  } a;
443  posneg_node_data posneg; /* for pos, neg, mp nodes */
444  beta_memory_node_data mem; /* for beta memory nodes */
445  cn_node_data cn; /* for cn, cn_partner nodes */
446  p_node_data p; /* for p nodes */
447  } b;
448 } rete_node;
449 
450 /* Now this function can safely be defined. */
452 {
453  return ( bnode_is_bottom_of_split_mp((x)->node_type) ? (x)->parent->parent : (x)->parent );
454 }
455 
456 /* ----------------------------------------------------------------------
457 
458  Structures and Declarations: Right Unlinking
459 
460 ---------------------------------------------------------------------- */
461 
462 /* Note: a node is right unlinked iff the low-order bit of
463  node->b.posneg.next_from_alpha_mem is 1 */
464 
465 /*#define node_is_right_unlinked(node) \
466  (((uint64_t)((node)->b.posneg.next_from_alpha_mem)) & 1)*/
467 inline uint64_t node_is_right_unlinked(rete_node * node)
468 {
469  return reinterpret_cast<uint64_t>(node->b.posneg.next_from_alpha_mem) & 1;
470 }
471 
472 /*#define mark_node_as_right_unlinked(node) { \
473  (node)->b.posneg.next_from_alpha_mem = static_cast<rete_node_struct *>((void *)1); }*/
475 {
476  node->b.posneg.next_from_alpha_mem = reinterpret_cast<rete_node_struct *>(1);
477 }
478 
479 //#define relink_to_right_mem(node) {
480 // rete_node *rtrm_ancestor, *rtrm_prev;
481 // /* find first ancestor that's linked */
482 // rtrm_ancestor = (node)->b.posneg.nearest_ancestor_with_same_am;
483 // while (rtrm_ancestor && node_is_right_unlinked(rtrm_ancestor))
484 // rtrm_ancestor = rtrm_ancestor->b.posneg.nearest_ancestor_with_same_am;
485 // if (rtrm_ancestor) {
486 // /* insert just before that ancestor */
487 // rtrm_prev = rtrm_ancestor->b.posneg.prev_from_alpha_mem;
488 // (node)->b.posneg.next_from_alpha_mem = rtrm_ancestor;
489 // (node)->b.posneg.prev_from_alpha_mem = rtrm_prev;
490 // rtrm_ancestor->b.posneg.prev_from_alpha_mem = (node);
491 // if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node);
492 // else (node)->b.posneg.alpha_mem_->beta_nodes = (node);
493 // } else {
494 // /* no such ancestor, insert at tail of list */
495 // rtrm_prev = (node)->b.posneg.alpha_mem_->last_beta_node;
496 // (node)->b.posneg.next_from_alpha_mem = NIL;
497 // (node)->b.posneg.prev_from_alpha_mem = rtrm_prev;
498 // (node)->b.posneg.alpha_mem_->last_beta_node = (node);
499 // if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node);
500 // else (node)->b.posneg.alpha_mem_->beta_nodes = (node);
501 // } }
502 inline void relink_to_right_mem(rete_node * node)
503 {
504  rete_node *rtrm_ancestor, *rtrm_prev;
505  /* find first ancestor that's linked */
506  rtrm_ancestor = (node)->b.posneg.nearest_ancestor_with_same_am;
507  while (rtrm_ancestor && node_is_right_unlinked(rtrm_ancestor))
508  rtrm_ancestor = rtrm_ancestor->b.posneg.nearest_ancestor_with_same_am;
509  if (rtrm_ancestor) {
510  /* insert just before that ancestor */
511  rtrm_prev = rtrm_ancestor->b.posneg.prev_from_alpha_mem;
512  (node)->b.posneg.next_from_alpha_mem = rtrm_ancestor;
513  (node)->b.posneg.prev_from_alpha_mem = rtrm_prev;
514  rtrm_ancestor->b.posneg.prev_from_alpha_mem = (node);
515  if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node);
516  else (node)->b.posneg.alpha_mem_->beta_nodes = (node);
517  } else {
518  /* no such ancestor, insert at tail of list */
519  rtrm_prev = (node)->b.posneg.alpha_mem_->last_beta_node;
520  (node)->b.posneg.next_from_alpha_mem = NIL;
521  (node)->b.posneg.prev_from_alpha_mem = rtrm_prev;
522  (node)->b.posneg.alpha_mem_->last_beta_node = (node);
523  if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node);
524  else (node)->b.posneg.alpha_mem_->beta_nodes = (node);
525  }
526 }
527 
528 /* This macro cannot be easily converted to an inline function.
529  Some additional changes are required.
530 */
531 #define unlink_from_right_mem(node) { \
532  if ((node)->b.posneg.next_from_alpha_mem == NIL) \
533  (node)->b.posneg.alpha_mem_->last_beta_node = \
534  (node)->b.posneg.prev_from_alpha_mem; \
535  remove_from_dll ((node)->b.posneg.alpha_mem_->beta_nodes, (node), \
536  b.posneg.next_from_alpha_mem, \
537  b.posneg.prev_from_alpha_mem); \
538  mark_node_as_right_unlinked (node); }
539 
540 /* ----------------------------------------------------------------------
541 
542  Structures and Declarations: Left Unlinking
543 
544 ---------------------------------------------------------------------- */
545 
546 /* Note: an unmerged positive node is left unlinked iff the low-order bit of
547  node->a.pos.next_from_beta_mem is 1 */
548 
549 /*#define node_is_left_unlinked(node) \
550  (((uint64_t)((node)->a.pos.next_from_beta_mem)) & 1)*/
551 inline uint64_t node_is_left_unlinked(rete_node * node)
552 {
553  return reinterpret_cast<uint64_t>(node->a.pos.next_from_beta_mem) & 1;
554 }
555 
556 /*#define mark_node_as_left_unlinked(node) { \
557  (node)->a.pos.next_from_beta_mem = static_cast<rete_node_struct *>((void *)1); }*/
559 {
560  node->a.pos.next_from_beta_mem = reinterpret_cast<rete_node_struct *>(1);
561 }
562 
563 /* This macro cannot be easily converted to an inline function.
564  Some additional changes are required.
565 */
566 #define relink_to_left_mem(node) { \
567  insert_at_head_of_dll ((node)->parent->b.mem.first_linked_child, (node), \
568  a.pos.next_from_beta_mem, \
569  a.pos.prev_from_beta_mem); }
570 
571 /* This macro cannot be easily converted to an inline function.
572  Some additional changes are required.
573 */
574 #define unlink_from_left_mem(node) { \
575  remove_from_dll ((node)->parent->b.mem.first_linked_child, (node), \
576  a.pos.next_from_beta_mem, \
577  a.pos.prev_from_beta_mem); \
578  mark_node_as_left_unlinked(node); }
579 
580 /* Note: for merged nodes, we still mark them as left-unlinked, just for
581  uniformity. This probably makes little difference in efficiency. */
582 
583 /*
584 #define make_mp_bnode_left_unlinked(node) {(node)->a.np.is_left_unlinked = 1;}
585 #define make_mp_bnode_left_linked(node) {(node)->a.np.is_left_unlinked = 0;}
586 #define mp_bnode_is_left_unlinked(node) ((node)->a.np.is_left_unlinked)
587 */
588 
590 {
591  (node)->a.np.is_left_unlinked = 1;
592 }
593 
595 {
596  (node)->a.np.is_left_unlinked = 0;
597 }
598 
599 inline unsigned mp_bnode_is_left_unlinked(rete_node * node)
600 {
601  return ((node)->a.np.is_left_unlinked);
602 }
603 
604 /* ----------------------------------------------------------------------
605 
606  Structures and Declarations: Tokens
607 
608 ---------------------------------------------------------------------- */
609 
610 /*#define new_left_token(New,current_node,parent_tok,parent_wme) { \
611  (New)->node = (current_node); \
612  insert_at_head_of_dll ((current_node)->a.np.tokens, (New), \
613  next_of_node, prev_of_node); \
614  (New)->first_child = NIL; \
615  (New)->parent = (parent_tok); \
616  insert_at_head_of_dll ((parent_tok)->first_child, (New), \
617  next_sibling, prev_sibling); \
618  (New)->w = (parent_wme); \
619  if (parent_wme) insert_at_head_of_dll ((parent_wme)->tokens, (New), \
620  next_from_wme, prev_from_wme); }*/
621 inline void new_left_token(token * New, rete_node * current_node,
622  token * parent_tok, wme * parent_wme)
623 {
624  (New)->node = (current_node);
625  insert_at_head_of_dll ((current_node)->a.np.tokens, (New),
626  next_of_node, prev_of_node);
627  (New)->first_child = NIL;
628  (New)->parent = (parent_tok);
629  insert_at_head_of_dll ((parent_tok)->first_child, (New),
630  next_sibling, prev_sibling);
631  (New)->w = (parent_wme);
632  if (parent_wme) insert_at_head_of_dll ((parent_wme)->tokens, (New),
633  next_from_wme, prev_from_wme);
634 }
635 
636 /* Note: (most) tokens are stored in hash table thisAgent->left_ht */
637 
638 /* ----------------------------------------------------------------------
639 
640  Structures and Declarations: Memory Hash Tables
641 
642  Tokens and alpha memory entries (right memory's) as stored in two
643  global hash tables. Unlike most hash tables in Soar, these two tables
644  are not dynamically resized -- their size is fixed at compile-time.
645 ---------------------------------------------------------------------- */
646 
647 /* --- Hash table sizes (actual sizes are powers of 2) --- */
648 #define LOG2_LEFT_HT_SIZE 14
649 #define LOG2_RIGHT_HT_SIZE 14
650 
651 #define LEFT_HT_SIZE (1 << LOG2_LEFT_HT_SIZE)
652 #define RIGHT_HT_SIZE (1 << LOG2_RIGHT_HT_SIZE)
653 
654 #define LEFT_HT_MASK (LEFT_HT_SIZE - 1)
655 #define RIGHT_HT_MASK (RIGHT_HT_SIZE - 1)
656 
657 
658 /* --- Given the hash value (hv), get contents of bucket header cell ---
659 #define left_ht_bucket(hv) \
660  (* ( ((token **) thisAgent->left_ht) + ((hv) & LEFT_HT_MASK)))
661 #define right_ht_bucket(hv) \
662  (* ( ((right_mem **) thisAgent->right_ht) + ((hv) & RIGHT_HT_MASK)))
663 */
664 
665 /* The return value is modified by the calling function,
666  hence the call by reference, */
667 inline token * & left_ht_bucket(agent* thisAgent, uint32_t hv)
668 {
669  return * (reinterpret_cast<token **>(thisAgent->left_ht) + (hv & LEFT_HT_MASK));
670 }
671 
672 inline right_mem * right_ht_bucket(agent* thisAgent, uint32_t hv)
673 {
674  return * (reinterpret_cast<right_mem **>(thisAgent->right_ht) + (hv & RIGHT_HT_MASK));
675 }
676 
677 /*#define insert_token_into_left_ht(tok,hv) { \
678  token **header_zy37; \
679  header_zy37 = ((token **) thisAgent->left_ht) + ((hv) & LEFT_HT_MASK); \
680  insert_at_head_of_dll (*header_zy37, (tok), \
681  a.ht.next_in_bucket, a.ht.prev_in_bucket); }*/
682 inline void insert_token_into_left_ht(agent* thisAgent, token * tok, uint32_t hv)
683 {
684  token **header_zy37;
685  header_zy37 = reinterpret_cast<token **>(thisAgent->left_ht) + (hv & LEFT_HT_MASK);
686  insert_at_head_of_dll (*header_zy37, tok,
687  a.ht.next_in_bucket, a.ht.prev_in_bucket);
688 }
689 
690 /*#define remove_token_from_left_ht(tok,hv) { \
691  fast_remove_from_dll (left_ht_bucket(hv), tok, token, \
692  a.ht.next_in_bucket, a.ht.prev_in_bucket); }*/
693 inline void remove_token_from_left_ht(agent* thisAgent, token * tok, uint32_t hv)
694 {
695  fast_remove_from_dll (left_ht_bucket(thisAgent, hv), tok, token,
696  a.ht.next_in_bucket, a.ht.prev_in_bucket);
697 }
698 
699 /* ----------------------------------------------------------------------
700 
701  Structures and Declarations: Beta Net Interpreter Routines
702 
703 ---------------------------------------------------------------------- */
704 
705 void (*(left_addition_routines[256])) (agent* thisAgent, rete_node *node, token *tok, wme *w) =
706 {
707  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
708  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
709  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
710  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
711  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
712  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
713  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
714  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
715  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
716  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
717  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
718  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
719  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
720  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
721  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
722  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
723 };
724 void (*(right_addition_routines[256])) (agent* thisAgent, rete_node *node, wme *w) =
725 {
726  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
727  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
728  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
729  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
730  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
731  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
732  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
733  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
734  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
735  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
736  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
737  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
738  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
739  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
740  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
741  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
742 };
743 
744 
745 void remove_token_and_subtree (agent* thisAgent, token *tok);
746 
747 /* ----------------------------------------------------------------------
748 
749  Structures and Declarations: Debugging Stuff
750 
751  These get invoked at the entry and exit points of all node activation
752  procedures. Good place to put debugging checks.
753 ---------------------------------------------------------------------- */
754 
755 /* Since these do nothing, I will not convert them to inline functions
756  for the time being. -ajc (5/6/02 */
757 #define activation_entry_sanity_check() {}
758 #define activation_exit_sanity_check() {}
759 
760 /* ----------------------------------------------------------------------
761 
762  Structures and Declarations: Null Activation Statistics
763 
764  Counts the number of null and non-null left activations. Note that
765  this only tallies activations of join nodes for positive conditions;
766  negative nodes and CN stuff is ignored.
767 ---------------------------------------------------------------------- */
768 
769 #ifdef NULL_ACTIVATION_STATS
770 
771 void null_activation_stats_for_right_activation (rete_node *node, rete_node *node_to_ignore_for_null_activation_stats)
772 {
773  if (node==node_to_ignore_for_activation_stats) return;
774  switch (node->node_type) {
775  case POSITIVE_BNODE:
777  thisAgent->num_right_activations++;
778  if (! node->parent->a.np.tokens)
779  thisAgent->num_null_right_activations++;
780  break;
781  case MP_BNODE:
782  case UNHASHED_MP_BNODE:
783  thisAgent->num_right_activations++;
784  if (! node->a.np.tokens) thisAgent->num_null_right_activations++;
785  break;
786  }
787 }
788 
790  switch (node->node_type) {
791  case POSITIVE_BNODE:
793  thisAgent->num_left_activations++;
794  if (node->b.posneg.alpha_mem_->right_mems==NIL)
795  thisAgent->num_null_left_activations++;
796  break;
797  case MP_BNODE:
798  case UNHASHED_MP_BNODE:
799  if (mp_bnode_is_left_unlinked(node)) return;
800  thisAgent->num_left_activations++;
801  if (node->b.posneg.alpha_mem_->right_mems==NIL)
802  thisAgent->num_null_left_activations++;
803  break;
804  }
805 }
806 
808  print ("\nActivations: %lu right (%lu null), %lu left (%lu null)\n",
809  thisAgent->num_right_activations,
810  thisAgent->num_null_right_activations,
811  thisAgent->num_left_activations,
812  thisAgent->num_null_left_activations);
813 }
814 
815 #else
816 
817 /* Since these do nothing, I will not convert them to inline functions
818  for the time being. -ajc (5/6/02 */
819 #define null_activation_stats_for_right_activation(node) {}
820 #define null_activation_stats_for_left_activation(node) {}
821 #define print_null_activation_stats() {}
822 
823 #endif
824 
825 /* ----------------------------------------------------------------------
826 
827  Structures and Declarations: Sharing Factors
828 
829  Sharing factors are computed/updated using two simple rules:
830  (1) Any time we add a new production to the net, when we get all
831  done and have created the p-node, etc., we increment the sharing
832  factor on every node the production uses.
833  (2) Any time we make a brand new node, we initialize its sharing
834  factor to 0. (This will get incremented shortly thereafter, due
835  to rule #1.)
836 
837  Note that there are fancy ways to compute/update sharing factors,
838  not requiring extra scanning-up-the-net all the time as rule 1 does.
839  I went with the ablve way to keep the code small and simple.
840 ---------------------------------------------------------------------- */
841 
842 #ifdef SHARING_FACTORS
843 
844 //#define init_sharing_stats_for_new_node(node) { (node)->sharing_factor = 0; }
845 inline void init_sharing_stats_for_new_node(node * node)
846 {
847  (node)->sharing_factor = 0;
848 }
849 
850 /*#define set_sharing_factor(node,sf) { \
851  int64_t ssf_237; \
852  ssf_237 = (sf) - ((node)->sharing_factor); \
853  (node)->sharing_factor = (sf); \
854  thisAgent->rete_node_counts_if_no_sharing[(node)->node_type]+=ssf_237; }*/
855 inline void set_sharing_factor(rete_node * node, uint64_t sf)
856 {
857  int64_t ssf_237;
858  ssf_237 = (sf) - ((node)->sharing_factor);
859  (node)->sharing_factor = (sf);
860  thisAgent->rete_node_counts_if_no_sharing[(node)->node_type]+=ssf_237;
861 }
862 
863 /* Scans from "node" up to the top node, adds "delta" to sharing factors. */
864 void adjust_sharing_factors_from_here_to_top (rete_node *node, int delta) {
865  while (node!=NIL) {
866  thisAgent->rete_node_counts_if_no_sharing[node->node_type] += delta;
867  node->sharing_factor += delta;
868  if (node->node_type==CN_BNODE) node = node->b.cn.partner;
869  else node = node->parent;
870  }
871 }
872 
873 #else
874 
875 /* Since these do nothing, I will not convert them to inline functions
876  for the time being. -ajc (5/6/02) */
877 #define init_sharing_stats_for_new_node(node) {}
878 #define set_sharing_factor(node,sf) {}
879 #define adjust_sharing_factors_from_here_to_top(node,delta) {}
880 
881 #endif
882 
883 /* ----------------------------------------------------------------------
884 
885  Structures and Declarations: (Extra) Rete Statistics
886 
887 ---------------------------------------------------------------------- */
888 
889 #ifdef TOKEN_SHARING_STATS
890 
891 /* gets real sharing factor -- converts "0" (temporary sharing factor on
892  newly created nodes while we're adding a production to the net) to 1 */
893 /*#define real_sharing_factor(node) \
894  ((node)->sharing_factor ? (node)->sharing_factor : 1)*/
895 inline uint64_t real_sharing_factor(rete_node * node)
896 {
897  return ((node)->sharing_factor ? (node)->sharing_factor : 1);
898 }
899 
900 /*#define token_added(node) { \
901  thisAgent->token_additions++; \
902  thisAgent->token_additions_without_sharing += real_sharing_factor(node);}*/
903 inline void token_added(rete_node * node)
904 {
905  thisAgent->token_additions++;
906  thisAgent->token_additions_without_sharing += real_sharing_factor(node);
907 }
908 
909 #else
910 
911 #define token_added(node) {}
912 
913 #endif
914 
915 /* --- Invoked on every right activation; add=TRUE means right addition --- */
916 /* NOT invoked on removals unless DO_ACTIVATION_STATS_ON_REMOVALS is set */
917 /*#define right_node_activation(node,add) { \
918  null_activation_stats_for_right_activation(node); }*/
919 inline void right_node_activation(rete_node * node, Bool /*add*/)
920 {
921  (void)node;
923 }
924 
925 /* --- Invoked on every left activation; add=TRUE means left addition --- */
926 /* NOT invoked on removals unless DO_ACTIVATION_STATS_ON_REMOVALS is set */
927 /*#define left_node_activation(node,add) { \
928  null_activation_stats_for_left_activation(node); }*/
929 inline void left_node_activation(rete_node * node, Bool /*add*/)
930 {
931  (void)node;
933 }
934 
935 /* --- The following two macros are used when creating/destroying nodes --- */
936 
937 /*#define init_new_rete_node_with_type(node,type) { \
938  (node)->node_type = (type); \
939  thisAgent->rete_node_counts[(type)]++; \
940  init_sharing_stats_for_new_node(node); }*/
941 inline void init_new_rete_node_with_type(agent* thisAgent, rete_node * node, byte type)
942 {
943  (node)->node_type = (type);
944  thisAgent->rete_node_counts[(type)]++;
946 }
947 
948 /*#define update_stats_for_destroying_node(node) { \
949  set_sharing_factor(node,0); \
950  thisAgent->rete_node_counts[(node)->node_type]--; }*/
951 inline void update_stats_for_destroying_node(agent* thisAgent, rete_node * node)
952 {
953  set_sharing_factor(node,0);
954  thisAgent->rete_node_counts[(node)->node_type]--;
955 }
956 
957 
958 
959 
960 
961 
962 
963 
964 
965 
966 
967 
968 
969 
970 
971 
972 
973 /* **********************************************************************
974 
975  SECTION 2: Match Set Changes
976 
977  Match set changes (i.e., additions or deletions of complete production
978  matches) are stored on two lists. There is one global list of all
979  pending ms changes. Each ms change is also stored on a local list
980  for its p-node, containing just the ms changes for that production.
981  The second list is needed for when a match is only temporarily
982  present during one elaboration cycle -- e.g., we make one change to
983  working memory which triggers an addition/retraction, but then make
984  another change to working memory which reverses the previous
985  addition/retraction. After the second change, the p-node gets activated
986  and has to quickly find the thing being reversed. The small local
987  list makes this possible.
988 
989  EXTERNAL INTERFACE:
990  Any_assertions_or_retractions_ready() returns TRUE iff there are any
991  pending changes to the match set. This is used to test for quiescence.
992  Get_next_assertion() retrieves a pending assertion (returning TRUE) or
993  returns FALSE is no more are available. Get_next_retraction() is
994  similar.
995 ********************************************************************** */
996 
997 
998 
999 /* REW: begin 08.20.97 */
1000 
1002 
1003  wme *lowest_goal_wme;
1004  goal_stack_level lowest_level_so_far;
1005  token *tok;
1006 
1007 #ifdef DEBUG_WATERFALL
1008  print_with_symbols(thisAgent, "\nMatch goal for assertion: %y", msc->p_node->b.p.prod->name);
1009 #endif
1010 
1011 
1012  lowest_goal_wme = NIL;
1013  lowest_level_so_far = -1;
1014 
1015  if (msc->w) {
1016  if (msc->w->id->id.isa_goal == TRUE) {
1017  lowest_goal_wme = msc->w;
1018  lowest_level_so_far = msc->w->id->id.level;
1019  }
1020  }
1021 
1022  for (tok=msc->tok; tok!=thisAgent->dummy_top_token; tok=tok->parent) {
1023  if (tok->w != NIL) {
1024  /* print_wme(tok->w); */
1025  if (tok->w->id->id.isa_goal == TRUE) {
1026 
1027  if (lowest_goal_wme == NIL)
1028  lowest_goal_wme = tok->w;
1029 
1030  else {
1031  if (tok->w->id->id.level > lowest_goal_wme->id->id.level)
1032  lowest_goal_wme = tok->w;
1033  }
1034  }
1035 
1036  }
1037  }
1038 
1039  if (lowest_goal_wme) {
1040 #ifdef DEBUG_WATERFALL
1041  print_with_symbols(thisAgent, " is [%y]", lowest_goal_wme->id);
1042 #endif
1043  return lowest_goal_wme->id;
1044  }
1045  { char msg[BUFFER_MSG_SIZE];
1046  print_with_symbols(thisAgent, "\nError: Did not find goal for ms_change assertion: %y\n", msc->p_node->b.p.prod->name);
1047  SNPRINTF(msg, BUFFER_MSG_SIZE,"\nError: Did not find goal for ms_change assertion: %s\n",
1048  symbol_to_string(thisAgent, msc->p_node->b.p.prod->name,TRUE,NIL, 0));
1049  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
1050  abort_with_fatal_error(thisAgent, msg);
1051  }
1052  return 0;
1053 }
1054 
1056 
1057 #ifdef DEBUG_WATERFALL
1058  print_with_symbols(thisAgent, "\nMatch goal level for retraction: %y", msc->inst->prod->name);
1059 #endif
1060 
1061  if (msc->inst->match_goal) {
1062  /* If there is a goal, just return the goal */
1063 #ifdef DEBUG_WATERFALL
1064  print_with_symbols(thisAgent, " is [%y]", msc->inst->match_goal);
1065 #endif
1066  return msc->inst->match_goal;
1067 
1068  } else {
1069 
1070 #ifdef DEBUG_WATERFALL
1071  print(" is NIL (nil goal retraction)");
1072 #endif
1073  return NIL;
1074 
1075  }
1076 }
1077 
1078 void print_assertion( agent* thisAgent, ms_change *msc) {
1079 
1080  if (msc->p_node)
1081  print_with_symbols(thisAgent, "\nAssertion: %y", msc->p_node->b.p.prod->name);
1082  else
1083  print(thisAgent, "\nAssertion exists but has no p_node");
1084 }
1085 
1086 void print_retraction( agent* thisAgent, ms_change *msc) {
1087 
1088  if (msc->p_node)
1089  print_with_symbols(thisAgent, "\nRetraction: %y", msc->p_node->b.p.prod->name);
1090  else
1091  print(thisAgent, "\nRetraction exists but has no p_node");
1092 }
1093 
1094 /* REW: end 08.20.97 */
1095 
1096 
1098 
1099  Symbol *goal;
1100 
1101  /* REW: begin 08.20.97 */
1102  /* Determining if assertions or retractions are ready require looping over
1103  all goals in Waterfall/Operand2 */
1104 
1105  if (thisAgent->nil_goal_retractions) return TRUE;
1106 
1107  /* Loop from bottom to top because we expect activity at
1108  the bottom usually */
1109 
1110  for (goal=thisAgent->bottom_goal;goal;goal=goal->id.higher_goal) {
1111  /* if there are any assertions or retrctions for this goal,
1112  return TRUE */
1113  if (goal->id.ms_o_assertions || goal->id.ms_i_assertions ||
1114  goal->id.ms_retractions)
1115  return TRUE;
1116  }
1117 
1118  /* if there are no nil_goal_retractions and no assertions or retractions
1119  for any goal then return FALSE -- there aren't any productions
1120  ready to fire or retract */
1121 
1122  return FALSE;
1123  /* REW: end 08.20.97 */
1124 }
1125 
1126 
1127 /* RCHONG: begin 10.11 */
1128 
1130  return (thisAgent->ms_i_assertions || thisAgent->ms_retractions);
1131 }
1132 
1133 /* RCHONG: end 10.11 */
1134 
1135 /* New waterfall model:
1136  *
1137  * postpone_assertion: formerly get_next_assertion. Removes the first
1138  * assertion from the assertion lists and adds it to the postponed
1139  * assertions list. Returns false if there are no assertions.
1140  *
1141  * consume_last_postponed_assertion: removes the first assertion from the
1142  * postponed assertions list, making it go away permenantly.
1143  *
1144  * restore_postponed_assertions: replaces the postponed assertions back on
1145  * the assertion lists.
1146  */
1147 Bool postpone_assertion (agent* thisAgent, production **prod, struct token_struct **tok, wme **w) {
1148  ms_change *msc = NIL;
1149 
1150  /* REW: begin 09.15.96 */
1151  /* REW: begin 08.20.97 */
1152 
1153  /* In Waterfall, we return only assertions that match in the
1154  currently active goal */
1155 
1156  if (thisAgent->active_goal) { /* Just do asserts for current goal */
1157  if (thisAgent->FIRING_TYPE == PE_PRODS) {
1158  if (! thisAgent->active_goal->id.ms_o_assertions) return FALSE;
1159 
1160  msc = thisAgent->active_goal->id.ms_o_assertions;
1161  remove_from_dll (thisAgent->ms_o_assertions, msc, next, prev);
1163  msc, next_in_level, prev_in_level);
1164 
1165  } else {
1166  /* IE PRODS */
1167  if (! thisAgent->active_goal->id.ms_i_assertions) return FALSE;
1168 
1169  msc = thisAgent->active_goal->id.ms_i_assertions;
1170  remove_from_dll (thisAgent->ms_i_assertions, msc, next, prev);
1172  msc, next_in_level, prev_in_level);
1173  }
1174 
1175  } else {
1176 
1177  /* If there is not an active goal, then there should not be any
1178  assertions. If there are, then we generate and error message
1179  and abort. */
1180 
1181  if ((thisAgent->ms_i_assertions) ||
1182  (thisAgent->ms_o_assertions)) {
1183 
1184  // Commented out 11/2007
1185  // laird: I would like us to remove that error message that happens
1186  // in Obscurebot. It just freaks people out and we have yet to see an error in Soar because of it.
1187 
1188  //char msg[BUFFER_MSG_SIZE];
1189  //strncpy(msg,"\nrete.c: Error: No active goal, but assertions are on the assertion list.", BUFFER_MSG_SIZE);
1190  //msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
1191  //abort_with_fatal_error(thisAgent, msg);
1192 
1193 }
1194 
1195  return FALSE; /* if we are in an initiazation and there are no
1196  assertions, just retrurn FALSE to terminate
1197  the procedure. */
1198 
1199 }
1200  /* REW: end 08.20.97 */
1201  /* REW: end 09.15.96 */
1202 
1204  next_of_node, prev_of_node);
1205  *prod = msc->p_node->b.p.prod;
1206  *tok = msc->tok;
1207  *w = msc->w;
1208 
1209  // save the assertion on the postponed list
1210  insert_at_head_of_dll (thisAgent->postponed_assertions, msc, next, prev);
1211 
1212  return TRUE;
1213 }
1214 
1216  assert (thisAgent->postponed_assertions);
1217 
1218  ms_change *msc = thisAgent->postponed_assertions;
1219 
1220  // get the most recently postponed assertion
1221  remove_from_dll (thisAgent->postponed_assertions, msc, next, prev);
1222 
1223  // kill it
1224  free_with_pool (&thisAgent->ms_change_pool, msc);
1225 }
1226 
1228 
1229  while(thisAgent->postponed_assertions) {
1230  ms_change *msc = thisAgent->postponed_assertions;
1231 
1232  // get the most recently postponed assertion
1233  remove_from_dll (thisAgent->postponed_assertions, msc, next, prev);
1234 
1235  assert (msc != NIL);
1236 
1237  // do the reverse of postpone_assertion
1239  msc, next_of_node, prev_of_node);
1240 
1241  assert (thisAgent->active_goal);
1242 
1243  if (thisAgent->FIRING_TYPE == PE_PRODS) {
1245  msc, next_in_level, prev_in_level);
1246  insert_at_head_of_dll (thisAgent->ms_o_assertions, msc, next, prev);
1247  } else {
1248  // IE
1250  msc, next_in_level, prev_in_level);
1251  insert_at_head_of_dll (thisAgent->ms_i_assertions, msc, next, prev);
1252  }
1253  }
1254 }
1255 
1257  ms_change *msc;
1258 
1259  /* just do the retractions for the current level */
1260 
1261  /* initialization condition (2.107/2.111) */
1262  if (thisAgent->active_level == 0) return FALSE;
1263 
1264  if (! thisAgent->active_goal->id.ms_retractions) return FALSE;
1265 
1266  msc = thisAgent->active_goal->id.ms_retractions;
1267 
1268  /* remove from the complete retraction list */
1269  remove_from_dll (thisAgent->ms_retractions, msc, next, prev);
1270  /* and remove from the Waterfall-specific list */
1272  msc, next_in_level, prev_in_level);
1273  if (msc->p_node)
1275  next_of_node, prev_of_node);
1276  *inst = msc->inst;
1277  free_with_pool (&thisAgent->ms_change_pool, msc);
1278  return TRUE;
1279 }
1280 
1281 
1282 
1283 
1284 
1285 /* REW: begin 08.20.97 */
1286 
1287 /* Retract an instantiation on the nil goal list. If there are no
1288  retractions on the nil goal retraction list, return FALSE. This
1289  procedure is only called in Operand2 mode, so there is no need for
1290  any checks for Operand2-specific processing. */
1291 
1293  ms_change *msc;
1294 
1295  if (! thisAgent->nil_goal_retractions) return FALSE;
1296  msc = thisAgent->nil_goal_retractions;
1297 
1298  /* Remove this retraction from the NIL goal list */
1299  remove_from_dll (thisAgent->nil_goal_retractions, msc,
1300  next_in_level, prev_in_level);
1301 
1302  /* next and prev set and used in Operand2 exactly as used in Soar 7 --
1303  so we have to make sure and delete this retraction from the regular
1304  list */
1305  remove_from_dll (thisAgent->ms_retractions, msc, next, prev);
1306 
1307  if (msc->p_node) {
1309  next_of_node, prev_of_node);
1310  }
1311  *inst = msc->inst;
1312  free_with_pool (&thisAgent->ms_change_pool, msc);
1313  return TRUE;
1314 
1315 }
1316 
1317 /* REW: end 08.20.97 */
1318 
1319 
1320 
1321 
1322 
1323 
1324 
1325 
1326 
1327 
1328 
1329 /* **********************************************************************
1330 
1331  SECTION 3: Alpha Portion of the Rete Net
1332 
1333  The alpha (top) part of the rete net consists of the alpha memories.
1334  Each of these memories is stored in one of 16 hash tables, depending
1335  on which fields it tests:
1336 
1337  bit 0 (value 1) indicates it tests the id slot
1338  bit 1 (value 2) indicates it tests the attr slot
1339  bit 2 (value 4) indicates it tests the value slot
1340  bit 3 (value 8) indicates it tests for an acceptable preference
1341 
1342  The hash tables are dynamically resized hash tables.
1343 
1344  Find_or_make_alpha_mem() either shares an existing alpha memory or
1345  creates a new one, adjusting reference counts accordingly.
1346  Remove_ref_to_alpha_mem() decrements the reference count and
1347  deallocates the alpha memory if it's no longer used.
1348 
1349  EXTERNAL INTERFACE:
1350  Add_wme_to_rete() and remove_wme_from_rete() do just what they say.
1351 ********************************************************************** */
1352 
1353 /* --- Returns TRUE iff the given wme goes into the given alpha memory --- */
1354 /*#define wme_matches_alpha_mem(w,am) ( \
1355  (((am)->id==NIL) || ((am)->id==(w)->id)) && \
1356  (((am)->attr==NIL) || ((am)->attr==(w)->attr)) && \
1357  (((am)->value==NIL) || ((am)->value==(w)->value)) && \
1358  ((am)->acceptable==(w)->acceptable))*/
1360 {
1361  return ((am->id==NIL) || (am->id==w->id)) &&
1362  ((am->attr==NIL) || (am->attr==w->attr)) &&
1363  ((am->value==NIL) || (am->value==w->value)) &&
1364  (am->acceptable==w->acceptable);
1365 }
1366 
1367 /* --- Returns hash value for the given id/attr/value symbols --- */
1368 /*#define alpha_hash_value(i,a,v,num_bits) \
1369  ( ( ((i) ? ((Symbol *)(i))->common.hash_id : 0) ^ \
1370  ((a) ? ((Symbol *)(a))->common.hash_id : 0) ^ \
1371  ((v) ? ((Symbol *)(v))->common.hash_id : 0) ) & \
1372  masks_for_n_low_order_bits[(num_bits)] )*/
1373 inline uint32_t alpha_hash_value(Symbol * i, Symbol * a, Symbol * v, short num_bits)
1374 {
1375  return
1376  ( ( (i ? i->common.hash_id : 0) ^
1377  (a ? a->common.hash_id : 0) ^
1378  (v ? v->common.hash_id : 0) ) &
1379  masks_for_n_low_order_bits[(num_bits)] );
1380 }
1381 
1382 /* --- rehash funciton for resizable hash table routines --- */
1383 uint32_t hash_alpha_mem (void *item, short num_bits) {
1384  alpha_mem *am;
1385 
1386  am = static_cast<alpha_mem_struct *>(item);
1387  return alpha_hash_value (am->id, am->attr, am->value, num_bits);
1388 }
1389 
1390 /* --- Which of the 16 hash tables to use? --- */
1391 /*#define table_for_tests(id,attr,value,acceptable) \
1392  thisAgent->alpha_hash_tables [ ((id) ? 1 : 0) + ((attr) ? 2 : 0) + \
1393  ((value) ? 4 : 0) + \
1394  ((acceptable) ? 8 : 0) ]*/
1395 inline hash_table * table_for_tests(agent* thisAgent,
1396  Symbol * id, Symbol * attr, Symbol * value,
1397  Bool acceptable)
1398 {
1399  return thisAgent->alpha_hash_tables [ (id ? 1 : 0) + (attr ? 2 : 0) +
1400  (value ? 4 : 0) +
1401  (acceptable ? 8 : 0) ];
1402 }
1403 
1404 //#define get_next_alpha_mem_id() (thisAgent->alpha_mem_id_counter++)
1406 {
1407  return thisAgent->alpha_mem_id_counter++;
1408 }
1409 
1410 /* --- Adds a WME to an alpha memory (create a right_mem for it), but doesn't
1411  inform any successors --- */
1412 void add_wme_to_alpha_mem (agent* thisAgent, wme *w, alpha_mem *am) {
1413  right_mem **header, *rm;
1414  uint32_t hv;
1415 
1416  /* --- allocate new right_mem, fill it fields --- */
1417  allocate_with_pool (thisAgent, &thisAgent->right_mem_pool, &rm);
1418  rm->w = w;
1419  rm->am = am;
1420 
1421  /* --- add it to dll's for the hash bucket, alpha mem, and wme --- */
1422  hv = am->am_id ^ w->id->common.hash_id;
1423  header = reinterpret_cast<right_mem **>(thisAgent->right_ht) + (hv & RIGHT_HT_MASK);
1424  insert_at_head_of_dll (*header, rm, next_in_bucket, prev_in_bucket);
1425  insert_at_head_of_dll (am->right_mems, rm, next_in_am, prev_in_am);
1426  insert_at_head_of_dll (w->right_mems, rm, next_from_wme, prev_from_wme);
1427 }
1428 
1429 /* --- Removes a WME (right_mem) from its alpha memory, but doesn't inform
1430  any successors --- */
1432  wme *w;
1433  alpha_mem *am;
1434  uint32_t hv;
1435  right_mem **header;
1436 
1437  w = rm->w;
1438  am = rm->am;
1439 
1440  /* --- remove it from dll's for the hash bucket, alpha mem, and wme --- */
1441  hv = am->am_id ^ w->id->common.hash_id;
1442  header = reinterpret_cast<right_mem **>(thisAgent->right_ht) + (hv & RIGHT_HT_MASK);
1443  remove_from_dll (*header, rm, next_in_bucket, prev_in_bucket);
1444  remove_from_dll (am->right_mems, rm, next_in_am, prev_in_am);
1445  remove_from_dll (w->right_mems, rm, next_from_wme, prev_from_wme);
1446 
1447  /* --- deallocate it --- */
1448  free_with_pool (&thisAgent->right_mem_pool, rm);
1449 }
1450 
1451 /* --- Looks for an existing alpha mem, returns it or NIL if not found --- */
1452 alpha_mem *find_alpha_mem (agent* thisAgent, Symbol *id, Symbol *attr,
1453  Symbol *value, Bool acceptable) {
1454  hash_table *ht;
1455  alpha_mem *am;
1456  uint32_t hash_value;
1457 
1458  ht = table_for_tests (thisAgent, id, attr, value, acceptable);
1459  hash_value = alpha_hash_value (id, attr, value, ht->log2size);
1460 
1461  for (am = reinterpret_cast<alpha_mem *>(*(ht->buckets+hash_value)); am!=NIL;
1462  am=am->next_in_hash_table)
1463  if ((am->id==id) && (am->attr==attr) &&
1464  (am->value==value) && (am->acceptable==acceptable))
1465  return am;
1466  return NIL;
1467 }
1468 
1469 /* --- Find and share existing alpha memory, or create new one. Adjusts
1470  the reference count on the alpha memory accordingly. --- */
1472  Symbol *value, Bool acceptable) {
1473  hash_table *ht;
1474  alpha_mem *am, *more_general_am;
1475  wme *w;
1476  right_mem *rm;
1477 
1478  /* --- look for an existing alpha mem --- */
1479  am = find_alpha_mem (thisAgent, id, attr, value, acceptable);
1480  if (am) {
1481  am->reference_count++;
1482  return am;
1483  }
1484 
1485  /* --- no existing alpha_mem found, so create a new one --- */
1486  allocate_with_pool (thisAgent, &thisAgent->alpha_mem_pool, &am);
1487  am->next_in_hash_table = NIL;
1488  am->right_mems = NIL;
1489  am->beta_nodes = NIL;
1490  am->last_beta_node = NIL;
1491  am->reference_count = 1;
1492  am->id = id;
1493  if (id) symbol_add_ref (id);
1494  am->attr = attr;
1495  if (attr) symbol_add_ref (attr);
1496  am->value = value;
1497  if (value) symbol_add_ref (value);
1498  am->acceptable = acceptable;
1499  am->am_id = get_next_alpha_mem_id(thisAgent);
1500  ht = table_for_tests (thisAgent, id, attr, value, acceptable);
1501  add_to_hash_table (thisAgent, ht, am);
1502 
1503  /* --- fill new mem with any existing matching WME's --- */
1504  more_general_am = NIL;
1505  if (id)
1506  more_general_am = find_alpha_mem (thisAgent, NIL, attr, value, acceptable);
1507  if (!more_general_am && value)
1508  more_general_am = find_alpha_mem (thisAgent, NIL, attr, NIL, acceptable);
1509  if (more_general_am) {
1510  /* --- fill new mem using the existing more general one --- */
1511  for (rm=more_general_am->right_mems; rm!=NIL; rm=rm->next_in_am)
1512  if (wme_matches_alpha_mem (rm->w, am))
1513  add_wme_to_alpha_mem (thisAgent, rm->w, am);
1514  } else {
1515  /* --- couldn't find such an existing mem, so do it the hard way --- */
1516  for (w=thisAgent->all_wmes_in_rete; w!=NIL; w=w->rete_next)
1517  if (wme_matches_alpha_mem (w,am)) add_wme_to_alpha_mem (thisAgent, w, am);
1518  }
1519 
1520  return am;
1521 }
1522 
1523 /* --- Using the given hash table and hash value, try to find a
1524  matching alpha memory in the indicated hash bucket. If we find one,
1525  we add the wme to it and inform successor nodes. --- */
1526 void add_wme_to_aht (agent* thisAgent, hash_table *ht, uint32_t hash_value, wme *w) {
1527  alpha_mem *am;
1528  rete_node *node, *next;
1529 
1530  hash_value = hash_value & masks_for_n_low_order_bits[ht->log2size];
1531  am = reinterpret_cast<alpha_mem *>(*(ht->buckets+hash_value));
1532  while (am!=NIL) {
1533  if (wme_matches_alpha_mem (w,am)) {
1534  /* --- found the right alpha memory, first add the wme --- */
1535  add_wme_to_alpha_mem (thisAgent, w, am);
1536 
1537  /* --- now call the beta nodes --- */
1538  for (node=am->beta_nodes; node!=NIL; node=next) {
1539  next = node->b.posneg.next_from_alpha_mem;
1540  (*(right_addition_routines[node->node_type]))(thisAgent,node,w);
1541  }
1542  return; /* only one possible alpha memory per table could match */
1543  }
1544  am = am->next_in_hash_table;
1545  }
1546 }
1547 
1548 /* We cannot use 'xor' as the name of a function because it is defined in UNIX. */
1549 //#define xor_op(i,a,v) ((i) ^ (a) ^ (v))
1551 {
1552  return ((i) ^ (a) ^ (v));
1553 }
1554 
1555 /* --- Adds a WME to the Rete. --- */
1556 void add_wme_to_rete (agent* thisAgent, wme *w) {
1557  uint32_t hi, ha, hv;
1558 
1559  /* --- add w to all_wmes_in_rete --- */
1560  insert_at_head_of_dll (thisAgent->all_wmes_in_rete, w, rete_next, rete_prev);
1561  thisAgent->num_wmes_in_rete++;
1562 
1563  /* --- it's not in any right memories or tokens yet --- */
1564  w->right_mems = NIL;
1565  w->tokens = NIL;
1566 
1567  /* --- add w to the appropriate alpha_mem in each of 8 possible tables --- */
1568  hi = w->id->common.hash_id;
1569  ha = w->attr->common.hash_id;
1570  hv = w->value->common.hash_id;
1571 
1572  if (w->acceptable) {
1573  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[8], xor_op( 0, 0, 0), w);
1574  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[9], xor_op(hi, 0, 0), w);
1575  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[10], xor_op( 0,ha, 0), w);
1576  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[11], xor_op(hi,ha, 0), w);
1577  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[12], xor_op( 0, 0,hv), w);
1578  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[13], xor_op(hi, 0,hv), w);
1579  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[14], xor_op( 0,ha,hv), w);
1580  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[15], xor_op(hi,ha,hv), w);
1581  } else {
1582  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[0], xor_op( 0, 0, 0), w);
1583  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[1], xor_op(hi, 0, 0), w);
1584  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[2], xor_op( 0,ha, 0), w);
1585  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[3], xor_op(hi,ha, 0), w);
1586  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[4], xor_op( 0, 0,hv), w);
1587  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[5], xor_op(hi, 0,hv), w);
1588  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[6], xor_op( 0,ha,hv), w);
1589  add_wme_to_aht (thisAgent, thisAgent->alpha_hash_tables[7], xor_op(hi,ha,hv), w);
1590  }
1591 
1592  w->epmem_id = EPMEM_NODEID_BAD;
1593  w->epmem_valid = NIL;
1594  {
1595  if ( thisAgent->epmem_db->get_status() == soar_module::connected )
1596  {
1597  // if identifier-valued and short-term, known value
1598  if ( ( w->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
1599  ( w->value->id.epmem_id != EPMEM_NODEID_BAD ) &&
1600  ( w->value->id.epmem_valid == thisAgent->epmem_validation ) &&
1601  ( !w->value->id.smem_lti ) )
1602  {
1603  // add id ref count
1604  (*thisAgent->epmem_id_ref_counts)[ w->value->id.epmem_id ]->insert( w );
1605  }
1606 
1607  // if known id
1608  if ( ( w->id->id.epmem_id != EPMEM_NODEID_BAD ) && ( w->id->id.epmem_valid == thisAgent->epmem_validation ) )
1609  {
1610  // add to add set
1611  thisAgent->epmem_wme_adds->insert( w->id );
1612  }
1613  }
1614  }
1615 
1616  if ( ( w->id->id.smem_lti ) && ( !thisAgent->smem_ignore_changes ) && smem_enabled( thisAgent ) && ( thisAgent->smem_params->mirroring->get_value() == soar_module::on ) )
1617  {
1618  std::pair< smem_pooled_symbol_set::iterator, bool > insert_result = thisAgent->smem_changed_ids->insert( w->id );
1619  if ( insert_result.second )
1620  {
1621  symbol_add_ref( w->id );
1622  }
1623  }
1624 }
1625 
1626 inline void _epmem_remove_wme( agent* my_agent, wme* w )
1627 {
1628  bool was_encoded = false;
1629 
1630  if ( w->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
1631  {
1632  bool lti = ( w->value->id.smem_lti != NIL );
1633 
1634  if ( ( w->epmem_id != EPMEM_NODEID_BAD ) && ( w->epmem_valid == my_agent->epmem_validation ) )
1635  {
1636  was_encoded = true;
1637 
1638  (*my_agent->epmem_edge_removals)[ w->epmem_id ] = true;
1639 
1640  // return to the id pool
1641  if ( !lti )
1642  {
1643  epmem_return_id_pool::iterator p = my_agent->epmem_id_replacement->find( w->epmem_id );
1644  (*p->second).push_front( std::make_pair( w->value->id.epmem_id, w->epmem_id ) );
1645  my_agent->epmem_id_replacement->erase( p );
1646  }
1647  }
1648 
1649  // reduce the ref count on the value
1650  if ( !lti && ( w->value->id.epmem_id != EPMEM_NODEID_BAD ) && ( w->value->id.epmem_valid == my_agent->epmem_validation ) )
1651  {
1652  epmem_wme_set* my_refs = (*my_agent->epmem_id_ref_counts)[ w->value->id.epmem_id ];
1653 
1654  epmem_wme_set::iterator rc_it = my_refs->find( w );
1655  if ( rc_it != my_refs->end() )
1656  {
1657  my_refs->erase( rc_it );
1658 
1659  // recurse if no incoming edges from top-state (i.e. not in transitive closure of top-state)
1660  bool recurse = true;
1661  for ( rc_it=my_refs->begin(); ( recurse && rc_it!=my_refs->end() ); rc_it++ )
1662  {
1663  if ( ( !(*rc_it) ) || ( (*rc_it)->id->id.level == my_agent->top_state->id.level ) )
1664  {
1665  recurse = false;
1666  }
1667  }
1668 
1669  if ( recurse )
1670  {
1671  my_refs->clear();
1672  my_agent->epmem_id_removes->push_front( w->value );
1673  }
1674  }
1675  }
1676  }
1677  else if ( ( w->epmem_id != EPMEM_NODEID_BAD ) && ( w->epmem_valid == my_agent->epmem_validation ) )
1678  {
1679  was_encoded = true;
1680 
1681  (*my_agent->epmem_node_removals)[ w->epmem_id ] = true;
1682  }
1683 
1684  if ( was_encoded )
1685  {
1687  w->epmem_valid = NIL;
1688  }
1689 }
1690 
1691 inline void _epmem_process_ids( agent* my_agent )
1692 {
1693  Symbol* id;
1694  slot* s;
1695  wme* w;
1696 
1697  while ( !my_agent->epmem_id_removes->empty() )
1698  {
1699  id = my_agent->epmem_id_removes->front();
1700  my_agent->epmem_id_removes->pop_front();
1701 
1702  assert( id->common.symbol_type == IDENTIFIER_SYMBOL_TYPE );
1703 
1704  if ( ( id->id.epmem_id != EPMEM_NODEID_BAD ) && ( id->id.epmem_valid == my_agent->epmem_validation ) )
1705  {
1706  // invalidate identifier encoding
1707  id->id.epmem_id = EPMEM_NODEID_BAD;
1708  id->id.epmem_valid = NIL;
1709 
1710  // impasse wmes
1711  for ( w=id->id.impasse_wmes; w!=NIL; w=w->next )
1712  {
1713  _epmem_remove_wme( my_agent, w );
1714  }
1715 
1716  // input wmes
1717  for ( w=id->id.input_wmes; w!=NIL; w=w->next )
1718  {
1719  _epmem_remove_wme( my_agent, w );
1720  }
1721 
1722  // regular wmes
1723  for ( s=id->id.slots; s!=NIL; s=s->next )
1724  {
1725  for ( w=s->wmes; w!=NIL; w=w->next )
1726  {
1727  _epmem_remove_wme( my_agent, w );
1728  }
1729 
1730  for ( w=s->acceptable_preference_wmes; w!=NIL; w=w->next )
1731  {
1732  _epmem_remove_wme( my_agent, w );
1733  }
1734  }
1735  }
1736  }
1737 }
1738 
1739 /* --- Removes a WME from the Rete. --- */
1740 void remove_wme_from_rete (agent* thisAgent, wme *w) {
1741  right_mem *rm;
1742  alpha_mem *am;
1743  rete_node *node, *next, *child;
1744  token *tok, *left;
1745 
1746  {
1747  if ( thisAgent->epmem_db->get_status() == soar_module::connected )
1748  {
1749  _epmem_remove_wme( thisAgent, w );
1750  _epmem_process_ids( thisAgent );
1751  }
1752  }
1753 
1754  if ( ( w->id->id.smem_lti ) && ( !thisAgent->smem_ignore_changes ) && smem_enabled( thisAgent ) && ( thisAgent->smem_params->mirroring->get_value() == soar_module::on ) )
1755  {
1756  std::pair< smem_pooled_symbol_set::iterator, bool > insert_result = thisAgent->smem_changed_ids->insert( w->id );
1757  if ( insert_result.second )
1758  {
1759  symbol_add_ref( w->id );
1760  }
1761  }
1762 
1763  /* --- remove w from all_wmes_in_rete --- */
1764  remove_from_dll (thisAgent->all_wmes_in_rete, w, rete_next, rete_prev);
1765  thisAgent->num_wmes_in_rete--;
1766 
1767  /* --- remove w from each alpha_mem it's in --- */
1768  while (w->right_mems) {
1769  rm = w->right_mems;
1770  am = rm->am;
1771  /* --- found the alpha memory, first remove the wme from it --- */
1772  remove_wme_from_alpha_mem (thisAgent, rm);
1773 
1774 #ifdef DO_ACTIVATION_STATS_ON_REMOVALS
1775  /* --- if doing statistics stuff, then activate each attached node --- */
1776  for (node=am->beta_nodes; node!=NIL; node=next) {
1777  next = node->b.posneg.next_from_alpha_mem;
1779  }
1780 #endif
1781 
1782  /* --- for left unlinking, then if the alpha memory just went to
1783  zero, left unlink any attached Pos or MP nodes --- */
1784  if (am->right_mems==NIL) {
1785  for (node=am->beta_nodes; node!=NIL; node=next) {
1786  next = node->b.posneg.next_from_alpha_mem;
1787  switch (node->node_type) {
1788  case POSITIVE_BNODE:
1790  unlink_from_left_mem (node);
1791  break;
1792  case MP_BNODE:
1793  case UNHASHED_MP_BNODE:
1795  break;
1796  } /* end of switch (node->node_type) */
1797  }
1798  }
1799  }
1800 
1801  /* --- tree-based removal of all tokens that involve w --- */
1802  while (w->tokens) {
1803  tok = w->tokens;
1804  node = tok->node;
1805  if (! tok->parent) {
1806  /* Note: parent pointer is NIL only on negative node negrm tokens */
1807  left = tok->a.neg.left_token;
1808  remove_from_dll (w->tokens, tok, next_from_wme, prev_from_wme);
1809  remove_from_dll (left->negrm_tokens, tok,
1810  a.neg.next_negrm, a.neg.prev_negrm);
1811  free_with_pool (&thisAgent->token_pool, tok);
1812  if (! left->negrm_tokens) { /* just went to 0, so call children */
1813  for (child=node->first_child; child!=NIL; child=child->next_sibling)
1814  (*(left_addition_routines[child->node_type]))(thisAgent,child,left,NIL);
1815  }
1816  } else {
1817  remove_token_and_subtree (thisAgent, w->tokens);
1818  }
1819  }
1820 }
1821 
1822 /* --- Decrements reference count, deallocates alpha memory if unused. --- */
1823 void remove_ref_to_alpha_mem (agent* thisAgent, alpha_mem *am) {
1824  hash_table *ht;
1825 
1826  am->reference_count--;
1827  if (am->reference_count!=0) return;
1828  /* --- remove from hash table, and deallocate the alpha_mem --- */
1829  ht = table_for_tests (thisAgent, am->id, am->attr, am->value, am->acceptable);
1830  remove_from_hash_table (thisAgent, ht, am);
1831  if (am->id) symbol_remove_ref (thisAgent, am->id);
1832  if (am->attr) symbol_remove_ref (thisAgent, am->attr);
1833  if (am->value) symbol_remove_ref (thisAgent, am->value);
1834  while (am->right_mems) remove_wme_from_alpha_mem (thisAgent, am->right_mems);
1835  free_with_pool (&thisAgent->alpha_mem_pool, am);
1836 }
1837 
1838 
1839 
1840 
1841 
1842 
1843 
1844 
1845 
1846 
1847 
1848 
1849 
1850 /* **********************************************************************
1851 
1852  SECTION 4: Beta Net Initialization and Primitive Construction Routines
1853 
1854  The following routines are the basic Rete net building routines.
1855  Init_dummy_top_node() creates the dummy top node (for the current
1856  agent). Make_new_mem_node(), make_new_positive_node(),
1857  make_new_mp_node(), make_new_negative_node(), make_new_cn_node(), and
1858  make_new_production_node() are the basic node creators. Split_mp_node()
1859  and merge_into_mp_node() do the dynamic merging/splitting of memory
1860  and positive nodes.
1861 ********************************************************************** */
1862 
1863 //#define get_next_beta_node_id() (thisAgent->beta_node_id_counter++)
1865 {
1866  return (thisAgent->beta_node_id_counter++);
1867 }
1868 
1869 /* ------------------------------------------------------------------------
1870  Init Dummy Top Node
1871 
1872  The dummy top node always has one token in it (WME=NIL). This is
1873  just there so that (real) root nodes in the beta net can be handled
1874  the same as non-root nodes.
1875 ------------------------------------------------------------------------ */
1876 
1877 void init_dummy_top_node (agent* thisAgent) {
1878  /* --- create the dummy top node --- */
1879  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool,
1880  &thisAgent->dummy_top_node);
1882  thisAgent->dummy_top_node->parent = NIL;
1883  thisAgent->dummy_top_node->first_child = NIL;
1884  thisAgent->dummy_top_node->next_sibling = NIL;
1885 
1886  /* --- create the dummy top token --- */
1887  allocate_with_pool (thisAgent, &thisAgent->token_pool,
1888  &thisAgent->dummy_top_token);
1889  thisAgent->dummy_top_token->parent = NIL;
1890  thisAgent->dummy_top_token->node = thisAgent->dummy_top_node;
1891  thisAgent->dummy_top_token->w = NIL;
1892  thisAgent->dummy_top_token->first_child = NIL;
1893  thisAgent->dummy_top_token->next_sibling = NIL;
1894  thisAgent->dummy_top_token->prev_sibling = NIL;
1895  thisAgent->dummy_top_token->next_from_wme = NIL;
1896  thisAgent->dummy_top_token->prev_from_wme = NIL;
1897  thisAgent->dummy_top_token->next_of_node = NIL;
1898  thisAgent->dummy_top_token->prev_of_node = NIL;
1899  thisAgent->dummy_top_node->a.np.tokens = thisAgent->dummy_top_token;
1900 }
1901 
1902 /* ------------------------------------------------------------------------
1903  Remove Node From Parents List of Children
1904 
1905  Splices a given node out of its parent's list of children. This would
1906  be a lot easier if the children lists were doubly-linked, but that
1907  would take up a lot of extra space.
1908 ------------------------------------------------------------------------ */
1909 
1911  rete_node *prev_sibling;
1912 
1913  prev_sibling = node->parent->first_child;
1914  if (prev_sibling == node) {
1915  node->parent->first_child = node->next_sibling;
1916  return;
1917  }
1918  while (prev_sibling->next_sibling != node)
1919  prev_sibling = prev_sibling->next_sibling;
1920  prev_sibling->next_sibling = node->next_sibling;
1921 }
1922 
1923 /* ------------------------------------------------------------------------
1924  Update Node With Matches From Above
1925 
1926  Calls a node's left-addition routine with each match (token) from
1927  the node's parent. DO NOT call this routine on (positive, unmerged)
1928  join nodes.
1929 ------------------------------------------------------------------------ */
1930 
1932 {
1933  rete_node *parent;
1934  rete_node *saved_parents_first_child, *saved_childs_next_sibling;
1935  right_mem *rm;
1936  token *tok;
1937 
1938  if (bnode_is_bottom_of_split_mp(child->node_type)) {
1939  char msg[BUFFER_MSG_SIZE];
1940  strncpy (msg, "\nrete.c: Internal error: update_node_with_matches_from_above called on split node", BUFFER_MSG_SIZE);
1941  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
1942  abort_with_fatal_error (thisAgent, msg);
1943  }
1944 
1945  parent = child->parent;
1946 
1947  /* --- if parent is dummy top node, tell child about dummy top token --- */
1948  if (parent->node_type==DUMMY_TOP_BNODE) {
1949  (*(left_addition_routines[child->node_type]))(thisAgent,child,thisAgent->dummy_top_token,NIL);
1950  return;
1951  }
1952 
1953  /* --- if parent is positive: first do surgery on parent's child list,
1954  to replace the list with "child"; then call parent's add_right
1955  routine with each wme in the parent's alpha mem; then do surgery
1956  to restore previous child list of parent. --- */
1957  if (bnode_is_positive(parent->node_type)) {
1958  /* --- If the node is right unlinked, then don't activate it. This is
1959  important because some interpreter routines rely on the node
1960  being right linked whenever it gets right activated. */
1961  if (node_is_right_unlinked (parent)) return;
1962  saved_parents_first_child = parent->first_child;
1963  saved_childs_next_sibling = child->next_sibling;
1964  parent->first_child = child;
1965  child->next_sibling = NIL;
1966  /* to avoid double-counting these right adds */
1967  rete_node* node_to_ignore_for_activation_stats = parent;
1968  for (rm=parent->b.posneg.alpha_mem_->right_mems; rm!=NIL; rm=rm->next_in_am)
1969  (*(right_addition_routines[parent->node_type]))(thisAgent,parent,rm->w);
1970  node_to_ignore_for_activation_stats = NIL;
1971  parent->first_child = saved_parents_first_child;
1972  child->next_sibling = saved_childs_next_sibling;
1973  return;
1974  }
1975 
1976  /* --- if parent is negative or cn: easy, just look at the list of tokens
1977  on the parent node. --- */
1978  for (tok=parent->a.np.tokens; tok!=NIL; tok=tok->next_of_node)
1979  if (! tok->negrm_tokens)
1980  (*(left_addition_routines[child->node_type])) (thisAgent,child,tok,NIL);
1981 }
1982 
1983 /* ------------------------------------------------------------------------
1984  Nearest Ancestor With Same AM
1985 
1986  Scans up the net and finds the first (i.e., nearest) ancestor node
1987  that uses a given alpha_mem. Returns that node, or NIL if none exists.
1988 ------------------------------------------------------------------------ */
1989 
1991  while (node->node_type!=DUMMY_TOP_BNODE) {
1992  if (node->node_type==CN_BNODE) node = node->b.cn.partner->parent;
1993  else node = real_parent_node(node);
1994  if (bnode_is_posneg(node->node_type) && (node->b.posneg.alpha_mem_==am))
1995  return node;
1996  }
1997  return NIL;
1998 }
1999 
2000 /* --------------------------------------------------------------------
2001  Make New Mem Node
2002 
2003  Make a new beta memory node, return a pointer to it.
2004 -------------------------------------------------------------------- */
2005 
2007  rete_node *parent, byte node_type,
2008  var_location left_hash_loc) {
2009  rete_node *node;
2010 
2011  /* --- create the node data structure, fill in fields --- */
2012  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &node);
2013  init_new_rete_node_with_type (thisAgent, node, node_type);
2014  node->parent = parent;
2015  node->next_sibling = parent->first_child;
2016  parent->first_child = node;
2017  node->first_child = NIL;
2018  node->b.mem.first_linked_child = NIL;
2019 
2020  /* These hash fields are not used for unhashed node types */
2021  node->left_hash_loc_field_num = left_hash_loc.field_num;
2022  node->left_hash_loc_levels_up = left_hash_loc.levels_up;
2023 
2024  node->node_id = get_next_beta_node_id(thisAgent);
2025  node->a.np.tokens = NIL;
2026 
2027  /* --- call new node's add_left routine with all the parent's tokens --- */
2028  update_node_with_matches_from_above (thisAgent, node);
2029 
2030  return node;
2031 }
2032 
2033 /* --------------------------------------------------------------------
2034  Make New Positive Node
2035 
2036  Make a new positive join node, return a pointer to it.
2037 -------------------------------------------------------------------- */
2038 
2040  rete_node *parent_mem, byte node_type,
2041  alpha_mem *am, rete_test *rt,
2042  Bool prefer_left_unlinking) {
2043  rete_node *node;
2044 
2045  /* --- create the node data structure, fill in fields --- */
2046  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &node);
2047  init_new_rete_node_with_type (thisAgent, node, node_type);
2048  node->parent = parent_mem;
2049  node->next_sibling = parent_mem->first_child;
2050  parent_mem->first_child = node;
2051  node->first_child = NIL;
2052  relink_to_left_mem (node);
2053  node->b.posneg.other_tests = rt;
2054  node->b.posneg.alpha_mem_ = am;
2056  nearest_ancestor_with_same_am (node, am);
2057  relink_to_right_mem (node);
2058 
2059  /* --- don't need to force WM through new node yet, as it's just a
2060  join node with no children --- */
2061 
2062  /* --- unlink the join node from one side if possible --- */
2063  if (! parent_mem->a.np.tokens) unlink_from_right_mem (node);
2064  if ((! am->right_mems) && ! node_is_right_unlinked (node))
2065  unlink_from_left_mem (node);
2066  if (prefer_left_unlinking && (! parent_mem->a.np.tokens) &&
2067  (! am->right_mems)) {
2068  relink_to_right_mem (node);
2069  unlink_from_left_mem (node);
2070  }
2071 
2072  return node;
2073 }
2074 
2075 /* --------------------------------------------------------------------
2076  Split MP Node
2077 
2078  Split a given MP node into separate M and P nodes, return a pointer
2079  to the new Memory node.
2080 -------------------------------------------------------------------- */
2081 
2082 rete_node *split_mp_node (agent* thisAgent, rete_node *mp_node) {
2083  rete_node mp_copy;
2084  rete_node *pos_node, *mem_node, *parent;
2085  byte mem_node_type, node_type;
2086  token *t;
2087 
2088  /* --- determine appropriate node types for new M and P nodes --- */
2089  if (mp_node->node_type==MP_BNODE) {
2090  node_type = POSITIVE_BNODE;
2091  mem_node_type = MEMORY_BNODE;
2092  } else {
2093  node_type = UNHASHED_POSITIVE_BNODE;
2094  mem_node_type = UNHASHED_MEMORY_BNODE;
2095  }
2096 
2097  /* --- save a copy of the MP data, then kill the MP node --- */
2098  mp_copy = *mp_node;
2099  parent = mp_node->parent;
2101  update_stats_for_destroying_node (thisAgent, mp_node); /* clean up rete stats stuff */
2102 
2103  /* --- the old MP node will get transmogrified into the new Pos node --- */
2104  pos_node = mp_node;
2105 
2106  /* --- create the new M node, transfer the MP node's tokens to it --- */
2107  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &mem_node);
2108  init_new_rete_node_with_type (thisAgent, mem_node, mem_node_type);
2109  set_sharing_factor (mem_node, mp_copy.sharing_factor);
2110 
2111  mem_node->parent = parent;
2112  mem_node->next_sibling = parent->first_child;
2113  parent->first_child = mem_node;
2114  mem_node->first_child = pos_node;
2115  mem_node->b.mem.first_linked_child = NIL;
2118  mem_node->node_id = mp_copy.node_id;
2119 
2120  mem_node->a.np.tokens = mp_node->a.np.tokens;
2121  for (t=mp_node->a.np.tokens; t!=NIL; t=t->next_of_node) t->node = mem_node;
2122 
2123  /* --- transmogrify the old MP node into the new Pos node --- */
2124  init_new_rete_node_with_type (thisAgent, pos_node, node_type);
2125  pos_node->parent = mem_node;
2126  pos_node->first_child = mp_copy.first_child;
2127  pos_node->next_sibling = NIL;
2128  pos_node->b.posneg = mp_copy.b.posneg;
2129  relink_to_left_mem (pos_node); /* for now, but might undo this below */
2130  set_sharing_factor (pos_node, mp_copy.sharing_factor);
2131 
2132  /* --- set join node's unlinking status according to mp_copy's --- */
2133  if (mp_bnode_is_left_unlinked(&mp_copy)) unlink_from_left_mem (pos_node);
2134 
2135  return mem_node;
2136 }
2137 
2138 /* --------------------------------------------------------------------
2139  Merge Into MP Node
2140 
2141  Merge a given Memory node and its one positive join child into an
2142  MP node, returning a pointer to the MP node.
2143 -------------------------------------------------------------------- */
2144 
2145 rete_node *merge_into_mp_node (agent* thisAgent, rete_node *mem_node) {
2146  rete_node *pos_node, *mp_node, *parent;
2147  rete_node pos_copy;
2148  byte node_type;
2149  token *t;
2150 
2151  pos_node = mem_node->first_child;
2152  parent = mem_node->parent;
2153 
2154  /* --- sanity check: Mem node must have exactly one child --- */
2155  if ((! pos_node) || pos_node->next_sibling) {
2156  char msg[BUFFER_MSG_SIZE];
2157  strncpy (msg, "\nrete.c: Internal error: tried to merge_into_mp_node, but <>1 child\n", BUFFER_MSG_SIZE);
2158  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
2159  abort_with_fatal_error(thisAgent, msg);
2160  }
2161 
2162  /* --- determine appropriate node type for new MP node --- */
2163  if (mem_node->node_type==MEMORY_BNODE) {
2164  node_type = MP_BNODE;
2165  } else {
2166  node_type = UNHASHED_MP_BNODE;
2167  }
2168 
2169  /* --- save a copy of the Pos data, then kill the Pos node --- */
2170  pos_copy = *pos_node;
2171  update_stats_for_destroying_node (thisAgent, pos_node); /* clean up rete stats stuff */
2172 
2173  /* --- the old Pos node gets transmogrified into the new MP node --- */
2174  mp_node = pos_node;
2175  init_new_rete_node_with_type (thisAgent, mp_node, node_type);
2176  set_sharing_factor (mp_node, pos_copy.sharing_factor);
2177  mp_node->b.posneg = pos_copy.b.posneg;
2178 
2179  /* --- transfer the Mem node's tokens to the MP node --- */
2180  mp_node->a.np.tokens = mem_node->a.np.tokens;
2181  for (t=mem_node->a.np.tokens; t!=NIL; t=t->next_of_node) t->node = mp_node;
2182  mp_node->left_hash_loc_field_num = mem_node->left_hash_loc_field_num;
2183  mp_node->left_hash_loc_levels_up = mem_node->left_hash_loc_levels_up;
2184  mp_node->node_id = mem_node->node_id;
2185 
2186  /* --- replace the Mem node with the new MP node --- */
2187  mp_node->parent = parent;
2188  mp_node->next_sibling = parent->first_child;
2189  parent->first_child = mp_node;
2190  mp_node->first_child = pos_copy.first_child;
2191 
2193  update_stats_for_destroying_node (thisAgent, mem_node); /* clean up rete stats stuff */
2194  free_with_pool (&thisAgent->rete_node_pool, mem_node);
2195 
2196  /* --- set MP node's unlinking status according to pos_copy's --- */
2197  make_mp_bnode_left_linked (mp_node);
2198  if (node_is_left_unlinked(&pos_copy)) make_mp_bnode_left_unlinked (mp_node);
2199 
2200  return mp_node;
2201 }
2202 
2203 /* --------------------------------------------------------------------
2204  Make New MP Node
2205 
2206  Make a new MP node, return a pointer to it.
2207 -------------------------------------------------------------------- */
2208 
2210  rete_node *parent, byte node_type,
2211  var_location left_hash_loc, alpha_mem *am,
2212  rete_test *rt, Bool prefer_left_unlinking) {
2213  rete_node *mem_node, *pos_node;
2214  byte mem_node_type, pos_node_type;
2215 
2216  if (node_type==MP_BNODE) {
2217  pos_node_type = POSITIVE_BNODE;
2218  mem_node_type = MEMORY_BNODE;
2219  } else {
2220  pos_node_type = UNHASHED_POSITIVE_BNODE;
2221  mem_node_type = UNHASHED_MEMORY_BNODE;
2222  }
2223  mem_node = make_new_mem_node (thisAgent, parent, mem_node_type, left_hash_loc);
2224  pos_node = make_new_positive_node (thisAgent, mem_node, pos_node_type, am, rt,
2225  prefer_left_unlinking);
2226  return merge_into_mp_node (thisAgent, mem_node);
2227 }
2228 
2229 /* --------------------------------------------------------------------
2230  Make New Negative Node
2231 
2232  Make a new negative node, return a pointer to it.
2233 -------------------------------------------------------------------- */
2234 
2236  rete_node *parent, byte node_type,
2237  var_location left_hash_loc,
2238  alpha_mem *am, rete_test *rt) {
2239  rete_node *node;
2240 
2241  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &node);
2242  init_new_rete_node_with_type (thisAgent, node, node_type);
2243  node->parent = parent;
2244  node->next_sibling = parent->first_child;
2245  parent->first_child = node;
2246  node->first_child = NIL;
2247  node->left_hash_loc_field_num = left_hash_loc.field_num;
2248  node->left_hash_loc_levels_up = left_hash_loc.levels_up;
2249  node->b.posneg.other_tests = rt;
2250  node->b.posneg.alpha_mem_ = am;
2251  node->a.np.tokens = NIL;
2253  nearest_ancestor_with_same_am (node, am);
2254  relink_to_right_mem (node);
2255 
2256  node->node_id = get_next_beta_node_id(thisAgent);
2257 
2258  /* --- call new node's add_left routine with all the parent's tokens --- */
2259  update_node_with_matches_from_above (thisAgent, node);
2260 
2261  /* --- if no tokens arrived from parent, unlink the node --- */
2262  if (! node->a.np.tokens) unlink_from_right_mem (node);
2263 
2264  return node;
2265 }
2266 
2267 /* --------------------------------------------------------------------
2268  Make New CN Node
2269 
2270  Make new CN and CN_PARTNER nodes, return a pointer to the CN node.
2271 -------------------------------------------------------------------- */
2272 
2274  rete_node *parent,
2275  rete_node *bottom_of_subconditions) {
2276  rete_node *node, *partner, *ncc_subconditions_top_node;
2277 
2278  /* --- Find top node in the subconditions branch --- */
2279  ncc_subconditions_top_node = NIL; /* unneeded, but avoids gcc -Wall warn */
2280  for (node=bottom_of_subconditions; node!=parent; node=node->parent) {
2281  ncc_subconditions_top_node = node;
2282  }
2283 
2284  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &node);
2285  init_new_rete_node_with_type (thisAgent, node, CN_BNODE);
2286  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &partner);
2287  init_new_rete_node_with_type (thisAgent, partner, CN_PARTNER_BNODE);
2288 
2289  /* NOTE: for improved efficiency, <node> should be on the parent's
2290  children list *after* the ncc subcontitions top node */
2291  remove_node_from_parents_list_of_children (ncc_subconditions_top_node);
2292  node->parent = parent;
2293  node->next_sibling = parent->first_child;
2294  ncc_subconditions_top_node->next_sibling = node;
2295  parent->first_child = ncc_subconditions_top_node;
2296  node->first_child = NIL;
2297 
2298  node->a.np.tokens = NIL;
2299  node->b.cn.partner = partner;
2300  node->node_id = get_next_beta_node_id(thisAgent);
2301 
2302  partner->parent = bottom_of_subconditions;
2303  partner->next_sibling = bottom_of_subconditions->first_child;
2304  bottom_of_subconditions->first_child = partner;
2305  partner->first_child = NIL;
2306  partner->a.np.tokens = NIL;
2307  partner->b.cn.partner = node;
2308 
2309  /* --- call partner's add_left routine with all the parent's tokens --- */
2310  update_node_with_matches_from_above (thisAgent, partner);
2311  /* --- call new node's add_left routine with all the parent's tokens --- */
2312  update_node_with_matches_from_above (thisAgent, node);
2313 
2314  return node;
2315 }
2316 
2317 /* --------------------------------------------------------------------
2318  Make New Production Node
2319 
2320  Make a new production node, return a pointer to it.
2321 
2322  Does not handle the following tasks:
2323  - filling in p_node->b.p.parents_nvn or discarding chunk variable names
2324  - filling in stuff on new_prod (except does fill in new_prod->p_node)
2325  - using update_node_with_matches_from_above (p_node) or handling
2326  an initial refracted instantiation
2327 -------------------------------------------------------------------- */
2328 
2330  rete_node *parent, production *new_prod) {
2331  rete_node *p_node;
2332 
2333  allocate_with_pool (thisAgent, &thisAgent->rete_node_pool, &p_node);
2334  init_new_rete_node_with_type (thisAgent, p_node, P_BNODE);
2335  new_prod->p_node = p_node;
2336  p_node->parent = parent;
2337  p_node->next_sibling = parent->first_child;
2338  parent->first_child = p_node;
2339  p_node->first_child = NIL;
2340  p_node->b.p.prod = new_prod;
2341  p_node->a.np.tokens = NIL;
2342  p_node->b.p.tentative_assertions = NIL;
2343  p_node->b.p.tentative_retractions = NIL;
2344  return p_node;
2345 }
2346 
2347 
2348 
2349 
2350 
2351 
2352 
2353 
2354 
2355 /* **********************************************************************
2356 
2357  SECTION 5: Beta Net Primitive Destruction Routines
2358 
2359  Deallocate_rete_test_list() deallocates a list of rete test structures,
2360  removing references to symbols within them.
2361 
2362  Deallocate_rete_node() deallocates a given beta node (which must
2363  not be a p_node), cleaning up any tokens it contains, removing
2364  references (to symbols and alpha memories). It also continues
2365  deallocating nodes up the net if they are no longer used.
2366 ********************************************************************** */
2367 
2369  rete_test *next_rt;
2370 
2371  while (rt) {
2372  next_rt = rt->next;
2373 
2375  symbol_remove_ref (thisAgent, rt->data.constant_referent);
2376  } else if (rt->type==DISJUNCTION_RETE_TEST) {
2378  }
2379 
2380  free_with_pool (&thisAgent->rete_test_pool, rt);
2381  rt = next_rt;
2382  }
2383 }
2384 
2385 void deallocate_rete_node (agent* thisAgent, rete_node *node) {
2386  rete_node *parent;
2387 
2388  /* --- don't deallocate the dummy top node --- */
2389  if (node==thisAgent->dummy_top_node) return;
2390 
2391  /* --- sanity check --- */
2392  if (node->node_type==P_BNODE) {
2393  char msg[BUFFER_MSG_SIZE];
2394  strncpy (msg, "Internal error: deallocate_rete_node() called on p-node.\n", BUFFER_MSG_SIZE);
2395  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
2396  abort_with_fatal_error(thisAgent, msg);
2397  }
2398 
2399  parent = node->parent;
2400 
2401  /* --- if a cn node, deallocate its partner first --- */
2402  if (node->node_type==CN_BNODE) deallocate_rete_node (thisAgent, node->b.cn.partner);
2403 
2404  /* --- clean up any tokens at the node --- */
2406  while (node->a.np.tokens) remove_token_and_subtree (thisAgent, node->a.np.tokens);
2407 
2408  /* --- stuff for posneg nodes only --- */
2409  if (bnode_is_posneg(node->node_type)) {
2410  deallocate_rete_test_list (thisAgent, node->b.posneg.other_tests);
2411  /* --- right unlink the node, cleanup alpha memory --- */
2412  if (! node_is_right_unlinked (node)) unlink_from_right_mem (node);
2413  remove_ref_to_alpha_mem (thisAgent, node->b.posneg.alpha_mem_);
2414  }
2415 
2416  /* --- remove the node from its parent's list --- */
2418 
2419  /* --- for unmerged pos. nodes: unlink, maybe merge its parent --- */
2421  if (! node_is_left_unlinked(node)) unlink_from_left_mem (node);
2422  /* --- if parent is mem node with just one child, merge them --- */
2423  if (parent->first_child && (! parent->first_child->next_sibling)) {
2424  merge_into_mp_node (thisAgent, parent);
2425  parent = NIL;
2426  }
2427  }
2428 
2429  update_stats_for_destroying_node (thisAgent, node); /* clean up rete stats stuff */
2430  free_with_pool (&thisAgent->rete_node_pool, node);
2431 
2432  /* --- if parent has no other children, deallocate it, and recurse --- */
2433  /* Added check to make sure that parent wasn't deallocated in previous merge */
2434  if ( parent && !parent->first_child) deallocate_rete_node (thisAgent, parent);
2435 }
2436 
2437 
2438 
2439 
2440 
2441 
2442 
2443 
2444 
2445 
2446 
2447 
2448 /* **********************************************************************
2449 
2450  SECTION 6: Variable Bindings and Locations
2451 
2452  As we build the network for a production, we have to keep track of
2453  where variables are bound -- i.e., at what earlier conditions/fields
2454  (if any) did a given variable occur? We could do this by scanning
2455  upwards -- look at all the earlier conditions to try to find an
2456  occurrence of the variable -- but that would take O(C) time, where
2457  C is the number of conditions. Instead, we store binding location
2458  information directly on the variables in the symbol table. Each
2459  variable has a field var.rete_binding_locations, which holds a
2460  stack (yes, a stack) of binding locations, with the most recent (i.e.,
2461  lowest in the Rete) binding on top of the stack. (It has to be a stack
2462  so we can push and pop bindings during the handling of conjunctive
2463  negations.)
2464 
2465  Whenever a variable is created, the symbol table routines initialize
2466  var.rete_binding_locations to NIL. It is important for the stack to
2467  get completely popped after we're done with each production addition,
2468  so it gets properly reset to NIL.
2469 
2470  The basic operations on these binding stacks are done with a few
2471  macros below. A binding location is represented by the CAR of a
2472  CONS -- the level and field numbers are crammed into the CAR.
2473  Var_is_bound() returns TRUE iff the given variable has been bound.
2474  Push_var_binding() pushes a new binding of the given variable.
2475  Pop_var_binding() pops the top binding.
2476 ********************************************************************** */
2477 
2478 //#define var_is_bound(v) (((Symbol *)(v))->var.rete_binding_locations != NIL)
2479 inline bool var_is_bound(Symbol * v)
2480 {
2481  return v->var.rete_binding_locations != NIL;
2482 }
2483 
2484 //#define varloc_to_dummy(depth,field_num) ((void *)(((depth)<<2) + (field_num)))
2485 inline void * varloc_to_dummy(rete_node_level depth, byte field_num)
2486 {
2487  return reinterpret_cast<void *>((depth << 2) + field_num);
2488 }
2489 
2490 //#define dummy_to_varloc_depth(d) (((uint64_t)(d))>>2)
2492 {
2493  return static_cast<rete_node_level>(reinterpret_cast<uintptr_t>(d) >> 2);
2494 }
2495 
2496 //#define dummy_to_varloc_field_num(d) (((uint64_t)(d)) & 3)
2498 {
2499  return static_cast<byte>(reinterpret_cast<uintptr_t>(d) & 3);
2500 }
2501 
2502 /*#define push_var_binding(v,depth,field_num) { \
2503  void *dummy_xy312; \
2504  dummy_xy312 = varloc_to_dummy ((depth), (field_num)); \
2505  push(thisAgent, dummy_xy312, ((Symbol *)(v))->var.rete_binding_locations); }*/
2506 inline void push_var_binding(agent* thisAgent, Symbol * v, rete_node_level depth, byte field_num)
2507 {
2508  void *dummy_xy312;
2509  dummy_xy312 = varloc_to_dummy (depth, field_num);
2510  push(thisAgent, dummy_xy312, v->var.rete_binding_locations);
2511 }
2512 
2513 /*#define pop_var_binding(v) { \
2514  cons *c_xy312; \
2515  c_xy312 = ((Symbol *)(v))->var.rete_binding_locations; \
2516  ((Symbol *)(v))->var.rete_binding_locations = c_xy312->rest; \
2517  free_cons (c_xy312); }*/
2518 inline void pop_var_binding(agent* thisAgent, void * v)
2519 {
2520  cons *c_xy312;
2521  c_xy312 = static_cast<Symbol *>(v)->var.rete_binding_locations;
2522  static_cast<Symbol *>(v)->var.rete_binding_locations = c_xy312->rest;
2523  free_cons (thisAgent, c_xy312);
2524 }
2525 
2526 /* -------------------------------------------------------------------
2527  Find Var Location
2528 
2529  This routine finds the most recent place a variable was bound.
2530  It does this simply by looking at the top of the binding stack
2531  for that variable. If there is any binding, its location is stored
2532  in the parameter *result, and the function returns TRUE. If no
2533  binding is found, the function returns FALSE.
2534 ------------------------------------------------------------------- */
2535 
2537  var_location *result) {
2538  void *dummy;
2539  if (! var->var.rete_binding_locations) return FALSE;
2540  dummy = var->var.rete_binding_locations->first;
2541  result->levels_up = current_depth - dummy_to_varloc_depth (dummy);
2542  result->field_num = dummy_to_varloc_field_num (dummy);
2543  return TRUE;
2544 }
2545 
2546 /* -------------------------------------------------------------------
2547  Bind Variables in Test
2548 
2549  This routine pushes bindings for variables occurring (i.e., being
2550  equality-tested) in a given test. It can do this in DENSE fashion
2551  (push a new binding for ANY variable) or SPARSE fashion (push a new
2552  binding only for previously-unbound variables), depending on the
2553  boolean "dense" parameter. Any variables receiving new bindings
2554  are also pushed onto the given "varlist".
2555 ------------------------------------------------------------------- */
2556 
2557 void bind_variables_in_test (agent* thisAgent,
2558  test t,
2559  rete_node_level depth,
2560  byte field_num,
2561  Bool dense,
2562  list **varlist) {
2563  Symbol *referent;
2564  complex_test *ct;
2565  cons *c;
2566 
2567  if (test_is_blank_test(t)) return;
2569  referent = referent_of_equality_test(t);
2570  if (referent->common.symbol_type!=VARIABLE_SYMBOL_TYPE) return;
2571  if (!dense && var_is_bound (referent)) return;
2572  push_var_binding (thisAgent, referent, depth, field_num);
2573  push(thisAgent, referent, *varlist);
2574  return;
2575  }
2576 
2577  ct = complex_test_from_test(t);
2578  if (ct->type==CONJUNCTIVE_TEST)
2579  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
2580  bind_variables_in_test (thisAgent, static_cast<char *>(c->first),
2581  depth, field_num, dense, varlist);
2582 }
2583 
2584 /* -------------------------------------------------------------------
2585  Pop Bindings and Deallocate List of Variables
2586 
2587  This routine takes a list of variables; for each item <v> on the
2588  list, it pops a binding of <v>. It also deallocates the list.
2589  This is often used for un-binding a group of variables which got
2590  bound in some procedure.
2591 ------------------------------------------------------------------- */
2592 
2594  while (vars) {
2595  cons *c;
2596  c = vars;
2597  vars = vars->rest;
2598  pop_var_binding (thisAgent, c->first);
2599  free_cons (thisAgent, c);
2600  }
2601 }
2602 
2603 
2604 
2605 
2606 
2607 
2608 
2609 
2610 
2611 
2612 
2613 
2614 
2615 /* **********************************************************************
2616 
2617  SECTION 7: Varnames and Node_Varnames
2618 
2619  Varnames and Node_Varnames (NVN) structures are used to record the names
2620  of variables bound (i.e., equality tested) at rete nodes. The only
2621  purpose of saving this information is so we can reconstruct the
2622  original source code for a production when we want to print it. For
2623  chunks, we don't save any of this information -- we just re-gensym
2624  the variable names on each printing (unless discard_chunk_varnames
2625  is set to FALSE).
2626 
2627  For each production, a chain of node_varnames structures is built,
2628  paralleling the structure of the rete net (i.e., the portion of the rete
2629  used for that production). There is a node_varnames structure for
2630  each Mem, Neg, or NCC node in that part, giving the names of variables
2631  bound in the id, attr, and value fields of the condition at that node.
2632 
2633  At each field, we could bind zero, one, or more variables. To
2634  save space, we use some bit-twiddling here. A "varnames" represents
2635  zero or more variables: NIL means zero; a pointer (with the low-order
2636  bit being 0) to a variable means just that one variable; and any
2637  other pointer (with the low-order bit set to 1) points (minus 1, of
2638  course) to a consed list of variables.
2639 
2640  Add_var_to_varnames() takes an existing varnames object (which can
2641  be NIL, for no variable names) and returns a new varnames object
2642  which adds (destructively!) a given variable to the previous one.
2643  Deallocate_varnames() deallocates a varnames object, removing references
2644  to symbols, etc. Deallocate_node_varnames() deallocates a whole
2645  chain of node_varnames structures, scanning up the net, etc.
2646 ********************************************************************** */
2647 
2648 typedef char varnames;
2649 
2650 /*
2651 #define one_var_to_varnames(x) ((varnames *) (x))
2652 #define var_list_to_varnames(x) ((varnames *) (((char *)(x)) + 1))
2653 #define varnames_is_one_var(x) (! (varnames_is_var_list(x)))
2654 #define varnames_is_var_list(x) (((uint64_t)(x)) & 1)
2655 #define varnames_to_one_var(x) ((Symbol *) (x))
2656 #define varnames_to_var_list(x) ((list *) (((char *)(x)) - 1))
2657 */
2658 
2659 inline varnames * one_var_to_varnames(Symbol * x) { return reinterpret_cast<varnames *>(x); }
2660 inline varnames * var_list_to_varnames(cons * x) { return reinterpret_cast<varnames *>(reinterpret_cast<char *>(x) + 1); }
2661 inline uint64_t varnames_is_var_list(varnames * x) { return reinterpret_cast<uint64_t>(x) & 1; }
2663 inline Symbol * varnames_to_one_var(varnames * x) { return reinterpret_cast<Symbol *>(x); }
2664 inline list * varnames_to_var_list(varnames * x) { return reinterpret_cast<list *>(static_cast<char *>(x) - 1); }
2665 
2671 
2672 typedef struct node_varnames_struct {
2677  } data;
2678 } node_varnames;
2679 
2681  varnames *old_varnames) {
2682  cons *c1, *c2;
2683 
2684  symbol_add_ref (var);
2685  if (old_varnames == NIL)
2686  return one_var_to_varnames(var);
2687  if (varnames_is_one_var(old_varnames)) {
2688  allocate_cons (thisAgent, &c1);
2689  allocate_cons (thisAgent, &c2);
2690  c1->first = var;
2691  c1->rest = c2;
2692  c2->first = varnames_to_one_var(old_varnames);
2693  c2->rest = NIL;
2694  return var_list_to_varnames(c1);
2695  }
2696  /* --- otherwise old_varnames is a list --- */
2697  allocate_cons (thisAgent, &c1);
2698  c1->first = var;
2699  c1->rest = varnames_to_var_list(old_varnames);
2700  return var_list_to_varnames(c1);
2701 }
2702 
2703 void deallocate_varnames (agent* thisAgent, varnames *vn) {
2704  Symbol *sym;
2705  list *symlist;
2706 
2707  if (vn == NIL) return;
2708  if (varnames_is_one_var(vn)) {
2709  sym = varnames_to_one_var(vn);
2710  symbol_remove_ref (thisAgent, sym);
2711  } else {
2712  symlist = varnames_to_var_list(vn);
2713  deallocate_symbol_list_removing_references (thisAgent, symlist);
2714  }
2715 }
2716 
2718  rete_node *node, rete_node *cutoff,
2719  node_varnames *nvn) {
2720  node_varnames *temp;
2721 
2722  while (node!=cutoff) {
2723  if (node->node_type==CN_BNODE) {
2724  deallocate_node_varnames (thisAgent, node->b.cn.partner->parent, node->parent,
2726  } else {
2727  deallocate_varnames (thisAgent, nvn->data.fields.id_varnames);
2728  deallocate_varnames (thisAgent, nvn->data.fields.attr_varnames);
2729  deallocate_varnames (thisAgent, nvn->data.fields.value_varnames);
2730  }
2731  node = real_parent_node (node);
2732  temp = nvn;
2733  nvn = nvn->parent;
2734  free_with_pool (&thisAgent->node_varnames_pool, temp);
2735  }
2736 }
2737 
2738 /* -------------------------------------------------------------------
2739  Creating the Node Varnames Structures for a List of Conditions
2740 
2741  Add_unbound_varnames_in_test() adds to an existing varnames object
2742  the names of any currently-unbound variables equality-tested in
2743  a given test. Make_nvn_for_posneg_cond() creates and returns the
2744  node_varnames structure for a single given (simple) positive or
2745  negative condition. Get_nvn_for_condition_list() creates the
2746  whole chain of NVN structures for a list of conditions, returning
2747  a pointer to the bottom structure in the chain.
2748 ------------------------------------------------------------------- */
2749 
2751  varnames *starting_vn) {
2752  cons *c;
2753  Symbol *referent;
2754  complex_test *ct;
2755 
2756  if (test_is_blank_test(t)) return starting_vn;
2758  referent = referent_of_equality_test(t);
2759  if (referent->common.symbol_type==VARIABLE_SYMBOL_TYPE)
2760  if (! var_is_bound (referent))
2761  starting_vn = add_var_to_varnames (thisAgent, referent, starting_vn);
2762  return starting_vn;
2763  }
2764 
2765  ct = complex_test_from_test(t);
2766 
2767  if (ct->type==CONJUNCTIVE_TEST) {
2768  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
2769  starting_vn = add_unbound_varnames_in_test (thisAgent, static_cast<char *>(c->first),
2770  starting_vn);
2771  }
2772  return starting_vn;
2773 }
2774 
2776  condition *cond,
2777  node_varnames *parent_nvn) {
2778  node_varnames *New;
2779  list *vars_bound;
2780 
2781  vars_bound = NIL;
2782 
2783  allocate_with_pool (thisAgent, &thisAgent->node_varnames_pool, &New);
2784  New->parent = parent_nvn;
2785 
2786  /* --- fill in varnames for id test --- */
2787  New->data.fields.id_varnames =
2788  add_unbound_varnames_in_test (thisAgent, cond->data.tests.id_test, NIL);
2789 
2790  /* --- add sparse bindings for id, then get attr field varnames --- */
2791  bind_variables_in_test (thisAgent, cond->data.tests.id_test, 0, 0, FALSE, &vars_bound);
2792  New->data.fields.attr_varnames =
2793  add_unbound_varnames_in_test (thisAgent, cond->data.tests.attr_test, NIL);
2794 
2795  /* --- add sparse bindings for attr, then get value field varnames --- */
2796  bind_variables_in_test (thisAgent, cond->data.tests.attr_test, 0, 0, FALSE,&vars_bound);
2797  New->data.fields.value_varnames =
2799 
2800  /* --- Pop the variable bindings for these conditions --- */
2801  pop_bindings_and_deallocate_list_of_variables (thisAgent, vars_bound);
2802 
2803  return New;
2804 }
2805 
2807  condition *cond_list,
2808  node_varnames *parent_nvn) {
2809  node_varnames *New = 0;
2810  condition *cond;
2811  list *vars;
2812 
2813  vars = NIL;
2814 
2815  for (cond=cond_list; cond!=NIL; cond=cond->next) {
2816 
2817  switch (cond->type) {
2818  case POSITIVE_CONDITION:
2819  New = make_nvn_for_posneg_cond (thisAgent, cond, parent_nvn);
2820 
2821  /* --- Add sparse variable bindings for this condition --- */
2822  bind_variables_in_test (thisAgent, cond->data.tests.id_test, 0, 0, FALSE, &vars);
2823  bind_variables_in_test (thisAgent, cond->data.tests.attr_test, 0, 0, FALSE, &vars);
2824  bind_variables_in_test (thisAgent, cond->data.tests.value_test, 0, 0, FALSE, &vars);
2825  break;
2826  case NEGATIVE_CONDITION:
2827  New = make_nvn_for_posneg_cond (thisAgent, cond, parent_nvn);
2828  break;
2830  allocate_with_pool (thisAgent, &thisAgent->node_varnames_pool, &New);
2831  New->parent = parent_nvn;
2833  get_nvn_for_condition_list (thisAgent, cond->data.ncc.top, parent_nvn);
2834  break;
2835  }
2836 
2837  parent_nvn = New;
2838  }
2839 
2840  /* --- Pop the variable bindings for these conditions --- */
2842 
2843  return parent_nvn;
2844 }
2845 
2846 
2847 
2848 
2849 
2850 
2851 
2852 
2853 
2854 
2855 
2856 
2857 
2858 
2859 
2860 
2861 
2862 
2863 
2864 /* **********************************************************************
2865 
2866  SECTION 8: Building the Rete Net: Condition-To-Node Converstion
2867 
2868  Build_network_for_condition_list() is the key routine here. (See
2869  description below.)
2870 ********************************************************************** */
2871 
2872 
2873 /* ---------------------------------------------------------------------
2874 
2875  Test Type <---> Relational (Rete) Test Type Conversion Tables
2876 
2877  These tables convert from xxx_TEST's (defined in soarkernel.h for various
2878  kinds of complex_test's) to xxx_RETE_TEST's (defined in rete.cpp for
2879  the different kinds of Rete tests), and vice-versa. We might just
2880  use the same set of constants for both purposes, but we want to be
2881  able to do bit-twiddling on the RETE_TEST types.
2882 
2883  (This stuff probably doesn't belong under "Building the Rete Net",
2884  but I wasn't sure where else to put it.)
2885 --------------------------------------------------------------------- */
2886 
2887 //
2888 // 255 == ERROR_TEST_TYPE. I use 255 here for brevity.
2889 //
2891 {
2892  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2893  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2894  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2895  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2896  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2897  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2898  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2899  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2900  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2901  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2902  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2903  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2904  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2905  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2906  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2907  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
2908 };
2909 
2910 //
2911 // 255 == ERROR_TEST_TYPE. I use 255 here for brevity.
2912 //
2914 {
2915  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2916  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2917  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2918  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2919  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2920  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2921  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2922  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2923  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2924  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2925  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2926  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2927  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2928  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2929  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2930  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
2931 };
2932 
2933 /* Warning: the two items below must not be the same as any xxx_TEST's defined
2934  in soarkernel.h for the types of complex_test's */
2935 #define EQUAL_TEST_TYPE 254
2936 #define ERROR_TEST_TYPE 255
2937 
2939 {
2940  /* This is to avoid multiple initializations. (This may not yet thread-safe.) */
2941  static bool bInit = FALSE;
2942  if (bInit)
2943  return;
2944  bInit = TRUE;
2945 
2946  /* we don't need ...[equal test] */
2953 
2961 }
2962 
2963 /* ------------------------------------------------------------------------
2964  Add Rete Tests for Test
2965 
2966  This is used for converting tests (from conditions) into the appropriate
2967  rete_test's and/or constant-to-be-tested-by-the-alpha-network. It takes
2968  all sub-tests from a given test, converts them into the necessary Rete
2969  tests (if any -- note that an equality test with a previously-unbound
2970  variable can be ignored), and destructively adds the Rete tests to
2971  the given "rt" parameter. The "current_depth" and "field_num" params
2972  tell where the current test originated.
2973 
2974  For any field, we can handle one equality-with-a-constant test in the
2975  alpha net. If the "*alpha_constant" parameter is initially NIL, this
2976  routine may also set *alpha_constant to point to the constant symbol
2977  for the alpha net to test (rather than creating the corresponding
2978  rete_test).
2979 
2980  Before calling this routine, variables should be bound densely for
2981  parent and higher conditions, and sparsely for the current condition.
2982 ------------------------------------------------------------------------ */
2983 
2984 void add_rete_tests_for_test (agent* thisAgent, test t,
2985  rete_node_level current_depth,
2986  byte field_num,
2987  rete_test **rt,
2988  Symbol **alpha_constant) {
2989  var_location where;
2990  where.var_location_struct::field_num = 0;
2991  where.var_location_struct::levels_up = 0;
2992  cons *c;
2993  rete_test *new_rt;
2994  complex_test *ct;
2995  Symbol *referent;
2996 
2997  if (test_is_blank_test(t)) return;
2998 
3000  referent = referent_of_equality_test(t);
3001 
3002  /* --- if constant test and alpha=NIL, install alpha test --- */
3003  if ((referent->common.symbol_type!=VARIABLE_SYMBOL_TYPE) &&
3004  (*alpha_constant==NIL)) {
3005  *alpha_constant = referent;
3006  return;
3007  }
3008 
3009  /* --- if constant, make = constant test --- */
3010  if (referent->common.symbol_type!=VARIABLE_SYMBOL_TYPE) {
3011  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3012  new_rt->right_field_num = field_num;
3014  new_rt->data.constant_referent = referent;
3015  symbol_add_ref (referent);
3016  new_rt->next = *rt;
3017  *rt = new_rt;
3018  return;
3019  }
3020 
3021  /* --- variable: if binding is for current field, do nothing --- */
3022  if (! find_var_location (referent, current_depth, &where)) {
3023  char msg[BUFFER_MSG_SIZE];
3024  print_with_symbols (thisAgent, "Error: Rete build found test of unbound var: %y\n",
3025  referent);
3026  SNPRINTF (msg, BUFFER_MSG_SIZE, "Error: Rete build found test of unbound var: %s\n",
3027  symbol_to_string(thisAgent, referent,TRUE, NIL, 0));
3028  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
3029  abort_with_fatal_error(thisAgent, msg);
3030  }
3031  if ((where.levels_up==0) && (where.field_num==field_num)) return;
3032 
3033  /* --- else make variable equality test --- */
3034  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3035  new_rt->right_field_num = field_num;
3037  new_rt->data.variable_referent = where;
3038  new_rt->next = *rt;
3039  *rt = new_rt;
3040  return;
3041  }
3042 
3043  ct = complex_test_from_test(t);
3044 
3045  switch (ct->type) {
3046 
3047  case NOT_EQUAL_TEST:
3048  case LESS_TEST:
3049  case GREATER_TEST:
3050  case LESS_OR_EQUAL_TEST:
3051  case GREATER_OR_EQUAL_TEST:
3052  case SAME_TYPE_TEST:
3053  /* --- if constant, make constant test --- */
3054  if (ct->data.referent->common.symbol_type!=VARIABLE_SYMBOL_TYPE) {
3055  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3056  new_rt->right_field_num = field_num;
3059  new_rt->data.constant_referent = ct->data.referent;
3061  new_rt->next = *rt;
3062  *rt = new_rt;
3063  return;
3064  }
3065  /* --- else make variable test --- */
3066  if (! find_var_location (ct->data.referent, current_depth, &where)) {
3067  char msg[BUFFER_MSG_SIZE];
3068  print_with_symbols (thisAgent, "Error: Rete build found test of unbound var: %y\n",
3069  ct->data.referent);
3070  SNPRINTF (msg, BUFFER_MSG_SIZE, "Error: Rete build found test of unbound var: %s\n",
3071  symbol_to_string(thisAgent, ct->data.referent,TRUE, NIL, 0));
3072  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
3073  abort_with_fatal_error(thisAgent, msg);
3074  }
3075  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3076  new_rt->right_field_num = field_num;
3079  new_rt->data.variable_referent = where;
3080  new_rt->next = *rt;
3081  *rt = new_rt;
3082  return;
3083 
3084  case DISJUNCTION_TEST:
3085  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3086  new_rt->right_field_num = field_num;
3087  new_rt->type = DISJUNCTION_RETE_TEST;
3088  new_rt->data.disjunction_list =
3090  new_rt->next = *rt;
3091  *rt = new_rt;
3092  return;
3093 
3094  case CONJUNCTIVE_TEST:
3095  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest) {
3096  add_rete_tests_for_test (thisAgent, static_cast<char *>(c->first),
3097  current_depth, field_num, rt, alpha_constant);
3098  }
3099  return;
3100 
3101  case GOAL_ID_TEST:
3102  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3103  new_rt->type = ID_IS_GOAL_RETE_TEST;
3104  new_rt->right_field_num = 0;
3105  new_rt->next = *rt;
3106  *rt = new_rt;
3107  return;
3108 
3109  case IMPASSE_ID_TEST:
3110  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &new_rt);
3111  new_rt->type = ID_IS_IMPASSE_RETE_TEST;
3112  new_rt->right_field_num = 0;
3113  new_rt->next = *rt;
3114  *rt = new_rt;
3115  return;
3116 
3117  default:
3118  { char msg[BUFFER_MSG_SIZE];
3119  SNPRINTF (msg, BUFFER_MSG_SIZE,"Error: found bad test type %d while building rete\n",
3120  ct->type);
3121  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
3122  abort_with_fatal_error(thisAgent, msg);
3123  }
3124  } /* end of switch statement */
3125 } /* end of function add_rete_tests_for_test() */
3126 
3127 
3128 
3129 /* ------------------------------------------------------------------------
3130  Rete Test Lists are Identical
3131 
3132  This is used for checking whether an existing Rete node can be
3133  shared, instead of building a new one.
3134 
3135  Single_rete_tests_are_identical() checks whether two (non-conjunctive)
3136  Rete tests are the same. (Note that in the case of disjunction tests,
3137  the symbols in the disjunction have to be in the same order; this
3138  simplifies and speeds up the code here, but unnecessarily reduces
3139  sharing.)
3140 
3141  Rete_test_lists_are_identical() checks whether two lists of Rete tests
3142  are identical. (Note that the lists have to be in the order; the code
3143  here doesn't check all possible orderings.)
3144 ------------------------------------------------------------------------ */
3145 
3147  cons *c1, *c2;
3148 
3149  if (rt1->type != rt2->type) return FALSE;
3150 
3151  if (rt1->right_field_num != rt2->right_field_num) return FALSE;
3152 
3155  rt2->data.variable_referent));
3156 
3158  return (rt1->data.constant_referent == rt2->data.constant_referent);
3159  }
3160 
3161  if (rt1->type==ID_IS_GOAL_RETE_TEST) return TRUE;
3162  if (rt1->type==ID_IS_IMPASSE_RETE_TEST) return TRUE;
3163 
3164  if (rt1->type == DISJUNCTION_RETE_TEST) {
3165  c1 = rt1->data.disjunction_list;
3166  c2 = rt2->data.disjunction_list;
3167  while ((c1!=NIL)&&(c2!=NIL)) {
3168  if (c1->first != c2->first) return FALSE;
3169  c1 = c1->rest;
3170  c2 = c2->rest;
3171  }
3172  if (c1==c2) return TRUE;
3173  return FALSE;
3174  }
3175  { char msg[BUFFER_MSG_SIZE];
3176  strncpy(msg,"Internal error: bad rete test type in single_rete_tests_are_identical\n", BUFFER_MSG_SIZE);
3177  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
3178  abort_with_fatal_error(thisAgent, msg);
3179  }
3180  return FALSE; /* unreachable, but without it, gcc -Wall warns here */
3181 }
3182 
3184  while (rt1 && rt2) {
3185  if (! single_rete_tests_are_identical(thisAgent, rt1,rt2))
3186  return FALSE;
3187  rt1 = rt1->next;
3188  rt2 = rt2->next;
3189  }
3190  if (rt1==rt2) return TRUE; /* make sure they both hit end-of-list */
3191  return FALSE;
3192 }
3193 
3194 /* ------------------------------------------------------------------------
3195  Extract Rete Test to Hash With
3196 
3197  Extracts from a Rete test list the variable equality test to use for
3198  hashing. Returns TRUE if successful, or FALSE if there was no such
3199  test to use for hashing. The Rete test list ("rt") is destructively
3200  modified to splice out the extracted test.
3201 ------------------------------------------------------------------------ */
3202 
3204  rete_test **rt,
3205  var_location *dest_hash_loc) {
3206  rete_test *prev, *current;
3207 
3208  /* --- look through rt list, find the first variable equality test --- */
3209  prev = NIL;
3210  for (current = *rt; current!=NIL; prev=current, current=current->next)
3211  if (current->type==VARIABLE_RELATIONAL_RETE_TEST +
3213 
3214  if (!current) return FALSE; /* no variable equality test was found */
3215 
3216  /* --- unlink it from rt --- */
3217  if (prev) prev->next = current->next; else *rt = current->next;
3218 
3219  /* --- extract info, and deallocate that single test --- */
3220  *dest_hash_loc = current->data.variable_referent;
3221  current->next = NIL;
3222  deallocate_rete_test_list (thisAgent, current);
3223  return TRUE;
3224 }
3225 
3226 /* ------------------------------------------------------------------------
3227  Make Node for Positive Cond
3228 
3229  Finds or creates a node for the given single condition <cond>, which
3230  must be a simple positive condition. The node is made a child of the
3231  given <parent> node. Variables for earlier conditions should be bound
3232  densely before this routine is called. The routine returns a pointer
3233  to the (newly-created or shared) node.
3234 ------------------------------------------------------------------------ */
3235 
3237  condition *cond,
3238  rete_node_level current_depth,
3239  rete_node *parent) {
3240  byte pos_node_type, mem_node_type, mp_node_type;
3241  Symbol *alpha_id, *alpha_attr, *alpha_value;
3242  rete_node *node, *mem_node, *mp_node;
3243  alpha_mem *am;
3244  rete_test *rt;
3245  Bool hash_this_node;
3246  var_location left_hash_loc;
3247  left_hash_loc.var_location_struct::field_num = 0;
3248  left_hash_loc.var_location_struct::levels_up = 0;
3249  list *vars_bound_here;
3250 
3251  alpha_id = alpha_attr = alpha_value = NIL;
3252  rt = NIL;
3253  vars_bound_here = NIL;
3254 
3255  /* --- Add sparse variable bindings for this condition --- */
3256  bind_variables_in_test (thisAgent, cond->data.tests.id_test, current_depth, 0,
3257  FALSE, &vars_bound_here);
3258  bind_variables_in_test (thisAgent, cond->data.tests.attr_test, current_depth, 1,
3259  FALSE, &vars_bound_here);
3260  bind_variables_in_test (thisAgent, cond->data.tests.value_test, current_depth, 2,
3261  FALSE, &vars_bound_here);
3262 
3263  /* --- Get Rete tests, alpha constants, and hash location --- */
3264  add_rete_tests_for_test (thisAgent, cond->data.tests.id_test, current_depth, 0,
3265  &rt, &alpha_id);
3266  hash_this_node = extract_rete_test_to_hash_with (thisAgent, &rt, &left_hash_loc);
3267  add_rete_tests_for_test (thisAgent, cond->data.tests.attr_test, current_depth, 1,
3268  &rt, &alpha_attr);
3269  add_rete_tests_for_test (thisAgent, cond->data.tests.value_test, current_depth, 2,
3270  &rt, &alpha_value);
3271 
3272  /* --- Pop sparse variable bindings for this condition --- */
3273  pop_bindings_and_deallocate_list_of_variables (thisAgent, vars_bound_here);
3274 
3275  /* --- Get alpha memory --- */
3276  am = find_or_make_alpha_mem (thisAgent, alpha_id, alpha_attr, alpha_value,
3278 
3279  /* --- Algorithm for adding node:
3280  1. look for matching mem node; if found then
3281  look for matching join node; create new one if no match
3282  2. no matching mem node: look for mp node with matching mem
3283  if found, if join part matches too, then done
3284  else delete mp node, create mem node and 2 joins
3285  if not matching mem node, create new mp node. */
3286 
3287  /* --- determine desired node types --- */
3288  if (hash_this_node) {
3289  pos_node_type = POSITIVE_BNODE;
3290  mem_node_type = MEMORY_BNODE;
3291  mp_node_type = MP_BNODE;
3292  } else {
3293  pos_node_type = UNHASHED_POSITIVE_BNODE;
3294  mem_node_type = UNHASHED_MEMORY_BNODE;
3295  mp_node_type = UNHASHED_MP_BNODE;
3296  }
3297 
3298  /* --- look for a matching existing memory node --- */
3299  for (mem_node=parent->first_child; mem_node!=NIL;
3300  mem_node=mem_node->next_sibling)
3301  if ((mem_node->node_type==mem_node_type) &&
3302  ((!hash_this_node) ||
3303  ((mem_node->left_hash_loc_field_num==left_hash_loc.field_num) &&
3304  (mem_node->left_hash_loc_levels_up==left_hash_loc.levels_up))))
3305  break;
3306 
3307  if (mem_node) { /* -- A matching memory node was found --- */
3308  /* --- look for a matching existing join node --- */
3309  for (node=mem_node->first_child; node!=NIL; node=node->next_sibling)
3310  if ((node->node_type==pos_node_type) &&
3311  (am == node->b.posneg.alpha_mem_) &&
3312  rete_test_lists_are_identical (thisAgent, node->b.posneg.other_tests, rt))
3313  break;
3314 
3315  if (node) { /* --- A matching join node was found --- */
3316  deallocate_rete_test_list (thisAgent, rt);
3317  remove_ref_to_alpha_mem (thisAgent, am);
3318  return node;
3319  } else { /* --- No match was found, so create a new node --- */
3320  node = make_new_positive_node (thisAgent, mem_node, pos_node_type, am ,rt, FALSE);
3321  return node;
3322  }
3323  }
3324 
3325  /* --- No matching memory node was found; look for MP with matching M --- */
3326  for (mp_node=parent->first_child; mp_node!=NIL;
3327  mp_node=mp_node->next_sibling)
3328  if ((mp_node->node_type==mp_node_type) &&
3329  ((!hash_this_node) ||
3330  ((mp_node->left_hash_loc_field_num==left_hash_loc.field_num) &&
3331  (mp_node->left_hash_loc_levels_up==left_hash_loc.levels_up))))
3332  break;
3333 
3334  if (mp_node) { /* --- Found matching M part of MP --- */
3335  if ((am == mp_node->b.posneg.alpha_mem_) &&
3336  rete_test_lists_are_identical (thisAgent, mp_node->b.posneg.other_tests, rt)) {
3337  /* --- Complete MP match was found --- */
3338  deallocate_rete_test_list (thisAgent, rt);
3339  remove_ref_to_alpha_mem (thisAgent, am);
3340  return mp_node;
3341  }
3342 
3343  /* --- Delete MP node, replace it with M and two positive joins --- */
3344  mem_node = split_mp_node (thisAgent, mp_node);
3345  node = make_new_positive_node (thisAgent, mem_node, pos_node_type, am, rt, FALSE);
3346  return node;
3347  }
3348 
3349  /* --- Didn't even find a matching M part of MP, so make a new MP node --- */
3350  return make_new_mp_node (thisAgent, parent, mp_node_type, left_hash_loc, am, rt, FALSE);
3351 }
3352 
3353 /* ------------------------------------------------------------------------
3354  Make Node for Negative Cond
3355 
3356  Finds or creates a node for the given single condition <cond>, which
3357  must be a simple negative (not ncc) condition. The node is made a
3358  child of the given <parent> node. Variables for earlier conditions
3359  should be bound densely before this routine is called. The routine
3360  returns a pointer to the (newly-created or shared) node.
3361 ------------------------------------------------------------------------ */
3362 
3364  condition *cond,
3365  rete_node_level current_depth,
3366  rete_node *parent) {
3367  byte node_type;
3368  Symbol *alpha_id, *alpha_attr, *alpha_value;
3369  rete_node *node;
3370  alpha_mem *am;
3371  rete_test *rt;
3372  Bool hash_this_node;
3373  var_location left_hash_loc;
3374  left_hash_loc.var_location_struct::field_num = 0;
3375  left_hash_loc.var_location_struct::levels_up = 0;
3376  list *vars_bound_here;
3377 
3378  alpha_id = alpha_attr = alpha_value = NIL;
3379  rt = NIL;
3380  vars_bound_here = NIL;
3381 
3382  /* --- Add sparse variable bindings for this condition --- */
3383  bind_variables_in_test (thisAgent, cond->data.tests.id_test, current_depth, 0,
3384  FALSE, &vars_bound_here);
3385  bind_variables_in_test (thisAgent, cond->data.tests.attr_test, current_depth, 1,
3386  FALSE, &vars_bound_here);
3387  bind_variables_in_test (thisAgent, cond->data.tests.value_test, current_depth, 2,
3388  FALSE, &vars_bound_here);
3389 
3390  /* --- Get Rete tests, alpha constants, and hash location --- */
3391  add_rete_tests_for_test (thisAgent, cond->data.tests.id_test, current_depth, 0,
3392  &rt, &alpha_id);
3393  hash_this_node = extract_rete_test_to_hash_with (thisAgent, &rt, &left_hash_loc);
3394  add_rete_tests_for_test (thisAgent, cond->data.tests.attr_test, current_depth, 1,
3395  &rt, &alpha_attr);
3396  add_rete_tests_for_test (thisAgent, cond->data.tests.value_test, current_depth, 2,
3397  &rt, &alpha_value);
3398 
3399  /* --- Pop sparse variable bindings for this condition --- */
3400  pop_bindings_and_deallocate_list_of_variables (thisAgent, vars_bound_here);
3401 
3402  /* --- Get alpha memory --- */
3403  am = find_or_make_alpha_mem (thisAgent, alpha_id, alpha_attr, alpha_value,
3405 
3406  /* --- determine desired node type --- */
3407  node_type = hash_this_node ? NEGATIVE_BNODE : UNHASHED_NEGATIVE_BNODE;
3408 
3409  /* --- look for a matching existing node --- */
3410  for (node=parent->first_child; node!=NIL; node=node->next_sibling)
3411  if ((node->node_type==node_type) &&
3412  (am == node->b.posneg.alpha_mem_) &&
3413  ((!hash_this_node) ||
3414  ((node->left_hash_loc_field_num==left_hash_loc.field_num) &&
3415  (node->left_hash_loc_levels_up==left_hash_loc.levels_up))) &&
3416  rete_test_lists_are_identical (thisAgent, node->b.posneg.other_tests, rt)) break;
3417 
3418  if (node) { /* --- A matching node was found --- */
3419  deallocate_rete_test_list (thisAgent, rt);
3420  remove_ref_to_alpha_mem (thisAgent, am);
3421  return node;
3422  } else { /* --- No match was found, so create a new node --- */
3423  node = make_new_negative_node (thisAgent, parent, node_type, left_hash_loc, am, rt);
3424  return node;
3425  }
3426 }
3427 
3428 /* ------------------------------------------------------------------------
3429  Build Network for Condition List
3430 
3431  This routine builds or shares the Rete network for the conditions in
3432  the given <cond_list>. <Depth_of_first_cond> tells the depth of the
3433  first condition/node; <parent> gives the parent node under which the
3434  network should be built or shared.
3435 
3436  Three "dest" parameters may be used for returing results from this
3437  routine. If <dest_bottom_node> is given as non-NIL, this routine
3438  fills it in with a pointer to the lowermost node in the resulting
3439  network. If <dest_bottom_depth> is non-NIL, this routine fills it
3440  in with the depth of the lowermost node. If <dest_vars_bound> is
3441  non_NIL, this routine fills it in with a list of variables bound
3442  in the given <cond_list>, and does not pop the bindings for those
3443  variables, in which case the caller is responsible for popping theose
3444  bindings. If <dest_vars_bound> is given as NIL, then this routine
3445  pops the bindings, and the caller does not have to do the cleanup.
3446 ------------------------------------------------------------------------ */
3447 
3449  condition *cond_list,
3450  rete_node_level depth_of_first_cond,
3451  rete_node *parent,
3452  rete_node **dest_bottom_node,
3453  rete_node_level *dest_bottom_depth,
3454  list **dest_vars_bound) {
3455  rete_node *node, *new_node, *child, *subconditions_bottom_node;
3456  condition *cond;
3457  rete_node_level current_depth;
3458  list *vars_bound;
3459 
3460  node = parent;
3461  current_depth = depth_of_first_cond;
3462  vars_bound = NIL;
3463 
3464  for (cond=cond_list; cond!=NIL; cond=cond->next) {
3465  switch (cond->type) {
3466 
3467  case POSITIVE_CONDITION:
3468  new_node = make_node_for_positive_cond (thisAgent, cond, current_depth, node);
3469  /* --- Add dense variable bindings for this condition --- */
3470  bind_variables_in_test (thisAgent, cond->data.tests.id_test, current_depth, 0,
3471  TRUE, &vars_bound);
3472  bind_variables_in_test (thisAgent, cond->data.tests.attr_test, current_depth, 1,
3473  TRUE, &vars_bound);
3474  bind_variables_in_test (thisAgent, cond->data.tests.value_test, current_depth, 2,
3475  TRUE, &vars_bound);
3476  break;
3477 
3478  case NEGATIVE_CONDITION:
3479  new_node = make_node_for_negative_cond (thisAgent, cond, current_depth, node);
3480  break;
3481 
3483  /* --- first, make the subconditions part of the rete --- */
3484  build_network_for_condition_list (thisAgent, cond->data.ncc.top, current_depth,
3485  node, &subconditions_bottom_node, NIL, NIL);
3486  /* --- look for an existing CN node --- */
3487  for (child=node->first_child; child!=NIL; child=child->next_sibling)
3488  if (child->node_type==CN_BNODE)
3489  if (child->b.cn.partner->parent==subconditions_bottom_node) break;
3490  /* --- share existing node or build new one --- */
3491  if (child) {
3492  new_node = child;
3493  } else {
3494  new_node = make_new_cn_node (thisAgent, node, subconditions_bottom_node);
3495  }
3496  break;
3497 
3498  default:
3499  new_node = NIL; /* unreachable, but without it gcc -Wall warns here */
3500  }
3501 
3502  node = new_node;
3503  current_depth++;
3504  }
3505 
3506  /* --- return results to caller --- */
3507  if (dest_bottom_node) *dest_bottom_node = node;
3508  if (dest_bottom_depth) *dest_bottom_depth = current_depth-1;
3509  if (dest_vars_bound) {
3510  *dest_vars_bound = vars_bound;
3511  } else {
3512  pop_bindings_and_deallocate_list_of_variables (thisAgent, vars_bound);
3513  }
3514 }
3515 
3516 
3517 
3518 
3519 
3520 
3521 
3522 
3523 
3524 
3525 
3526 
3527 
3528 /* ************************************************************************
3529 
3530  SECTION 9: Production Addition and Excising
3531 
3532  EXTERNAL INTERFACE:
3533  Add_production_to_rete() adds a given production, with a given LHS,
3534  to the Rete. Excise_production_from_rete() removes a given production
3535  from the Rete.
3536 ************************************************************************ */
3537 
3538 /* ---------------------------------------------------------------------
3539  Same RHS
3540 
3541  Tests whether two RHS's (i.e., action lists) are the same (except
3542  for function calls). This is used for finding duplicate productions.
3543 --------------------------------------------------------------------- */
3544 
3545 Bool same_rhs (action *rhs1, action *rhs2, bool rl_chunk_stop) {
3546  action *a1, *a2;
3547 
3548  /* --- Scan through the two RHS's; make sure there's no function calls,
3549  and make sure the actions are all the same. --- */
3550  /* --- Warning: this relies on the representation of rhs_value's:
3551  two of the same funcall will not be equal (==), but two of the
3552  same symbol, reteloc, or unboundvar will be equal (==). --- */
3553 
3554  a1 = rhs1;
3555  a2 = rhs2;
3556 
3557  while (a1 && a2) {
3558  if (a1->type == FUNCALL_ACTION) return FALSE;
3559  if (a2->type == FUNCALL_ACTION) return FALSE;
3560  if (a1->preference_type != a2->preference_type) return FALSE;
3561  if (a1->id != a2->id) return FALSE;
3562  if (a1->attr != a2->attr) return FALSE;
3563  if (a1->value != a2->value) return FALSE;
3565  if (a1->referent != a2->referent)
3566  {
3567  bool stop=true;
3568  if (rl_chunk_stop)
3569  {
3571  {
3572  Symbol* a1r = rhs_value_to_symbol(a1->referent);
3573  Symbol* a2r = rhs_value_to_symbol(a2->referent);
3574 
3575  if (((a1r->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) || (a1r->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE)) &&
3576  ((a2r->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) || (a2r->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE)))
3577  {
3578  if (((a1==rhs1) && (!a1->next)) && ((a2==rhs2) && (!a2->next)))
3579  {
3580  stop=false;
3581  }
3582  }
3583  }
3584  }
3585  if (stop) return FALSE;
3586  }
3587  a1 = a1->next;
3588  a2 = a2->next;
3589  }
3590 
3591  /* --- If we reached the end of one RHS but not the other, then
3592  they must be different --- */
3593  if (a1 != a2) return FALSE;
3594 
3595  /* --- If we got this far, the RHS's must be identical. --- */
3596  return TRUE;
3597 }
3598 
3599 /* ---------------------------------------------------------------------
3600  Fixup RHS-Value Variable References
3601 
3602  After we've built the network for a production, we go through its
3603  RHS and replace all the variables with reteloc's and unboundvar indices.
3604  For each variable <v> on the RHS, if <v> is bound on the LHS, then
3605  we replace RHS references to it with a specification of where its
3606  LHS binding can be found, e.g., "the value field four levels up".
3607  Each RHS variable <v> not bound on the LHS is replaced with an index,
3608  e.g., "unbound varible number 6". As we're doing this, we keep track
3609  of the names of all the unbound variables.
3610 
3611  When this routine is called, variables should be bound (densely) for
3612  the entire LHS.
3613 --------------------------------------------------------------------- */
3614 
3615 
3617  rete_node_level bottom_depth,
3618  list * & rhs_unbound_vars_for_new_prod,
3619  uint64_t & num_rhs_unbound_vars_for_new_prod,
3620  tc_number rhs_unbound_vars_tc)
3621 {
3622  cons *c;
3623  Symbol *sym;
3624  var_location var_loc;
3625  var_loc.var_location_struct::levels_up = 0;
3626  var_loc.var_location_struct::field_num = 0;
3627  uint64_t index;
3628 
3629  if (rhs_value_is_symbol(*rv)) {
3630  sym = rhs_value_to_symbol (*rv);
3631  if (sym->common.symbol_type!=VARIABLE_SYMBOL_TYPE) return;
3632  /* --- Found a variable. Is is bound on the LHS? --- */
3633  if (find_var_location (sym, static_cast<rete_node_level>(bottom_depth+1), &var_loc)) {
3634  /* --- Yes, replace it with reteloc --- */
3635  symbol_remove_ref (thisAgent, sym);
3636  *rv = reteloc_to_rhs_value (var_loc.field_num, var_loc.levels_up-1);
3637  } else {
3638  /* --- No, replace it with rhs_unboundvar --- */
3639  if (sym->var.tc_num != rhs_unbound_vars_tc) {
3640  symbol_add_ref (sym);
3641  push(thisAgent, sym, rhs_unbound_vars_for_new_prod);
3642  sym->var.tc_num = rhs_unbound_vars_tc;
3643  index = num_rhs_unbound_vars_for_new_prod++;
3644  sym->var.current_binding_value = reinterpret_cast<Symbol *>(index);
3645  } else {
3646  index = reinterpret_cast<uint64_t>(sym->var.current_binding_value);
3647  }
3648  *rv = unboundvar_to_rhs_value (index);
3649  symbol_remove_ref (thisAgent, sym);
3650  }
3651  return;
3652  }
3653 
3654  if (rhs_value_is_funcall(*rv)) {
3655  for (c=rhs_value_to_funcall_list(*rv)->rest; c!=NIL; c=c->rest)
3656  fixup_rhs_value_variable_references (thisAgent, reinterpret_cast<rhs_value *>(&(c->first)),
3657  bottom_depth, rhs_unbound_vars_for_new_prod,
3658  num_rhs_unbound_vars_for_new_prod,
3659  rhs_unbound_vars_tc);
3660  }
3661 }
3662 
3663 /* ---------------------------------------------------------------------
3664  Update Max RHS Unbound Variables
3665 
3666  When a production is fired, we use an array of gensyms to store
3667  the bindings for the RHS unbound variables. We have to grow the
3668  memory block allocated for this array any time a production comes
3669  along with more RHS unbound variables than we've ever seen before.
3670  This procedure checks the number of RHS unbound variables for a new
3671  production, and grows the array if necessary.
3672 --------------------------------------------------------------------- */
3673 
3674 void update_max_rhs_unbound_variables (agent* thisAgent, uint64_t num_for_new_production) {
3675  if (num_for_new_production > thisAgent->max_rhs_unbound_variables) {
3677  thisAgent->max_rhs_unbound_variables = num_for_new_production;
3678  thisAgent->rhs_variable_bindings = (Symbol **)
3680  sizeof(Symbol *), MISCELLANEOUS_MEM_USAGE);
3681  }
3682 }
3683 
3684 /* ---------------------------------------------------------------------
3685  Add Production to Rete
3686 
3687  Add_production_to_rete() adds a given production, with a given LHS,
3688  to the rete. If "refracted_inst" is non-NIL, it should point to an
3689  initial instantiation of the production. This routine returns
3690  DUPLICATE_PRODUCTION if the production was a duplicate; else
3691  NO_REFRACTED_INST if no refracted inst. was given; else either
3692  REFRACTED_INST_MATCHED or REFRACTED_INST_DID_NOT_MATCH.
3693 
3694  The initial refracted instantiation is provided so the initial
3695  instantiation of a newly-build chunk doesn't get fired. We handle
3696  this as follows. We store the initial instantiation as a "tentative
3697  retraction" on the new p-node. Then we inform the p-node of any
3698  matches (tokens from above). If any of them is the same as the
3699  refracted instantiation, then that instantiation will get removed
3700  from "tentative_retractions". When the p-node has been informed of
3701  all matches, we just check whether the instantiation is still on
3702  tentative_retractions. If not, there was a match (and the p-node's
3703  activation routine filled in the token info on the instantiation for
3704  us). If so, there was no match for the refracted instantiation.
3705 
3706  BUGBUG should we check for duplicate justifications?
3707 --------------------------------------------------------------------- */
3708 
3709 byte add_production_to_rete (agent* thisAgent, production *p, condition *lhs_top, instantiation *refracted_inst, Bool warn_on_duplicates, Bool ignore_rhs)
3710 {
3711  rete_node *bottom_node, *p_node;
3712  rete_node_level bottom_depth;
3713  list *vars_bound;
3714  ms_change *msc;
3715  action *a;
3716  byte production_addition_result;
3717 
3718  /* --- build the network for all the conditions --- */
3719  build_network_for_condition_list (thisAgent, lhs_top, 1, thisAgent->dummy_top_node,
3720  &bottom_node, &bottom_depth, &vars_bound);
3721 
3722  /* --- change variable names in RHS to Rete location references or
3723  unbound variable indices --- */
3724  list* rhs_unbound_vars_for_new_prod = NIL;
3725  uint64_t num_rhs_unbound_vars_for_new_prod = 0;
3726  tc_number rhs_unbound_vars_tc = get_new_tc_number(thisAgent);
3727  for (a=p->action_list; a!=NIL; a=a->next) {
3728  fixup_rhs_value_variable_references (thisAgent, &(a->value), bottom_depth,
3729  rhs_unbound_vars_for_new_prod, num_rhs_unbound_vars_for_new_prod, rhs_unbound_vars_tc);
3730  if (a->type==MAKE_ACTION) {
3731  fixup_rhs_value_variable_references (thisAgent, &(a->id), bottom_depth,
3732  rhs_unbound_vars_for_new_prod, num_rhs_unbound_vars_for_new_prod, rhs_unbound_vars_tc);
3733  fixup_rhs_value_variable_references (thisAgent, &(a->attr), bottom_depth,
3734  rhs_unbound_vars_for_new_prod, num_rhs_unbound_vars_for_new_prod, rhs_unbound_vars_tc);
3736  fixup_rhs_value_variable_references (thisAgent, &(a->referent), bottom_depth,
3737  rhs_unbound_vars_for_new_prod, num_rhs_unbound_vars_for_new_prod, rhs_unbound_vars_tc);
3738  }
3739  }
3740 
3741  /* --- clean up variable bindings created by build_network...() --- */
3742  pop_bindings_and_deallocate_list_of_variables (thisAgent, vars_bound);
3743 
3744  update_max_rhs_unbound_variables (thisAgent, num_rhs_unbound_vars_for_new_prod);
3745 
3746  /* --- look for an existing p node that matches --- */
3747  for (p_node=bottom_node->first_child; p_node!=NIL;
3748  p_node=p_node->next_sibling) {
3749  if (p_node->node_type != P_BNODE) continue;
3750  if ( !ignore_rhs && !same_rhs (p_node->b.p.prod->action_list, p->action_list, thisAgent->rl_params->chunk_stop->get_value()==soar_module::on)) continue;
3751  /* --- duplicate production found --- */
3752  if (warn_on_duplicates)
3753  {
3754  std::stringstream output;
3755  output << "\nIgnoring "
3756  << symbol_to_string( thisAgent, p->name, TRUE, 0, 0 )
3757  << " because it is a duplicate of "
3758  << symbol_to_string( thisAgent, p_node->b.p.prod->name, TRUE, 0, 0 )
3759  << " ";
3760  xml_generate_warning( thisAgent, output.str().c_str() );
3761 
3762  print_with_symbols (thisAgent, "\nIgnoring %y because it is a duplicate of %y ",
3763  p->name, p_node->b.p.prod->name);
3764  }
3765  deallocate_symbol_list_removing_references (thisAgent, rhs_unbound_vars_for_new_prod);
3766  return DUPLICATE_PRODUCTION;
3767  }
3768 
3769  /* --- build a new p node --- */
3770  p_node = make_new_production_node (thisAgent, bottom_node, p);
3772 
3773 
3774  /* KJC 1/28/98 left these comments in to support REW comments below
3775  but commented out the operand_mode code */
3776  /* RCHONG: begin 10.11 */
3777  /*
3778 
3779  in operand, we don't want to refract the instantiation. consider
3780  this situation: a PE chunk was created during the IE phase. that
3781  instantiation shouldn't be applied and we prevent this from
3782  happening (see chunk_instantiation() in chunk.c). we eventually get
3783  to the OUTPUT_PHASE, then the QUIESCENCE_PHASE. up to this point,
3784  the chunk hasn't done it's thing. we start the PE_PHASE. now, it
3785  is at this time that the just-built PE chunk should match and fire.
3786  if we were to refract the chunk, it wouldn't fire it at this point
3787  and it's actions would never occur. by not refracting it, we allow
3788  the chunk to match and fire.
3789 
3790  caveat: we must refract justifications, otherwise they would fire
3791  and in doing so would produce more chunks/justifications.
3792 
3793  if ((thisAgent->operand_mode == TRUE) && 1)
3794  if (refracted_inst != NIL) {
3795  if (refracted_inst->prod->type != JUSTIFICATION_PRODUCTION_TYPE)
3796  refracted_inst = NIL;
3797  }
3798  */
3799  /* RCHONG: end 10.11 */
3800 
3801  /* REW: begin 09.15.96 */
3802  /* In Operand2, for now, we want both chunks and justifications to be
3803  treated as refracted instantiations, at least for now. At some point,
3804  this issue needs to be re-visited for chunks that immediately match with
3805  a different instantiation and a different type of support than the
3806  original, chunk-creating instantion. */
3807  /* REW: end 09.15.96 */
3808 
3809 
3810  /* --- handle initial refraction by adding it to tentative_retractions --- */
3811  if (refracted_inst) {
3812  insert_at_head_of_dll (p->instantiations, refracted_inst, next, prev);
3813  refracted_inst->rete_token = NIL;
3814  refracted_inst->rete_wme = NIL;
3815  allocate_with_pool (thisAgent, &thisAgent->ms_change_pool, &msc);
3816  msc->inst = refracted_inst;
3817  msc->p_node = p_node;
3818  /* REW: begin 08.20.97 */
3819  /* Because the RETE 'artificially' refracts this instantiation (ie, it is
3820  not actually firing -- the original instantiation fires but not the
3821  chunk), we make the refracted instantiation of the chunk a nil_goal
3822  retraction, rather than associating it with the activity of its match
3823  goal. In p_node_left_addition, where the tentative assertion will be
3824  generated, we make it a point to look at the goal value and exrtac
3825  from the appropriate list; here we just make a a simplifying
3826  assumption that the goal is NIL (although, in reality), it never will
3827  be. */
3828 
3829  /* This initialization is necessary (for at least safety reasons, for all
3830  msc's, regardless of the mode */
3831  msc->level = 0;
3832  msc->goal = NIL;
3833 #ifdef DEBUG_WATERFALL
3834  print_with_symbols(thisAgent, "\n %y is a refracted instantiation",
3835  refracted_inst->prod->name);
3836 #endif
3837 
3839  msc, next_in_level, prev_in_level);
3840  /* REW: end 08.20.97 */
3841 
3842 #ifdef BUG_139_WORKAROUND
3843  msc->p_node->b.p.prod->already_fired = 0; /* RPM workaround for bug #139; mark prod as not fired yet */
3844 #endif
3845 
3846  insert_at_head_of_dll (thisAgent->ms_retractions, msc, next, prev);
3848  next_of_node, prev_of_node);
3849  }
3850 
3851  /* --- call new node's add_left routine with all the parent's tokens --- */
3852  update_node_with_matches_from_above (thisAgent, p_node);
3853 
3854  /* --- store result indicator --- */
3855  if (! refracted_inst) {
3856  production_addition_result = NO_REFRACTED_INST;
3857  } else {
3858  remove_from_dll (p->instantiations, refracted_inst, next, prev);
3859  if (p_node->b.p.tentative_retractions) {
3860  production_addition_result = REFRACTED_INST_DID_NOT_MATCH;
3861  msc = p_node->b.p.tentative_retractions;
3862  p_node->b.p.tentative_retractions = NIL;
3863  remove_from_dll (thisAgent->ms_retractions, msc, next, prev);
3864  /* REW: begin 10.03.97 */ /* BUGFIX 2.125 */
3865  if (msc->goal) {
3867  next_in_level, prev_in_level);
3868  } else {
3870  msc, next_in_level, prev_in_level);
3871  }
3872  /* REW: end 10.03.97 */
3873 
3874 
3875  free_with_pool (&thisAgent->ms_change_pool, msc);
3876 
3877  } else {
3878  production_addition_result = REFRACTED_INST_MATCHED;
3879  }
3880  }
3881 
3882  /* --- if not a chunk, store variable name information --- */
3884  p->p_node->b.p.parents_nvn = NIL;
3885  p->rhs_unbound_variables = NIL;
3886  deallocate_symbol_list_removing_references (thisAgent, rhs_unbound_vars_for_new_prod);
3887  } else {
3888  p->p_node->b.p.parents_nvn = get_nvn_for_condition_list (thisAgent, lhs_top, NIL);
3890  destructively_reverse_list (rhs_unbound_vars_for_new_prod);
3891  }
3892 
3893  /* --- invoke callback functions --- */
3894  soar_invoke_callbacks (thisAgent, PRODUCTION_JUST_ADDED_CALLBACK, static_cast<soar_call_data>(p));
3895 
3896  //#ifdef _WINDOWS
3897  // add_production_to_stat_lists(new_prod);
3898  //#endif
3899 
3900  return production_addition_result;
3901 }
3902 
3903 /* ---------------------------------------------------------------------
3904  Excise Production from Rete
3905 
3906  This removes a given production from the Rete net, and enqueues all
3907  its existing instantiations as pending retractions.
3908 --------------------------------------------------------------------- */
3909 
3911 {
3912  rete_node *p_node, *parent;
3913  ms_change *msc;
3914 
3915  soar_invoke_callbacks (thisAgent, PRODUCTION_JUST_ABOUT_TO_BE_EXCISED_CALLBACK, static_cast<soar_call_data>(p));
3916 
3917 //#ifdef _WINDOWS
3918 // remove_production_from_stat_lists(prod_to_be_excised);
3919 //#endif
3920 
3921  p_node = p->p_node;
3922  p->p_node = NIL; /* mark production as not being in the rete anymore */
3923  parent = p_node->parent;
3924 
3925  /* --- deallocate the variable name information --- */
3926  if (p_node->b.p.parents_nvn)
3927  deallocate_node_varnames (thisAgent, parent, thisAgent->dummy_top_node,
3928  p_node->b.p.parents_nvn);
3929 
3930  /* --- cause all existing instantiations to retract, by removing any
3931  tokens at the node --- */
3932  while (p_node->a.np.tokens) remove_token_and_subtree (thisAgent, p_node->a.np.tokens);
3933 
3934  /* --- At this point, there are no tentative_assertion's. Now set
3935  the p_node field of all tentative_retractions to NIL, to indicate
3936  that the p_node is being excised --- */
3937  for (msc=p_node->b.p.tentative_retractions; msc!=NIL; msc=msc->next_of_node)
3938  msc->p_node = NIL;
3939 
3940  /* --- finally, excise the p_node --- */
3942  update_stats_for_destroying_node (thisAgent, p_node); /* clean up rete stats stuff */
3943  free_with_pool (&thisAgent->rete_node_pool, p_node);
3944 
3945  /* --- update sharing factors on the path from here to the top node --- */
3947 
3948  /* --- and propogate up the net --- */
3949  if (! parent->first_child)
3950  deallocate_rete_node (thisAgent, parent);
3951 }
3952 
3953 
3954 
3955 
3956 
3957 
3958 
3959 
3960 
3961 
3962 
3963 
3964 
3965 /* **********************************************************************
3966 
3967  SECTION 10: Building Conditions (instantiated or not) from the Rete Net
3968 
3969  These routines are used for two things. First, when we want to print
3970  out the source code for a production, we need to reconstruct its
3971  conditions and actions. Second, when we fire a production, we need to
3972  build its instantiated conditions. (These are used for run-time
3973  o-support calculations and for backtracing.)
3974 
3975  Conceptually, we do this all top-down, by starting at the top Rete
3976  node and walking down to the p-node for the desired production.
3977  (The actual implementation starts at the p-node, of course, and
3978  walks its way up the net recursively.) As we work our way down, at
3979  each level:
3980  For instantiating a top-level positive condition:
3981  Just build a simple instantiated condition by looking at the
3982  WME it matched. Also record any "<>" tests.
3983  For instantiating anything else, or for rebuilding the LHS:
3984  Look at the Rete node and use it to figure out what the
3985  LHS condition looked like.
3986 
3987  EXTERNAL INTERFACE:
3988  P_node_to_conditions_and_nots() takes a p_node and (optionally) a
3989  token/wme pair, and reconstructs the (optionally instantiated) LHS
3990  for the production. It also reconstructs the RHS actions.
3991  Get_symbol_from_rete_loc() takes a token/wme pair and a location
3992  specification (levels_up/field_num), examines the match (token/wme),
3993  and returns the symbol at that location.
3994 ********************************************************************** */
3995 
3996 /* ----------------------------------------------------------------------
3997  Add Gensymmed Equality Test
3998 
3999  This routine destructively modifies a given test, adding to it a test
4000  for equality with a new gensym variable.
4001 ---------------------------------------------------------------------- */
4002 
4003 void add_gensymmed_equality_test (agent* thisAgent, test *t, char first_letter) {
4004  Symbol *New;
4005  test eq_test;
4006  char prefix[2];
4007 
4008  prefix[0] = first_letter;
4009  prefix[1] = 0;
4010  New = generate_new_variable (thisAgent, prefix);
4011  eq_test = make_equality_test (New);
4012  symbol_remove_ref (thisAgent, New);
4013  add_new_test_to_test (thisAgent, t, eq_test);
4014 }
4015 
4016 /* ----------------------------------------------------------------------
4017  Var Bound in Reconstructed Conds
4018 
4019  We're reconstructing the conditions for a production in top-down
4020  fashion. Suppose we come to a Rete test checking for equality with
4021  the "value" field 3 levels up. In that case, for the current condition,
4022  we want to include an equality test for whatever variable got bound
4023  in the value field 3 levels up. This function scans up the list
4024  of conditions reconstructed so far, and finds the appropriate variable.
4025 ---------------------------------------------------------------------- */
4026 
4028  condition *cond, /* current cond */
4029  byte where_field_num,
4030  rete_node_level where_levels_up) {
4031  test t;
4032  complex_test *ct;
4033  cons *c;
4034 
4035  while (where_levels_up) { where_levels_up--; cond = cond->prev; }
4036 
4037  if (where_field_num==0) t = cond->data.tests.id_test;
4038  else if (where_field_num==1) t = cond->data.tests.attr_test;
4039  else t = cond->data.tests.value_test;
4040 
4041  if (test_is_blank_test(t)) goto abort_var_bound_in_reconstructed_conds;
4043 
4044  ct = complex_test_from_test(t);
4045  if (ct->type==CONJUNCTIVE_TEST) {
4046  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
4047  if ( (! test_is_blank_test (static_cast<test>(c->first))) &&
4048  (test_is_blank_or_equality_test (static_cast<test>(c->first))) )
4049  return referent_of_equality_test (static_cast<test>(c->first));
4050  }
4051 
4052  abort_var_bound_in_reconstructed_conds:
4053  { char msg[BUFFER_MSG_SIZE];
4054  strncpy (msg, "Internal error in var_bound_in_reconstructed_conds\n", BUFFER_MSG_SIZE);
4055  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
4056  abort_with_fatal_error(thisAgent, msg);
4057  }
4058  return 0; /* unreachable, but without it, gcc -Wall warns here */
4059 }
4060 
4061 /* ----------------------------------------------------------------------
4062  Add Rete Test List to Tests
4063 
4064  Given the additional Rete tests (besides the hashed equality test) at
4065  a certain node, we need to convert them into the equivalent tests in
4066  the conditions being reconstructed. This procedure does this -- it
4067  destructively modifies the given currently-being-reconstructed-cond
4068  by adding any necessary extra tests to its three field tests.
4069 ---------------------------------------------------------------------- */
4070 
4072  condition *cond, /* current cond */
4073  rete_test *rt) {
4074  Symbol *referent;
4075  test New;
4076  complex_test *new_ct;
4077  byte test_type;
4078 
4079  for ( ; rt!=NIL; rt=rt->next) {
4080 
4081  if (rt->type==ID_IS_GOAL_RETE_TEST) {
4082  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &new_ct);
4083  New = make_test_from_complex_test(new_ct);
4084  new_ct->type = GOAL_ID_TEST;
4085  } else if (rt->type==ID_IS_IMPASSE_RETE_TEST) {
4086  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &new_ct);
4087  New = make_test_from_complex_test(new_ct);
4088  new_ct->type = IMPASSE_ID_TEST;
4089  } else if (rt->type==DISJUNCTION_RETE_TEST) {
4090  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &new_ct);
4091  New = make_test_from_complex_test(new_ct);
4092  new_ct->type = DISJUNCTION_TEST;
4093  new_ct->data.disjunction_list =
4095  } else if (test_is_constant_relational_test(rt->type)) {
4096  test_type =
4098  referent = rt->data.constant_referent;
4099  symbol_add_ref (referent);
4100  if (test_type==EQUAL_TEST_TYPE) {
4102  } else {
4103  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &new_ct);
4104  New = make_test_from_complex_test(new_ct);
4105  new_ct->type = test_type;
4106  new_ct->data.referent = referent;
4107  }
4108  } else if (test_is_variable_relational_test(rt->type)) {
4109  test_type =
4111  if (! rt->data.variable_referent.levels_up) {
4112  /* --- before calling var_bound_in_reconstructed_conds, make sure
4113  there's an equality test in the referent location (add one if
4114  there isn't one already there), otherwise there'd be no variable
4115  there to test against --- */
4116  if (rt->data.variable_referent.field_num==0) {
4118  (cond->data.tests.id_test, NIL))
4119  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.id_test), 's');
4120  } else if (rt->data.variable_referent.field_num==1) {
4122  (cond->data.tests.attr_test, NIL))
4123  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.attr_test), 'a');
4124  } else {
4126  (cond->data.tests.value_test, NIL))
4127  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.value_test),
4129  }
4130  }
4131  referent = var_bound_in_reconstructed_conds (thisAgent, cond,
4134  symbol_add_ref (referent);
4135  if (test_type==EQUAL_TEST_TYPE) {
4137  } else {
4138  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &new_ct);
4139  New = make_test_from_complex_test(new_ct);
4140  new_ct->type = test_type;
4141  new_ct->data.referent = referent;
4142  }
4143  } else {
4144  char msg[BUFFER_MSG_SIZE];
4145  strncpy (msg, "Error: bad test_type in add_rete_test_to_test\n", BUFFER_MSG_SIZE);
4146  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
4147  abort_with_fatal_error(thisAgent, msg);
4148  New = NIL; /* unreachable, but without it gcc -Wall warns here */
4149  }
4150 
4151  if (rt->right_field_num==0)
4152  add_new_test_to_test (thisAgent, &(cond->data.tests.id_test), New);
4153  else if (rt->right_field_num==2)
4154  add_new_test_to_test (thisAgent, &(cond->data.tests.value_test), New);
4155  else
4156  add_new_test_to_test (thisAgent, &(cond->data.tests.attr_test), New);
4157  }
4158 }
4159 
4160 /* ----------------------------------------------------------------------
4161  Collect Nots
4162 
4163  When we build the instantiated conditions for a production being
4164  fired, we also record all the "<>" tests between pairs of identifiers.
4165  (This information is used during chunking.) This procedure looks for
4166  any such <> tests in the given Rete test list (from the "other tests"
4167  at a Rete node), and adds records of them to the global variable
4168  nots_found_in_production. "Right_wme" is the wme that matched
4169  the current condition; "cond" is the currently-being-reconstructed
4170  condition.
4171 ---------------------------------------------------------------------- */
4172 
4173 //not_struct *nots_found_in_production; /* collected <> tests */
4174 
4175 void collect_nots (agent* thisAgent,
4176  rete_test *rt,
4177  wme *right_wme,
4178  condition *cond,
4179  not_struct * & nots_found_in_production) {
4180  not_struct *new_not;
4181  Symbol *right_sym;
4182  Symbol *referent;
4183 
4184  for ( ; rt!=NIL; rt=rt->next) {
4185 
4186  if (! test_is_not_equal_test(rt->type)) continue;
4187 
4188  right_sym = field_from_wme (right_wme, rt->right_field_num);
4189 
4190  if (right_sym->common.symbol_type != IDENTIFIER_SYMBOL_TYPE) continue;
4191 
4192  if (rt->type == CONSTANT_RELATIONAL_RETE_TEST +
4194  referent = rt->data.constant_referent;
4195  if (referent->common.symbol_type!=IDENTIFIER_SYMBOL_TYPE) continue;
4196  allocate_with_pool (thisAgent, &thisAgent->not_pool, &new_not);
4197  new_not->next = nots_found_in_production;
4198  nots_found_in_production = new_not;
4199  new_not->s1 = right_sym;
4200  symbol_add_ref (right_sym);
4201  new_not->s2 = referent;
4202  symbol_add_ref (referent);
4203  continue;
4204  }
4205 
4206  if (rt->type == VARIABLE_RELATIONAL_RETE_TEST +
4208  referent = var_bound_in_reconstructed_conds (thisAgent, cond,
4211  if (referent->common.symbol_type!=IDENTIFIER_SYMBOL_TYPE) continue;
4212  allocate_with_pool (thisAgent, &thisAgent->not_pool, &new_not);
4213  new_not->next = nots_found_in_production;
4214  nots_found_in_production = new_not;
4215  new_not->s1 = right_sym;
4216  symbol_add_ref (right_sym);
4217  new_not->s2 = referent;
4218  symbol_add_ref (referent);
4219  continue;
4220  }
4221  }
4222 }
4223 
4224 /* ----------------------------------------------------------------------
4225  Add Varnames to Test
4226 
4227  This routine adds (an equality test for) each variable in "vn" to
4228  the given test "t", destructively modifying t. This is used for
4229  restoring the original variables to test in a hand-coded production
4230  when we reconstruct its conditions.
4231 ---------------------------------------------------------------------- */
4232 
4233 void add_varnames_to_test (agent* thisAgent, varnames *vn, test *t) {
4234  test New;
4235  cons *c;
4236 
4237  if (vn == NIL) return;
4238  if (varnames_is_one_var(vn)) {
4240  add_new_test_to_test (thisAgent, t, New);
4241  } else {
4242  for (c=varnames_to_var_list(vn); c!=NIL; c=c->rest) {
4243  New = make_equality_test (static_cast<Symbol *>(c->first));
4244  add_new_test_to_test (thisAgent, t, New);
4245  }
4246  }
4247 }
4248 
4249 /* ----------------------------------------------------------------------
4250  Add Hash Info to ID Test
4251 
4252  This routine adds an equality test to the id field test in a given
4253  condition, destructively modifying that id test. The equality test
4254  is the one appropriate for the given hash location (field_num/levels_up).
4255 ---------------------------------------------------------------------- */
4256 
4258  condition *cond,
4259  byte field_num,
4260  rete_node_level levels_up) {
4261  Symbol *temp;
4262  test New;
4263 
4264  temp = var_bound_in_reconstructed_conds (thisAgent, cond, field_num, levels_up);
4265  New = make_equality_test (temp);
4266  add_new_test_to_test (thisAgent, &(cond->data.tests.id_test), New);
4267 }
4268 
4269 /* ----------------------------------------------------------------------
4270  Rete Node To Conditions
4271 
4272  This is the main routine for reconstructing the LHS source code, and
4273  for building instantiated conditions when a production is fired.
4274  It builds the conditions corresponding to the given rete node ("node")
4275  and all its ancestors, up to the given "cutoff" node. The given
4276  node_varnames structure "nvn", if non-NIL, should be the node_varnames
4277  corresponding to "node". <tok,w> (if they are non-NIL) specifies the
4278  token/wme pair that emerged from "node" -- these are used only when
4279  firing, not when reconstructing. "conds_for_cutoff_and_up" should be
4280  the lowermost cond in the already-constructed chain of conditions
4281  for the "cutoff" node and higher. "Dest_top_cond" and "dest_bottom_cond"
4282  get filled in with the highest and lowest conditions built by this
4283  procedure.
4284 ---------------------------------------------------------------------- */
4285 
4286 /* NOTE: clean this procedure up somehow? */
4287 
4288 void rete_node_to_conditions (agent* thisAgent,
4289  rete_node *node,
4290  node_varnames *nvn,
4291  rete_node *cutoff,
4292  token *tok,
4293  wme *w,
4294  condition *conds_for_cutoff_and_up,
4295  condition **dest_top_cond,
4296  condition **dest_bottom_cond,
4297  not_struct * & nots_found_in_production) {
4298  condition *cond;
4299  alpha_mem *am;
4300 
4301  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &cond);
4302  if (real_parent_node(node)==cutoff) {
4303  cond->prev = conds_for_cutoff_and_up; /* if this is the top of an NCC, this
4304  will get replaced by NIL later */
4305  *dest_top_cond = cond;
4306  } else {
4307  rete_node_to_conditions (thisAgent, real_parent_node(node),
4308  nvn ? nvn->parent : NIL,
4309  cutoff,
4310  tok ? tok->parent : NIL,
4311  tok ? tok->w : NIL,
4312  conds_for_cutoff_and_up,
4313  dest_top_cond, &(cond->prev),
4314  nots_found_in_production);
4315  cond->prev->next = cond;
4316  }
4317  cond->next = NIL;
4318  *dest_bottom_cond = cond;
4319 
4320  if (node->node_type==CN_BNODE) {
4322  rete_node_to_conditions (thisAgent, node->b.cn.partner->parent,
4323  nvn ? nvn->data.bottom_of_subconditions : NIL,
4324  node->parent,
4325  NIL,
4326  NIL,
4327  cond->prev,
4328  &(cond->data.ncc.top),
4329  &(cond->data.ncc.bottom),
4330  nots_found_in_production);
4331  cond->data.ncc.top->prev = NIL;
4332  } else {
4333  if (bnode_is_positive(node->node_type))
4334  cond->type = POSITIVE_CONDITION;
4335  else
4336  cond->type = NEGATIVE_CONDITION;
4337 
4338  if (w && (cond->type==POSITIVE_CONDITION)) {
4339  /* --- make simple tests and collect nots --- */
4340  cond->data.tests.id_test = make_equality_test (w->id);
4344  cond->bt.wme_ = w;
4345  if (node->b.posneg.other_tests) /* don't bother if there are no tests*/
4346  collect_nots (thisAgent, node->b.posneg.other_tests, w, cond,
4347  nots_found_in_production);
4348  } else {
4349  am = node->b.posneg.alpha_mem_;
4354 
4355  if (nvn) {
4356  add_varnames_to_test (thisAgent, nvn->data.fields.id_varnames,
4357  &(cond->data.tests.id_test));
4358  add_varnames_to_test (thisAgent, nvn->data.fields.attr_varnames,
4359  &(cond->data.tests.attr_test));
4361  &(cond->data.tests.value_test));
4362  }
4363 
4364  /* --- on hashed nodes, add equality test for the hash function --- */
4365  if ((node->node_type==MP_BNODE) || (node->node_type==NEGATIVE_BNODE)) {
4366  add_hash_info_to_id_test (thisAgent, cond,
4368  node->left_hash_loc_levels_up);
4369  } else if (node->node_type==POSITIVE_BNODE) {
4370  add_hash_info_to_id_test (thisAgent, cond,
4373  }
4374 
4375  /* --- if there are other tests, add them too --- */
4376  if (node->b.posneg.other_tests)
4377  add_rete_test_list_to_tests (thisAgent, cond, node->b.posneg.other_tests);
4378 
4379  /* --- if we threw away the variable names, make sure there's some
4380  equality test in each of the three fields --- */
4381  if (! nvn) {
4383  (cond->data.tests.id_test, NIL))
4384  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.id_test), 's');
4386  (cond->data.tests.attr_test, NIL))
4387  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.attr_test), 'a');
4389  (cond->data.tests.value_test, NIL))
4390  add_gensymmed_equality_test (thisAgent, &(cond->data.tests.value_test),
4392  }
4393  }
4394  }
4395 }
4396 
4397 /* -------------------------------------------------------------------
4398  Reconstructing the RHS Actions of a Production
4399 
4400  When we print a production (but not when we fire one), we have to
4401  reconstruct the RHS actions. This is because many of the variables
4402  in the RHS have been replaced by references to Rete locations (i.e.,
4403  rather than specifying <v>, we specify "value field 3 levels up"
4404  or "the 7th RHS unbound variable". The routines below copy rhs_value's
4405  and actions, and substitute variable names for such references.
4406  For RHS unbound variables, we gensym new variable names.
4407 ------------------------------------------------------------------- */
4408 
4410  rhs_value rv,
4411  condition *cond,
4412  char first_letter) {
4413  cons *c, *new_c, *prev_new_c;
4414  list *fl, *new_fl;
4415  Symbol *sym;
4416  int64_t index;
4417  char prefix[2];
4418 
4419  if (rhs_value_is_reteloc(rv)) {
4420  sym = var_bound_in_reconstructed_conds (thisAgent, cond,
4423  symbol_add_ref (sym);
4424  return symbol_to_rhs_value (sym);
4425  }
4426 
4427  if (rhs_value_is_unboundvar(rv))
4428  {
4429  index = static_cast<int64_t>(rhs_value_to_unboundvar(rv));
4430  if (! *(thisAgent->rhs_variable_bindings+index))
4431  {
4432  prefix[0] = first_letter;
4433  prefix[1] = 0;
4434 
4435  sym = generate_new_variable (thisAgent, prefix);
4436  *(thisAgent->rhs_variable_bindings+index) = sym;
4437 
4438  if (thisAgent->highest_rhs_unboundvar_index < index)
4439  {
4440  thisAgent->highest_rhs_unboundvar_index = index;
4441  }
4442  }
4443  else
4444  {
4445  sym = *(thisAgent->rhs_variable_bindings+index);
4446  symbol_add_ref (sym);
4447  }
4448  return symbol_to_rhs_value (sym);
4449  }
4450 
4451  if (rhs_value_is_funcall(rv)) {
4452  fl = rhs_value_to_funcall_list(rv);
4453  allocate_cons (thisAgent, &new_fl);
4454  new_fl->first = fl->first;
4455  prev_new_c = new_fl;
4456  for (c=fl->rest; c!=NIL; c=c->rest) {
4457  allocate_cons (thisAgent, &new_c);
4458  new_c->first = copy_rhs_value_and_substitute_varnames (thisAgent,
4459  static_cast<char *>(c->first),
4460  cond, first_letter);
4461  prev_new_c->rest = new_c;
4462  prev_new_c = new_c;
4463  }
4464  prev_new_c->rest = NIL;
4465  return funcall_list_to_rhs_value (new_fl);
4466  } else {
4468  return rv;
4469  }
4470 }
4471 
4473  action *actions,
4474  condition *cond) {
4475  action *old, *New, *prev, *first;
4476  char first_letter;
4477 
4478  prev = NIL;
4479  first = NIL; /* unneeded, but without it gcc -Wall warns here */
4480  old = actions;
4481  while (old) {
4482  allocate_with_pool (thisAgent, &thisAgent->action_pool, &New);
4483  if (prev) prev->next = New; else first = New;
4484  prev = New;
4485  New->type = old->type;
4486  New->preference_type = old->preference_type;
4487  New->support = old->support;
4488  if (old->type==FUNCALL_ACTION) {
4489  New->value = copy_rhs_value_and_substitute_varnames (thisAgent,
4490  old->value, cond,
4491  'v');
4492  } else {
4493  New->id = copy_rhs_value_and_substitute_varnames (thisAgent, old->id, cond, 's');
4494  New->attr = copy_rhs_value_and_substitute_varnames (thisAgent, old->attr, cond,'a');
4495  first_letter = first_letter_from_rhs_value (New->attr);
4496  New->value = copy_rhs_value_and_substitute_varnames (thisAgent, old->value, cond,
4497  first_letter);
4500  cond, first_letter);
4501  }
4502  old = old->next;
4503  }
4504  if (prev) prev->next = NIL; else first = NIL;
4505  return first;
4506 }
4507 
4508 /* -----------------------------------------------------------------------
4509  P Node to Conditions and Nots
4510  Get Symbol From Rete Loc
4511 
4512  P_node_to_conditions_and_nots() takes a p_node and (optionally) a
4513  token/wme pair, and reconstructs the (optionally instantiated) LHS
4514  for the production. If "dest_rhs" is non-NIL, it also reconstructs
4515  the RHS actions, and fills in dest_rhs with the action list.
4516  Note: if tok!=NIL, this routine also returns (in dest_nots) the
4517  top-level positive "<>" tests. If tok==NIL, dest_nots is not used.
4518 
4519  Get_symbol_from_rete_loc() takes a token/wme pair and a location
4520  specification (levels_up/field_num), examines the match (token/wme),
4521  and returns the symbol at that location. The firer uses this for
4522  resolving references in RHS actions to variables bound on the LHS.
4523 ----------------------------------------------------------------------- */
4524 
4526  rete_node *p_node,
4527  token *tok,
4528  wme *w,
4529  condition **dest_top_cond,
4530  condition **dest_bottom_cond,
4531  not_struct **dest_nots,
4532  action **dest_rhs) {
4533  cons *c;
4534  Symbol **cell;
4535  int64_t index;
4536  production *prod;
4537 
4538  prod = p_node->b.p.prod;
4539 
4540  not_struct *nots_found_in_production = NIL;
4541  if (tok==NIL) w=NIL; /* just for safety */
4542  reset_variable_generator (thisAgent, NIL, NIL); /* we'll be gensymming new vars */
4543  rete_node_to_conditions (thisAgent,
4544  p_node->parent,
4545  p_node->b.p.parents_nvn,
4546  thisAgent->dummy_top_node,
4547  tok, w, NIL,
4548  dest_top_cond, dest_bottom_cond,
4549  nots_found_in_production);
4550  if (tok) *dest_nots = nots_found_in_production;
4551  nots_found_in_production = NIL; /* just for safety */
4552  if (dest_rhs)
4553  {
4554  thisAgent->highest_rhs_unboundvar_index = -1;
4555  if (prod->rhs_unbound_variables)
4556  {
4557  cell = thisAgent->rhs_variable_bindings;
4558  for (c=prod->rhs_unbound_variables; c!=NIL; c=c->rest)
4559  {
4560  *(cell++) = static_cast<symbol_union *>(c->first);
4561  thisAgent->highest_rhs_unboundvar_index++;
4562  }
4563  }
4564  *dest_rhs = copy_action_list_and_substitute_varnames (thisAgent,
4565  prod->action_list,
4566  *dest_bottom_cond);
4567  index = 0;
4568  cell = thisAgent->rhs_variable_bindings;
4569  while (index++ <= thisAgent->highest_rhs_unboundvar_index) *(cell++) = NIL;
4570  }
4571 }
4572 
4573 Symbol *get_symbol_from_rete_loc (unsigned short levels_up,
4574  byte field_num,
4575  token *tok, wme *w) {
4576  while (levels_up) {
4577  levels_up--;
4578  w = tok->w;
4579  tok = tok->parent;
4580  }
4581  if (field_num==0) return w->id;
4582  if (field_num==1) return w->attr;
4583  return w->value;
4584 }
4585 
4586 
4587 /* **********************************************************************
4588 
4589  SECTION 11: Rete Test Evaluation Routines
4590 
4591  These routines perform the "other tests" stored at positive and
4592  negative join nodes. Each is passed parameters: the rete_test
4593  to be performed, and the <token,wme> pair on which to perform the
4594  test.
4595 ********************************************************************** */
4596 
4597 Bool error_rete_test_routine (agent* thisAgent, rete_test *rt, token *left, wme *w);
4598 #define ertr error_rete_test_routine
4599 Bool ( (*(rete_test_routines[256]))
4600  (agent* thisAgent, rete_test *rt, token *left, wme *w)) =
4601 {
4617  ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr, ertr
4618 };
4619 
4620 /*#define match_left_and_right(rete_test,left,w) \
4621  ( (*(rete_test_routines[(rete_test)->type])) \
4622  ((rete_test),(left),(w)) )*/
4623 inline Bool match_left_and_right(agent* thisAgent, rete_test * _rete_test,
4624  token * left, wme * w)
4625 {
4626  return ( (*(rete_test_routines[(_rete_test)->type])) \
4627  (thisAgent,(_rete_test),(left),(w)) );
4628 }
4629 
4630 /* This macro cannot be easily converted to an inline function.
4631  Some additional changes are required.
4632 */
4633 /*
4634 #define numeric_comparison_between_symbols(s1,s2,comparator_op) ( \
4635  ( ((s1)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) && \
4636  ((s2)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) ) ? \
4637  (((s1)->ic.value) comparator_op ((s2)->ic.value)) : \
4638  ( ((s1)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) && \
4639  ((s2)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) ) ? \
4640  (((s1)->ic.value) comparator_op ((s2)->fc.value)) : \
4641  ( ((s1)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) && \
4642  ((s2)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) ) ? \
4643  (((s1)->fc.value) comparator_op ((s2)->ic.value)) : \
4644  ( ((s1)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) && \
4645  ((s2)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) ) ? \
4646  (((s1)->fc.value) comparator_op ((s2)->fc.value)) : \
4647  FALSE )
4648 */
4649 
4650 /* Note: "=" and "<>" tests always return FALSE when one argument is
4651  an integer and the other is a floating point number */
4652 
4653 #define numcmp(x,y) (((x) < (y)) ? -1 : (((x) > (y)) ? 1 : 0))
4654 
4655 /* return -1, 0, or 1 if s1 is less than, equal to, or greater than s2,
4656  * respectively
4657  */
4658 inline int64_t compare_symbols(Symbol* s1, Symbol* s2) {
4659  switch (s1->common.symbol_type) {
4661  switch (s2->common.symbol_type) {
4663  return s1->ic.value - s2->ic.value;
4665  return numcmp(s1->ic.value, s2->fc.value);
4666  default:
4667  return -1;
4668  }
4670  switch (s2->common.symbol_type) {
4672  return numcmp(s1->fc.value, s2->ic.value);
4674  return numcmp(s1->fc.value, s2->fc.value);
4675  default:
4676  return -1;
4677  }
4679  switch (s2->common.symbol_type) {
4682  return 1;
4684  return strcmp(s1->sc.name, s2->sc.name);
4685  default:
4686  return -1;
4687  }
4689  switch (s2->common.symbol_type) {
4693  return 1;
4695  if (s1->id.name_letter == s2->id.name_letter)
4696  return static_cast<int64_t>(s1->id.name_number - s2->id.name_number);
4697  else
4698  return numcmp(s1->id.name_letter, s2->id.name_letter);
4699  default:
4700  return -1;
4701  }
4702  default:
4703  return -1;
4704  }
4705 }
4706 
4707 Bool error_rete_test_routine (agent* thisAgent, rete_test * /*rt*/, token * /*left*/, wme * /*w*/) {
4708  char msg[BUFFER_MSG_SIZE];
4709  strncpy (msg, "Internal error: bad rete test type, hit error_rete_test_routine\n", BUFFER_MSG_SIZE);
4710  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
4711  abort_with_fatal_error(thisAgent, msg);
4712  return FALSE; /* unreachable, but without it, gcc -Wall warns here */
4713 }
4714 
4715 Bool id_is_goal_rete_test_routine (agent* /*thisAgent*/, rete_test * /*rt*/, token * /*left*/, wme *w) {
4716  return w->id->id.isa_goal;
4717 }
4718 
4719 Bool id_is_impasse_rete_test_routine (agent* /*thisAgent*/, rete_test * /*rt*/, token * /*left*/, wme *w) {
4720  return w->id->id.isa_impasse;
4721 }
4722 
4723 Bool disjunction_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token * /*left*/, wme *w) {
4724  Symbol *sym;
4725  cons *c;
4726 
4727  sym = field_from_wme (w,rt->right_field_num);
4728  for (c=rt->data.disjunction_list; c!=NIL; c=c->rest)
4729  if (c->first==sym) return TRUE;
4730  return FALSE;
4731 }
4732 
4733 Bool constant_equal_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token * /*left*/, wme *w) {
4734  Symbol *s1, *s2;
4735 
4736  s1 = field_from_wme (w,rt->right_field_num);
4737  s2 = rt->data.constant_referent;
4738  return (s1 == s2);
4739 }
4740 
4742  wme *w) {
4743  Symbol *s1, *s2;
4744 
4745  s1 = field_from_wme (w,rt->right_field_num);
4746  s2 = rt->data.constant_referent;
4747  return (s1 != s2);
4748 }
4749 
4750 Bool constant_less_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token * /*left*/, wme *w) {
4751  Symbol *s1, *s2;
4752 
4753  s1 = field_from_wme (w,rt->right_field_num);
4754  s2 = rt->data.constant_referent;
4755  return static_cast<Bool>(compare_symbols(s1, s2) < 0);
4756 }
4757 
4758 Bool constant_greater_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token * /*left*/, wme *w) {
4759  Symbol *s1, *s2;
4760 
4761  s1 = field_from_wme (w,rt->right_field_num);
4762  s2 = rt->data.constant_referent;
4763  return static_cast<Bool>(compare_symbols(s1, s2) > 0);
4764 }
4765 
4767  wme *w) {
4768  Symbol *s1, *s2;
4769 
4770  s1 = field_from_wme (w,rt->right_field_num);
4771  s2 = rt->data.constant_referent;
4772  return static_cast<Bool>(compare_symbols(s1, s2) <= 0);
4773 }
4774 
4776  wme *w) {
4777  Symbol *s1, *s2;
4778 
4779  s1 = field_from_wme (w,rt->right_field_num);
4780  s2 = rt->data.constant_referent;
4781  return static_cast<Bool>(compare_symbols(s1, s2) >= 0);
4782 }
4783 
4785  wme *w) {
4786  Symbol *s1, *s2;
4787 
4788  s1 = field_from_wme (w,rt->right_field_num);
4789  s2 = rt->data.constant_referent;
4790  return static_cast<Bool>(s1->common.symbol_type == s2->common.symbol_type);
4791 }
4792 
4793 Bool variable_equal_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token *left, wme *w) {
4794  Symbol *s1, *s2;
4795  int i;
4796 
4797  s1 = field_from_wme (w, rt->right_field_num);
4798 
4799  if (rt->data.variable_referent.levels_up!=0) {
4800  i = rt->data.variable_referent.levels_up - 1;
4801  while (i!=0) {
4802  left = left->parent;
4803  i--;
4804  }
4805  w = left->w;
4806  }
4808 
4809  return (s1 == s2);
4810 }
4811 
4813  wme *w) {
4814  Symbol *s1, *s2;
4815  int i;
4816 
4817  s1 = field_from_wme (w, rt->right_field_num);
4818 
4819  if (rt->data.variable_referent.levels_up!=0) {
4820  i = rt->data.variable_referent.levels_up - 1;
4821  while (i!=0) {
4822  left = left->parent;
4823  i--;
4824  }
4825  w = left->w;
4826  }
4828 
4829  return (s1 != s2);
4830 }
4831 
4832 Bool variable_less_rete_test_routine (agent* /*thisAgent*/, rete_test *rt, token *left, wme *w) {
4833  Symbol *s1, *s2;
4834  int i;
4835 
4836  s1 = field_from_wme (w, rt->right_field_num);
4837 
4838  if (rt->data.variable_referent.levels_up!=0) {
4839  i = rt->data.variable_referent.levels_up - 1;
4840  while (i!=0) {
4841  left = left->parent;
4842  i--;
4843  }
4844  w = left->w;
4845  }
4847 
4848  return static_cast<Bool>(compare_symbols(s1, s2) < 0);
4849 }
4850 
4852  Symbol *s1, *s2;
4853  int i;
4854 
4855  s1 = field_from_wme (w, rt->right_field_num);
4856 
4857  if (rt->data.variable_referent.levels_up!=0) {
4858  i = rt->data.variable_referent.levels_up - 1;
4859  while (i!=0) {
4860  left = left->parent;
4861  i--;
4862  }
4863  w = left->w;
4864  }
4866 
4867  return static_cast<Bool>(compare_symbols(s1, s2) > 0);
4868 }
4869 
4871  wme *w) {
4872  Symbol *s1, *s2;
4873  int i;
4874 
4875  s1 = field_from_wme (w, rt->right_field_num);
4876 
4877  if (rt->data.variable_referent.levels_up!=0) {
4878  i = rt->data.variable_referent.levels_up - 1;
4879  while (i!=0) {
4880  left = left->parent;
4881  i--;
4882  }
4883  w = left->w;
4884  }
4886 
4887  return static_cast<Bool>(compare_symbols(s1, s2) <= 0);
4888 }
4889 
4891  wme *w) {
4892  Symbol *s1, *s2;
4893  int i;
4894 
4895  s1 = field_from_wme (w, rt->right_field_num);
4896 
4897  if (rt->data.variable_referent.levels_up!=0) {
4898  i = rt->data.variable_referent.levels_up - 1;
4899  while (i!=0) {
4900  left = left->parent;
4901  i--;
4902  }
4903  w = left->w;
4904  }
4906 
4907  return static_cast<Bool>(compare_symbols(s1, s2) >= 0);
4908 }
4909 
4911  wme *w) {
4912  Symbol *s1, *s2;
4913  int i;
4914 
4915  s1 = field_from_wme (w, rt->right_field_num);
4916 
4917  if (rt->data.variable_referent.levels_up!=0) {
4918  i = rt->data.variable_referent.levels_up - 1;
4919  while (i!=0) {
4920  left = left->parent;
4921  i--;
4922  }
4923  w = left->w;
4924  }
4926  return (s1->common.symbol_type == s2->common.symbol_type);
4927 }
4928 
4929 
4930 
4931 /* ************************************************************************
4932 
4933  SECTION 12: Beta Node Interpreter Routines: Mem, Pos, and MP Nodes
4934 
4935 ************************************************************************ */
4936 
4937 void positive_node_left_addition (agent* thisAgent, rete_node *node, token *New,
4938  Symbol *hash_referent);
4939 void unhashed_positive_node_left_addition (agent* thisAgent, rete_node *node, token *New);
4940 
4941 void rete_error_left (agent* thisAgent, rete_node *node, token * /*t*/, wme * /*w*/) {
4942  char msg[BUFFER_MSG_SIZE];
4943  SNPRINTF (msg, BUFFER_MSG_SIZE, "Rete net error: tried to left-activate node of type %d\n",
4944  node->node_type);
4945  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
4946  abort_with_fatal_error(thisAgent, msg);
4947 }
4948 
4949 void rete_error_right (agent* thisAgent, rete_node *node, wme * /*w*/) {
4950  char msg[BUFFER_MSG_SIZE];
4951  SNPRINTF (msg, BUFFER_MSG_SIZE, "Rete net error: tried to right-activate node of type %d\n",
4952  node->node_type);
4953  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
4954  abort_with_fatal_error(thisAgent, msg);
4955 }
4956 
4958  token *tok, wme *w) {
4959  uint32_t hv;
4960  Symbol *referent;
4961  rete_node *child, *next;
4962  token *New;
4963 
4965  left_node_activation(node,TRUE);
4966 
4967  {
4968  int levels_up;
4969  token *t;
4970 
4971  levels_up = node->left_hash_loc_levels_up;
4972  if (levels_up==1) {
4973  referent = field_from_wme (w, node->left_hash_loc_field_num);
4974  } else { /* --- levels_up > 1 --- */
4975  for (t=tok, levels_up -= 2; levels_up!=0; levels_up--) t=t->parent;
4976  referent = field_from_wme (t->w, node->left_hash_loc_field_num);
4977  }
4978  }
4979 
4980  hv = node->node_id ^ referent->common.hash_id;
4981 
4982  /* --- build new left token, add it to the hash table --- */
4983  token_added(node);
4984  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
4985  new_left_token (New, node, tok, w);
4986  insert_token_into_left_ht (thisAgent, New, hv);
4987  New->a.ht.referent = referent;
4988 
4989  /* --- inform each linked child (positive join) node --- */
4990  for (child=node->b.mem.first_linked_child; child!=NIL; child=next) {
4991  next = child->a.pos.next_from_beta_mem;
4992  positive_node_left_addition (thisAgent, child, New, referent);
4993  }
4995 }
4996 
4998  rete_node *node, token *tok,
4999  wme *w) {
5000  uint32_t hv;
5001  rete_node *child, *next;
5002  token *New;
5003 
5005  left_node_activation(node,TRUE);
5006 
5007  hv = node->node_id;
5008 
5009  /* --- build new left token, add it to the hash table --- */
5010  token_added(node);
5011  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5012  new_left_token (New, node, tok, w);
5013  insert_token_into_left_ht (thisAgent, New, hv);
5014  New->a.ht.referent = NIL;
5015 
5016  /* --- inform each linked child (positive join) node --- */
5017  for (child=node->b.mem.first_linked_child; child!=NIL; child=next) {
5018  next = child->a.pos.next_from_beta_mem;
5019  unhashed_positive_node_left_addition (thisAgent, child, New);
5020  }
5022 }
5023 
5025  rete_node *node, token *New,
5026  Symbol *hash_referent) {
5027  uint32_t right_hv;
5028  right_mem *rm;
5029  alpha_mem *am;
5030  rete_test *rt;
5031  Bool failed_a_test;
5032  rete_node *child;
5033 
5035  left_node_activation(node,TRUE);
5036 
5037  am = node->b.posneg.alpha_mem_;
5038 
5039  if (node_is_right_unlinked(node)) {
5040  relink_to_right_mem (node);
5041  if (am->right_mems==NIL) {
5042  unlink_from_left_mem (node);
5044  return;
5045  }
5046  }
5047 
5048  /* --- look through right memory for matches --- */
5049  right_hv = am->am_id ^ hash_referent->common.hash_id;
5050  for (rm=right_ht_bucket(thisAgent, right_hv); rm!=NIL; rm=rm->next_in_bucket) {
5051  if (rm->am != am) continue;
5052  /* --- does rm->w match New? --- */
5053  if (hash_referent != rm->w->id) continue;
5054  failed_a_test = FALSE;
5055  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5056  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5057  failed_a_test = TRUE;
5058  break;
5059  }
5060  if (failed_a_test) continue;
5061  /* --- match found, so call each child node --- */
5062  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5063  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,rm->w);
5064  }
5066 }
5067 
5069  right_mem *rm;
5070  rete_test *rt;
5071  Bool failed_a_test;
5072  rete_node *child;
5073 
5075  left_node_activation(node,TRUE);
5076 
5077  if (node_is_right_unlinked(node)) {
5078  relink_to_right_mem (node);
5079  if (node->b.posneg.alpha_mem_->right_mems==NIL) {
5080  unlink_from_left_mem (node);
5082  return;
5083  }
5084  }
5085 
5086  /* --- look through right memory for matches --- */
5087  for (rm=node->b.posneg.alpha_mem_->right_mems; rm!=NIL;
5088 rm=rm->next_in_am) {
5089  /* --- does rm->w match new? --- */
5090  failed_a_test = FALSE;
5091  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5092  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5093  failed_a_test = TRUE;
5094  break;
5095  }
5096  if (failed_a_test) continue;
5097  /* --- match found, so call each child node --- */
5098  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5099  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,rm->w);
5100  }
5102 }
5103 
5104 void mp_node_left_addition (agent* thisAgent, rete_node *node, token *tok, wme *w) {
5105  uint32_t hv;
5106  Symbol *referent;
5107  rete_node *child;
5108  token *New;
5109  uint32_t right_hv;
5110  right_mem *rm;
5111  alpha_mem *am;
5112  rete_test *rt;
5113  Bool failed_a_test;
5114 
5116  left_node_activation(node,TRUE);
5117 
5118  {
5119  int levels_up;
5120  token *t;
5121 
5122  levels_up = node->left_hash_loc_levels_up;
5123  if (levels_up==1) {
5124  referent = field_from_wme (w, node->left_hash_loc_field_num);
5125  } else { /* --- levels_up > 1 --- */
5126  for (t=tok, levels_up -= 2; levels_up!=0; levels_up--) t=t->parent;
5127  referent = field_from_wme (t->w, node->left_hash_loc_field_num);
5128  }
5129  }
5130 
5131  hv = node->node_id ^ referent->common.hash_id;
5132 
5133  /* --- build new left token, add it to the hash table --- */
5134  token_added(node);
5135  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5136  new_left_token (New, node, tok, w);
5137  insert_token_into_left_ht (thisAgent, New, hv);
5138  New->a.ht.referent = referent;
5139 
5140  if (mp_bnode_is_left_unlinked(node)) {
5142  return;
5143  }
5144 
5145  am = node->b.posneg.alpha_mem_;
5146 
5147  if (node_is_right_unlinked(node)) {
5148  relink_to_right_mem (node);
5149  if (am->right_mems==NIL) {
5152  return;
5153  }
5154  }
5155 
5156  /* --- look through right memory for matches --- */
5157  right_hv = am->am_id ^ referent->common.hash_id;
5158  for (rm=right_ht_bucket(thisAgent, right_hv); rm!=NIL; rm=rm->next_in_bucket) {
5159  if (rm->am != am) continue;
5160  /* --- does rm->w match new? --- */
5161  if (referent != rm->w->id) continue;
5162  failed_a_test = FALSE;
5163  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5164  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5165  failed_a_test = TRUE;
5166  break;
5167  }
5168  if (failed_a_test) continue;
5169  /* --- match found, so call each child node --- */
5170  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5171  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,rm->w);
5172  }
5174 }
5175 
5176 
5178  token *tok, wme *w) {
5179  uint32_t hv;
5180  rete_node *child;
5181  token *New;
5182  right_mem *rm;
5183  rete_test *rt;
5184  Bool failed_a_test;
5185 
5187  left_node_activation(node,TRUE);
5188 
5189  hv = node->node_id;
5190 
5191  /* --- build new left token, add it to the hash table --- */
5192  token_added(node);
5193  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5194  new_left_token (New, node, tok, w);
5195  insert_token_into_left_ht (thisAgent, New, hv);
5196  New->a.ht.referent = NIL;
5197 
5198  if (mp_bnode_is_left_unlinked(node)) return;
5199 
5200  if (node_is_right_unlinked(node)) {
5201  relink_to_right_mem (node);
5202  if (node->b.posneg.alpha_mem_->right_mems==NIL) {
5205  return;
5206  }
5207  }
5208 
5209  /* --- look through right memory for matches --- */
5210  for (rm=node->b.posneg.alpha_mem_->right_mems; rm!=NIL;
5211 rm=rm->next_in_am) {
5212  /* --- does rm->w match new? --- */
5213  failed_a_test = FALSE;
5214  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5215  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5216  failed_a_test = TRUE;
5217  break;
5218  }
5219  if (failed_a_test) continue;
5220  /* --- match found, so call each child node --- */
5221  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5222  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,rm->w);
5223  }
5225 }
5226 
5227 void positive_node_right_addition (agent* thisAgent, rete_node *node, wme *w) {
5228  uint32_t hv;
5229  token *tok;
5230  Symbol *referent;
5231  rete_test *rt;
5232  Bool failed_a_test;
5233  rete_node *child;
5234 
5237 
5238  if (node_is_left_unlinked(node)) {
5239  relink_to_left_mem (node);
5240  if (! node->parent->a.np.tokens) {
5241  unlink_from_right_mem (node);
5243  return;
5244  }
5245  }
5246 
5247  referent = w->id;
5248  hv = node->parent->node_id ^ referent->common.hash_id;
5249 
5250  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5251  if (tok->node != node->parent) continue;
5252  /* --- does tok match w? --- */
5253  if (tok->a.ht.referent != referent) continue;
5254  failed_a_test = FALSE;
5255  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5256  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5257  failed_a_test = TRUE;
5258  break;
5259  }
5260  if (failed_a_test) continue;
5261  /* --- match found, so call each child node --- */
5262  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5263  (*(left_addition_routines[child->node_type]))(thisAgent,child,tok,w);
5264  }
5266 }
5267 
5269  uint32_t hv;
5270  token *tok;
5271  rete_test *rt;
5272  Bool failed_a_test;
5273  rete_node *child;
5274 
5277 
5278  if (node_is_left_unlinked(node)) {
5279  relink_to_left_mem (node);
5280  if (! node->parent->a.np.tokens) {
5281  unlink_from_right_mem (node);
5283  return;
5284  }
5285  }
5286 
5287  hv = node->parent->node_id;
5288 
5289  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5290  if (tok->node != node->parent) continue;
5291  /* --- does tok match w? --- */
5292  failed_a_test = FALSE;
5293  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5294  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5295  failed_a_test = TRUE;
5296  break;
5297  }
5298  if (failed_a_test) continue;
5299  /* --- match found, so call each child node --- */
5300  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5301  (*(left_addition_routines[child->node_type]))(thisAgent,child,tok,w);
5302  }
5304 }
5305 
5306 void mp_node_right_addition (agent* thisAgent, rete_node *node, wme *w) {
5307  uint32_t hv;
5308  token *tok;
5309  Symbol *referent;
5310  rete_test *rt;
5311  Bool failed_a_test;
5312  rete_node *child;
5313 
5316 
5317  if (mp_bnode_is_left_unlinked(node)) {
5319  if (! node->a.np.tokens) {
5320  unlink_from_right_mem (node);
5322  return;
5323  }
5324  }
5325 
5326  referent = w->id;
5327  hv = node->node_id ^ referent->common.hash_id;
5328 
5329  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5330  if (tok->node != node) continue;
5331  /* --- does tok match w? --- */
5332  if (tok->a.ht.referent != referent) continue;
5333  failed_a_test = FALSE;
5334  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5335  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5336  failed_a_test = TRUE;
5337  break;
5338  }
5339  if (failed_a_test) continue;
5340  /* --- match found, so call each child node --- */
5341  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5342  (*(left_addition_routines[child->node_type]))(thisAgent,child,tok,w);
5343  }
5345 }
5346 
5347 void unhashed_mp_node_right_addition (agent* thisAgent, rete_node *node, wme *w) {
5348  uint32_t hv;
5349  token *tok;
5350  rete_test *rt;
5351  Bool failed_a_test;
5352  rete_node *child;
5353 
5356 
5357  if (mp_bnode_is_left_unlinked(node)) {
5359  if (! node->a.np.tokens) {
5360  unlink_from_right_mem (node);
5362  return;
5363  }
5364  }
5365 
5366  hv = node->node_id;
5367 
5368  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5369  if (tok->node != node) continue;
5370  /* --- does tok match w? --- */
5371  failed_a_test = FALSE;
5372  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5373  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5374  failed_a_test = TRUE;
5375  break;
5376  }
5377  if (failed_a_test) continue;
5378  /* --- match found, so call each child node --- */
5379  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5380  (*(left_addition_routines[child->node_type]))(thisAgent,child,tok,w);
5381  }
5383 }
5384 
5385 /* ************************************************************************
5386 
5387  SECTION 13: Beta Node Interpreter Routines: Negative Nodes
5388 
5389 ************************************************************************ */
5390 
5392  token *tok, wme *w) {
5393  uint32_t hv, right_hv;
5394  Symbol *referent;
5395  right_mem *rm;
5396  alpha_mem *am;
5397  rete_test *rt;
5398  Bool failed_a_test;
5399  rete_node *child;
5400  token *New;
5401 
5403  left_node_activation(node,TRUE);
5404 
5405  if (node_is_right_unlinked(node)) relink_to_right_mem (node);
5406 
5407  {
5408  int levels_up;
5409  token *t;
5410 
5411  levels_up = node->left_hash_loc_levels_up;
5412  if (levels_up==1) {
5413  referent = field_from_wme (w, node->left_hash_loc_field_num);
5414  } else { /* --- levels_up > 1 --- */
5415  for (t=tok, levels_up -= 2; levels_up!=0; levels_up--) t=t->parent;
5416  referent = field_from_wme (t->w, node->left_hash_loc_field_num);
5417  }
5418  }
5419 
5420  hv = node->node_id ^ referent->common.hash_id;
5421 
5422  /* --- build new token, add it to the hash table --- */
5423  token_added(node);
5424  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5425  new_left_token (New, node, tok, w);
5426  insert_token_into_left_ht (thisAgent, New, hv);
5427  New->a.ht.referent = referent;
5428  New->negrm_tokens = NIL;
5429 
5430  /* --- look through right memory for matches --- */
5431  am = node->b.posneg.alpha_mem_;
5432  right_hv = am->am_id ^ referent->common.hash_id;
5433  for (rm=right_ht_bucket(thisAgent, right_hv); rm!=NIL; rm=rm->next_in_bucket) {
5434  if (rm->am != am) continue;
5435  /* --- does rm->w match new? --- */
5436  if (referent != rm->w->id) continue;
5437  failed_a_test = FALSE;
5438  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5439  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5440  failed_a_test = TRUE;
5441  break;
5442  }
5443  if (failed_a_test) continue;
5444  { token *t;
5445  allocate_with_pool (thisAgent, &thisAgent->token_pool, &t);
5446  t->node = node;
5447  t->parent = NIL;
5448  t->w = rm->w;
5449  t->a.neg.left_token = New;
5450  insert_at_head_of_dll (rm->w->tokens, t, next_from_wme, prev_from_wme);
5451  t->first_child = NIL;
5453  a.neg.next_negrm, a.neg.prev_negrm);
5454  }
5455  }
5456 
5457  /* --- if no matches were found, call each child node --- */
5458  if (! New->negrm_tokens) {
5459  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5460  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,NIL);
5461  }
5463 }
5464 
5466  token *tok, wme *w) {
5467  uint32_t hv;
5468  rete_test *rt;
5469  Bool failed_a_test;
5470  right_mem *rm;
5471  rete_node *child;
5472  token *New;
5473 
5475  left_node_activation(node,TRUE);
5476 
5477  if (node_is_right_unlinked(node)) relink_to_right_mem (node);
5478 
5479  hv = node->node_id;
5480 
5481  /* --- build new token, add it to the hash table --- */
5482  token_added(node);
5483  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5484  new_left_token (New, node, tok, w);
5485  insert_token_into_left_ht (thisAgent, New, hv);
5486  New->a.ht.referent = NIL;
5487  New->negrm_tokens = NIL;
5488 
5489  /* --- look through right memory for matches --- */
5490  for (rm=node->b.posneg.alpha_mem_->right_mems; rm!=NIL; rm=rm->next_in_am) {
5491  /* --- does rm->w match new? --- */
5492  failed_a_test = FALSE;
5493  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5494  if (! match_left_and_right (thisAgent, rt, New, rm->w)) {
5495  failed_a_test = TRUE;
5496  break;
5497  }
5498  if (failed_a_test) continue;
5499  { token *t;
5500  allocate_with_pool (thisAgent, &thisAgent->token_pool, &t);
5501  t->node = node;
5502  t->parent = NIL;
5503  t->w = rm->w;
5504  t->a.neg.left_token = New;
5505  insert_at_head_of_dll (rm->w->tokens, t,next_from_wme,prev_from_wme);
5506  t->first_child = NIL;
5508  a.neg.next_negrm, a.neg.prev_negrm);
5509  }
5510  }
5511 
5512  /* --- if no matches were found, call each child node --- */
5513  if (! New->negrm_tokens) {
5514  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5515  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,NIL);
5516  }
5518 }
5519 
5520 void negative_node_right_addition (agent* thisAgent, rete_node *node, wme *w) {
5521  uint32_t hv;
5522  token *tok;
5523  Symbol *referent;
5524  rete_test *rt;
5525  Bool failed_a_test;
5526 
5529 
5530  referent = w->id;
5531  hv = node->node_id ^ referent->common.hash_id;
5532 
5533  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5534  if (tok->node != node) continue;
5535  /* --- does tok match w? --- */
5536  if (tok->a.ht.referent != referent) continue;
5537  failed_a_test = FALSE;
5538  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5539  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5540  failed_a_test = TRUE;
5541  break;
5542  }
5543  if (failed_a_test) continue;
5544  /* --- match found: build new negrm token, remove descendent tokens --- */
5545  { token *t;
5546  allocate_with_pool (thisAgent, &thisAgent->token_pool, &t);
5547  t->node = node;
5548  t->parent = NIL;
5549  t->w = w;
5550  t->a.neg.left_token = tok;
5551  insert_at_head_of_dll (w->tokens, t, next_from_wme, prev_from_wme);
5552  t->first_child = NIL;
5554  a.neg.next_negrm, a.neg.prev_negrm);
5555  }
5556  while (tok->first_child) remove_token_and_subtree (thisAgent, tok->first_child);
5557  }
5559 }
5560 
5562  uint32_t hv;
5563  token *tok;
5564  rete_test *rt;
5565  Bool failed_a_test;
5566 
5569 
5570  hv = node->node_id;
5571 
5572  for (tok=left_ht_bucket(thisAgent, hv); tok!=NIL; tok=tok->a.ht.next_in_bucket) {
5573  if (tok->node != node) continue;
5574  /* --- does tok match w? --- */
5575  failed_a_test = FALSE;
5576  for (rt=node->b.posneg.other_tests; rt!=NIL; rt=rt->next)
5577  if (! match_left_and_right (thisAgent, rt, tok, w)) {
5578  failed_a_test = TRUE;
5579  break;
5580  }
5581  if (failed_a_test) continue;
5582  /* --- match found: build new negrm token, remove descendent tokens --- */
5583  { token *t;
5584  allocate_with_pool (thisAgent, &thisAgent->token_pool, &t);
5585  t->node = node;
5586  t->parent = NIL;
5587  t->w = w;
5588  t->a.neg.left_token = tok;
5589  insert_at_head_of_dll (w->tokens, t, next_from_wme, prev_from_wme);
5590  t->first_child = NIL;
5592  a.neg.next_negrm, a.neg.prev_negrm);
5593  }
5594  while (tok->first_child) remove_token_and_subtree (thisAgent, tok->first_child);
5595  }
5597 }
5598 
5599 /* ************************************************************************
5600 
5601  SECTION 14: Beta Node Interpreter Routines: CN and CN_PARTNER Nodes
5602 
5603  These routines can support either the CN node hearing about new left
5604  tokens before the CN_PARTNER, or vice-versa. This makes them a bit
5605  more complex than they would be otherwise.
5606 ************************************************************************ */
5607 
5608 void cn_node_left_addition (agent* thisAgent, rete_node *node, token *tok, wme *w) {
5609  uint32_t hv;
5610  token *t, *New;
5611  rete_node *child;
5612 
5614  left_node_activation(node,TRUE);
5615 
5616  hv = node->node_id ^ cast_and_possibly_truncate<uint32_t>(tok) ^ cast_and_possibly_truncate<uint32_t>(w);
5617 
5618  /* --- look for a matching left token (since the partner node might have
5619  heard about this new token already, in which case it would have done
5620  the CN node's work already); if found, exit --- */
5621  for (t=left_ht_bucket(thisAgent, hv); t!=NIL; t=t->a.ht.next_in_bucket)
5622  if ((t->node==node)&&(t->parent==tok)&&(t->w==w)) return;
5623 
5624  /* --- build left token, add it to the hash table --- */
5625  token_added(node);
5626  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5627  new_left_token (New, node, tok, w);
5628  insert_token_into_left_ht (thisAgent, New, hv);
5629  New->negrm_tokens = NIL;
5630 
5631  /* --- pass the new token on to each child node --- */
5632  for (child=node->first_child; child!=NIL; child=child->next_sibling)
5633  (*(left_addition_routines[child->node_type]))(thisAgent,child,New,NIL);
5634 
5636 }
5637 
5639  token *tok, wme *w) {
5640  rete_node *partner, *temp;
5641  uint32_t hv;
5642  token *left, *negrm_tok;
5643 
5645  left_node_activation(node,TRUE);
5646 
5647  partner = node->b.cn.partner;
5648 
5649  /* --- build new negrm token --- */
5650  token_added(node);
5651  allocate_with_pool (thisAgent, &thisAgent->token_pool, &negrm_tok);
5652  new_left_token (negrm_tok, node, tok, w);
5653 
5654  /* --- advance (tok,w) up to the token from the top of the branch --- */
5655  temp = node->parent;
5656  while (temp != partner->parent) {
5657  temp = real_parent_node (temp);
5658  w = tok->w;
5659  tok = tok->parent;
5660  }
5661 
5662  /* --- look for the matching left token --- */
5663  hv = partner->node_id ^ cast_and_possibly_truncate<uint32_t>(tok) ^ cast_and_possibly_truncate<uint32_t>(w);
5664  for (left=left_ht_bucket(thisAgent, hv); left!=NIL; left=left->a.ht.next_in_bucket)
5665  if ((left->node==partner)&&(left->parent==tok)&&(left->w==w)) break;
5666 
5667  /* --- if not found, create a new left token --- */
5668  if (!left) {
5669  token_added(partner);
5670  allocate_with_pool (thisAgent, &thisAgent->token_pool, &left);
5671  new_left_token (left, partner, tok, w);
5672  insert_token_into_left_ht (thisAgent, left, hv);
5673  left->negrm_tokens = NIL;
5674  }
5675 
5676  /* --- add new negrm token to the left token --- */
5677  negrm_tok->a.neg.left_token = left;
5678  insert_at_head_of_dll (left->negrm_tokens, negrm_tok,
5679  a.neg.next_negrm, a.neg.prev_negrm);
5680 
5681  /* --- remove any descendent tokens of the left token --- */
5682  while (left->first_child) remove_token_and_subtree (thisAgent, left->first_child);
5683 
5685 }
5686 
5687 /* ************************************************************************
5688 
5689  SECTION 15: Beta Node Interpreter Routines: Production Nodes
5690 
5691  During each elaboration cycle, we buffer the assertions (new matches)
5692  and retractions (old no-longer-present matches) in "tentative_assertions"
5693  and "tentative_retractions" on each p-node. We have to buffer them
5694  because a match could appear and then disappear during one e-cycle
5695  (e.g., add one WME, this creates a match, then remove another WME,
5696  and the match goes away). A match can also disappear then re-appear
5697  (example case involves an NCC -- create a match fot the NCC by adding
5698  a WME inside it, then remove another WME for a different condition
5699  inside the NCC). When one of these "stobe" situations occurs,
5700  we don't want to actually fire the production or retract the
5701  instantiation -- hence the buffering.
5702 ************************************************************************ */
5703 
5704 /* ----------------------------------------------------------------------
5705  P Node Left Addition
5706 
5707  Algorithm:
5708 
5709  Does this token match (wme's equal) one of tentative_retractions?
5710  (We have to check instantiation structure for this--when an
5711  instantiation retracts then re-asserts in one e-cycle, the
5712  token itself will be different, but all the wme's tested positively
5713  will be the same.)
5714  If so, remove that tentative_retraction.
5715  If not, store this new token in tentative_assertions.
5716 ---------------------------------------------------------------------- */
5717 
5718 void p_node_left_addition (agent* thisAgent, rete_node *node, token *tok, wme *w) {
5719  ms_change *msc;
5720  condition *cond;
5721  token *current_token, *New;
5722  wme *current_wme;
5723  rete_node *current_node;
5724  Bool match_found;
5725 
5726 
5727  /* RCHONG: begin 10.11 */
5728 
5729  int prod_type;
5730  token *OPERAND_curr_tok, *temp_tok;
5731 
5732  action *act;
5733  Bool operator_proposal,op_elab;
5734  char action_attr[50];
5735 
5736  int pass;
5737  wme *lowest_goal_wme;
5738 
5739  /* RCHONG: end 10.11 */
5740 
5742  left_node_activation(node,TRUE);
5743 
5744  /* --- build new left token (used only for tree-based remove) --- */
5745  token_added(node);
5746  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
5747  new_left_token (New, node, tok, w);
5748 
5749  /* --- check for match in tentative_retractions --- */
5750  match_found = FALSE;
5751  for (msc=node->b.p.tentative_retractions; msc!=NIL; msc=msc->next_of_node) {
5752  match_found = TRUE;
5754  current_token = tok;
5755  current_wme = w;
5756  current_node = node->parent;
5757  while (current_node->node_type!=DUMMY_TOP_BNODE) {
5758  if (bnode_is_positive(current_node->node_type))
5759  if (current_wme != cond->bt.wme_) {
5760  match_found=FALSE; break;
5761  }
5762  current_node = real_parent_node (current_node);
5763  current_wme = current_token->w;
5764  current_token = current_token->parent;
5765  cond = cond->prev;
5766  }
5767  if (match_found) break;
5768  }
5769 
5770 #ifdef BUG_139_WORKAROUND
5771  /* --- test workaround for bug #139: don't rematch justifications; let them be removed --- */
5772  /* note that the justification is added to the retraction list when it is first created, so
5773  we let it match the first time, but not after that */
5774  if (match_found && node->b.p.prod->type == JUSTIFICATION_PRODUCTION_TYPE) {
5775  if (node->b.p.prod->already_fired) {
5776  return;
5777  } else {
5778  node->b.p.prod->already_fired = 1;
5779  }
5780  }
5781 #endif
5782 
5783  /* --- if match found tentative_retractions, remove it --- */
5784  if (match_found) {
5785  msc->inst->rete_token = tok;
5786  msc->inst->rete_wme = w;
5788  next_of_node, prev_of_node);
5789  remove_from_dll (thisAgent->ms_retractions, msc, next, prev);
5790  /* REW: begin 08.20.97 */
5791  if (msc->goal) {
5792  remove_from_dll (msc->goal->id.ms_retractions, msc,
5793  next_in_level, prev_in_level);
5794  } else {
5795  // RPM 6/05
5796  // This if statement is to avoid a crash we get on most platforms in Soar 7 mode
5797  // It's unknown what consequences it has, but the Soar 7 demos seem to work
5798  // To return things to how they were, simply remove the if statement (but leave
5799  // the remove_from_dll line).
5800 
5801  // voigtjr 2009: returning things to how they were now that soar7 is removed
5802  //if(thisAgent->nil_goal_retractions)
5803  {
5805  msc, next_in_level, prev_in_level);
5806  }
5807  }
5808  /* REW: end 08.20.97 */
5809 
5810  free_with_pool (&thisAgent->ms_change_pool, msc);
5811 #ifdef DEBUG_RETE_PNODES
5812  print_with_symbols (thisAgent, "\nRemoving tentative retraction: %y",
5813  node->b.p.prod->name);
5814 #endif
5816  return;
5817  }
5818 
5819  /* --- no match found, so add new assertion --- */
5820 #ifdef DEBUG_RETE_PNODES
5821  print_with_symbols (thisAgent, "\nAdding tentative assertion: %y",
5822  node->b.p.prod->name);
5823 #endif
5824 
5825  allocate_with_pool (thisAgent, &thisAgent->ms_change_pool, &msc);
5826  msc->tok = tok;
5827  msc->w = w;
5828  msc->p_node = node;
5829  msc->inst = NIL; /* just for safety */
5830  /* REW: begin 08.20.97 */
5831  /* initialize goal regardless of run mode */
5832  msc->level = 0;
5833  msc->goal = NIL;
5834  /* REW: end 08.20.97 */
5835 
5836  /* RCHONG: begin 10.11 */
5837 
5838  /* (this is a RCHONG comment, but might also apply to Operand2...?)
5839 
5840  what we have to do now is to, essentially, determine the kind of
5841  support this production would get based on its present complete
5842  matches. once i know the support, i can then know into which match
5843  set list to put "msc".
5844 
5845  this code is used to make separate PE productions from IE
5846  productions by putting them into different match set lists. in
5847  non-OPERAND, these matches would all go into one list.
5848 
5849  BUGBUG i haven't tested this with a production that has more than
5850  one match where the matches could have different support. is that
5851  even possible???
5852 
5853  */
5854 
5855  /* operand code removed 1/22/99 - kjc */
5856 
5857  /* REW: begin 09.15.96 */
5858  /* REW: begin 08.20.97 */
5859  /* Find the goal and level for this ms change */
5860  msc->goal = find_goal_for_match_set_change_assertion(thisAgent, msc);
5861  msc->level = msc->goal->id.level;
5862 #ifdef DEBUG_WATERFALL
5863  print("\n Level of goal is %d", msc->level);
5864 #endif
5865  /* REW: end 08.20.97 */
5866 
5867  prod_type = IE_PRODS;
5868 
5869  if (node->b.p.prod->declared_support == DECLARED_O_SUPPORT)
5870  prod_type = PE_PRODS;
5871 
5872  else if (node->b.p.prod->declared_support == DECLARED_I_SUPPORT)
5873  prod_type = IE_PRODS;
5874 
5875  else if (node->b.p.prod->declared_support == UNDECLARED_SUPPORT) {
5876 
5877  /*
5878  check if the instantiation is proposing an operator. if it
5879  is, then this instantiation is i-supported.
5880  */
5881 
5882  operator_proposal = FALSE;
5883 
5884  for (act = node->b.p.prod->action_list; act != NIL ; act = act->next) {
5885  if ((act->type == MAKE_ACTION) &&
5886  (rhs_value_is_symbol(act->attr))) {
5887  if ((strcmp(rhs_value_to_string (thisAgent, act->attr, action_attr, 50),
5888  "operator") == NIL) &&
5890  operator_proposal = TRUE;
5891  prod_type = !PE_PRODS;
5892  break;
5893  }
5894  }
5895  }
5896 
5897  if (operator_proposal == FALSE) {
5898 
5899  /*
5900  examine all the different matches for this productions
5901  */
5902 
5903  for (OPERAND_curr_tok = node->a.np.tokens;
5904  OPERAND_curr_tok != NIL;
5905  OPERAND_curr_tok = OPERAND_curr_tok->next_of_node) {
5906 
5907  /*
5908 
5909  i'll need to make two passes over each set of wmes that
5910  match this production. the first pass looks for the lowest
5911  goal identifier. the second pass looks for a wme of the form:
5912 
5913  (<lowest-goal-id> ^operator ...)
5914 
5915  if such a wme is found, then this production is a PE_PROD.
5916  otherwise, it's a IE_PROD.
5917 
5918  admittedly, this implementation is kinda sloppy. i need to
5919  clean it up some.
5920 
5921  BUGBUG this check only looks at positive conditions. we
5922  haven't really decided what testing the absence of the
5923  operator will do. this code assumes that such a productions
5924  (instantiation) would get i-support.
5925 
5926  Modified 1/00 by KJC for o-support-mode == 3: prods that have ONLY operator
5927  elaborations (<o> ^attr ^value) are IE_PROD. If prod has
5928  both operator applications and <o> elabs, then it's PE_PROD
5929  and the user is warned that <o> elabs will be o-supported.
5930 
5931  */
5932  op_elab = FALSE;
5933  lowest_goal_wme = NIL;
5934 
5935  for (pass = 0; pass != 2; pass++) {
5936 
5937  temp_tok = OPERAND_curr_tok;
5938  while (temp_tok != NIL) {
5939  while (temp_tok->w == NIL) {
5940  temp_tok = temp_tok->parent;
5941  if (temp_tok == NIL) break;
5942  }
5943  if (temp_tok == NIL) break;
5944  if (temp_tok->w == NIL) break;
5945 
5946  if (pass == 0) {
5947  if (temp_tok->w->id->id.isa_goal == TRUE) {
5948  if (lowest_goal_wme == NIL)
5949  lowest_goal_wme = temp_tok->w;
5950  else {
5951  if (temp_tok->w->id->id.level >
5952  lowest_goal_wme->id->id.level)
5953  lowest_goal_wme = temp_tok->w;
5954  }
5955  }
5956  } else {
5957  if ((temp_tok->w->attr == thisAgent->operator_symbol) && (temp_tok->w->acceptable == FALSE) && (temp_tok->w->id == lowest_goal_wme->id)) {
5958  if ((thisAgent->o_support_calculation_type == 3) || (thisAgent->o_support_calculation_type == 4)) {
5959  /* iff RHS has only operator elaborations
5960  then it's IE_PROD, otherwise PE_PROD, so
5961  look for non-op-elabs in the actions KJC 1/00 */
5962 
5963 
5964  /* We also need to check reteloc's to see if they
5965  are referring to operator augmentations before determining
5966  if this is an operator elaboration
5967  */
5968 
5969  for (act = node->b.p.prod->action_list; act != NIL ; act = act->next) {
5970  if (act->type == MAKE_ACTION) {
5971  if ((rhs_value_is_symbol(act->id)) &&
5972 
5976  (rhs_value_to_symbol(act->id) ==
5977  temp_tok->w->value)) {
5978  op_elab = TRUE;
5979  } else if ( (thisAgent->o_support_calculation_type == 4)
5980  && (rhs_value_is_reteloc(act->id))
5982  op_elab = TRUE;
5983  } else {
5984  /* this is not an operator elaboration */
5985  prod_type = PE_PRODS;
5986  }
5987  } // act->type == MAKE_ACTION
5988  } // for
5989  } else {
5990  prod_type = PE_PRODS;
5991  break;
5992  }
5993  }
5994  } /* end if (pass == 0) ... */
5995  temp_tok = temp_tok->parent;
5996  } /* end while (temp_tok != NIL) ... */
5997 
5998  if (prod_type == PE_PRODS) {
5999  if ((thisAgent->o_support_calculation_type != 3) && (thisAgent->o_support_calculation_type != 4)) {
6000  break;
6001  } else if (op_elab == TRUE) {
6002 
6003  /* warn user about mixed actions */
6004 
6005  if ((thisAgent->o_support_calculation_type == 3) && thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM]) {
6006  print_with_symbols(thisAgent, "\nWARNING: operator elaborations mixed with operator applications\nget o_support in prod %y",
6007  node->b.p.prod->name);
6008 
6009  // XML generation
6011  add_to_growable_string(thisAgent, &gs, "WARNING: operator elaborations mixed with operator applications\nget o_support in prod ");
6012  add_to_growable_string(thisAgent, &gs, symbol_to_string(thisAgent, node->b.p.prod->name, true, 0, 0));
6014  free_growable_string(thisAgent, gs);
6015 
6016  prod_type = PE_PRODS;
6017  break;
6018  }
6019  else if ((thisAgent->o_support_calculation_type == 4) && thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM]) {
6020  print_with_symbols(thisAgent, "\nWARNING: operator elaborations mixed with operator applications\nget i_support in prod %y",
6021  node->b.p.prod->name);
6022 
6023  // XML generation
6025  add_to_growable_string(thisAgent, &gs, "WARNING: operator elaborations mixed with operator applications\nget i_support in prod ");
6026  add_to_growable_string(thisAgent, &gs, symbol_to_string(thisAgent, node->b.p.prod->name, true, 0, 0));
6028  free_growable_string(thisAgent, gs);
6029 
6030  prod_type = IE_PRODS;
6031  break;
6032  }
6033  }
6034  }
6035  } /* end for pass = */
6036  } /* end for loop checking all matches */
6037 
6038  /* BUG: IF you print lowest_goal_wme here, you don't get what
6039  you'd expect. Instead of the lowest goal WME, it looks like
6040  you get the lowest goal WME in the first/highest assertion of
6041  all the matches for this production. So, if there is a single
6042  match, you get the right number. If there are multiple matches
6043  for the same production, you get the lowest goal of the
6044  highest match goal production (or maybe just the first to
6045  fire?). I don;t know for certain if this is the behavior
6046  Ron C. wanted or if it's a bug --
6047  i need to talk to him about it. */
6048 
6049  } /* end if (operator_proposal == FALSE) */
6050 
6051  } /* end UNDECLARED_SUPPORT */
6052 
6053  if (prod_type == PE_PRODS) {
6054  insert_at_head_of_dll (thisAgent->ms_o_assertions, msc, next, prev);
6055 
6056  /* REW: begin 08.20.97 */
6058  msc, next_in_level, prev_in_level);
6059  /* REW: end 08.20.97 */
6060 
6061 
6063 
6064  if (thisAgent->soar_verbose_flag == TRUE) {
6065  print_with_symbols(thisAgent, "\n RETE: putting [%y] into ms_o_assertions",
6066  node->b.p.prod->name);
6067  char buf[256];
6068  SNPRINTF(buf, 254, "RETE: putting [%s] into ms_o_assertions", symbol_to_string(thisAgent, node->b.p.prod->name, true, 0, 0));
6069  xml_generate_verbose(thisAgent, buf);
6070  }
6071  }
6072 
6073  else {
6075  msc, next, prev);
6076 
6077  /* REW: end 08.20.97 */
6079  msc, next_in_level, prev_in_level);
6080  /* REW: end 08.20.97 */
6081 
6083 
6084  if (thisAgent->soar_verbose_flag == TRUE) {
6085  print_with_symbols(thisAgent, "\n RETE: putting [%y] into ms_i_assertions",
6086  node->b.p.prod->name);
6087  char buf[256];
6088  SNPRINTF(buf, 254, "RETE: putting [%s] into ms_i_assertions", symbol_to_string(thisAgent, node->b.p.prod->name, true, 0, 0));
6089  xml_generate_verbose(thisAgent, buf);
6090  }
6091  }
6092  /* REW: end 09.15.96 */
6093 
6094  // :interrupt
6095  if (node->b.p.prod->interrupt) {
6096  node->b.p.prod->interrupt++;
6097  thisAgent->stop_soar++;
6098 
6099  // Note that this production name might not be completely accurate.
6100  // If two productions match, the last matched production name will be
6101  // saved, but if this production then gets retracted on the same
6102  // elaboration cycle, while the first matching production remains
6103  // on the assertion list, Soar will still halt, but the production
6104  // named will be inaccurate.
6105  print_with_symbols(thisAgent, "\n*** Production match-time interrupt (:interrupt), probably from %y\n", node->b.p.prod->name);
6106  print(thisAgent, " [Phase] (Interrupt, Stop) is [%d] (%d,%d)\n", thisAgent->current_phase, node->b.p.prod->interrupt, thisAgent->stop_soar);
6107 
6108  thisAgent->reason_for_stopping = ":interrupt";
6109  }
6110 
6111  /* RCHONG: end 10.11 */
6112 
6114  next_of_node, prev_of_node);
6116 }
6117 
6118 /* ----------------------------------------------------------------------
6119  P Node Left Removal
6120 
6121  Algorithm:
6122 
6123  Does this token match (eq) one of the tentative_assertions?
6124  If so, just remove that tentative_assertion.
6125  If not, find the instantiation corresponding to this token
6126  and add it to tentative_retractions.
6127 ---------------------------------------------------------------------- */
6128 
6129 /* BUGBUG shouldn't need to pass in both tok and w -- should have the
6130  p-node's token get passed in instead, and have it point to the
6131  corresponding instantiation structure. */
6132 
6133 void p_node_left_removal (agent* thisAgent, rete_node *node, token *tok, wme *w) {
6134  ms_change *msc;
6135  instantiation *inst;
6136 
6138 
6139  /* --- check for match in tentative_assertions --- */
6140  for (msc=node->b.p.tentative_assertions; msc!=NIL; msc=msc->next_of_node) {
6141  if ((msc->tok==tok) && (msc->w==w)) {
6142  /* --- match found in tentative_assertions, so remove it --- */
6143  remove_from_dll (node->b.p.tentative_assertions, msc, next_of_node, prev_of_node);
6144 
6145  // :interrupt
6146  if (node->b.p.prod->interrupt > 1) {
6147  node->b.p.prod->interrupt--;
6148  thisAgent->stop_soar--;
6149  if (thisAgent->soar_verbose_flag == TRUE) {
6150  print(thisAgent, "RETRACTION (1) reset interrupt to READY -- (Interrupt, Stop) to (%d, %d)\n", node->b.p.prod->interrupt, thisAgent->stop_soar);
6151  }
6152  }
6153 
6154  /* REW: begin 09.15.96 */
6155  if (node->b.p.prod->OPERAND_which_assert_list == O_LIST) {
6156  remove_from_dll (thisAgent->ms_o_assertions, msc, next, prev);
6157  /* REW: begin 08.20.97 */
6158  /* msc already defined for the assertion so the goal should be defined
6159  as well. */
6160  remove_from_dll (msc->goal->id.ms_o_assertions, msc,
6161  next_in_level, prev_in_level);
6162  /* REW: end 08.20.97 */
6163  }
6164  else if (node->b.p.prod->OPERAND_which_assert_list == I_LIST) {
6165  remove_from_dll (thisAgent->ms_i_assertions, msc, next, prev);
6166  /* REW: begin 08.20.97 */
6167  remove_from_dll (msc->goal->id.ms_i_assertions, msc,
6168  next_in_level, prev_in_level);
6169  /* REW: end 08.20.97 */
6170  }
6171  /* REW: end 09.15.96 */
6172 
6173  free_with_pool (&thisAgent->ms_change_pool, msc);
6174 #ifdef DEBUG_RETE_PNODES
6175  print_with_symbols (thisAgent, "\nRemoving tentative assertion: %y",
6176  node->b.p.prod->name);
6177 #endif
6179  return;
6180  }
6181  } /* end of for loop */
6182 
6183  /* --- find the instantiation corresponding to this token --- */
6184  for (inst=node->b.p.prod->instantiations; inst!=NIL; inst=inst->next)
6185  if ((inst->rete_token==tok)&&(inst->rete_wme==w)) break;
6186 
6187  if (inst) {
6188  /* --- add that instantiation to tentative_retractions --- */
6189 #ifdef DEBUG_RETE_PNODES
6190  print_with_symbols (thisAgent, "\nAdding tentative retraction: %y",
6191  node->b.p.prod->name);
6192 #endif
6193 
6194  inst->rete_token = NIL;
6195  inst->rete_wme = NIL;
6196  allocate_with_pool (thisAgent, &thisAgent->ms_change_pool, &msc);
6197  msc->inst = inst;
6198  msc->p_node = node;
6199  msc->tok = NIL; /* just for safety */
6200  msc->w = NIL; /* just for safety */
6201  /* REW: begin 08.20.97 */
6202  msc->level = 0; /* just for safety */
6203  msc->goal = NIL; /* just for safety */
6204  /* REW: end 08.20.97 */
6206  next_of_node, prev_of_node);
6207 
6208  /* REW: begin 08.20.97 */
6209  /* Determine what the goal of the msc is and add it to that
6210  goal's list of retractions */
6212  msc->level = msc->goal->id.level;
6213 
6214 #ifdef DEBUG_WATERFALL
6215  print("\n Level of retraction is: %d", msc->level);
6216 #endif
6217 
6218  if (msc->goal->id.link_count == 0) {
6219  /* BUG (potential) (Operand2/Waterfall: 2.101)
6220  When a goal is removed in the stack, it is not immediately garbage
6221  collected, meaning that the goal pointer is still valid when the
6222  retraction is created. So the goal for a retraction will always be
6223  valid, even though, for retractions caused by goal removals, the
6224  goal will be removed at the next WM phase. (You can see this by
6225  printing the identifier for the goal in the elaboration cycle
6226  after goal removal. It's still there, although nothing is attacjed
6227  to it. One elab later, the identifier itself is removed.) Because
6228  Waterfall needs to know if the goal is valid or not, I look at the
6229  link_count on the symbol. A link_count of 0 is the trigger for the
6230  garbage collection so this solution should work -- I just make the
6231  pointer NIL to ensure that the retractions get added to the
6232  NIL_goal_retraction list. However, if the link_count is never
6233  *not* zero for an already removed goal, this solution will fail,
6234  resulting in both the retraction never being able to fire and a
6235  memory leak (because the items on the ms_change list on the symbol
6236  will never be freed). */
6237  /* print("\nThis goal is being removed. Changing msc goal pointer to NIL."); */
6238  msc->goal = NIL;
6239  }
6240 
6241  /* Put on the original retraction list */
6242  insert_at_head_of_dll (thisAgent->ms_retractions, msc, next, prev);
6243  if (msc->goal) { /* Goal exists */
6245  next_in_level, prev_in_level);
6246  }
6247  else { /* NIL Goal; put on the NIL Goal list */
6249  msc, next_in_level, prev_in_level);
6250  }
6251 
6252 #ifdef DEBUG_WATERFALL
6253  print_with_symbols(thisAgent, "\nRetraction: %y", msc->inst->prod->name);
6254  print(" is active at level %d\n", msc->level);
6255 
6256  { ms_change *assertion;
6257  print("\n Retractions list:\n");
6258  for (assertion=thisAgent->ms_retractions;
6259  assertion;
6260  assertion=assertion->next) {
6261  print_with_symbols(thisAgent, " Retraction: %y ",
6262  assertion->p_node->b.p.prod->name);
6263  print(" at level %d\n", assertion->level);
6264  }
6265 
6266  if (thisAgent->nil_goal_retractions) {
6267  print("\nCurrent NIL Goal list:\n");
6268  assertion = NIL;
6269  for (assertion=thisAgent->nil_goal_retractions;
6270  assertion;
6271  assertion=assertion->next_in_level) {
6272  print_with_symbols(thisAgent, " Retraction: %y ",
6273  assertion->p_node->b.p.prod->name);
6274  print(" at level %d\n", assertion->level);
6275  if (assertion->goal) print("This assertion has non-NIL goal pointer.\n");
6276  }
6277  }
6278  }
6279 #endif
6280  /* REW: end 08.20.97 */
6281 
6283  return;
6284  }
6285 
6286  /* REW: begin 09.15.96 */
6287 
6288  if (thisAgent->soar_verbose_flag == TRUE) {
6289  print_with_symbols (thisAgent, "\n%y: ",node->b.p.prod->name);
6290  char buf[256];
6291  SNPRINTF(buf, 254, "%s: ", symbol_to_string(thisAgent, node->b.p.prod->name, true, 0, 0));
6292  xml_generate_verbose(thisAgent, buf);
6293  }
6294 
6295  /* REW: end 09.15.96 */
6296 #ifdef BUG_139_WORKAROUND
6297  if (node->b.p.prod->type == JUSTIFICATION_PRODUCTION_TYPE) {
6298 #ifdef BUG_139_WORKAROUND_WARNING
6299  print(thisAgent, "\nWarning: can't find an existing inst to retract (BUG 139 WORKAROUND)\n");
6300  xml_generate_warning(thisAgent, "Warning: can't find an existing inst to retract (BUG 139 WORKAROUND)");
6301 #endif
6302  return;
6303  }
6304 #endif
6305 
6306  { char msg[BUFFER_MSG_SIZE];
6307  strncpy (msg,
6308  "Internal error: can't find existing instantiation to retract\n", BUFFER_MSG_SIZE);
6309  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
6310  abort_with_fatal_error(thisAgent, msg);
6311  }
6312 }
6313 
6314 /* ************************************************************************
6315 
6316  SECTION 16: Beta Node Interpreter Routines: Tree-Based Removal
6317 
6318  This routine does tree-based removal of a token and its descendents.
6319  Note that it uses a nonrecursive tree traversal; each iteration, the
6320  leaf being deleted is the leftmost leaf in the tree.
6321 ************************************************************************ */
6322 
6323 void remove_token_and_subtree (agent* thisAgent, token *root) {
6324  rete_node *node, *child, *next;
6325  token *tok, *next_value_for_tok, *left, *t, *next_t;
6326  byte node_type;
6327 
6328  tok = root;
6329 
6330  while (TRUE) {
6331  /* --- move down to the leftmost leaf --- */
6332  while (tok->first_child) tok = tok->first_child;
6333  next_value_for_tok = tok->next_sibling ? tok->next_sibling : tok->parent;
6334 
6335  /* --- cleanup stuff common to all types of nodes --- */
6336  node = tok->node;
6338  fast_remove_from_dll (node->a.np.tokens, tok, token, next_of_node,
6339  prev_of_node);
6341  next_sibling,prev_sibling);
6342  if (tok->w) fast_remove_from_dll (tok->w->tokens, tok, token,
6343  next_from_wme, prev_from_wme);
6344  node_type = node->node_type;
6345 
6346  /* --- for merged Mem/Pos nodes --- */
6347  if ((node_type==MP_BNODE)||(node_type==UNHASHED_MP_BNODE)) {
6348  remove_token_from_left_ht (thisAgent, tok, node->node_id ^
6349  (tok->a.ht.referent ?
6350  tok->a.ht.referent->common.hash_id : 0));
6351  if (! mp_bnode_is_left_unlinked(node)) {
6352  if (! node->a.np.tokens) unlink_from_right_mem (node);
6353  }
6354 
6355  /* --- for P nodes --- */
6356  } else if (node_type==P_BNODE) {
6357  p_node_left_removal (thisAgent, node, tok->parent, tok->w);
6358 
6359  /* --- for Negative nodes --- */
6360  } else if ((node_type==NEGATIVE_BNODE) ||
6361  (node_type==UNHASHED_NEGATIVE_BNODE)) {
6362  remove_token_from_left_ht (thisAgent, tok, node->node_id ^
6363  (tok->a.ht.referent ?
6364  tok->a.ht.referent->common.hash_id : 0));
6365  if (! node->a.np.tokens) unlink_from_right_mem (node);
6366  for (t=tok->negrm_tokens; t!=NIL; t=next_t) {
6367  next_t = t->a.neg.next_negrm;
6368  fast_remove_from_dll(t->w->tokens,t,token,next_from_wme,prev_from_wme);
6369  free_with_pool (&thisAgent->token_pool, t);
6370  }
6371 
6372  /* --- for Memory nodes --- */
6373  } else if ((node_type==MEMORY_BNODE)||(node_type==UNHASHED_MEMORY_BNODE)) {
6374  remove_token_from_left_ht (thisAgent, tok, node->node_id ^
6375  (tok->a.ht.referent ?
6376  tok->a.ht.referent->common.hash_id : 0));
6377 #ifdef DO_ACTIVATION_STATS_ON_REMOVALS
6378  /* --- if doing statistics stuff, then activate each attached node --- */
6379  for (child=node->b.mem.first_linked_child; child!=NIL; child=next) {
6380  next = child->a.pos.next_from_beta_mem;
6381  left_node_activation (child,FALSE);
6382  }
6383 #endif
6384  /* --- for right unlinking, then if the beta memory just went to
6385  zero, right unlink any attached Pos nodes --- */
6386  if (! node->a.np.tokens) {
6387  for (child=node->b.mem.first_linked_child; child!=NIL; child=next) {
6388  next = child->a.pos.next_from_beta_mem;
6389  unlink_from_right_mem (child);
6390  }
6391  }
6392 
6393  /* --- for CN nodes --- */
6394  } else if (node_type==CN_BNODE) {
6395  remove_token_from_left_ht (thisAgent, tok, node->node_id ^
6396  static_cast<uint32_t>(reinterpret_cast<uint64_t>(tok->parent)) ^
6397  static_cast<uint32_t>(reinterpret_cast<uint64_t>(tok->w))); // double cast necessary for avoiding precision loss warning
6398  for (t=tok->negrm_tokens; t!=NIL; t=next_t) {
6399  next_t = t->a.neg.next_negrm;
6400  if (t->w) fast_remove_from_dll (t->w->tokens, t, token,
6401  next_from_wme, prev_from_wme);
6403  next_of_node, prev_of_node);
6405  next_sibling, prev_sibling);
6406  free_with_pool (&thisAgent->token_pool, t);
6407  }
6408 
6409  /* --- for CN Partner nodes --- */
6410  } else if (node_type==CN_PARTNER_BNODE) {
6411  left = tok->a.neg.left_token;
6413  a.neg.next_negrm, a.neg.prev_negrm);
6414  if (! left->negrm_tokens) { /* just went to 0, so call children */
6415  for (child=left->node->first_child; child!=NIL;
6416  child=child->next_sibling)
6417  (*(left_addition_routines[child->node_type]))(thisAgent,child,left,NIL);
6418  }
6419 
6420  } else {
6421  char msg[BUFFER_MSG_SIZE];
6422  SNPRINTF (msg, BUFFER_MSG_SIZE,
6423  "Internal error: bad node type %d in remove_token_and_subtree\n",
6424  node->node_type);
6425  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
6426  abort_with_fatal_error(thisAgent, msg);
6427  }
6428 
6429  free_with_pool (&thisAgent->token_pool, tok);
6430  if (tok==root) break; /* if leftmost leaf was the root, we're done */
6431  tok = next_value_for_tok; /* else go get the leftmost leaf again */
6432  }
6433 }
6434 
6435 
6436 
6437 
6438 /* **********************************************************************
6439 
6440  SECTION 17: Fast, Compact Save/Reload of the Whole Rete Net
6441 
6442  These routines handle the fastsave/load of the Rete net. The basic
6443  format of the file is as follows. We first write out an initial
6444  "magic number" string; this is just used during reload to make sure
6445  the file we're trying to load actually *is* a fastsave file. Next
6446  comes the version number. IF YOU CHANGE THE FILE FORMAT, CHANGE THE
6447  VERSION NUMBER. PROVIDING BACKWARD COMPATIBILITY OR A CONVERSION
6448  UTILITY IS STRONGLY RECOMMENDED.
6449 
6450  After that, we just dump out all the symbols (except for identifiers)
6451  in the system. Next, we write out all the alpha memories (just the
6452  id/attr/value form they take, not the WMEs they contain). Then,
6453  the actual Rete net. This is written out as a preorder traversal of
6454  the Rete tree -- the record for each node consists of some data for
6455  that particular node, followed by the records for each of its children.
6456  NCC's are handled by ignoring the CN node during the preorder traversal,
6457  but writing out the data for the CN_PARTNER node and pretending the CN
6458  node's children actually belong to the CN_PARTNER. (This is done so that
6459  when we reload the net, the whole NCC subnetwork and CN/CNP stuff gets
6460  reloaded and reconstructed *before* any nodes underneath the CN node.
6461 
6462  File format (version 3):
6463  [Note: all 16-bit or 32-bit words are written LSB first]
6464 
6465  magic number sequence: "SoarCompactReteNet\n"
6466  1 byte: 0 (null termination for the above string)
6467  1 byte: format version number (current version is version 3)
6468 
6469  4 bytes: number of sym_constants
6470  4 bytes: number of variables
6471  4 bytes: number of int_constants
6472  4 bytes: number of float_constants
6473  names of all sym_constants (each a null-terminated string)
6474  names of all variables (each a null-terminated string)
6475  values of all int_constants (each as a null-terminated ASCII string)
6476  values of all float_constants (each as a null-terminated ASCII string)
6477 
6478  4 bytes: number of alpha memories
6479  definitions of all alpha memories, each of the form:
6480  12 bytes: indices of the symbols in the id, attr, and value fields
6481  (0 if the field has no symbol in it)
6482  1 byte: 0-->normal, 1-->acceptable preference test
6483 
6484  4 bytes: number of children of the root node
6485  node records for each child of the root node
6486 
6487  Node record:
6488  1 byte: node type
6489  data for node:
6490  posneg nodes: if hashed (and not P): 3 bytes -- hash field num, levels up
6491  4 bytes -- index of alpha memory
6492  record for rete test list (for other tests)
6493  pos and mp nodes: 1 byte -- 1 if left unlinked, 0 else
6494  mem nodes: if hashed: 3 bytes -- hash field num, levels up
6495  cn nodes: no record at all (not even node type) -- handled with cn_p
6496  node record instead. Basically, we ignore the cn node when
6497  writing the net, and pretend the cn/cn_p pair is one big
6498  node underneath the subnetwork.
6499  cn_p nodes: number of conjuncts in the NCC
6500  p_nodes: 4 bytes: name of production (symindex)
6501  1 byte (0 or 1): flag -- is there a documentation string
6502  if yes: documentation string (null-terminated string)
6503  1 byte: type
6504  1 byte: declared support
6505  record for the list of RHS actions
6506  4 bytes: number of RHS unbound variables
6507  RHS unbound variables (symindices for each one)
6508  1 byte (0 or 1): flag -- is there node_varnames info?
6509  if yes: node_varnames records for this production
6510  4 bytes: number of children
6511  node records for each child
6512 
6513  EXTERNAL INTERFACE:
6514  Save_rete_net() and load_rete_net() save and load everything to and
6515  from the given (already open) files. They return TRUE if successful,
6516  FALSE if any error occurred.
6517 ********************************************************************** */
6518 
6519 FILE *rete_fs_file; /* File handle we're using -- "fs" for "fast-save" */
6520 Bool rete_net_64; // used by reteload_eight_bytes, retesave_eight_bytes, BADBAD global, fix with rete_fs_file above
6521 
6522 /* ----------------------------------------------------------------------
6523  Save/Load Bytes, Short and Long Integers
6524 
6525  These are the lowest-level routines for accessing the FS file. Note
6526  that all 16-bit or 32-bit words are written LSB first. We do this
6527  carefully, so that fastsave files will be portable across machine
6528  types (big-endian vs. little-endian).
6529 ---------------------------------------------------------------------- */
6530 
6531 void retesave_one_byte (uint8_t b, FILE* /*f*/) {
6532  fputc (b, rete_fs_file);
6533 }
6534 
6535 uint8_t reteload_one_byte (FILE* f) {
6536  return static_cast<uint8_t>(fgetc (f));
6537 }
6538 
6539 void retesave_two_bytes (uint16_t w, FILE* f) {
6540  retesave_one_byte (static_cast<uint8_t>(w & 0xFF), f);
6541  retesave_one_byte (static_cast<uint8_t>((w >> 8) & 0xFF), f);
6542 }
6543 
6544 uint16_t reteload_two_bytes (FILE* f) {
6545  uint16_t i;
6546  i = reteload_one_byte(f);
6547  i += (reteload_one_byte(f) << 8);
6548  return i;
6549 }
6550 
6551 void retesave_four_bytes (uint32_t w, FILE* f) {
6552  retesave_one_byte (static_cast<uint8_t>(w & 0xFF), f);
6553  retesave_one_byte (static_cast<uint8_t>((w >> 8) & 0xFF), f);
6554  retesave_one_byte (static_cast<uint8_t>((w >> 16) & 0xFF), f);
6555  retesave_one_byte (static_cast<uint8_t>((w >> 24) & 0xFF), f);
6556 }
6557 
6559  uint32_t i;
6560  i = reteload_one_byte (f);
6561  i += (reteload_one_byte(f) << 8);
6562  i += (reteload_one_byte(f) << 16);
6563  i += (reteload_one_byte(f) << 24);
6564  return i;
6565 }
6566 
6567 void retesave_eight_bytes (uint64_t w, FILE* f) {
6568  if (!rete_net_64) {
6569  retesave_four_bytes(static_cast<uint32_t>(w), f);
6570  return;
6571  }
6572  retesave_one_byte (static_cast<uint8_t>(w & 0xFF), f);
6573  retesave_one_byte (static_cast<uint8_t>((w >> 8) & 0xFF), f);
6574  retesave_one_byte (static_cast<uint8_t>((w >> 16) & 0xFF), f);
6575  retesave_one_byte (static_cast<uint8_t>((w >> 24) & 0xFF), f);
6576  retesave_one_byte (static_cast<uint8_t>((w >> 32) & 0xFF), f);
6577  retesave_one_byte (static_cast<uint8_t>((w >> 40) & 0xFF), f);
6578  retesave_one_byte (static_cast<uint8_t>((w >> 48) & 0xFF), f);
6579  retesave_one_byte (static_cast<uint8_t>((w >> 56) & 0xFF), f);
6580 }
6581 
6582 uint64_t reteload_eight_bytes (FILE* f) {
6583  if (!rete_net_64)
6584  return reteload_four_bytes(f);
6585 
6586  uint64_t i;
6587  uint64_t tmp;
6588  i = reteload_one_byte(f);
6589  tmp = reteload_one_byte(f);
6590  i += (tmp << 8);
6591  tmp = reteload_one_byte(f);
6592  i += (tmp << 16);
6593  tmp = reteload_one_byte(f);
6594  i += (tmp << 24);
6595  tmp = reteload_one_byte(f);
6596  i += (tmp << 32);
6597  tmp = reteload_one_byte(f);
6598  i += (tmp << 40);
6599  tmp = reteload_one_byte(f);
6600  i += (tmp << 48);
6601  tmp = reteload_one_byte(f);
6602  i += (tmp << 56);
6603  return i;
6604 }
6605 
6606 /* ----------------------------------------------------------------------
6607  Save/Load Strings
6608 
6609  Strings are written as null-terminated sequences of characters, just
6610  like the usual C format. Reteload_string() leaves the result in
6611  reteload_string_buf[].
6612 ---------------------------------------------------------------------- */
6613 
6615 
6616 void retesave_string (const char *s, FILE* f) {
6617  while (*s) {
6618  retesave_one_byte (*s,f);
6619  s++;
6620  }
6621  retesave_one_byte (0,f);
6622 }
6623 
6624 void reteload_string (FILE* f) {
6625  int i, ch;
6626  i = 0;
6627  do {
6628  ch = reteload_one_byte(f);
6629  reteload_string_buf[i++] = static_cast<char>(ch);
6630  } while (ch);
6631 }
6632 
6633 /* ----------------------------------------------------------------------
6634  Save/Load Symbols
6635 
6636  We write out symbol names once at the beginning of the file, and
6637  thereafter refer to symbols using 32-bit index numbers instead of their
6638  full names. Retesave_symbol_and_assign_index() writes out one symbol
6639  and assigns it an index (stored in sym->common.a.retesave_symindex).
6640  Index numbers are assigned sequentially -- the first symbol in the file
6641  has index number 1, the second has number 2, etc. Retesave_symbol_table()
6642  saves the whole symbol table, using the following format:
6643 
6644  4 bytes: number of sym_constants
6645  4 bytes: number of variables
6646  4 bytes: number of int_constants
6647  4 bytes: number of float_constants
6648  names of all sym_constants (each a null-terminated string)
6649  names of all variables (each a null-terminated string)
6650  values of all int_constants (each as a null-term. ASCII string)
6651  values of all float_constants (each as a null-term. ASCII string)
6652 
6653  To reload symbols, we read the records and make new symbols, and
6654  also create an array (reteload_symbol_table) that maps from the
6655  index numbers to the Symbol structures. Reteload_all_symbols() does
6656  this. Reteload_symbol_from_index() reads an index number and returns
6657  the appropriate Symbol (without incrementing its reference count).
6658  Reteload_free_symbol_table() frees up the symbol table when we're done.
6659 ---------------------------------------------------------------------- */
6660 
6661 Bool retesave_symbol_and_assign_index (agent* thisAgent, void *item, void* userdata) {
6662  Symbol *sym;
6663  FILE* f = reinterpret_cast<FILE*>(userdata);
6664 
6665  sym = static_cast<symbol_union *>(item);
6666  thisAgent->current_retesave_symindex++;
6667  sym->common.a.retesave_symindex = thisAgent->current_retesave_symindex;
6668  retesave_string (symbol_to_string (thisAgent, sym, FALSE, NIL, 0), f);
6669  return FALSE;
6670 }
6671 
6672 void retesave_symbol_table (agent* thisAgent, FILE* f) {
6673  thisAgent->current_retesave_symindex = 0;
6674 
6679 
6682  do_for_all_items_in_hash_table (thisAgent, thisAgent->variable_hash_table,
6688 }
6689 
6690 void reteload_all_symbols (agent* thisAgent, FILE* f) {
6691  uint64_t num_sym_constants, num_variables;
6692  uint64_t num_int_constants, num_float_constants;
6693  Symbol **current_place_in_symtab;
6694  uint64_t i;
6695 
6696  num_sym_constants = reteload_eight_bytes(f);
6697  num_variables = reteload_eight_bytes(f);
6698  num_int_constants = reteload_eight_bytes(f);
6699  num_float_constants = reteload_eight_bytes(f);
6700 
6701  thisAgent->reteload_num_syms = num_sym_constants + num_variables + num_int_constants
6702  + num_float_constants;
6703 
6704  /* --- allocate memory for the symbol table --- */
6705  thisAgent->reteload_symbol_table = (Symbol **)
6706  allocate_memory (thisAgent, thisAgent->reteload_num_syms*sizeof(char *),MISCELLANEOUS_MEM_USAGE);
6707 
6708  /* --- read in all the symbols from the file --- */
6709  current_place_in_symtab = thisAgent->reteload_symbol_table;
6710  for (i=0; i<num_sym_constants; i++) {
6711  reteload_string(f);
6712  *(current_place_in_symtab++) = make_sym_constant (thisAgent, reteload_string_buf);
6713  }
6714  for (i=0; i<num_variables; i++) {
6715  reteload_string(f);
6716  *(current_place_in_symtab++) = make_variable (thisAgent, reteload_string_buf);
6717  }
6718  for (i=0; i<num_int_constants; i++) {
6719  reteload_string(f);
6720  *(current_place_in_symtab++) =
6721  make_int_constant (thisAgent, strtol(reteload_string_buf,NULL,10));
6722  }
6723  for (i=0; i<num_float_constants; i++) {
6724  reteload_string(f);
6725  *(current_place_in_symtab++) =
6726  make_float_constant (thisAgent, strtod(reteload_string_buf,NULL));
6727  }
6728 }
6729 
6730 Symbol *reteload_symbol_from_index (agent* thisAgent, FILE* f) {
6731  uint64_t index;
6732 
6733  index = reteload_eight_bytes(f);
6734  if (index==0) return NIL;
6735  index--;
6736  if (index >= thisAgent->reteload_num_syms) {
6737  char msg[BUFFER_MSG_SIZE];
6738  strncpy (msg, "Internal error (file corrupted?): symbol count too small\n", BUFFER_MSG_SIZE);
6739  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
6740  abort_with_fatal_error(thisAgent, msg);
6741  }
6742  return *(thisAgent->reteload_symbol_table+index);
6743 }
6744 
6746  uint64_t i;
6747 
6748  for (i=0; i<thisAgent->reteload_num_syms; i++)
6749  symbol_remove_ref (thisAgent, *(thisAgent->reteload_symbol_table+i));
6751 }
6752 
6753 /* ----------------------------------------------------------------------
6754  Save/Load Alpha Memories
6755 
6756  We write out alpha memories once, near the beginning of the file, and
6757  thereafter refer to them using 32-bit index numbers (just like symbols).
6758  Retesave_alpha_mem_and_assign_index() writes out one alpha memory
6759  and assigns it an index (stored in am->retesave_amindex). Index numbers
6760  are assigned sequentially -- the first alpha memory in the file has
6761  index number 1, the second has number 2, etc. Retesave_alpha_memories()
6762  writes out all the alpha memories, in the following format:
6763 
6764  4 bytes: number of alpha memories
6765  definitions of all alpha memories, each of the form:
6766  12 bytes: indices of the symbols in the id, attr, and value fields
6767  (0 if the field has no symbol in it)
6768  1 byte: 0-->normal, 1-->acceptable preference test
6769 
6770  To reload alpha memories, we read the records and make new AM's, and
6771  also create an array (reteload_am_table) that maps from the
6772  index numbers to the alpha_mem structures. Reteload_alpha_memories()
6773  does this. Reteload_am_from_index() reads an index number and returns
6774  the appropriate alpha_mem (without incrementing its reference count).
6775  Reteload_free_am_table() frees up the table when we're done.
6776 ---------------------------------------------------------------------- */
6777 
6778 Bool retesave_alpha_mem_and_assign_index (agent* thisAgent, void *item, void* userdata) {
6779  alpha_mem *am;
6780  FILE* f = reinterpret_cast<FILE*>(userdata);
6781 
6782  am = static_cast<alpha_mem_struct *>(item);
6783  thisAgent->current_retesave_amindex++;
6784  am->retesave_amindex = thisAgent->current_retesave_amindex;
6785  retesave_eight_bytes (am->id ? am->id->common.a.retesave_symindex : 0,f);
6786  retesave_eight_bytes (am->attr ? am->attr->common.a.retesave_symindex : 0,f);
6787  retesave_eight_bytes (am->value ? am->value->common.a.retesave_symindex : 0,f);
6788  retesave_one_byte (static_cast<byte>(am->acceptable ? 1 : 0),f);
6789  return FALSE;
6790 }
6791 
6792 void retesave_alpha_memories (agent* thisAgent, FILE* f) {
6793  uint64_t i, num_ams;
6794 
6795  thisAgent->current_retesave_amindex = 0;
6796  num_ams = 0;
6797  for (i=0; i<16; i++) num_ams += thisAgent->alpha_hash_tables[i]->count;
6798  retesave_eight_bytes (num_ams,f);
6799  for (i=0; i<16; i++)
6800  do_for_all_items_in_hash_table (thisAgent, thisAgent->alpha_hash_tables[i],
6802 }
6803 
6804 void reteload_alpha_memories (agent* thisAgent, FILE* f) {
6805  uint64_t i;
6806  Symbol *id, *attr, *value;
6807  Bool acceptable;
6808 
6809  thisAgent->reteload_num_ams = reteload_eight_bytes(f);
6810  thisAgent->reteload_am_table = (alpha_mem **)
6811  allocate_memory (thisAgent, thisAgent->reteload_num_ams*sizeof(char *),MISCELLANEOUS_MEM_USAGE);
6812  for (i=0; i<thisAgent->reteload_num_ams; i++) {
6813  id = reteload_symbol_from_index(thisAgent,f);
6814  attr = reteload_symbol_from_index(thisAgent,f);
6815  value = reteload_symbol_from_index(thisAgent,f);
6816  acceptable = reteload_one_byte(f) ? TRUE : FALSE;
6817  *(thisAgent->reteload_am_table+i) = find_or_make_alpha_mem (thisAgent,id,attr,value,acceptable);
6818  }
6819 }
6820 
6821 alpha_mem *reteload_am_from_index (agent* thisAgent, FILE* f) {
6822  uint64_t amindex;
6823 
6824  amindex = reteload_eight_bytes(f) - 1;
6825  if (amindex >= thisAgent->reteload_num_ams) {
6826  char msg[BUFFER_MSG_SIZE];
6827  strncpy (msg,
6828  "Internal error (file corrupted?): alpha mem count too small\n", BUFFER_MSG_SIZE);
6829  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
6830  abort_with_fatal_error(thisAgent, msg);
6831  }
6832  return *(thisAgent->reteload_am_table+amindex);
6833 }
6834 
6835 void reteload_free_am_table (agent* thisAgent) {
6836  uint64_t i;
6837 
6838  for (i=0; i<thisAgent->reteload_num_ams; i++)
6839  remove_ref_to_alpha_mem (thisAgent, *(thisAgent->reteload_am_table+i));
6840  free_memory (thisAgent, thisAgent->reteload_am_table, MISCELLANEOUS_MEM_USAGE);
6841 }
6842 
6843 /* ----------------------------------------------------------------------
6844  Save/Load Varnames and Node_Varnames
6845 
6846  These routines write out and read in node varnames records.
6847 
6848  Node_varnames record:
6849  records (in bottom-up order) -- start at bottom, walk up net,
6850  into NCC's as we go along; for each node, write three field varnames
6851 
6852  varnames record:
6853  type (1 byte): 0=null, 1=one var, 2=list
6854  if one var: 4 bytes (symindex)
6855  if list: 4 bytes (number of items) + list of symindices
6856 ---------------------------------------------------------------------- */
6857 
6858 void retesave_varnames (varnames *names, FILE* f) {
6859  list *c;
6860  uint64_t i;
6861  Symbol *sym;
6862 
6863  if (! names) {
6864  retesave_one_byte (0,f);
6865  } else if (varnames_is_one_var(names)) {
6866  retesave_one_byte (1,f);
6867  sym = varnames_to_one_var (names);
6868  retesave_eight_bytes (sym->common.a.retesave_symindex,f);
6869  } else {
6870  retesave_one_byte (2,f);
6871  for (i=0, c=varnames_to_var_list(names); c!=NIL; i++, c=c->rest);
6872  retesave_eight_bytes (i,f);
6873  for (c=varnames_to_var_list(names); c!=NIL; c=c->rest)
6874  retesave_eight_bytes (static_cast<Symbol *>(c->first)->common.a.retesave_symindex,f);
6875  }
6876 }
6877 
6878 varnames *reteload_varnames (agent* thisAgent, FILE* f) {
6879  list *c;
6880  uint64_t i, count;
6881  Symbol *sym;
6882 
6883  i = reteload_one_byte (f);
6884  if (i==0) return NIL;
6885  if (i==1) {
6886  sym = reteload_symbol_from_index (thisAgent,f);
6887  symbol_add_ref (sym);
6888  return one_var_to_varnames (sym);
6889  } else {
6890  count = reteload_eight_bytes (f);
6891  c = NIL;
6892  while (count--) {
6893  sym = reteload_symbol_from_index (thisAgent,f);
6894  symbol_add_ref (sym);
6895  push(thisAgent, sym, c);
6896  }
6898  return var_list_to_varnames (c);
6899  }
6900 }
6901 
6902 void retesave_node_varnames (node_varnames *nvn, rete_node *node, FILE* f) {
6903  while (TRUE) {
6904  if (node->node_type == DUMMY_TOP_BNODE) return;
6905  if (node->node_type == CN_BNODE) {
6906  node=node->b.cn.partner->parent;
6907  nvn = nvn->data.bottom_of_subconditions;
6908  continue;
6909  }
6913  nvn = nvn->parent;
6914  node = real_parent_node (node);
6915  }
6916 }
6917 
6918 node_varnames *reteload_node_varnames (agent* thisAgent, rete_node *node, FILE* f) {
6919  node_varnames *nvn, *nvn_for_ncc;
6920  rete_node *temp;
6921 
6922  if (node->node_type == DUMMY_TOP_BNODE) return NIL;
6923  allocate_with_pool (thisAgent, &thisAgent->node_varnames_pool, &nvn);
6924  if (node->node_type == CN_BNODE) {
6925  temp = node->b.cn.partner->parent;
6926  nvn_for_ncc = reteload_node_varnames (thisAgent, temp,f);
6927  nvn->data.bottom_of_subconditions = nvn_for_ncc;
6928  while (temp!=node->parent) {
6929  temp = real_parent_node(temp);
6930  nvn_for_ncc = nvn_for_ncc->parent;
6931  }
6932  nvn->parent = nvn_for_ncc;
6933  } else {
6934  nvn->data.fields.id_varnames = reteload_varnames (thisAgent,f);
6935  nvn->data.fields.attr_varnames = reteload_varnames (thisAgent,f);
6936  nvn->data.fields.value_varnames = reteload_varnames (thisAgent,f);
6937  nvn->parent = reteload_node_varnames (thisAgent, real_parent_node(node),f);
6938  }
6939  return nvn;
6940 }
6941 
6942 /* ----------------------------------------------------------------------
6943  Save/Load RHS Values
6944 
6945  RHS value record:
6946  1 byte: type (0=symbol, 1=funcall, 2=reteloc, 3=rhs_unbound_var)
6947  for symbols: 4 bytes (symindex)
6948  for funcalls: symindex of function name, 4 bytes (# of args),
6949  rhs value record for each arg
6950  for retelocs: 1 byte (field num) + 2 bytes (levels up)
6951  for rhs_unbound_vars: 4 bytes (symindex)
6952 ---------------------------------------------------------------------- */
6953 
6954 void retesave_rhs_value (rhs_value rv, FILE* f) {
6955  uint64_t i;
6956  Symbol *sym;
6957  cons *c;
6958 
6959  if (rhs_value_is_symbol(rv)) {
6960  retesave_one_byte (0,f);
6961  sym = rhs_value_to_symbol (rv);
6962  retesave_eight_bytes (sym->common.a.retesave_symindex,f);
6963  } else if (rhs_value_is_funcall(rv)) {
6964  retesave_one_byte (1,f);
6965  c = rhs_value_to_funcall_list (rv);
6966  sym = static_cast<rhs_function *>(c->first)->name;
6967  retesave_eight_bytes (sym->common.a.retesave_symindex,f);
6968  c=c->rest;
6969  for (i=0; c!=NIL; i++, c=c->rest);
6970  retesave_eight_bytes (i,f);
6971  for (c=rhs_value_to_funcall_list(rv)->rest; c!=NIL; c=c->rest)
6972  retesave_rhs_value (static_cast<rhs_value>(c->first),f);
6973  } else if (rhs_value_is_reteloc(rv)) {
6974  retesave_one_byte (2,f);
6977  } else {
6978  retesave_one_byte (3,f);
6980  }
6981 }
6982 
6983 rhs_value reteload_rhs_value (agent* thisAgent, FILE* f) {
6984  rhs_value rv, temp;
6985  uint64_t i, count;
6986  Symbol *sym;
6987  byte type, field_num;
6988  int levels_up;
6989  list *funcall_list;
6990  rhs_function *rf;
6991 
6992  type = reteload_one_byte(f);
6993  switch (type) {
6994  case 0:
6995  sym = reteload_symbol_from_index(thisAgent,f);
6996  symbol_add_ref (sym);
6997  rv = symbol_to_rhs_value (sym);
6998  break;
6999  case 1:
7000  funcall_list = NIL;
7001  sym = reteload_symbol_from_index(thisAgent,f);
7002 
7003  /* NLD: 4/30/2011
7004  * I'm fairly certain function calls do not need an added ref.
7005  *
7006  * I traced through production parsing and the RHS function name is not kept around there. Instead, it "finds" the symbol
7007  * (as opposed to "make", which adds a ref) and uses that to hash to the existing RHS function structure (which keeps a
7008  * ref on the symbol name). The initial symbol ref comes from init_built_in_rhs_functions (+1 ref) and then is removed
7009  * later via remove_built_in_rhs_functions (-1 ref).
7010  *
7011  * The parallel in rete-net loading is the symbol table that is loaded in via reteload_all_symbols (+1 ref) and then freed
7012  * in reteload_free_symbol_table (-1 ref).
7013  */
7014  // symbol_add_ref (sym);
7015 
7016  rf = lookup_rhs_function (thisAgent, sym);
7017  if (!rf) {
7018  char msg[BUFFER_MSG_SIZE];
7019  print_with_symbols (thisAgent, "Error: can't load this file because it uses an undefined RHS function %y\n", sym);
7020  SNPRINTF (msg, BUFFER_MSG_SIZE, "Error: can't load this file because it uses an undefined RHS function %s\n", symbol_to_string(thisAgent, sym,TRUE,NIL, 0));
7021  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
7022  abort_with_fatal_error(thisAgent, msg);
7023  }
7024  push(thisAgent, rf, funcall_list);
7025  count = reteload_eight_bytes(f);
7026  while (count--) {
7027  temp = reteload_rhs_value(thisAgent,f);
7028  push(thisAgent, temp, funcall_list);
7029  }
7030  funcall_list = destructively_reverse_list (funcall_list);
7031  rv = funcall_list_to_rhs_value (funcall_list);
7032  break;
7033  case 2:
7034  field_num = reteload_one_byte(f);
7035  levels_up = reteload_two_bytes(f);
7036  rv = reteloc_to_rhs_value (field_num, static_cast<rete_node_level>(levels_up));
7037  break;
7038  case 3:
7039  i = reteload_eight_bytes(f);
7040  update_max_rhs_unbound_variables (thisAgent, i+1);
7041  rv = unboundvar_to_rhs_value (i);
7042  break;
7043  default:
7044  { char msg[BUFFER_MSG_SIZE];
7045  strncpy (msg, "Internal error (file corrupted?): bad rhs_value type\n", BUFFER_MSG_SIZE);
7046  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
7047  abort_with_fatal_error(thisAgent, msg);
7048  }
7049  rv = NIL; /* unreachable, but without it gcc -Wall warns */
7050  }
7051  return rv;
7052 }
7053 
7054 /* ----------------------------------------------------------------------
7055  Save/Load RHS Actions
7056 
7057  Record for a single RHS action:
7058  1 byte: type
7059  1 byte: preference type
7060  1 byte: support
7061  for FUNCALL_ACTION's: rhs value record for value
7062  for MAKE_ACTION's: rhs value records for id, attr, value,
7063  and referent if binary
7064 
7065  Record for a list of RHS actions:
7066  4 bytes: number of RHS actions in the list
7067  record for each one (as above)
7068 ---------------------------------------------------------------------- */
7069 
7070 void retesave_rhs_action (action *a, FILE* f) {
7071  retesave_one_byte (a->type,f);
7073  retesave_one_byte (a->support,f);
7074  if (a->type==FUNCALL_ACTION) {
7075  retesave_rhs_value (a->value,f);
7076  } else { /* MAKE_ACTION's */
7077  retesave_rhs_value (a->id,f);
7078  retesave_rhs_value (a->attr,f);
7079  retesave_rhs_value (a->value,f);
7082  }
7083 }
7084 
7085 action *reteload_rhs_action (agent* thisAgent, FILE* f) {
7086  action *a;
7087 
7088  allocate_with_pool (thisAgent, &thisAgent->action_pool, &a);
7089  a->type = reteload_one_byte(f);
7091  a->support = reteload_one_byte(f);
7092  if (a->type==FUNCALL_ACTION) {
7093  a->id = NIL; a->attr = NIL; a->referent = NIL;
7094  a->value = reteload_rhs_value(thisAgent,f);
7095  } else { /* MAKE_ACTION's */
7096  a->id = reteload_rhs_value(thisAgent,f);
7097  a->attr = reteload_rhs_value(thisAgent,f);
7098  a->value = reteload_rhs_value(thisAgent,f);
7100  a->referent = reteload_rhs_value(thisAgent,f);
7101  else
7102  a->referent = NIL;
7103  }
7104  return a;
7105 }
7106 
7107 void retesave_action_list (action *first_a, FILE* f) {
7108  uint64_t i;
7109  action *a;
7110 
7111  for (i=0, a=first_a; a!=NIL; i++, a=a->next);
7112  retesave_eight_bytes (i,f);
7113  for (a=first_a; a!=NIL; a=a->next) retesave_rhs_action (a,f);
7114 }
7115 
7116 action *reteload_action_list (agent* thisAgent, FILE* f) {
7117  action *a, *prev_a, *first_a;
7118  uint64_t count;
7119 
7120  count = reteload_eight_bytes (f);
7121  prev_a = NIL;
7122  first_a = NIL; /* unneeded, but without it gcc -Wall warns here */
7123  while (count--) {
7124  a = reteload_rhs_action (thisAgent,f );
7125  if (prev_a) prev_a->next = a; else first_a = a;
7126  prev_a = a;
7127  }
7128  if (prev_a) prev_a->next = NIL; else first_a = NIL;
7129  return first_a;
7130 }
7131 
7132 /* ----------------------------------------------------------------------
7133  Save/Load Rete Tests
7134 
7135  Record for a single Rete test:
7136  1 byte: test type
7137  1 byte: right_field_num
7138  other data:
7139  for relational test to variable: 3 bytes -- field num (1), levels up (2)
7140  for relational test to constant: 4 bytes -- symindex of the constant
7141  for disjunctions: 4 bytes (number of disjuncts) then list of symindices
7142 
7143  Record for a list of Rete tests:
7144  2 bytes -- number of tests in the list
7145  Rete test records (as above) for each one
7146 ---------------------------------------------------------------------- */
7147 
7148 void retesave_rete_test (rete_test *rt, FILE* f) {
7149  int i; cons *c;
7150 
7151  retesave_one_byte (rt->type,f);
7154  retesave_eight_bytes(rt->data.constant_referent->common.a.retesave_symindex, f);
7155  } else if (test_is_variable_relational_test(rt->type)) {
7158  } else if (rt->type==DISJUNCTION_RETE_TEST) {
7159  for (i=0, c=rt->data.disjunction_list; c!=NIL; i++, c=c->rest);
7160  retesave_two_bytes (static_cast<uint16_t>(i),f);
7161  for (c=rt->data.disjunction_list; c!=NIL; c=c->rest)
7162  retesave_eight_bytes (static_cast<Symbol *>(c->first)->common.a.retesave_symindex,f);
7163  }
7164 }
7165 
7166 rete_test *reteload_rete_test (agent* thisAgent, FILE* f) {
7167  rete_test *rt;
7168  Symbol *sym;
7169  uint64_t count;
7170  list *temp;
7171 
7172  allocate_with_pool (thisAgent, &thisAgent->rete_test_pool, &rt);
7173  rt->type = reteload_one_byte(f);
7175 
7179  } else if (test_is_variable_relational_test(rt->type)) {
7182  } else if (rt->type==DISJUNCTION_RETE_TEST) {
7183  count = reteload_two_bytes(f);
7184  temp = NIL;
7185  while (count--) {
7186  sym = reteload_symbol_from_index(thisAgent,f);
7187  symbol_add_ref (sym);
7188  push(thisAgent, sym, temp);
7189  }
7191  }
7192  return rt;
7193 }
7194 
7195 void retesave_rete_test_list (rete_test *first_rt, FILE* f) {
7196  uint64_t i;
7197  rete_test *rt;
7198 
7199  for (i=0, rt=first_rt; rt!=NIL; i++, rt=rt->next);
7200  retesave_two_bytes (static_cast<uint16_t>(i),f);
7201  for (rt=first_rt; rt!=NIL; rt=rt->next)
7202  retesave_rete_test (rt,f);
7203 }
7204 
7205 rete_test *reteload_rete_test_list (agent* thisAgent, FILE* f) {
7206  rete_test *rt, *prev_rt, *first;
7207  uint64_t count;
7208 
7209  prev_rt = NIL;
7210  first = NIL; /* unneeded, but without it gcc -Wall warns here */
7211  count = reteload_two_bytes(f);
7212  while (count--) {
7213  rt = reteload_rete_test(thisAgent,f);
7214  if (prev_rt) prev_rt->next = rt; else first = rt;
7215  prev_rt = rt;
7216  }
7217  if (prev_rt) prev_rt->next = NIL; else first = NIL;
7218  return first;
7219 }
7220 
7221 /* ----------------------------------------------------------------------
7222  Save/Load Rete Nodes
7223 
7224  These routines save/reload data for Rete nodes (and their descendents).
7225  Retesave_children_of_node() writes out the records for the children
7226  of a given node (and their descendents). Retesave_rete_node_and_children()
7227  writes out the record for a given node (which includes the records for all
7228  its descendents). The records have the following format:
7229 
7230  Node record:
7231  1 byte: node type
7232  data for node:
7233  posneg nodes: if hashed (and not P): 3 bytes: hash field num, levels up
7234  4 bytes -- index of alpha memory
7235  record for rete test list (for other tests)
7236  pos and mp nodes: 1 byte -- 1 if left unlinked, 0 else
7237  mem nodes: if hashed: 3 bytes -- hash field num, levels up
7238  cn nodes: no record at all (not even node type) -- handled with cn_p
7239  node record instead. Basically, we ignore the cn node when
7240  writing the net, and pretend the cn/cn_p pair is one big
7241  node underneath the subnetwork.
7242  cn_p nodes: number of conjuncts in the NCC
7243  p_nodes: 4 bytes: name of production (symindex)
7244  1 byte (0 or 1): flag -- is there a documentation string
7245  if yes: documentation string (null-terminated string)
7246  1 byte: type
7247  1 byte: declared support
7248  record for the list of RHS actions
7249  4 bytes: number of RHS unbound variables
7250  RHS unbound variables (symindices for each one)
7251  1 byte (0 or 1): flag -- is there node_varnames info?
7252  if yes: node_varnames records for this production
7253  4 bytes: number of children
7254  node records for each child
7255 
7256  Note that we write out a flag indicating whether join nodes are
7257  currently left-unlinked or not. This is for the join nodes underneath
7258  a huge fan-out from a beta memory -- most of these will be left-unlinked.
7259  Since by default we right-unlink newly-created nodes rather than
7260  left-unlinking them, without special handling these nodes would be
7261  right-unlinked when we reload the network. This would lead to a large
7262  startup penalty due to a large number of initial null left activations.
7263 
7264  Reteload_node_and_children() reads in the record for a given node and
7265  all its descendents, and reconstructs the Rete network structures.
7266 ---------------------------------------------------------------------- */
7267 
7268 void retesave_rete_node_and_children (agent* thisAgent, rete_node *node, FILE* f);
7269 
7270 void retesave_children_of_node (agent* thisAgent, rete_node *node, FILE* f) {
7271  uint64_t i; rete_node *child;
7272 
7273  /* --- Count number of non-CN-node children. --- */
7274  for (i=0, child=node->first_child; child; child=child->next_sibling)
7275  if (child->node_type != CN_BNODE) i++;
7276  retesave_eight_bytes (i,f);
7277 
7278  /* --- Write out records for all the node's children except CN's. --- */
7279  for (child=node->first_child; child; child=child->next_sibling)
7280  if (child->node_type != CN_BNODE)
7281  retesave_rete_node_and_children (thisAgent, child, f);
7282 }
7283 
7284 void retesave_rete_node_and_children (agent* thisAgent, rete_node *node, FILE* f) {
7285  uint64_t i;
7286  production *prod;
7287  cons *c;
7288  rete_node *temp;
7289 
7290  if (node->node_type == CN_BNODE) return; /* ignore CN nodes */
7291 
7292  retesave_one_byte (node->node_type,f);
7293 
7294  switch (node->node_type) {
7295  case MEMORY_BNODE:
7298  /* ... and fall through to the next case below ... */
7299  case UNHASHED_MEMORY_BNODE:
7300  break;
7301 
7302  case MP_BNODE:
7305  /* ... and fall through to the next case below ... */
7306  case UNHASHED_MP_BNODE:
7309  retesave_one_byte (static_cast<byte>(node->a.np.is_left_unlinked ? 1 : 0),f);
7310  break;
7311 
7312  case POSITIVE_BNODE:
7316  retesave_one_byte (static_cast<byte>(node_is_left_unlinked(node) ? 1 : 0),f);
7317  break;
7318 
7319  case NEGATIVE_BNODE:
7322  /* ... and fall through to the next case below ... */
7326  break;
7327 
7328  case CN_PARTNER_BNODE:
7329  i=0;
7330  temp = real_parent_node (node);
7331  while (temp != node->b.cn.partner->parent) {
7332  temp = real_parent_node (temp);
7333  i++;
7334  }
7335  retesave_eight_bytes (i,f);
7336  break;
7337 
7338  case P_BNODE:
7339  prod = node->b.p.prod;
7340  retesave_eight_bytes (prod->name->common.a.retesave_symindex,f);
7341  if (prod->documentation) {
7342  retesave_one_byte (1,f);
7343  retesave_string (prod->documentation,f);
7344  } else {
7345  retesave_one_byte (0,f);
7346  }
7347  retesave_one_byte (prod->type,f);
7349  retesave_action_list (prod->action_list,f );
7350  for (i=0, c=prod->rhs_unbound_variables; c!=NIL; i++, c=c->rest);
7351  retesave_eight_bytes (i,f);
7352  for (c=prod->rhs_unbound_variables; c!=NIL; c=c->rest)
7353  retesave_eight_bytes (static_cast<Symbol *>(c->first)->common.a.retesave_symindex,f);
7354  if (node->b.p.parents_nvn) {
7355  retesave_one_byte (1,f);
7356  retesave_node_varnames (node->b.p.parents_nvn, node->parent, f);
7357  } else {
7358  retesave_one_byte (0,f);
7359  }
7360  break;
7361 
7362  default:
7363  {char msg[BUFFER_MSG_SIZE];
7364  SNPRINTF (msg, BUFFER_MSG_SIZE,
7365  "Internal error: fastsave found node type %d\n", node->node_type);
7366  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
7367  abort_with_fatal_error(thisAgent, msg);
7368  }
7369  } /* end of switch statement */
7370 
7371  /* --- For cn_p nodes, write out the CN node's children instead --- */
7372  if (node->node_type == CN_PARTNER_BNODE) node = node->b.cn.partner;
7373  /* --- Write out records for all the node's children. --- */
7374  retesave_children_of_node (thisAgent, node,f);
7375 }
7376 
7377 void reteload_node_and_children (agent* thisAgent, rete_node *parent, FILE* f) {
7378  byte type, left_unlinked_flag;
7379  rete_node *New, *ncc_top;
7380  uint64_t count;
7381  alpha_mem *am;
7382  production *prod;
7383  Symbol *sym;
7384  list *ubv_list;
7385  var_location left_hash_loc;
7386  rete_test *other_tests;
7387 
7388  type = reteload_one_byte(f);
7389 
7390  /*
7391  Initializing the left_hash_loc structure to flag values.
7392  It gets passed into some of the various make_new_??? functions
7393  below but is never used (hopefully) for UNHASHED node types.
7394  */
7395  left_hash_loc.field_num = static_cast<byte>(-1);
7396  left_hash_loc.levels_up = static_cast<rete_node_level>(-1);
7397 
7398  switch (type) {
7399  case MEMORY_BNODE:
7400  left_hash_loc.field_num = reteload_one_byte(f);
7401  left_hash_loc.levels_up = static_cast<rete_node_level>(reteload_two_bytes(f));
7402  /* ... and fall through to the next case below ... */
7403  case UNHASHED_MEMORY_BNODE:
7404  New = make_new_mem_node (thisAgent, parent, type, left_hash_loc);
7405  break;
7406 
7407  case MP_BNODE:
7408  left_hash_loc.field_num = reteload_one_byte(f);
7409  left_hash_loc.levels_up = static_cast<rete_node_level>(reteload_two_bytes(f));
7410  /* ... and fall through to the next case below ... */
7411  case UNHASHED_MP_BNODE:
7412  am = reteload_am_from_index(thisAgent,f);
7413  am->reference_count++;
7414  other_tests = reteload_rete_test_list(thisAgent,f);
7415  left_unlinked_flag = reteload_one_byte(f);
7416  New = make_new_mp_node (thisAgent, parent, type, left_hash_loc, am, other_tests,
7417  left_unlinked_flag != 0);
7418  break;
7419 
7420  case POSITIVE_BNODE:
7422  am = reteload_am_from_index(thisAgent,f);
7423  am->reference_count++;
7424  other_tests = reteload_rete_test_list(thisAgent,f);
7425  left_unlinked_flag = reteload_one_byte(f);
7426  New = make_new_positive_node (thisAgent, parent, type, am, other_tests,
7427  left_unlinked_flag != 0);
7428  break;
7429 
7430  case NEGATIVE_BNODE:
7431  left_hash_loc.field_num = reteload_one_byte(f);
7432  left_hash_loc.levels_up = static_cast<rete_node_level>(reteload_two_bytes(f));
7433  /* ... and fall through to the next case below ... */
7435  am = reteload_am_from_index(thisAgent,f);
7436  am->reference_count++;
7437  other_tests = reteload_rete_test_list(thisAgent,f);
7438  New = make_new_negative_node (thisAgent, parent, type, left_hash_loc, am,other_tests);
7439  break;
7440 
7441  case CN_PARTNER_BNODE:
7442  count = reteload_eight_bytes(f);
7443  ncc_top = parent;
7444  while (count--) ncc_top = real_parent_node (ncc_top);
7445  New = make_new_cn_node (thisAgent, ncc_top, parent);
7446  break;
7447 
7448  case P_BNODE:
7449  allocate_with_pool (thisAgent, &thisAgent->production_pool, &prod);
7450  prod->reference_count = 1;
7451  prod->firing_count = 0;
7452  prod->trace_firings = FALSE;
7453  prod->instantiations = NIL;
7454  prod->filename = NIL;
7455  prod->p_node = NIL;
7456  prod->interrupt = FALSE;
7457 
7458  sym = reteload_symbol_from_index (thisAgent,f);
7459  symbol_add_ref (sym);
7460  prod->name = sym;
7461  sym->sc.production = prod;
7462  if (reteload_one_byte(f)) {
7463  reteload_string(f);
7465  } else {
7466  prod->documentation = NIL;
7467  }
7468  prod->type = reteload_one_byte (f);
7469  prod->declared_support = reteload_one_byte (f);
7470  prod->action_list = reteload_action_list (thisAgent,f);
7471 
7472  count = reteload_eight_bytes (f);
7473  update_max_rhs_unbound_variables (thisAgent, count);
7474  ubv_list = NIL;
7475  while (count--) {
7476  sym = reteload_symbol_from_index(thisAgent,f);
7477  symbol_add_ref (sym);
7478  push(thisAgent, sym, ubv_list);
7479  }
7481 
7483  prod, next, prev);
7484  thisAgent->num_productions_of_type[prod->type]++;
7485 
7486  // Soar-RL stuff
7487  prod->rl_update_count = 0.0;
7488  prod->rl_delta_bar_delta_beta = -3.0;
7489  prod->rl_delta_bar_delta_h = 0.0;
7490  prod->rl_update_count = 0;
7491  prod->rl_rule = false;
7492  prod->rl_ecr = 0.0;
7493  prod->rl_efr = 0.0;
7494  if ( ( prod->type != JUSTIFICATION_PRODUCTION_TYPE ) && ( prod->type != TEMPLATE_PRODUCTION_TYPE ) )
7495  {
7496  prod->rl_rule = rl_valid_rule( prod );
7497  if ( prod->rl_rule )
7498  {
7500 
7501  if ( prod->documentation )
7502  {
7503  rl_rule_meta( thisAgent, prod );
7504  }
7505  }
7506  }
7507  prod->rl_template_conds = NIL;
7509 
7510  New = make_new_production_node (thisAgent, parent, prod);
7512  if (reteload_one_byte(f)) {
7513  New->b.p.parents_nvn = reteload_node_varnames (thisAgent, parent,f);
7514  } else {
7515  New->b.p.parents_nvn = NIL;
7516  }
7517 
7518  /* --- call new node's add_left routine with all the parent's tokens --- */
7519  update_node_with_matches_from_above (thisAgent, New);
7520 
7521  /* --- invoke callback on the production --- */
7522  soar_invoke_callbacks (thisAgent, PRODUCTION_JUST_ADDED_CALLBACK, static_cast<soar_call_data>(prod));
7523 
7524  break;
7525 
7526  default:
7527  { char msg[BUFFER_MSG_SIZE];
7528  SNPRINTF (msg, BUFFER_MSG_SIZE,"Internal error: fastload found node type %d\n", type);
7529  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
7530  abort_with_fatal_error(thisAgent, msg);
7531  New = NIL; /* unreachable, but without it gcc -Wall warns here */
7532  }
7533  } /* end of switch statement */
7534 
7535  /* --- read in the children of the node --- */
7536  count = reteload_eight_bytes(f);
7537  while (count--) reteload_node_and_children (thisAgent, New,f);
7538 }
7539 
7540 /* ----------------------------------------------------------------------
7541  Save/Load The Whole Net
7542 
7543  Save_rete_net() and load_rete_net() save and load everything to and
7544  from the given (already open) files. They return TRUE if successful,
7545  FALSE if any error occurred.
7546 ---------------------------------------------------------------------- */
7547 
7548 Bool save_rete_net (agent* thisAgent, FILE *dest_file, Bool use_rete_net_64) {
7549 
7550  /* --- make sure there are no justifications present --- */
7552  print (thisAgent, "Internal error: save_rete_net() with justifications present.\n");
7553  return FALSE;
7554  }
7555 
7556  rete_fs_file = dest_file;
7557  rete_net_64 = use_rete_net_64;
7558  uint8_t version = use_rete_net_64 ? 4 : 3;
7559 
7560  retesave_string ("SoarCompactReteNet\n",dest_file);
7561  retesave_one_byte (version,dest_file); /* format version number */
7562  retesave_symbol_table(thisAgent, dest_file);
7563  retesave_alpha_memories(thisAgent,dest_file);
7564  retesave_children_of_node (thisAgent, thisAgent->dummy_top_node,dest_file);
7565  return TRUE;
7566 }
7567 
7568 Bool load_rete_net (agent* thisAgent, FILE *source_file) {
7569  int format_version_num;
7570  uint64_t i, count;
7571 
7572  /* RDF: 20020814 RDF Cleaning up the agent working memory and production
7573  memory to avoid unecessary errors in this function. */
7574  reinitialize_soar(thisAgent);
7575  excise_all_productions(thisAgent, TRUE);
7576 
7577  /* DONE clearing old productions */
7578 
7579  /* --- check for empty system --- */
7580  if (thisAgent->all_wmes_in_rete) {
7581  print (thisAgent, "Internal error: load_rete_net() called with nonempty WM.\n");
7582  return FALSE;
7583  }
7584  for (i=0; i<NUM_PRODUCTION_TYPES; i++)
7585  if (thisAgent->num_productions_of_type[i]) {
7586  print (thisAgent, "Internal error: load_rete_net() called with nonempty PM.\n");
7587  return FALSE;
7588  }
7589 
7590  // BADBAD: this is global, used in retesave_one_byte
7591  rete_fs_file = source_file;
7592 
7593  /* --- read file header, make sure it's a valid file --- */
7594  reteload_string(source_file);
7595  if (strcmp(reteload_string_buf,"SoarCompactReteNet\n")) {
7596  print (thisAgent, "This file isn't a Soar fastsave file.\n");
7597  return FALSE;
7598  }
7599  format_version_num = reteload_one_byte(source_file);
7600  switch(format_version_num)
7601  {
7602  case 3:
7603  // Since there's already a global, I'm putting the 32- or 64-bit switch out there globally
7604  rete_net_64 = FALSE; // used by reteload_eight_bytes
7605  break;
7606  case 4:
7607  // Since there's already a global, I'm putting the 32- or 64-bit switch out there globally
7608  rete_net_64 = TRUE; // used by reteload_eight_bytes
7609  break;
7610  default:
7611  print (thisAgent, "This file is in a format (version %d) I don't understand.\n", format_version_num);
7612  return FALSE;
7613  }
7614 
7615  reteload_all_symbols(thisAgent,source_file);
7616  reteload_alpha_memories(thisAgent,source_file);
7617  count = reteload_eight_bytes(source_file);
7618  while (count--) reteload_node_and_children (thisAgent, thisAgent->dummy_top_node,source_file);
7619 
7620  /* --- clean up auxilliary tables --- */
7621  reteload_free_am_table(thisAgent);
7622  reteload_free_symbol_table(thisAgent);
7623 
7624  /* RDF: 20020814 Now adding the top state and io symbols and wmes */
7625  init_agent_memory(thisAgent);
7626 
7627  return TRUE;
7628 }
7629 
7630 
7631 
7632 
7633 
7634 
7635 
7636 
7637 
7638 
7639 
7640 
7641 
7642 
7643 /* **********************************************************************
7644 
7645  SECTION 18: Statistics and User Interface Utilities
7646 
7647  EXTERNAL INTERFACE:
7648  Count_rete_tokens_for_production() returns a count of the number of
7649  tokens currently in use for the given production.
7650  Print_partial_match_information(), print_match_set(), and
7651  print_rete_statistics() do printouts for various interface routines.
7652  Get_node_count_statistic() is for TclSoar to get an individual stat.
7653 ********************************************************************** */
7654 
7655 /* ----------------------------------------------------------------------
7656  Count Rete Tokens For Production
7657 
7658  Returns a count of the number of tokens currently in use for the given
7659  production. The count does not include:
7660  tokens in the p_node (i.e., tokens representing complete matches)
7661  local join result tokens on (real) tokens in negative/NCC nodes
7662 ---------------------------------------------------------------------- */
7663 
7664 uint64_t count_rete_tokens_for_production (agent* thisAgent, production *prod) {
7665  uint64_t count;
7666  rete_node *node;
7667  token *tok;
7668 
7669  if (! prod->p_node) return 0;
7670  node = prod->p_node->parent;
7671  count = 0;
7672  while (node!=thisAgent->dummy_top_node) {
7673  if ((node->node_type != POSITIVE_BNODE) &&
7674  (node->node_type != UNHASHED_POSITIVE_BNODE)) {
7675  for (tok=node->a.np.tokens; tok!=NIL; tok=tok->next_of_node) count++;
7676  }
7677  if (node->node_type==CN_BNODE) node = node->b.cn.partner->parent;
7678  else node = node->parent;
7679  }
7680  return count;
7681 }
7682 
7683 /* --------------------------------------------------------------------
7684  Rete Statistics
7685 
7686  Get_all_node_count_stats() sets up the three arrays actual[],
7687  if_no_merging[], and if_no_sharing[] to contain the current node
7688  counts of each type of node. Actual[] gives the actual count.
7689  If_no_merging[] tells what the count would be if we never merged
7690  Mem and Pos nodes into MP nodes. If_no_sharing[] tells what the
7691  count would be if we didn't share beta nodes across productions AND
7692  didn't merge Mem+Pos into MP nodes. (I did it this way so we can
7693  tell what the static sharing factor is *without* having to worry
7694  about the merging stuff, which is not a standard Rete technique.)
7695 
7696  Print_node_count_statistics() prints everything out.
7697  Get_node_count_statistic() is the main routine for TclSoar.
7698  Print_rete_statistics() is the main routine for non-TclSoar.
7699 -------------------------------------------------------------------- */
7700 void init_bnode_type_names(agent* /*thisAgent*/)
7701 {
7702  static bool bnode_initialzied = false;
7703 
7704  //
7705  // This should be properly locked.
7706  //
7707  if(!bnode_initialzied)
7708  {
7709  bnode_type_names[UNHASHED_MEMORY_BNODE] = "unhashed memory";
7710  bnode_type_names[MEMORY_BNODE] = "memory";
7711  bnode_type_names[UNHASHED_MP_BNODE] = "unhashed mem-pos";
7712  bnode_type_names[MP_BNODE] = "mem-pos";
7713  bnode_type_names[UNHASHED_POSITIVE_BNODE] = "unhashed positive";
7714  bnode_type_names[POSITIVE_BNODE] = "positive";
7715  bnode_type_names[NEGATIVE_BNODE] = "negative";
7716  bnode_type_names[UNHASHED_NEGATIVE_BNODE] = "unhashed negative";
7717  bnode_type_names[DUMMY_TOP_BNODE] = "dummy top";
7718  bnode_type_names[DUMMY_MATCHES_BNODE] = "dummy matches";
7719  bnode_type_names[CN_BNODE] = "conj. neg.";
7720  bnode_type_names[CN_PARTNER_BNODE] = "conj. neg. partner";
7721  bnode_type_names[P_BNODE] = "production";
7722 
7723  bnode_initialzied = true;
7724  }
7725 }
7726 
7727 
7728 
7729 void get_all_node_count_stats (agent* thisAgent) {
7730  int i;
7731 
7732  //
7733  // This sanity check should no longer be neccessary.
7734  //
7735  /* --- sanity check: make sure we've got names for all the bnode types --- */
7736  //for (i=0; i<256; i++)
7737  // if (thisAgent->rete_node_counts[i] &&
7738  // (*bnode_type_names[i] == 0)) {
7739  // print (thisAgent, "Internal eror: unknown node type [%d] has nonzero count.\n",i);
7740  // }
7741  init_bnode_type_names(thisAgent);
7742 
7743  /* --- calculate the three arrays --- */
7744  for (i=0; i<256; i++) {
7745  thisAgent->actual[i] = thisAgent->rete_node_counts[i];
7746  thisAgent->if_no_merging[i] = thisAgent->rete_node_counts[i];
7747  thisAgent->if_no_sharing[i] = thisAgent->rete_node_counts_if_no_sharing[i];
7748  }
7749 
7750  /* --- don't want the dummy matches node to show up as a real node --- */
7751  thisAgent->actual[DUMMY_MATCHES_BNODE] = 0;
7752  thisAgent->if_no_merging[DUMMY_MATCHES_BNODE] = 0;
7753  thisAgent->if_no_sharing[DUMMY_MATCHES_BNODE] = 0;
7754 
7755  /* --- If no merging or sharing, each MP node would be 1 Mem + 1 Pos --- */
7756  thisAgent->if_no_merging[MEMORY_BNODE] += thisAgent->if_no_merging[MP_BNODE];
7757  thisAgent->if_no_merging[POSITIVE_BNODE] += thisAgent->if_no_merging[MP_BNODE];
7758  thisAgent->if_no_merging[MP_BNODE] = 0;
7761  thisAgent->if_no_merging[UNHASHED_MP_BNODE] = 0;
7762  thisAgent->if_no_sharing[MEMORY_BNODE] += thisAgent->if_no_sharing[MP_BNODE];
7763  thisAgent->if_no_sharing[POSITIVE_BNODE] += thisAgent->if_no_sharing[MP_BNODE];
7764  thisAgent->if_no_sharing[MP_BNODE] = 0;
7767  thisAgent->if_no_sharing[UNHASHED_MP_BNODE] = 0;
7768 }
7769 
7770 /* Returns 0 if result invalid, 1 if result valid */
7772  char * node_type_name,
7773  char * column_name,
7774  uint64_t * result)
7775 {
7776  int i;
7777  uint64_t tot;
7778 
7779  get_all_node_count_stats(thisAgent);
7780 
7781  if (!strcmp("total", node_type_name))
7782  {
7783  if (!strcmp("actual", column_name))
7784  {
7785  for (tot=0, i=0; i<256; i++) tot+=thisAgent->actual[i];
7786  *result = tot;
7787  }
7788  else if (!strcmp("if-no-merging", column_name))
7789  {
7790  for (tot=0, i=0; i<256; i++) tot+=thisAgent->if_no_merging[i];
7791  *result = tot;
7792  }
7793 #ifdef SHARING_FACTORS
7794  else if (!strcmp("if-no-sharing", column_name))
7795  {
7796  for (tot=0, i=0; i<256; i++) tot+=thisAgent->if_no_sharing[i];
7797  *result = tot;
7798  }
7799 #endif
7800  else
7801  {
7802  return 0;
7803  }
7804  }
7805  else
7806  {
7807  for (i=0; i<256; i++)
7808  if (!strcmp(bnode_type_names[i], node_type_name))
7809  {
7810  if (!strcmp("actual", column_name))
7811  {
7812  *result = thisAgent->actual[i];
7813  }
7814  else if (!strcmp("if-no-merging", column_name))
7815  {
7816  *result = thisAgent->if_no_merging[i];
7817  }
7818 #ifdef SHARING_FACTORS
7819  else if (!strcmp("if-no-sharing", column_name))
7820  {
7821  *result = thisAgent->if_no_sharing[i];
7822  }
7823 #endif
7824  else
7825  {
7826  return 0;
7827  }
7828  return 1;
7829  }
7830  return 0;
7831  }
7832 
7833  return 1;
7834 }
7835 
7836 /* ----------------------------------------------------------------------
7837 
7838  Partial Match Information: Utilities
7839 
7840  To get info on partial matches for a given production, we use several
7841  helper routines. Get_all_left_tokens_emerging_from_node() returns
7842  the tokens (chained via their next_of_node links) that are currently
7843  the output resulting from a given node. (I'm not sure, but I think
7844  that with the new tree-based removal, this routine is no longer needed,
7845  as the tokens are always available on a list on some child node, but
7846  I didn't bother rewriting these routines.) The routine obtains these
7847  tokens by temporarily making the "dummy_matches_node" a child of the
7848  given node, and then calling update_node_with_matches_from_above().
7849  The dummy_matches_node_left_addition() routine then gets activated
7850  for each token, and it builds up the list. When the caller is done,
7851  it should call deallocate_token_list() to free up this list.
7852 
7853  Print_whole_token() prints out a given token in the format appropriate
7854  for the given wme_trace_type: either a list of timetags, a list of
7855  WMEs, or no printout at all.
7856 ---------------------------------------------------------------------- */
7857 
7858 void dummy_matches_node_left_addition (agent* thisAgent, rete_node * /*node*/, token *tok, wme *w)
7859 {
7860  token *New;
7861 
7862  /* --- just add a token record to dummy_matches_node_tokens --- */
7863  allocate_with_pool (thisAgent, &thisAgent->token_pool, &New);
7864  New->node = NIL;
7865  New->parent = tok;
7866  New->w = w;
7867  New->next_of_node = thisAgent->dummy_matches_node_tokens;
7868  thisAgent->dummy_matches_node_tokens = New;
7869 }
7870 
7872 {
7873  token *result;
7874  rete_node dummy_matches_node;
7875 
7876  thisAgent->dummy_matches_node_tokens = NIL;
7877  dummy_matches_node.node_type = DUMMY_MATCHES_BNODE;
7878  dummy_matches_node.parent = node;
7879  dummy_matches_node.first_child = NIL;
7880  dummy_matches_node.next_sibling = NIL;
7881  update_node_with_matches_from_above (thisAgent, &dummy_matches_node);
7882  result = thisAgent->dummy_matches_node_tokens;
7883  return result;
7884 }
7885 
7886 void deallocate_token_list (agent* thisAgent, token *t) {
7887  token *next;
7888 
7889  while (t) {
7890  next = t->next_of_node;
7891  free_with_pool (&thisAgent->token_pool, t);
7892  t = next;
7893  }
7894 }
7895 
7896 void print_whole_token (agent* thisAgent, token *t, wme_trace_type wtt) {
7897  if (t==thisAgent->dummy_top_token) return;
7898  print_whole_token (thisAgent, t->parent, wtt);
7899  if (t->w) {
7900  if (wtt==TIMETAG_WME_TRACE) print (thisAgent, "%lu", t->w->timetag);
7901  else if (wtt==FULL_WME_TRACE) print_wme (thisAgent, t->w);
7902  if (wtt!=NONE_WME_TRACE) print (thisAgent, " ");
7903  }
7904 }
7905 
7906 /* ----------------------------------------------------------------------
7907 
7908  Printing Partial Match Information
7909 
7910  This is for the "matches" command. Print_partial_match_information()
7911  is called from the interface routine; ppmi_aux() is a helper function.
7912  We first call p_node_to_conditions_and_nots() to get the condition
7913  list for the LHS. We then (conceptually) start at the top of the
7914  net, with the first condition; for each condition, we collect the
7915  tokens output by the previous node, to find the number of matches here.
7916  We print the # of matches here; print this condition. If this is
7917  the first cond that didn't have any match, then we also print its
7918  matches-for-left and matches-for-right.
7919 
7920  Of course, we can't actually start at the top of the net and work our
7921  way down, since we'd have no way to find our way the the correct
7922  p-node. So instead, we use a recursive procedure that basically does
7923  the same thing.
7924 ---------------------------------------------------------------------- */
7925 
7926 /* --- Print stuff for given node and higher, up to but not including the
7927  cutoff node. Return number of matches at the given node/cond. --- */
7928 int64_t ppmi_aux (agent* thisAgent, /* current agent */
7929  rete_node *node, /* current node */
7930  rete_node *cutoff, /* don't print cutoff node or any higher */
7931  condition *cond, /* cond for current node */
7932  wme_trace_type wtt, /* what type of printout to use */
7933  int indent) { /* number of spaces indent */
7934  token *tokens, *t, *parent_tokens;
7935  right_mem *rm;
7936  int64_t matches_one_level_up;
7937  int64_t matches_at_this_level;
7938 #define MATCH_COUNT_STRING_BUFFER_SIZE 20
7939  char match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE];
7940  rete_node *parent;
7941 
7942  /* --- find the number of matches for this condition --- */
7943  tokens = get_all_left_tokens_emerging_from_node (thisAgent, node);
7944  matches_at_this_level = 0;
7945  for (t=tokens; t!=NIL; t=t->next_of_node) matches_at_this_level++;
7946  deallocate_token_list (thisAgent, tokens);
7947 
7948  /* --- if we're at the cutoff node, we're done --- */
7949  if (node==cutoff) return matches_at_this_level;
7950 
7951  /* --- do stuff higher up --- */
7952  parent = real_parent_node(node);
7953  matches_one_level_up = ppmi_aux (thisAgent, parent, cutoff,
7954  cond->prev, wtt, indent);
7955 
7956  /* --- Form string for current match count: If an earlier cond had no
7957  matches, just leave it blank; if this is the first 0, use ">>>>" --- */
7958  if (! matches_one_level_up) {
7959  strncpy (match_count_string, " ", MATCH_COUNT_STRING_BUFFER_SIZE);
7960  match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
7961  } else if (! matches_at_this_level) {
7962  strncpy (match_count_string, ">>>>", MATCH_COUNT_STRING_BUFFER_SIZE);
7963  match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
7964  } else {
7965  SNPRINTF (match_count_string, MATCH_COUNT_STRING_BUFFER_SIZE, "%4ld", static_cast<long int>(matches_at_this_level));
7966  match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
7967  }
7968 
7969  /* --- print extra indentation spaces --- */
7970  print_spaces (thisAgent, indent);
7971 
7972  if (cond->type==CONJUNCTIVE_NEGATION_CONDITION) {
7973  /* --- recursively print match counts for the NCC subconditions --- */
7974  print (thisAgent, " -{\n");
7975  ppmi_aux (thisAgent, real_parent_node(node->b.cn.partner),
7976  parent,
7977  cond->data.ncc.bottom,
7978  wtt,
7979  indent+5);
7980  print_spaces (thisAgent, indent);
7981  print (thisAgent, "%s }\n", match_count_string);
7982  } else {
7983  print (thisAgent, "%s", match_count_string);
7984  print_condition (thisAgent, cond);
7985  print (thisAgent, "\n");
7986  /* --- if this is the first match-failure (0 matches), print info on
7987  matches for left and right --- */
7988  if (matches_one_level_up && (!matches_at_this_level)) {
7989  if (wtt!=NONE_WME_TRACE) {
7990  print_spaces (thisAgent, indent);
7991  print (thisAgent, "*** Matches For Left ***\n");
7992  parent_tokens = get_all_left_tokens_emerging_from_node (thisAgent, parent);
7993  for (t=parent_tokens; t!=NIL; t=t->next_of_node) {
7994  print_spaces (thisAgent, indent);
7995  print_whole_token (thisAgent, t, wtt);
7996  print (thisAgent, "\n");
7997  }
7998  deallocate_token_list (thisAgent, parent_tokens);
7999  print_spaces (thisAgent, indent);
8000  print (thisAgent, "*** Matches for Right ***\n");
8001  print_spaces (thisAgent, indent);
8002  for (rm=node->b.posneg.alpha_mem_->right_mems; rm!=NIL;
8003  rm=rm->next_in_am) {
8004  if (wtt==TIMETAG_WME_TRACE) print (thisAgent, "%lu", rm->w->timetag);
8005  else if (wtt==FULL_WME_TRACE) print_wme (thisAgent, rm->w);
8006  print (thisAgent, " ");
8007  }
8008  print (thisAgent, "\n");
8009  }
8010  } /* end of if (matches_one_level_up ...) */
8011  }
8012 
8013  /* --- return result --- */
8014  return matches_at_this_level;
8015 }
8016 
8018  wme_trace_type wtt) {
8019  condition *top_cond, *bottom_cond;
8020  int64_t n;
8021  token *tokens, *t;
8022 
8023  p_node_to_conditions_and_nots (thisAgent, p_node, NIL, NIL, &top_cond, &bottom_cond,
8024  NIL, NIL);
8025  n = ppmi_aux (thisAgent, p_node->parent, thisAgent->dummy_top_node, bottom_cond,
8026  wtt, 0);
8027  print (thisAgent, "\n%d complete matches.\n", n);
8028  if (n && (wtt!=NONE_WME_TRACE)) {
8029  print (thisAgent, "*** Complete Matches ***\n");
8030  tokens = get_all_left_tokens_emerging_from_node (thisAgent, p_node->parent);
8031  for (t=tokens; t!=NIL; t=t->next_of_node) {
8032  print_whole_token (thisAgent, t, wtt);
8033  print (thisAgent, "\n");
8034  }
8035  deallocate_token_list (thisAgent, tokens);
8036  }
8037  deallocate_condition_list (thisAgent, top_cond);
8038 }
8039 
8040 /* ----------------------------------------------------------------------
8041 
8042  Used by the "ms" command -- prints out the current match set.
8043 ---------------------------------------------------------------------- */
8044 
8045 typedef struct match_set_trace {
8047  int count;
8049  /* REW: begin 08.20.97 */
8050  /* Add match goal to the print of the matching production */
8052  /* REW: end 08.20.97 */
8053 } MS_trace;
8054 
8056  MS_trace *tmp;
8057  for(tmp = trace; tmp; tmp=tmp->next) {
8058  if(tmp->sym == sym) return tmp;
8059  }
8060  return 0;
8061 }
8062 
8063 /* REW: begin 10.22.97 */
8065  MS_trace *tmp;
8066  for(tmp = trace; tmp; tmp=tmp->next) {
8067  if((tmp->sym == sym) && (goal == tmp->goal)) return tmp;
8068  }
8069  return 0;
8070 }
8071 /* REW: end 10.22.97 */
8072 
8073 void print_match_set (agent* thisAgent, wme_trace_type wtt, ms_trace_type mst) {
8074  ms_change *msc;
8075  token temp_token;
8076  MS_trace *ms_trace = NIL, *tmp;
8077 
8078  /* --- Print assertions --- */
8079 
8080 
8081  /* REW: begin 09.15.96 */
8082  if (mst == MS_ASSERT_RETRACT || mst == MS_ASSERT) {
8083  print (thisAgent, "O Assertions:\n");
8084  for (msc=thisAgent->ms_o_assertions; msc!=NIL; msc=msc->next) {
8085 
8086  if(wtt != NONE_WME_TRACE) {
8087  print_with_symbols (thisAgent, " %y ", msc->p_node->b.p.prod->name);
8088  /* REW: begin 08.20.97 */
8089  /* Add match goal to the print of the matching production */
8090  print_with_symbols(thisAgent, " [%y] ", msc->goal);
8091  /* REW: end 08.20.97 */
8092  temp_token.parent = msc->tok;
8093  temp_token.w = msc->w;
8094  print_whole_token (thisAgent, &temp_token, wtt);
8095  print (thisAgent, "\n");
8096  }
8097  else {
8098  /* REW: begin 10.22.97 */
8099  if((tmp = in_ms_trace_same_goal(msc->p_node->b.p.prod->name,
8100  ms_trace, msc->goal))!=NIL) {
8101  /* REW: end 10.22.97 */
8102  tmp->count++;
8103  }
8104  else {
8105  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace), MISCELLANEOUS_MEM_USAGE));
8106  tmp->sym = msc->p_node->b.p.prod->name;
8107  tmp->count = 1;
8108  tmp->next = ms_trace;
8109  /* REW: begin 08.20.97 */
8110  /* Add match goal to the print of the matching production */
8111  tmp->goal = msc->goal;
8112  /* REW: end 08.20.97 */
8113  ms_trace = tmp;
8114  }
8115  }
8116  }
8117 
8118  if (wtt == NONE_WME_TRACE) {
8119  while (ms_trace) {
8120  tmp = ms_trace; ms_trace = tmp->next;
8121  print_with_symbols (thisAgent, " %y ", tmp->sym);
8122  /* REW: begin 08.20.97 */
8123  /* BUG: for now this will print the goal of the first
8124  assertion inspected, even though there can be multiple
8125  assertions at different levels.
8126  See 2.110 in the OPERAND-CHANGE-LOG. */
8127  print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8128  /* REW: end 08.20.97 */
8129  if (tmp->count > 1)
8130  print(thisAgent, "(%d)\n", tmp->count);
8131  else
8132  print(thisAgent, "\n");
8133  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8134  }
8135  }
8136  }
8137 
8138  if (mst == MS_ASSERT_RETRACT || mst == MS_ASSERT) {
8139  print (thisAgent, "I Assertions:\n");
8140  for (msc=thisAgent->ms_i_assertions; msc!=NIL; msc=msc->next) {
8141 
8142  if(wtt != NONE_WME_TRACE) {
8143  print_with_symbols (thisAgent, " %y ", msc->p_node->b.p.prod->name);
8144  /* REW: begin 08.20.97 */
8145  /* Add match goal to the print of the matching production */
8146  print_with_symbols(thisAgent, " [%y] ", msc->goal);
8147  /* REW: end 08.20.97 */
8148  temp_token.parent = msc->tok;
8149  temp_token.w = msc->w;
8150  print_whole_token (thisAgent, &temp_token, wtt);
8151  print (thisAgent, "\n");
8152  }
8153  else {
8154  /* REW: begin 10.22.97 */
8155  if((tmp = in_ms_trace_same_goal(msc->p_node->b.p.prod->name,
8156  ms_trace, msc->goal))!=NIL) {
8157  /* REW: end 10.22.97 */
8158  tmp->count++;
8159  }
8160  else {
8161  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace),
8163  tmp->sym = msc->p_node->b.p.prod->name;
8164  tmp->count = 1;
8165  tmp->next = ms_trace;
8166  /* REW: begin 08.20.97 */
8167  /* Add match goal to the print of the matching production */
8168  tmp->goal = msc->goal;
8169  /* REW: end 08.20.97 */
8170  ms_trace = tmp;
8171  }
8172  }
8173  }
8174 
8175  if (wtt == NONE_WME_TRACE) {
8176  while (ms_trace) {
8177  tmp = ms_trace; ms_trace = tmp->next;
8178  print_with_symbols (thisAgent, " %y ", tmp->sym);
8179  /* REW: begin 08.20.97 */
8180  /* BUG: for now this will print the goal of the first
8181  assertion inspected, even though there can be multiple
8182  assertions at different levels.
8183  See 2.110 in the OPERAND-CHANGE-LOG. */
8184  print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8185  /* REW: end 08.20.97 */
8186  if (tmp->count > 1)
8187  print(thisAgent, "(%d)\n", tmp->count);
8188  else
8189  print(thisAgent, "\n");
8190  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8191  }
8192  }
8193  }
8194  /* REW: end 09.15.96 */
8195 
8196  /* --- Print retractions --- */
8197  if (mst == MS_ASSERT_RETRACT || mst == MS_RETRACT) {
8198  print (thisAgent, "Retractions:\n");
8199  for (msc=thisAgent->ms_retractions; msc!=NIL; msc=msc->next) {
8200  if(wtt != NONE_WME_TRACE) {
8201  print (thisAgent, " ");
8202  print_instantiation_with_wmes (thisAgent, msc->inst, wtt, -1);
8203  print (thisAgent, "\n");
8204  } else {
8205  if(msc->inst->prod) {
8206  /* REW: begin 10.22.97 */
8207  if((tmp = in_ms_trace_same_goal(msc->inst->prod->name,
8208  ms_trace, msc->goal))!=NIL) {
8209  /* REW: end 10.22.97 */
8210  tmp->count++;
8211  } else {
8212  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace),
8214  tmp->sym = msc->inst->prod->name;
8215  tmp->count = 1;
8216  tmp->next = ms_trace;
8217  /* REW: begin 08.20.97 */
8218  /* Add match goal to the print of the matching production */
8219  tmp->goal = msc->goal;
8220  /* REW: end 08.20.97 */
8221  ms_trace = tmp;
8222  }
8223  }
8224  }
8225  }
8226  if(wtt == NONE_WME_TRACE) {
8227  while (ms_trace) {
8228  tmp = ms_trace; ms_trace = tmp->next;
8229  print_with_symbols (thisAgent, " %y ", tmp->sym);
8230  /* REW: begin 08.20.97 */
8231  /* BUG: for now this will print the goal of the first assertion
8232  inspected, even though there can be multiple assertions at
8233 
8234  different levels.
8235  See 2.110 in the OPERAND-CHANGE-LOG. */
8236  if (tmp->goal)
8237  print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8238  else
8239  print(thisAgent, " [NIL] ");
8240  /* REW: end 08.20.97 */
8241  if(tmp->count > 1)
8242  print(thisAgent, "(%d)\n", tmp->count);
8243  else
8244  print(thisAgent, "\n");
8245  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8246  }
8247  }
8248  }
8249 }
8250 
8252 //
8253 // XML Generation functions.
8254 //
8255 // These are currently local to rete while I'm working on matches.
8256 // They should eventually move to their own file with a new header.
8257 //
8259 
8260 void xml_whole_token (agent* thisAgent, token *t, wme_trace_type wtt) {
8261  if (t==thisAgent->dummy_top_token) return;
8262  xml_whole_token (thisAgent, t->parent, wtt);
8263  if (t->w) {
8264  if (wtt==TIMETAG_WME_TRACE) xml_att_val(thisAgent, kWME_TimeTag, t->w->timetag);
8265  else if (wtt==FULL_WME_TRACE) xml_object (thisAgent, t->w);
8266  //if (wtt!=NONE_WME_TRACE) print (thisAgent, " ");
8267  }
8268 }
8269 
8271  condition *cond;
8272  cond = static_cast<condition_struct *>(dc->item);
8273  if (cond->type==CONJUNCTIVE_NEGATION_CONDITION) return FALSE;
8274  return tests_are_equal (thisAgent->id_test_to_match, cond->data.tests.id_test, false);
8275 }
8276 
8277 #if 0
8278 // Not currently using
8279 // xml_test is based on test_to_string.
8280 void xml_test (agent* thisAgent, char const* pTag, test t) {
8281  char *dest = 0 ;
8282  size_t dest_size = 0 ;
8283  cons *c;
8284  complex_test *ct;
8285  char *ch;
8286 
8287  if (test_is_blank_test(t)) {
8288  //if (!dest) dest=thisAgent->printed_output_string;
8289  xml_att_val(thisAgent, pTag, "[BLANK TEST]") ; // Using tag as attribute name
8290  //strncpy (dest, "[BLANK TEST]", dest_size); /* this should never get executed */
8291  //dest[dest_size - 1] = 0; /* ensure null termination */
8292  //return dest;
8293  return ;
8294  }
8295 
8297  xml_att_val(thisAgent, pTag, referent_of_equality_test(t)) ; // Using tag as attribute name
8298  return ;
8299  //return symbol_to_string (thisAgent, referent_of_equality_test(t), TRUE, dest, dest_size);
8300  }
8301 
8302  if (!dest) {
8303  dest=thisAgent->printed_output_string;
8304  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
8305  }
8306  ch = dest;
8307  ct = complex_test_from_test(t);
8308 
8309  switch (ct->type) {
8310  case NOT_EQUAL_TEST:
8311  strncpy (ch, "<> ", dest_size - (ch - dest));
8312  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8313  while (*ch)
8314  ch++;
8315  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8316  break;
8317  case LESS_TEST:
8318  strncpy (ch, "< ", dest_size - (ch - dest));
8319  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8320  while (*ch) ch++;
8321  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8322  break;
8323  case GREATER_TEST:
8324  strncpy (ch, "> ", dest_size - (ch - dest));
8325  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8326  while (*ch) ch++;
8327  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8328  break;
8329  case LESS_OR_EQUAL_TEST:
8330  strncpy (ch, "<= ", dest_size - (ch - dest));
8331  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8332  while (*ch) ch++;
8333  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8334  break;
8335  case GREATER_OR_EQUAL_TEST:
8336  strncpy (ch, ">= ", dest_size - (ch - dest));
8337  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8338  while (*ch) ch++;
8339  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8340  break;
8341  case SAME_TYPE_TEST:
8342  strncpy (ch, "<=> ", dest_size - (ch - dest));
8343  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8344  while (*ch) ch++;
8345  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
8346  break;
8347  case DISJUNCTION_TEST:
8348  // BUGBUG: Need to think this through more carefully
8349  xml_att_val(thisAgent, pTag, "BUGBUG--Adding disjunction in XML--not done yet") ;
8350  strncpy (ch, "<< ", dest_size - (ch - dest));
8351  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8352  while (*ch) ch++;
8353  for (c=ct->data.disjunction_list; c!=NIL; c=c->rest) {
8354  symbol_to_string (thisAgent, static_cast<symbol_union *>(c->first), TRUE, ch, dest_size - (ch - dest));
8355  while (*ch) ch++;
8356  *(ch++) = ' ';
8357  }
8358  strncpy (ch, ">>", dest_size - (ch - dest));
8359  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8360  break;
8361  case CONJUNCTIVE_TEST:
8362  // BUGBUG: Need to think this through more carefully
8363  xml_att_val(thisAgent, pTag, "BUGBUG--Adding conjunction in XML--not done yet") ;
8364  strncpy (ch, "{ ", dest_size - (ch - dest));
8365  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8366  while (*ch) ch++;
8367  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest) {
8368  xml_test (thisAgent, pTag, static_cast<char *>(c->first)) ; //, ch, dest_size - (ch - dest));
8369  while (*ch) ch++;
8370  *(ch++) = ' ';
8371  }
8372  strncpy (ch, "}", dest_size - (ch - dest));
8373  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8374  break;
8375  case GOAL_ID_TEST:
8376  strncpy (dest, "[GOAL ID TEST]", dest_size - (ch - dest)); /* this should never get executed */
8377  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8378  break;
8379  case IMPASSE_ID_TEST:
8380  strncpy (dest, "[IMPASSE ID TEST]", dest_size - (ch - dest)); /* this should never get executed */
8381  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
8382  break;
8383  }
8384 
8385  xml_att_val(thisAgent, pTag, dest) ;
8386  return ;
8387 }
8388 #endif //0
8389 
8390 #define XML_CONDITION_LIST_TEMP_SIZE 10000
8391 void xml_condition_list (agent* thisAgent, condition *conds,
8392  int indent, Bool internal) {
8393  dl_list *conds_not_yet_printed, *tail_of_conds_not_yet_printed;
8394  dl_list *conds_for_this_id;
8395  dl_cons *dc;
8396  condition *c;
8397  Bool removed_goal_test, removed_impasse_test;
8398  test id_test;
8399 
8400  if (!conds) return;
8401 
8402  /* --- build dl_list of all the actions --- */
8403  conds_not_yet_printed = NIL;
8404  tail_of_conds_not_yet_printed = NIL;
8405 
8406  for (c=conds; c!=NIL; c=c->next)
8407  {
8408  allocate_with_pool (thisAgent, &thisAgent->dl_cons_pool, &dc);
8409  dc->item = c;
8410  if (conds_not_yet_printed)
8411  {
8412  tail_of_conds_not_yet_printed->next = dc;
8413  }
8414  else
8415  {
8416  conds_not_yet_printed = dc;
8417  }
8418  dc->prev = tail_of_conds_not_yet_printed;
8419  tail_of_conds_not_yet_printed = dc;
8420  }
8421  tail_of_conds_not_yet_printed->next = NIL;
8422 
8423  /* --- main loop: find all conds for first id, print them together --- */
8424  Bool did_one_line_already = FALSE;
8425  while (conds_not_yet_printed)
8426  {
8427  if (did_one_line_already)
8428  {
8429  //print (thisAgent, "\n");
8430  //print_spaces (thisAgent, indent);
8431  }
8432  else
8433  {
8434  did_one_line_already = TRUE;
8435  }
8436 
8437  dc = conds_not_yet_printed;
8438  remove_from_dll (conds_not_yet_printed, dc, next, prev);
8439  c = static_cast<condition_struct *>(dc->item);
8441  {
8442  free_with_pool (&thisAgent->dl_cons_pool, dc);
8443  //print_string (thisAgent, "-{");
8444  xml_begin_tag(thisAgent, kTagConjunctive_Negation_Condition);
8445  xml_condition_list (thisAgent, c->data.ncc.top, indent+2, internal);
8446  xml_end_tag(thisAgent, kTagConjunctive_Negation_Condition);
8447  //print_string (thisAgent, "}");
8448  continue;
8449  }
8450 
8451  /* --- normal pos/neg conditions --- */
8452  removed_goal_test = removed_impasse_test = FALSE;
8453  id_test = copy_test_removing_goal_impasse_tests(thisAgent, c->data.tests.id_test,
8454  &removed_goal_test,
8455  &removed_impasse_test);
8456  thisAgent->id_test_to_match = copy_of_equality_test_found_in_test (thisAgent, id_test);
8457 
8458  /* --- collect all cond's whose id test matches this one --- */
8459  conds_for_this_id = dc;
8460  dc->prev = NIL;
8461  if (internal)
8462  {
8463  dc->next = NIL;
8464  }
8465  else
8466  {
8467  dc->next = extract_dl_list_elements (thisAgent, &conds_not_yet_printed,
8469  }
8470 
8471  // DJP: Moved this loop out so we get a condition tag per condition on this id
8472  // rather than an id with a series of conditions.
8473  while (conds_for_this_id)
8474  {
8475  /* --- print the collected cond's all together --- */
8476  //print_string (thisAgent, " (");
8477  xml_begin_tag(thisAgent, kTagCondition);
8478 
8479  if (removed_goal_test)
8480  {
8481  //print_string (thisAgent, "state ");
8482  xml_att_val(thisAgent, kConditionTest, kConditionTestState);
8483 
8484  }
8485 
8486  if (removed_impasse_test)
8487  {
8488  //print_string (thisAgent, "impasse ");
8489  xml_att_val(thisAgent, kConditionTest, kConditionTestImpasse);
8490  }
8491 
8492  //print_string (thisAgent, test_to_string (thisAgent, id_test, NULL, 0));
8493  //xml_test(thisAgent, kConditionId, id_test) ;
8494  xml_att_val(thisAgent, kConditionId, test_to_string(thisAgent, id_test, NULL, 0)) ;
8495  deallocate_test (thisAgent, thisAgent->id_test_to_match);
8496  deallocate_test (thisAgent, id_test);
8497 
8498  //growable_string gs = make_blank_growable_string(thisAgent);
8499  dc = conds_for_this_id;
8500  conds_for_this_id = conds_for_this_id->next;
8501  c = static_cast<condition_struct *>(dc->item);
8502  free_with_pool (&thisAgent->dl_cons_pool, dc);
8503 
8504  { /* --- build and print attr/value test for condition c --- */
8505  char temp[XML_CONDITION_LIST_TEMP_SIZE], *ch;
8506 
8507  memset(temp,0,XML_CONDITION_LIST_TEMP_SIZE);
8508  ch = temp;
8509  //strncpy (ch, " ", XML_CONDITION_LIST_TEMP_SIZE - (ch - temp));
8510  if (c->type==NEGATIVE_CONDITION)
8511  {
8512  strncat (ch, "-", XML_CONDITION_LIST_TEMP_SIZE - (ch - temp));
8513  }
8514 
8515  //strncat (ch, "^", XML_CONDITION_LIST_TEMP_SIZE - (ch - temp));
8516  while (*ch) ch++;
8517  test_to_string (thisAgent, c->data.tests.attr_test, ch, XML_CONDITION_LIST_TEMP_SIZE - (ch - temp));
8518  while (*ch) ch++;
8519 
8520  *ch = 0 ; // Terminate
8521  xml_att_val(thisAgent, kAttribute, temp) ;
8522 
8523  // Reset the ch pointer
8524  ch = temp ;
8526  {
8527  *(ch++) = ' ';
8528  test_to_string (thisAgent, c->data.tests.value_test, ch, XML_CONDITION_LIST_TEMP_SIZE - (ch - temp));
8529  while (*ch) ch++;
8531  {
8532  strncpy (ch, " +", XML_CONDITION_LIST_TEMP_SIZE - (ch - temp)); while (*ch) ch++;
8533  }
8534  }
8535  *ch = 0;
8536  if (thisAgent->printer_output_column + (ch - temp) >= COLUMNS_PER_LINE)
8537  {
8538  //print_string (thisAgent, "\n");
8539  //print_spaces (thisAgent, indent+6);
8540  }
8541  //print_string (thisAgent, temp);
8542  //add_to_growable_string(thisAgent, &gs, temp);
8543  xml_att_val(thisAgent, kValue, temp);
8544  }
8545  //free_growable_string(thisAgent, gs);
8546  }
8547  //print_string (thisAgent, ")");
8548  xml_end_tag(thisAgent, kTagCondition);
8549  } /* end of while (conds_not_yet_printed) */
8550 }
8551 
8552 void xml_condition (agent* thisAgent, condition *cond) {
8553  condition *old_next, *old_prev;
8554 
8555  old_next = cond->next;
8556  old_prev = cond->prev;
8557  cond->next = NIL;
8558  cond->prev = NIL;
8559  xml_condition_list (thisAgent, cond, 0, TRUE);
8560  cond->next = old_next;
8561  cond->prev = old_prev;
8562 }
8563 
8565  wme_trace_type wtt, int action)
8566 {
8567  int PRINTING = -1;
8568  int FIRING = 0;
8569  int RETRACTING = 1;
8570  condition *cond;
8571 
8572 
8573  if (action == PRINTING) {
8574  xml_begin_tag(thisAgent, kTagProduction);
8575  } else if (action == FIRING) {
8576  xml_begin_tag(thisAgent, kTagProduction_Firing);
8577  xml_begin_tag(thisAgent, kTagProduction);
8578  } else if (action == RETRACTING) {
8579  xml_begin_tag(thisAgent, kTagProduction_Retracting);
8580  xml_begin_tag(thisAgent, kTagProduction);
8581  }
8582 
8583  if (inst->prod) {
8584  //print_with_symbols (thisAgent, "%y", inst->prod->name);
8585  xml_att_val(thisAgent, kProduction_Name, inst->prod->name);
8586  } else {
8587  //print (thisAgent, "[dummy production]");
8588  xml_att_val(thisAgent, kProduction_Name, "[dummy_production]");
8589 
8590  }
8591 
8592  //print (thisAgent, "\n");
8593 
8594  if (wtt==NONE_WME_TRACE) {
8595  if (action == PRINTING) {
8596  xml_end_tag(thisAgent, kTagProduction);
8597  } else if (action == FIRING) {
8598  xml_end_tag(thisAgent, kTagProduction);
8599  xml_end_tag(thisAgent, kTagProduction_Firing);
8600  } else if (action == RETRACTING) {
8601  xml_end_tag(thisAgent, kTagProduction);
8602  xml_end_tag(thisAgent, kTagProduction_Retracting);
8603  }
8604  return;
8605  }
8606 
8607  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next)
8608  if (cond->type==POSITIVE_CONDITION) {
8609  switch (wtt) {
8610  case TIMETAG_WME_TRACE:
8611  //print (thisAgent, " %lu", cond->bt.wme_->timetag);
8612 
8613  xml_begin_tag(thisAgent, kTagWME);
8614  xml_att_val(thisAgent, kWME_TimeTag, cond->bt.wme_->timetag);
8615  xml_end_tag(thisAgent, kTagWME);
8616 
8617  break;
8618  case FULL_WME_TRACE:
8619  if (action != RETRACTING) {
8620  //print (thisAgent, " ");
8621  xml_object (thisAgent, cond->bt.wme_);
8622  } else {
8623  // Not all conds available when retracting, depending on DO_TOP_LEVEL_REF_CTS
8624  #ifdef DO_TOP_LEVEL_REF_CTS
8625  //print (thisAgent, " ");
8626  xml_object (thisAgent, cond->bt.wme_);
8627  #else
8628 
8629  // Wmes that matched the LHS of a retraction may already be free'd; just print tt.
8630  //print (thisAgent, " %lu", cond->bt.wme_->timetag);
8631 
8632  xml_begin_tag(thisAgent, kTagWME);
8633  xml_att_val(thisAgent, kWME_TimeTag, cond->bt.wme_->timetag);
8634  xml_end_tag(thisAgent, kTagWME);
8635 
8636  #endif
8637  }
8638  break;
8639  }
8640  }
8641 
8642  if (action == PRINTING) {
8643  xml_end_tag(thisAgent, kTagProduction);
8644  } else if (action == FIRING) {
8645  xml_end_tag(thisAgent, kTagProduction);
8646  xml_end_tag(thisAgent, kTagProduction_Firing);
8647  } else if (action == RETRACTING) {
8648  xml_end_tag(thisAgent, kTagProduction);
8649  xml_end_tag(thisAgent, kTagProduction_Retracting);
8650  }
8651 }
8652 
8654 //
8655 // XML version of print_match_set().
8656 //
8657 // Based on the print logic but generates XML directly.
8658 //
8660 void xml_match_set (agent* thisAgent, wme_trace_type wtt, ms_trace_type mst) {
8661  ms_change *msc;
8662  token temp_token;
8663  MS_trace *ms_trace = NIL, *tmp;
8664 
8665  /* --- Print assertions --- */
8666 
8667  /* REW: begin 09.15.96 */
8668  if (mst == MS_ASSERT_RETRACT || mst == MS_ASSERT) {
8669  //print (thisAgent, "O Assertions:\n");
8670  xml_begin_tag(thisAgent, kOAssertions) ;
8671 
8672  for (msc=thisAgent->ms_o_assertions; msc!=NIL; msc=msc->next) {
8673 
8674  if(wtt != NONE_WME_TRACE) {
8675  xml_begin_tag(thisAgent, kTagProduction) ;
8676  xml_att_val(thisAgent, kName, msc->p_node->b.p.prod->name) ;
8677  xml_att_val(thisAgent, kGoal, msc->goal) ;
8678  //print_with_symbols (thisAgent, " %y ", msc->p_node->b.p.prod->name);
8679  /* REW: begin 08.20.97 */
8680  /* Add match goal to the print of the matching production */
8681  //print_with_symbols(thisAgent, " [%y] ", msc->goal);
8682 
8683  /* REW: end 08.20.97 */
8684  temp_token.parent = msc->tok;
8685  temp_token.w = msc->w;
8686  xml_whole_token (thisAgent, &temp_token, wtt);
8687  //print (thisAgent, "\n");
8688  xml_end_tag(thisAgent, kTagProduction) ;
8689  }
8690  else {
8691  /* REW: begin 10.22.97 */
8692  if((tmp = in_ms_trace_same_goal(msc->p_node->b.p.prod->name,
8693  ms_trace, msc->goal))!=NIL) {
8694  /* REW: end 10.22.97 */
8695  tmp->count++;
8696  }
8697  else {
8698  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace), MISCELLANEOUS_MEM_USAGE));
8699  tmp->sym = msc->p_node->b.p.prod->name;
8700  tmp->count = 1;
8701  tmp->next = ms_trace;
8702  /* REW: begin 08.20.97 */
8703  /* Add match goal to the print of the matching production */
8704  tmp->goal = msc->goal;
8705  /* REW: end 08.20.97 */
8706  ms_trace = tmp;
8707  }
8708  }
8709  }
8710 
8711  if (wtt == NONE_WME_TRACE) {
8712  while (ms_trace) {
8713  xml_begin_tag(thisAgent, kTagProduction) ;
8714  tmp = ms_trace; ms_trace = tmp->next;
8715  xml_att_val(thisAgent, kName, tmp->sym) ;
8716  xml_att_val(thisAgent, kGoal, tmp->goal) ;
8717  if (tmp->count > 1)
8718  xml_att_val(thisAgent, kCount, tmp->count) ; // DJP -- No idea what this count is
8719  //print_with_symbols (thisAgent, " %y ", tmp->sym);
8720  /* REW: begin 08.20.97 */
8721  /* BUG: for now this will print the goal of the first
8722  assertion inspected, even though there can be multiple
8723  assertions at different levels.
8724  See 2.110 in the OPERAND-CHANGE-LOG. */
8725  //print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8726  /* REW: end 08.20.97 */
8727  //if (tmp->count > 1)
8728  // print(thisAgent, "(%d)\n", tmp->count);
8729  //else
8730  // print(thisAgent, "\n");
8731  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8732  xml_end_tag(thisAgent, kTagProduction) ;
8733  }
8734  }
8735  xml_end_tag(thisAgent, kOAssertions) ;
8736  }
8737 
8738  if (mst == MS_ASSERT_RETRACT || mst == MS_ASSERT) {
8739  //print (thisAgent, "I Assertions:\n");
8740  xml_begin_tag(thisAgent, kIAssertions) ;
8741  for (msc=thisAgent->ms_i_assertions; msc!=NIL; msc=msc->next) {
8742 
8743  if(wtt != NONE_WME_TRACE) {
8744  //print_with_symbols (thisAgent, " %y ", msc->p_node->b.p.prod->name);
8745  /* REW: begin 08.20.97 */
8746  /* Add match goal to the print of the matching production */
8747  //print_with_symbols(thisAgent, " [%y] ", msc->goal);
8748  xml_begin_tag(thisAgent, kTagProduction) ;
8749  xml_att_val(thisAgent, kName, msc->p_node->b.p.prod->name) ;
8750  xml_att_val(thisAgent, kGoal, msc->goal) ;
8751 
8752  /* REW: end 08.20.97 */
8753  temp_token.parent = msc->tok;
8754  temp_token.w = msc->w;
8755  xml_whole_token (thisAgent, &temp_token, wtt);
8756  //print (thisAgent, "\n");
8757  xml_end_tag(thisAgent, kTagProduction) ;
8758  }
8759  else {
8760  /* REW: begin 10.22.97 */
8761  if((tmp = in_ms_trace_same_goal(msc->p_node->b.p.prod->name,
8762  ms_trace, msc->goal))!=NIL) {
8763  /* REW: end 10.22.97 */
8764  tmp->count++;
8765  }
8766  else {
8767  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace),
8769  tmp->sym = msc->p_node->b.p.prod->name;
8770  tmp->count = 1;
8771  tmp->next = ms_trace;
8772  /* REW: begin 08.20.97 */
8773  /* Add match goal to the print of the matching production */
8774  tmp->goal = msc->goal;
8775  /* REW: end 08.20.97 */
8776  ms_trace = tmp;
8777  }
8778  }
8779  }
8780 
8781  if (wtt == NONE_WME_TRACE) {
8782  while (ms_trace) {
8783  tmp = ms_trace; ms_trace = tmp->next;
8784  xml_begin_tag(thisAgent, kTagProduction) ;
8785  xml_att_val(thisAgent, kName, tmp->sym) ;
8786  xml_att_val(thisAgent, kGoal, tmp->goal) ;
8787  if (tmp->count > 1)
8788  xml_att_val(thisAgent, kCount, tmp->count) ; // DJP -- No idea what this count is
8789  //print_with_symbols (thisAgent, " %y ", tmp->sym);
8790  /* REW: begin 08.20.97 */
8791  /* BUG: for now this will print the goal of the first
8792  assertion inspected, even though there can be multiple
8793  assertions at different levels.
8794  See 2.110 in the OPERAND-CHANGE-LOG. */
8795  //print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8796  /* REW: end 08.20.97 */
8797  //if (tmp->count > 1)
8798  // print(thisAgent, "(%d)\n", tmp->count);
8799  //else
8800  // print(thisAgent, "\n");
8801 
8802  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8803  xml_end_tag(thisAgent, kTagProduction) ;
8804  }
8805  }
8806  }
8807  xml_end_tag(thisAgent, kIAssertions) ;
8808  /* REW: end 09.15.96 */
8809 
8810  if (mst == MS_ASSERT_RETRACT || mst == MS_RETRACT) {
8811  xml_begin_tag(thisAgent, kRetractions) ;
8812  //print (thisAgent, "Retractions:\n");
8813  for (msc=thisAgent->ms_retractions; msc!=NIL; msc=msc->next) {
8814  if(wtt != NONE_WME_TRACE) {
8815  //print (thisAgent, " ");
8816  xml_instantiation_with_wmes (thisAgent, msc->inst, wtt, -1);
8817  //print (thisAgent, "\n");
8818  } else {
8819  if(msc->inst->prod) {
8820  /* REW: begin 10.22.97 */
8821  if((tmp = in_ms_trace_same_goal(msc->inst->prod->name,
8822  ms_trace, msc->goal))!=NIL) {
8823  /* REW: end 10.22.97 */
8824  tmp->count++;
8825  } else {
8826  tmp = static_cast<match_set_trace *>(allocate_memory(thisAgent, sizeof(MS_trace),
8828  tmp->sym = msc->inst->prod->name;
8829  tmp->count = 1;
8830  tmp->next = ms_trace;
8831  /* REW: begin 08.20.97 */
8832  /* Add match goal to the print of the matching production */
8833  tmp->goal = msc->goal;
8834  /* REW: end 08.20.97 */
8835  ms_trace = tmp;
8836  }
8837  }
8838  }
8839  }
8840  if(wtt == NONE_WME_TRACE) {
8841  while (ms_trace) {
8842  tmp = ms_trace; ms_trace = tmp->next;
8843  xml_begin_tag(thisAgent, kTagProduction) ;
8844  xml_att_val(thisAgent, kName, tmp->sym) ;
8845  if (tmp->goal)
8846  xml_att_val(thisAgent, kGoal, tmp->goal) ;
8847  else
8848  xml_att_val(thisAgent, kGoal, "NIL") ;
8849  if (tmp->count > 1)
8850  xml_att_val(thisAgent, kCount, tmp->count) ; // DJP -- No idea what this count is
8851  //print_with_symbols (thisAgent, " %y ", tmp->sym);
8852  /* REW: begin 08.20.97 */
8853  /* BUG: for now this will print the goal of the first assertion
8854  inspected, even though there can be multiple assertions at
8855 
8856  different levels.
8857  See 2.110 in the OPERAND-CHANGE-LOG. */
8858  //if (tmp->goal)
8859  // print_with_symbols(thisAgent, " [%y] ", tmp->goal);
8860  //else
8861  // print(thisAgent, " [NIL] ");
8862  /* REW: end 08.20.97 */
8863  //if(tmp->count > 1)
8864  // print(thisAgent, "(%d)\n", tmp->count);
8865  //else
8866  // print(thisAgent, "\n");
8867  free_memory(thisAgent, tmp, MISCELLANEOUS_MEM_USAGE);
8868  xml_end_tag(thisAgent, kTagProduction) ;
8869  }
8870  }
8871  }
8872 }
8873 
8874 /* --- Print stuff for given node and higher, up to but not including the
8875  cutoff node. Return number of matches at the given node/cond. --- */
8876 int64_t xml_aux (agent* thisAgent, /* current agent */
8877  rete_node *node, /* current node */
8878  rete_node *cutoff, /* don't print cutoff node or any higher */
8879  condition *cond, /* cond for current node */
8880  wme_trace_type wtt, /* what type of printout to use */
8881  int indent) { /* number of spaces indent */
8882  token *tokens, *t, *parent_tokens;
8883  right_mem *rm;
8884  int64_t matches_one_level_up;
8885  int64_t matches_at_this_level;
8886  //#define MATCH_COUNT_STRING_BUFFER_SIZE 20
8887  //char match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE];
8888  rete_node *parent;
8889 
8890  /* --- find the number of matches for this condition --- */
8891  tokens = get_all_left_tokens_emerging_from_node (thisAgent, node);
8892  matches_at_this_level = 0;
8893  for (t=tokens; t!=NIL; t=t->next_of_node) matches_at_this_level++;
8894  deallocate_token_list (thisAgent, tokens);
8895 
8896  /* --- if we're at the cutoff node, we're done --- */
8897  if (node==cutoff) return matches_at_this_level;
8898 
8899  /* --- do stuff higher up --- */
8900  parent = real_parent_node(node);
8901  matches_one_level_up = xml_aux (thisAgent, parent, cutoff,
8902  cond->prev, wtt, indent);
8903 
8904  /* --- Form string for current match count: If an earlier cond had no
8905  matches, just leave it blank; if this is the first 0, use ">>>>" --- */
8906  if (! matches_one_level_up) {
8907  //xml_att_val(thisAgent, kMatchCount, 0) ;
8908  //strncpy (match_count_string, " ", MATCH_COUNT_STRING_BUFFER_SIZE);
8909  //match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
8910  } else if (! matches_at_this_level) {
8911  //xml_att_val(thisAgent, kMatchCount, 0) ;
8912  //strncpy (match_count_string, ">>>>", MATCH_COUNT_STRING_BUFFER_SIZE);
8913  //match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
8914  } else {
8915  //xml_att_val(thisAgent, kMatchCount, matches_at_this_level) ;
8916  //SNPRINTF (match_count_string, MATCH_COUNT_STRING_BUFFER_SIZE, "%4ld", matches_at_this_level);
8917  //match_count_string[MATCH_COUNT_STRING_BUFFER_SIZE - 1] = 0; /* ensure null termination */
8918  }
8919 
8920  /* --- print extra indentation spaces --- */
8921  //print_spaces (thisAgent, indent);
8922 
8923  if (cond->type==CONJUNCTIVE_NEGATION_CONDITION) {
8924  /* --- recursively print match counts for the NCC subconditions --- */
8925  xml_begin_tag(thisAgent, kTagConjunctive_Negation_Condition) ;
8926  //print (thisAgent, " -{\n");
8927  xml_aux (thisAgent, real_parent_node(node->b.cn.partner),
8928  parent,
8929  cond->data.ncc.bottom,
8930  wtt,
8931  indent+5);
8932  //print_spaces (thisAgent, indent);
8933  //print (thisAgent, "%s }\n", match_count_string);
8934  xml_end_tag(thisAgent, kTagConjunctive_Negation_Condition) ;
8935  } else {
8936  //print (thisAgent, "%s", match_count_string);
8937  xml_condition (thisAgent, cond);
8938 
8939  // DJP: This is a trick to let us insert more attributes into xml_condition().
8940  xml_move_current_to_last_child(thisAgent) ;
8941  // DJP: Moved this test from earlier down to here as no longer building match_count_string
8942  if (!matches_one_level_up)
8943  xml_att_val(thisAgent, kMatchCount, 0) ;
8944  else
8945  xml_att_val(thisAgent, kMatchCount, matches_at_this_level) ;
8946  xml_move_current_to_parent(thisAgent) ;
8947 
8948  //print (thisAgent, "\n");
8949  /* --- if this is the first match-failure (0 matches), print info on
8950  matches for left and right --- */
8951  if (matches_one_level_up && (!matches_at_this_level)) {
8952  if (wtt!=NONE_WME_TRACE) {
8953  //print_spaces (thisAgent, indent);
8954  xml_begin_tag(thisAgent, kTagLeftMatches) ;
8955  //print (thisAgent, "*** Matches For Left ***\n");
8956  parent_tokens = get_all_left_tokens_emerging_from_node (thisAgent, parent);
8957  for (t=parent_tokens; t!=NIL; t=t->next_of_node) {
8958  //print_spaces (thisAgent, indent);
8959  xml_begin_tag(thisAgent, kTagToken) ;
8960  xml_whole_token (thisAgent, t, wtt);
8961  xml_end_tag(thisAgent, kTagToken) ;
8962  //print (thisAgent, "\n");
8963  }
8964  deallocate_token_list (thisAgent, parent_tokens);
8965  xml_end_tag(thisAgent, kTagLeftMatches) ;
8966  //print_spaces (thisAgent, indent);
8967  //print (thisAgent, "*** Matches for Right ***\n");
8968  xml_begin_tag(thisAgent, kTagRightMatches) ;
8969  //print_spaces (thisAgent, indent);
8970  for (rm=node->b.posneg.alpha_mem_->right_mems; rm!=NIL;
8971  rm=rm->next_in_am) {
8972  //if (wtt==TIMETAG_WME_TRACE) print (thisAgent, "%lu", rm->w->timetag);
8973  //else if (wtt==FULL_WME_TRACE) print_wme (thisAgent, rm->w);
8974  //print (thisAgent, " ");
8975  if (wtt==TIMETAG_WME_TRACE) xml_att_val(thisAgent, kWME_TimeTag, rm->w->timetag);
8976  else if (wtt==FULL_WME_TRACE) xml_object (thisAgent, rm->w);
8977  }
8978  xml_end_tag(thisAgent, kTagRightMatches) ;
8979  //print (thisAgent, "\n");
8980  }
8981  } /* end of if (matches_one_level_up ...) */
8982  }
8983 
8984  /* --- return result --- */
8985  return matches_at_this_level;
8986 }
8987 
8989  condition *top_cond, *bottom_cond;
8990  int64_t n;
8991  token *tokens, *t;
8992 
8993  xml_begin_tag(thisAgent, kTagProduction) ;
8994  p_node_to_conditions_and_nots (thisAgent, p_node, NIL, NIL, &top_cond, &bottom_cond,
8995  NIL, NIL);
8996  n = xml_aux (thisAgent, p_node->parent, thisAgent->dummy_top_node, bottom_cond,
8997  wtt, 0);
8998  xml_att_val(thisAgent, kMatches, n) ;
8999  //print (thisAgent, "\n%d complete matches.\n", n);
9000  if (n && (wtt!=NONE_WME_TRACE)) {
9001  print (thisAgent, "*** Complete Matches ***\n");
9002  tokens = get_all_left_tokens_emerging_from_node (thisAgent, p_node->parent);
9003  for (t=tokens; t!=NIL; t=t->next_of_node) {
9004  xml_whole_token (thisAgent, t, wtt);
9005  //print (thisAgent, "\n");
9006  }
9007  deallocate_token_list (thisAgent, tokens);
9008  }
9009  deallocate_condition_list (thisAgent, top_cond);
9010  xml_end_tag(thisAgent, kTagProduction) ;
9011 }
9012 
9013 
9014 /* **********************************************************************
9015 
9016  SECTION 19: Rete Initialization
9017 
9018  EXTERNAL INTERFACE:
9019  Init_rete() initializes everything.
9020 ********************************************************************** */
9022 {
9023  static bool is_initialized = false;
9024  if(!is_initialized)
9025  {
9036 
9043 
9044  is_initialized = true;
9045  }
9046 }
9047 
9048 
9049 void init_rete (agent* thisAgent) {
9050 
9051  /*
9052  This function consists of two parts. The first initializes variables
9053  pertaining to a particular agent. The second initializes some important
9054  globals (bnode type names, addition routines, and test routines).
9055  Originally, these two parts were ordered the other way.
9056 
9057  The globals should only be initialized once (when the rete for the first
9058  agent is initialized), whereas everything else should be initialized on
9059  every call to the function (i.e. whenever the rete for a new agent is
9060  initialized).
9061 
9062  Therefore, the order has been switched so that the agent-specific
9063  variables are initialized first. Once this is done, a simple test of a
9064  static boolean variable indicates whether or not the globals have already
9065  been initialized. If they have, then the function exits prematurely.
9066 
9067  As far as I can see, this switch has no undesired effects, since the
9068  agent-specific function calls in the first part do not depend upon the
9069  global variables defined in the second part.
9070 
9071  -AJC (8/9/02)
9072  */
9073 
9074  int i;
9075 
9076  init_memory_pool (thisAgent, &thisAgent->alpha_mem_pool, sizeof(alpha_mem),
9077  "alpha mem");
9078  init_memory_pool (thisAgent, &thisAgent->rete_test_pool, sizeof(rete_test),
9079  "rete test");
9080  init_memory_pool (thisAgent, &thisAgent->rete_node_pool, sizeof(rete_node),
9081  "rete node");
9082  init_memory_pool (thisAgent, &thisAgent->node_varnames_pool,sizeof(node_varnames),
9083  "node varnames");
9084  init_memory_pool (thisAgent, &thisAgent->token_pool, sizeof(token), "token");
9085  init_memory_pool (thisAgent, &thisAgent->right_mem_pool, sizeof(right_mem),
9086  "right mem");
9087  init_memory_pool (thisAgent, &thisAgent->ms_change_pool, sizeof(ms_change),
9088  "ms change");
9089 
9090  for (i=0; i<16; i++)
9091  thisAgent->alpha_hash_tables[i] = make_hash_table (thisAgent, 0, hash_alpha_mem);
9092 
9094  (thisAgent, sizeof(char *) * LEFT_HT_SIZE, HASH_TABLE_MEM_USAGE);
9096  (thisAgent, sizeof(char *) * RIGHT_HT_SIZE, HASH_TABLE_MEM_USAGE);
9097 
9098  init_dummy_top_node(thisAgent);
9099 
9100  thisAgent->max_rhs_unbound_variables = 1;
9101  thisAgent->rhs_variable_bindings = (Symbol **)
9103 
9104  /* This is still not thread-safe. -AJC (8/9/02) */
9105  static Bool bInit = FALSE;
9106  if (bInit)
9107  return;
9108 
9109  bInit = TRUE;
9110 
9112 
9113  init_bnode_type_names(thisAgent);
9114 
9116 
9117  //
9118  // rete_test_routines is now statically initialized.
9119  //
9120  //for (i=0; i<256; i++) rete_test_routines[i] = error_rete_test_routine;
9122  rete_test_routines[ID_IS_GOAL_RETE_TEST] = id_is_goal_rete_test_routine;
9124  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9127  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9130  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9133  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9136  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9139  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9142  rete_test_routines[CONSTANT_RELATIONAL_RETE_TEST +
9145  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9148  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9151  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9154  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9157  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9160  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9163  rete_test_routines[VARIABLE_RELATIONAL_RETE_TEST +
9166 }