Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

io.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  io.c
00004  *
00005  * =======================================================================
00006  *  
00007  * 
00008  *                General Soar I/O System Routines
00009  *
00010  * User-defined Soar I/O routines should be added at system startup time
00011  * via calls to add_input_function() and add_output_function().  These 
00012  * calls add things to the system's list of (1) functions to be called 
00013  * every input cycle, and (2) symbol-to-function mappings for output
00014  * commands.  File io.c contains the system I/O mechanism itself (i.e.,
00015  * the stuff that calls the input and output functions), plus the text
00016  * I/O routines.
00017  *
00018  * Init_soar_io() does what it says.  Do_input_cycle() and do_output_cycle()
00019  * perform the entire input and output cycles -- these routines are called 
00020  * once per elaboration cycle.  (once per Decision cycle in Soar 8).
00021  * The output module is notified about WM changes via a call to
00022  * inform_output_module_of_wm_changes().
00023  *  
00024  * =======================================================================
00025  *
00026  * Copyright 1995-2003 Carnegie Mellon University,
00027  *                                                                               University of Michigan,
00028  *                                                                               University of Southern California/Information
00029  *                                                                               Sciences Institute. All rights reserved.
00030  *                                                                              
00031  * Redistribution and use in source and binary forms, with or without
00032  * modification, are permitted provided that the following conditions are met:
00033  *
00034  * 1.   Redistributions of source code must retain the above copyright notice,
00035  *              this list of conditions and the following disclaimer. 
00036  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00037  *              this list of conditions and the following disclaimer in the documentation
00038  *              and/or other materials provided with the distribution. 
00039  *
00040  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00041  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00042  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00043  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00044  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00045  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00046  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00047  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00048  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00049  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00050  * The views and conclusions contained in the software and documentation are
00051  * those of the authors and should not be interpreted as representing official
00052  * policies, either expressed or implied, of Carnegie Mellon University, the
00053  * University of Michigan, the University of Southern California/Information
00054  * Sciences Institute, or the Soar consortium.
00055  * =======================================================================
00056  */
00057 
00058 /* ==================================================================
00059                          I/O Code for Soar 6
00060 
00061          General Soar I/O System Routines, and Text I/O Routines
00062 
00063    See comments in soarkernel.h for more information.
00064    ================================================================== */
00065 
00066 #include "soarkernel.h"
00067 #include <ctype.h>
00068 #include <errno.h>
00069 #include "soarapiUtils.h"
00070 
00071 #ifdef _AIX                     /* excludeFromBuildInfo */
00072 #include <sys/select.h>
00073 #endif
00074 
00075 extern void gds_invalid_so_remove_goal(wme * w);
00076 void calculate_output_link_tc_info(output_link * ol);
00077 
00078 /* ====================================================================
00079                             Input Routines
00080 
00081    Get_new_io_identifier(), get_io_sym_constant(), get_io_int_constant(),
00082    and get_io_float_constant() just call the appropriate symbol table
00083    routines.  This has the effect of incrementing the reference count
00084    on the symbol (or creating one with a reference count of 1).
00085    Release_io_symbol() just decrements the reference count.
00086 
00087    Add_input_wme() and remove_input_wme() call the add_wme_to_wm() and
00088    remove_wme_from_wm() routines in decide.c to do their work.  
00089 
00090    Do_input_cycle() is the top-level routine which calls all the
00091    individual user-defined input functions, etc.  
00092 
00093    All this stuff is really simple, and consequently pretty vulnerable
00094    to buggy user-written I/O code.  A more sophisticated version would
00095    be bullet-proofed against bad arguments to get_xxx(), add_input_wme(),
00096    and remove_input_wme().  Right now add_input_wme() and remove_input_wme()
00097    do some error checking, but they're nowhere near bullet-proof.
00098 ==================================================================== */
00099 
00100 Symbol *get_new_io_identifier(char first_letter)
00101 {
00102 
00103     return make_new_identifier(first_letter, TOP_GOAL_LEVEL);
00104 }
00105 
00106 Symbol *get_io_sym_constant(char *name)
00107 {
00108     return make_sym_constant(name);
00109 }
00110 
00111 Symbol *get_io_int_constant(long value)
00112 {
00113     return make_int_constant(value);
00114 }
00115 
00116 Symbol *get_io_float_constant(float value)
00117 {
00118     return make_float_constant(value);
00119 }
00120 
00121 void release_io_symbol(Symbol * sym)
00122 {
00123     symbol_remove_ref(sym);
00124 }
00125 
00126 wme *add_input_wme(Symbol * id, Symbol * attr, Symbol * value)
00127 {
00128     wme *w;
00129 
00130     /* --- a little bit of error checking --- */
00131     if (!(id && attr && value)) {
00132         print("Error: an input routine gave a NULL argument to add_input_wme.\n");
00133         return NIL;
00134     }
00135     /* --- go ahead and add the wme --- */
00136     w = make_wme(id, attr, value, FALSE);
00137     insert_at_head_of_dll(id->id.input_wmes, w, next, prev);
00138     add_wme_to_wm(w);
00139 
00140     /* SW NOTE:
00141      * In an ideal world, we could capture input wmes here as well as
00142      * in soar_cAddWme.  However the problem is that based on the arguments
00143      * to this function it is impossible to determine if the wme which is
00144      * being added references new, or just previously defined symbols. 
00145      * As a result, we cannot capture input wmes asserted using this function
00146      * with any hope of accurately replaying them in the future.
00147      */
00148 
00149     return w;
00150 }
00151 
00152 bool remove_input_wme(wme * w)
00153 {
00154     wme *temp;
00155 
00156     /* --- a little bit of error checking --- */
00157     if (!w) {
00158         print("Error: an input routine called remove_input_wme on a NULL wme.\n");
00159         return FALSE;
00160     }
00161     for (temp = w->id->id.input_wmes; temp != NIL; temp = temp->next)
00162         if (temp == w)
00163             break;
00164     if (!temp) {
00165         print("Error: an input routine called remove_input_wme on a wme that\n");
00166         print("isn't one of the input wmes currently in working memory.\n");
00167         return FALSE;
00168     }
00169 
00170     /* SW NOTE:
00171      * Since we cannot capture wmes asserted during add_input_wme,
00172      * we will not capture wmes removed during this function call.
00173      */
00174 
00175     /* Note: for efficiency, it might be better to use a hash table for the
00176        above test, rather than scanning the linked list.  We could have one
00177        global hash table for all the input wmes in the system. */
00178     /* --- go ahead and remove the wme --- */
00179     remove_from_dll(w->id->id.input_wmes, w, next, prev);
00180     /* REW: begin 09.15.96 */
00181 #ifndef SOAR_8_ONLY
00182     if (current_agent(operand2_mode)) {
00183 #endif
00184         if (w->gds) {
00185             if (w->gds->goal != NIL) {
00186                 if (current_agent(soar_verbose_flag))
00187                     print("\nremove_input_wme: Removing goal %d because element in GDS changed.\n",
00188                           w->gds->goal->id.level);
00189                 gds_invalid_so_remove_goal(w);
00190                 /* NOTE: the call to remove_wme_from_wm will take care
00191                    of checking if GDS should be removed */
00192             }
00193         }
00194 #ifndef SOAR_8_ONLY
00195     }
00196 #endif
00197 
00198     /* REW: end   09.15.96 */
00199 
00200     remove_wme_from_wm(w);
00201 
00202     return TRUE;
00203 }
00204 
00205 #ifndef NO_IO_CALLBACKS
00206 
00207 void do_input_cycle(void)
00208 {
00209     wme *w;
00210 
00211 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00212     if (current_agent(sysparams)[TRACE_PHASES_SYSPARAM])
00213         print("\n--- Input Phase --- \n");
00214 #endif
00215 
00216     if (current_agent(prev_top_state) && (!current_agent(top_state))) {
00217         /* --- top state was just removed --- */
00218         release_io_symbol(current_agent(io_header));
00219         release_io_symbol(current_agent(io_header_input));
00220         release_io_symbol(current_agent(io_header_output));
00221         current_agent(io_header) = NIL; /* RBD added 3/25/95 */
00222         current_agent(io_header_input) = NIL;   /* RBD added 3/25/95 */
00223         current_agent(io_header_output) = NIL;  /* KJC added 3/3/99 */
00224         current_agent(io_header_link) = NIL;    /* KJC added 3/3/99 */
00225         soar_invoke_callbacks(soar_agent, INPUT_PHASE_CALLBACK, (soar_call_data) TOP_STATE_JUST_REMOVED);
00226     } else if ((!current_agent(prev_top_state)) && current_agent(top_state)) {
00227         /* --- top state was just created --- */
00228         /* Create io structure on top state. */
00229         current_agent(io_header) = get_new_io_identifier('I');
00230         current_agent(io_header_link) = add_input_wme(current_agent(top_state),
00231                                                       current_agent(io_symbol), current_agent(io_header));
00232         current_agent(io_header_input) = get_new_io_identifier('I');
00233         current_agent(io_header_output) = get_new_io_identifier('I');
00234         w = add_input_wme(current_agent(io_header), make_sym_constant("input-link"), current_agent(io_header_input));
00235 
00236         w = add_input_wme(current_agent(io_header), make_sym_constant("output-link"), current_agent(io_header_output));
00237 
00238         /* --- add top state io link before calling input phase callback so
00239          * --- code can use "wmem" command.
00240          */
00241         do_buffered_wm_and_ownership_changes();
00242 
00243         soar_invoke_callbacks(soar_agent, INPUT_PHASE_CALLBACK, (soar_call_data) TOP_STATE_JUST_CREATED);
00244     }
00245 
00246     /* --- if there is a top state, do the normal input cycle --- */
00247 
00248     if (current_agent(top_state)) {
00249         soar_invoke_callbacks(soar_agent, INPUT_PHASE_CALLBACK, (soar_call_data) NORMAL_INPUT_CYCLE);
00250     }
00251 
00252     /* --- do any WM resulting changes --- */
00253     do_buffered_wm_and_ownership_changes();
00254 
00255     /* --- save current top state for next time --- */
00256     current_agent(prev_top_state) = current_agent(top_state);
00257 
00258     /* --- reset the output-link status flag to FALSE
00259      * --- when running til output, only want to stop if agent
00260      * --- does add-wme to output.  don't stop if add-wme done
00261      * --- during input cycle (eg simulator updates sensor status)
00262      *     KJC 11/23/98
00263      */
00264     current_agent(output_link_changed) = FALSE;
00265 }
00266 
00267 #else
00268 void do_input_cycle(void)
00269 {
00270 
00271     /*
00272        I don't really think we need to do this,
00273        so I'll try not using it.
00274      */
00275     /* do_buffered_wm_and_ownership_changes(); */
00276     current_agent(prev_top_state) = current_agent(top_state);
00277     current_agent(output_link_changed) = FALSE;
00278 
00279 }
00280 
00281 #endif                          /* NO_IO_CALLBACKS */
00282 
00283 /* ====================================================================
00284                           Output Routines
00285 
00286    Inform_output_module_of_wm_changes() and do_output_cycle() are the
00287    two top-level entry points to the output routines.  The former is
00288    called by the working memory manager, and the latter from the top-level
00289    phase sequencer.
00290   
00291    This module maintains information about all the existing output links
00292    and the identifiers and wmes that are in the transitive closure of them.
00293    On each output link wme, we put a pointer to an output_link structure.
00294    Whenever inform_output_module_of_wm_changes() is called, we look for
00295    new output links and modifications/removals of old ones, and update
00296    the output_link structures accordingly.
00297 
00298    Transitive closure information is kept as follows:  each output_link
00299    structure has a list of all the ids in the link's TC.  Each id in
00300    the system has a list of all the output_link structures that it's
00301    in the TC of.
00302 
00303    After some number of calls to inform_output_module_of_wm_changes(),
00304    eventually do_output_cycle() gets called.  It scans through the list
00305    of output links and calls the necessary output function for each
00306    link that has changed in some way (add/modify/remove).
00307 ==================================================================== */
00308 
00309 /* --- output link statuses --- */
00310 #define NEW_OL_STATUS 0         /* just created it */
00311 #define UNCHANGED_OL_STATUS 1   /* normal status */
00312 #define MODIFIED_BUT_SAME_TC_OL_STATUS 2        /* some value in its TC has been
00313                                                    modified, but the ids in its TC
00314                                                    are the same */
00315 #define MODIFIED_OL_STATUS 3    /* the set of ids in its TC has
00316                                    changed */
00317 #define REMOVED_OL_STATUS 4     /* link has just been removed */
00318 
00319 /* --------------------------------------------------------------------
00320                    Output Link Status Updates on WM Changes
00321 
00322    Top-state link changes:
00323 
00324      For wme addition: (top-state ^link-attr anything)
00325         create new output_link structure; mark it "new"
00326      For wme removal:  (top-state ^link-attr anything)
00327         mark the output_link "removed"
00328 
00329    TC of existing link changes:
00330 
00331      For wme addition or removal: (<id> ^att constant):
00332        for each link in associated_output_links(id), 
00333          mark link "modified but same tc" (unless it's already marked
00334          some other more serious way)
00335  
00336      For wme addition or removal: (<id> ^att <id2>):
00337        for each link in associated_output_links(id), 
00338          mark link "modified" (unless it's already marked
00339          some other more serious way)
00340 
00341    Note that we don't update all the TC information after every WM change.
00342    The TC info doesn't get updated until do_output_cycle() is called.
00343 -------------------------------------------------------------------- */
00344 
00345 #define LINK_NAME_SIZE 1024
00346 void update_for_top_state_wme_addition(wme * w)
00347 {
00348     output_link *ol;
00349     soar_callback *cb;
00350     char link_name[LINK_NAME_SIZE];
00351 
00352     /* --- check whether the attribute is an output function --- */
00353     symbol_to_string(w->attr, FALSE, link_name, LINK_NAME_SIZE);
00354 
00355     cb = soar_exists_callback_id(soar_agent, OUTPUT_PHASE_CALLBACK, link_name);
00356 
00357     if (!cb)
00358         return;
00359 
00360     /* --- create new output link structure --- */
00361     allocate_with_pool(&current_agent(output_link_pool), &ol);
00362     insert_at_head_of_dll(current_agent(existing_output_links), ol, next, prev);
00363 
00364     ol->status = NEW_OL_STATUS;
00365     ol->link_wme = w;
00366     wme_add_ref(w);
00367     ol->ids_in_tc = NIL;
00368     ol->cb = cb;
00369     /* --- make wme point to the structure --- */
00370     w->output_link = ol;
00371 
00372     /* SW 07 10 2003
00373        previously, this wouldn't be done until the first OUTPUT phase.
00374        However, if we add an output command in the 1st decision cycle,
00375        Soar seems to ignore it.
00376 
00377        There may be two things going on, the first having to do with the tc 
00378        calculation, which may get done too late, in such a way that the
00379        initial calculation includes the command.  The other thing appears
00380        to be that some data structures are not initialized until the first 
00381        output phase.  Namely, id->associated_output_links does not seem
00382        reflect the current output links until the first output-phase.
00383 
00384        To get past these issues, we fake a transitive closure calculation
00385        with the knowledge that the only thing on the output link at this
00386        point is the output-link identifier itself.  This way, we capture
00387        a snapshot of the empty output link, so Soar can detect any changes
00388        that might occur before the first output_phase. */
00389 
00390     current_agent(output_link_tc_num) = get_new_tc_number();
00391     ol->link_wme->value->id.tc_num = current_agent(output_link_tc_num);
00392     current_agent(output_link_for_tc) = ol;
00393     /* --- add output_link to id's list --- */
00394     push(current_agent(output_link_for_tc), ol->link_wme->value->id.associated_output_links);
00395 
00396 }
00397 
00398 void update_for_top_state_wme_removal(wme * w)
00399 {
00400     if (!w->output_link)
00401         return;
00402     w->output_link->status = REMOVED_OL_STATUS;
00403 }
00404 
00405 void update_for_io_wme_change(wme * w)
00406 {
00407     cons *c;
00408     output_link *ol;
00409 
00410     for (c = w->id->id.associated_output_links; c != NIL; c = c->rest) {
00411         ol = c->first;
00412         if (w->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE) {
00413             /* --- mark ol "modified" --- */
00414             if ((ol->status == UNCHANGED_OL_STATUS) || (ol->status == MODIFIED_BUT_SAME_TC_OL_STATUS))
00415                 ol->status = MODIFIED_OL_STATUS;
00416         } else {
00417             /* --- mark ol "modified but same tc" --- */
00418             if (ol->status == UNCHANGED_OL_STATUS)
00419                 ol->status = MODIFIED_BUT_SAME_TC_OL_STATUS;
00420         }
00421     }
00422 }
00423 
00424 void inform_output_module_of_wm_changes(list * wmes_being_added, list * wmes_being_removed)
00425 {
00426     cons *c;
00427     wme *w;
00428 
00429     /* if wmes are added, set flag so can stop when running til output */
00430     for (c = wmes_being_added; c != NIL; c = c->rest) {
00431         w = c->first;
00432 
00433         if (w->id == current_agent(io_header)) {
00434             update_for_top_state_wme_addition(w);
00435             current_agent(output_link_changed) = TRUE;  /* KJC 11/23/98 */
00436         }
00437         if (w->id->id.associated_output_links) {
00438             update_for_io_wme_change(w);
00439             current_agent(output_link_changed) = TRUE;  /* KJC 11/23/98 */
00440         }
00441 #if DEBUG_RTO
00442         else {
00443             char id[100];
00444 
00445             symbol_to_string(w->id, FALSE, id);
00446             if (!strcmp(id, "I3")) {
00447                 print("--> Added to I3, but doesn't register as an OL change!");
00448             }
00449         }
00450 #endif
00451 
00452     }
00453     for (c = wmes_being_removed; c != NIL; c = c->rest) {
00454         w = c->first;
00455         if (w->id == current_agent(io_header))
00456             update_for_top_state_wme_removal(w);
00457         if (w->id->id.associated_output_links)
00458             update_for_io_wme_change(w);
00459     }
00460 }
00461 
00462 /* --------------------------------------------------------------------
00463                      Updating Link TC Information
00464 
00465    We make no attempt to do the TC updating intelligently.  Whenever the
00466    TC changes, we throw away all the old TC info and recalculate the new
00467    TC from scratch.  I figure that this part of the system won't get
00468    used very frequently and I hope it won't be a time hog.
00469 
00470    Remove_output_link_tc_info() and calculate_output_link_tc_info() are
00471    the main routines here.
00472 -------------------------------------------------------------------- */
00473 
00474 void remove_output_link_tc_info(output_link * ol)
00475 {
00476     cons *c, *prev_c;
00477     Symbol *id;
00478 
00479     while (ol->ids_in_tc) {     /* for each id in the old TC... */
00480         c = ol->ids_in_tc;
00481         ol->ids_in_tc = c->rest;
00482         id = c->first;
00483         free_cons(c);
00484 
00485         /* --- remove "ol" from the list of associated_output_links(id) --- */
00486         prev_c = NIL;
00487         for (c = id->id.associated_output_links; c != NIL; prev_c = c, c = c->rest)
00488             if (c->first == ol)
00489                 break;
00490         if (!c) {
00491             char msg[MESSAGE_SIZE];
00492             strncpy(msg, "io.c: Internal error: can't find output link in id's list\n", MESSAGE_SIZE);
00493             msg[MESSAGE_SIZE - 1] = 0;
00494             abort_with_fatal_error(msg);
00495         }
00496         if (prev_c)
00497             prev_c->rest = c->rest;
00498         else
00499             id->id.associated_output_links = c->rest;
00500         free_cons(c);
00501         symbol_remove_ref(id);
00502     }
00503 }
00504 
00505 void add_id_to_output_link_tc(Symbol * id)
00506 {
00507     slot *s;
00508     wme *w;
00509 
00510     /* --- if id is already in the TC, exit --- */
00511     if (id->id.tc_num == current_agent(output_link_tc_num))
00512         return;
00513     id->id.tc_num = current_agent(output_link_tc_num);
00514 
00515     /* --- add id to output_link's list --- */
00516     push(id, current_agent(output_link_for_tc)->ids_in_tc);
00517     symbol_add_ref(id);         /* make sure the id doesn't get deallocated before we
00518                                    have a chance to free the cons cell we just added */
00519 
00520     /* --- add output_link to id's list --- */
00521     push(current_agent(output_link_for_tc), id->id.associated_output_links);
00522 
00523     /* --- do TC through working memory --- */
00524     /* --- scan through all wmes for all slots for this id --- */
00525     for (w = id->id.input_wmes; w != NIL; w = w->next)
00526         if (w->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE)
00527             add_id_to_output_link_tc(w->value);
00528     for (s = id->id.slots; s != NIL; s = s->next)
00529         for (w = s->wmes; w != NIL; w = w->next)
00530             if (w->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE)
00531                 add_id_to_output_link_tc(w->value);
00532     /* don't need to check impasse_wmes, because we couldn't have a pointer
00533        to a goal or impasse identifier */
00534 }
00535 
00536 void calculate_output_link_tc_info(output_link * ol)
00537 {
00538     /* --- if link doesn't have any substructure, there's no TC --- */
00539     if (ol->link_wme->value->common.symbol_type != IDENTIFIER_SYMBOL_TYPE)
00540         return;
00541 
00542     /* --- do TC starting with the link wme's value --- */
00543     current_agent(output_link_for_tc) = ol;
00544     current_agent(output_link_tc_num) = get_new_tc_number();
00545     add_id_to_output_link_tc(ol->link_wme->value);
00546 }
00547 
00548 /* --------------------------------------------------------------------
00549                     Building the list of IO_Wme's
00550 
00551    These routines create and destroy the list of io_wme's in the TC
00552    of a given output_link.  Get_io_wmes_for_output_link() and
00553    deallocate_io_wme_list() are the main entry points.  The TC info
00554    must have already been calculated for the given output link before
00555    get_io_wmes_for_output_link() is called.
00556 -------------------------------------------------------------------- */
00557 
00558 void add_wme_to_collected_io_wmes(wme * w)
00559 {
00560     io_wme *new;
00561 
00562     allocate_with_pool(&current_agent(io_wme_pool), &new);
00563     new->next = current_agent(collected_io_wmes);
00564     current_agent(collected_io_wmes) = new;
00565     new->id = w->id;
00566     new->attr = w->attr;
00567     new->value = w->value;
00568 }
00569 
00570 io_wme *get_io_wmes_for_output_link(output_link * ol)
00571 {
00572     cons *c;
00573     Symbol *id;
00574     slot *s;
00575     wme *w;
00576 
00577     current_agent(collected_io_wmes) = NIL;
00578     add_wme_to_collected_io_wmes(ol->link_wme);
00579     for (c = ol->ids_in_tc; c != NIL; c = c->rest) {
00580         id = c->first;
00581         for (w = id->id.input_wmes; w != NIL; w = w->next)
00582             add_wme_to_collected_io_wmes(w);
00583         for (s = id->id.slots; s != NIL; s = s->next)
00584             for (w = s->wmes; w != NIL; w = w->next)
00585                 add_wme_to_collected_io_wmes(w);
00586     }
00587     return current_agent(collected_io_wmes);
00588 }
00589 
00590 void deallocate_io_wme_list(io_wme * iw)
00591 {
00592     io_wme *next;
00593 
00594     while (iw) {
00595         next = iw->next;
00596         free_with_pool(&current_agent(io_wme_pool), iw);
00597         iw = next;
00598     }
00599 }
00600 
00601 /* --------------------------------------------------------------------
00602                            Do Output Cycle
00603 
00604    This routine is called from the top-level sequencer, and it performs
00605    the whole output phase.  It scans through the list of existing output
00606    links, and takes the appropriate action on each one that's changed.
00607 -------------------------------------------------------------------- */
00608 
00609 /* Struct used to pass output data to callback functions */
00610 output_call_info output_call_data;
00611 
00612 #ifndef NO_IO_CALLBACKS
00613 
00614 void do_output_cycle(void)
00615 {
00616     output_link *ol, *next_ol;
00617     io_wme *iw_list;
00618 
00619 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00620     if (current_agent(sysparams)[TRACE_PHASES_SYSPARAM])
00621         print("\n--- Output Phase ---\n");
00622 #endif
00623 
00624     for (ol = current_agent(existing_output_links); ol != NIL; ol = next_ol) {
00625         next_ol = ol->next;
00626 
00627         switch (ol->status) {
00628         case UNCHANGED_OL_STATUS:
00629             /* --- link is unchanged, so do nothing --- */
00630             break;
00631 
00632         case NEW_OL_STATUS:
00633 
00634             /* --- calculate tc, and call the output function --- */
00635             calculate_output_link_tc_info(ol);
00636             iw_list = get_io_wmes_for_output_link(ol);
00637             output_call_data.mode = ADDED_OUTPUT_COMMAND;
00638             output_call_data.outputs = iw_list;
00639             (ol->cb->function) (soar_agent, ol->cb->data, &output_call_data);
00640             deallocate_io_wme_list(iw_list);
00641             ol->status = UNCHANGED_OL_STATUS;
00642             break;
00643 
00644         case MODIFIED_BUT_SAME_TC_OL_STATUS:
00645             /* --- don't have to redo the TC, but do call the output function --- */
00646             iw_list = get_io_wmes_for_output_link(ol);
00647             output_call_data.mode = MODIFIED_OUTPUT_COMMAND;
00648             output_call_data.outputs = iw_list;
00649             (ol->cb->function) (soar_agent, ol->cb->data, &output_call_data);
00650             deallocate_io_wme_list(iw_list);
00651             ol->status = UNCHANGED_OL_STATUS;
00652             break;
00653 
00654         case MODIFIED_OL_STATUS:
00655             /* --- redo the TC, and call the output function */
00656             remove_output_link_tc_info(ol);
00657             calculate_output_link_tc_info(ol);
00658             iw_list = get_io_wmes_for_output_link(ol);
00659             output_call_data.mode = MODIFIED_OUTPUT_COMMAND;
00660             output_call_data.outputs = iw_list;
00661             (ol->cb->function) (soar_agent, ol->cb->data, &output_call_data);
00662             deallocate_io_wme_list(iw_list);
00663             ol->status = UNCHANGED_OL_STATUS;
00664             break;
00665 
00666         case REMOVED_OL_STATUS:
00667             /* --- call the output function, and free output_link structure --- */
00668             remove_output_link_tc_info(ol);     /* sets ids_in_tc to NIL */
00669             iw_list = get_io_wmes_for_output_link(ol);  /* gives just the link wme */
00670             output_call_data.mode = REMOVED_OUTPUT_COMMAND;
00671             output_call_data.outputs = iw_list;
00672             (ol->cb->function) (soar_agent, ol->cb->data, &output_call_data);
00673             deallocate_io_wme_list(iw_list);
00674             wme_remove_ref(ol->link_wme);
00675             remove_from_dll(current_agent(existing_output_links), ol, next, prev);
00676             free_with_pool(&current_agent(output_link_pool), ol);
00677             break;
00678         }
00679     }                           /* end of for ol */
00680 }
00681 #else
00682 
00683 void do_output_cycle(void)
00684 {
00685     output_link *ol, *next_ol;
00686 
00687     for (ol = current_agent(existing_output_links); ol != NIL; ol = next_ol) {
00688         next_ol = ol->next;
00689 
00690         switch (ol->status) {
00691         case UNCHANGED_OL_STATUS:
00692             /* --- link is unchanged, so do nothing --- */
00693             break;
00694 
00695         default:
00696             print("io.c: Error -- Output Link has changed, but kernel was built with NO_IO_CALLBACKS\n");
00697         }
00698     }
00699 }
00700 #endif
00701 
00702 /* --------------------------------------------------------------------
00703                           Get Output Value
00704 
00705    This is a simple utility function for use in users' output functions.
00706    It finds things in an io_wme chain.  It takes "outputs" (the io_wme
00707    chain), and "id" and "attr" (symbols to match against the wmes), and
00708    returns the value from the first wme in the chain with a matching id
00709    and attribute.  Either "id" or "attr" (or both) can be specified as
00710    "don't care" by giving NULL (0) pointers for them instead of pointers
00711    to symbols.  If no matching wme is found, the function returns a
00712    NULL pointer.
00713 -------------------------------------------------------------------- */
00714 
00715 Symbol *get_output_value(io_wme * outputs, Symbol * id, Symbol * attr)
00716 {
00717     io_wme *iw;
00718 
00719     for (iw = outputs; iw != NIL; iw = iw->next)
00720         if (((id == NIL) || (id == iw->id)) && ((attr == NIL) || (attr == iw->attr)))
00721             return iw->value;
00722     return NIL;
00723 }
00724 
00725 /* ====================================================================
00726 
00727    Utilities that used to be part of text I/O, but we still need 
00728    them now that text I/O is gone.
00729    
00730    Get_next_io_symbol_from_text_input_line() is used by the "accept"
00731    RHS function.
00732 
00733 ==================================================================== */
00734 
00735 /* --------------------------------------------------------------------
00736                     Parsing a Line of Text Input
00737 
00738    Get_next_io_symbol_from_text_input_line (char **text_read_position) is
00739    the main text input parser.  It reads text from text_read_position
00740    and returns a (Symbol *) for the first item read.  It updates
00741    text_read_position to point to the next character not yet read.
00742    If end-of-line is reached without any symbol being read, NIL is
00743    returned.
00744 -------------------------------------------------------------------- */
00745 
00746 bool tio_constituent_char[256];
00747 bool tio_whitespace[256];
00748 
00749 Symbol *get_io_symbol_from_tio_constituent_string(char *input_string)
00750 {
00751     int int_val;
00752     float float_val;
00753     bool possible_id, possible_var, possible_sc, possible_ic, possible_fc;
00754     bool rereadable;
00755 
00756     determine_possible_symbol_types_for_string(input_string,
00757                                                strlen(input_string),
00758                                                &possible_id,
00759                                                &possible_var, &possible_sc, &possible_ic, &possible_fc, &rereadable);
00760 
00761     /* --- check whether it's an integer --- */
00762     if (possible_ic) {
00763         errno = 0;
00764         int_val = strtol(input_string, NULL, 10);
00765         if (errno) {
00766             print("Text Input Error: bad integer (probably too large)\n");
00767             return NIL;
00768         }
00769         return get_io_int_constant(int_val);
00770     }
00771 
00772     /* --- check whether it's a floating point number --- */
00773     if (possible_fc) {
00774         errno = 0;
00775         /*float_val = (float) strtod (input_string,NULL,10); */
00776         float_val = (float) strtod(input_string, NULL);
00777         if (errno) {
00778             print("Text Input Error: bad floating point number\n");
00779             return NIL;
00780         }
00781         return get_io_float_constant(float_val);
00782     }
00783 
00784     /* --- otherwise it must be a symbolic constant --- */
00785     return get_io_sym_constant(input_string);
00786 }
00787 
00788 #define MAX_TEXT_INPUT_LINE_LENGTH 1000 /* used to be in soarkernel.h */
00789 
00790 Symbol *get_next_io_symbol_from_text_input_line(char **text_read_position)
00791 {
00792     char *ch;
00793     char input_string[MAX_TEXT_INPUT_LINE_LENGTH + 2];
00794     int input_lexeme_length;
00795 
00796     ch = *text_read_position;
00797 
00798     /* --- scan past any whitespace --- */
00799     while (tio_whitespace[(unsigned char) (*ch)])
00800         ch++;
00801 
00802     /* --- if end of line, return NIL --- */
00803     if ((*ch == '\n') || (*ch == 0)) {
00804         *text_read_position = ch;
00805         return NIL;
00806     }
00807 
00808     /* --- if not a constituent character, return single-letter symbol --- */
00809     if (!tio_constituent_char[(unsigned char) (*ch)]) {
00810         input_string[0] = *ch++;
00811         input_string[1] = 0;
00812         *text_read_position = ch;
00813         return get_io_sym_constant(input_string);
00814     }
00815 
00816     /* --- read string of constituents --- */
00817     input_lexeme_length = 0;
00818     while (tio_constituent_char[(unsigned char) (*ch)])
00819         input_string[input_lexeme_length++] = *ch++;
00820 
00821     /* --- return the appropriate kind of symbol --- */
00822     input_string[input_lexeme_length] = 0;
00823     *text_read_position = ch;
00824     return get_io_symbol_from_tio_constituent_string(input_string);
00825 }
00826 
00827 /* ====================================================================
00828 
00829                    Initialization for Soar I/O
00830 
00831 ==================================================================== */
00832 
00833 char extra_tio_constituents[] = "+-._";
00834 
00835 void init_soar_io(void)
00836 {
00837     unsigned int i;
00838 
00839     init_memory_pool(&current_agent(output_link_pool), sizeof(output_link), "output link");
00840     init_memory_pool(&current_agent(io_wme_pool), sizeof(io_wme), "io wme");
00841 
00842     /* --- setup constituent_char array --- */
00843     for (i = 0; i < 256; i++)
00844         tio_constituent_char[i] = (char) isalnum(i);
00845     for (i = 0; i < strlen(extra_tio_constituents); i++)
00846         tio_constituent_char[(int) extra_tio_constituents[i]] = TRUE;
00847 
00848     /* --- setup whitespace array --- */
00849     for (i = 0; i < 256; i++)
00850         tio_whitespace[i] = (char) isspace(i);
00851     tio_whitespace[(int) '\n'] = FALSE; /* for text i/o, crlf isn't whitespace */
00852 }

Generated on Thu Dec 11 13:00:16 2003 for Soar Kernel by doxygen 1.3.5