Basic (C64 v2): step0
authorJoel Martin <github@martintribe.org>
Mon, 5 Sep 2016 01:23:56 +0000 (20:23 -0500)
committerJoel Martin <github@martintribe.org>
Mon, 5 Sep 2016 01:25:26 +0000 (20:25 -0500)
This works with cbmbasic from https://github.com/mist64/cbmbasic. The
cbmbasic interpreter needs to be on the PATH.

The actually sources are *.in.bas which are "compiled" to *.bas using
the qb2cbm.sh. qb2cbm.sh translates from a QBasic-ish format to a line
numbered source with include files inlined (REM $INCLUDE: 'file.bas').
One additional advantage is that the *.in.bas versions can also be
indented and qb2cbm.sh will remove the indenting in the translated
code.

Makefile
basic/Makefile [new file with mode: 0644]
basic/qb2cbm.sh [new file with mode: 0755]
basic/readline.in.bas [new file with mode: 0644]
basic/run [new file with mode: 0755]
basic/step0_repl.in.bas [new file with mode: 0755]

index f1727fb..a788427 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -77,7 +77,7 @@ DOCKERIZE =
 # Settings
 #
 
-IMPLS = ada awk bash c d chuck clojure coffee clisp cpp crystal cs erlang elisp \
+IMPLS = ada awk bash basic c d chuck clojure coffee clisp cpp crystal cs erlang elisp \
        elixir es6 factor forth fsharp go groovy guile haskell haxe \
        io java julia js kotlin logo lua make mal ocaml matlab miniMAL \
        nim objc objpascal perl perl6 php plpgsql plsql powershell ps \
@@ -146,6 +146,7 @@ STEP_TEST_FILES = $(strip $(wildcard \
 ada_STEP_TO_PROG =     ada/$($(1))
 awk_STEP_TO_PROG =     awk/$($(1)).awk
 bash_STEP_TO_PROG =    bash/$($(1)).sh
+basic_STEP_TO_PROG =   basic/$($(1)).bas
 c_STEP_TO_PROG =       c/$($(1))
 d_STEP_TO_PROG =       d/$($(1))
 chuck_STEP_TO_PROG =   chuck/$($(1)).ck
diff --git a/basic/Makefile b/basic/Makefile
new file mode 100644 (file)
index 0000000..1e50294
--- /dev/null
@@ -0,0 +1,3 @@
+
+step%.bas: step%.in.bas readline.in.bas
+       ./qb2cbm.sh $< > $@
diff --git a/basic/qb2cbm.sh b/basic/qb2cbm.sh
new file mode 100755 (executable)
index 0000000..4a3105e
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+set -e
+
+DEBUG=${DEBUG:-}
+
+infile=$1
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
+[ "${infile}" ] || die "Usage: <infile>"
+
+input=$(cat ${infile})
+
+[ "${DEBUG}" ] && echo >&2 "Processing includes"
+
+full="${input}"
+declare -A included
+
+while [[ ${input} =~ REM\ \$INCLUDE:\ \'.*\' ]]; do
+    full=""
+    while read -r line; do
+        if [[ ${line} =~ REM\ \$INCLUDE:\ \'.*\' ]]; then
+            include=${line#REM \$INCLUDE: \'}
+            include=${include%\'}
+            # Only include it once
+            if [ "${included[${include}]}" ];then
+                [ "${DEBUG}" ] && echo >&2 "already included: ${include}"
+                continue
+            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"
+        else
+            full="${full}${line}\n"
+        fi
+    done < <(echo -e "${input}")
+    input="${full}"
+done
+
+
+[ "${DEBUG}" ] && echo >&2 "Renumbering"
+
+data=""
+declare -A labels
+
+lnum=10
+while read -r line; do
+    if [[ ${line} =~ ^\ *$ ]]; then
+            [ "${DEBUG}" ] && echo >&2 "found blank line after $lnum"
+            data="${data}\n"
+            continue
+    elif [[ ${line} =~ ^[A-Za-z_]*:$ ]]; then
+        label=${line%:}
+        [ "${DEBUG}" ] && echo >&2 "found label ${label} at $lnum"
+        labels[${label}]=$lnum
+        data="${data}${lnum} REM ${label}:\n"
+    else
+        data="${data}${lnum} ${line}\n"
+    fi
+    lnum=$(( lnum + 10 ))
+done < <(echo -e "${input}")
+
+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")
+done
+
+echo -en "${data}"
diff --git a/basic/readline.in.bas b/basic/readline.in.bas
new file mode 100644 (file)
index 0000000..3d0999b
--- /dev/null
@@ -0,0 +1,30 @@
+EOF=0
+
+REM READLINE(A$) -> R$
+READLINE:
+  EOF=0
+  PROMPT$=A$
+  PRINT PROMPT$;
+  CH$="": LINE$="": CH=0
+  READCH:
+    GET CH$: IF CH$="" THEN READCH
+    CH=ASC(CH$)
+    IF (CH=4 OR CH=0) THEN EOF=1: GOTO RL_DONE: REM EOF
+    IF (CH=127) THEN GOSUB RL_BACKSPACE
+    IF (CH=127) THEN GOTO READCH
+    IF (CH<32 OR CH>127) AND CH<>13 THEN READCH
+    IF LEN(LINE$)<255 AND CH$<>CHR$(13) THEN LINE$=LINE$+CH$
+    IF LEN(LINE$)<255 AND CH$<>CHR$(13) THEN GOTO READCH
+  RL_DONE:
+    R$=LINE$
+    RETURN
+
+  REM Assumes LINE$ has input buffer
+  RL_BACKSPACE:
+  IF LEN(LINE$)=0 THEN RL_BACKSPACE_ONCE:
+    PRINT CHR$(157) + CHR$(157) + "  " + CHR$(157) + CHR$(157);
+    LINE$=LEFT$(LINE$, LEN(LINE$)-1)
+    RETURN
+  RL_BACKSPACE_ONCE:
+    PRINT CHR$(157) + " " + CHR$(157);
+    RETURN 
diff --git a/basic/run b/basic/run
new file mode 100755 (executable)
index 0000000..2fe259b
--- /dev/null
+++ b/basic/run
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec cbmbasic $(dirname $0)/${STEP:-stepA_mal}.bas "${@}"
diff --git a/basic/step0_repl.in.bas b/basic/step0_repl.in.bas
new file mode 100755 (executable)
index 0000000..706bfd2
--- /dev/null
@@ -0,0 +1,34 @@
+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
+