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
535 : cons-param-stack ( -- listobj )
538 depth 2- object-stack-base @ = if
542 depth 2- object-stack-base @ do
554 cons drop continuation-type
557 : continuation->pstack-list
560 : continuation->rstack-list
563 : restore-param-stack ( continuation -- obj_stack continuation )
566 continuation->pstack-list
568 ( Idea: allocate stack space first using psp!,
569 then copy objects from list. )
572 : restore-continuation
573 \ TODO: replace current parameter and return stacks with
574 \ contents of continuation object.
579 \ ---- Primitives ---- {{{
581 : make-primitive ( cfa -- )
588 rot primitive-proc-type ( var prim )
589 global-env obj@ define-var
592 : ensure-arg-count ( args n -- )
594 drop nil objeq? false = if
595 except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
599 except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
606 : ensure-arg-type-and-count ( tn tn-1 ... t2 t1 args n -- )
608 drop nil objeq? false = if
609 except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
613 except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
616 2dup cdr 2swap car ( ... t1 n args' arg1 )
617 2rot 1- swap 2swap rot ( ... args' n-1 arg1 t1 )
619 except-message: ." Incorrect type for primitive procedure." recoverable-exception throw
627 : push-args-to-stack ( args -- arg1 arg2 ... argn )
637 : add-fa-checks ( cfa n -- cfa' )
638 here current @ 1+ dup @ , !
642 ['] 2dup , ['] lit , , ['] ensure-arg-count ,
643 ['] push-args-to-stack ,
644 ['] lit , , ['] execute ,
648 : add-fa-type-checks ( cfa t1 t2 ... tn n -- cfa' )
649 here current @ 1+ dup @ , !
656 dup ( cfa t1 t2 ... tn n m )
661 rot ['] lit , , ( cfa t1 t2 ... tn-1 n m )
667 ['] lit , , ['] ensure-arg-type-and-count ,
669 ['] push-args-to-stack ,
670 ['] lit , , ['] execute ,
676 : make-fa-primitive ( cfa n -- )
677 add-fa-checks make-primitive ;
679 : make-fa-type-primitive ( cfa t1 t2 ... tn n -- )
680 add-fa-type-checks make-primitive ;
683 bold fg red ." Incorrect argument type." reset-term cr
687 : ensure-arg-type ( arg type -- arg )
689 except-message: ." Incorrect argument type for primitive procedure." recoverable-exception throw
696 \ ---- Macros ---- {{{
700 ( Look up macro in macro table. Returns nil if
702 : lookup-macro ( name_symbol -- proc )
704 symbol-type istype? invert if
705 \ Early exit if argument is not a symbol
727 : make-macro ( name_symbol params body env -- )
730 2swap ( proc name_symbol )
737 2over 2over ( proc name table name table )
739 2swap 2drop ( proc table )
751 macro-table obj@ cons
760 variable stored-parse-idx
761 create parse-str 161 allot
762 variable parse-str-span
764 create parse-idx-stack 10 allot
765 variable parse-idx-sp
766 parse-idx-stack parse-idx-sp !
769 parse-idx @ parse-idx-sp @ !
774 parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
778 parse-idx-sp @ @ parse-idx ! ;
782 '\n' parse-str parse-str-span @ + !
783 1 parse-str-span +! ;
786 4 parse-str parse-str-span @ + !
787 1 parse-str-span +! ;
794 current-input-port obj@ console-i/o-port obj@ objeq? if
795 parse-str 160 expect cr
796 span @ parse-str-span !
798 parse-str 160 current-input-port obj@ fileport>fid orig-read-line
799 drop swap parse-str-span !
801 parse-str-span @ 0= and if append-eof then
812 : charavailable? ( -- bool )
813 parse-str-span @ parse-idx @ > ;
815 : nextchar ( -- char )
816 charavailable? false = if getline then
817 parse-str parse-idx @ + @ ;
820 : whitespace? ( -- bool )
832 nextchar [char] ( = or
833 nextchar [char] ) = or
836 : commentstart? ( -- bool )
837 nextchar [char] ; = ;
841 false \ Indicates whether or not we're eating a comment
844 dup whitespace? or commentstart? or
846 dup nextchar '\n' = and if
847 invert \ Stop eating comment
849 dup false = commentstart? and if
850 invert \ Begin eating comment
865 nextchar [char] - = ;
868 nextchar [char] + = ;
870 : fixnum? ( -- bool )
896 : flonum? ( -- bool )
903 \ Record starting parse idx:
904 \ Want to detect whether any characters (following +/-) were eaten.
911 [char] . nextchar = if
918 [char] e nextchar = [char] E nextchar = or if
926 drop pop-parse-idx false exit
934 \ This is a real number if characters were
935 \ eaten and the next characer is a delimiter.
936 parse-idx @ < delim? and
941 : ratnum? ( -- bool )
949 pop-parse-idx false exit
958 [char] / nextchar <> if
959 pop-parse-idx false exit
965 pop-parse-idx false exit
977 : boolean? ( -- bool )
978 nextchar [char] # <> if false exit then
985 and if pop-parse-idx false exit then
997 : str-equiv? ( str -- bool )
1014 delim? false = if drop false then
1019 : character? ( -- bool )
1020 nextchar [char] # <> if false exit then
1025 nextchar [char] \ <> if pop-parse-idx false exit then
1029 S" newline" str-equiv? if pop-parse-idx true exit then
1030 S" space" str-equiv? if pop-parse-idx true exit then
1031 S" tab" str-equiv? if pop-parse-idx true exit then
1033 charavailable? false = if pop-parse-idx false exit then
1039 nextchar [char] ( = ;
1041 : string? ( -- bool )
1042 nextchar [char] " = ;
1044 : readfixnum ( -- fixnum )
1055 10 * nextchar [char] 0 - +
1064 : readflonum ( -- flonum )
1066 dup 0< swap abs i->f
1068 [char] . nextchar = if
1074 nextchar [char] 0 - i->f ( f exp d )
1075 over f/ rot f+ ( exp f' )
1076 swap 10.0 f* ( f' exp' )
1083 [char] e nextchar = [char] E nextchar = or if
1086 readfixnum drop i->f
1097 : make-rational ( fixnum fixnum -- ratnum|fixnum )
1104 fixnum-type swap fixnum-type
1105 cons drop ratnum-type
1109 : readratnum ( -- ratnum )
1110 readfixnum inc-parse-idx readfixnum
1114 : readbool ( -- bool-obj )
1117 nextchar [char] f = if
1128 : readchar ( -- char-obj )
1132 S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
1133 S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
1134 S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
1136 nextchar character-type
1141 : readstring ( -- charlist )
1146 nextchar [char] " <>
1148 nextchar [char] \ = if
1151 [char] n of '\n' endof
1152 [char] " of [char] " endof
1158 inc-parse-idx character-type
1161 ( firstchar prevchar thischar )
1164 2drop 2swap 2drop 2dup ( thischar thischar )
1166 ( firstchar thischar prevchar )
1167 2over 2swap set-cdr! ( firstchar thischar )
1171 \ Discard previous character
1177 ." No delimiter following right double quote. Aborting." cr
1189 : readsymbol ( -- charlist )
1190 delim? if nil exit then
1192 nextchar inc-parse-idx character-type
1199 : readpair ( -- pairobj )
1203 nextchar [char] ) = if
1208 ." No delimiter following right paren. Aborting." cr
1217 \ Read first pair element
1222 nextchar [char] . = if
1227 ." No delimiter following '.'. Aborting." cr
1241 \ Parse a scheme expression
1276 nextchar [char] " <> if
1277 bold red ." Missing closing double-quote." reset-term cr
1295 nextchar [char] ) <> if
1296 bold red ." Missing closing paren." reset-term cr
1305 nextchar [char] ' = if
1307 quote-symbol recurse nil cons cons exit
1310 nextchar [char] ` = if
1312 quasiquote-symbol recurse nil cons cons exit
1315 nextchar [char] , = if
1317 nextchar [char] @ = if
1319 unquote-splicing-symbol recurse nil cons cons exit
1321 unquote-symbol recurse nil cons cons exit
1331 nextchar [char] ) = if
1333 except-message: ." unmatched closing parenthesis." recoverable-exception throw
1336 \ Anything else is parsed as a symbol
1337 readsymbol charlist>symbol
1339 \ Replace λ with lambda
1340 2dup λ-symbol objeq? if
1349 \ ---- Syntax ---- {{{
1351 : self-evaluating? ( obj -- obj bool )
1352 boolean-type istype? if true exit then
1353 fixnum-type istype? if true exit then
1354 flonum-type istype? if true exit then
1355 ratnum-type istype? if true exit then
1356 character-type istype? if true exit then
1357 string-type istype? if true exit then
1358 nil-type istype? if true exit then
1359 none-type istype? if true exit then
1364 : tagged-list? ( obj tag-obj -- obj bool )
1366 pair-type istype? false = if
1372 : quote? ( obj -- obj bool )
1373 quote-symbol tagged-list? ;
1375 : quote-body ( quote-obj -- quote-body-obj )
1378 : variable? ( obj -- obj bool )
1379 symbol-type istype? ;
1381 : definition? ( obj -- obj bool )
1382 define-symbol tagged-list? ;
1384 : definition-var ( obj -- var )
1387 : definition-val ( obj -- val )
1390 : assignment? ( obj -- obj bool )
1391 set!-symbol tagged-list? ;
1393 : assignment-var ( obj -- var )
1396 : assignment-val ( obj -- val )
1399 : macro-definition? ( obj -- obj bool )
1400 define-macro-symbol tagged-list? ;
1402 : macro-definition-name ( exp -- mname )
1405 : macro-definition-params ( exp -- params )
1408 : macro-definition-body ( exp -- body )
1411 : if? ( obj -- obj bool )
1412 if-symbol tagged-list? ;
1414 : if-predicate ( ifobj -- pred )
1417 : if-consequent ( ifobj -- conseq )
1420 : if-alternative ( ifobj -- alt|none )
1428 : false? ( boolobj -- boolean )
1429 boolean-type istype? if
1430 false boolean-type objeq?
1436 : true? ( boolobj -- bool )
1439 : lambda? ( obj -- obj bool )
1440 lambda-symbol tagged-list? ;
1442 : lambda-parameters ( obj -- params )
1445 : lambda-body ( obj -- body )
1448 : application? ( obj -- obj bool )
1451 : operator ( obj -- operator )
1454 : operands ( obj -- operands )
1457 : nooperands? ( operands -- bool )
1460 : first-operand ( operands -- operand )
1463 : rest-operands ( operands -- other-operands )
1466 : procedure-params ( proc -- params )
1467 drop pair-type car ;
1469 : procedure-body ( proc -- body )
1470 drop pair-type cdr car ;
1472 : procedure-env ( proc -- body )
1473 drop pair-type cdr cdr car ;
1475 ( Ensure terminating symbol arg name is handled
1476 specially to allow for variadic procedures. )
1477 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1479 2over nil? false = if
1480 except-message: ." Too many arguments for compound procedure." recoverable-exception throw
1487 symbol-type istype? if
1497 except-message: ." Too few arguments for compound procedure." recoverable-exception throw
1504 recurse ( argvals argnames argvals'' argnames'' )
1505 2rot car 2swap cons ( argvals argvals'' argnames' )
1506 2rot car 2rot cons ( argnames' argvals' )
1512 \ ---- Analyze ---- {{{
1514 : evaluate-eproc ( eproc env --- res )
1525 2drop \ get rid of null
1529 \ Final element of eproc list is primitive procedure
1530 drop \ dump type signifier
1532 goto \ jump straight to primitive procedure (executor)
1535 : self-evaluating-executor ( exp env -- exp )
1538 : analyze-self-evaluating ( exp --- eproc )
1539 ['] self-evaluating-executor primitive-proc-type
1543 : quote-executor ( exp env -- exp )
1546 : analyze-quoted ( exp -- eproc )
1549 ['] quote-executor primitive-proc-type
1553 : variable-executor ( var env -- val )
1556 : analyze-variable ( exp -- eproc )
1557 ['] variable-executor primitive-proc-type
1561 : definition-executor ( var val-eproc env -- ok )
1562 2swap 2over ( var env val-eproc env )
1563 evaluate-eproc 2swap ( var val env )
1568 : analyze-definition ( exp -- eproc )
1570 2swap definition-val analyze
1572 ['] definition-executor primitive-proc-type
1576 : assignment-executor ( var val-eproc env -- ok )
1577 2swap 2over ( var env val-eproc env )
1578 evaluate-eproc 2swap ( var val env )
1583 : analyze-assignment ( exp -- eproc )
1585 2swap assignment-val analyze ( var val-eproc )
1587 ['] assignment-executor primitive-proc-type
1591 : sequence-executor ( eproc-list env -- res )
1595 2dup cdr ( env elist elist-rest)
1598 -2rot car 2over ( elist-rest env elist-head env )
1599 evaluate-eproc ( elist-rest env head-res )
1600 2drop 2swap ( env elist-rest )
1604 ['] evaluate-eproc goto
1608 : (analyze-sequence) ( explist -- eproc-list )
1617 : analyze-sequence ( explist -- eproc )
1619 ['] sequence-executor primitive-proc-type
1624 : macro-definition-executor ( name params bproc env -- ok )
1625 make-macro ok-symbol
1628 : analyze-macro-definition ( exp -- eproc )
1629 2dup macro-definition-name
1630 2swap 2dup macro-definition-params
1631 2swap macro-definition-body analyze-sequence
1633 ['] macro-definition-executor primitive-proc-type
1634 nil cons cons cons cons
1637 : if-executor ( cproc aproc pproc env -- res )
1638 2swap 2over ( cproc aproc env pproc env -- res )
1647 ['] evaluate-eproc goto
1650 : analyze-if ( exp -- eproc )
1651 2dup if-consequent analyze
1652 2swap 2dup if-alternative analyze
1653 2swap if-predicate analyze
1655 ['] if-executor primitive-proc-type
1656 nil cons cons cons cons
1659 : lambda-executor ( params bproc env -- res )
1661 ( Although this is packaged up as a regular compound procedure,
1662 the "body" element contains an _eproc_ to be evaluated in an
1663 environment resulting from extending env with the parameter
1667 : analyze-lambda ( exp -- eproc )
1668 2dup lambda-parameters
1672 except-message: ." encountered lambda with an empty body." recoverable-exception throw
1677 ['] lambda-executor primitive-proc-type
1681 : operand-eproc-list ( operands -- eprocs )
1689 : evaluate-operand-eprocs ( env aprocs -- vals )
1693 2over 2over car 2swap evaluate-eproc ( env aprocs thisval )
1694 -2rot cdr recurse ( thisval restvals )
1699 : apply ( vals proc )
1701 primitive-proc-type of
1705 compound-proc-type of
1706 2dup procedure-body ( argvals proc bproc )
1707 -2rot 2dup procedure-params ( bproc argvals proc argnames )
1708 -2rot procedure-env ( bproc argnames argvals procenv )
1714 extend-env ( bproc env )
1716 ['] evaluate-eproc goto
1719 continuation-type of
1720 \ TODO: Apply continuation
1723 except-message: ." object '" drop print ." ' not applicable." recoverable-exception throw
1727 : application-executor ( operator-proc arg-procs env -- res )
1728 2rot 2over ( aprocs env fproc env )
1729 evaluate-eproc ( aprocs env proc )
1731 -2rot 2swap ( proc env aprocs )
1732 evaluate-operand-eprocs ( proc vals )
1739 : analyze-application ( exp -- eproc )
1740 2dup operator analyze
1741 2swap operands operand-eproc-list
1743 ['] application-executor primitive-proc-type
1747 :noname ( exp --- eproc )
1749 self-evaluating? if analyze-self-evaluating exit then
1751 quote? if analyze-quoted exit then
1753 variable? if analyze-variable exit then
1755 definition? if analyze-definition exit then
1757 assignment? if analyze-assignment exit then
1759 macro-definition? if analyze-macro-definition exit then
1761 if? if analyze-if exit then
1763 lambda? if analyze-lambda exit then
1765 application? if analyze-application exit then
1767 except-message: ." tried to analyze unknown expression type." recoverable-exception throw
1773 \ ---- Macro Expansion ---- {{{
1775 ( Simply evaluates the given procedure with expbody as its argument. )
1776 : macro-eval ( proc expbody -- result )
1778 2dup procedure-body ( expbody proc bproc )
1779 -2rot 2dup procedure-params ( bproc expbody proc argnames )
1780 -2rot procedure-env ( bproc argnames expbody procenv )
1786 extend-env ( bproc env )
1788 ['] evaluate-eproc goto
1791 : expand-macro ( exp -- result )
1792 pair-type istype? invert if exit then
1794 2dup car symbol-type istype? invert if 2drop exit then
1796 lookup-macro nil? if 2drop exit then
1798 2over cdr macro-eval
1800 2dup no-match-symbol objeq? if
1806 R> drop ['] expand goto-deferred
1809 : expand-definition ( exp -- result )
1813 2swap definition-val expand
1814 nil ( define var val' nil )
1818 : expand-assignment ( exp -- result )
1822 2swap assignment-val expand
1823 nil ( define var val' nil )
1827 : expand-list ( exp -- res )
1835 : macro-definition-nameparams
1838 : expand-define-macro ( exp -- res )
1839 define-macro-symbol 2swap
1840 2dup macro-definition-nameparams
1841 2swap macro-definition-body expand-list
1845 : expand-lambda ( exp -- res )
1847 2dup lambda-parameters
1848 2swap lambda-body expand-list
1852 : expand-if ( exp -- res )
1855 2dup if-predicate expand
1856 2swap 2dup if-consequent expand
1857 2swap if-alternative none? if
1865 : expand-application ( exp -- res )
1866 2dup operator expand
1867 2swap operands expand-list
1871 :noname ( exp -- result )
1874 self-evaluating? if exit then
1878 definition? if expand-definition exit then
1880 assignment? if expand-assignment exit then
1882 macro-definition? if expand-define-macro exit then
1884 lambda? if expand-lambda exit then
1886 if? if expand-if exit then
1888 application? if expand-application exit then
1894 :noname ( exp env -- res )
1895 2swap expand analyze 2swap evaluate-eproc
1898 \ ---- Print ---- {{{
1900 : printfixnum ( fixnum -- ) drop 0 .R ;
1902 : printflonum ( flonum -- ) drop f. ;
1904 : printratnum ( ratnum -- )
1906 car print ." /" cdr print
1909 : printbool ( bool -- )
1917 : printchar ( charobj -- )
1920 9 of ." #\tab" endof
1921 bl of ." #\space" endof
1922 '\n' of ." #\newline" endof
1928 : (printstring) ( stringobj -- )
1929 nil? if 2drop exit then
1933 '\n' of ." \n" drop endof
1934 [char] \ of ." \\" drop endof
1935 [char] " of [char] \ emit [char] " emit drop endof
1941 : printstring ( stringobj -- )
1946 : printsymbol ( symbolobj -- )
1947 nil-type istype? if 2drop exit then
1953 : printnil ( nilobj -- )
1956 : printpair ( pairobj -- )
1960 nil-type istype? if 2drop exit then
1961 pair-type istype? if space recurse exit then
1965 : printprim ( primobj -- )
1966 2drop ." <primitive procedure>" ;
1968 : printcomp ( primobj -- )
1969 2drop ." <compound procedure>" ;
1971 : printcont ( primobj --)
1972 2drop ." <continuation>" ;
1974 : printnone ( noneobj -- )
1975 2drop ." Unspecified return value" ;
1977 : printport ( port -- )
1981 fixnum-type istype? if printfixnum exit then
1982 flonum-type istype? if printflonum exit then
1983 ratnum-type istype? if printratnum exit then
1984 boolean-type istype? if printbool exit then
1985 character-type istype? if printchar exit then
1986 string-type istype? if printstring exit then
1987 symbol-type istype? if printsymbol exit then
1988 nil-type istype? if printnil exit then
1989 pair-type istype? if ." (" printpair ." )" exit then
1990 primitive-proc-type istype? if printprim exit then
1991 compound-proc-type istype? if printcomp exit then
1992 continuation-type istype? if printcont exit then
1993 none-type istype? if printnone exit then
1994 port-type istype? if printport exit then
1996 except-message: ." tried to print object with unknown type." recoverable-exception throw
2001 \ ---- Garbage Collection ---- {{{
2003 ( Notes on garbage collection:
2004 This is a mark-sweep garbage collector, invoked by cons.
2005 The roots of the object tree used by the marking routine
2006 include all objects in the parameter stack, and several
2007 other fixed roots such as global-env, symbol-table, macro-table,
2008 and the console-i/o-port.
2010 NO OTHER OBJECTS WILL BE MARKED!
2012 This places implicit restrictions on when cons can be invoked.
2013 Invoking cons when live objects are stored on the return stack
2014 or in other variables than the above will result in possible
2015 memory corruption if the cons triggers the GC. )
2018 : pairlike? ( obj -- obj bool )
2019 pair-type istype? if true exit then
2020 string-type istype? if true exit then
2021 symbol-type istype? if true exit then
2022 compound-proc-type istype? if true exit then
2023 port-type istype? if true exit then
2028 : pairlike-marked? ( obj -- obj bool )
2029 over nextfrees + @ 0=
2032 : mark-pairlike ( obj -- obj )
2033 over nextfrees + 0 swap !
2042 : gc-mark-obj ( obj -- )
2044 pairlike? invert if 2drop exit then
2045 pairlike-marked? if 2drop exit then
2056 scheme-memsize nextfree !
2057 0 scheme-memsize 1- do
2058 nextfrees i + @ 0<> if
2059 nextfree @ nextfrees i + !
2065 \ Following a GC, this gives the amount of free memory
2069 nextfrees i + @ 0= if 1+ then
2073 \ Debugging word - helps spot memory that is retained
2076 nextfrees i + @ 0<> if
2088 symbol-table obj@ gc-mark-obj
2089 macro-table obj@ gc-mark-obj
2090 console-i/o-port obj@ gc-mark-obj
2091 global-env obj@ gc-mark-obj
2093 depth object-stack-base @ do
2102 \ ." (" gc-count-marked . ." pairs marked as used.)" cr
2103 ; is collect-garbage
2107 \ ---- Loading files ---- {{{
2109 : load ( addr n -- finalResult )
2114 ok-symbol ( port res )
2118 \ bold fg blue ." READ from " 2over drop . ." ==> " reset-term
2120 2over read-port ( port res obj )
2125 2dup EOF character-type objeq? if
2126 2drop 2swap close-port
2130 2swap 2drop ( port obj )
2132 global-env obj@ eval ( port res )
2138 \ ---- Standard Library ---- {{{
2140 include scheme-primitives.4th
2142 init-object-stack-base
2143 s" scheme-library.scm" load 2drop
2149 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
2150 : repl-body ( -- bool )
2151 cr bold fg green ." > " reset-term
2155 2dup EOF character-type objeq? if
2157 bold fg blue ." Moriturus te saluto." reset-term cr
2161 global-env obj@ eval
2163 fg cyan ." ; " print reset-term
2171 init-object-stack-base
2173 \ Display welcome message
2174 welcome-symbol nil cons global-env obj@ eval 2drop
2179 recoverable-exception of false endof
2180 unrecoverable-exception of true endof