add documentation
This commit is contained in:
parent
cd252f5908
commit
601451e9a9
2
Makefile
2
Makefile
|
@ -12,8 +12,6 @@ keithlisp: $(OBJS)
|
||||||
|
|
||||||
$(OBJ_DIR)/%.o: %.c
|
$(OBJ_DIR)/%.o: %.c
|
||||||
gcc -c -o $@ $^ $(CFLAGS)
|
gcc -c -o $@ $^ $(CFLAGS)
|
||||||
$(OBJ_DIR):
|
|
||||||
mkdir $(OBJ_DIR)
|
|
||||||
|
|
||||||
.PHONY = clean
|
.PHONY = clean
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# The Keithlisp Programmer's Reference
|
||||||
|
Keithlisp is a tiny, stripped-down version of Lisp written in C. It's
|
||||||
|
vaguely reminiscent of Common Lisp; however, **Keithlisp does NOT fully
|
||||||
|
adhere to the Common Lisp standard!** Many Common Lisp functions are not
|
||||||
|
present or behave differently in Keithlisp.
|
||||||
|
|
||||||
|
## General Advice
|
||||||
|
- Keithlisp assumes that you, the programmer, know what you are doing.
|
||||||
|
**Many Keithlisp functions will segfault or otherwise misbehave if you
|
||||||
|
do not provide them with the data they expect.**
|
||||||
|
- Keithlisp doesn't properly parse tokens longer than 64 bytes. This
|
||||||
|
includes string literals.
|
||||||
|
- Strings in Keithlisp are sequences of *bytes*. A zero byte does not
|
||||||
|
necessarily correspond to the end of a string.
|
||||||
|
- You can comment out `#define LISP_USE_ATOMS_ALIST` in main.h to remove
|
||||||
|
the atoms_alist. This significantly reduces Keithlisp's memory
|
||||||
|
footprint, but makes it impossible to retrieve the name of an atom.
|
||||||
|
- Keithlisp doesn't automatically garbage-collect return values at the
|
||||||
|
moment, but this may change in the future. If you know whether a
|
||||||
|
function will return a cons/string referenced elsewhere or not, you
|
||||||
|
should insert a garbage collector annotation in your code.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
- [The Keithlisp Standard Library](stdlib.md)
|
|
@ -0,0 +1,9 @@
|
||||||
|
# The Keithlisp Standard Library
|
||||||
|
Keithlisp's built-in functions are grouped into the following
|
||||||
|
categories:
|
||||||
|
|
||||||
|
- [Special Forms](stdlib_forms.md)
|
||||||
|
- [Arithmetic](stdlib_arithmetic.md)
|
||||||
|
- [Boolean Logic](stdlib_boolean.md)
|
||||||
|
- [Comparison](stdlib_comparison.md)
|
||||||
|
- [Miscellaneous Functions](stdlib_misc.md)
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Arithmetic
|
||||||
|
|
||||||
|
## `+`
|
||||||
|
```
|
||||||
|
(+ &rest numbers)
|
||||||
|
```
|
||||||
|
`+` returns the sum of its arguments, skipping any inputs that are not
|
||||||
|
ints or floats. If no arguments are given, it returns 0.
|
||||||
|
|
||||||
|
## `-`
|
||||||
|
```
|
||||||
|
(- number)
|
||||||
|
(- number &rest more-numbers)
|
||||||
|
```
|
||||||
|
If called with one argument, `-` returns the negation of that argument.
|
||||||
|
If called with more than one argument, `-` subtracts `more-numbers` from
|
||||||
|
`number`, skipping any inputs that are not ints or floats.
|
||||||
|
If `number` is not an int or float, `-` returns nil.
|
||||||
|
|
||||||
|
## `*`
|
||||||
|
```
|
||||||
|
(* &rest numbers)
|
||||||
|
```
|
||||||
|
`*` returns the product of its arguments, skipping any inputs that are
|
||||||
|
not ints or floats. If no arguments are given, it returns 1.
|
||||||
|
|
||||||
|
## `/`
|
||||||
|
```
|
||||||
|
(/ number &rest more-numbers)
|
||||||
|
```
|
||||||
|
`/` divides `number` by `more-numbers`, skipping any inputs that are not
|
||||||
|
ints or floats. It always returns a float.
|
||||||
|
If `number` is not an int or float, `/` returns nil.
|
||||||
|
|
||||||
|
## `int/`
|
||||||
|
```
|
||||||
|
(int/ number &rest more-numbers)
|
||||||
|
```
|
||||||
|
`int/` performs integer division, dividing `number` by `more-numbers`,
|
||||||
|
skipping any inputs that are not ints or floats, and returning an int.
|
||||||
|
If `number` is not an int or float, `int/`returns nil.
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Boolean Logic
|
||||||
|
|
||||||
|
## `not`
|
||||||
|
```
|
||||||
|
(not value)
|
||||||
|
```
|
||||||
|
`not` returns nil if `value` is non-nil, otherwise, it returns `t`.
|
||||||
|
|
||||||
|
## `or`
|
||||||
|
```
|
||||||
|
(or &rest values)
|
||||||
|
```
|
||||||
|
|
||||||
|
`or` returns its first non-nil argument, or nil if all arguments are
|
||||||
|
nil.
|
||||||
|
|
||||||
|
## `and`
|
||||||
|
```
|
||||||
|
(and &rest values)
|
||||||
|
```
|
||||||
|
`and` returns its last argument if all arguments are non-nil, or nil if
|
||||||
|
it encounters a nil argument.
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Comparison
|
||||||
|
|
||||||
|
## `eq`
|
||||||
|
```
|
||||||
|
(eq value &rest values)
|
||||||
|
```
|
||||||
|
`eq` returns t if all `values` are equal to `value`, or nil if any are
|
||||||
|
not.
|
||||||
|
|
||||||
|
For two values to be equal according to `eq`, they must have the same
|
||||||
|
type. In all cases **except strings**, `eq` compares the immediate value
|
||||||
|
as returned by [`addr-of`](stdlib_misc.md#addr-of). However, `eq` will
|
||||||
|
return t for strings with identical contents, **regardless of their
|
||||||
|
addresses**. To check if two strings reference the same location in
|
||||||
|
memory, compare their `addr-of`.
|
||||||
|
|
||||||
|
## `=`
|
||||||
|
```
|
||||||
|
(= value &rest values)
|
||||||
|
```
|
||||||
|
`=` compares its arguments numerically. If they are all equal, it
|
||||||
|
returns `value`, otherwise it returns nil. If any argument is not an int
|
||||||
|
or float, `=` returns nil.
|
||||||
|
|
||||||
|
## `<`
|
||||||
|
```
|
||||||
|
(< value &rest values)
|
||||||
|
```
|
||||||
|
`<` compares its arguments numerically. If the sequence is monotonically
|
||||||
|
increasing, it returns the last argument, otherwise it returns nil. If
|
||||||
|
any argument is not an int or float, `<` returns nil.
|
||||||
|
|
||||||
|
## `>`
|
||||||
|
```
|
||||||
|
(> value &rest values)
|
||||||
|
```
|
||||||
|
`>` compares its arguments numerically. If the sequence is monotonically
|
||||||
|
decreasing, it returns the last argument, otherwise it returns nil. If
|
||||||
|
any argument is not an int or float, `>` returns nil.
|
||||||
|
|
||||||
|
## `<=`
|
||||||
|
```
|
||||||
|
(<= value &rest values)
|
||||||
|
```
|
||||||
|
`<=` compares its arguments numerically. If the sequence is
|
||||||
|
monotonically nondecreasing, it returns the last argument, otherwise it
|
||||||
|
returns nil. If any argument is not an int or float, `<=` returns nil.
|
||||||
|
|
||||||
|
## `>=`
|
||||||
|
```
|
||||||
|
(>= value &rest values)
|
||||||
|
```
|
||||||
|
`>=` compares its arguments numerically. If the sequence is
|
||||||
|
monotonically nonincreasing, it returns the last argument, otherwise it
|
||||||
|
returns nil. If any argument is not an int or float, `>=` returns nil.
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Special Forms
|
||||||
|
Special forms are constructs that Keithlisp evaluates differently from
|
||||||
|
other S-expressions.
|
||||||
|
|
||||||
|
## `quote`
|
||||||
|
```
|
||||||
|
quote form
|
||||||
|
```
|
||||||
|
`quote` returns `form` as-is, without performing any evaluation on it.
|
||||||
|
|
||||||
|
## `cond`
|
||||||
|
```
|
||||||
|
cond {(test {body-form}*)}*
|
||||||
|
```
|
||||||
|
`cond` is Keithlisp's basic conditional operator. It consists of zero or
|
||||||
|
more clauses (a `test`, followed by zero or more `body-form`s).
|
||||||
|
|
||||||
|
`cond` evaluates the `test` of each clause in order. If the result is
|
||||||
|
non-nil, `cond` skips all remaining clauses, evaluates each `body-form`,
|
||||||
|
and returns the result of the last one (or the result of `test` if the
|
||||||
|
clause has no `body-form`s). Otherwise, if all `test`s evaluate to nil,
|
||||||
|
or no clauses were given, `cond` returns nil.
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Miscellaneous Functions
|
||||||
|
|
||||||
|
## `type-of`
|
||||||
|
```
|
||||||
|
(type-of value)
|
||||||
|
```
|
||||||
|
`type-of` returns an atom indicating the type of `value`, or nil if
|
||||||
|
`value` is nil.
|
||||||
|
|
||||||
|
## `addr-of`
|
||||||
|
```
|
||||||
|
(addr-of value)
|
||||||
|
```
|
||||||
|
`addr-of` returns the raw immediate value of its argument as an int. For
|
||||||
|
conses, strings, and funptrs, this is their memory address (hence the
|
||||||
|
name). For ints, floats, and atoms, this returns their raw bytes
|
||||||
|
interpreted as a signed 2's-complement integer.
|
||||||
|
|
||||||
|
## `set`
|
||||||
|
```
|
||||||
|
(set atom value)
|
||||||
|
```
|
||||||
|
`set` changes the value associated with `atom` in syms-alist, or creates
|
||||||
|
the association if it does not already exist. It returns `value`.
|
||||||
|
|
||||||
|
## `fun`
|
||||||
|
```
|
||||||
|
(fun atom)
|
||||||
|
```
|
||||||
|
`fun` returns the function associated with `atom` in funs-alist, or nil
|
||||||
|
if no such function exists.
|
29
main.c
29
main.c
|
@ -235,7 +235,7 @@ void lisp_evaluate(lisp_cons* cons, lisp_value* value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cons->car.value.atom == atom_cond) {
|
if (cons->car.value.atom == atom_cond) {
|
||||||
lisp_macro_cond(cons->cdr.value.cons, value);
|
lisp_sform_cond(cons->cdr.value.cons, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lisp_cons* fun_cons = lisp_alist_get(funs_alist, cons->car);
|
lisp_cons* fun_cons = lisp_alist_get(funs_alist, cons->car);
|
||||||
|
@ -279,7 +279,7 @@ void lisp_evaluate(lisp_cons* cons, lisp_value* value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lisp_atom atom_cond;
|
lisp_atom atom_cond;
|
||||||
void lisp_macro_cond(lisp_cons* cons, lisp_value* value) {
|
void lisp_sform_cond(lisp_cons* cons, lisp_value* value) {
|
||||||
while (cons != NULL) {
|
while (cons != NULL) {
|
||||||
lisp_cons* fork = cons->car.value.cons;
|
lisp_cons* fork = cons->car.value.cons;
|
||||||
lisp_evaluate_value(fork->car, value);
|
lisp_evaluate_value(fork->car, value);
|
||||||
|
@ -298,15 +298,32 @@ void lisp_macro_cond(lisp_cons* cons, lisp_value* value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void lisp_set(lisp_cons* cons, lisp_value* value) {
|
void lisp_set(lisp_cons* cons, lisp_value* value) {
|
||||||
|
lisp_value key = cons->car;
|
||||||
|
*value = cons->cdr.value.cons->car;
|
||||||
|
lisp_cons** pairptr = lisp_alist_getptr(syms_alist, key);
|
||||||
|
if (pairptr != NULL) {
|
||||||
|
(*pairptr)->cdr = *value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lisp_cons* new_alist = dbg_malloc(sizeof(lisp_cons));
|
||||||
lisp_cons* pair = dbg_malloc(sizeof(lisp_cons));
|
lisp_cons* pair = dbg_malloc(sizeof(lisp_cons));
|
||||||
pair->car = cons->car;
|
pair->car = key;
|
||||||
pair->cdr = cons->cdr.value.cons->car;
|
pair->cdr = *value;
|
||||||
lisp_alist_put(&syms_alist, pair);
|
new_alist->car.type = LISP_T_CONS;
|
||||||
*value = pair->cdr;
|
new_alist->car.value.cons = pair;
|
||||||
|
new_alist->cdr.type = LISP_T_CONS;
|
||||||
|
new_alist->cdr.value.cons = syms_alist;
|
||||||
|
syms_alist = new_alist;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lisp_fun(lisp_cons* cons, lisp_value* value) {
|
void lisp_fun(lisp_cons* cons, lisp_value* value) {
|
||||||
lisp_cons* pair = lisp_alist_get(funs_alist, cons->car);
|
lisp_cons* pair = lisp_alist_get(funs_alist, cons->car);
|
||||||
|
if (pair == NULL) {
|
||||||
|
value->type = LISP_T_CONS;
|
||||||
|
value->value.cons = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
*value = pair->cdr;
|
*value = pair->cdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
main.h
2
main.h
|
@ -58,6 +58,6 @@ lisp_atom lisp_defun_native(lisp_string* lstr, lisp_native_fun funptr);
|
||||||
void lisp_evaluate_value(lisp_value input, lisp_value* result);
|
void lisp_evaluate_value(lisp_value input, lisp_value* result);
|
||||||
void lisp_evaluate(lisp_cons* cons, lisp_value* value);
|
void lisp_evaluate(lisp_cons* cons, lisp_value* value);
|
||||||
extern lisp_atom atom_cond;
|
extern lisp_atom atom_cond;
|
||||||
void lisp_macro_cond(lisp_cons* cons, lisp_value* value);
|
void lisp_sform_cond(lisp_cons* cons, lisp_value* value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -202,7 +202,8 @@ void lisp_eq(lisp_cons* cons, lisp_value* value) {
|
||||||
}
|
}
|
||||||
cons = cons->cdr.value.cons;
|
cons = cons->cdr.value.cons;
|
||||||
}
|
}
|
||||||
*value = cmp_value;
|
value->type = LISP_T_ATOM;
|
||||||
|
value->value.atom = atom_t;
|
||||||
}
|
}
|
||||||
void lisp_num_eq(lisp_cons* cons, lisp_value* value) {
|
void lisp_num_eq(lisp_cons* cons, lisp_value* value) {
|
||||||
lisp_value cmp_value = cons->car;
|
lisp_value cmp_value = cons->car;
|
||||||
|
|
Loading…
Reference in New Issue