RCSNUSP/COBOL: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Added works with tag.)
m (Fixed syntax highlighting.)
 
(6 intermediate revisions by one other user not shown)
Line 1: Line 1:
This an SNUSP interpreter written in COBOL. It supports Modular SNUSP but not yet Bloated SNUSP.
This an SNUSP interpreter written in COBOL. It supports Modular and Bloated SNUSP.


The file path to the code is passed as a parameter to the program. The file is assumed to be a text file with lines having a maximum length of 100 characters. It is also assumed the file will not be more than 1024 lines long.
The file path to the code is passed as a parameter to the program. The file is assumed to be a text file with lines having a maximum length of 100 characters. It is also assumed the file will not be more than 1024 lines long.


The memory is a 2048 byte array and the stack has a size of 512.
The memory is a 2048 byte array, the stack has a size of 512 and there is a maximum of 16 threads.


{{works with|GNU Cobol|2.0}}
{{works with|GNU Cobol|2.0}}

<lang cobol> >>SOURCE FREE
snusp.cob:
<syntaxhighlight lang="cobol"> >>SOURCE FREE
IDENTIFICATION DIVISION.
IDENTIFICATION DIVISION.
PROGRAM-ID. snusp-interpreter.
PROGRAM-ID. snusp-interpreter.

ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION find-initial
FUNCTION ALL INTRINSIC
.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY "dd-program-arguments.cpy".
COPY "dd-code-area.cpy".
01 program-state-flag PIC X VALUE SPACE.
88 program-ok VALUE SPACE.
88 empty-stack VALUE "E".
88 out-of-code-space VALUE "O".
01 memory-area.
03 memory-rows OCCURS 1024 TIMES.
05 memory-cols OCCURS 1024 TIMES.
07 memory-cell USAGE BINARY-CHAR.
07 memory-cell-char REDEFINES memory-cell PIC X.
01 num-threads PIC 99 COMP VALUE 1.
01 threads-data-area.
03 threads-data OCCURS 1 TO 16 TIMES
DEPENDING ON num-threads
INDEXED BY thread-idx.
05 thread-status-flag PIC X VALUE SPACE.
88 thread-started VALUE "Y".
05 call-stack.
07 calls OCCURS 512 TIMES
INDEXED BY stack-idx.
09 direction PIC X.
88 up-dir VALUE "U".
88 down-dir VALUE "D".
88 left-dir VALUE "L".
88 right-dir VALUE "R".
09 instruction-ptr.
11 ip-line USAGE INDEX.
11 ip-char USAGE INDEX.
05 memory-pointer.
07 row-idx USAGE INDEX.
07 col-idx USAGE INDEX.
01 input-char PIC X.
01 current-thread-idx USAGE INDEX.
PROCEDURE DIVISION.
000-main SECTION.
001-prepare-code.
CALL "parse-arguments" USING program-arguments
IF code-file-path = SPACES
DISPLAY "No file path specified."
STOP RUN
END-IF
CALL "read-code-file" USING CONTENT code-file-path, REFERENCE code-area

MOVE find-initial(code-area) TO instruction-ptr (1, 1)
.
010-interpret-code.
SET right-dir (1, 1) TO TRUE
SET stack-idx, thread-idx, row-idx (1), col-idx (1) TO 1
PERFORM UNTIL num-threads = 0
PERFORM VARYING thread-idx FROM 1 BY 1 UNTIL thread-idx > num-threads
PERFORM 100-move-instruction-ptr
IF out-of-code-space
PERFORM 200-stop-thread
END-IF
EVALUATE code-chars (ip-line (thread-idx, stack-idx),
ip-char (thread-idx, stack-idx))
*> Core SNUSP
WHEN "<" *> LEFT
SET col-idx (thread-idx) DOWN BY 1
WHEN ">" *> RIGHT
SET col-idx (thread-idx) UP BY 1
WHEN "+" *> INCR
ADD 1 TO
memory-cell (row-idx (thread-idx), col-idx (thread-idx))
WHEN "-" *> DECR
SUBTRACT 1 FROM
memory-cell (row-idx (thread-idx), col-idx (thread-idx))
WHEN "." *> WRITE
IF NOT write-numbers
DISPLAY memory-cell-char (row-idx (thread-idx),
col-idx (thread-idx))
ELSE
DISPLAY memory-cell (row-idx (thread-idx),
col-idx (thread-idx))
END-IF
WHEN "," *> READ
IF NOT read-numbers
ACCEPT memory-cell-char (row-idx (thread-idx),
col-idx (thread-idx))
ELSE
ACCEPT memory-cell (row-idx (thread-idx),
col-idx (thread-idx))
END-IF
*> LURD (/ is not used as it is mucks up syntax highlighting.)
WHEN X"5C"
EVALUATE TRUE
WHEN up-dir (thread-idx, stack-idx)
SET left-dir (thread-idx, stack-idx) TO TRUE
WHEN down-dir (thread-idx, stack-idx)
SET right-dir (thread-idx, stack-idx) TO TRUE
WHEN left-dir (thread-idx, stack-idx)
SET up-dir (thread-idx, stack-idx) TO TRUE
WHEN right-dir (thread-idx, stack-idx)
SET down-dir (thread-idx, stack-idx) TO TRUE
END-EVALUATE
WHEN "/" *> RULD
EVALUATE TRUE
WHEN up-dir (thread-idx, stack-idx)
SET right-dir (thread-idx, stack-idx) TO TRUE
WHEN down-dir (thread-idx, stack-idx)
SET left-dir (thread-idx, stack-idx) TO TRUE
WHEN left-dir (thread-idx, stack-idx)
SET down-dir (thread-idx, stack-idx) TO TRUE
WHEN right-dir (thread-idx, stack-idx)
SET up-dir (thread-idx, stack-idx) TO TRUE
END-EVALUATE
WHEN "!" *> SKIP
PERFORM 100-move-instruction-ptr
WHEN "?" *> SKIPZ
IF memory-cell (row-idx (thread-idx), col-idx (thread-idx))
= 0
PERFORM 100-move-instruction-ptr
END-IF
*> Modular SNUSP
WHEN "@" *> ENTER
*> Push current direction and IP location onto call stack
MOVE calls (thread-idx, stack-idx)
TO calls (thread-idx, stack-idx + 1)
SET stack-idx UP BY 1
WHEN "#" *> LEAVE
IF stack-idx <> 1
*> Pop direction and IP location off call stack and
*> advance the IP one step.
SET stack-idx DOWN BY 1
PERFORM 100-move-instruction-ptr
ELSE
PERFORM 200-stop-thread
END-IF
*> Bloated SNUSP
WHEN ":" *> UP
SET row-idx (thread-idx) UP BY 1
WHEN ";" *> DOWN
SET row-idx (thread-idx) DOWN BY 1
WHEN "&" *> SPLIT
*> Create a new thread
ADD 1 TO num-threads
MOVE call-stack (thread-idx) TO call-stack (num-threads)
MOVE memory-pointer (thread-idx) TO call-stack (num-threads)
SET thread-started (thread-idx) TO TRUE
WHEN "%" *> RAND
COMPUTE memory-cell (row-idx (thread-idx),
col-idx (thread-idx)) =
FUNCTION MOD(FUNCTION RANDOM,
memory-cell (row-idx (thread-idx),
col-idx (thread-idx)) + 1)
WHEN OTHER
CONTINUE
END-EVALUATE
IF out-of-code-space
PERFORM 200-stop-thread
END-IF
END-PERFORM
END-PERFORM
.
099-terminate.
GOBACK
.
100-move-instruction-ptr SECTION.
EVALUATE TRUE
WHEN up-dir (thread-idx, stack-idx)
SET ip-line (thread-idx, stack-idx) DOWN BY 1
WHEN down-dir (thread-idx, stack-idx)
SET ip-line (thread-idx, stack-idx) UP BY 1
WHEN left-dir (thread-idx, stack-idx)
SET ip-char (thread-idx, stack-idx) DOWN BY 1
WHEN right-dir (thread-idx, stack-idx)
SET ip-char (thread-idx, stack-idx) UP BY 1
END-EVALUATE
.
199-exit.
EXIT
.
200-stop-thread SECTION.
*> Shift data from following threads over stopped thread.
SET current-thread-idx TO thread-idx
PERFORM VARYING thread-idx FROM thread-idx BY 1
UNTIL NOT thread-started (thread-idx + 1)
OR thread-idx = num-threads
MOVE threads-data (thread-idx + 1) TO threads-data (thread-idx)
END-PERFORM
SUBTRACT 1 FROM num-threads
SET thread-idx TO current-thread-idx
.
299-exit.
EXIT
.
END PROGRAM snusp-interpreter.


IDENTIFICATION DIVISION.
PROGRAM-ID. parse-arguments.

DATA DIVISION.
LOCAL-STORAGE SECTION.
01 num-args PIC 9 COMP.
01 arg-num PIC 9 COMP.
01 arg PIC X(100).

COPY "dd-flag-constants.cpy".

01 program-flag PIC X.
88 help-flag VALUE Help-Flag-Char.
88 read-num-flag VALUE Read-Num-Flag-Char.
88 write-num-flag VALUE Write-Num-Flag-Char.

LINKAGE SECTION.
COPY "dd-program-arguments.cpy".

PROCEDURE DIVISION USING program-arguments.
ACCEPT num-args FROM ARGUMENT-NUMBER
IF num-args = 0
CALL "display-help"
STOP RUN
END-IF

PERFORM VARYING arg-num FROM 1 BY 1 UNTIL arg-num > num-args
DISPLAY arg-num UPON ARGUMENT-NUMBER
ACCEPT arg FROM ARGUMENT-VALUE
EVALUATE TRUE
WHEN arg (1:1) = Flag-Indicator
MOVE arg (2:1) TO program-flag
EVALUATE TRUE
WHEN help-flag
CALL "display-help"
STOP RUN
WHEN read-num-flag
SET read-numbers TO TRUE
WHEN write-num-flag
SET write-numbers TO TRUE
WHEN OTHER
DISPLAY "Flag '" FUNCTION TRIM(arg) "' not recongnized."
END-EVALUATE
WHEN code-file-path <> SPACES
DISPLAY "Argument " arg-num " ignored - only one source code "
"file can be interpreted."
WHEN OTHER
MOVE arg TO code-file-path
END-EVALUATE
END-PERFORM
.
END PROGRAM parse-arguments.


IDENTIFICATION DIVISION.
PROGRAM-ID. read-code-file.


ENVIRONMENT DIVISION.
ENVIRONMENT DIVISION.
Line 16: Line 303:
ORGANIZATION LINE SEQUENTIAL
ORGANIZATION LINE SEQUENTIAL
FILE STATUS code-file-status.
FILE STATUS code-file-status.

DATA DIVISION.
DATA DIVISION.
FILE SECTION.
FILE SECTION.
FD code-file.
FD code-file.
01 code-record PIC X(100).
01 code-record PIC X(100).

WORKING-STORAGE SECTION.
LOCAL-STORAGE SECTION.
01 code-file-path PIC X(100).
01 code-file-status PIC 99.
01 code-file-status PIC 99.
88 end-of-code-file VALUE 10.
88 end-of-code-file VALUE 10.


LINKAGE SECTION.
01 num-lines PIC 9(4) COMP.
01 code-area.
COPY "dd-code-area.cpy".
03 code-lines OCCURS 1 TO 1024 TIMES
DEPENDING ON num-lines
INDEXED BY line-idx.
05 code-chars PIC X OCCURS 100 TIMES
INDEXED BY char-idx.


01 program-state-flag PIC X VALUE SPACE.
01 code-file-path PIC X(100).
88 program-ok VALUE SPACE.
88 empty-stack VALUE "E".
88 out-of-code-space VALUE "O".


PROCEDURE DIVISION USING code-file-path, code-area.
01 array-area.
03 array OCCURS 2048 TIMES
INDEXED BY table-idx.
05 array-table USAGE BINARY-CHAR.
05 array-table-char REDEFINES array-table PIC X.

01 call-stack-area.
03 calls OCCURS 512 TIMES INDEXED BY stack-idx.
05 direction PIC X.
88 up-dir VALUE "U".
88 down-dir VALUE "D".
88 left-dir VALUE "L".
88 right-dir VALUE "R".
05 ip-line USAGE INDEX.
05 ip-char USAGE INDEX.
01 input-char PIC X.

PROCEDURE DIVISION.
DECLARATIVES.
DECLARATIVES.
code-file-error SECTION.
code-file-error SECTION.
USE AFTER ERROR ON code-file.
USE AFTER ERROR ON code-file.
DISPLAY "An error occurred while using " code-file-path
DISPLAY "An error occurred while using " FUNCTION TRIM(code-file-path)
DISPLAY "Error code " code-file-status
DISPLAY "Error code " code-file-status
DISPLAY "Error triggered at " FUNCTION TRIM(FUNCTION EXCEPTION-LOCATION)
" by " FUNCTION EXCEPTION-STATEMENT "."
DISPLAY "The program will terminate."
DISPLAY "The program will terminate."

GOBACK
STOP RUN
.
.
END DECLARATIVES.
END DECLARATIVES.


000-main SECTION.
ACCEPT code-file-path FROM COMMAND-LINE

*> Store contents of file into memory.
OPEN INPUT code-file
OPEN INPUT code-file
PERFORM VARYING line-idx FROM 1 BY 1 UNTIL end-of-code-file
PERFORM VARYING line-idx FROM 1 BY 1 UNTIL end-of-code-file
ADD 1 TO num-lines
READ code-file INTO code-lines (line-idx)
READ code-file INTO code-lines (line-idx)
NOT AT END
ADD 1 TO num-lines
AT END
AT END
SUBTRACT 1 FROM num-lines
EXIT PERFORM
EXIT PERFORM
END-READ
END-READ
END-PERFORM
END-PERFORM

CLOSE code-file
CLOSE code-file
.
END PROGRAM read-code-file.
*> Search for any initial charcters.

PERFORM VARYING ip-line (1) FROM 1 BY 1 UNTIL ip-line (1) > num-lines

AFTER ip-char (1) FROM 1 BY 1 UNTIL ip-char (1) > 100
IDENTIFICATION DIVISION.
IF code-chars (ip-line (1), ip-char (1)) = "$"
FUNCTION-ID. find-initial.

DATA DIVISION.
LINKAGE SECTION.
COPY "dd-code-area.cpy".

01 instruction-ptr.
03 ip-line USAGE INDEX.
03 ip-char USAGE INDEX.

PROCEDURE DIVISION USING code-area RETURNING instruction-ptr.
PERFORM VARYING ip-line FROM 1 BY 1 UNTIL ip-line > num-lines
AFTER ip-char FROM 1 BY 1 UNTIL ip-char > 100
IF code-chars (ip-line, ip-char) = "$"
EXIT PERFORM
EXIT PERFORM
END-IF
END-IF
END-PERFORM
END-PERFORM
*> Set position to first char if no initial characters were found.
*> Set position to first char if no initial characters were found.
IF ip-line (1) > num-lines
IF ip-line > num-lines
SET line-idx, char-idx TO 1
SET ip-line, ip-char TO 1
END-IF
END-IF
.
END FUNCTION find-initial.
*> Interpret the code while the instruction pointer remains in code space.
SET right-dir (1) TO TRUE
SET stack-idx TO 1
PERFORM UNTIL NOT program-ok
PERFORM 100-move-instruction-ptr
IF out-of-code-space
EXIT PERFORM
END-IF
EVALUATE code-chars (ip-line (stack-idx), ip-char (stack-idx))
*> Core SNUSP
WHEN ">"
SET table-idx UP BY 1


WHEN "<"
SET table-idx DOWN BY 1


IDENTIFICATION DIVISION.
WHEN "+"
PROGRAM-ID. display-help.
ADD 1 TO array-table (table-idx)


DATA DIVISION.
WHEN "-"
WORKING-STORAGE SECTION.
SUBTRACT 1 FROM array-table (table-idx)
COPY "dd-flag-constants.cpy".


WHEN "."
01 Tab-Char CONSTANT X"09".
DISPLAY array-table-char (table-idx)


PROCEDURE DIVISION.
WHEN ","
DISPLAY "This is a interpreter for SNUSP written in COBOL."
ACCEPT array-table-char (table-idx)
DISPLAY "The file path to the source code should be specified as a "
"command-line argument."
DISPLAY "This program supports the following flags as arguments:"
DISPLAY Tab-Char Flag-Indicator Help-Flag-Char ": Displays this help "
"message."
DISPLAY Tab-Char Flag-Indicator Write-Num-Flag-Char ": Display memory "
"contents as numbers."
DISPLAY Tab-Char Flag-Indicator Read-Num-Flag-Char ": Reads a byte to "
"memory as a number."
.
END PROGRAM display-help.</syntaxhighlight>


dd-code-area.cpy:
WHEN X"5C" *> Backslash mucks up syntax highlighting
<syntaxhighlight lang="cobol">01 code-area.
EVALUATE TRUE
WHEN up-dir (stack-idx)
03 num-lines PIC 9(4) COMP.
SET left-dir (stack-idx) TO TRUE
03 code-lines OCCURS 1 TO 1024 TIMES
WHEN down-dir (stack-idx)
DEPENDING ON num-lines
SET right-dir (stack-idx) TO TRUE
INDEXED BY line-idx.
WHEN left-dir (stack-idx)
05 code-chars PIC X OCCURS 100 TIMES.</syntaxhighlight>
SET up-dir (stack-idx) TO TRUE
WHEN right-dir (stack-idx)
SET down-dir (stack-idx) TO TRUE
END-EVALUATE


dd-flag-constants.cpy:
<syntaxhighlight lang="cobol">01 Flag-Indicator CONSTANT "-".
WHEN "/"
EVALUATE TRUE
01 Help-Flag-Char CONSTANT "h".
WHEN up-dir (stack-idx)
01 Read-Num-Flag-Char CONSTANT "r".
SET right-dir (stack-idx) TO TRUE
01 Write-Num-Flag-Char CONSTANT "w".</syntaxhighlight>
WHEN down-dir (stack-idx)
SET left-dir (stack-idx) TO TRUE
WHEN left-dir (stack-idx)
SET down-dir (stack-idx) TO TRUE
WHEN right-dir (stack-idx)
SET up-dir (stack-idx) TO TRUE
END-EVALUATE
WHEN "!"
PERFORM 100-move-instruction-ptr
WHEN "?"
IF array-table (table-idx) = 0
PERFORM 100-move-instruction-ptr
END-IF


dd-program-arguments.cpy:
*> Modular SNUSP
<syntaxhighlight lang="cobol">01 program-arguments.
WHEN "@"
*> Push current direction and IP location onto call stack
03 code-file-path PIC X(100).
MOVE calls (stack-idx) TO calls (stack-idx + 1)
03 read-flag PIC X.
SET stack-idx UP BY 1
88 read-numbers VALUE "N".
03 write-flag PIC X.
WHEN "#"
88 write-numbers VALUE "N".</syntaxhighlight>
IF stack-idx <> 1
*> Pop direction and IP location off call stack and advance
*> the IP one step.
SET stack-idx DOWN BY 1
PERFORM 100-move-instruction-ptr
ELSE
SET empty-stack TO TRUE
END-IF

*> Bloated SNUSP
WHEN OTHER
CONTINUE
END-EVALUATE
END-PERFORM

GOBACK
.
100-move-instruction-ptr SECTION.
EVALUATE TRUE
WHEN up-dir (stack-idx)
SET ip-line (stack-idx) DOWN BY 1
WHEN down-dir (stack-idx)
SET ip-line (stack-idx) UP BY 1
WHEN left-dir (stack-idx)
SET ip-char (stack-idx) DOWN BY 1
WHEN right-dir (stack-idx)
SET ip-char (stack-idx) UP BY 1
END-EVALUATE
.
END PROGRAM snusp-interpreter.</lang>

Latest revision as of 08:39, 1 September 2022

This an SNUSP interpreter written in COBOL. It supports Modular and Bloated SNUSP.

The file path to the code is passed as a parameter to the program. The file is assumed to be a text file with lines having a maximum length of 100 characters. It is also assumed the file will not be more than 1024 lines long.

The memory is a 2048 byte array, the stack has a size of 512 and there is a maximum of 16 threads.

Works with: GNU Cobol version 2.0

snusp.cob:

       >>SOURCE FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. snusp-interpreter.

ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
    FUNCTION find-initial
    FUNCTION ALL INTRINSIC
    .
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY "dd-program-arguments.cpy".
 
COPY "dd-code-area.cpy".
    
01  program-state-flag                  PIC X VALUE SPACE.
    88  program-ok                      VALUE SPACE.
    88  empty-stack                     VALUE "E".
    88  out-of-code-space               VALUE "O".
 
01  memory-area.
    03  memory-rows                     OCCURS 1024 TIMES.
        05  memory-cols                 OCCURS 1024 TIMES.
            07  memory-cell             USAGE BINARY-CHAR.
            07  memory-cell-char        REDEFINES memory-cell PIC X.
 
01  num-threads                         PIC 99 COMP VALUE 1.
01  threads-data-area.
    03  threads-data                    OCCURS 1 TO 16 TIMES
                                        DEPENDING ON num-threads
                                        INDEXED BY thread-idx.
        05  thread-status-flag          PIC X VALUE SPACE.
            88  thread-started          VALUE "Y".
        05  call-stack.
            07  calls                   OCCURS 512 TIMES
                                        INDEXED BY stack-idx.
                09  direction           PIC X.
                    88  up-dir          VALUE "U".
                    88  down-dir        VALUE "D".
                    88  left-dir        VALUE "L".
                    88  right-dir       VALUE "R".
                09  instruction-ptr.
                    11  ip-line         USAGE INDEX.
                    11  ip-char         USAGE INDEX.
        05  memory-pointer.
            07  row-idx                 USAGE INDEX.
            07  col-idx                 USAGE INDEX.
 
01  input-char                          PIC X.
 
01  current-thread-idx                  USAGE INDEX.
 
PROCEDURE DIVISION.
000-main SECTION.
001-prepare-code.
    CALL "parse-arguments" USING program-arguments
    IF code-file-path = SPACES
        DISPLAY "No file path specified."
        STOP RUN
    END-IF
    
    CALL "read-code-file" USING CONTENT code-file-path, REFERENCE code-area

    MOVE find-initial(code-area) TO instruction-ptr (1, 1)
    .
010-interpret-code.
    SET right-dir (1, 1) TO TRUE
    SET stack-idx, thread-idx, row-idx (1), col-idx (1) TO 1
    PERFORM UNTIL num-threads = 0
        PERFORM VARYING thread-idx FROM 1 BY 1 UNTIL thread-idx > num-threads
            PERFORM 100-move-instruction-ptr
            IF out-of-code-space
                PERFORM 200-stop-thread
            END-IF
 
            EVALUATE code-chars (ip-line (thread-idx, stack-idx),
                    ip-char (thread-idx, stack-idx))
                *> Core SNUSP
                WHEN "<" *> LEFT
                    SET col-idx (thread-idx) DOWN BY 1
 
                WHEN ">" *> RIGHT
                    SET col-idx (thread-idx) UP BY 1
 
                WHEN "+" *> INCR
                    ADD 1 TO
                       memory-cell (row-idx (thread-idx), col-idx (thread-idx))
 
                WHEN "-" *> DECR
                    SUBTRACT 1 FROM
                        memory-cell (row-idx (thread-idx), col-idx (thread-idx))
 
                WHEN "." *> WRITE
                    IF NOT write-numbers
                        DISPLAY memory-cell-char (row-idx (thread-idx),
                            col-idx (thread-idx))
                    ELSE
                        DISPLAY memory-cell (row-idx (thread-idx),
                            col-idx (thread-idx))
                    END-IF
 
                WHEN "," *> READ
                    IF NOT read-numbers
                        ACCEPT memory-cell-char (row-idx (thread-idx),
                            col-idx (thread-idx))
                    ELSE
                        ACCEPT memory-cell (row-idx (thread-idx),
                            col-idx (thread-idx))
                    END-IF
 
                *> LURD (/ is not used as it is mucks up syntax highlighting.)
                WHEN X"5C"
                    EVALUATE TRUE
                        WHEN up-dir (thread-idx, stack-idx)
                            SET left-dir (thread-idx, stack-idx) TO TRUE
                        WHEN down-dir (thread-idx, stack-idx)
                            SET right-dir (thread-idx, stack-idx) TO TRUE
                        WHEN left-dir (thread-idx, stack-idx)
                            SET up-dir (thread-idx, stack-idx) TO TRUE
                        WHEN right-dir (thread-idx, stack-idx)
                            SET down-dir (thread-idx, stack-idx) TO TRUE
                    END-EVALUATE
 
 
                WHEN "/" *> RULD
                    EVALUATE TRUE
                        WHEN up-dir (thread-idx, stack-idx)
                            SET right-dir (thread-idx, stack-idx) TO TRUE
                        WHEN down-dir (thread-idx, stack-idx)
                            SET left-dir (thread-idx, stack-idx) TO TRUE
                        WHEN left-dir (thread-idx, stack-idx)
                            SET down-dir (thread-idx, stack-idx) TO TRUE
                        WHEN right-dir (thread-idx, stack-idx)
                            SET up-dir (thread-idx, stack-idx) TO TRUE
                    END-EVALUATE
 
                WHEN "!" *> SKIP
                    PERFORM 100-move-instruction-ptr
 
                WHEN "?" *> SKIPZ
                    IF memory-cell (row-idx (thread-idx), col-idx (thread-idx))
                            = 0
                        PERFORM 100-move-instruction-ptr
                    END-IF
 
                *> Modular SNUSP
                WHEN "@" *> ENTER
                    *> Push current direction and IP location onto call stack
                    MOVE calls (thread-idx, stack-idx)
                        TO calls (thread-idx, stack-idx + 1)
                    SET stack-idx UP BY 1
 
                WHEN "#" *> LEAVE
                    IF stack-idx <> 1
                        *> Pop direction and IP location off call stack and
                        *> advance the IP one step.
                        SET stack-idx DOWN BY 1
                        PERFORM 100-move-instruction-ptr
                    ELSE
                        PERFORM 200-stop-thread
                    END-IF
 
                *> Bloated SNUSP
                WHEN ":" *> UP
                    SET row-idx (thread-idx) UP BY 1
 
                WHEN ";" *> DOWN
                    SET row-idx (thread-idx) DOWN BY 1
 
                WHEN "&" *> SPLIT
                    *> Create a new thread
                    ADD 1 TO num-threads
                    MOVE call-stack (thread-idx) TO call-stack (num-threads)
                    MOVE memory-pointer (thread-idx) TO call-stack (num-threads)
                    SET thread-started (thread-idx) TO TRUE
 
                WHEN "%" *> RAND
                    COMPUTE memory-cell (row-idx (thread-idx),
                            col-idx (thread-idx)) =
                        FUNCTION MOD(FUNCTION RANDOM,
                            memory-cell (row-idx (thread-idx),
                                col-idx (thread-idx)) + 1)
                                
                WHEN OTHER
                    CONTINUE
            END-EVALUATE
 
            IF out-of-code-space
                PERFORM 200-stop-thread
            END-IF
        END-PERFORM
    END-PERFORM
    .
099-terminate.
    GOBACK
    .
100-move-instruction-ptr SECTION.
    EVALUATE TRUE
        WHEN up-dir (thread-idx, stack-idx)
            SET ip-line (thread-idx, stack-idx) DOWN BY 1
        WHEN down-dir (thread-idx, stack-idx)
            SET ip-line (thread-idx, stack-idx) UP BY 1
        WHEN left-dir (thread-idx, stack-idx)
            SET ip-char (thread-idx, stack-idx) DOWN BY 1
        WHEN right-dir (thread-idx, stack-idx)
            SET ip-char (thread-idx, stack-idx) UP BY 1
    END-EVALUATE
    .
199-exit.
    EXIT
    .
200-stop-thread SECTION.
    *> Shift data from following threads over stopped thread.
    SET current-thread-idx TO thread-idx
    PERFORM VARYING thread-idx FROM thread-idx BY 1
            UNTIL NOT thread-started (thread-idx + 1)
                OR thread-idx = num-threads
        MOVE threads-data (thread-idx + 1) TO threads-data (thread-idx)
    END-PERFORM
 
    SUBTRACT 1 FROM num-threads
    SET thread-idx TO current-thread-idx
    .
299-exit.
    EXIT
    .
END PROGRAM snusp-interpreter.


IDENTIFICATION DIVISION.
PROGRAM-ID. parse-arguments.

DATA DIVISION.
LOCAL-STORAGE SECTION.
01  num-args                            PIC 9 COMP.
01  arg-num                             PIC 9 COMP.
01  arg                                 PIC X(100).

COPY "dd-flag-constants.cpy".

01  program-flag                        PIC X.
    88  help-flag                       VALUE Help-Flag-Char.
    88  read-num-flag                   VALUE Read-Num-Flag-Char.
    88  write-num-flag                  VALUE Write-Num-Flag-Char.

LINKAGE SECTION.
COPY "dd-program-arguments.cpy".

PROCEDURE DIVISION USING program-arguments.
    ACCEPT num-args FROM ARGUMENT-NUMBER
    IF num-args = 0
        CALL "display-help"
        STOP RUN
    END-IF

    PERFORM VARYING arg-num FROM 1 BY 1 UNTIL arg-num > num-args
        DISPLAY arg-num UPON ARGUMENT-NUMBER
        ACCEPT arg FROM ARGUMENT-VALUE
        EVALUATE TRUE
            WHEN arg (1:1) = Flag-Indicator
                MOVE arg (2:1) TO program-flag
                EVALUATE TRUE
                    WHEN help-flag
                        CALL "display-help"
                        STOP RUN
                    WHEN read-num-flag
                        SET read-numbers TO TRUE
                    WHEN write-num-flag
                        SET write-numbers TO TRUE
                    WHEN OTHER
                        DISPLAY "Flag '" FUNCTION TRIM(arg) "' not recongnized."
                END-EVALUATE
 
            WHEN code-file-path <> SPACES
                DISPLAY "Argument " arg-num " ignored - only one source code "
                    "file can be interpreted."
 
            WHEN OTHER
                MOVE arg TO code-file-path
        END-EVALUATE
    END-PERFORM
    .
END PROGRAM parse-arguments.


IDENTIFICATION DIVISION.
PROGRAM-ID. read-code-file.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT code-file ASSIGN code-file-path
        ORGANIZATION LINE SEQUENTIAL
        FILE STATUS code-file-status.
 
DATA DIVISION.
FILE SECTION.
FD  code-file.
01  code-record                         PIC X(100).
 
LOCAL-STORAGE SECTION.
01  code-file-status                    PIC 99.
    88  end-of-code-file                VALUE 10.

LINKAGE SECTION.
COPY "dd-code-area.cpy".

01  code-file-path                      PIC X(100).

PROCEDURE DIVISION USING code-file-path, code-area.
DECLARATIVES.
code-file-error SECTION.
    USE AFTER ERROR ON code-file.
 
    DISPLAY "An error occurred while using " FUNCTION TRIM(code-file-path)
    DISPLAY "Error code " code-file-status
    DISPLAY "The program will terminate."
 
    STOP RUN
    .
END DECLARATIVES.

    OPEN INPUT code-file
    PERFORM VARYING line-idx FROM 1 BY 1 UNTIL end-of-code-file
        READ code-file INTO code-lines (line-idx)
            NOT AT END
                ADD 1 TO num-lines
            AT END
                EXIT PERFORM
        END-READ
    END-PERFORM
 
    CLOSE code-file
    .
END PROGRAM read-code-file.


IDENTIFICATION DIVISION.
FUNCTION-ID. find-initial.

DATA DIVISION.
LINKAGE SECTION.
COPY "dd-code-area.cpy".

01  instruction-ptr.
    03  ip-line                         USAGE INDEX.
    03  ip-char                         USAGE INDEX.

PROCEDURE DIVISION USING code-area RETURNING instruction-ptr.
    PERFORM VARYING ip-line FROM 1 BY 1 UNTIL ip-line > num-lines
            AFTER ip-char FROM 1 BY 1 UNTIL ip-char > 100
        IF code-chars (ip-line, ip-char) = "$"
            EXIT PERFORM
        END-IF
    END-PERFORM
 
    *> Set position to first char if no initial characters were found.
    IF ip-line > num-lines
        SET ip-line, ip-char TO 1
    END-IF
    .
END FUNCTION find-initial.


IDENTIFICATION DIVISION.
PROGRAM-ID. display-help.

DATA DIVISION.
WORKING-STORAGE SECTION.
COPY "dd-flag-constants.cpy".

01  Tab-Char                            CONSTANT X"09".

PROCEDURE DIVISION.
    DISPLAY "This is a interpreter for SNUSP written in COBOL."
    DISPLAY "The file path to the source code should be specified as a "
        "command-line argument."
    DISPLAY "This program supports the following flags as arguments:"
    DISPLAY Tab-Char Flag-Indicator Help-Flag-Char ": Displays this help "
        "message."
    DISPLAY Tab-Char Flag-Indicator Write-Num-Flag-Char ": Display memory "
        "contents as numbers."
    DISPLAY Tab-Char Flag-Indicator Read-Num-Flag-Char ": Reads a byte to  "
        "memory as a number."
    .
END PROGRAM display-help.

dd-code-area.cpy:

01  code-area.
    03  num-lines                       PIC 9(4) COMP.
    03  code-lines                      OCCURS 1 TO 1024 TIMES
                                        DEPENDING ON num-lines
                                        INDEXED BY line-idx.
        05  code-chars                  PIC X OCCURS 100 TIMES.

dd-flag-constants.cpy:

01  Flag-Indicator                      CONSTANT "-".
01  Help-Flag-Char                      CONSTANT "h".
01  Read-Num-Flag-Char                  CONSTANT "r".
01  Write-Num-Flag-Char                 CONSTANT "w".

dd-program-arguments.cpy:

01  program-arguments.
    03  code-file-path                  PIC X(100).
    03  read-flag                       PIC X.
        88 read-numbers                 VALUE "N". 
    03  write-flag                      PIC X.
        88  write-numbers               VALUE "N".