diff --git a/.gitignore b/.gitignore index 69a42e4..786da8e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ lisp *.stackdump temp/ printer +sample # Xcode diff --git a/Makefile b/Makefile index 169282e..e95c46f 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,13 @@ CFLAGS = -Wall -pedantic -Wstrict-prototypes -O3 LDLIBS = -lm CC=cc -all: lisp printer +all: lisp printer sample lisp: repl.c lisp.h ${CC} repl.c -o $@ ${CFLAGS} ${LDLIBS} printer: printer.c lisp.h ${CC} printer.c -o $@ ${CFLAGS} ${LDLIBS} + +sample: sample.c lisp.h + ${CC} sample.c -o $@ ${CFLAGS} ${LDLIBS} diff --git a/README.md b/README.md index 6165a09..3dacd3d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ I created this while reading [SICP](https://github.com/justinmeiners/sicp-excerc ### Interactive programming with Read, eval, print loop. ```bash -$ ./lisp_i +$ ./lisp > (define (sqr x) (* x x))) > (define length 40) > (define area 0) @@ -58,26 +58,20 @@ $ ./lisp_i 1600 ``` -### Embedding in a program +### Quickstart ```c LispContext ctx = lisp_init(); +lisp_load_lib(ctx); -// load lisp program (add 1 and 2) LispError error; Lisp program = lisp_read("(+ 1 2)", &error, ctx); - -// execute program using global environment Lisp result = lisp_eval(program, &error, ctx); -// prints 3 -lisp_print(result); +if (error != LISP_ERROR_NONE) + lisp_print(result); ; => 3 -// you are responsible for garbage collection -lisp_collect(ctx, env); -// ... -// shutdown also garbage collects -lisp_shutdown(ctx, env); +lisp_shutdown(ctx); ``` ### Loading Data @@ -103,12 +97,11 @@ Lisp Loading the structure in C. ```c -// setup lisp without any library -LispContext ctx = lisp_init_empty(); +LispContext ctx = lisp_init(); // load lisp structure Lisp data = lisp_read_file(file, ctx); // get value for age -Lisp age = lisp_vector_for_key(data, lisp_make_symbol("AGE", ctx), ctx); +Lisp age = lisp_list_assoc(data, lisp_make_symbol("AGE", ctx), ctx); // ... lisp_shutdown(ctx); ``` @@ -118,51 +111,49 @@ lisp_shutdown(ctx); C functions can be used to extend the interpreter, or call into C code. ```c -Lisp sum_of_squares(Lisp args, LispError* e, LispContext ctx) +Lisp integer_range(Lisp args, LispError* e, LispContext ctx) { // first argument - Lisp accum = lisp_car(args); - // remaining arguments - Lisp rest = lisp_cdr(args); + LispInt start = lisp_int(lisp_car(args)); + args = lisp_cdr(args); + // second argument + LispInt end = lisp_int(lisp_car(args)); - // iterate over the rest of the arguments - while (!lisp_is_null(rest)) + if (end < start) { - Lisp val = lisp_car(rest); - - // make this work for integers and floats - if (lisp_type(accum) == LISP_INT) - { - accum.int_val += lisp_int(val) * lisp_int(val); - } - else if (lisp_type(accum) == LISP_FLOAT) - { - accum.float_val += lisp_float(val) * lisp_float(val); - } - - rest = lisp_cdr(rest); + *e = LISP_ERROR_OUT_OF_BOUNDS; + return lisp_make_null(); } - return accum; + 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(sum_of_squares); +Lisp func = lisp_make_func(integers_in_range); -// add to enviornment with symbol SUM-OF-SQUARES -lisp_env_set(env, lisp_make_symbol("SUM-OF-SQAURES", ctx), func, ctx); +// 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 -(sum-of-squares 1 2 3) -; returns 1 + 4 + 9 = 14 +(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.1415); -lisp_env_set(env, lisp_make_symbol("PI", ctx), pi, ctx); +Lisp pi = lisp_make_real(3.141592); +lisp_env_define(env, lisp_make_symbol("PI", ctx), pi, ctx); ``` ## Macros @@ -173,7 +164,8 @@ Common Lisp style (`defmacro`) is available with the name `define-macro`. ## Garbage Collection -Unlike most languages, the garbage collector is not called automatically. You must call it yourself in C: +Garbage is only collected if it is explicitly told to. +You can invoke the garbage collector in C: lisp_collect(ctx); @@ -185,8 +177,9 @@ Note that whenever a collect is issued ANY `Lisp` value which is not accessible through the global environment may become invalid. Be careful what variables you hold onto in C. -Most C functions which are callable from Lisp -don't have a problem as `eval` cannot be called within them. + +Don't call `eval` in a custom defined C function unless +you know what you are doing. See [internals](INTERNALS.md) for more details. diff --git a/lisp.h b/lisp.h index 4f941ee..9d13966 100644 --- a/lisp.h +++ b/lisp.h @@ -117,7 +117,7 @@ typedef enum LISP_ERROR_OUT_OF_BOUNDS, LISP_ERROR_SPLICE, - LISP_ERROR_BAD_ARG, + LISP_ERROR_ARG_TYPE, LISP_ERROR_TOO_MANY_ARGS, LISP_ERROR_TOO_FEW_ARGS, LISP_ERROR_RUNTIME, @@ -256,7 +256,9 @@ Lisp lisp_list_for_key(Lisp l, Lisp key); // O(n) // Vectors (heterogeneous) -Lisp lisp_make_vector(int n, Lisp x, LispContext ctx); +Lisp lisp_make_vector(int n, LispContext ctx); +Lisp lisp_make_vector2(int n, Lisp fill, LispContext ctx); + int lisp_vector_length(Lisp v); Lisp lisp_vector_ref(Lisp v, int i); void lisp_vector_set(Lisp v, int i, Lisp x); @@ -950,7 +952,7 @@ static char* _vector_types(Vector* v) return base + sizeof(Vector) + sizeof(LispVal) * _vector_len(v); } -Lisp lisp_make_vector(int n, Lisp x, LispContext ctx) +Lisp lisp_make_vector2(int n, Lisp fill, LispContext ctx) { size_t size = sizeof(Vector) + sizeof(LispVal) * n + sizeof(char) * n; Vector* vector = gc_alloc(size, LISP_VECTOR, ctx); @@ -959,14 +961,16 @@ Lisp lisp_make_vector(int n, Lisp x, LispContext ctx) for (int i = 0; i < n; ++i) { - vector->entries[i] = x.val; - entry_types[i] = (char)x.type; + vector->entries[i] = fill.val; + entry_types[i] = (char)fill.type; } LispVal val; val.ptr_val = vector; return (Lisp) { val, LISP_VECTOR }; } +Lisp lisp_make_vector(int n, LispContext ctx) { return lisp_make_vector2(n, lisp_make_null(), ctx); } + static Vector* vector_get_(Lisp v) { assert(lisp_type(v) == LISP_VECTOR); @@ -1028,7 +1032,7 @@ Lisp lisp_subvector(Lisp old, int start, int end, LispContext ctx) if (end > m) end = m; int n = end - start; - Lisp new_v = lisp_make_vector(n, lisp_make_null(), ctx); + Lisp new_v = lisp_make_vector(n, ctx); Vector* dst = vector_get_(new_v); memcpy(dst->entries, src->entries + start, sizeof(LispVal) * n); memcpy(_vector_types(dst), _vector_types(src) + start, sizeof(char) * n); @@ -1047,7 +1051,7 @@ Lisp lisp_vector_grow(Lisp v, int n, LispContext ctx) } else { - Lisp new_v = lisp_make_vector(n, lisp_vector_ref(v, 0), ctx); + Lisp new_v = lisp_make_vector2(n, lisp_vector_ref(v, 0), ctx); Vector* dst = vector_get_(new_v); memcpy(dst->entries, src->entries, sizeof(LispVal) * m); memcpy(_vector_types(dst), _vector_types(src), sizeof(char) * m); @@ -1105,8 +1109,8 @@ static void table_grow_(Lisp t, size_t new_capacity, LispContext ctx) Lisp old_vals = { table->vals, LISP_VECTOR }; table->capacity = new_capacity; - table->keys = lisp_make_vector(new_capacity, lisp_make_null(), ctx).val; - table->vals = lisp_make_vector(new_capacity, lisp_make_null(), ctx).val; + table->keys = lisp_make_vector(new_capacity, ctx).val; + table->vals = lisp_make_vector(new_capacity, ctx).val; table->size = 0; for (int i = 0; i < old_capacity; ++i) @@ -2128,7 +2132,7 @@ static Lisp parse_list_r(Lexer* lex, jmp_buf error_jmp, LispContext ctx) if (lisp_is_null(v)) { - v = lisp_make_vector(16, x, ctx); + v = lisp_make_vector2(16, x, ctx); } else { @@ -3277,7 +3281,7 @@ const char* lisp_error_string(LispError error) return "eval error: attempt to apply something which was not an operator"; case LISP_ERROR_UNKNOWN_EVAL: return "eval error: got into a bad state"; - case LISP_ERROR_BAD_ARG: + case LISP_ERROR_ARG_TYPE: return "eval error: bad argument type"; case LISP_ERROR_TOO_MANY_ARGS: return "eval error: too many arguments"; @@ -3548,14 +3552,19 @@ static Lisp sch_add(Lisp args, LispError* e, LispContext ctx) // TODO: types while (lisp_is_pair(args)) { - if (lisp_type(accum) == LISP_INT) + if (lisp_type(accum) == LISP_REAL) { - accum.val.int_val += lisp_int(lisp_car(args)); + accum.val.real_val += lisp_real(lisp_car(args)); } - else if (lisp_type(accum) == LISP_REAL) + else if (lisp_type(lisp_car(args)) == LISP_REAL) { + accum = lisp_make_real((LispReal)lisp_int(accum)); accum.val.real_val += lisp_real(lisp_car(args)); } + else + { + accum.val.int_val += lisp_real(lisp_car(args)); + } args = lisp_cdr(args); } return accum; @@ -3578,7 +3587,7 @@ static Lisp sch_sub(Lisp args, LispError* e, LispContext ctx) } else { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } args = lisp_cdr(args); @@ -3603,7 +3612,7 @@ static Lisp sch_mult(Lisp args, LispError* e, LispContext ctx) } else { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } args = lisp_cdr(args); @@ -3628,7 +3637,7 @@ static Lisp sch_divide(Lisp args, LispError* e, LispContext ctx) } else { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } args = lisp_cdr(args); @@ -3650,7 +3659,7 @@ static Lisp sch_less(Lisp args, LispError* e, LispContext ctx) case LISP_REAL: return lisp_make_bool(lisp_real(a) < lisp_real(b)); default: - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } } @@ -3672,7 +3681,7 @@ static Lisp sch_to_exact(Lisp args, LispError* e, LispContext ctx) case LISP_STRING: return lisp_make_int(atoi(lisp_string(val))); default: - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } } @@ -3690,7 +3699,7 @@ static Lisp sch_to_inexact(Lisp args, LispError* e, LispContext ctx) case LISP_STRING: return lisp_make_real(atof(lisp_string(val))); default: - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } } @@ -3713,7 +3722,7 @@ static Lisp sch_to_string(Lisp args, LispError* e, LispContext ctx) case LISP_STRING: return val; default: - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } } @@ -3725,7 +3734,7 @@ static Lisp sch_symbol_to_string(Lisp args, LispError* e, LispContext ctx) Lisp val = lisp_car(args); if (lisp_type(val) != LISP_SYMBOL) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } else @@ -3745,7 +3754,7 @@ static Lisp sch_string_to_symbol(Lisp args, LispError* e, LispContext ctx) Lisp val = lisp_car(args); if (lisp_type(val) != LISP_STRING) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } else @@ -3808,7 +3817,7 @@ static Lisp sch_string_copy(Lisp args, LispError* e, LispContext ctx) Lisp val = lisp_car(args); if (lisp_type(val) != LISP_STRING) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } return lisp_make_string(lisp_string(val), ctx); @@ -3829,7 +3838,7 @@ static Lisp sch_string_ref(Lisp args, LispError* e, LispContext ctx) Lisp index = lisp_car(lisp_cdr(args)); if (lisp_type(str) != LISP_STRING || lisp_type(index) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -3843,7 +3852,7 @@ static Lisp sch_string_set(Lisp args, LispError* e, LispContext ctx) Lisp val = lisp_list_ref(args, 2); if (lisp_type(str) != LISP_STRING || lisp_type(index) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4066,7 +4075,7 @@ static Lisp sch_abs(Lisp args, LispError* e, LispContext ctx) case LISP_REAL: return lisp_make_real(fabs(lisp_real(lisp_car(args)))); default: - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } } @@ -4074,7 +4083,7 @@ static Lisp sch_abs(Lisp args, LispError* e, LispContext ctx) static Lisp sch_vector(Lisp args, LispError* e, LispContext ctx) { int N = lisp_list_length(args); - Lisp v = lisp_make_vector(N, lisp_make_null(), ctx); + Lisp v = lisp_make_vector(N, ctx); for (int i = 0; i < N; ++i) { lisp_vector_set(v, i, lisp_car(args)); @@ -4094,7 +4103,7 @@ static Lisp sch_make_vector(Lisp args, LispError* e, LispContext ctx) if (lisp_type(length) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4110,7 +4119,7 @@ static Lisp sch_make_vector(Lisp args, LispError* e, LispContext ctx) val = lisp_car(lisp_cdr(args)); } - return lisp_make_vector(lisp_int(length), val, ctx); + return lisp_make_vector2(lisp_int(length), val, ctx); } static Lisp sch_vector_grow(Lisp args, LispError* e, LispContext ctx) @@ -4120,7 +4129,7 @@ static Lisp sch_vector_grow(Lisp args, LispError* e, LispContext ctx) if (lisp_type(length) != LISP_INT || lisp_type(v) != LISP_VECTOR) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4138,7 +4147,7 @@ static Lisp sch_vector_length(Lisp args, LispError* e, LispContext ctx) Lisp v = lisp_car(args); if (lisp_type(v) != LISP_VECTOR) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4152,7 +4161,7 @@ static Lisp sch_vector_ref(Lisp args, LispError* e, LispContext ctx) if (lisp_type(v) != LISP_VECTOR || lisp_type(i) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4173,7 +4182,7 @@ static Lisp sch_vector_set(Lisp args, LispError* e, LispContext ctx) if (lisp_type(v) != LISP_VECTOR || lisp_type(i) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4195,7 +4204,7 @@ static Lisp sch_vector_swap(Lisp args, LispError* e, LispContext ctx) if (lisp_type(v) != LISP_VECTOR || lisp_type(i) != LISP_INT || lisp_type(j) != LISP_INT) { - *e = LISP_ERROR_BAD_ARG; + *e = LISP_ERROR_ARG_TYPE; return lisp_make_null(); } @@ -4238,7 +4247,7 @@ static Lisp sch_list_to_vector(Lisp args, LispError* e, LispContext ctx) { Lisp l = lisp_car(args); int n = lisp_list_length(l); - Lisp v = lisp_make_vector(n, lisp_make_null(), ctx); + Lisp v = lisp_make_vector(n, ctx); int i = 0; while (!lisp_is_null(l)) diff --git a/printer.c b/printer.c index bf4ff4e..66bbf15 100644 --- a/printer.c +++ b/printer.c @@ -6,8 +6,6 @@ #define LISP_NO_LIB #include "lisp.h" -#define LINE_MAX 2048 - int main(int argc, const char* argv[]) { LispContext ctx = lisp_init(); diff --git a/repl.c b/repl.c index a1d531b..d364034 100644 --- a/repl.c +++ b/repl.c @@ -9,8 +9,6 @@ #define LISP_IMPLEMENTATION #include "lisp.h" -#define LINE_MAX 2048 - int main(int argc, const char* argv[]) { const char* file_path = NULL; @@ -105,8 +103,8 @@ int main(int argc, const char* argv[]) while (!feof(stdin)) { printf("> "); - char line[LINE_MAX]; - fgets(line, LINE_MAX, stdin); + char line[LISP_FILE_CHUNK_SIZE]; + fgets(line, LISP_FILE_CHUNK_SIZE, stdin); clock_t start_time = clock(); LispError error; diff --git a/sample.c b/sample.c new file mode 100644 index 0000000..4c37880 --- /dev/null +++ b/sample.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#define LISP_IMPLEMENTATION +#include "lisp.h" + +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_make_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; +} + +int main(int argc, const char* argv[]) +{ + LispContext ctx = lisp_init(); + lisp_load_lib(ctx); + + // wrap in Lisp object + Lisp func = lisp_make_func(integer_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); + Lisp pi = lisp_make_real(3.141592); + lisp_env_define(env, lisp_make_symbol("PI", ctx), pi, ctx); + + LispError e; + Lisp result = lisp_eval(lisp_read("(INTEGER-RANGE 5 15)", &e, ctx), &e, ctx); + lisp_print(result); + + if (e != LISP_ERROR_NONE) fprintf(stderr, "error: %s\n", lisp_error_string(e)); + + lisp_shutdown(ctx); + return 0; +} +