~brenns10/funlisp

ref: 346874b165ff60236fe82742a0ffacb415b6b7ec funlisp/inc/funlisp.h -rw-r--r-- 5.6 KiB
346874b1Stephen Brennan A hello world 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 * funlisp.h: public funlisp interface
 *
 * Stephen Brennan <stephen@brennan.io>
 */

#ifndef _FUNLISP_H
#define _FUNLISP_H

#include <stdio.h> /* for FILE* */

/*
 * A runtime object -- you need one to execute any code. Note the typedef.
 * Although I generally prefer no struct typedefs, these names are long enough
 * to merit it.
 */
typedef struct lisp_runtime lisp_runtime;

/*
 * Use these functions to create and free a runtime. You must use
 * lisp_runtime_free() to cleanup every runtime you create.
 */
lisp_runtime *lisp_runtime_new(void);
void lisp_runtime_free(lisp_runtime *);

/*
 * In funlisp, (almost) everything is a lisp_value -- that is, can be cast to a
 * lisp_value * and operated on. Integers, Strings, Code, etc. The only thing
 * which is not a lisp_value is the lisp_runtime.
 */
typedef struct lisp_value {
	struct lisp_type  *type; /* type object, USE ME TO TYPE CHECK */
	struct lisp_value *next; /* don't touch me */
	char mark;               /* don't touch me */
} lisp_value;

/*
 * Here are the lisp_values out there:
 */
typedef struct lisp_type lisp_type;
typedef struct lisp_scope lisp_scope;
typedef struct lisp_list lisp_list;
typedef struct lisp_symbol lisp_symbol;
typedef struct lisp_error lisp_error;
typedef struct lisp_integer lisp_integer;
typedef struct lisp_string lisp_string;
typedef struct lisp_builtin lisp_builtin;
typedef struct lisp_lambda lisp_lambda;

/*
 * Each one has an associated "type" object (which you saw above):
 * You can compare the value->type to these, in order to see what type object
 * you have.
 */
extern lisp_type *type_type;
extern lisp_type *type_scope;
extern lisp_type *type_list;
extern lisp_type *type_symbol;
extern lisp_type *type_error;
extern lisp_type *type_integer;
extern lisp_type *type_string;
extern lisp_type *type_builtin;
extern lisp_type *type_lambda;

/*
 * No matter what type you have, it can be printed. This does NOT print a
 * trailing newline, so as to give you the most flexibility.
 */
void lisp_print(FILE *f, lisp_value *value);

/*
 * Since lisp_values can be code, they can be evaluated on a runtime, in a given
 * scope. They, of course, return values.
 */
lisp_value *lisp_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *value);

/*
 * You get lisp_value's by parsing strings. This parses exactly one lisp
 * expression out of a string and returns the code object, which needs to be
 * evaluated. Will return NULL if there is no expression in the string.
 */
lisp_value *lisp_parse(lisp_runtime *rt, char *input);

/*
 * Using files is a bit more straightforward. Loading a file will parse and
 * execute each expression in the file, returning the result of evaluating the
 * last expression (which could be NULL if there was no code).  You don't need
 * to eval the resulting object (it has already been evaluated).  Typically, you
 * will want to run some callback though (e.g. a main function).
 */
lisp_value *lisp_load_file(lisp_runtime *rt, lisp_scope *scope, FILE *input);

/*
 * To do most of this stuff, you need a scope! Thankfully, you can get a new,
 * default scope (containing the language default builtins) quite easily with
 * this function.
 */
lisp_scope *lisp_new_default_scope(lisp_runtime *rt);

/*
 * You can also get a new, empty scope with:
 */
lisp_scope *lisp_new_empty_scope(lisp_runtime *rt);

/*
 * With the above, you should be able to infer what basic use should look like:
 *
 *   lisp_runtime *rt = lisp_runtime_new();
 *   lisp_scope *scope = lisp_new_default_scope(rt);
 *   lisp_value *parsed = lisp_parse(rt, my_cool_code);
 *   lisp_value *result = lisp_eval(rt, scope, parsed);
 *   lisp_print(stdout, result);
 *
 * However, we're missing the memory management stuff here. How does it all get
 * cleaned up?
 *
 * Funlisp uses a mark-and-sweep garbage collector. It cleverly avoids the
 * cycles in data structures (very common in lisp), and frees up unused
 * lisp_values. However, it does need your help.
 */

/*
 * Each time you want to invoke the garbage collector, you should "mark" all the
 * objects you're interested in keeping. Marking an object marks every object
 * you can access from it, using a breadth-first search (so it could be a quite
 * expensive operation). This makes all the objects safe from the following
 * "sweep" operation, which frees all unmarked objects.
 *
 * The normal, correct think to do after evaluating some code, is to mark the
 * scope. Anything still there should be kept. Anything else can probably be
 * cleaned up.
 *
 * NOTE: be explicit about marking. If you do the following:
 *   lisp_value *result = lisp_eval(rt, scope, some_cool_code);
 *   lisp_mark((lisp_value*)scope);
 * There is a very good chance that `result` will be freed on the next sweep.
 * Yeah.
 */
void lisp_mark(lisp_runtime *rt, lisp_value *v);

/*
 * Now, once you've marked your scope (and any other values you're interested
 * in), sweep everything away!
 */
void lisp_sweep(lisp_runtime *rt);


/* UTILITIES */

/*
 * Convert the array of strings into a lisp list of string objects.
 * list: an array of strings
 * n: length of the array
 * can_free: Does the interpreter take ownership of the memory pointed at by
 *   the strings? If so, can_free should be non-zero. If not, it should be 0.
 */
lisp_value *lisp_list_of_strings(lisp_runtime *rt, char **list, size_t n, char can_free);

/*
 * Given a lisp_value, put it inside a list of size 0 and return it.
 */
lisp_value *lisp_singleton_list(lisp_runtime *rt, lisp_value *entry);

/*
 * Run the main function, if it exists. Otherwise, return NULL.
 */
lisp_value *lisp_run_main_if_exists(lisp_runtime *rt, lisp_scope *scope,
                                    int argc, char **argv);

#endif