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
28#include "memory/conspage.h"
29#include "debug.h"
30#include "memory/dump.h"
31#include "ops/equal.h"
32#include "arith/integer.h"
33#include "ops/intern.h"
34#include "io/io.h"
35#include "ops/lispops.h"
36#include "io/print.h"
37#include "io/read.h"
38#include "memory/stack.h"
39#include "memory/vectorspace.h"
40
41/**
42 * @brief the name of the symbol to which the prompt is bound;
43 *
44 * Set in init to `*prompt*`
45 */
47
48/*
49 * also to create in this section:
50 * struct cons_pointer lisp_let( struct cons_pointer args, struct cons_pointer env,
51 * struct stack_frame* frame);
52 *
53 * and others I haven't thought of yet.
54 */
55
56/**
57 * Useful building block; evaluate this single form in the context of this
58 * parent stack frame and this environment.
59 * @param parent the parent stack frame.
60 * @param form the form to be evaluated.
61 * @param env the evaluation environment.
62 * @return the result of evaluating the form.
63 */
64struct cons_pointer eval_form( struct stack_frame *parent,
65 struct cons_pointer parent_pointer,
66 struct cons_pointer form,
67 struct cons_pointer env ) {
68 debug_print( L"eval_form: ", DEBUG_EVAL );
71
72 struct cons_pointer result = form;
73 switch ( pointer2cell( form ).tag.value ) {
74 /* things which evaluate to themselves */
75 case EXCEPTIONTV:
76 case FREETV: // shouldn't happen, but anyway...
77 // FUNCTIONTV, LAMBDATV, NLAMBDATV, SPECIALTV ?
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 VECTORPOINTTV: ?
89 case WRITETV:
90 break;
91 default:
92 {
93 struct cons_pointer next_pointer =
94 make_empty_frame( parent_pointer );
95 inc_ref( next_pointer );
96
97 struct stack_frame *next = get_stack_frame( next_pointer );
98 set_reg( next, 0, form );
99 next->args = 1;
100
101 result = lisp_eval( next, next_pointer, env );
102
103 if ( !exceptionp( result ) ) {
104 /* if we're returning an exception, we should NOT free the
105 * stack frame. Corollary is, when we free an exception, we
106 * should free all the frames it's holding on to. */
107 dec_ref( next_pointer );
108 }
109 }
110 break;
111 }
112
113 debug_print( L"eval_form returning: ", DEBUG_EVAL );
116
117 return result;
118}
119
120/**
121 * Evaluate all the forms in this `list` in the context of this stack `frame`
122 * and this `env`, and return a list of their values. If the arg passed as
123 * `list` is not in fact a list, return NIL.
124 * @param frame the stack frame.
125 * @param list the list of forms to be evaluated.
126 * @param env the evaluation environment.
127 * @return a list of the the results of evaluating the forms.
128 */
129struct cons_pointer eval_forms( struct stack_frame *frame,
130 struct cons_pointer frame_pointer,
131 struct cons_pointer list,
132 struct cons_pointer env ) {
133 struct cons_pointer result = NIL;
134
135 while ( consp( list ) ) {
136 result =
137 make_cons( eval_form( frame, frame_pointer, c_car( list ), env ),
138 result );
139 list = c_cdr( list );
140 }
141
142 return c_reverse( result );
143}
144
145/**
146 * OK, the idea here (and I know this is less than perfect) is that the basic `try`
147 * special form in PSSE takes two arguments, the first, `body`, being a list of forms,
148 * and the second, `catch`, being a catch handler (which is also a list of forms).
149 * Forms from `body` are evaluated in turn until one returns an exception object,
150 * or until the list is exhausted. If the list was exhausted, then the value of
151 * evaluating the last form in `body` is returned. If an exception was encountered,
152 * then each of the forms in `catch` is evaluated and the value of the last of
153 * those is returned.
154 *
155 * This is experimental. It almost certainly WILL change.
156 */
157struct cons_pointer lisp_try( struct stack_frame *frame,
158 struct cons_pointer frame_pointer,
159 struct cons_pointer env ) {
160 struct cons_pointer result =
161 c_progn( frame, frame_pointer, frame->arg[0], env );
162
163 if ( exceptionp( result ) ) {
164 // TODO: need to put the exception into the environment!
165 result = c_progn( frame, frame_pointer, frame->arg[1],
168 ( L"*exception*" ), result ), env ) );
169 }
170
171 return result;
172}
173
174
175/**
176 * Return the object list (root namespace).
177 *
178 * * (oblist)
179 *
180 * @param frame the stack frame in which the expression is to be interpreted;
181 * @param frame_pointer a pointer to my stack_frame.
182 * @param env my environment (ignored).
183 * @return the root namespace.
184 */
185struct cons_pointer
186lisp_oblist( struct stack_frame *frame, struct cons_pointer frame_pointer,
187 struct cons_pointer env ) {
188 return oblist;
189}
190
191/**
192 * Used to construct the body for `lambda` and `nlambda` expressions.
193 */
194struct cons_pointer compose_body( struct stack_frame *frame ) {
195 struct cons_pointer body = frame->more;
196
197 for ( int i = args_in_frame - 1; i > 0; i-- ) {
198 if ( !nilp( body ) ) {
199 body = make_cons( frame->arg[i], body );
200 } else if ( !nilp( frame->arg[i] ) ) {
201 body = make_cons( frame->arg[i], body );
202 }
203 }
204
205 debug_print( L"compose_body returning ", DEBUG_LAMBDA );
207
208 return body;
209}
210
211/**
212 * Construct an interpretable function. *NOTE* that if `args` is a single symbol
213 * rather than a list, a varargs function will be created.
214 *
215 * (lambda args body)
216 *
217 * @param frame the stack frame in which the expression is to be interpreted;
218 * @param frame_pointer a pointer to my stack_frame.
219 * @param env the environment in which it is to be intepreted.
220 * @return an interpretable function with these `args` and this `body`.
221 */
222struct cons_pointer
223lisp_lambda( struct stack_frame *frame, struct cons_pointer frame_pointer,
224 struct cons_pointer env ) {
225 return make_lambda( frame->arg[0], compose_body( frame ) );
226}
227
228/**
229 * Construct an interpretable special form. *NOTE* that if `args` is a single symbol
230 * rather than a list, a varargs special form will be created.
231 *
232 * (nlambda args body)
233 *
234 * @param frame the stack frame in which the expression is to be interpreted;
235 * @param frame_pointer a pointer to my stack_frame.
236 * @param env the environment in which it is to be intepreted.
237 * @return an interpretable special form with these `args` and this `body`.
238 */
239struct cons_pointer
240lisp_nlambda( struct stack_frame *frame, struct cons_pointer frame_pointer,
241 struct cons_pointer env ) {
242 return make_nlambda( frame->arg[0], compose_body( frame ) );
243}
244
245void log_binding( struct cons_pointer name, struct cons_pointer val ) {
246 debug_print( L"\n\tBinding ", DEBUG_ALLOC );
248 debug_print( L" to ", DEBUG_ALLOC );
250}
251
252/**
253 * Evaluate a lambda or nlambda expression.
254 */
255struct cons_pointer
256eval_lambda( struct cons_space_object cell, struct stack_frame *frame,
257 struct cons_pointer frame_pointer, struct cons_pointer env ) {
258 struct cons_pointer result = NIL;
259 debug_print( L"eval_lambda called\n", DEBUG_LAMBDA );
261
262 struct cons_pointer new_env = env;
263 struct cons_pointer names = cell.payload.lambda.args;
264 struct cons_pointer body = cell.payload.lambda.body;
265
266 if ( consp( names ) ) {
267 /* if `names` is a list, bind successive items from that list
268 * to values of arguments */
269 for ( int i = 0; i < frame->args && consp( names ); i++ ) {
270 struct cons_pointer name = c_car( names );
271 struct cons_pointer val = frame->arg[i];
272
273 new_env = set( name, val, new_env );
274 log_binding( name, val );
275
276 names = c_cdr( names );
277 }
278 inc_ref( new_env );
279
280 /* \todo if there's more than `args_in_frame` arguments, bind those too. */
281 } else if ( symbolp( names ) ) {
282 /* if `names` is a symbol, rather than a list of symbols,
283 * then bind a list of the values of args to that symbol. */
284 /* \todo eval all the things in frame->more */
285 struct cons_pointer vals =
286 eval_forms( frame, frame_pointer, frame->more, env );
287
288 for ( int i = args_in_frame - 1; i >= 0; i-- ) {
289 struct cons_pointer val =
290 eval_form( frame, frame_pointer, frame->arg[i], env );
291
292 if ( nilp( val ) && nilp( vals ) ) { /* nothing */
293 } else {
294 vals = make_cons( val, vals );
295 }
296 }
297
298 new_env = set( names, vals, new_env );
299 inc_ref( new_env );
300 }
301
302 while ( !nilp( body ) ) {
303 struct cons_pointer sexpr = c_car( body );
304 body = c_cdr( body );
305
306 debug_print( L"In lambda: evaluating ", DEBUG_LAMBDA );
309
310 /* if a result is not the terminal result in the lambda, it's a
311 * side effect, and needs to be GCed */
312 if ( !nilp( result ) )
313 dec_ref( result );
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.special.
450 executable ) ( get_stack_frame( next_pointer ),
451 next_pointer, env );
452 debug_print( L"Special form returning: ", DEBUG_EVAL );
455 dec_ref( next_pointer );
456 }
457 }
458 break;
459
460 default:
461 {
462 int bs = sizeof( wchar_t ) * 1024;
463 wchar_t *buffer = malloc( bs );
464 memset( buffer, '\0', bs );
465 swprintf( buffer, bs,
466 L"Unexpected cell with tag %d (%4.4s) in function position",
467 fn_cell.tag.value, &fn_cell.tag.bytes[0] );
468 struct cons_pointer message =
469 c_string_to_lisp_string( buffer );
470 free( buffer );
471 result = throw_exception( message, frame_pointer );
472 }
473 }
474 }
475
476 debug_print( L"c_apply: returning: ", DEBUG_EVAL );
479
480 return result;
481}
482
483/**
484 * Function; evaluate the expression which is the first argument in the frame;
485 * further arguments are ignored.
486 *
487 * * (eval expression)
488 *
489 * @param frame my stack_frame.
490 * @param frame_pointer a pointer to my stack_frame.
491 * @param env my environment.
492 * @return
493 * * If `expression` is a number, string, `nil`, or `t`, returns `expression`.
494 * * If `expression` is a symbol, returns the value that expression is bound
495 * to in the evaluation environment (`env`).
496 * * If `expression` is a list, expects the car to be something that evaluates to a
497 * function or special form:
498 * * If a function, evaluates all the other top level elements in `expression` and
499 * passes them in a stack frame as arguments to the function;
500 * * If a special form, passes the cdr of expression to the special form as argument.
501 * @exception if `expression` is a symbol which is not bound in `env`.
502 */
503struct cons_pointer
504lisp_eval( struct stack_frame *frame, struct cons_pointer frame_pointer,
505 struct cons_pointer env ) {
506 debug_print( L"Eval: ", DEBUG_EVAL );
507 debug_dump_object( frame_pointer, DEBUG_EVAL );
508
509 struct cons_pointer result = frame->arg[0];
510 struct cons_space_object cell = pointer2cell( frame->arg[0] );
511
512 switch ( cell.tag.value ) {
513 case CONSTV:
514 result = c_apply( frame, frame_pointer, env );
515 break;
516
517 case SYMBOLTV:
518 {
519 struct cons_pointer canonical =
520 internedp( frame->arg[0], env );
521 if ( nilp( canonical ) ) {
522 struct cons_pointer message =
524 ( L"Attempt to take value of unbound symbol." ),
525 frame->arg[0] );
526 result = throw_exception( message, frame_pointer );
527 } else {
528 result = c_assoc( canonical, env );
529 inc_ref( result );
530 }
531 }
532 break;
533 /*
534 * \todo
535 * the Clojure practice of having a map serve in the function place of
536 * an s-expression is a good one and I should adopt it;
537 * H'mmm... this is working, but it isn't here. Where is it?
538 */
539 default:
540 result = frame->arg[0];
541 break;
542 }
543
544 debug_print( L"Eval returning ", DEBUG_EVAL );
545 debug_dump_object( result, DEBUG_EVAL );
546
547 return result;
548}
549
550
551/**
552 * Function; apply the function which is the result of evaluating the
553 * first argument to the list of values which is the result of evaluating
554 * the second argument
555 *
556 * * (apply fn args)
557 *
558 * @param frame my stack_frame.
559 * @param frame_pointer a pointer to my stack_frame.
560 * @param env my environment.
561 * @return the result of applying `fn` to `args`.
562 */
563struct cons_pointer
564lisp_apply( struct stack_frame *frame, struct cons_pointer frame_pointer,
565 struct cons_pointer env ) {
566 debug_print( L"Apply: ", DEBUG_EVAL );
567 debug_dump_object( frame_pointer, DEBUG_EVAL );
568
569 set_reg( frame, 0, make_cons( frame->arg[0], frame->arg[1] ) );
570 set_reg( frame, 1, NIL );
571
572 struct cons_pointer result = c_apply( frame, frame_pointer, env );
573
574 debug_print( L"Apply returning ", DEBUG_EVAL );
575 debug_dump_object( result, DEBUG_EVAL );
576
577 return result;
578}
579
580
581/**
582 * Special form;
583 * returns its argument (strictly first argument - only one is expected but
584 * this isn't at this stage checked) unevaluated.
585 *
586 * * (quote a)
587 *
588 * @param frame my stack_frame.
589 * @param frame_pointer a pointer to my stack_frame.
590 * @param env my environment (ignored).
591 * @return `a`, unevaluated,
592 */
593struct cons_pointer
594lisp_quote( struct stack_frame *frame, struct cons_pointer frame_pointer,
595 struct cons_pointer env ) {
596 return frame->arg[0];
597}
598
599
600/**
601 * Function;
602 * binds the value of `name` in the `namespace` to value of `value`, altering
603 * the namespace in so doing. Retuns `value`.
604 * `namespace` defaults to the oblist.
605 * \todo doesn't actually work yet for namespaces which are not the oblist.
606 *
607 * * (set name value)
608 * * (set name value namespace)
609 *
610 * @param frame my stack_frame.
611 * @param frame_pointer a pointer to my stack_frame.
612 * @param env my environment (ignored).
613 * @return `value`
614 */
615struct cons_pointer
616lisp_set( struct stack_frame *frame, struct cons_pointer frame_pointer,
617 struct cons_pointer env ) {
618 struct cons_pointer result = NIL;
619 struct cons_pointer namespace =
620 nilp( frame->arg[2] ) ? oblist : frame->arg[2];
621
622 if ( symbolp( frame->arg[0] ) ) {
623 deep_bind( frame->arg[0], frame->arg[1] );
624 result = frame->arg[1];
625 } else {
626 result =
629 ( L"The first argument to `set` is not a symbol: " ),
630 make_cons( frame->arg[0], NIL ) ),
631 frame_pointer );
632 }
633
634 return result;
635}
636
637
638/**
639 * Special form;
640 * binds `symbol` in the `namespace` to value of `value`, altering
641 * the namespace in so doing, and returns value. `namespace` defaults to
642 * the value of `oblist`.
643 * \todo doesn't actually work yet for namespaces which are not the oblist.
644 *
645 * * (set! symbol value)
646 * * (set! symbol value namespace)
647 *
648 * @param frame my stack_frame.
649 * @param frame_pointer a pointer to my stack_frame.
650 * @param env my environment (ignored).
651 * @return `value`
652 */
653struct cons_pointer
654lisp_set_shriek( struct stack_frame *frame, struct cons_pointer frame_pointer,
655 struct cons_pointer env ) {
656 struct cons_pointer result = NIL;
657 struct cons_pointer namespace = frame->arg[2];
658
659 if ( symbolp( frame->arg[0] ) ) {
660 struct cons_pointer val =
661 eval_form( frame, frame_pointer, frame->arg[1], env );
662 deep_bind( frame->arg[0], val );
663 result = val;
664 } else {
665 result =
668 ( L"The first argument to `set!` is not a symbol: " ),
669 make_cons( frame->arg[0], NIL ) ),
670 frame_pointer );
671 }
672
673 return result;
674}
675
676/**
677 * @return true if `arg` represents an end of string, else false.
678 * \todo candidate for moving to a memory/string.c file
679 */
680bool end_of_stringp( struct cons_pointer arg ) {
681 return nilp( arg ) ||
682 ( stringp( arg ) &&
683 pointer2cell( arg ).payload.string.character == ( wint_t ) '\0' );
684}
685
686/**
687 * Function;
688 * returns a cell constructed from a and b. If a is of type string but its
689 * cdr is nill, and b is of type string, then returns a new string cell;
690 * otherwise returns a new cons cell.
691 *
692 * Thus: `(cons "a" "bcd") -> "abcd"`, but `(cons "ab" "cd") -> ("ab" . "cd")`
693 *
694 * * (cons a b)
695 *
696 * @param frame my stack_frame.
697 * @param frame_pointer a pointer to my stack_frame.
698 * @param env my environment (ignored).
699 * @return a new cons cell whose `car` is `a` and whose `cdr` is `b`.
700 */
701struct cons_pointer
702lisp_cons( struct stack_frame *frame, struct cons_pointer frame_pointer,
703 struct cons_pointer env ) {
704 struct cons_pointer car = frame->arg[0];
705 struct cons_pointer cdr = frame->arg[1];
706 struct cons_pointer result;
707
708 if ( nilp( car ) && nilp( cdr ) ) {
709 return NIL;
710 } else if ( stringp( car ) && stringp( cdr ) &&
711 end_of_stringp( c_cdr( car ) ) ) {
712 result =
713 make_string( pointer2cell( car ).payload.string.character, cdr );
714 } else {
715 result = make_cons( car, cdr );
716 }
717
718 return result;
719}
720
721/**
722 * Function;
723 * returns the first item (head) of a sequence. Valid for cons cells,
724 * strings, read streams and TODO other things which can be considered as sequences.
725 *
726 * * (car expression)
727 *
728 * @param frame my stack_frame.
729 * @param frame_pointer a pointer to my stack_frame.
730 * @param env my environment (ignored).
731 * @return the first item (head) of `expression`.
732 * @exception if `expression` is not a sequence.
733 */
734struct cons_pointer
735lisp_car( struct stack_frame *frame, struct cons_pointer frame_pointer,
736 struct cons_pointer env ) {
737 struct cons_pointer result = NIL;
738 struct cons_space_object cell = pointer2cell( frame->arg[0] );
739
740 switch ( cell.tag.value ) {
741 case CONSTV:
742 result = cell.payload.cons.car;
743 break;
744 case NILTV:
745 break;
746 case READTV:
747 result =
748 make_string( url_fgetwc( cell.payload.stream.stream ), NIL );
749 break;
750 case STRINGTV:
751 result = make_string( cell.payload.string.character, NIL );
752 break;
753 default:
754 result =
756 ( L"Attempt to take CAR of non sequence" ),
757 frame_pointer );
758 }
759
760 return result;
761}
762
763/**
764 * Function;
765 * returns the remainder of a sequence when the head is removed. Valid for cons cells,
766 * strings, read streams and TODO other things which can be considered as sequences.
767 * *NOTE* that if the argument is an input stream, the first character is removed AND
768 * DISCARDED.
769 *
770 * * (cdr expression)
771 *
772 * @param frame my stack_frame.
773 * @param frame_pointer a pointer to my stack_frame.
774 * @param env my environment (ignored).
775 * @return the remainder of `expression` when the head is removed.
776 * @exception if `expression` is not a sequence.
777 */
778struct cons_pointer
779lisp_cdr( struct stack_frame *frame, struct cons_pointer frame_pointer,
780 struct cons_pointer env ) {
781 struct cons_pointer result = NIL;
782 struct cons_space_object cell = pointer2cell( frame->arg[0] );
783
784 switch ( cell.tag.value ) {
785 case CONSTV:
786 result = cell.payload.cons.cdr;
787 break;
788 case NILTV:
789 break;
790 case READTV:
791 url_fgetwc( cell.payload.stream.stream );
792 result = frame->arg[0];
793 break;
794 case STRINGTV:
795 result = cell.payload.string.cdr;
796 break;
797 default:
798 result =
800 ( L"Attempt to take CDR of non sequence" ),
801 frame_pointer );
802 }
803
804 return result;
805}
806
807/**
808 * Function: return, as an integer, the length of the sequence indicated by
809 * the first argument, or zero if it is not a sequence.
810 *
811 * * (length any)
812 *
813 * @param frame my stack_frame.
814 * @param frame_pointer a pointer to my stack_frame.
815 * @param env my environment (ignored).
816 * @return the length of `any`, if it is a sequence, or zero otherwise.
817 */
819 struct cons_pointer frame_pointer,
820 struct cons_pointer env ) {
821 return make_integer( c_length( frame->arg[0] ), NIL );
822}
823
824/**
825 * Function; look up the value of a `key` in a `store`.
826 *
827 * * (assoc key store)
828 *
829 * @param frame my stack_frame.
830 * @param frame_pointer a pointer to my stack_frame.
831 * @param env my environment (ignored).
832 * @return the value associated with `key` in `store`, or `nil` if not found.
833 */
834struct cons_pointer
835lisp_assoc( struct stack_frame *frame, struct cons_pointer frame_pointer,
836 struct cons_pointer env ) {
837 return c_assoc( frame->arg[0], frame->arg[1] );
838}
839
840struct cons_pointer c_keys( struct cons_pointer store ) {
841 struct cons_pointer result = NIL;
842
843 if ( hashmapp( store ) ) {
844 result = hashmap_keys( store );
845 } else if ( consp( store ) ) {
846 for ( struct cons_pointer c = store; !nilp( c ); c = c_cdr( c ) ) {
847 result = make_cons( c_car( c ), result );
848 }
849 }
850
851 return result;
852}
853
854struct cons_pointer lisp_keys( struct stack_frame *frame,
855 struct cons_pointer frame_pointer,
856 struct cons_pointer env ) {
857 return c_keys( frame->arg[0] );
858}
859
860/**
861 * Function; are these two objects the same object? Shallow, cheap equality.
862 *
863 * * (eq a b)
864 *
865 * @param frame my stack_frame.
866 * @param frame_pointer a pointer to my stack_frame.
867 * @param env my environment (ignored).
868 * @return `t` if `a` and `b` are pointers to the same object, else `nil`;
869 */
870struct cons_pointer lisp_eq( struct stack_frame *frame,
871 struct cons_pointer frame_pointer,
872 struct cons_pointer env ) {
873 return eq( frame->arg[0], frame->arg[1] ) ? TRUE : NIL;
874}
875
876/**
877 * Function; are these two arguments identical? Deep, expensive equality.
878 *
879 * * (equal a b)
880 *
881 * @param frame my stack_frame.
882 * @param frame_pointer a pointer to my stack_frame.
883 * @param env my environment (ignored).
884 * @return `t` if `a` and `b` are recursively identical, else `nil`.
885 */
886struct cons_pointer
887lisp_equal( struct stack_frame *frame, struct cons_pointer frame_pointer,
888 struct cons_pointer env ) {
889 return equal( frame->arg[0], frame->arg[1] ) ? TRUE : NIL;
890}
891
892/**
893 * Function; read one complete lisp form and return it. If read-stream is specified and
894 * is a read stream, then read from that stream, else the stream which is the value of
895 * `*in*` in the environment.
896 *
897 * * (read)
898 * * (read read-stream)
899 *
900 * @param frame my stack_frame.
901 * @param frame_pointer a pointer to my stack_frame.
902 * @param env my environment.
903 * @return the expression read.
904 */
905struct cons_pointer
906lisp_read( struct stack_frame *frame, struct cons_pointer frame_pointer,
907 struct cons_pointer env ) {
908#ifdef DEBUG
909 debug_print( L"entering lisp_read\n", DEBUG_IO );
910#endif
911 URL_FILE *input;
912
913 struct cons_pointer in_stream = readp( frame->arg[0] ) ?
914 frame->arg[0] : get_default_stream( true, env );
915
916 if ( readp( in_stream ) ) {
917 debug_print( L"lisp_read: setting input stream\n", DEBUG_IO );
918 debug_dump_object( in_stream, DEBUG_IO );
919 input = pointer2cell( in_stream ).payload.stream.stream;
920 inc_ref( in_stream );
921 } else {
922 input = file_to_url_file( stdin );
923 }
924
925 struct cons_pointer result = read( frame, frame_pointer, env, input );
926 debug_print( L"lisp_read returning\n", DEBUG_IO );
927 debug_dump_object( result, DEBUG_IO );
928
929 if ( readp( in_stream ) ) {
930 dec_ref( in_stream );
931 } else {
932 free( input );
933 }
934
935
936 return result;
937}
938
939
940/**
941 * reverse a sequence (if it is a sequence); else return it unchanged.
942 */
943struct cons_pointer c_reverse( struct cons_pointer arg ) {
944 struct cons_pointer result = NIL;
945
946 if ( sequencep( arg ) ) {
947 for ( struct cons_pointer p = arg; sequencep( p ); p = c_cdr( p ) ) {
948 struct cons_space_object o = pointer2cell( p );
949 switch ( o.tag.value ) {
950 case CONSTV:
951 result = make_cons( o.payload.cons.car, result );
952 break;
953 case STRINGTV:
954 result = make_string( o.payload.string.character, result );
955 break;
956 case SYMBOLTV:
957 result =
958 make_symbol_or_key( o.payload.string.character, result,
959 SYMBOLTV );
960 break;
961 }
962 }
963 } else {
964 result = arg;
965 }
966
967 return result;
968}
969
970
971/**
972 * Function; reverse the order of members in s sequence.
973 *
974 * * (reverse sequence)
975 *
976 * @param frame my stack_frame.
977 * @param frame_pointer a pointer to my stack_frame.
978 * @param env my environment (ignored).
979 * @return a sequence like this `sequence` but with the members in the reverse order.
980 */
982 struct cons_pointer frame_pointer,
983 struct cons_pointer env ) {
984 return c_reverse( frame->arg[0] );
985}
986
987/**
988 * Function: dump/inspect one complete lisp expression and return NIL. If
989 * write-stream is specified and is a write stream, then print to that stream,
990 * else the stream which is the value of
991 * `*out*` in the environment.
992 *
993 * * (inspect expr)
994 * * (inspect expr write-stream)
995 *
996 * @param frame my stack_frame.
997 * @param frame_pointer a pointer to my stack_frame.
998 * @param env my environment (from which the stream may be extracted).
999 * @return NIL.
1000 */
1002 struct cons_pointer frame_pointer,
1003 struct cons_pointer env ) {
1004 debug_print( L"Entering lisp_inspect\n", DEBUG_IO );
1005 struct cons_pointer result = NIL;
1006 struct cons_pointer out_stream = writep( frame->arg[1] )
1007 ? frame->arg[1]
1008 : get_default_stream( false, env );
1009 URL_FILE *output;
1010
1011 if ( writep( out_stream ) ) {
1012 debug_print( L"lisp_inspect: setting output stream\n", DEBUG_IO );
1013 debug_dump_object( out_stream, DEBUG_IO );
1014 output = pointer2cell( out_stream ).payload.stream.stream;
1015 } else {
1016 output = file_to_url_file( stderr );
1017 }
1018
1019 dump_object( output, frame->arg[0] );
1020
1021 debug_print( L"Leaving lisp_inspect", DEBUG_IO );
1022
1023 return result;
1024}
1025
1026/**
1027 * Function; print one complete lisp expression and return NIL. If write-stream is specified and
1028 * is a write stream, then print to that stream, else the stream which is the value of
1029 * `*out*` in the environment.
1030 *
1031 * * (print expr)
1032 * * (print expr write-stream)
1033 *
1034 * @param frame my stack_frame.
1035 * @param frame_pointer a pointer to my stack_frame.
1036 * @param env my environment (from which the stream may be extracted).
1037 * @return NIL.
1038 */
1039struct cons_pointer
1040lisp_print( struct stack_frame *frame, struct cons_pointer frame_pointer,
1041 struct cons_pointer env ) {
1042 debug_print( L"Entering print\n", DEBUG_IO );
1043 struct cons_pointer result = NIL;
1044 URL_FILE *output;
1045 struct cons_pointer out_stream = writep( frame->arg[1] ) ?
1046 frame->arg[1] : get_default_stream( false, env );
1047
1048 if ( writep( out_stream ) ) {
1049 debug_print( L"lisp_print: setting output stream\n", DEBUG_IO );
1050 debug_dump_object( out_stream, DEBUG_IO );
1051 output = pointer2cell( out_stream ).payload.stream.stream;
1052 inc_ref( out_stream );
1053 } else {
1054 output = file_to_url_file( stderr );
1055 }
1056
1057 debug_print( L"lisp_print: about to print\n", DEBUG_IO );
1058 debug_dump_object( frame->arg[0], DEBUG_IO );
1059
1060 result = print( output, frame->arg[0] );
1061
1062 debug_print( L"lisp_print returning\n", DEBUG_IO );
1063 debug_dump_object( result, DEBUG_IO );
1064
1065 if ( writep( out_stream ) ) {
1066 dec_ref( out_stream );
1067 } else {
1068 free( output );
1069 }
1070
1071 return result;
1072}
1073
1074
1075/**
1076 * Function: get the Lisp type of the single argument.
1077 *
1078 * * (type expression)
1079 *
1080 * @param frame my stack frame.
1081 * @param frame_pointer a pointer to my stack_frame.
1082 * @param env my environment (ignored).
1083 * @return As a Lisp string, the tag of `expression`.
1084 */
1085struct cons_pointer
1086lisp_type( struct stack_frame *frame, struct cons_pointer frame_pointer,
1087 struct cons_pointer env ) {
1088 return c_type( frame->arg[0] );
1089}
1090
1091/**
1092 * Evaluate each of these expressions in this `env`ironment over this `frame`,
1093 * returning only the value of the last.
1094 */
1095struct cons_pointer
1096c_progn( struct stack_frame *frame, struct cons_pointer frame_pointer,
1097 struct cons_pointer expressions, struct cons_pointer env ) {
1098 struct cons_pointer result = NIL;
1099
1100 while ( consp( expressions ) ) {
1101 struct cons_pointer r = result;
1102 inc_ref( r );
1103 result = eval_form( frame, frame_pointer, c_car( expressions ), env );
1104 dec_ref( r );
1105
1106 expressions = exceptionp( result ) ? NIL : c_cdr( expressions );
1107 }
1108
1109 return result;
1110}
1111
1112
1113/**
1114 * Special form; evaluate the expressions which are listed in my arguments
1115 * sequentially and return the value of the last. This function is called 'do'
1116 * in some dialects of Lisp.
1117 *
1118 * * (progn expressions...)
1119 *
1120 * @param frame my stack frame.
1121 * @param frame_pointer a pointer to my stack_frame.
1122 * @param env the environment in which expressions are evaluated.
1123 * @return the value of the last `expression` of the sequence which is my single
1124 * argument.
1125 */
1126struct cons_pointer
1127lisp_progn( struct stack_frame *frame, struct cons_pointer frame_pointer,
1128 struct cons_pointer env ) {
1129 struct cons_pointer result = NIL;
1130
1131 for ( int i = 0; i < args_in_frame && !nilp( frame->arg[i] ); i++ ) {
1132 struct cons_pointer r = result;
1133 inc_ref( r );
1134
1135 result = eval_form( frame, frame_pointer, frame->arg[i], env );
1136
1137 dec_ref( r );
1138 }
1139
1140 if ( consp( frame->more ) ) {
1141 result = c_progn( frame, frame_pointer, frame->more, env );
1142 }
1143
1144 return result;
1145}
1146
1147/**
1148 * Special form: conditional. Each `clause` is expected to be a list; if the first
1149 * item in such a list evaluates to non-NIL, the remaining items in that list
1150 * are evaluated in turn and the value of the last returned. If no arg `clause`
1151 * has a first element which evaluates to non NIL, then NIL is returned.
1152 *
1153 * * (cond clauses...)
1154 *
1155 * @param frame my stack frame.
1156 * @param frame_pointer a pointer to my stack_frame.
1157 * @param env the environment in which arguments will be evaluated.
1158 * @return the value of the last expression of the first successful `clause`.
1159 */
1160struct cons_pointer
1161lisp_cond( struct stack_frame *frame, struct cons_pointer frame_pointer,
1162 struct cons_pointer env ) {
1163 struct cons_pointer result = NIL;
1164 bool done = false;
1165
1166 for ( int i = 0; i < args_in_frame && !done; i++ ) {
1167 struct cons_pointer clause_pointer = frame->arg[i];
1168 debug_print( L"Cond clause: ", DEBUG_EVAL );
1169 debug_dump_object( clause_pointer, DEBUG_EVAL );
1170
1171 if ( consp( clause_pointer ) ) {
1172 struct cons_space_object cell = pointer2cell( clause_pointer );
1173 result =
1174 eval_form( frame, frame_pointer, c_car( clause_pointer ),
1175 env );
1176
1177 if ( !nilp( result ) ) {
1178 result =
1179 c_progn( frame, frame_pointer, c_cdr( clause_pointer ),
1180 env );
1181 done = true;
1182 }
1183 } else if ( nilp( clause_pointer ) ) {
1184 done = true;
1185 } else {
1187 ( L"Arguments to `cond` must be lists" ),
1188 frame_pointer );
1189 }
1190 }
1191 /* \todo if there are more than 8 clauses we need to continue into the
1192 * remainder */
1193
1194 return result;
1195}
1196
1197/**
1198 * Throw an exception.
1199 * `throw_exception` is a misnomer, because it doesn't obey the calling signature of a
1200 * lisp function; but it is nevertheless to be preferred to make_exception. A
1201 * real `throw_exception`, which does, will be needed.
1202 * object pointing to it. Then this should become a normal lisp function
1203 * which expects a normally bound frame and environment, such that
1204 * frame->arg[0] is the message, and frame->arg[1] is the cons-space
1205 * pointer to the frame in which the exception occurred.
1206 */
1207struct cons_pointer
1209 struct cons_pointer frame_pointer ) {
1210 debug_print( L"\nERROR: ", DEBUG_EVAL );
1211 debug_dump_object( message, DEBUG_EVAL );
1212 struct cons_pointer result = NIL;
1213
1214 struct cons_space_object cell = pointer2cell( message );
1215
1216 if ( cell.tag.value == EXCEPTIONTV ) {
1217 result = message;
1218 } else {
1219 result = make_exception( message, frame_pointer );
1220 }
1221
1222 return result;
1223}
1224
1225/**
1226 * Function; create an exception. Exceptions are special in as much as if an
1227 * exception is created in the binding of the arguments of any function, the
1228 * function will return the exception rather than whatever else it would
1229 * normally return. A function which detects a problem it cannot resolve
1230 * *should* return an exception.
1231 *
1232 * * (exception message frame)
1233 *
1234 * @param frame my stack frame.
1235 * @param frame_pointer a pointer to my stack_frame.
1236 * @param env the environment in which arguments will be evaluated.
1237 * @return areturns an exception whose message is this `message`, and whose
1238 * stack frame is the parent stack frame when the function is invoked.
1239 * `message` does not have to be a string but should be something intelligible
1240 * which can be read.
1241 * If `message` is itself an exception, returns that instead.
1242 */
1243struct cons_pointer
1244lisp_exception( struct stack_frame *frame, struct cons_pointer frame_pointer,
1245 struct cons_pointer env ) {
1246 struct cons_pointer message = frame->arg[0];
1247 return exceptionp( message ) ? message : throw_exception( message,
1248 frame->previous );
1249}
1250
1251/**
1252 * Function: the read/eval/print loop.
1253 *
1254 * * (repl)
1255 * * (repl prompt)
1256 * * (repl prompt input_stream output_stream)
1257 *
1258 * @param frame my stack frame.
1259 * @param frame_pointer a pointer to my stack_frame.
1260 * @param env the environment in which epressions will be evaluated.
1261 * @return the value of the last expression read.
1262 */
1263struct cons_pointer lisp_repl( struct stack_frame *frame,
1264 struct cons_pointer frame_pointer,
1265 struct cons_pointer env ) {
1266 struct cons_pointer expr = NIL;
1267
1268 debug_printf( DEBUG_REPL, L"Entering new inner REPL\n" );
1269
1270 struct cons_pointer input = get_default_stream( true, env );
1271 struct cons_pointer output = get_default_stream( false, env );
1272// struct cons_pointer prompt_name = c_string_to_lisp_symbol( L"*prompt*" );
1273 struct cons_pointer old_oblist = oblist;
1274 struct cons_pointer new_env = env;
1275
1276 if ( truep( frame->arg[0] ) ) {
1277 new_env = set( prompt_name, frame->arg[0], new_env );
1278 }
1279 if ( readp( frame->arg[1] ) ) {
1280 new_env =
1281 set( c_string_to_lisp_symbol( L"*in*" ), frame->arg[1], new_env );
1282 input = frame->arg[1];
1283 }
1284 if ( readp( frame->arg[2] ) ) {
1285 new_env =
1286 set( c_string_to_lisp_symbol( L"*out*" ), frame->arg[2], new_env );
1287 output = frame->arg[2];
1288 }
1289
1290 inc_ref( input );
1291 inc_ref( output );
1293
1294 URL_FILE *os = pointer2cell( output ).payload.stream.stream;
1295
1296
1297 /* \todo this is subtly wrong. If we were evaluating
1298 * (print (eval (read)))
1299 * then the stack frame for read would have the stack frame for
1300 * eval as parent, and it in turn would have the stack frame for
1301 * print as parent.
1302 */
1303 while ( readp( input ) && writep( output )
1304 && !url_feof( pointer2cell( input ).payload.stream.stream ) ) {
1305 /* OK, here's a really subtle problem: because lists are immutable, anything
1306 * bound in the oblist subsequent to this function being invoked isn't in the
1307 * environment. So, for example, changes to *prompt* or *log* made in the oblist
1308 * are not visible. So copy changes made in the oblist into the enviroment.
1309 * \todo the whole process of resolving symbol values needs to be revisited
1310 * when we get onto namespaces. */
1311 /* OK, there's something even more subtle here if the root namespace is a map.
1312 * H'mmmm... */
1313 if ( !eq( oblist, old_oblist ) ) {
1314 struct cons_pointer cursor = oblist;
1315
1316 while ( !nilp( cursor ) && !eq( cursor, old_oblist ) ) {
1317 struct cons_pointer old_new_env = new_env;
1319 ( L"lisp_repl: copying new oblist binding into REPL environment:\n",
1320 DEBUG_REPL );
1321 debug_print_object( c_car( cursor ), DEBUG_REPL );
1323
1324 new_env = make_cons( c_car( cursor ), new_env );
1325 inc_ref( new_env );
1326 dec_ref( old_new_env );
1327 cursor = c_cdr( cursor );
1328 }
1329 old_oblist = oblist;
1330 }
1331
1332 println( os );
1333
1334 struct cons_pointer prompt = c_assoc( prompt_name, new_env );
1335 if ( !nilp( prompt ) ) {
1336 print( os, prompt );
1337 }
1338
1339 expr = lisp_read( get_stack_frame( frame_pointer ), frame_pointer,
1340 new_env );
1341
1342 if ( exceptionp( expr )
1343 && url_feof( pointer2cell( input ).payload.stream.stream ) ) {
1344 /* suppress printing end of stream exception */
1345 break;
1346 }
1347
1348 println( os );
1349
1350 print( os, eval_form( frame, frame_pointer, expr, new_env ) );
1351
1352 dec_ref( expr );
1353 }
1354
1355 dec_ref( input );
1356 dec_ref( output );
1358 dec_ref( new_env );
1359
1360 debug_printf( DEBUG_REPL, L"Leaving inner repl\n" );
1361
1362 return expr;
1363}
1364
1365/**
1366 * Function. return the source code of the object which is its first argument,
1367 * if it is an executable and has source code.
1368 *
1369 * * (source object)
1370 *
1371 * @param frame my stack frame.
1372 * @param frame_pointer a pointer to my stack_frame.
1373 * @param env the environment (ignored).
1374 * @return the source of the `object` indicated, if it is a function, a lambda,
1375 * an nlambda, or a spcial form; else `nil`.
1376 */
1378 struct cons_pointer frame_pointer,
1379 struct cons_pointer env ) {
1380 struct cons_pointer result = NIL;
1381 struct cons_space_object cell = pointer2cell( frame->arg[0] );
1382 struct cons_pointer source_key = c_string_to_lisp_keyword( L"source" );
1383 switch ( cell.tag.value ) {
1384 case FUNCTIONTV:
1385 result = c_assoc( source_key, cell.payload.function.meta );
1386 break;
1387 case SPECIALTV:
1388 result = c_assoc( source_key, cell.payload.special.meta );
1389 break;
1390 case LAMBDATV:
1391 result = make_cons( c_string_to_lisp_symbol( L"lambda" ),
1392 make_cons( cell.payload.lambda.args,
1393 cell.payload.lambda.body ) );
1394 break;
1395 case NLAMBDATV:
1396 result = make_cons( c_string_to_lisp_symbol( L"nlambda" ),
1397 make_cons( cell.payload.lambda.args,
1398 cell.payload.lambda.body ) );
1399 break;
1400 }
1401 // \todo suffers from premature GC, and I can't see why!
1402 inc_ref( result );
1403
1404 return result;
1405}
1406
1407/**
1408 * A version of append which can conveniently be called from C.
1409 */
1410struct cons_pointer c_append( struct cons_pointer l1, struct cons_pointer l2 ) {
1411 switch ( pointer2cell( l1 ).tag.value ) {
1412 case CONSTV:
1413 if ( pointer2cell( l1 ).tag.value == pointer2cell( l2 ).tag.value ) {
1414 if ( nilp( c_cdr( l1 ) ) ) {
1415 return make_cons( c_car( l1 ), l2 );
1416 } else {
1417 return make_cons( c_car( l1 ),
1418 c_append( c_cdr( l1 ), l2 ) );
1419 }
1420 } else {
1422 ( L"Can't append: not same type" ), NIL );
1423 }
1424 break;
1425 case KEYTV:
1426 case STRINGTV:
1427 case SYMBOLTV:
1428 if ( pointer2cell( l1 ).tag.value == pointer2cell( l2 ).tag.value ) {
1429 if ( nilp( c_cdr( l1 ) ) ) {
1430 return
1431 make_string_like_thing( ( pointer2cell( l1 ).payload.
1432 string.character ), l2,
1433 pointer2cell( l1 ).tag.value );
1434 } else {
1435 return
1436 make_string_like_thing( ( pointer2cell( l1 ).payload.
1437 string.character ),
1438 c_append( c_cdr( l1 ), l2 ),
1439 pointer2cell( l1 ).tag.value );
1440 }
1441 } else {
1443 ( L"Can't append: not same type" ), NIL );
1444 }
1445 break;
1446 default:
1448 ( L"Can't append: not a sequence" ), NIL );
1449 break;
1450 }
1451}
1452
1453/**
1454 * should really be overwritten with a version in Lisp, since this is much easier to write in Lisp
1455 */
1457 struct cons_pointer frame_pointer,
1458 struct cons_pointer env ) {
1459 struct cons_pointer result = fetch_arg( frame, ( frame->args - 1 ) );
1460
1461 for ( int a = frame->args - 2; a >= 0; a-- ) {
1462 result = c_append( fetch_arg( frame, a ), result );
1463 }
1464
1465 return result;
1466}
1467
1469 struct cons_pointer frame_pointer,
1470 struct cons_pointer env ) {
1471 struct cons_pointer result = NIL;
1472 debug_print( L"Mapcar: ", DEBUG_EVAL );
1473 debug_dump_object( frame_pointer, DEBUG_EVAL );
1474 int i = 0;
1475
1476 for ( struct cons_pointer c = frame->arg[1]; truep( c ); c = c_cdr( c ) ) {
1477 struct cons_pointer expr =
1478 make_cons( frame->arg[0], make_cons( c_car( c ), NIL ) );
1479 inc_ref( expr );
1480
1481 debug_printf( DEBUG_EVAL, L"Mapcar %d, evaluating ", i );
1484
1485 struct cons_pointer r = eval_form( frame, frame_pointer, expr, env );
1486
1487 if ( exceptionp( r ) ) {
1488 result = r;
1489 inc_ref( expr ); // to protect exception from the later dec_ref
1490 break;
1491 } else {
1492 result = make_cons( r, result );
1493 }
1494 debug_printf( DEBUG_EVAL, L"Mapcar %d, result is ", i++ );
1495 debug_print_object( result, DEBUG_EVAL );
1497
1498 dec_ref( expr );
1499 }
1500
1501 result = consp( result ) ? c_reverse( result ) : result;
1502
1503 debug_print( L"Mapcar returning: ", DEBUG_EVAL );
1504 debug_print_object( result, DEBUG_EVAL );
1506
1507 return result;
1508}
1509
1510struct cons_pointer lisp_list( struct stack_frame *frame,
1511 struct cons_pointer frame_pointer,
1512 struct cons_pointer env ) {
1513 struct cons_pointer result = frame->more;
1514
1515 for ( int a = nilp( result ) ? frame->args - 1 : args_in_frame - 1;
1516 a >= 0; a-- ) {
1517 result = make_cons( fetch_arg( frame, a ), result );
1518 }
1519
1520 return result;
1521}
1522
1523/**
1524 * Special form: evaluate a series of forms in an environment in which
1525 * these bindings are bound.
1526 * This is `let*` in Common Lisp parlance; `let` in Clojure parlance.
1527 */
1528struct cons_pointer lisp_let( struct stack_frame *frame,
1529 struct cons_pointer frame_pointer,
1530 struct cons_pointer env ) {
1531 struct cons_pointer bindings = env;
1532 struct cons_pointer result = NIL;
1533
1534 for ( struct cons_pointer cursor = frame->arg[0];
1535 truep( cursor ); cursor = c_cdr( cursor ) ) {
1536 struct cons_pointer pair = c_car( cursor );
1537 struct cons_pointer symbol = c_car( pair );
1538
1539 if ( symbolp( symbol ) ) {
1540 bindings =
1542 ( symbol,
1543 eval_form( frame, frame_pointer, c_cdr( pair ),
1544 bindings ) ), bindings );
1545
1546 } else {
1547 result =
1549 ( L"Let: cannot bind, not a symbol" ),
1550 frame_pointer );
1551 break;
1552 }
1553 }
1554
1555 /* i.e., no exception yet */
1556 for ( int form = 1; !exceptionp( result ) && form < frame->args; form++ ) {
1557 result =
1558 eval_form( frame, frame_pointer, fetch_arg( frame, form ),
1559 bindings );
1560 }
1561
1562 return result;
1563
1564}
1565
1566// struct cons_pointer c_concat( struct cons_pointer a, struct cons_pointer b) {
1567// struct cons_pointer result = b;
1568
1569// if ( nilp( b.tag.value)) {
1570// result = make_cons( a, b);
1571// } else {
1572// if ( ! nilp( a)) {
1573// if (a.tag.value == b.tag.value) {
1574
1575// struct cons_pointer tail = c_concat( c_cdr( a), b);
1576
1577// switch ( a.tag.value) {
1578// case CONSTV:
1579// result = make_cons( c_car( a), tail);
1580// break;
1581// case KEYTV:
1582// case STRINGTV:
1583// case SYMBOLTV:
1584// result = make_string_like_thing()
1585
1586// }
1587
1588// } else {
1589// // throw an exception
1590// }
1591// }
1592// }
1593
1594
1595
1596// return result;
1597// }
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_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_LAMBDA
Print messages debugging lambda functions (interpretation).
Definition debug.h:63
#define DEBUG_REPL
Print messages debugging the read eval print loop.
Definition debug.h:70
#define DEBUG_IO
Print messages debugging input/output operations.
Definition debug.h:56
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:21
#define DEBUG_EVAL
Print messages debugging evaluation.
Definition debug.h:49
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:247
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:24
int url_feof(URL_FILE *file)
Definition fopen.c:286
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:162
struct cons_pointer internedp(struct cons_pointer key, struct cons_pointer store)
Implementation of interned? in C.
Definition intern.c:281
struct cons_pointer c_assoc(struct cons_pointer key, struct cons_pointer store)
Implementation of assoc in C.
Definition intern.c:327
struct cons_pointer oblist
The global object list/or, to put it differently, the root namespace.
Definition intern.c:48
struct cons_pointer deep_bind(struct cons_pointer key, struct cons_pointer value)
Binds this key to this value in the global oblist.
Definition intern.c:447
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:412
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:240
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:835
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:1377
struct cons_pointer lisp_mapcar(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Definition lispops.c:1468
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:702
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:870
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:906
struct cons_pointer throw_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Throw an exception.
Definition lispops.c:1208
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:1244
struct cons_pointer c_reverse(struct cons_pointer arg)
reverse a sequence (if it is a sequence); else return it unchanged.
Definition lispops.c:943
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:735
struct cons_pointer compose_body(struct stack_frame *frame)
Used to construct the body for lambda and nlambda expressions.
Definition lispops.c:194
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:64
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:1528
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:1001
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:818
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:157
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:564
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:1096
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:504
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:129
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:1086
struct cons_pointer c_keys(struct cons_pointer store)
Definition lispops.c:840
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:1127
bool end_of_stringp(struct cons_pointer arg)
Definition lispops.c:680
struct cons_pointer lisp_keys(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Definition lispops.c:854
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:887
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:1263
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:654
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:1456
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:594
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:616
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:223
struct cons_pointer lisp_print(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Function; print one complete lisp expression and return NIL.
Definition lispops.c:1040
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:779
struct cons_pointer lisp_list(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Definition lispops.c:1510
void log_binding(struct cons_pointer name, struct cons_pointer val)
Definition lispops.c:245
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:1410
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:186
struct cons_pointer lisp_cond(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer env)
Special form: conditional.
Definition lispops.c:1161
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:981
struct cons_pointer prompt_name
the name of the symbol to which the prompt is bound;
Definition lispops.c:46
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:256
void println(URL_FILE *output)
Definition print.c:275
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:151
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:183
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:286
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