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
13
42
43#ifndef MLISP_TOKEN_SZ_MAX
44# define MLISP_TOKEN_SZ_MAX 4096
45#endif /* !MLISP_TOKEN_SZ_MAX */
46
47#ifndef MLISP_EXEC_TRACE_LVL
48# define MLISP_EXEC_TRACE_LVL 0
49#endif /* !MLISP_EXEC_TRACE_LVL */
50
51#ifndef MLISP_STACK_TRACE_LVL
52# define MLISP_STACK_TRACE_LVL 0
53#endif /* !MLISP_STACK_TRACE_LVL */
54
55#define MLISP_ENV_FLAG_BUILTIN 0x02
56
58#define MLISP_ENV_FLAG_CMP_GT 0x10
59
61#define MLISP_ENV_FLAG_CMP_LT 0x20
62
64#define MLISP_ENV_FLAG_CMP_EQ 0x40
65
67#define MLISP_ENV_FLAG_ARI_ADD 0x10
68
70#define MLISP_ENV_FLAG_ARI_MUL 0x20
71
72#define MLISP_ENV_FLAG_ARI_DIV 0x40
73
74#define MLISP_ENV_FLAG_ARI_MOD 0x80
75
76#define MLISP_ENV_FLAG_ANO_OR 0x10
77
78#define MLISP_ENV_FLAG_ANO_AND 0x20
79
81#define MLISP_ENV_FLAG_DEFINE_GLOBAL 0x10
82
83#define MLISP_AUTOLOCK_EXEC_ENV 0x01
84
85#define MLISP_AUTOLOCK_CHILD_IDX 0x02
86
87#define MLISP_AUTOLOCK_VISIT_CT 0x04
88
89#define MLISP_AUTOLOCK_PARSER_AST 0x08
90
91#define MLISP_AUTOLOCK_GLOBAL_ENV 0x10
92
97
102#define MLISP_STACK_FLAG_PEEK 0x01
103
107#define mlisp_stack_pop( exec, o ) mlisp_stack_pop_ex( exec, o, 0 )
108
117#define mlisp_stack_push( exec, i, ctype ) \
118 (_mlisp_stack_push_ ## ctype( exec, (ctype)i ))
119
120#if defined( MLISP_DUMP_ENABLED ) || defined( DOCUMENTATION )
121
128 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
129
130#endif /* MLISP_DUMP_ENABLED || DOCUMENTATION */
131
139 struct MLISP_EXEC_STATE* exec, struct MLISP_STACK_NODE* o, uint8_t flags );
140 /* mlisp_stack */
142
143#if defined( MLISP_DUMP_ENABLED ) || defined( DOCUMENTATION )
144
151 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t global );
152
153#endif /* MLISP_DUMP_ENABLED || DOCUMENTATION */
154
155struct MLISP_ENV_NODE* mlisp_env_get(
156 struct MLISP_EXEC_STATE* exec, const char* key );
157
158MERROR_RETVAL mlisp_env_unset(
159 struct MLISP_EXEC_STATE* exec, const char* token, size_t token_sz,
160 uint8_t global );
161
162MERROR_RETVAL mlisp_env_set(
163 struct MLISP_EXEC_STATE* exec,
164 const char* token, size_t token_sz, uint8_t env_type, const void* data,
165 uint8_t global, uint8_t flags );
166
167ssize_t mlisp_count_builtins( struct MLISP_EXEC_STATE* exec );
168
169MERROR_RETVAL mlisp_check_state(
170 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
171
178 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
179
192 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
193 const char* lambda );
194
195MERROR_RETVAL mlisp_exec_add_env_builtins(
196 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec );
197
198MERROR_RETVAL mlisp_exec_init(
199 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t flags );
200
206 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
207 struct MDATA_TABLE* global_env );
208
209void mlisp_exec_free( struct MLISP_EXEC_STATE* exec );
210
211MERROR_RETVAL mlisp_deserialize_prepare_EXEC_STATE(
212 struct MLISP_EXEC_STATE* exec, size_t i );
213
214#define _MLISP_TYPE_TABLE_PUSH_PROTO( idx, ctype, name, const_name, fmt ) \
215 MERROR_RETVAL _mlisp_stack_push_ ## ctype( \
216 struct MLISP_EXEC_STATE* exec, ctype i );
217
218MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_PUSH_PROTO )
219
220 /* mlisp */
221
222#define mlisp_ast_has_ready_children( exec_child_idx, n ) \
223 ((exec_child_idx) < (n)->ast_idx_children_sz)
224
225#ifdef MLISPE_C
226
227uint16_t g_mlispe_last_uid = 0;
228
239static MERROR_RETVAL _mlisp_preempt(
240 MERROR_RETVAL retval, const char* caller, struct MLISP_PARSER* parser,
241 size_t n_idx, struct MLISP_EXEC_STATE* exec, size_t new_idx );
242
243static MERROR_RETVAL _mlisp_step_iter(
244 struct MLISP_PARSER* parser,
245 size_t n_idx, struct MLISP_EXEC_STATE* exec );
246
247static MERROR_RETVAL _mlisp_reset_child_pcs(
248 const struct MLISP_PARSER* parser,
249 size_t n_idx, struct MLISP_EXEC_STATE* exec );
250
251static MERROR_RETVAL _mlisp_autolock(
252 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
253 uint8_t mask, uint8_t autolock[MLISP_EXEC_ENV_FRAME_CT_MAX]
254) {
255 MERROR_RETVAL retval = MERROR_OK;
256 int8_t env_iter = 0;
257
258 maug_mzero( autolock, MLISP_EXEC_ENV_FRAME_CT_MAX );
259
260 /* Autolock vectors used below. */
261 if( MLISP_AUTOLOCK_EXEC_ENV == (MLISP_AUTOLOCK_EXEC_ENV & mask) ) {
262 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
263 if( !mdata_table_is_locked( &(exec->env[env_iter]) ) ) {
264#if MLISP_EXEC_TRACE_LVL > 0
265 debug_printf( MLISP_EXEC_TRACE_LVL,
266 "%u: engaging autolock for exec env frame %d...",
267 exec->uid, env_iter );
268#endif /* MLISP_EXEC_TRACE_LVL */
269 mdata_table_lock( &(exec->env[env_iter]) );
270 autolock[env_iter] |= MLISP_AUTOLOCK_EXEC_ENV;
271 }
272 }
273 }
274 if(
275 MLISP_AUTOLOCK_CHILD_IDX == (MLISP_AUTOLOCK_CHILD_IDX & mask) &&
276 !mdata_vector_is_locked( &(exec->per_node_child_idx) )
277 ) {
278#if MLISP_EXEC_TRACE_LVL > 0
279 debug_printf( MLISP_EXEC_TRACE_LVL,
280 "%u: engaging autolock for exec per-node child index...", exec->uid );
281#endif /* MLISP_EXEC_TRACE_LVL */
282 mdata_vector_lock( &(exec->per_node_child_idx) );
283 autolock[0] |= MLISP_AUTOLOCK_CHILD_IDX;
284 }
285 if(
286 MLISP_AUTOLOCK_VISIT_CT == (MLISP_AUTOLOCK_VISIT_CT & mask) &&
287 !mdata_vector_is_locked( &(exec->per_node_visit_ct) )
288 ) {
289#if MLISP_EXEC_TRACE_LVL > 0
290 debug_printf( MLISP_EXEC_TRACE_LVL,
291 "%u: engaging autolock for per-node visit count...", exec->uid );
292#endif /* MLISP_EXEC_TRACE_LVL */
293 mdata_vector_lock( &(exec->per_node_visit_ct) );
294 autolock[0] |= MLISP_AUTOLOCK_VISIT_CT;
295 }
296 if(
297 MLISP_AUTOLOCK_PARSER_AST == (MLISP_AUTOLOCK_PARSER_AST & mask) &&
298 !mdata_vector_is_locked( &(parser->ast) )
299 ) {
300#if MLISP_EXEC_TRACE_LVL > 0
301 debug_printf( MLISP_EXEC_TRACE_LVL,
302 "%u: engaging autolock for parser AST...", exec->uid );
303#endif /* MLISP_EXEC_TRACE_LVL */
304 mdata_vector_lock( &(parser->ast) );
305 autolock[0] |= MLISP_AUTOLOCK_PARSER_AST;
306 }
307 if(
308 MLISP_AUTOLOCK_GLOBAL_ENV == (MLISP_AUTOLOCK_GLOBAL_ENV & mask) &&
309 NULL != exec->global_env &&
310 0 < mdata_table_ct( exec->global_env ) &&
311 !mdata_table_is_locked( exec->global_env )
312 ) {
313#if MLISP_EXEC_TRACE_LVL > 0
314 debug_printf( MLISP_EXEC_TRACE_LVL,
315 "%u: engaging autolock for global env...", exec->uid );
316#endif /* MLISP_EXEC_TRACE_LVL */
317 mdata_table_lock( exec->global_env );
318 autolock[0] |= MLISP_AUTOLOCK_GLOBAL_ENV;
319 }
320
321cleanup:
322 return retval;
323}
324
325/* === */
326
327static void _mlisp_autounlock(
328 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
329 uint8_t autolock[MLISP_EXEC_ENV_FRAME_CT_MAX]
330) {
331 int8_t env_iter = 0;
332 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
333 if(
334 MLISP_AUTOLOCK_EXEC_ENV ==
335 (MLISP_AUTOLOCK_EXEC_ENV & autolock[env_iter])
336 ) {
337 mdata_table_unlock( &(exec->env[env_iter]) );
338 }
339 }
340 if( MLISP_AUTOLOCK_CHILD_IDX == (MLISP_AUTOLOCK_CHILD_IDX & autolock[0]) ) {
341 mdata_vector_unlock( &(exec->per_node_child_idx) );
342 }
343 if( MLISP_AUTOLOCK_VISIT_CT == (MLISP_AUTOLOCK_VISIT_CT & autolock[0]) ) {
344 mdata_vector_unlock( &(exec->per_node_visit_ct) );
345 }
346 if(
347 MLISP_AUTOLOCK_PARSER_AST == (MLISP_AUTOLOCK_PARSER_AST & autolock[0])
348 ) {
349 mdata_vector_unlock( &(parser->ast) );
350 }
351 if(
352 MLISP_AUTOLOCK_GLOBAL_ENV == (MLISP_AUTOLOCK_GLOBAL_ENV & autolock[0])
353 ) {
354 mdata_table_unlock( exec->global_env );
355 }
356}
357
358/* === */
359
360/* Stack Functions */
361
362/* === */
363
364#ifdef MLISP_DUMP_ENABLED
365
367 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
368) {
369 MERROR_RETVAL retval = MERROR_OK;
370 size_t i = 0;
371 struct MLISP_STACK_NODE* n_stack = NULL;
372
373# define _MLISP_TYPE_TABLE_DUMPS( idx, ctype, name, const_name, fmt ) \
374 } else if( MLISP_TYPE_ ## const_name == n_stack->type ) { \
375 debug_printf( MLISP_STACK_TRACE_LVL, \
376 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (" #const_name "): " fmt, \
377 exec->uid, i, n_stack->value.name );
378
379 mdata_vector_lock( &(exec->stack) );
380 mdata_strpool_lock( &(parser->strpool) ); \
381 while( i < mdata_vector_ct( &(exec->stack) ) ) {
382 n_stack = mdata_vector_get( &(exec->stack), i, struct MLISP_STACK_NODE );
383
384 /* Handle special exceptions. */
385 if( MLISP_TYPE_STR == n_stack->type ) {
386 debug_printf( 1,
387 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (STR): %s",
388 exec->uid, i, mdata_strpool_get(
389 &(parser->strpool), n_stack->value.strpool_idx ) );
390
391 } else if( MLISP_TYPE_CB == n_stack->type ) {
392 debug_printf( 1,
393 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (CB): %p",
394 exec->uid, i, n_stack->value.cb );
395
396 } else if( MLISP_TYPE_LAMBDA == n_stack->type ) {
397 debug_printf( 1,
398 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (LAMBDA): "
399 SIZE_T_FMT,
400 exec->uid, i, n_stack->value.lambda );
401
402 /*
403 } else if( MLISP_TYPE_ARGS_S == n_stack->type ) {
404 debug_printf( 1,
405 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (ARGS_S): "
406 SIZE_T_FMT,
407 exec->uid, i, n_stack->value.args_start );
408
409 } else if( MLISP_TYPE_ARGS_E == n_stack->type ) {
410 debug_printf( 1,
411 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (ARGS_E): "
412 SIZE_T_FMT,
413 exec->uid, i, n_stack->value.args_end );
414 */
415
416 } else if( MLISP_TYPE_BEGIN == n_stack->type ) {
417 debug_printf( 1,
418 "%u: " MLISP_TRACE_SIGIL " stack " SIZE_T_FMT " (BEGIN): "
419 SIZE_T_FMT,
420 exec->uid, i, n_stack->value.begin );
421
422 /* Handle numeric types. */
423 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_DUMPS );
424 } else {
425 error_printf( "invalid stack type: %u", n_stack->type );
426 }
427 i++;
428 }
429 mdata_strpool_unlock( &(parser->strpool) );
430 mdata_vector_unlock( &(exec->stack) );
431
432cleanup:
433
434 assert( mdata_strpool_is_locked( &(parser->strpool) ) );
435
436 return retval;
437}
438
439#endif /* MLISP_DUMP_ENABLED */
440
441/* === */
442
443#define _MLISP_TYPE_TABLE_PUSH( idx, ctype, name, const_name, fmt ) \
444 MERROR_RETVAL _mlisp_stack_push_ ## ctype( \
445 struct MLISP_EXEC_STATE* exec, ctype i \
446 ) { \
447 ssize_t stack_idx = 0; \
448 struct MLISP_STACK_NODE n_stack; \
449 MERROR_RETVAL retval = MERROR_OK; \
450 debug_printf( MLISP_STACK_TRACE_LVL, \
451 "%u: pushing " #const_name " onto stack: " fmt, exec->uid, i ); \
452 n_stack.type = MLISP_TYPE_ ## const_name; \
453 n_stack.value.name = i; \
454 stack_idx = mdata_vector_append( \
455 &(exec->stack), &n_stack, sizeof( struct MLISP_STACK_NODE ) ); \
456 if( 0 > stack_idx ) { \
457 retval = mdata_retval( stack_idx ); \
458 } \
459 return retval; \
460 }
461
462MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_PUSH );
463
464/* === */
465
467 struct MLISP_EXEC_STATE* exec, struct MLISP_STACK_NODE* o, uint8_t flags
468) {
469 MERROR_RETVAL retval = MERROR_OK;
470 struct MLISP_STACK_NODE* n_stack = NULL;
471 size_t n_idx = 0;
472
473 maug_mzero( o, sizeof( struct MLISP_STACK_NODE ) );
474
475 /* Check for valid stack pointer. */
476 maug_cleanup_if_eq(
477 mdata_vector_ct( &(exec->stack) ), 0, SIZE_T_FMT, MERROR_OVERFLOW );
478
479 n_idx = mdata_vector_ct( &(exec->stack) ) - 1;
480
481 /* Perform the pop! */
482 mdata_vector_lock( &(exec->stack) );
483 n_stack = mdata_vector_get(
484 &(exec->stack), n_idx, struct MLISP_STACK_NODE );
485 assert( NULL != n_stack );
486 memcpy( o, n_stack, sizeof( struct MLISP_STACK_NODE ) );
487 n_stack = NULL;
488 mdata_vector_unlock( &(exec->stack) );
489
490#if MLISP_STACK_TRACE_LVL > 0
491# define _MLISP_TYPE_TABLE_POPD( idx, ctype, name, const_name, fmt ) \
492 } else if( MLISP_TYPE_ ## const_name == o->type ) { \
493 if( MLISP_STACK_FLAG_PEEK == (MLISP_STACK_FLAG_PEEK & flags) ) { \
494 debug_printf( MLISP_STACK_TRACE_LVL, \
495 "%u: peeking (%ut): " SSIZE_T_FMT ": " fmt, \
496 exec->uid, n_idx, o->type, o->value.name ); \
497 } else { \
498 debug_printf( MLISP_STACK_TRACE_LVL, \
499 "%u: popping (%ut): " SSIZE_T_FMT ": " fmt, \
500 exec->uid, n_idx, o->type, o->value.name ); \
501 }
502
503 if( 0 ) {
504 MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_POPD )
505 }
506#endif /* MLISP_STACK_TRACE_LVL */
507
509 retval = mdata_vector_remove( &(exec->stack), n_idx );
510 }
511
512cleanup:
513
514 return retval;
515}
516
517/* === */
518
519/* Env Functons */
520
521/* === */
522
523#if defined( MLISP_DUMP_ENABLED )
524
525static MERROR_RETVAL _mlisp_env_dump_iter(
526 const struct MDATA_TABLE_KEY* key, void* data, size_t data_sz,
527 void* cb_data, size_t cb_data_sz, size_t idx
528) {
529 MERROR_RETVAL retval = MERROR_OK;
530 struct MLISP_ENV_NODE* e = (struct MLISP_ENV_NODE*)data;
531 struct MLISP_PARSER parser;
532 struct MLISP_EXEC_STATE* exec = (struct MLISP_EXEC_STATE*)cb_data;
533
534 maug_mzero( &parser, sizeof( struct MLISP_PARSER ) );
535
536# define _MLISP_TYPE_TABLE_DUMPE( idx, ctype, name, const_name, fmt ) \
537 } else if( MLISP_TYPE_ ## const_name == e->type ) { \
538 debug_printf( 1, \
539 "%u: " MLISP_TRACE_SIGIL " env \"%s\" (" #const_name "): " fmt, \
540 exec->uid, key->string, e->value.name );
541
542 if( MLISP_ENV_FLAG_BUILTIN == (MLISP_ENV_FLAG_BUILTIN & e->flags) ) {
543 /* Skip builtins. */
544 return MERROR_OK;
545 }
546
547 debug_printf( 1, "%s: %p: 0x%02x", key, e, e->type );
548
549 if( 0 ) {
550 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_DUMPE );
551 /* Handle special exceptions. */
552 } else if( MLISP_TYPE_STR == e->type ) {
553 debug_printf( 1,
554 "%u: " MLISP_TRACE_SIGIL " env \"%s\" (STR): %s",
555 exec->uid, key,
556 mdata_strpool_get( &(parser.strpool), e->value.strpool_idx ) );
557
558 } else if( MLISP_TYPE_CB == e->type ) {
559 debug_printf( 1,
560 "%u: " MLISP_TRACE_SIGIL " env \"%s\" (CB): %p",
561 exec->uid, key, e->value.cb );
562
563 } else if( MLISP_TYPE_LAMBDA == e->type ) {
564 debug_printf( 1,
565 "%u: " MLISP_TRACE_SIGIL " env \"%s\" (LAMBDA): " SIZE_T_FMT,
566 exec->uid, key, e->value.lambda );
567
568 } else {
569 error_printf( MLISP_TRACE_SIGIL " invalid env type: %u", e->type );
570 }
571
572 return retval;
573}
574
575/* === */
576
578 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t global
579) {
580 MERROR_RETVAL retval = MERROR_OK;
581 int8_t env_iter = 0;
582 uint8_t autolock[MLISP_EXEC_ENV_FRAME_CT_MAX];
583
584 retval = _mlisp_autolock(
585 NULL, exec, MLISP_AUTOLOCK_EXEC_ENV | MLISP_AUTOLOCK_GLOBAL_ENV,
586 autolock );
587 maug_cleanup_if_not_ok();
588
589 if( global ) {
590 debug_printf( 1, "# global env:" );
591 retval = mdata_table_iter(
592 exec->global_env, _mlisp_env_dump_iter, exec, 0 );
593 } else {
594 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
595 debug_printf( 1, "# env frame %d:", env_iter );
596 retval = mdata_table_iter(
597 &(exec->env[env_iter]), _mlisp_env_dump_iter, exec, 0 );
598 maug_cleanup_if_not_ok();
599 }
600 }
601
602cleanup:
603
604 _mlisp_autounlock( NULL, exec, autolock );
605
606 return retval;
607}
608
609#endif /* MLISP_DUMP_ENABLED */
610
611/* === */
612
613struct MLISP_ENV_NODE* mlisp_env_get(
614 struct MLISP_EXEC_STATE* exec, const char* key
615) {
616 struct MLISP_ENV_NODE* e = NULL;
617 struct MDATA_TABLE* env = NULL;
618 MERROR_RETVAL retval = MERROR_OK;
619 int8_t env_iter = exec->env_select;
620
621 while( 0 <= env_iter ) {
622 env = &(exec->env[env_iter]);
623
624 /* At the very least, the caller using this should be in the same lock
625 * context as this search, since we're returning a pointer. So no
626 * autolock!
627 */
628 assert( mdata_table_is_locked( env ) );
629
630 e = mdata_table_get( env, key, struct MLISP_ENV_NODE );
631 if( NULL != e ) {
632 /* Found something, so short-circuit! */
633 goto cleanup;
634 }
635
636 /* Try a higher frame. */
637 env_iter--;
638 }
639
640 /* Did not find anything in the local env, so try the global env if there
641 * is one!
642 */
643 if( NULL != exec->global_env ) {
644 assert( mdata_table_is_locked( exec->global_env ) );
645 e = mdata_table_get( exec->global_env, key, struct MLISP_ENV_NODE );
646 }
647
648cleanup:
649
650 if( MERROR_OK != retval ) {
651 e = NULL;
652 }
653
654 return e;
655}
656
657/* === */
658
659MERROR_RETVAL mlisp_env_unset(
660 struct MLISP_EXEC_STATE* exec, const char* token, size_t token_sz,
661 uint8_t global
662) {
663 MERROR_RETVAL retval = MERROR_OK;
664 uint8_t autolock[MLISP_EXEC_ENV_FRAME_CT_MAX];
665 int8_t env_iter = exec->env_select;
666 struct MDATA_TABLE* env = NULL;
667
668 /* TODO: Unset in global env if requested. */
669
670 maug_mzero( autolock, MLISP_EXEC_ENV_FRAME_CT_MAX );
671
672 while( 0 <= env_iter ) {
673#if MLISP_EXEC_TRACE_LVL > 0
674 debug_printf( MLISP_EXEC_TRACE_LVL,
675 "%u: attempting to undefine %s in frame %d...",
676 exec->uid, token, env_iter );
677#endif /* MLISP_EXEC_TRACE_LVL */
678
679 env = &(exec->env[env_iter]);
680
681 if( !mdata_table_is_locked( env ) ) {
682 mdata_table_lock( env );
683 autolock[env_iter] |= 0x02;
684 }
685
686 retval = mdata_table_unset( env, token );
687
688 env_iter--;
689 }
690
691 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
692 if( 0x02 == (0x02 & autolock[env_iter]) ) {
693 env = &(exec->env[env_iter]);
694 assert( mdata_table_is_locked( env ) );
695 mdata_table_unlock( env );
696 }
697 }
698
699 return retval;
700}
701
702/* === */
703
704MERROR_RETVAL mlisp_env_set(
705 struct MLISP_EXEC_STATE* exec,
706 const char* token, size_t token_sz, uint8_t env_type, const void* data,
707 uint8_t global, uint8_t flags
708) {
709 MERROR_RETVAL retval = MERROR_OK;
710 struct MLISP_ENV_NODE e;
711 struct MDATA_TABLE* env = NULL;
712
713 /* Builtins can only be inserted into frame 0! */
714 assert(
715 (MLISP_ENV_FLAG_BUILTIN != (MLISP_ENV_FLAG_BUILTIN & flags)) ||
716 0 == exec->env_select );
717
718 /* Default to current local env frame, but switch to global if requested. */
719 env = &(exec->env[exec->env_select]);
720 if( global ) {
721 if( NULL != exec->global_env ) {
722 env = exec->global_env;
723 } else {
724 error_printf( "global env requested but not present!" );
725 retval = MERROR_EXEC;
726 goto cleanup;
727 }
728 }
729
730 if( 0 == token_sz ) {
731 token_sz = maug_strlen( token );
732 }
733
734 assert( NULL != env );
735 assert( 0 < token_sz );
736
737 assert( !mdata_table_is_locked( env ) );
738
739 /* Find previous env nodes with same token and change. */
740 /* Ignore the retval, since it doesn't really matter if this fails. */
741 mdata_table_unset( env, token );
742
743#if MLISP_EXEC_TRACE_LVL > 0
744# define _MLISP_TYPE_TABLE_ASGN( idx, ctype, name, const_name, fmt ) \
745 case idx: \
746 debug_printf( MLISP_EXEC_TRACE_LVL, \
747 "%u: setting env %d: \"%s\": #" fmt, \
748 exec->uid, exec->env_select, token, (ctype)*((ctype*)data) ); \
749 e.value.name = *((ctype*)data); \
750 break;
751#else
752# define _MLISP_TYPE_TABLE_ASGN( idx, ctype, name, const_name, fmt ) \
753 case idx: \
754 e.value.name = *((ctype*)data); \
755 break;
756#endif /* MLISP_EXEC_TRACE_LVL */
757
758 /* Setup the new node to copy. */
759 maug_mzero( &e, sizeof( struct MLISP_ENV_NODE ) );
760 e.flags = flags;
761 maug_cleanup_if_not_ok();
762 e.type = env_type;
763 switch( env_type ) {
764 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ASGN );
765
766 /* Special cases: */
767
768 case 4 /* MLISP_TYPE_STR */:
769 /* TODO: Don't use strpool for this! */
770#if MLISP_EXEC_TRACE_LVL > 0
771 debug_printf( MLISP_EXEC_TRACE_LVL,
772 "%u: setting env %d: \"%s\": strpool(" SSIZE_T_FMT ")",
773 exec->uid, exec->env_select, token, *((ssize_t*)data) );
774#endif /* MLISP_EXEC_TRACE_LVL */
775 e.value.strpool_idx = *((mdata_strpool_idx_t*)data);
776 break;
777
778 case 5 /* MLISP_TYPE_CB */:
779#if MLISP_EXEC_TRACE_LVL > 0
780 debug_printf( MLISP_EXEC_TRACE_LVL,
781 "%u: setting env %d: \"%s\": 0x%p",
782 exec->uid, exec->env_select, token, (mlisp_env_cb_t)data );
783#endif /* MLISP_EXEC_TRACE_LVL */
784 e.value.cb = (mlisp_env_cb_t)data;
785 break;
786
787 case 6 /* MLISP_TYPE_LAMBDA */:
788#if MLISP_EXEC_TRACE_LVL > 0
789 debug_printf( MLISP_EXEC_TRACE_LVL,
790 "%u: setting env %d: \"%s\": node #" SSIZE_T_FMT,
791 exec->uid, exec->env_select, token, *((mlisp_lambda_t*)data) );
792#endif /* MLISP_EXEC_TRACE_LVL */
793 e.value.lambda = *((mlisp_lambda_t*)data);
794 break;
795
796 case 10: /* MLISP_TYPE_BEGIN */
797 /* We probably called a lambda that takes an arg without placing an
798 * arg on the stack for it to take up!
799 *
800 * This could be a script error, but it could also be a lambda being
801 * itered into after its finished executing (and thus has no arg on the
802 * stack waiting for it).
803 *
804 * MERROR_RESET signals the calling program we're embedded in to deal
805 * with this situation, maybe by restarting the script with a fresh env.
806 */
807 error_printf(
808 "%u: underflow %s: missing lambda arg?",
809 exec->uid, token );
810 retval = MERROR_RESET;
811 goto cleanup;
812
813 default:
814 error_printf( "invalid type: %d", env_type );
815 retval = MERROR_EXEC;
816 goto cleanup;
817 }
818
819 retval = mdata_table_set( env, token, &e, sizeof( struct MLISP_ENV_NODE ) );
820
821cleanup:
822
823 return retval;
824}
825
826/* === */
827
828static MERROR_RETVAL _mlisp_env_cb_cmp(
829 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
830 size_t args_c, void* cb_data, uint8_t flags
831) {
832 MERROR_RETVAL retval = MERROR_OK;
833 struct MLISP_STACK_NODE tmp;
834 uint8_t truth = 0;
835
836 /* The compiler seems to get a bit too eager if optimization is turned on,
837 * and ends up flubbing the comparison if these aren't volatile.
838 */
839 volatile int a_int,
840 b_int,
841 a_type,
842 b_type;
843 volatile int* cur_int = NULL;
844
845 mdata_strpool_lock( &(parser->strpool) );
846
847 /* XXX: If we put a mutable variable first, it gets modified? */
848
849#if MLISP_EXEC_TRACE_LVL > 0
850# define _MLISP_TYPE_TABLE_CMP( idx, ctype, name, const_name, fmt ) \
851 } else if( MLISP_TYPE_ ## const_name == tmp.type ) { \
852 *cur_int = (int)tmp.value.name; \
853 debug_printf( MLISP_EXEC_TRACE_LVL, \
854 "%u: cmp: pop " fmt " (%d)", exec->uid, tmp.value.name, *cur_int );
855#else
856# define _MLISP_TYPE_TABLE_CMP( idx, ctype, name, const_name, fmt ) \
857 } else if( MLISP_TYPE_ ## const_name == tmp.type ) { \
858 *cur_int = (int)tmp.value.name;
859#endif /* MLISP_EXEC_TRACE_LVL */
860
861 retval = mlisp_stack_pop( exec, &tmp );
862 maug_cleanup_if_not_ok();
863 cur_int = &b_int;
864 if( MLISP_TYPE_STR == tmp.type ) {
865 /* TODO: Buffer string for later comparison. */
866 a_type = MLISP_TYPE_STR;
867
868 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_CMP )
869 } else {
870 error_printf( "cmp: invalid type: %d", tmp.type );
871 retval = MERROR_EXEC;
872 goto cleanup;
873 }
874
875 retval = mlisp_stack_pop( exec, &tmp );
876 maug_cleanup_if_not_ok();
877 cur_int = &a_int;
878 if( MLISP_TYPE_STR == tmp.type ) {
879 /* TODO: Buffer string for later comparison. */
880 b_type = MLISP_TYPE_STR;
881
882 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_CMP )
883 } else {
884 error_printf( "cmp: invalid type!" );
885 retval = MERROR_EXEC;
886 goto cleanup;
887 }
888
889 /* TODO: String comparison? */
890 if( MLISP_TYPE_STR == a_type || MLISP_TYPE_STR == b_type ) {
891
892 /* TODO: Do a strncmp() and push 1 if true. */
893 retval = mlisp_stack_push( exec, 0, mlisp_bool_t );
894 goto cleanup;
895 }
896
897 /* String comparison didn't catch, so it must be a number comparison? */
899#if MLISP_EXEC_TRACE_LVL > 0
900 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: cmp %d > %d",
901 exec->uid, a_int, b_int );
902#endif /* MLISP_EXEC_TRACE_LVL */
903 truth = a_int > b_int;
904 } else if( MLISP_ENV_FLAG_CMP_LT == (MLISP_ENV_FLAG_CMP_LT & flags) ) {
905#if MLISP_EXEC_TRACE_LVL > 0
906 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: cmp %d < %d",
907 exec->uid, a_int, b_int );
908#endif /* MLISP_EXEC_TRACE_LVL */
909 truth = a_int < b_int;
910 } else if( MLISP_ENV_FLAG_CMP_EQ == (MLISP_ENV_FLAG_CMP_EQ & flags) ) {
911#if MLISP_EXEC_TRACE_LVL > 0
912 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: cmp %d == %d",
913 exec->uid, a_int, b_int );
914#endif /* MLISP_EXEC_TRACE_LVL */
915 truth = a_int == b_int;
916 } else {
917 error_printf( "invalid parameter provided to _mlisp_env_cb_cmp()!" );
918 retval = MERROR_EXEC;
919 goto cleanup;
920 }
921
922 retval = mlisp_stack_push( exec, truth, mlisp_bool_t );
923
924cleanup:
925
926 mdata_strpool_unlock( &(parser->strpool) );
927
928 return retval;
929}
930
931/* === */
932
933static MERROR_RETVAL _mlisp_env_cb_arithmetic(
934 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
935 size_t args_c, void* cb_data, uint8_t flags
936) {
937 MERROR_RETVAL retval = MERROR_OK;
938 struct MLISP_STACK_NODE num;
939 /* TODO: Vary type based on multiplied types. */
940 int16_t num_out = 0;
941 size_t i = 0;
942
943# define _MLISP_TYPE_TABLE_ARI1( idx, ctype, name, const_name, fmt ) \
944 } else if( MLISP_TYPE_ ## const_name == num.type ) { \
945 num_out = num.value.name;
946
947 retval = mlisp_stack_pop( exec, &num );
948 maug_cleanup_if_not_ok();
949
950 if( 0 ) {
951 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ARI1 )
952 } else {
953 error_printf( "arithmetic: invalid type!" );
954 retval = MERROR_EXEC;
955 goto cleanup;
956 }
957
958# define _MLISP_TYPE_TABLE_ARI2( idx, ctype, name, const_name, fmt ) \
959 } else if( \
960 MLISP_TYPE_ ## const_name == num.type && \
961 MLISP_ENV_FLAG_ARI_ADD == (MLISP_ENV_FLAG_ARI_ADD & flags) \
962 ) { \
963 debug_printf( MLISP_EXEC_TRACE_LVL, \
964 "%u: arithmetic: %d + " fmt, exec->uid, num_out, num.value.name ); \
965 num_out += num.value.name; \
966 } else if( \
967 MLISP_TYPE_ ## const_name == num.type && \
968 MLISP_ENV_FLAG_ARI_MUL == (MLISP_ENV_FLAG_ARI_MUL & flags) \
969 ) { \
970 debug_printf( MLISP_EXEC_TRACE_LVL, \
971 "%u: arithmetic: %d * " fmt, exec->uid, num_out, num.value.name ); \
972 num_out *= num.value.name; \
973 } else if( \
974 MLISP_TYPE_ ## const_name == num.type && \
975 MLISP_ENV_FLAG_ARI_DIV == (MLISP_ENV_FLAG_ARI_DIV & flags) \
976 ) { \
977 debug_printf( MLISP_EXEC_TRACE_LVL, \
978 "%u: arithmetic: %d / " fmt, exec->uid, num_out, num.value.name ); \
979 num_out /= num.value.name; \
980
981 for( i = 0 ; args_c - 1 > i ; i++ ) {
982 retval = mlisp_stack_pop( exec, &num );
983 maug_cleanup_if_not_ok();
984
985 if( 0 ) {
986 MLISP_NUM_TYPE_TABLE( _MLISP_TYPE_TABLE_ARI2 )
987
988 } else if(
989 MLISP_TYPE_INT == num.type &&
990 MLISP_ENV_FLAG_ARI_MOD == (MLISP_ENV_FLAG_ARI_MOD & flags)
991 ) {
992 /* Modulus is a special case, as you can't mod by float. */
993 debug_printf( MLISP_EXEC_TRACE_LVL,
994 "%u: arithmetic: %d %% %d", exec->uid, num_out, num.value.integer );
995 num_out %= num.value.integer;
996 } else {
997 error_printf( "arithmetic: invalid type!" );
998 retval = MERROR_EXEC;
999 goto cleanup;
1000 }
1001 }
1002
1003 debug_printf( MLISP_EXEC_TRACE_LVL,
1004 "%u: arithmetic result: %d", exec->uid, num_out );
1005
1006 retval = mlisp_stack_push( exec, num_out, int16_t );
1007
1008cleanup:
1009
1010 mdata_strpool_unlock( &(parser->strpool) );
1011
1012 return retval;
1013}
1014
1015/* === */
1016
1017static MERROR_RETVAL _mlisp_env_cb_define(
1018 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1019 size_t args_c, void* cb_data, uint8_t flags
1020) {
1021 MERROR_RETVAL retval = MERROR_OK;
1022 struct MLISP_STACK_NODE key;
1023 struct MLISP_STACK_NODE val;
1024 MAUG_MHANDLE key_tmp_h = (MAUG_MHANDLE)NULL;
1025 char* key_tmp = NULL;
1026 uint8_t global = 0;
1027
1028#if MLISP_EXEC_TRACE_LVL > 0
1029 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: entering define callback...",
1030 exec->uid );
1031#endif /* MLISP_EXEC_TRACE_LVL */
1032
1033 retval = mlisp_stack_pop( exec, &val );
1034 maug_cleanup_if_not_ok();
1035
1036 retval = mlisp_stack_pop( exec, &key );
1037 maug_cleanup_if_not_ok();
1038
1039 if( MLISP_TYPE_STR != key.type ) {
1040 /* TODO: Do we want to allow defining other types? */
1041 /* TODO: We can use _mlisp_eval_token_strpool, maybe? */
1042 error_printf( "define: invalid key type: %d", key.type );
1043 retval = MERROR_EXEC;
1044 goto cleanup;
1045 }
1046
1047#if MLISP_EXEC_TRACE_LVL > 0
1048 debug_printf( MLISP_EXEC_TRACE_LVL,
1049 "%u: extracting define term for idx: " SIZE_T_FMT,
1050 exec->uid, key.value.strpool_idx );
1051#endif /* MLISP_EXEC_TRACE_LVL */
1052
1053 key_tmp_h = mdata_strpool_extract(
1054 &(parser->strpool), key.value.strpool_idx );
1055 /* TODO: Handle this gracefully. */
1056 assert( (MAUG_MHANDLE)NULL != key_tmp_h );
1057
1058 maug_mlock( key_tmp_h, key_tmp );
1059 maug_cleanup_if_null_lock( char*, key_tmp );
1060
1061#if MLISP_EXEC_TRACE_LVL > 0
1062 debug_printf( MLISP_EXEC_TRACE_LVL,
1063 "%u: define \"%s\" (strpool(" SIZE_T_FMT "))...",
1064 exec->uid, key_tmp, key.value.strpool_idx );
1065#endif /* MLISP_EXEC_TRACE_LVL */
1066
1067 /* Figure out the env to use. */
1068 if(
1070 ) {
1071#if MLISP_EXEC_TRACE_LVL > 0
1072 debug_printf( MLISP_EXEC_TRACE_LVL,
1073 "%u: using global env...", exec->uid );
1074#endif /* MLISP_EXEC_TRACE_LVL */
1075 global = 1;
1076 }
1077
1078 /* Perform the insertion. */
1079 retval = mlisp_env_set(
1080 exec, key_tmp, maug_strlen( key_tmp ), val.type, &(val.value),
1081 global, 0 );
1082 maug_cleanup_if_not_ok();
1083
1084#if MLISP_EXEC_TRACE_LVL > 0
1085 debug_printf( MLISP_EXEC_TRACE_LVL,
1086 "%u: setup env node: %s",
1087 exec->uid, key_tmp );
1088#endif /* MLISP_EXEC_TRACE_LVL */
1089
1090cleanup:
1091
1092 if( NULL != key_tmp ) {
1093 maug_munlock( key_tmp_h, key_tmp );
1094 }
1095
1096 if( (MAUG_MHANDLE)NULL != key_tmp_h ) {
1097 maug_mfree( key_tmp_h );
1098 }
1099
1100 return retval;
1101}
1102
1103/* === */
1104
1105static MERROR_RETVAL _mlisp_env_cb_if(
1106 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1107 size_t args_c, void* cb_data, uint8_t flags
1108) {
1109 MERROR_RETVAL retval = MERROR_OK;
1110 size_t* p_if_child_idx = NULL;
1111 struct MLISP_STACK_NODE s;
1112 struct MLISP_AST_NODE* n = NULL;
1113
1114#if MLISP_EXEC_TRACE_LVL > 0
1115 debug_printf( MLISP_EXEC_TRACE_LVL,
1116 "%u: qrqrqrqrqr STEP IF qrqrqrqrqr", exec->uid );
1117#endif /* MLISP_EXEC_TRACE_LVL */
1118
1119 /* Grab the current exec index for the child vector for this node. */
1120 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1121 p_if_child_idx = mdata_vector_get(
1122 &(exec->per_node_child_idx), n_idx, size_t );
1123 assert( NULL != p_if_child_idx );
1124#if MLISP_EXEC_TRACE_LVL > 0
1125 debug_printf( MLISP_EXEC_TRACE_LVL,
1126 "%u: node " SIZE_T_FMT " child idx: " SIZE_T_FMT,
1127 exec->uid, n_idx, *p_if_child_idx );
1128#endif /* MLISP_EXEC_TRACE_LVL */
1129
1130 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1131
1132 if( 0 == *p_if_child_idx ) {
1133 /* Evaluating if condition. */
1134#if MLISP_EXEC_TRACE_LVL > 0
1135 debug_printf( MLISP_EXEC_TRACE_LVL,
1136 "%u: stepping into condition...", exec->uid );
1137#endif /* MLISP_EXEC_TRACE_LVL */
1138 retval = _mlisp_step_iter(
1139 parser, n->ast_idx_children[*p_if_child_idx], exec );
1140#if MLISP_EXEC_TRACE_LVL > 0
1141 debug_printf( MLISP_EXEC_TRACE_LVL,
1142 "%u: ...stepped out of condition", exec->uid );
1143#endif /* MLISP_EXEC_TRACE_LVL */
1144
1145 /* Vary the child we jump to based on the boolean val on the stack. */
1146 if( MERROR_OK == retval ) {
1147 /* Condition evaluation complete. */
1148
1149 /* Pop the result and check it. */
1150 retval = mlisp_stack_pop( exec, &s );
1151 maug_cleanup_if_not_ok();
1152 if( MLISP_TYPE_BOOLEAN != s.type ) {
1153 error_printf( "(if) can only evaluate boolean type!" );
1154 retval = MERROR_EXEC;
1155 goto cleanup;
1156 }
1157
1158 /* Set the child pointer to 1 if TRUE and 2 if FALSE. */
1159 retval = _mlisp_preempt(
1160 retval, "if", parser, n_idx, exec,
1161 /* Flip boolean and increment. */
1162 (1 - s.value.boolean) + 1 );
1163 }
1164
1165 } else if( args_c > *p_if_child_idx ) { /* 3 if else present, else 2. */
1166 /* Pursuing TRUE or FALSE clause. */
1167
1168#if MLISP_EXEC_TRACE_LVL > 0
1169 debug_printf( MLISP_EXEC_TRACE_LVL,
1170 "%u: descending into IF path: " SIZE_T_FMT,
1171 exec->uid, *p_if_child_idx );
1172#endif /* MLISP_EXEC_TRACE_LVL */
1173
1174 /* Prepare for stepping. */
1175
1176 /* Step and check. */
1177 retval = _mlisp_step_iter(
1178 parser, n->ast_idx_children[*p_if_child_idx], exec );
1179 retval = _mlisp_preempt(
1180 retval, "if", parser, n_idx, exec, 3 );
1181 }
1182
1183cleanup:
1184
1185#if MLISP_EXEC_TRACE_LVL > 0
1186 debug_printf( MLISP_EXEC_TRACE_LVL,
1187 "%u: qrqrqrqrqr END STEP IF qrqrqrqrqr", exec->uid );
1188#endif /* MLISP_EXEC_TRACE_LVL */
1189
1190 return retval;
1191}
1192
1193/* === */
1194
1195#ifndef MAUG_NO_RETRO
1196/* TODO: Define this callback in retroflat in line with dependency guidelines.
1197 */
1198
1199static MERROR_RETVAL _mlisp_env_cb_random(
1200 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1201 size_t args_c, void* cb_data, uint8_t flags
1202) {
1203 MERROR_RETVAL retval = MERROR_OK;
1204 struct MLISP_STACK_NODE mod;
1205 int16_t random_int = 0;
1206
1207 retval = mlisp_stack_pop( exec, &mod );
1208 maug_cleanup_if_not_ok();
1209
1210 if( MLISP_TYPE_INT != mod.type ) {
1211 /* TODO: Setup float. */
1212 error_printf( "random: invalid modulus type: %d", mod.type );
1213 retval = MERROR_EXEC;
1214 goto cleanup;
1215 }
1216
1217 random_int = retroflat_get_rand() % mod.value.integer;
1218
1219#if MLISP_EXEC_TRACE_LVL > 0
1220 debug_printf( MLISP_EXEC_TRACE_LVL,
1221 "%u: random: %d", exec->uid, random_int );
1222#endif /* MLISP_EXEC_TRACE_LVL */
1223
1224 mlisp_stack_push( exec, random_int, int16_t );
1225
1226cleanup:
1227
1228 return retval;
1229}
1230
1231#endif /* !MAUG_NO_RETRO */
1232
1233/* === */
1234
1235static MERROR_RETVAL _mlisp_env_cb_ano(
1236 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, size_t n_idx,
1237 size_t args_c, void* cb_data, uint8_t flags
1238) {
1239 MERROR_RETVAL retval = MERROR_OK;
1240 struct MLISP_STACK_NODE val;
1241 mlisp_bool_t val_out =
1242 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ?
1243 0 : 1;
1244 size_t i = 0;
1245
1246 /* TODO: Switch this to a step_or() function so that we can opt not to
1247 * evaluate conditions unless prior stepped children are false.
1248 */
1249
1250 for( i = 0 ; args_c > i ; i++ ) {
1251 retval = mlisp_stack_pop( exec, &val );
1252 maug_cleanup_if_not_ok();
1253
1254 if( MLISP_TYPE_BOOLEAN != val.type ) {
1255 error_printf( "or: invalid boolean type: %d", val.type );
1256 }
1257
1258 if( val.value.boolean ) {
1259#if MLISP_EXEC_TRACE_LVL > 0
1260 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: found TRUE in %s!",
1261 exec->uid,
1262 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ?
1263 "or" : "and" );
1264#endif /* MLISP_EXEC_TRACE_LVL */
1265 val_out =
1266 MLISP_ENV_FLAG_ANO_OR == (MLISP_ENV_FLAG_ANO_OR & flags) ? 1 : 0;
1267 }
1268 }
1269
1270 retval = _mlisp_stack_push_mlisp_bool_t( exec, val_out );
1271
1272cleanup:
1273
1274 return retval;
1275}
1276
1277/* === */
1278
1279/* Execution Functions */
1280
1281/* === */
1282
1283static MERROR_RETVAL _mlisp_preempt(
1284 MERROR_RETVAL retval, const char* caller, struct MLISP_PARSER* parser,
1285 size_t n_idx, struct MLISP_EXEC_STATE* exec, size_t new_idx
1286) {
1287 struct MLISP_AST_NODE* n = NULL;
1288 size_t* p_child_idx = NULL;
1289
1290 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1291 p_child_idx = mdata_vector_get(
1292 &(exec->per_node_child_idx), n_idx, size_t );
1293 assert( NULL != p_child_idx );
1294
1295 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1296
1297 if( 0 < n->token_idx ) {
1298 mdata_strpool_lock( &(parser->strpool) );
1299#if MLISP_EXEC_TRACE_LVL > 0
1300 debug_printf( MLISP_EXEC_TRACE_LVL,
1301 "%u: eval step " SSIZE_T_FMT " under (%s) %s...",
1302 exec->uid, *p_child_idx, caller,
1303 mdata_strpool_get( &(parser->strpool), n->token_idx ) );
1304#endif /* MLISP_EXEC_TRACE_LVL */
1305 mdata_strpool_unlock( &(parser->strpool) );
1306#if MLISP_EXEC_TRACE_LVL > 0
1307 } else {
1308 debug_printf( MLISP_EXEC_TRACE_LVL,
1309 "%u: eval step " SSIZE_T_FMT " under (%s) (empty token)...",
1310 exec->uid, *p_child_idx, caller );
1311#endif /* MLISP_EXEC_TRACE_LVL */
1312 }
1313
1314 if( MERROR_OK != retval ) {
1315 /* Something bad happened, so don't increment! */
1316#if MLISP_EXEC_TRACE_LVL > 0
1317 debug_printf( MLISP_EXEC_TRACE_LVL,
1318 "%u: not incrementing node " SIZE_T_FMT " child idx from "
1319 SIZE_T_FMT " (retval: 0x%x)!",
1320 exec->uid, n_idx, *p_child_idx, retval );
1321#endif /* MLISP_EXEC_TRACE_LVL */
1322 goto cleanup;
1323 }
1324
1325 /* Could not exec *this* node yet, so don't increment its parent. */
1326 retval = MERROR_PREEMPT;
1327
1328 /* Increment this node, since the child actually executed. */
1329 (*p_child_idx) = new_idx;
1330#if MLISP_EXEC_TRACE_LVL > 0
1331 debug_printf( MLISP_EXEC_TRACE_LVL,
1332 "%u: incremented node " SIZE_T_FMT " child idx to: " SIZE_T_FMT,
1333 exec->uid, n_idx, *p_child_idx );
1334#endif /* MLISP_EXEC_TRACE_LVL */
1335
1336cleanup:
1337
1338 assert( !mdata_strpool_is_locked( &(parser->strpool) ) );
1339
1340 return retval;
1341}
1342
1343/* === */
1344
1345static MERROR_RETVAL _mlisp_step_iter_children(
1346 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1347) {
1348 MERROR_RETVAL retval = MERROR_OK;
1349 size_t* p_child_idx = NULL;
1350 struct MLISP_AST_NODE* n = NULL;
1351
1352 /* Grab the current exec index for the child vector for this node. */
1353 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1354 p_child_idx = mdata_vector_get(
1355 &(exec->per_node_child_idx), n_idx, size_t );
1356 assert( NULL != p_child_idx );
1357#if MLISP_EXEC_TRACE_LVL > 0
1358 debug_printf( MLISP_EXEC_TRACE_LVL,
1359 "%u: node " SIZE_T_FMT " child idx: " SIZE_T_FMT,
1360 exec->uid, n_idx, *p_child_idx );
1361#endif /* MLISP_EXEC_TRACE_LVL */
1362
1363 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1364
1365 if(
1366 (
1367 MLISP_AST_FLAG_LAMBDA == (MLISP_AST_FLAG_LAMBDA & n->flags) &&
1368 0 == *p_child_idx
1369 ) ||
1370 MLISP_AST_FLAG_IF == (MLISP_AST_FLAG_IF & n->flags)
1371 ) {
1372 /* A lambda definition was found, and its exec counter is still pointing
1373 * to the arg list. This means the lambda was *not* called on the last
1374 * heartbeat, and we're probably just enountering its definition.
1375 *
1376 * Lambdas are lazily evaluated, so don't pursue it further until it's
1377 * called (stee _mlisp_step_lambda() for more info on this.
1378 */
1379#if MLISP_EXEC_TRACE_LVL > 0
1380 debug_printf( MLISP_EXEC_TRACE_LVL,
1381 "%u: skipping lambda children...", exec->uid );
1382#endif /* MLISP_EXEC_TRACE_LVL */
1383 goto cleanup;
1384 }
1385
1386 if( mlisp_ast_has_ready_children( *p_child_idx, n ) ) {
1387 /* Call the next uncalled child. */
1388
1389 if(
1390 MLISP_AST_FLAG_DEFINE == (MLISP_AST_FLAG_DEFINE & n->flags) &&
1391 0 == *p_child_idx
1392 ) {
1393 /* The next child is a term to be defined. */
1394#if MLISP_EXEC_TRACE_LVL > 0
1395 debug_printf( MLISP_EXEC_TRACE_LVL,
1396 "%u: setting MLISP_EXEC_FLAG_DEF_TERM!", exec->uid );
1397#endif /* MLISP_EXEC_TRACE_LVL */
1398 exec->flags |= MLISP_EXEC_FLAG_DEF_TERM;
1399 } else {
1400 exec->flags &= ~MLISP_EXEC_FLAG_DEF_TERM;
1401 }
1402
1403 /* Step and check. */
1404 retval = _mlisp_step_iter(
1405 parser, n->ast_idx_children[*p_child_idx], exec );
1406 retval = _mlisp_preempt(
1407 retval, "node", parser, n_idx, exec, (*p_child_idx) + 1 );
1408 goto cleanup;
1409 }
1410
1411cleanup:
1412
1413 return retval;
1414}
1415
1416/* === */
1417
1418static MERROR_RETVAL _mlisp_step_lambda_args(
1419 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1420) {
1421 MERROR_RETVAL retval = MERROR_OK;
1422 ssize_t arg_idx = 0;
1423 struct MLISP_STACK_NODE stack_n_arg;
1424 struct MLISP_AST_NODE* ast_n_arg = NULL;
1425 MAUG_MHANDLE key_tmp_h = (MAUG_MHANDLE)NULL;
1426 char* key_tmp = NULL;
1427 struct MLISP_AST_NODE* n = NULL;
1428 int16_t null_val = 0;
1429
1430 /* Pop stack into args into the env. These are all the results of previous
1431 * evaluations, before the lambda call, so we can just grab them all in
1432 * one go!
1433 */
1434
1435 /* Create a new env and bump up env_select. */
1436 if( MLISP_EXEC_ENV_FRAME_CT_MAX > exec->env_select + 1 ) {
1437 exec->env_select++;
1438#if MLISP_EXEC_TRACE_LVL > 0
1439 debug_printf( MLISP_EXEC_TRACE_LVL, "selecting env frame: %d",
1440 exec->env_select );
1441#endif /* MLISP_EXEC_TRACE_LVL */
1442 assert( 0 == mdata_table_ct( &(exec->env[exec->env_select]) ) );
1443
1444 /* Toss a constant into the new env so it's not as wonky. */
1445 retval = mlisp_env_set(
1446 exec, "null", 4, MLISP_TYPE_INT, &null_val, 0, 0 );
1447 } else {
1448 error_printf( "env frame overflow!" );
1449 retval = MERROR_OVERFLOW;
1450 goto cleanup;
1451 }
1452
1453 /* Get the current args node. */
1454 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1455 arg_idx = n->ast_idx_children_sz - 1;
1456
1457 while( 0 <= arg_idx ) {
1458
1459 retval = mlisp_stack_pop( exec, &stack_n_arg );
1460 maug_cleanup_if_not_ok();
1461
1462 ast_n_arg = mdata_vector_get(
1463 &(parser->ast), n->ast_idx_children[arg_idx],
1464 struct MLISP_AST_NODE );
1465
1466 /* Pull out the arg name from the strpool so we can call env_set(). */
1467 key_tmp_h = mdata_strpool_extract(
1468 &(parser->strpool), ast_n_arg->token_idx );
1469 /* TODO: Handle this gracefully. */
1470 assert( (MAUG_MHANDLE)NULL != key_tmp_h );
1471
1472 maug_mlock( key_tmp_h, key_tmp );
1473 maug_cleanup_if_null_lock( char*, key_tmp );
1474
1475 retval = mlisp_env_set(
1476 exec, key_tmp, 0, stack_n_arg.type, &(stack_n_arg.value), 0, 0 );
1477 maug_cleanup_if_not_ok();
1478
1479 maug_munlock( key_tmp_h, key_tmp );
1480 maug_mfree( key_tmp_h );
1481
1482 arg_idx--;
1483 }
1484
1485cleanup:
1486
1487 if( NULL != key_tmp ) {
1488 maug_munlock( key_tmp_h, key_tmp );
1489 }
1490
1491 if( (MAUG_MHANDLE)NULL != key_tmp_h ) {
1492 maug_mfree( key_tmp_h );
1493 }
1494
1495 return retval;
1496}
1497
1498/* === */
1499
1500static MERROR_RETVAL _mlisp_reset_child_pcs(
1501 const struct MLISP_PARSER* parser,
1502 size_t n_idx, struct MLISP_EXEC_STATE* exec
1503) {
1504 MERROR_RETVAL retval = MERROR_OK;
1505 size_t* p_child_idx = NULL;
1506 size_t* p_visit_ct = NULL;
1507 struct MLISP_AST_NODE* n = NULL;
1508 size_t i = 0;
1509
1510 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1511 assert( mdata_vector_is_locked( &(parser->ast) ) );
1512
1513 /* Perform the actual reset. */
1514#if MLISP_EXEC_TRACE_LVL > 0
1515 debug_printf( MLISP_EXEC_TRACE_LVL,
1516 "%u: resetting node " SIZE_T_FMT " child idx to 0", exec->uid, n_idx );
1517#endif /* MLISP_EXEC_TRACE_LVL */
1518 p_child_idx = mdata_vector_get( &(exec->per_node_child_idx), n_idx, size_t );
1519 assert( NULL != p_child_idx );
1520 *p_child_idx = 0;
1521
1522#if MLISP_EXEC_TRACE_LVL > 0
1523 debug_printf( MLISP_EXEC_TRACE_LVL,
1524 "%u: resetting node " SIZE_T_FMT " visit count to 0", exec->uid, n_idx );
1525#endif /* MLISP_EXEC_TRACE_LVL */
1526 p_visit_ct = mdata_vector_get( &(exec->per_node_visit_ct), n_idx, size_t );
1527 assert( NULL != p_visit_ct );
1528 *p_visit_ct = 0;
1529
1530 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1531
1532 /* Call reset on all children. */
1533 for( i = 0 ; n->ast_idx_children_sz > i ; i++ ) {
1534 retval = _mlisp_reset_child_pcs( parser, n->ast_idx_children[i], exec );
1535 maug_cleanup_if_not_ok();
1536 }
1537
1538cleanup:
1539
1540 return retval;
1541}
1542
1543/* === */
1544
1545static MERROR_RETVAL _mlisp_reset_lambda(
1546 const struct MLISP_PARSER* parser,
1547 size_t n_idx, struct MLISP_EXEC_STATE* exec
1548) {
1549 MERROR_RETVAL retval = MERROR_OK;
1550
1551#if MLISP_EXEC_TRACE_LVL > 0
1552 debug_printf( MLISP_EXEC_TRACE_LVL,
1553 "%u: resetting lambda " SIZE_T_FMT "...", exec->uid, n_idx );
1554#endif /* MLISP_EXEC_TRACE_LVL */
1555
1556 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1557
1558 /* Move up one env frame. */
1559 assert( !mdata_table_is_locked( &(exec->env[exec->env_select]) ) );
1560 assert( 0 < exec->env_select );
1561 mdata_table_free( &(exec->env[exec->env_select]) );
1562 exec->env_select--;
1563
1564 /* Reset per-node program counters. */
1565 retval = _mlisp_reset_child_pcs( parser, n_idx, exec );
1566
1567 return retval;
1568}
1569
1570/* === */
1571
1572/* This is internal-only and should only be called from _mlisp_step_iter()! */
1573static MERROR_RETVAL _mlisp_step_lambda(
1574 struct MLISP_PARSER* parser,
1575 size_t n_idx, struct MLISP_EXEC_STATE* exec
1576) {
1577 MERROR_RETVAL retval = MERROR_OK;
1578 size_t* p_lambda_child_idx = NULL;
1579 size_t* p_args_child_idx = NULL;
1580 struct MLISP_AST_NODE* n = NULL;
1581 size_t* p_n_last_lambda = NULL;
1582 ssize_t append_retval = 0;
1583
1584#ifdef MLISP_DEBUG_TRACE
1585 exec->trace[exec->trace_depth++] = n_idx;
1586 assert( exec->trace_depth <= MLISP_DEBUG_TRACE );
1587#endif /* MLISP_DEBUG_TRACE */
1588
1589 /* n_idx is the node of this lambda. */
1590 mdata_vector_lock( &(exec->lambda_trace) );
1591 p_n_last_lambda = mdata_vector_get_last( &(exec->lambda_trace), size_t );
1592 mdata_vector_unlock( &(exec->lambda_trace) );
1593 if( NULL != p_n_last_lambda && n_idx == *p_n_last_lambda ) {
1594 /* This is a recursive call, so get rid of the lambda context so we can
1595 * replace it with a new one afterwards.
1596 */
1597#if MLISP_EXEC_TRACE_LVL > 0
1598 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: TRACE TAIL TIME!", exec->uid );
1599#endif /* MLISP_EXEC_TRACE_LVL */
1600 /*
1601 assert(
1602 !mdata_table_is_locked( &(exec->env) ) );
1603 */
1604 _mlisp_reset_lambda( parser, n_idx, exec );
1605 retval = mdata_vector_remove_last( &(exec->lambda_trace) );
1606 maug_cleanup_if_not_ok();
1607 }
1608
1609#if MLISP_EXEC_TRACE_LVL > 0
1610 debug_printf( MLISP_EXEC_TRACE_LVL,
1611 "%u: xvxvxvxvxvxvx STEP LAMBDA " SIZE_T_FMT " xvxvxvxvxvx",
1612 exec->uid, n_idx );
1613#endif /* MLISP_EXEC_TRACE_LVL */
1614
1615 /* Note that we passed through this lambda to detect tail calls later. */
1616 append_retval = mdata_vector_append(
1617 &(exec->lambda_trace), &n_idx, sizeof( size_t ) );
1618 retval = mdata_retval( append_retval );
1619 maug_cleanup_if_not_ok();
1620
1621 /* Grab the current exec index for the child vector for this node. */
1622 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1623 p_lambda_child_idx = mdata_vector_get(
1624 &(exec->per_node_child_idx), n_idx, size_t );
1625 assert( NULL != p_lambda_child_idx );
1626#if MLISP_EXEC_TRACE_LVL > 0
1627 debug_printf( MLISP_EXEC_TRACE_LVL,
1628 "%u: lambda node " SIZE_T_FMT " child idx: " SIZE_T_FMT,
1629 exec->uid, n_idx, *p_lambda_child_idx );
1630#endif /* MLISP_EXEC_TRACE_LVL */
1631
1632 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1633
1634 /* There needs to be an arg node and an exec node. */
1635 /* TODO: Handle this gracefully. */
1636 assert( 1 < n->ast_idx_children_sz );
1637
1638 if( 0 == *p_lambda_child_idx ) {
1639 /* Parse the args passed to this lambda into the env, temporarily. */
1640
1641 /* Get the current args node child index. */
1642 assert( mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
1643 p_args_child_idx = mdata_vector_get(
1644 &(exec->per_node_child_idx),
1645 n->ast_idx_children[*p_lambda_child_idx], size_t );
1646 assert( NULL != p_args_child_idx );
1647#if MLISP_EXEC_TRACE_LVL > 0
1648 debug_printf( MLISP_EXEC_TRACE_LVL,
1649 "%u: child idx for args AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1650 exec->uid, *p_lambda_child_idx, *p_args_child_idx );
1651#endif /* MLISP_EXEC_TRACE_LVL */
1652
1653 /* Pop stack into args in the env. */
1654 retval = _mlisp_step_lambda_args(
1655 parser, n->ast_idx_children[*p_lambda_child_idx], exec );
1656 if( MERROR_OK != retval && MERROR_PREEMPT != retval ) {
1657 /* Something bad happened! */
1658 goto cleanup;
1659 }
1660
1661 if( MERROR_OK == retval ) {
1662 /* Set *after-arg* delimiter in env after last arg. */
1663 /*
1664 retval = mlisp_env_set(
1665 parser, exec, "$ARGS_E$", 0, MLISP_TYPE_ARGS_E, &n_idx, NULL, 0 );
1666 maug_cleanup_if_not_ok();
1667 */
1668
1669 /* Increment child idx so we call the exec child on next heartbeat. */
1670 (*p_lambda_child_idx)++;
1671#if MLISP_EXEC_TRACE_LVL > 0
1672 debug_printf( MLISP_EXEC_TRACE_LVL,
1673 "%u: incremented node " SIZE_T_FMT " child idx to: " SIZE_T_FMT,
1674 exec->uid, n_idx, *p_lambda_child_idx );
1675#endif /* MLISP_EXEC_TRACE_LVL */
1676 }
1677
1678 /* Set the error to MERROR_PREEMPT so that caller knows this lambda isn't
1679 * finished executing.
1680 */
1681 retval = MERROR_PREEMPT;
1682
1683 } else if( mlisp_ast_has_ready_children( *p_lambda_child_idx, n ) ) {
1684 /* Dive into first lambda child until we no longer can. */
1685
1686 /*
1687 assert(
1688 !mdata_table_is_locked( &(exec->env) ) );
1689 */
1690 assert(
1691 NULL == exec->global_env ||
1692 !mdata_table_is_locked( exec->global_env ) );
1693
1694 retval = _mlisp_step_iter(
1695 parser, n->ast_idx_children[*p_lambda_child_idx], exec );
1696
1697 retval = _mlisp_preempt(
1698 retval, "lambda", parser, n_idx, exec, (*p_lambda_child_idx) + 1 );
1699
1700 } else {
1701 /* No more children to execute! */
1702 /* assert(
1703 !mdata_table_is_locked( &(exec->env) ) ); */
1704 assert(
1705 NULL == exec->global_env ||
1706 !mdata_table_is_locked( exec->global_env ) );
1707 _mlisp_reset_lambda( parser, n_idx, exec );
1708 }
1709
1710 /* TODO: If MERROR_PREEMPT is not returned, remove args_s and args_e? */
1711
1712cleanup:
1713
1714#if MLISP_EXEC_TRACE_LVL > 0
1715 debug_printf( MLISP_EXEC_TRACE_LVL,
1716 "%u: xvxvxvxvxvxvx END STEP LAMBDA " SIZE_T_FMT " xvxvxvxvxvx",
1717 exec->uid, n_idx );
1718#endif /* MLISP_EXEC_TRACE_LVL */
1719
1720 /* Cleanup the passthrough note for this heartbeat. */
1721 mdata_vector_remove_last( &(exec->lambda_trace) );
1722
1723 return retval;
1724}
1725
1726/* === */
1727
1728static MERROR_RETVAL _mlisp_stack_cleanup(
1729 struct MLISP_PARSER* parser, size_t n_idx, struct MLISP_EXEC_STATE* exec
1730) {
1731 MERROR_RETVAL retval = MERROR_OK;
1732 ssize_t i = 0;
1733 struct MLISP_STACK_NODE o;
1734
1735 /* Pop elements off the stack until we hit the matching begin frame. */
1736 i = mdata_vector_ct( &(exec->stack) ) - 1;
1737 while( 0 <= i ) {
1738
1739 retval = mlisp_stack_pop( exec, &o );
1740 maug_cleanup_if_not_ok();
1741
1742 if( MLISP_TYPE_BEGIN == o.type && n_idx == o.value.begin ) {
1743 break;
1744 }
1745
1746 i--;
1747 }
1748
1749cleanup:
1750
1751 return retval;
1752}
1753
1754/* === */
1755
1760static MERROR_RETVAL _mlisp_eval_token_strpool(
1761 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
1762 size_t token_idx, size_t token_sz, struct MLISP_ENV_NODE* e_out
1763) {
1764 MERROR_RETVAL retval = MERROR_OK;
1765 struct MLISP_ENV_NODE* p_e = NULL;
1766 char* strpool_token = NULL;
1767
1768 /* Make sure we're sharing env context with our caller! */
1769 /* assert(
1770 mdata_table_is_locked( &(exec->env) ) ); */
1771 assert( /* Also make sure we're sharing ctx for global env if present! */
1772 NULL == exec->global_env ||
1773 mdata_table_is_locked( exec->global_env ) );
1774
1775 mdata_strpool_lock( &(parser->strpool) );
1776
1777 /* TODO: Use exec_state strpool. */
1778 strpool_token = mdata_strpool_get( &(parser->strpool), token_idx );
1779 assert( NULL != strpool_token );
1780
1781#if MLISP_EXEC_TRACE_LVL > 0
1782 debug_printf( MLISP_EXEC_TRACE_LVL,
1783 "%u: eval token: \"%s\" (strlen: " SIZE_T_FMT "r/" SIZE_T_FMT "d)",
1784 exec->uid, strpool_token, token_sz, maug_strlen( strpool_token ) );
1785#endif /* MLISP_EXEC_TRACE_LVL */
1786 if( 0 == strncmp( strpool_token, "begin", token_sz ) ) {
1787 /* Fake env node e to signal step_iter() to place/cleanup stack frame. */
1788 e_out->type = MLISP_TYPE_BEGIN;
1789
1790 } else if( NULL != (p_e = mlisp_env_get( exec, strpool_token ) ) ) {
1791 /* A literal found in the environment. */
1792#if MLISP_EXEC_TRACE_LVL > 0
1793 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: found %s in env!",
1794 exec->uid, strpool_token );
1795#endif /* MLISP_EXEC_TRACE_LVL */
1796
1797 /* Copy onto native stack so we can unlock env in case this is a
1798 * callback that needs to execute. */
1799 memcpy( e_out, p_e, sizeof( struct MLISP_ENV_NODE ) );
1800 p_e = NULL;
1801
1802 } else if( maug_is_num( strpool_token, token_sz, 10, 1 ) ) {
1803 /* Fake env node e from a numeric literal. */
1804#if MLISP_EXEC_TRACE_LVL > 0
1805 debug_printf( MLISP_EXEC_TRACE_LVL,
1806 "%u: did not find %s in env, but it is a number...",
1807 exec->uid, strpool_token );
1808#endif /* MLISP_EXEC_TRACE_LVL */
1809 e_out->value.integer = maug_atos32( strpool_token, token_sz );
1810 e_out->type = MLISP_TYPE_INT;
1811
1812 } else if( maug_is_float( strpool_token, token_sz ) ) {
1813#if MLISP_EXEC_TRACE_LVL > 0
1814 debug_printf( MLISP_EXEC_TRACE_LVL,
1815 "%u: did not find %s in env, but it is a float...",
1816 exec->uid, strpool_token );
1817#endif /* MLISP_EXEC_TRACE_LVL */
1818 /* Fake env node e from a floating point numeric literal. */
1819 e_out->value.floating = maug_atof( strpool_token, token_sz );
1820 e_out->type = MLISP_TYPE_FLOAT;
1821
1822 } else {
1823#if MLISP_EXEC_TRACE_LVL > 0
1824 error_printf( "%u: could not make sense of token: %s",
1825 exec->uid, strpool_token );
1826#endif /* MLISP_EXEC_TRACE_LVL */
1827
1828 }
1829
1830cleanup:
1831
1832 if( mdata_strpool_is_locked( &(parser->strpool) ) ) {
1833 mdata_strpool_unlock( &(parser->strpool) );
1834 }
1835
1836#if MLISP_EXEC_TRACE_LVL > 0
1837 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: eval token complete!",
1838 exec->uid );
1839#endif /* MLISP_EXEC_TRACE_LVL */
1840
1841 return retval;
1842}
1843
1844static MERROR_RETVAL _mlisp_step_iter(
1845 struct MLISP_PARSER* parser,
1846 size_t n_idx, struct MLISP_EXEC_STATE* exec
1847) {
1848 MERROR_RETVAL retval = MERROR_OK;
1849 struct MLISP_ENV_NODE e;
1850 struct MLISP_AST_NODE* n = NULL;
1851 size_t* p_visit_ct = NULL;
1852 mlisp_env_cb_t e_cb = NULL;
1853 uint8_t e_flags = 0;
1854 mlisp_lambda_t e_lambda = 0;
1855 int8_t env_iter = 0;
1856
1857 /* With -O2, gcc seems to sometimes(?) push an arbitrary integer to the
1858 * stack, unless we use this variable force it to pass the literal index.
1859 * This *seems* to resolve the issue.
1860 */
1861 volatile mdata_strpool_idx_t node_strpool_idx = 0;
1862
1863#ifdef MLISP_DEBUG_TRACE
1864 exec->trace[exec->trace_depth++] = n_idx;
1865 assert( exec->trace_depth <= MLISP_DEBUG_TRACE );
1866#endif /* MLISP_DEBUG_TRACE */
1867
1868 n = mdata_vector_get( &(parser->ast), n_idx, struct MLISP_AST_NODE );
1869
1870 assert( mdata_vector_is_locked( &(exec->per_node_visit_ct) ) );
1871 p_visit_ct = mdata_vector_get(
1872 &(exec->per_node_visit_ct), n_idx, size_t );
1873 assert( NULL != p_visit_ct );
1874 (*p_visit_ct)++;
1875#if MLISP_EXEC_TRACE_LVL > 0
1876 debug_printf( MLISP_EXEC_TRACE_LVL,
1877 "%u: visit count for AST node " SIZE_T_FMT ": " SIZE_T_FMT,
1878 exec->uid, n_idx, *p_visit_ct );
1879#endif /* MLISP_EXEC_TRACE_LVL */
1880
1881 /* Push a stack frame marker on the first visit to a BEGIN node. */
1882 if(
1883 MLISP_AST_FLAG_BEGIN == (MLISP_AST_FLAG_BEGIN & n->flags) &&
1884 1 == *p_visit_ct
1885 ) {
1886 /* Push a stack frame on first visit. */
1887 retval = _mlisp_stack_push_mlisp_begin_t( exec, n_idx );
1888 maug_cleanup_if_not_ok();
1889 }
1890
1891 if(
1892 MERROR_OK !=
1893 (retval = _mlisp_step_iter_children( parser, n_idx, exec ))
1894 ) {
1895 goto cleanup;
1896 }
1897
1898 /* Check for special types like lambda, that are lazily evaluated. */
1899 if( MLISP_AST_FLAG_LAMBDA == (MLISP_AST_FLAG_LAMBDA & n->flags) ) {
1900 /* Push the lambda to the stack so that the "define" above it can
1901 * grab it and associate it with the env.
1902 */
1903 /* TODO: Assert node above it is a define! */
1904 mlisp_stack_push( exec, n_idx, mlisp_lambda_t );
1905 goto cleanup;
1906 }
1907
1908 /* Now that the children have been evaluated above, evaluate this node.
1909 * Assume all the previously called children are now on the stack.
1910 */
1911
1912 /* Lock the env so we can grab the token from it and evalauate it below
1913 * in one swoop without an unlock.
1914 */
1915 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
1916#if MLISP_EXEC_TRACE_LVL > 0
1917 debug_printf( MLISP_EXEC_TRACE_LVL,
1918 "%u: locking local env %d...", exec->uid, env_iter );
1919#endif /* MLISP_EXEC_TRACE_LVL */
1920 mdata_table_lock( &(exec->env[env_iter]) );
1921 }
1922
1923 assert(
1924 NULL == exec->global_env || !mdata_table_is_locked( exec->global_env ) );
1925 if( NULL != exec->global_env ) {
1926 mdata_table_lock( exec->global_env );
1927 }
1928
1929 /* Grab the token for this node and figure out what it is. */
1930 retval = _mlisp_eval_token_strpool(
1931 parser, exec, n->token_idx, n->token_sz, &e );
1932 maug_cleanup_if_not_ok();
1933
1934 /* Prepare to step. */
1935
1936#if MLISP_EXEC_TRACE_LVL > 0
1937 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: acting on evaluated token...",
1938 exec->uid );
1939#endif /* MLISP_EXEC_TRACE_LVL */
1940
1941 /* Put the token or its result (if callable) on the stack. */
1942# define _MLISP_TYPE_TABLE_ENVE( idx, ctype, name, const_name, fmt ) \
1943 } else if( MLISP_TYPE_ ## const_name == e.type ) { \
1944 debug_printf( MLISP_EXEC_TRACE_LVL, \
1945 "%u: pushing env: " fmt " to stack...", \
1946 exec->uid, e.value.name ); \
1947 retval = _mlisp_stack_push_ ## ctype( exec, e.value.name ); \
1948 maug_cleanup_if_not_ok();
1949
1950 if( MLISP_EXEC_FLAG_DEF_TERM == (MLISP_EXEC_FLAG_DEF_TERM & exec->flags) ) {
1951 /* Avoid a deadlock when *re*-assigning terms caused by term being
1952 * evaluated before it is defined.
1953 */
1954#if MLISP_EXEC_TRACE_LVL > 0
1955 debug_printf( MLISP_EXEC_TRACE_LVL,
1956 "%u: special case! pushing literal to stack: " SSIZE_T_FMT,
1957 exec->uid, n->token_idx );
1958#endif /* MLISP_EXEC_TRACE_LVL */
1959 node_strpool_idx = n->token_idx;
1960 retval = _mlisp_stack_push_mdata_strpool_idx_t( exec, node_strpool_idx );
1961 maug_cleanup_if_not_ok();
1962 } else if( MLISP_TYPE_BEGIN == e.type ) {
1963 /* Cleanup the stack that's been pushed by children since this BEGIN's
1964 * initial visit.
1965 */
1966#if MLISP_EXEC_TRACE_LVL > 0
1967 debug_printf( MLISP_EXEC_TRACE_LVL,
1968 "%u: rewinding stack for begin on node " SSIZE_T_FMT,
1969 exec->uid, n_idx );
1970#endif /* MLISP_EXEC_TRACE_LVL */
1971 retval = _mlisp_stack_cleanup( parser, n_idx, exec );
1972 maug_cleanup_if_not_ok();
1973
1974 /* Push a replacement BEGIN that can be caught later and throw an
1975 * MERROR_RESET.
1976 */
1977 retval = _mlisp_stack_push_mlisp_begin_t( exec, n_idx );
1978 maug_cleanup_if_not_ok();
1979
1980 } else if( MLISP_TYPE_CB == e.type ) {
1981 /* This is a special case... rather than pushing the callback, *execute*
1982 * it and let it push its result to the stack. This will create a
1983 * redundant case below, but that can't be helped...
1984 */
1985
1986#if MLISP_EXEC_TRACE_LVL > 0
1987 debug_printf( MLISP_EXEC_TRACE_LVL,
1988 "%u: special case! executing callback: %p", exec->uid, e_cb );
1989#endif /* MLISP_EXEC_TRACE_LVL */
1990
1991 /* Unlock the env so the callback below can use it if needed. */
1992 e_cb = e.value.cb;
1993 e_flags = e.flags;
1994 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
1995 mdata_table_unlock( &(exec->env[env_iter]) );
1996 }
1997 if( NULL != exec->global_env ) {
1998 mdata_table_unlock( exec->global_env );
1999 }
2000
2001 retval = e_cb(
2002 parser, exec, n_idx, n->ast_idx_children_sz, NULL, e_flags );
2003
2004 /* Relock it for the benefit of the unlock in cleanup. */
2005 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2006 mdata_table_lock( &(exec->env[env_iter]) );
2007 }
2008 if( NULL != exec->global_env ) {
2009 mdata_table_lock( exec->global_env );
2010 }
2011
2012 } else if( MLISP_TYPE_LAMBDA == e.type ) {
2013
2014#if MLISP_EXEC_TRACE_LVL > 0
2015 debug_printf( MLISP_EXEC_TRACE_LVL,
2016 "%u: special case! executing lambda...", exec->uid );
2017#endif /* MLISP_EXEC_TRACE_LVL */
2018
2019 /* Create a "portal" into the lambda. The execution chain stays pointing
2020 * to this lambda-call node, but _mlisp_step_lambda() returns
2021 * MERROR_PREEMPT up the chain for subsequent heartbeats, until lambda is
2022 * done.
2023 */
2024 e_lambda = e.value.lambda;
2025 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2026 mdata_table_unlock( &(exec->env[env_iter]) );
2027 }
2028 if( NULL != exec->global_env ) {
2029 mdata_table_unlock( exec->global_env );
2030 }
2031
2032 retval = _mlisp_step_lambda( parser, e_lambda, exec );
2033
2034 /* Relock it for the benefit of the unlock in cleanup. */
2035 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2036 mdata_table_lock( &(exec->env[env_iter]) );
2037 }
2038 if( NULL != exec->global_env ) {
2039 mdata_table_lock( exec->global_env );
2040 }
2041
2042 MLISP_TYPE_TABLE( _MLISP_TYPE_TABLE_ENVE )
2043 } else {
2044#if MLISP_EXEC_TRACE_LVL > 0
2045 debug_printf( MLISP_EXEC_TRACE_LVL, "pushing literal into stack" );
2046#endif /* !MLISP_EXEC_TRACE_LVL */
2047 retval = _mlisp_stack_push_mdata_strpool_idx_t( exec, n->token_idx );
2048 maug_cleanup_if_not_ok();
2049 }
2050
2051cleanup:
2052
2053 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2054 mdata_table_unlock( &(exec->env[env_iter]) );
2055 }
2056
2057 if( NULL != exec->global_env ) {
2058 mdata_table_unlock( exec->global_env );
2059 }
2060
2061 return retval;
2062}
2063
2064/* === */
2065
2066static MERROR_RETVAL _mlisp_count_builtins_iter(
2067 const struct MDATA_TABLE_KEY* key, void* data, size_t data_sz,
2068 void* cb_data, size_t cb_data_sz, size_t idx
2069) {
2070 MERROR_RETVAL retval = MERROR_OK;
2071 struct MLISP_ENV_NODE* e = (struct MLISP_ENV_NODE*)data;
2072 ssize_t* p_builtins = (ssize_t*)cb_data;
2073
2074 if( MLISP_ENV_FLAG_BUILTIN == (MLISP_ENV_FLAG_BUILTIN & e->flags) ) {
2075 (*p_builtins)++;
2076 }
2077
2078 return retval;
2079}
2080
2081/* === */
2082
2083ssize_t mlisp_count_builtins( struct MLISP_EXEC_STATE* exec ) {
2084 MERROR_RETVAL retval = MERROR_OK;
2085 ssize_t builtins = 0;
2086 int autolock = 0;
2087
2088 if( 0 == mdata_table_ct( &(exec->env[0]) ) ) {
2089 goto cleanup;
2090 }
2091
2092 if( !mdata_table_is_locked( &(exec->env[0]) ) ) {
2093 mdata_table_lock( &(exec->env[0]) );
2094 autolock = 1;
2095 }
2096
2097 retval = mdata_table_iter(
2098 &(exec->env[0]), _mlisp_count_builtins_iter, &builtins, 0 );
2099
2100cleanup:
2101
2102 if( MERROR_OK != retval ) {
2103 builtins = merror_retval_to_sz( retval );
2104 }
2105
2106 if( autolock ) {
2107 mdata_table_unlock( &(exec->env[0]) );
2108 }
2109
2110 return builtins;
2111}
2112
2113/* === */
2114
2115MERROR_RETVAL mlisp_check_state(
2116 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
2117) {
2118 MERROR_RETVAL retval = MERROR_OK;
2119
2120 if( !mlisp_check_ast( parser ) ) {
2121 error_printf( "no valid AST present; could not exec!" );
2122 retval = MERROR_EXEC;
2123 goto cleanup;
2124 }
2125
2126 if(
2127 MLISP_EXEC_FLAG_INITIALIZED != (exec->flags & MLISP_EXEC_FLAG_INITIALIZED)
2128 ) {
2129 retval = MERROR_EXEC;
2130 goto cleanup;
2131 }
2132
2133cleanup:
2134
2135 return retval;
2136}
2137
2138/* === */
2139
2141 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
2142) {
2143 MERROR_RETVAL retval = MERROR_OK;
2144#ifdef MLISP_DEBUG_TRACE
2145 size_t i = 0;
2146 char trace_str[MLISP_DEBUG_TRACE * 5];
2147 maug_ms_t ms_start = 0;
2148 maug_ms_t ms_end = 0;
2149
2150 ms_start = retroflat_get_ms();
2151#endif /* MLISP_DEBUG_TRACE */
2152
2153#if MLISP_EXEC_TRACE_LVL > 0
2154 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: heartbeat start", exec->uid );
2155#endif /* MLISP_EXEC_TRACE_LVL */
2156
2157 /* These can remain locked for the whole step, as they're never added or
2158 * removed.
2159 */
2160 assert( !mdata_vector_is_locked( &(exec->per_node_child_idx) ) );
2161 assert( !mdata_vector_is_locked( &(exec->per_node_visit_ct) ) );
2162 assert( !mdata_vector_is_locked( &(parser->ast) ) );
2163 mdata_vector_lock( &(exec->per_node_child_idx) );
2164 mdata_vector_lock( &(exec->per_node_visit_ct) );
2165 mdata_vector_lock( &(parser->ast) );
2166
2167 /* Disable transient flags. */
2168 exec->flags &= MLISP_EXEC_FLAG_TRANSIENT_MASK;
2169 assert( 0 == mdata_vector_ct( &(exec->lambda_trace) ) );
2170
2171#ifdef MLISP_DEBUG_TRACE
2172 exec->trace_depth = 0;
2173#endif /* MLISP_DEBUG_TRACE */
2174
2175 /* Find next unevaluated symbol. */
2176 retval = _mlisp_step_iter( parser, 0, exec );
2177 if( MERROR_PREEMPT == retval ) {
2178 /* There's still more to execute. */
2179 retval = MERROR_OK;
2180 } else if( MERROR_OK == retval ) {
2181 /* The last node executed completely. */
2182#if MLISP_EXEC_TRACE_LVL > 0
2183 debug_printf( MLISP_EXEC_TRACE_LVL,
2184 "%u: execution terminated successfully", exec->uid );
2185#endif /* MLISP_EXEC_TRACE_LVL */
2186 retval = MERROR_EXEC; /* Signal the caller: we're out of instructions! */
2187#if MLISP_EXEC_TRACE_LVL > 0
2188 } else {
2189 debug_printf( MLISP_EXEC_TRACE_LVL,
2190 "%u: execution terminated with retval: %d", exec->uid, retval );
2191#endif /* MLISP_EXEC_TRACE_LVL */
2192 }
2193
2194#ifdef MLISP_DEBUG_TRACE
2195 ms_end = retroflat_get_ms();
2196
2197 maug_mzero( trace_str, MLISP_DEBUG_TRACE * 5 );
2198 for( i = 0 ; exec->trace_depth > i ; i++ ) {
2199 maug_snprintf(
2200 &(trace_str[maug_strlen( trace_str )]),
2201 (MLISP_DEBUG_TRACE * 5) - maug_strlen( trace_str ),
2202 SIZE_T_FMT ", ", exec->trace[i] );
2203 }
2204#if MLISP_EXEC_TRACE_LVL > 0
2205 debug_printf( MLISP_EXEC_TRACE_LVL,
2206 "%u: " MLISP_TRACE_SIGIL " HBEXEC (%u): %s",
2207 exec->uid, ms_end - ms_start, trace_str );
2208#endif /* MLISP_EXEC_TRACE_LVL */
2209#endif /* MLISP_DEBUG_TRACE */
2210
2211cleanup:
2212
2213#if MLISP_EXEC_TRACE_LVL > 0
2214 debug_printf( MLISP_EXEC_TRACE_LVL,
2215 "%u: heartbeat end: %x", exec->uid, retval );
2216#endif /* MLISP_EXEC_TRACE_LVL */
2217
2218 assert( mdata_vector_is_locked( &(parser->ast) ) );
2219 mdata_vector_unlock( &(parser->ast) );
2220 mdata_vector_unlock( &(exec->per_node_visit_ct) );
2221 mdata_vector_unlock( &(exec->per_node_child_idx) );
2222
2223 return retval;
2224}
2225
2226/* === */
2227
2229 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
2230 const char* lambda
2231) {
2232 struct MLISP_ENV_NODE* e = NULL;
2233 MERROR_RETVAL retval = MERROR_OK;
2234 uint8_t autolock[MLISP_EXEC_ENV_FRAME_CT_MAX];
2235 mlisp_lambda_t lambda_idx = 0;
2236 struct MLISP_AST_NODE* n = NULL;
2237 int8_t env_iter = 0;
2238
2239 if( MERROR_OK != mlisp_check_state( parser, exec ) ) {
2240 error_printf( "mlisp not ready!" );
2241 retval = MERROR_EXEC;
2242 goto cleanup;
2243 }
2244
2245 retval = _mlisp_autolock( parser, exec, 0xff, autolock );
2246 maug_cleanup_if_not_ok();
2247
2248 /* Find the AST node for the lambda. */
2249 e = mlisp_env_get( exec, lambda );
2250 if( NULL == e ) {
2251 error_printf( "lambda \"%s\" not found!", lambda );
2252 retval = MERROR_OVERFLOW;
2253 goto cleanup;
2254 }
2255 lambda_idx = e->value.lambda;
2256
2257 /* Autounlock just env so _mlisp_step_lambda() works. */
2258 /* We use autolock with the env minimally to avoid passing around bad
2259 * pointers.
2260 */
2261 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2262 if( MLISP_AUTOLOCK_EXEC_ENV == (MLISP_AUTOLOCK_EXEC_ENV & autolock[0]) ) {
2263 mdata_table_unlock( &(exec->env[env_iter]) );
2264 autolock[env_iter] &= ~MLISP_AUTOLOCK_EXEC_ENV;
2265 }
2266 }
2267 if(
2268 MLISP_AUTOLOCK_GLOBAL_ENV == (MLISP_AUTOLOCK_GLOBAL_ENV & autolock[0])
2269 ) {
2270 mdata_table_unlock( exec->global_env );
2271 autolock[0] &= ~MLISP_AUTOLOCK_GLOBAL_ENV;
2272 }
2273
2274#if MLISP_EXEC_TRACE_LVL > 0
2275 debug_printf( MLISP_EXEC_TRACE_LVL, "%u: lambda \"%s\" is AST node idx %ld",
2276 exec->uid, lambda, lambda_idx );
2277#endif /* MLISP_EXEC_TRACE_LVL */
2278
2279 n = mdata_vector_get( &(parser->ast), lambda_idx, struct MLISP_AST_NODE );
2280 assert( MLISP_AST_FLAG_LAMBDA == (MLISP_AST_FLAG_LAMBDA & n->flags) );
2281
2282 /* Jump execution to the lambda on next iter. */
2283 retval = _mlisp_step_lambda( parser, lambda_idx, exec );
2284
2285cleanup:
2286
2287 _mlisp_autounlock( parser, exec, autolock );
2288
2289 return retval;
2290}
2291
2292/* === */
2293
2294MERROR_RETVAL mlisp_exec_add_env_builtins(
2295 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec
2296) {
2297 MERROR_RETVAL retval = MERROR_OK;
2298
2299 retval = mlisp_env_set(
2300 exec, "gdefine", 7, MLISP_TYPE_CB, _mlisp_env_cb_define,
2301 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_DEFINE_GLOBAL );
2302 maug_cleanup_if_not_ok();
2303
2304 retval = mlisp_env_set(
2305 exec, "and", 3, MLISP_TYPE_CB, _mlisp_env_cb_ano,
2306 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ANO_AND );
2307 maug_cleanup_if_not_ok();
2308
2309 retval = mlisp_env_set(
2310 exec, "or", 2, MLISP_TYPE_CB, _mlisp_env_cb_ano,
2311 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ANO_OR );
2312 maug_cleanup_if_not_ok();
2313
2314#ifndef MAUG_NO_RETRO
2315/* TODO: Call this in retroflat in line with dependency guidelines. */
2316 retval = mlisp_env_set(
2317 exec, "random", 6, MLISP_TYPE_CB, _mlisp_env_cb_random,
2318 0, MLISP_ENV_FLAG_BUILTIN );
2319 maug_cleanup_if_not_ok();
2320#endif /* !MAUG_NO_RETRO */
2321
2322 retval = mlisp_env_set(
2323 exec, "if", 2, MLISP_TYPE_CB, _mlisp_env_cb_if,
2324 0, MLISP_ENV_FLAG_BUILTIN );
2325 maug_cleanup_if_not_ok();
2326
2327 retval = mlisp_env_set(
2328 exec, "define", 6, MLISP_TYPE_CB, _mlisp_env_cb_define,
2329 0, MLISP_ENV_FLAG_BUILTIN );
2330 maug_cleanup_if_not_ok();
2331
2332 retval = mlisp_env_set(
2333 exec, "*", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2334 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_MUL );
2335 maug_cleanup_if_not_ok();
2336
2337 retval = mlisp_env_set(
2338 exec, "+", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2339 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_ADD );
2340 maug_cleanup_if_not_ok();
2341
2342 retval = mlisp_env_set(
2343 exec, "/", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2344 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_DIV );
2345 maug_cleanup_if_not_ok();
2346
2347 retval = mlisp_env_set(
2348 exec, "%", 1, MLISP_TYPE_CB, _mlisp_env_cb_arithmetic,
2349 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_ARI_MOD );
2350 maug_cleanup_if_not_ok();
2351
2352 retval = mlisp_env_set(
2353 exec, "<", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2354 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_LT );
2355 maug_cleanup_if_not_ok();
2356
2357 retval = mlisp_env_set(
2358 exec, ">", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2359 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_GT );
2360 maug_cleanup_if_not_ok();
2361
2362 retval = mlisp_env_set(
2363 exec, "=", 1, MLISP_TYPE_CB, _mlisp_env_cb_cmp,
2364 0, MLISP_ENV_FLAG_BUILTIN | MLISP_ENV_FLAG_CMP_EQ );
2365 maug_cleanup_if_not_ok();
2366
2367cleanup:
2368
2369 return retval;
2370}
2371
2372/* === */
2373
2374MERROR_RETVAL mlisp_exec_init(
2375 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec, uint8_t flags
2376) {
2377 MERROR_RETVAL retval = MERROR_OK;
2378 ssize_t append_retval = 0;
2379 size_t zero = 0;
2380 int16_t null_val = 0;
2381
2382 assert( 0 == exec->flags );
2383
2384 maug_mzero( exec, sizeof( struct MLISP_EXEC_STATE ) );
2385
2386 exec->flags = flags;
2387 exec->uid = g_mlispe_last_uid++;
2388
2389 /* Setup lambda visit stack so it can be locked on first step. */
2390 append_retval = mdata_vector_append(
2391 &(exec->lambda_trace), &zero, sizeof( size_t ) );
2392 if( 0 > append_retval ) {
2393 retval = mdata_retval( append_retval );
2394 }
2395 maug_cleanup_if_not_ok();
2396 mdata_vector_remove_last( &(exec->lambda_trace) );
2397
2398 /* Define a constant so that the table is never empty, which makes things
2399 * run more smoothly.
2400 */
2401 retval = mlisp_env_set(
2402 exec, "null", 4, MLISP_TYPE_INT, &null_val, 0, 0 );
2403
2404 /* Create the node PCs. */
2405 append_retval = mdata_vector_append(
2406 &(exec->per_node_child_idx), &zero, sizeof( size_t ) );
2407 if( 0 > append_retval ) {
2408 retval = mdata_retval( append_retval );
2409 }
2410 maug_cleanup_if_not_ok();
2411
2412 /* Make sure there's an exec child node for every AST node. */
2413 while(
2414 mdata_vector_ct( &(exec->per_node_child_idx) ) <=
2415 mdata_vector_ct( &(parser->ast) )
2416 ) {
2417 append_retval = mdata_vector_append( &(exec->per_node_child_idx), &zero,
2418 sizeof( size_t ) );
2419 if( 0 > append_retval ) {
2420 retval = mdata_retval( append_retval );
2421 }
2422 maug_cleanup_if_not_ok();
2423 }
2424
2425 /* Create the node visit counters. */
2426 append_retval = mdata_vector_append(
2427 &(exec->per_node_visit_ct), &zero, sizeof( size_t ) );
2428 if( 0 > append_retval ) {
2429 retval = mdata_retval( append_retval );
2430 }
2431 maug_cleanup_if_not_ok();
2432
2433 /* Make sure there's an exec visit count for every AST node. */
2434 while(
2435 mdata_vector_ct( &(exec->per_node_visit_ct) ) <=
2436 mdata_vector_ct( &(parser->ast) )
2437 ) {
2438 append_retval = mdata_vector_append( &(exec->per_node_visit_ct), &zero,
2439 sizeof( size_t ) );
2440 if( 0 > append_retval ) {
2441 retval = mdata_retval( append_retval );
2442 }
2443 maug_cleanup_if_not_ok();
2444 }
2445
2446 exec->flags |= MLISP_EXEC_FLAG_INITIALIZED;
2447
2448 /* Setup initial env. */
2449
2450 retval = mlisp_exec_add_env_builtins( parser, exec );
2451
2452cleanup:
2453
2454 if( MERROR_OK != retval ) {
2455 error_printf( "mlisp exec initialization failed: %d", retval );
2456 }
2457
2458 return retval;
2459}
2460
2461/* === */
2462
2464 struct MLISP_PARSER* parser, struct MLISP_EXEC_STATE* exec,
2465 struct MDATA_TABLE* global_env
2466) {
2467 MERROR_RETVAL retval = MERROR_OK;
2468 int16_t null_val = 0;
2469
2470 exec->global_env = global_env;
2471
2472 if( 0 == mdata_table_ct( global_env ) ) {
2473 /* Things get very wonky if the env is completely empty due to how empty
2474 * vectors respond to locking. This is a simple way of working around
2475 * that rather than adding a lot of special cases!
2476 */
2477 retval = mlisp_env_set(
2478 exec, "null", 4, MLISP_TYPE_INT, &null_val, 1, 0 );
2479 }
2480
2481 return retval;
2482}
2483
2484/* === */
2485
2486void mlisp_exec_free( struct MLISP_EXEC_STATE* exec ) {
2487 int8_t env_iter = 0;
2488
2489#if MLISP_EXEC_TRACE_LVL > 0
2490 debug_printf( MLISP_EXEC_TRACE_LVL,
2491 "%u: destroying exec (stack: " SIZE_T_FMT ", env: " SIZE_T_FMT ")...",
2492 exec->uid,
2493 mdata_vector_ct( &(exec->stack) ),
2494 mdata_table_ct( &(exec->env[exec->env_select]) ) );
2495#endif /* MLISP_EXEC_TRACE_LVL */
2496 mdata_vector_free( &(exec->per_node_child_idx) );
2497 mdata_vector_free( &(exec->per_node_visit_ct) );
2498 mdata_vector_free( &(exec->stack) );
2499 for( env_iter = exec->env_select ; 0 <= env_iter ; env_iter-- ) {
2500 mdata_table_free( &(exec->env[env_iter]) );
2501 }
2502 mdata_vector_free( &(exec->lambda_trace) );
2503 exec->flags = 0;
2504#if MLISP_EXEC_TRACE_LVL > 0
2505 debug_printf( MLISP_EXEC_TRACE_LVL, "exec destroyed!" );
2506#endif /* MLISP_EXEC_TRACE_LVL */
2507}
2508
2509/* === */
2510
2511MERROR_RETVAL mlisp_deserialize_prepare_EXEC_STATE(
2512 struct MLISP_EXEC_STATE* exec, size_t i
2513) {
2514 MERROR_RETVAL retval = MERROR_OK;
2515 /* TODO: Re-add built-in function definitions. */
2516 /* TODO: Provide mechanism for program using maug to re-add function
2517 * definitions!
2518 */
2519 return retval;
2520}
2521
2522#else
2523
2524# define MLISP_PSTATE_TABLE_CONST( name, idx ) \
2525 extern MAUG_CONST uint8_t SEG_MCONST name;
2526
2527MLISP_PARSER_PSTATE_TABLE( MLISP_PSTATE_TABLE_CONST )
2528
2529#ifdef MPARSER_TRACE_NAMES
2530extern MAUG_CONST char* SEG_MCONST gc_mlisp_pstate_names[];
2531#endif /* MPARSER_TRACE_NAMES */
2532
2533#endif /* MLISPE_C */
2534
2535#endif /* !MLISPE_H */
2536
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define MERROR_PREEMPT
Indicates MLISP_AST_NODE can be executed again on next step iter pass.
Definition merror.h:58
#define MERROR_RESET
Indicates MLISP_EXEC_STATE has reached a condition where it has run out of instructions.
Definition merror.h:64
MAUG_MHANDLE mdata_strpool_extract(struct MDATA_STRPOOL *sp, mdata_strpool_idx_t idx)
Return a dynamically-allocated memory handle containing the contents of the string at the given index...
#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_dump(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec)
Dump the stack from the given parser/exec combination.
#define mlisp_stack_pop(exec, o)
Wrapper for mlisp_stack_pop() with no flags.
Definition mlispe.h:107
#define MLISP_STACK_FLAG_PEEK
Flag for mlisp_stack_pop_ex() indicating the value should not be removed from the stack.
Definition mlispe.h:102
#define mlisp_stack_push(exec, i, ctype)
Push a value onto MLISP_EXEC_STATE::stack.
Definition mlispe.h:117
MERROR_RETVAL mlisp_stack_pop_ex(struct MLISP_EXEC_STATE *exec, struct MLISP_STACK_NODE *o, uint8_t flags)
Pop a value off of (removing from) MLISP_EXEC_STATE::stack and copy it to a provided output.
#define MLISP_TYPE_TABLE(f)
Table of other types.
Definition mlisps.h:74
#define MLISP_NUM_TYPE_TABLE(f)
Table of numeric types.
Definition mlisps.h:64
MERROR_RETVAL mlisp_exec_set_global_env(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec, struct MDATA_TABLE *global_env)
Set the given exec state to use the given vector as a global variable environment....
#define MLISP_ENV_FLAG_CMP_GT
Flag for _mlisp_env_cb_cmp() specifying TRUE if A > B.
Definition mlispe.h:58
MERROR_RETVAL(* mlisp_env_cb_t)(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec, size_t n_idx, size_t args_c, uint8_t *cb_data, uint8_t flags)
A callback to attach to an mlisp command with mlisp_env_set() with MLISP_TYPE_CB.
Definition mlisps.h:92
#define MLISP_ENV_FLAG_ARI_MUL
Flag for _mlisp_env_cb_arithmetic() specifying to multiply A * B.
Definition mlispe.h:70
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:67
#define MLISP_ENV_FLAG_CMP_EQ
Flag for _mlisp_env_cb_cmp() specifying TRUE if A == B.
Definition mlispe.h:64
MERROR_RETVAL mlisp_env_dump(struct MLISP_PARSER *parser, struct MLISP_EXEC_STATE *exec, uint8_t global)
Dump the environment from the given parser/exec combination.
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:61
#define MLISP_ENV_FLAG_DEFINE_GLOBAL
Flag for _mlisp_env_cb_define() specifying global env.
Definition mlispe.h:81
MLISP Interpreter/Parser Structs.
Definition mdata.h:121
Definition mdata.h:127
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:107
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:184
struct MDATA_VECTOR per_node_child_idx
The hild index that will be visited on next visit of each node.
Definition mlisps.h:151
struct MDATA_VECTOR per_node_visit_ct
The number of times each node has been visited ever.
Definition mlisps.h:143
struct MDATA_VECTOR stack
A stack of data values resulting from evaluating statements.
Definition mlisps.h:154
uint8_t flags
Flags which dictate the behavior of this object.
Definition mlisps.h:140
struct MDATA_TABLE env[MLISP_EXEC_ENV_FRAME_CT_MAX]
Environment in which statements are defined if ::MLISP_.
Definition mlisps.h:171
int8_t env_select
The current topmost frame of MLISP_EXEC_STATE::env. Please see that for more information.
Definition mlisps.h:176
Definition mlisps.h:199
Definition mlisps.h:113