Core VSLisp library is what you can use by function calls from your own program, or to write new VSLisp module. It also contains default Lisp system functions implementation (all that CARs, CDRs and other DEFUNs). To use it you'll need to know essential data structures (internals of Lists and Atoms), and a few simple core functions.
The main data structure of VSLisp core library is LIST.
It is defined in file l_defs.h
.
typedef struct {
unsigned char f;
unsigned short int g;
void * head;
void * tail;
} _List;
f
is a type code. First bit of f
is: 0 - ATOM, 1 - LIST.
For ATOM there are following structure:
typedef struct {
unsigned char f;
unsigned short int g;
char * a;
char * b;
} * ATOM;
For LIST:
typedef struct _list {
unsigned char f;
unsigned short int g;
union _l1 * h;
union _l1 * t;
} * LIST;
Where union _l1
is union of atom and list structures.
E.g. to access character value of ATOM head of LIST l, type
l->h.a->a
. To get (cdr (cdr l))
use
l->t.l->t.l
.
g
stands for Garbage Collector - it's just a number of links to
this node. Try not to corrupt this field, and use it correctly.
head
, tail
are pointers to head and tail, or, for ATOM,
pointer to ATOM value and, maybe, some extra information (destructor
function for MISC Atom, or array size for ARRAY Atom).
ilisp_init(int n)
VSLisp core library must be initialized bylisp_init
call.n
is a maximum number of symbols in global symbol table. IfLP_MALLOC
was not defined, ilisp_init() will create lists pool, which can be pretty large. If you don't like it, and there is no need in extreme evaluation speed, uncomment that macro definition inl_defs.h
, and rebuild the library.
LIST leval(LIST l,symtab *tab)
Evaluate listl
, in symbol contexttab
. For default (global) context, use callReturnGlobal()
to get a pointer. There was a strange troubles with direct usage of global variable.
LIST onesymeval(LIST l,symtab *tab)
Evaluate head ofl
if it is a list, or return value of symbol, which name is a head ofl
. Symbol name searched in symbol tabletab
, then in global table of current context. (for more information of contexts seesymtab
structure definition inl_defs.h
.
aatl1(LIST l)
Garbage Collector function. Call it every time before releasing pointer to LIST or ATOM structure, especially for returned by leval() and onesymeval(). Ignoration ofaatl1
call is a best way to huge memory leaks. Again, setg
field correctly (to 0 when unlinked list or atom created), if you wantaatl1
understand it right.
LIST mklist(LIST a,LIST b)
Create a new list with heada
and tailb
. If it is not linked to other list,g
field must be zeroed.mklist
can be used to create ATOM with valuea
andb=NIL
or something you need in special ATOM.f
field must be set to ATOM type in this case.
deffun(symtab *tab,char *n,LIST (*f)(symtab *tab,LIST l))
Define hookf
for a new system function namedn
. See example below.tab
is needed to get current context, just useReturnGlobal()
here.
There are only ONE function defined internally
in leval()
function body.
x1 <-- (quote x1)
- return x1 without evaluation.
LIST my_cool_function(symtab *tab,LIST l)
{
LIST t1,t2,ret;
char *s;
int l1,l2;
t2=l->t.l; /* Get first parameter. It is 'quoted' */
t1=onesymeval(l->t.l->t.l,tab); /* Evaluate second parameter */
/* We hope, t2 is ATOM. Any type checks are too expensive, let's believe
in programmer's accuracy */
l1=(int)GetDouble((ATOM)t1);
s=(char *)malloc(l1+1); /* t1 is a 'double' atom, which is
a length of a new string */
l2=strlen(((ATOM)t2)->a); /* ((ATOM)t2)->a is a char * value of
atom t2 */
if(l2<l1) strcpy(((ATOM)t2)->a,s);
else
strncpy(((ATOM)t2),s,l1);
ret=mklist((LIST)s,NIL);
ret->f=0; /* ret is ATOM now */
ret->g=0; /* NEVER forget this! */
aatl1(t1); /* tell GC about t1, we don't need it any more */
return ret;
}
....
deffun(ReturnGlobal(),"my-cool-function",&my_cool_function);
....
It was a pretty unusable function, which resizes string. But it illustrates all tricks you need to write a usable one.