' ABCD interpretor
' Coded by Larry R. Lowe in QB64 for windows 10

' Original ABC Esoteric Programming language created by orange, a user from the esolang wiki at https://esolangs.org/wiki/ABC
' This interpretor is the original ABC programming language with different 1 single character commands, and a few extra ones thrown in
' This version has among other things a second independant accumulator, and choice blocks.

' ! - Toggle remark mode on or off. When on everything between the first ! and the second ! is a comment
' # - runs the file specified between the % signs
' $ - Toggle ASCII output mode.  When on, the c instruction prints the first accumulator as an ASCII character, if it is >0 and <256.
' % - toggle the next filename to run block on or off
' & - Toggle the direction of the multiplication or division of the first accumulator.
' * - Toggle direction of first accumulator. When on, a + command decreases the first accumulatorm and when off a + command increases the first accumulator
' ; - Debug.  Prints out first accumulator as a number and ascii character.
' ? - Set first accumulator to a random number between 0 and first accumulator
' ^ - prints a space
' | - clear the screen
' + - Increase or decrease the value of the first accumulator
' < - Invert first accumulator
' > - Output the first accumulator
' 0 - Begin/ end choice0 block containing a file name to begin interpreting if 0 is pressed
' 1 - Begin/ end choice1 block containing a file name to begin interpreting if 1 is pressed
' 2 - Begin/ end choice2 block containing a file name to begin interpreting if 2 is pressed
' 3 - Begin/ end choice3 block containing a file name to begin interpreting if 3 is pressed
' 4 - Begin/ end choice4 block containing a file name to begin interpreting if 4 is pressed
' 5 - Begin/ end choice5 block containing a file name to begin interpreting if 5 is pressed
' 6 - Begin/ end choice6 block containing a file name to begin interpreting if 6 is pressed
' 7 - Begin/ end choice7 block containing a file name to begin interpreting if 7 is pressed
' 8 - Begin/ end choice8 block containing a file name to begin interpreting if 8 is pressed
' 9 - Begin/ end choice9 block containing a file name to begin interpreting if 9 is pressed
' a - Increase or decrease the value of the second accumulator
' b - Increrment the background color number by 1
' c - decrerment the background color number by 1
' d - multiply the second accumulator by 1, 5 or 10
' e - toggle the amount to increase or decrease the first accumulator by 1, 5 orf 10
' f - Increrment the foreground color number by 1
' g - decrerment the foreground color number by 1
' h - Toggle ASCII output mode.  When on, the c instruction prints the second accumulator as an ASCII character, if it is >0 and <256.
' i - Set second accumulator to a random number between 0 and second accumulator
' k - resets the foreground color to 0
' l - Loop back to the beginning of the program.  first accumulator and ASCII mode does not reset.
' m - resets the background color to 0
' n - prints a newline
' o - changes the foreground and background to the colors specified by the k, m, f, g, b and c commands
' p - Toggle direction of second accumulator. When on, an a command decreases the second accumulatorm and when off an a command increases the second accumulator
' r - Set first accumulator to 0
' t - Output the second accumulator
' u - Invert second accumulator
' v - Toggle the direction of the multiplication or division of the second accumulator. When off the x command multiplies the first accumulator by 1, 5 or 10, when on it divides it by 1, 5 or 10
' w - toggle the amount to increase or decrease the second accumulator by 1, 5 orf 10
' x - multiply the first accumulator by 1, 5 or 10
' z - Set second accumulator to 0
' H - Print Hello World
' ) - starts a cat block for use in writing a cat pgogram
' K - waits until ther user presses 0 througgh 9. It works with the above listed choice blocks. At least 1 choice block must be in the source code to use the K command.
' j - move the source code pointer up 1 line
' J - move the source code pointer down 1 line
' s - move the source code pointer left 1 character
' S - move the source code pointer right 1 character


begin:


' Here we mak it full screen. If it looks like it is going to be unstable, or if it screws up, just remove these 2 lines
'_FULLSCREEN


DIM prog$(50000), a$(50000)

row = 1: col = 1
fc = 7 ' foreground color variable
bc = 0 ' bsackground color variable
x = 0 ' index for an array to load in the prgoram 1 line at a time
ln = 1 ' starting line of code to begin interpreting
acc = 0 ' first accumulator variable
acc2 = 0 ' second accumulator variable
aom = 0 ' variable to hold first accumulator output mode
aom2 = 0 ' variable to hold second accumulator output mode
p = 1 ' variable to hold the number representing which character in the line we are interpreting
plus = 1 ' variable to hold the amount added to or subtracted from the first accumulator
plus2 = 1 ' variable to hold the amount added to or subtracted from the second accumulator

ad = 0 ' sets the first accumulator direction mode 0 =add 1 = subtract
ad2 = 0 ' sets the second accumulator direction mode 0 =add 1 = subtract
mul = 0
mul2 = 0
fm = 0
lem = 0
lem2 = 0
c0 = 0
c1 = 0
c2 = 0
c3 = 0
c4 = 0
c5 = 0
c6 = 0
c7 = 0
c8 = 0
c9 = 0
cat = 0

ptjt = 0
ltjt = 0

cm = 0 ' variable to hold the comment mode 0 = not the beginning of a comment 1 = beginning of comment
'The "!" toggles this cm variable

COLOR fc, bc: CLS

' This routine is where we get the command line to determine if the user gave the interpretor a file name to work with.

filename$ = COMMAND$

filename$ = "f:\abcd\hello world-6.abcd"

load:

' here we load a systems file coded in ABCD itself, to tell the user that a file name is required on the command line.
' it is for that reason, you can't just click on the ABCD icon and expect to get results.

IF filename$ = "" THEN LOCATE 24, 1: PRINT "Usage abcd filename.abcd": END

' We will open the file in sequential access mode and read it in one line at a time

OPEN filename$ FOR INPUT AS #1
WHILE NOT EOF(1)
    x = x + 1
    LINE INPUT #1, prog$(x)

WEND
CLOSE #1
tlns = x
x = 1

' now that we have the source code read into the computer's memory,
' we start at line 1 and we go from the beginning of it to the end of it
' one character at a time because all commands are single characters in length.
' We now parse each character so it does not matter if our source code is all
' on one line, or if it is broken up into many lines.

parse:

m$ = MID$(prog$(ln), p, 1)


' Here we do what is necessary if we find a block.
' The !, @, 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9 characters
' are block commands. A block must contain one of these
' commands followed by the required information, and then
' end with the same command. A comment block starts and
' ends with the ! character. Everything between the two
' exclamation points is ignored by the interpretor as it
' is a comment.

IF cm = 1 AND m$ <> "!" THEN GOTO advance
IF cm = 1 AND m$ = "!" THEN cm = 0: GOTO advance

IF fm = 0 AND m$ = "%" THEN fm = 1: GOTO advance
IF fm = 1 AND m$ <> "%" THEN nfn$ = nfn$ + m$: GOTO advance
IF fm = 1 AND m$ = "%" THEN fm = 0: GOTO advance


IF c0 = 0 AND m$ = "0" THEN c1 = 1: GOTO advance
IF c0 = 1 AND m$ <> "0" THEN choice0$ = choice0$ + m$: GOTO advance
IF c0 = 1 AND m$ = "0" THEN c0 = 0: GOTO advance

IF c1 = 0 AND m$ = "1" THEN c1 = 1: GOTO advance
IF c1 = 1 AND m$ <> "1" THEN choice1$ = choice1$ + m$: GOTO advance
IF c1 = 1 AND m$ = "1" THEN c0 = 0: GOTO advance

IF c2 = 0 AND m$ = "2" THEN c2 = 1: GOTO advance
IF c2 = 1 AND m$ <> "2" THEN choice2$ = choice2$ + m$: GOTO advance
IF c2 = 1 AND m$ = "2" THEN c2 = 0: GOTO advance

IF c3 = 0 AND m$ = "3" THEN c3 = 1: GOTO advance
IF c3 = 1 AND m$ <> "3" THEN choice3$ = choice3$ + m$: GOTO advance
IF c3 = 1 AND m$ = "3" THEN c3 = 0: GOTO advance

IF c4 = 0 AND m$ = "4" THEN c4 = 1: GOTO advance
IF c4 = 1 AND m$ <> "4" THEN choice4$ = choice4$ + m$: GOTO advance
IF c4 = 1 AND m$ = "4" THEN c4 = 0: GOTO advance

IF c5 = 0 AND m$ = "5" THEN c5 = 1: GOTO advance
IF c5 = 1 AND m$ <> "5" THEN choice5$ = choice5$ + m$: GOTO advance
IF c5 = 1 AND m$ = "5" THEN c5 = 0: GOTO advance

IF c6 = 0 AND m$ = "6" THEN c6 = 1: GOTO advance
IF c6 = 1 AND m$ <> "6" THEN choice6$ = choice6$ + m$: GOTO advance
IF c6 = 1 AND m$ = "6" THEN c6 = 0: GOTO advance

IF c7 = 0 AND m$ = "7" THEN c7 = 1: GOTO advance
IF c7 = 1 AND m$ <> "7" THEN choice7$ = choice7$ + m$: GOTO advance
IF c7 = 1 AND m$ = "7" THEN c7 = 0: GOTO advance

IF c8 = 0 AND m$ = "8" THEN c8 = 1: GOTO advance
IF c8 = 1 AND m$ <> "8" THEN choice8$ = choice8$ + m$: GOTO advance
IF c8 = 1 AND m$ = "8" THEN c8 = 0: GOTO advance

IF c9 = 0 AND m$ = "9" THEN c9 = 1: GOTO advance
IF c9 = 1 AND m$ <> "9" THEN choice9$ = choice9$ + m$: GOTO advance
IF c9 = 1 AND m$ = "9" THEN c9 = 0: GOTO advance

IF cat = 0 AND m$ = ")" THEN cat = 1: cat$ = cat$ + m$: GOTO advance
IF cat = 1 AND m$ <> ")" THEN cat$ = cat$ + m$: GOTO advance
IF cat = 1 AND m$ = ")" THEN c9 = 0: cat$ = cat$ + m$: PRINT cat$;: GOTO advance



SELECT CASE m$


    CASE "$"
        IF aom = 0 THEN aom = 1: GOTO advance
        IF aom = 1 THEN aom = 0: GOTO advance

    CASE "("
        IF lem = 0 THEN lem = 1: GOTO advance
        IF lem = 1 THEN lem = 0: GOTO advance


    CASE "h"
        IF aom2 = 0 THEN aom2 = 1: GOTO advance
        IF aom2 = 1 THEN aom2 = 0: GOTO advance


    CASE "*"
        IF ad = 0 THEN ad = 1: GOTO advance
        IF ad = 1 THEN ad = 0: GOTO advance

    CASE "&"
        IF mul = 0 THEN mul = 1: GOTO advance
        IF mul = 1 THEN mul = 0: GOTO advance

    CASE "v"
        IF mul2 = 0 THEN mul2 = 1: GOTO advance
        IF mul2 = 1 THEN mul2 = 0: GOTO advance


    CASE "p"
        IF ad2 = 0 THEN ad2 = 1: GOTO advance
        IF ad2 = 1 THEN ad2 = 0: GOTO advance


    CASE ";"
        PRINT STR$(acc); " "; CHR$(acc)
        GOTO advance

    CASE "+"
        IF ad = 0 THEN
            acc = acc + plus
            GOTO advance
        END IF
        IF ad = 1 THEN
            acc = acc - plus
            GOTO advance
        END IF

    CASE "x"
        IF mul = 0 THEN
            acc = acc * plus
            GOTO advance
        END IF
        IF mul = 1 THEN
            acc = acc / plus
            GOTO advance
        END IF


    CASE "a"
        IF ad2 = 0 THEN
            acc2 = acc2 + plus
            GOTO advance
        END IF
        IF ad2 = 1 THEN
            acc2 = acc2 - plus
            GOTO advance
        END IF

    CASE "d"
        IF mul2 = 0 THEN
            acc2 = acc2 * plus2
            GOTO advance
        END IF
        IF mul2 = 1 THEN
            acc2 = acc2 / plus2
            GOTO advance
        END IF



    CASE ">"

        IF aom = 0 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF

        IF aom = 1 AND acc <> 0 AND acc < 256 THEN
            PRINT CHR$(acc);
            GOTO advance
        END IF

        IF aom = 1 AND acc = 0 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF

        IF aom = 1 AND acc > 255 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF


    CASE "t"
        IF LEN(STR$(acc)) = 1 THEN GOTO advance
        acc$ = STR$(acc): accl = LEN(acc$)
        FOR i = accl TO 1 STEP -1
            tmp$ = tmp$ + MID$(acc$, i, 1)
        NEXT i
        acc = VAL(tmp$)
        GOTO advance

    CASE "u"

        IF aom = 0 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF

        IF aom = 1 AND acc <> 0 AND acc < 256 THEN
            PRINT CHR$(acc);
            GOTO advance
        END IF

        IF aom = 1 AND acc = 0 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF

        IF aom = 1 AND acc > 255 THEN
            PRINT LTRIM$(STR$(acc));
            GOTO advance
        END IF


    CASE "<"
        IF LEN(STR$(acc)) = 1 THEN GOTO advance
        acc$ = STR$(acc): accl = LEN(acc$)
        FOR i = accl TO 1 STEP -1
            tmp$ = tmp$ + MID$(acc$, i, 1)
        NEXT i
        acc = VAL(tmp$)
        GOTO advance


    CASE "e"

        IF plus = 1 THEN plus = 5: GOTO advance
        IF plus = 5 THEN plus = 10: GOTO advance
        IF plus = 10 THEN plus = 1: GOTO advance


    CASE "w"

        IF plus2 = 1 THEN plus2 = 5: GOTO advance
        IF plus2 = 5 THEN plus2 = 10: GOTO advance
        IF plus2 = 10 THEN plus2 = 1: GOTO advance

    CASE "f"
        fc = fc + 1
        GOTO advance

    CASE "g"
        fc = fc - 1
        GOTO advance

    CASE "b"
        bc = bc + 1
        GOTO advance

    CASE "c"
        bc = bc - 1
        GOTO advance

    CASE "|"
        CLS
        GOTO advance

    CASE "k"
        fc = 0
        GOTO advance

    CASE "m"
        bc = 0
        GOTO advance

    CASE "l"
        ln = 1
        GOTO advance

    CASE "r"
        acc = 0
        GOTO advance:

    CASE "z"
        acc2 = 0
        GOTO advance:

    CASE "o"

        IF fc > 0 AND bc > 0 THEN
            COLOR fc, bc
            GOTO advance:
        END IF

        IF fc = 0 AND bc <> 0 THEN
            COLOR 7, bc
            GOTO advance:
        END IF

    CASE "?"
        getnum:
        RANDOMIZE TIMER

        ntg = INT(RND * acc)
        IF ntg > acc THEN GOTO getnum

        IF lem = 1 AND ntg < 10 THEN GOTO getnum
        acc = ntg
        GOTO advance

    CASE "i"
        getnum2:
        RANDOMIZE TIMER

        ntg2 = INT(RND * acc2)
        IF ntg2 > acc2 THEN GOTO getnum2
        IF lem2 = 1 AND ntg2 < 10 THEN GOTO getnum2
        acc2 = ntg2
        GOTO advance


    CASE "n"
        PRINT " "

    CASE "~"
        getnum3:
        RANDOMIZE TIMER

        ntg = INT(RND * 9)
        IF ntg < 0 THEN GOTO getnum3
        IF ntg > 9 THEN GOTO getnum3
        acc = ntg
        GOTO advance



    CASE "^"
        PRINT " ";

    CASE "!"

        IF cm = 0 THEN
            cm = 1
            GOTO advance
        END IF

        IF cm = 1 THEN
            cm = 0
            GOTO advance
        END IF

    CASE "%"

        IF fm = 0 THEN
            fm = 1
            GOTO advance
        END IF

        IF fm = 1 THEN
            fm = 0
            GOTO advance
        END IF

    CASE ""
        GOTO advance

    CASE "0"

        IF c0 = 0 THEN
            c0 = 1
            GOTO advance
        END IF

        IF c0 = 1 THEN
            c0 = 0
            GOTO advance
        END IF

    CASE "1"

        IF c1 = 0 THEN
            c1 = 1
            GOTO advance
        END IF

        IF c1 = 1 THEN
            c1 = 0
            GOTO advance
        END IF

    CASE "2"

        IF c2 = 0 THEN
            c2 = 1
            GOTO advance
        END IF

        IF c2 = 1 THEN
            c2 = 0
            GOTO advance
        END IF

    CASE "3"

        IF c3 = 0 THEN
            c3 = 1
            GOTO advance
        END IF

        IF c3 = 1 THEN
            c3 = 0
            GOTO advance
        END IF

    CASE "4"

        IF c4 = 0 THEN
            c4 = 1
            GOTO advance
        END IF

        IF c4 = 1 THEN
            c4 = 0
            GOTO advance
        END IF

    CASE "5"

        IF c5 = 0 THEN
            c5 = 1
            GOTO advance
        END IF

        IF c5 = 1 THEN
            c5 = 0
            GOTO advance
        END IF

    CASE "6"

        IF c6 = 0 THEN
            c6 = 1
            GOTO advance
        END IF

        IF c6 = 1 THEN
            c6 = 0
            GOTO advance
        END IF

    CASE "7"

        IF c7 = 0 THEN
            c7 = 1
            GOTO advance
        END IF

        IF c7 = 1 THEN
            c7 = 0
            GOTO advance
        END IF

    CASE "8"

        IF c8 = 0 THEN
            c8 = 1
            GOTO advance
        END IF

        IF c8 = 1 THEN
            c8 = 0
            GOTO advance
        END IF

    CASE "9"

        IF c9 = 0 THEN
            c9 = 1
            GOTO advance
        END IF

        IF c9 = 1 THEN
            c9 = 0
            GOTO advance
        END IF

    CASE ")"

        IF cat = 0 THEN
            cat = 1
            GOTO advance
        END IF

        IF cat = 1 THEN
            cat = 0
            GOTO advance
        END IF




        ' This is the routine where we wait for the user to press only the keys 0-9, nad thehn we load a new source code based on what they chose.
        ' There must be at least one choice block in your code, otherwise the interpretor does not have any source code file to interpret at that
        ' point and will crash.

    CASE "K"
        getkey:
        z$ = INKEY$
        IF z$ = "" THEN GOTO getkey
        IF z$ = "0" THEN filename$ = choice0$: GOTO load
        IF z$ = "1" THEN filename$ = choice1$: GOTO load
        IF z$ = "2" THEN filename$ = choice2$: GOTO load
        IF z$ = "3" THEN filename$ = choice3$: GOTO load
        IF z$ = "4" THEN filename$ = choice4$: GOTO load
        IF z$ = "5" THEN filename$ = choice5$: GOTO load
        IF z$ = "6" THEN filename$ = choice6$: GOTO load
        IF z$ = "7" THEN filename$ = choice7$: GOTO load
        IF z$ = "8" THEN filename$ = choice8$: GOTO load
        IF z$ = "9" THEN filename$ = choice9$: GOTO load
        GOTO getkey

    CASE "H"
        PRINT "hello world";
        GOTO advance


    CASE "B"
        ltjt = 0
        GOTO advance



    CASE "j"
        IF ln = 1 THEN
            GOTO advance
        END IF
        ln = ln - 1
        GOTO parse

    CASE "J"
        IF ln = tlns THEN
            GOTO advance
        END IF
        IF ln = 50000 THEN
            GOTO advance
        END IF

        ln = ln + 1
        GOTO parse

    CASE "s"
        IF p = 1 THEN
            GOTO advance
        END IF
        p = p - 1
        GOTO parse

    CASE "S"



        IF p = LEN(prog$(ln)) THEN
            GOTO advance
        END IF


        GOTO advance



    CASE "\"
        END







        ' This routine ignores everytthing else that is not a command and just moves on.
    CASE ELSE
        GOTO advance



END SELECT

' The routines advance is where we move to the next character in the string that is the current line of code
' When we reach the end of the line, the advance2 routine moves to the next line and reset the position to the
' first character and start from there.

advance:

p = p + 1
IF p >= LEN(prog$(ln)) + 1 THEN GOTO advance2

GOTO parse

advance2:
ln = ln + 1: p = 1
IF ln = tlns + 1 AND nfn$ = "" THEN
    END
END IF

IF ln = 50001 AND nfn$ = "" THEN
    END
END IF



IF ln = tlns + 1 AND nfn$ <> "" THEN


    filename$ = nfn$: nfn$ = "": x = 0: p = 1: ln = 1: GOTO load
END IF

IF ln = 50001 AND nfn$ <> "" THEN


    filename$ = nfn$: nfn$ = "": x = 0: p = 1: ln = 1: GOTO load
END IF


GOTO parse
