Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
print.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: print.cpp
11  *
12  * =======================================================================
13  * These are the routines that support printing Soar data structures.
14  *
15  * obsolete comments deleted
16  * =======================================================================
17  */
18 /* =================================================================
19  Printing Utility Routines for Soar 6
20  ================================================================= */
21 
22 #include <stdlib.h>
23 
24 #include "print.h"
25 #include "kernel.h"
26 #include "agent.h"
27 #include "symtab.h"
28 #include "init_soar.h"
29 #include "wmem.h"
30 #include "gdatastructs.h"
31 #include "rete.h"
32 #include "rhsfun.h"
33 #include "production.h"
34 #include "instantiations.h"
35 #include "xml.h"
36 #include "soar_TraceNames.h"
37 
38 #include <stdarg.h>
39 
40 using namespace soar_TraceNames;
41 
42 /* -------------------------------------------------------------------
43  Print_string() and print_spaces() do the obvious things.
44  Print() is exactly like printf() in C, except it prints to both
45  the screen and log file (if there is one). Print_with_symbols()
46  is sort of like print, but only takes two kinds of escape sequences
47  in the format string:
48  %y -- print a symbol
49  %% -- print a "%" sign
50 
51  Sometimes we need to know the current output column so we can put
52  a line break in the right place. Get_printer_output_column() returns
53  the current column number (1 means the start of the line).
54  Tell_printer_that_output_column_has_been_reset () is called from the
55  lexer every time it reads a line from the keyboard--since after the
56  user types a line (and hits return) the output column is reset.
57 ------------------------------------------------------------------- */
58 
59 int get_printer_output_column (agent* thisAgent) {
60  return thisAgent->printer_output_column;
61 }
62 
64  thisAgent->printer_output_column = 1;
65 }
66 
67 
68 /* -----------------------------------------------------------------------
69  Print_string
70 
71  This routine prints the given string, and updates printer_output_column.
72  (This routine is called from the other print(), etc. routines.)
73 ----------------------------------------------------------------------- */
74 
75 
76 
77 void print_string (agent* thisAgent, const char *s) {
78  const char *ch;
79 
80  for (ch=s; *ch!=0; ch++) {
81  if (*ch=='\n')
82  thisAgent->printer_output_column = 1;
83  else
84  thisAgent->printer_output_column++;
85  }
86 
87  soar_invoke_callbacks(thisAgent, PRINT_CALLBACK, static_cast<soar_call_data>(const_cast<char *>(s)));
88 }
89 
90 /* ---------------------------------------------------------------
91  Print, Print_with_symbols, Print_spaces
92 
93  These are the main printing routines. (The code is ugly because
94  it has to take a variable number of arguments, and there are two
95  ways to do this, depending on whether we're using a fully ANSI
96  compatible compiler or not.)
97 --------------------------------------------------------------- */
98 
99 /* --- size of output buffer for a single call to one of these routines --- */
100 #define PRINT_BUFSIZE 5000 /* This better be large enough!! */
101 
102 void print (agent* thisAgent, const char *format, ...) {
103  va_list args;
104  char buf[PRINT_BUFSIZE];
105 
106  va_start (args, format);
107  vsprintf (buf, format, args);
108  va_end (args);
109  print_string (thisAgent, buf);
110 }
111 
112 void vsnprintf_with_symbols(agent* thisAgent, char* dest, size_t count, const char *format, va_list args) {
113  char *ch;
114 
115  ch = dest;
116  while (TRUE) {
117  /* --- copy anything up to the first "%" --- */
118  while ((*format != '%') && (*format != 0)) *(ch++) = *(format++);
119  if (*format == 0) break;
120  /* --- handle the %-thingy --- */
121  if (*(format+1)=='y') {
122  /* the size of the remaining buffer (after ch) is
123  the difference between the address of ch and
124  the address of the beginning of the buffer
125  */
126  symbol_to_string (thisAgent, va_arg(args, Symbol *), TRUE, ch, count - (ch - dest));
127  while (*ch) ch++;
128  } else {
129  *(ch++) = '%';
130  }
131  format += 2;
132  }
133  *ch = 0;
134 }
135 
136 void print_with_symbols (agent* thisAgent, const char *format, ...) {
137  va_list args;
138  char buf[PRINT_BUFSIZE];
139 
140  va_start (args, format);
141  vsnprintf_with_symbols(thisAgent, buf, PRINT_BUFSIZE, format, args);
142  va_end (args);
143  print_string (thisAgent, buf);
144 }
145 
146 void snprintf_with_symbols (agent* thisAgent, char* dest, size_t count, const char *format, ...) {
147  va_list args;
148  va_start (args, format);
149  vsnprintf_with_symbols(thisAgent, dest, count, format, args);
150  va_end (args);
151 }
152 
153 void print_spaces (agent* thisAgent, int n) {
154  char *ch;
155  char buf[PRINT_BUFSIZE];
156 
157  ch = buf;
158  while (n) { *(ch++)=' '; n--; }
159  *ch=0;
160  print_string (thisAgent, buf);
161 }
162 
163 /* ------------------------------------------------------------------------
164  String to Escaped String Conversion
165  {Symbol, Test, RHS Value} to String Conversion
166 
167  These routines produce strings. Each takes an optional parameter "dest"
168  which, if non-nil, points to the destination buffer for the result string.
169  If dest is nil, these routines use a global buffer, and return a pointer
170  to it. (Otherwise "dest" itself is returned.) Note that a single global
171  buffer is shared by all three routines, so callers should assume the
172  buffer will be destroyed by the next call to these routines with dest=NIL.
173 
174  String_to_escaped_string() takes a string and a first/last char,
175  and produces an "escaped string" representation of the string; i.e.,
176  a string that uses '\' escapes to include special characters.
177  For example, input 'ab"c' with first/last character '"' yields
178  '"ab\"c"'. This is used for printing quoted strings and for printing
179  symbols using |vbar| notation.
180 
181  Symbol_to_string() converts a symbol to a string. The "rereadable"
182  parameter indicates whether a rereadable representation is desired.
183  Normally symbols are printed rereadably, but for (write) and Text I/O,
184  we don't want this.
185 
186  Test_to_string() takes a test and produces a string representation.
187  Rhs_value_to_string() takes an rhs_value and produces a string
188  representation. The rhs_value MUST NOT be a reteloc.
189 ----------------------------------------------------------------------- */
190 
191 char *string_to_escaped_string (agent* thisAgent, char *s,
192  char first_and_last_char, char *dest) {
193  char *ch;
194 
195  if (!dest) dest = thisAgent->printed_output_string;
196  ch = dest;
197  *ch++ = first_and_last_char;
198  while (*s) {
199  if ((*s==first_and_last_char)||(*s=='\\')) *ch++ = '\\';
200  *ch++ = *s++;
201  }
202  *ch++ = first_and_last_char;
203  *ch = 0;
204  return dest;
205 }
206 
207 
208 char const* symbol_to_typeString(agent* /*thisAgent*/, Symbol* sym)
209 {
210  switch(sym->common.symbol_type) {
212  return kTypeVariable ;
214  return kTypeID ;
216  return kTypeInt ;
218  return kTypeDouble ;
220  return kTypeString ;
221  default:
222  return 0 ;
223  }
224 }
225 
226 char *symbol_to_string (agent* thisAgent, Symbol *sym,
227  Bool rereadable, char *dest, size_t dest_size) {
228  Bool possible_id, possible_var, possible_sc, possible_ic, possible_fc;
229  Bool is_rereadable;
230  Bool has_angle_bracket;
231 
232  switch(sym->common.symbol_type) {
234  if (!dest) return sym->var.name;
235  strncpy (dest, sym->var.name, dest_size);
236  dest[dest_size - 1] = 0; /* ensure null termination */
237  return dest;
238 
240  if (!dest) {
241  dest=thisAgent->printed_output_string;
242  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
243  }
244  if (sym->id.smem_lti == NIL) {
245  // NOT an lti (long term identifier), print like we always have
246  SNPRINTF (dest, dest_size, "%c%llu", sym->id.name_letter, static_cast<long long unsigned>(sym->id.name_number));
247  }
248  else {
249  // IS an lti (long term identifier), prepend an @ symbol
250  SNPRINTF (dest, dest_size, "@%c%llu", sym->id.name_letter, static_cast<long long unsigned>(sym->id.name_number));
251  }
252  dest[dest_size - 1] = 0; /* ensure null termination */
253  return dest;
254 
256  if (!dest) {
257  dest=thisAgent->printed_output_string;
258  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
259  }
260  SNPRINTF (dest, dest_size, "%ld", static_cast<long int>(sym->ic.value));
261  dest[dest_size - 1] = 0; /* ensure null termination */
262  return dest;
263 
265  if (!dest) {
266  dest=thisAgent->printed_output_string;
267  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
268  }
269  SNPRINTF (dest, dest_size, "%#.16g", sym->fc.value);
270  dest[dest_size - 1] = 0; /* ensure null termination */
271  { /* --- strip off trailing zeros --- */
272  char *start_of_exponent;
273  char *end_of_mantissa;
274  start_of_exponent = dest;
275  while ((*start_of_exponent != 0) && (*start_of_exponent != 'e'))
276  start_of_exponent++;
277  end_of_mantissa = start_of_exponent - 1;
278  while (*end_of_mantissa == '0') end_of_mantissa--;
279  end_of_mantissa++;
280  while (*start_of_exponent) *end_of_mantissa++ = *start_of_exponent++;
281  *end_of_mantissa = 0;
282  }
283  return dest;
284 
286  if (!rereadable) {
287  if (!dest) return sym->sc.name;
288  strncpy (dest, sym->sc.name, dest_size);
289  return dest;
290  }
292  strlen (sym->sc.name),
293  &possible_id,
294  &possible_var,
295  &possible_sc,
296  &possible_ic,
297  &possible_fc,
298  &is_rereadable);
299 
300  has_angle_bracket = sym->sc.name[0] == '<' ||
301  sym->sc.name[strlen(sym->sc.name)-1] == '>';
302 
303  if ((!possible_sc) || possible_var || possible_ic || possible_fc ||
304  (!is_rereadable) ||
305  has_angle_bracket) {
306  /* BUGBUG if in context where id's could occur, should check
307  possible_id flag here also */
308  return string_to_escaped_string (thisAgent, sym->sc.name, '|', dest);
309  }
310  if (!dest) return sym->sc.name;
311  strncpy (dest, sym->sc.name, dest_size);
312  return dest;
313 
314  default:
315  {
316  char msg[BUFFER_MSG_SIZE];
317  strncpy(msg, "Internal Soar Error: symbol_to_string called on bad symbol\n", BUFFER_MSG_SIZE);
318  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
319  abort_with_fatal_error(thisAgent, msg);
320  }
321  }
322  return NIL; /* unreachable, but without it, gcc -Wall warns here */
323 }
324 
325 
326 
327 
328 char *test_to_string (agent* thisAgent, test t, char *dest, size_t dest_size) {
329  cons *c;
330  complex_test *ct;
331  char *ch;
332 
333  if (test_is_blank_test(t)) {
334  if (!dest) dest=thisAgent->printed_output_string;
335  strncpy (dest, "[BLANK TEST]", dest_size); /* this should never get executed */
336  dest[dest_size - 1] = 0; /* ensure null termination */
337  return dest;
338  }
339 
341  return symbol_to_string (thisAgent, referent_of_equality_test(t), TRUE, dest, dest_size);
342  }
343 
344  if (!dest) {
345  dest=thisAgent->printed_output_string;
346  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
347  }
348  ch = dest;
349  ct = complex_test_from_test(t);
350 
351  switch (ct->type) {
352  case NOT_EQUAL_TEST:
353  strncpy (ch, "<> ", dest_size - (ch - dest));
354  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
355  while (*ch)
356  ch++;
357  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
358  break;
359  case LESS_TEST:
360  strncpy (ch, "< ", dest_size - (ch - dest));
361  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
362  while (*ch) ch++;
363  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
364  break;
365  case GREATER_TEST:
366  strncpy (ch, "> ", dest_size - (ch - dest));
367  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
368  while (*ch) ch++;
369  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
370  break;
371  case LESS_OR_EQUAL_TEST:
372  strncpy (ch, "<= ", dest_size - (ch - dest));
373  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
374  while (*ch) ch++;
375  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
376  break;
378  strncpy (ch, ">= ", dest_size - (ch - dest));
379  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
380  while (*ch) ch++;
381  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
382  break;
383  case SAME_TYPE_TEST:
384  strncpy (ch, "<=> ", dest_size - (ch - dest));
385  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
386  while (*ch) ch++;
387  symbol_to_string (thisAgent, ct->data.referent, TRUE, ch, dest_size - (ch - dest));
388  break;
389  case DISJUNCTION_TEST:
390  strncpy (ch, "<< ", dest_size - (ch - dest));
391  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
392  while (*ch) ch++;
393  for (c=ct->data.disjunction_list; c!=NIL; c=c->rest) {
394  symbol_to_string (thisAgent, static_cast<symbol_union *>(c->first), TRUE, ch, dest_size - (ch - dest));
395  while (*ch) ch++;
396  *(ch++) = ' ';
397  }
398  strncpy (ch, ">>", dest_size - (ch - dest));
399  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
400  break;
401  case CONJUNCTIVE_TEST:
402  strncpy (ch, "{ ", dest_size - (ch - dest));
403  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
404  while (*ch) ch++;
405  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest) {
406  test_to_string (thisAgent, static_cast<char *>(c->first), ch, dest_size - (ch - dest));
407  while (*ch) ch++;
408  *(ch++) = ' ';
409  }
410  strncpy (ch, "}", dest_size - (ch - dest));
411  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
412  break;
413  case GOAL_ID_TEST:
414  strncpy (dest, "[GOAL ID TEST]", dest_size - (ch - dest)); /* this should never get executed */
415  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
416  break;
417  case IMPASSE_ID_TEST:
418  strncpy (dest, "[IMPASSE ID TEST]", dest_size - (ch - dest)); /* this should never get executed */
419  ch[dest_size - (ch - dest) - 1] = 0; /* ensure null termination */
420  break;
421  }
422  return dest;
423 }
424 
425 char *rhs_value_to_string (agent* thisAgent, rhs_value rv, char *dest, size_t dest_size) {
426  cons *c;
427  list *fl;
428  rhs_function *rf;
429  char *ch;
430 
431  if (rhs_value_is_reteloc(rv)) {
432  char msg[BUFFER_MSG_SIZE];
433  strncpy (msg, "Internal error: rhs_value_to_string called on reteloc.\n", BUFFER_MSG_SIZE);
434  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
435  abort_with_fatal_error(thisAgent, msg);
436  }
437 
438  if (rhs_value_is_symbol(rv)) {
439  return symbol_to_string (thisAgent, rhs_value_to_symbol(rv), TRUE, dest, dest_size);
440  }
441 
442  fl = rhs_value_to_funcall_list(rv);
443  rf = static_cast<rhs_function_struct *>(fl->first);
444 
445  if (!dest) {
446  dest=thisAgent->printed_output_string;
447  dest_size = MAX_LEXEME_LENGTH*2+10; /* from agent.h */
448  }
449  ch = dest;
450 
451  strncpy (ch, "(", dest_size);
452  ch[dest_size - 1] = 0;
453  while (*ch) ch++;
454 
455  if (!strcmp(rf->name->sc.name,"+")) {
456  strncpy (ch, "+", dest_size - (ch - dest));
457  ch[dest_size - (ch - dest) - 1] = 0;
458  } else if (!strcmp(rf->name->sc.name,"-")) {
459  strncpy (ch, "-", dest_size - (ch - dest));
460  ch[dest_size - (ch - dest) - 1] = 0;
461  } else {
462  symbol_to_string (thisAgent, rf->name, TRUE, ch, dest_size - (ch - dest));
463  }
464 
465  while (*ch) ch++;
466  for (c=fl->rest; c!=NIL; c=c->rest) {
467  strncpy (ch, " ", dest_size - (ch - dest));
468  ch[dest_size - (ch - dest) - 1] = 0;
469  while (*ch)
470  ch++;
471  rhs_value_to_string (thisAgent, static_cast<char *>(c->first), ch, dest_size - (ch - dest));
472  while (*ch)
473  ch++;
474  }
475  strncpy (ch, ")", dest_size - (ch - dest));
476  ch[dest_size - (ch - dest) - 1] = 0;
477  return dest;
478 }
479 
480 /* ------------------------------------------------------------------
481  Print Condition List
482 
483  This prints a list of conditions. The "indent" parameter tells
484  how many spaces to indent each line other than the first--the first
485  line is not indented (the caller must handle this). The last line
486  is printed without a trailing linefeed. The "internal" parameter,
487  if TRUE, indicates that the condition list should be printed in
488  internal format--one condition per line, without grouping all the
489  conditions for the same id into one line.
490 ------------------------------------------------------------------ */
491 
493  condition *cond;
494  cond = static_cast<condition_struct *>(dc->item);
495  if (cond->type==CONJUNCTIVE_NEGATION_CONDITION) return FALSE;
496  return tests_are_equal (thisAgent->id_test_to_match, cond->data.tests.id_test, false);
497 }
498 
499 /*
500 ==============================
501 
502 ==============================
503 */
504 #define PRINT_CONDITION_LIST_TEMP_SIZE 10000
505 void print_condition_list (agent* thisAgent, condition *conds,
506  int indent, Bool internal) {
507  dl_list *conds_not_yet_printed, *tail_of_conds_not_yet_printed;
508  dl_list *conds_for_this_id;
509  dl_cons *dc;
510  condition *c;
511  Bool removed_goal_test, removed_impasse_test;
512  test id_test;
513 
514  if (!conds) return;
515 
516  /* --- build dl_list of all the actions --- */
517  conds_not_yet_printed = NIL;
518  tail_of_conds_not_yet_printed = NIL;
519 
520  for (c=conds; c!=NIL; c=c->next)
521  {
522  allocate_with_pool (thisAgent, &thisAgent->dl_cons_pool, &dc);
523  dc->item = c;
524  if (conds_not_yet_printed)
525  {
526  tail_of_conds_not_yet_printed->next = dc;
527  }
528  else
529  {
530  conds_not_yet_printed = dc;
531  }
532  dc->prev = tail_of_conds_not_yet_printed;
533  tail_of_conds_not_yet_printed = dc;
534  }
535  tail_of_conds_not_yet_printed->next = NIL;
536 
537  /* --- main loop: find all conds for first id, print them together --- */
538  Bool did_one_line_already = FALSE;
539  while (conds_not_yet_printed)
540  {
541  if (did_one_line_already)
542  {
543  print (thisAgent, "\n");
544  print_spaces (thisAgent, indent);
545  }
546  else
547  {
548  did_one_line_already = TRUE;
549  }
550 
551  dc = conds_not_yet_printed;
552  remove_from_dll (conds_not_yet_printed, dc, next, prev);
553  c = static_cast<condition_struct *>(dc->item);
555  {
556  free_with_pool (&thisAgent->dl_cons_pool, dc);
557  print_string (thisAgent, "-{");
558  xml_begin_tag(thisAgent, kTagConjunctive_Negation_Condition);
559  print_condition_list (thisAgent, c->data.ncc.top, indent+2, internal);
560  xml_end_tag(thisAgent, kTagConjunctive_Negation_Condition);
561  print_string (thisAgent, "}");
562  continue;
563  }
564 
565  /* --- normal pos/neg conditions --- */
566  removed_goal_test = removed_impasse_test = FALSE;
567  id_test = copy_test_removing_goal_impasse_tests(thisAgent, c->data.tests.id_test,
568  &removed_goal_test,
569  &removed_impasse_test);
570  thisAgent->id_test_to_match = copy_of_equality_test_found_in_test (thisAgent, id_test);
571 
572  /* --- collect all cond's whose id test matches this one --- */
573  conds_for_this_id = dc;
574  dc->prev = NIL;
575  if (internal)
576  {
577  dc->next = NIL;
578  }
579  else
580  {
581  dc->next = extract_dl_list_elements (thisAgent, &conds_not_yet_printed,
583  }
584 
585  /* --- print the collected cond's all together --- */
586  print_string (thisAgent, " (");
587  xml_begin_tag(thisAgent, kTagCondition);
588 
589  if (removed_goal_test)
590  {
591  print_string (thisAgent, "state ");
592  xml_att_val(thisAgent, kConditionTest, kConditionTestState);
593 
594  }
595 
596  if (removed_impasse_test)
597  {
598  print_string (thisAgent, "impasse ");
599  xml_att_val(thisAgent, kConditionTest, kConditionTestImpasse);
600  }
601 
602  print_string (thisAgent, test_to_string (thisAgent, id_test, NULL, 0));
603  xml_att_val(thisAgent, kConditionId, test_to_string (thisAgent, id_test, NULL, 0));
604  deallocate_test (thisAgent, thisAgent->id_test_to_match);
605  deallocate_test (thisAgent, id_test);
606 
608  while (conds_for_this_id)
609  {
610  dc = conds_for_this_id;
611  conds_for_this_id = conds_for_this_id->next;
612  c = static_cast<condition_struct *>(dc->item);
613  free_with_pool (&thisAgent->dl_cons_pool, dc);
614 
615  { /* --- build and print attr/value test for condition c --- */
616  char temp[PRINT_CONDITION_LIST_TEMP_SIZE], *ch;
617 
618  memset(temp,0,PRINT_CONDITION_LIST_TEMP_SIZE);
619  ch = temp;
620  strncpy (ch, " ", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
621  if (c->type==NEGATIVE_CONDITION)
622  {
623  strncat (ch, "-", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
624  }
625  while (*ch) ch++;
626 
627  strncpy (ch, "^", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
628  while (*ch) ch++;
629  test_to_string (thisAgent, c->data.tests.attr_test, ch, PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
630  while (*ch) ch++;
632  {
633  *(ch++) = ' ';
634  test_to_string (thisAgent, c->data.tests.value_test, ch, PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
635  while (*ch) ch++;
637  {
638  strncpy (ch, " +", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
639  while (*ch) ch++;
640  }
641  }
642  *ch = 0;
643  if (thisAgent->printer_output_column + (ch - temp) >= COLUMNS_PER_LINE)
644  {
645  print_string (thisAgent, "\n");
646  print_spaces (thisAgent, indent+6);
647  }
648  print_string (thisAgent, temp);
649  add_to_growable_string(thisAgent, &gs, temp);
650  }
651  }
652  xml_att_val(thisAgent, kCondition, text_of_growable_string(gs));
653  free_growable_string(thisAgent, gs);
654  print_string (thisAgent, ")");
655  xml_end_tag(thisAgent, kTagCondition);
656  } /* end of while (conds_not_yet_printed) */
657 }
658 
659 /* ------------------------------------------------------------------
660  Print Action List
661 
662  This prints a list of actions. The "indent" parameter tells how
663  many spaces to indent each line other than the first--the first
664  line is not indented (the caller must handle this). The last line
665  is printed without a trailing linefeed. The "internal" parameter,
666  if TRUE, indicates that the action list should be printed in
667  internal format--one action per line, without grouping all the
668  actions for the same id into one line.
669  Note: the actions MUST NOT contain any reteloc's.
670 ------------------------------------------------------------------ */
671 
673  action *a;
674  a = static_cast<action_struct *>(dc->item);
675  if (a->type!=MAKE_ACTION) return FALSE;
676  return (rhs_value_to_symbol(a->id) == thisAgent->action_id_to_match);
677 }
678 
679 #define PRINT_ACTION_LIST_TEMP_SIZE 10000
680 void print_action_list (agent* thisAgent, action *actions,
681  int indent, Bool internal) {
682  Bool did_one_line_already;
683  dl_list *actions_not_yet_printed, *tail_of_actions_not_yet_printed;
684  dl_list *actions_for_this_id;
685  dl_cons *dc;
686  action *a;
687 
688  if (!actions) return;
689 
690  did_one_line_already = FALSE;
691 
692  /* --- build dl_list of all the actions --- */
693  actions_not_yet_printed = NIL;
694  tail_of_actions_not_yet_printed = NIL;
695  for (a=actions; a!=NIL; a=a->next) {
696  allocate_with_pool (thisAgent, &thisAgent->dl_cons_pool, &dc);
697  dc->item = a;
698  if (actions_not_yet_printed) tail_of_actions_not_yet_printed->next = dc;
699  else actions_not_yet_printed = dc;
700  dc->prev = tail_of_actions_not_yet_printed;
701  tail_of_actions_not_yet_printed = dc;
702  }
703  tail_of_actions_not_yet_printed->next = NIL;
704 
705  /* --- main loop: find all actions for first id, print them together --- */
706  while (actions_not_yet_printed) {
707  if (did_one_line_already) {
708  print (thisAgent, "\n");
709  print_spaces (thisAgent, indent);
710  } else {
711  did_one_line_already = TRUE;
712  }
713  dc = actions_not_yet_printed;
714  remove_from_dll (actions_not_yet_printed, dc, next, prev);
715  a = static_cast<action_struct *>(dc->item);
716  if (a->type==FUNCALL_ACTION) {
717  free_with_pool (&thisAgent->dl_cons_pool, dc);
718  xml_begin_tag(thisAgent, kTagAction);
719  print_string (thisAgent, rhs_value_to_string (thisAgent, a->value, NULL, 0));
720  xml_att_val(thisAgent, kAction, rhs_value_to_string (thisAgent, a->value, NULL, 0));
721  xml_end_tag(thisAgent, kTagAction);
722  continue;
723  }
724 
725  /* --- normal make actions --- */
726  /* --- collect all actions whose id matches the first action's id --- */
727  actions_for_this_id = dc;
728  thisAgent->action_id_to_match = rhs_value_to_symbol(a->id);
729  dc->prev = NIL;
730  if (internal) {
731  dc->next = NIL;
732  } else {
733  dc->next = extract_dl_list_elements (thisAgent, &actions_not_yet_printed,
735  }
736 
737  /* --- print the collected actions all together --- */
738  print_with_symbols (thisAgent, "(%y", thisAgent->action_id_to_match);
739  xml_begin_tag(thisAgent, kTagAction);
740  xml_att_val(thisAgent, kActionId, thisAgent->action_id_to_match);
742  while (actions_for_this_id) {
743  dc = actions_for_this_id;
744  actions_for_this_id = actions_for_this_id->next;
745  a = static_cast<action_struct *>(dc->item);
746  free_with_pool (&thisAgent->dl_cons_pool, dc);
747 
748  { /* --- build and print attr/value test for action a --- */
749  char temp[PRINT_ACTION_LIST_TEMP_SIZE], *ch;
750 
751  ch = temp;
752  strncpy (ch, " ^", PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp)); while (*ch) ch++;
753  rhs_value_to_string (thisAgent, a->attr, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
754  while (*ch) ch++;
755  *(ch++) = ' ';
756  rhs_value_to_string (thisAgent, a->value, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
757  while (*ch) ch++;
758  *(ch++) = ' ';
759  *(ch++) = preference_type_indicator (thisAgent, a->preference_type);
761  *(ch++) = ' ';
762  rhs_value_to_string (thisAgent, a->referent, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
763  while (*ch) ch++;
764  }
765  *ch = 0;
766  if (thisAgent->printer_output_column + (ch - temp) >=
768  print_string (thisAgent, "\n");
769  print_spaces (thisAgent, indent+6);
770  }
771  print_string (thisAgent, temp);
772  add_to_growable_string(thisAgent, &gs, temp);
773  }
774  }
775  xml_att_val(thisAgent, kAction, text_of_growable_string(gs));
776  free_growable_string(thisAgent, gs);
777  print_string (thisAgent, ")");
778  xml_end_tag(thisAgent, kTagAction);
779  } /* end of while (actions_not_yet_printed) */
780 }
781 
782 /* ------------------------------------------------------------------
783  Print Production
784 
785  This prints a production. The "internal" parameter, if TRUE,
786  indicates that the LHS and RHS should be printed in internal format.
787 ------------------------------------------------------------------ */
788 
789 void print_production (agent* thisAgent, production *p, Bool internal) {
790  condition *top, *bottom;
791  action *rhs;
792 
793  /*
794  --- print "sp" and production name ---
795  */
796  print_with_symbols (thisAgent, "sp {%y\n", p->name);
797 
798  xml_begin_tag(thisAgent, kTagProduction);
799  xml_att_val(thisAgent, kProduction_Name, p->name);
800 
801  /*
802  --- print optional documention string ---
803  */
804  if (p->documentation)
805  {
806  char temp[MAX_LEXEME_LENGTH*2+10];
807  string_to_escaped_string (thisAgent, p->documentation, '"', temp);
808  print (thisAgent, " %s\n", temp);
809  xml_att_val(thisAgent, kProductionDocumentation, temp);
810  }
811 
812  /*
813  --- print any flags ---
814  */
815  switch (p->type)
816  {
818  print_string (thisAgent, " :default\n");
819  xml_att_val(thisAgent, kProductionType, kProductionTypeDefault);
820  break;
821  case USER_PRODUCTION_TYPE:
822  break;
823  case CHUNK_PRODUCTION_TYPE:
824  print_string (thisAgent, " :chunk\n");
825  xml_att_val(thisAgent, kProductionType, kProductionTypeChunk);
826  break;
828  print_string (thisAgent, " :justification ;# not reloadable\n");
829  xml_att_val(thisAgent, kProductionType, kProductionTypeJustification);
830  break;
832  print_string (thisAgent, " :template\n");
833  xml_att_val(thisAgent, kProductionType, kProductionTypeTemplate);
834  break;
835  }
836 
838  {
839  print_string (thisAgent, " :o-support\n");
840  xml_att_val(thisAgent, kProductionDeclaredSupport, kProductionDeclaredOSupport);
841  }
842 
844  {
845  print_string (thisAgent, " :i-support\n");
846  xml_att_val(thisAgent, kProductionDeclaredSupport, kProductionDeclaredISupport);
847  }
848 
849  if (p->interrupt)
850  print_string(thisAgent, " :interrupt\n");
851 
852  /*
853  --- print the LHS and RHS ---
854  */
855  p_node_to_conditions_and_nots (thisAgent, p->p_node, NIL, NIL,
856  &top, &bottom, NIL,&rhs);
857  print_string (thisAgent, " ");
858 
859  xml_begin_tag(thisAgent, kTagConditions);
860  print_condition_list (thisAgent, top, 3, internal);
861  xml_end_tag(thisAgent, kTagConditions);
862  deallocate_condition_list (thisAgent, top);
863 
864  print_string (thisAgent, "\n -->\n ");
865  print_string (thisAgent, " ");
866  xml_begin_tag(thisAgent, kTagActions);
867  print_action_list (thisAgent, rhs, 4, internal);
868  xml_end_tag(thisAgent, kTagActions);
869  print_string (thisAgent, "\n}\n");
870  xml_end_tag(thisAgent, kTagProduction);
871 
872  deallocate_action_list (thisAgent, rhs);
873 }
874 
875 /* ------------------------------------------------------------------
876  Other Printing Utilities
877 
878  Print_condition() prints a single condition. Print_action() prints
879  a single action (which MUST NOT contain any reteloc's).
880  Note that these routines work by calling print_condition_list() and
881  print_action_list(), respectively, so they print a linefeed if the
882  output would go past COLUMNS_PER_LINE.
883 
884  Preference_type_indicator() returns a character corresponding to
885  a given preference type (byte)--for example, given BEST_PREFERENCE_TYPE,
886  it returns '>'.
887 
888  Print_preference() prints a given preference. Print_wme() prints a
889  wme (including the timetag). Print_instantiation_with_wmes() prints
890  an instantiation's production name and the wmes it matched, using a
891  given wme_trace_type (e.g., TIMETAG_WME_TRACE).
892 ------------------------------------------------------------------ */
893 
894 void print_condition (agent* thisAgent, condition *cond) {
895  condition *old_next, *old_prev;
896 
897  old_next = cond->next;
898  old_prev = cond->prev;
899  cond->next = NIL;
900  cond->prev = NIL;
901  print_condition_list (thisAgent, cond, 0, TRUE);
902  cond->next = old_next;
903  cond->prev = old_prev;
904 }
905 
906 void print_action (agent* thisAgent, action *a) {
907  action *old_next;
908 
909  old_next = a->next;
910  a->next = NIL;
911  print_action_list (thisAgent, a, 0, TRUE);
912  a->next = old_next;
913 }
914 
915 char preference_type_indicator (agent* thisAgent, byte type) {
916  switch (type) {
917  case ACCEPTABLE_PREFERENCE_TYPE: return '+';
918  case REQUIRE_PREFERENCE_TYPE: return '!';
919  case REJECT_PREFERENCE_TYPE: return '-';
920  case PROHIBIT_PREFERENCE_TYPE: return '~';
921  case RECONSIDER_PREFERENCE_TYPE: return '@';
922  case NUMERIC_INDIFFERENT_PREFERENCE_TYPE: return '=';
923  case UNARY_INDIFFERENT_PREFERENCE_TYPE: return '=';
924  case BINARY_INDIFFERENT_PREFERENCE_TYPE: return '=';
925  case UNARY_PARALLEL_PREFERENCE_TYPE: return '&';
926  case BINARY_PARALLEL_PREFERENCE_TYPE: return '&';
927  case BEST_PREFERENCE_TYPE: return '>';
928  case BETTER_PREFERENCE_TYPE: return '>';
929  case WORST_PREFERENCE_TYPE: return '<';
930  case WORSE_PREFERENCE_TYPE: return '<';
931  default:
932  { char msg[BUFFER_MSG_SIZE];
933  strncpy(msg,
934  "print.c: Error: bad type passed to preference_type_indicator\n", BUFFER_MSG_SIZE);
935  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
936  abort_with_fatal_error(thisAgent, msg);
937  }
938  }
939  return 0; /* unreachable, but without it, gcc -Wall warns here */
940 }
941 
942 void print_preference (agent* thisAgent, preference *pref) {
943  char pref_type = preference_type_indicator (thisAgent, pref->type);
944 
945  print_with_symbols (thisAgent, "(%y ^%y %y ", pref->id, pref->attr, pref->value);
946  print (thisAgent, "%c", pref_type);
947  if (preference_is_binary(pref->type)) {
948  print_with_symbols (thisAgent, " %y", pref->referent);
949  }
950  if (pref->o_supported) print_string (thisAgent, " :O ");
951  print_string (thisAgent, ")");
952  print (thisAgent, "\n");
953 
954  // <preference id="s1" attr="foo" value="123" pref_type=">"></preference>
955  xml_begin_tag(thisAgent, kTagPreference);
956  xml_att_val(thisAgent, kWME_Id, pref->id);
957  xml_att_val(thisAgent, kWME_Attribute, pref->attr);
958  xml_att_val(thisAgent, kWME_Value, pref->value);
959 
960  char buf[2];
961  buf[0] = pref_type;
962  buf[1] = 0;
963  xml_att_val(thisAgent, kPreference_Type, buf);
964 
965  if (preference_is_binary(pref->type)) {
966  xml_att_val(thisAgent, kReferent, pref->referent);
967  }
968  if (pref->o_supported) {
969  xml_att_val(thisAgent, kOSupported, ":O");
970  }
971  xml_end_tag(thisAgent, kTagPreference);
972 
973 }
974 //#ifdef USE_TCL
975 
976 /* kjh(CUSP-B2) begin */
977 
978 extern "C" Bool passes_wme_filtering(agent* thisAgent, wme *w, Bool isAdd);
979 void
981  if (passes_wme_filtering(thisAgent, w,TRUE))
982  {
983  print (thisAgent, "=>WM: ");
984  xml_begin_tag(thisAgent, kTagWMEAdd);
985  print_wme(thisAgent, w);
986  xml_end_tag(thisAgent, kTagWMEAdd);
987  }
988 }
989 
990 void filtered_print_wme_remove(agent* thisAgent, wme *w)
991 {
992  if (passes_wme_filtering(thisAgent, w,FALSE))
993  {
994  print (thisAgent, "<=WM: ");
995  xml_begin_tag(thisAgent, kTagWMERemove);
996  print_wme(thisAgent, w); /* print_wme takes care of tagged output itself */
997  xml_end_tag(thisAgent, kTagWMERemove);
998  }
999 }
1000 //#endif /* USE_TCL */
1001 
1002 /* kjh(CUSP-B2) end */
1003 
1004 void print_wme (agent* thisAgent, wme *w) {
1005  print (thisAgent, "(%lu: ", w->timetag);
1006  print_with_symbols (thisAgent, "%y ^%y %y", w->id, w->attr, w->value);
1007 
1008  if (wma_enabled(thisAgent))
1009  {
1010  print (thisAgent, " [%0.2g]", wma_get_wme_activation(thisAgent, w, true));
1011  }
1012 
1013  if (w->acceptable) print_string (thisAgent, " +");
1014  print_string (thisAgent, ")");
1015  print (thisAgent, "\n");
1016 
1017  // <wme tag="123" id="s1" attr="foo" attrtype="string" val="123" valtype="string"></wme>
1018  xml_object( thisAgent, w );
1019 }
1020 
1021 void print_wme_without_timetag (agent* thisAgent, wme *w) {
1022  print_with_symbols (thisAgent, "(%y ^%y %y", w->id, w->attr, w->value);
1023  if (w->acceptable) print_string (thisAgent, " +");
1024  print_string (thisAgent, ")");
1025  print (thisAgent, "\n");
1026 
1027  // <wme id="s1" attr="foo" attrtype="string" val="123" valtype="string"></wme>
1028  xml_object( thisAgent, w, XML_WME_NO_TIMETAG );
1029 }
1030 
1031 //#ifdef USE_TCL
1032 void print_wme_for_tcl (agent* thisAgent, wme *w)
1033 {
1034  print (thisAgent, "%lu: ", w->timetag);
1035  print_with_symbols (thisAgent, "%y ^%y %y", w->id, w->attr, w->value);
1036  if (w->acceptable) print_string (thisAgent, " +");
1037 }
1038 //#endif /* USE_TCL */
1039 
1041  wme_trace_type wtt, int action)
1042 {
1043  int PRINTING = -1;
1044  int FIRING = 0;
1045  int RETRACTING = 1;
1046  condition *cond;
1047 
1048 
1049  if (action == PRINTING) {
1050  xml_begin_tag(thisAgent, kTagProduction);
1051  } else if (action == FIRING) {
1052  xml_begin_tag(thisAgent, kTagProduction_Firing);
1053  xml_begin_tag(thisAgent, kTagProduction);
1054  } else if (action == RETRACTING) {
1055  xml_begin_tag(thisAgent, kTagProduction_Retracting);
1056  xml_begin_tag(thisAgent, kTagProduction);
1057  }
1058 
1059  if (inst->prod) {
1060  print_with_symbols (thisAgent, "%y", inst->prod->name);
1061  xml_att_val(thisAgent, kProduction_Name, symbol_to_string (thisAgent, inst->prod->name, true, 0, 0));
1062  } else {
1063  print (thisAgent, "[dummy production]");
1064  xml_att_val(thisAgent, kProduction_Name, "[dummy_production]");
1065 
1066  }
1067 
1068  print (thisAgent, "\n");
1069 
1070  if (wtt==NONE_WME_TRACE) {
1071  if (action == PRINTING) {
1072  xml_end_tag(thisAgent, kTagProduction);
1073  } else if (action == FIRING) {
1074  xml_end_tag(thisAgent, kTagProduction);
1075  xml_end_tag(thisAgent, kTagProduction_Firing);
1076  } else if (action == RETRACTING) {
1077  xml_end_tag(thisAgent, kTagProduction);
1078  xml_end_tag(thisAgent, kTagProduction_Retracting);
1079  }
1080  return;
1081  }
1082 
1083  for (cond=inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next)
1084  if (cond->type==POSITIVE_CONDITION) {
1085  switch (wtt) {
1086  case TIMETAG_WME_TRACE:
1087  print (thisAgent, " %lu", cond->bt.wme_->timetag);
1088 
1089  xml_begin_tag(thisAgent, kTagWME);
1090  xml_att_val(thisAgent, kWME_TimeTag, cond->bt.wme_->timetag);
1091  xml_end_tag(thisAgent, kTagWME);
1092 
1093  break;
1094  case FULL_WME_TRACE:
1095  // Not all conds and wme_'s available when retracting, depending on DO_TOP_LEVEL_REF_CTS
1096  #ifdef DO_TOP_LEVEL_REF_CTS
1097  print (thisAgent, " ");
1098  print_wme (thisAgent, cond->bt.wme_);
1099  #else
1100  if (action != RETRACTING && cond->bt.level > TOP_GOAL_LEVEL) {
1101  print (thisAgent, " ");
1102  print_wme (thisAgent, cond->bt.wme_);
1103  } else {
1104  // Wmes that matched the LHS of a retraction may already be free'd; just print tt.
1105  print (thisAgent, " %lu", cond->bt.wme_->timetag);
1106 
1107  xml_begin_tag(thisAgent, kTagWME);
1108  xml_att_val(thisAgent, kWME_TimeTag, cond->bt.wme_->timetag);
1109  xml_end_tag(thisAgent, kTagWME);
1110  }
1111  #endif
1112  break;
1113  }
1114  }
1115 
1116  if (action == PRINTING) {
1117  xml_end_tag(thisAgent, kTagProduction);
1118  } else if (action == FIRING) {
1119  xml_end_tag(thisAgent, kTagProduction);
1120  xml_end_tag(thisAgent, kTagProduction_Firing);
1121  } else if (action == RETRACTING) {
1122  xml_end_tag(thisAgent, kTagProduction);
1123  xml_end_tag(thisAgent, kTagProduction_Retracting);
1124  }
1125 }
1126 
1127 /***************************************************************************
1128 * Function : print_list_of_conditions
1129 **************************************************************************/
1130 
1131 void print_list_of_conditions(agent* thisAgent, condition *cond) {
1132 
1133  while (cond != NULL) {
1134  if (get_printer_output_column(thisAgent) >= COLUMNS_PER_LINE-20)
1135  print (thisAgent, "\n ");
1136  print_condition (thisAgent, cond);
1137  print (thisAgent, "\n");
1138 
1139  cond = cond->next;
1140  }
1141 }
1142 
1143 void print_phase (agent* thisAgent, const char * s, bool end_of_phase)
1144 {
1145  // should be more consistent with creating string, but for now, for
1146  // consistency with previous versions, we'll let calling code set string.
1147  print (thisAgent, s);
1148 
1149  // the rest is all for tagged output events
1150 
1151  xml_begin_tag(thisAgent, kTagPhase);
1152 
1153  if (end_of_phase) {
1154  xml_att_val(thisAgent, kPhase_Status, kPhaseStatus_End);
1155  }
1156 
1157  switch (thisAgent->current_phase) {
1158  case INPUT_PHASE:
1159  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Input);
1160  break;
1161  case PREFERENCE_PHASE:
1162  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Pref);
1163  break;
1164  case WM_PHASE:
1165  xml_att_val(thisAgent, kPhase_Name, kPhaseName_WM);
1166  switch (thisAgent->FIRING_TYPE) {
1167  case PE_PRODS: /* no longer needed; Soar8 has PROPOSE/APPLY */
1168  xml_att_val(thisAgent, kPhase_FiringType, kPhaseFiringType_PE);
1169  break;
1170  case IE_PRODS:
1171  xml_att_val(thisAgent, kPhase_FiringType, kPhaseFiringType_IE);
1172  break;
1173  }
1174  break;
1175  case DECISION_PHASE:
1176  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Decision);
1177  break;
1178  case OUTPUT_PHASE:
1179  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Output);
1180  break;
1181  case PROPOSE_PHASE:
1182  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Propose);
1183  break;
1184  case APPLY_PHASE:
1185  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Apply);
1186  break;
1187  default:
1188  xml_att_val(thisAgent, kPhase_Name, kPhaseName_Unknown);
1189  break;
1190  } // end switch
1191 
1192  xml_end_tag(thisAgent, kTagPhase);
1193  return;
1194 }
1195 
1196 /*
1197 ===========================
1198 
1199 ===========================
1200 */
1201 Bool wme_filter_component_match(Symbol * filterComponent, Symbol * wmeComponent) {
1202  if ((filterComponent->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE) &&
1203  (!strcmp(filterComponent->sc.name,"*")))
1204  return TRUE;
1205 
1206  return(filterComponent == wmeComponent);
1207 
1208 }
1209 
1210 /*
1211 ===========================
1212 
1213 ===========================
1214 */
1215 Bool passes_wme_filtering(agent* thisAgent, wme * w, Bool isAdd) {
1216  cons *c;
1217  wme_filter *wf;
1218 
1219  /* print ("testing wme for filtering: "); print_wme(w); */
1220 
1221  for (c=thisAgent->wme_filter_list; c!=NIL; c=c->rest) {
1222  wf = (wme_filter *) c->first;
1223  /* print_with_symbols(thisAgent, " trying filter: %y ^%y %y\n",wf->id,wf->attr,wf->value); */
1224  if ( ((isAdd && wf->adds) || ((!isAdd) && wf->removes)) &&
1225  (!wme_filter_component_match(wf->id,w->id)
1227  || !wme_filter_component_match(wf->value,w->value)))
1228  return FALSE;
1229  }
1230  return TRUE; /* no defined filters match -> w passes */
1231 }