4 include term-colours.4th
7 include catch-throw.4th
33 make-type boolean-type
34 make-type character-type
40 make-type primitive-proc-type
41 make-type compound-proc-type
42 make-type continuation-type
44 : istype? ( obj type -- obj bool )
49 \ ---- Exceptions ---- {{{
51 variable nextexception
54 create nextexception @ ,
63 make-exception recoverable-exception
64 make-exception unrecoverable-exception
66 : throw reset-term cr throw ;
70 \ ---- List-structured memory ---- {{{
72 20000 constant scheme-memsize
74 create car-cells scheme-memsize allot
75 create car-type-cells scheme-memsize allot
76 create cdr-cells scheme-memsize allot
77 create cdr-type-cells scheme-memsize allot
79 create nextfrees scheme-memsize allot
90 nextfrees nextfree @ + @
93 nextfree @ scheme-memsize >= if
97 nextfree @ scheme-memsize >= if
98 except-message: ." Out of memory!" unrecoverable-exception throw
102 : cons ( car-obj cdr-obj -- pair-obj )
103 cdr-type-cells nextfree @ + !
104 cdr-cells nextfree @ + !
105 car-type-cells nextfree @ + !
106 car-cells nextfree @ + !
112 : car ( pair-obj -- car-obj )
114 dup car-cells + @ swap
118 : cdr ( pair-obj -- car-obj )
120 dup cdr-cells + @ swap
124 : set-car! ( obj pair-obj -- )
126 rot swap car-type-cells + !
130 : set-cdr! ( obj pair-obj -- )
132 rot swap cdr-type-cells + !
136 variable object-stack-base
137 : init-object-stack-base
138 depth object-stack-base ! ;
141 : nil? nil-type istype? ;
144 : none? none-type istype? ;
146 : objvar create nil swap , , ;
148 : value@ ( objvar -- val ) @ ;
149 : type@ ( objvar -- type ) 1+ @ ;
150 : value! ( newval objvar -- ) ! ;
151 : type! ( newtype objvar -- ) 1+ ! ;
152 : obj! ( newobj objvar -- ) dup rot swap 1+ ! ! ;
153 : obj@ ( objvar -- obj ) dup @ swap 1+ @ ;
155 : objeq? ( obj obj -- bool )
158 : 2rot ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
159 >R >R ( a1 a2 b1 b2 )
160 2swap ( b1 b2 a1 a2 )
161 R> R> ( b1 b2 a1 a2 c1 c2 )
165 : -2rot ( a1 a2 b1 b2 c1 c2 -- c1 c2 a1 a2 b1 b2 )
166 2swap ( a1 a2 c1 c2 b1 b2 )
167 >R >R ( a1 a2 c1 c2 )
168 2swap ( c1 c2 a1 a2 )
172 : 2pick ( an bn an-1 bn-1 ... a0 b0 n -- an bn an-1 bn-1 ... a0 b0 an bn )
178 \ ---- Pre-defined symbols ---- {{{
182 : duplicate-charlist ( charlist -- copy )
184 2dup car 2swap cdr recurse cons
187 : charlist-equiv ( charlist charlist -- bool )
196 2drop 2drop true exit
198 2drop 2drop false exit
203 2drop 2drop false exit
210 car drop -rot car drop = if
211 cdr 2swap cdr recurse
217 : charlist>symbol ( charlist -- symbol-obj )
236 drop symbol-type 2dup
237 symbol-table obj@ cons
242 : cstr>charlist ( addr n -- charlist )
246 2dup drop @ character-type 2swap
254 : create-symbol ( -- )
262 does> dup @ swap 1+ @
265 create-symbol quote quote-symbol
266 create-symbol quasiquote quasiquote-symbol
267 create-symbol unquote unquote-symbol
268 create-symbol unquote-splicing unquote-splicing-symbol
269 create-symbol define define-symbol
270 create-symbol define-macro define-macro-symbol
271 create-symbol set! set!-symbol
272 create-symbol ok ok-symbol
273 create-symbol if if-symbol
274 create-symbol lambda lambda-symbol
275 create-symbol λ λ-symbol
276 create-symbol eof eof-symbol
277 create-symbol no-match no-match-symbol
279 \ Symbol to be bound to welcome message procedure by library
280 create-symbol welcome welcome-symbol
284 \ ---- Port I/O ---- {{{
286 ( Ports are pairs with the fid in the car and the peek buffer in the cdr. )
288 : fileport>fid ( fileport -- fid )
289 drop pair-type car drop ;
291 : get-last-peek ( fileport -- char/nil )
294 : set-last-peek ( char/nil fileport -- )
295 drop pair-type set-cdr!
298 : fid>fileport ( fid -- fileport )
299 fixnum-type nil cons drop port-type ;
301 : open-input-file ( addr n -- fileport )
302 r/o open-file drop fid>fileport
305 : close-port ( fileport -- )
306 fileport>fid close-file drop
309 objvar console-i/o-port
310 0 fixnum-type nil cons drop port-type console-i/o-port obj!
312 objvar current-input-port
313 console-i/o-port obj@ current-input-port obj!
315 : read-char ( port -- char )
316 2dup get-last-peek nil? if
318 2dup console-i/o-port obj@ objeq? if
322 fileport>fid pad 1 rot read-file 0= if
333 : peek-char ( port -- char )
334 2dup get-last-peek nil? if
336 2dup 2rot set-last-peek
342 variable read-line-buffer-span
343 variable read-line-buffer-offset
345 ( Hack to save original read-line while we transition to new one. )
346 : orig-read-line immediate
349 : read-line ( port -- string )
354 0 read-line-buffer-offset !
356 2over nil 2swap set-last-peek
358 2drop nil nil cons exit
361 1 read-line-buffer-offset !
365 2dup console-i/o-port obj@ objeq? if
367 pad read-line-buffer-offset @ + 200 expect cr
368 span @ read-line-buffer-offset @ + read-line-buffer-span !
370 pad read-line-buffer-offset @ + 200 2over fileport>fid orig-read-line
371 drop swap read-line-buffer-offset @ + read-line-buffer-span !
377 read-line-buffer-span @ 0>
379 pad read-line-buffer-span @ 1- + @ character-type 2swap cons
380 -1 read-line-buffer-span +!
384 nil cons drop string-type
390 : read-port ( fileport -- obj )
391 current-input-port obj!
394 : read-console ( -- obj )
395 console-i/o-port obj@ read-port ;
399 \ ---- Environments ---- {{{
401 : enclosing-env ( env -- env )
404 : first-frame ( env -- frame )
407 : make-frame ( vars vals -- frame )
410 : add-frame-to-env ( frame env -- env )
413 : frame-vars ( frame -- vars )
416 : frame-vals ( frame -- vals )
419 : add-binding ( var val frame -- )
420 2swap 2over frame-vals cons
422 2swap 2over frame-vars cons
426 : extend-env ( vars vals env -- env )
428 2swap add-frame-to-env
431 : get-vals-frame ( var frame -- vals | nil )
433 2swap frame-vals ( var vars vals )
439 -2rot ( vals var vars )
440 2over 2over car objeq? if
452 : get-vals ( var env -- vals | nil )
457 2over 2over first-frame
458 get-vals-frame nil? false = if
459 2swap 2drop 2swap 2drop
471 objvar var \ Used only for error messages
472 : lookup-var ( var env -- val )
476 except-message: ." tried to read unbound variable '" var obj@ print ." '."
477 recoverable-exception throw
483 : set-var ( var val env -- )
484 2rot 2dup var obj! ( val env var )
485 2swap ( val var env )
487 except-message: ." tried to set unbound variable '" var obj@ print ." '."
488 recoverable-exception throw
496 : define-var ( var val env -- )
497 first-frame ( var val frame )
498 2rot 2swap 2over 2over ( val var frame var frame )
500 get-vals-frame nil? if
505 ( val var frame vals )
506 2swap 2drop 2swap 2drop
511 : make-procedure ( params body env -- proc )
514 drop compound-proc-type
518 nil nil nil extend-env
523 \ ---- Continuations ---- {{{
525 : cons-return-stack ( -- listobj )
531 i 1+ @ fixnum-type 2swap cons
534 rsp@ 1- rsp0 - fixnum-type 2swap cons
537 : cons-param-stack ( -- listobj )
540 depth 2- object-stack-base @ = if
544 depth 2- object-stack-base @ do
551 depth 2- fixnum-type 2swap cons
558 cons drop continuation-type
561 : continuation->pstack-list
564 : continuation->rstack-list
567 : restore-param-stack ( continuation -- obj_stack continuation )
569 continuation->pstack-list
572 ( Allocate stack space first using psp!,
573 then copy objects from list. )
576 object-stack-base @ psp0 + + psp!
580 car drop 2- 0 swap do
583 PSP0 object-stack-base @ + i + 2 + !
584 PSP0 object-stack-base @ + i + 1 + !
593 : restore-continuation
594 \ TODO: replace current parameter and return stacks with
595 \ contents of continuation object.
600 \ ---- Primitives ---- {{{
602 : make-primitive ( cfa -- )
609 rot primitive-proc-type ( var prim )
610 global-env obj@ define-var
613 : ensure-arg-count ( args n -- )
615 drop nil objeq? false = if
616 except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
620 except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
627 : ensure-arg-type-and-count ( tn tn-1 ... t2 t1 args n -- )
629 drop nil objeq? false = if
630 except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
634 except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
637 2dup cdr 2swap car ( ... t1 n args' arg1 )
638 2rot 1- swap 2swap rot ( ... args' n-1 arg1 t1 )
640 except-message: ." Incorrect type for primitive procedure." recoverable-exception throw
648 : push-args-to-stack ( args -- arg1 arg2 ... argn )
658 : add-fa-checks ( cfa n -- cfa' )
659 here current @ 1+ dup @ , !
663 ['] 2dup , ['] lit , , ['] ensure-arg-count ,
664 ['] push-args-to-stack ,
665 ['] lit , , ['] execute ,
669 : add-fa-type-checks ( cfa t1 t2 ... tn n -- cfa' )
670 here current @ 1+ dup @ , !
677 dup ( cfa t1 t2 ... tn n m )
682 rot ['] lit , , ( cfa t1 t2 ... tn-1 n m )
688 ['] lit , , ['] ensure-arg-type-and-count ,
690 ['] push-args-to-stack ,
691 ['] lit , , ['] execute ,
697 : make-fa-primitive ( cfa n -- )
698 add-fa-checks make-primitive ;
700 : make-fa-type-primitive ( cfa t1 t2 ... tn n -- )
701 add-fa-type-checks make-primitive ;
704 bold fg red ." Incorrect argument type." reset-term cr
708 : ensure-arg-type ( arg type -- arg )
710 except-message: ." Incorrect argument type for primitive procedure." recoverable-exception throw
717 \ ---- Macros ---- {{{
721 ( Look up macro in macro table. Returns nil if
723 : lookup-macro ( name_symbol -- proc )
725 symbol-type istype? invert if
726 \ Early exit if argument is not a symbol
748 : make-macro ( name_symbol params body env -- )
751 2swap ( proc name_symbol )
758 2over 2over ( proc name table name table )
760 2swap 2drop ( proc table )
772 macro-table obj@ cons
781 variable stored-parse-idx
782 create parse-str 161 allot
783 variable parse-str-span
785 create parse-idx-stack 10 allot
786 variable parse-idx-sp
787 parse-idx-stack parse-idx-sp !
790 parse-idx @ parse-idx-sp @ !
795 parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
799 parse-idx-sp @ @ parse-idx ! ;
803 '\n' parse-str parse-str-span @ + !
804 1 parse-str-span +! ;
807 4 parse-str parse-str-span @ + !
808 1 parse-str-span +! ;
815 current-input-port obj@ console-i/o-port obj@ objeq? if
816 parse-str 160 expect cr
817 span @ parse-str-span !
819 parse-str 160 current-input-port obj@ fileport>fid orig-read-line
820 drop swap parse-str-span !
822 parse-str-span @ 0= and if append-eof then
833 : charavailable? ( -- bool )
834 parse-str-span @ parse-idx @ > ;
836 : nextchar ( -- char )
837 charavailable? false = if getline then
838 parse-str parse-idx @ + @ ;
841 : whitespace? ( -- bool )
853 nextchar [char] ( = or
854 nextchar [char] ) = or
857 : commentstart? ( -- bool )
858 nextchar [char] ; = ;
862 false \ Indicates whether or not we're eating a comment
865 dup whitespace? or commentstart? or
867 dup nextchar '\n' = and if
868 invert \ Stop eating comment
870 dup false = commentstart? and if
871 invert \ Begin eating comment
886 nextchar [char] - = ;
889 nextchar [char] + = ;
891 : fixnum? ( -- bool )
917 : flonum? ( -- bool )
924 \ Record starting parse idx:
925 \ Want to detect whether any characters (following +/-) were eaten.
932 [char] . nextchar = if
939 [char] e nextchar = [char] E nextchar = or if
947 drop pop-parse-idx false exit
955 \ This is a real number if characters were
956 \ eaten and the next characer is a delimiter.
957 parse-idx @ < delim? and
962 : ratnum? ( -- bool )
970 pop-parse-idx false exit
979 [char] / nextchar <> if
980 pop-parse-idx false exit
986 pop-parse-idx false exit
998 : boolean? ( -- bool )
999 nextchar [char] # <> if false exit then
1004 nextchar [char] t <>
1005 nextchar [char] f <>
1006 and if pop-parse-idx false exit then
1018 : str-equiv? ( str -- bool )
1035 delim? false = if drop false then
1040 : character? ( -- bool )
1041 nextchar [char] # <> if false exit then
1046 nextchar [char] \ <> if pop-parse-idx false exit then
1050 S" newline" str-equiv? if pop-parse-idx true exit then
1051 S" space" str-equiv? if pop-parse-idx true exit then
1052 S" tab" str-equiv? if pop-parse-idx true exit then
1054 charavailable? false = if pop-parse-idx false exit then
1060 nextchar [char] ( = ;
1062 : string? ( -- bool )
1063 nextchar [char] " = ;
1065 : readfixnum ( -- fixnum )
1076 10 * nextchar [char] 0 - +
1085 : readflonum ( -- flonum )
1087 dup 0< swap abs i->f
1089 [char] . nextchar = if
1095 nextchar [char] 0 - i->f ( f exp d )
1096 over f/ rot f+ ( exp f' )
1097 swap 10.0 f* ( f' exp' )
1104 [char] e nextchar = [char] E nextchar = or if
1107 readfixnum drop i->f
1118 : make-rational ( fixnum fixnum -- ratnum|fixnum )
1125 fixnum-type swap fixnum-type
1126 cons drop ratnum-type
1130 : readratnum ( -- ratnum )
1131 readfixnum inc-parse-idx readfixnum
1135 : readbool ( -- bool-obj )
1138 nextchar [char] f = if
1149 : readchar ( -- char-obj )
1153 S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
1154 S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
1155 S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
1157 nextchar character-type
1162 : readstring ( -- charlist )
1167 nextchar [char] " <>
1169 nextchar [char] \ = if
1172 [char] n of '\n' endof
1173 [char] " of [char] " endof
1179 inc-parse-idx character-type
1182 ( firstchar prevchar thischar )
1185 2drop 2swap 2drop 2dup ( thischar thischar )
1187 ( firstchar thischar prevchar )
1188 2over 2swap set-cdr! ( firstchar thischar )
1192 \ Discard previous character
1198 ." No delimiter following right double quote. Aborting." cr
1210 : readsymbol ( -- charlist )
1211 delim? if nil exit then
1213 nextchar inc-parse-idx character-type
1220 : readpair ( -- pairobj )
1224 nextchar [char] ) = if
1229 ." No delimiter following right paren. Aborting." cr
1238 \ Read first pair element
1243 nextchar [char] . = if
1248 ." No delimiter following '.'. Aborting." cr
1262 \ Parse a scheme expression
1297 nextchar [char] " <> if
1298 bold red ." Missing closing double-quote." reset-term cr
1316 nextchar [char] ) <> if
1317 bold red ." Missing closing paren." reset-term cr
1326 nextchar [char] ' = if
1328 quote-symbol recurse nil cons cons exit
1331 nextchar [char] ` = if
1333 quasiquote-symbol recurse nil cons cons exit
1336 nextchar [char] , = if
1338 nextchar [char] @ = if
1340 unquote-splicing-symbol recurse nil cons cons exit
1342 unquote-symbol recurse nil cons cons exit
1352 nextchar [char] ) = if
1354 except-message: ." unmatched closing parenthesis." recoverable-exception throw
1357 \ Anything else is parsed as a symbol
1358 readsymbol charlist>symbol
1360 \ Replace λ with lambda
1361 2dup λ-symbol objeq? if
1370 \ ---- Syntax ---- {{{
1372 : self-evaluating? ( obj -- obj bool )
1373 boolean-type istype? if true exit then
1374 fixnum-type istype? if true exit then
1375 flonum-type istype? if true exit then
1376 ratnum-type istype? if true exit then
1377 character-type istype? if true exit then
1378 string-type istype? if true exit then
1379 nil-type istype? if true exit then
1380 none-type istype? if true exit then
1385 : tagged-list? ( obj tag-obj -- obj bool )
1387 pair-type istype? false = if
1393 : quote? ( obj -- obj bool )
1394 quote-symbol tagged-list? ;
1396 : quote-body ( quote-obj -- quote-body-obj )
1399 : variable? ( obj -- obj bool )
1400 symbol-type istype? ;
1402 : definition? ( obj -- obj bool )
1403 define-symbol tagged-list? ;
1405 : definition-var ( obj -- var )
1408 : definition-val ( obj -- val )
1411 : assignment? ( obj -- obj bool )
1412 set!-symbol tagged-list? ;
1414 : assignment-var ( obj -- var )
1417 : assignment-val ( obj -- val )
1420 : macro-definition? ( obj -- obj bool )
1421 define-macro-symbol tagged-list? ;
1423 : macro-definition-name ( exp -- mname )
1426 : macro-definition-params ( exp -- params )
1429 : macro-definition-body ( exp -- body )
1432 : if? ( obj -- obj bool )
1433 if-symbol tagged-list? ;
1435 : if-predicate ( ifobj -- pred )
1438 : if-consequent ( ifobj -- conseq )
1441 : if-alternative ( ifobj -- alt|none )
1449 : false? ( boolobj -- boolean )
1450 boolean-type istype? if
1451 false boolean-type objeq?
1457 : true? ( boolobj -- bool )
1460 : lambda? ( obj -- obj bool )
1461 lambda-symbol tagged-list? ;
1463 : lambda-parameters ( obj -- params )
1466 : lambda-body ( obj -- body )
1469 : application? ( obj -- obj bool )
1472 : operator ( obj -- operator )
1475 : operands ( obj -- operands )
1478 : nooperands? ( operands -- bool )
1481 : first-operand ( operands -- operand )
1484 : rest-operands ( operands -- other-operands )
1487 : procedure-params ( proc -- params )
1488 drop pair-type car ;
1490 : procedure-body ( proc -- body )
1491 drop pair-type cdr car ;
1493 : procedure-env ( proc -- body )
1494 drop pair-type cdr cdr car ;
1496 ( Ensure terminating symbol arg name is handled
1497 specially to allow for variadic procedures. )
1498 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1500 2over nil? false = if
1501 except-message: ." Too many arguments for compound procedure." recoverable-exception throw
1508 symbol-type istype? if
1518 except-message: ." Too few arguments for compound procedure." recoverable-exception throw
1525 recurse ( argvals argnames argvals'' argnames'' )
1526 2rot car 2swap cons ( argvals argvals'' argnames' )
1527 2rot car 2rot cons ( argnames' argvals' )
1533 \ ---- Analyze ---- {{{
1535 : evaluate-eproc ( eproc env --- res )
1546 2drop \ get rid of null
1550 \ Final element of eproc list is primitive procedure
1551 drop \ dump type signifier
1553 goto \ jump straight to primitive procedure (executor)
1556 : self-evaluating-executor ( exp env -- exp )
1559 : analyze-self-evaluating ( exp --- eproc )
1560 ['] self-evaluating-executor primitive-proc-type
1564 : quote-executor ( exp env -- exp )
1567 : analyze-quoted ( exp -- eproc )
1570 ['] quote-executor primitive-proc-type
1574 : variable-executor ( var env -- val )
1577 : analyze-variable ( exp -- eproc )
1578 ['] variable-executor primitive-proc-type
1582 : definition-executor ( var val-eproc env -- ok )
1583 2swap 2over ( var env val-eproc env )
1584 evaluate-eproc 2swap ( var val env )
1589 : analyze-definition ( exp -- eproc )
1591 2swap definition-val analyze
1593 ['] definition-executor primitive-proc-type
1597 : assignment-executor ( var val-eproc env -- ok )
1598 2swap 2over ( var env val-eproc env )
1599 evaluate-eproc 2swap ( var val env )
1604 : analyze-assignment ( exp -- eproc )
1606 2swap assignment-val analyze ( var val-eproc )
1608 ['] assignment-executor primitive-proc-type
1612 : sequence-executor ( eproc-list env -- res )
1616 2dup cdr ( env elist elist-rest)
1619 -2rot car 2over ( elist-rest env elist-head env )
1620 evaluate-eproc ( elist-rest env head-res )
1621 2drop 2swap ( env elist-rest )
1625 ['] evaluate-eproc goto
1629 : (analyze-sequence) ( explist -- eproc-list )
1638 : analyze-sequence ( explist -- eproc )
1640 ['] sequence-executor primitive-proc-type
1645 : macro-definition-executor ( name params bproc env -- ok )
1646 make-macro ok-symbol
1649 : analyze-macro-definition ( exp -- eproc )
1650 2dup macro-definition-name
1651 2swap 2dup macro-definition-params
1652 2swap macro-definition-body analyze-sequence
1654 ['] macro-definition-executor primitive-proc-type
1655 nil cons cons cons cons
1658 : if-executor ( cproc aproc pproc env -- res )
1659 2swap 2over ( cproc aproc env pproc env -- res )
1668 ['] evaluate-eproc goto
1671 : analyze-if ( exp -- eproc )
1672 2dup if-consequent analyze
1673 2swap 2dup if-alternative analyze
1674 2swap if-predicate analyze
1676 ['] if-executor primitive-proc-type
1677 nil cons cons cons cons
1680 : lambda-executor ( params bproc env -- res )
1682 ( Although this is packaged up as a regular compound procedure,
1683 the "body" element contains an _eproc_ to be evaluated in an
1684 environment resulting from extending env with the parameter
1688 : analyze-lambda ( exp -- eproc )
1689 2dup lambda-parameters
1693 except-message: ." encountered lambda with an empty body." recoverable-exception throw
1698 ['] lambda-executor primitive-proc-type
1702 : operand-eproc-list ( operands -- eprocs )
1710 : evaluate-operand-eprocs ( env aprocs -- vals )
1714 2over 2over car 2swap evaluate-eproc ( env aprocs thisval )
1715 -2rot cdr recurse ( thisval restvals )
1720 : apply ( vals proc )
1722 primitive-proc-type of
1726 compound-proc-type of
1727 2dup procedure-body ( argvals proc bproc )
1728 -2rot 2dup procedure-params ( bproc argvals proc argnames )
1729 -2rot procedure-env ( bproc argnames argvals procenv )
1735 extend-env ( bproc env )
1737 ['] evaluate-eproc goto
1740 continuation-type of
1741 \ TODO: Apply continuation
1744 except-message: ." object '" drop print ." ' not applicable." recoverable-exception throw
1748 : application-executor ( operator-proc arg-procs env -- res )
1749 2rot 2over ( aprocs env fproc env )
1750 evaluate-eproc ( aprocs env proc )
1752 -2rot 2swap ( proc env aprocs )
1753 evaluate-operand-eprocs ( proc vals )
1760 : analyze-application ( exp -- eproc )
1761 2dup operator analyze
1762 2swap operands operand-eproc-list
1764 ['] application-executor primitive-proc-type
1768 :noname ( exp --- eproc )
1770 self-evaluating? if analyze-self-evaluating exit then
1772 quote? if analyze-quoted exit then
1774 variable? if analyze-variable exit then
1776 definition? if analyze-definition exit then
1778 assignment? if analyze-assignment exit then
1780 macro-definition? if analyze-macro-definition exit then
1782 if? if analyze-if exit then
1784 lambda? if analyze-lambda exit then
1786 application? if analyze-application exit then
1788 except-message: ." tried to analyze unknown expression type." recoverable-exception throw
1794 \ ---- Macro Expansion ---- {{{
1796 ( Simply evaluates the given procedure with expbody as its argument. )
1797 : macro-eval ( proc expbody -- result )
1799 2dup procedure-body ( expbody proc bproc )
1800 -2rot 2dup procedure-params ( bproc expbody proc argnames )
1801 -2rot procedure-env ( bproc argnames expbody procenv )
1807 extend-env ( bproc env )
1809 ['] evaluate-eproc goto
1812 : expand-macro ( exp -- result )
1813 pair-type istype? invert if exit then
1815 2dup car symbol-type istype? invert if 2drop exit then
1817 lookup-macro nil? if 2drop exit then
1819 2over cdr macro-eval
1821 2dup no-match-symbol objeq? if
1827 R> drop ['] expand goto-deferred
1830 : expand-definition ( exp -- result )
1834 2swap definition-val expand
1835 nil ( define var val' nil )
1839 : expand-assignment ( exp -- result )
1843 2swap assignment-val expand
1844 nil ( define var val' nil )
1848 : expand-list ( exp -- res )
1856 : macro-definition-nameparams
1859 : expand-define-macro ( exp -- res )
1860 define-macro-symbol 2swap
1861 2dup macro-definition-nameparams
1862 2swap macro-definition-body expand-list
1866 : expand-lambda ( exp -- res )
1868 2dup lambda-parameters
1869 2swap lambda-body expand-list
1873 : expand-if ( exp -- res )
1876 2dup if-predicate expand
1877 2swap 2dup if-consequent expand
1878 2swap if-alternative none? if
1886 : expand-application ( exp -- res )
1887 2dup operator expand
1888 2swap operands expand-list
1892 :noname ( exp -- result )
1895 self-evaluating? if exit then
1899 definition? if expand-definition exit then
1901 assignment? if expand-assignment exit then
1903 macro-definition? if expand-define-macro exit then
1905 lambda? if expand-lambda exit then
1907 if? if expand-if exit then
1909 application? if expand-application exit then
1915 :noname ( exp env -- res )
1916 2swap expand analyze 2swap evaluate-eproc
1919 \ ---- Print ---- {{{
1921 : printfixnum ( fixnum -- ) drop 0 .R ;
1923 : printflonum ( flonum -- ) drop f. ;
1925 : printratnum ( ratnum -- )
1927 car print ." /" cdr print
1930 : printbool ( bool -- )
1938 : printchar ( charobj -- )
1941 9 of ." #\tab" endof
1942 bl of ." #\space" endof
1943 '\n' of ." #\newline" endof
1949 : (printstring) ( stringobj -- )
1950 nil? if 2drop exit then
1954 '\n' of ." \n" drop endof
1955 [char] \ of ." \\" drop endof
1956 [char] " of [char] \ emit [char] " emit drop endof
1962 : printstring ( stringobj -- )
1967 : printsymbol ( symbolobj -- )
1968 nil-type istype? if 2drop exit then
1974 : printnil ( nilobj -- )
1977 : printpair ( pairobj -- )
1981 nil-type istype? if 2drop exit then
1982 pair-type istype? if space recurse exit then
1986 : printprim ( primobj -- )
1987 2drop ." <primitive procedure>" ;
1989 : printcomp ( primobj -- )
1990 2drop ." <compound procedure>" ;
1992 : printcont ( primobj --)
1993 2drop ." <continuation>" ;
1995 : printnone ( noneobj -- )
1996 2drop ." Unspecified return value" ;
1998 : printport ( port -- )
2002 fixnum-type istype? if printfixnum exit then
2003 flonum-type istype? if printflonum exit then
2004 ratnum-type istype? if printratnum exit then
2005 boolean-type istype? if printbool exit then
2006 character-type istype? if printchar exit then
2007 string-type istype? if printstring exit then
2008 symbol-type istype? if printsymbol exit then
2009 nil-type istype? if printnil exit then
2010 pair-type istype? if ." (" printpair ." )" exit then
2011 primitive-proc-type istype? if printprim exit then
2012 compound-proc-type istype? if printcomp exit then
2013 continuation-type istype? if printcont exit then
2014 none-type istype? if printnone exit then
2015 port-type istype? if printport exit then
2017 except-message: ." tried to print object with unknown type." recoverable-exception throw
2022 \ ---- Garbage Collection ---- {{{
2024 ( Notes on garbage collection:
2025 This is a mark-sweep garbage collector, invoked by cons.
2026 The roots of the object tree used by the marking routine
2027 include all objects in the parameter stack, and several
2028 other fixed roots such as global-env, symbol-table, macro-table,
2029 and the console-i/o-port.
2031 NO OTHER OBJECTS WILL BE MARKED!
2033 This places implicit restrictions on when cons can be invoked.
2034 Invoking cons when live objects are stored on the return stack
2035 or in other variables than the above will result in possible
2036 memory corruption if the cons triggers the GC. )
2039 : pairlike? ( obj -- obj bool )
2040 pair-type istype? if true exit then
2041 string-type istype? if true exit then
2042 symbol-type istype? if true exit then
2043 compound-proc-type istype? if true exit then
2044 port-type istype? if true exit then
2049 : pairlike-marked? ( obj -- obj bool )
2050 over nextfrees + @ 0=
2053 : mark-pairlike ( obj -- obj )
2054 over nextfrees + 0 swap !
2063 : gc-mark-obj ( obj -- )
2065 pairlike? invert if 2drop exit then
2066 pairlike-marked? if 2drop exit then
2077 scheme-memsize nextfree !
2078 0 scheme-memsize 1- do
2079 nextfrees i + @ 0<> if
2080 nextfree @ nextfrees i + !
2086 \ Following a GC, this gives the amount of free memory
2090 nextfrees i + @ 0= if 1+ then
2094 \ Debugging word - helps spot memory that is retained
2097 nextfrees i + @ 0<> if
2109 symbol-table obj@ gc-mark-obj
2110 macro-table obj@ gc-mark-obj
2111 console-i/o-port obj@ gc-mark-obj
2112 global-env obj@ gc-mark-obj
2114 depth object-stack-base @ do
2123 \ ." (" gc-count-marked . ." pairs marked as used.)" cr
2124 ; is collect-garbage
2130 \ ---- Loading files ---- {{{
2132 : load ( addr n -- finalResult )
2137 ok-symbol ( port res )
2141 \ bold fg blue ." READ from " 2over drop . ." ==> " reset-term
2143 2over read-port ( port res obj )
2148 2dup EOF character-type objeq? if
2149 2drop 2swap close-port
2153 2swap 2drop ( port obj )
2155 global-env obj@ eval ( port res )
2161 \ ---- Standard Library ---- {{{
2163 include scheme-primitives.4th
2165 init-object-stack-base
2166 s" scheme-library.scm" load 2drop
2172 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
2173 : repl-body ( -- bool )
2174 cr bold fg green ." > " reset-term
2178 2dup EOF character-type objeq? if
2180 bold fg blue ." Moriturus te saluto." reset-term cr
2184 global-env obj@ eval
2186 fg cyan ." ; " print reset-term
2194 init-object-stack-base
2196 \ Display welcome message
2197 welcome-symbol nil cons global-env obj@ eval 2drop
2202 recoverable-exception of false endof
2203 unrecoverable-exception of true endof