@@ -35,26 +35,24 @@ char escape(char c) {
35
35
}
36
36
}
37
37
38
- bool needs_escape (char c) {
39
- return strchr (" {}\b\t\n\v\f\r\a\\\" " , c) != NULL ;
40
- }
41
-
42
- void free_payload (object* o) { free (o->as_ptr ); }
38
+ static void free_payload (object* o) { free (o->as_ptr ); }
39
+ static object* mark_car_only (tinobsy::vm* _, object* o) { return car (o); }
43
40
44
41
// ------------------------ core types -----------------
45
42
// these will later be swapped for actual objects
46
43
47
44
// cons = car, cdr
48
45
const object_type cons_type (" cons" , tinobsy::markcons, NULL , NULL );
46
+ const object_type obj_type (" object" , tinobsy::markcons, NULL , NULL );
49
47
// --------- primitive/ish types ---------------
50
- const object_type string_type (" string" , NULL , free_payload, NULL );
51
- const object_type symbol_type (" symbol" , NULL , free_payload, NULL );
52
- const object_type c_function_type (" c_function" , NULL , NULL , NULL );
53
- const object_type integer_type (" int" , NULL , NULL , NULL );
54
- const object_type float_type (" float" , NULL , NULL , NULL );
48
+ const object_type string_type (" string" , mark_car_only , free_payload, NULL );
49
+ const object_type symbol_type (" symbol" , mark_car_only , free_payload, NULL );
50
+ const object_type c_function_type (" c_function" , mark_car_only , NULL , NULL );
51
+ const object_type integer_type (" int" , mark_car_only , NULL , NULL );
52
+ const object_type float_type (" float" , mark_car_only , NULL , NULL );
55
53
const object_type* primitives[] = { &string_type, &symbol_type, &c_function_type, &integer_type, &float_type, NULL };
56
54
57
- void pickle ::mark_globals () {
55
+ void pvm ::mark_globals () {
58
56
this ->markobject (this ->queue );
59
57
this ->markobject (this ->globals );
60
58
this ->markobject (this ->function_registry );
@@ -99,9 +97,9 @@ object* delassoc(object** list, object* key) {
99
97
return NULL ;
100
98
}
101
99
102
- // ---------- EVAL ENGINE --------------------------------------------
100
+ // ---------- STACK MACHINE --------------------------------------------
103
101
104
- void pickle ::start_thread () {
102
+ void pvm ::start_thread () {
105
103
// thread is list of (data stack, next instruction, instruction stack)
106
104
object* new_thread = this ->cons (nil, this ->cons (nil, nil));
107
105
if (!this ->queue ) {
@@ -116,15 +114,15 @@ void pickle::start_thread() {
116
114
cdr (last) = this ->queue ;
117
115
}
118
116
119
- void pickle ::step () {
117
+ void pvm ::step () {
120
118
next_inst:
121
119
if (!this ->queue ) return ;
122
120
object* next_type = car (cdr (this ->curr_thread ()));
123
121
object* op = this ->pop_inst ();
124
122
if (!op) {
125
123
object* last = this ->queue ;
126
124
if (cdr (last) == last) {
127
- // last thread: nothing to do
125
+ // last thread and nothing to do
128
126
this ->queue = nil;
129
127
return ;
130
128
}
@@ -136,46 +134,216 @@ void pickle::step() {
136
134
object* type = car (op);
137
135
if (eqcmp (type, next_type) != 0 ) goto next_inst;
138
136
object* inst_name = car (cdr (op));
139
- object* inst_payload = cdr (cdr (op));
137
+ object* cookie = cdr (cdr (op));
140
138
object* pair = assoc (this ->function_registry , inst_name);
141
139
ASSERT (pair, " Unknown instruction %s" , this ->stringof (inst_name));
142
- next_type = this ->fptr (cdr (pair))(this , inst_payload );
140
+ next_type = this ->fptr (cdr (pair))(this , cookie, next_type );
143
141
car (cdr (this ->curr_thread ())) = next_type;
144
142
this ->queue = cdr (this ->queue );
145
143
}
146
144
147
145
// --------------- PARSER --------------------------------------
148
146
147
+ typedef struct {
148
+ const char * data;
149
+ size_t i;
150
+ size_t len;
151
+ } pstate;
152
+
153
+ #define pos (s->i)
154
+ #define restore pos =
155
+ #define advance pos +=
156
+ #define next pos++
157
+ #define look (s->data[pos])
158
+ #define at (z ) (&s->data[z])
159
+ #define here at (pos)
160
+ #define eofp (pos >= s->len)
161
+ #define test (f ) (f(look))
162
+ #define chomp (str ) (!strncmp(here, str, strlen(str)) ? advance strlen (str) : false)
163
+
164
+ static void bufadd(char ** b, char c) {
165
+ // super not memory efficient, it reallocs the buffer every time
166
+ char * ob = *b;
167
+ asprintf (b, " %s%c" , *b ? *b : " " , c);
168
+ free (ob);
169
+ }
170
+ static void bufcat (char ** b, const char * c, int n) {
171
+ char * ob = *b;
172
+ asprintf (b, " %s%.*s" , *b ? *b : " " , n, c);
173
+ free (ob);
174
+ }
175
+
176
+
177
+ static object* do_parse (pvm* vm, pstate* s, bool * error, char * special) {
178
+ char c = look;
179
+ char * b = NULL ;
180
+ char * b2 = NULL ;
181
+ object* result = nil;
182
+ if (isalpha (c)) {
183
+ size_t p = pos;
184
+ while (!eofp && test (isalpha)) next;
185
+ bufcat (&b, at (p), pos - p);
186
+ result = vm->sym (b);
187
+ }
188
+ else if (isdigit (c)) {
189
+ double d; int64_t n;
190
+ int num;
191
+ int ok = sscanf (here, " %lg%n" , &d, &num);
192
+ if (ok == 2 ) result = vm->number (d);
193
+ else {
194
+ ok = sscanf (here, " %" SCNi64 " %n" , &n, &num);
195
+ if (ok == 2 ) result = vm->integer (n);
196
+ else {
197
+ *error = true ;
198
+ result = vm->string (" scanf error" );
199
+ }
200
+ }
201
+ if (ok == 2 ) advance num;
202
+ }
203
+ else if (isspace (c) && c != ' \n ' ) {
204
+ result = vm->sym (" SPACE" );
205
+ while (test (isspace) && c != ' \n ' ) next;
206
+ }
207
+ else if (c == ' #' ) {
208
+ // get comment or 1-character # operator
209
+ next;
210
+ if (look != ' #' ) {
211
+ // it's a # operator
212
+ result = vm->sym (" #" );
213
+ } else {
214
+ next;
215
+ if (look != ' #' ) {
216
+ // it's a line comment
217
+ do bufadd (&b, look), next; while (look != ' \n ' );
218
+ result = vm->string (b);
219
+ } else {
220
+ // it's a block comment
221
+ bufcat (&b2, " ###" , 3 );
222
+ next;
223
+ while (look == ' #' ) bufadd (&b2, ' #' ), next;
224
+ do bufadd (&b, look), next; while (!eofp && !chomp (b2));
225
+ if (eofp) {
226
+ *error = true ;
227
+ result = vm->string (" error: unterminated block comment" );
228
+ goto done;
229
+ }
230
+ result = vm->string (b);
231
+ }
232
+ }
233
+ }
234
+ else if (c == ' "' || c == ' \' ' ) {
235
+ char start = c;
236
+ next;
237
+ while (look != start && !eofp && look != ' \n ' ) {
238
+ char ch = look;
239
+ if (ch == ' \\ ' ) {
240
+ next;
241
+ ch = unescape (ch);
242
+ }
243
+ if (ch) bufadd (&b, ch);
244
+ next;
245
+ }
246
+ if (look != start) {
247
+ *error = true ;
248
+ result = vm->string (" error: unclosed string" );
249
+ }
250
+ else result = vm->string (b);
251
+ }
252
+ else if (c == ' \n ' ) {
253
+ getindent:
254
+ // parser block
255
+ next; // eat newline
256
+ while (test (isspace) && look != ' \n ' ) {
257
+ bufadd (&b2, look);
258
+ next;
259
+ }
260
+ if (look == ' \n ' ) {
261
+ free (b2);
262
+ b2 = NULL ;
263
+ goto getindent;
264
+ }
265
+ // validate indent
266
+ for (char * c = b2; *c; c++) {
267
+ if (*c != *b2) {
268
+ *error = true ;
269
+ result = vm->string (" error: mix of spaces and tabs indenting block" );
270
+ goto done;
271
+ }
272
+ }
273
+ for (;;) {
274
+ // get one line
275
+ do bufadd (&b, look), next; while (!eofp && look != ' \n ' );
276
+ bufadd (&b, ' \n ' );
277
+ if (eofp) break ;
278
+ // check indent and break
279
+ chompindent:
280
+ if (!chomp (b2)) {
281
+ // if indent does not chomp, expect a blank line
282
+ bool has_indent = false ;
283
+ while (test (isspace) && look != ' \n ' ) has_indent = true , next;
284
+ if (look == ' \n ' ) {
285
+ next;
286
+ bufadd (&b, ' \n ' );
287
+ goto chompindent;
288
+ }
289
+ // not a blank line
290
+ if (has_indent) {
291
+ result = vm->string (" error: unindent does not match previous indent" );
292
+ *error = true ;
293
+ goto done;
294
+ }
295
+ // completely unindented
296
+ else break ;
297
+ }
298
+ }
299
+ result = vm->string (b);
300
+ }
301
+ else if (strchr (" (){}[]" , c)) {
302
+ *special = c;
303
+ }
304
+ else if (ispunct (c)) {
305
+ // must test for other punctuation last to allow other special cases to take precedence
306
+ bufadd (&b, c);
307
+ result = vm->sym (b);
308
+ }
309
+ else {
310
+ *error = true ;
311
+ result = vm->string (" unknown parser error" );
312
+ }
313
+ done:
314
+ free (b);
315
+ free (b2);
316
+ return result;
317
+ }
318
+
149
319
// Can be called by the program
150
- void parse (pickle* vm, object* args, object* env, object* cont, object* fail_cont) {
320
+ object* parse (pvm* vm, object* cookie, object* inst_type) {
321
+ (void )cookie;
151
322
DBG (" parsing" );
152
- // getarg(vm, args, 0, &string_type, env, fail_cont, vm->wrap_func(PICKLE_INLINE_FUNC {
153
- // GOTTEN_ARG(s);
154
- // const char* str = (const char*)(s->cells[0].as_chars);
155
- // object* result = s->cells[1].as_obj;
156
- // const char* message;
157
- // bool success = true;
158
- // if (result) { // Saved preparse
159
- // if (result->schema == &error_type) success = false;
160
- // goto done;
161
- // }
162
- // result = vm->wrap_string("Hello, World! parse result i am."); /* TODO: replace this with the actual parse code */
163
- // done:
164
- // if (success) vm->set_retval(vm->list(1, result), env, cont, fail_cont);
165
- // else {
166
- // result = vm->wrap_error(vm->wrap_symbol("SyntaxError"), vm->list(1, vm->wrap_string(message), result), cont);
167
- // vm->set_failure(result, env, cont, fail_cont);
168
- // }
169
- // s->cells[1].as_obj = result; // Save parse for later if constantly eval'ing string (i.e. a loop)
170
- // }));
323
+ object* string = vm->pop ();
324
+ if (string->type != &string_type) {
325
+ vm->push_data (vm->string (" error: non string to parse()" ));
326
+ return vm->sym (" error" );
327
+ }
328
+ const char * str = vm->stringof (string);
329
+ pstate s = { .data = str, .i = 0 , .len = strlen (str) };
330
+ bool error = false ;
331
+ char special = 0 ;
332
+ object* result = do_parse (vm, &s, &error, &special);
333
+ if (special) {
334
+ result = vm->string (" unknown syntax error" );
335
+ error = true ;
336
+ }
337
+ vm->push_data (result);
338
+ return error ? vm->sym (" error" ) : nil;
171
339
}
172
340
173
- static object* get_best_match (pickle * vm, object* ast, object** env) {
341
+ static object* get_best_match (pvm * vm, object* ast, object** env) {
174
342
return NULL ;
175
343
}
176
344
177
345
// Eval(list) ::= apply_first_pattern(list), then eval(remaining list), else list if no patterns match
178
- void eval (pickle * vm, object* args , object* env, object* cont, object* fail_cont ) {
346
+ object* eval (pvm * vm, object* cookie , object* inst_type ) {
179
347
// object* ast = car(args);
180
348
// // returns Match object: 0=pattern, 1=handler body, 2=match details for splice; and updates env with bindings
181
349
// object* oldenv = env;
@@ -207,16 +375,15 @@ void eval(pickle* vm, object* args, object* env, object* cont, object* fail_cont
207
375
// }
208
376
}
209
377
210
- void splice_match (pickle * vm, object* args , object* env, object* cont, object* fail_cont ) {
378
+ object* splice_match (pvm * vm, object* cookie , object* inst_type ) {
211
379
// TODO(sm);
212
380
}
213
381
214
382
// ------------------- Circular-reference-proof object dumper -----------------------
215
383
// ---------- (based on https://stackoverflow.com/a/78169673/23626926) --------------
216
384
217
- static void make_refs_list (pickle * vm, object* obj, object** alist) {
385
+ static void make_refs_list (pvm * vm, object* obj, object** alist) {
218
386
again:
219
- DBG ();
220
387
if (obj == NULL || obj->type != &cons_type) return ;
221
388
object* entry = assoc (*alist, obj);
222
389
if (entry) {
@@ -231,7 +398,7 @@ static void make_refs_list(pickle* vm, object* obj, object** alist) {
231
398
232
399
// returns zero if the object doesn't need a #N# marker
233
400
// otherwise returns N (negative if not first time)
234
- static int64_t reffed (pickle * vm, object* obj, object* alist, int64_t * counter) {
401
+ static int64_t reffed (pvm * vm, object* obj, object* alist, int64_t * counter) {
235
402
object* entry = assoc (alist, obj);
236
403
if (entry) {
237
404
int64_t value = vm->intof (cdr (entry));
@@ -251,13 +418,24 @@ static int64_t reffed(pickle* vm, object* obj, object* alist, int64_t* counter)
251
418
return 0 ;
252
419
}
253
420
254
- static void print_with_refs (pickle * vm, object* obj, object* alist, int64_t * counter) {
421
+ static void print_with_refs (pvm * vm, object* obj, object* alist, int64_t * counter) {
255
422
if (obj == nil) {
256
423
printf (" NIL" );
257
424
return ;
258
425
}
259
426
#define PRINTTYPE (t, f, fmt ) else if (obj->type == t) printf(fmt, obj->f)
260
- PRINTTYPE (&string_type, as_chars, " \" %s\" " );
427
+ else if (obj->type == &string_type) {
428
+ putchar (' "' );
429
+ for (char * c = obj->as_chars ; *c; c++) {
430
+ char e = escape (*c);
431
+ if (e != *c) {
432
+ putchar (' \\ ' );
433
+ putchar (e);
434
+ }
435
+ else putchar (*c);
436
+ }
437
+ putchar (' "' );
438
+ }
261
439
PRINTTYPE (&symbol_type, as_chars, strchr (obj->as_chars , ' ' ) ? " #|%s|" : " %s" );
262
440
PRINTTYPE (&integer_type, as_big_int, " %" PRId64);
263
441
PRINTTYPE (&float_type, as_double, " %lg" );
@@ -301,7 +479,7 @@ static void print_with_refs(pickle* vm, object* obj, object* alist, int64_t* cou
301
479
}
302
480
}
303
481
304
- void pickle ::dump (object* obj) {
482
+ void pvm ::dump (object* obj) {
305
483
object* alist = NULL ;
306
484
int64_t counter = 1 ;
307
485
make_refs_list (this , obj, &alist);
0 commit comments