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

parser.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  parser.c
00004  *
00005  * =======================================================================
00006  *                  Production (SP) Parser for Soar 6
00007  *
00008  * There are two top-level routines here:  init_parser(), which
00009  * should be called at startup time, and parse_production(), which
00010  * reads an SP (starting from the production name), builds a production,
00011  * adds it to the rete, and returns a pointer to the new production
00012  * (or NIL if any error occurred).
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 #include "soarkernel.h"
00048 #include <ctype.h>
00049 
00050 /* =================================================================
00051                    Placeholder (Dummy) Variables
00052    
00053    In attribute paths (and some other places) we need to create dummy
00054    variables.  But we need to make sure these dummy variables don't
00055    accidently have the same names as variables that occur later in
00056    the user's production.  So, we create "placeholder" variables, whose
00057    names have funky characters in them so they couldn't possibly occur
00058    in user-written code.  When we're all done parsing the production, we
00059    go back and replace the placeholder variables with "real" variables 
00060    (names without funky characters), making sure the real variables
00061    don't occur anywhere else in the production.
00062 ================================================================= */
00063 
00064 unsigned long placeholder_counter[26];
00065 
00066 void reset_placeholder_variable_generator(void)
00067 {
00068     int i;
00069     for (i = 0; i < 26; i++)
00070         placeholder_counter[i] = 1;
00071 }
00072 
00073 /* -----------------------------------------------------------------
00074                Make Placeholder (Dummy) Equality Test
00075    
00076    Creates and returns a test for equality with a newly generated
00077    placeholder variable.
00078 ----------------------------------------------------------------- */
00079 
00080 #define NAMEBUF_SIZE 30
00081 test make_placeholder_test(char first_letter)
00082 {
00083     char namebuf[NAMEBUF_SIZE];
00084     Symbol *new_var;
00085 
00086     if (isalpha(first_letter)) {
00087         if (isupper(first_letter))
00088             first_letter = (char) tolower(first_letter);
00089     } else {
00090         first_letter = 'v';
00091     }
00092     /* --- create variable with "#" in its name:  this couldn't possibly be a
00093        variable in the user's code, since the lexer doesn't handle "#" --- */
00094     snprintf(namebuf, NAMEBUF_SIZE, "<#%c*%lu>", first_letter, placeholder_counter[first_letter - 'a']++);
00095     namebuf[NAMEBUF_SIZE - 1] = 0;      /* snprintf doesn't set last char to null if output is truncated */
00096     new_var = make_variable(namebuf);
00097     /* --- indicate that there is no corresponding "real" variable yet --- */
00098     new_var->var.current_binding_value = NIL;
00099     /* --- return an equality test for that variable --- */
00100     return make_equality_test_without_adding_reference(new_var);
00101 }
00102 
00103 /* -----------------------------------------------------------------
00104             Substituting Real Variables for Placeholders
00105    
00106    When done parsing the production, we go back and substitute "real"
00107    variables for all the placeholders.  This is done by walking all the
00108    LHS conditions and destructively modifying any tests involving
00109    placeholders.  The placeholder-->real mapping is maintained on each
00110    placeholder symbol: placeholder->var.current_binding_value is the
00111    corresponding "real" variable, or NIL if no such "real" variable has
00112    been created yet.
00113 
00114    To use this, first call reset_variable_generator (lhs, rhs) with the
00115    lhs and rhs of the production just parsed; then call
00116    substitute_for_placeholders_in_condition_list (lhs).
00117 ----------------------------------------------------------------- */
00118 
00119 void substitute_for_placeholders_in_symbol(Symbol ** sym)
00120 {
00121     char prefix[3];
00122     Symbol *var;
00123     bool just_created;
00124 
00125     /* --- if not a variable, do nothing --- */
00126     if ((*sym)->common.symbol_type != VARIABLE_SYMBOL_TYPE)
00127         return;
00128     /* --- if not a placeholder variable, do nothing --- */
00129     if (*((*sym)->var.name + 1) != '#')
00130         return;
00131 
00132     just_created = FALSE;
00133 
00134     if (!(*sym)->var.current_binding_value) {
00135         prefix[0] = *((*sym)->var.name + 2);
00136         prefix[1] = '*';
00137         prefix[2] = 0;
00138         (*sym)->var.current_binding_value = generate_new_variable(prefix);
00139         just_created = TRUE;
00140     }
00141 
00142     var = (*sym)->var.current_binding_value;
00143     symbol_remove_ref(*sym);
00144     *sym = var;
00145     if (!just_created)
00146         symbol_add_ref(var);
00147 }
00148 
00149 void substitute_for_placeholders_in_test(test * t)
00150 {
00151     cons *c;
00152     complex_test *ct;
00153 
00154     if (test_is_blank_test(*t))
00155         return;
00156     if (test_is_blank_or_equality_test(*t)) {
00157         substitute_for_placeholders_in_symbol((Symbol **) t);
00158         /* Warning: this relies on the representation of tests */
00159         return;
00160     }
00161 
00162     ct = complex_test_from_test(*t);
00163 
00164     switch (ct->type) {
00165     case GOAL_ID_TEST:
00166     case IMPASSE_ID_TEST:
00167     case DISJUNCTION_TEST:
00168         return;
00169     case CONJUNCTIVE_TEST:
00170         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
00171             substitute_for_placeholders_in_test((test *) (&(c->first)));
00172         return;
00173     default:                   /* relational tests other than equality */
00174         substitute_for_placeholders_in_symbol(&(ct->data.referent));
00175         return;
00176     }
00177 }
00178 
00179 void substitute_for_placeholders_in_condition_list(condition * cond)
00180 {
00181     for (; cond != NIL; cond = cond->next) {
00182         switch (cond->type) {
00183         case POSITIVE_CONDITION:
00184         case NEGATIVE_CONDITION:
00185             substitute_for_placeholders_in_test(&(cond->data.tests.id_test));
00186             substitute_for_placeholders_in_test(&(cond->data.tests.attr_test));
00187             substitute_for_placeholders_in_test(&(cond->data.tests.value_test));
00188             break;
00189         case CONJUNCTIVE_NEGATION_CONDITION:
00190             substitute_for_placeholders_in_condition_list(cond->data.ncc.top);
00191             break;
00192         }
00193     }
00194 }
00195 
00196 /* begin KJC 10/19/98 */
00197 void substitute_for_placeholders_in_action_list(action * a)
00198 {
00199     for (; a != NIL; a = a->next) {
00200         if (a->type == MAKE_ACTION) {
00201             if (rhs_value_is_symbol(a->id)) {
00202                 substitute_for_placeholders_in_symbol((Symbol **) & (a->id));
00203             }
00204             if (rhs_value_is_symbol(a->attr))
00205                 substitute_for_placeholders_in_symbol((Symbol **) & (a->attr));
00206             if (rhs_value_is_symbol(a->value))
00207                 substitute_for_placeholders_in_symbol((Symbol **) & (a->value));
00208         }
00209     }
00210 }
00211 
00212 /* end KJC 10/19/98 */
00213 
00214 /* =================================================================
00215 
00216    Grammar for left hand sides of productions
00217 
00218    <lhs> ::= <cond>+
00219    <cond> ::= <positive_cond> | - <positive_cond>
00220    <positive_cond> ::= <conds_for_one_id> | { <cond>+ }
00221    <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
00222    <id_test> ::= <test>
00223    <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*
00224    <attr_test> ::= <test>
00225    <value_test> ::= <test> [+] | <conds_for_one_id> [+]
00226 
00227    <test> ::= <conjunctive_test> | <simple_test>
00228    <conjunctive_test> ::= { <simple_test>+ }
00229    <simple_test> ::= <disjunction_test> | <relational_test>
00230    <disjunction_test> ::= << <constant>* >>
00231    <relational_test> ::= [<relation>] <single_test>
00232    <relation> ::= <> | < | > | <= | >= | = | <=>
00233    <single_test> ::= variable | <constant>
00234    <constant> ::= sym_constant | int_constant | float_constant
00235 
00236 ================================================================= */
00237 
00238 char *help_on_lhs_grammar[] = {
00239     "Grammar for left hand sides of productions:",
00240     "",
00241     "   <lhs> ::= <cond>+",
00242     "   <cond> ::= <positive_cond> | - <positive_cond>",
00243     "   <positive_cond> ::= <conds_for_one_id> | { <cond>+ }",
00244     "   <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )",
00245     "   <id_test> ::= <test>",
00246     "   <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*",
00247     "   <attr_test> ::= <test>",
00248     "   <value_test> ::= <test> [+] | <conds_for_one_id> [+]",
00249     "",
00250     "   <test> ::= <conjunctive_test> | <simple_test>",
00251     "   <conjunctive_test> ::= { <simple_test>+ }",
00252     "   <simple_test> ::= <disjunction_test> | <relational_test>",
00253     "   <disjunction_test> ::= << <constant>* >>",
00254     "   <relational_test> ::= [<relation>] <single_test>",
00255     "   <relation> ::= <> | < | > | <= | >= | = | <=>",
00256     "   <single_test> ::= variable | <constant>",
00257     "   <constant> ::= sym_constant | int_constant | float_constant",
00258     "",
00259     "See also:  rhs-grammar, sp",
00260     0
00261 };
00262 
00263 /* =================================================================
00264 
00265                   Make symbol for current lexeme
00266 
00267 ================================================================= */
00268 
00269 Symbol *make_symbol_for_current_lexeme(void)
00270 {
00271     switch (current_agent(lexeme).type) {
00272     case SYM_CONSTANT_LEXEME:
00273         return make_sym_constant(current_agent(lexeme).string);
00274     case VARIABLE_LEXEME:
00275         return make_variable(current_agent(lexeme).string);
00276     case INT_CONSTANT_LEXEME:
00277         return make_int_constant(current_agent(lexeme).int_val);
00278     case FLOAT_CONSTANT_LEXEME:
00279         return make_float_constant(current_agent(lexeme).float_val);
00280 
00281     case IDENTIFIER_LEXEME:
00282         {
00283             char msg[MESSAGE_SIZE];
00284             strncpy(msg, "parser.c: Internal error:  ID found in make_symbol_for_current_lexeme\n", MESSAGE_SIZE);
00285             msg[MESSAGE_SIZE - 1] = 0;  /* strncpy doesn't set last char to null if output is truncated */
00286             abort_with_fatal_error(msg);
00287         }
00288     default:
00289         {
00290             char msg[MESSAGE_SIZE];
00291             snprintf(msg, MESSAGE_SIZE,
00292                      "parser.c: Internal error:  bad lexeme type in make_symbol_for_current_lexeme\n, current_agent(lexeme).string=%s\n",
00293                      current_agent(lexeme).string);
00294             msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
00295             abort_with_fatal_error(msg);
00296         }
00297     }
00298     return NIL;                 /* unreachable, but without it, gcc -Wall warns here */
00299 }
00300 
00301 /* =================================================================
00302                           Routines for Tests
00303 
00304    The following routines are used to parse and build <test>'s.
00305    At entry, they expect the current lexeme to be the start of a
00306    test.  At exit, they leave the current lexeme at the first lexeme
00307    following the test.  They return the test read, or NIL if any
00308    error occurred.  (They never return a blank_test.)
00309 ================================================================= */
00310 
00311 /* -----------------------------------------------------------------
00312                       Parse Relational Test
00313                       
00314    <relational_test> ::= [<relation>] <single_test>
00315    <relation> ::= <> | < | > | <= | >= | = | <=>
00316    <single_test> ::= variable | <constant>
00317    <constant> ::= sym_constant | int_constant | float_constant
00318 ----------------------------------------------------------------- */
00319 
00320 test parse_relational_test(void)
00321 {
00322     byte test_type;
00323     bool use_equality_test;
00324     test t;
00325     Symbol *referent;
00326     complex_test *ct;
00327 
00328     use_equality_test = FALSE;
00329     test_type = NOT_EQUAL_TEST; /* unnecessary, but gcc -Wall warns without it */
00330 
00331     /* --- read optional relation symbol --- */
00332     switch (current_agent(lexeme).type) {
00333     case EQUAL_LEXEME:
00334         use_equality_test = TRUE;
00335         get_lexeme();
00336         break;
00337 
00338     case NOT_EQUAL_LEXEME:
00339         test_type = NOT_EQUAL_TEST;
00340         get_lexeme();
00341         break;
00342 
00343     case LESS_LEXEME:
00344         test_type = LESS_TEST;
00345         get_lexeme();
00346         break;
00347 
00348     case GREATER_LEXEME:
00349         test_type = GREATER_TEST;
00350         get_lexeme();
00351         break;
00352 
00353     case LESS_EQUAL_LEXEME:
00354         test_type = LESS_OR_EQUAL_TEST;
00355         get_lexeme();
00356         break;
00357 
00358     case GREATER_EQUAL_LEXEME:
00359         test_type = GREATER_OR_EQUAL_TEST;
00360         get_lexeme();
00361         break;
00362 
00363     case LESS_EQUAL_GREATER_LEXEME:
00364         test_type = SAME_TYPE_TEST;
00365         get_lexeme();
00366         break;
00367 
00368     default:
00369         use_equality_test = TRUE;
00370         break;
00371     }
00372 
00373     /* --- read variable or constant --- */
00374     switch (current_agent(lexeme).type) {
00375     case SYM_CONSTANT_LEXEME:
00376     case INT_CONSTANT_LEXEME:
00377     case FLOAT_CONSTANT_LEXEME:
00378     case VARIABLE_LEXEME:
00379         referent = make_symbol_for_current_lexeme();
00380         get_lexeme();
00381         if (use_equality_test) {
00382             t = make_equality_test_without_adding_reference(referent);
00383         } else {
00384             allocate_with_pool(&current_agent(complex_test_pool), &ct);
00385             ct->type = test_type;
00386             ct->data.referent = referent;
00387             t = make_test_from_complex_test(ct);
00388         }
00389         return t;
00390 
00391     default:
00392         print("Expected variable or constant for test\n");
00393         print_location_of_most_recent_lexeme();
00394         return NIL;
00395     }
00396 }
00397 
00398 /* -----------------------------------------------------------------
00399                       Parse Disjunction Test
00400                       
00401    <disjunction_test> ::= << <constant>* >>
00402    <constant> ::= sym_constant | int_constant | float_constant
00403 ----------------------------------------------------------------- */
00404 
00405 test parse_disjunction_test(void)
00406 {
00407     complex_test *ct;
00408     test t;
00409 
00410     if (current_agent(lexeme).type != LESS_LESS_LEXEME) {
00411         print("Expected << to begin disjunction test\n");
00412         print_location_of_most_recent_lexeme();
00413         return NIL;
00414     }
00415     get_lexeme();
00416 
00417     allocate_with_pool(&current_agent(complex_test_pool), &ct);
00418     ct->type = DISJUNCTION_TEST;
00419     ct->data.disjunction_list = NIL;
00420     t = make_test_from_complex_test(ct);
00421 
00422     while (current_agent(lexeme).type != GREATER_GREATER_LEXEME) {
00423         switch (current_agent(lexeme).type) {
00424         case SYM_CONSTANT_LEXEME:
00425         case INT_CONSTANT_LEXEME:
00426         case FLOAT_CONSTANT_LEXEME:
00427             push(make_symbol_for_current_lexeme(), ct->data.disjunction_list);
00428             get_lexeme();
00429             break;
00430         default:
00431             print("Expected constant or >> while reading disjunction test\n");
00432             print_location_of_most_recent_lexeme();
00433             deallocate_test(t);
00434             return NIL;
00435         }
00436     }
00437     get_lexeme();               /* consume the >> */
00438     ct->data.disjunction_list = destructively_reverse_list(ct->data.disjunction_list);
00439     return t;
00440 }
00441 
00442 /* -----------------------------------------------------------------
00443                         Parse Simple Test
00444                       
00445    <simple_test> ::= <disjunction_test> | <relational_test>
00446 ----------------------------------------------------------------- */
00447 
00448 test parse_simple_test(void)
00449 {
00450     if (current_agent(lexeme).type == LESS_LESS_LEXEME)
00451         return parse_disjunction_test();
00452     return parse_relational_test();
00453 }
00454 
00455 /* -----------------------------------------------------------------
00456                             Parse Test
00457                       
00458     <test> ::= <conjunctive_test> | <simple_test>
00459     <conjunctive_test> ::= { <simple_test>+ }
00460 ----------------------------------------------------------------- */
00461 
00462 test parse_test(void)
00463 {
00464     complex_test *ct;
00465     test t, temp;
00466 
00467     if (current_agent(lexeme).type != L_BRACE_LEXEME)
00468         return parse_simple_test();
00469     /* --- parse and return conjunctive test --- */
00470     get_lexeme();
00471     t = make_blank_test();
00472     do {
00473         temp = parse_simple_test();
00474         if (!temp) {
00475             deallocate_test(t);
00476             return NIL;
00477         }
00478         add_new_test_to_test(&t, temp);
00479     } while (current_agent(lexeme).type != R_BRACE_LEXEME);
00480     get_lexeme();               /* consume the "}" */
00481 
00482     if (test_is_complex_test(t)) {
00483         ct = complex_test_from_test(t);
00484         if (ct->type == CONJUNCTIVE_TEST)
00485             ct->data.conjunct_list = destructively_reverse_list(ct->data.conjunct_list);
00486     }
00487 
00488     return t;
00489 }
00490 
00491 /* =================================================================
00492                         Routines for Conditions
00493 
00494    Various routines here are used to parse and build conditions, etc.
00495    At entry, each expects the current lexeme to be at the start of whatever
00496    thing they're supposed to parse.  At exit, each leaves the current
00497    lexeme at the first lexeme following the parsed object.  Each returns
00498    a list of the conditions they parsed, or NIL if any error occurred.
00499 ================================================================= */
00500 
00501 /* -----------------------------------------------------------------
00502                           Fill In Id Tests
00503                          Fill In Attr Tests
00504 
00505    As low-level routines (e.g., parse_value_test_star) parse, they
00506    leave the id (and sometimes attribute) test fields blank (NIL) in the
00507    condition structures they return.  The calling routine must fill in
00508    the id tests and/or attribute tests.  These routines fill in any
00509    still-blank {id, attr} tests with copies of a given test.  They try
00510    to add non-equality portions of the test only once, if possible.
00511 ----------------------------------------------------------------- */
00512 
00513 void fill_in_id_tests(condition * conds, test t)
00514 {
00515     condition *positive_c, *c;
00516     test equality_test_from_t;
00517 
00518     /* --- see if there's at least one positive condition --- */
00519     for (positive_c = conds; positive_c != NIL; positive_c = positive_c->next)
00520         if ((positive_c->type == POSITIVE_CONDITION) && (positive_c->data.tests.id_test == NIL))
00521             break;
00522 
00523     if (positive_c) {           /* --- there is at least one positive condition --- */
00524         /* --- add just the equality test to most of the conditions --- */
00525         equality_test_from_t = copy_of_equality_test_found_in_test(t);
00526         for (c = conds; c != NIL; c = c->next) {
00527             if (c->type == CONJUNCTIVE_NEGATION_CONDITION)
00528                 fill_in_id_tests(c->data.ncc.top, equality_test_from_t);
00529             else if (c->data.tests.id_test == NIL)
00530                 c->data.tests.id_test = copy_test(equality_test_from_t);
00531         }
00532         deallocate_test(equality_test_from_t);
00533         /* --- add the whole test to one positive condition --- */
00534         deallocate_test(positive_c->data.tests.id_test);
00535         positive_c->data.tests.id_test = copy_test(t);
00536         return;
00537     }
00538 
00539     /* --- all conditions are negative --- */
00540     for (c = conds; c != NIL; c = c->next) {
00541         if (c->type == CONJUNCTIVE_NEGATION_CONDITION) {
00542             fill_in_id_tests(c->data.ncc.top, t);
00543         } else {
00544             if (c->data.tests.id_test == NIL)
00545                 c->data.tests.id_test = copy_test(t);
00546         }
00547     }
00548 }
00549 
00550 void fill_in_attr_tests(condition * conds, test t)
00551 {
00552     condition *positive_c, *c;
00553     test equality_test_from_t;
00554 
00555     /* --- see if there's at least one positive condition --- */
00556     for (positive_c = conds; positive_c != NIL; positive_c = positive_c->next)
00557         if ((positive_c->type == POSITIVE_CONDITION) && (positive_c->data.tests.attr_test == NIL))
00558             break;
00559 
00560     if (positive_c) {           /* --- there is at least one positive condition --- */
00561         /* --- add just the equality test to most of the conditions --- */
00562         equality_test_from_t = copy_of_equality_test_found_in_test(t);
00563         for (c = conds; c != NIL; c = c->next) {
00564             if (c->type == CONJUNCTIVE_NEGATION_CONDITION)
00565                 fill_in_attr_tests(c->data.ncc.top, equality_test_from_t);
00566             else if (c->data.tests.attr_test == NIL)
00567                 c->data.tests.attr_test = copy_test(equality_test_from_t);
00568         }
00569         deallocate_test(equality_test_from_t);
00570         /* --- add the whole test to one positive condition --- */
00571         deallocate_test(positive_c->data.tests.attr_test);
00572         positive_c->data.tests.attr_test = copy_test(t);
00573         return;
00574     }
00575 
00576     /* --- all conditions are negative --- */
00577     for (c = conds; c != NIL; c = c->next) {
00578         if (c->type == CONJUNCTIVE_NEGATION_CONDITION) {
00579             fill_in_attr_tests(c->data.ncc.top, t);
00580         } else {
00581             if (c->data.tests.attr_test == NIL)
00582                 c->data.tests.attr_test = copy_test(t);
00583         }
00584     }
00585 }
00586 
00587 /* -----------------------------------------------------------------
00588                      Negate Condition List
00589    
00590    Returns the negation of the given condition list.  If the given
00591    list is a single positive or negative condition, it just toggles
00592    the type.  If the given list is a single ncc, it strips off the ncc
00593    part and returns the subconditions.  Otherwise it makes a new ncc
00594    using the given conditions.
00595 ----------------------------------------------------------------- */
00596 
00597 condition *negate_condition_list(condition * conds)
00598 {
00599     condition *temp, *last;
00600 
00601     if (conds->next == NIL) {
00602         /* --- only one condition to negate, so toggle the type --- */
00603         switch (conds->type) {
00604         case POSITIVE_CONDITION:
00605             conds->type = NEGATIVE_CONDITION;
00606             return conds;
00607         case NEGATIVE_CONDITION:
00608             conds->type = POSITIVE_CONDITION;
00609             return conds;
00610         case CONJUNCTIVE_NEGATION_CONDITION:
00611             temp = conds->data.ncc.top;
00612             free_with_pool(&current_agent(condition_pool), conds);
00613             return temp;
00614         }
00615     }
00616     /* --- more than one condition; so build a conjunctive negation --- */
00617     allocate_with_pool(&current_agent(condition_pool), &temp);
00618     temp->type = CONJUNCTIVE_NEGATION_CONDITION;
00619     temp->next = NIL;
00620     temp->prev = NIL;
00621     temp->data.ncc.top = conds;
00622     for (last = conds; last->next != NIL; last = last->next);
00623     temp->data.ncc.bottom = last;
00624     return temp;
00625 }
00626 
00627 /* -----------------------------------------------------------------
00628                         Parse Value Test Star
00629                       
00630    <value_test> ::= <test> [+] | <conds_for_one_id> [+]
00631 
00632    (This routine parses <value_test>*, given as input the id_test and
00633    attr_test already read.)
00634 ----------------------------------------------------------------- */
00635 
00636 condition *parse_conds_for_one_id(char first_letter_if_no_id_given, test * dest_id_test);
00637 
00638 condition *parse_value_test_star(char first_letter)
00639 {
00640     condition *c, *last_c, *first_c, *new_conds;
00641     test value_test;
00642     bool acceptable;
00643 
00644     if ((current_agent(lexeme).type == MINUS_LEXEME) ||
00645         (current_agent(lexeme).type == UP_ARROW_LEXEME) || (current_agent(lexeme).type == R_PAREN_LEXEME)) {
00646         /* --- value omitted, so create dummy value test --- */
00647         allocate_with_pool(&current_agent(condition_pool), &c);
00648         c->type = POSITIVE_CONDITION;
00649         c->next = c->prev = NIL;
00650         c->data.tests.id_test = NIL;
00651         c->data.tests.attr_test = NIL;
00652         c->data.tests.value_test = make_placeholder_test(first_letter);
00653         c->test_for_acceptable_preference = FALSE;
00654         return c;
00655     }
00656 
00657     first_c = NIL;
00658     last_c = NIL;
00659     do {
00660         if (current_agent(lexeme).type == L_PAREN_LEXEME) {
00661             /* --- read <conds_for_one_id>, take the id_test from it --- */
00662             new_conds = parse_conds_for_one_id(first_letter, &value_test);
00663             if (!new_conds) {
00664                 deallocate_condition_list(first_c);
00665                 return NIL;
00666             }
00667         } else {
00668             /* --- read <value_test> --- */
00669             new_conds = NIL;
00670             value_test = parse_test();
00671             if (!value_test) {
00672                 deallocate_condition_list(first_c);
00673                 return NIL;
00674             }
00675             if (!test_includes_equality_test_for_symbol(value_test, NIL)) {
00676                 add_new_test_to_test(&value_test, make_placeholder_test(first_letter));
00677             }
00678         }
00679         /* --- check for acceptable preference indicator --- */
00680         acceptable = FALSE;
00681         if (current_agent(lexeme).type == PLUS_LEXEME) {
00682             acceptable = TRUE;
00683             get_lexeme();
00684         }
00685         /* --- build condition using the new value test --- */
00686         allocate_with_pool(&current_agent(condition_pool), &c);
00687         insert_at_head_of_dll(new_conds, c, next, prev);
00688         c->type = POSITIVE_CONDITION;
00689         c->data.tests.id_test = NIL;
00690         c->data.tests.attr_test = NIL;
00691         c->data.tests.value_test = value_test;
00692         c->test_for_acceptable_preference = acceptable;
00693         /* --- add new conditions to the end of the list --- */
00694         if (last_c)
00695             last_c->next = new_conds;
00696         else
00697             first_c = new_conds;
00698         new_conds->prev = last_c;
00699         for (last_c = new_conds; last_c->next != NIL; last_c = last_c->next);
00700     } while ((current_agent(lexeme).type != MINUS_LEXEME) &&
00701              (current_agent(lexeme).type != UP_ARROW_LEXEME) && (current_agent(lexeme).type != R_PAREN_LEXEME));
00702     return first_c;
00703 }
00704 
00705 /* -----------------------------------------------------------------
00706                       Parse Attr Value Tests
00707                       
00708    <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*
00709    <attr_test> ::= <test>
00710 
00711    (This routine parses <attr_value_tests>, given as input the id_test 
00712    already read.)
00713 ----------------------------------------------------------------- */
00714 
00715 condition *parse_attr_value_tests(void)
00716 {
00717     test id_test_to_use, attr_test;
00718     bool negate_it;
00719     condition *first_c, *last_c, *c, *new_conds;
00720 
00721     /* --- read optional minus sign --- */
00722     negate_it = FALSE;
00723     if (current_agent(lexeme).type == MINUS_LEXEME) {
00724         negate_it = TRUE;
00725         get_lexeme();
00726     }
00727 
00728     /* --- read up arrow --- */
00729     if (current_agent(lexeme).type != UP_ARROW_LEXEME) {
00730         print("Expected ^ followed by attribute\n");
00731         print_location_of_most_recent_lexeme();
00732         return NIL;
00733     }
00734     get_lexeme();
00735 
00736     first_c = NIL;
00737     last_c = NIL;
00738 
00739     /* --- read first <attr_test> --- */
00740     attr_test = parse_test();
00741     if (!attr_test)
00742         return NIL;
00743     if (!test_includes_equality_test_for_symbol(attr_test, NIL)) {
00744         add_new_test_to_test(&attr_test, make_placeholder_test('a'));
00745     }
00746 
00747     /* --- read optional attribute path --- */
00748     id_test_to_use = NIL;
00749     while (current_agent(lexeme).type == PERIOD_LEXEME) {
00750         get_lexeme();           /* consume the "." */
00751         /* --- setup for next attribute in path:  make a dummy variable,
00752            create a new condition in the path --- */
00753         allocate_with_pool(&current_agent(condition_pool), &c);
00754         c->type = POSITIVE_CONDITION;
00755         if (last_c)
00756             last_c->next = c;
00757         else
00758             first_c = c;
00759         c->next = NIL;
00760         c->prev = last_c;
00761         last_c = c;
00762         if (id_test_to_use)
00763             c->data.tests.id_test = copy_test(id_test_to_use);
00764         else
00765             c->data.tests.id_test = NIL;
00766         c->data.tests.attr_test = attr_test;
00767         id_test_to_use = make_placeholder_test(first_letter_from_test(attr_test));
00768         c->data.tests.value_test = id_test_to_use;
00769         c->test_for_acceptable_preference = FALSE;
00770         /* --- update id and attr tests for the next path element --- */
00771         attr_test = parse_test();
00772         if (!attr_test) {
00773             deallocate_condition_list(first_c);
00774             return NIL;
00775         }
00776 /* AGR 544 begin */
00777         if (!test_includes_equality_test_for_symbol(attr_test, NIL)) {
00778             add_new_test_to_test(&attr_test, make_placeholder_test('a'));
00779         }
00780 /* AGR 544 end */
00781     }                           /* end of while (current_agent(lexeme).type==PERIOD_LEXEME) */
00782 
00783     /* --- finally, do the <value_test>* part --- */
00784     new_conds = parse_value_test_star(first_letter_from_test(attr_test));
00785     if (!new_conds) {
00786         deallocate_condition_list(first_c);
00787         deallocate_test(attr_test);
00788         return NIL;
00789     }
00790     fill_in_attr_tests(new_conds, attr_test);
00791     if (id_test_to_use)
00792         fill_in_id_tests(new_conds, id_test_to_use);
00793     deallocate_test(attr_test);
00794     if (last_c)
00795         last_c->next = new_conds;
00796     else
00797         first_c = new_conds;
00798     new_conds->prev = last_c;
00799     /* should update last_c here, but it's not needed anymore */
00800 
00801     /* --- negate everything if necessary --- */
00802     if (negate_it)
00803         first_c = negate_condition_list(first_c);
00804 
00805     return first_c;
00806 }
00807 
00808 /* -----------------------------------------------------------------
00809                     Parse Head Of Conds For One Id
00810                       
00811    <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
00812    <id_test> ::= <test>
00813 
00814    This routine parses the "( [state|impasse] [<id_test>]" part of
00815    <conds_for_one_id> and returns the resulting id_test (or NIL if
00816    any error occurs).
00817 ----------------------------------------------------------------- */
00818 
00819 test parse_head_of_conds_for_one_id(char first_letter_if_no_id_given)
00820 {
00821     test id_test, id_goal_impasse_test, check_for_symconstant;
00822     complex_test *ct;
00823     Symbol *sym;
00824 
00825     if (current_agent(lexeme).type != L_PAREN_LEXEME) {
00826         print("Expected ( to begin condition element\n");
00827         print_location_of_most_recent_lexeme();
00828         return NIL;
00829     }
00830     get_lexeme();
00831 
00832     /* --- look for goal/impasse indicator --- */
00833     if (current_agent(lexeme).type == SYM_CONSTANT_LEXEME) {
00834         if (!strcmp(current_agent(lexeme).string, "state")) {
00835             allocate_with_pool(&current_agent(complex_test_pool), &ct);
00836             ct->type = GOAL_ID_TEST;
00837             id_goal_impasse_test = make_test_from_complex_test(ct);
00838             get_lexeme();
00839             first_letter_if_no_id_given = 's';
00840         } else if (!strcmp(current_agent(lexeme).string, "impasse")) {
00841             allocate_with_pool(&current_agent(complex_test_pool), &ct);
00842             ct->type = IMPASSE_ID_TEST;
00843             id_goal_impasse_test = make_test_from_complex_test(ct);
00844             get_lexeme();
00845             first_letter_if_no_id_given = 'i';
00846         } else {
00847             id_goal_impasse_test = make_blank_test();
00848         }
00849     } else {
00850         id_goal_impasse_test = make_blank_test();
00851     }
00852 
00853     /* --- read optional id test; create dummy one if none given --- */
00854     if ((current_agent(lexeme).type != MINUS_LEXEME) &&
00855         (current_agent(lexeme).type != UP_ARROW_LEXEME) && (current_agent(lexeme).type != R_PAREN_LEXEME)) {
00856         id_test = parse_test();
00857         if (!id_test) {
00858             deallocate_test(id_goal_impasse_test);
00859             return NIL;
00860         }
00861         if (!test_includes_equality_test_for_symbol(id_test, NIL)) {
00862             add_new_test_to_test(&id_test, make_placeholder_test(first_letter_if_no_id_given));
00863         } else {
00864             check_for_symconstant = copy_of_equality_test_found_in_test(id_test);
00865             sym = referent_of_equality_test(check_for_symconstant);
00866             deallocate_test(check_for_symconstant);     /* RBD added 3/28/95 */
00867             if (sym->common.symbol_type != VARIABLE_SYMBOL_TYPE) {
00868                 print_with_symbols("Warning: Constant %y in id field test.\n", sym);
00869                 print("         This will never match.\n");
00870                 print_location_of_most_recent_lexeme();
00871                 deallocate_test(id_test);       /* AGR 527c */
00872                 return NIL;     /* AGR 527c */
00873             }
00874         }
00875     } else {
00876         id_test = make_placeholder_test(first_letter_if_no_id_given);
00877     }
00878 
00879     /* --- add the goal/impasse test to the id test --- */
00880     add_new_test_to_test(&id_test, id_goal_impasse_test);
00881 
00882     /* --- return the resulting id test --- */
00883     return id_test;
00884 }
00885 
00886 /* -----------------------------------------------------------------
00887                     Parse Tail Of Conds For One Id
00888                       
00889    <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
00890    <id_test> ::= <test>
00891 
00892    This routine parses the "<attr_value_tests>* )" part of <conds_for_one_id>
00893    and returns the resulting conditions (or NIL if any error occurs).
00894    It does not fill in the id tests of the conditions.
00895 ----------------------------------------------------------------- */
00896 
00897 condition *parse_tail_of_conds_for_one_id(void)
00898 {
00899     condition *first_c, *last_c, *new_conds;
00900     condition *c;
00901 
00902     /* --- if no <attr_value_tests> are given, create a dummy one --- */
00903     if (current_agent(lexeme).type == R_PAREN_LEXEME) {
00904         get_lexeme();           /* consume the right parenthesis */
00905         allocate_with_pool(&current_agent(condition_pool), &c);
00906         c->type = POSITIVE_CONDITION;
00907         c->next = NIL;
00908         c->prev = NIL;
00909         c->data.tests.id_test = NIL;
00910         c->data.tests.attr_test = make_placeholder_test('a');
00911         c->data.tests.value_test = make_placeholder_test('v');
00912         c->test_for_acceptable_preference = FALSE;
00913         return c;
00914     }
00915 
00916     /* --- read <attr_value_tests>* --- */
00917     first_c = NIL;
00918     last_c = NIL;
00919     while (current_agent(lexeme).type != R_PAREN_LEXEME) {
00920         new_conds = parse_attr_value_tests();
00921         if (!new_conds) {
00922             deallocate_condition_list(first_c);
00923             return NIL;
00924         }
00925         if (last_c)
00926             last_c->next = new_conds;
00927         else
00928             first_c = new_conds;
00929         new_conds->prev = last_c;
00930         for (last_c = new_conds; last_c->next != NIL; last_c = last_c->next);
00931     }
00932 
00933     /* --- reached the end of the condition --- */
00934     get_lexeme();               /* consume the right parenthesis */
00935 
00936     return first_c;
00937 }
00938 
00939 /* -----------------------------------------------------------------
00940                       Parse Conds For One Id
00941                       
00942    <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
00943    <id_test> ::= <test>
00944 
00945    This routine parses <conds_for_one_id> and returns the conditions (of
00946    NIL if any error occurs).
00947 
00948    If the argument dest_id_test is non-NULL, then *dest_id_test is set
00949    to the resulting complete id test (which includes any goal/impasse test),
00950    and in all the conditions the id field is filled in with just the
00951    equality portion of id_test.
00952 
00953    If the argument dest_id_test is NULL, then the complete id_test is
00954    included in the conditions.
00955 ----------------------------------------------------------------- */
00956 
00957 condition *parse_conds_for_one_id(char first_letter_if_no_id_given, test * dest_id_test)
00958 {
00959     condition *conds;
00960     test id_test, equality_test_from_id_test;
00961 
00962     /* --- parse the head --- */
00963     id_test = parse_head_of_conds_for_one_id(first_letter_if_no_id_given);
00964     if (!id_test)
00965         return NIL;
00966 
00967     /* --- parse the tail --- */
00968     conds = parse_tail_of_conds_for_one_id();
00969     if (!conds) {
00970         deallocate_test(id_test);
00971         return NIL;
00972     }
00973 
00974     /* --- fill in the id test in all the conditions just read --- */
00975     if (dest_id_test) {
00976         *dest_id_test = id_test;
00977         equality_test_from_id_test = copy_of_equality_test_found_in_test(id_test);
00978         fill_in_id_tests(conds, equality_test_from_id_test);
00979         deallocate_test(equality_test_from_id_test);
00980     } else {
00981         fill_in_id_tests(conds, id_test);
00982         deallocate_test(id_test);
00983     }
00984 
00985     return conds;
00986 }
00987 
00988 /* -----------------------------------------------------------------
00989                             Parse Cond
00990                       
00991    <cond> ::= <positive_cond> | - <positive_cond>
00992    <positive_cond> ::= <conds_for_one_id> | { <cond>+ }
00993 ----------------------------------------------------------------- */
00994 
00995 condition *parse_cond_plus(void);
00996 
00997 condition *parse_cond(void)
00998 {
00999     condition *c;
01000     bool negate_it;
01001 
01002     /* --- look for leading "-" sign --- */
01003     negate_it = FALSE;
01004     if (current_agent(lexeme).type == MINUS_LEXEME) {
01005         negate_it = TRUE;
01006         get_lexeme();
01007     }
01008 
01009     /* --- parse <positive_cond> --- */
01010     if (current_agent(lexeme).type == L_BRACE_LEXEME) {
01011         /* --- read conjunctive condition --- */
01012         get_lexeme();
01013         c = parse_cond_plus();
01014         if (!c)
01015             return NIL;
01016         if (current_agent(lexeme).type != R_BRACE_LEXEME) {
01017             print("Expected } to end conjunctive condition\n");
01018             print_location_of_most_recent_lexeme();
01019             deallocate_condition_list(c);
01020             return NIL;
01021         }
01022         get_lexeme();           /* consume the R_BRACE */
01023     } else {
01024         /* --- read conds for one id --- */
01025         c = parse_conds_for_one_id('s', NULL);
01026         if (!c)
01027             return NIL;
01028     }
01029 
01030     /* --- if necessary, handle the negation --- */
01031     if (negate_it)
01032         c = negate_condition_list(c);
01033 
01034     return c;
01035 }
01036 
01037 /* -----------------------------------------------------------------
01038                             Parse Cond Plus
01039                       
01040    (Parses <cond>+ and builds a condition list.)
01041 ----------------------------------------------------------------- */
01042 
01043 condition *parse_cond_plus(void)
01044 {
01045     condition *first_c, *last_c, *new_conds;
01046 
01047     first_c = NIL;
01048     last_c = NIL;
01049     do {
01050         /* --- get individual <cond> --- */
01051         new_conds = parse_cond();
01052         if (!new_conds) {
01053             deallocate_condition_list(first_c);
01054             return NIL;
01055         }
01056         if (last_c)
01057             last_c->next = new_conds;
01058         else
01059             first_c = new_conds;
01060         new_conds->prev = last_c;
01061         for (last_c = new_conds; last_c->next != NIL; last_c = last_c->next);
01062     } while ((current_agent(lexeme).type == MINUS_LEXEME) ||
01063              (current_agent(lexeme).type == L_PAREN_LEXEME) || (current_agent(lexeme).type == L_BRACE_LEXEME));
01064     return first_c;
01065 }
01066 
01067 /* -----------------------------------------------------------------
01068                             Parse LHS
01069                       
01070    (Parses <lhs> and builds a condition list.)
01071 
01072    <lhs> ::= <cond>+
01073 ----------------------------------------------------------------- */
01074 
01075 condition *parse_lhs(void)
01076 {
01077     condition *c;
01078 
01079     c = parse_cond_plus();
01080     if (!c)
01081         return NIL;
01082     return c;
01083 }
01084 
01085 /* =================================================================
01086 
01087                         Routines for Actions
01088 
01089    The following routines are used to parse and build actions, etc.
01090    Except as otherwise noted, at entry each routine expects the
01091    current lexeme to be at the start of whatever thing it's supposed
01092    to parse.  At exit, each leaves the current lexeme at the first
01093    lexeme following the parsed object.
01094 ================================================================= */
01095 
01096 /* =====================================================================
01097 
01098    Grammar for right hand sides of productions
01099 
01100    <rhs> ::= <rhs_action>*
01101    <rhs_action> ::= ( variable <attr_value_make>+ ) | <function_call>
01102    <function_call> ::= ( <function_name> <rhs_value>* )
01103    <function_name> ::= sym_constant | + | -
01104      (WARNING: might need others besides +, - here if the lexer changes)
01105    <rhs_value> ::= <constant> | <function_call> | variable
01106    <constant> ::= sym_constant | int_constant | float_constant
01107    <attr_value_make> ::= ^ <rhs_value> <value_make>+
01108    <value_make> ::= <rhs_value> <preferences>
01109 
01110    <preferences> ::= [,] | <preference_specifier>+   
01111    <preference-specifier> ::= <naturally-unary-preference> [,]
01112                             | <forced-unary-preference>
01113                             | <binary-preference> <rhs_value> [,]
01114    <naturally-unary-preference> ::= + | - | ! | ~ | @
01115    <binary-preference> ::= > | = | < | &
01116    <any-preference> ::= <naturally-unary-preference> | <binary-preference>
01117    <forced-unary-preference> ::= <binary-preference> 
01118                                  {<any-preference> | , | ) | ^}  
01119      ;but the parser shouldn't consume the <any-preference>, ")" or "^" 
01120       lexeme here
01121 ===================================================================== */
01122 
01123 char *help_on_rhs_grammar[] = {
01124     "Grammar for right hand sides of productions:",
01125     "",
01126     "   <rhs> ::= <rhs_action>*",
01127     "   <rhs_action> ::= ( variable <attr_value_make>+ ) | <function_call>",
01128     "   <function_call> ::= ( <function_name> <rhs_value>* )",
01129     "   <function_name> ::= sym_constant | + | -",
01130     "   <rhs_value> ::= <constant> | <function_call> | variable",
01131     "   <constant> ::= sym_constant | int_constant | float_constant",
01132     "   <attr_value_make> ::= ^ <rhs_value> <value_make>+",
01133     "   <value_make> ::= <rhs_value> <preferences>",
01134     "",
01135     "   <preferences> ::= [,] | <preference_specifier>+",
01136     "   <preference-specifier> ::= <naturally-unary-preference> [,]",
01137     "                            | <forced-unary-preference>",
01138     "                            | <binary-preference> <rhs_value> [,]",
01139     "   <naturally-unary-preference> ::= + | - | ! | ~ | @",
01140     "   <binary-preference> ::= > | = | < | &",
01141     "   <any-preference> ::= <naturally-unary-preference> | <binary-preference>",
01142     "   <forced-unary-preference> ::= <binary-preference> ",
01143     "                                 {<any-preference> | , | ) | ^}",
01144     "     ;but the parser shouldn't consume the <any-preference>, \")\" or \"^\"",
01145     "      lexeme here",
01146     "",
01147     "See also:  lhs-grammar, sp",
01148     0
01149 };
01150 
01151 /* -----------------------------------------------------------------
01152                  Parse Function Call After Lparen
01153 
01154    Parses a <function_call> after the "(" has already been consumed.
01155    At entry, the current lexeme should be the function name.  Returns
01156    an rhs_value, or NIL if any error occurred.
01157 
01158    <function_call> ::= ( <function_name> <rhs_value>* )
01159    <function_name> ::= sym_constant | + | -
01160      (Warning: might need others besides +, - here if the lexer changes)
01161 ----------------------------------------------------------------- */
01162 
01163 rhs_value parse_rhs_value(void);
01164 
01165 rhs_value parse_function_call_after_lparen(bool is_stand_alone_action)
01166 {
01167     rhs_function *rf;
01168     Symbol *fun_name;
01169     list *fl;
01170     cons *c, *prev_c;
01171     rhs_value arg_rv;
01172     int num_args;
01173 
01174     /* --- read function name, find the rhs_function structure --- */
01175     if (current_agent(lexeme).type == PLUS_LEXEME)
01176         fun_name = find_sym_constant("+");
01177     else if (current_agent(lexeme).type == MINUS_LEXEME)
01178         fun_name = find_sym_constant("-");
01179     else
01180         fun_name = find_sym_constant(current_agent(lexeme).string);
01181     if (!fun_name) {
01182         print("No RHS function named %s\n", current_agent(lexeme).string);
01183         print_location_of_most_recent_lexeme();
01184         return NIL;
01185     }
01186     rf = lookup_rhs_function(fun_name);
01187     if (!rf) {
01188         print("No RHS function named %s\n", current_agent(lexeme).string);
01189         print_location_of_most_recent_lexeme();
01190         return NIL;
01191     }
01192 
01193     /* --- make sure stand-alone/rhs_value is appropriate --- */
01194     if (is_stand_alone_action && (!rf->can_be_stand_alone_action)) {
01195         print("Function %s cannot be used as a stand-alone action\n", current_agent(lexeme).string);
01196         print_location_of_most_recent_lexeme();
01197         return NIL;
01198     }
01199     if ((!is_stand_alone_action) && (!rf->can_be_rhs_value)) {
01200         print("Function %s can only be used as a stand-alone action\n", current_agent(lexeme).string);
01201         print_location_of_most_recent_lexeme();
01202         return NIL;
01203     }
01204 
01205     /* --- build list of rhs_function and arguments --- */
01206     allocate_cons(&fl);
01207     fl->first = rf;
01208     prev_c = fl;
01209     get_lexeme();               /* consume function name, advance to argument list */
01210     num_args = 0;
01211     while (current_agent(lexeme).type != R_PAREN_LEXEME) {
01212         arg_rv = parse_rhs_value();
01213         if (!arg_rv) {
01214             prev_c->rest = NIL;
01215             deallocate_rhs_value(funcall_list_to_rhs_value(fl));
01216             return NIL;
01217         }
01218         num_args++;
01219         allocate_cons(&c);
01220         c->first = arg_rv;
01221         prev_c->rest = c;
01222         prev_c = c;
01223     }
01224     prev_c->rest = NIL;
01225 
01226     /* --- check number of arguments --- */
01227     if ((rf->num_args_expected != -1) && (rf->num_args_expected != num_args)) {
01228         print("Wrong number of arguments to function %s (expected %d)\n", rf->name->sc.name, rf->num_args_expected);
01229         print_location_of_most_recent_lexeme();
01230         deallocate_rhs_value(funcall_list_to_rhs_value(fl));
01231         return NIL;
01232     }
01233 
01234     get_lexeme();               /* consume the right parenthesis */
01235     return funcall_list_to_rhs_value(fl);
01236 }
01237 
01238 /* -----------------------------------------------------------------
01239                           Parse RHS Value
01240 
01241    Parses an <rhs_value>.  Returns an rhs_value, or NIL if any error
01242    occurred.
01243 
01244    <rhs_value> ::= <constant> | <function_call> | variable
01245    <constant> ::= sym_constant | int_constant | float_constant
01246 ----------------------------------------------------------------- */
01247 
01248 rhs_value parse_rhs_value(void)
01249 {
01250     rhs_value rv;
01251 
01252     if (current_agent(lexeme).type == L_PAREN_LEXEME) {
01253         get_lexeme();
01254         return parse_function_call_after_lparen(FALSE);
01255     }
01256     if ((current_agent(lexeme).type == SYM_CONSTANT_LEXEME) ||
01257         (current_agent(lexeme).type == INT_CONSTANT_LEXEME) ||
01258         (current_agent(lexeme).type == FLOAT_CONSTANT_LEXEME) || (current_agent(lexeme).type == VARIABLE_LEXEME)) {
01259         rv = symbol_to_rhs_value(make_symbol_for_current_lexeme());
01260         get_lexeme();
01261         return rv;
01262     }
01263     print("Illegal value for RHS value\n");
01264     print_location_of_most_recent_lexeme();
01265     return FALSE;
01266 }
01267 
01268 /* -----------------------------------------------------------------
01269                          Is Preference Lexeme
01270 
01271    Given a token type, returns TRUE if the token is a preference
01272    lexeme.
01273 
01274 ----------------------------------------------------------------- */
01275 
01276 bool is_preference_lexeme(enum lexer_token_type test_lexeme)
01277 {
01278     switch (test_lexeme) {
01279 
01280     case PLUS_LEXEME:
01281         return TRUE;
01282     case MINUS_LEXEME:
01283         return TRUE;
01284     case EXCLAMATION_POINT_LEXEME:
01285         return TRUE;
01286     case TILDE_LEXEME:
01287         return TRUE;
01288     case AT_LEXEME:
01289         return TRUE;
01290     case GREATER_LEXEME:
01291         return TRUE;
01292     case EQUAL_LEXEME:
01293         return TRUE;
01294     case LESS_LEXEME:
01295         return TRUE;
01296     case AMPERSAND_LEXEME:
01297         return TRUE;
01298     default:
01299         return FALSE;
01300     }
01301 }
01302 
01303 /* -----------------------------------------------------------------
01304                Parse Preference Specifier Without Referent
01305 
01306    Parses a <preference-specifier>.  Returns the appropriate 
01307    xxx_PREFERENCE_TYPE (see soarkernel.h).
01308 
01309    Note:  in addition to the grammar below, if there is no preference
01310    specifier given, then this routine returns ACCEPTABLE_PREFERENCE_TYPE.
01311    Also, for <binary-preference>'s, this routine *does not* read the
01312    <rhs_value> referent.  This must be done by the caller routine.
01313 
01314    <preference-specifier> ::= <naturally-unary-preference> [,]
01315                             | <forced-unary-preference>
01316                             | <binary-preference> <rhs_value> [,]
01317    <naturally-unary-preference> ::= + | - | ! | ~ | @
01318    <binary-preference> ::= > | = | < | &
01319    <any-preference> ::= <naturally-unary-preference> | <binary-preference>
01320    <forced-unary-preference> ::= <binary-preference> 
01321                                  {<any-preference> | , | ) | ^}  
01322      ;but the parser shouldn't consume the <any-preference>, ")" or "^" 
01323       lexeme here
01324 ----------------------------------------------------------------- */
01325 
01326 byte parse_preference_specifier_without_referent(void)
01327 {
01328     switch (current_agent(lexeme).type) {
01329 
01330     case PLUS_LEXEME:
01331         get_lexeme();
01332         if (current_agent(lexeme).type == COMMA_LEXEME)
01333             get_lexeme();
01334         return ACCEPTABLE_PREFERENCE_TYPE;
01335 
01336     case MINUS_LEXEME:
01337         get_lexeme();
01338         if (current_agent(lexeme).type == COMMA_LEXEME)
01339             get_lexeme();
01340         return REJECT_PREFERENCE_TYPE;
01341 
01342     case EXCLAMATION_POINT_LEXEME:
01343         get_lexeme();
01344         if (current_agent(lexeme).type == COMMA_LEXEME)
01345             get_lexeme();
01346         return REQUIRE_PREFERENCE_TYPE;
01347 
01348     case TILDE_LEXEME:
01349         get_lexeme();
01350         if (current_agent(lexeme).type == COMMA_LEXEME)
01351             get_lexeme();
01352         return PROHIBIT_PREFERENCE_TYPE;
01353 
01354     case AT_LEXEME:
01355         get_lexeme();
01356         if (current_agent(lexeme).type == COMMA_LEXEME)
01357             get_lexeme();
01358         return RECONSIDER_PREFERENCE_TYPE;
01359 
01360 /****************************************************************************
01361  * [Soar-Bugs #55] <forced-unary-preference> ::= <binary-preference> 
01362  *                                             {<any-preference> | , | ) | ^} 
01363  *
01364  *   Forced unary preferences can now occur when a binary preference is
01365  *   followed by a ",", ")", "^" or any preference specifier
01366  ****************************************************************************/
01367 
01368     case GREATER_LEXEME:
01369         get_lexeme();
01370         if ((current_agent(lexeme).type != COMMA_LEXEME) &&
01371             (current_agent(lexeme).type != R_PAREN_LEXEME) &&
01372             (current_agent(lexeme).type != UP_ARROW_LEXEME) && (!is_preference_lexeme(current_agent(lexeme).type)))
01373             return BETTER_PREFERENCE_TYPE;
01374         /* --- forced unary preference --- */
01375         if (current_agent(lexeme).type == COMMA_LEXEME)
01376             get_lexeme();
01377         return BEST_PREFERENCE_TYPE;
01378 
01379     case EQUAL_LEXEME:
01380         get_lexeme();
01381         if ((current_agent(lexeme).type != COMMA_LEXEME) &&
01382             (current_agent(lexeme).type != R_PAREN_LEXEME) &&
01383             (current_agent(lexeme).type != UP_ARROW_LEXEME) && (!is_preference_lexeme(current_agent(lexeme).type)))
01384             return BINARY_INDIFFERENT_PREFERENCE_TYPE;
01385         /* --- forced unary preference --- */
01386         if (current_agent(lexeme).type == COMMA_LEXEME)
01387             get_lexeme();
01388         return UNARY_INDIFFERENT_PREFERENCE_TYPE;
01389 
01390     case LESS_LEXEME:
01391         get_lexeme();
01392         if ((current_agent(lexeme).type != COMMA_LEXEME) &&
01393             (current_agent(lexeme).type != R_PAREN_LEXEME) &&
01394             (current_agent(lexeme).type != UP_ARROW_LEXEME) && (!is_preference_lexeme(current_agent(lexeme).type)))
01395             return WORSE_PREFERENCE_TYPE;
01396         /* --- forced unary preference --- */
01397         if (current_agent(lexeme).type == COMMA_LEXEME)
01398             get_lexeme();
01399         return WORST_PREFERENCE_TYPE;
01400 
01401     case AMPERSAND_LEXEME:
01402         get_lexeme();
01403         if ((current_agent(lexeme).type != COMMA_LEXEME) &&
01404             (current_agent(lexeme).type != R_PAREN_LEXEME) &&
01405             (current_agent(lexeme).type != UP_ARROW_LEXEME) && (!is_preference_lexeme(current_agent(lexeme).type)))
01406             return BINARY_PARALLEL_PREFERENCE_TYPE;
01407         /* --- forced unary preference --- */
01408         if (current_agent(lexeme).type == COMMA_LEXEME)
01409             get_lexeme();
01410         return UNARY_PARALLEL_PREFERENCE_TYPE;
01411 
01412     default:
01413         /* --- if no preference given, make it an acceptable preference --- */
01414         return ACCEPTABLE_PREFERENCE_TYPE;
01415     }                           /* end of switch statement */
01416 }
01417 
01418 /* -----------------------------------------------------------------
01419                          Parse Preferences
01420 
01421    Given the id, attribute, and value already read, this routine
01422    parses zero or more <preference-specifier>'s.  It builds and
01423    returns an action list for these RHS make's.  It returns NIL if
01424    any error occurred.
01425 
01426    <value_make> ::= <rhs_value> <preferences>
01427    <preferences> ::= [,] | <preference_specifier>+   
01428    <preference-specifier> ::= <naturally-unary-preference> [,]
01429                             | <forced-unary-preference>
01430                             | <binary-preference> <rhs_value> [,]
01431 ----------------------------------------------------------------- */
01432 
01433 action *parse_preferences(Symbol * id, rhs_value attr, rhs_value value)
01434 {
01435     action *a;
01436     action *prev_a;
01437     rhs_value referent;
01438     byte preference_type;
01439     bool saw_plus_sign;
01440 
01441     /* --- Note: this routine is set up so if there's not preference type
01442        indicator at all, we return a single acceptable preference make --- */
01443 
01444     prev_a = NIL;
01445 
01446     saw_plus_sign = (bool) (current_agent(lexeme).type == PLUS_LEXEME);
01447     preference_type = parse_preference_specifier_without_referent();
01448     if ((preference_type == ACCEPTABLE_PREFERENCE_TYPE) && (!saw_plus_sign)) {
01449         /* If the routine gave us a + pref without seeing a + sign, then it's
01450            just giving us the default acceptable preference.  Look for optional
01451            comma. */
01452         if (current_agent(lexeme).type == COMMA_LEXEME)
01453             get_lexeme();
01454     }
01455 
01456     for (;;) {
01457         /* --- read referent --- */
01458         if (preference_is_binary(preference_type)) {
01459             referent = parse_rhs_value();
01460             if (!referent) {
01461                 deallocate_action_list(prev_a);
01462                 return NIL;
01463             }
01464             if (current_agent(lexeme).type == COMMA_LEXEME)
01465                 get_lexeme();
01466         } else {
01467             referent = NIL;     /* unnecessary, but gcc -Wall warns without it */
01468         }
01469 
01470         /* --- create the appropriate action --- */
01471         allocate_with_pool(&current_agent(action_pool), &a);
01472         a->next = prev_a;
01473         prev_a = a;
01474         a->type = MAKE_ACTION;
01475         a->preference_type = preference_type;
01476         a->id = symbol_to_rhs_value(id);
01477         symbol_add_ref(id);
01478         a->attr = copy_rhs_value(attr);
01479         a->value = copy_rhs_value(value);
01480         if (preference_is_binary(preference_type))
01481             a->referent = referent;
01482 
01483         /* --- look for another preference type specifier --- */
01484         saw_plus_sign = (bool) (current_agent(lexeme).type == PLUS_LEXEME);
01485         preference_type = parse_preference_specifier_without_referent();
01486 
01487         /* --- exit loop when done reading preferences --- */
01488         if ((preference_type == ACCEPTABLE_PREFERENCE_TYPE) && (!saw_plus_sign))
01489             /* If the routine gave us a + pref without seeing a + sign, then it's
01490                just giving us the default acceptable preference, it didn't see any
01491                more preferences specified. */
01492             return prev_a;
01493     }
01494 }
01495 
01496 /* KJC begin:  10.09.98 */
01497 /* modified 3.99 to take out parallels and only create acceptables */
01498 /* -----------------------------------------------------------------
01499               Parse Preferences for Soar8 Non-Operators
01500 
01501    Given the id, attribute, and value already read, this routine
01502    parses zero or more <preference-specifier>'s.  If preferences
01503    other than reject and acceptable are specified, it prints
01504    a warning message that they are being ignored.  It builds an
01505    action list for creating an ACCEPTABLE preference.  If binary 
01506    preferences are encountered, a warning message is printed and 
01507    the production is ignored (returns NIL).  It returns NIL if any 
01508    other error occurred.  This works in conjunction with the code
01509    that supports attribute_preferences_mode == 2.  Anywhere that
01510    attribute_preferences_mode == 2 is tested, the code now tests
01511    for operand2_mode == TRUE.
01512 
01513    <value_make> ::= <rhs_value> <preferences>
01514    <preferences> ::= [,] | <preference_specifier>+   
01515    <preference-specifier> ::= <naturally-unary-preference> [,]
01516                             | <forced-unary-preference>
01517                             | <binary-preference> <rhs_value> [,]
01518 ----------------------------------------------------------------- */
01519 
01520 action *parse_preferences_soar8_non_operator(Symbol * id, rhs_value attr, rhs_value value)
01521 {
01522     action *a;
01523     action *prev_a;
01524     rhs_value referent;
01525     byte preference_type;
01526     bool saw_plus_sign;
01527 
01528     /* --- Note: this routine is set up so if there's not preference type
01529        indicator at all, we return an acceptable preference make
01530        and a parallel preference make.  For non-operators, allow
01531        only REJECT_PREFERENCE_TYPE, (and UNARY_PARALLEL and ACCEPTABLE).
01532        If any other preference type indicator is found, a warning or
01533        error msg (error only on binary prefs) is printed. --- */
01534 
01535     prev_a = NIL;
01536 
01537     saw_plus_sign = (bool) (current_agent(lexeme).type == PLUS_LEXEME);
01538     preference_type = parse_preference_specifier_without_referent();
01539     if ((preference_type == ACCEPTABLE_PREFERENCE_TYPE) && (!saw_plus_sign)) {
01540         /* If the routine gave us a + pref without seeing a + sign, then it's
01541            just giving us the default acceptable preference.  Look for optional
01542            comma. */
01543         if (current_agent(lexeme).type == COMMA_LEXEME)
01544             get_lexeme();
01545     }
01546 
01547     for (;;) {
01548         /* step through the pref list, print warning messages when necessary. */
01549 
01550         /* --- read referent --- */
01551         if (preference_is_binary(preference_type)) {
01552             print("\nERROR: in Soar8, binary preference illegal for non-operator.");
01553             print_with_symbols("\n id = %y\t attr = %y\t value = %y\n",
01554                                id, rhs_value_to_symbol(attr), rhs_value_to_symbol(value));
01555             deallocate_action_list(prev_a);
01556             return NIL;
01557         } else {
01558             referent = NIL;     /* unnecessary, but gcc -Wall warns without it */
01559         }
01560 
01561         if ((preference_type != ACCEPTABLE_PREFERENCE_TYPE) && (preference_type != REJECT_PREFERENCE_TYPE)) {
01562             print
01563                 ("\nWARNING: in Soar8, the only allowable non-operator preference \nis REJECT - .\nIgnoring specified preferences.\n");
01564             print_with_symbols("id = %y\t attr = %y\t value = %y\n", id, rhs_value_to_symbol(attr),
01565                                rhs_value_to_symbol(value));
01566             print_location_of_most_recent_lexeme();
01567         }
01568 
01569         if (preference_type == REJECT_PREFERENCE_TYPE) {
01570             /* --- create the appropriate action --- */
01571             allocate_with_pool(&current_agent(action_pool), &a);
01572             a->next = prev_a;
01573             prev_a = a;
01574             a->type = MAKE_ACTION;
01575             a->preference_type = preference_type;
01576             a->id = symbol_to_rhs_value(id);
01577             symbol_add_ref(id);
01578             a->attr = copy_rhs_value(attr);
01579             a->value = copy_rhs_value(value);
01580         }
01581 
01582         /* --- look for another preference type specifier --- */
01583         saw_plus_sign = (bool) (current_agent(lexeme).type == PLUS_LEXEME);
01584         preference_type = parse_preference_specifier_without_referent();
01585 
01586         /* --- exit loop when done reading preferences --- */
01587         if ((preference_type == ACCEPTABLE_PREFERENCE_TYPE) && (!saw_plus_sign)) {
01588             /* If the routine gave us a + pref without seeing a + sign, then it's
01589                just giving us the default acceptable preference, it didn't see any
01590                more preferences specified. */
01591 
01592             /* for soar8, if this wasn't a REJECT preference, then
01593                create acceptable preference makes.  */
01594             if (prev_a == NIL) {
01595 
01596                 allocate_with_pool(&current_agent(action_pool), &a);
01597                 a->next = prev_a;
01598                 prev_a = a;
01599                 a->type = MAKE_ACTION;
01600                 a->preference_type = ACCEPTABLE_PREFERENCE_TYPE;
01601                 a->id = symbol_to_rhs_value(id);
01602                 symbol_add_ref(id);
01603                 a->attr = copy_rhs_value(attr);
01604                 a->value = copy_rhs_value(value);
01605             }
01606             return prev_a;
01607         }
01608     }
01609 }
01610 
01611 /* KJC end:  10.09.98 */
01612 
01613 /* -----------------------------------------------------------------
01614                       Parse Attr Value Make
01615 
01616    Given the id already read, this routine parses an <attr_value_make>.
01617    It builds and returns an action list for these RHS make's.  It
01618    returns NIL if any error occurred.
01619 
01620    <attr_value_make> ::= ^ <rhs_value> <value_make>+
01621    <value_make> ::= <rhs_value> <preferences>
01622 ----------------------------------------------------------------- */
01623 
01624 action *parse_attr_value_make(Symbol * id)
01625 {
01626     rhs_value attr, value;
01627     action *all_actions, *new_actions, *last;
01628     Symbol *old_id, *new_var;
01629     char namebuf[NAMEBUF_SIZE], first_letter;
01630 
01631     if (current_agent(lexeme).type != UP_ARROW_LEXEME) {
01632         print("Expected ^ in RHS make action\n");
01633         print_location_of_most_recent_lexeme();
01634         return NIL;
01635     }
01636     old_id = id;
01637 
01638     get_lexeme();               /* consume up-arrow, advance to attribute */
01639     attr = parse_rhs_value();
01640     if (!attr)
01641         return NIL;
01642     all_actions = NIL;
01643 
01644     /*  allow dot notation "." in RHS attribute path  10/15/98 KJC */
01645     while (current_agent(lexeme).type == PERIOD_LEXEME) {
01646         get_lexeme();           /* consume the "."  */
01647         /* set up for next attribute in path: make dummy variable,
01648            and create new action in the path */
01649 
01650         /* --- create variable with "#" in its name:  this couldn't possibly be a
01651            variable in the user's code, since the lexer doesn't handle "#" --- */
01652         /* KJC used same format so could steal code... */
01653         first_letter = first_letter_from_rhs_value(attr);
01654         snprintf(namebuf, NAMEBUF_SIZE, "<#%c*%lu>", first_letter, placeholder_counter[first_letter - 'a']++);
01655         namebuf[NAMEBUF_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
01656         new_var = make_variable(namebuf);
01657         /* --- indicate that there is no corresponding "real" variable yet --- */
01658         new_var->var.current_binding_value = NIL;
01659 
01660         /* parse_preferences actually creates the action.  eventhough
01661            there aren't really any preferences to read, we need the default
01662            acceptable and parallel prefs created for all attributes in path */
01663 #ifndef SOAR_8_ONLY
01664         if (current_agent(operand2_mode) &&
01665 #else
01666         if (
01667 #endif
01668                (strcmp(rhs_value_to_string(attr, NULL, 0), "operator") != 0)) {
01669             new_actions = parse_preferences_soar8_non_operator(id, attr, symbol_to_rhs_value(new_var));
01670         } else {
01671             new_actions = parse_preferences(id, attr, symbol_to_rhs_value(new_var));
01672         }
01673         for (last = new_actions; last->next != NIL; last = last->next);
01674         last->next = all_actions;
01675         all_actions = new_actions;
01676 
01677         /* if there was a "." then there must be another attribute
01678            set id for next action and get the next attribute */
01679         id = new_var;
01680         attr = parse_rhs_value();
01681         if (!attr)
01682             return NIL;
01683 
01684     }                           /* end of while (current_agent(lexeme).type == PERIOD_LEXEME */
01685     /* end KJC 10/15/98 */
01686 
01687     do {
01688         value = parse_rhs_value();
01689         if (!value) {
01690             deallocate_rhs_value(attr);
01691             deallocate_action_list(all_actions);
01692             return NIL;
01693         }
01694 #ifndef SOAR_8_ONLY
01695         if (current_agent(operand2_mode) &&
01696 #else
01697         if (
01698 #endif
01699                (strcmp(rhs_value_to_string(attr, NULL, 0), "operator") != 0)) {
01700             new_actions = parse_preferences_soar8_non_operator(id, attr, value);
01701         } else {
01702             new_actions = parse_preferences(id, attr, value);
01703         }
01704         deallocate_rhs_value(value);
01705         if (!new_actions) {
01706             deallocate_rhs_value(attr);
01707             return NIL;
01708         }
01709         for (last = new_actions; last->next != NIL; last = last->next);
01710         last->next = all_actions;
01711         all_actions = new_actions;
01712     } while ((current_agent(lexeme).type != R_PAREN_LEXEME) && (current_agent(lexeme).type != UP_ARROW_LEXEME));
01713 
01714     deallocate_rhs_value(attr);
01715     return all_actions;
01716 }
01717 
01718 /* -----------------------------------------------------------------
01719                         Parse RHS Action
01720 
01721    Parses an <rhs_action> and returns an action list.  If any error
01722    occurrs, NIL is returned.
01723 
01724    <rhs_action> ::= ( variable <attr_value_make>+ ) | <function_call>
01725 ----------------------------------------------------------------- */
01726 
01727 action *parse_rhs_action(void)
01728 {
01729     action *all_actions, *new_actions, *last;
01730     Symbol *var;
01731     rhs_value funcall_value;
01732 
01733     if (current_agent(lexeme).type != L_PAREN_LEXEME) {
01734         print("Expected ( to begin RHS action\n");
01735         print_location_of_most_recent_lexeme();
01736         return NIL;
01737     }
01738     get_lexeme();
01739     if (current_agent(lexeme).type != VARIABLE_LEXEME) {
01740         /* --- the action is a function call --- */
01741         funcall_value = parse_function_call_after_lparen(TRUE);
01742         if (!funcall_value)
01743             return NIL;
01744         allocate_with_pool(&current_agent(action_pool), &all_actions);
01745         all_actions->type = FUNCALL_ACTION;
01746         all_actions->next = NIL;
01747         all_actions->value = funcall_value;
01748         return all_actions;
01749     }
01750     /* --- the action is a regular make action --- */
01751     var = make_variable(current_agent(lexeme).string);
01752     get_lexeme();
01753     all_actions = NIL;
01754     while (current_agent(lexeme).type != R_PAREN_LEXEME) {
01755         new_actions = parse_attr_value_make(var);
01756         if (new_actions) {
01757             for (last = new_actions; last->next != NIL; last = last->next);
01758             last->next = all_actions;
01759             all_actions = new_actions;
01760         } else {
01761             symbol_remove_ref(var);
01762             deallocate_action_list(all_actions);
01763             return NIL;
01764         }
01765     }
01766     get_lexeme();               /* consume the right parenthesis */
01767     symbol_remove_ref(var);
01768     return all_actions;
01769 }
01770 
01771 /* -----------------------------------------------------------------
01772                             Parse RHS
01773 
01774    Parses the <rhs> and sets *dest_rhs to the resulting action list.
01775    Returns TRUE if successful, FALSE if any error occurred.
01776 
01777    <rhs> ::= <rhs_action>*
01778 ----------------------------------------------------------------- */
01779 
01780 bool parse_rhs(action ** dest_rhs)
01781 {
01782     action *all_actions, *new_actions, *last;
01783 
01784     all_actions = NIL;
01785     while (current_agent(lexeme).type != R_PAREN_LEXEME) {
01786         new_actions = parse_rhs_action();
01787         if (new_actions) {
01788             for (last = new_actions; last->next != NIL; last = last->next);
01789             last->next = all_actions;
01790             all_actions = new_actions;
01791         } else {
01792             deallocate_action_list(all_actions);
01793             return FALSE;
01794         }
01795     }
01796     *dest_rhs = all_actions;
01797     return TRUE;
01798 }
01799 
01800 /* =================================================================
01801                  Destructively Reverse Action List
01802 
01803    As the parser builds the action list for the RHS, it adds each new
01804    action onto the front of the list.  This results in the order of
01805    the actions getting reversed.  This has certain problems--for example,
01806    if there are several (write) actions on the RHS, reversing their order
01807    means the output lines get printed in the wrong order.  To avoid this
01808    problem, we reverse the list after building it.
01809 
01810    This routine destructively reverses an action list.
01811 ================================================================= */
01812 
01813 action *destructively_reverse_action_list(action * a)
01814 {
01815     action *prev, *current, *next;
01816 
01817     prev = NIL;
01818     current = a;
01819     while (current) {
01820         next = current->next;
01821         current->next = prev;
01822         prev = current;
01823         current = next;
01824     }
01825     return prev;
01826 }
01827 
01828 /* =================================================================
01829                         Parse Production
01830 
01831    This routine reads a production (everything inside the body of the
01832    "sp" command), builds a production, and adds it to the rete.
01833 
01834    If successful, it returns a pointer to the new production struct.
01835    If any error occurred, it returns NIL (and may or may not read
01836    the rest of the body of the sp).
01837 ================================================================= */
01838 
01839 production *parse_production(void)
01840 {
01841     Symbol *name;
01842     char *documentation;
01843     condition *lhs, *lhs_top, *lhs_bottom;
01844     action *rhs;
01845     production *p;
01846     byte declared_support;
01847     byte prod_type;
01848     byte rete_addition_result;
01849     bool rhs_okay;
01850     bool interrupt_on_match;
01851 
01852     reset_placeholder_variable_generator();
01853 
01854     /* --- read production name --- */
01855     if (current_agent(lexeme).type != SYM_CONSTANT_LEXEME) {
01856         print("Expected symbol for production name\n");
01857         print_location_of_most_recent_lexeme();
01858         return NIL;
01859     }
01860     name = make_sym_constant(current_agent(lexeme).string);
01861     get_lexeme();
01862 
01863     /* --- if there's already a prod with this name, excise it --- */
01864     if (name->sc.production) {
01865         excise_production(name->sc.production, (bool) (TRUE && current_agent(sysparams)[TRACE_LOADING_SYSPARAM]));
01866     }
01867 
01868     /* --- read optional documentation string --- */
01869     if (current_agent(lexeme).type == QUOTED_STRING_LEXEME) {
01870         documentation = make_memory_block_for_string(current_agent(lexeme).string);
01871         get_lexeme();
01872     } else {
01873         documentation = NIL;
01874     }
01875 
01876     /* --- read optional flags --- */
01877     declared_support = UNDECLARED_SUPPORT;
01878     prod_type = USER_PRODUCTION_TYPE;
01879     interrupt_on_match = FALSE;
01880     for (;;) {
01881         if (current_agent(lexeme).type != SYM_CONSTANT_LEXEME)
01882             break;
01883         if (!strcmp(current_agent(lexeme).string, ":o-support")) {
01884             declared_support = DECLARED_O_SUPPORT;
01885             get_lexeme();
01886             continue;
01887         }
01888         if (!strcmp(current_agent(lexeme).string, ":i-support")) {
01889             declared_support = DECLARED_I_SUPPORT;
01890             get_lexeme();
01891             continue;
01892         }
01893         if (!strcmp(current_agent(lexeme).string, ":chunk")) {
01894             prod_type = CHUNK_PRODUCTION_TYPE;
01895             get_lexeme();
01896             continue;
01897         }
01898         if (!strcmp(current_agent(lexeme).string, ":default")) {
01899             prod_type = DEFAULT_PRODUCTION_TYPE;
01900             get_lexeme();
01901             continue;
01902         }
01903         if (!strcmp(current_agent(lexeme).string, ":interrupt")) {
01904 #ifdef MATCHTIME_INTERRUPT
01905             interrupt_on_match = TRUE;
01906 #else
01907             print("WARNING :interrupt is not supported with the current build options...");
01908 #endif
01909             get_lexeme();
01910             continue;
01911         }
01912         break;
01913     }                           /* end of while (TRUE) */
01914 
01915     /* --- read the LHS --- */
01916     lhs = parse_lhs();
01917     if (!lhs) {
01918         print_with_symbols("(Ignoring production %y)\n\n", name);
01919         if (documentation)
01920             free_memory_block_for_string(documentation);
01921         symbol_remove_ref(name);
01922 /*    if (! reading_from_top_level()) respond_to_load_errors ();  AGR 527c */
01923         return NIL;
01924     }
01925 
01926     /* --- read the "-->" --- */
01927     if (current_agent(lexeme).type != RIGHT_ARROW_LEXEME) {
01928         print("Expected --> in production\n");
01929         print_location_of_most_recent_lexeme();
01930         print_with_symbols("(Ignoring production %y)\n\n", name);
01931         if (documentation)
01932             free_memory_block_for_string(documentation);
01933         symbol_remove_ref(name);
01934         deallocate_condition_list(lhs);
01935 /*    if (! reading_from_top_level()) respond_to_load_errors ();  AGR 527c */
01936         return NIL;
01937     }
01938     get_lexeme();
01939 
01940     /* --- read the RHS --- */
01941     rhs_okay = parse_rhs(&rhs);
01942     if (!rhs_okay) {
01943         print_with_symbols("(Ignoring production %y)\n\n", name);
01944         if (documentation)
01945             free_memory_block_for_string(documentation);
01946         symbol_remove_ref(name);
01947         deallocate_condition_list(lhs);
01948 /*    if (! reading_from_top_level()) respond_to_load_errors ();  AGR 527c */
01949         return NIL;
01950     }
01951     rhs = destructively_reverse_action_list(rhs);
01952 
01953     /* --- finally, make sure there's a closing right parenthesis (but
01954        don't consume it) --- */
01955     if (current_agent(lexeme).type != R_PAREN_LEXEME) {
01956         print("Expected ) to end production\n");
01957         print_location_of_most_recent_lexeme();
01958         if (documentation)
01959             free_memory_block_for_string(documentation);
01960         print_with_symbols("(Ignoring production %y)\n\n", name);
01961         symbol_remove_ref(name);
01962         deallocate_condition_list(lhs);
01963         deallocate_action_list(rhs);
01964 /*    if (! reading_from_top_level()) respond_to_load_errors ();  AGR 527c */
01965         return NIL;
01966     }
01967 
01968     /* --- replace placeholder variables with real variables --- */
01969     reset_variable_generator(lhs, rhs);
01970     substitute_for_placeholders_in_condition_list(lhs);
01971     substitute_for_placeholders_in_action_list(rhs);
01972 
01973     /* --- everything parsed okay, so make the production structure --- */
01974     lhs_top = lhs;
01975     for (lhs_bottom = lhs; lhs_bottom->next != NIL; lhs_bottom = lhs_bottom->next);
01976     p = make_production(prod_type, name, &lhs_top, &lhs_bottom, &rhs, TRUE);
01977 
01978     if (!p) {
01979         if (documentation)
01980             free_memory_block_for_string(documentation);
01981         print_with_symbols("(Ignoring production %y)\n\n", name);
01982         symbol_remove_ref(name);
01983         deallocate_condition_list(lhs_top);
01984         deallocate_action_list(rhs);
01985 /*    if (! reading_from_top_level()) respond_to_load_errors ();  AGR 527c */
01986         return NIL;
01987     }
01988 
01989     p->documentation = documentation;
01990     p->declared_support = declared_support;
01991     p->interrupt = interrupt_on_match;
01992     rete_addition_result = add_production_to_rete(p, lhs_top, NIL, TRUE);
01993     deallocate_condition_list(lhs_top);
01994 
01995     if (rete_addition_result == DUPLICATE_PRODUCTION) {
01996         excise_production(p, FALSE);
01997         p = NIL;
01998     }
01999 
02000     return p;
02001 }
02002 
02003 /* =================================================================
02004                           Init Parser
02005 
02006    This routine initializes the parser.  At present, all it does is
02007    set up the help screens for the LHS and RHS grammars.
02008 ================================================================= */
02009 
02010 /* 
02011   This is not longer used.
02012 
02013 void init_parser (void) {
02014   add_help ("lhs-grammar", help_on_lhs_grammar);
02015   add_help ("rhs-grammar", help_on_rhs_grammar);
02016 }
02017 */

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