This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
|
||||
Lisp Interpreter
|
||||
================
|
||||
|
||||
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. -- Philip Greenspun
|
||||
|
||||
An embeddable lisp/scheme interpreter written in C.
|
||||
It includes a subset of R5RS with some extensions from MIT Scheme.
|
||||
|
||||
I created this while reading [SICP](https://github.com/justinmeiners/sicp-excercises) to improve my knowledge of lisp
|
||||
and to make an implementation that allows me to easily add scripting to my own programs.
|
||||
|
||||
### Philosophy
|
||||
|
||||
- **Simple**: This project doesn't aim to be optimal, or fully standards compliant.
|
||||
It is just a robust foundation for scripting.
|
||||
It is implemented as a recursive AST walker on the C stack.
|
||||
|
||||
If you need more try [s7](https://ccrma.stanford.edu/software/snd/snd/s7.html) or [chicken](https://www.call-cc.org)
|
||||
|
||||
- **Unintrusive**: Just copy in the header file. Turn on and off major features with build macros. It should be portable between major platforms.
|
||||
|
||||
- **Unsurprising**: You should be able to read the source code and understand how it works.
|
||||
The C API should work how you expect.
|
||||
|
||||
- **First class data**: Lisp s-expressions are undervalued as an alternative to JSON or XML.
|
||||
Preprocessor flags can remove most scheme features if you just want to read s-expressions
|
||||
and manipulate them in C.
|
||||
|
||||
### Features
|
||||
|
||||
- C99, no dependencies, two files.
|
||||
- Core lisp language `if`, `let`, `do`, `lambda`, `cons`, `eval`, etc.
|
||||
- Subset of scheme R5RS library: lists, vectors, hash tables, integers, real numbers, characters, strings, and integers.
|
||||
- Common lisp goodies: unhygenic macros (`define-macro`), `push`, `dotimes`.
|
||||
- Easy to integrate C functions.
|
||||
- Exact [garbage collection](#garbage-collection) with explicit invocation.
|
||||
- REPL command line tool.
|
||||
- Efficient parsing and manipulation of large data files.
|
||||
|
||||
### Non-Features
|
||||
|
||||
- Compiler or VM.
|
||||
- Full numeric tower.
|
||||
- Full call/cc. This only supports simple stack jumps.
|
||||
- syntax rules.
|
||||
- UNIX system interface/IO library.
|
||||
|
||||
### Examples
|
||||
|
||||
### Interactive programming with Read, eval, print loop.
|
||||
```bash
|
||||
$ ./lisp
|
||||
> (define (sqr x) (* x x)))
|
||||
> (define length 40)
|
||||
> (define area 0)
|
||||
> (set! area (sqr length))
|
||||
1600
|
||||
```
|
||||
|
||||
### Quickstart
|
||||
|
||||
```c
|
||||
LispContext ctx = lisp_init();
|
||||
lisp_load_lib(ctx);
|
||||
|
||||
LispError error;
|
||||
Lisp program = lisp_read("(+ 1 2)", &error, ctx);
|
||||
Lisp result = lisp_eval(program, &error, ctx);
|
||||
|
||||
if (error != LISP_ERROR_NONE)
|
||||
lisp_print(result); ; => 3
|
||||
|
||||
lisp_shutdown(ctx);
|
||||
```
|
||||
|
||||
### Loading Data
|
||||
|
||||
Lisp s-expressions can be used as a lightweight substitute to JSON or XML.
|
||||
Looking up keys which are reused is even more efficient due to symbol comparison.
|
||||
|
||||
JSON
|
||||
```json
|
||||
{
|
||||
"name" : "Bob Jones",
|
||||
"age" : 54,
|
||||
"city" : "SLC",
|
||||
}
|
||||
```
|
||||
|
||||
Lisp
|
||||
```scheme
|
||||
#((name . "Bob Jones")
|
||||
(age . 54)
|
||||
(city . "SLC"))
|
||||
```
|
||||
Loading the structure in C.
|
||||
|
||||
```c
|
||||
LispContext ctx = lisp_init();
|
||||
// load lisp structure
|
||||
Lisp data = lisp_read_file(file, ctx);
|
||||
// get value for age
|
||||
Lisp age_entry = lisp_avector_ref(data, lisp_make_symbol("AGE", ctx), ctx);
|
||||
// ...
|
||||
lisp_shutdown(ctx);
|
||||
```
|
||||
|
||||
### Calling C functions
|
||||
|
||||
C functions can be used to extend the interpreter, or call into C code.
|
||||
|
||||
```c
|
||||
Lisp integer_range(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
// first argument
|
||||
LispInt start = lisp_int(lisp_car(args));
|
||||
args = lisp_cdr(args);
|
||||
// second argument
|
||||
LispInt end = lisp_int(lisp_car(args));
|
||||
|
||||
if (end < start)
|
||||
{
|
||||
*e = LISP_ERROR_OUT_OF_BOUNDS;
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
LispInt n = end - start;
|
||||
Lisp numbers = lisp_make_vector(n, ctx);
|
||||
|
||||
for (LispInt i = 0; i < n; ++i)
|
||||
lisp_vector_set(numbers, i, lisp_make_int(start + i));
|
||||
|
||||
return numbers;
|
||||
}
|
||||
// ...
|
||||
|
||||
// wrap in Lisp object
|
||||
Lisp func = lisp_make_func(integers_in_range);
|
||||
|
||||
// add to enviornment with symbol INTEGER-RANGE
|
||||
Lisp env = lisp_env_global(ctx);
|
||||
lisp_env_define(env, lisp_make_symbol("INTEGER-RANGE", ctx), func, ctx);
|
||||
```
|
||||
|
||||
In Lisp
|
||||
```scheme
|
||||
(integer-range 5 15)
|
||||
; => #(5 6 7 8 9 10 11 12 13 14)
|
||||
```
|
||||
Constants can also be stored in the environment in a similar fashion.
|
||||
|
||||
```c
|
||||
Lisp pi = lisp_make_real(3.141592);
|
||||
lisp_env_define(env, lisp_make_symbol("PI", ctx), pi, ctx);
|
||||
```
|
||||
### Macros
|
||||
|
||||
Common Lisp style (`defmacro`) is available with the name `define-macro`.
|
||||
|
||||
(define-macro nil! (lambda (x)
|
||||
`(set! ,x '()))
|
||||
|
||||
### Garbage Collection
|
||||
|
||||
Garbage is only collected if it is explicitly told to.
|
||||
You can invoke the garbage collector in C:
|
||||
|
||||
lisp_collect(ctx);
|
||||
|
||||
OR in lisp code:
|
||||
|
||||
(gc-flip)
|
||||
|
||||
Note that whenever a collect is issued
|
||||
ANY `Lisp` value in `C`which is not accessible
|
||||
through the global environment may become invalid.
|
||||
Be careful what variables you hold onto in C.
|
||||
|
||||
Don't call `eval` in a custom defined C function unless you know what you are doing.
|
||||
|
||||
See [internals](INTERNALS.md) for more details.
|
||||
|
||||
## Documentation
|
||||
|
||||
For the language refer to [MIT Scheme](https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_toc.html)
|
||||
with the understanding that not everything is missing.
|
||||
If we do implement a feature that MIT scheme has, we will try to follow their specificaiton.
|
||||
|
||||
For the C API refer to the header and sample programs (`repl.c`, `printer.c`).
|
||||
|
||||
## Project License
|
||||
|
||||
Copyright (c) 2020 Justin Meiners
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
Reference in New Issue
Block a user