Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
parser.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: parser.cpp
11  *
12  * =======================================================================
13  * Production (SP) Parser for Soar 6
14  *
15  * There are two top-level routines here: init_parser(), which
16  * should be called at startup time, and parse_production(), which
17  * reads an SP (starting from the production name), builds a production,
18  * adds it to the rete, and returns a pointer to the new production
19  * (or NIL if any error occurred).
20  * =======================================================================
21  */
22 
23 #include <stdlib.h>
24 
25 #include "parser.h"
26 #include "symtab.h"
27 #include "kernel.h"
28 #include "gdatastructs.h"
29 #include "production.h"
30 #include "mem.h"
31 #include "rhsfun.h"
32 #include "agent.h"
33 #include "init_soar.h"
34 #include "print.h"
35 #include "rete.h"
36 #include "xml.h"
37 #include "reinforcement_learning.h"
38 #include "semantic_memory.h"
39 
40 #include <ctype.h>
41 
42 /* =================================================================
43  Placeholder (Dummy) Variables
44 
45  In attribute paths (and some other places) we need to create dummy
46  variables. But we need to make sure these dummy variables don't
47  accidently have the same names as variables that occur later in
48  the user's production. So, we create "placeholder" variables, whose
49  names have funky characters in them so they couldn't possibly occur
50  in user-written code. When we're all done parsing the production, we
51  go back and replace the placeholder variables with "real" variables
52  (names without funky characters), making sure the real variables
53  don't occur anywhere else in the production.
54 ================================================================= */
55 
56 
58  int i;
59  for (i=0; i<26; i++) thisAgent->placeholder_counter[i] = 1;
60 }
61 
62 Symbol *make_placeholder_var(agent* thisAgent, char first_letter) {
63  Symbol *v;
64  char buf[30];
65  int i;
66 
67  if (!isalpha(first_letter)) {
68  first_letter = 'v';
69  }
70  i = tolower(first_letter) - static_cast<int>('a');
71  assert (i >= 0 && i < 26);
72 
73  /* --- create variable with "#" in its name: this couldn't possibly be a
74  variable in the user's code, since the lexer doesn't handle "#" --- */
75  SNPRINTF (buf, sizeof(buf)-1, "<#%c*%lu>", first_letter, static_cast<long unsigned int>(thisAgent->placeholder_counter[i]++));
76  buf[sizeof(buf)-1] = '\0';
77 
78  v = make_variable(thisAgent, buf);
79  /* --- indicate that there is no corresponding "real" variable yet --- */
80  v->var.current_binding_value = NIL;
81 
82  return v;
83 }
84 
85 /* -----------------------------------------------------------------
86  Make Placeholder (Dummy) Equality Test
87 
88  Creates and returns a test for equality with a newly generated
89  placeholder variable.
90 ----------------------------------------------------------------- */
91 
92 test make_placeholder_test (agent* thisAgent, char first_letter) {
93  Symbol *new_var = make_placeholder_var(thisAgent, first_letter);
95 }
96 
97 /* -----------------------------------------------------------------
98  Substituting Real Variables for Placeholders
99 
100  When done parsing the production, we go back and substitute "real"
101  variables for all the placeholders. This is done by walking all the
102  LHS conditions and destructively modifying any tests involving
103  placeholders. The placeholder-->real mapping is maintained on each
104  placeholder symbol: placeholder->var.current_binding_value is the
105  corresponding "real" variable, or NIL if no such "real" variable has
106  been created yet.
107 
108  To use this, first call reset_variable_generator (lhs, rhs) with the
109  lhs and rhs of the production just parsed; then call
110  substitute_for_placeholders_in_condition_list (lhs).
111 ----------------------------------------------------------------- */
112 
114  char prefix[3];
115  Symbol *var;
116  Bool just_created;
117 
118  /* --- if not a variable, do nothing --- */
119  if ((*sym)->common.symbol_type!=VARIABLE_SYMBOL_TYPE) return;
120  /* --- if not a placeholder variable, do nothing --- */
121  if (*((*sym)->var.name + 1) != '#') return;
122 
123  just_created = FALSE;
124 
125  if (! (*sym)->var.current_binding_value) {
126  prefix[0] = *((*sym)->var.name + 2);
127  prefix[1] = '*';
128  prefix[2] = 0;
129  (*sym)->var.current_binding_value = generate_new_variable (thisAgent, prefix);
130  just_created = TRUE;
131  }
132 
133  var = (*sym)->var.current_binding_value;
134  symbol_remove_ref (thisAgent, *sym);
135  *sym = var;
136  if (!just_created) symbol_add_ref (var);
137 }
138 
140  cons *c;
141  complex_test *ct;
142 
143  if (test_is_blank_test(*t)) return;
145  substitute_for_placeholders_in_symbol (thisAgent, (Symbol **) t);
146  /* Warning: this relies on the representation of tests */
147  return;
148  }
149 
150  ct = complex_test_from_test(*t);
151 
152  switch (ct->type) {
153  case GOAL_ID_TEST:
154  case IMPASSE_ID_TEST:
155  case DISJUNCTION_TEST:
156  return;
157  case CONJUNCTIVE_TEST:
158  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
159  substitute_for_placeholders_in_test (thisAgent, reinterpret_cast<test *>(&(c->first)));
160  return;
161  default: /* relational tests other than equality */
163  return;
164  }
165 }
166 
168  condition *cond) {
169  for ( ; cond!=NIL; cond=cond->next) {
170  switch (cond->type) {
171  case POSITIVE_CONDITION:
172  case NEGATIVE_CONDITION:
176  break;
179  break;
180  }
181  }
182 }
183 /* begin KJC 10/19/98 */
185  for ( ; a!=NIL; a=a->next) {
186  if (a->type == MAKE_ACTION) {
187  if (rhs_value_is_symbol(a->id)) {
188  substitute_for_placeholders_in_symbol(thisAgent, (Symbol **)&(a->id));
189  }
190  if (rhs_value_is_symbol(a->attr))
191  substitute_for_placeholders_in_symbol(thisAgent, (Symbol **)&(a->attr));
192  if (rhs_value_is_symbol(a->value))
193  substitute_for_placeholders_in_symbol(thisAgent, (Symbol **)&(a->value));
194  }
195  }
196 }
197 /* end KJC 10/19/98 */
198 
199 /* =================================================================
200 
201  Grammar for left hand sides of productions
202 
203  <lhs> ::= <cond>+
204  <cond> ::= <positive_cond> | - <positive_cond>
205  <positive_cond> ::= <conds_for_one_id> | { <cond>+ }
206  <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
207  <id_test> ::= <test>
208  <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*
209  <attr_test> ::= <test>
210  <value_test> ::= <test> [+] | <conds_for_one_id> [+]
211 
212  <test> ::= <conjunctive_test> | <simple_test>
213  <conjunctive_test> ::= { <simple_test>+ }
214  <simple_test> ::= <disjunction_test> | <relational_test>
215  <disjunction_test> ::= << <constant>* >>
216  <relational_test> ::= [<relation>] <single_test>
217  <relation> ::= <> | < | > | <= | >= | = | <=>
218  <single_test> ::= <variable> | <constant>
219  <constant> ::= sym_constant | int_constant | float_constant
220  <variable> ::= variable | lti
221 
222 ================================================================= */
223 
224 const char *help_on_lhs_grammar[] = {
225 "Grammar for left hand sides of productions:",
226 "",
227 " <lhs> ::= <cond>+",
228 " <cond> ::= <positive_cond> | - <positive_cond>",
229 " <positive_cond> ::= <conds_for_one_id> | { <cond>+ }",
230 " <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )",
231 " <id_test> ::= <test>",
232 " <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*",
233 " <attr_test> ::= <test>",
234 " <value_test> ::= <test> [+] | <conds_for_one_id> [+]",
235 "",
236 " <test> ::= <conjunctive_test> | <simple_test>",
237 " <conjunctive_test> ::= { <simple_test>+ }",
238 " <simple_test> ::= <disjunction_test> | <relational_test>",
239 " <disjunction_test> ::= << <constant>* >>",
240 " <relational_test> ::= [<relation>] <single_test>",
241 " <relation> ::= <> | < | > | <= | >= | = | <=>",
242 " <single_test> ::= <variable> | <constant>",
243 " <constant> ::= sym_constant | int_constant | float_constant",
244 " <variable> ::= variable | lti",
245 "",
246 "See also: rhs-grammar, sp",
247 0 };
248 
249 
250 
251 /* =================================================================
252 
253  Make symbol for current lexeme
254 
255 ================================================================= */
256 
257 Symbol *make_symbol_for_current_lexeme (agent* thisAgent, bool allow_lti) {
258  switch (thisAgent->lexeme.type) {
259  case SYM_CONSTANT_LEXEME: return make_sym_constant (thisAgent, thisAgent->lexeme.string);
260  case VARIABLE_LEXEME: return make_variable (thisAgent, thisAgent->lexeme.string);
261  case INT_CONSTANT_LEXEME: return make_int_constant (thisAgent, thisAgent->lexeme.int_val);
262  case FLOAT_CONSTANT_LEXEME: return make_float_constant (thisAgent, thisAgent->lexeme.float_val);
263 
264  case IDENTIFIER_LEXEME:
265  if (!allow_lti) {
266  char msg[BUFFER_MSG_SIZE];
267  strncpy(msg, "parser.c: Internal error: ID found in make_symbol_for_current_lexeme\n", BUFFER_MSG_SIZE);
268  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
269  abort_with_fatal_error(thisAgent, msg);
270  }
271  else {
272  smem_lti_id lti_id = smem_lti_get_id( thisAgent, thisAgent->lexeme.id_letter, thisAgent->lexeme.id_number );
273 
274  if ( lti_id == NIL ) {
275  char msg[BUFFER_MSG_SIZE];
276  strncpy(msg, "parser.c: Internal error: invalid long-term identifier found in make_symbol_for_current_lexeme\n", BUFFER_MSG_SIZE);
277  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
278  abort_with_fatal_error(thisAgent, msg);
279  }
280  else {
281  return smem_lti_soar_make( thisAgent, lti_id, thisAgent->lexeme.id_letter, thisAgent->lexeme.id_number, SMEM_LTI_UNKNOWN_LEVEL );
282  }
283  }
284  break;
285  default:
286  { char msg[BUFFER_MSG_SIZE];
287  SNPRINTF(msg, BUFFER_MSG_SIZE, "parser.c: Internal error: bad lexeme type in make_symbol_for_current_lexeme\n, thisAgent->lexeme.string=%s\n", thisAgent->lexeme.string);
288  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
289  abort_with_fatal_error(thisAgent, msg);
290  }
291  }
292  return NIL; /* unreachable, but without it, gcc -Wall warns here */
293 }
294 
295 /* =================================================================
296  Routines for Tests
297 
298  The following routines are used to parse and build <test>'s.
299  At entry, they expect the current lexeme to be the start of a
300  test. At exit, they leave the current lexeme at the first lexeme
301  following the test. They return the test read, or NIL if any
302  error occurred. (They never return a blank_test.)
303 ================================================================= */
304 
305 /* -----------------------------------------------------------------
306  Parse Relational Test
307 
308  <relational_test> ::= [<relation>] <single_test>
309  <relation> ::= <> | < | > | <= | >= | = | <=>
310  <single_test> ::= <variable> | <constant>
311  <constant> ::= sym_constant | int_constant | float_constant
312  <variable> ::= variable | lti
313 ----------------------------------------------------------------- */
314 
316  byte test_type;
317  Bool use_equality_test;
318  test t;
319  Symbol *referent;
320  complex_test *ct;
321 
322  use_equality_test = FALSE;
323  test_type = NOT_EQUAL_TEST; /* unnecessary, but gcc -Wall warns without it */
324 
325  /* --- read optional relation symbol --- */
326  switch(thisAgent->lexeme.type) {
327  case EQUAL_LEXEME:
328  use_equality_test = TRUE;
329  get_lexeme(thisAgent);
330  break;
331 
332  case NOT_EQUAL_LEXEME:
333  test_type = NOT_EQUAL_TEST;
334  get_lexeme(thisAgent);
335  break;
336 
337  case LESS_LEXEME:
338  test_type = LESS_TEST;
339  get_lexeme(thisAgent);
340  break;
341 
342  case GREATER_LEXEME:
343  test_type = GREATER_TEST;
344  get_lexeme(thisAgent);
345  break;
346 
347  case LESS_EQUAL_LEXEME:
348  test_type = LESS_OR_EQUAL_TEST;
349  get_lexeme(thisAgent);
350  break;
351 
353  test_type = GREATER_OR_EQUAL_TEST;
354  get_lexeme(thisAgent);
355  break;
356 
358  test_type = SAME_TYPE_TEST;
359  get_lexeme(thisAgent);
360  break;
361 
362  default:
363  use_equality_test = TRUE;
364  break;
365  }
366 
367  // Check for long term identifier notation
368  bool id_lti = parse_lti(thisAgent);
369 
370  /* --- read variable or constant --- */
371  switch (thisAgent->lexeme.type) {
372  case SYM_CONSTANT_LEXEME:
373  case INT_CONSTANT_LEXEME:
375  case VARIABLE_LEXEME:
376  case IDENTIFIER_LEXEME: // IDENTIFIER_LEXEME only possible if id_lti true due to set_lexer_allow_ids above
377  referent = make_symbol_for_current_lexeme(thisAgent, id_lti);
378  get_lexeme(thisAgent);
379  if (use_equality_test) {
381  } else {
382  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
383  ct->type = test_type;
384  ct->data.referent = referent;
386  }
387  return t;
388 
389  default:
390  print (thisAgent, "Expected variable or constant for test\n");
392  return NIL;
393  }
394 }
395 
396 /* -----------------------------------------------------------------
397  Parse Disjunction Test
398 
399  <disjunction_test> ::= << <constant>* >>
400  <constant> ::= sym_constant | int_constant | float_constant
401 ----------------------------------------------------------------- */
402 
404  complex_test *ct;
405  test t;
406 
407  if (thisAgent->lexeme.type!=LESS_LESS_LEXEME) {
408  print (thisAgent, "Expected << to begin disjunction test\n");
410  return NIL;
411  }
412  get_lexeme(thisAgent);
413 
414  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
415  ct->type = DISJUNCTION_TEST;
416  ct->data.disjunction_list = NIL;
418 
419  while (thisAgent->lexeme.type!=GREATER_GREATER_LEXEME) {
420  switch (thisAgent->lexeme.type) {
421  case SYM_CONSTANT_LEXEME:
422  case INT_CONSTANT_LEXEME:
424  push (thisAgent, make_symbol_for_current_lexeme(thisAgent, false), ct->data.disjunction_list);
425  get_lexeme(thisAgent);
426  break;
427  default:
428  print (thisAgent, "Expected constant or >> while reading disjunction test\n");
430  deallocate_test (thisAgent, t);
431  return NIL;
432  }
433  }
434  get_lexeme(thisAgent); /* consume the >> */
435  ct->data.disjunction_list =
437  return t;
438 }
439 
440 /* -----------------------------------------------------------------
441  Parse Simple Test
442 
443  <simple_test> ::= <disjunction_test> | <relational_test>
444 ----------------------------------------------------------------- */
445 
447  if (thisAgent->lexeme.type==LESS_LESS_LEXEME)
448  return parse_disjunction_test(thisAgent);
449  return parse_relational_test(thisAgent);
450 }
451 
452 /* -----------------------------------------------------------------
453  Parse Test
454 
455  <test> ::= <conjunctive_test> | <simple_test>
456  <conjunctive_test> ::= { <simple_test>+ }
457 ----------------------------------------------------------------- */
458 
459 test parse_test (agent* thisAgent) {
460  complex_test *ct;
461  test t, temp;
462 
463  if (thisAgent->lexeme.type!=L_BRACE_LEXEME)
464  return parse_simple_test(thisAgent);
465  /* --- parse and return conjunctive test --- */
466  get_lexeme(thisAgent);
467  t = make_blank_test();
468  do {
469  temp = parse_simple_test(thisAgent);
470  if (!temp) {
471  deallocate_test(thisAgent, t);
472  return NIL;
473  }
474  add_new_test_to_test (thisAgent, &t, temp);
475  } while (thisAgent->lexeme.type!=R_BRACE_LEXEME);
476  get_lexeme(thisAgent); /* consume the "}" */
477 
478  if (test_is_complex_test(t)) {
479  ct = complex_test_from_test(t);
480  if (ct->type==CONJUNCTIVE_TEST)
481  ct->data.conjunct_list =
483  }
484 
485  return t;
486 }
487 
488 /* =================================================================
489  Routines for Conditions
490 
491  Various routines here are used to parse and build conditions, etc.
492  At entry, each expects the current lexeme to be at the start of whatever
493  thing they're supposed to parse. At exit, each leaves the current
494  lexeme at the first lexeme following the parsed object. Each returns
495  a list of the conditions they parsed, or NIL if any error occurred.
496 ================================================================= */
497 
498 /* -----------------------------------------------------------------
499  Fill In Id Tests
500  Fill In Attr Tests
501 
502  As low-level routines (e.g., parse_value_test_star) parse, they
503  leave the id (and sometimes attribute) test fields blank (NIL) in the
504  condition structures they return. The calling routine must fill in
505  the id tests and/or attribute tests. These routines fill in any
506  still-blank {id, attr} tests with copies of a given test. They try
507  to add non-equality portions of the test only once, if possible.
508 ----------------------------------------------------------------- */
509 
510 void fill_in_id_tests (agent* thisAgent, condition *conds, test t) {
511  condition *positive_c, *c;
512  test equality_test_from_t;
513 
514  /* --- see if there's at least one positive condition --- */
515  for (positive_c=conds; positive_c!=NIL; positive_c=positive_c->next)
516  if ((positive_c->type==POSITIVE_CONDITION) &&
517  (positive_c->data.tests.id_test==NIL)) break;
518 
519  if (positive_c) { /* --- there is at least one positive condition --- */
520  /* --- add just the equality test to most of the conditions --- */
521  equality_test_from_t = copy_of_equality_test_found_in_test (thisAgent, t);
522  for (c=conds; c!=NIL; c=c->next) {
524  fill_in_id_tests (thisAgent, c->data.ncc.top, equality_test_from_t);
525  else if (c->data.tests.id_test==NIL)
526  c->data.tests.id_test = copy_test (thisAgent, equality_test_from_t);
527  }
528  deallocate_test (thisAgent, equality_test_from_t);
529  /* --- add the whole test to one positive condition --- */
530  deallocate_test (thisAgent, positive_c->data.tests.id_test);
531  positive_c->data.tests.id_test = copy_test (thisAgent, t);
532  return;
533  }
534 
535  /* --- all conditions are negative --- */
536  for (c=conds; c!=NIL; c=c->next) {
538  fill_in_id_tests (thisAgent, c->data.ncc.top, t);
539  } else {
540  if (c->data.tests.id_test==NIL)
541  c->data.tests.id_test = copy_test (thisAgent, t);
542  }
543  }
544 }
545 
546 void fill_in_attr_tests (agent* thisAgent, condition *conds, test t) {
547  condition *positive_c, *c;
548  test equality_test_from_t;
549 
550  /* --- see if there's at least one positive condition --- */
551  for (positive_c=conds; positive_c!=NIL; positive_c=positive_c->next)
552  if ((positive_c->type==POSITIVE_CONDITION) &&
553  (positive_c->data.tests.attr_test==NIL)) break;
554 
555  if (positive_c) { /* --- there is at least one positive condition --- */
556  /* --- add just the equality test to most of the conditions --- */
557  equality_test_from_t = copy_of_equality_test_found_in_test (thisAgent, t);
558  for (c=conds; c!=NIL; c=c->next) {
560  fill_in_attr_tests (thisAgent, c->data.ncc.top, equality_test_from_t);
561  else if (c->data.tests.attr_test==NIL)
562  c->data.tests.attr_test = copy_test (thisAgent, equality_test_from_t);
563  }
564  deallocate_test (thisAgent, equality_test_from_t);
565  /* --- add the whole test to one positive condition --- */
566  deallocate_test (thisAgent, positive_c->data.tests.attr_test);
567  positive_c->data.tests.attr_test = copy_test (thisAgent, t);
568  return;
569  }
570 
571  /* --- all conditions are negative --- */
572  for (c=conds; c!=NIL; c=c->next) {
574  fill_in_attr_tests (thisAgent, c->data.ncc.top, t);
575  } else {
576  if (c->data.tests.attr_test==NIL)
577  c->data.tests.attr_test = copy_test (thisAgent, t);
578  }
579  }
580 }
581 
582 /* -----------------------------------------------------------------
583  Negate Condition List
584 
585  Returns the negation of the given condition list. If the given
586  list is a single positive or negative condition, it just toggles
587  the type. If the given list is a single ncc, it strips off the ncc
588  part and returns the subconditions. Otherwise it makes a new ncc
589  using the given conditions.
590 ----------------------------------------------------------------- */
591 
593  condition *temp, *last;
594 
595  if (conds->next==NIL) {
596  /* --- only one condition to negate, so toggle the type --- */
597  switch (conds->type) {
598  case POSITIVE_CONDITION:
599  conds->type = NEGATIVE_CONDITION;
600  return conds;
601  case NEGATIVE_CONDITION:
602  conds->type = POSITIVE_CONDITION;
603  return conds;
605  temp = conds->data.ncc.top;
606  free_with_pool (&thisAgent->condition_pool, conds);
607  return temp;
608  }
609  }
610  /* --- more than one condition; so build a conjunctive negation --- */
611  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &temp);
613  temp->next = NIL;
614  temp->prev = NIL;
615  temp->data.ncc.top = conds;
616  for (last=conds; last->next!=NIL; last=last->next);
617  temp->data.ncc.bottom = last;
618  return temp;
619 }
620 
621 /* -----------------------------------------------------------------
622  Parse Value Test Star
623 
624  <value_test> ::= <test> [+] | <conds_for_one_id> [+]
625 
626  (This routine parses <value_test>*, given as input the id_test and
627  attr_test already read.)
628 ----------------------------------------------------------------- */
629 
631  char first_letter_if_no_id_given,
632  test *dest_id_test);
633 
634 condition *parse_value_test_star (agent* thisAgent, char first_letter) {
635  condition *c, *last_c, *first_c, *new_conds;
636  test value_test;
637  Bool acceptable;
638 
639  if ((thisAgent->lexeme.type==MINUS_LEXEME) ||
640  (thisAgent->lexeme.type==UP_ARROW_LEXEME) ||
641  (thisAgent->lexeme.type==R_PAREN_LEXEME)) {
642  /* --- value omitted, so create dummy value test --- */
643  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &c);
645  c->next = c->prev = NIL;
646  c->data.tests.id_test = NIL;
647  c->data.tests.attr_test = NIL;
648  c->data.tests.value_test = make_placeholder_test (thisAgent, first_letter);
650  return c;
651  }
652 
653  first_c = NIL;
654  last_c = NIL;
655  do {
656  if (thisAgent->lexeme.type==L_PAREN_LEXEME) {
657  /* --- read <conds_for_one_id>, take the id_test from it --- */
658  new_conds = parse_conds_for_one_id (thisAgent, first_letter, &value_test);
659  if (!new_conds) {
660  deallocate_condition_list (thisAgent, first_c);
661  return NIL;
662  }
663  } else {
664  /* --- read <value_test> --- */
665  new_conds = NIL;
666  value_test = parse_test(thisAgent);
667  if (!value_test) {
668  deallocate_condition_list (thisAgent, first_c);
669  return NIL;
670  }
671  if (! test_includes_equality_test_for_symbol (value_test, NIL)) {
672  add_new_test_to_test (thisAgent, &value_test,make_placeholder_test(thisAgent, first_letter));
673  }
674  }
675  /* --- check for acceptable preference indicator --- */
676  acceptable = FALSE;
677  if (thisAgent->lexeme.type==PLUS_LEXEME) { acceptable = TRUE; get_lexeme(thisAgent); }
678  /* --- build condition using the new value test --- */
679  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &c);
680  insert_at_head_of_dll (new_conds, c, next, prev);
682  c->data.tests.id_test = NIL;
683  c->data.tests.attr_test = NIL;
684  c->data.tests.value_test = value_test;
685  c->test_for_acceptable_preference = acceptable;
686  /* --- add new conditions to the end of the list --- */
687  if (last_c) last_c->next = new_conds; else first_c = new_conds;
688  new_conds->prev = last_c;
689  for (last_c=new_conds; last_c->next!=NIL; last_c=last_c->next);
690  } while ((thisAgent->lexeme.type!=MINUS_LEXEME) &&
691  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
692  (thisAgent->lexeme.type!=R_PAREN_LEXEME));
693  return first_c;
694 }
695 
696 /* -----------------------------------------------------------------
697  Parse Attr Value Tests
698 
699  <attr_value_tests> ::= [-] ^ <attr_test> [.<attr_test>]* <value_test>*
700  <attr_test> ::= <test>
701 
702  (This routine parses <attr_value_tests>, given as input the id_test
703  already read.)
704 ----------------------------------------------------------------- */
705 
707  test id_test_to_use, attr_test;
708  Bool negate_it;
709  condition *first_c, *last_c, *c, *new_conds;
710 
711  /* --- read optional minus sign --- */
712  negate_it = FALSE;
713  if (thisAgent->lexeme.type==MINUS_LEXEME) { negate_it = TRUE; get_lexeme(thisAgent); }
714 
715  /* --- read up arrow --- */
716  if (thisAgent->lexeme.type!=UP_ARROW_LEXEME) {
717  print (thisAgent, "Expected ^ followed by attribute\n");
719  return NIL;
720  }
721  get_lexeme(thisAgent);
722 
723  first_c = NIL;
724  last_c = NIL;
725 
726  /* --- read first <attr_test> --- */
727  attr_test = parse_test(thisAgent);
728  if (!attr_test) return NIL;
729  if (! test_includes_equality_test_for_symbol (attr_test, NIL)) {
730  add_new_test_to_test (thisAgent, &attr_test, make_placeholder_test (thisAgent, 'a'));
731  }
732 
733  /* --- read optional attribute path --- */
734  id_test_to_use = NIL;
735  while (thisAgent->lexeme.type==PERIOD_LEXEME) {
736  get_lexeme(thisAgent); /* consume the "." */
737  /* --- setup for next attribute in path: make a dummy variable,
738  create a new condition in the path --- */
739  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &c);
741  if (last_c) last_c->next = c; else first_c = c;
742  c->next = NIL;
743  c->prev = last_c;
744  last_c = c;
745  if (id_test_to_use)
746  c->data.tests.id_test = copy_test (thisAgent, id_test_to_use);
747  else
748  c->data.tests.id_test = NIL;
749  c->data.tests.attr_test = attr_test;
750  id_test_to_use = make_placeholder_test (thisAgent, first_letter_from_test(attr_test));
751  c->data.tests.value_test = id_test_to_use;
753  /* --- update id and attr tests for the next path element --- */
754  attr_test = parse_test (thisAgent);
755  if (!attr_test) {
756  deallocate_condition_list (thisAgent, first_c);
757  return NIL;
758  }
759 /* AGR 544 begin */
760  if (! test_includes_equality_test_for_symbol (attr_test, NIL)) {
761  add_new_test_to_test (thisAgent, &attr_test,make_placeholder_test(thisAgent, 'a'));
762  }
763 /* AGR 544 end */
764  } /* end of while (thisAgent->lexeme.type==PERIOD_LEXEME) */
765 
766  /* --- finally, do the <value_test>* part --- */
767  new_conds = parse_value_test_star (thisAgent, first_letter_from_test (attr_test));
768  if (!new_conds) {
769  deallocate_condition_list (thisAgent, first_c);
770  deallocate_test (thisAgent, attr_test);
771  return NIL;
772  }
773  fill_in_attr_tests (thisAgent, new_conds, attr_test);
774  if (id_test_to_use) fill_in_id_tests (thisAgent, new_conds, id_test_to_use);
775  deallocate_test (thisAgent, attr_test);
776  if (last_c) last_c->next = new_conds; else first_c = new_conds;
777  new_conds->prev = last_c;
778  /* should update last_c here, but it's not needed anymore */
779 
780  /* --- negate everything if necessary --- */
781  if (negate_it) first_c = negate_condition_list (thisAgent, first_c);
782 
783  return first_c;
784 }
785 
786 /* -----------------------------------------------------------------
787  Parse Head Of Conds For One Id
788 
789  <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
790  <id_test> ::= <test>
791 
792  This routine parses the "( [state|impasse] [<id_test>]" part of
793  <conds_for_one_id> and returns the resulting id_test (or NIL if
794  any error occurs).
795 ----------------------------------------------------------------- */
796 
797 test parse_head_of_conds_for_one_id (agent* thisAgent, char first_letter_if_no_id_given) {
798  test id_test, id_goal_impasse_test, check_for_symconstant;
799  complex_test *ct;
800  Symbol *sym;
801 
802  if (thisAgent->lexeme.type!=L_PAREN_LEXEME) {
803  print (thisAgent, "Expected ( to begin condition element\n");
805  return NIL;
806  }
807  get_lexeme(thisAgent);
808 
809  /* --- look for goal/impasse indicator --- */
810  if (thisAgent->lexeme.type==SYM_CONSTANT_LEXEME) {
811  if (!strcmp(thisAgent->lexeme.string,"state")) {
812  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
813  ct->type = GOAL_ID_TEST;
814  id_goal_impasse_test = make_test_from_complex_test(ct);
815  get_lexeme(thisAgent);
816  first_letter_if_no_id_given = 's';
817  } else if (!strcmp(thisAgent->lexeme.string,"impasse")) {
818  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
819  ct->type = IMPASSE_ID_TEST;
820  id_goal_impasse_test = make_test_from_complex_test(ct);
821  get_lexeme(thisAgent);
822  first_letter_if_no_id_given = 'i';
823  } else {
824  id_goal_impasse_test = make_blank_test();
825  }
826  } else {
827  id_goal_impasse_test = make_blank_test();
828  }
829 
830  /* --- read optional id test; create dummy one if none given --- */
831  if ((thisAgent->lexeme.type!=MINUS_LEXEME) &&
832  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
833  (thisAgent->lexeme.type!=R_PAREN_LEXEME)) {
834  id_test = parse_test(thisAgent);
835  if (!id_test) {
836  deallocate_test (thisAgent, id_goal_impasse_test);
837  return NIL;
838  }
839  if (! test_includes_equality_test_for_symbol (id_test, NIL)) {
841  (thisAgent, &id_test, make_placeholder_test (thisAgent, first_letter_if_no_id_given));
842  } else {
843  check_for_symconstant = copy_of_equality_test_found_in_test(thisAgent, id_test);
844  sym = referent_of_equality_test(check_for_symconstant);
845  deallocate_test (thisAgent, check_for_symconstant); /* RBD added 3/28/95 */
846 
847  // Symbol type can only be IDENTIFIER_SYMBOL_TYPE if it is a long term identifier (lti),
848  // Otherwise, it isn't possible to have an IDENTIFIER_SYMBOL_TYPE here.
849  if((sym->common.symbol_type != VARIABLE_SYMBOL_TYPE) && (sym->common.symbol_type != IDENTIFIER_SYMBOL_TYPE)) {
850  print_with_symbols(thisAgent, "Warning: Constant %y in id field test.\n", sym);
851  print(thisAgent, " This will never match.\n");
852 
854  add_to_growable_string(thisAgent, &gs, "Warning: Constant ");
855  add_to_growable_string(thisAgent, &gs, symbol_to_string(thisAgent, sym, true, 0, 0));
856  add_to_growable_string(thisAgent, &gs, " in id field test.\n This will never match.");
858  free_growable_string(thisAgent, gs);
859  //TODO: should we append this to the previous XML message or create a new message for it?
861  deallocate_test (thisAgent, id_test); /* AGR 527c */
862  return NIL; /* AGR 527c */
863  }
864  }
865  } else {
866  id_test = make_placeholder_test (thisAgent, first_letter_if_no_id_given);
867  }
868 
869  /* --- add the goal/impasse test to the id test --- */
870  add_new_test_to_test (thisAgent, &id_test, id_goal_impasse_test);
871 
872  /* --- return the resulting id test --- */
873  return id_test;
874 }
875 
876 /* -----------------------------------------------------------------
877  Parse Tail Of Conds For One Id
878 
879  <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
880  <id_test> ::= <test>
881 
882  This routine parses the "<attr_value_tests>* )" part of <conds_for_one_id>
883  and returns the resulting conditions (or NIL if any error occurs).
884  It does not fill in the id tests of the conditions.
885 ----------------------------------------------------------------- */
886 
888  condition *first_c, *last_c, *new_conds;
889  condition *c;
890 
891  /* --- if no <attr_value_tests> are given, create a dummy one --- */
892  if (thisAgent->lexeme.type==R_PAREN_LEXEME) {
893  get_lexeme(thisAgent); /* consume the right parenthesis */
894  allocate_with_pool (thisAgent, &thisAgent->condition_pool, &c);
896  c->next = NIL;
897  c->prev = NIL;
898  c->data.tests.id_test = NIL;
899  c->data.tests.attr_test = make_placeholder_test (thisAgent, 'a');
900  c->data.tests.value_test = make_placeholder_test (thisAgent, 'v');
902  return c;
903  }
904 
905  /* --- read <attr_value_tests>* --- */
906  first_c = NIL;
907  last_c = NIL;
908  while (thisAgent->lexeme.type!=R_PAREN_LEXEME) {
909  new_conds = parse_attr_value_tests (thisAgent);
910  if (!new_conds) {
911  deallocate_condition_list (thisAgent, first_c);
912  return NIL;
913  }
914  if (last_c) last_c->next = new_conds; else first_c = new_conds;
915  new_conds->prev = last_c;
916  for (last_c=new_conds; last_c->next!=NIL; last_c=last_c->next);
917  }
918 
919  /* --- reached the end of the condition --- */
920  get_lexeme(thisAgent); /* consume the right parenthesis */
921 
922  return first_c;
923 }
924 
925 /* -----------------------------------------------------------------
926  Parse Conds For One Id
927 
928  <conds_for_one_id> ::= ( [state|impasse] [<id_test>] <attr_value_tests>* )
929  <id_test> ::= <test>
930 
931  This routine parses <conds_for_one_id> and returns the conditions (of
932  NIL if any error occurs).
933 
934  If the argument dest_id_test is non-NULL, then *dest_id_test is set
935  to the resulting complete id test (which includes any goal/impasse test),
936  and in all the conditions the id field is filled in with just the
937  equality portion of id_test.
938 
939  If the argument dest_id_test is NULL, then the complete id_test is
940  included in the conditions.
941 ----------------------------------------------------------------- */
942 
943 condition *parse_conds_for_one_id (agent* thisAgent, char first_letter_if_no_id_given,
944  test *dest_id_test) {
945  condition *conds;
946  test id_test, equality_test_from_id_test;
947 
948  /* --- parse the head --- */
949  id_test = parse_head_of_conds_for_one_id (thisAgent, first_letter_if_no_id_given);
950  if (! id_test) return NIL;
951 
952  /* --- parse the tail --- */
953  conds = parse_tail_of_conds_for_one_id (thisAgent);
954  if (! conds) {
955  deallocate_test (thisAgent, id_test);
956  return NIL;
957  }
958 
959  /* --- fill in the id test in all the conditions just read --- */
960  if (dest_id_test) {
961  *dest_id_test = id_test;
962  equality_test_from_id_test = copy_of_equality_test_found_in_test (thisAgent, id_test);
963  fill_in_id_tests (thisAgent, conds, equality_test_from_id_test);
964  deallocate_test (thisAgent, equality_test_from_id_test);
965  } else {
966  fill_in_id_tests (thisAgent, conds, id_test);
967  deallocate_test (thisAgent, id_test);
968  }
969 
970  return conds;
971 }
972 
973 /* -----------------------------------------------------------------
974  Parse Cond
975 
976  <cond> ::= <positive_cond> | - <positive_cond>
977  <positive_cond> ::= <conds_for_one_id> | { <cond>+ }
978 ----------------------------------------------------------------- */
979 
980 condition *parse_cond_plus (agent* thisAgent);
981 
982 condition *parse_cond (agent* thisAgent) {
983  condition *c;
984  Bool negate_it;
985 
986  /* --- look for leading "-" sign --- */
987  negate_it = FALSE;
988  if (thisAgent->lexeme.type==MINUS_LEXEME) { negate_it = TRUE; get_lexeme(thisAgent); }
989 
990  /* --- parse <positive_cond> --- */
991  if (thisAgent->lexeme.type==L_BRACE_LEXEME) {
992  /* --- read conjunctive condition --- */
993  get_lexeme(thisAgent);
994  c = parse_cond_plus(thisAgent);
995  if (!c) return NIL;
996  if (thisAgent->lexeme.type!=R_BRACE_LEXEME) {
997  print (thisAgent, "Expected } to end conjunctive condition\n");
999  deallocate_condition_list (thisAgent, c);
1000  return NIL;
1001  }
1002  get_lexeme(thisAgent); /* consume the R_BRACE */
1003  } else {
1004  /* --- read conds for one id --- */
1005  c = parse_conds_for_one_id (thisAgent, 's', NULL);
1006  if (!c) return NIL;
1007  }
1008 
1009  /* --- if necessary, handle the negation --- */
1010  if (negate_it) c = negate_condition_list (thisAgent, c);
1011 
1012  return c;
1013 }
1014 
1015 /* -----------------------------------------------------------------
1016  Parse Cond Plus
1017 
1018  (Parses <cond>+ and builds a condition list.)
1019 ----------------------------------------------------------------- */
1020 
1022  condition *first_c, *last_c, *new_conds;
1023 
1024  first_c = NIL;
1025  last_c = NIL;
1026  do {
1027  /* --- get individual <cond> --- */
1028  new_conds = parse_cond (thisAgent);
1029  if (!new_conds) {
1030  deallocate_condition_list (thisAgent, first_c);
1031  return NIL;
1032  }
1033  if (last_c) last_c->next = new_conds; else first_c = new_conds;
1034  new_conds->prev = last_c;
1035  for (last_c=new_conds; last_c->next!=NIL; last_c=last_c->next);
1036  } while ((thisAgent->lexeme.type==MINUS_LEXEME) ||
1037  (thisAgent->lexeme.type==L_PAREN_LEXEME) ||
1038  (thisAgent->lexeme.type==L_BRACE_LEXEME));
1039  return first_c;
1040 }
1041 
1042 /* -----------------------------------------------------------------
1043  Parse LHS
1044 
1045  (Parses <lhs> and builds a condition list.)
1046 
1047  <lhs> ::= <cond>+
1048 ----------------------------------------------------------------- */
1049 
1050 condition *parse_lhs (agent* thisAgent) {
1051  condition *c;
1052 
1053  c = parse_cond_plus (thisAgent);
1054  if (!c) return NIL;
1055  return c;
1056 }
1057 
1058 
1059 
1060 /* =================================================================
1061 
1062  Routines for Actions
1063 
1064  The following routines are used to parse and build actions, etc.
1065  Except as otherwise noted, at entry each routine expects the
1066  current lexeme to be at the start of whatever thing it's supposed
1067  to parse. At exit, each leaves the current lexeme at the first
1068  lexeme following the parsed object.
1069 ================================================================= */
1070 
1071 /* =====================================================================
1072 
1073  Grammar for right hand sides of productions
1074 
1075  <rhs> ::= <rhs_action>*
1076  <rhs_action> ::= ( <variable> <attr_value_make>+ ) | <function_call>
1077  <function_call> ::= ( <function_name> <rhs_value>* )
1078  <function_name> ::= sym_constant | + | -
1079  (WARNING: might need others besides +, - here if the lexer changes)
1080  <rhs_value> ::= <constant> | <function_call> | <variable>
1081  <constant> ::= sym_constant | int_constant | float_constant
1082  <attr_value_make> ::= ^ <rhs_value> <value_make>+
1083  <value_make> ::= <rhs_value> <preferences>
1084  <variable> ::= variable | lti
1085 
1086  <preferences> ::= [,] | <preference_specifier>+
1087  <preference-specifier> ::= <naturally-unary-preference> [,]
1088  | <forced-unary-preference>
1089  | <binary-preference> <rhs_value> [,]
1090  <naturally-unary-preference> ::= + | - | ! | ~ | @
1091  <binary-preference> ::= > | = | < | &
1092  <any-preference> ::= <naturally-unary-preference> | <binary-preference>
1093  <forced-unary-preference> ::= <binary-preference>
1094  {<any-preference> | , | ) | ^}
1095  ;but the parser shouldn't consume the <any-preference>, ")" or "^"
1096  lexeme here
1097 ===================================================================== */
1098 
1099 const char *help_on_rhs_grammar[] = {
1100 "Grammar for right hand sides of productions:",
1101 "",
1102 " <rhs> ::= <rhs_action>*",
1103 " <rhs_action> ::= ( <variable> <attr_value_make>+ ) | <function_call>",
1104 " <function_call> ::= ( <function_name> <rhs_value>* )",
1105 " <function_name> ::= sym_constant | + | -",
1106 " <rhs_value> ::= <constant> | <function_call> | <variable>",
1107 " <constant> ::= sym_constant | int_constant | float_constant",
1108 " <attr_value_make> ::= ^ <rhs_value> <value_make>+",
1109 " <value_make> ::= <rhs_value> <preferences>",
1110 " <variable> ::= variable | lti",
1111 "",
1112 " <preferences> ::= [,] | <preference_specifier>+",
1113 " <preference-specifier> ::= <naturally-unary-preference> [,]",
1114 " | <forced-unary-preference>",
1115 " | <binary-preference> <rhs_value> [,]",
1116 " <naturally-unary-preference> ::= + | - | ! | ~ | @",
1117 " <binary-preference> ::= > | = | < | &",
1118 " <any-preference> ::= <naturally-unary-preference> | <binary-preference>",
1119 " <forced-unary-preference> ::= <binary-preference> ",
1120 " {<any-preference> | , | ) | ^}",
1121 " ;but the parser shouldn't consume the <any-preference>, \")\" or \"^\"",
1122 " lexeme here",
1123 "",
1124 "See also: lhs-grammar, sp",
1125 0 };
1126 
1127 /* -----------------------------------------------------------------
1128  Parse Function Call After Lparen
1129 
1130  Parses a <function_call> after the "(" has already been consumed.
1131  At entry, the current lexeme should be the function name. Returns
1132  an rhs_value, or NIL if any error occurred.
1133 
1134  <function_call> ::= ( <function_name> <rhs_value>* )
1135  <function_name> ::= sym_constant | + | -
1136  (Warning: might need others besides +, - here if the lexer changes)
1137 ----------------------------------------------------------------- */
1138 
1139 rhs_value parse_rhs_value (agent* thisAgent);
1140 
1142  Bool is_stand_alone_action) {
1143  rhs_function *rf;
1144  Symbol *fun_name;
1145  list *fl;
1146  cons *c, *prev_c;
1147  rhs_value arg_rv;
1148  int num_args;
1149 
1150  /* --- read function name, find the rhs_function structure --- */
1151  if (thisAgent->lexeme.type==PLUS_LEXEME) fun_name = find_sym_constant (thisAgent, "+");
1152  else if (thisAgent->lexeme.type==MINUS_LEXEME) fun_name = find_sym_constant (thisAgent, "-");
1153  else fun_name = find_sym_constant (thisAgent, thisAgent->lexeme.string);
1154  if (!fun_name) {
1155  print (thisAgent, "No RHS function named %s\n",thisAgent->lexeme.string);
1157  return NIL;
1158  }
1159  rf = lookup_rhs_function (thisAgent, fun_name);
1160  if (!rf) {
1161  print (thisAgent, "No RHS function named %s\n",thisAgent->lexeme.string);
1163  return NIL;
1164  }
1165 
1166  /* --- make sure stand-alone/rhs_value is appropriate --- */
1167  if (is_stand_alone_action && (! rf->can_be_stand_alone_action)) {
1168  print (thisAgent, "Function %s cannot be used as a stand-alone action\n",
1169  thisAgent->lexeme.string);
1171  return NIL;
1172  }
1173  if ((! is_stand_alone_action) && (! rf->can_be_rhs_value)) {
1174  print (thisAgent, "Function %s can only be used as a stand-alone action\n",
1175  thisAgent->lexeme.string);
1177  return NIL;
1178  }
1179 
1180  /* --- build list of rhs_function and arguments --- */
1181  allocate_cons (thisAgent, &fl);
1182  fl->first = rf;
1183  prev_c = fl;
1184  get_lexeme(thisAgent); /* consume function name, advance to argument list */
1185  num_args = 0;
1186  while (thisAgent->lexeme.type!=R_PAREN_LEXEME) {
1187  arg_rv = parse_rhs_value (thisAgent);
1188  if (!arg_rv) {
1189  prev_c->rest = NIL;
1191  return NIL;
1192  }
1193  num_args++;
1194  allocate_cons (thisAgent, &c);
1195  c->first = arg_rv;
1196  prev_c->rest = c;
1197  prev_c = c;
1198  }
1199  prev_c->rest = NIL;
1200 
1201  /* --- check number of arguments --- */
1202  if ((rf->num_args_expected != -1) && (rf->num_args_expected != num_args)) {
1203  print (thisAgent, "Wrong number of arguments to function %s (expected %d)\n",
1204  rf->name->sc.name, rf->num_args_expected);
1207  return NIL;
1208  }
1209 
1210  get_lexeme(thisAgent); /* consume the right parenthesis */
1211  return funcall_list_to_rhs_value(fl);
1212 }
1213 
1214 /* -----------------------------------------------------------------
1215  Parse RHS Value
1216 
1217  Parses an <rhs_value>. Returns an rhs_value, or NIL if any error
1218  occurred.
1219 
1220  <rhs_value> ::= <constant> | <function_call> | <variable>
1221  <constant> ::= sym_constant | int_constant | float_constant
1222  <variable> ::= variable | lti
1223 ----------------------------------------------------------------- */
1224 
1226  rhs_value rv;
1227 
1228  if (thisAgent->lexeme.type==L_PAREN_LEXEME) {
1229  get_lexeme(thisAgent);
1230  return parse_function_call_after_lparen (thisAgent, FALSE);
1231  }
1232 
1233  // Check for long term identifier notation
1234  bool id_lti = parse_lti(thisAgent);
1235 
1236  if ((thisAgent->lexeme.type==SYM_CONSTANT_LEXEME) ||
1237  (thisAgent->lexeme.type==INT_CONSTANT_LEXEME) ||
1238  (thisAgent->lexeme.type==FLOAT_CONSTANT_LEXEME) ||
1239  (thisAgent->lexeme.type==VARIABLE_LEXEME) ||
1240  (thisAgent->lexeme.type==IDENTIFIER_LEXEME)) {
1241  // IDENTIFIER_LEXEME only possible if id_lti true due to set_lexer_allow_ids above
1242  rv = symbol_to_rhs_value (make_symbol_for_current_lexeme (thisAgent, id_lti));
1243  get_lexeme(thisAgent);
1244  return rv;
1245  }
1246  print (thisAgent, "Illegal value for RHS value\n");
1248  return FALSE;
1249 }
1250 
1251 
1252 /* -----------------------------------------------------------------
1253  Is Preference Lexeme
1254 
1255  Given a token type, returns TRUE if the token is a preference
1256  lexeme.
1257 
1258 ----------------------------------------------------------------- */
1259 
1261 {
1262  switch (test_lexeme) {
1263 
1264  case PLUS_LEXEME:
1265  return TRUE;
1266  case MINUS_LEXEME:
1267  return TRUE;
1269  return TRUE;
1270  case TILDE_LEXEME:
1271  return TRUE;
1272  case GREATER_LEXEME:
1273  return TRUE;
1274  case EQUAL_LEXEME:
1275  return TRUE;
1276  case LESS_LEXEME:
1277  return TRUE;
1278  case AMPERSAND_LEXEME:
1279  return TRUE;
1280  default:
1281  return FALSE;
1282  }
1283 }
1284 
1285 /* -----------------------------------------------------------------
1286  Parse Preference Specifier Without Referent
1287 
1288  Parses a <preference-specifier>. Returns the appropriate
1289  xxx_PREFERENCE_TYPE (see soarkernel.h).
1290 
1291  Note: in addition to the grammar below, if there is no preference
1292  specifier given, then this routine returns ACCEPTABLE_PREFERENCE_TYPE.
1293  Also, for <binary-preference>'s, this routine *does not* read the
1294  <rhs_value> referent. This must be done by the caller routine.
1295 
1296  <preference-specifier> ::= <naturally-unary-preference> [,]
1297  | <forced-unary-preference>
1298  | <binary-preference> <rhs_value> [,]
1299  <naturally-unary-preference> ::= + | - | ! | ~ | @
1300  <binary-preference> ::= > | = | < | &
1301  <any-preference> ::= <naturally-unary-preference> | <binary-preference>
1302  <forced-unary-preference> ::= <binary-preference>
1303  {<any-preference> | , | ) | ^}
1304  ;but the parser shouldn't consume the <any-preference>, ")" or "^"
1305  lexeme here
1306 ----------------------------------------------------------------- */
1307 
1309  switch (thisAgent->lexeme.type) {
1310 
1311  case PLUS_LEXEME:
1312  get_lexeme(thisAgent);
1313  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1315 
1316  case MINUS_LEXEME:
1317  get_lexeme(thisAgent);
1318  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1319  return REJECT_PREFERENCE_TYPE;
1320 
1322  get_lexeme(thisAgent);
1323  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1324  return REQUIRE_PREFERENCE_TYPE;
1325 
1326  case TILDE_LEXEME:
1327  get_lexeme(thisAgent);
1328  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1329  return PROHIBIT_PREFERENCE_TYPE;
1330 
1331 /****************************************************************************
1332  * [Soar-Bugs #55] <forced-unary-preference> ::= <binary-preference>
1333  * {<any-preference> | , | ) | ^}
1334  *
1335  * Forced unary preferences can now occur when a binary preference is
1336  * followed by a ",", ")", "^" or any preference specifier
1337  ****************************************************************************/
1338 
1339  case GREATER_LEXEME:
1340  get_lexeme(thisAgent);
1341  if ((thisAgent->lexeme.type!=COMMA_LEXEME) &&
1342  (thisAgent->lexeme.type!=R_PAREN_LEXEME) &&
1343  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
1344  (!is_preference_lexeme(thisAgent->lexeme.type)))
1345  return BETTER_PREFERENCE_TYPE;
1346  /* --- forced unary preference --- */
1347  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1348  return BEST_PREFERENCE_TYPE;
1349 
1350  case EQUAL_LEXEME:
1351  get_lexeme(thisAgent);
1352  if ((thisAgent->lexeme.type!=COMMA_LEXEME) &&
1353  (thisAgent->lexeme.type!=R_PAREN_LEXEME) &&
1354  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
1355  (!is_preference_lexeme(thisAgent->lexeme.type)))
1356  {
1357 
1358  if ((thisAgent->lexeme.type == INT_CONSTANT_LEXEME) ||
1359  (thisAgent->lexeme.type == FLOAT_CONSTANT_LEXEME))
1361  else
1363  }
1364 
1365  /* --- forced unary preference --- */
1366  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1368 
1369  case LESS_LEXEME:
1370  get_lexeme(thisAgent);
1371  if ((thisAgent->lexeme.type!=COMMA_LEXEME) &&
1372  (thisAgent->lexeme.type!=R_PAREN_LEXEME) &&
1373  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
1374  (!is_preference_lexeme(thisAgent->lexeme.type)))
1375  return WORSE_PREFERENCE_TYPE;
1376  /* --- forced unary preference --- */
1377  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1378  return WORST_PREFERENCE_TYPE;
1379 
1380  case AMPERSAND_LEXEME:
1381  get_lexeme(thisAgent);
1382  if ((thisAgent->lexeme.type!=COMMA_LEXEME) &&
1383  (thisAgent->lexeme.type!=R_PAREN_LEXEME) &&
1384  (thisAgent->lexeme.type!=UP_ARROW_LEXEME) &&
1385  (!is_preference_lexeme(thisAgent->lexeme.type)))
1387  /* --- forced unary preference --- */
1388  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1390 
1391  default:
1392  /* --- if no preference given, make it an acceptable preference --- */
1394  } /* end of switch statement */
1395 }
1396 
1397 /* -----------------------------------------------------------------
1398  Parse Preferences
1399 
1400  Given the id, attribute, and value already read, this routine
1401  parses zero or more <preference-specifier>'s. It builds and
1402  returns an action list for these RHS make's. It returns NIL if
1403  any error occurred.
1404 
1405  <value_make> ::= <rhs_value> <preferences>
1406  <preferences> ::= [,] | <preference_specifier>+
1407  <preference-specifier> ::= <naturally-unary-preference> [,]
1408  | <forced-unary-preference>
1409  | <binary-preference> <rhs_value> [,]
1410 ----------------------------------------------------------------- */
1411 
1413  rhs_value attr, rhs_value value) {
1414  action *a;
1415  action *prev_a;
1416  rhs_value referent;
1417  byte preference_type;
1418  Bool saw_plus_sign;
1419 
1420  /* --- Note: this routine is set up so if there's not preference type
1421  indicator at all, we return a single acceptable preference make --- */
1422 
1423  prev_a = NIL;
1424 
1425  saw_plus_sign = (thisAgent->lexeme.type==PLUS_LEXEME);
1426  preference_type = parse_preference_specifier_without_referent (thisAgent);
1427  if ((preference_type==ACCEPTABLE_PREFERENCE_TYPE) && (! saw_plus_sign)) {
1428  /* If the routine gave us a + pref without seeing a + sign, then it's
1429  just giving us the default acceptable preference. Look for optional
1430  comma. */
1431  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1432  }
1433 
1434  while (TRUE) {
1435  /* --- read referent --- */
1436  if (preference_is_binary(preference_type)) {
1437  referent = parse_rhs_value(thisAgent);
1438  if (! referent) {
1439  deallocate_action_list (thisAgent, prev_a);
1440  return NIL;
1441  }
1442  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1443  } else {
1444  referent = NIL; /* unnecessary, but gcc -Wall warns without it */
1445  }
1446 
1447  /* --- create the appropriate action --- */
1448  allocate_with_pool (thisAgent, &thisAgent->action_pool, &a);
1449  a->next = prev_a;
1450  prev_a = a;
1451  a->type = MAKE_ACTION;
1452  a->preference_type = preference_type;
1453  a->id = symbol_to_rhs_value(id);
1454  symbol_add_ref (id);
1455  a->attr = copy_rhs_value (thisAgent, attr);
1456  a->value = copy_rhs_value (thisAgent, value);
1457  if (preference_is_binary(preference_type)) a->referent = referent;
1458 
1459  /* --- look for another preference type specifier --- */
1460  saw_plus_sign = (thisAgent->lexeme.type==PLUS_LEXEME);
1461  preference_type = parse_preference_specifier_without_referent (thisAgent);
1462 
1463  /* --- exit loop when done reading preferences --- */
1464  if ((preference_type==ACCEPTABLE_PREFERENCE_TYPE) && (! saw_plus_sign))
1465  /* If the routine gave us a + pref without seeing a + sign, then it's
1466  just giving us the default acceptable preference, it didn't see any
1467  more preferences specified. */
1468  return prev_a;
1469  }
1470 }
1471 
1472 /* KJC begin: 10.09.98 */
1473 /* modified 3.99 to take out parallels and only create acceptables */
1474 /* -----------------------------------------------------------------
1475  Parse Preferences for Soar8 Non-Operators
1476 
1477  Given the id, attribute, and value already read, this routine
1478  parses zero or more <preference-specifier>'s. If preferences
1479  other than reject and acceptable are specified, it prints
1480  a warning message that they are being ignored. It builds an
1481  action list for creating an ACCEPTABLE preference. If binary
1482  preferences are encountered, a warning message is printed and
1483  the production is ignored (returns NIL). It returns NIL if any
1484  other error occurred.
1485 
1486  <value_make> ::= <rhs_value> <preferences>
1487  <preferences> ::= [,] | <preference_specifier>+
1488  <preference-specifier> ::= <naturally-unary-preference> [,]
1489  | <forced-unary-preference>
1490  | <binary-preference> <rhs_value> [,]
1491 ----------------------------------------------------------------- */
1492 
1494  rhs_value attr, rhs_value value)
1495 {
1496  action *a;
1497  action *prev_a;
1498  rhs_value referent;
1499  byte preference_type;
1500  Bool saw_plus_sign;
1501 
1502  /* JC ADDED: for printint */
1503  char szPrintAttr[256];
1504  char szPrintValue[256];
1505  char szPrintId[256];
1506 
1507  /* --- Note: this routine is set up so if there's not preference type
1508  indicator at all, we return an acceptable preference make
1509  and a parallel preference make. For non-operators, allow
1510  only REJECT_PREFERENCE_TYPE, (and UNARY_PARALLEL and ACCEPTABLE).
1511  If any other preference type indicator is found, a warning or
1512  error msg (error only on binary prefs) is printed. --- */
1513 
1514  prev_a = NIL;
1515 
1516  saw_plus_sign = (thisAgent->lexeme.type==PLUS_LEXEME);
1517  preference_type = parse_preference_specifier_without_referent (thisAgent);
1518  if ((preference_type==ACCEPTABLE_PREFERENCE_TYPE) && (! saw_plus_sign)) {
1519  /* If the routine gave us a + pref without seeing a + sign, then it's
1520  just giving us the default acceptable preference. Look for optional
1521  comma. */
1522  if (thisAgent->lexeme.type==COMMA_LEXEME) get_lexeme(thisAgent);
1523  }
1524 
1525  while (TRUE) {
1526  /* step through the pref list, print warning messages when necessary. */
1527 
1528  /* --- read referent --- */
1529  if (preference_is_binary(preference_type))
1530  {
1531  print (thisAgent, "\nERROR: in Soar8, binary preference illegal for non-operator.");
1532 
1533  /* JC BUG FIX: Have to check to make sure that the rhs_values are converted to strings
1534  correctly before we print */
1535  rhs_value_to_string(thisAgent, attr, szPrintAttr, 256);
1536  rhs_value_to_string(thisAgent, value, szPrintValue, 256);
1537  symbol_to_string(thisAgent, id, TRUE, szPrintId, 256);
1538  print(thisAgent, "id = %s\t attr = %s\t value = %s\n", szPrintId, szPrintAttr, szPrintValue);
1539 
1540  deallocate_action_list (thisAgent, prev_a);
1541  return NIL;
1542 
1543  } else {
1544  referent = NIL; /* unnecessary, but gcc -Wall warns without it */
1545  }
1546 
1547  if ( (preference_type != ACCEPTABLE_PREFERENCE_TYPE) &&
1548  (preference_type != REJECT_PREFERENCE_TYPE) ) {
1549  print (thisAgent, "\nWARNING: in Soar8, the only allowable non-operator preference \nis REJECT - .\nIgnoring specified preferences.\n");
1550  xml_generate_warning(thisAgent, "WARNING: in Soar8, the only allowable non-operator preference \nis REJECT - .\nIgnoring specified preferences.");
1551 
1552  /* JC BUG FIX: Have to check to make sure that the rhs_values are converted to strings
1553  correctly before we print */
1554  rhs_value_to_string(thisAgent, attr, szPrintAttr, 256);
1555  rhs_value_to_string(thisAgent, value, szPrintValue, 256);
1556  symbol_to_string(thisAgent, id, TRUE, szPrintId, 256);
1557  print(thisAgent, "id = %s\t attr = %s\t value = %s\n", szPrintId, szPrintAttr, szPrintValue);
1558 
1560  }
1561 
1562  if (preference_type == REJECT_PREFERENCE_TYPE) {
1563  /* --- create the appropriate action --- */
1564  allocate_with_pool (thisAgent, &thisAgent->action_pool, &a);
1565  a->next = prev_a;
1566  prev_a = a;
1567  a->type = MAKE_ACTION;
1568  a->preference_type = preference_type;
1569  a->id = symbol_to_rhs_value(id);
1570  symbol_add_ref (id);
1571  a->attr = copy_rhs_value (thisAgent, attr);
1572  a->value = copy_rhs_value (thisAgent, value);
1573  }
1574 
1575  /* --- look for another preference type specifier --- */
1576  saw_plus_sign = (thisAgent->lexeme.type==PLUS_LEXEME);
1577  preference_type = parse_preference_specifier_without_referent (thisAgent);
1578 
1579  /* --- exit loop when done reading preferences --- */
1580  if ((preference_type==ACCEPTABLE_PREFERENCE_TYPE) && (! saw_plus_sign)) {
1581  /* If the routine gave us a + pref without seeing a + sign, then it's
1582  just giving us the default acceptable preference, it didn't see any
1583  more preferences specified. */
1584 
1585  /* for soar8, if this wasn't a REJECT preference, then
1586  create acceptable preference makes. */
1587  if (prev_a == NIL) {
1588 
1589  allocate_with_pool (thisAgent, &thisAgent->action_pool, &a);
1590  a->next = prev_a;
1591  prev_a = a;
1592  a->type = MAKE_ACTION;
1594  a->id = symbol_to_rhs_value(id);
1595  symbol_add_ref (id);
1596  a->attr = copy_rhs_value (thisAgent, attr);
1597  a->value = copy_rhs_value (thisAgent, value);
1598  }
1599  return prev_a;
1600  }
1601  }
1602 }
1603 /* KJC end: 10.09.98 */
1604 
1605 /* -----------------------------------------------------------------
1606  Parse Attr Value Make
1607 
1608  Given the id already read, this routine parses an <attr_value_make>.
1609  It builds and returns an action list for these RHS make's. It
1610  returns NIL if any error occurred.
1611 
1612  <attr_value_make> ::= ^ <rhs_value> <value_make>+
1613  <value_make> ::= <rhs_value> <preferences>
1614 ----------------------------------------------------------------- */
1615 
1617 {
1618  rhs_value attr, value;
1619  action *all_actions, *new_actions, *last;
1620  Symbol *old_id, *new_var;
1621 
1622  /* JC Added, need to store the attribute name */
1623  char szAttribute[256];
1624 
1625  if (thisAgent->lexeme.type!=UP_ARROW_LEXEME) {
1626  print (thisAgent, "Expected ^ in RHS make action\n");
1628  return NIL;
1629  }
1630  old_id = id;
1631 
1632  get_lexeme(thisAgent); /* consume up-arrow, advance to attribute */
1633  attr = parse_rhs_value(thisAgent);
1634  if (! attr)
1635  return NIL;
1636 
1637  /* JC Added, we will need the attribute as a string, so we get it here */
1638  rhs_value_to_string(thisAgent, attr, szAttribute, 256);
1639 
1640  all_actions = NIL;
1641 
1642  /* allow dot notation "." in RHS attribute path 10/15/98 KJC */
1643  while (thisAgent->lexeme.type == PERIOD_LEXEME)
1644  {
1645  get_lexeme(thisAgent); /* consume the "." */
1646 
1647  /* set up for next attribute in path: make dummy variable,
1648  and create new action in the path */
1649  new_var = make_placeholder_var(thisAgent, first_letter_from_rhs_value (attr));
1650 
1651  /* parse_preferences actually creates the action. eventhough
1652  there aren't really any preferences to read, we need the default
1653  acceptable and parallel prefs created for all attributes in path */
1654  if(strcmp(szAttribute,"operator") != 0)
1655  {
1656  new_actions = parse_preferences_soar8_non_operator (thisAgent, id, attr,
1657  symbol_to_rhs_value(new_var));
1658  }
1659  else
1660  {
1661  new_actions = parse_preferences (thisAgent, id, attr, symbol_to_rhs_value(new_var));
1662  }
1663 
1664  for (last=new_actions; last->next!=NIL; last=last->next)
1665  /* continue */;
1666 
1667  last->next = all_actions;
1668  all_actions = new_actions;
1669 
1670  /* NLD Added. Once we create an action for the
1671  intermediate dot notation attribute/value pair,
1672  remove references for each. Prevents memory
1673  leaks for rules containing dot notation. */
1674  deallocate_rhs_value (thisAgent, attr);
1675  symbol_remove_ref(thisAgent, new_var);
1676 
1677  /* if there was a "." then there must be another attribute
1678  set id for next action and get the next attribute */
1679  id = new_var;
1680  attr = parse_rhs_value(thisAgent);
1681  if (! attr)
1682  return NIL;
1683 
1684  /* JC Added. We need to get the new attribute's name */
1685  rhs_value_to_string(thisAgent, attr, szAttribute, 256);
1686  }
1687  /* end of while (thisAgent->lexeme.type == PERIOD_LEXEME */
1688  /* end KJC 10/15/98 */
1689 
1690  do {
1691  value = parse_rhs_value(thisAgent);
1692  if (!value) {
1693  deallocate_rhs_value (thisAgent, attr);
1694  deallocate_action_list (thisAgent, all_actions);
1695  return NIL;
1696  }
1697  if(strcmp(szAttribute,"operator") != 0)
1698  {
1699  new_actions = parse_preferences_soar8_non_operator (thisAgent, id, attr, value);
1700  }
1701  else
1702  {
1703  new_actions = parse_preferences (thisAgent, id, attr, value);
1704  }
1705  deallocate_rhs_value (thisAgent, value);
1706  if (!new_actions) {
1707  deallocate_rhs_value (thisAgent, attr);
1708  return NIL;
1709  }
1710  for (last=new_actions; last->next!=NIL; last=last->next);
1711  last->next = all_actions;
1712  all_actions = new_actions;
1713  } while ((thisAgent->lexeme.type!=R_PAREN_LEXEME) &&
1714  (thisAgent->lexeme.type!=UP_ARROW_LEXEME));
1715 
1716  deallocate_rhs_value (thisAgent, attr);
1717  return all_actions;
1718 }
1719 
1720 /* -----------------------------------------------------------------
1721  Parse RHS Action
1722 
1723  Parses an <rhs_action> and returns an action list. If any error
1724  occurrs, NIL is returned.
1725 
1726  <rhs_action> ::= ( <variable> <attr_value_make>+ ) | <function_call>
1727 ----------------------------------------------------------------- */
1728 
1730  action *all_actions, *new_actions, *last;
1731  Symbol *var = NULL;
1732  rhs_value funcall_value;
1733 
1734  if (thisAgent->lexeme.type!=L_PAREN_LEXEME) {
1735  print (thisAgent, "Expected ( to begin RHS action\n");
1737  return NIL;
1738  }
1739  get_lexeme(thisAgent);
1740 
1741  // Check for long term identifier notation
1742  bool id_lti = parse_lti(thisAgent);
1743 
1744  if ((thisAgent->lexeme.type!=VARIABLE_LEXEME) && (thisAgent->lexeme.type!=IDENTIFIER_LEXEME)) {
1745  /* --- the action is a function call --- */
1746  funcall_value = parse_function_call_after_lparen (thisAgent, TRUE);
1747  if (!funcall_value) return NIL;
1748  allocate_with_pool (thisAgent, &thisAgent->action_pool, &all_actions);
1749  all_actions->type = FUNCALL_ACTION;
1750  all_actions->next = NIL;
1751  all_actions->value = funcall_value;
1752  return all_actions;
1753  }
1754  /* --- the action is a regular make action --- */
1755  if (id_lti) {
1756  smem_lti_id lti_id = smem_lti_get_id( thisAgent, thisAgent->lexeme.id_letter, thisAgent->lexeme.id_number );
1757 
1758  if ( lti_id == NIL ) {
1759  char msg[BUFFER_MSG_SIZE];
1760  strncpy(msg, "parser.c: Internal error: invalid long-term identifier found in make_symbol_for_current_lexeme\n", BUFFER_MSG_SIZE);
1761  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
1762  abort_with_fatal_error(thisAgent, msg);
1763  }
1764  else {
1765  var = smem_lti_soar_make( thisAgent, lti_id, thisAgent->lexeme.id_letter, thisAgent->lexeme.id_number, SMEM_LTI_UNKNOWN_LEVEL );
1766  }
1767  }
1768  else {
1769  var = make_variable (thisAgent, thisAgent->lexeme.string);
1770  }
1771 
1772  get_lexeme(thisAgent);
1773  all_actions = NIL;
1774  while (thisAgent->lexeme.type!=R_PAREN_LEXEME) {
1775  new_actions = parse_attr_value_make (thisAgent, var);
1776  if (new_actions) {
1777  for (last=new_actions; last->next!=NIL; last=last->next);
1778  last->next = all_actions;
1779  all_actions = new_actions;
1780  } else {
1781  symbol_remove_ref (thisAgent, var);
1782  deallocate_action_list (thisAgent, all_actions);
1783  return NIL;
1784  }
1785  }
1786  get_lexeme(thisAgent); /* consume the right parenthesis */
1787  symbol_remove_ref (thisAgent, var);
1788  return all_actions;
1789 }
1790 
1791 bool parse_lti(agent* thisAgent) {
1792  switch(thisAgent->lexeme.type) {
1793  case AT_LEXEME:
1794  {
1795  Bool saved = get_lexer_allow_ids(thisAgent);
1796  set_lexer_allow_ids(thisAgent, true);
1797  get_lexeme(thisAgent);
1798  set_lexer_allow_ids(thisAgent, saved);
1799  }
1800  return true;
1801 
1802  default:
1803  break;
1804  }
1805  return false;
1806 }
1807 
1808 /* -----------------------------------------------------------------
1809  Parse RHS
1810 
1811  Parses the <rhs> and sets *dest_rhs to the resulting action list.
1812  Returns TRUE if successful, FALSE if any error occurred.
1813 
1814  <rhs> ::= <rhs_action>*
1815 ----------------------------------------------------------------- */
1816 
1817 Bool parse_rhs (agent* thisAgent, action **dest_rhs) {
1818  action *all_actions, *new_actions, *last;
1819 
1820  all_actions = NIL;
1821  while (thisAgent->lexeme.type!=R_PAREN_LEXEME) {
1822  new_actions = parse_rhs_action (thisAgent);
1823  if (new_actions) {
1824  for (last=new_actions; last->next!=NIL; last=last->next);
1825  last->next = all_actions;
1826  all_actions = new_actions;
1827  } else {
1828  deallocate_action_list (thisAgent, all_actions);
1829  return FALSE;
1830  }
1831  }
1832  *dest_rhs = all_actions;
1833  return TRUE;
1834 }
1835 
1836 
1837 
1838 
1839 /* =================================================================
1840  Destructively Reverse Action List
1841 
1842  As the parser builds the action list for the RHS, it adds each new
1843  action onto the front of the list. This results in the order of
1844  the actions getting reversed. This has certain problems--for example,
1845  if there are several (write) actions on the RHS, reversing their order
1846  means the output lines get printed in the wrong order. To avoid this
1847  problem, we reverse the list after building it.
1848 
1849  This routine destructively reverses an action list.
1850 ================================================================= */
1851 
1853  action *prev, *current, *next;
1854 
1855  prev = NIL;
1856  current = a;
1857  while (current) {
1858  next = current->next;
1859  current->next = prev;
1860  prev = current;
1861  current = next;
1862  }
1863  return prev;
1864 }
1865 
1866 
1867 /* =================================================================
1868  Parse Production
1869 
1870  This routine reads a production (everything inside the body of the
1871  "sp" command), builds a production, and adds it to the rete.
1872 
1873  If successful, it returns a pointer to the new production struct.
1874  If any error occurred, it returns NIL (and may or may not read
1875  the rest of the body of the sp).
1876 ================================================================= */
1877 
1878 production *parse_production (agent* thisAgent, unsigned char* rete_addition_result) {
1879  Symbol *name;
1880  char *documentation;
1881  condition *lhs, *lhs_top, *lhs_bottom;
1882  action *rhs;
1883  production *p;
1884  byte declared_support;
1885  byte prod_type;
1886 
1887  // voigtjr: added to parameter list so that CLI can ignore the error
1888  // of a duplicate production with a different name
1889  //byte rete_addition_result;
1890  Bool rhs_okay;
1891  Bool interrupt_on_match;
1892 
1894 
1895  /* --- read production name --- */
1896  if (thisAgent->lexeme.type!=SYM_CONSTANT_LEXEME) {
1897  print (thisAgent, "Expected symbol for production name\n");
1899  return NIL;
1900  }
1901  name = make_sym_constant (thisAgent, thisAgent->lexeme.string);
1902  get_lexeme(thisAgent);
1903 
1904  /* --- if there's already a prod with this name, excise it --- */
1905  if (name->sc.production) {
1906  excise_production (thisAgent, name->sc.production, (TRUE && thisAgent->sysparams[TRACE_LOADING_SYSPARAM]));
1907  }
1908 
1909  /* --- read optional documentation string --- */
1910  if (thisAgent->lexeme.type==QUOTED_STRING_LEXEME) {
1911  documentation = make_memory_block_for_string (thisAgent, thisAgent->lexeme.string);
1912  get_lexeme(thisAgent);
1913  } else {
1914  documentation = NIL;
1915  }
1916 
1917  /* --- read optional flags --- */
1918  declared_support = UNDECLARED_SUPPORT;
1919  prod_type = USER_PRODUCTION_TYPE;
1920  interrupt_on_match = FALSE;
1921  while (TRUE) {
1922  if (thisAgent->lexeme.type!=SYM_CONSTANT_LEXEME) break;
1923  if (!strcmp(thisAgent->lexeme.string,":o-support")) {
1924  declared_support = DECLARED_O_SUPPORT;
1925  get_lexeme(thisAgent);
1926  continue;
1927  }
1928  if (!strcmp(thisAgent->lexeme.string,":i-support")) {
1929  declared_support = DECLARED_I_SUPPORT;
1930  get_lexeme(thisAgent);
1931  continue;
1932  }
1933  if (!strcmp(thisAgent->lexeme.string,":chunk")) {
1934  prod_type = CHUNK_PRODUCTION_TYPE;
1935  get_lexeme(thisAgent);
1936  continue;
1937  }
1938  if (!strcmp(thisAgent->lexeme.string,":default")) {
1939  prod_type = DEFAULT_PRODUCTION_TYPE;
1940  get_lexeme(thisAgent);
1941  continue;
1942  }
1943  if (!strcmp(thisAgent->lexeme.string,":template")) {
1944  prod_type = TEMPLATE_PRODUCTION_TYPE;
1945  get_lexeme(thisAgent);
1946  continue;
1947  }
1948  if (!strcmp(thisAgent->lexeme.string, ":interrupt")) {
1949  interrupt_on_match = TRUE;
1950  get_lexeme(thisAgent);
1951  continue;
1952  }
1953  break;
1954  } /* end of while (TRUE) */
1955 
1956  /* --- read the LHS --- */
1957  lhs = parse_lhs(thisAgent);
1958  if (! lhs) {
1959  print_with_symbols (thisAgent, "(Ignoring production %y)\n\n", name);
1960  if (documentation) free_memory_block_for_string (thisAgent, documentation);
1961  symbol_remove_ref (thisAgent, name);
1962 /* if (! reading_from_top_level()) respond_to_load_errors (); AGR 527c */
1963  return NIL;
1964  }
1965 
1966  /* --- read the "-->" --- */
1967  if (thisAgent->lexeme.type!=RIGHT_ARROW_LEXEME) {
1968  print (thisAgent, "Expected --> in production\n");
1970  print_with_symbols (thisAgent, "(Ignoring production %y)\n\n", name);
1971  if (documentation) free_memory_block_for_string (thisAgent, documentation);
1972  symbol_remove_ref (thisAgent, name);
1973  deallocate_condition_list (thisAgent, lhs);
1974 /* if (! reading_from_top_level()) respond_to_load_errors (); AGR 527c */
1975  return NIL;
1976  }
1977  get_lexeme(thisAgent);
1978 
1979  /* --- read the RHS --- */
1980  rhs_okay = parse_rhs (thisAgent, &rhs);
1981  if (!rhs_okay) {
1982  print_with_symbols (thisAgent, "(Ignoring production %y)\n\n", name);
1983  if (documentation) free_memory_block_for_string (thisAgent, documentation);
1984  symbol_remove_ref (thisAgent, name);
1985  deallocate_condition_list (thisAgent, lhs);
1986 /* if (! reading_from_top_level()) respond_to_load_errors (); AGR 527c */
1987  return NIL;
1988  }
1990 
1991  /* --- finally, make sure there's a closing right parenthesis (but
1992  don't consume it) --- */
1993  if (thisAgent->lexeme.type!=R_PAREN_LEXEME) {
1994  print (thisAgent, "Expected ) to end production\n");
1996  if (documentation) free_memory_block_for_string (thisAgent, documentation);
1997  print_with_symbols (thisAgent, "(Ignoring production %y)\n\n", name);
1998  symbol_remove_ref (thisAgent, name);
1999  deallocate_condition_list (thisAgent, lhs);
2000  deallocate_action_list (thisAgent, rhs);
2001 /* if (! reading_from_top_level()) respond_to_load_errors (); AGR 527c */
2002  return NIL;
2003  }
2004 
2005  /* --- replace placeholder variables with real variables --- */
2006  reset_variable_generator (thisAgent, lhs, rhs);
2009 
2010  /* --- everything parsed okay, so make the production structure --- */
2011  lhs_top = lhs;
2012  for (lhs_bottom=lhs; lhs_bottom->next!=NIL; lhs_bottom=lhs_bottom->next);
2013  p = make_production (thisAgent, prod_type, name, &lhs_top, &lhs_bottom, &rhs, TRUE);
2014 
2015  if (!p) {
2016  if (documentation) free_memory_block_for_string (thisAgent, documentation);
2017  print_with_symbols (thisAgent, "(Ignoring production %y)\n\n", name);
2018  symbol_remove_ref (thisAgent, name);
2019  deallocate_condition_list (thisAgent, lhs_top);
2020  deallocate_action_list (thisAgent, rhs);
2021 /* if (! reading_from_top_level()) respond_to_load_errors (); AGR 527c */
2022  return NIL;
2023  }
2024 
2025  if ( prod_type == TEMPLATE_PRODUCTION_TYPE )
2026  {
2027  if ( !rl_valid_template( p ) )
2028  {
2029  print_with_symbols( thisAgent, "Invalid Soar-RL template (%y)\n\n", name );
2030  excise_production( thisAgent, p, false );
2031  return NIL;
2032  }
2033  }
2034 
2035  p->documentation = documentation;
2036  p->declared_support = declared_support;
2037  p->interrupt = interrupt_on_match;
2038  *rete_addition_result = add_production_to_rete (thisAgent, p, lhs_top, NIL, TRUE);
2039  deallocate_condition_list (thisAgent, lhs_top);
2040 
2041  if (*rete_addition_result==DUPLICATE_PRODUCTION) {
2042  excise_production (thisAgent, p, FALSE);
2043  p = NIL;
2044  }
2045 
2046  if ( p && p->rl_rule && p->documentation )
2047  {
2048  rl_rule_meta( thisAgent, p );
2049  }
2050 
2051  return p;
2052 }
2053 
2054 /* =================================================================
2055  Init Parser
2056 
2057  This routine initializes the parser. At present, all it does is
2058  set up the help screens for the LHS and RHS grammars.
2059 ================================================================= */
2060 
2061 /*
2062  This is not longer used.
2063 
2064 void init_parser (void) {
2065  add_help (thisAgent, "lhs-grammar", help_on_lhs_grammar);
2066  add_help (thisAgent, "rhs-grammar", help_on_rhs_grammar);
2067 }
2068 */
2069