Basic: step2 basics. Vectors and hash-maps.
authorJoel Martin <github@martintribe.org>
Sun, 11 Sep 2016 03:13:27 +0000 (22:13 -0500)
committerJoel Martin <github@martintribe.org>
Sun, 11 Sep 2016 03:13:27 +0000 (22:13 -0500)
Adjust step2 tests to keep values within 2 byte int range.

basic/Makefile
basic/printer.in.bas
basic/qb2cbm.sh
basic/reader.in.bas
basic/step0_repl.in.bas
basic/step1_read_print.in.bas
basic/step2_eval.in.bas [new file with mode: 0755]
basic/types.in.bas
tests/step2_eval.mal

index 42ed31f..2cfdbf3 100644 (file)
@@ -3,5 +3,9 @@ step%.bas: step%.in.bas
        ./qb2cbm.sh $< > $@
 
 step0_repl.bas: readline.in.bas
-
 step1_read_print.bas: readline.in.bas types.in.bas reader.in.bas printer.in.bas
+step2_eval.bas: readline.in.bas types.in.bas reader.in.bas printer.in.bas
+
+tests/%.bas: tests/%.in.bas readline.in.bas types.in.bas reader.in.bas printer.in.bas
+       ./qb2cbm.sh $< > $@
+
dissimilarity index 68%
index af9a85f..3c4c412 100644 (file)
@@ -1,67 +1,81 @@
-REM PR_STR(A%) -> R$
-PR_STR:
-  T%=ZT%(A%)
-  REM PRINT "A%: " + STR$(A%) + ", T%: " + STR$(T%)
-  IF T%=0 THEN R$="nil": RETURN
-  IF T%=1 THEN R$="false": RETURN
-  IF T%=2 THEN R$="true": RETURN
-  IF T%=3 THEN PR_INTEGER
-  IF T%=5 THEN PR_STRING
-  IF T%=6 THEN PR_KEYWORD
-  IF T%=7 THEN PR_SYMBOL
-  IF T%=8 THEN PR_LIST
-  R$="#<unknown>"
-  RETURN
-
-  PR_INTEGER:
-    T%=ZV%(A%)
-    R$=STR$(T%)
-    IF T%<0 THEN RETURN
-    REM Remove initial space
-    R$=RIGHT$(R$, LEN(R$)-1)
-    RETURN
-  PR_STRING:
-    R$=CHR$(34) + ZS$(ZV%(A%)) + CHR$(34)
-    RETURN
-  PR_KEYWORD:
-    R$=":keyword"
-    RETURN
-  PR_SYMBOL:
-    R$=ZS$(ZV%(A%))
-    RETURN
-  PR_LIST:
-    IF PT%=-1 THEN RR$=""
-    RR$=RR$+"("
-    REM keep track of where we are in the list
-    PT%=PT%+1
-    PS%(PT%)= A%
-    PR_LIST_LOOP:
-      IF ZV%(A%) = 0 THEN PR_LIST_DONE
-      A%=A%+1
-      REM Push whether we are rendering a list on stack
-      PT%=PT%+1
-      IF ZT%(A%) = 8 THEN PS%(PT%) = 1
-      IF ZT%(A%) <> 8 THEN PS%(PT%) = 0
-      GOSUB PR_STR
-      REM check append then pop off stack
-      IF PS%(PT%) = 1 THEN RR$=RR$
-      IF PS%(PT%) = 0 THEN RR$=RR$+R$
-      PT%=PT%-1
-      REM Go to next list element
-      A%=ZV%(PS%(PT%))
-      PS%(PT%) = A%
-      IF ZV%(A%) <> 0 THEN RR$=RR$+" "
-      GOTO PR_LIST_LOOP
-    PR_LIST_DONE:
-      PT%=PT%-1
-      RR$=RR$+")"
-      IF PT%=-1 THEN R$=RR$
-      RETURN
-
-
-PR_MEMORY:
-  PRINT "Memory:"
-  FOR I=0 TO ZI%-1
-    PRINT " " + STR$(I) + ": type: " + STR$(ZT%(I)) + ", value: " + STR$(ZV%(I))
-    NEXT I
-  RETURN
+REM PR_STR(AZ%) -> R$
+PR_STR:
+  T%=Z%(AZ%,0)
+  REM PRINT "AZ%: " + STR$(AZ%) + ", T%: " + STR$(T%)
+  IF T%=15 THEN AZ%=Z%(AZ%,1): GOTO PR_STR
+  IF T%=0 THEN R$="nil": RETURN
+  IF (T%=1) AND (Z%(AZ%,1)=0) THEN R$="false": RETURN
+  IF (T%=1) AND (Z%(AZ%,1)=1) THEN R$="true": RETURN
+  IF T%=2 THEN PR_INTEGER
+  IF T%=4 THEN PR_STRING
+  IF T%=5 THEN PR_SYMBOL
+  IF T%=6 THEN PR_SEQ
+  IF T%=8 THEN PR_SEQ
+  IF T%=10 THEN PR_SEQ
+  IF T%=12 THEN PR_FUNCTION
+  R$="#<unknown>"
+  RETURN
+
+  PR_INTEGER:
+    T%=Z%(AZ%,1)
+    R$=STR$(T%)
+    IF T%<0 THEN RETURN
+    REM Remove initial space
+    R$=RIGHT$(R$, LEN(R$)-1)
+    RETURN
+  PR_STRING:
+    R$=CHR$(34) + ZS$(Z%(AZ%,1)) + CHR$(34)
+    RETURN
+  PR_SYMBOL:
+    R$=ZS$(Z%(AZ%,1))
+    RETURN
+  PR_SEQ:
+    IF PT%=-1 THEN RR$=""
+    IF T%=6 THEN RR$=RR$+"("
+    IF T%=8 THEN RR$=RR$+"["
+    IF T%=10 THEN RR$=RR$+"{"
+    REM push where we are in the sequence
+    PT%=PT%+1
+    PS%(PT%)= AZ%
+    PR_SEQ_LOOP:
+      IF Z%(AZ%,1) = 0 THEN PR_SEQ_DONE
+      AZ%=AZ%+1
+      REM Push type we are rendering on the stack
+      PT%=PT%+1
+      PS%(PT%) = Z%(AZ%,0)
+      GOSUB PR_STR
+      REM check type and pop off stack
+      T%=PS%(PT%)
+      IF (T% >= 6) AND (T% <= 11) THEN RR$=RR$
+      IF (T% < 6) OR (T% > 11) THEN RR$=RR$+R$
+      PT%=PT%-1
+      REM Go to next list element
+      AZ%=Z%(PS%(PT%),1)
+      PS%(PT%) = AZ%
+      IF Z%(AZ%,1) <> 0 THEN RR$=RR$+" "
+      GOTO PR_SEQ_LOOP
+    PR_SEQ_DONE:
+      T%=Z%(PS%(PT%),0)
+      PT%=PT%-1
+      IF T%=6 THEN RR$=RR$+")"
+      IF T%=8 THEN RR$=RR$+"]"
+      IF T%=10 THEN RR$=RR$+"}"
+      IF PT%=-1 THEN R$=RR$
+      RETURN
+  PR_FUNCTION:
+    T1%=Z%(AZ%,1)
+    R$="#<function" + STR$(T1%) + ">"
+    RETURN
+    
+
+
+PR_MEMORY:
+  PRINT "Value Memory (Z%):"
+  FOR I=0 TO ZI%-1
+    PRINT " " + STR$(I) + ": type: " + STR$(Z%(I,0)) + ", value: " + STR$(Z%(I,1))
+    NEXT I
+  PRINT "String Memory (ZS%):"
+  FOR I=0 TO ZJ%-1
+    PRINT " " + STR$(I) + ": '" + ZS$(I) + "'"
+    NEXT I
+  RETURN
index 9b3cd3f..7c5930c 100755 (executable)
@@ -3,6 +3,8 @@
 set -e
 
 DEBUG=${DEBUG:-}
+KEEP_REM=${KEEP_REM:-}
+KEEP_REM_LABELS=${KEEP_REM_LABELS:-}
 
 infile=$1
 
@@ -33,7 +35,11 @@ while [[ ${input} =~ REM\ \$INCLUDE:\ \'.*\' ]]; do
             fi
             [ "${DEBUG}" ] && echo >&2 "including: ${include}"
             included[${include}]="done"
-            full="${full}\nREM vvv BEGIN '${include}' vvv\n$(cat ${include})\nREM vvv END '${include}' vvv\n"
+            if [ "${KEEP_REM}" ]; then
+                full="${full}\nREM vvv BEGIN '${include}' vvv\n$(cat ${include})\nREM vvv END '${include}' vvv\n"
+            else
+                full="${full}\n$(cat ${include})\n"
+            fi
         else
             full="${full}${line}\n"
         fi
@@ -50,27 +56,43 @@ declare -A labels
 lnum=10
 while read -r line; do
     if [[ ${line} =~ ^\ *# ]]; then
-        [ "${DEBUG}" ] && echo >&2 "ignoring # style comment after $lnum"
+        [ "${DEBUG}" ] && echo >&2 "ignoring # style comment at $lnum"
+        continue
+    elif [[ -z "${KEEP_REM}" && ${line} =~ ^\ *REM ]]; then
+        [ "${DEBUG}" ] && echo >&2 "dropping REM comment: ${line}"
         continue
     elif [[ ${line} =~ ^\ *$ ]]; then
-            [ "${DEBUG}" ] && echo >&2 "found blank line after $lnum"
+            [ "${DEBUG}" ] && echo >&2 "found blank line at $lnum"
             data="${data}\n"
             continue
-    elif [[ ${line} =~ ^[A-Za-z_]*:$ ]]; then
+    elif [[ ${line} =~ ^[A-Za-z_][A-Za-z0-9_]*:$ ]]; then
         label=${line%:}
         [ "${DEBUG}" ] && echo >&2 "found label ${label} at $lnum"
         labels[${label}]=$lnum
-        data="${data}${lnum} REM ${label}:\n"
+        if [ -n "${KEEP_REM_LABELS}" ]; then
+            data="${data}${lnum} REM ${label}:\n"
+        else
+            continue
+        fi
     else
         data="${data}${lnum} ${line}\n"
     fi
     lnum=$(( lnum + 10 ))
 done < <(echo -e "${input}")
 
+if [[ -z "${KEEP_REM}" ]]; then
+    [ "${DEBUG}" ] && echo >&2 "Dropping line ending REMs"
+    data=$(echo -e "${data}" | sed "s/: REM [^\n]*$//")
+fi
+
 for label in "${!labels[@]}"; do
     [ "${DEBUG}" ] && echo >&2 "Updating label: ${label}"
     lnum=${labels[${label}]}
-    data=$(echo "${data}" | sed "s/\(THEN\|GOTO\|GOSUB\) ${label}\>/\1 ${lnum}: REM \1 ${label}/g")
+    if [ -n "${KEEP_REM_LABELS}" ]; then
+        data=$(echo "${data}" | sed "s/\(THEN\|GOTO\|GOSUB\) ${label}\>/\1 ${lnum}: REM ${label}/g")
+    else
+        data=$(echo "${data}" | sed "s/\(THEN\|GOTO\|GOSUB\) ${label}\>/\1 ${lnum}/g")
+    fi
 done
 
-echo -en "${data}"
+echo -e "${data}"
index b50aa9c..40d3d68 100644 (file)
@@ -49,56 +49,69 @@ READ_FORM:
   IF (T$="true") THEN R%=2: GOTO READ_FORM_DONE
   CH$=MID$(T$,1,1)
   REM PRINT "CH$: [" + CH$ + "](" + STR$(ASC(CH$)) + ")"
-  IF (CH$ >= "0") AND (CH$ <= "9") OR (CH$ = "-") THEN READ_NUMBER
+  IF (CH$ >= "0") AND (CH$ <= "9") THEN READ_NUMBER
+  IF (CH$ = "-") THEN READ_SYMBOL_MAYBE
+
   IF (CH$ = CHR$(34)) THEN READ_STRING
-  IF (CH$ = "(") THEN READ_LIST
-  IF (CH$ = ")") THEN READ_LIST_END
+  IF (CH$ = "(") THEN T%=6: GOTO READ_SEQ
+  IF (CH$ = ")") THEN T%=6: GOTO READ_SEQ_END
+  IF (CH$ = "[") THEN T%=8: GOTO READ_SEQ
+  IF (CH$ = "]") THEN T%=8: GOTO READ_SEQ_END
+  IF (CH$ = "{") THEN T%=10: GOTO READ_SEQ
+  IF (CH$ = "}") THEN T%=10: GOTO READ_SEQ_END
   GOTO READ_SYMBOL
 
   READ_NUMBER:
     REM PRINT "READ_NUMBER"
-    ZT%(ZI%) = 3
-    ZV%(ZI%) = VAL(T$)
+    Z%(ZI%,0) = 2
+    Z%(ZI%,1) = VAL(T$)
     R%=ZI%
     ZI%=ZI%+1
     GOTO READ_FORM_DONE
   READ_STRING:
     REM PRINT "READ_STRING"
-    ZT%(ZI%) = 5
-    ZV%(ZI%) = ZJ%
+    Z%(ZI%,0) = 4
+    Z%(ZI%,1) = ZJ%
     R%=ZI%
     ZI%=ZI%+1
     ZS$(ZJ%) = MID$(T$, 2, LEN(T$)-2)
     REM ZS$(ZJ%) = T$
     ZJ%=ZJ%+1
     GOTO READ_FORM_DONE
+  READ_SYMBOL_MAYBE:
+    CH$=MID$(T$,2,1)
+    IF (CH$ >= "0") AND (CH$ <= "9") THEN READ_NUMBER
   READ_SYMBOL:
     REM PRINT "READ_SYMBOL"
-    ZT%(ZI%) = 7
-    ZV%(ZI%) = ZJ%
+    Z%(ZI%,0) = 5
+    Z%(ZI%,1) = ZJ%
     R%=ZI%
     ZI%=ZI%+1
     ZS$(ZJ%) = T$
     ZJ%=ZJ%+1
     GOTO READ_FORM_DONE
 
-  READ_LIST:
-    REM PRINT "READ_LIST"
+  READ_SEQ:
+    REM PRINT "READ_SEQ"
     REM push start ptr on the stack
     PT%=PT%+1
     PS%(PT%) = ZI%
+    REM push current sequence type
+    PT%=PT%+1
+    PS%(PT%) = T%
     REM push current ptr on the stack
     PT%=PT%+1
     PS%(PT%) = ZI%
     GOTO READ_FORM_DONE
 
-  READ_LIST_END:
-    REM PRINT "READ_LIST_END"
+  READ_SEQ_END:
+    REM PRINT "READ_SEQ_END"
     IF PT%=-1 THEN ER%=1: ER$="unexpected ')'": RETURN
-    REM Set return value to current list
-    PT%=PT%-1: REM pop current ptr off the stack
-    R%=PS%(PT%): REM start ptr to list
+    REM Set return value to current sequence
+    PT%=PT%-2: REM pop current ptr and type off the stack
+    R%=PS%(PT%): REM ptr to start of sequence to return
     PT%=PT%-1: REM pop start ptr off the stack
+    IF (PS%(PT%+2)) <> T% THEN ER%=1: ER$="sequence mismatch": RETURN
     GOTO READ_FORM_DONE
 
 
@@ -109,10 +122,10 @@ READ_FORM:
     IF T$="" THEN ER%=1: ER$="unexpected EOF": RETURN
     REM add list end entry (next pointer is 0 for now)
     REM PRINT "READ_FORM_DONE next list entry"
-    ZT%(ZI%) = 8
-    ZV%(ZI%) = 0
+    Z%(ZI%,0) = PS%(PT%- 1)
+    Z%(ZI%,1) = 0
     REM update prior pointer if not first
-    IF PS%(PT%)<>ZI% THEN ZV%(PS%(PT%)) = ZI%
+    IF PS%(PT%)<>ZI% THEN Z%(PS%(PT%),1) = ZI%
     REM update previous pointer to outself
     PS%(PT%) = ZI%
     ZI%=ZI%+1: REM slot for list element
dissimilarity index 74%
index 706bfd2..7398f14 100755 (executable)
@@ -1,34 +1,40 @@
-GOTO MAIN
-
-REM $INCLUDE: 'readline.in.bas'
-
-REM /* READ(A$) -> R$ */
-MAL_READ:
-  R$=A$
-  RETURN
-
-REM /* EVAL(A$, E%) -> R$ */
-EVAL:
-  GOSUB MAL_READ: REM /* call READ */
-  RETURN
-
-REM /* PRINT(A$) -> R$ */
-MAL_PRINT:
-  GOSUB EVAL: REM /* call EVAL */
-  RETURN
-
-REM /* REP(A$) -> R$ */
-REP:
-  GOSUB MAL_PRINT: REM /* call PRINT */
-  PRINT R$
-  RETURN
-
-REM /* main program loop */
-MAIN:
-  A$="user> "
-  GOSUB READLINE: REM /* call input parser */
-  IF EOF=1 THEN END
-  A$=R$
-  GOSUB REP: REM /* call REP */
-  GOTO MAIN
-
+GOTO MAIN
+
+REM $INCLUDE: 'readline.in.bas'
+
+REM READ(A$) -> R$
+MAL_READ:
+  R$=A$
+  RETURN
+
+REM EVAL(A$, E%) -> R$
+EVAL:
+  R$=A$
+  RETURN
+
+REM PRINT(A$) -> R$
+MAL_PRINT:
+  R$=A$
+  RETURN
+
+REM REP(A$) -> R$
+REP:
+  GOSUB MAL_READ
+  A%=R%: GOSUB EVAL
+  A%=R%: GOSUB MAL_PRINT
+  RETURN
+
+REM MAIN program
+MAIN:
+  MAIN_LOOP:
+    A$="user> "
+    GOSUB READLINE: REM /* call input parser */
+    IF EOF=1 THEN GOTO MAIN_DONE
+    A$=R$: GOSUB REP: REM /* call REP */
+    PRINT R$
+    GOTO MAIN_LOOP
+
+  MAIN_DONE:
+    PRINT "Free: " + STR$(FRE(0))
+    END
+
index 76cd861..9ada93f 100755 (executable)
@@ -5,44 +5,42 @@ REM $INCLUDE: 'types.in.bas'
 REM $INCLUDE: 'reader.in.bas'
 REM $INCLUDE: 'printer.in.bas'
 
-REM /* READ(A$) -> R% */
+REM READ(A$) -> R%
 MAL_READ:
   GOSUB READ_STR
   RETURN
 
-REM /* EVAL(A%, E%) -> R% */
+REM EVAL(A%, E%) -> R%
 EVAL:
   R%=A%
   RETURN
 
-REM /* PRINT(A%) -> R$ */
+REM PRINT(A%) -> R$
 MAL_PRINT:
-  GOSUB PR_STR
+  AZ%=A%: GOSUB PR_STR
   RETURN
 
-REM /* REP(A$) -> R$ */
+REM REP(A$) -> R$
 REP:
   GOSUB MAL_READ
   IF ER% THEN RETURN
-  A%=R%
-  GOSUB EVAL
+  A%=R%: GOSUB EVAL
   IF ER% THEN RETURN
-  A%=R%
-  GOSUB MAL_PRINT
+  A%=R%: GOSUB MAL_PRINT
   IF ER% THEN RETURN
-  PRINT R$
   RETURN
 
-REM /* main program loop */
+REM MAIN program
 MAIN:
   GOSUB INIT_MEMORY
+
   MAIN_LOOP:
     A$="user> "
     GOSUB READLINE: REM /* call input parser */
-    IF EOF=1 THEN END
-    A$=R$
-    GOSUB REP: REM /* call REP */
+    IF EOF=1 THEN GOTO MAIN_DONE
+    A$=R$: GOSUB REP: REM /* call REP */
     IF ER% THEN GOTO ERROR
+    PRINT R$
     GOTO MAIN_LOOP
 
     ERROR:
@@ -51,3 +49,7 @@ MAIN:
       ER$=""
       GOTO MAIN_LOOP
 
+  MAIN_DONE:
+    PRINT "Free: " + STR$(FRE(0))
+    END
+
diff --git a/basic/step2_eval.in.bas b/basic/step2_eval.in.bas
new file mode 100755 (executable)
index 0000000..9a9e48b
--- /dev/null
@@ -0,0 +1,226 @@
+GOTO MAIN
+
+REM $INCLUDE: 'readline.in.bas'
+REM $INCLUDE: 'types.in.bas'
+REM $INCLUDE: 'reader.in.bas'
+REM $INCLUDE: 'printer.in.bas'
+
+REM READ(A$) -> R%
+MAL_READ:
+  GOSUB READ_STR
+  RETURN
+
+REM EVAL_AST(A%, E%) -> R%
+EVAL_AST:
+  ZL%=ZL%+2: ZZ%(ZL%-1)=E%: ZZ%(ZL%)=A%
+  IF ER%=1 THEN GOTO EVAL_AST_RETURN
+
+  REM AZ%=A%: GOSUB PR_STR
+  REM PRINT "EVAL_AST: " + R$ + "(" + STR$(R%) + ")"
+
+  T%=Z%(A%,0)
+  IF T%=5 THEN EVAL_AST_SYMBOL
+  IF T%=6 THEN EVAL_AST_LIST
+  R%=A%
+  GOTO EVAL_AST_RETURN
+
+  EVAL_AST_SYMBOL:
+    HM%=E%: K%=A%: GOSUB HASHMAP_GET
+    IF T3%=0 THEN ER%=1: ER$="'" + ZS$(Z%(A%,1)) + "' not found"
+    GOTO EVAL_AST_RETURN
+  
+  EVAL_AST_LIST:
+    REM push future return value (new list)
+    ZL%=ZL%+1
+    ZZ%(ZL%)=ZI%
+    REM push previous new list entry
+    ZL%=ZL%+1
+    ZZ%(ZL%)=ZI%
+
+    EVAL_AST_LIST_LOOP:
+      REM create new list entry
+      Z%(ZI%,0)=6
+      Z%(ZI%,1)=0
+      ZI%=ZI%+1
+
+      REM check if we are done evaluating the list
+      IF Z%(A%,1)=0 THEN GOTO EVAL_AST_LIST_LOOP_DONE
+
+      REM create value ptr placeholder
+      Z%(ZI%,0)=15
+      Z%(ZI%,1)=0
+      ZI%=ZI%+1
+
+      REM call EVAL for each entry
+      A%=A%+1: GOSUB EVAL
+      A%=A%-1
+
+      REM update previous list entry to point to current entry
+      Z%(ZZ%(ZL%),1)=ZI%
+      REM update previous value pointer to evaluated entry
+      Z%(ZZ%(ZL%)+1,1)=R%
+      REM update previous ptr to current entry
+      ZZ%(ZL%)=ZI%
+
+      REM process the next list entry
+      A%=Z%(A%,1)
+
+      GOTO EVAL_AST_LIST_LOOP
+    EVAL_AST_LIST_LOOP_DONE:
+      REM pop previous new list entry value
+      ZL%=ZL%-1
+      REM pop return value (new list)
+      R%=ZZ%(ZL%)
+      ZL%=ZL%-1
+      GOTO EVAL_AST_RETURN
+
+  EVAL_AST_RETURN:
+    E%=ZZ%(ZL%-1): A%=ZZ%(ZL%): ZL%=ZL%-2
+    RETURN
+
+REM EVAL(A%, E%)) -> R%
+EVAL:
+  ZL%=ZL%+2: ZZ%(ZL%-1)=E%: ZZ%(ZL%)=A%
+  IF ER%=1 THEN GOTO EVAL_RETURN
+
+  REM AZ%=A%: GOSUB PR_STR
+  REM PRINT "EVAL: " + R$ + "(" + STR$(R%) + ")"
+
+  GOSUB LIST_Q
+  IF R% THEN GOTO APPLY_LIST
+  REM ELSE
+    GOSUB EVAL_AST
+    GOTO EVAL_RETURN
+
+  APPLY_LIST:
+    GOSUB EMPTY_Q
+    IF R% THEN R%=A%: GOTO EVAL_RETURN
+
+    GOSUB EVAL_AST
+    IF ER%=1 THEN GOTO EVAL_RETURN
+    F%=R%+1
+    AR%=Z%(R%,1): REM REST
+    R%=F%: GOSUB DEREF
+    F%=R%
+    IF Z%(F%,0)<>12 THEN ER%=1: ER$="apply of non-function": GOTO EVAL_RETURN
+    GOSUB DO_FUNCTION
+
+    GOTO EVAL_RETURN
+
+  EVAL_RETURN:
+    E%=ZZ%(ZL%-1): A%=ZZ%(ZL%): ZL%=ZL%-2
+    RETURN
+
+REM DO_FUNCTION(F%, AR%)
+DO_FUNCTION:
+  AZ%=F%: GOSUB PR_STR
+  F$=R$
+  AZ%=AR%: GOSUB PR_STR
+  AR$=R$
+
+  REM Get the function number
+  FF%=Z%(F%,1)
+
+  REM Get argument values
+  R%=AR%+1: GOSUB DEREF
+  AA%=Z%(R%,1)
+  R%=Z%(AR%,1)+1: GOSUB DEREF
+  AB%=Z%(R%,1)
+
+  REM Allocate the return value
+  R%=ZI%
+  ZI%=ZI%+1
+
+  REM Switch on the function number
+  IF FF%=1 THEN DO_ADD
+  IF FF%=2 THEN DO_SUB
+  IF FF%=3 THEN DO_MULT
+  IF FF%=4 THEN DO_DIV
+  ER%=1: ER$="unknown function" + STR$(FF%): RETURN
+
+  DO_ADD:
+    Z%(R%,0)=2
+    Z%(R%,1)=AA%+AB%
+    GOTO DO_FUNCTION_DONE
+  DO_SUB:
+    Z%(R%,0)=2
+    Z%(R%,1)=AA%-AB%
+    GOTO DO_FUNCTION_DONE
+  DO_MULT:
+    Z%(R%,0)=2
+    Z%(R%,1)=AA%*AB%
+    GOTO DO_FUNCTION_DONE
+  DO_DIV:
+    Z%(R%,0)=2
+    Z%(R%,1)=AA%/AB%
+    GOTO DO_FUNCTION_DONE
+
+  DO_FUNCTION_DONE:
+    RETURN
+
+REM PRINT(A%) -> R$
+MAL_PRINT:
+  AZ%=A%: GOSUB PR_STR
+  RETURN
+
+REM REP(A$) -> R$
+REM Assume RE% has repl_env
+REP:
+  GOSUB MAL_READ
+  IF ER% THEN RETURN
+  A%=R%: E%=RE%: GOSUB EVAL
+  IF ER% THEN RETURN
+  A%=R%: GOSUB MAL_PRINT
+  IF ER% THEN RETURN
+  RETURN
+
+REM MAIN program
+MAIN:
+  GOSUB INIT_MEMORY
+
+  REM repl_env
+  GOSUB HASHMAP
+  RE%=R%
+
+  REM + function
+  A%=1: GOSUB NATIVE_FUNCTION
+  HM%=RE%: K$="+": V%=R%: GOSUB ASSOC1_S
+  RE%=R%
+
+  REM - function
+  A%=2: GOSUB NATIVE_FUNCTION
+  HM%=RE%: K$="-": V%=R%: GOSUB ASSOC1_S
+  RE%=R%
+
+  REM * function
+  A%=3: GOSUB NATIVE_FUNCTION
+  HM%=RE%: K$="*": V%=R%: GOSUB ASSOC1_S
+  RE%=R%
+
+  REM / function
+  A%=4: GOSUB NATIVE_FUNCTION
+  HM%=RE%: K$="/": V%=R%: GOSUB ASSOC1_S
+  RE%=R%
+
+  AZ%=RE%: GOSUB PR_STR
+  PRINT "env: " + R$ + "(" + STR$(RE%) + ")"
+
+  MAIN_LOOP:
+    A$="user> "
+    GOSUB READLINE: REM /* call input parser */
+    IF EOF=1 THEN GOTO MAIN_DONE
+    A$=R$: GOSUB REP: REM /* call REP */
+    IF ER% THEN GOTO ERROR
+    PRINT R$
+    GOTO MAIN_LOOP
+
+    ERROR:
+      PRINT "Error: " + ER$
+      ER%=0
+      ER$=""
+      GOTO MAIN_LOOP
+
+  MAIN_DONE:
+    PRINT "Free: " + STR$(FRE(0)): REM abc
+    END
+
dissimilarity index 83%
index 302f0bf..e7ee253 100644 (file)
-REM               TYPE% -> VALUE%
-REM nil           0     -> (unused)
-REM false         1     -> (unused)
-REM true          2     -> (unused)
-REM integer       3     -> int value
-REM float         4     -> ???
-REM string        5     -> ZS$ index
-REM keyword       6     -> ZS$ index
-REM symbol        7     -> ZS$ index
-REM list next     8     -> ZT% index / or 0
-REM                        followed by value unless empty
-REM vector next   9     -> ZT% index / or 0
-REM                        followed by value unless empty
-REM hashmap       12    -> ???
-REM mal function  13    -> ???
-REM atom          14    -> TYPE% index
-
-INIT_MEMORY:
-  REM global error state
-  ER%=0
-  ER$=""
-
-  REM boxes memory elements
-  SZ%=4096
-  DIM ZT%(SZ%): REM TYPE ARRAY
-  DIM ZV%(SZ%): REM VALUE ARRAY
-
-  REM Predefine nil, false, true
-  ZT%(0) = 0
-  ZT%(1) = 1
-  ZT%(2) = 2
-  ZI%=3
-
-  REM string memory
-  ZJ%=0
-  DIM ZS$(1024)
-
-  REM logic stack
-  PT%=-1: REM index of top of PS% stack
-  DIM PS%(128): REM stack of ZT% indexes
-
-  REM environment
-  REM DIM EKEYS$(1024)
-  REM DIM EVALS%(1024)
-  RETURN
+REM                  Z 0   ->  1
+REM nil                0   ->  (unused)
+REM boolean            1   ->  0: false, 1: true
+REM integer            2   ->  int value
+REM float              3   ->  ???
+REM string/kw          4   ->  ZS$ index
+REM symbol             5   ->  ZS$ index
+REM list next/val      6   ->  next Z% index / or 0
+REM                    followed by value (unless empty)
+REM vector next/val    8   ->  next Z% index / or 0
+REM                    followed by value (unless empty)
+REM hashmap next/val   10  ->  next Z% index / or 0
+REM                    followed by key or value (alternating)
+REM function           12  ->  function index
+REM mal function       13  ->  ???
+REM atom               14  ->  Z% index
+REM reference/ptr      15  ->  Z% index / or 0
+
+INIT_MEMORY:
+  T%=FRE(0)
+  
+  S1%=4096: REM Z% (boxed memory) size (X2)
+  S2%=512: REM ZS% (string memory) size
+  S3%=64: REM PS% (logic stack) size
+  S4%=256: REM ZE% (environments) size
+  S5%=512: REM ZZ% (call stack) size
+
+  REM global error state
+  ER%=0
+  ER$=""
+
+  REM boxed element memory
+  DIM Z%(S1%,1): REM TYPE ARRAY
+
+  REM Predefine nil, false, true
+  Z%(0,0) = 0
+  Z%(1,0) = 1
+  Z%(1,1) = 0
+  Z%(2,0) = 1
+  Z%(2,1) = 1
+  ZI%=3
+
+  REM string memory storage
+  ZJ%=0
+  DIM ZS$(S2%)
+
+  REM environments
+  ZK%=0
+  DIM ZE%(S4%): REM data hashmap Z% index
+  DIM ZO%(S4%): REM outer ZE% index (or -1)
+
+  REM call stack
+  ZL%=-1
+  DIM ZZ%(S5%): REM stack of Z% indexes
+
+  REM logic stack
+  PT%=-1: REM index of top of PS% stack
+  DIM PS%(S3%): REM stack of Z% indexes
+
+  REM PRINT "Lisp data memory: " + STR$(T%-FRE(0))
+  REM PRINT "Interpreter working memory: " + STR$(FRE(0))
+  RETURN
+
+REM DEREF(R%) -> R%
+DEREF:
+  IF Z%(R%,0)=15 THEN R%=Z%(R%,1): GOTO DEREF
+  RETURN
+
+
+REM LIST functions
+
+LIST_Q:
+  R%=0
+  IF Z%(A%,0)=6 THEN R%=1
+  RETURN
+
+EMPTY_Q:
+  R%=0
+  IF Z%(A%,1)=0 THEN R%=1
+  RETURN
+
+REM HASHMAP functions
+
+REM HASHMAP() -> R%
+HASHMAP:
+  Z%(ZI%,0) = 10
+  Z%(ZI%,1) = 0
+  R%=ZI%
+  ZI%=ZI%+1
+  RETURN
+
+REM ASSOC1(HM%, K%, V%) -> R%
+ASSOC1:
+  R%=ZI%
+  REM key ptr
+  Z%(ZI%,0) = 10
+  Z%(ZI%,1) = ZI%+2: REM value
+  ZI%=ZI%+1
+  Z%(ZI%,0) = 15
+  Z%(ZI%,1) = K%
+  ZI%=ZI%+1
+  REM value ptr
+  Z%(ZI%,0) = 10
+  Z%(ZI%,1) = HM%: REM hashmap to assoc onto
+  ZI%=ZI%+1
+  Z%(ZI%,0) = 15
+  Z%(ZI%,1) = V%
+  ZI%=ZI%+1
+  RETURN
+
+REM ASSOC1(HM%, K$, V%) -> R%
+ASSOC1_S:
+  REM add the key string, then call ASSOC1
+  K%=ZI%
+  ZS$(ZJ%) = K$
+  Z%(ZI%,0) = 4
+  Z%(ZI%,1) = ZJ%
+  ZI%=ZI%+1
+  ZJ%=ZJ%+1
+  GOSUB ASSOC1
+  RETURN
+
+REM HASHMAP_GET(HM%, K%) -> R%
+HASHMAP_GET:
+  H2%=HM%
+  T1$=ZS$(Z%(K%,1)): REM search key string
+  T3%=0: REM whether found or not (for HASHMAP_CONTAINS)
+  R%=0
+  HASHMAP_GET_LOOP:
+    REM no matching key found
+    IF Z%(H2%,1)=0 THEN R%=0: RETURN
+    REM follow value ptrs
+    T2%=H2%+1
+    HASHMAP_GET_DEREF:
+      IF Z%(T2%,0)=15 THEN T2%=Z%(T2%,1): GOTO HASHMAP_GET_DEREF
+    REM get key string
+    T2$=ZS$(Z%(T2%,1))
+    REM if they are equal, we found it
+    IF T1$=T2$ THEN T3%=1: R%=Z%(H2%,1)+1: RETURN
+    REM skip to next key
+    H2%=Z%(Z%(H2%,1),1)
+    GOTO HASHMAP_GET_LOOP
+
+REM HASHMAP_CONTAINS(HM%, K%) -> R%
+HASHMAP_CONTAINS:
+  GOSUB HASHMAP_GET
+  R%=1: REM false
+  IF T3%=1 THEN R%=2: REM true
+  RETURN
+
+REM NATIVE_FUNCTION(A%) -> R%
+NATIVE_FUNCTION:
+  Z%(ZI%,0) = 12
+  Z%(ZI%,1) = A%
+  R%=ZI%
+  ZI%=ZI%+1
+  RETURN
+
+MAL_FUNCTION:
+  RETURN
index a077d20..c92fa84 100644 (file)
 (/ (- (+ 5 (* 2 3)) 3) 4)
 ;=>2
 
-(/ (- (+ 515 (* 222 311)) 302) 27)
-;=>2565
+(/ (- (+ 515 (* 87 311)) 302) 27)
+;=>1010
 
 (* -3 6)
 ;=>-18
 
-(/ (- (+ 515 (* -222 311)) 296) 27)
-;=>-2549
+(/ (- (+ 515 (* -87 311)) 296) 27)
+;=>-994
 
 (abc 1 2 3)
 ; .*\'abc\' not found.*