Go backward to Adding Input and Output routines.
Go up to I/O.
Go forward to Adding user interface commands.
Adding RHS functions
====================
You can define new actions for a production. This requires writing new C
code. You cannot create new predicates for the conditions of a production.
To illustrate the method for creating new production actions, we will add a
square-it function that takes a number (say x) and returns ^{2}can be either
an integer or a floating-point number, and the returned value will be of the
same type as x.
The first thing you have to decide about an RHS function is whether it can be
a *stand-alone* function, or a *value* (or both). Stand-alone function are
things like (write) and (halt), which appear on the RHS as entire actions all
by themselves. Value functions, on the other hand, are used as values within
other actions or function calls. For example:
...
-->
(write (crlf) |Hello there, the new count is | (+ <n> 1))
(<s> ^count (+ <n> 1))
(halt))
The + and crlf functions are value functions, not stand-alone --- it doesn't
make much sense to use the + function all by itself. (One could envision a
stand-alone version of crlf, but in Soar it has to be an argument to write.)
Obviously our square-it function will be a value function, not stand-alone.
The second thing to decide is how many arguments your new RHS function can
take. Some functions, like our square-it function, know exactly how many
arguments they expect (in our case, just 1). Other functions, like write or
+, take any number of arguments.
Having decided these two things, we can now proceed to write our function.
This involves (1) adding a line to system_startup_hook() in file hooks.c that
informs Soar that our new RHS function is available, and (2) writing a C
function that implements the new RHS function.
First, we alter hooks.c as follows:
/* ADD THE FOLLOWING DECLARATION */
extern Symbol *my_square_it_function (list *args);
void system_startup_hook (void) {
...etc...
/* ADD THE FOLLOWING LINE */
add_rhs_function (make_sym_constant("square-it"),
my_square_it_function,
1,
TRUE,
FALSE);
...etc...
}
Add_rhs_function() is the Soar routine you call to tell Soar that you've got
this RHS function you want to make available. It takes five parameters: (1)
a pointer to a symbol whose name is the name of the RHS function; (2) the C
function you're about to write, which implements the new RHS function; (3) an
integer indicating how many arguments the RHS function expects---if it takes
any number of arguments, use -1 for this parameter; (4) a flag (TRUE or
FALSE) indicating whether it can be a value function, and (5) a flag
indicating whether it can be a stand-alone action. (At lease one of the last
two must be TRUE.)
Now we'll write my_square_it_function, which will implement square-it. When
an RHS function is used, Soar invokes the corresponding C function with one
parameter, a linked list of the arguments to the RHS function. This linked
list is a list of cons structures, each of which has a first field (pointing
to the argument) and a rest field (pointing to the next cons in the list, or
set to NIL (i.e., 0) if this is the last one). (Sound familiar?) Each
argument is a symbol.
The C function's job is to examine this list, and create and return a symbol
representing the "result value" of the RHS function call. If it's a
stand-alone function---so it has no "result value"---or if there isn't a
result value because of some error condition, then the C function should
return NIL (i.e., the NULL pointer) instead.
Here's how we'd do our square-it RHS function:
Symbol *my_square_it_function (list *args) {
Symbol *x_sym;
/* --- get the one argument --- */
x_sym = args->first;
/* --- What to do? It depends on the type of x. --- */
if (x_sym->common.symbol_type == INT_CONSTANT_SYMBOL_TYPE) {
/* --- x is an integer, so return an integer --- */
return make_int_constant (x_sym->ic.value * x_sym->ic.value);
}
if (x_sym->common.symbol_type == FLOAT_CONSTANT_SYMBOL_TYPE) {
/* --- x is floating-point, so return a floating-point --- */
return make_float_constant (x_sym->fc.value * x_sym->fc.value);
}
/* --- otherwise x isn't a number, so we have an error --- */
print_with_symbols ("Error: non-number (%y) passed to square-it\n",
x_sym);
return NIL; /* don't want to return a "real" value */
}
Recall from Section See Basics of adding C code that common.symbol_type,
ic.value, and fc.value are fields within a symbol structure. The
make_int_constant() and make_float_constant() functions are Soar functions
that create symbols of those types (or find existing symbols if they're
already around). The function make_sym_constant() creates symbolic
constants: make_sym_constant("foo").
For some other examples of RHS functions, see the file rhsfun.c. Most of the
functions there are pretty straightforward.