Post Scarcity
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
lispops.c
Go to the documentation of this file.
1/*
2 * lispops.c
3 *
4 * List processing operations.
5 *
6 * The general idea here is that a list processing operation is a
7 * function which takes two arguments, both cons_pointers:
8 *
9 * 1. args, the argument list to this function;
10 * 2. env, the environment in which this function should be evaluated;
11 *
12 * and returns a cons_pointer, the result.
13 *
14 * They must all have the same signature so that I can call them as
15 * function pointers.
16 *
17 * (c) 2017 Simon Brooke <simon@journeyman.cc>
18 * Licensed under GPL version 2.0, or, at your option, any later version.
19 */
20
21#include <ctype.h>
22#include <stdbool.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "arith/integer.h"
28#include "arith/peano.h"
29#include "debug.h"
30#include "io/io.h"
31#include "io/print.h"
32#include "io/read.h"
33#include "memory/conspage.h"
35#include "memory/stack.h"
36#include "memory/vectorspace.h"
37#include "memory/dump.h"
38#include "ops/equal.h"
39#include "ops/intern.h"
40#include "ops/lispops.h"
41
42/**
43 * @brief the name of the symbol to which the prompt is bound;
44 *
45 * Set in init to `*prompt*`
46 */
48
49/*
50 * also to create in this section:
51 * struct cons_pointer lisp_let( struct cons_pointer args, struct cons_pointer env,
52 * struct stack_frame* frame);
53 *
54 * and others I haven't thought of yet.
55 */
56
57/**
58 * Useful building block; evaluate this single form in the context of this
59 * parent stack frame and this environment.
60 * @param parent the parent stack frame.
61 * @param form the form to be evaluated.
62 * @param env the evaluation environment.
63 * @return the result of evaluating the form.
64 */
65struct cons_pointer eval_form( struct stack_frame *parent,
66 struct cons_pointer parent_pointer,
67 struct cons_pointer form,
68 struct cons_pointer env ) {
69 debug_print( L"eval_form: ", DEBUG_EVAL );
72
73 struct cons_pointer result = form;
74 switch ( pointer2cell( form ).tag.value ) {
75 /* things which evaluate to themselves */
76 case EXCEPTIONTV:
77 case FREETV: // shouldn't happen, but anyway...
78 case INTEGERTV:
79 case KEYTV:
80 case LOOPTV: // don't think this should happen...
81 case NILTV:
82 case RATIOTV:
83 case REALTV:
84 case READTV:
85 case STRINGTV:
86 case TIMETV:
87 case TRUETV:
88 case WRITETV:
89 break;
90 default:
91 {
92 struct cons_pointer next_pointer =
93 make_empty_frame( parent_pointer );
94 // inc_ref( next_pointer );
95
96 struct stack_frame *next = get_stack_frame( next_pointer );
97 set_reg( next, 0, form );
98 next->args = 1;
99
100 result = lisp_eval( next, next_pointer, env );
101
102 if ( !exceptionp( result ) ) {
103 /* if we're returning an exception, we should NOT free the
104 * stack frame. Corollary is, when we free an exception, we
105 * should free all the frames it's holding on to. */
106 dec_ref( next_pointer );
107 }
108 }
109 break;
110 }
111
112 debug_print( L"eval_form ", DEBUG_EVAL );
114 debug_print( L" returning: ", DEBUG_EVAL );
117
118 return result;
119}
120
121/**
122 * Evaluate all the forms in this `list` in the context of this stack `frame`
123 * and this `env`, and return a list of their values. If the arg passed as
124 * `list` is not in fact a list, return NIL.
125 * @param frame the stack frame.
126 * @param list the list of forms to be evaluated.
127 * @param env the evaluation environment.
128 * @return a list of the the results of evaluating the forms.
129 */
130struct cons_pointer eval_forms( struct stack_frame *frame,
131 struct cons_pointer frame_pointer,
132 struct cons_pointer list,
133 struct cons_pointer env ) {
134 struct cons_pointer result = NIL;
135
136 while ( consp( list ) ) {
137 result =
138 make_cons( eval_form( frame, frame_pointer, c_car( list ), env ),
139 result );
140 list = c_cdr( list );
141 }
142
143 return c_reverse( result );
144}
145
146/**
147 * OK, the idea here (and I know this is less than perfect) is that the basic `try`
148 * special form in PSSE takes two arguments, the first, `body`, being a list of forms,
149 * and the second, `catch`, being a catch handler (which is also a list of forms).
150 * Forms from `body` are evaluated in turn until one returns an exception object,
151 * or until the list is exhausted. If the list was exhausted, then the value of
152 * evaluating the last form in `body` is returned. If an exception was encountered,
153 * then each of the forms in `catch` is evaluated and the value of the last of
154 * those is returned.
155 *
156 * This is experimental. It almost certainly WILL change.
157 */
158struct cons_pointer lisp_try( struct stack_frame *frame,
159 struct cons_pointer frame_pointer,
160 struct cons_pointer env ) {
161 struct cons_pointer result =
162 c_progn( frame, frame_pointer, frame->arg[0], env );
163
164 if ( exceptionp( result ) ) {
165 // TODO: need to put the exception into the environment!
166 result = c_progn( frame, frame_pointer, frame->arg[1],
169 ( L"*exception*" ), result ), env ) );
170 }
171
172 return result;
173}
174
175
176/**
177 * Return the object list (root namespace).
178 *
179 * * (oblist)
180 *
181 * @param frame the stack frame in which the expression is to be interpreted;
182 * @param frame_pointer a pointer to my stack_frame.
183 * @param env my environment (ignored).
184 * @return the root namespace.
185 */
186struct cons_pointer
187lisp_oblist( struct stack_frame *frame, struct cons_pointer frame_pointer,
188 struct cons_pointer env ) {
189 return oblist;
190}
191
192/**
193 * Used to construct the body for `lambda` and `nlambda` expressions.
194 */
195struct cons_pointer compose_body( struct stack_frame *frame ) {
196 struct cons_pointer body = frame->more;
197
198 for ( int i = args_in_frame - 1; i > 0; i-- ) {
199 if ( !nilp( body ) ) {
200 body = make_cons( frame->arg[i], body );
201 } else if ( !nilp( frame->arg[i] ) ) {
202 body = make_cons( frame->arg[i], body );
203 }
204 }
205
206 debug_print( L"compose_body returning ", DEBUG_LAMBDA );
208
209 return body;
210}
211
212/**
213 * Construct an interpretable function. *NOTE* that if `args` is a single symbol
214 * rather than a list, a varargs function will be created.
215 *
216 * (lambda args body)
217 *
218 * @param frame the stack frame in which the expression is to be interpreted;
219 * @param frame_pointer a pointer to my stack_frame.
220 * @param env the environment in which it is to be intepreted.
221 * @return an interpretable function with these `args` and this `body`.
222 */
223struct cons_pointer
224lisp_lambda( struct stack_frame *frame, struct cons_pointer frame_pointer,
225 struct cons_pointer env ) {
226 return make_lambda( frame->arg[0], compose_body( frame ) );
227}
228
229/**
230 * Construct an interpretable special form. *NOTE* that if `args` is a single symbol
231 * rather than a list, a varargs special form will be created.
232 *
233 * (nlambda args body)
234 *
235 * @param frame the stack frame in which the expression is to be interpreted;
236 * @param frame_pointer a pointer to my stack_frame.
237 * @param env the environment in which it is to be intepreted.
238 * @return an interpretable special form with these `args` and this `body`.
239 */
240struct cons_pointer
241lisp_nlambda( struct stack_frame *frame, struct cons_pointer frame_pointer,
242 struct cons_pointer env ) {
243 return make_nlambda( frame->arg[0], compose_body( frame ) );
244}
245
246
247/**
248 * Evaluate a lambda or nlambda expression.
249 */
250struct cons_pointer
251eval_lambda( struct cons_space_object cell, struct stack_frame *frame,
252 struct cons_pointer frame_pointer, struct cons_pointer env ) {
253 struct cons_pointer result = NIL;
254#ifdef DEBUG
255 debug_print( L"eval_lambda called\n", DEBUG_LAMBDA );
257#endif
258
259 struct cons_pointer new_env = env;
260 struct cons_pointer names = cell.payload.lambda.args;
261 struct cons_pointer body = cell.payload.lambda.body;
262
263 if ( consp( names ) ) {
264 /* if `names` is a list, bind successive items from that list
265 * to values of arguments */
266 for ( int i = 0; i < frame->args && consp( names ); i++ ) {
267 struct cons_pointer name = c_car( names );
268 struct cons_pointer val = frame->arg[i];
269
270 new_env = set( name, val, new_env );
271 debug_print_binding( name, val, false, DEBUG_BIND );
272
273 names = c_cdr( names );
274 }
275// inc_ref( new_env );
276
277 /* \todo if there's more than `args_in_frame` arguments, bind those too. */
278 } else if ( symbolp( names ) ) {
279 /* if `names` is a symbol, rather than a list of symbols,
280 * then bind a list of the values of args to that symbol. */
281 /* \todo eval all the things in frame->more */
282 struct cons_pointer vals =
283 eval_forms( frame, frame_pointer, frame->more, env );
284
285 for ( int i = args_in_frame - 1; i >= 0; i-- ) {
286 struct cons_pointer val =
287 eval_form( frame, frame_pointer, frame->arg[i], env );
288
289 if ( nilp( val ) && nilp( vals ) ) { /* nothing */
290 } else {
291 vals = make_cons( val, vals );
292 }
293 }
294
295 new_env = set( names, vals, new_env );
296// inc_ref( new_env );
297 }
298
299 while ( !nilp( body ) ) {
300 struct cons_pointer sexpr = c_car( body );
301 body = c_cdr( body );
302
303 debug_print( L"In lambda: evaluating ", DEBUG_LAMBDA );
305 // debug_print( L"\t env is: ", DEBUG_LAMBDA );
306 // debug_print_object( new_env, DEBUG_LAMBDA );
308
309 /* if a result is not the terminal result in the lambda, it's a
310 * side effect, and needs to be GCed */
311 if ( !nilp( result ) ) {
312 dec_ref( result );
313 }
314
315 result = eval_form( frame, frame_pointer, sexpr, new_env );
316
317 if ( exceptionp( result ) ) {
318 break;
319 }
320 }
321
322 // dec_ref( new_env );
323
324 debug_print( L"eval_lambda returning: \n", DEBUG_LAMBDA );
327
328 return result;
329}
330
331
332/**
333 * Internal guts of apply.
334 * @param frame the stack frame, expected to have only one argument, a list
335 * comprising something that evaluates to a function and its arguments.
336 * @param env The evaluation environment.
337 * @return the result of evaluating the function with its arguments.
338 */
339struct cons_pointer
340c_apply( struct stack_frame *frame, struct cons_pointer frame_pointer,
341 struct cons_pointer env ) {
342 debug_print( L"Entering c_apply\n", DEBUG_EVAL );
343 struct cons_pointer result = NIL;
344
345 struct cons_pointer fn_pointer =
346 eval_form( frame, frame_pointer, c_car( frame->arg[0] ), env );
347
348 if ( exceptionp( fn_pointer ) ) {
349 result = fn_pointer;
350 } else {
351 struct cons_space_object fn_cell = pointer2cell( fn_pointer );
352 struct cons_pointer args = c_cdr( frame->arg[0] );
353
354 switch ( fn_cell.tag.value ) {
355 case EXCEPTIONTV:
356 /* just pass exceptions straight back */
357 result = fn_pointer;
358 break;
359
360 case FUNCTIONTV:
361 {
362 struct cons_pointer exep = NIL;
363 struct cons_pointer next_pointer =
364 make_stack_frame( frame_pointer, args, env );
365// inc_ref( next_pointer );
366 if ( exceptionp( next_pointer ) ) {
367 result = next_pointer;
368 } else {
369 struct stack_frame *next =
370 get_stack_frame( next_pointer );
371
372 result =
373 ( *fn_cell.payload.function.executable ) ( next,
374 next_pointer,
375 env );
376 dec_ref( next_pointer );
377 }
378 }
379 break;
380
381 case KEYTV:
382 result = c_assoc( fn_pointer,
383 eval_form( frame,
384 frame_pointer,
385 c_car( c_cdr( frame->arg[0] ) ),
386 env ) );
387 break;
388
389 case LAMBDATV:
390 {
391 struct cons_pointer exep = NIL;
392 struct cons_pointer next_pointer =
393 make_stack_frame( frame_pointer, args, env );
394// inc_ref( next_pointer );
395 if ( exceptionp( next_pointer ) ) {
396 result = next_pointer;
397 } else {
398 struct stack_frame *next =
399 get_stack_frame( next_pointer );
400 result =
401 eval_lambda( fn_cell, next, next_pointer, env );
402 if ( !exceptionp( result ) ) {
403 dec_ref( next_pointer );
404 }
405 }
406 }
407 break;
408
409 case VECTORPOINTTV:
410 switch ( pointer_to_vso( fn_pointer )->header.tag.value ) {
411 case HASHTV:
412 /* \todo: if arg[0] is a CONS, treat it as a path */
413 result = c_assoc( eval_form( frame,
414 frame_pointer,
415 c_car( c_cdr
416 ( frame->arg
417 [0] ) ), env ),
418 fn_pointer );
419 break;
420 }
421 break;
422
423 case NLAMBDATV:
424 {
425 struct cons_pointer next_pointer =
426 make_special_frame( frame_pointer, args, env );
427// inc_ref( next_pointer );
428 if ( exceptionp( next_pointer ) ) {
429 result = next_pointer;
430 } else {
431 struct stack_frame *next =
432 get_stack_frame( next_pointer );
433 result =
434 eval_lambda( fn_cell, next, next_pointer, env );
435 dec_ref( next_pointer );
436 }
437 }
438 break;
439
440 case SPECIALTV:
441 {
442 struct cons_pointer next_pointer =
443 make_special_frame( frame_pointer, args, env );
444 inc_ref( next_pointer );
445 if ( exceptionp( next_pointer ) ) {
446 result = next_pointer;
447 } else {
448 result =
449 ( *fn_cell.payload.
450 special.executable ) ( get_stack_frame
451 ( next_pointer ),
452 next_pointer, env );
453 debug_print( L"Special form returning: ", DEBUG_EVAL );
456 dec_ref( next_pointer );
457 }
458 }
459 break;
460
461 default:
462 {
463 int bs = sizeof( wchar_t ) * 1024;
464 wchar_t *buffer = malloc( bs );
465 memset( buffer, '\0', bs );
466 swprintf( buffer, bs,
467 L"Unexpected cell with tag %d (%4.4s) in function position",
468 fn_cell.tag.value, &fn_cell.tag.bytes[0] );
469 struct cons_pointer message =
470 c_string_to_lisp_string( buffer );
471 free( buffer );
472 result = throw_exception( message, frame_pointer );
473 }
474 }
475 }
476
477 debug_print( L"c_apply: returning: ", DEBUG_EVAL );
480
481 return result;
482}
483
484/**
485 * Function; evaluate the expression which is the first argument in the frame;
486 * further arguments are ignored.
487 *
488 * * (eval expression)
489 *
490 * @param frame my stack_frame.
491 * @param frame_pointer a pointer to my stack_frame.
492 * @param env my environment.
493 * @return
494 * * If `expression` is a number, string, `nil`, or `t`, returns `expression`.
495 * * If `expression` is a symbol, returns the value that expression is bound
496 * to in the evaluation environment (`env`).
497 * * If `expression` is a list, expects the car to be something that evaluates to a
498 * function or special form:
499 * * If a function, evaluates all the other top level elements in `expression` and
500 * passes them in a stack frame as arguments to the function;
501 * * If a special form, passes the cdr of expression to the special form as argument.
502 * @exception if `expression` is a symbol which is not bound in `env`.
503 */
504struct cons_pointer
505lisp_eval( struct stack_frame *frame, struct cons_pointer frame_pointer,
506 struct cons_pointer env ) {
507 debug_print( L"Eval: ", DEBUG_EVAL );
508 debug_dump_object( frame_pointer, DEBUG_EVAL );
509
510 struct cons_pointer result = frame->arg[0];
511 struct cons_space_object cell = pointer2cell( frame->arg[0] );
512
513 switch ( cell.tag.value ) {
514 case CONSTV:
515 result = c_apply( frame, frame_pointer, env );
516 break;
517
518 case SYMBOLTV:
519 {
520 struct cons_pointer canonical =
521 internedp( frame->arg[0], env );
522 if ( nilp( canonical ) ) {
523 struct cons_pointer message =
525 ( L"Attempt to take value of unbound symbol." ),
526 frame->arg[0] );
527 result = throw_exception( message, frame_pointer );
528 } else {
529 result = c_assoc( canonical, env );
530 inc_ref( result );
531 }
532 }
533 break;
534 /*
535 * \todo
536 * the Clojure practice of having a map serve in the function place of
537 * an s-expression is a good one and I should adopt it;
538 * H'mmm... this is working, but it isn't here. Where is it?
539 */
540 default:
541 result = frame->arg[0];
542 break;
543 }
544
545 debug_print( L"Eval returning ", DEBUG_EVAL );
546 debug_dump_object( result, DEBUG_EVAL );
547
548 return result;
549}
550
551
552/**
553 * Function; apply the function which is the result of evaluating the
554 * first argument to the list of values which is the result of evaluating
555 * the second argument
556 *
557 * * (apply fn args)
558 *
559 * @param frame my stack_frame.
560 * @param frame_pointer a pointer to my stack_frame.
561 * @param env my environment.
562 * @return the result of applying `fn` to `args`.
563 */
564struct cons_pointer
565lisp_apply( struct stack_frame *frame, struct cons_pointer frame_pointer,
566 struct cons_pointer env ) {
567 debug_print( L"Apply: ", DEBUG_EVAL );
568 debug_dump_object( frame_pointer, DEBUG_EVAL );
569
570 set_reg( frame, 0, make_cons( frame->arg[0], frame->arg[1] ) );
571 set_reg( frame, 1, NIL );
572
573 struct cons_pointer result = c_apply( frame, frame_pointer, env );
574
575 debug_print( L"Apply returning ", DEBUG_EVAL );
576 debug_dump_object( result, DEBUG_EVAL );
577
578 return result;
579}
580
581
582/**
583 * Special form;
584 * returns its argument (strictly first argument - only one is expected but
585 * this isn't at this stage checked) unevaluated.
586 *
587 * * (quote a)
588 *
589 * @param frame my stack_frame.
590 * @param frame_pointer a pointer to my stack_frame.
591 * @param env my environment (ignored).
592 * @return `a`, unevaluated,
593 */
594struct cons_pointer
595lisp_quote( struct stack_frame *frame, struct cons_pointer frame_pointer,
596 struct cons_pointer env ) {
597 return frame->arg[0];
598}
599
600
601/**
602 * Function;
603 * binds the value of `name` in the `namespace` to value of `value`, altering
604 * the namespace in so doing. Retuns `value`.
605 * `namespace` defaults to the oblist.
606 * \todo doesn't actually work yet for namespaces which are not the oblist.
607 *
608 * * (set name value)
609 * * (set name value namespace)
610 *
611 * @param frame my stack_frame.
612 * @param frame_pointer a pointer to my stack_frame.
613 * @param env my environment (ignored).
614 * @return `value`
615 */
616struct cons_pointer
617lisp_set( struct stack_frame *frame, struct cons_pointer frame_pointer,
618 struct cons_pointer env ) {
619 struct cons_pointer result = NIL;
620 struct cons_pointer namespace =
621 nilp( frame->arg[2] ) ? oblist : frame->arg[2];
622
623 if ( symbolp( frame->arg[0] ) ) {
624 deep_bind( frame->arg[0], frame->arg[1] );
625 result = frame->arg[1];
626 } else {
627 result =
630 ( L"The first argument to `set` is not a symbol: " ),
631 make_cons( frame->arg[0], NIL ) ),
632 frame_pointer );
633 }
634
635 return result;
636}
637
638
639/**
640 * Special form;
641 * binds `symbol` in the `namespace` to value of `value`, altering
642 * the namespace in so doing, and returns value. `namespace` defaults to
643 * the value of `oblist`.
644 * \todo doesn't actually work yet for namespaces which are not the oblist.
645 *
646 * * (set! symbol value)
647 * * (set! symbol value namespace)
648 *
649 * @param frame my stack_frame.
650 * @param frame_pointer a pointer to my stack_frame.
651 * @param env my environment (ignored).
652 * @return `value`
653 */
654struct cons_pointer
655lisp_set_shriek( struct stack_frame *frame, struct cons_pointer frame_pointer,
656 struct cons_pointer env ) {
657 struct cons_pointer result = NIL;
658 struct cons_pointer namespace = frame->arg[2];
659
660 if ( symbolp( frame->arg[0] ) ) {
661 struct cons_pointer val =
662 eval_form( frame, frame_pointer, frame->arg[1], env );
663 deep_bind( frame->arg[0], val );
664 result = val;
665 } else {
666 result =
669 ( L"The first argument to `set!` is not a symbol: " ),
670 make_cons( frame->arg[0], NIL ) ),
671 frame_pointer );
672 }
673
674 return result;
675}
676
677/**
678 * @return true if `arg` represents an end of string, else false.
679 * \todo candidate for moving to a memory/string.c file
680 */
681bool end_of_stringp( struct cons_pointer arg ) {
682 return nilp( arg ) ||
683 ( stringp( arg ) &&
684 pointer2cell( arg ).payload.string.character == ( wint_t ) '\0' );
685}
686
687/**
688 * Function;
689 * returns a cell constructed from a and b. If a is of type string but its
690 * cdr is nill, and b is of type string, then returns a new string cell;
691 * otherwise returns a new cons cell.
692 *
693 * Thus: `(cons "a" "bcd") -> "abcd"`, but `(cons "ab" "cd") -> ("ab" . "cd")`
694 *
695 * * (cons a b)
696 *
697 * @param frame my stack_frame.
698 * @param frame_pointer a pointer to my stack_frame.
699 * @param env my environment (ignored).
700 * @return a new cons cell whose `car` is `a` and whose `cdr` is `b`.
701 */
702struct cons_pointer
703lisp_cons( struct stack_frame *frame, struct cons_pointer frame_pointer,
704 struct cons_pointer env ) {
705 struct cons_pointer car = frame->arg[0];
706 struct cons_pointer cdr = frame->arg[1];
707 struct cons_pointer result;
708
709 if ( nilp( car ) && nilp( cdr ) ) {
710 return NIL;
711 } else if ( stringp( car ) && stringp( cdr ) &&
712 end_of_stringp( c_cdr( car ) ) ) {
713 result =
714 make_string( pointer2cell( car ).payload.string.character, cdr );
715 } else {
716 result = make_cons( car, cdr );
717 }
718
719 return result;
720}
721
722/**
723 * Function;
724 * returns the first item (head) of a sequence. Valid for cons cells,
725 * strings, read streams and TODO other things which can be considered as sequences.
726 *
727 * * (car expression)
728 *
729 * @param frame my stack_frame.
730 * @param frame_pointer a pointer to my stack_frame.
731 * @param env my environment (ignored).
732 * @return the first item (head) of `expression`.
733 * @exception if `expression` is not a sequence.
734 */
735struct cons_pointer
736lisp_car( struct stack_frame *frame, struct cons_pointer frame_pointer,
737 struct cons_pointer env ) {
738 struct cons_pointer result = NIL;
739 struct cons_space_object cell = pointer2cell( frame->arg[0] );
740
741 switch ( cell.tag.value ) {
742 case CONSTV:
743 result = cell.payload.cons.car;
744 break;
745 case NILTV:
746 break;
747 case READTV:
748 result =
749 make_string( url_fgetwc( cell.payload.stream.stream ), NIL );
750 break;
751 case STRINGTV:
752 result = make_string( cell.payload.string.character, NIL );
753 break;
754 default:
755 result =
757 ( L"Attempt to take CAR of non sequence" ),
758 frame_pointer );
759 }
760
761 return result;
762}
763
764/**
765 * Function;
766 * returns the remainder of a sequence when the head is removed. Valid for cons cells,
767 * strings, read streams and TODO other things which can be considered as sequences.
768 * *NOTE* that if the argument is an input stream, the first character is removed AND
769 * DISCARDED.
770 *
771 * * (cdr expression)
772 *
773 * @param frame my stack_frame.
774 * @param frame_pointer a pointer to my stack_frame.
775 * @param env my environment (ignored).
776 * @return the remainder of `expression` when the head is removed.
777 * @exception if `expression` is not a sequence.
778 */
779struct cons_pointer
780lisp_cdr( struct stack_frame *frame, struct cons_pointer frame_pointer,
781 struct cons_pointer env ) {
782 struct cons_pointer result = NIL;
783 struct cons_space_object cell = pointer2cell( frame->arg[0] );
784
785 switch ( cell.tag.value ) {
786 case CONSTV:
787 result = cell.payload.cons.cdr;
788 break;
789 case NILTV:
790 break;
791 case READTV:
792 url_fgetwc( cell.payload.stream.stream );
793 result = frame->arg[0];
794 break;
795 case STRINGTV:
796 result = cell.payload.string.cdr;
797 break;
798 default:
799 result =
801 ( L"Attempt to take CDR of non sequence" ),
802 frame_pointer );
803 }
804
805 return result;
806}
807
808/**
809 * Function: return, as an integer, the length of the sequence indicated by
810 * the first argument, or zero if it is not a sequence.
811 *
812 * * (length any)
813 *
814 * @param frame my stack_frame.
815 * @param frame_pointer a pointer to my stack_frame.
816 * @param env my environment (ignored).
817 * @return the length of `any`, if it is a sequence, or zero otherwise.
818 */
820 struct cons_pointer frame_pointer,
821 struct cons_pointer env ) {
822 return make_integer( c_length( frame->arg[0] ), NIL );
823}
824
825/**
826 * Function; look up the value of a `key` in a `store`.
827 *
828 * * (assoc key store)
829 *
830 * @param frame my stack_frame.
831 * @param frame_pointer a pointer to my stack_frame.
832 * @param env my environment (ignored).
833 * @return the value associated with `key` in `store`, or `nil` if not found.
834 */
835struct cons_pointer
836lisp_assoc( struct stack_frame *frame, struct cons_pointer frame_pointer,
837 struct cons_pointer env ) {
838 return c_assoc( frame->arg[0], frame->arg[1] );
839}
840
841struct cons_pointer c_keys( struct cons_pointer store ) {
842 struct cons_pointer result = NIL;
843
844 if ( hashmapp( store ) ) {
845 result = hashmap_keys( store );
846 } else if ( consp( store ) ) {
847 for ( struct cons_pointer c = store; !nilp( c ); c = c_cdr( c ) ) {
848 result = make_cons( c_car( c ), result );
849 }
850 }
851
852 return result;
853}
854
855struct cons_pointer lisp_keys( struct stack_frame *frame,
856 struct cons_pointer frame_pointer,
857 struct cons_pointer env ) {
858 return c_keys( frame->arg[0] );
859}
860
861/**
862 * Function; are these two objects the same object? Shallow, cheap equality.
863 *
864 * * (eq a b)
865 *
866 * @param frame my stack_frame.
867 * @param frame_pointer a pointer to my stack_frame.
868 * @param env my environment (ignored).
869 * @return `t` if `a` and `b` are pointers to the same object, else `nil`;
870 */
871struct cons_pointer lisp_eq( struct stack_frame *frame,
872 struct cons_pointer frame_pointer,
873 struct cons_pointer env ) {
874 struct cons_pointer result = TRUE;
875
876 if ( frame->args > 1 ) {
877 for ( int b = 1; ( truep( result ) ) && ( b < frame->args ); b++ ) {
878 result = eq( frame->arg[0], fetch_arg( frame, b ) ) ? TRUE : NIL;
879 }
880 }
881
882 return result;
883}
884
885/**
886 * Function; are these two arguments identical? Deep, expensive equality.
887 *
888 * * (equal a b)
889 *
890 * @param frame my stack_frame.
891 * @param frame_pointer a pointer to my stack_frame.
892 * @param env my environment (ignored).
893 * @return `t` if `a` and `b` are recursively identical, else `nil`.
894 */
895struct cons_pointer
896lisp_equal( struct stack_frame *frame, struct cons_pointer frame_pointer,
897 struct cons_pointer env ) {
898 struct cons_pointer result = TRUE;
899
900 if ( frame->args > 1 ) {
901 for ( int b = 1; ( truep( result ) ) && ( b < frame->args ); b++ ) {
902 result =
903 equal( frame->arg[0], fetch_arg( frame, b ) ) ? TRUE : NIL;
904 }
905 }
906
907 return result;
908}
909
910long int c_count( struct cons_pointer p ) {
911 struct cons_space_object *cell = &pointer2cell( p );
912 int result = 0;
913
914 switch ( cell->tag.value ) {
915 case CONSTV:
916 case STRINGTV:
917 /* I think doctrine is that you cannot treat symbols or keywords as
918 * sequences, although internally, of course, they are. Integers are
919 * also internally sequences, but also should not be treated as such.
920 */
921 for ( p; !nilp( p ); p = c_cdr( p ) ) {
922 result++;
923 }
924 }
925
926 return result;
927}
928
929/**
930 * Function: return the number of top level forms in the object which is
931 * the first (and only) argument, if it is a sequence (which for current
932 * purposes means a list or a string)
933 *
934 * * (count l)
935 *
936 * @param frame my stack_frame.
937 * @param frame_pointer a pointer to my stack_frame.
938 * @param env my environment (ignored).
939 * @return the number of top level forms in a list, or characters in a
940 * string, else 0.
941 */
942struct cons_pointer
943lisp_count( struct stack_frame *frame, struct cons_pointer frame_pointer,
944 struct cons_pointer env ) {
945 return acquire_integer( c_count( frame->arg[0] ), NIL );
946}
947
948/**
949 * Function; read one complete lisp form and return it. If read-stream is specified and
950 * is a read stream, then read from that stream, else the stream which is the value of
951 * `*in*` in the environment.
952 *
953 * * (read)
954 * * (read read-stream)
955 *
956 * @param frame my stack_frame.
957 * @param frame_pointer a pointer to my stack_frame.
958 * @param env my environment.
959 * @return the expression read.
960 */
961struct cons_pointer
962lisp_read( struct stack_frame *frame, struct cons_pointer frame_pointer,
963 struct cons_pointer env ) {
964#ifdef DEBUG
965 debug_print( L"entering lisp_read\n", DEBUG_IO );
966#endif
967 URL_FILE *input;
968
969 struct cons_pointer in_stream = readp( frame->arg[0] ) ?
970 frame->arg[0] : get_default_stream( true, env );
971
972 if ( readp( in_stream ) ) {
973 debug_print( L"lisp_read: setting input stream\n", DEBUG_IO );
974 debug_dump_object( in_stream, DEBUG_IO );
975 input = pointer2cell( in_stream ).payload.stream.stream;
976 inc_ref( in_stream );
977 } else {
978 input = file_to_url_file( stdin );
979 }
980
981 struct cons_pointer result = read( frame, frame_pointer, env, input );
982 debug_print( L"lisp_read returning\n", DEBUG_IO );
983 debug_dump_object( result, DEBUG_IO );
984
985 if ( readp( in_stream ) ) {
986 dec_ref( in_stream );
987 } else {
988 free( input );
989 }
990
991
992 return result;
993}
994
995
996/**
997 * reverse a sequence (if it is a sequence); else return it unchanged.
998 */
999struct cons_pointer c_reverse( struct cons_pointer arg ) {
1000 struct cons_pointer result = NIL;
1001
1002 if ( sequencep( arg ) ) {
1003 for ( struct cons_pointer p = arg; sequencep( p ); p = c_cdr( p ) ) {
1004 struct cons_space_object o = pointer2cell( p );
1005 switch ( o.tag.value ) {
1006 case CONSTV:
1007 result = make_cons( o.payload.cons.car, result );
1008 break;
1009 case STRINGTV:
1010 result = make_string( o.payload.string.character, result );
1011 break;
1012 case SYMBOLTV:
1013 result =
1014 make_symbol_or_key( o.payload.string.character, result,
1015 SYMBOLTV );
1016 break;
1017 }
1018 }
1019 } else {
1020 result = arg;
1021 }
1022
1023 return result;
1024}
1025
1026
1027/**
1028 * Function; reverse the order of members in s sequence.
1029 *
1030 * * (reverse sequence)
1031 *
1032 * @param frame my stack_frame.
1033 * @param frame_pointer a pointer to my stack_frame.
1034 * @param env my environment (ignored).
1035 * @return a sequence like this `sequence` but with the members in the reverse order.
1036 */
1038 struct cons_pointer frame_pointer,
1039 struct cons_pointer env ) {
1040 return c_reverse( frame->arg[0] );
1041}
1042
1043/**
1044 * Function: dump/inspect one complete lisp expression and return NIL. If
1045 * write-stream is specified and is a write stream, then print to that stream,
1046 * else the stream which is the value of
1047 * `*out*` in the environment.
1048 *
1049 * * (inspect expr)
1050 * * (inspect expr write-stream)
1051 *
1052 * @param frame my stack_frame.
1053 * @param frame_pointer a pointer to my stack_frame.
1054 * @param env my environment (from which the stream may be extracted).
1055 * @return NIL.
1056 */
1058 struct cons_pointer frame_pointer,
1059 struct cons_pointer env ) {
1060 debug_print( L"Entering lisp_inspect\n", DEBUG_IO );
1061 struct cons_pointer result = NIL;
1062 struct cons_pointer out_stream = writep( frame->arg[1] )
1063 ? frame->arg[1]
1064 : get_default_stream( false, env );
1065 URL_FILE *output;
1066
1067 if ( writep( out_stream ) ) {
1068 debug_print( L"lisp_inspect: setting output stream\n", DEBUG_IO );
1069 debug_dump_object( out_stream, DEBUG_IO );
1070 output = pointer2cell( out_stream ).payload.stream.stream;
1071 } else {
1072 output = file_to_url_file( stderr );
1073 }
1074
1075 dump_object( output, frame->arg[0] );
1076
1077 debug_print( L"Leaving lisp_inspect", DEBUG_IO );
1078
1079 return result;
1080}
1081
1082
1083/**
1084 * Function: get the Lisp type of the single argument.
1085 *
1086 * * (type expression)
1087 *
1088 * @param frame my stack frame.
1089 * @param frame_pointer a pointer to my stack_frame.
1090 * @param env my environment (ignored).
1091 * @return As a Lisp string, the tag of `expression`.
1092 */
1093struct cons_pointer
1094lisp_type( struct stack_frame *frame, struct cons_pointer frame_pointer,
1095 struct cons_pointer env ) {
1096 return c_type( frame->arg[0] );
1097}
1098
1099/**
1100 * Evaluate each of these expressions in this `env`ironment over this `frame`,
1101 * returning only the value of the last.
1102 */
1103struct cons_pointer
1104c_progn( struct stack_frame *frame, struct cons_pointer frame_pointer,
1105 struct cons_pointer expressions, struct cons_pointer env ) {
1106 struct cons_pointer result = NIL;
1107
1108 while ( consp( expressions ) ) {
1109 struct cons_pointer r = result;
1110 inc_ref( r );
1111 result = eval_form( frame, frame_pointer, c_car( expressions ), env );
1112 dec_ref( r );
1113
1114 expressions = exceptionp( result ) ? NIL : c_cdr( expressions );
1115 }
1116
1117 return result;
1118}
1119
1120
1121/**
1122 * Special form; evaluate the expressions which are listed in my arguments
1123 * sequentially and return the value of the last. This function is called 'do'
1124 * in some dialects of Lisp.
1125 *
1126 * * (progn expressions...)
1127 *
1128 * @param frame my stack frame.
1129 * @param frame_pointer a pointer to my stack_frame.
1130 * @param env the environment in which expressions are evaluated.
1131 * @return the value of the last `expression` of the sequence which is my single
1132 * argument.
1133 */
1134struct cons_pointer
1135lisp_progn( struct stack_frame *frame, struct cons_pointer frame_pointer,
1136 struct cons_pointer env ) {
1137 struct cons_pointer result = NIL;
1138
1139 for ( int i = 0; i < args_in_frame && !nilp( frame->arg[i] ); i++ ) {
1140 struct cons_pointer r = result;
1141 inc_ref( r );
1142
1143 result = eval_form( frame, frame_pointer, frame->arg[i], env );
1144
1145 dec_ref( r );
1146 }
1147
1148 if ( consp( frame->more ) ) {
1149 result = c_progn( frame, frame_pointer, frame->more, env );
1150 }
1151
1152 return result;
1153}
1154
1155/**
1156 * @brief evaluate a single cond clause; if the test part succeeds return a
1157 * pair whose car is TRUE and whose cdr is the value of the action part
1158 */
1160 struct stack_frame *frame,
1161 struct cons_pointer frame_pointer,
1162 struct cons_pointer env ) {
1163 struct cons_pointer result = NIL;
1164
1165#ifdef DEBUG
1166 debug_print( L"\n\tCond clause: ", DEBUG_EVAL );
1167 debug_print_object( clause, DEBUG_EVAL );
1169#endif
1170
1171 if ( consp( clause ) ) {
1172 struct cons_pointer val =
1173 eval_form( frame, frame_pointer, c_car( clause ),
1174 env );
1175
1176 if ( !nilp( val ) ) {
1177 result =
1178 make_cons( TRUE,
1179 c_progn( frame, frame_pointer, c_cdr( clause ),
1180 env ) );
1181
1182#ifdef DEBUG
1183 debug_print( L"\n\t\tclause succeeded; returning: ", DEBUG_EVAL );
1184 debug_print_object( result, DEBUG_EVAL );
1186 } else {
1187 debug_print( L"\n\t\tclause failed.\n", DEBUG_EVAL );
1188#endif
1189 }
1190 } else {
1192 ( L"Arguments to `cond` must be lists" ),
1193 frame_pointer );
1194 }
1195
1196 return result;
1197}
1198
1199/**
1200 * Special form: conditional. Each `clause` is expected to be a list; if the first
1201 * item in such a list evaluates to non-NIL, the remaining items in that list
1202 * are evaluated in turn and the value of the last returned. If no arg `clause`
1203 * has a first element which evaluates to non NIL, then NIL is returned.
1204 *
1205 * * (cond clauses...)
1206 *
1207 * @param frame my stack frame.
1208 * @param frame_pointer a pointer to my stack_frame.
1209 * @param env the environment in which arguments will be evaluated.
1210 * @return the value of the last expression of the first successful `clause`.
1211 */
1212struct cons_pointer
1213lisp_cond( struct stack_frame *frame, struct cons_pointer frame_pointer,
1214 struct cons_pointer env ) {
1215 struct cons_pointer result = NIL;
1216 bool done = false;
1217
1218 for ( int i = 0; ( i < frame->args ) && !done; i++ ) {
1219 struct cons_pointer clause_pointer = fetch_arg( frame, i );
1220
1221 result = eval_cond_clause( clause_pointer, frame, frame_pointer, env );
1222
1223 if ( !nilp( result ) && truep( c_car( result ) ) ) {
1224 result = c_cdr( result );
1225 done = true;
1226 break;
1227 }
1228 }
1229#ifdef DEBUG
1230 debug_print( L"\tCond returning: ", DEBUG_EVAL );
1231 debug_print_object( result, DEBUG_EVAL );
1233#endif
1234
1235 return result;
1236}
1237
1238/**
1239 * Throw an exception.
1240 * `throw_exception` is a misnomer, because it doesn't obey the calling signature of a
1241 * lisp function; but it is nevertheless to be preferred to make_exception. A
1242 * real `throw_exception`, which does, will be needed.
1243 * object pointing to it. Then this should become a normal lisp function
1244 * which expects a normally bound frame and environment, such that
1245 * frame->arg[0] is the message, and frame->arg[1] is the cons-space
1246 * pointer to the frame in which the exception occurred.
1247 */
1248struct cons_pointer
1250 struct cons_pointer frame_pointer ) {
1251 debug_print( L"\nERROR: ", DEBUG_EVAL );
1252 debug_dump_object( message, DEBUG_EVAL );
1253 struct cons_pointer result = NIL;
1254
1255 struct cons_space_object cell = pointer2cell( message );
1256
1257 if ( cell.tag.value == EXCEPTIONTV ) {
1258 result = message;
1259 } else {
1260 result = make_exception( message, frame_pointer );
1261 }
1262
1263 return result;
1264}
1265
1266/**
1267 * Function; create an exception. Exceptions are special in as much as if an
1268 * exception is created in the binding of the arguments of any function, the
1269 * function will return the exception rather than whatever else it would
1270 * normally return. A function which detects a problem it cannot resolve
1271 * *should* return an exception.
1272 *
1273 * * (exception message frame)
1274 *
1275 * @param frame my stack frame.
1276 * @param frame_pointer a pointer to my stack_frame.
1277 * @param env the environment in which arguments will be evaluated.
1278 * @return areturns an exception whose message is this `message`, and whose
1279 * stack frame is the parent stack frame when the function is invoked.
1280 * `message` does not have to be a string but should be something intelligible
1281 * which can be read.
1282 * If `message` is itself an exception, returns that instead.
1283 */
1284struct cons_pointer
1285lisp_exception( struct stack_frame *frame, struct cons_pointer frame_pointer,
1286 struct cons_pointer env ) {
1287 struct cons_pointer message = frame->arg[0];
1288 return exceptionp( message ) ? message : throw_exception( message,
1289 frame->
1290 previous );
1291}
1292
1293/**
1294 * Function: the read/eval/print loop.
1295 *
1296 * * (repl)
1297 * * (repl prompt)
1298 * * (repl prompt input_stream output_stream)
1299 *
1300 * @param frame my stack frame.
1301 * @param frame_pointer a pointer to my stack_frame.
1302 * @param env the environment in which epressions will be evaluated.
1303 * @return the value of the last expression read.
1304 */
1305struct cons_pointer lisp_repl( struct stack_frame *frame,
1306 struct cons_pointer frame_pointer,
1307 struct cons_pointer env ) {
1308 struct cons_pointer expr = NIL;
1309
1310 debug_printf( DEBUG_REPL, L"Entering new inner REPL\n" );
1311
1312 struct cons_pointer input = get_default_stream( true, env );
1313 struct cons_pointer output = get_default_stream( false, env );
1314 struct cons_pointer old_oblist = oblist;
1315 struct cons_pointer new_env = env;
1316
1317 if ( truep( frame->arg[0] ) ) {
1318 new_env = set( prompt_name, frame->arg[0], new_env );
1319 }
1320 if ( readp( frame->arg[1] ) ) {
1321 new_env =
1322 set( c_string_to_lisp_symbol( L"*in*" ), frame->arg[1], new_env );
1323 input = frame->arg[1];
1324 }
1325 if ( readp( frame->arg[2] ) ) {
1326 new_env =
1327 set( c_string_to_lisp_symbol( L"*out*" ), frame->arg[2], new_env );
1328 output = frame->arg[2];
1329 }
1330
1331 inc_ref( input );
1332 inc_ref( output );
1334
1335 URL_FILE *os = pointer2cell( output ).payload.stream.stream;
1336
1337
1338 /* \todo this is subtly wrong. If we were evaluating
1339 * (print (eval (read)))
1340 * then the stack frame for read would have the stack frame for
1341 * eval as parent, and it in turn would have the stack frame for
1342 * print as parent.
1343 */
1344 while ( readp( input ) && writep( output )
1345 && !url_feof( pointer2cell( input ).payload.stream.stream ) ) {
1346 /* OK, here's a really subtle problem: because lists are immutable, anything
1347 * bound in the oblist subsequent to this function being invoked isn't in the
1348 * environment. So, for example, changes to *prompt* or *log* made in the oblist
1349 * are not visible. So copy changes made in the oblist into the enviroment.
1350 * \todo the whole process of resolving symbol values needs to be revisited
1351 * when we get onto namespaces. */
1352 /* OK, there's something even more subtle here if the root namespace is a map.
1353 * H'mmmm... */
1354 if ( !eq( oblist, old_oblist ) ) {
1355 struct cons_pointer cursor = oblist;
1356
1357 while ( !nilp( cursor ) && !eq( cursor, old_oblist ) ) {
1358 struct cons_pointer old_new_env = new_env;
1360 ( L"lisp_repl: copying new oblist binding into REPL environment:\n",
1361 DEBUG_REPL );
1362 debug_print_object( c_car( cursor ), DEBUG_REPL );
1364
1365 new_env = make_cons( c_car( cursor ), new_env );
1366 inc_ref( new_env );
1367 dec_ref( old_new_env );
1368 cursor = c_cdr( cursor );
1369 }
1370 old_oblist = oblist;
1371 }
1372
1373 println( os );
1374
1375 struct cons_pointer prompt = c_assoc( prompt_name, new_env );
1376 if ( !nilp( prompt ) ) {
1377 print( os, prompt );
1378 }
1379
1380 expr = lisp_read( get_stack_frame( frame_pointer ), frame_pointer,
1381 new_env );
1382
1383 if ( exceptionp( expr )
1384 && url_feof( pointer2cell( input ).payload.stream.stream ) ) {
1385 /* suppress printing end of stream exception */
1386 dec_ref( expr );
1387 break;
1388 }
1389
1390 println( os );
1391
1392 print( os, eval_form( frame, frame_pointer, expr, new_env ) );
1393
1394 dec_ref( expr );
1395 }
1396
1397 dec_ref( input );
1398 dec_ref( output );
1400 dec_ref( new_env );
1401
1402 debug_printf( DEBUG_REPL, L"Leaving inner repl\n" );
1403
1404 return expr;
1405}
1406
1407/**
1408 * Function. return the source code of the object which is its first argument,
1409 * if it is an executable and has source code.
1410 *
1411 * * (source object)
1412 *
1413 * @param frame my stack frame.
1414 * @param frame_pointer a pointer to my stack_frame.
1415 * @param env the environment (ignored).
1416 * @return the source of the `object` indicated, if it is a function, a lambda,
1417 * an nlambda, or a spcial form; else `nil`.
1418 */
1420 struct cons_pointer frame_pointer,
1421 struct cons_pointer env ) {
1422 struct cons_pointer result = NIL;
1423 struct cons_space_object cell = pointer2cell( frame->arg[0] );
1424 struct cons_pointer source_key = c_string_to_lisp_keyword( L"source" );
1425 switch ( cell.tag.value ) {
1426 case FUNCTIONTV:
1427 result = c_assoc( source_key, cell.payload.function.meta );
1428 break;
1429 case SPECIALTV:
1430 result = c_assoc( source_key, cell.payload.special.meta );
1431 break;
1432 case LAMBDATV:
1433 result = make_cons( c_string_to_lisp_symbol( L"lambda" ),
1434 make_cons( cell.payload.lambda.args,
1435 cell.payload.lambda.body ) );
1436 break;
1437 case NLAMBDATV:
1438 result = make_cons( c_string_to_lisp_symbol( L"nlambda" ),
1439 make_cons( cell.payload.lambda.args,
1440 cell.payload.lambda.body ) );
1441 break;
1442 }
1443 // \todo suffers from premature GC, and I can't see why!
1444 inc_ref( result );
1445
1446 return result;
1447}
1448
1449/**
1450 * A version of append which can conveniently be called from C.
1451 */
1452struct cons_pointer c_append( struct cons_pointer l1, struct cons_pointer l2 ) {
1453 switch ( pointer2cell( l1 ).tag.value ) {
1454 case CONSTV:
1455 if ( pointer2cell( l1 ).tag.value == pointer2cell( l2 ).tag.value ) {
1456 if ( nilp( c_cdr( l1 ) ) ) {
1457 return make_cons( c_car( l1 ), l2 );
1458 } else {
1459 return make_cons( c_car( l1 ),
1460 c_append( c_cdr( l1 ), l2 ) );
1461 }
1462 } else {
1464 ( L"Can't append: not same type" ), NIL );
1465 }
1466 break;
1467 case KEYTV:
1468 case STRINGTV:
1469 case SYMBOLTV:
1470 if ( pointer2cell( l1 ).tag.value == pointer2cell( l2 ).tag.value ) {
1471 if ( nilp( c_cdr( l1 ) ) ) {
1472 return
1474 payload.string.character ),
1475 l2,
1476 pointer2cell( l1 ).tag.value );
1477 } else {
1478 return
1480 payload.string.character ),
1481 c_append( c_cdr( l1 ), l2 ),
1482 pointer2cell( l1 ).tag.value );
1483 }
1484 } else {
1486 ( L"Can't append: not same type" ), NIL );
1487 }
1488 break;
1489 default:
1491 ( L"Can't append: not a sequence" ), NIL );
1492 break;
1493 }
1494}
1495
1496/**
1497 * should really be overwritten with a version in Lisp, since this is much easier to write in Lisp
1498 */
1500 struct cons_pointer frame_pointer,
1501 struct cons_pointer env ) {
1502 struct cons_pointer result = fetch_arg( frame, ( frame->args - 1 ) );
1503
1504 for ( int a = frame->args - 2; a >= 0; a-- ) {
1505 result = c_append( fetch_arg( frame, a ), result );
1506 }
1507
1508 return result;
1509}
1510
1512 struct cons_pointer frame_pointer,
1513 struct cons_pointer env ) {
1514 struct cons_pointer result = NIL;
1515 debug_print( L"Mapcar: ", DEBUG_EVAL );
1516 debug_dump_object( frame_pointer, DEBUG_EVAL );
1517 int i = 0;
1518
1519 for ( struct cons_pointer c = frame->arg[1]; truep( c ); c = c_cdr( c ) ) {
1520 struct cons_pointer expr =
1521 make_cons( frame->arg[0], make_cons( c_car( c ), NIL ) );
1522 inc_ref( expr );
1523
1524 debug_printf( DEBUG_EVAL, L"Mapcar %d, evaluating ", i );
1527
1528 struct cons_pointer r = eval_form( frame, frame_pointer, expr, env );
1529
1530 if ( exceptionp( r ) ) {
1531 result = r;
1532 inc_ref( expr ); // to protect exception from the later dec_ref
1533 break;
1534 } else {
1535 result = make_cons( r, result );
1536 }
1537 debug_printf( DEBUG_EVAL, L"Mapcar %d, result is ", i++ );
1538 debug_print_object( result, DEBUG_EVAL );
1540
1541 dec_ref( expr );
1542 }
1543
1544 result = consp( result ) ? c_reverse( result ) : result;
1545
1546 debug_print( L"Mapcar returning: ", DEBUG_EVAL );
1547 debug_print_object( result, DEBUG_EVAL );
1549
1550 return result;
1551}
1552
1553/**
1554 * @brief construct and return a list of arbitrarily many arguments.
1555 *
1556 * @param frame The stack frame.
1557 * @param frame_pointer A pointer to the stack frame.
1558 * @param env The evaluation environment.
1559 * @return struct cons_pointer a pointer to the result
1560 */
1561struct cons_pointer lisp_list( struct stack_frame *frame,
1562 struct cons_pointer frame_pointer,
1563 struct cons_pointer env ) {
1564 struct cons_pointer result = frame->more;
1565
1566 for ( int a = nilp( result ) ? frame->args - 1 : args_in_frame - 1;
1567 a >= 0; a-- ) {
1568 result = make_cons( fetch_arg( frame, a ), result );
1569 }
1570
1571 return result;
1572}
1573
1574
1575
1576/**
1577 * Special form: evaluate a series of forms in an environment in which
1578 * these bindings are bound.
1579 * This is `let*` in Common Lisp parlance; `let` in Clojure parlance.
1580 */
1581struct cons_pointer lisp_let( struct stack_frame *frame,
1582 struct cons_pointer frame_pointer,
1583 struct cons_pointer env ) {
1584 struct cons_pointer bindings = env;
1585 struct cons_pointer result = NIL;
1586
1587 for ( struct cons_pointer cursor = frame->arg[0];
1588 truep( cursor ); cursor = c_cdr( cursor ) ) {
1589 struct cons_pointer pair = c_car( cursor );
1590 struct cons_pointer symbol = c_car( pair );
1591
1592 if ( symbolp( symbol ) ) {
1593 struct cons_pointer val =
1594 eval_form( frame, frame_pointer, c_cdr( pair ),
1595 bindings );
1596
1597 debug_print_binding( symbol, val, false, DEBUG_BIND );
1598
1599 bindings = make_cons( make_cons( symbol, val ), bindings );
1600 } else {
1601 result =
1603 ( L"Let: cannot bind, not a symbol" ),
1604 frame_pointer );
1605 break;
1606 }
1607 }
1608
1609 debug_print( L"\nlet: bindings complete.\n", DEBUG_BIND );
1610
1611 /* i.e., no exception yet */
1612 for ( int form = 1; !exceptionp( result ) && form < frame->args; form++ ) {
1613 result =
1614 eval_form( frame, frame_pointer, fetch_arg( frame, form ),
1615 bindings );
1616 }
1617
1618 /* release the local bindings as they go out of scope! **BUT**
1619 * bindings were consed onto the front of env, so caution... */
1620 // for (struct cons_pointer cursor = bindings; !eq( cursor, env); cursor = c_cdr(cursor)) {
1621 // dec_ref( cursor);
1622 // }
1623
1624 return result;
1625
1626}
1627
1628/**
1629 * @brief Boolean `and` of arbitrarily many arguments.
1630 *
1631 * @param frame The stack frame.
1632 * @param frame_pointer A pointer to the stack frame.
1633 * @param env The evaluation environment.
1634 * @return struct cons_pointer a pointer to the result
1635 */
1636struct cons_pointer lisp_and( struct stack_frame *frame,
1637 struct cons_pointer frame_pointer,
1638 struct cons_pointer env ) {
1639 bool accumulator = true;
1640 struct cons_pointer result = frame->more;
1641
1642 for ( int a = 0; accumulator == true && a < frame->args; a++ ) {
1643 accumulator = truthy( fetch_arg( frame, a ) );
1644 }
1645#
1646 return accumulator ? TRUE : NIL;
1647}
1648
1649/**
1650 * @brief Boolean `or` of arbitrarily many arguments.
1651 *
1652 * @param frame The stack frame.
1653 * @param frame_pointer A pointer to the stack frame.
1654 * @param env The evaluation environment.
1655 * @return struct cons_pointer a pointer to the result
1656 */
1657struct cons_pointer lisp_or( struct stack_frame *frame,
1658 struct cons_pointer frame_pointer,
1659 struct cons_pointer env ) {
1660 bool accumulator = false;
1661 struct cons_pointer result = frame->more;
1662
1663 for ( int a = 0; accumulator == false && a < frame->args; a++ ) {
1664 accumulator = truthy( fetch_arg( frame, a ) );
1665 }
1666
1667 return accumulator ? TRUE : NIL;
1668}
1669
1670/**
1671 * @brief Logical inverese: if the first argument is `nil`, return `t`, else `nil`.
1672 *
1673 * @param frame The stack frame.
1674 * @param frame_pointer A pointer to the stack frame.
1675 * @param env The evaluation environment.
1676 * @return struct cons_pointer `t` if the first argument is `nil`, else `nil`.
1677 */
1678struct cons_pointer lisp_not( struct stack_frame *frame,
1679 struct cons_pointer frame_pointer,
1680 struct cons_pointer env ) {
1681 return nilp( frame->arg[0] ) ? TRUE : NIL;
1682}
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
struct cons_pointer make_cons(struct cons_pointer car, struct cons_pointer cdr)
Construct a cons cell from this pair of pointers.
#define KEYTV
The string KEYW, considered as an unsigned int.
#define SPECIALTV
The string SPFM, considered as an unsigned int.
#define exceptionp(conspoint)
true if conspoint points to an exception, else false
#define writep(conspoint)
true if conspoint points to a write stream cell, else false.
#define VECTORPOINTTV
The string VECP, considered as an unsigned int.
int args
the number of arguments provided.
#define truep(conspoint)
true if conspoint points to something that is truthy, i.e.
#define SYMBOLTV
The string SYMB, considered as an unsigned int.
#define args_in_frame
union cons_space_object::@2 tag
union cons_space_object::@3 payload
#define NIL
a cons pointer which points to the special NIL cell
#define FUNCTIONTV
The string FUNC, considered as an unsigned int.
struct cons_pointer make_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Construct an exception cell.
struct cons_pointer make_lambda(struct cons_pointer args, struct cons_pointer body)
Construct a lambda (interpretable source) cell.
struct cons_pointer make_symbol_or_key(wint_t c, struct cons_pointer tail, uint32_t tag)
Construct a symbol or keyword from the character c and this tail.
struct cons_pointer c_string_to_lisp_keyword(wchar_t *symbol)
Return a lisp keyword representation of this wide character string.
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
#define STRINGTV
The string STRG, considered as an unsigned int.
struct cons_pointer make_string_like_thing(wint_t c, struct cons_pointer tail, uint32_t tag)
Construct a string from this character (which later will be UTF) and this tail.
#define TIMETV
The string TIME, considered as an unsigned int.
#define INTEGERTV
The string INTR, considered as an unsigned int.
#define FREETV
The string FREE, considered as an unsigned int.
#define readp(conspoint)
true if conspoint points to a read stream cell, else false
int c_length(struct cons_pointer arg)
Implementation of length in C.
#define RATIOTV
The string RTIO, considered as an unsigned int.
#define consp(conspoint)
true if conspoint points to a cons cell, else false
#define CONSTV
The string CONS, considered as an unsigned int.
#define LOOPTV
The string LOOX, considered as an unsigned int.
struct cons_pointer make_string(wint_t c, struct cons_pointer tail)
Construct a string from the character c and this tail.
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
#define NLAMBDATV
The string NLMD, considered as an unsigned int.
#define TRUETV
The string TRUE, considered as an unsigned int.
#define symbolp(conspoint)
true if conspoint points to a symbol cell, else false
#define REALTV
The string REAL, considered as an unsigned int.
struct cons_pointer c_string_to_lisp_string(wchar_t *string)
Return a lisp string representation of this wide character string.
struct cons_pointer inc_ref(struct cons_pointer pointer)
increment the reference count of the object at this cons pointer.
#define NILTV
The string NIL, considered as an unsigned int.
#define EXCEPTIONTV
The string EXEP, considered as an unsigned int.
#define TRUE
a cons pointer which points to the special T cell
#define sequencep(conspoint)
true if conspoint points to a sequence (list, string or, later, vector), else false.
#define stringp(conspoint)
true if conspoint points to a string cell, else false
struct cons_pointer c_string_to_lisp_symbol(wchar_t *symbol)
Return a lisp symbol representation of this wide character string.
struct cons_pointer c_car(struct cons_pointer arg)
Implementation of car in C.
#define LAMBDATV
The string LMDA, considered as an unsigned int.
#define WRITETV
The string WRIT, considered as an unsigned int.
struct cons_pointer c_type(struct cons_pointer pointer)
Get the Lisp type of the single argument.
struct cons_pointer make_nlambda(struct cons_pointer args, struct cons_pointer body)
Construct an nlambda (interpretable source) cell; to a lambda as a special form is to a function.
#define READTV
The string READ, considered as an unsigned int.
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
struct cons_pointer make_cons(struct cons_pointer car, struct cons_pointer cdr)
Construct a cons cell from this pair of pointers.
An indirect pointer to a cons cell.
an object in cons space.
A stack frame.
void debug_print_binding(struct cons_pointer key, struct cons_pointer val, bool deep, int level)
Standardise printing of binding trace messages.
Definition debug.c:150
void debug_println(int level)
print a line feed to stderr, if verbosity matches level.
Definition debug.c:85
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:136
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:101
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:41
void debug_print_object(struct cons_pointer pointer, int level)
print the object indicated by this pointer to stderr, if verbosity matches level.
Definition debug.c:119
#define DEBUG_BIND
Print messages debugging symbol binding.
Definition debug.h:38
#define DEBUG_LAMBDA
Print messages debugging lambda functions (interpretation).
Definition debug.h:66
#define DEBUG_REPL
Print messages debugging the read eval print loop.
Definition debug.h:73
#define DEBUG_IO
Print messages debugging input/output operations.
Definition debug.h:59
#define DEBUG_EVAL
Print messages debugging evaluation.
Definition debug.h:52
void dump_object(URL_FILE *output, struct cons_pointer pointer)
dump the object at this cons_pointer to this output stream.
Definition dump.c:59
bool equal(struct cons_pointer a, struct cons_pointer b)
Deep, and thus expensive, equality: true if these two objects have identical structure,...
Definition equal.c:332
bool eq(struct cons_pointer a, struct cons_pointer b)
Shallow, and thus cheap, equality: true if these two objects are the same object, else false.
Definition equal.c:28
int url_feof(URL_FILE *file)
Definition fopen.c:286
struct cons_pointer acquire_integer(int64_t value, struct cons_pointer more)
Supply small valued integers from the small integer cache, if available.
Definition integer.c:129
struct cons_pointer make_integer(int64_t value, struct cons_pointer more)
Allocate an integer cell representing this value and return a cons_pointer to it.
Definition integer.c:89
struct cons_pointer hashmap_keys(struct cons_pointer mapp)
return a flat list of all the keys in the hashmap indicated by map.
Definition intern.c:163
struct cons_pointer internedp(struct cons_pointer key, struct cons_pointer store)
Implementation of interned? in C.
Definition intern.c:282
struct cons_pointer c_assoc(struct cons_pointer key, struct cons_pointer store)
Implementation of assoc in C.
Definition intern.c:329
struct cons_pointer oblist
The global object list/or, to put it differently, the root namespace.
Definition intern.c:49
struct cons_pointer deep_bind(struct cons_pointer key, struct cons_pointer value)
Binds this key to this value in the global oblist, and returns the key.
Definition intern.c:452
struct cons_pointer set(struct cons_pointer key, struct cons_pointer value, struct cons_pointer store)
Return a new key/value store containing all the key/value pairs in this store with this key/value pai...
Definition intern.c:424
wint_t url_fgetwc(URL_FILE *input)
get one wide character from the buffer.
Definition io.c:151
URL_FILE * file_to_url_file(FILE *f)
given this file handle f, return a new url_file handle wrapping it.
Definition io.c:134
struct cons_pointer get_default_stream(bool inputp, struct cons_pointer env)
Resutn the current default input, or of inputp is false, output stream from this environment.
Definition io.c:411
struct cons_pointer lisp_nlambda(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Construct an interpretable special form.
Definition lispops.c:241
struct cons_pointer lisp_assoc(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; look up the value of a key in a store.
Definition lispops.c:836
struct cons_pointer c_apply(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Internal guts of apply.
Definition lispops.c:340
struct cons_pointer lisp_source(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function.
Definition lispops.c:1419
struct cons_pointer lisp_mapcar(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Definition lispops.c:1511
struct cons_pointer lisp_cons(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; returns a cell constructed from a and b.
Definition lispops.c:703
struct cons_pointer lisp_eq(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; are these two objects the same object? Shallow, cheap equality.
Definition lispops.c:871
struct cons_pointer lisp_read(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; read one complete lisp form and return it.
Definition lispops.c:962
struct cons_pointer throw_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Throw an exception.
Definition lispops.c:1249
struct cons_pointer lisp_exception(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; create an exception.
Definition lispops.c:1285
struct cons_pointer c_reverse(struct cons_pointer arg)
reverse a sequence (if it is a sequence); else return it unchanged.
Definition lispops.c:999
struct cons_pointer lisp_car(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; returns the first item (head) of a sequence.
Definition lispops.c:736
struct cons_pointer compose_body(struct stack_frame *frame)
Used to construct the body for lambda and nlambda expressions.
Definition lispops.c:195
struct cons_pointer eval_form(struct stack_frame *parent, struct cons_pointer parent_pointer, struct cons_pointer form, struct cons_pointer env)
Useful building block; evaluate this single form in the context of this parent stack frame and this e...
Definition lispops.c:65
struct cons_pointer lisp_count(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function: return the number of top level forms in the object which is the first (and only) argument,...
Definition lispops.c:943
struct cons_pointer lisp_let(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form: evaluate a series of forms in an environment in which these bindings are bound.
Definition lispops.c:1581
struct cons_pointer lisp_inspect(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function: dump/inspect one complete lisp expression and return NIL.
Definition lispops.c:1057
struct cons_pointer lisp_or(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Boolean or of arbitrarily many arguments.
Definition lispops.c:1657
struct cons_pointer lisp_length(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function: return, as an integer, the length of the sequence indicated by the first argument,...
Definition lispops.c:819
struct cons_pointer lisp_try(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
OK, the idea here (and I know this is less than perfect) is that the basic try special form in PSSE t...
Definition lispops.c:158
struct cons_pointer lisp_apply(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; apply the function which is the result of evaluating the first argument to the list of valu...
Definition lispops.c:565
long int c_count(struct cons_pointer p)
Definition lispops.c:910
struct cons_pointer c_progn(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer expressions, struct cons_pointer env)
Evaluate each of these expressions in this environment over this frame, returning only the value of t...
Definition lispops.c:1104
struct cons_pointer lisp_eval(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; evaluate the expression which is the first argument in the frame; further arguments are ign...
Definition lispops.c:505
struct cons_pointer eval_forms(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer list, struct cons_pointer env)
Evaluate all the forms in this list in the context of this stack frame and this env,...
Definition lispops.c:130
struct cons_pointer lisp_type(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function: get the Lisp type of the single argument.
Definition lispops.c:1094
struct cons_pointer eval_cond_clause(struct cons_pointer clause, struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
evaluate a single cond clause; if the test part succeeds return a pair whose car is TRUE and whose cd...
Definition lispops.c:1159
struct cons_pointer c_keys(struct cons_pointer store)
Definition lispops.c:841
struct cons_pointer lisp_progn(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form; evaluate the expressions which are listed in my arguments sequentially and return the v...
Definition lispops.c:1135
bool end_of_stringp(struct cons_pointer arg)
Definition lispops.c:681
struct cons_pointer lisp_keys(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Definition lispops.c:855
struct cons_pointer lisp_equal(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; are these two arguments identical? Deep, expensive equality.
Definition lispops.c:896
struct cons_pointer lisp_repl(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function: the read/eval/print loop.
Definition lispops.c:1305
struct cons_pointer lisp_set_shriek(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form; binds symbol in the namespace to value of value, altering the namespace in so doing,...
Definition lispops.c:655
struct cons_pointer lisp_append(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
should really be overwritten with a version in Lisp, since this is much easier to write in Lisp
Definition lispops.c:1499
struct cons_pointer lisp_not(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Logical inverese: if the first argument is nil, return t, else nil.
Definition lispops.c:1678
struct cons_pointer lisp_quote(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form; returns its argument (strictly first argument - only one is expected but this isn't at ...
Definition lispops.c:595
struct cons_pointer lisp_set(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; binds the value of name in the namespace to value of value, altering the namespace in so do...
Definition lispops.c:617
struct cons_pointer lisp_lambda(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Construct an interpretable function.
Definition lispops.c:224
struct cons_pointer lisp_cdr(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; returns the remainder of a sequence when the head is removed.
Definition lispops.c:780
struct cons_pointer lisp_list(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
construct and return a list of arbitrarily many arguments.
Definition lispops.c:1561
struct cons_pointer c_append(struct cons_pointer l1, struct cons_pointer l2)
A version of append which can conveniently be called from C.
Definition lispops.c:1452
struct cons_pointer lisp_oblist(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Return the object list (root namespace).
Definition lispops.c:187
struct cons_pointer lisp_cond(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form: conditional.
Definition lispops.c:1213
struct cons_pointer lisp_reverse(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; reverse the order of members in s sequence.
Definition lispops.c:1037
struct cons_pointer prompt_name
the name of the symbol to which the prompt is bound;
Definition lispops.c:47
struct cons_pointer eval_lambda(struct cons_space_object cell, struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Evaluate a lambda or nlambda expression.
Definition lispops.c:251
struct cons_pointer lisp_and(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Boolean and of arbitrarily many arguments.
Definition lispops.c:1636
#define truthy(arg)
return true if arg is nil, else false.
Definition peano.h:45
void println(URL_FILE *output)
Definition print.c:329
struct cons_pointer print(URL_FILE *output, struct cons_pointer pointer)
Print the cons-space object indicated by pointer to the stream indicated by output.
Definition print.c:156
struct cons_pointer read(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env, URL_FILE *input)
Read the next object on this input stream and return a cons_pointer to it.
Definition read.c:559
struct cons_pointer make_special_frame(struct cons_pointer previous, struct cons_pointer args, struct cons_pointer env)
A 'special' frame is exactly like a normal stack frame except that the arguments are unevaluated.
Definition stack.c:181
struct cons_pointer fetch_arg(struct stack_frame *frame, unsigned int index)
Fetch a pointer to the value of the local variable at this index.
Definition stack.c:323
void set_reg(struct stack_frame *frame, int reg, struct cons_pointer value)
set a register in a stack frame.
Definition stack.c:33
struct stack_frame * get_stack_frame(struct cons_pointer pointer)
get the actual stackframe object from this pointer, or NULL if pointer is not a stackframe pointer.
Definition stack.c:53
struct cons_pointer make_empty_frame(struct cons_pointer previous)
Make an empty stack frame, and return it.
Definition stack.c:75
struct cons_pointer make_stack_frame(struct cons_pointer previous, struct cons_pointer args, struct cons_pointer env)
Allocate a new stack frame with its previous pointer set to this value, its arguments set up from the...
Definition stack.c:116
#define pointer_to_vso(pointer)
given a pointer to a vector space object, return the object.
Definition vectorspace.h:55
#define HASHTV
Definition vectorspace.h:30
#define hashmapp(conspoint)
Definition vectorspace.h:32