' 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
' A -
' B -
' C -
' D -
' E -
' F -
' G -
' H -
' I -
' L -
' M -
' B -
' O -
' P -
' Q -
' q -
' R -
' T -
' U -
' V -
' W -
' X -
' Y -


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 "$": aom = aom XOR 1

    CASE "(": lem = lem XOR 1

    CASE "h": aom2 = aom2 XOR 1

    CASE "*": ad = ad XOR 1

    CASE "&": mul = mul XOR 1

    CASE "v": mul2 = mul2 XOR 1

    CASE "p": ad2 = ad2 XOR 1

    CASE ";": PRINT STR$(acc); " "; CHR$(acc)

    CASE "+"
        IF ad = 0 THEN
            acc = acc + plus
        ELSEIF ad = 1 THEN
            acc = acc - plus
        END IF

    CASE "x"
        IF mul = 0 THEN
            acc = acc * plus
        ELSEIF mul = 1 THEN
            acc = acc / plus
        END IF

    CASE "a"
        IF ad2 = 0 THEN
            acc2 = acc2 + plus
        ELSEIF ad2 = 1 THEN
            acc2 = acc2 - plus
            GOTO advance
        END IF

    CASE "d"
        IF mul2 = 0 THEN
            acc2 = acc2 * plus2
        ELSEIF mul2 = 1 THEN
            acc2 = acc2 / plus2
        END IF

    CASE ">"
        IF aom = 0 THEN PRINT LTRIM$(STR$(acc));
        IF aom = 1 THEN
            IF acc = 0 OR acc > 255 THEN
                PRINT LTRIM$(STR$(acc));
            ELSE
                PRINT CHR$(acc);
            END IF
        END IF

    CASE "u"

        IF aom2 = 0 THEN PRINT LTRIM$(STR$(acc2));
        IF aom2 = 1 THEN PRINT LTRIM$(CHR$(acc2));
        IF acc2 = 0 OR acc2 > 255 THEN
            PRINT LTRIM$(STR$(acc2));
        ELSE
            PRINT CHR$(acc2);
        END IF

    CASE "t"
        IF LEN(STR$(acc)) <> 1 THEN
            acc$ = STR$(acc): accl = LEN(acc$)
            FOR i = accl TO 1 STEP -1
                tmp$ = tmp$ + MID$(acc$, i, 1)
            NEXT i
            acc = VAL(tmp$)
        END IF

    CASE "<"
        IF LEN(STR$(acc)) <> 1 THEN
            acc$ = STR$(acc): accl = LEN(acc$)
            FOR i = accl TO 1 STEP -1
                tmp$ = tmp$ + MID$(acc$, i, 1)
            NEXT i
            acc = VAL(tmp$)
        END IF




    CASE "e"
        IF plus = 1 THEN
            plus = 5
        ELSEIF plus = 5 THEN
            plus = 10
        ELSEIF plus = 10 THEN
            plus = 1
        END IF

    CASE "w"
        IF plus2 = 1 THEN
            plus2 = 5: 'GOTO advance
        ELSEIF plus2 = 5 THEN
            plus2 = 10: 'GOTO advance
        ELSEIF plus2 = 10 THEN
            plus2 = 1: 'GOTO advance
        END IF

    CASE "f": fc = fc + 1

    CASE "g": fc = fc - 1

    CASE "b": bc = bc + 1

    CASE "c": bc = bc - 1

    CASE "|": CLS

    CASE "k": fc = 0

    CASE "m": bc = 0

    CASE "l": ln = 1

    CASE "r": acc = 0

    CASE "z": acc2 = 0

    CASE "o"
        IF fc > 0 AND bc > 0 THEN COLOR fc, bc
        IF fc = 0 AND bc <> 0 THEN COLOR 7, bc

    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

    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

    CASE "n": PRINT " " ' print a newline

    CASE "~"
        getnum3:
        RANDOMIZE TIMER

        ntg = INT(RND * 9)
        IF ntg < 0 THEN GOTO getnum3
        IF ntg > 9 THEN GOTO getnum3
        acc = ntg

    CASE "^": PRINT " ";

    CASE "!": cm = cm XOR 1

    CASE "%": fm = fm XOR 1

    CASE ""

    CASE "0": c0 = c0 XOR 1

    CASE "1": c1 = c1 XOR 1

    CASE "2": c2 = c2 XOR 1

    CASE "3": c3 = c3 XOR 1

    CASE "4": c4 = c4 XOR 1

    CASE "5": c5 = c5 XOR 1

    CASE "6": c6 = c6 XOR 1

    CASE "7": c7 = c7 XOR 1

    CASE "8": c8 = c8 XOR 1

    CASE "9": c9 = c9 XOR 1

    CASE ")": cat = cat XOR 1

        ' 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"
        DO
            z$ = INKEY$
            _LIMIT 30 'play nice with your CPU and other apps while waiting on the user to press their key
        LOOP UNTIL z$ >= "0" AND z$ <= "9"
        IF z$ = "0" THEN filename$ = choice0$
        IF z$ = "1" THEN filename$ = choice1$
        IF z$ = "2" THEN filename$ = choice2$
        IF z$ = "3" THEN filename$ = choice3$
        IF z$ = "4" THEN filename$ = choice4$
        IF z$ = "5" THEN filename$ = choice5$
        IF z$ = "6" THEN filename$ = choice6$
        IF z$ = "7" THEN filename$ = choice7$
        IF z$ = "8" THEN filename$ = choice8$
        IF z$ = "9" THEN filename$ = choice9$
        GOTO load

    CASE "H": PRINT "hello world";


        ' go up a line of code
    CASE "j"
        IF ln = 1 THEN
            GOTO advance2
        END IF

        IF ptmt = 0 AND ltmt = 0 THEN
            ptmt = p
            ltmt = ln
        END IF


        ltmt = ltmt - 1

        ' go down a line of code

    CASE "J"

        IF ln = tlns THEN
            GOTO advance2
        END IF

        IF ln = 50000 THEN
            GOTO advance2
        END IF

        IF ptmt = 0 AND ltmt = 0 THEN
            ptmt = p
            ltmt = ln
        END IF

        ltmt = ltmt + 1

        ' go left one character within the current line
    CASE "s"
        IF p = 1 THEN
            GOTO advance
        END IF

        IF ptmt = 0 AND ltmt = 0 THEN
            ptmt = p
            ltmt = ln
        END IF

        ptmt = ptmt - 1

        ' go right one character within the current line

    CASE "S"

        IF p = LEN(prog$(ln)) THEN
            GOTO advance
        END IF

        IF ptmt = 0 AND ltmt = 0 THEN
            ptmt = p
            ltmt = ln
        END IF

        ptmt = ptmt + 1


    CASE "/"
        p = ptmt
        ln = ltmt
        GOTO parse


    CASE "\": END

    CASE ELSE ' This routine ignores everytthing else that is not a command and just moves on.

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

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
