Basic: step9 basics
authorJoel Martin <github@martintribe.org>
Sun, 9 Oct 2016 04:41:33 +0000 (23:41 -0500)
committerJoel Martin <github@martintribe.org>
Sun, 9 Oct 2016 04:41:33 +0000 (23:41 -0500)
- Change ER% to be -2 for no error, -1 for raw string error, and >=0
  as pointer to an error object.

14 files changed:
basic/Makefile
basic/core.in.bas
basic/env.in.bas
basic/reader.in.bas
basic/step1_read_print.in.bas
basic/step2_eval.in.bas
basic/step3_env.in.bas
basic/step4_if_fn_do.in.bas
basic/step5_tco.in.bas
basic/step6_file.in.bas
basic/step7_quote.in.bas
basic/step8_macros.in.bas
basic/step9_try.in.bas [new file with mode: 0755]
basic/types.in.bas

index d1cfd66..f39c563 100644 (file)
@@ -18,7 +18,7 @@ step0_repl.bas: $(STEP0_DEPS)
 step1_read_print.bas step2_eval.bas: $(STEP1_DEPS)
 step3_env.bas: $(STEP3_DEPS)
 step4_if_fn_do.bas step5_tco.bas step6_file.bas step7_quote.bas: $(STEP4_DEPS)
-step8_macros.bas: $(STEP4_DEPS)
+step8_macros.bas step9_try.bas: $(STEP4_DEPS)
 
 tests/%.bas: tests/%.in.bas
        ./basicpp.py $(BASICPP_OPTS) $< > $@
@@ -28,10 +28,10 @@ tests/%.prg: tests/%.bas
        petcat -text -w2 -o $@ $<.tmp
        rm $<.tmp
 
-mal.prg: step8_macros.prg
+mal.prg: step9_try.prg
        cp $< $@
 
-SOURCES_LISP = env.in.bas core.in.bas step8_macros.in.bas
+SOURCES_LISP = env.in.bas core.in.bas step9_try.in.bas
 SOURCES = readline.in.bas types.in.bas reader.in.bas printer.in.bas $(SOURCES_LISP)
 
 .PHONY: stats
index fc744c3..cc1629b 100644 (file)
@@ -9,69 +9,57 @@ DO_FUNCTION:
   R%=Z%(AR%,1)+1:GOSUB DEREF_R:AB%=R%
 
   REM Switch on the function number
-  IF FF%>=61 THEN ER%=1:ER$="unknown function"+STR$(FF%):RETURN
+  IF FF%>=61 THEN ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
   IF FF%>=53 THEN DO_53
   IF FF%>=39 THEN DO_39
   IF FF%>=27 THEN DO_27
   IF FF%>=18 THEN DO_18
   IF FF%>=11 THEN DO_11
 
-  ON FF% GOTO DO_EQUAL_Q
-  REM IF FF%=1 THEN DO_EQUAL_Q
-
+  ON FF% GOTO DO_EQUAL_Q,DO_THROW,DO_NIL_Q,DO_TRUE_Q,DO_FALSE_Q,DO_STRING_Q,DO_SYMBOL,DO_SYMBOL_Q
   DO_11:
   ON FF%-10 GOTO DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READLINE,DO_READ_STRING,DO_SLURP
-  REM IF FF%=11 THEN DO_PR_STR
-  REM IF FF%=12 THEN DO_STR
-  REM IF FF%=13 THEN DO_PRN
-  REM IF FF%=14 THEN DO_PRINTLN
-  REM IF FF%=15 THEN DO_READLINE
-  REM IF FF%=16 THEN DO_READ_STRING
-  REM IF FF%=17 THEN DO_SLURP
-
   DO_18:
   ON FF%-17 GOTO DO_LT,DO_LTE,DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS
-  REM IF FF%=18 THEN DO_LT
-  REM IF FF%=19 THEN DO_LTE
-  REM IF FF%=20 THEN DO_GT
-  REM IF FF%=21 THEN DO_GTE
-  REM IF FF%=22 THEN DO_ADD
-  REM IF FF%=23 THEN DO_SUB
-  REM IF FF%=24 THEN DO_MULT
-  REM IF FF%=25 THEN DO_DIV
-  REM IF FF%=26 THEN DO_TIME_MS
-
   DO_27:
-  ON FF%-26 GOTO DO_LIST,DO_LIST_Q
-  REM IF FF%=27 THEN DO_LIST
-  REM IF FF%=28 THEN DO_LIST_Q
-
+  ON FF%-26 GOTO DO_LIST,DO_LIST_Q,DO_VECTOR,DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q
   DO_39:
-  ON FF%-39 GOTO DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT
-  REM IF FF%=40 THEN DO_CONS
-  REM IF FF%=41 THEN DO_CONCAT
-  REM IF FF%=42 THEN DO_NTH
-  REM IF FF%=43 THEN DO_FIRST
-  REM IF FF%=44 THEN DO_REST
-  REM IF FF%=45 THEN DO_EMPTY_Q
-  REM IF FF%=46 THEN DO_COUNT
-
+  ON FF%-38 GOTO DO_SEQUENTIAL_Q,DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT,DO_APPLY,DO_MAP
   DO_53:
   ON FF%-52 GOTO DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,DO_PR_MEMORY,DO_PR_MEMORY_SUMMARY,DO_EVAL
-  REM IF FF%=53 THEN DO_ATOM
-  REM IF FF%=54 THEN DO_ATOM_Q
-  REM IF FF%=55 THEN DO_DEREF
-  REM IF FF%=56 THEN DO_RESET_BANG
-  REM IF FF%=57 THEN DO_SWAP_BANG
-
-  REM IF FF%=58 THEN DO_PR_MEMORY
-  REM IF FF%=59 THEN DO_PR_MEMORY_SUMMARY
-  REM IF FF%=60 THEN DO_EVAL
 
   DO_EQUAL_Q:
     A%=AA%:B%=AB%:GOSUB EQUAL_Q
     R%=R%+1
     RETURN
+  DO_THROW:
+    ER%=AA%
+    Z%(ER%,0)=Z%(ER%,0)+16
+    R%=0
+    RETURN
+  DO_NIL_Q:
+    R%=1
+    IF AA%=0 THEN R%=2
+    RETURN
+  DO_TRUE_Q:
+    R%=1
+    IF AA%=2 THEN R%=2
+    RETURN
+  DO_FALSE_Q:
+    R%=1
+    IF AA%=1 THEN R%=2
+    RETURN
+  DO_STRING_Q:
+    R%=1
+    IF (Z%(AA%,0)AND15)=4 THEN R%=2
+    RETURN
+  DO_SYMBOL:
+    R%=0
+    RETURN
+  DO_SYMBOL_Q:
+    R%=1
+    IF (Z%(AA%,0)AND15)=5 THEN R%=2
+    RETURN
 
   DO_PR_STR:
     AZ%=AR%:PR%=1:SE$=" ":GOSUB PR_STR_SEQ
@@ -108,7 +96,7 @@ DO_FUNCTION:
       IF ASC(A$)=10 THEN R$=R$+CHR$(13)
       IF (ASC(A$)<>10) AND (A$<>"") THEN R$=R$+A$
       IF (ST AND 64) THEN GOTO DO_SLURP_DONE
-      IF (ST AND 255) THEN ER%=-1:ER%="File read error "+STR$(ST):RETURN
+      IF (ST AND 255) THEN ER%=-1:ER$="File read error "+STR$(ST):RETURN
       GOTO DO_SLURP_LOOP
     DO_SLURP_DONE:
       CLOSE 1
@@ -164,7 +152,25 @@ DO_FUNCTION:
     A%=AA%:GOSUB LIST_Q
     R%=R%+1: REM map to mal false/true
     RETURN
+  DO_VECTOR:
+    R%=0
+    RETURN
+  DO_VECTOR_Q:
+    R%=1
+    IF (Z%(AA%,0)AND15)=7 THEN R%=2
+    RETURN
+  DO_HASH_MAP:
+    R%=0
+    RETURN
+  DO_MAP_Q:
+    R%=1
+    IF (Z%(AA%,0)AND15)=8 THEN R%=2
+    RETURN
 
+  DO_SEQUENTIAL_Q:
+    R%=1
+    IF (Z%(AA%,0)AND15)=6 OR (Z%(AA%,0)AND15)=7 THEN R%=2
+    RETURN
   DO_CONS:
     A%=AA%:B%=AB%:GOSUB CONS
     RETURN
@@ -217,7 +223,7 @@ DO_FUNCTION:
   DO_NTH:
     B%=Z%(AB%,1)
     A%=AA%:GOSUB COUNT
-    IF R%<=B% THEN R%=0:ER%=1:ER$="nth: index out of range":RETURN
+    IF R%<=B% THEN R%=0:ER%=-1:ER$="nth: index out of range":RETURN
     DO_NTH_LOOP:
       IF B%=0 THEN GOTO DO_NTH_DONE
       B%=B%-1
@@ -247,6 +253,87 @@ DO_FUNCTION:
     Z%(R%,0)=2+16
     Z%(R%,1)=R4%
     RETURN
+  DO_APPLY:
+    F%=AA%
+    AR%=Z%(AR%,1)
+    A%=AR%:GOSUB COUNT:R4%=R%
+
+    REM no intermediate args, just call APPLY directly
+    IF R4%<=1 THEN AR%=Z%(AR%+1,1):GOSUB APPLY:RETURN
+
+    REM prepend intermediate args to final args element
+    A%=AR%:B%=0:C%=R4%-1:GOSUB SLICE
+    REM release the terminator of new list (we skip over it)
+    AY%=Z%(R6%,1):GOSUB RELEASE
+    REM attach end of slice to final args element
+    Z%(R6%,1)=Z%(A%+1,1)
+    Z%(Z%(A%+1,1),0)=Z%(Z%(A%+1,1),0)+16
+
+    ZL%=ZL%+1:ZZ%(ZL%)=R%: REM push/save new args for release
+    AR%=R%:GOSUB APPLY
+    AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE: REM pop/release new args
+    RETURN
+  DO_MAP:
+    F%=AA%
+
+    REM first result list element
+    SZ%=2:GOSUB ALLOC
+
+    REM push future return val, prior entry, F% and AB%
+    ZL%=ZL%+4:ZZ%(ZL%-3)=R%:ZZ%(ZL%-2)=0:ZZ%(ZL%-1)=F%:ZZ%(ZL%)=AB%
+
+    DO_MAP_LOOP:
+      REM set base values
+      Z%(R%,0)=6+16:Z%(R%,1)=0
+      Z%(R%+1,0)=14:Z%(R%+1,1)=0
+
+      REM set previous to current if not the first element
+      IF ZZ%(ZL%-2)<>0 THEN Z%(ZZ%(ZL%-2),1)=R%
+      REM update previous reference to current
+      ZZ%(ZL%-2)=R%
+
+      IF Z%(AB%,1)=0 THEN GOTO DO_MAP_DONE
+
+      REM create argument list for apply call
+      SZ%=2:GOSUB ALLOC
+      Z%(R%,0)=6+16:Z%(R%,1)=0
+      Z%(R%+1,0)=14:Z%(R%+1,1)=0
+      AR%=R%: REM save end of list temporarily
+      SZ%=2:GOSUB ALLOC
+      Z%(R%,0)=6+16:Z%(R%,1)=AR%
+      REM inc ref cnt of referred argument
+      A%=Z%(AB%+1,1): Z%(A%,0)=Z%(A%,0)+16
+      Z%(R%+1,0)=14:Z%(R%+1,1)=A%
+
+      REM push argument list
+      ZL%=ZL%+1:ZZ%(ZL%)=R%
+
+      AR%=R%:GOSUB APPLY
+
+      REM pop apply args are release them
+      AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
+
+      REM set the result value
+      Z%(ZZ%(ZL%-2)+1,1)=R%
+
+      REM restore F%
+      F%=ZZ%(ZL%-1)
+
+      REM update AB% to next source element
+      ZZ%(ZL%)=Z%(ZZ%(ZL%),1)
+      AB%=ZZ%(ZL%)
+
+      REM allocate next element
+      SZ%=2:GOSUB ALLOC
+
+      GOTO DO_MAP_LOOP
+
+    DO_MAP_DONE:
+      REM get return val
+      R%=ZZ%(ZL%-3)
+      REM pop everything off stack
+      ZL%=ZL%-4
+      RETURN
 
   DO_ATOM:
     SZ%=1:GOSUB ALLOC
@@ -322,6 +409,13 @@ INIT_CORE_NS:
   REM must match DO_FUNCTION mappings
 
   K$="=":A%=1:GOSUB INIT_CORE_SET_FUNCTION
+  K$="throw":A%=2:GOSUB INIT_CORE_SET_FUNCTION
+  K$="nil?":A%=3:GOSUB INIT_CORE_SET_FUNCTION
+  K$="true?":A%=4:GOSUB INIT_CORE_SET_FUNCTION
+  K$="false?":A%=5:GOSUB INIT_CORE_SET_FUNCTION
+  K$="string?":A%=6:GOSUB INIT_CORE_SET_FUNCTION
+  K$="symbol":A%=7:GOSUB INIT_CORE_SET_FUNCTION
+  K$="symbol?":A%=8:GOSUB INIT_CORE_SET_FUNCTION
 
   K$="pr-str":A%=11:GOSUB INIT_CORE_SET_FUNCTION
   K$="str":A%=12:GOSUB INIT_CORE_SET_FUNCTION
@@ -343,7 +437,12 @@ INIT_CORE_NS:
 
   K$="list":A%=27:GOSUB INIT_CORE_SET_FUNCTION
   K$="list?":A%=28:GOSUB INIT_CORE_SET_FUNCTION
+  K$="vector":A%=29:GOSUB INIT_CORE_SET_FUNCTION
+  K$="vector?":A%=30:GOSUB INIT_CORE_SET_FUNCTION
+  K$="hash-map":A%=31:GOSUB INIT_CORE_SET_FUNCTION
+  K$="map?":A%=32:GOSUB INIT_CORE_SET_FUNCTION
 
+  K$="sequential?":A%=39:GOSUB INIT_CORE_SET_FUNCTION
   K$="cons":A%=40:GOSUB INIT_CORE_SET_FUNCTION
   K$="concat":A%=41:GOSUB INIT_CORE_SET_FUNCTION
   K$="nth":A%=42:GOSUB INIT_CORE_SET_FUNCTION
@@ -351,6 +450,8 @@ INIT_CORE_NS:
   K$="rest":A%=44:GOSUB INIT_CORE_SET_FUNCTION
   K$="empty?":A%=45:GOSUB INIT_CORE_SET_FUNCTION
   K$="count":A%=46:GOSUB INIT_CORE_SET_FUNCTION
+  K$="apply":A%=47:GOSUB INIT_CORE_SET_FUNCTION
+  K$="map":A%=48:GOSUB INIT_CORE_SET_FUNCTION
 
   K$="atom":A%=53:GOSUB INIT_CORE_SET_FUNCTION
   K$="atom?":A%=54:GOSUB INIT_CORE_SET_FUNCTION
index c2f0c4e..1a8be3f 100644 (file)
@@ -88,7 +88,7 @@ ENV_FIND:
 REM ENV_GET(E%, K%) -> R%
 ENV_GET:
   GOSUB ENV_FIND
-  IF R%=-1 THEN R%=0:ER%=1:ER$="'"+ZS$(Z%(K%,1))+"' not found":RETURN
+  IF R%=-1 THEN R%=0:ER%=-1:ER$="'"+ZS$(Z%(K%,1))+"' not found":RETURN
   R%=T4%:GOSUB DEREF_R
   Z%(R%,0)=Z%(R%,0)+16
   RETURN
index 7454ef6..4f75b98 100644 (file)
@@ -38,7 +38,7 @@ READ_ATOM:
 
 REM READ_FORM(A$, IDX%) -> R%
 READ_FORM:
-  IF ER% THEN RETURN
+  IF ER%<>-2 THEN RETURN
   GOSUB SKIP_SPACES
   GOSUB READ_TOKEN
   IF T$="" AND SD%>0 THEN ER$="unexpected EOF":GOTO READ_FORM_ABORT
@@ -190,7 +190,7 @@ READ_FORM:
     GOTO READ_FORM
 
   READ_FORM_ABORT:
-    ER%=1
+    ER%=-1
     R%=0
     READ_FORM_ABORT_UNWIND:
       IF SD%=0 THEN RETURN
index 415fd06..ae921bc 100755 (executable)
@@ -25,10 +25,10 @@ MAL_PRINT:
 REM REP(A$) -> R$
 REP:
   GOSUB MAL_READ
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB EVAL
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -51,7 +51,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -62,6 +62,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 3c1c246..41e91db 100755 (executable)
@@ -19,7 +19,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -35,7 +35,7 @@ EVAL_AST:
   EVAL_AST_SYMBOL:
     HM%=E%:K%=A%:GOSUB HASHMAP_GET
     GOSUB DEREF_R
-    IF T3%=0 THEN ER%=1:ER$="'"+ZS$(Z%(A%,1))+"' not found":GOTO EVAL_AST_RETURN
+    IF T3%=0 THEN ER%=-1:ER$="'"+ZS$(Z%(A%,1))+"' not found":GOTO EVAL_AST_RETURN
     Z%(R%,0)=Z%(R%,0)+16
     GOTO EVAL_AST_RETURN
 
@@ -88,7 +88,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -123,8 +123,8 @@ EVAL:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  REM AZ%=A%: GOSUB PR_STR
-  REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
+  REM AZ%=A%:PR%=1:GOSUB PR_STR
+  REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
 
   GOSUB DEREF_A
 
@@ -143,19 +143,17 @@ EVAL:
       R3%=R%
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
       F%=R%+1
 
       AR%=Z%(R%,1): REM rest
       R%=F%:GOSUB DEREF_R:F%=R%
-      IF (Z%(F%,0)AND15)<>9 THEN ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      IF (Z%(F%,0)AND15)<>9 THEN ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
       GOSUB DO_FUNCTION
       AY%=R3%:GOSUB RELEASE
       GOTO EVAL_RETURN
 
   EVAL_RETURN:
-    REM an error occured, free up any new value
-    IF ER%=1 THEN AY%=R%:GOSUB RELEASE
 
     LV%=LV%-1: REM track basic return stack level
 
@@ -190,7 +188,7 @@ DO_FUNCTION:
   IF FF%=2 THEN GOTO DO_SUB
   IF FF%=3 THEN GOTO DO_MULT
   IF FF%=4 THEN GOTO DO_DIV
-  ER%=1:ER$="unknown function"+STR$(FF%):RETURN
+  ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
 
   DO_ADD:
     Z%(R%,0)=2+16
@@ -223,11 +221,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -272,7 +270,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -283,6 +281,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index fcb24d1..04c2fe9 100755 (executable)
@@ -20,7 +20,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -86,7 +86,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -102,9 +102,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -169,7 +169,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -208,12 +208,12 @@ EVAL:
       R3%=R%
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
       F%=R%+1
 
       AR%=Z%(R%,1): REM rest
       R%=F%:GOSUB DEREF_R:F%=R%
-      IF (Z%(F%,0)AND15)<>9 THEN ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      IF (Z%(F%,0)AND15)<>9 THEN ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
       GOSUB DO_FUNCTION
       AY%=R3%:GOSUB RELEASE
       GOTO EVAL_RETURN
@@ -258,7 +258,7 @@ DO_FUNCTION:
   IF FF%=2 THEN GOTO DO_SUB
   IF FF%=3 THEN GOTO DO_MULT
   IF FF%=4 THEN GOTO DO_DIV
-  ER%=1:ER$="unknown function"+STR$(FF%):RETURN
+  ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
 
   DO_ADD:
     Z%(R%,0)=2+16
@@ -291,11 +291,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -318,20 +318,20 @@ MAIN:
 
   E%=RE%
   REM + function
-  A%=1: GOSUB NATIVE_FUNCTION
-  K$="+": V%=R%: GOSUB ENV_SET_S
+  A%=1:GOSUB NATIVE_FUNCTION
+  K$="+":V%=R%:GOSUB ENV_SET_S
 
   REM - function
-  A%=2: GOSUB NATIVE_FUNCTION
-  K$="-": V%=R%: GOSUB ENV_SET_S
+  A%=2:GOSUB NATIVE_FUNCTION
+  K$="-":V%=R%:GOSUB ENV_SET_S
 
   REM * function
-  A%=3: GOSUB NATIVE_FUNCTION
-  K$="*": V%=R%: GOSUB ENV_SET_S
+  A%=3:GOSUB NATIVE_FUNCTION
+  K$="*":V%=R%:GOSUB ENV_SET_S
 
   REM / function
-  A%=4: GOSUB NATIVE_FUNCTION
-  K$="/": V%=R%: GOSUB ENV_SET_S
+  A%=4:GOSUB NATIVE_FUNCTION
+  K$="/":V%=R%:GOSUB ENV_SET_S
 
   ZT%=ZI%: REM top of memory after base repl_env
 
@@ -341,7 +341,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -352,6 +352,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 024ed72..4dd86f3 100755 (executable)
@@ -21,7 +21,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -87,7 +87,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -103,9 +103,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -181,7 +181,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -261,7 +261,7 @@ EVAL:
       EVAL_AST_RETURN_3:
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM push f/args for release after call
       ZL%=ZL%+1:ZZ%(ZL%)=R%
@@ -276,7 +276,7 @@ EVAL:
 
       REM if error, pop and return f/args for release by caller
       R%=ZZ%(ZL%):ZL%=ZL%-1
-      ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
 
       EVAL_DO_FUNCTION:
         GOSUB DO_FUNCTION
@@ -340,7 +340,7 @@ RE:
   R1%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
 
@@ -355,11 +355,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -395,7 +395,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -406,6 +406,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 2dbb3c1..1291bef 100755 (executable)
@@ -21,7 +21,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -87,7 +87,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -103,9 +103,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -181,7 +181,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -270,7 +270,7 @@ EVAL:
       EVAL_AST_RETURN_3:
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM push f/args for release after call
       ZL%=ZL%+1:ZZ%(ZL%)=R%
@@ -285,7 +285,7 @@ EVAL:
 
       REM if error, pop and return f/args for release by caller
       R%=ZZ%(ZL%):ZL%=ZL%-1
-      ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
 
       EVAL_DO_FUNCTION:
         GOSUB DO_FUNCTION
@@ -349,7 +349,7 @@ RE:
   R1%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
 
@@ -364,11 +364,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -404,7 +404,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -415,6 +415,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 4e197e7..d75b86a 100755 (executable)
@@ -21,7 +21,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -87,7 +87,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -103,9 +103,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -181,7 +181,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -270,7 +270,7 @@ EVAL:
       EVAL_AST_RETURN_3:
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM push f/args for release after call
       ZL%=ZL%+1:ZZ%(ZL%)=R%
@@ -285,7 +285,7 @@ EVAL:
 
       REM if error, pop and return f/args for release by caller
       R%=ZZ%(ZL%):ZL%=ZL%-1
-      ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
 
       EVAL_DO_FUNCTION:
         GOSUB DO_FUNCTION
@@ -349,7 +349,7 @@ RE:
   R1%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
 
@@ -364,11 +364,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -423,7 +423,7 @@ MAIN:
     REM run a single mal program and exit
     A$="(load-file (first -*ARGS*-))"
     GOSUB RE
-    IF ER%<>0 THEN GOSUB PRINT_ERROR
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR
     END
 
   REPL_LOOP:
@@ -432,7 +432,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -443,6 +443,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 2bbc29a..2c496e4 100755 (executable)
@@ -94,7 +94,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -160,7 +160,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -176,9 +176,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -256,7 +256,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -359,7 +359,7 @@ EVAL:
       EVAL_AST_RETURN_3:
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM push f/args for release after call
       ZL%=ZL%+1:ZZ%(ZL%)=R%
@@ -374,7 +374,7 @@ EVAL:
 
       REM if error, pop and return f/args for release by caller
       R%=ZZ%(ZL%):ZL%=ZL%-1
-      ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
 
       EVAL_DO_FUNCTION:
         GOSUB DO_FUNCTION
@@ -438,7 +438,7 @@ RE:
   R1%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
 
@@ -453,11 +453,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -512,7 +512,7 @@ MAIN:
     REM run a single mal program and exit
     A$="(load-file (first -*ARGS*-))"
     GOSUB RE
-    IF ER%<>0 THEN GOSUB PRINT_ERROR
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR
     END
 
   REPL_LOOP:
@@ -521,7 +521,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -532,6 +532,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
index 5c38aed..f51a5d2 100755 (executable)
@@ -115,7 +115,7 @@ MACROEXPAND:
     REM intermediate form) then free it
     IF A%<>AY% THEN ZM%=ZM%+1:ZR%(ZM%,0)=A%:ZR%(ZM%,1)=LV%
   
-    IF ER%<>0 THEN GOTO MACROEXPAND_DONE
+    IF ER%<>-2 THEN GOTO MACROEXPAND_DONE
     GOTO MACROEXPAND_LOOP
 
   MACROEXPAND_DONE:
@@ -129,7 +129,7 @@ EVAL_AST:
   REM push A% and E% on the stack
   ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
 
-  IF ER%<>0 THEN GOTO EVAL_AST_RETURN
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
 
   GOSUB DEREF_A
 
@@ -195,7 +195,7 @@ EVAL_AST:
       REM update previous value pointer to evaluated entry
       Z%(ZZ%(ZL%)+1,1)=R%
 
-      IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
 
       REM allocate the next entry
       SZ%=2:GOSUB ALLOC
@@ -211,9 +211,9 @@ EVAL_AST:
       GOTO EVAL_AST_SEQ_LOOP
     EVAL_AST_SEQ_LOOP_DONE:
       REM if no error, get return value (new seq)
-      IF ER%=0 THEN R%=ZZ%(ZL%-1)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
       REM otherwise, free the return value and return nil
-      IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
 
       REM pop previous, return, index and type
       ZL%=ZL%-4
@@ -299,7 +299,7 @@ EVAL:
       A%=A2%:GOSUB EVAL: REM eval a2
       A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
 
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM set a1 in env to a2
       K%=A1%:V%=R%:GOSUB ENV_SET
@@ -426,7 +426,7 @@ EVAL:
       EVAL_AST_RETURN_3:
 
       REM if error, return f/args for release by caller
-      IF ER%<>0 THEN GOTO EVAL_RETURN
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
 
       REM push f/args for release after call
       ZL%=ZL%+1:ZZ%(ZL%)=R%
@@ -441,7 +441,7 @@ EVAL:
 
       REM if error, pop and return f/args for release by caller
       R%=ZZ%(ZL%):ZL%=ZL%-1
-      ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
 
       EVAL_DO_FUNCTION:
         GOSUB DO_FUNCTION
@@ -505,7 +505,7 @@ RE:
   R1%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
 
@@ -520,11 +520,11 @@ REP:
   R1%=0:R2%=0
   GOSUB MAL_READ
   R1%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:E%=RE%:GOSUB EVAL
   R2%=R%
-  IF ER%<>0 THEN GOTO REP_DONE
+  IF ER%<>-2 THEN GOTO REP_DONE
 
   A%=R%:GOSUB MAL_PRINT
   RT$=R$
@@ -588,7 +588,7 @@ MAIN:
     REM run a single mal program and exit
     A$="(load-file (first -*ARGS*-))"
     GOSUB RE
-    IF ER%<>0 THEN GOSUB PRINT_ERROR
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR
     END
 
   REPL_LOOP:
@@ -597,7 +597,7 @@ MAIN:
 
     A$=R$:GOSUB REP: REM call REP
 
-    IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
     PRINT R$
     GOTO REPL_LOOP
 
@@ -608,6 +608,6 @@ MAIN:
 
   PRINT_ERROR:
     PRINT "Error: "+ER$
-    ER%=0:ER$=""
+    ER%=-2:ER$=""
     RETURN
 
diff --git a/basic/step9_try.in.bas b/basic/step9_try.in.bas
new file mode 100755 (executable)
index 0000000..4b212fd
--- /dev/null
@@ -0,0 +1,647 @@
+REM POKE 1, PEEK(1)AND248: REM enable all ROM areas as RAM
+REM POKE 55,0: POKE 56,192: CLR: REM move BASIC end from $A000 to $C000
+GOTO MAIN
+
+REM $INCLUDE: 'readline.in.bas'
+REM $INCLUDE: 'types.in.bas'
+REM $INCLUDE: 'reader.in.bas'
+REM $INCLUDE: 'printer.in.bas'
+REM $INCLUDE: 'env.in.bas'
+REM $INCLUDE: 'core.in.bas'
+
+REM $INCLUDE: 'debug.in.bas'
+
+REM READ(A$) -> R%
+MAL_READ:
+  GOSUB READ_STR
+  RETURN
+
+REM PAIR_Q(B%) -> R%
+PAIR_Q:
+  R%=0
+  IF (Z%(B%,0)AND15)<>6 AND (Z%(B%,0)AND15)<>7 THEN RETURN
+  IF (Z%(B%,1)=0) THEN RETURN
+  R%=1
+  RETURN
+
+REM QUASIQUOTE(A%) -> R%
+QUASIQUOTE:
+  B%=A%:GOSUB PAIR_Q
+  IF R%=1 THEN GOTO QQ_UNQUOTE
+    REM ['quote, ast]
+    AS$="quote":T%=5:GOSUB STRING
+    B2%=R%:B1%=A%:GOSUB LIST2
+
+    RETURN
+
+  QQ_UNQUOTE:
+    R%=A%+1:GOSUB DEREF_R
+    IF (Z%(R%,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
+    IF ZS$(Z%(R%,1))<>"unquote" THEN GOTO QQ_SPLICE_UNQUOTE
+      REM [ast[1]]
+      R%=Z%(A%,1)+1:GOSUB DEREF_R
+      Z%(R%,0)=Z%(R%,0)+16
+
+      RETURN
+
+  QQ_SPLICE_UNQUOTE:
+    REM push A% on the stack
+    ZL%=ZL%+1:ZZ%(ZL%)=A%
+    REM rest of cases call quasiquote on ast[1..]
+    A%=Z%(A%,1):GOSUB QUASIQUOTE:T6%=R%
+    REM pop A% off the stack
+    A%=ZZ%(ZL%):ZL%=ZL%-1
+
+    REM set A% to ast[0] for last two cases
+    A%=A%+1:GOSUB DEREF_A
+
+    B%=A%:GOSUB PAIR_Q
+    IF R%=0 THEN GOTO QQ_DEFAULT
+    B%=A%+1:GOSUB DEREF_B
+    IF (Z%(B%,0)AND15)<>5 THEN GOTO QQ_DEFAULT
+    IF ZS$(Z%(B%,1))<>"splice-unquote" THEN QQ_DEFAULT
+      REM ['concat, ast[0][1], quasiquote(ast[1..])]
+
+      B%=Z%(A%,1)+1:GOSUB DEREF_B:B2%=B%
+      AS$="concat":T%=5:GOSUB STRING:B3%=R%
+      B1%=T6%:GOSUB LIST3
+      REM release inner quasiquoted since outer list takes ownership
+      AY%=B1%:GOSUB RELEASE
+      RETURN
+
+  QQ_DEFAULT:
+    REM ['cons, quasiquote(ast[0]), quasiquote(ast[1..])]
+
+    REM push T6% on the stack
+    ZL%=ZL%+1:ZZ%(ZL%)=T6%
+    REM A% set above to ast[0]
+    GOSUB QUASIQUOTE:B2%=R%
+    REM pop T6% off the stack
+    T6%=ZZ%(ZL%):ZL%=ZL%-1
+
+    AS$="cons":T%=5:GOSUB STRING:B3%=R%
+    B1%=T6%:GOSUB LIST3
+    REM release inner quasiquoted since outer list takes ownership
+    AY%=B1%:GOSUB RELEASE
+    AY%=B2%:GOSUB RELEASE
+    RETURN
+
+REM MACROEXPAND(A%, E%) -> A%:
+MACROEXPAND:
+  REM push original A%
+  ZL%=ZL%+1:ZZ%(ZL%)=A%
+
+  MACROEXPAND_LOOP:
+    REM list?
+    IF (Z%(A%,0)AND15)<>6 THEN GOTO MACROEXPAND_DONE
+    REM non-empty?
+    IF Z%(A%,1)=0 THEN GOTO MACROEXPAND_DONE
+    B%=A%+1:GOSUB DEREF_B
+    REM symbol? in first position
+    IF (Z%(B%,0)AND15)<>5 THEN GOTO MACROEXPAND_DONE
+    REM defined in environment?
+    K%=B%:GOSUB ENV_FIND
+    IF R%=-1 THEN GOTO MACROEXPAND_DONE
+    B%=T4%:GOSUB DEREF_B
+    REM macro?
+    IF (Z%(B%,0)AND15)<>11 THEN GOTO MACROEXPAND_DONE
+  
+    REM apply
+    F%=B%:AR%=Z%(A%,1):GOSUB APPLY
+    A%=R%
+
+    AY%=ZZ%(ZL%)
+    REM if previous A% was not the first A% into macroexpand (i.e. an
+    REM intermediate form) then free it
+    IF A%<>AY% THEN ZM%=ZM%+1:ZR%(ZM%,0)=A%:ZR%(ZM%,1)=LV%
+  
+    IF ER%<>-2 THEN GOTO MACROEXPAND_DONE
+    GOTO MACROEXPAND_LOOP
+
+  MACROEXPAND_DONE:
+    ZL%=ZL%-1: REM pop original A%
+    RETURN
+
+REM EVAL_AST(A%, E%) -> R%
+REM called using GOTO to avoid basic return address stack usage
+REM top of stack should have return label index
+EVAL_AST:
+  REM push A% and E% on the stack
+  ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
+
+  IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
+
+  GOSUB DEREF_A
+
+  T%=Z%(A%,0)AND15
+  IF T%=5 THEN GOTO EVAL_AST_SYMBOL
+  IF T%>=6 AND T%<=8 THEN GOTO EVAL_AST_SEQ
+
+  REM scalar: deref to actual value and inc ref cnt
+  R%=A%:GOSUB DEREF_R
+  Z%(R%,0)=Z%(R%,0)+16
+  GOTO EVAL_AST_RETURN
+
+  EVAL_AST_SYMBOL:
+    K%=A%:GOSUB ENV_GET
+    GOTO EVAL_AST_RETURN
+
+  EVAL_AST_SEQ:
+    REM allocate the first entry
+    SZ%=2:GOSUB ALLOC
+
+    REM make space on the stack
+    ZL%=ZL%+4
+    REM push type of sequence
+    ZZ%(ZL%-3)=T%
+    REM push sequence index
+    ZZ%(ZL%-2)=-1
+    REM push future return value (new sequence)
+    ZZ%(ZL%-1)=R%
+    REM push previous new sequence entry
+    ZZ%(ZL%)=R%
+
+    EVAL_AST_SEQ_LOOP:
+      REM set new sequence entry type (with 1 ref cnt)
+      Z%(R%,0)=ZZ%(ZL%-3)+16
+      Z%(R%,1)=0
+      REM create value ptr placeholder
+      Z%(R%+1,0)=14
+      Z%(R%+1,1)=0
+
+      REM update index
+      ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
+
+      REM check if we are done evaluating the source sequence
+      IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+
+      REM if hashmap, skip eval of even entries (keys)
+      IF (ZZ%(ZL%-3)=8) AND ((ZZ%(ZL%-2) AND 1)=0) THEN GOTO EVAL_AST_DO_REF
+      GOTO EVAL_AST_DO_EVAL
+
+      EVAL_AST_DO_REF:
+        R%=A%+1:GOSUB DEREF_R: REM deref to target of referred entry
+        Z%(R%,0)=Z%(R%,0)+16: REM inc ref cnt of referred value
+        GOTO EVAL_AST_ADD_VALUE
+
+      EVAL_AST_DO_EVAL:
+        REM call EVAL for each entry
+        A%=A%+1:GOSUB EVAL
+        A%=A%-1
+        GOSUB DEREF_R: REM deref to target of evaluated entry
+
+      EVAL_AST_ADD_VALUE:
+
+      REM update previous value pointer to evaluated entry
+      Z%(ZZ%(ZL%)+1,1)=R%
+
+      IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
+
+      REM allocate the next entry
+      SZ%=2:GOSUB ALLOC
+
+      REM update previous sequence entry value to point to new entry
+      Z%(ZZ%(ZL%),1)=R%
+      REM update previous ptr to current entry
+      ZZ%(ZL%)=R%
+
+      REM process the next sequence entry from source list
+      A%=Z%(A%,1)
+
+      GOTO EVAL_AST_SEQ_LOOP
+    EVAL_AST_SEQ_LOOP_DONE:
+      REM if no error, get return value (new seq)
+      IF ER%=-2 THEN R%=ZZ%(ZL%-1)
+      REM otherwise, free the return value and return nil
+      IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
+
+      REM pop previous, return, index and type
+      ZL%=ZL%-4
+      GOTO EVAL_AST_RETURN
+
+  EVAL_AST_RETURN:
+    REM pop A% and E% off the stack
+    E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
+
+    REM pop EVAL AST return label/address
+    RN%=ZZ%(ZL%):ZL%=ZL%-1
+    ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
+    RETURN
+
+REM EVAL(A%, E%)) -> R%
+EVAL:
+  LV%=LV%+1: REM track basic return stack level
+
+  REM push A% and E% on the stack
+  ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
+
+  EVAL_TCO_RECUR:
+
+  REM AZ%=A%:PR%=1:GOSUB PR_STR
+  REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
+
+  GOSUB DEREF_A
+
+  GOSUB LIST_Q
+  IF R% THEN GOTO APPLY_LIST
+  EVAL_NOT_LIST:
+  REM ELSE
+    REM push EVAL_AST return label/address
+    ZL%=ZL%+1:ZZ%(ZL%)=1
+    GOTO EVAL_AST
+    EVAL_AST_RETURN_1:
+
+    GOTO EVAL_RETURN
+
+  APPLY_LIST:
+    GOSUB MACROEXPAND
+
+    GOSUB LIST_Q
+    IF R%<>1 THEN GOTO EVAL_NOT_LIST
+
+    GOSUB EMPTY_Q
+    IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
+
+    A0%=A%+1
+    R%=A0%:GOSUB DEREF_R:A0%=R%
+
+    REM get symbol in A$
+    IF (Z%(A0%,0)AND15)<>5 THEN A$=""
+    IF (Z%(A0%,0)AND15)=5 THEN A$=ZS$(Z%(A0%,1))
+
+    IF A$="def!" THEN GOTO EVAL_DEF
+    IF A$="let*" THEN GOTO EVAL_LET
+    IF A$="quote" THEN GOTO EVAL_QUOTE
+    IF A$="quasiquote" THEN GOTO EVAL_QUASIQUOTE
+    IF A$="defmacro!" THEN GOTO EVAL_DEFMACRO
+    IF A$="macroexpand" THEN GOTO EVAL_MACROEXPAND
+    IF A$="try*" THEN GOTO EVAL_TRY
+    IF A$="do" THEN GOTO EVAL_DO
+    IF A$="if" THEN GOTO EVAL_IF
+    IF A$="fn*" THEN GOTO EVAL_FN
+    GOTO EVAL_INVOKE
+
+    EVAL_GET_A3:
+      A3%=Z%(Z%(Z%(A%,1),1),1)+1
+      R%=A3%:GOSUB DEREF_R:A3%=R%
+    EVAL_GET_A2:
+      A2%=Z%(Z%(A%,1),1)+1
+      R%=A2%:GOSUB DEREF_R:A2%=R%
+    EVAL_GET_A1:
+      A1%=Z%(A%,1)+1
+      R%=A1%:GOSUB DEREF_R:A1%=R%
+      RETURN
+
+    EVAL_DEF:
+      REM PRINT "def!"
+      GOSUB EVAL_GET_A2: REM set a1% and a2%
+
+      ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
+      A%=A2%:GOSUB EVAL: REM eval a2
+      A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
+
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
+
+      REM set a1 in env to a2
+      K%=A1%:V%=R%:GOSUB ENV_SET
+      GOTO EVAL_RETURN
+
+    EVAL_LET:
+      REM PRINT "let*"
+      GOSUB EVAL_GET_A2: REM set a1% and a2%
+
+      ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
+      ZL%=ZL%+1:ZZ%(ZL%)=E%: REM push env for for later release
+
+      REM create new environment with outer as current environment
+      EO%=E%:GOSUB ENV_NEW
+      E%=R%
+      EVAL_LET_LOOP:
+        IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
+
+        ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
+        REM eval current A1 odd element
+        A%=Z%(A1%,1)+1:GOSUB EVAL
+        A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
+
+        REM set environment: even A1% key to odd A1% eval'd above
+        K%=A1%+1:V%=R%:GOSUB ENV_SET
+        AY%=R%:GOSUB RELEASE: REM release our use, ENV_SET took ownership
+
+        REM skip to the next pair of A1% elements
+        A1%=Z%(Z%(A1%,1),1)
+        GOTO EVAL_LET_LOOP
+
+      EVAL_LET_LOOP_DONE:
+        E4%=ZZ%(ZL%):ZL%=ZL%-1: REM pop previous env
+
+        REM release previous environment if not the current EVAL env
+        IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
+
+        A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
+        A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
+
+    EVAL_DO:
+      A%=Z%(A%,1): REM rest
+
+      REM TODO: TCO
+
+      REM push EVAL_AST return label/address
+      ZL%=ZL%+1:ZZ%(ZL%)=2
+      GOTO EVAL_AST
+      EVAL_AST_RETURN_2:
+
+      ZL%=ZL%+1:ZZ%(ZL%)=R%: REM push eval'd list
+      A%=R%:GOSUB LAST: REM return the last element
+      AY%=ZZ%(ZL%):ZL%=ZL%-1: REM pop eval'd list
+      GOSUB RELEASE: REM release the eval'd list
+      GOTO EVAL_RETURN
+
+    EVAL_QUOTE:
+      R%=Z%(A%,1)+1:GOSUB DEREF_R
+      Z%(R%,0)=Z%(R%,0)+16
+      GOTO EVAL_RETURN
+
+    EVAL_QUASIQUOTE:
+      R%=Z%(A%,1)+1:GOSUB DEREF_R
+      A%=R%:GOSUB QUASIQUOTE
+      REM add quasiquote result to pending release queue to free when
+      REM next lower EVAL level returns (LV%)
+      ZM%=ZM%+1:ZR%(ZM%,0)=R%:ZR%(ZM%,1)=LV%
+
+      A%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
+
+    EVAL_DEFMACRO:
+      REM PRINT "defmacro!"
+      GOSUB EVAL_GET_A2: REM set a1% and a2%
+
+      ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
+      A%=A2%:GOSUB EVAL: REM eval a2
+      A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
+
+      REM change function to macro
+      Z%(R%,0)=Z%(R%,0)+1
+
+      REM set a1 in env to a2
+      K%=A1%:V%=R%:GOSUB ENV_SET
+      GOTO EVAL_RETURN
+
+    EVAL_MACROEXPAND:
+      REM PRINT "macroexpand"
+      R%=Z%(A%,1)+1:GOSUB DEREF_R
+      A%=R%:GOSUB MACROEXPAND:R%=A%
+
+      REM since we are returning it unevaluated, inc the ref cnt
+      Z%(R%,0)=Z%(R%,0)+16
+      GOTO EVAL_RETURN
+
+    EVAL_TRY:
+      REM PRINT "try*"
+      GOSUB EVAL_GET_A1: REM set a1%, a2%, and a3%
+
+      ZL%=ZL%+1:ZZ%(ZL%)=A%: REM push/save A%
+      A%=A1%:GOSUB EVAL: REM eval a1
+      A%=ZZ%(ZL%):ZL%=ZL%-1: REM pop/restore A%
+
+      REM if there is not error or catch block then return
+      IF ER%=-2 OR Z%(A%,1)=0 THEN GOTO EVAL_RETURN
+
+      REM create environment for the catch block eval
+      EO%=E%:GOSUB ENV_NEW:E%=R%
+
+      GOSUB EVAL_GET_A2: REM set a1% and a2%
+      A%=A2%:GOSUB EVAL_GET_A2: REM set a1% and a2% from catch block
+
+      REM create object for ER%=-1 type raw string errors
+      IF ER%=-1 THEN AS$=ER$:T%=4:GOSUB STRING:ER%=R%:Z%(R%,0)=Z%(R%,0)+16
+
+      REM bind the catch symbol to the error object
+      K%=A1%:V%=ER%:GOSUB ENV_SET
+      AY%=R%:GOSUB RELEASE: REM release out use, env took ownership
+
+      REM unset error for catch eval
+      ER%=-2:ER$=""
+
+      A%=A2%:GOSUB EVAL
+
+      GOTO EVAL_RETURN
+
+    EVAL_IF:
+      GOSUB EVAL_GET_A1: REM set a1%
+      REM push A%
+      ZL%=ZL%+1:ZZ%(ZL%)=A%
+      A%=A1%:GOSUB EVAL
+      REM pop A%
+      A%=ZZ%(ZL%):ZL%=ZL%-1
+      IF (R%=0) OR (R%=1) THEN GOTO EVAL_IF_FALSE
+
+      EVAL_IF_TRUE:
+        AY%=R%:GOSUB RELEASE
+        GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
+        A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
+      EVAL_IF_FALSE:
+        AY%=R%:GOSUB RELEASE
+        REM if no false case (A3%), return nil
+        IF Z%(Z%(Z%(A%,1),1),1)=0 THEN R%=0:GOTO EVAL_RETURN
+        GOSUB EVAL_GET_A3: REM set a1% - a3% after EVAL
+        A%=A3%:GOTO EVAL_TCO_RECUR: REM TCO loop
+
+    EVAL_FN:
+      GOSUB EVAL_GET_A2: REM set a1% and a2%
+      A%=A2%:P%=A1%:GOSUB MAL_FUNCTION
+      GOTO EVAL_RETURN
+
+    EVAL_INVOKE:
+      REM push EVAL_AST return label/address
+      ZL%=ZL%+1:ZZ%(ZL%)=3
+      GOTO EVAL_AST
+      EVAL_AST_RETURN_3:
+
+      REM if error, return f/args for release by caller
+      IF ER%<>-2 THEN GOTO EVAL_RETURN
+
+      REM push f/args for release after call
+      ZL%=ZL%+1:ZZ%(ZL%)=R%
+
+      F%=R%+1
+
+      AR%=Z%(R%,1): REM rest
+      R%=F%:GOSUB DEREF_R:F%=R%
+
+      IF (Z%(F%,0)AND15)=9 THEN GOTO EVAL_DO_FUNCTION
+      IF (Z%(F%,0)AND15)=10 THEN GOTO EVAL_DO_MAL_FUNCTION
+
+      REM if error, pop and return f/args for release by caller
+      R%=ZZ%(ZL%):ZL%=ZL%-1
+      ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
+
+      EVAL_DO_FUNCTION:
+        GOSUB DO_FUNCTION
+
+        REM pop and release f/args
+        AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
+        GOTO EVAL_RETURN
+
+      EVAL_DO_MAL_FUNCTION:
+        E4%=E%: REM save the current environment for release
+
+        REM create new environ using env stored with function
+        EO%=Z%(F%+1,1):BI%=Z%(F%+1,0):EX%=AR%:GOSUB ENV_NEW_BINDS
+
+        REM release previous env if it is not the top one on the
+        REM stack (ZZ%(ZL%-2)) because our new env refers to it and
+        REM we no longer need to track it (since we are TCO recurring)
+        IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
+
+        REM claim the AST before releasing the list containing it
+        A%=Z%(F%,1):Z%(A%,0)=Z%(A%,0)+16
+        REM add AST to pending release queue to free as soon as EVAL
+        REM actually returns (LV%+1)
+        ZM%=ZM%+1:ZR%(ZM%,0)=A%:ZR%(ZM%,1)=LV%+1
+
+        REM pop and release f/args
+        AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
+
+        REM A% set above
+        E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
+
+  EVAL_RETURN:
+    REM AZ%=R%: PR%=1: GOSUB PR_STR
+    REM PRINT "EVAL_RETURN R%: ["+R$+"] ("+STR$(R%)+"), LV%:"+STR$(LV%)+",ER%:"+STR$(ER%)
+
+    REM release environment if not the top one on the stack
+    IF E%<>ZZ%(ZL%-1) THEN AY%=E%:GOSUB RELEASE
+
+    LV%=LV%-1: REM track basic return stack level
+
+    REM release everything we couldn't release earlier
+    GOSUB RELEASE_PEND
+
+    REM trigger GC
+    TA%=FRE(0)
+
+    REM pop A% and E% off the stack
+    E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
+
+    RETURN
+
+REM PRINT(A%) -> R$
+MAL_PRINT:
+  AZ%=A%:PR%=1:GOSUB PR_STR
+  RETURN
+
+REM RE(A$) -> R%
+REM Assume RE% has repl_env
+REM caller must release result
+RE:
+  R1%=0
+  GOSUB MAL_READ
+  R1%=R%
+  IF ER%<>-2 THEN GOTO REP_DONE
+
+  A%=R%:E%=RE%:GOSUB EVAL
+
+  REP_DONE:
+    REM Release memory from MAL_READ
+    IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
+    RETURN: REM caller must release result of EVAL
+
+REM REP(A$) -> R$
+REM Assume RE% has repl_env
+REP:
+  R1%=0:R2%=0
+  GOSUB MAL_READ
+  R1%=R%
+  IF ER%<>-2 THEN GOTO REP_DONE
+
+  A%=R%:E%=RE%:GOSUB EVAL
+  R2%=R%
+  IF ER%<>-2 THEN GOTO REP_DONE
+
+  A%=R%:GOSUB MAL_PRINT
+  RT$=R$
+
+  REP_DONE:
+    REM Release memory from MAL_READ and EVAL
+    IF R2%<>0 THEN AY%=R2%:GOSUB RELEASE
+    IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
+    R$=RT$
+    RETURN
+
+REM MAIN program
+MAIN:
+  GOSUB INIT_MEMORY
+
+  LV%=0
+
+  REM create repl_env
+  EO%=-1:GOSUB ENV_NEW:RE%=R%
+
+  REM core.EXT: defined in Basic
+  E%=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
+
+  ZT%=ZI%: REM top of memory after base repl_env
+
+  REM core.mal: defined using the language itself
+  A$="(def! not (fn* (a) (if a false true)))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  A$="(def! load-file (fn* (f) (eval (read-string (str "
+  A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
+  A$=A$+" (if (> (count xs) 1) (nth xs 1) (throw "+CHR$(34)+"odd number of"
+  A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)"
+  A$=A$+" `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  REM load the args file
+  A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  REM set the argument list
+  A$="(def! *ARGV* (rest -*ARGS*-))"
+  GOSUB RE:AY%=R%:GOSUB RELEASE
+
+  REM get the first argument
+  A$="(first -*ARGS*-)"
+  GOSUB RE
+
+  REM if there is an argument, then run it as a program
+  IF R%<>0 THEN AY%=R%:GOSUB RELEASE:GOTO RUN_PROG
+  REM no arguments, start REPL loop
+  IF R%=0 THEN GOTO REPL_LOOP
+
+  RUN_PROG:
+    REM run a single mal program and exit
+    A$="(load-file (first -*ARGS*-))"
+    GOSUB RE
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR
+    END
+
+  REPL_LOOP:
+    A$="user> ":GOSUB READLINE: REM call input parser
+    IF EOF=1 THEN GOTO QUIT
+
+    A$=R$:GOSUB REP: REM call REP
+
+    IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
+    PRINT R$
+    GOTO REPL_LOOP
+
+  QUIT:
+    REM P1%=ZT%: P2%=-1: GOSUB PR_MEMORY
+    GOSUB PR_MEMORY_SUMMARY
+    END
+
+  PRINT_ERROR:
+    REM if the error is an object, then print and free it
+    IF ER%>=0 THEN AZ%=ER%:PR%=0:GOSUB PR_STR:ER$=R$:AY%=ER%:GOSUB RELEASE
+    PRINT "Error: "+ER$
+    ER%=-2:ER$=""
+    RETURN
+
index fcb02a2..13c7932 100644 (file)
@@ -31,7 +31,11 @@ INIT_MEMORY:
   S4%=64: REM ZR% (release stack) size (4 bytes each)
 
   REM global error state
-  ER%=0:ER$=""
+  REM  -2 : no error
+  REM  -1 : string error in ER$
+  REM >=0 : pointer to error object
+  ER%=-2
+  ER$=""
 
   REM boxed element memory
   DIM Z%(S1%,1): REM TYPE ARRAY
@@ -131,9 +135,9 @@ RELEASE:
   REM PRINT "RELEASE AY%:"+STR$(AY%)+"["+R$+"] (byte0:"+STR$(Z%(AY%,0))+")"
 
   REM sanity check not already freed
-  IF (U6%)=15 THEN ER%=1:ER$="Free of free memory: "+STR$(AY%):RETURN
+  IF (U6%)=15 THEN ER%=-1:ER$="Free of free memory: "+STR$(AY%):RETURN
   IF U6%=14 THEN GOTO RELEASE_REFERENCE
-  IF Z%(AY%,0)<15 THEN ER%=1:ER$="Free of freed object: "+STR$(AY%):RETURN
+  IF Z%(AY%,0)<15 THEN ER%=-1:ER$="Free of freed object: "+STR$(AY%):RETURN
 
   REM decrease reference count by one
   Z%(AY%,0)=Z%(AY%,0)-16
@@ -148,8 +152,8 @@ RELEASE:
   IF U6%=11 THEN GOTO RELEASE_MAL_FUNCTION
   IF U6%=12 THEN GOTO RELEASE_ATOM
   IF U6%=13 THEN GOTO RELEASE_ENV
-  IF U6%=15 THEN ER%=1:ER$="RELEASE of already freed: "+STR$(AY%):RETURN
-  ER%=1:ER$="RELEASE not defined for type "+STR$(U6%):RETURN
+  IF U6%=15 THEN ER%=-1:ER$="RELEASE of already freed: "+STR$(AY%):RETURN
+  ER%=-1:ER$="RELEASE not defined for type "+STR$(U6%):RETURN
 
   RELEASE_SIMPLE:
     REM simple type (no recursing), just call FREE on it
@@ -161,7 +165,7 @@ RELEASE:
     GOTO RELEASE_TOP
   RELEASE_SEQ:
     IF Z%(AY%,1)=0 THEN GOTO RELEASE_SIMPLE_2
-    IF Z%(AY%+1,0)<>14 THEN ER%=1:ER$="invalid list value"+STR$(AY%+1):RETURN
+    IF Z%(AY%+1,0)<>14 THEN ER%=-1:ER$="invalid list value"+STR$(AY%+1):RETURN
     REM add value and next element to stack
     RC%=RC%+2:ZL%=ZL%+2:ZZ%(ZL%-1)=Z%(AY%+1,1):ZZ%(ZL%)=Z%(AY%,1)
     GOTO RELEASE_SIMPLE_2
@@ -198,7 +202,6 @@ RELEASE:
 
 REM RELEASE_PEND(LV%) -> nil
 RELEASE_PEND:
-  REM REM IF ER%<>0 THEN RETURN
   IF ZM%<0 THEN RETURN
   IF ZR%(ZM%,1)<=LV% THEN RETURN
   REM PRINT "RELEASE_PEND releasing:"+STR$(ZR%(ZM%,0))
@@ -366,12 +369,14 @@ CONS:
 
 REM SLICE(A%,B%,C%) -> R%
 REM make copy of sequence A% from index B% to C%
+REM returns R6% as reference to last element of slice
+REM returns A% as next element following slice (of original)
 SLICE:
   I=0
   R5%=-1: REM temporary for return as R%
   R6%=0: REM previous list element
   SLICE_LOOP:
-    REM always allocate at list one list element
+    REM always allocate at least one list element
     SZ%=2:GOSUB ALLOC
     Z%(R%,0)=6+16:Z%(R%,1)=0:Z%(R%+1,0)=14:Z%(R%+1,1)=0
     IF R5%=-1 THEN R5%=R%