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

print.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  print.c
00004  *
00005  * =======================================================================
00006  *  These are the routines that support printing Soar data structures.
00007  *  
00008  *  Everything eventually ends up in print_string which calls the Tcl
00009  *  interface routine Soar_LogAndPrint.  Logging and I/O redirection are
00010  *  now done through Tcl.  There's also one odd piece of user i/o done
00011  *  in decide.c to manage indifferent-selection -ask.  (it needs work)
00012  *  see more detailed comments in soarkernel.h
00013  * =======================================================================
00014  *
00015  * Copyright 1995-2003 Carnegie Mellon University,
00016  *                                                                               University of Michigan,
00017  *                                                                               University of Southern California/Information
00018  *                                                                               Sciences Institute. All rights reserved.
00019  *                                                                              
00020  * Redistribution and use in source and binary forms, with or without
00021  * modification, are permitted provided that the following conditions are met:
00022  *
00023  * 1.   Redistributions of source code must retain the above copyright notice,
00024  *              this list of conditions and the following disclaimer. 
00025  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00026  *              this list of conditions and the following disclaimer in the documentation
00027  *              and/or other materials provided with the distribution. 
00028  *
00029  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00030  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00031  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00032  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00033  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00034  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00035  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00036  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00037  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00038  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00039  * The views and conclusions contained in the software and documentation are
00040  * those of the authors and should not be interpreted as representing official
00041  * policies, either expressed or implied, of Carnegie Mellon University, the
00042  * University of Michigan, the University of Southern California/Information
00043  * Sciences Institute, or the Soar consortium.
00044  * =======================================================================
00045  */
00046 /* =================================================================
00047                  Printing Utility Routines for Soar 6
00048    ================================================================= */
00049 
00050 #include "soarkernel.h"
00051 #include <stdio.h>
00052 #include <ctype.h>
00053 
00054 #ifdef USE_STDARGS
00055 #include <stdarg.h>
00056 #else
00057 #include <varargs.h>
00058 #endif
00059 
00060 /* -------------------------------------------------------------------
00061     Printing with an Optional Log File and with Redirection to a File
00062 
00063    We want to print stuff not only to the screen but also to a log
00064    file (if one is currently being used).  The print_string(), print(),
00065    print_with_symbols(), and print_spaces() routines do this.
00066 
00067    Start_log_file() and stop_log_file() open and close the current log
00068    file.  Print_string_to_log_file_only() is called by the lexer to
00069    echo keyboard input to the log file (it's already on the screen, so
00070    we don't want to print it there too).
00071 
00072    Print_string() and print_spaces() do the obvious things.
00073    Print() is exactly like printf() in C, except it prints to both
00074    the screen and log file (if there is one).  Print_with_symbols()
00075    is sort of like print, but only takes two kinds of escape sequences
00076    in the format string: 
00077        %y  -- print a symbol
00078        %%  -- print a "%" sign
00079 
00080    Sometimes we need to know the current output column so we can put
00081    a line break in the right place.  Get_printer_output_column() returns
00082    the current column number (1 means the start of the line). 
00083    Tell_printer_that_output_column_has_been_reset () is called from the
00084    lexer every time it reads a line from the keyboard--since after the
00085    user types a line (and hits return) the output column is reset.
00086 
00087    We also support temporarily redirecting all printing output to
00088    another file.  This is done by calling start_redirection_to_file()
00089    and stop_redirection_to_file().  In between these calls, all screen
00090    and log file output is turned off, and printing is done only to the
00091    redirection file.
00092 ------------------------------------------------------------------- */
00093 
00094 void start_log_file(char *filename, bool append)
00095 {
00096     if (current_agent(logging_to_file))
00097         stop_log_file();
00098 
00099     sys_chdir(current_agent(top_dir_stack)->directory); /* AGR 568 */
00100     current_agent(log_file) = fopen(filename, (append ? "a" : "w"));
00101 
00102     if (current_agent(log_file)) {
00103         current_agent(logging_to_file) = TRUE;
00104         current_agent(log_file_name) = make_memory_block_for_string(filename);
00105         print("Logging to file %s\n", filename);
00106     } else {
00107         /* --- error when opening the file --- */
00108         print("Error: unable to open file %s\n", filename);
00109     }
00110 }
00111 
00112 void stop_log_file(void)
00113 {
00114     if (!current_agent(logging_to_file))
00115         return;
00116     print("Closing log file %s\n", current_agent(log_file_name));
00117     if (fclose(current_agent(log_file)))
00118         print("Error: unable to close file %s\n", current_agent(log_file_name));
00119     free_memory_block_for_string(current_agent(log_file_name));
00120     current_agent(logging_to_file) = FALSE;
00121 }
00122 
00123 void print_string_to_log_file_only(char *string)
00124 {
00125     fputs(string, current_agent(log_file));
00126 }
00127 
00128 int get_printer_output_column(void)
00129 {
00130     return current_agent(printer_output_column);
00131 }
00132 
00133 void tell_printer_that_output_column_has_been_reset(void)
00134 {
00135     current_agent(printer_output_column) = 1;
00136 }
00137 
00138 void start_redirection_to_file(FILE * already_opened_file)
00139 {
00140     current_agent(saved_printer_output_column) = current_agent(printer_output_column);
00141     current_agent(printer_output_column) = 1;
00142     current_agent(redirecting_to_file) = TRUE;
00143     current_agent(redirection_file) = already_opened_file;
00144 }
00145 
00146 void stop_redirection_to_file(void)
00147 {
00148     current_agent(redirecting_to_file) = FALSE;
00149     current_agent(printer_output_column) = current_agent(saved_printer_output_column);
00150 }
00151 
00152 /* -----------------------------------------------------------------------
00153                              Print_string
00154 
00155    This routine prints the given string, and updates printer_output_column.  
00156    (This routine is called from the other print(), etc. routines.)
00157 ----------------------------------------------------------------------- */
00158 
00159 void print_string(char *s)
00160 {
00161     char *ch;
00162 
00163     for (ch = s; *ch != 0; ch++) {
00164         if (*ch == '\n') {
00165             current_agent(printer_output_column) = 1;
00166         } else {
00167             current_agent(printer_output_column)++;
00168         }
00169     }
00170 
00171     if (current_agent(redirecting_to_file)) {
00172         fputs(s, current_agent(redirection_file));
00173     } else {
00174         /* this code is never executed since using_output_string is never true
00175            if (current_agent(using_output_string)) 
00176            strcat(current_agent(output_string),s);
00177            else {
00178          */
00179         /* 
00180            A bunch of crazy, interface dependent functions used to be called
00181            here.  I am removing all of that, and using the Tcl interface
00182            as a model of what the generalized behavior should in fact be.
00183            The old version, which used the Tcl interface simply made a function
00184            call to the interface layer which then simply invoked the 
00185            LOG and PRINT callbacks, which seems like the way to go.
00186 
00187            081699 SW
00188          */
00189 
00190         soar_invoke_first_callback(soar_agent, LOG_CALLBACK, s);
00191         soar_invoke_first_callback(soar_agent, PRINT_CALLBACK, s);
00192     }
00193     if (current_agent(logging_to_file)) {
00194         fputs(s, current_agent(log_file));
00195     }
00196 }
00197 
00198 /* ---------------------------------------------------------------
00199                Print, Print_with_symbols, Print_spaces
00200   
00201    These are the main printing routines.  (The code is ugly because
00202    it has to take a variable number of arguments, and there are two
00203    ways to do this, depending on whether we're using a fully ANSI
00204    compatible compiler or not.)
00205 --------------------------------------------------------------- */
00206 
00207 /* --- size of output buffer for a single call to one of these routines --- */
00208 #define PRINT_BUFSIZE 4096      /* This better be large enough!! */
00209 
00210 #ifdef USE_STDARGS
00211 void print(char *format, ...)
00212 {
00213     va_list args;
00214     char buf[PRINT_BUFSIZE];
00215 
00216     va_start(args, format);
00217 #else
00218 void print(va_alist)
00219 va_dcl
00220 {
00221     va_list args;
00222     char *format;
00223     char buf[PRINT_BUFSIZE];
00224 
00225     va_start(args);
00226     format = va_arg(args, char *);
00227 #endif
00228     vsnprintf(buf, PRINT_BUFSIZE, format, args);
00229     va_end(args);
00230     print_string(buf);
00231 }
00232 
00233 #ifdef USE_STDARGS
00234 void print_with_symbols(char *format, ...)
00235 {
00236     va_list args;
00237     char buf[PRINT_BUFSIZE];
00238     char *ch;
00239 
00240     va_start(args, format);
00241 #else
00242 void print_with_symbols(va_alist)
00243 va_dcl
00244 {
00245     va_list args;
00246     char buf[PRINT_BUFSIZE];
00247     char *ch, *format;
00248 
00249     va_start(args);
00250     format = va_arg(args, char *);
00251 #endif
00252     ch = buf;
00253 
00254     for (;;) {
00255         /* --- copy anything up to the first "%" --- */
00256         while ((*format != '%') && (*format != 0))
00257             *(ch++) = *(format++);
00258 
00259         if (*format == 0)
00260             break;
00261 
00262         /* --- handle the %-thingy --- */
00263         if (*(format + 1) == 'y') {
00264             /* the size of the remaining buffer (after ch) is
00265                the difference between the address of ch and
00266                the address of the beginning of the buffer
00267              */
00268             symbol_to_string(va_arg(args, Symbol *), TRUE, ch, PRINT_BUFSIZE - (ch - buf));
00269             while (*ch)
00270                 ch++;
00271 
00272         } else {
00273             *(ch++) = '%';
00274         }
00275 
00276         format += 2;
00277     }
00278 
00279     va_end(args);
00280 
00281     *ch = 0;
00282     print_string(buf);
00283 }
00284 
00285 void print_spaces(int n)
00286 {
00287     /*char *ch;
00288        ch = buf;
00289        while (n) { *(ch++)=' '; n--; }
00290        *ch=0; */
00291 
00292     char buf[PRINT_BUFSIZE];
00293 
00294     if (n >= PRINT_BUFSIZE) {
00295         n = PRINT_BUFSIZE - 1;
00296     }
00297 
00298     memset(buf, ' ', n);
00299 
00300     buf[n] = 0;
00301 
00302     print_string(buf);
00303 }
00304 
00305 /* ------------------------------------------------------------------------
00306                 String to Escaped String Conversion
00307            {Symbol, Test, RHS Value} to String Conversion
00308 
00309    These routines produce strings.  Each takes an optional parameter "dest"
00310    which, if non-nil, points to the destination buffer for the result string.
00311    If dest is nil, these routines use a global buffer, and return a pointer
00312    to it.  (Otherwise "dest" itself is returned.)  Note that a single global
00313    buffer is shared by all three routines, so callers should assume the
00314    buffer will be destroyed by the next call to these routines with dest=NIL.
00315 
00316    String_to_escaped_string() takes a string and a first/last char,
00317    and produces an "escaped string" representation of the string; i.e.,
00318    a string that uses '\' escapes to include special characters.
00319    For example, input 'ab"c' with first/last character '"' yields
00320    '"ab\"c"'.  This is used for printing quoted strings and for printing
00321    symbols using |vbar| notation.
00322  
00323    Symbol_to_string() converts a symbol to a string.  The "rereadable"
00324    parameter indicates whether a rereadable representation is desired.
00325    Normally symbols are printed rereadably, but for (write) and Text I/O,
00326    we don't want this.
00327 
00328    Test_to_string() takes a test and produces a string representation.
00329    Rhs_value_to_string() takes an rhs_value and produces a string
00330    representation.  The rhs_value MUST NOT be a reteloc.
00331 ----------------------------------------------------------------------- */
00332 
00333 char *string_to_escaped_string(char *s, char first_and_last_char, char *dest)
00334 {
00335     char *ch;
00336 
00337     if (!dest)
00338         dest = current_agent(printed_output_string);
00339     ch = dest;
00340     *ch++ = first_and_last_char;
00341     while (*s) {
00342         if ((*s == first_and_last_char) || (*s == '\\'))
00343             *ch++ = '\\';
00344         *ch++ = *s++;
00345     }
00346     *ch++ = first_and_last_char;
00347     *ch = 0;
00348     return dest;
00349 }
00350 
00351 char *symbol_to_string(Symbol * sym, bool rereadable, char *dest, size_t dest_size)
00352 {
00353     bool possible_id, possible_var, possible_sc, possible_ic, possible_fc;
00354     bool is_rereadable;
00355     bool has_angle_bracket;
00356 
00357     if (!sym) {
00358                 if (!dest) {
00359                         dest = current_agent(printed_output_string);
00360                         dest_size = PRINTED_OUTPUT_STRING_SIZE;
00361                 }
00362 
00363         strncpy(dest, "(NULL)", dest_size);
00364         dest[dest_size - 1] = 0;
00365         return dest;
00366     }
00367 
00368     switch (sym->common.symbol_type) {
00369     case VARIABLE_SYMBOL_TYPE:
00370         if (!dest) {
00371             return sym->var.name;
00372                 }
00373 
00374         strncpy(dest, sym->var.name, dest_size);
00375         dest[dest_size - 1] = 0;
00376         return dest;
00377 
00378     case IDENTIFIER_SYMBOL_TYPE:
00379         if (!dest) {
00380             dest = current_agent(printed_output_string);
00381             dest_size = PRINTED_OUTPUT_STRING_SIZE;
00382         }
00383         snprintf(dest, dest_size, "%c%lu", sym->id.name_letter, sym->id.name_number);
00384         dest[dest_size - 1] = 0;        /* snprintf doesn't set last char to null if output is truncated */
00385         return dest;
00386 
00387     case INT_CONSTANT_SYMBOL_TYPE:
00388         if (!dest) {
00389             dest = current_agent(printed_output_string);
00390             dest_size = PRINTED_OUTPUT_STRING_SIZE;
00391         }
00392         snprintf(dest, dest_size, "%ld", sym->ic.value);
00393         dest[dest_size - 1] = 0;        /* snprintf doesn't set last char to null if output is truncated */
00394         return dest;
00395 
00396     case FLOAT_CONSTANT_SYMBOL_TYPE:
00397         if (!dest) {
00398             dest = current_agent(printed_output_string);
00399             dest_size = PRINTED_OUTPUT_STRING_SIZE;
00400         }
00401         snprintf(dest, dest_size, "%#g", sym->fc.value);
00402         dest[dest_size - 1] = 0;        /* snprintf doesn't set last char to null if output is truncated */
00403         {                       /* --- strip off trailing zeros --- */
00404             char *start_of_exponent;
00405             char *end_of_mantissa;
00406             start_of_exponent = dest;
00407             while ((*start_of_exponent != 0) && (*start_of_exponent != 'e'))
00408                 start_of_exponent++;
00409             end_of_mantissa = start_of_exponent - 1;
00410             while (*end_of_mantissa == '0')
00411                 end_of_mantissa--;
00412             end_of_mantissa++;
00413             while (*start_of_exponent)
00414                 *end_of_mantissa++ = *start_of_exponent++;
00415             *end_of_mantissa = 0;
00416         }
00417         return dest;
00418 
00419     case SYM_CONSTANT_SYMBOL_TYPE:
00420         if (!rereadable) {
00421             if (!dest)
00422                 return sym->sc.name;
00423             strncpy(dest, sym->sc.name, dest_size);
00424             dest[dest_size - 1] = 0;
00425             return dest;
00426         }
00427         determine_possible_symbol_types_for_string(sym->sc.name,
00428                                                    strlen(sym->sc.name),
00429                                                    &possible_id,
00430                                                    &possible_var,
00431                                                    &possible_sc, &possible_ic, &possible_fc, &is_rereadable);
00432 
00433         has_angle_bracket = (bool) (sym->sc.name[0] == '<' || sym->sc.name[strlen(sym->sc.name) - 1] == '>');
00434 
00435         if ((!possible_sc) || possible_var || possible_ic || possible_fc || (!is_rereadable) || has_angle_bracket) {
00436             /* BUGBUG if in context where id's could occur, should check
00437                possible_id flag here also */
00438             return string_to_escaped_string(sym->sc.name, '|', dest);
00439         }
00440         if (!dest)
00441             return sym->sc.name;
00442         strncpy(dest, sym->sc.name, dest_size);
00443         dest[dest_size - 1] = 0;
00444         return dest;
00445 
00446     default:
00447         {
00448             char msg[MESSAGE_SIZE];
00449             strncpy(msg, "Internal Soar Error:  symbol_to_string called on bad symbol\n", MESSAGE_SIZE);
00450             msg[MESSAGE_SIZE - 1] = 0;
00451 
00452             abort_with_fatal_error(msg);
00453         }
00454     }
00455     return NIL;                 /* unreachable, but without it, gcc -Wall warns here */
00456 }
00457 
00458 char *test_to_string(test t, char *dest, size_t dest_size)
00459 {
00460     cons *c;
00461     complex_test *ct;
00462     char *ch;
00463 
00464     if (test_is_blank_test(t)) {
00465         if (!dest) {
00466             dest = current_agent(printed_output_string);
00467                         dest_size = PRINTED_OUTPUT_STRING_SIZE;
00468                 }
00469         snprintf(dest, dest_size, "[BLANK TEST]");      /* this should never get executed */
00470         dest[dest_size - 1] = 0;        /* snprintf doesn't set last char to null if output is truncated */
00471         return dest;
00472     }
00473 
00474     if (test_is_blank_or_equality_test(t)) {
00475         return symbol_to_string(referent_of_equality_test(t), TRUE, dest, dest_size);
00476     }
00477 
00478     if (!dest) {
00479         dest = current_agent(printed_output_string);
00480                 dest_size = PRINTED_OUTPUT_STRING_SIZE;
00481         }
00482 
00483     ch = dest;
00484     ct = complex_test_from_test(t);
00485 
00486     switch (ct->type) {
00487     case NOT_EQUAL_TEST:
00488         strncpy(ch, "<> ", dest_size - (ch - dest));
00489         ch[dest_size - (ch - dest) - 1] = 0;
00490         while (*ch)
00491             ch++;
00492         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00493         break;
00494     case LESS_TEST:
00495         strncpy(ch, "< ", dest_size - (ch - dest));
00496         ch[dest_size - (ch - dest) - 1] = 0;
00497         while (*ch)
00498             ch++;
00499         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00500         break;
00501     case GREATER_TEST:
00502         strncpy(ch, "> ", dest_size - (ch - dest));
00503         ch[dest_size - (ch - dest) - 1] = 0;
00504         while (*ch)
00505             ch++;
00506         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00507         break;
00508     case LESS_OR_EQUAL_TEST:
00509         strncpy(ch, "<= ", dest_size - (ch - dest));
00510         ch[dest_size - (ch - dest) - 1] = 0;
00511         while (*ch)
00512             ch++;
00513         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00514         break;
00515     case GREATER_OR_EQUAL_TEST:
00516         strncpy(ch, ">= ", dest_size - (ch - dest));
00517         ch[dest_size - (ch - dest) - 1] = 0;
00518         while (*ch)
00519             ch++;
00520         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00521         break;
00522     case SAME_TYPE_TEST:
00523         strncpy(ch, "<=> ", dest_size - (ch - dest));
00524         ch[dest_size - (ch - dest) - 1] = 0;
00525         while (*ch)
00526             ch++;
00527         symbol_to_string(ct->data.referent, TRUE, ch, dest_size - (ch - dest));
00528         break;
00529     case DISJUNCTION_TEST:
00530         strncpy(ch, "<< ", dest_size - (ch - dest));
00531         ch[dest_size - (ch - dest) - 1] = 0;
00532         while (*ch)
00533             ch++;
00534         for (c = ct->data.disjunction_list; c != NIL; c = c->rest) {
00535             symbol_to_string(c->first, TRUE, ch, dest_size - (ch - dest));
00536             while (*ch)
00537                 ch++;
00538             *(ch++) = ' ';
00539         }
00540         strncpy(ch, ">>", dest_size - (ch - dest));
00541         ch[dest_size - (ch - dest) - 1] = 0;
00542         break;
00543     case CONJUNCTIVE_TEST:
00544         strncpy(ch, "{ ", dest_size - (ch - dest));
00545         ch[dest_size - (ch - dest) - 1] = 0;
00546         while (*ch)
00547             ch++;
00548         for (c = ct->data.conjunct_list; c != NIL; c = c->rest) {
00549             test_to_string(c->first, ch, dest_size - (ch - dest));
00550             while (*ch)
00551                 ch++;
00552             *(ch++) = ' ';
00553         }
00554         strncpy(ch, "}", dest_size - (ch - dest));
00555         ch[dest_size - (ch - dest) - 1] = 0;
00556         break;
00557     case GOAL_ID_TEST:
00558         strncpy(dest, "[GOAL ID TEST]", dest_size);     /* this should never get executed */
00559         dest[dest_size - 1] = 0;
00560         break;
00561     case IMPASSE_ID_TEST:
00562         strncpy(dest, "[IMPASSE ID TEST]", dest_size);  /* this should never get executed */
00563         dest[dest_size - 1] = 0;
00564         break;
00565     }
00566     return dest;
00567 }
00568 
00569 char *rhs_value_to_string(rhs_value rv, char *dest, size_t dest_size)
00570 {
00571     cons *c;
00572     list *fl;
00573     rhs_function *rf;
00574     char *ch;
00575 
00576     if (rhs_value_is_reteloc(rv)) {
00577         char msg[MESSAGE_SIZE];
00578         strncpy(msg, "Internal error: rhs_value_to_string called on reteloc.\n", MESSAGE_SIZE);
00579         msg[MESSAGE_SIZE - 1] = 0;
00580         abort_with_fatal_error(msg);
00581     }
00582 
00583     if (rhs_value_is_symbol(rv)) {
00584         return symbol_to_string(rhs_value_to_symbol(rv), TRUE, dest, dest_size);
00585     }
00586 
00587     fl = rhs_value_to_funcall_list(rv);
00588     rf = fl->first;
00589 
00590     if (!dest) {
00591         dest = current_agent(printed_output_string);
00592                 dest_size = PRINTED_OUTPUT_STRING_SIZE;
00593         }
00594     ch = dest;
00595 
00596     strncpy(ch, "(", dest_size - (ch - dest));
00597     ch[dest_size - (ch - dest) - 1] = 0;
00598     while (*ch)
00599         ch++;
00600 
00601     if (!strcmp(rf->name->sc.name, "+")) {
00602         strncpy(ch, "(", dest_size - (ch - dest));
00603         ch[dest_size - (ch - dest) - 1] = 0;
00604     } else if (!strcmp(rf->name->sc.name, "-")) {
00605         strncpy(ch, "-", dest_size - (ch - dest));
00606         ch[dest_size - (ch - dest) - 1] = 0;
00607     } else {
00608         symbol_to_string(rf->name, TRUE, ch, dest_size - (ch - dest));
00609     }
00610 
00611     while (*ch)
00612         ch++;
00613     for (c = fl->rest; c != NIL; c = c->rest) {
00614         strncpy(ch, " ", dest_size - (ch - dest));
00615         ch[dest_size - (ch - dest) - 1] = 0;
00616         while (*ch)
00617             ch++;
00618         rhs_value_to_string(c->first, ch, dest_size - (ch - dest));
00619         while (*ch)
00620             ch++;
00621     }
00622     strncpy(ch, ")", dest_size - (ch - dest));
00623     ch[dest_size - (ch - dest) - 1] = 0;
00624     return dest;
00625 }
00626 
00627 /* ------------------------------------------------------------------
00628                         Print Condition List
00629 
00630    This prints a list of conditions.  The "indent" parameter tells
00631    how many spaces to indent each line other than the first--the first
00632    line is not indented (the caller must handle this).  The last line
00633    is printed without a trailing linefeed.  The "internal" parameter,
00634    if TRUE, indicates that the condition list should be printed in
00635    internal format--one condition per line, without grouping all the
00636    conditions for the same id into one line.
00637 ------------------------------------------------------------------ */
00638 
00639 test id_test_to_match;
00640 
00641 bool pick_conds_with_matching_id_test(dl_cons * dc)
00642 {
00643     condition *cond;
00644     cond = dc->item;
00645     if (cond->type == CONJUNCTIVE_NEGATION_CONDITION)
00646         return FALSE;
00647     return tests_are_equal(id_test_to_match, cond->data.tests.id_test);
00648 }
00649 
00650 #define PRINT_CONDITION_LIST_TEMP_SIZE 10000
00651 void print_condition_list(condition * conds, int indent, bool internal)
00652 {
00653     bool did_one_line_already;
00654     dl_list *conds_not_yet_printed, *tail_of_conds_not_yet_printed;
00655     dl_list *conds_for_this_id;
00656     dl_cons *dc;
00657     condition *c;
00658     bool removed_goal_test, removed_impasse_test;
00659     test id_test;
00660 
00661     if (!conds)
00662         return;
00663 
00664     did_one_line_already = FALSE;
00665 
00666     /* --- build dl_list of all the actions --- */
00667     conds_not_yet_printed = NIL;
00668     tail_of_conds_not_yet_printed = NIL;
00669     for (c = conds; c != NIL; c = c->next) {
00670         allocate_with_pool(&current_agent(dl_cons_pool), &dc);
00671         dc->item = c;
00672         if (conds_not_yet_printed)
00673             tail_of_conds_not_yet_printed->next = dc;
00674         else
00675             conds_not_yet_printed = dc;
00676         dc->prev = tail_of_conds_not_yet_printed;
00677         tail_of_conds_not_yet_printed = dc;
00678     }
00679     tail_of_conds_not_yet_printed->next = NIL;
00680 
00681     /* --- main loop: find all conds for first id, print them together --- */
00682     while (conds_not_yet_printed) {
00683         if (did_one_line_already) {
00684             print("\n");
00685             print_spaces(indent);
00686         } else {
00687             did_one_line_already = TRUE;
00688         }
00689         dc = conds_not_yet_printed;
00690         remove_from_dll(conds_not_yet_printed, dc, next, prev);
00691         c = dc->item;
00692 
00693         if (c->type == CONJUNCTIVE_NEGATION_CONDITION) {
00694             free_with_pool(&current_agent(dl_cons_pool), dc);
00695             print_string("-{");
00696             print_condition_list(c->data.ncc.top, indent + 2, internal);
00697             print_string("}");
00698             continue;
00699         }
00700 
00701         /* --- normal pos/neg conditions --- */
00702         removed_goal_test = removed_impasse_test = FALSE;
00703         id_test = copy_test_removing_goal_impasse_tests
00704             (c->data.tests.id_test, &removed_goal_test, &removed_impasse_test);
00705         id_test_to_match = copy_of_equality_test_found_in_test(id_test);
00706 
00707         /* --- collect all cond's whose id test matches this one --- */
00708         conds_for_this_id = dc;
00709         dc->prev = NIL;
00710         if (internal) {
00711             dc->next = NIL;
00712 
00713         } else {
00714             dc->next = extract_dl_list_elements(&conds_not_yet_printed, pick_conds_with_matching_id_test);
00715         }
00716 
00717         /* --- print the collected cond's all together --- */
00718         print_string(" (");
00719         if (removed_goal_test)
00720             print_string("state ");
00721         if (removed_impasse_test)
00722             print_string("impasse ");
00723         print_string(test_to_string(id_test, NULL, 0));
00724         deallocate_test(id_test_to_match);
00725         deallocate_test(id_test);
00726         while (conds_for_this_id) {
00727             dc = conds_for_this_id;
00728             conds_for_this_id = conds_for_this_id->next;
00729             c = dc->item;
00730             free_with_pool(&current_agent(dl_cons_pool), dc);
00731 
00732             {                   /* --- build and print attr/value test for condition c --- */
00733                 char temp[PRINT_CONDITION_LIST_TEMP_SIZE], *ch;
00734 
00735                 ch = temp;
00736 
00737                 strncpy(ch, " ", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00738                 ch[PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp) - 1] = 0;
00739                 if (c->type == NEGATIVE_CONDITION) {
00740                     strncat(ch, "-", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00741                     ch[PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp) - 1] = 0;
00742                     while (*ch)
00743                         ch++;
00744                 }
00745 
00746                 strncat(ch, "^", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00747                 ch[PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp) - 1] = 0;
00748                 while (*ch)
00749                     ch++;
00750 
00751                 test_to_string(c->data.tests.attr_test, ch, PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00752                 while (*ch)
00753                     ch++;
00754                 if (!test_is_blank_test(c->data.tests.value_test)) {
00755                     *(ch++) = ' ';
00756                     test_to_string(c->data.tests.value_test, ch, PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00757                     while (*ch)
00758                         ch++;
00759                     if (c->test_for_acceptable_preference) {
00760                         strncpy(ch, " +", PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp));
00761                         ch[PRINT_CONDITION_LIST_TEMP_SIZE - (ch - temp) - 1] = 0;
00762                         while (*ch)
00763                             ch++;
00764                     }
00765                 }
00766                 *ch = 0;
00767                 if (current_agent(printer_output_column) + (ch - temp) >= COLUMNS_PER_LINE) {
00768                     print_string("\n");
00769                     print_spaces(indent + 6);
00770                 }
00771                 print_string(temp);
00772             }
00773         }
00774 
00775         print_string(")");
00776     }                           /* end of while (conds_not_yet_printed) */
00777 }
00778 
00779 /* ------------------------------------------------------------------
00780                         Print Action List
00781 
00782    This prints a list of actions.  The "indent" parameter tells how
00783    many spaces to indent each line other than the first--the first
00784    line is not indented (the caller must handle this).  The last line
00785    is printed without a trailing linefeed.  The "internal" parameter,
00786    if TRUE, indicates that the action list should be printed in
00787    internal format--one action per line, without grouping all the
00788    actions for the same id into one line.
00789    Note:  the actions MUST NOT contain any reteloc's.
00790 ------------------------------------------------------------------ */
00791 
00792 Symbol *action_id_to_match;
00793 
00794 bool pick_actions_with_matching_id(dl_cons * dc)
00795 {
00796     action *a;
00797     a = dc->item;
00798     if (a->type != MAKE_ACTION)
00799         return FALSE;
00800     return (bool) (rhs_value_to_symbol(a->id) == action_id_to_match);
00801 }
00802 
00803 #define PRINT_ACTION_LIST_TEMP_SIZE 10000
00804 void print_action_list(action * actions, int indent, bool internal)
00805 {
00806     bool did_one_line_already;
00807     dl_list *actions_not_yet_printed, *tail_of_actions_not_yet_printed;
00808     dl_list *actions_for_this_id;
00809     dl_cons *dc;
00810     action *a;
00811 
00812     if (!actions)
00813         return;
00814 
00815     did_one_line_already = FALSE;
00816 
00817     /* --- build dl_list of all the actions --- */
00818     actions_not_yet_printed = NIL;
00819     tail_of_actions_not_yet_printed = NIL;
00820     for (a = actions; a != NIL; a = a->next) {
00821         allocate_with_pool(&current_agent(dl_cons_pool), &dc);
00822         dc->item = a;
00823         if (actions_not_yet_printed)
00824             tail_of_actions_not_yet_printed->next = dc;
00825         else
00826             actions_not_yet_printed = dc;
00827         dc->prev = tail_of_actions_not_yet_printed;
00828         tail_of_actions_not_yet_printed = dc;
00829     }
00830     tail_of_actions_not_yet_printed->next = NIL;
00831 
00832     /* --- main loop: find all actions for first id, print them together --- */
00833     while (actions_not_yet_printed) {
00834         if (did_one_line_already) {
00835             print("\n");
00836             print_spaces(indent);
00837         } else {
00838             did_one_line_already = TRUE;
00839         }
00840         dc = actions_not_yet_printed;
00841         remove_from_dll(actions_not_yet_printed, dc, next, prev);
00842         a = dc->item;
00843         if (a->type == FUNCALL_ACTION) {
00844             free_with_pool(&current_agent(dl_cons_pool), dc);
00845             print_string(rhs_value_to_string(a->value, NULL, 0));
00846             continue;
00847         }
00848 
00849         /* --- normal make actions --- */
00850         /* --- collect all actions whose id matches the first action's id --- */
00851         actions_for_this_id = dc;
00852         action_id_to_match = rhs_value_to_symbol(a->id);
00853         dc->prev = NIL;
00854         if (internal) {
00855             dc->next = NIL;
00856         } else {
00857             dc->next = extract_dl_list_elements(&actions_not_yet_printed, pick_actions_with_matching_id);
00858         }
00859 
00860         /* --- print the collected actions all together --- */
00861         print_with_symbols("(%y", action_id_to_match);
00862         while (actions_for_this_id) {
00863             dc = actions_for_this_id;
00864             actions_for_this_id = actions_for_this_id->next;
00865             a = dc->item;
00866             free_with_pool(&current_agent(dl_cons_pool), dc);
00867 
00868             {                   /* --- build and print attr/value test for action a --- */
00869                 char temp[PRINT_ACTION_LIST_TEMP_SIZE], *ch;
00870 
00871                 ch = temp;
00872                 strncpy(ch, " ^", PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
00873                 ch[PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp) - 1] = 0;
00874                 while (*ch)
00875                     ch++;
00876                 rhs_value_to_string(a->attr, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
00877                 while (*ch)
00878                     ch++;
00879                 *(ch++) = ' ';
00880                 rhs_value_to_string(a->value, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
00881                 while (*ch)
00882                     ch++;
00883                 *(ch++) = ' ';
00884                 *(ch++) = preference_type_indicator(a->preference_type);
00885                 if (preference_is_binary(a->preference_type)) {
00886                     *(ch++) = ' ';
00887                     rhs_value_to_string(a->referent, ch, PRINT_ACTION_LIST_TEMP_SIZE - (ch - temp));
00888                     while (*ch)
00889                         ch++;
00890                 }
00891                 *ch = 0;
00892                 if (current_agent(printer_output_column) + (ch - temp) >= COLUMNS_PER_LINE) {
00893                     print_string("\n");
00894                     print_spaces(indent + 6);
00895                 }
00896                 print_string(temp);
00897             }
00898         }
00899         print_string(")");
00900     }                           /* end of while (actions_not_yet_printed) */
00901 }
00902 
00903 /* ------------------------------------------------------------------
00904                          Print Production
00905 
00906    This prints a production.  The "internal" parameter, if TRUE,
00907    indicates that the LHS and RHS should be printed in internal format.
00908 ------------------------------------------------------------------ */
00909 
00910 void print_production(production * p, bool internal)
00911 {
00912     condition *top, *bottom;
00913     action *rhs;
00914 
00915     /* --- print "sp" and production name --- */
00916     print_with_symbols("sp {%y\n", p->name);
00917     /* --- print optional documention string --- */
00918     if (p->documentation) {
00919         char temp[MAX_LEXEME_LENGTH * 2 + 10];
00920         string_to_escaped_string(p->documentation, '"', temp);
00921         print("    %s\n", temp);
00922     }
00923     /* --- print any flags --- */
00924     switch (p->type) {
00925     case DEFAULT_PRODUCTION_TYPE:
00926         print_string("    :default\n");
00927         break;
00928     case USER_PRODUCTION_TYPE:
00929         break;
00930     case CHUNK_PRODUCTION_TYPE:
00931         print_string("    :chunk\n");
00932         break;
00933     case JUSTIFICATION_PRODUCTION_TYPE:
00934         print_string("    :justification ;# not reloadable\n");
00935         break;
00936     }
00937     if (p->declared_support == DECLARED_O_SUPPORT)
00938         print_string("    :o-support\n");
00939     else if (p->declared_support == DECLARED_I_SUPPORT)
00940         print_string("    :i-support\n");
00941 #ifdef MATCHTIME_INTERRUPT
00942     if (p->interrupt)
00943         print_string("    :interrupt\n");
00944 #endif
00945 
00946     /* --- print the LHS and RHS --- */
00947     p_node_to_conditions_and_nots(p->p_node, NIL, NIL, &top, &bottom, NIL, &rhs);
00948     print_string("   ");
00949     print_condition_list(top, 3, internal);
00950     deallocate_condition_list(top);
00951     print_string("\n    -->\n  ");
00952     print_string("  ");
00953     print_action_list(rhs, 4, internal);
00954     print_string("\n}\n");
00955     deallocate_action_list(rhs);
00956 }
00957 
00958 /* ------------------------------------------------------------------
00959                        Other Printing Utilities
00960 
00961    Print_condition() prints a single condition.  Print_action() prints
00962    a single action (which MUST NOT contain any reteloc's).
00963    Note that these routines work by calling print_condition_list() and
00964    print_action_list(), respectively, so they print a linefeed if the
00965    output would go past COLUMNS_PER_LINE.
00966 
00967    Preference_type_indicator() returns a character corresponding to
00968    a given preference type (byte)--for example, given BEST_PREFERENCE_TYPE,
00969    it returns '>'.
00970 
00971    Print_preference() prints a given preference.  Print_wme() prints a
00972    wme (including the timetag).  Print_instantiation_with_wmes() prints
00973    an instantiation's production name and the wmes it matched, using a
00974    given wme_trace_type (e.g., TIMETAG_WME_TRACE).
00975 ------------------------------------------------------------------ */
00976 
00977 void print_condition(condition * cond)
00978 {
00979     condition *old_next, *old_prev;
00980 
00981     old_next = cond->next;
00982     old_prev = cond->prev;
00983     cond->next = NIL;
00984     cond->prev = NIL;
00985     print_condition_list(cond, 0, TRUE);
00986     cond->next = old_next;
00987     cond->prev = old_prev;
00988 }
00989 
00990 void print_action(action * a)
00991 {
00992     action *old_next;
00993 
00994     old_next = a->next;
00995     a->next = NIL;
00996     print_action_list(a, 0, TRUE);
00997     a->next = old_next;
00998 }
00999 
01000 char preference_type_indicator(byte type)
01001 {
01002     switch (type) {
01003     case ACCEPTABLE_PREFERENCE_TYPE:
01004         return '+';
01005     case REQUIRE_PREFERENCE_TYPE:
01006         return '!';
01007     case REJECT_PREFERENCE_TYPE:
01008         return '-';
01009     case PROHIBIT_PREFERENCE_TYPE:
01010         return '~';
01011     case RECONSIDER_PREFERENCE_TYPE:
01012         return '@';
01013     case UNARY_INDIFFERENT_PREFERENCE_TYPE:
01014         return '=';
01015     case BINARY_INDIFFERENT_PREFERENCE_TYPE:
01016         return '=';
01017     case UNARY_PARALLEL_PREFERENCE_TYPE:
01018         return '&';
01019     case BINARY_PARALLEL_PREFERENCE_TYPE:
01020         return '&';
01021     case BEST_PREFERENCE_TYPE:
01022         return '>';
01023     case BETTER_PREFERENCE_TYPE:
01024         return '>';
01025     case WORST_PREFERENCE_TYPE:
01026         return '<';
01027     case WORSE_PREFERENCE_TYPE:
01028         return '<';
01029     default:
01030         {
01031             char msg[MESSAGE_SIZE];
01032             strncpy(msg, "print.c: Error: bad type passed to preference_type_indicator\n", MESSAGE_SIZE);
01033             msg[MESSAGE_SIZE - 1] = 0;
01034             abort_with_fatal_error(msg);
01035         }
01036     }
01037     return 0;                   /* unreachable, but without it, gcc -Wall warns here */
01038 }
01039 
01040 void print_preference(preference * pref)
01041 {
01042     print_with_symbols("(%y ^%y %y ", pref->id, pref->attr, pref->value);
01043     print("%c", preference_type_indicator(pref->type));
01044     if (preference_is_binary(pref->type)) {
01045         print_with_symbols(" %y", pref->referent);
01046     }
01047     if (pref->o_supported)
01048         print_string("  :O ");
01049     print_string(")");
01050     print("\n");
01051 }
01052 
01053 void detailed_print_preference(preference * pref)
01054 {
01055     int i, end;
01056     char space[48];
01057 
01058     print_with_symbols("(%y ^%y %y ", pref->id, pref->attr, pref->value);
01059     print("%c", preference_type_indicator(pref->type));
01060     if (preference_is_binary(pref->type)) {
01061         print_with_symbols(" %y", pref->referent);
01062     }
01063     if (pref->o_supported)
01064         print_string("  :O ");
01065 
01066     end = 37 - get_printer_output_column();
01067     for (i = 0; i < end; i++) {
01068         space[i] = ' ';
01069 
01070     }
01071     space[i] = '\0';
01072 
01073     print(")%s", space);
01074     print(" (refs, in-tm, addr) (%lu, %d, %p)", pref->reference_count, pref->in_tm, pref);
01075     print("\n");
01076 }
01077 
01078 void watchful_print_preference(preference * pref)
01079 {
01080     int i, end;
01081     char space[48];
01082 
01083     print_with_symbols("(%y ^%y %y ", pref->id, pref->attr, pref->value);
01084     print("%c", preference_type_indicator(pref->type));
01085     if (preference_is_binary(pref->type)) {
01086         print_with_symbols(" %y", pref->referent);
01087     }
01088     if (pref->o_supported)
01089         print_string("  :O ");
01090 
01091     end = 37 - get_printer_output_column();
01092     for (i = 0; i < end; i++) {
01093         space[i] = ' ';
01094 
01095     }
01096     space[i] = '\0';
01097 
01098     print(")%s", space);
01099     print(" (id->id.level) (%d)", pref->id->id.level);
01100     print("\n");
01101 }
01102 
01103 /* kjh(CUSP-B2) begin */
01104 extern bool passes_wme_filtering(wme * w, bool isAdd);
01105 void filtered_print_wme_add(wme * w)
01106 {
01107     if (passes_wme_filtering(w, TRUE)) {
01108         print("=>WM: ");
01109         print_wme(w);
01110     }
01111 }
01112 void filtered_print_wme_remove(wme * w)
01113 {
01114     if (passes_wme_filtering(w, FALSE)) {
01115         print("<=WM: ");
01116         print_wme(w);
01117     }
01118 }
01119 
01120 /* kjh(CUSP-B2) end */
01121 
01122 void print_wme(wme * w)
01123 {
01124     print("(%lu: ", w->timetag);
01125     print_with_symbols("%y ^%y %y", w->id, w->attr, w->value);
01126     if (w->acceptable)
01127         print_string(" +");
01128     print(")\n");
01129 }
01130 
01131 void detailed_print_wme(wme * w)
01132 {
01133     int end, i;
01134     char space[48];
01135 
01136     print("(%lu: ", w->timetag);
01137     print_with_symbols("%y ^%y %y", w->id, w->attr, w->value);
01138     if (w->acceptable)
01139         print_string(" +");
01140 
01141     end = 37 - get_printer_output_column();
01142     for (i = 0; i < end; i++) {
01143         space[i] = ' ';
01144 
01145     }
01146     space[i] = '\0';
01147 
01148     print(")%s", space);
01149 
01150     print("(refs, addr) (%lu, %p)", w->reference_count, w);
01151     print("\n");
01152 
01153 }
01154 
01155 void print_instantiation_with_wmes(instantiation * inst, wme_trace_type wtt)
01156 {
01157     condition *cond;
01158 
01159     if (inst->prod)
01160         print_with_symbols("%y", inst->prod->name);
01161     else
01162         print("[dummy production]");
01163     print("\n");
01164 
01165     if (wtt == NONE_WME_TRACE)
01166         return;
01167 
01168     for (cond = inst->top_of_instantiated_conditions; cond != NIL; cond = cond->next)
01169         if (cond->type == POSITIVE_CONDITION) {
01170             switch (wtt) {
01171             case TIMETAG_WME_TRACE:
01172                 print(" %lu", cond->bt.wme->timetag);
01173                 break;
01174             case FULL_WME_TRACE:
01175                 print(" ");
01176                 print_wme(cond->bt.wme);
01177                 break;
01178             }
01179         }
01180 }
01181 
01182 /***************************************************************************
01183 * Function     : print_list_of_conditions
01184 **************************************************************************/
01185 
01186 void print_list_of_conditions(condition * cond)
01187 {
01188 
01189     while (cond != NULL) {
01190         if (get_printer_output_column() >= COLUMNS_PER_LINE - 20)
01191             print("\n      ");
01192         print_condition(cond);
01193         print("\n");
01194 
01195         cond = cond->next;
01196     }
01197 }

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