Go backward to General procedure.
Go up to Basics of adding C code.
Go forward to Utility functions.
The symbol table
----------------
Whether you're adding I/O routines, RHS functions, or interface commands,
you'll probably need to use the symbol table. Having to use the symbol table
is the main thing that makes it tricky to add code to Soar, and if you don't
do this right, your code may well cause a core dump, so be careful. There
are five types of symbols:
* symbolic constants (e.g., problem-space)
* integer constants (e.g., 37)
* floating-point constants (e.g., 6.02e+23)
* identifiers (e.g., G37)
* variables (e.g., <sg>)
The symbol table entry for every symbol is a structure; what fields this
structure contains depends on the type of symbol. Your code will be using
pointers to these symbol structures. The complete structures are defined in
soar.h, but you'll probably only need to know a few fields. Given a pointer
(say, declared as "Symbol *sym;") to a symbol:
* sym->common.symbol_type indicates what type of symbol it is. This field
is equal to one of five constants defined in soar.h:
* SYM_CONSTANT_SYMBOL_TYPE
* INT_CONSTANT_SYMBOL_TYPE
* FLOAT_CONSTANT_SYMBOL_TYPE
* IDENTIFIER_SYMBOL_TYPE
* VARIABLE_SYMBOL_TYPE
* For symbolic constants, sym->sc.name is a pointer (char *), which points
to the symbol's name (a null-terminated string of characters).
* For integer constants, sym->ic.value is of type long, and indicates the
integer value.
* For floating-point constants, sym->fc.value is of type float, and
indicates the floating-point value.
* For identifiers, sym->id.name_letter is of type char, and indicates the
first letter of the identifier's name. sym->id.name_number is of type
unsigned long and indicates the identifier's number. For example, for
G37, name_letter is `G' and name_number is 37.
* For variables, sym->var.name is a pointer (char *), that points to the
variable's name (a null-terminated string of characters).
Your code can obtain symbols in two ways: (1) it may get handed symbols
(actually, pointers to symbols) as parameters when it gets called by Soar, or
(2) it may invoke Soar functions that manipulate the symbol table to create
its own symbols. (More on this in later sections.)
*Important: your code should treat these symbol structures as
read-only.* Attempting to write into one of them could cause nasty things to
happen.
The symbol table mechanism guarantees that there are never simultaneously two
different structures for the same type of symbol with the same name or value.
So to compare symbols, you can just use C's "==" test on the pointers to
them:
Symbol *sym1, *sym2;
...
/* check whether sym1 and sym2 are identical */
if (sym1 == sym2) print ("They're the same!\n");
...
Soar does the garbage collection of "dead" symbols via reference-counting,
not stop-and-copy. This means symbols never get moved in memory. Once a
symbol is created, it stays in the same address until *it* gets garbage
collected. (2) (See The symbol table-Footnotes)