add documentation

This commit is contained in:
~keith 2021-09-23 09:51:39 -04:00
parent cd252f5908
commit 601451e9a9
11 changed files with 230 additions and 10 deletions

View File

@ -12,8 +12,6 @@ keithlisp: $(OBJS)
$(OBJ_DIR)/%.o: %.c
gcc -c -o $@ $^ $(CFLAGS)
$(OBJ_DIR):
mkdir $(OBJ_DIR)
.PHONY = clean
clean:

24
doc/main.md Normal file
View File

@ -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)

9
doc/stdlib.md Normal file
View File

@ -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)

41
doc/stdlib_arithmetic.md Normal file
View File

@ -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.

22
doc/stdlib_boolean.md Normal file
View File

@ -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.

55
doc/stdlib_comparison.md Normal file
View File

@ -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.

22
doc/stdlib_forms.md Normal file
View File

@ -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.

31
doc/stdlib_misc.md Normal file
View File

@ -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
View File

@ -235,7 +235,7 @@ void lisp_evaluate(lisp_cons* cons, lisp_value* value) {
return;
}
if (cons->car.value.atom == atom_cond) {
lisp_macro_cond(cons->cdr.value.cons, value);
lisp_sform_cond(cons->cdr.value.cons, value);
return;
}
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;
void lisp_macro_cond(lisp_cons* cons, lisp_value* value) {
void lisp_sform_cond(lisp_cons* cons, lisp_value* value) {
while (cons != NULL) {
lisp_cons* fork = cons->car.value.cons;
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) {
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));
pair->car = cons->car;
pair->cdr = cons->cdr.value.cons->car;
lisp_alist_put(&syms_alist, pair);
*value = pair->cdr;
pair->car = key;
pair->cdr = *value;
new_alist->car.type = LISP_T_CONS;
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) {
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;
}

2
main.h
View File

@ -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(lisp_cons* cons, lisp_value* value);
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

View File

@ -202,7 +202,8 @@ void lisp_eq(lisp_cons* cons, lisp_value* value) {
}
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) {
lisp_value cmp_value = cons->car;