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