Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
io.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: io.cpp
11  *
12  * =======================================================================
13  *
14  *
15  * General Soar I/O System Routines
16  *
17  * User-defined Soar I/O routines should be added at system startup time
18  * via calls to add_input_function() and add_output_function(). These
19  * calls add things to the system's list of (1) functions to be called
20  * every input cycle, and (2) symbol-to-function mappings for output
21  * commands. File io.cpp contains the system I/O mechanism itself (i.e.,
22  * the stuff that calls the input and output functions), plus the text
23  * I/O routines.
24  *
25  * Init_soar_io() does what it says. Do_input_cycle() and do_output_cycle()
26  * perform the entire input and output cycles -- these routines are called
27  * once per elaboration cycle. (once per Decision cycle in Soar 8).
28  * The output module is notified about WM changes via a call to
29  * inform_output_module_of_wm_changes().
30  *
31  * =======================================================================
32  */
33 
34 
35 /* ==================================================================
36  I/O Code for Soar 6
37 
38  General Soar I/O System Routines, and Text I/O Routines
39 
40  See comments in soarkernel.h for more information.
41  ================================================================== */
42 
43 #include <stdlib.h>
44 
45 #include "io_soar.h"
46 #include "callback.h"
47 #include "agent.h"
48 #include "print.h"
49 #include "init_soar.h"
50 #include "gdatastructs.h"
51 #include "wmem.h"
52 #include "symtab.h"
53 #include "decide.h"
54 #include "production.h"
55 #include "lexer.h"
56 #include "xml.h"
57 #include "soar_TraceNames.h"
58 #include "utilities.h"
59 
60 #include "wma.h"
61 
62 #include <ctype.h>
63 
64 #include <assert.h>
65 
66 using namespace soar_TraceNames;
67 
68 
69 extern void gds_invalid_so_remove_goal (agent* thisAgent, wme *w);
70 
71 /* ====================================================================
72  Adding New Input and Output Functions
73 
74  The system maintains a list of all the input functions to be called
75  every input cycle, and another list of all the symbol-to-function
76  mappings for output commands. Add_input_function() and
77  add_output_function() should be called at system startup time to
78  install each I/O function.
79 ==================================================================== */
80 
82  soar_callback_data cb_data,
83  soar_callback_free_fn free_fn, const char * name) {
84  soar_add_callback(thisAgent, INPUT_PHASE_CALLBACK, f, INPUT_PHASE_CALLBACK, cb_data, free_fn, name);
85 }
86 
87 void remove_input_function (agent* thisAgent, const char * name) {
89 }
90 
91 void add_output_function (agent* thisAgent,
93  soar_callback_data cb_data,
94  soar_callback_free_fn free_fn,
95  int eventID,
96  const char * output_link_name)
97 {
98  if (soar_exists_callback_id (thisAgent, OUTPUT_PHASE_CALLBACK, output_link_name)
99  != NULL)
100  {
101  print (thisAgent, "Error: tried to add_output_function with duplicate name %s\n",
102  output_link_name);
103  /* Replaced deprecated control_c_handler with an appropriate assertion */
104  //control_c_handler(0);
105  assert(0 && "error in io.cpp (control_c_handler() used to be called here)");
106  }
107  else
108  {
109  soar_add_callback(thisAgent, OUTPUT_PHASE_CALLBACK, f, eventID, cb_data, free_fn,
110  output_link_name);
111  }
112 }
113 
114 void remove_output_function (agent* thisAgent, const char * name) {
115  soar_callback * cb;
116  output_link *ol;
117 
118  /* Remove indexing structures ... */
119 
120  cb = soar_exists_callback_id(thisAgent, OUTPUT_PHASE_CALLBACK, name);
121  if (!cb) return;
122 
123  for (ol=thisAgent->existing_output_links; ol!=NIL; ol=ol->next)
124  {
125  if (ol->cb == cb)
126  {
127  /* Remove ol entry */
128  ol->link_wme->output_link = NULL;
129  wme_remove_ref(thisAgent, ol->link_wme);
131  free_with_pool(&(thisAgent->output_link_pool), ol);
132  break;
133  }
134  }
135 
137 }
138 
139 /* ====================================================================
140  Input Routines
141 
142  Get_new_io_identifier(), get_io_sym_constant(), get_io_int_constant(),
143  and get_io_float_constant() just call the appropriate symbol table
144  routines. This has the effect of incrementing the reference count
145  on the symbol (or creating one with a reference count of 1).
146  Release_io_symbol() just decrements the reference count.
147 
148  Add_input_wme() and remove_input_wme() call the add_wme_to_wm() and
149  remove_wme_from_wm() routines in decide.cpp to do their work.
150 
151  Do_input_cycle() is the top-level routine which calls all the
152  individual user-defined input functions, etc.
153 
154  All this stuff is really simple, and consequently pretty vulnerable
155  to buggy user-written I/O code. A more sophisticated version would
156  be bullet-proofed against bad arguments to get_xxx(), add_input_wme(),
157  and remove_input_wme(). Right now add_input_wme() and remove_input_wme()
158  do some error checking, but they're nowhere near bullet-proof.
159 ==================================================================== */
160 
161 Symbol *get_new_io_identifier(agent* thisAgent, char first_letter)
162 {
163  return make_new_identifier (thisAgent, first_letter, TOP_GOAL_LEVEL);
164 }
165 
166 Symbol *get_io_identifier (agent* thisAgent, char first_letter, uint64_t number) {
167  Symbol* id = find_identifier(thisAgent, first_letter, number) ;
168 
169  // DJP: The other "make_<type>" methods either make a new object or incremenent the refence
170  // on an existing object. So I'm going to make this method function the same way for identifiers.
171  if (id)
172  {
173  symbol_add_ref(id);
174  }
175  else
176  {
177  id = make_new_identifier (thisAgent, first_letter, TOP_GOAL_LEVEL);
178  }
179 
180  return id ;
181 }
182 
183 Symbol *get_io_sym_constant (agent* thisAgent, char const *name) {
184  return make_sym_constant (thisAgent, name);
185 }
186 
187 Symbol *get_io_int_constant (agent* thisAgent, int64_t value) {
188  return make_int_constant (thisAgent, value);
189 }
190 
191 Symbol *get_io_float_constant (agent* thisAgent, double value) {
192  return make_float_constant (thisAgent, value);
193 }
194 
195 uint64_t release_io_symbol (agent* thisAgent, Symbol *sym) {
196  return symbol_remove_ref (thisAgent, sym);
197 }
198 
199 wme *add_input_wme (agent* thisAgent, Symbol *id, Symbol *attr, Symbol *value) {
200  wme *w;
201 
202  /* --- a little bit of error checking --- */
203  if (! (id && attr && value)) {
204  print (thisAgent, "Error: an input routine gave a NULL argument to add_input_wme.\n");
205  return NIL;
206  }
207 
208  /* --- go ahead and add the wme --- */
209  w = make_wme (thisAgent, id, attr, value, FALSE);
211 
212  if ( wma_enabled( thisAgent ) )
213  {
214  wma_activate_wme( thisAgent, w );
215  }
216 
217  add_wme_to_wm (thisAgent, w);
218 
219  //PrintDebugFormat("Added wme with timetag %d to id %c%d ",w->timetag,id->id.name_letter,id->id.name_number) ;
220 
221  return w;
222 }
223 
224 wme* find_input_wme_by_timetag_from_id (agent* thisAgent, Symbol* idSym, uint64_t timetag, tc_number tc) {
225  wme *pWME,*w;
226 
227  //PrintDebugFormat("Scanning id %c%d", idSym->id.name_letter, idSym->id.name_number) ;
228 
229  // Mark this id as having been visited (the key here is that tc numbers always increase so tc_num must be < tc until it's marked)
230  idSym->id.tc_num = tc ;
231 
232  // This is inefficient. Using a hash table could save a lot here.
233  for (pWME = idSym->id.input_wmes; pWME != NIL; pWME = pWME->next)
234  {
235  //PrintDebugFormat("Timetag %ld", pWME->timetag) ;
236  if (pWME->timetag == timetag)
237  return pWME ;
238 
239  // NOTE: The test for the tc_num keeps us from getting stuck in loops within graphs
240  if (pWME->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE && pWME->value->id.tc_num != tc)
241  {
242  w = find_input_wme_by_timetag_from_id(thisAgent, pWME->value, timetag, tc) ;
243  if (w)
244  return w ;
245  }
246  }
247 
248  return NIL ;
249 }
250 
251 Bool remove_input_wme (agent* thisAgent, wme *w) {
252  wme *temp;
253 
254  /* --- a little bit of error checking --- */
255  if (!w) {
256  print (thisAgent, "Error: an input routine called remove_input_wme on a NULL wme.\n");
257  return FALSE;
258  }
259  for (temp=w->id->id.input_wmes; temp!=NIL; temp=temp->next)
260  if (temp==w) break;
261  if (!temp) {
262  print (thisAgent, "Error: an input routine called remove_input_wme on a wme that\n");
263  print (thisAgent, "isn't one of the input wmes currently in working memory.\n");
264  return FALSE;
265  }
266  /* Note: for efficiency, it might be better to use a hash table for the
267  above test, rather than scanning the linked list. We could have one
268  global hash table for all the input wmes in the system. */
269  /* --- go ahead and remove the wme --- */
271  /* REW: begin 09.15.96 */
272  if (w->gds) {
273  if (w->gds->goal != NIL) {
274  gds_invalid_so_remove_goal(thisAgent, w);
275  /* NOTE: the call to remove_wme_from_wm will take care
276  of checking if GDS should be removed */
277  }
278  }
279  /* REW: end 09.15.96 */
280 
281  remove_wme_from_wm (thisAgent, w);
282 
283  return TRUE;
284 }
285 
286 
287 void do_input_cycle (agent* thisAgent) {
288 
289  if (thisAgent->prev_top_state && (!thisAgent->top_state)) {
290  /* --- top state was just removed --- */
292  reinterpret_cast<soar_call_data>(TOP_STATE_JUST_REMOVED) );
293  release_io_symbol (thisAgent, thisAgent->io_header);
294  release_io_symbol (thisAgent, thisAgent->io_header_input);
295  release_io_symbol (thisAgent, thisAgent->io_header_output);
296  thisAgent->io_header = NIL; /* RBD added 3/25/95 */
297  thisAgent->io_header_input = NIL; /* RBD added 3/25/95 */
298  thisAgent->io_header_output = NIL; /* KJC added 3/3/99 */
299  thisAgent->io_header_link = NIL; /* KJC added 3/3/99 */
300  } else if ((!thisAgent->prev_top_state) && thisAgent->top_state) {
301  /* --- top state was just created --- */
302  /* Create io structure on top state. */
303  /*
304  thisAgent->io_header = get_new_io_identifier (thisAgent, 'I');
305  thisAgent->io_header_link = add_input_wme (thisAgent,
306  thisAgent->top_state,
307  thisAgent->io_symbol,
308  thisAgent->io_header);
309  thisAgent->io_header_input = get_new_io_identifier (thisAgent, 'I');
310  thisAgent->io_header_output = get_new_io_identifier (thisAgent, 'I');
311  add_input_wme (thisAgent, thisAgent->io_header,
312  make_sym_constant(thisAgent, "input-link"),
313  thisAgent->io_header_input);
314  add_input_wme (thisAgent, thisAgent->io_header,
315  make_sym_constant(thisAgent, "output-link"),
316  thisAgent->io_header_output);
317  */
318  /* --- add top state io link before calling input phase callback so
319  * --- code can use "wmem" command.
320  */
321  /*
322  do_buffered_wm_and_ownership_changes(thisAgent);
323 
324  soar_invoke_callbacks(thisAgent, thisAgent, INPUT_PHASE_CALLBACK,
325  (soar_call_data) TOP_STATE_JUST_CREATED);
326  */
327  }
328 
329  /* --- if there is a top state, do the normal input cycle --- */
330 
331  if (thisAgent->top_state) {
333  reinterpret_cast<soar_call_data>(NORMAL_INPUT_CYCLE) );
334  }
335 
336  /* --- do any WM resulting changes --- */
338 
339  /* --- save current top state for next time --- */
340  thisAgent->prev_top_state = thisAgent->top_state;
341 
342  /* --- reset the output-link status flag to FALSE
343  * --- when running til output, only want to stop if agent
344  * --- does add-wme to output. don't stop if add-wme done
345  * --- during input cycle (eg simulator updates sensor status)
346  * KJC 11/23/98
347  */
348  thisAgent->output_link_changed = FALSE;
349 
350 }
351 
352 /* ====================================================================
353  Output Routines
354 
355  Inform_output_module_of_wm_changes() and do_output_cycle() are the
356  two top-level entry points to the output routines. The former is
357  called by the working memory manager, and the latter from the top-level
358  phase sequencer.
359 
360  This module maintains information about all the existing output links
361  and the identifiers and wmes that are in the transitive closure of them.
362  On each output link wme, we put a pointer to an output_link structure.
363  Whenever inform_output_module_of_wm_changes() is called, we look for
364  new output links and modifications/removals of old ones, and update
365  the output_link structures accordingly.
366 
367  Transitive closure information is kept as follows: each output_link
368  structure has a list of all the ids in the link's TC. Each id in
369  the system has a list of all the output_link structures that it's
370  in the TC of.
371 
372  After some number of calls to inform_output_module_of_wm_changes(),
373  eventually do_output_cycle() gets called. It scans through the list
374  of output links and calls the necessary output function for each
375  link that has changed in some way (add/modify/remove).
376 ==================================================================== */
377 
378 /* --- output link statuses --- */
379 #define NEW_OL_STATUS 0 /* just created it */
380 #define UNCHANGED_OL_STATUS 1 /* normal status */
381 #define MODIFIED_BUT_SAME_TC_OL_STATUS 2 /* some value in its TC has been
382  modified, but the ids in its TC
383  are the same */
384 #define MODIFIED_OL_STATUS 3 /* the set of ids in its TC has
385  changed */
386 #define REMOVED_OL_STATUS 4 /* link has just been removed */
387 
388 /* --------------------------------------------------------------------
389  Output Link Status Updates on WM Changes
390 
391  Top-state link changes:
392 
393  For wme addition: (top-state ^link-attr anything)
394  create new output_link structure; mark it "new"
395  For wme removal: (top-state ^link-attr anything)
396  mark the output_link "removed"
397 
398  TC of existing link changes:
399 
400  For wme addition or removal: (<id> ^att constant):
401  for each link in associated_output_links(id),
402  mark link "modified but same tc" (unless it's already marked
403  some other more serious way)
404 
405  For wme addition or removal: (<id> ^att <id2>):
406  for each link in associated_output_links(id),
407  mark link "modified" (unless it's already marked
408  some other more serious way)
409 
410  Note that we don't update all the TC information after every WM change.
411  The TC info doesn't get updated until do_output_cycle() is called.
412 -------------------------------------------------------------------- */
413 
414 #define LINK_NAME_SIZE 1024
415 void update_for_top_state_wme_addition (agent* thisAgent, wme *w) {
416  output_link *ol;
417  soar_callback *cb;
418  char link_name[LINK_NAME_SIZE];
419 
420  /* --- check whether the attribute is an output function --- */
421  symbol_to_string(thisAgent, w->attr, FALSE, link_name, LINK_NAME_SIZE);
422  cb = soar_exists_callback_id(thisAgent, OUTPUT_PHASE_CALLBACK, link_name);
423  if (!cb) return;
424 
425  /* --- create new output link structure --- */
426  allocate_with_pool (thisAgent, &thisAgent->output_link_pool, &ol);
428 
429  ol->status = NEW_OL_STATUS;
430  ol->link_wme = w;
431  wme_add_ref (w);
432  ol->ids_in_tc = NIL;
433  ol->cb = cb;
434  /* --- make wme point to the structure --- */
435  w->output_link = ol;
436 
437  /* SW 07 10 2003
438  previously, this wouldn't be done until the first OUTPUT phase.
439  However, if we add an output command in the 1st decision cycle,
440  Soar seems to ignore it.
441 
442  There may be two things going on, the first having to do with the tc
443  calculation, which may get done too late, in such a way that the
444  initial calculation includes the command. The other thing appears
445  to be that some data structures are not initialized until the first
446  output phase. Namely, id->associated_output_links does not seem
447  reflect the current output links until the first output-phase.
448 
449  To get past these issues, we fake a transitive closure calculation
450  with the knowledge that the only thing on the output link at this
451  point is the output-link identifier itself. This way, we capture
452  a snapshot of the empty output link, so Soar can detect any changes
453  that might occur before the first output_phase. */
454 
455  /* KJC & RPM 10/06 commenting out SW's change.
456  See near end of init_agent_memory for details */
457  //thisAgent->output_link_tc_num = get_new_tc_number(thisAgent);
458  //ol->link_wme->value->id.tc_num = thisAgent->output_link_tc_num;
459  //thisAgent->output_link_for_tc = ol;
461  //push(thisAgent, thisAgent->output_link_for_tc, ol->link_wme->value->id.associated_output_links);
462 }
463 
465  if (! w->output_link) return;
467 }
468 
469 void update_for_io_wme_change (wme *w) {
470  cons *c;
471  output_link *ol;
472 
473  for (c=w->id->id.associated_output_links; c!=NIL; c=c->rest) {
474  ol = static_cast<output_link_struct *>(c->first);
475  if (w->value->common.symbol_type==IDENTIFIER_SYMBOL_TYPE) {
476  /* --- mark ol "modified" --- */
477  if ((ol->status==UNCHANGED_OL_STATUS) ||
480  } else {
481  /* --- mark ol "modified but same tc" --- */
482  if (ol->status==UNCHANGED_OL_STATUS)
484  }
485  }
486 }
487 
488 void inform_output_module_of_wm_changes (agent* thisAgent,
489  list *wmes_being_added,
490  list *wmes_being_removed) {
491  cons *c;
492  wme *w;
493 
494  /* if wmes are added, set flag so can stop when running til output */
495  for (c=wmes_being_added; c!=NIL; c=c->rest) {
496  w = static_cast<wme_struct *>(c->first);
497  if (w->id==thisAgent->io_header) {
498  update_for_top_state_wme_addition (thisAgent, w);
499  thisAgent->output_link_changed = TRUE; /* KJC 11/23/98 */
500  thisAgent->d_cycle_last_output = thisAgent->d_cycle_count; /* KJC 11/17/05 */
501  }
502  if (w->id->id.associated_output_links) {
504  thisAgent->output_link_changed = TRUE; /* KJC 11/23/98 */
505  thisAgent->d_cycle_last_output = thisAgent->d_cycle_count; /* KJC 11/17/05 */
506  }
507 
508  #if DEBUG_RTO
509  else {
510  char id[100];
511 
512  symbol_to_string(thisAgent, w->id, FALSE, id, 100 );
513  if ( !strcmp( id, "I3" ) ) {
514  print(thisAgent, "--> Added to I3, but doesn't register as an OL change!" );
515  }
516  }
517  #endif
518 
519  }
520  for (c=wmes_being_removed; c!=NIL; c=c->rest) {
521  w = static_cast<wme_struct *>(c->first);
522  if (w->id==thisAgent->io_header) update_for_top_state_wme_removal (w);
524  }
525 }
526 
527 /* --------------------------------------------------------------------
528  Updating Link TC Information
529 
530  We make no attempt to do the TC updating intelligently. Whenever the
531  TC changes, we throw away all the old TC info and recalculate the new
532  TC from scratch. I figure that this part of the system won't get
533  used very frequently and I hope it won't be a time hog.
534 
535  Remove_output_link_tc_info() and calculate_output_link_tc_info() are
536  the main routines here.
537 -------------------------------------------------------------------- */
538 
539 void remove_output_link_tc_info (agent* thisAgent, output_link *ol) {
540  cons *c, *prev_c;
541  Symbol *id;
542 
543  while (ol->ids_in_tc) { /* for each id in the old TC... */
544  c = ol->ids_in_tc;
545  ol->ids_in_tc = c->rest;
546  id = static_cast<symbol_union *>(c->first);
547  free_cons (thisAgent, c);
548 
549  /* --- remove "ol" from the list of associated_output_links(id) --- */
550  prev_c = NIL;
551  for (c=id->id.associated_output_links; c!=NIL; prev_c=c, c=c->rest)
552  if (c->first == ol) break;
553  if (!c) {
554  char msg[BUFFER_MSG_SIZE];
555  strncpy(msg,"io.c: Internal error: can't find output link in id's list\n", BUFFER_MSG_SIZE);
556  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
557  abort_with_fatal_error(thisAgent, msg);
558  }
559  if (prev_c) prev_c->rest = c->rest;
560  else id->id.associated_output_links = c->rest;
561  free_cons (thisAgent, c);
562  symbol_remove_ref (thisAgent, id);
563  }
564 }
565 
566 
567 void add_id_to_output_link_tc (agent* thisAgent, Symbol *id) {
568  slot *s;
569  wme *w;
570 
571  /* --- if id is already in the TC, exit --- */
572  if (id->id.tc_num == thisAgent->output_link_tc_num) return;
573  id->id.tc_num = thisAgent->output_link_tc_num;
574 
575 
576  /* --- add id to output_link's list --- */
577  push (thisAgent, id, thisAgent->output_link_for_tc->ids_in_tc);
578  symbol_add_ref (id); /* make sure the id doesn't get deallocated before we
579  have a chance to free the cons cell we just added */
580 
581  /* --- add output_link to id's list --- */
582  push (thisAgent, thisAgent->output_link_for_tc, id->id.associated_output_links);
583 
584  /* --- do TC through working memory --- */
585  /* --- scan through all wmes for all slots for this id --- */
586  for (w=id->id.input_wmes; w!=NIL; w=w->next)
587  if (w->value->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
588  add_id_to_output_link_tc (thisAgent, w->value);
589  for (s=id->id.slots; s!=NIL; s=s->next)
590  for (w=s->wmes; w!=NIL; w=w->next)
591  if (w->value->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
592  add_id_to_output_link_tc (thisAgent, w->value);
593  /* don't need to check impasse_wmes, because we couldn't have a pointer
594  to a goal or impasse identifier */
595 }
596 
597 void calculate_output_link_tc_info (agent* thisAgent, output_link *ol) {
598  /* --- if link doesn't have any substructure, there's no TC --- */
599  if (ol->link_wme->value->common.symbol_type!=IDENTIFIER_SYMBOL_TYPE) return;
600 
601  /* --- do TC starting with the link wme's value --- */
602  thisAgent->output_link_for_tc = ol;
603  thisAgent->output_link_tc_num = get_new_tc_number(thisAgent);
604  add_id_to_output_link_tc (thisAgent, ol->link_wme->value);
605 }
606 
607 /* --------------------------------------------------------------------
608  Building the list of IO_Wme's
609 
610  These routines create and destroy the list of io_wme's in the TC
611  of a given output_link. Get_io_wmes_for_output_link() and
612  deallocate_io_wme_list() are the main entry points. The TC info
613  must have already been calculated for the given output link before
614  get_io_wmes_for_output_link() is called.
615 -------------------------------------------------------------------- */
616 
617 void add_wme_to_collected_io_wmes (agent* thisAgent, wme *w) {
618  io_wme *New;
619 
620  allocate_with_pool (thisAgent, &thisAgent->io_wme_pool, &New);
621  New->next = thisAgent->collected_io_wmes;
622  thisAgent->collected_io_wmes = New;
623  New->id = w->id;
624  New->attr = w->attr;
625  New->value = w->value;
626  New->timetag = w->timetag ;
627 }
628 
630  cons *c;
631  Symbol *id;
632  slot *s;
633  wme *w;
634 
635  thisAgent->collected_io_wmes = NIL;
636  add_wme_to_collected_io_wmes (thisAgent, ol->link_wme);
637  for (c=ol->ids_in_tc; c!=NIL; c=c->rest) {
638  id = static_cast<symbol_union *>(c->first);
639  for (w=id->id.input_wmes; w!=NIL; w=w->next)
640  add_wme_to_collected_io_wmes (thisAgent, w);
641  for (s=id->id.slots; s!=NIL; s=s->next)
642  for (w=s->wmes; w!=NIL; w=w->next)
643  add_wme_to_collected_io_wmes (thisAgent, w);
644  }
645  return thisAgent->collected_io_wmes;
646 }
647 
648 void deallocate_io_wme_list (agent* thisAgent, io_wme *iw) {
649  io_wme *next;
650 
651  while (iw) {
652  next = iw->next;
653  free_with_pool (&thisAgent->io_wme_pool, iw);
654  iw = next;
655  }
656 }
657 
658 /* --------------------------------------------------------------------
659  Do Output Cycle
660 
661  This routine is called from the top-level sequencer, and it performs
662  the whole output phase. It scans through the list of existing output
663  links, and takes the appropriate action on each one that's changed.
664 -------------------------------------------------------------------- */
666 /* Struct used to pass output data to callback functions */
667 
668 void do_output_cycle (agent* thisAgent) {
669  output_link *ol, *next_ol;
670  io_wme *iw_list;
671  output_call_info output_call_data;
672 
673  for (ol=thisAgent->existing_output_links; ol!=NIL; ol=next_ol) {
674  next_ol = ol->next;
675 
676  switch (ol->status) {
677  case UNCHANGED_OL_STATUS:
678  /* --- output link is unchanged, so do nothing --- */
679  break;
680 
681  case NEW_OL_STATUS:
682  /* --- calculate tc, and call the output function --- */
683  calculate_output_link_tc_info (thisAgent, ol);
684  iw_list = get_io_wmes_for_output_link (thisAgent, ol);
685  output_call_data.mode = ADDED_OUTPUT_COMMAND;
686  output_call_data.outputs = iw_list;
687  #ifndef NO_TIMING_STUFF /* moved here from do_one_top_level_phase June 05. KJC */
688  thisAgent->timers_phase.stop();
689  thisAgent->timers_kernel.stop();
690  thisAgent->timers_total_kernel_time.update(thisAgent->timers_kernel);
691  thisAgent->timers_decision_cycle_phase[thisAgent->current_phase].update(thisAgent->timers_phase);
692  thisAgent->timers_kernel.start();
693  #endif
694  if (ol->cb) (ol->cb->function)(thisAgent, ol->cb->eventid, ol->cb->data, &output_call_data);
695  #ifndef NO_TIMING_STUFF
696  thisAgent->timers_kernel.stop();
697  thisAgent->timers_output_function_cpu_time.update(thisAgent->timers_kernel);
698  thisAgent->timers_kernel.start();
699  thisAgent->timers_phase.start();
700  #endif
701  deallocate_io_wme_list (thisAgent, iw_list);
703  break;
704 
706  /* --- don't have to redo the TC, but do call the output function --- */
707  iw_list = get_io_wmes_for_output_link (thisAgent, ol);
708  output_call_data.mode = MODIFIED_OUTPUT_COMMAND;
709  output_call_data.outputs = iw_list;
710  #ifndef NO_TIMING_STUFF /* moved here from do_one_top_level_phase June 05. KJC */
711  thisAgent->timers_phase.stop();
712  thisAgent->timers_kernel.stop();
713  thisAgent->timers_total_kernel_time.update(thisAgent->timers_kernel);
714  thisAgent->timers_decision_cycle_phase[thisAgent->current_phase].update(thisAgent->timers_phase);
715  thisAgent->timers_kernel.start();
716  #endif
717  if (ol->cb) (ol->cb->function)(thisAgent, ol->cb->eventid, ol->cb->data, &output_call_data);
718  #ifndef NO_TIMING_STUFF
719  thisAgent->timers_kernel.stop();
720  thisAgent->timers_output_function_cpu_time.update(thisAgent->timers_kernel);
721  thisAgent->timers_kernel.start();
722  thisAgent->timers_phase.start();
723  #endif
724  deallocate_io_wme_list (thisAgent, iw_list);
726  break;
727 
728  case MODIFIED_OL_STATUS:
729  /* --- redo the TC, and call the output function */
730  remove_output_link_tc_info (thisAgent, ol);
731  calculate_output_link_tc_info (thisAgent, ol);
732  iw_list = get_io_wmes_for_output_link (thisAgent, ol);
733  output_call_data.mode = MODIFIED_OUTPUT_COMMAND;
734  output_call_data.outputs = iw_list;
735  #ifndef NO_TIMING_STUFF /* moved here from do_one_top_level_phase June 05. KJC */
736  thisAgent->timers_phase.stop();
737  thisAgent->timers_kernel.stop();
738  thisAgent->timers_total_kernel_time.update(thisAgent->timers_kernel);
739  thisAgent->timers_decision_cycle_phase[thisAgent->current_phase].update(thisAgent->timers_phase);
740  thisAgent->timers_kernel.start();
741  #endif
742  if (ol->cb) (ol->cb->function)(thisAgent, ol->cb->eventid, ol->cb->data, &output_call_data);
743  #ifndef NO_TIMING_STUFF
744  thisAgent->timers_kernel.stop();
745  thisAgent->timers_output_function_cpu_time.update(thisAgent->timers_kernel);
746  thisAgent->timers_kernel.start();
747  thisAgent->timers_phase.start();
748  #endif
749  deallocate_io_wme_list (thisAgent, iw_list);
751  break;
752 
753  case REMOVED_OL_STATUS:
754  /* --- call the output function, and free output_link structure --- */
755  remove_output_link_tc_info (thisAgent, ol); /* sets ids_in_tc to NIL */
756  iw_list = get_io_wmes_for_output_link (thisAgent, ol); /* gives just the link wme */
757  output_call_data.mode = REMOVED_OUTPUT_COMMAND;
758  output_call_data.outputs = iw_list;
759  #ifndef NO_TIMING_STUFF /* moved here from do_one_top_level_phase June 05. KJC */
760  thisAgent->timers_phase.stop();
761  thisAgent->timers_kernel.stop();
762  thisAgent->timers_total_kernel_time.update(thisAgent->timers_kernel);
763  thisAgent->timers_decision_cycle_phase[thisAgent->current_phase].update(thisAgent->timers_phase);
764  thisAgent->timers_kernel.start();
765  #endif
766  if (ol->cb) (ol->cb->function)(thisAgent, ol->cb->eventid, ol->cb->data, &output_call_data);
767  #ifndef NO_TIMING_STUFF
768  thisAgent->timers_kernel.stop();
769  thisAgent->timers_output_function_cpu_time.update(thisAgent->timers_kernel);
770  thisAgent->timers_kernel.start();
771  thisAgent->timers_phase.start();
772  #endif
773  deallocate_io_wme_list (thisAgent, iw_list);
774  wme_remove_ref (thisAgent, ol->link_wme);
775  remove_from_dll (thisAgent->existing_output_links, ol, next, prev);
776  free_with_pool (&thisAgent->output_link_pool, ol);
777  break;
778  }
779  } /* end of for ol */
780 
781 }
782 
783 /* --------------------------------------------------------------------
784  Get Output Value
785 
786  This is a simple utility function for use in users' output functions.
787  It finds things in an io_wme chain. It takes "outputs" (the io_wme
788  chain), and "id" and "attr" (symbols to match against the wmes), and
789  returns the value from the first wme in the chain with a matching id
790  and attribute. Either "id" or "attr" (or both) can be specified as
791  "don't care" by giving NULL (0) pointers for them instead of pointers
792  to symbols. If no matching wme is found, the function returns a
793  NULL pointer.
794 -------------------------------------------------------------------- */
795 
796 Symbol *get_output_value (io_wme *outputs, Symbol *id, Symbol *attr) {
797  io_wme *iw;
798 
799  for (iw=outputs; iw!=NIL; iw=iw->next)
800  if ( ((id==NIL)||(id==iw->id)) &&
801  ((attr==NIL)||(attr==iw->attr)) ) return iw->value;
802  return NIL;
803 }
804 
805 /* ====================================================================
806 
807  Utilities that used to be part of text I/O, but we still need
808  them now that text I/O is gone.
809 
810  Get_next_io_symbol_from_text_input_line() is used by the "accept"
811  RHS function.
812 
813 ==================================================================== */
814 
815 /* --------------------------------------------------------------------
816  Parsing a Line of Text Input
817 
818  Get_next_io_symbol_from_text_input_line (char **text_read_position) is
819  the main text input parser. It reads text from text_read_position
820  and returns a (Symbol *) for the first item read. It updates
821  text_read_position to point to the next character not yet read.
822  If end-of-line is reached without any symbol being read, NIL is
823  returned.
824 -------------------------------------------------------------------- */
825 
827 Bool tio_whitespace[256];
828 
829 Symbol *get_io_symbol_from_tio_constituent_string (agent* thisAgent, char *input_string) {
830  int int_val;
831  double float_val;
832  Bool possible_id, possible_var, possible_sc, possible_ic, possible_fc;
833  Bool rereadable;
834 
836  strlen(input_string),
837  &possible_id,
838  &possible_var,
839  &possible_sc,
840  &possible_ic,
841  &possible_fc,
842  &rereadable);
843 
844  /* --- check whether it's an integer --- */
845  if (possible_ic) {
846  errno = 0;
847  int_val = strtol (input_string,NULL,10);
848  if (errno) {
849  print (thisAgent, "Text Input Error: bad integer (probably too large)\n");
850  return NIL;
851  }
852  return get_io_int_constant (thisAgent, int_val);
853  }
854 
855  /* --- check whether it's a floating point number --- */
856  if (possible_fc) {
857  errno = 0;
858  float_val = strtod (input_string,NULL);
859  if (errno) {
860  print (thisAgent, "Text Input Error: bad floating point number\n");
861  return NIL;
862  }
863  return get_io_float_constant (thisAgent, float_val);
864  }
865 
866  /* --- otherwise it must be a symbolic constant --- */
867  return get_io_sym_constant (thisAgent, input_string);
868 }
870 #define MAX_TEXT_INPUT_LINE_LENGTH 1000 /* used to be in soarkernel.h */
871 
873  char **text_read_position) {
874  char *ch;
875  char input_string[MAX_TEXT_INPUT_LINE_LENGTH+2];
876  int input_lexeme_length;
877 
878  ch = *text_read_position;
879 
880  /* --- scan past any whitespace --- */
881  while (tio_whitespace[static_cast<unsigned char>(*ch)]) ch++;
882 
883  /* --- if end of line, return NIL --- */
884  if ((*ch=='\n')||(*ch==0)) { *text_read_position = ch; return NIL; }
885 
886  /* --- if not a constituent character, return single-letter symbol --- */
887  if (! tio_constituent_char[static_cast<unsigned char>(*ch)]) {
888  input_string[0] = *ch++;
889  input_string[1] = 0;
890  *text_read_position = ch;
891  return get_io_sym_constant (thisAgent, input_string);
892  }
893 
894  /* --- read string of constituents --- */
895  input_lexeme_length = 0;
896  while (tio_constituent_char[static_cast<unsigned char>(*ch)])
897  input_string[input_lexeme_length++] = *ch++;
898 
899  /* --- return the appropriate kind of symbol --- */
900  input_string[input_lexeme_length] = 0;
901  *text_read_position = ch;
902  return get_io_symbol_from_tio_constituent_string (thisAgent, input_string);
903 }
904 
905 /* ====================================================================
906 
907  Initialization for Soar I/O
909 ==================================================================== */
911 char extra_tio_constituents[] = "+-._";
912 
913 void init_soar_io (agent* thisAgent) {
914  unsigned int i;
915 
916  init_memory_pool (thisAgent, &thisAgent->output_link_pool, sizeof(output_link), "output link");
917  init_memory_pool (thisAgent, &thisAgent->io_wme_pool, sizeof(io_wme), "io wme");
918 
919  /* --- setup constituent_char array --- */
920  for (i=0; i<256; i++) tio_constituent_char[i] = (isalnum(i) != 0);
921  for (i=0; i<strlen(extra_tio_constituents); i++)
922  tio_constituent_char[static_cast<int>(extra_tio_constituents[i])]=TRUE;
923 
924  /* --- setup whitespace array --- */
925  for (i=0; i<256; i++) tio_whitespace[i] = (isspace(i) != 0);
926  tio_whitespace[static_cast<int>('\n')]=FALSE; /* for text i/o, crlf isn't whitespace */
927 }
928