Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / basic / core.in.bas
dissimilarity index 78%
index b3dccb6..009d81a 100644 (file)
-
-REM DO_FUNCTION(F, AR)
-DO_FUNCTION:
-  REM Get the function number
-  FF=Z%(F,1)
-
-  REM Get argument values
-  R=AR+1:GOSUB DEREF_R:AA=R
-  R=Z%(AR,1)+1:GOSUB DEREF_R:AB=R
-
-  REM Switch on the function number
-  IF FF>58 THEN ER=-1:ER$="unknown function"+STR$(FF):RETURN
-  ON FF/10+1 GOTO DO_1_9,DO_10_19,DO_20_29,DO_30_39,DO_40_49,DO_50_59
-
-  DO_1_9:
-  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_KEYWORD
-  DO_10_19:
-  ON FF-9 GOTO DO_KEYWORD_Q,DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READ_STRING,DO_READLINE,DO_SLURP,DO_LT,DO_LTE
-  DO_20_29:
-  ON FF-19 GOTO DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS,DO_LIST,DO_LIST_Q,DO_VECTOR
-  DO_30_39:
-  ON FF-29 GOTO DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q,DO_THROW,DO_THROW,DO_THROW,DO_THROW,DO_THROW,DO_THROW,DO_SEQUENTIAL_Q
-  DO_40_49:
-  ON FF-39 GOTO DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT,DO_APPLY,DO_MAP,DO_THROW
-  DO_50_59:
-  ON FF-49 GOTO DO_THROW,DO_THROW,DO_THROW,DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,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:
-    T=5:L=Z%(AA,1):GOSUB ALLOC
-    RETURN
-  DO_SYMBOL_Q:
-    R=1
-    IF (Z%(AA,0)AND15)=5 THEN R=2
-    RETURN
-  DO_KEYWORD:
-    A=Z%(AA,1)
-    AS$=S$(A)
-    IF MID$(AS$,1,1)<>CHR$(127) THEN AS$=CHR$(127)+AS$
-    GOSUB STRING_
-    T=4:L=R:GOSUB ALLOC
-    RETURN
-  DO_KEYWORD_Q:
-    R=1
-    IF (Z%(AA,0)AND15)<>4 THEN RETURN
-    IF MID$(S$(Z%(AA,1)),1,1)<>CHR$(127) THEN RETURN
-    R=2
-    RETURN
-
-  DO_PR_STR:
-    AZ=AR:PR=1:SE$=" ":GOSUB PR_STR_SEQ
-    AS$=R$:T=4:GOSUB STRING
-    RETURN
-  DO_STR:
-    AZ=AR:PR=0:SE$="":GOSUB PR_STR_SEQ
-    AS$=R$:T=4:GOSUB STRING
-    RETURN
-  DO_PRN:
-    AZ=AR:PR=1:SE$=" ":GOSUB PR_STR_SEQ
-    PRINT R$
-    R=0
-    RETURN
-  DO_PRINTLN:
-    AZ=AR:PR=0:SE$=" ":GOSUB PR_STR_SEQ
-    PRINT R$
-    R=0
-    RETURN
-  DO_READ_STRING:
-    A$=S$(Z%(AA,1))
-    GOSUB READ_STR
-    RETURN
-  DO_READLINE:
-    A$=S$(Z%(AA,1)):GOSUB READLINE
-    IF EOF=1 THEN EOF=0:R=0:RETURN
-    AS$=R$:T=4:GOSUB STRING
-    RETURN
-  DO_SLURP:
-    R$=""
-    REM OPEN 1,8,2,S$(Z%(AA,1))+",SEQ,R"
-    REM OPEN 1,8,2,S$(Z%(AA,1))
-    OPEN 1,8,0,S$(Z%(AA,1))
-    DO_SLURP_LOOP:
-      A$=""
-      GET#1,A$
-      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
-      GOTO DO_SLURP_LOOP
-    DO_SLURP_DONE:
-      CLOSE 1
-      AS$=R$:T=4:GOSUB STRING
-      RETURN
-
-  DO_LT:
-    R=1
-    IF Z%(AA,1)<Z%(AB,1) THEN R=2
-    RETURN
-  DO_LTE:
-    R=1
-    IF Z%(AA,1)<=Z%(AB,1) THEN R=2
-    RETURN
-  DO_GT:
-    R=1
-    IF Z%(AA,1)>Z%(AB,1) THEN R=2
-    RETURN
-  DO_GTE:
-    R=1
-    IF Z%(AA,1)>=Z%(AB,1) THEN R=2
-    RETURN
-
-  DO_ADD:
-    T=2:L=Z%(AA,1)+Z%(AB,1):GOSUB ALLOC
-    RETURN
-  DO_SUB:
-    T=2:L=Z%(AA,1)-Z%(AB,1):GOSUB ALLOC
-    RETURN
-  DO_MULT:
-    T=2:L=Z%(AA,1)*Z%(AB,1):GOSUB ALLOC
-    RETURN
-  DO_DIV:
-    T=2:L=Z%(AA,1)/Z%(AB,1):GOSUB ALLOC
-    RETURN
-  DO_TIME_MS:
-    R=0
-    RETURN
-
-  DO_LIST:
-    R=AR
-    Z%(R,0)=Z%(R,0)+16
-    RETURN
-  DO_LIST_Q:
-    A=AA:GOSUB LIST_Q
-    R=R+1: REM map to mal false/true
-    RETURN
-  DO_VECTOR:
-    A=AR:T=7:GOSUB FORCE_SEQ_TYPE
-    RETURN
-  DO_VECTOR_Q:
-    R=1
-    IF (Z%(AA,0)AND15)=7 THEN R=2
-    RETURN
-  DO_HASH_MAP:
-    A=AR:T=8:GOSUB FORCE_SEQ_TYPE
-    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:
-    T=6:L=AB:N=AA:GOSUB ALLOC
-    RETURN
-  DO_CONCAT:
-    REM if empty arguments, return empty list
-    IF Z%(AR,1)=0 THEN R=3:Z%(R,0)=Z%(R,0)+16:RETURN
-
-    REM single argument
-    IF Z%(Z%(AR,1),1)<>0 THEN GOTO DO_CONCAT_MULT
-      REM force to list type
-      A=AA:T=6:GOSUB FORCE_SEQ_TYPE
-      RETURN
-
-    REM multiple arguments
-    DO_CONCAT_MULT:
-      CZ%=X: REM save current stack position
-      REM push arguments onto the stack
-      DO_CONCAT_STACK:
-        R=AR+1:GOSUB DEREF_R
-        X=X+1:S%(X)=R: REM push sequence
-        AR=Z%(AR,1)
-        IF Z%(AR,1)<>0 THEN GOTO DO_CONCAT_STACK
-
-    REM pop last argument as our seq to prepend to
-    AB=S%(X):X=X-1
-    REM last arg/seq is not copied so we need to inc ref to it
-    Z%(AB,0)=Z%(AB,0)+16
-    DO_CONCAT_LOOP:
-      IF X=CZ% THEN R=AB:RETURN
-      AA=S%(X):X=X-1: REM pop off next seq to prepend
-      IF Z%(AA,1)=0 THEN GOTO DO_CONCAT_LOOP: REM skip empty seqs
-      A=AA:B=0:C=-1:GOSUB SLICE
-
-      REM release the terminator of new list (we skip over it)
-      AY=Z%(R6,1):GOSUB RELEASE
-      REM attach new list element before terminator (last actual
-      REM element to the next sequence
-      Z%(R6,1)=AB
-
-      AB=R
-      GOTO DO_CONCAT_LOOP
-  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
-    DO_NTH_LOOP:
-      IF B=0 THEN GOTO DO_NTH_DONE
-      B=B-1
-      AA=Z%(AA,1)
-      GOTO DO_NTH_LOOP
-    DO_NTH_DONE:
-      R=Z%(AA+1,1)
-      Z%(R,0)=Z%(R,0)+16
-      RETURN
-  DO_FIRST:
-    IF AA=0 THEN R=0:RETURN
-    IF Z%(AA,1)=0 THEN R=0
-    IF Z%(AA,1)<>0 THEN R=AA+1:GOSUB DEREF_R
-    IF R<>0 THEN Z%(R,0)=Z%(R,0)+16
-    RETURN
-  DO_REST:
-    IF AA=0 THEN R=3:Z%(R,0)=Z%(R,0)+16:RETURN
-    IF Z%(AA,1)=0 THEN A=AA
-    IF Z%(AA,1)<>0 THEN A=Z%(AA,1)
-    T=6:GOSUB FORCE_SEQ_TYPE
-    RETURN
-  DO_EMPTY_Q:
-    R=1
-    IF Z%(AA,1)=0 THEN R=2
-    RETURN
-  DO_COUNT:
-    A=AA:GOSUB COUNT
-    T=2:L=R:GOSUB ALLOC
-    RETURN
-  DO_APPLY:
-    F=AA
-    AR=Z%(AR,1)
-    A=AR:GOSUB COUNT:R4=R
-
-    A=Z%(AR+1,1)
-    REM no intermediate args, but not a list, so convert it first
-    IF R4<=1 AND (Z%(A,0)AND15)<>6 THEN :T=6:GOSUB FORCE_SEQ_TYPE:GOTO DO_APPLY_2
-    REM no intermediate args, just call APPLY directly
-    IF R4<=1 THEN AR=A: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
-
-    DO_APPLY_2:
-      X=X+1:S%(X)=R: REM push/save new args for release
-      AR=R:GOSUB APPLY
-      AY=S%(X):X=X-1:GOSUB RELEASE: REM pop/release new args
-      RETURN
-  DO_MAP:
-    F=AA
-
-    REM first result list element
-    T=6:L=0:N=0:GOSUB ALLOC
-
-    REM push future return val, prior entry, F and AB
-    X=X+4:S%(X-3)=R:S%(X-2)=0:S%(X-1)=F:S%(X)=AB
-
-    DO_MAP_LOOP:
-      REM set previous to current if not the first element
-      IF S%(X-2)<>0 THEN Z%(S%(X-2),1)=R
-      REM update previous reference to current
-      S%(X-2)=R
-
-      IF Z%(AB,1)=0 THEN GOTO DO_MAP_DONE
-
-      REM create argument list for apply call
-      Z%(3,0)=Z%(3,0)+16
-      REM inc ref cnt of referred argument
-      T=6:L=3:N=Z%(AB+1,1):GOSUB ALLOC
-
-      REM push argument list
-      X=X+1:S%(X)=R
-
-      AR=R:GOSUB APPLY
-
-      REM pop apply args are release them
-      AY=S%(X):X=X-1:GOSUB RELEASE
-
-      REM set the result value
-      Z%(S%(X-2)+1,1)=R
-
-      REM restore F
-      F=S%(X-1)
-
-      REM update AB to next source element
-      S%(X)=Z%(S%(X),1)
-      AB=S%(X)
-
-      REM allocate next element
-      T=6:L=0:N=0:GOSUB ALLOC
-
-      GOTO DO_MAP_LOOP
-
-    DO_MAP_DONE:
-      REM get return val
-      R=S%(X-3)
-      REM pop everything off stack
-      X=X-4
-      RETURN
-
-  DO_ATOM:
-    T=12:L=AA:GOSUB ALLOC
-    RETURN
-  DO_ATOM_Q:
-    R=1
-    IF (Z%(AA,0)AND15)=12 THEN R=2
-    RETURN
-  DO_DEREF:
-    R=Z%(AA,1):GOSUB DEREF_R
-    Z%(R,0)=Z%(R,0)+16
-    RETURN
-  DO_RESET_BANG:
-    R=AB
-    REM release current value
-    AY=Z%(AA,1):GOSUB RELEASE
-    REM inc ref by 2 for atom ownership and since we are returning it
-    Z%(R,0)=Z%(R,0)+32
-    REM update value
-    Z%(AA,1)=R
-    RETURN
-  DO_SWAP_BANG:
-    F=AB
-
-    REM add atom to front of the args list
-    T=6:L=Z%(Z%(AR,1),1):N=Z%(AA,1):GOSUB ALLOC: REM cons
-    AR=R
-
-    REM push args for release after
-    X=X+1:S%(X)=AR
-
-    REM push atom
-    X=X+1:S%(X)=AA
-
-    GOSUB APPLY
-
-    REM pop atom
-    AA=S%(X):X=X-1
-
-    REM pop and release args
-    AY=S%(X):X=X-1:GOSUB RELEASE
-
-    REM use reset to update the value
-    AB=R:GOSUB DO_RESET_BANG
-
-    REM but decrease ref cnt of return by 1 (not sure why)
-    AY=R:GOSUB RELEASE
-
-    RETURN
-
-  DO_PR_MEMORY:
-    P1%=ZT%:P2%=-1:GOSUB PR_MEMORY
-    RETURN
-  DO_PR_MEMORY_SUMMARY:
-    GOSUB PR_MEMORY_SUMMARY
-    RETURN
-
-  DO_EVAL:
-    A=AA:E=RE%:GOSUB EVAL
-    RETURN
-
-INIT_CORE_SET_FUNCTION:
-  GOSUB NATIVE_FUNCTION
-  V=R:GOSUB ENV_SET_S
-  RETURN
-
-REM INIT_CORE_NS(E)
-INIT_CORE_NS:
-  REM create the environment mapping
-  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$="keyword":A=9:GOSUB INIT_CORE_SET_FUNCTION
-  K$="keyword?":A=10:GOSUB INIT_CORE_SET_FUNCTION
-
-  K$="pr-str":A=11:GOSUB INIT_CORE_SET_FUNCTION
-  K$="str":A=12:GOSUB INIT_CORE_SET_FUNCTION
-  K$="prn":A=13:GOSUB INIT_CORE_SET_FUNCTION
-  K$="println":A=14:GOSUB INIT_CORE_SET_FUNCTION
-  K$="read-string":A=15:GOSUB INIT_CORE_SET_FUNCTION
-  K$="readline":A=16:GOSUB INIT_CORE_SET_FUNCTION
-  K$="slurp":A=17:GOSUB INIT_CORE_SET_FUNCTION
-
-  K$="<":A=18:GOSUB INIT_CORE_SET_FUNCTION
-  K$="<=":A=19:GOSUB INIT_CORE_SET_FUNCTION
-  K$=">":A=20:GOSUB INIT_CORE_SET_FUNCTION
-  K$=">=":A=21:GOSUB INIT_CORE_SET_FUNCTION
-  K$="+":A=22:GOSUB INIT_CORE_SET_FUNCTION
-  K$="-":A=23:GOSUB INIT_CORE_SET_FUNCTION
-  K$="*":A=24:GOSUB INIT_CORE_SET_FUNCTION
-  K$="/":A=25:GOSUB INIT_CORE_SET_FUNCTION
-  K$="time-ms":A=26:GOSUB INIT_CORE_SET_FUNCTION
-
-  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$="assoc":A=33:GOSUB INIT_CORE_SET_FUNCTION
-  K$="dissoc":A=34:GOSUB INIT_CORE_SET_FUNCTION
-  K$="get":A=35:GOSUB INIT_CORE_SET_FUNCTION
-  K$="contains?":A=36:GOSUB INIT_CORE_SET_FUNCTION
-  K$="keys":A=37:GOSUB INIT_CORE_SET_FUNCTION
-  K$="vals":A=38: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
-  K$="first":A=43:GOSUB INIT_CORE_SET_FUNCTION
-  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$="with-meta":A=51:GOSUB INIT_CORE_SET_FUNCTION
-  K$="meta":A=52:GOSUB INIT_CORE_SET_FUNCTION
-  K$="atom":A=53:GOSUB INIT_CORE_SET_FUNCTION
-  K$="atom?":A=54:GOSUB INIT_CORE_SET_FUNCTION
-  K$="deref":A=55:GOSUB INIT_CORE_SET_FUNCTION
-  K$="reset!":A=56:GOSUB INIT_CORE_SET_FUNCTION
-  K$="swap!":A=57:GOSUB INIT_CORE_SET_FUNCTION
-
-  K$="eval":A=58:GOSUB INIT_CORE_SET_FUNCTION
-
-  RETURN
+REM APPLY should really be in types.in.bas but it is here because it
+REM calls DO_TCO_FUNCTION so it will cause syntax errors for steps1-3
+REM if it is in types.in.bas because there are unresolved labels.
+
+REM APPLY(F, AR) -> R
+REM   - restores E
+REM   - call using GOTO and with return label/address on the stack
+SUB APPLY
+  REM if metadata, get the actual object
+  GOSUB TYPE_F
+  IF T=14 THEN F=Z%(F+1):GOSUB TYPE_F
+
+  ON T-8 GOTO APPLY_FUNCTION,APPLY_MAL_FUNCTION,APPLY_MAL_FUNCTION
+
+  APPLY_FUNCTION:
+    REM regular function
+    IF Z%(F+1)<64 THEN GOSUB DO_FUNCTION:GOTO APPLY_DONE
+    REM for recur functions (apply, map, swap!), use GOTO
+    IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION
+    GOTO APPLY_DONE
+
+  APPLY_MAL_FUNCTION:
+    Q=E:GOSUB PUSH_Q: REM save the current environment
+
+    REM create new environ using env and params stored in the
+    REM function and bind the params to the apply arguments
+    C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS
+
+    A=Z%(F+1):E=R:CALL EVAL
+
+    AY=E:GOSUB RELEASE: REM release the new environment
+
+    GOSUB POP_Q:E=Q: REM pop/restore the saved environment
+
+  APPLY_DONE:
+END SUB
+
+
+REM DO_TCO_FUNCTION(F, AR)
+SUB DO_TCO_FUNCTION
+  G=Z%(F+1)
+
+  REM Get argument values
+  A=Z%(AR+2)
+  B=Z%(Z%(AR+1)+2)
+
+REM PRINT "F:"+STR$(F)+", Z%(F):"+STR$(Z%(F))+", G:"+STR$(G)
+  ON G-64 GOTO DO_APPLY,DO_MAP,DO_SWAP_BANG
+
+  DO_APPLY:
+    F=A
+    AR=Z%(AR+1)
+    A=AR:GOSUB COUNT:C=R
+
+    A=Z%(AR+2)
+    REM no intermediate args, but not a list, so convert it first
+    GOSUB TYPE_A
+    IF C<=1 AND T<>6 THEN T=6:GOSUB FORCE_SEQ_TYPE:GOTO DO_APPLY_2
+    REM no intermediate args, just call APPLY directly
+    IF C<=1 THEN GOTO DO_APPLY_1
+
+    REM prepend intermediate args to final args element
+    A=AR:B=0:C=C-1:GOSUB SLICE
+    REM release the terminator of new list (we skip over it)
+    REM we already checked for an empty list above, so R6 is pointer
+    REM a real non-empty list
+    AY=Z%(R6+1):GOSUB RELEASE
+    REM attach end of slice to final args element
+    A2=Z%(A+2)
+    Z%(R6+1)=A2
+    Z%(A2)=Z%(A2)+32
+
+    GOTO DO_APPLY_2
+
+    DO_APPLY_1:
+      AR=A:CALL APPLY
+
+      GOTO DO_TCO_FUNCTION_DONE
+
+    DO_APPLY_2:
+      GOSUB PUSH_R: REM push/save new args for release
+
+      AR=R:CALL APPLY
+
+      REM pop/release new args
+      GOSUB POP_Q:AY=Q
+      GOSUB RELEASE
+      GOTO DO_TCO_FUNCTION_DONE
+
+  DO_MAP:
+    F=A
+
+    REM setup the stack for the loop
+    T=6:GOSUB MAP_LOOP_START
+
+    DO_MAP_LOOP:
+      IF Z%(B+1)=0 THEN GOTO DO_MAP_DONE
+
+      REM create argument list for apply
+      T=6:L=6:M=Z%(B+2):GOSUB ALLOC
+
+      GOSUB PUSH_R: REM push argument list
+      Q=F:GOSUB PUSH_Q: REM push F
+      Q=B:GOSUB PUSH_Q: REM push B
+
+      AR=R:CALL APPLY
+
+      GOSUB POP_Q:B=Q: REM pop B
+      GOSUB POP_Q:F=Q: REM pop F
+      GOSUB POP_Q: REM pop apply args and release them
+      AY=Q:GOSUB RELEASE
+
+      REM main value is result of apply
+      M=R
+
+      B=Z%(B+1): REM go to the next element
+
+      REM if error, release the unattached element
+      IF ER<>-2 THEN AY=R:GOSUB RELEASE:GOTO DO_MAP_DONE
+
+      REM update the return sequence structure
+      REM release N since list takes full ownership
+      C=1:T=6:GOSUB MAP_LOOP_UPDATE
+
+      GOTO DO_MAP_LOOP
+
+    DO_MAP_DONE:
+      REM cleanup stack and get return value
+      GOSUB MAP_LOOP_DONE
+      GOTO DO_TCO_FUNCTION_DONE
+
+  DO_SWAP_BANG:
+    F=B
+
+    REM add atom to front of the args list
+    T=6:L=Z%(Z%(AR+1)+1):M=Z%(A+1):GOSUB ALLOC: REM cons
+    AR=R
+
+    REM push args for release after
+    Q=AR:GOSUB PUSH_Q
+
+    REM push atom
+    GOSUB PUSH_A
+
+    CALL APPLY
+
+    REM pop atom
+    GOSUB POP_A
+
+    REM pop and release args
+    GOSUB POP_Q:AY=Q
+    GOSUB RELEASE
+
+    REM use reset to update the value
+    B=R:GOSUB DO_RESET_BANG
+
+    REM but decrease ref cnt of return by 1 (not sure why)
+    AY=R:GOSUB RELEASE
+
+    GOTO DO_TCO_FUNCTION_DONE
+
+  DO_TCO_FUNCTION_DONE:
+END SUB
+
+REM DO_FUNCTION(F, AR)
+DO_FUNCTION:
+  REM Get the function number
+  G=Z%(F+1)
+
+  REM Get argument values
+  A=Z%(AR+2):A1=Z%(A+1)
+  B=Z%(Z%(AR+1)+2):B1=Z%(B+1)
+
+  REM Switch on the function number
+  REM MEMORY DEBUGGING:
+  REM IF G>59 THEN ER=-1:E$="unknown function"+STR$(G):RETURN
+  ON INT(G/10)+1 GOTO DO_1_9,DO_10_19,DO_20_29,DO_30_39,DO_40_49,DO_50_59,DO_60_69
+
+  DO_1_9:
+  ON G GOTO DO_EQUAL_Q,DO_THROW,DO_NIL_Q,DO_TRUE_Q,DO_FALSE_Q,DO_STRING_Q,DO_SYMBOL,DO_SYMBOL_Q,DO_KEYWORD
+  DO_10_19:
+  ON G-9 GOTO DO_KEYWORD_Q,DO_NUMBER_Q,DO_FN_Q,DO_MACRO_Q,DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READ_STRING,DO_READLINE
+  DO_20_29:
+  ON G-19 GOTO DO_SLURP,DO_LT,DO_LTE,DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS
+  DO_30_39:
+  ON G-29 GOTO DO_LIST,DO_LIST_Q,DO_VECTOR,DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q,DO_ASSOC,DO_THROW,DO_GET,DO_CONTAINS
+  DO_40_49:
+  ON G-39 GOTO DO_KEYS,DO_VALS,DO_SEQUENTIAL_Q,DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT
+  DO_50_59:
+  ON G-49 GOTO DO_CONJ,DO_SEQ,DO_WITH_META,DO_META,DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_EVAL,DO_READ_FILE
+  DO_60_69:
+  ON G-59 GOTO DO_PR_MEMORY_SUMMARY
+
+  DO_EQUAL_Q:
+    GOSUB EQUAL_Q
+    GOTO RETURN_TRUE_FALSE
+  DO_THROW:
+    ER=A
+    Z%(ER)=Z%(ER)+32
+    R=-1
+    RETURN
+  DO_NIL_Q:
+    R=A=0
+    GOTO RETURN_TRUE_FALSE
+  DO_TRUE_Q:
+    R=A=4
+    GOTO RETURN_TRUE_FALSE
+  DO_FALSE_Q:
+    R=A=2
+    GOTO RETURN_TRUE_FALSE
+  DO_STRING_Q:
+    R=0
+    GOSUB TYPE_A
+    IF T<>4 THEN GOTO RETURN_TRUE_FALSE
+    IF MID$(S$(A1),1,1)=CHR$(127) THEN GOTO RETURN_TRUE_FALSE
+    R=1
+    GOTO RETURN_TRUE_FALSE
+  DO_SYMBOL:
+    B$=S$(A1)
+    T=5:GOSUB STRING
+    RETURN
+  DO_SYMBOL_Q:
+    GOSUB TYPE_A
+    R=T=5
+    GOTO RETURN_TRUE_FALSE
+  DO_KEYWORD:
+    B$=S$(A1)
+    IF MID$(B$,1,1)<>CHR$(127) THEN B$=CHR$(127)+B$
+    T=4:GOSUB STRING
+    RETURN
+  DO_KEYWORD_Q:
+    R=0
+    GOSUB TYPE_A
+    IF T<>4 THEN GOTO RETURN_TRUE_FALSE
+    IF MID$(S$(A1),1,1)<>CHR$(127) THEN GOTO RETURN_TRUE_FALSE
+    R=1
+    GOTO RETURN_TRUE_FALSE
+  DO_NUMBER_Q:
+    GOSUB TYPE_A
+    R=T=2
+    GOTO RETURN_TRUE_FALSE
+  DO_FN_Q:
+    GOSUB TYPE_A
+    R=T=9 OR T=10
+    GOTO RETURN_TRUE_FALSE
+  DO_MACRO_Q:
+    GOSUB TYPE_A
+    R=T=11
+    GOTO RETURN_TRUE_FALSE
+
+  DO_PR_STR:
+    AZ=AR:B=1:B$=" ":GOSUB PR_STR_SEQ
+    B$=R$:T=4:GOSUB STRING
+    RETURN
+  DO_STR:
+    AZ=AR:B=0:B$="":GOSUB PR_STR_SEQ
+    B$=R$:T=4:GOSUB STRING
+    RETURN
+  DO_PRN:
+    AZ=AR:B=1:B$=" ":GOSUB PR_STR_SEQ
+    PRINT R$
+    R=0
+    GOTO INC_REF_R
+  DO_PRINTLN:
+    AZ=AR:B=0:B$=" ":GOSUB PR_STR_SEQ
+    PRINT R$
+    R=0
+    GOTO INC_REF_R
+  DO_READ_STRING:
+    A$=S$(A1)
+    GOSUB READ_STR
+    RETURN
+  DO_READLINE:
+    A$=S$(A1):GOSUB READLINE
+    IF EZ>0 THEN EZ=0:R=0:GOTO INC_REF_R
+    B$=R$:T=4:GOSUB STRING
+    RETURN
+  DO_SLURP:
+    R$=""
+    EZ=0
+    #cbm OPEN 2,8,0,S$(A1)
+    #qbasic A$=S$(A1)
+    #qbasic IF NOT _FILEEXISTS(A$) THEN ER=-1:E$="File not found":RETURN
+    #qbasic OPEN A$ FOR INPUT AS #2
+    DO_SLURP_LOOP:
+      C$=""
+      RJ=1:GOSUB READ_FILE_CHAR
+      #cbm IF ASC(C$)=10 THEN R$=R$+CHR$(13)
+      #qbasic IF ASC(C$)=10 THEN R$=R$+CHR$(10)
+      IF (ASC(C$)<>10) AND (C$<>"") THEN R$=R$+C$
+      IF EZ>0 THEN GOTO DO_SLURP_DONE
+      GOTO DO_SLURP_LOOP
+    DO_SLURP_DONE:
+      CLOSE 2
+      IF ER>-2 THEN RETURN
+      B$=R$:T=4:GOSUB STRING
+      RETURN
+
+  DO_LT:
+    R=A1<B1
+    GOTO RETURN_TRUE_FALSE
+  DO_LTE:
+    R=A1<=B1
+    GOTO RETURN_TRUE_FALSE
+  DO_GT:
+    R=A1>B1
+    GOTO RETURN_TRUE_FALSE
+  DO_GTE:
+    R=A1>=B1
+    GOTO RETURN_TRUE_FALSE
+
+  DO_ADD:
+    T=2:L=A1+B1:GOSUB ALLOC
+    RETURN
+  DO_SUB:
+    T=2:L=A1-B1:GOSUB ALLOC
+    RETURN
+  DO_MULT:
+    T=2:L=A1*B1:GOSUB ALLOC
+    RETURN
+  DO_DIV:
+    T=2:L=A1/B1:GOSUB ALLOC
+    RETURN
+  DO_TIME_MS:
+    #cbm T=2:L=INT((TI-BT)*16.667):GOSUB ALLOC
+    #qbasic T=2:L=INT((TIMER(0.001)-BT#)*1000):GOSUB ALLOC
+    RETURN
+
+  DO_LIST:
+    R=AR
+    GOTO INC_REF_R
+  DO_LIST_Q:
+    GOSUB LIST_Q
+    GOTO RETURN_TRUE_FALSE
+  DO_VECTOR:
+    A=AR:T=7:GOSUB FORCE_SEQ_TYPE
+    RETURN
+  DO_VECTOR_Q:
+    GOSUB TYPE_A
+    R=T=7
+    GOTO RETURN_TRUE_FALSE
+  DO_HASH_MAP:
+    REM setup the stack for the loop
+    T=8:GOSUB MAP_LOOP_START
+
+    A=AR
+    DO_HASH_MAP_LOOP:
+      IF Z%(A+1)=0 THEN GOTO DO_HASH_MAP_LOOP_DONE
+
+      M=Z%(A+2)
+      N=Z%(Z%(A+1)+2)
+
+      A=Z%(Z%(A+1)+1): REM skip two
+
+      REM update the return sequence structure
+      REM do not release M and N since we are pulling them from the
+      REM arguments (and not creating them here)
+      C=0:GOSUB MAP_LOOP_UPDATE
+
+      GOTO DO_HASH_MAP_LOOP
+
+    DO_HASH_MAP_LOOP_DONE:
+      REM cleanup stack and get return value
+      GOSUB MAP_LOOP_DONE
+      RETURN
+
+  DO_MAP_Q:
+    GOSUB TYPE_A
+    R=T=8
+    GOTO RETURN_TRUE_FALSE
+  DO_ASSOC:
+    H=A
+    AR=Z%(AR+1)
+    DO_ASSOC_LOOP:
+      K=Z%(AR+2)
+      C=Z%(Z%(AR+1)+2)
+      Z%(H)=Z%(H)+32
+      GOSUB ASSOC1:H=R
+      AR=Z%(Z%(AR+1)+1)
+      IF AR=0 OR Z%(AR+1)=0 THEN RETURN
+      GOTO DO_ASSOC_LOOP
+  DO_GET:
+    IF A=0 THEN R=0:GOTO INC_REF_R
+    H=A:K=B:GOSUB HASHMAP_GET
+    GOTO INC_REF_R
+  DO_CONTAINS:
+    H=A:K=B:GOSUB HASHMAP_CONTAINS
+    GOTO RETURN_TRUE_FALSE
+  DO_KEYS:
+    T1=0
+    GOTO DO_KEYS_VALS
+  DO_VALS:
+    T1=1
+  DO_KEYS_VALS:
+    REM setup the stack for the loop
+    T=6:GOSUB MAP_LOOP_START
+
+    DO_KEYS_VALS_LOOP:
+      IF Z%(A+1)=0 THEN GOTO DO_KEYS_VALS_LOOP_DONE
+
+      IF T1=0 THEN M=Z%(A+2)
+      IF T1=1 THEN M=Z%(A+3)
+
+      A=Z%(A+1): REM next element
+
+      REM update the return sequence structure
+      REM do not release N since we are pulling it from the
+      REM hash-map (and not creating them here)
+      C=0:GOSUB MAP_LOOP_UPDATE
+
+      GOTO DO_KEYS_VALS_LOOP
+
+    DO_KEYS_VALS_LOOP_DONE:
+      REM cleanup stack and get return value
+      GOSUB MAP_LOOP_DONE
+      RETURN
+
+  DO_SEQUENTIAL_Q:
+    GOSUB TYPE_A
+    R=T=6 OR T=7
+    GOTO RETURN_TRUE_FALSE
+  DO_CONS:
+    T=6:L=B:M=A:GOSUB ALLOC
+    RETURN
+  DO_CONCAT:
+    REM always a list
+    R=6:GOSUB INC_REF_R
+    GOSUB PUSH_R: REM current value
+    GOSUB PUSH_R: REM return value
+
+    DO_CONCAT_LOOP:
+      IF AR<16 THEN GOTO DO_CONCAT_DONE: REM no more elements
+
+      REM slice/copy current element to a list
+      A=Z%(AR+2)
+      IF A<16 THEN GOTO DO_CONCAT_LOOP_NEXT: REM skip empty elements
+      B=0:C=-1:GOSUB SLICE
+
+      GOSUB PEEK_Q: REM return value
+      REM if this is the first element, set return element
+      IF Q=6 THEN Q=R:GOSUB PUT_Q:GOTO DO_CONCAT_LOOP_AGAIN
+      REM otherwise Q<>6, so attach current to sliced
+      GOSUB PEEK_Q_1
+      Z%(Q+1)=R
+
+      DO_CONCAT_LOOP_AGAIN:
+        REM update current to end of sliced list
+        Q=R6:GOSUB PUT_Q_1
+        REM dec empty since no longer part of slice
+        AY=6:GOSUB RELEASE
+      DO_CONCAT_LOOP_NEXT:
+        REM next list element
+        AR=Z%(AR+1)
+        GOTO DO_CONCAT_LOOP
+
+    DO_CONCAT_DONE:
+      GOSUB POP_R: REM pop return value
+      GOSUB POP_Q: REM pop current
+      RETURN
+
+  DO_NTH:
+    B=B1
+    GOSUB COUNT
+    IF R<=B THEN R=-1:ER=-1:E$="nth: index out of range":RETURN
+    DO_NTH_LOOP:
+      IF B=0 THEN GOTO DO_NTH_DONE
+      B=B-1
+      A=Z%(A+1)
+      GOTO DO_NTH_LOOP
+    DO_NTH_DONE:
+      R=Z%(A+2)
+      GOTO INC_REF_R
+  DO_FIRST:
+    R=0
+    IF A=0 THEN GOTO INC_REF_R
+    IF A1<>0 THEN R=Z%(A+2)
+    GOTO INC_REF_R
+  DO_REST:
+    IF A=0 THEN R=6:GOTO INC_REF_R
+    IF A1<>0 THEN A=A1: REM get the next sequence element
+    T=6:GOSUB FORCE_SEQ_TYPE
+    RETURN
+  DO_EMPTY_Q:
+    R=A1=0
+    GOTO RETURN_TRUE_FALSE
+  DO_COUNT:
+    GOSUB COUNT
+    T=2:L=R:GOSUB ALLOC
+    RETURN
+  DO_CONJ:
+    R=0
+    GOTO INC_REF_R
+  DO_SEQ:
+    R=0
+    GOTO INC_REF_R
+
+  DO_WITH_META:
+    GOSUB TYPE_A
+    REM remove existing metadata first
+    IF T=14 THEN A=A1:GOTO DO_WITH_META
+    T=14:L=A:M=B:GOSUB ALLOC
+    RETURN
+  DO_META:
+    R=0
+    GOSUB TYPE_A
+    IF T=14 THEN R=Z%(A+2)
+    GOTO INC_REF_R
+  DO_ATOM:
+    T=12:L=A:GOSUB ALLOC
+    RETURN
+  DO_ATOM_Q:
+    GOSUB TYPE_A
+    R=T=12
+    GOTO RETURN_TRUE_FALSE
+  DO_DEREF:
+    R=A1
+    GOTO INC_REF_R
+  DO_RESET_BANG:
+    R=B
+    REM release current value
+    REM can't use A1 here because DO_RESET_BANG is called from swap!
+    AY=Z%(A+1):GOSUB RELEASE
+    REM inc ref by 2 for atom ownership and since we are returning it
+    Z%(R)=Z%(R)+64
+    REM update value
+    Z%(A+1)=R
+    RETURN
+
+  DO_EVAL:
+    Q=E:GOSUB PUSH_Q: REM push/save environment
+    E=D:CALL EVAL
+    GOSUB POP_Q:E=Q
+    RETURN
+
+  DO_READ_FILE:
+    A$=S$(A1)
+    GOSUB READ_FILE
+    RETURN
+
+  REM DO_PR_MEMORY:
+  REM   P1=ZT:P2=-1:GOSUB PR_MEMORY
+  REM   RETURN
+  DO_PR_MEMORY_SUMMARY:
+    REM GOSUB PR_MEMORY_SUMMARY
+    GOSUB PR_MEMORY_SUMMARY_SMALL
+    R=0
+    GOTO INC_REF_R
+    RETURN
+
+INIT_CORE_SET_FUNCTION:
+  T=9:L=A:GOSUB ALLOC: REM native function
+  C=R:GOSUB ENV_SET_S
+  A=A+1
+  RETURN
+
+REM INIT_CORE_NS(E)
+INIT_CORE_NS:
+  REM create the environment mapping
+  REM must match DO_FUNCTION mappings
+
+  A=1
+  B$="=":GOSUB INIT_CORE_SET_FUNCTION: REM A=1
+  B$="throw":GOSUB INIT_CORE_SET_FUNCTION: REM A=2
+  B$="nil?":GOSUB INIT_CORE_SET_FUNCTION: REM A=3
+  B$="true?":GOSUB INIT_CORE_SET_FUNCTION: REM A=4
+  B$="false?":GOSUB INIT_CORE_SET_FUNCTION: REM A=5
+  B$="string?":GOSUB INIT_CORE_SET_FUNCTION: REM A=6
+  B$="symbol":GOSUB INIT_CORE_SET_FUNCTION: REM A=7
+  B$="symbol?":GOSUB INIT_CORE_SET_FUNCTION: REM A=8
+  B$="keyword":GOSUB INIT_CORE_SET_FUNCTION: REM A=9
+  B$="keyword?":GOSUB INIT_CORE_SET_FUNCTION: REM A=10
+  B$="number?":GOSUB INIT_CORE_SET_FUNCTION: REM A=11
+  B$="fn?":GOSUB INIT_CORE_SET_FUNCTION: REM A=12
+  B$="macro?":GOSUB INIT_CORE_SET_FUNCTION: REM A=13
+
+  B$="pr-str":GOSUB INIT_CORE_SET_FUNCTION: REM A=14
+  B$="str":GOSUB INIT_CORE_SET_FUNCTION: REM A=15
+  B$="prn":GOSUB INIT_CORE_SET_FUNCTION: REM A=16
+  B$="println":GOSUB INIT_CORE_SET_FUNCTION: REM A=17
+  B$="read-string":GOSUB INIT_CORE_SET_FUNCTION: REM A=18
+  B$="readline":GOSUB INIT_CORE_SET_FUNCTION: REM A=19
+  B$="slurp":GOSUB INIT_CORE_SET_FUNCTION: REM A=20
+
+  B$="<":GOSUB INIT_CORE_SET_FUNCTION: REM A=21
+  B$="<=":GOSUB INIT_CORE_SET_FUNCTION: REM A=22
+  B$=">":GOSUB INIT_CORE_SET_FUNCTION: REM A=23
+  B$=">=":GOSUB INIT_CORE_SET_FUNCTION: REM A=24
+  B$="+":GOSUB INIT_CORE_SET_FUNCTION: REM A=25
+  B$="-":GOSUB INIT_CORE_SET_FUNCTION: REM A=26
+  B$="*":GOSUB INIT_CORE_SET_FUNCTION: REM A=27
+  B$="/":GOSUB INIT_CORE_SET_FUNCTION: REM A=28
+  B$="time-ms":GOSUB INIT_CORE_SET_FUNCTION: REM A=29
+
+  B$="list":GOSUB INIT_CORE_SET_FUNCTION: REM A=30
+  B$="list?":GOSUB INIT_CORE_SET_FUNCTION: REM A=31
+  B$="vector":GOSUB INIT_CORE_SET_FUNCTION: REM A=32
+  B$="vector?":GOSUB INIT_CORE_SET_FUNCTION: REM A=33
+  B$="hash-map":GOSUB INIT_CORE_SET_FUNCTION: REM A=34
+  B$="map?":GOSUB INIT_CORE_SET_FUNCTION: REM A=35
+  B$="assoc":GOSUB INIT_CORE_SET_FUNCTION: REM A=36
+  B$="dissoc":GOSUB INIT_CORE_SET_FUNCTION: REM A=37
+  B$="get":GOSUB INIT_CORE_SET_FUNCTION: REM A=38
+  B$="contains?":GOSUB INIT_CORE_SET_FUNCTION: REM A=39
+  B$="keys":GOSUB INIT_CORE_SET_FUNCTION: REM A=40
+  B$="vals":GOSUB INIT_CORE_SET_FUNCTION: REM A=41
+
+  B$="sequential?":GOSUB INIT_CORE_SET_FUNCTION: REM A=42
+  B$="cons":GOSUB INIT_CORE_SET_FUNCTION: REM A=43
+  B$="concat":GOSUB INIT_CORE_SET_FUNCTION: REM A=44
+  B$="nth":GOSUB INIT_CORE_SET_FUNCTION: REM A=45
+  B$="first":GOSUB INIT_CORE_SET_FUNCTION: REM A=46
+  B$="rest":GOSUB INIT_CORE_SET_FUNCTION: REM A=47
+  B$="empty?":GOSUB INIT_CORE_SET_FUNCTION: REM A=48
+  B$="count":GOSUB INIT_CORE_SET_FUNCTION: REM A=49
+
+  B$="conj":GOSUB INIT_CORE_SET_FUNCTION: REM A=50
+  B$="seq":GOSUB INIT_CORE_SET_FUNCTION: REM A=51
+
+  B$="with-meta":GOSUB INIT_CORE_SET_FUNCTION: REM A=52
+  B$="meta":GOSUB INIT_CORE_SET_FUNCTION: REM A=53
+  B$="atom":GOSUB INIT_CORE_SET_FUNCTION: REM A=54
+  B$="atom?":GOSUB INIT_CORE_SET_FUNCTION: REM A=55
+  B$="deref":GOSUB INIT_CORE_SET_FUNCTION: REM A=56
+  B$="reset!":GOSUB INIT_CORE_SET_FUNCTION: REM A=57
+
+  B$="eval":GOSUB INIT_CORE_SET_FUNCTION: REM A=58
+  B$="read-file":GOSUB INIT_CORE_SET_FUNCTION: REM A=59
+  B$="pr-memory-summary":GOSUB INIT_CORE_SET_FUNCTION: REM A=60
+
+  REM these are in DO_TCO_FUNCTION
+  A=65
+  B$="apply":GOSUB INIT_CORE_SET_FUNCTION: REM A=65
+  B$="map":GOSUB INIT_CORE_SET_FUNCTION: REM A=66
+  B$="swap!":GOSUB INIT_CORE_SET_FUNCTION: REM A=67
+
+  RETURN