maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
mlispe.h
Go to the documentation of this file.
1
2#ifndef MLISPE_H
3#define MLISPE_H
4
5#include <mlisps.h>
6
14
15#ifndef MLISP_TOKEN_SZ_MAX
16# define MLISP_TOKEN_SZ_MAX 4096
17#endif /* !MLISP_TOKEN_SZ_MAX */
18
19#ifndef MLISP_EXEC_TRACE_LVL
20# define MLISP_EXEC_TRACE_LVL 0
21#endif /* !MLISP_EXEC_TRACE_LVL */
22
23#define MLISP_ENV_FLAG_BUILTIN 0x02
24
26#define MLISP_ENV_FLAG_CMP_GT 0x10
27
29#define MLISP_ENV_FLAG_CMP_LT 0x20
30
32#define MLISP_ENV_FLAG_CMP_EQ 0x40
33
35#define MLISP_ENV_FLAG_ARI_ADD 0x10
36
38#define MLISP_ENV_FLAG_ARI_MUL 0x20
39
40#define MLISP_ENV_FLAG_ARI_DIV 0x40
41
42#define MLISP_ENV_FLAG_ARI_MOD 0x80
43
44#define MLISP_ENV_FLAG_ANO_OR 0x10
45
46#define MLISP_ENV_FLAG_ANO_AND 0x20
47
52
61#define mlisp_stack_push( exec, i, ctype ) \
62 (_mlisp_stack_push_ ## ctype( exec, (ctype)i ))
63
64MERROR_RETVAL mlisp_stack_dump(
65 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
66
74 struct MLISP_EXEC_STATE* exec, struct MLISP_STACK_NODE* o );
75 /* mlisp_stack */
77
78MERROR_RETVAL mlisp_env_dump(
79 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
80
92 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
93 const char* strpool, size_t token_strpool_idx, size_t token_strpool_sz );
94
95MERROR_RETVAL mlisp_env_unset(
96 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
97 const char* token, size_t token_sz );
98
99MERROR_RETVAL mlisp_env_set(
100 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
101 const char* token, size_t token_sz, uint8_t env_type, const void* data,
102 void* cb_data, uint8_t flags );
103
104MERROR_RETVAL mlisp_check_state(
105 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
106
113 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
114
127 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
128 const char* lambda );
129
130MERROR_RETVAL mlisp_exec_init(
131 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t flags );
132
133void mlisp_exec_free( struct MLISP_EXEC_STATE* exec );
134
135#define _MLISP_TYPE_TABLE_PUSH_PROTO( idx, ctype, name, const_name, fmt ) \
136 MERROR_RETVAL _mlisp_stack_push_ ## ctype( \
137 struct MLISP_EXEC_STATE* exec, ctype i );
138
139MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_PUSH_PROTO )
140
141 /* mlisp */
142
143#define mlisp_ast_has_ready_children( exec_child_idx, n ) \
144 ((exec_child_idx) < (n)->ast_idx_children_sz)
145
146#ifdef MLISPE_C
147
148static MERROR_RETVAL _mlisp_preempt(
149 const char* caller, struct MLISP_PARSER* parser,
150 size_t n_idx, struct MLISP_EXEC_STATE* exec, size_t* p_child_idx,
151 size_t new_idx );
152
153static MERROR_RETVAL _mlisp_step_iter(
154 struct MLISP_PARSER* parser,
155 size_t n_idx, struct MLISP_EXEC_STATE* exec );
156
157/* === */
158
159/* Stack Functions */
160
161/* === */
162
163MERROR_RETVAL mlisp_stack_dump(
164 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
165) {
166 MERROR_RETVAL retval = MERROR_OK;
167 size_t i = 0;
168 char* strpool = NULL;
169 struct MLISP_STACK_NODE* n_stack = NULL;
170
171# define _MLISP_TYPE_TABLE_DUMPS( idx, ctype, name, const_name, fmt ) \
172 } else if( MLISP_TYPE_ ## const_name == n_stack->type ) { \
173 debug_printf( MLISP_EXEC_TRACE_LVL, \
174 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (" #const_name "): " fmt, \
175 i, n_stack->value.name );
176
177 mdata_vector_lock( &(exec->stack) );
178 mdata_strpool_lock( &(parser->strpool), strpool ); \
179 while( i < mdata_vector_ct( &(exec->stack) ) ) {
180 n_stack = mdata_vector_get( &(exec->stack), i, struct MLISP_STACK_NODE );
181
182 /* Handle special exceptions. */
183 if( MLISP_TYPE_STR == n_stack->type ) {
184 debug_printf( MLISP_EXEC_TRACE_LVL,
185 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (STR): %s",
186 i, &(strpool[n_stack->value.strpool_idx]) );
187
188 } else if( MLISP_TYPE_CB == n_stack->type ) {
189 debug_printf( MLISP_EXEC_TRACE_LVL,
190 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (CB): %p",
191 i, n_stack->value.cb );
192
193 } else if( MLISP_TYPE_LAMBDA == n_stack->type ) {
194 debug_printf( MLISP_EXEC_TRACE_LVL,
195 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (LAMBDA): " SIZE_T_FMT,
196 i, n_stack->value.lambda );
197
198 } else if( MLISP_TYPE_ARGS_S == n_stack->type ) {
199 debug_printf( MLISP_EXEC_TRACE_LVL,
200 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (ARGS_S): " SIZE_T_FMT,
201 i, n_stack->value.args_start );
202
203 } else if( MLISP_TYPE_ARGS_E == n_stack->type ) {
204 debug_printf( MLISP_EXEC_TRACE_LVL,
205 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (ARGS_E): " SIZE_T_FMT,
206 i, n_stack->value.args_end );
207
208 } else if( MLISP_TYPE_BEGIN == n_stack->type ) {
209 debug_printf( MLISP_EXEC_TRACE_LVL,
210 MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (BEGIN): " SIZE_T_FMT,
211 i, n_stack->value.begin );
212
213 /* Handle numeric types. */
214 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_DUMPS );
215 } else {
216 error_printf( "invalid stack type: %u", n_stack->type );
217 }
218 i++;
219 }
220 mdata_strpool_unlock( &(parser->strpool), strpool );
221 mdata_vector_unlock( &(exec->stack) );
222
223cleanup:
224
225 assert( NULL == strpool );
226
227 return retval;
228}
229
230/* === */
231
232#define _MLISP_TYPE_TABLE_PUSH( idx, ctype, name, const_name, fmt ) \
233 MERROR_RETVAL _mlisp_stack_push_ ## ctype( \
234 struct MLISP_EXEC_STATE* exec, ctype i \
235 ) { \
236 struct MLISP_STACK_NODE n_stack; \
237 MERROR_RETVAL retval = MERROR_OK; \
238 debug_printf( MLISP_EXEC_TRACE_LVL, \
239 "pushing " #const_name " onto stack: " fmt, i ); \
240 n_stack.type = MLISP_TYPE_ ## const_name; \
241 n_stack.value.name = i; \
242 retval = mdata_vector_append( \
243 &(exec->stack), &n_stack, sizeof( struct MLISP_STACK_NODE ) ); \
244 if( 0 > retval ) { \
245 retval = mdata_retval( retval ); \
246 } else { \
247 retval = 0; \
248 } \
249 return retval; \
250 }
251
252MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_PUSH );
253
254/* === */
255
257 struct MLISP_EXEC_STATE* exec, struct MLISP_STACK_NODE* o
258) {
259 MERROR_RETVAL retval = MERROR_OK;
260 struct MLISP_STACK_NODE* n_stack = NULL;
261 size_t n_idx = 0;
262
263 /* Check for valid stack pointer. */
264 if( mdata_vector_ct( &(exec->stack) ) == 0 ) {
265 error_printf( "stack underflow!" );
266 retval = MERROR_OVERFLOW;
267 goto cleanup;
268 }
269
270 n_idx = mdata_vector_ct( &(exec->stack) ) - 1;
271
272 /* Perform the pop! */
273 mdata_vector_lock( &(exec->stack) );
274 n_stack = mdata_vector_get(
275 &(exec->stack), n_idx, struct MLISP_STACK_NODE );
276 assert( NULL != n_stack );
277 memcpy( o, n_stack, sizeof( struct MLISP_STACK_NODE ) );
278 n_stack = NULL;
279 mdata_vector_unlock( &(exec->stack) );
280
281# define _MLISP_TYPE_TABLE_POPD( idx, ctype, name, const_name, fmt ) \
282 } else if( MLISP_TYPE_ ## const_name == o->type ) { \
283 debug_printf( MLISP_EXEC_TRACE_LVL, \
284 "popping: " SSIZE_T_FMT ": " fmt, n_idx, o->value.name );
285
286 if( 0 ) {
287 MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_POPD )
288 }
289
290 retval = mdata_vector_remove( &(exec->stack), n_idx );
291
292cleanup:
293
294 return retval;
295}
296
297/* === */
298
299/* Env Functons */
300
301/* === */
302
303MERROR_RETVAL mlisp_env_dump(
304 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
305) {
306 MERROR_RETVAL retval = MERROR_OK;
307 size_t i = 0;
308 char* strpool = NULL;
309 struct MLISP_ENV_NODE* e = NULL;
310 struct MDATA_VECTOR* env = NULL;
311
312# define _MLISP_TYPE_TABLE_DUMPE( idx, ctype, name, const_name, fmt ) \
313 } else if( MLISP_TYPE_ ## const_name == e->type ) { \
314 debug_printf( MLISP_EXEC_TRACE_LVL, \
315 MLISP_TRACE_SIGIL " env " SIZE_T_FMT \
316 " \"%s\" (" #const_name "): " fmt, \
317 i, &(strpool[e->name_strpool_idx]), e->value.name ); \
318
319 if(
320 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
321 ) {
322 env = &(parser->env);
323 } else {
324 env = &(exec->env);
325 }
326 assert( NULL != env->data_h );
327 assert( NULL != parser->strpool.str_h );
328 mdata_strpool_lock( &(parser->strpool), strpool );
329 assert( NULL != strpool );
330 mdata_vector_lock( env );
331 while( i < mdata_vector_ct( env ) ) {
332 assert( mdata_vector_is_locked( env ) );
333 e = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
334 assert( NULL != e );
335
336 if( MLISP_ENV_FLAG_BUILTIN == (MLISP_ENV_FLAG_BUILTIN & e->flags) ) {
337 /* Skip builtins. */
338 i++;
339 continue;
340 }
341
342 if( 0 ) {
343 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_DUMPE );
344 /* Handle special exceptions. */
345 } else if( MLISP_TYPE_STR == e->type ) {
346 debug_printf( MLISP_EXEC_TRACE_LVL,
347 MLISP_TRACE_SIGIL " env " SIZE_T_FMT " \"%s\" (STR): %s",
348 i, &(strpool[e->name_strpool_idx]),
349 &(strpool[e->value.strpool_idx]) );
350
351 } else if( MLISP_TYPE_CB == e->type ) {
352 debug_printf( MLISP_EXEC_TRACE_LVL,
353 MLISP_TRACE_SIGIL " env " SIZE_T_FMT " \"%s\" (CB): %p",
354 i, &(strpool[e->name_strpool_idx]), e->value.cb );
355
356 } else if( MLISP_TYPE_LAMBDA == e->type ) {
357 debug_printf( MLISP_EXEC_TRACE_LVL,
358 MLISP_TRACE_SIGIL " env " SIZE_T_FMT
359 " \"%s\" (LAMBDA): " SIZE_T_FMT,
360 i, &(strpool[e->name_strpool_idx]), e->value.lambda );
361
362 } else if( MLISP_TYPE_ARGS_S == e->type ) {
363 debug_printf( MLISP_EXEC_TRACE_LVL,
364 MLISP_TRACE_SIGIL " env " SIZE_T_FMT
365 " \"%s\" (ARGS_S): " SIZE_T_FMT,
366 i, &(strpool[e->name_strpool_idx]), e->value.args_start );
367
368 } else if( MLISP_TYPE_ARGS_E == e->type ) {
369 debug_printf( MLISP_EXEC_TRACE_LVL,
370 MLISP_TRACE_SIGIL " env " SIZE_T_FMT
371 " \"%s\" (ARGS_E): " SIZE_T_FMT,
372 i, &(strpool[e->name_strpool_idx]), e->value.args_end );
373
374 } else {
375 error_printf( MLISP_TRACE_SIGIL " invalid env type: %u", e->type );
376 }
377 i++;
378 }
379 mdata_vector_unlock( env );
380 mdata_strpool_unlock( &(parser->strpool), strpool );
381
382cleanup:
383
384 return retval;
385}
386
387/* === */
388
389struct MLISP_ENV_NODE* mlisp_env_get(
390 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, const char* key
391) {
392 struct MLISP_ENV_NODE* node_out = NULL;
393 struct MLISP_ENV_NODE* node_test = NULL;
394 ssize_t i = 0;
395 struct MDATA_VECTOR* env = NULL;
396 MERROR_RETVAL retval = MERROR_OK;
397 char* strpool;
398
399 if(
400 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
401 ) {
402 env = &(parser->env);
403 } else {
404 env = &(exec->env);
405 }
406 i = mdata_vector_ct( env ) - 1;
407
408 /* This requires env be locked before entrance! */
409 assert( mdata_vector_is_locked( env ) );
410
411 mdata_strpool_lock( &(parser->strpool), strpool );
412
413 while( 0 <= i ) {
414 assert( mdata_vector_is_locked( env ) );
415 node_test = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
416 if( 0 == strncmp(
417 &(strpool[node_test->name_strpool_idx]), key, strlen( key )
418 ) ) {
419 node_out = node_test;
420 break;
421 }
422 i--;
423 }
424
425cleanup:
426
427 if( MERROR_OK != retval ) {
428 node_out = NULL;
429 }
430
431 if( NULL != strpool ) {
432 mdata_strpool_unlock( &(parser->strpool), strpool );
433 }
434
435 return node_out;
436}
437
438/* === */
439
441 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
442 const char* strpool, size_t token_strpool_idx, size_t token_strpool_sz
443) {
444 struct MLISP_ENV_NODE* node_out = NULL;
445 struct MLISP_ENV_NODE* node_test = NULL;
446 ssize_t i = 0;
447 struct MDATA_VECTOR* env = NULL;
448
449 if(
450 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
451 ) {
452 env = &(parser->env);
453 } else {
454 env = &(exec->env);
455 }
456 i = mdata_vector_ct( env ) - 1;
457
458 /* This requires env be locked before entrance! */
459 assert( mdata_vector_is_locked( env ) );
460
461 while( 0 <= i ) {
462 assert( mdata_vector_is_locked( env ) );
463 node_test = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
464 if( 0 == strncmp(
465 &(strpool[node_test->name_strpool_idx]),
466 &(strpool[token_strpool_idx]),
467 token_strpool_sz + 1
468 ) ) {
469 node_out = node_test;
470 break;
471 }
472 i--;
473 }
474
475 return node_out;
476}
477
478/* === */
479
480MERROR_RETVAL mlisp_env_unset(
481 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
482 const char* token, size_t token_sz
483) {
484 MERROR_RETVAL retval = MERROR_OK;
485 ssize_t i = 0;
486 struct MLISP_ENV_NODE* e = NULL;
487 char* strpool = NULL;
488 struct MDATA_VECTOR* env = NULL;
489
490 if(
491 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
492 ) {
493 env = &(parser->env);
494 } else {
495 env = &(exec->env);
496 }
497
498 assert( !mdata_vector_is_locked( env ) );
499 mdata_vector_lock( env );
500
501 mdata_strpool_lock( &(parser->strpool), strpool );
502
503 debug_printf( MLISP_TRACE_LVL, "attempting to undefine %s...", token );
504
505 /* Search for the given token in the env. */
506 for( i = mdata_vector_ct( env ) - 1 ; 0 <= i ; i-- ) {
507 assert( mdata_vector_is_locked( env ) );
508 e = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
509
510 /* TODO: This could be problematic if MLISP_EXEC_FLAG_SHARED_ENV is
511 * enabled...
512 */
513 if( MLISP_TYPE_ARGS_E == e->type ) {
514 debug_printf( MLISP_EXEC_TRACE_LVL,
515 "reached end of env stack frame: " SSIZE_T_FMT, i );
516 goto cleanup;
517 }
518
519 if( 0 != strncmp(
520 token, &(strpool[e->name_strpool_idx]), token_sz + 1 )
521 ) {
522 continue;
523 }
524
525 /* Remove the token. */
526 debug_printf( MLISP_EXEC_TRACE_LVL,
527 "found token %s: %s (" SSIZE_T_FMT "), removing...",
528 token, &(strpool[e->name_strpool_idx]), i );
529 mdata_vector_unlock( env );
530
531 retval = mdata_vector_remove( env, i );
532 mdata_vector_lock( env );
533 goto cleanup;
534 }
535
536cleanup:
537
538 assert( mdata_vector_is_locked( env ) );
539 mdata_vector_unlock( env );
540
541 mdata_strpool_unlock( &(parser->strpool), strpool );
542
543 return retval;
544}
545
546/* === */
547
548MERROR_RETVAL mlisp_env_set(
549 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
550 const char* token, size_t token_sz, uint8_t env_type, const void* data,
551 void* cb_data, uint8_t flags
552) {
553 MERROR_RETVAL retval = MERROR_OK;
554 struct MLISP_ENV_NODE e;
555 ssize_t new_idx_out = -1;
556 struct MDATA_VECTOR* env = NULL;
557
558 if(
559 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
560 ) {
561 debug_printf( MLISP_EXEC_TRACE_LVL, "using parser env..." );
562 env = &(parser->env);
563 } else {
564 debug_printf( MLISP_EXEC_TRACE_LVL, "using exec env..." );
565 env = &(exec->env);
566 }
567
568 assert( NULL != env );
569
570 if( 0 == token_sz ) {
571 token_sz = maug_strlen( token );
572 }
573 assert( 0 < token_sz );
574
575 /* TODO: Find previous env nodes with same token and change. */
576
577 retval = mlisp_env_unset( parser, exec, token, token_sz );
578 assert( 0 == retval );
579 maug_cleanup_if_not_ok();
580
581# define _MLISP_TYPE_TABLE_ASGN( idx, ctype, name, const_name, fmt ) \
582 case idx: \
583 debug_printf( MLISP_EXEC_TRACE_LVL, \
584 "setting env: \"%s\": #" fmt, \
585 token, (ctype)*((ctype*)data) ); \
586 e.value.name = *((ctype*)data); \
587 break;
588
589 /* Setup the new node to copy. */
590 maug_mzero( &e, sizeof( struct MLISP_ENV_NODE ) );
591 e.flags = flags;
592 e.name_strpool_idx =
593 mdata_strpool_append( &(parser->strpool), token, token_sz );
594 if( 0 > e.name_strpool_idx ) {
595 retval = mdata_retval( e.name_strpool_idx );
596 }
597 maug_cleanup_if_not_ok();
598 e.type = env_type;
599 e.cb_data = cb_data;
600 switch( env_type ) {
601 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ASGN );
602
603 /* Special cases: */
604
605 case 4 /* MLISP_TYPE_STR */:
606 debug_printf( MLISP_EXEC_TRACE_LVL,
607 "setting env: \"%s\": strpool(" SSIZE_T_FMT ")",
608 token, *((ssize_t*)data) );
609 e.value.strpool_idx = *((mdata_strpool_idx_t*)data);
610 break;
611
612 case 5 /* MLISP_TYPE_CB */:
613 debug_printf( MLISP_EXEC_TRACE_LVL,
614 "setting env: \"%s\": 0x%p", token, (mlisp_env_cb_t)data );
615 e.value.cb = (mlisp_env_cb_t)data;
616 break;
617
618 case 6 /* MLISP_TYPE_LAMBDA */:
619 debug_printf( MLISP_EXEC_TRACE_LVL,
620 "setting env: \"%s\": node #" SSIZE_T_FMT,
621 token, *((mlisp_lambda_t*)data) );
622 e.value.lambda = *((mlisp_lambda_t*)data);
623 break;
624
625 case 7: /* MLISP_TYPE_ARGS_S */
626 debug_printf( MLISP_EXEC_TRACE_LVL,
627 "setting env: \"%s\": node #" SSIZE_T_FMT,
628 token, *((mlisp_args_t*)data) );
629 e.value.args_start = *((mlisp_args_t*)data);
630 break;
631
632 case 8: /* MLISP_TYPE_ARGS_E */
633 debug_printf( MLISP_EXEC_TRACE_LVL,
634 "setting env: \"%s\": node #" SSIZE_T_FMT,
635 token, *((mlisp_arge_t*)data) );
636 e.value.args_end = *((mlisp_arge_t*)data);
637 break;
638
639 default:
640 error_printf( "attempted to define invalid type: %d", env_type );
641 retval = MERROR_EXEC;
642 goto cleanup;
643 }
644
645 /* Add the node to the env. */
646 new_idx_out = mdata_vector_append(
647 env, &e, sizeof( struct MLISP_ENV_NODE ) );
648 assert( 0 < mdata_vector_ct( env ) );
649 debug_printf( MLISP_EXEC_TRACE_LVL, "env %p has " SIZE_T_FMT " nodes",
650 env, mdata_vector_ct( env ) );
651 if( 0 > new_idx_out ) {
652 retval = mdata_retval( new_idx_out );
653 }
654 maug_cleanup_if_not_ok();
655
656 debug_printf( MLISP_EXEC_TRACE_LVL, "setup env node " SSIZE_T_FMT ": %s",
657 new_idx_out, token );
658
659cleanup:
660
661 return retval;
662}
663
664/* === */
665
666static ssize_t _mlisp_env_get_env_frame(
667 struct MLISP_EXEC_STATE* exec, struct MLISP_PARSER* parser,
668 struct MLISP_ENV_NODE* e_out
669) {
670 MERROR_RETVAL retval = MERROR_OK;
671 ssize_t ret_idx = 0;
672 struct MLISP_ENV_NODE* e = NULL;
673 ssize_t i = 0;
674 uint8_t autolock = 0;
675 struct MDATA_VECTOR* env = NULL;
676
677 if(
678 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
679 ) {
680 env = &(parser->env);
681 } else {
682 env = &(exec->env);
683 }
684
685 if( !mdata_vector_is_locked( env ) ) {
686 mdata_vector_lock( env );
687 autolock = 1;
688 }
689
690 for( i = mdata_vector_ct( env ) - 1; 0 <= i ; i-- ) {
691 /* debug_printf( MLISP_EXEC_TRACE_LVL,
692 "getting frame (trying " SSIZE_T_FMT "...)", i ); */
693 assert( mdata_vector_is_locked( env ) );
694 e = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
695 assert( NULL != e );
696
697 if( MLISP_TYPE_ARGS_S != e->type ) {
698 /* Hunt for the initial env arg separator. */
699 continue;
700 }
701
702 debug_printf( MLISP_EXEC_TRACE_LVL,
703 "found initial env arg separator " SSIZE_T_FMT " with ret: "
704 SSIZE_T_FMT,
705 i, e->value.args_start );
706
707 ret_idx = i;
708 if( NULL != e_out ) {
709 memcpy( e_out, e, sizeof( struct MLISP_ENV_NODE ) );
710 }
711 break;
712 }
713
714cleanup:
715
716 if( autolock ) {
717 mdata_vector_unlock( env );
718 }
719
720 if( MERROR_OK != retval ) {
721 ret_idx = retval * -1;
722 }
723
724 return ret_idx;
725}
726
727/* === */
728
729static ssize_t _mlisp_env_prune_args(
730 struct MLISP_EXEC_STATE* exec, struct MLISP_PARSER* parser
731) {
732 ssize_t ret_idx = 0;
733 MERROR_RETVAL retval = MERROR_OK;
734 ssize_t i = 0;
735 struct MLISP_ENV_NODE* e = NULL;
736 size_t removed = 0;
737 struct MDATA_VECTOR* env = NULL;
738
739 if(
740 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
741 ) {
742 env = &(parser->env);
743 } else {
744 env = &(exec->env);
745 }
746
747 /* This function modifies the env, so existing locks might break. */
748 assert( !mdata_vector_is_locked( env ) );
749
750 assert( 0 < mdata_vector_ct( env ) );
751
752 mdata_vector_lock( env );
753
754 /* Get the most recent start frame in the env. */
755 i = _mlisp_env_get_env_frame( exec, parser, NULL );
756 debug_printf( MLISP_EXEC_TRACE_LVL,
757 "pruning env args starting from env frame " SSIZE_T_FMT "...", i );
758 e = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
759 assert( NULL != e );
760
761 while( MLISP_TYPE_ARGS_E != e->type ) {
762 mdata_vector_unlock( env );
763 retval = mdata_vector_remove( env, i );
764 maug_cleanup_if_not_ok();
765 mdata_vector_lock( env );
766
767 /* Refresh e based on what i *now* points to. */
768 assert( mdata_vector_is_locked( env ) );
769 e = mdata_vector_get( env, i, struct MLISP_ENV_NODE );
770 assert( NULL != e );
771
772 removed++;
773 }
774
775 /* Remove the actual terminal separator. */
776 mdata_vector_unlock( env );
777 retval = mdata_vector_remove( env, i );
778 maug_cleanup_if_not_ok();
779 mdata_vector_lock( env );
780
781 debug_printf( MLISP_EXEC_TRACE_LVL,
782 "removed " SIZE_T_FMT " args!", removed );
783
784cleanup:
785
786 mdata_vector_unlock( env );
787
788 if( MERROR_OK != retval ) {
789 ret_idx = retval * -1;
790 }
791
792 return ret_idx;
793}
794
795/* === */
796
797static MERROR_RETVAL _mlisp_env_cb_cmp(
798 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
799 size_t args_c, void* cb_data, uint8_t flags
800) {
801 MERROR_RETVAL retval = MERROR_OK;
802 struct MLISP_STACK_NODE tmp;
803 char* strpool = NULL;
804 uint8_t truth = 0;
805 int a_int,
806 b_int;
807 int* cur_int = NULL;
808
809 /* XXX: If we put a mutable variable first, it gets modified? */
810
811# define _MLISP_TYPE_TABLE_CMP( idx, ctype, name, const_name, fmt ) \
812 } else if( MLISP_TYPE_ ## const_name == tmp.type ) { \
813 *cur_int = (int)tmp.value.name; \
814 debug_printf( MLISP_EXEC_TRACE_LVL, \
815 "cmp: pop " fmt " (%d)", tmp.value.name, *cur_int );
816
817 retval = mlisp_stack_pop( exec, &tmp );
818 maug_cleanup_if_not_ok();
819 cur_int = &b_int;
820 if( 0 ) {
821 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_CMP )
822 } else {
823 error_printf( "cmp: invalid type!" );
824 retval = MERROR_EXEC;
825 goto cleanup;
826 }
827
828 retval = mlisp_stack_pop( exec, &tmp );
829 maug_cleanup_if_not_ok();
830 cur_int = &a_int;
831 if( 0 ) {
832 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_CMP )
833 } else {
834 error_printf( "cmp: invalid type!" );
835 retval = MERROR_EXEC;
836 goto cleanup;
837 }
838
839 /* TODO: String comparison? */
840
842 debug_printf( MLISP_EXEC_TRACE_LVL, "cmp %d > %d", a_int, b_int );
843 truth = a_int > b_int;
844 } else if( MLISP_ENV_FLAG_CMP_LT == (MLISP_ENV_FLAG_CMP_LT & flags) ) {
845 debug_printf( MLISP_EXEC_TRACE_LVL, "cmp %d < %d", a_int, b_int );
846 truth = a_int < b_int;
847 } else if( MLISP_ENV_FLAG_CMP_EQ == (MLISP_ENV_FLAG_CMP_EQ & flags) ) {
848 debug_printf( MLISP_EXEC_TRACE_LVL, "cmp %d == %d", a_int, b_int );
849 truth = a_int == b_int;
850 } else {
851 error_printf( "invalid parameter provided to _mlisp_env_cb_cmp()!" );
852 retval = MERROR_EXEC;
853 goto cleanup;
854 }
855
856 retval = mlisp_stack_push( exec, truth, mlisp_bool_t );
857
858cleanup:
859
860 mdata_strpool_unlock( &(parser->strpool), strpool );
861
862
863 return retval;
864}
865
866/* === */
867
868static MERROR_RETVAL _mlisp_env_cb_arithmetic(
869 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
870 size_t args_c, void* cb_data, uint8_t flags
871) {
872 MERROR_RETVAL retval = MERROR_OK;
873 struct MLISP_STACK_NODE num;
874 char* strpool = NULL;
875 /* TODO: Vary type based on multiplied types. */
876 int16_t num_out = 0;
877 size_t i = 0;
878
879# define _MLISP_TYPE_TABLE_ARI1( idx, ctype, name, const_name, fmt ) \
880 } else if( MLISP_TYPE_ ## const_name == num.type ) { \
881 num_out = num.value.name;
882
883 retval = mlisp_stack_pop( exec, &num );
884 maug_cleanup_if_not_ok();
885
886 if( 0 ) {
887 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ARI1 )
888 } else {
889 error_printf( "arithmetic: invalid type!" );
890 retval = MERROR_EXEC;
891 goto cleanup;
892 }
893
894# define _MLISP_TYPE_TABLE_ARI2( idx, ctype, name, const_name, fmt ) \
895 } else if( \
896 MLISP_TYPE_ ## const_name == num.type && \
897 MLISP_ENV_FLAG_ARI_ADD == (MLISP_ENV_FLAG_ARI_ADD & flags) \
898 ) { \
899 debug_printf( MLISP_EXEC_TRACE_LVL, \
900 "arithmetic: %d + " fmt, num_out, num.value.name ); \
901 num_out += num.value.name; \
902 } else if( \
903 MLISP_TYPE_ ## const_name == num.type && \
904 MLISP_ENV_FLAG_ARI_MUL == (MLISP_ENV_FLAG_ARI_MUL & flags) \
905 ) { \
906 debug_printf( MLISP_EXEC_TRACE_LVL, \
907 "arithmetic: %d * " fmt, num_out, num.value.name ); \
908 num_out *= num.value.name; \
909 } else if( \
910 MLISP_TYPE_ ## const_name == num.type && \
911 MLISP_ENV_FLAG_ARI_DIV == (MLISP_ENV_FLAG_ARI_DIV & flags) \
912 ) { \
913 debug_printf( MLISP_EXEC_TRACE_LVL, \
914 "arithmetic: %d / " fmt, num_out, num.value.name ); \
915 num_out /= num.value.name; \
916
917 for( i = 0 ; args_c - 1 > i ; i++ ) {
918 retval = mlisp_stack_pop( exec, &num );
919 maug_cleanup_if_not_ok();
920
921 if( 0 ) {
922 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ARI2 )
923
924 } else if(
925 MLISP_TYPE_INT == num.type &&
926 MLISP_ENV_FLAG_ARI_MOD == (MLISP_ENV_FLAG_ARI_MOD & flags)
927 ) {
928 /* Modulus is a special case, as you can't mod by float. */
929 debug_printf( MLISP_EXEC_TRACE_LVL,
930 "arithmetic: %d %% %d", num_out, num.value.integer );
931 num_out %= num.value.integer;
932 } else {
933 error_printf( "arithmetic: invalid type!" );
934 retval = MERROR_EXEC;
935 goto cleanup;
936 }
937 }
938
939 debug_printf( MLISP_EXEC_TRACE_LVL, "arithmetic result: %d", num_out );
940
941 retval = mlisp_stack_push( exec, num_out, int16_t );
942
943cleanup:
944
945 mdata_strpool_unlock( &(parser->strpool), strpool );
946
947 return retval;
948}
949
950/* === */
951
952static MERROR_RETVAL _mlisp_env_cb_define(
953 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
954 size_t args_c, void* cb_data, uint8_t flags
955) {
956 MERROR_RETVAL retval = MERROR_OK;
957 struct MLISP_STACK_NODE key;
958 struct MLISP_STACK_NODE val;
959 MAUG_MHANDLE key_tmp_h = NULL;
960 char* key_tmp = NULL;
961
962 retval = mlisp_stack_pop( exec, &val );
963 maug_cleanup_if_not_ok();
964
965 retval = mlisp_stack_pop( exec, &key );
966 maug_cleanup_if_not_ok();
967
968 if( MLISP_TYPE_STR != key.type ) {
969 /* TODO: Do we want to allow defining other types? */
970 /* TODO: We can use _mlisp_eval_token_strpool, maybe? */
971 error_printf( "define: invalid key type: %d", key.type );
972 retval = MERROR_EXEC;
973 goto cleanup;
974 }
975
976 key_tmp_h = mdata_strpool_extract(
977 &(parser->strpool), key.value.strpool_idx );
978 /* TODO: Handle this gracefully. */
979 assert( NULL != key_tmp_h );
980
981 maug_mlock( key_tmp_h, key_tmp );
982 maug_cleanup_if_null_lock( char*, key_tmp );
983
984 debug_printf( MLISP_EXEC_TRACE_LVL,
985 "define \"%s\" (strpool(" SIZE_T_FMT "))...",
986 key_tmp, key.value.strpool_idx );
987
988 retval = mlisp_env_set(
989 parser, exec, key_tmp, 0, val.type, &(val.value), NULL, 0 );
990
991cleanup:
992
993 if( NULL != key_tmp ) {
994 maug_munlock( key_tmp_h, key_tmp );
995 }
996
997 if( NULL != key_tmp_h ) {
998 maug_mfree( key_tmp_h );
999 }
1000
1001 return retval;
1002}
1003
1004/* === */
1005
1006static MERROR_RETVAL _mlisp_env_cb_if(
1007 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1008 size_t args_c, void* cb_data, uint8_t flags
1009) {
1010 MERROR_RETVAL retval = MERROR_OK;
1011 size_t* p_if_child_idx = NULL;
1012 struct MLISP_STACK_NODE s;
1013 struct MLISP_AST_NODE* n = NULL;
1014
1015 debug_printf( MLISP_EXEC_TRACE_LVL, "qrqrqrqrqr STEP IF qrqrqrqrqr" );
1016
1017 /* Grab the current exec index for the child vector for this node. */
1018 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1019 p_if_child_idx = mdata_vector_get(
1020 &(exec->per_node_child_idx), n_idx, size_t );
1021 assert( NULL != p_if_child_idx );
1022 debug_printf( MLISP_EXEC_TRACE_LVL,
1023 "child idx for if AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1024 n_idx, *p_if_child_idx );
1025
1026 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1027
1028 if( 0 == *p_if_child_idx ) {
1029 /* Evaluating if condition. */
1030 debug_printf( MLISP_EXEC_TRACE_LVL, "stepping into condition..." );
1031 retval = _mlisp_step_iter(
1032 parser, n->ast_idx_children[*p_if_child_idx], exec );
1033 debug_printf( MLISP_EXEC_TRACE_LVL, "...stepped out of condition" );
1034
1035 /* Vary the child we jump to based on the boolean val on the stack. */
1036 if( MERROR_OK == retval ) {
1037 /* Condition evaluation complete. */
1038
1039 /* Pop the result and check it. */
1040 retval = mlisp_stack_pop( exec, &s );
1041 maug_cleanup_if_not_ok();
1042 if( MLISP_TYPE_BOOLEAN != s.type ) {
1043 error_printf( "(if) can only evaluate boolean type!" );
1044 retval = MERROR_EXEC;
1045 goto cleanup;
1046 }
1047
1048 /* Set the child pointer to 1 if TRUE and 2 if FALSE. */
1049 retval = _mlisp_preempt(
1050 "if", parser, n_idx, exec, p_if_child_idx,
1051 /* Flip boolean and increment. */
1052 (1 - s.value.boolean) + 1 );
1053 }
1054
1055 } else if( args_c > *p_if_child_idx ) { /* 3 if else present, else 2. */
1056 /* Pursuing TRUE or FALSE clause. */
1057
1058 debug_printf( MLISP_EXEC_TRACE_LVL,
1059 "descending into IF path: " SIZE_T_FMT, *p_if_child_idx );
1060
1061 /* Prepare for stepping. */
1062
1063 /* Step and check. */
1064 retval = _mlisp_step_iter(
1065 parser, n->ast_idx_children[*p_if_child_idx], exec );
1066 if( MERROR_OK == retval ) {
1067 retval = _mlisp_preempt(
1068 "if", parser, n_idx, exec, p_if_child_idx, 3 );
1069 }
1070 }
1071
1072cleanup:
1073
1074 debug_printf( MLISP_EXEC_TRACE_LVL, "qrqrqrqrqr END STEP IF qrqrqrqrqr" );
1075
1076 return retval;
1077}
1078
1079/* === */
1080
1081static MERROR_RETVAL _mlisp_env_cb_random(
1082 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1083 size_t args_c, void* cb_data, uint8_t flags
1084) {
1085 MERROR_RETVAL retval = MERROR_OK;
1086 struct MLISP_STACK_NODE mod;
1087 int16_t random_int = 0;
1088
1089 retval = mlisp_stack_pop( exec, &mod );
1090 maug_cleanup_if_not_ok();
1091
1092 if( MLISP_TYPE_INT != mod.type ) {
1093 /* TODO: Setup float. */
1094 error_printf( "random: invalid modulus type: %d", mod.type );
1095 retval = MERROR_EXEC;
1096 goto cleanup;
1097 }
1098
1099 random_int = retroflat_get_rand() % mod.value.integer;
1100
1101 debug_printf( MLISP_EXEC_TRACE_LVL, "random: %d", random_int );
1102
1103 mlisp_stack_push( exec, random_int, int16_t );
1104
1105cleanup:
1106
1107 return retval;
1108}
1109
1110/* === */
1111
1112static MERROR_RETVAL _mlisp_env_cb_ano(
1113 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1114 size_t args_c, void* cb_data, uint8_t flags
1115) {
1116 MERROR_RETVAL retval = MERROR_OK;
1117 struct MLISP_STACK_NODE val;
1118 mlisp_bool_t val_out =
1119 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ?
1120 0 : 1;
1121 size_t i = 0;
1122
1123 /* TODO: Switch this to a step_or() function so that we can opt not to
1124 * evaluate conditions unless prior stepped children are false.
1125 */
1126
1127 for( i = 0 ; args_c > i ; i++ ) {
1128 retval = mlisp_stack_pop( exec, &val );
1129 maug_cleanup_if_not_ok();
1130
1131 if( MLISP_TYPE_BOOLEAN != val.type ) {
1132 error_printf( "or: invalid boolean type: %d", val.type );
1133 }
1134
1135 if( val.value.boolean ) {
1136 debug_printf( MLISP_EXEC_TRACE_LVL, "found TRUE in %s!",
1137 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ?
1138 "or" : "and" );
1139 val_out =
1140 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ? 1 : 0;
1141 }
1142 }
1143
1144 retval = _mlisp_stack_push_mlisp_bool_t( exec, val_out );
1145
1146cleanup:
1147
1148 return retval;
1149}
1150
1151/* === */
1152
1153/* Execution Functions */
1154
1155/* === */
1156
1157static MERROR_RETVAL _mlisp_preempt(
1158 const char* caller, struct MLISP_PARSER* parser,
1159 size_t n_idx, struct MLISP_EXEC_STATE* exec, size_t* p_child_idx,
1160 size_t new_idx
1161) {
1162 /* Could not exec *this* node yet, so don't increment its parent. */
1163 MERROR_RETVAL retval = MERROR_PREEMPT;
1164 char* strpool = NULL;
1165 struct MLISP_AST_NODE* n = NULL;
1166
1167 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1168
1169 mdata_strpool_lock( &(parser->strpool), strpool );
1170 assert( 0 < maug_strlen( &(strpool[n->token_idx]) ) );
1171 debug_printf( MLISP_EXEC_TRACE_LVL,
1172 "eval step " SSIZE_T_FMT " under (%s) %s...",
1173 *p_child_idx, caller, &(strpool[n->token_idx]) );
1174 mdata_strpool_unlock( &(parser->strpool), strpool );
1175
1176 /* Increment this node, since the child actually executed. */
1177 (*p_child_idx) = new_idx;
1178 debug_printf( MLISP_EXEC_TRACE_LVL,
1179 "incremented " SIZE_T_FMT " child idx to: " SIZE_T_FMT,
1180 n_idx, *p_child_idx );
1181
1182cleanup:
1183
1184 assert( NULL == strpool );
1185
1186 return retval;
1187}
1188
1189/* === */
1190
1191static MERROR_RETVAL _mlisp_step_iter_children(
1192 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1193) {
1194 MERROR_RETVAL retval = MERROR_OK;
1195 size_t* p_child_idx = NULL;
1196 struct MLISP_AST_NODE* n = NULL;
1197
1198 /* Grab the current exec index for the child vector for this node. */
1199 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1200 p_child_idx = mdata_vector_get(
1201 &(exec->per_node_child_idx), n_idx, size_t );
1202 assert( NULL != p_child_idx );
1203 debug_printf( MLISP_EXEC_TRACE_LVL,
1204 "child idx for AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1205 n_idx, *p_child_idx );
1206
1207 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1208
1209 if(
1210 (
1211 MLISP_AST_FLAG_LAMBDA == (MLISP_AST_FLAG_LAMBDA & n->flags) &&
1212 0 == *p_child_idx
1213 ) ||
1214 MLISP_AST_FLAG_IF == (MLISP_AST_FLAG_IF & n->flags)
1215 ) {
1216 /* A lambda definition was found, and its exec counter is still pointing
1217 * to the arg list. This means the lambda was *not* called on the last
1218 * heartbeat, and we're probably just enountering its definition.
1219 *
1220 * Lambdas are lazily evaluated, so don't pursue it further until it's
1221 * called (stee _mlisp_step_lambda() for more info on this.
1222 */
1223 debug_printf( MLISP_EXEC_TRACE_LVL, "skipping lambda children..." );
1224 goto cleanup;
1225 }
1226
1227 if( mlisp_ast_has_ready_children( *p_child_idx, n ) ) {
1228 /* Call the next uncalled child. */
1229
1230 if(
1231 MLISP_AST_FLAG_DEFINE == (MLISP_AST_FLAG_DEFINE & n->flags) &&
1232 0 == *p_child_idx
1233 ) {
1234 /* The next child is a term to be defined. */
1235 debug_printf( MLISP_EXEC_TRACE_LVL,
1236 "setting MLISP_EXEC_FLAG_DEF_TERM!" );
1237 exec->flags |= MLISP_EXEC_FLAG_DEF_TERM;
1238 } else {
1239 exec->flags &= ~MLISP_EXEC_FLAG_DEF_TERM;
1240 }
1241
1242 /* Step and check. */
1243 retval = _mlisp_step_iter(
1244 parser, n->ast_idx_children[*p_child_idx], exec );
1245 if( MERROR_OK == retval ) {
1246 retval = _mlisp_preempt(
1247 "node", parser, n_idx, exec, p_child_idx, (*p_child_idx) + 1 );
1248 }
1249 goto cleanup;
1250
1251#if 0
1252 } else {
1253 /* Reset the node's child pointer to 0... this will allow it to be
1254 * re-entered later.
1255 */
1256 debug_printf( MDATA_TRACE_LVL,
1257 "resetting node " SIZE_T_FMT " child pointer to 0...",
1258 n_idx );
1259 *p_child_idx = 0;
1260#endif
1261 }
1262
1263cleanup:
1264
1265 return retval;
1266}
1267
1268/* === */
1269
1270static MERROR_RETVAL _mlisp_step_lambda_args(
1271 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1272) {
1273 MERROR_RETVAL retval = MERROR_OK;
1274 ssize_t arg_idx = 0;
1275 struct MLISP_STACK_NODE stack_n_arg;
1276 struct MLISP_AST_NODE* ast_n_arg = NULL;
1277 MAUG_MHANDLE key_tmp_h = NULL;
1278 char* key_tmp = NULL;
1279 struct MLISP_AST_NODE* n = NULL;
1280
1281 /* Pop stack into args into the env. These are all the results of previous
1282 * evaluations, before the lambda call, so we can just grab them all in
1283 * one go!
1284 */
1285
1286 /* Get the current args node. */
1287 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1288 arg_idx = n->ast_idx_children_sz - 1;
1289
1290 while( 0 <= arg_idx ) {
1291
1292 retval = mlisp_stack_pop( exec, &stack_n_arg );
1293 maug_cleanup_if_not_ok();
1294
1295 ast_n_arg = mdata_vector_get(
1296 &(parser->ast), n->ast_idx_children[arg_idx],
1297 struct MLISP_AST_NODE );
1298
1299 /* Pull out the arg name from the strpool so we can call env_set(). */
1300 key_tmp_h = mdata_strpool_extract(
1301 &(parser->strpool), ast_n_arg->token_idx );
1302 /* TODO: Handle this gracefully. */
1303 assert( NULL != key_tmp_h );
1304
1305 maug_mlock( key_tmp_h, key_tmp );
1306 maug_cleanup_if_null_lock( char*, key_tmp );
1307
1308 retval = mlisp_env_set(
1309 parser, exec, key_tmp, 0, stack_n_arg.type, &(stack_n_arg.value),
1310 NULL, 0 );
1311 maug_cleanup_if_not_ok();
1312
1313 maug_munlock( key_tmp_h, key_tmp );
1314 maug_mfree( key_tmp_h );
1315
1316 arg_idx--;
1317 }
1318
1319cleanup:
1320
1321 if( NULL != key_tmp ) {
1322 maug_munlock( key_tmp_h, key_tmp );
1323 }
1324
1325 if( NULL != key_tmp_h ) {
1326 maug_mfree( key_tmp_h );
1327 }
1328
1329 return retval;
1330}
1331
1332/* === */
1333
1334static MERROR_RETVAL _mlisp_reset_child_pcs(
1335 struct MLISP_PARSER* parser,
1336 size_t n_idx, struct MLISP_EXEC_STATE* exec
1337) {
1338 MERROR_RETVAL retval = MERROR_OK;
1339 size_t* p_child_idx = NULL;
1340 size_t* p_visit_ct = NULL;
1341 struct MLISP_AST_NODE* n = NULL;
1342 size_t i = 0;
1343
1344 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1345 assert( mdata_vector_is_locked( &(parser->ast) ) );
1346
1347 /* Perform the actual reset. */
1348 debug_printf( MLISP_TRACE_LVL, "resetting PC on node: " SIZE_T_FMT, n_idx );
1349 p_child_idx = mdata_vector_get( &(exec->per_node_child_idx), n_idx, size_t );
1350 assert( NULL != p_child_idx );
1351 *p_child_idx = 0;
1352
1353 debug_printf( MLISP_TRACE_LVL,
1354 "resetting visit count on node: " SIZE_T_FMT, n_idx );
1355 p_visit_ct = mdata_vector_get( &(exec->per_node_visit_ct), n_idx, size_t );
1356 assert( NULL != p_visit_ct );
1357 *p_visit_ct = 0;
1358
1359 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1360
1361 /* Call reset on all children. */
1362 for( i = 0 ; n->ast_idx_children_sz > i ; i++ ) {
1363 retval = _mlisp_reset_child_pcs( parser, n->ast_idx_children[i], exec );
1364 maug_cleanup_if_not_ok();
1365 }
1366
1367cleanup:
1368
1369 return retval;
1370}
1371
1372/* === */
1373
1374static MERROR_RETVAL _mlisp_reset_lambda(
1375 struct MLISP_PARSER* parser,
1376 size_t n_idx, struct MLISP_EXEC_STATE* exec
1377) {
1378 MERROR_RETVAL retval = MERROR_OK;
1379 ssize_t ret_idx = 0;
1380 struct MDATA_VECTOR* env = NULL;
1381
1382 if(
1383 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
1384 ) {
1385 env = &(parser->env);
1386 } else {
1387 env = &(exec->env);
1388 }
1389
1390 debug_printf( MLISP_EXEC_TRACE_LVL,
1391 "resetting lambda " SIZE_T_FMT "...", n_idx );
1392
1393 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1394 assert( !mdata_vector_is_locked( env ) );
1395
1396 /* Clear off lambda stack args. */
1397 ret_idx = _mlisp_env_prune_args( exec, parser );
1398 if( 0 > ret_idx ) {
1399 retval = ret_idx * -1;
1400 }
1401 maug_cleanup_if_not_ok();
1402
1403 /* Reset per-node program counters. */
1404 retval = _mlisp_reset_child_pcs( parser, n_idx, exec );
1405
1406cleanup:
1407
1408 return retval;
1409}
1410
1411/* === */
1412
1413/* This is internal-only and should only be called from _mlisp_step_iter()! */
1414static MERROR_RETVAL _mlisp_step_lambda(
1415 struct MLISP_PARSER* parser,
1416 size_t n_idx, struct MLISP_EXEC_STATE* exec
1417) {
1418 MERROR_RETVAL retval = MERROR_OK;
1419 size_t* p_lambda_child_idx = NULL;
1420 size_t* p_args_child_idx = NULL;
1421 struct MLISP_AST_NODE* n = NULL;
1422 size_t* p_n_last_lambda = NULL;
1423 ssize_t append_retval = 0;
1424
1425#ifdef MLISP_DEBUG_TRACE
1426 exec->trace[exec->trace_depth++] = n_idx;
1427 assert( exec->trace_depth <= MLISP_DEBUG_TRACE );
1428#endif /* MLISP_DEBUG_TRACE */
1429
1430 /* n_idx is the node of this lambda. */
1431 mdata_vector_lock( &(exec->lambda_trace) );
1432 p_n_last_lambda = mdata_vector_get_last( &(exec->lambda_trace), size_t );
1433 mdata_vector_unlock( &(exec->lambda_trace) );
1434 if( NULL != p_n_last_lambda && n_idx == *p_n_last_lambda ) {
1435 /* This is a recursive call, so get rid of the lambda context so we can
1436 * replace it with a new one afterwards.
1437 */
1438 debug_printf( MLISP_EXEC_TRACE_LVL, "TRACE TAIL TIME!" );
1439 _mlisp_reset_lambda( parser, n_idx, exec );
1440 retval = mdata_vector_remove_last( &(exec->lambda_trace) );
1441 maug_cleanup_if_not_ok();
1442 }
1443
1444 debug_printf( MLISP_EXEC_TRACE_LVL,
1445 "xvxvxvxvxvxvx STEP LAMBDA " SIZE_T_FMT " xvxvxvxvxvx", n_idx );
1446
1447 /* Note that we passed through this lambda to detect tail calls later. */
1448 append_retval = mdata_vector_append(
1449 &(exec->lambda_trace), &n_idx, sizeof( size_t ) );
1450 retval = mdata_retval( append_retval );
1451 maug_cleanup_if_not_ok();
1452
1453 /* Grab the current exec index for the child vector for this node. */
1454 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1455 p_lambda_child_idx = mdata_vector_get(
1456 &(exec->per_node_child_idx), n_idx, size_t );
1457 assert( NULL != p_lambda_child_idx );
1458 debug_printf( MLISP_EXEC_TRACE_LVL,
1459 "child idx for lambda AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1460 n_idx, *p_lambda_child_idx );
1461
1462 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1463
1464 /* There needs to be an arg node and an exec node. */
1465 /* TODO: Handle this gracefully. */
1466 assert( 1 < n->ast_idx_children_sz );
1467
1468 if( 0 == *p_lambda_child_idx ) {
1469 /* Parse the args passed to this lambda into the env, temporarily. */
1470
1471 /* Get the current args node child index. */
1472 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1473 p_args_child_idx = mdata_vector_get(
1474 &(exec->per_node_child_idx),
1475 n->ast_idx_children[*p_lambda_child_idx], size_t );
1476 assert( NULL != p_args_child_idx );
1477 debug_printf( MLISP_EXEC_TRACE_LVL,
1478 "child idx for args AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1479 *p_lambda_child_idx, *p_args_child_idx );
1480
1481 if( 0 == *p_args_child_idx ) {
1482 /* Set return call in env before first arg, in *before-arg* delimiter,
1483 * so the args can be stripped off later when we return. */
1484 retval = mlisp_env_set(
1485 parser, exec, "$ARGS_S$", 0, MLISP_TYPE_ARGS_S, &n_idx, NULL, 0 );
1486 maug_cleanup_if_not_ok();
1487 }
1488
1489 /* Pop stack into args in the env. */
1490 retval = _mlisp_step_lambda_args(
1491 parser, n->ast_idx_children[*p_lambda_child_idx], exec );
1492 if( MERROR_OK != retval && MERROR_PREEMPT != retval ) {
1493 /* Something bad happened! */
1494 goto cleanup;
1495 }
1496
1497 if( MERROR_OK == retval ) {
1498 /* Set *after-arg* delimiter in env after last arg. */
1499 retval = mlisp_env_set(
1500 parser, exec, "$ARGS_E$", 0, MLISP_TYPE_ARGS_E, &n_idx, NULL, 0 );
1501 maug_cleanup_if_not_ok();
1502
1503 /* Increment child idx so we call the exec child on next heartbeat. */
1504 (*p_lambda_child_idx)++;
1505 debug_printf( MLISP_EXEC_TRACE_LVL,
1506 "incremented " SIZE_T_FMT " child idx to: " SIZE_T_FMT,
1507 n_idx, *p_lambda_child_idx );
1508 }
1509
1510 /* Set the error to MERROR_PREEMPT so that caller knows this lambda isn't
1511 * finished executing.
1512 */
1513 retval = MERROR_PREEMPT;
1514
1515 } else if( mlisp_ast_has_ready_children( *p_lambda_child_idx, n ) ) {
1516 /* Dive into first lambda child until we no longer can. */
1517
1518 retval = _mlisp_step_iter(
1519 parser, n->ast_idx_children[*p_lambda_child_idx], exec );
1520
1521 if( MERROR_OK == retval ) {
1522 retval = _mlisp_preempt(
1523 "lambda", parser, n_idx, exec, p_lambda_child_idx,
1524 (*p_lambda_child_idx) + 1 );
1525 }
1526
1527 } else {
1528 /* No more children to execute! */
1529 _mlisp_reset_lambda( parser, n_idx, exec );
1530 }
1531
1532 /* TODO: If MERROR_PREEMPT is not returned, remove args_s and args_e? */
1533
1534cleanup:
1535
1536 debug_printf( MLISP_EXEC_TRACE_LVL,
1537 "xvxvxvxvxvxvx END STEP LAMBDA " SIZE_T_FMT " xvxvxvxvxvx", n_idx );
1538
1539 /* Cleanup the passthrough note for this heartbeat. */
1540 mdata_vector_remove_last( &(exec->lambda_trace) );
1541
1542 return retval;
1543}
1544
1545/* === */
1546
1547/* === */
1548
1549static MERROR_RETVAL _mlisp_stack_cleanup(
1550 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1551) {
1552 MERROR_RETVAL retval = MERROR_OK;
1553 ssize_t i = 0;
1554 struct MLISP_STACK_NODE o;
1555
1556 /* Pop elements off the stack until we hit the matching begin frame. */
1557 i = mdata_vector_ct( &(exec->stack) ) - 1;
1558 while( 0 <= i ) {
1559
1560 retval = mlisp_stack_pop( exec, &o );
1561 maug_cleanup_if_not_ok();
1562
1563 if( MLISP_TYPE_BEGIN == o.type && n_idx == o.value.begin ) {
1564 break;
1565 }
1566
1567 i--;
1568 }
1569
1570cleanup:
1571
1572 return retval;
1573}
1574
1575/* === */
1576
1577static MERROR_RETVAL _mlisp_eval_token_strpool(
1578 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
1579 size_t token_idx, size_t token_sz, struct MLISP_ENV_NODE* e_out
1580) {
1581 uint8_t autolock = 0;
1582 char* strpool = NULL;
1583 MERROR_RETVAL retval = MERROR_OK;
1584 struct MLISP_ENV_NODE* p_e = NULL;
1585 struct MDATA_VECTOR* env = NULL;
1586
1587 if(
1588 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
1589 ) {
1590 env = &(parser->env);
1591 } else {
1592 env = &(exec->env);
1593 }
1594
1595 if( !mdata_vector_is_locked( env ) ) {
1596 assert( env->data_h != NULL );
1597 mdata_vector_lock( env );
1598 autolock = 1;
1599 }
1600
1601 mdata_strpool_lock( &(parser->strpool), strpool );
1602
1603 assert( 0 < maug_strlen( &(strpool[token_idx]) ) );
1604
1605 debug_printf( MLISP_EXEC_TRACE_LVL,
1606 "eval token: \"%s\" (maug_strlen: " SIZE_T_FMT ")",
1607 &(strpool[token_idx]), maug_strlen( &(strpool[token_idx]) ) );
1608 if( 0 == strncmp( &(strpool[token_idx]), "begin", token_sz ) ) {
1609 /* Fake env node e to signal step_iter() to place/cleanup stack frame. */
1610 e_out->type = MLISP_TYPE_BEGIN;
1611
1612 } else if( NULL != (p_e = mlisp_env_get_strpool(
1613 parser, exec, strpool, token_idx, token_sz
1614 ) ) ) {
1615 /* A literal found in the environment. */
1616 debug_printf( MLISP_EXEC_TRACE_LVL, "found %s in env!",
1617 &(strpool[p_e->name_strpool_idx]) );
1618
1619 /* Copy onto native stack so we can unlock env in case this is a
1620 * callback that needs to execute. */
1621 memcpy( e_out, p_e, sizeof( struct MLISP_ENV_NODE ) );
1622 p_e = NULL;
1623
1624 } else if( maug_is_num( &(strpool[token_idx]), token_sz, 10, 1 ) ) {
1625 /* Fake env node e from a numeric literal. */
1626 e_out->value.integer =
1627 maug_atos32( &(strpool[token_idx]), token_sz );
1628 e_out->type = MLISP_TYPE_INT;
1629
1630 } else if( maug_is_float( &(strpool[token_idx]), token_sz ) ) {
1631 /* Fake env node e from a floating point numeric literal. */
1632 e_out->value.floating = maug_atof( &(strpool[token_idx]), token_sz );
1633 e_out->type = MLISP_TYPE_FLOAT;
1634
1635 }
1636
1637cleanup:
1638
1639 if( autolock ) {
1640 mdata_vector_unlock( env );
1641 }
1642
1643 mdata_strpool_unlock( &(parser->strpool), strpool );
1644
1645 return retval;
1646}
1647
1648static MERROR_RETVAL _mlisp_step_iter(
1649 struct MLISP_PARSER* parser,
1650 size_t n_idx, struct MLISP_EXEC_STATE* exec
1651) {
1652 MERROR_RETVAL retval = MERROR_OK;
1653 struct MLISP_ENV_NODE e;
1654 struct MLISP_AST_NODE* n = NULL;
1655 size_t* p_visit_ct = NULL;
1656
1657#ifdef MLISP_DEBUG_TRACE
1658 exec->trace[exec->trace_depth++] = n_idx;
1659 assert( exec->trace_depth <= MLISP_DEBUG_TRACE );
1660#endif /* MLISP_DEBUG_TRACE */
1661
1662 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1663
1664 assert( mdata_vector_is_locked( &(exec->per_node_visit_ct) ) );
1665 p_visit_ct = mdata_vector_get(
1666 &(exec->per_node_visit_ct), n_idx, size_t );
1667 assert( NULL != p_visit_ct );
1668 (*p_visit_ct)++;
1669 debug_printf( MLISP_EXEC_TRACE_LVL,
1670 "visit count for AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1671 n_idx, *p_visit_ct );
1672
1673 /* Push a stack frame marker on the first visit to a BEGIN node. */
1674 if(
1675 MLISP_AST_FLAG_BEGIN == (MLISP_AST_FLAG_BEGIN & n->flags) &&
1676 1 == *p_visit_ct
1677 ) {
1678 /* Push a stack frame on first visit. */
1679 retval = _mlisp_stack_push_mlisp_begin_t( exec, n_idx );
1680 maug_cleanup_if_not_ok();
1681 }
1682
1683 if(
1684 MERROR_OK !=
1685 (retval = _mlisp_step_iter_children( parser, n_idx, exec ))
1686 ) {
1687 goto cleanup;
1688 }
1689
1690 /* Check for special types like lambda, that are lazily evaluated. */
1691 if( MLISP_AST_FLAG_LAMBDA == (MLISP_AST_FLAG_LAMBDA & n->flags) ) {
1692 /* Push the lambda to the stack so that the "define" above it can
1693 * grab it and associate it with the env.
1694 */
1695 /* TODO: Assert node above it is a define! */
1696 mlisp_stack_push( exec, n_idx, mlisp_lambda_t );
1697 goto cleanup;
1698 }
1699
1700 /* Now that the children have been evaluated above, evaluate this node.
1701 * Assume all the previously called children are now on the stack.
1702 */
1703
1704 /* Grab the token for this node and figure out what it is. */
1705
1706 retval = _mlisp_eval_token_strpool(
1707 parser, exec, n->token_idx, n->token_sz, &e );
1708 maug_cleanup_if_not_ok();
1709
1710 /* Prepare to step. */
1711
1712 /* Put the token or its result (if callable) on the stack. */
1713# define _MLISP_TYPE_TABLE_ENVE( idx, ctype, name, const_name, fmt ) \
1714 } else if( MLISP_TYPE_ ## const_name == e.type ) { \
1715 retval = _mlisp_stack_push_ ## ctype( exec, e.value.name ); \
1716 maug_cleanup_if_not_ok();
1717
1718 if( MLISP_EXEC_FLAG_DEF_TERM == (MLISP_EXEC_FLAG_DEF_TERM & exec->flags) ) {
1719 /* Avoid a deadlock when *re*-assigning terms caused by term being
1720 * evaluated before it is defined.
1721 */
1722 debug_printf( MLISP_EXEC_TRACE_LVL,
1723 "special case! pushing literal to define: " SSIZE_T_FMT,
1724 n->token_idx );
1725 retval = _mlisp_stack_push_mdata_strpool_idx_t( exec, n->token_idx );
1726 maug_cleanup_if_not_ok();
1727 } else if( MLISP_TYPE_BEGIN == e.type ) {
1728 /* Cleanup the stack that's been pushed by children since this BEGIN's
1729 * initial visit.
1730 */
1731 retval = _mlisp_stack_cleanup( parser, n_idx, exec );
1732
1733 } else if( MLISP_TYPE_CB == e.type ) {
1734 /* This is a special case... rather than pushing the callback, *execute*
1735 * it and let it push its result to the stack. This will create a
1736 * redundant case below, but that can't be helped...
1737 */
1738 retval = e.value.cb(
1739 parser, exec, n_idx, n->ast_idx_children_sz, e.cb_data, e.flags );
1740
1741 } else if( MLISP_TYPE_LAMBDA == e.type ) {
1742 /* Create a "portal" into the lambda. The execution chain stays pointing
1743 * to this lambda-call node, but _mlisp_step_lambda() returns
1744 * MERROR_PREEMPT up the chain for subsequent heartbeats, until lambda is
1745 * done.
1746 */
1747 retval = _mlisp_step_lambda( parser, e.value.lambda, exec );
1748
1749 MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_ENVE )
1750 } else {
1751 retval = _mlisp_stack_push_mdata_strpool_idx_t( exec, n->token_idx );
1752 maug_cleanup_if_not_ok();
1753 }
1754
1755cleanup:
1756
1757 return retval;
1758}
1759
1760/* === */
1761
1762MERROR_RETVAL mlisp_check_state(
1763 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
1764) {
1765 /* struct MDATA_VECTOR* env = NULL; */
1766 MERROR_RETVAL retval = MERROR_OK;
1767
1768 /* Prepare env for mlisp_env_get() below. */
1769 /*
1770 if(
1771 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
1772 ) {
1773 env = &(parser->env);
1774 } else {
1775 env = &(exec->env);
1776 }
1777 */
1778
1779 if( !mlisp_check_ast( parser ) ) {
1780 error_printf( "no valid AST present; could not exec!" );
1781 retval = MERROR_EXEC;
1782 goto cleanup;
1783 }
1784
1785 /*
1786 if( NULL == env->data_bytes ) {
1787 error_printf( "no valid env present; could not exec!" );
1788 retval = MERROR_EXEC;
1789 goto cleanup;
1790 }
1791 */
1792
1793 if(
1794 MLISP_EXEC_FLAG_INITIALIZED != (exec->flags & MLISP_EXEC_FLAG_INITIALIZED)
1795 ) {
1796 retval = MERROR_EXEC;
1797 goto cleanup;
1798 }
1799
1800cleanup:
1801
1802 return retval;
1803}
1804
1805/* === */
1806
1808 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
1809) {
1810 MERROR_RETVAL retval = MERROR_OK;
1811#ifdef MLISP_DEBUG_TRACE
1812 size_t i = 0;
1813 char trace_str[MLISP_DEBUG_TRACE * 5];
1814 maug_ms_t ms_start = 0;
1815 maug_ms_t ms_end = 0;
1816
1817 ms_start = retroflat_get_ms();
1818#endif /* MLISP_DEBUG_TRACE */
1819
1820 debug_printf( MLISP_EXEC_TRACE_LVL, "heartbeat start" );
1821
1822 /* These can remain locked for the whole step, as they're never added or
1823 * removed.
1824 */
1825 assert( !mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1826 assert( !mdata_vector_is_locked( &(exec->per_node_visit_ct) ) );
1827 assert( !mdata_vector_is_locked( &(parser->ast) ) );
1828 mdata_vector_lock( &(exec->per_node_child_idx) );
1829 mdata_vector_lock( &(exec->per_node_visit_ct) );
1830 mdata_vector_lock( &(parser->ast) );
1831
1832 /* Disable transient flags. */
1833 exec->flags &= MLISP_EXEC_FLAG_TRANSIENT_MASK;
1834 assert( 0 == mdata_vector_ct( &(exec->lambda_trace) ) );
1835
1836#ifdef MLISP_DEBUG_TRACE
1837 exec->trace_depth = 0;
1838#endif /* MLISP_DEBUG_TRACE */
1839
1840 /* Find next unevaluated symbol. */
1841 retval = _mlisp_step_iter( parser, 0, exec );
1842 if( MERROR_PREEMPT == retval ) {
1843 /* There's still more to execute. */
1844 retval = MERROR_OK;
1845 } else if( MERROR_OK == retval ) {
1846 /* The last node executed completely. */
1847 debug_printf( MLISP_EXEC_TRACE_LVL, "execution terminated successfully" );
1848 retval = MERROR_EXEC; /* Signal the caller: we're out of instructions! */
1849 } else {
1850 debug_printf( MLISP_EXEC_TRACE_LVL,
1851 "execution terminated with retval: %d", retval );
1852 }
1853
1854#ifdef MLISP_DEBUG_TRACE
1855 ms_end = retroflat_get_ms();
1856
1857 maug_mzero( trace_str, MLISP_DEBUG_TRACE * 5 );
1858 for( i = 0 ; exec->trace_depth > i ; i++ ) {
1859 maug_snprintf(
1860 &(trace_str[maug_strlen( trace_str )]),
1861 (MLISP_DEBUG_TRACE * 5) - maug_strlen( trace_str ),
1862 SIZE_T_FMT ", ", exec->trace[i] );
1863 }
1864 debug_printf( MLISP_EXEC_TRACE_LVL,
1865 MLISP_TRACE_SIGIL " HBEXEC (%u): %s",
1866 ms_end - ms_start, trace_str );
1867#endif /* MLISP_DEBUG_TRACE */
1868
1869cleanup:
1870
1871 debug_printf( MLISP_EXEC_TRACE_LVL, "heartbeat end: %x", retval );
1872
1873 assert( mdata_vector_is_locked( &(parser->ast) ) );
1874 mdata_vector_unlock( &(parser->ast) );
1875 mdata_vector_unlock( &(exec->per_node_visit_ct) );
1876 mdata_vector_unlock( &(exec->per_node_child_idx) );
1877
1878 return retval;
1879}
1880
1881/* === */
1882
1884 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
1885 const char* lambda
1886) {
1887 struct MLISP_ENV_NODE* e = NULL;
1888 MERROR_RETVAL retval = MERROR_OK;
1889 uint8_t autolock = 0;
1890 struct MDATA_VECTOR* env = NULL;
1891 mlisp_lambda_t lambda_idx = 0;
1892
1893 /* Prepare env for mlisp_env_get() below. */
1894 if(
1895 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
1896 ) {
1897 env = &(parser->env);
1898 } else {
1899 env = &(exec->env);
1900 }
1901
1902 if( MERROR_OK != mlisp_check_state( parser, exec ) ) {
1903 error_printf( "mlisp not ready!" );
1904 retval = MERROR_EXEC;
1905 goto cleanup;
1906 }
1907
1908 /* Autolock vectors used below. */
1909 /* TODO: Should this be a reusable macro? */
1910 if( !mdata_vector_is_locked( env ) ) {
1911 assert( env->data_h != NULL );
1912 mdata_vector_lock( env );
1913 autolock |= 0x01;
1914 }
1915 if( !mdata_vector_is_locked( &(exec->per_node_child_idx) ) ) {
1917 autolock |= 0x02;
1918 }
1919 if( !mdata_vector_is_locked( &(exec->per_node_visit_ct) ) ) {
1921 autolock |= 0x04;
1922 }
1923 if( !mdata_vector_is_locked( &(parser->ast) ) ) {
1924 mdata_vector_lock( &(parser->ast) );
1925 autolock |= 0x08;
1926 }
1927
1928 /* Find the AST node for the lambda. */
1929 e = mlisp_env_get( parser, exec, lambda );
1930 if( NULL == e ) {
1931 error_printf( "lambda \"%s\" not found!", lambda );
1932 retval = MERROR_OVERFLOW;
1933 goto cleanup;
1934 }
1935 lambda_idx = e->value.lambda;
1936
1937 /* Autounlock just env so _mlisp_step_lambda() works. */
1938 /* TODO: We shouldn't need to do this if we reuse the multiple autolock...
1939 */
1940 if( 0x01 == (0x01 & autolock) ) {
1941 mdata_vector_unlock( env );
1942 autolock &= ~0x01;
1943 }
1944
1945 debug_printf( MLISP_EXEC_TRACE_LVL, "lambda \"%s\" is AST node idx %ld",
1946 lambda, lambda_idx );
1947
1948 /* Jump execution to the lambda on next iter. */
1949 retval = _mlisp_step_lambda( parser, lambda_idx, exec );
1950
1951cleanup:
1952
1953 /* See autolocks above. */
1954 if( 0x01 == (0x01 & autolock) ) {
1955 mdata_vector_unlock( env );
1956 }
1957 if( 0x02 == (0x02 & autolock) ) {
1959 }
1960 if( 0x04 == (0x04 & autolock) ) {
1962 }
1963 if( 0x08 == (0x08 & autolock) ) {
1964 mdata_vector_unlock( &(parser->ast) );
1965 }
1966
1967 return retval;
1968}
1969
1970/* === */
1971
1972MERROR_RETVAL mlisp_exec_init(
1973 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t flags
1974) {
1975 MERROR_RETVAL retval = MERROR_OK;
1976 ssize_t append_retval = 0;
1977 size_t zero = 0;
1978
1979 assert( 0 == exec->flags );
1980
1981 maug_mzero( exec, sizeof( struct MLISP_EXEC_STATE ) );
1982
1983 exec->flags = flags;
1984
1985 /* Setup lambda visit stack so it can be locked on first step. */
1986 append_retval = mdata_vector_append(
1987 &(exec->lambda_trace), &zero, sizeof( size_t ) );
1988 if( 0 > append_retval ) {
1989 retval = mdata_retval( append_retval );
1990 }
1991 maug_cleanup_if_not_ok();
1992 mdata_vector_remove_last( &(exec->lambda_trace) );
1993
1994 /* TODO: MLISP_EXEC_FLAG_SHARED_ENV might not be such a great idea, since
1995 * it could let lambdas clobber each others stack frames on env.
1996 * Maybe make sure those stack frames are obeyed by lambda caller idx?
1997 */
1998 if(
1999 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & exec->flags)
2000 ) {
2001 append_retval = mdata_vector_alloc(
2002 &(parser->env), sizeof( struct MLISP_ENV_NODE ),
2004 } else {
2005 append_retval = mdata_vector_alloc(
2006 &(exec->env), sizeof( struct MLISP_ENV_NODE ), MDATA_VECTOR_INIT_SZ );
2007 }
2008 /* TODO: Cleanup partially allocated object. */
2009 if( 0 > append_retval ) {
2010 retval = mdata_retval( append_retval );
2011 }
2012 maug_cleanup_if_not_ok();
2013
2014 /* Create the node PCs. */
2015 append_retval = mdata_vector_append(
2016 &(exec->per_node_child_idx), &zero, sizeof( size_t ) );
2017 if( 0 > append_retval ) {
2018 retval = mdata_retval( append_retval );
2019 }
2020 maug_cleanup_if_not_ok();
2021
2022 /* Make sure there's an exec child node for every AST node. */
2023 while(
2024 mdata_vector_ct( &(exec->per_node_child_idx) ) <=
2025 mdata_vector_ct( &(parser->ast) )
2026 ) {
2027 append_retval = mdata_vector_append( &(exec->per_node_child_idx), &zero,
2028 sizeof( size_t ) );
2029 if( 0 > append_retval ) {
2030 retval = mdata_retval( append_retval );
2031 }
2032 maug_cleanup_if_not_ok();
2033 }
2034
2035 /* Create the node visit counters. */
2036 append_retval = mdata_vector_append(
2037 &(exec->per_node_visit_ct), &zero, sizeof( size_t ) );
2038 if( 0 > append_retval ) {
2039 retval = mdata_retval( append_retval );
2040 }
2041 maug_cleanup_if_not_ok();
2042
2043 /* Make sure there's an exec visit count for every AST node. */
2044 while(
2045 mdata_vector_ct( &(exec->per_node_visit_ct) ) <=
2046 mdata_vector_ct( &(parser->ast) )
2047 ) {
2048 append_retval = mdata_vector_append( &(exec->per_node_visit_ct), &zero,
2049 sizeof( size_t ) );
2050 if( 0 > append_retval ) {
2051 retval = mdata_retval( append_retval );
2052 }
2053 maug_cleanup_if_not_ok();
2054 }
2055
2056 exec->flags |= MLISP_EXEC_FLAG_INITIALIZED;
2057
2058 /* Setup initial env. */
2059
2060 if(
2061 MLISP_EXEC_FLAG_SHARED_ENV == (MLISP_EXEC_FLAG_SHARED_ENV & flags) &&
2062 0 < mdata_vector_ct( &(parser->env) )
2063 ) {
2064 debug_printf( MLISP_EXEC_TRACE_LVL, "skipping initialized environment!" );
2065 goto cleanup;
2066 }
2067
2068 retval = mlisp_env_set(
2069 parser, exec, "and", 2, MLISP_TYPE_CB, _mlisp_env_cb_ano,
2070 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ANO_AND );
2071 maug_cleanup_if_not_ok();
2072 retval = mlisp_env_set(
2073 parser, exec, "or", 2, MLISP_TYPE_CB, _mlisp_env_cb_ano,
2074 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ANO_OR );
2075 maug_cleanup_if_not_ok();
2076 retval = mlisp_env_set(
2077 parser, exec, "random", 6, MLISP_TYPE_CB, _mlisp_env_cb_random,
2078 NULL, MLISP_ENV_FLAG_BUILTIN );
2079 maug_cleanup_if_not_ok();
2080 retval = mlisp_env_set(
2081 parser, exec, "if", 2, MLISP_TYPE_CB, _mlisp_env_cb_if,
2082 NULL, MLISP_ENV_FLAG_BUILTIN );
2083 maug_cleanup_if_not_ok();
2084 retval = mlisp_env_set(
2085 parser, exec, "define", 6, MLISP_TYPE_CB, _mlisp_env_cb_define,
2086 NULL, MLISP_ENV_FLAG_BUILTIN );
2087 maug_cleanup_if_not_ok();
2088 retval = mlisp_env_set(
2089 parser, exec, "*", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2090 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_MUL );
2091 maug_cleanup_if_not_ok();
2092 retval = mlisp_env_set(
2093 parser, exec, "+", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2094 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_ADD );
2095 maug_cleanup_if_not_ok();
2096 retval = mlisp_env_set(
2097 parser, exec, "/", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2098 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_DIV );
2099 maug_cleanup_if_not_ok();
2100 retval = mlisp_env_set(
2101 parser, exec, "%", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2102 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_MOD );
2103 maug_cleanup_if_not_ok();
2104 retval = mlisp_env_set(
2105 parser, exec, "<", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2106 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_LT );
2107 maug_cleanup_if_not_ok();
2108 retval = mlisp_env_set(
2109 parser, exec, ">", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2110 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_GT );
2111 maug_cleanup_if_not_ok();
2112 retval = mlisp_env_set(
2113 parser, exec, "=", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2114 NULL, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_EQ );
2115 maug_cleanup_if_not_ok();
2116
2117cleanup:
2118
2119 if( MERROR_OK != retval ) {
2120 error_printf( "mlisp exec initialization failed: %d", retval );
2121 }
2122
2123 return retval;
2124}
2125
2126/* === */
2127
2128void mlisp_exec_free( struct MLISP_EXEC_STATE* exec ) {
2129 debug_printf( MLISP_EXEC_TRACE_LVL,
2130 "destroying exec (stack: " SIZE_T_FMT ", env: " SIZE_T_FMT ")...",
2131 mdata_vector_ct( &(exec->stack) ), mdata_vector_ct( &(exec->env) ) );
2132 mdata_vector_free( &(exec->per_node_child_idx) );
2133 mdata_vector_free( &(exec->per_node_visit_ct) );
2134 mdata_vector_free( &(exec->stack) );
2135 mdata_vector_free( &(exec->env) );
2136 mdata_vector_free( &(exec->lambda_trace) );
2137 exec->flags = 0;
2138 debug_printf( MLISP_EXEC_TRACE_LVL, "exec destroyed!" );
2139}
2140
2141#else
2142
2143# define MLISP_PSTATE_TABLE_CONST( name, idx ) \
2144 extern MAUG_CONST uint8_t SEG_MCONST name;
2145
2146MLISP_PARSER_PSTATE_TABLE( MLISP_PSTATE_TABLE_CONST )
2147
2148#ifdef MPARSER_TRACE_NAMES
2149extern MAUG_CONST char* SEG_MCONST gc_mlisp_pstate_names[];
2150#endif /* MPARSER_TRACE_NAMES */
2151
2152#endif /* MLISPE_C */
2153
2154#endif /* !MLISPE_H */
2155
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define maug_mzero(ptr, sz)
Zero the block of memory pointed to by ptr.
Definition mmem.h:62
int32_t maug_atos32(const char *buffer, size_t buffer_sz)
#define mdata_vector_lock(v)
Lock the vector. This should be done when items from the vector are actively being referenced,...
Definition mdata.h:241
#define mdata_vector_unlock(v)
Unlock the vector so items may be added and removed.
Definition mdata.h:261
#define MDATA_VECTOR_INIT_SZ
Default initial value for MDATA_VECTOR::ct_max.
Definition mdata.h:37
ssize_t mdata_vector_append(struct MDATA_VECTOR *v, const void *item, size_t item_sz)
Append an item to the specified vector.
MERROR_RETVAL mdata_vector_remove(struct MDATA_VECTOR *v, size_t idx)
Remove item at the given index, shifting subsequent items up by 1.
#define mdata_vector_ct(v)
Number of items of MDATA_VECTOR::item_sz bytes actively stored in this vector.
Definition mdata.h:295
MERROR_RETVAL mdata_vector_alloc(struct MDATA_VECTOR *v, size_t item_sz, size_t item_ct_init)
#define mlisp_check_ast(parser)
Macro to check if a parser contains a valid AST ready to be executed.
Definition mlispp.h:77
MERROR_RETVAL mlisp_stack_pop(struct MLISP_EXEC_STATE *exec, struct MLISP_STACK_NODE *o)
Pop a value off of (removing from) MLISP_EXEC_STATE::stack and copy it to a provided output.
#define mlisp_stack_push(exec, i, ctype)
Push a value onto MLISP_EXEC_STATE::stack.
Definition mlispe.h:61
#define MLISP_TYPE_TABLE(f)
Table of other types.
Definition mlisps.h:80
#define MLISP_NUM_TYPE_TABLE(f)
Table of numeric types.
Definition mlisps.h:70
#define MLISP_ENV_FLAG_CMP_GT
Flag for _mlisp_env_cb_cmp() specifying TRUE if A > B.
Definition mlispe.h:26
#define MLISP_ENV_FLAG_ARI_MUL
Flag for _mlisp_env_cb_arithmetic() specifying to multiply A * B.
Definition mlispe.h:38
MERROR_RETVAL mlisp_step(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec)
Iterate the current exec_state() starting from the next MLISP_AST_NODE to be executed according to th...
#define MLISP_ENV_FLAG_ARI_ADD
Flag for _mlisp_env_cb_arithmetic() specifying to add A + B.
Definition mlispe.h:35
#define MLISP_ENV_FLAG_CMP_EQ
Flag for _mlisp_env_cb_cmp() specifying TRUE if A == B.
Definition mlispe.h:32
MERROR_RETVAL mlisp_step_lambda(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec, const char *lambda)
Iterate the current exec_state() starting from the lambda named.
#define MLISP_ENV_FLAG_CMP_LT
Flag for _mlisp_env_cb_cmp() specifying TRUE if A < B.
Definition mlispe.h:29
struct MLISP_ENV_NODE * mlisp_env_get_strpool(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec, const char *strpool, size_t token_strpool_idx, size_t token_strpool_sz)
Get a node from the environment denoted by a string in the strpool.
MLISP Interpreter/Parser Structs.
A vector of uniformly-sized objects, stored contiguously.
Definition mdata.h:89
MAUG_MHANDLE data_h
Handle for allocated items (unlocked).
Definition mdata.h:92
Definition mlisps.h:118
size_t ast_idx_children_sz
Number of children in MLISP_AST_NODE::ast_idx_children.
Definition mlisps.h:126
Definition mlisps.h:105
Current execution state to associate with a MLISP_PARSER.
Definition mlisps.h:136
struct MDATA_VECTOR lambda_trace
Path through any lambdas the execution has entered during this heartbeat cycle. Used to detect tail c...
Definition mlisps.h:161
struct MDATA_VECTOR per_node_child_idx
The hild index that will be visited on next visit of each node.
Definition mlisps.h:147
struct MDATA_VECTOR per_node_visit_ct
The number of times each node has been visited ever.
Definition mlisps.h:140
struct MDATA_VECTOR stack
A stack of data values resulting from evaluating statements.
Definition mlisps.h:149
uint8_t flags
Flags which dictate the behavior of this object.
Definition mlisps.h:138
struct MDATA_VECTOR env
Environment in which statements are defined if ::MLISP_.
Definition mlisps.h:156
Definition mlisps.h:169
struct MDATA_VECTOR env
Definitions to use if ::MLISP_EXEC_FLAG_DEF_TERM is defined on the accompanying MLISP_EXEC_STATE::fla...
Definition mlisps.h:177
Definition mlisps.h:113