eatspaces now eats comments
[scheme.forth.jl.git] / scheme.4th
index 3f57b1f..72897be 100644 (file)
@@ -6,17 +6,18 @@ include defer-is.4th
 
 \ ------ Types ------
 
-0 constant number-type
+0 constant fixnum-type
 1 constant boolean-type
 2 constant character-type
 3 constant string-type
 4 constant nil-type
 5 constant pair-type
 6 constant symbol-type
+7 constant primitive-type
 : istype? ( obj type -- obj bool )
     over = ;
 
-\ ------ Cons cell memory ------
+\ ------ Cons cell memory ------ {{{
 
 1000 constant N
 create car-cells N allot
@@ -82,7 +83,23 @@ variable nextfree
 : objeq? ( obj obj -- bool )
     rot = -rot = and ;
 
-\ ---- Pre-defined symbols ----
+: 2rot ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
+    >R >R ( a1 a2 b1 b2 )
+    2swap ( b1 b2 a1 a2 )
+    R> R> ( b1 b2 a1 a2 c1 c2 )
+    2swap
+;
+
+: -2rot ( a1 a2 b1 b2 c1 c2 -- c1 c2 a1 a2 b1 b2 )
+    2swap ( a1 a2 c1 c2 b1 b2 )
+    >R >R ( a1 a2 c1 c2 )
+    2swap ( c1 c2 a1 a2 )
+    R> R>
+;
+
+\ }}}
+
+\ ---- Pre-defined symbols ---- {{{
 
 objvar symbol-table
 
@@ -115,13 +132,15 @@ objvar symbol-table
     does> dup @ swap 1+ @
 ;
 
-create-symbol quote quote-symbol
-create-symbol define define-symbol
-create-symbol set! set!-symbol
+create-symbol quote     quote-symbol
+create-symbol define    define-symbol
+create-symbol set!      set!-symbol
+create-symbol ok        ok-symbol
+create-symbol if        if-symbol
 
-\ ---- Environments ----
+\ }}}
 
-objvar global-env
+\ ---- Environments ---- {{{
 
 : enclosing-env ( env -- env )
     cdr ;
@@ -140,9 +159,9 @@ objvar global-env
 
 : add-binding ( var val frame -- )
     2swap 2over frame-vals cons
-    2over set-car!
+    2over set-cdr!
     2swap 2over frame-vars cons
-    swap set-cdr!
+    2swap set-car!
 ;
 
 : extend-env ( vars vals env -- env )
@@ -155,7 +174,7 @@ objvar global-env
 objvar vars
 objvar vals
 
-: lookup-var-frame ( var frame -- val? bool )
+: get-vars-vals-frame ( var frame -- bool )
     2dup frame-vars vars setobj
     frame-vals vals setobj
 
@@ -163,8 +182,7 @@ objvar vals
         vars fetchobj nil objeq? false =
     while
         2dup vars fetchobj car objeq? if
-            2drop
-            vals fetchobj car true
+            2drop true
             exit
         then
 
@@ -175,67 +193,127 @@ objvar vals
     2drop false
 ;
 
-: lookup-var ( var env -- val )
+: get-vars-vals ( var env -- vars? vals? bool )
+
     begin
         2dup nil objeq? false =
     while
         2over 2over first-frame
-        lookup-var-frame if
-            -rot 2drop -rot 2drop
+        get-vars-vals-frame if
+            2drop 2drop
+            vars fetchobj vals fetchobj true
             exit
         then
 
         enclosing-env
     repeat
 
-    bold fg red ." Unbound variable.  Aborting." reset-term cr
-    abort
+    2drop 2drop
+    false
 ;
 
-objvar val
+hide vars
+hide vals
 
-: set-var-frame ( var frame -- )
-    2dup frame-vars vars setobj
-    frame-vals vals setobj
+: lookup-var ( var env -- val )
+    get-vars-vals if
+        2swap 2drop car
+    else
+        bold fg red ." Tried to read unbound variable." reset-term cr abort
+    then
+;
 
-    begin
-        vars fetchobj nil objeq? false =
-    while
-        2dup vars fetchobj car objeq? if
-            2drop
-            \ *** TODO ***
-        then
+: set-var ( var val env -- )
+    >R >R 2swap R> R> ( val var env )
+    get-vars-vals if
+        2swap 2drop ( val vals )
+        set-car!
+    else
+        bold fg red ." Tried to set unbound variable." reset-term cr abort
+    then
+;
 
-        vars fetchobj cdr vars setobj
-        vals fetchobj cdr vals setobj
-    repeat
+objvar env
+
+: define-var ( var val env -- )
+    env setobj 
+
+    2over env fetchobj ( var val var env )
+    get-vars-vals if
+        2swap 2drop ( var val vals )
+        set-car!
+        2drop
+    else
+        env fetchobj
+        first-frame ( var val frame )
+        add-binding
+    then
 ;
 
+hide env
 
-: set-var ( var val env -- )
+objvar global-env
+nil nil nil extend-env
+global-env setobj
+
+\ }}}
+
+\ ---- Primitives ---- {{{
 
-    2swap val setobj
+: make-primitive ( cfa -- )
+    bl word
+    count
+
+    (create-symbol)
+    drop symbol-type
     
-    begin
-        2dup nil objeq? false =
-    while
-        2over 2over first-frame
-        set-var-frame if
-            exit
+    2dup
+
+    symbol-table fetchobj
+    cons
+    symbol-table setobj
+
+    rot primitive-type ( var prim )
+    global-env fetchobj define-var
+;
+
+: arg-count-error
+            bold fg red ." Incorrect argument count." reset-term cr
+            abort
+;
+
+: ensure-arg-count ( args n -- )
+    dup 0= if
+        drop nil objeq? false = if
+            arg-count-error
         then
+    else
+        -rot 2dup nil objeq? if
+            arg-count-error
+        then
+        
+        cdr rot 1- recurse
+    then
+;
 
-        enclosing-env
-    repeat
+: arg-type-error
+            bold fg red ." Incorrect argument type." reset-term cr
+            abort
+;
 
-    bold fg red ." Unbound variable. Aborting." reset-term cr
-    abort
+: ensure-arg-type ( arg type -- arg )
+    istype? false = if
+        arg-type-error
+    then
 ;
 
-hide vars
-hide vals
-hide val
+include scheme-primitives.4th
+
+\ }}}
 
-\ ---- Read ----
+\ ---- Read ---- {{{
+
+defer read
 
 variable parse-idx
 variable stored-parse-idx
@@ -299,12 +377,27 @@ parse-idx-stack parse-idx-sp !
     nextchar [char] ) = or
 ;
 
+: commentstart? ( -- bool )
+    nextchar [char] ; = ;
+
 : eatspaces
+
+    false \ Indicates whether or not we're eating a comment
+
     begin
-        whitespace?
+        dup whitespace? or commentstart? or
     while
+        dup nextchar '\n' = and if
+            invert \ Stop eating comment
+        else
+            dup false = commentstart? and if   
+                invert \ Begin eating comment
+            then
+        then
+
         inc-parse-idx
     repeat
+    drop
 ;
 
 : digit? ( -- bool )
@@ -315,10 +408,23 @@ parse-idx-stack parse-idx-sp !
 : minus? ( -- bool )
     nextchar [char] - = ;
 
-: number? ( -- bool )
-    digit? minus? or false = if
-        false
-        exit
+: plus? ( -- bool )
+    nextchar [char] + = ;
+
+: fixnum? ( -- bool )
+    minus? plus? or if
+        inc-parse-idx
+
+        delim? if
+            dec-parse-idx
+            false exit
+        else
+            dec-parse-idx
+        then
+    else
+        digit? false = if
+            false exit
+        then
     then
 
     push-parse-idx
@@ -405,8 +511,11 @@ parse-idx-stack parse-idx-sp !
     nextchar [char] " = ;
 
 : readnum ( -- num-atom )
-    minus? dup if
+    plus? minus? or if
+        minus?
         inc-parse-idx
+    else
+        false
     then
 
     0
@@ -418,7 +527,7 @@ parse-idx-stack parse-idx-sp !
 
     swap if negate then
 
-    number-type
+    fixnum-type
 ;
 
 : readbool ( -- bool-atom )
@@ -544,8 +653,6 @@ parse-idx-stack parse-idx-sp !
     symbol-table setobj
 ;
 
-defer read
-
 : readpair ( -- pairobj )
     eatspaces
 
@@ -593,7 +700,7 @@ defer read
 
     eatspaces
 
-    number? if
+    fixnum? if
         readnum
         exit
     then
@@ -610,6 +717,7 @@ defer read
 
     string? if
         inc-parse-idx
+
         readstring
         drop string-type
 
@@ -619,6 +727,7 @@ defer read
         then
 
         inc-parse-idx
+
         exit
     then
 
@@ -656,11 +765,15 @@ defer read
 
 ; is read
 
-\ ---- Eval ----
+\ }}}
+
+\ ---- Eval ---- {{{
+
+defer eval
 
 : self-evaluating? ( obj -- obj bool )
     boolean-type istype? if true exit then
-    number-type istype? if true exit then
+    fixnum-type istype? if true exit then
     character-type istype? if true exit then
     string-type istype? if true exit then
     nil-type istype? if true exit then
@@ -681,8 +794,115 @@ defer read
 
 : quote-body ( quote-obj -- quote-body-obj )
     cadr ;
+
+: variable? ( obj -- obj bool )
+    symbol-type istype? ;
+
+: definition? ( obj -- obj bool )
+    define-symbol tagged-list? ;
+
+: definition-var ( obj -- var )
+    cdr car ;
+
+: definition-val ( obj -- val )
+    cdr cdr car ;
+
+: assignment? ( obj -- obj bool )
+    set!-symbol tagged-list? ;
+
+: assignment-var ( obj -- var )
+    cdr car ;
+    
+: assignment-val ( obj -- val )
+    cdr cdr car ;
+
+: eval-definition ( obj env -- res )
+    2swap 
+    2over 2over ( env obj env obj )
+    definition-val 2swap ( env obj valexp env )
+    eval  ( env obj val )
+    
+    2swap definition-var 2swap ( env var val )
+
+    2rot ( var val env )
+    define-var
+
+    ok-symbol
+;
     
-: eval ( obj env -- result )
+: eval-assignment ( obj env -- res )
+    2swap 
+    2over 2over ( env obj env obj )
+    assignment-val 2swap ( env obj valexp env )
+    eval  ( env obj val )
+    
+    2swap assignment-var 2swap ( env var val )
+
+    2rot ( var val env )
+    set-var
+
+    ok-symbol
+;
+
+: if? ( obj -- obj bool )
+    if-symbol tagged-list? ;
+
+: if-predicate ( ifobj -- pred )
+    cdr car ;
+
+: if-consequent ( ifobj -- conseq )
+    cdr cdr car ;
+
+: if-alternative ( ifobj -- alt|false )
+    cdr cdr cdr
+    2dup nil objeq? if
+        2drop false
+    else
+        car
+    then ;
+
+: false? ( boolobj -- boolean )
+    boolean-type istype? if
+        false boolean-type objeq?
+    else
+        2drop false
+    then
+;
+
+: true? ( boolobj -- bool )
+    false? invert ;
+
+: application? ( obj -- obj bool)
+    pair-type istype? ;
+
+: operator ( obj -- operator )
+    car ;
+
+: operands ( obj -- operands )
+    cdr ;
+
+: nooperands? ( operands -- bool )
+    nil objeq? ;
+
+: first-operand ( operands -- operand )
+    car ;
+
+: rest-operands ( operands -- other-operands )
+    cdr ;
+
+: list-of-vals ( args env -- vals )
+    2swap
+
+    2dup nooperands? if
+        2swap 2drop
+    else
+        2over 2over first-operand 2swap eval
+        -2rot rest-operands 2swap recurse
+        cons
+    then
+;
+
+:noname ( obj env -- result )
     2swap
 
     self-evaluating? if
@@ -696,11 +916,60 @@ defer read
         exit
     then
 
+    variable? if
+        2swap lookup-var
+        exit
+    then
+
+    definition? if
+        2swap eval-definition
+        exit
+    then
+
+    assignment? if
+        2swap eval-assignment
+        exit
+    then
+
+    if? if
+        2over 2over
+        if-predicate
+        2swap eval 
+
+        true? if
+            if-consequent
+        else
+            if-alternative
+        then
+
+        2swap ['] eval goto
+    then
+
+    application? if
+        2over 2over
+        operator 2swap eval
+
+        primitive-type istype? false = if
+            bold fg red ." Object not applicable. Aboring." reset-term cr
+            abort
+        then
+
+        -2rot
+        operands 2swap list-of-vals
+
+        2swap drop execute
+        exit
+    then
+
     bold fg red ." Error evaluating expression - unrecognized type. Aborting." reset-term cr
     abort
-;
+; is eval
+
+\ }}}
 
-\ ---- Print ----
+\ ---- Print ---- {{{
+
+defer print
 
 : printnum ( numobj -- ) drop 0 .R ;
 
@@ -751,7 +1020,6 @@ defer read
 : printnil ( nilobj -- )
     2drop ." ()" ;
 
-defer print
 : printpair ( pairobj -- )
     2dup
     car print
@@ -761,19 +1029,25 @@ defer print
     ."  . " print
 ;
 
+: printprim ( primobj -- )
+    2drop ." <primitive procedure>" ;
+
 :noname ( obj -- )
-    number-type istype? if printnum exit then
+    fixnum-type istype? if printnum exit then
     boolean-type istype? if printbool exit then
     character-type istype? if printchar exit then
     string-type istype? if printstring exit then
     symbol-type istype? if printsymbol exit then
     nil-type istype? if printnil exit then
     pair-type istype? if ." (" printpair ." )" exit then
+    primitive-type istype? if printprim exit then
 
     bold fg red ." Error printing expression - unrecognized type. Aborting" reset-term cr
     abort
 ; is print
 
+\ }}}
+
 \ ---- REPL ----
 
 : repl
@@ -791,3 +1065,5 @@ defer print
 ;
 
 forth definitions
+
+\ vim:fdm=marker