Execute SNUSP

From Rosetta Code
Task
Execute SNUSP
You are encouraged to solve this task according to the task description, using any language you may know.
Execute SNUSP is an implementation of SNUSP. Other implementations of SNUSP.

RCSNUSP is a set of SNUSP compilers and interpreters written for Rosetta Code in a variety of languages. Below are links to each of the versions of RCSNUSP.

An implementation need only properly implement the Core SNUSP instructions ('$', '\', '/', '+', '-', '<', '>', ',', '.', '!', and '?'). Modular SNUSP ('#', '@') and Bloated SNUSP (':', ';', '%', and '&') are also allowed, but not required. Any extra characters that you implement should be noted in the description of your implementation. Any cell size is allowed, EOF support is optional, as is whether you have bounded or unbounded memory.

11l

Translation of: Python
V HW = ‘
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/’

F snusp(store, code)
   V ds = [Byte(0)] * store
   V dp = 0
   V cs = code.split("\n")
   V ipr = 0
   V ipc = 0

   L(row) cs
      ipc = row.findi(‘$’)
      I ipc != -1
         ipr = L.index
         L.break

   V id = 0

   F step()
      I @id [&] 1
         @ipr += 1 - (@id [&] 2)
      E
         @ipc += 1 - (@id [&] 2)

   L ipr >= 0 & ipr < cs.len & ipc >= 0 & ipc < cs[ipr].len
      S cs[ipr][ipc]
         ‘>’
            dp++
         ‘<’
            dp--
         ‘+’
            ds[dp]++
         ‘-’
            ds[dp]--
         ‘.’
            :stdout.write(Char(code' ds[dp]))
         ‘,’
            ds[dp] = Byte(:stdin.read(1).code)
         ‘/’
            id = ~id
         ‘\’
            id (+)= 1
         ‘!’
            step()
         ‘?’
            I !(ds[dp])
               step()
      step()

snusp(5, HW)
Output:
Hello World!

Ada

See Execute SNUSP/Ada.

ALGOL 68

See Execute SNUSP/Algol68.

AutoHotkey

See RCSNUSP/AutoHotkey.

C

See RCSNUSP/C.

C++

See RCSNUSP/C++.

COBOL

See RCSNUSP/COBOL.

D

See RCSNUSP/D.

EasyLang

Translation of: Go
proc snusp dlen raw$ . .
   len ds[] dlen
   is$[] = strsplit raw$ "\n"
   for s$ in is$[]
      is$[][] &= strchars s$
   .
   for ipr to len is$[][]
      for ipc to len is$[ipr][]
         if is$[ipr][ipc] = "$"
            break 2
         .
      .
   .
   dp = 1
   id = 0
   # 
   subr step
      if id mod 2 = 0
         ipc += 1 - bitand id 2
      else
         ipr += 1 - bitand id 2
      .
   .
   while ipr >= 1 and ipr <= len is$[][] and ipc >= 1 and ipc <= len is$[ipr][]
      c$ = is$[ipr][ipc]
      if c$ = ">"
         dp += 1
      elif c$ = "<"
         dp -= 1
      elif c$ = "+"
         ds[dp] += 1
      elif c$ = "-"
         ds[dp] -= 1
      elif c$ = "."
         write strchar ds[dp]
      elif c$ = ","
         # ds[dp] = strcode input
      elif c$ = "/"
         id = bitxor id 3
      elif c$ = "\\"
         id = bitxor id 1
      elif c$ = "!"
         step
      elif c$ = "?"
         if ds[dp] = 0
            step
         .
      .
      step
   .
.
s$ = input
while s$ <> ""
   cod$ &= "\n" & s$
   s$ = input
.
snusp 5 cod$
# 
input_data
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/

F#

See RCSNUSP/F Sharp.

Factor

See RCSNUSP/Factor.

FreeBASIC

' Rosetta Code problem: https://rosettacode.org/wiki/Execute_SNUSP
' by Jjuanhdez, 05/2024
' The interpreter below implements Core SNUSP:

Const HW = "/++++!/===========?\>++.>+.+++++++..+++" & Chr(10) & _
"\+++\ | /+>+++++++>/ /++++++++++<<.++>./" & Chr(10) & _
"$+++/ | \+++++++++>\ \+++++.>.+++.-----\" & Chr(10) & _
"      \==-<<<<+>+++/ /=.>.+>.--------.-/"

Dim Shared As Integer ipf, ipc ' instruction pointers in row and column
Dim Shared As Integer direcc   ' direction (0 = right, 1 = down, 2 = left, 3 = up)

Sub Paso()
    If direcc And 1 Then
        ipf += 1 - (direcc And 2)
    Else
        ipc += 1 - (direcc And 2)
    End If
End Sub

Sub SNUSP (dsLen As Integer, SNUSPcode As String)
    Dim As Ubyte ad(dsLen - 1)  ' data store
    Dim As Integer dp           ' data pointer
    Dim As String cb(dsLen)     ' two-way code storage
    Dim As String fila, op, linea
    Dim As Integer r, i, j
    
    dp = 0
    i = 1
    j = 1
    ipf = 0
    ipc = 0
    direcc = 0
    
    While i <= Len(SNUSPcode)
        If Mid(SNUSPcode, i, 1) = Chr(10) Then
            cb(j) = linea
            linea = ""
            j += 1
        Else
            linea &= Mid(SNUSPcode, i, 1)
        End If
        i += 1
    Wend
    cb(j) = linea
    
    For r = 0 To Ubound(cb)
        fila = cb(r)
        ipc = Instr(fila, "$") - 1
        If ipc >= 0 Then
            ipf = r
            Exit For
        End If
    Next r
    
    While ipf >= 0 And ipf <= Ubound(cb) And ipc >= 0 And ipc < Len(cb(ipf))
        op = Mid(cb(ipf), ipc + 1, 1)
        Select Case op
        Case ">": dp += 1                 ' RIGTH
        Case "<": dp -= 1                 ' LEFT
        Case "+": ad(dp) += 1             ' INCR
        Case "-": ad(dp) -= 1             ' DECR
        Case ",": Input ad(dp)            ' READ
        Case ".": Print Chr(ad(dp));      ' WRITE
        Case "/": direcc = Not direcc     ' RULD
        Case "\": direcc Xor= 1           ' LURD
        Case "!": Paso                    ' SKIP
        Case "?": If ad(dp) = 0 Then Paso ' SKIPZ
        End Select
        Paso
    Wend
    Print Chr(ad(dp));
End Sub

SNUSP(5, HW)

Sleep

Go

See RCSNUSP/Go.

Haskell

See RCSNUSP/Haskell.

Icon and Unicon

#
# snusp.icn, A Modular SNUSP interpreter
#

$define VERSION 0.6

# allow a couple of cli options
link options

# directions
$define DRIGHT 1
$define DLEFT  2
$define DUP    3
$define DDOWN  4

record position(row, col)
global dir, ip, ram

procedure main(argv)
   local ch, codespace, col, dp, fn, line
   local row := 1
   local wid := 0
   local dirs := []
   local ips := []
   local opts, verbose, debug

   opts := options(argv, "-h! -v! -d!", errorproc)
   \opts["v"] & verbose := 1
   \opts["h"] & show_help(verbose)
   \opts["d"] & debug := 1
   
   ip := position(1,1)

   # initial direction
   dir := DRIGHT 

   # prepare initial memory
   ram := list(1, 0)

   # prepare code field
   codespace := []

   fn := open(argv[1], "r") | &input
   if (fn === &input) & \opts["h"] then return

   while line := read(fn) do {
      put(codespace, line)
      wid := max(*line, wid)
   }
   if *codespace = 0 then return
   every line := !codespace do {
      codespace[row] := left(codespace[row], wid)
      # track starting indicator
      if /col := find("$", codespace[row]) then {
         ip.row := row
         ip.col := col
      }
      row +:= 1
   }

   if \verbose then {
      write("Starting at ", ip.row, ", ", ip.col, " with codespace:")
      every write(!codespace)
   }

   dp := 1
   repeat {
      if not (ch := codespace[ip.row][ip.col]) then break 
      if \debug then {
         write(&errout, "dir: ", dir, " ch: ", ch, " [", ord(ch), "]",
                        " row: ", ip.row, " col: ", ip.col,
                        " dp: ", dp, " ram[dp]: ", ram[dp])
      }
      case ch of {
         # six of the bf instructions
         "+": ram[dp] +:= 1
         "-": ram[dp] -:= 1
         ">": resize(dp +:= 1)
         "<": dp -:= 1
         ".": writes(char(ram[dp]) | char(0))
         ",": ram[dp] := getche()
         # direction change, LURD, RULD, SKIP, SKIPZ
         "\\": { # LURD
            case dir of {
               DRIGHT: dir := DDOWN
               DLEFT:  dir := DUP
               DUP:    dir := DLEFT
               DDOWN:  dir := DRIGHT
            }
         } 
         "/": { # RULD
            case dir of {
               DRIGHT: dir := DUP
               DLEFT:  dir := DDOWN
               DUP:    dir := DRIGHT
               DDOWN:  dir := DLEFT
            }
         }
         "!": step()
         "?": { # skipz
            if ram[dp] = 0 then {
               step()
            }
         }
         # modular SNUSP
         "@": { # Enter
            push(dirs, dir)
            push(ips, copy(ip))
         }
         "#": { # Leave
            if *dirs < 1 then break
            dir := pop(dirs)
            ip := pop(ips)
            step()
         }
      }
      step()
   }
end

# advance the ip depending on direction
procedure step()
   case dir of {
      DRIGHT: ip.col +:= 1
      DLEFT:  ip.col -:= 1
      DUP:    ip.row -:= 1
      DDOWN:  ip.row +:= 1
   }
end

# enlarge memory when needed
procedure resize(elements)
   until *ram >= elements do put(ram, 0)
end 

# quick help or verbose help
procedure show_help(verbose)
   write("SNUSP interpeter in Unicon, version ", VERSION)
   write("CORE and MODULAR, not yet BLOATED")
   write()
   write("Usage: unicon snusp.icn -x [filename] [-h|-v|-d]")
   write(" -h, help")
   write(" -v, verbose (and verbose help")
   write(" -d, debug (step tracer)")
   if \verbose then {
      write()
      write("Instructions:")
      write(" + INCR,  Increment current memory location")
      write(" - DECR,  Decrement current memory location")
      write(" > RIGHT, Advance memory pointer")
      write(" < LEFT,  Retreat memory pointer")
      write(" . WRITE, Output contents of current memory cell, in ASCII")
      write(" , READ,  Accept key and place byte value in current memory cell")
      write(" \\ LURD, If going:")
      write("           left,  go up")
      write("           up,    go left")
      write("           right, go down")
      write("           down,  go right")
      write(" / RULD, If going:")
      write("           right, go up")
      write("           up,    go right")
      write("           left,  go down")
      write("           down,  go left")
      write(" !, SKIP,  Move forward one step in current direction")
      write(" ?, SKIPZ, If current memory cell is zero then SKIP")
      write("Modular SNUSP adds:")
      write(" @, ENTER, Push direction and instruction pointer")
      write(" #, LEAVE, Pop direction and instruction pointer and SKIP")
      write()
      write("All other characters are NOOP, explicitly includes =,|,spc")
      write(" $, can set the starting location; first one found")
      write()
      write("Hello world examples:")
      write()
      write("CORE SNUSP:")
      write("/++++!/===========?\\>++.>+.+++++++..+++\\")
      write("\\+++\\ | /+>+++++++>/ /++++++++++<<.++>./")
      write("$+++/ | \\+++++++++>\\ \\+++++.>.+++.-----\\")
      write("      \\==-<<<<+>+++/ /=.>.+>.--------.-/")
      write()
      write("Modular SNUSP:")
      write("      /@@@@++++#               #+++@@\                #-----@@@\\n")
      write("$@\\H.@/e.+++++++l.l.+++o.>>++++.< .<@/w.@\\o.+++r.++@\\l.@\\d.>+.@/.#")
      write("  \\@@@@=>++++>+++++<<@+++++#       #---@@/!=========/!==/")
      write()
   }
end
Output:

Using the Modular SNUSP sample

prompt$ unicon -s snusp.icn -x hello.snusp
Hello, world!

J

This program places no limits on the program or data size. Perhaps I'll revisit and write a tacit version of the SNUSP interpreter.

Note 'snusp'

    Without $ character the program counter starts at top left (0 0) moving to the right (0 1)

    >       increment the pointer (to point to the next cell to the right).
    <       decrement the pointer (to point to the next cell to the left).
    +       increment (increase by one) the cell at the pointer.
    -       decrement (decrease by one) the cell at the pointer.
    .       output the value of the cell at the pointer as a character.
    ,       accept one character of input, storing its value in the cell at the pointer.
    \/      mirrors
    ?       skip if memory pointer is 0
    !       skip
    $       optional start program here (also heading to the right)

)

smoutput 'Toroidal programs run forever.  Use ^C to interrupt.'

main =: 3 : 0  NB. use: main 'program.snusp'
 PROGRAM =: [;._2 LF ,~ 1!:1 boxopen y
 SHAPE =: $PROGRAM
 PC =: SHAPE#:(,PROGRAM) i.'$'
 PCSTEP =: 0 1
 CELL =: 0
 CELLS =: ,0
 while. 1 do. NB. for_i. i.400 do.
  INSTRUCTION =: (<PC) { PROGRAM
  STEP =: PCSTEP
  select. INSTRUCTION
  case. '<' do.
   CELL =: <: CELL
   if. CELL < 0 do.
    CELL =: 0
    CELLS =: 0 , CELLS
   end.
  case. '>' do.
   CELL =: >: CELL
   if. CELL >: # CELLS do.
    CELLS =: CELLS , 0
   end.
  case. ;/'-+' do. CELLS =: CELL ((<:'- +'i.INSTRUCTION)+{)`[`]} CELLS
  case. '.' do. 1!:2&4 (CELL{CELLS){a.
  case. ',' do. CELLS =: (1!:1<1) CELL } CELLS
  fcase. '/' do. STEP =: - STEP
  case. '\' do. STEP =: PCSTEP =: |. STEP
  case. '?' do. STEP =: +:^:(0 = CELL{CELLS) STEP
  case. '!' do. STEP =: +: STEP
  end.
  PC =: (| (PC + STEP + ])) SHAPE  NB. toroidal
  NB. smoutput PC;CELL;CELLS  NB. debug
 end.
)

Store

\      display  JJ and linefeed, then loop forever
\      +++++++++++++++++++++++++++++++++++++\
    ! /+++++++++++++++++++++++++++++++++++++/
    / \..<+++++\
      \ . +++++/

as J.snusp

   load'snusp.ijs'  NB. the j code above
Toroidal programs run forever.  Use ^C to interrupt.
   
   main'J.snusp'
JJ
^C
|attention interrupt: main
   

Java

See RCSNUSP/Java

JavaScript

See RCSNUSP/JavaScript.

Julia

This Modular SNUSP interpreter uses modular calls to echo the first 2 characters entered. Try typing "Hi" at the prompt.

const echo2 =  raw"""
       /==!/======ECHO==,==.==#
       |   |
$==>==@/==@/==<==#"""

@enum Direction left up right down

function snusp(datalength, progstring)
    stack = Vector{Tuple{Int, Int, Direction}}()
    data = zeros(datalength)
    dp = ipx = ipy = 1
    direction = right    # default to go to right at beginning

    lines = split(progstring, "\n")
    lmax = maximum(map(length, lines))
    lines = map(x -> rpad(x, lmax), lines)
    for (y, li) in enumerate(lines)
        if (x = findfirst("\$", li)) != nothing
            (ipx, ipy) = (x[1], y)
        end
    end

    instruction = Dict([('>', ()-> dp += 1),
        ('<', ()-> (dp -= 1; if dp < 0 running = false end)), ('+', ()-> data[dp] += 1),
        ('-', ()-> data[dp] -= 1), (',', ()-> (data[dp] = read(stdin, UInt8))),
        ('.', ()->print(Char(data[dp]))),
        ('/', ()-> (d = Int(direction); d += (iseven(d) ? 3 : 5); direction = Direction(d % 4))),
        ('\\', ()-> (d = Int(direction); d += (iseven(d) ? 1 : -1); direction = Direction(d))),
        ('!', () -> ipnext()), ('?', ()-> if data[dp] == 0 ipnext() end),
        ('@', ()-> push!(stack, (ipx, ipy, direction))),
        ('#', ()-> if length(stack) > 0 (ipx, ipy, direction) = pop!(stack) end),
        ('\n', ()-> (running = false))])

    inboundsx(plus) = (plus ? (ipx < lmax) : (ipx > 1)) ? true : exit(data[dp])
    inboundsy(plus) = (plus ? (ipy < length(lines)) : (ipy > 1)) ? true : exit(data[dp])
    function ipnext()
        if direction == right && inboundsx(true)     ipx += 1
        elseif direction == left && inboundsx(false) ipx -= 1
        elseif direction == down && inboundsy(true)  ipy += 1
        elseif direction == up && inboundsy(false)   ipy -= 1
        end
    end

    running = true
    while running
        cmdcode = lines[ipy][ipx]
        if haskey(instruction, cmdcode)
            instruction[cmdcode]()
        end
        ipnext()
    end
    exit(data[dp])
end

snusp(100, echo2)
Output:

> Hi
Hi
>

Kotlin

Translation of: Go
// version 1.1.2

// requires 5 chars (10 bytes) of data store
const val hw = """
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/"""

// input is a multi-line string.
fun snusp(dlen: Int, raw: String) {
    val ds = CharArray(dlen)  // data store
    var dp = 0                // data pointer
    var s = raw

    // remove leading '\n' from string if present
    s = s.trimStart('\n')

    // make 2 dimensional instruction store and declare instruction pointers
    val cs = s.split('\n')
    var ipr = 0
    var ipc = 0

    // look for starting instruction
    findStart@  for ((r, row) in cs.withIndex()) {
        for ((i, c) in row.withIndex()) {
            if (c == '$') {
                ipr = r
                ipc = i
                break@findStart
            }
        }
    }

    var id = 0
    val step = fun() {
        if (id and 1 == 0)
            ipc += 1 - (id and 2)
        else
            ipr += 1 - (id and 2)
    }

    // execute
    while ((ipr in 0 until cs.size) && (ipc in 0 until cs[ipr].length)) {
        when (cs[ipr][ipc]) {
            '>'  -> dp++
            '<'  -> dp--
            '+'  -> ds[dp]++
            '-'  -> ds[dp]--
            '.'  -> print(ds[dp])
            ','  -> ds[dp] = readLine()!![0]
            '/'  -> id = id.inv()
            '\\' -> id = id xor 1
            '!'  -> step()
            '?'  -> if (ds[dp] == '\u0000') step()
        }
        step()
    }
}

fun main(args: Array<String>) {
    snusp(5, hw)
}
Output:
Hello World!

Lua

See RCSNUSP/Lua.

Mathematica /Wolfram Language

See RCSNUSP/Mathematica.

Nim

Translation of: Python
import strutils

# Requires 5 bytes of data store.
const Hw = r"""
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/"""

#---------------------------------------------------------------------------------------------------

proc snusp(dsLen: int; code: string) =
  ## The interpreter.

  var
    ds = newSeq[byte](dsLen)  # Data store.
    dp = 0                    # Data pointer.
    cs = code.splitLines()    # Two dimensional code store.
    ipr, ipc = 0              # Instruction pointers in row ans column.
    dir = 0                   # Direction (0 = right, 1 = down, 2 = left, 3 = up).

  # Initialize instruction pointers.
  for r, row in cs:
    ipc = row.find('$')
    if ipc >= 0:
      ipr = r
      break

  #.................................................................................................

  func step() =
    ## Update the instruction pointers acccording to the direction.

    if (dir and 1) == 0:
      inc ipc, 1 - (dir and 2)
    else:
      inc ipr, 1 - (dir and 2)

  #.................................................................................................

  # Interpreter loop.
  while ipr in 0..cs.high and ipc in 0..cs[ipr].high:
    case cs[ipr][ipc]
    of '>': inc dp
    of '<': dec dp
    of '+': inc ds[dp]
    of '-': dec ds[dp]
    of '.': stdout.write chr(ds[dp])
    of ',': ds[dp] = byte(stdin.readLine()[0])
    of '/': dir = not dir
    of '\\': dir = dir xor 1
    of '!': step()
    of '?': (if ds[dp] == 0: step())
    else: discard
    step()

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:
  snusp(5, Hw)
Output:
Hello World!

OCaml

See RCSNUSP/OCaml.

Perl

See RCSNUSP/Perl.

Phix

Translation of: Go
with javascript_semantics
integer id = 0, ipr = 1, ipc = 1
 
procedure step()
    if and_bits(id,1) == 0 then
        ipc += 1 - and_bits(id,2)
    else 
        ipr += 1 - and_bits(id,2)
    end if
end procedure
 
procedure snusp(integer dlen, string s)
    sequence ds = repeat(0,dlen)  -- data store
    integer dp = 1                -- data pointer
 
    -- remove leading '\n' from string if present
    s = trim_head(s,'\n')
 
    -- make 2 dimensional instruction store and set instruction pointers
    sequence cs = split(s,'\n')
    ipr = 1
    ipc = 1
 
    -- look for starting instruction
    for i=1 to length(cs) do
        ipc = find('$',cs[i])
        if ipc then
            ipr = i
            exit
        end if
    end for
 
    id = 0
 
    -- execute
    while ipr>=1 and ipr<=length(cs)
      and ipc>=1 and ipc<=length(cs[ipr]) do
        integer op = cs[ipr][ipc]
        switch op do
            case '>' : dp += 1
            case '<' : dp -= 1
            case '+' : ds[dp] += 1
            case '-' : ds[dp] -= 1
            case '.' : puts(1,ds[dp])
            case ',' : ds[dp] = getc(0)
            case '/' : id = not_bits(id)
            case '\\': id = xor_bits(id,1)
            case '!' : step()
            case '?' : if ds[dp]=0 then step() end if
        end switch
        step()
    end while
end procedure
 
constant hw = """
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/"""
 
snusp(5, hw)
Output:
Hello World!

PicoLisp

See RCSNUSP/PicoLisp.

Python

Translation of: Go
#!/usr/bin/env python3

HW = r'''
/++++!/===========?\>++.>+.+++++++..+++\
\+++\ | /+>+++++++>/ /++++++++++<<.++>./
$+++/ | \+++++++++>\ \+++++.>.+++.-----\
      \==-<<<<+>+++/ /=.>.+>.--------.-/'''

def snusp(store, code):
    ds = bytearray(store)  # data store
    dp = 0                 # data pointer
    cs = code.splitlines() # 2 dimensional code store
    ipr, ipc = 0, 0        # instruction pointers in row and column
    for r, row in enumerate(cs):
        try:
            ipc = row.index('$')
            ipr = r
            break
        except ValueError:
            pass
    rt, dn, lt, up = range(4)
    id = rt  # instruction direction.  starting direction is always rt
    def step():
        nonlocal ipr, ipc
        if id&1:
            ipr += 1 - (id&2)
        else:
            ipc += 1 - (id&2)
    while ipr >= 0 and ipr < len(cs) and ipc >= 0 and ipc < len(cs[ipr]):
        op = cs[ipr][ipc]
        if op == '>':
            dp += 1
        elif op == '<':
            dp -= 1
        elif op == '+':
            ds[dp] += 1
        elif op == '-':
            ds[dp] -= 1
        elif op == '.':
            print(chr(ds[dp]), end='')
        elif op == ',':
            ds[dp] = input()
        elif op == '/':
            id = ~id
        elif op == '\\':
            id ^= 1
        elif op == '!':
            step()
        elif op == '?':
            if not ds[dp]:
                step()
        step()

if __name__ == '__main__':
    snusp(5, HW)
Output:
Hello World!

Racket

See RCSNUSP/Racket.

Raku

(formerly Perl 6)

Works with: Rakudo version 2017.02

Implementation of modular SNUSP.

class SNUSP {

    has @!inst-pointer;
    has @!call-stack;
    has @!direction;
    has @!memory;
    has $!mem-pointer;

    method run ($code) {
        init();
        my @code = pad( |$code.lines );
        for @code.kv -> $r, @l {
           my $index = @l.grep( /'$'/, :k );
           if $index {
               @!inst-pointer = $r, $index;
               last
           }
        }

        loop {
            my $instruction = @code[@!inst-pointer[0]; @!inst-pointer[1]];
            given $instruction {
                when '>'  { $!mem-pointer++ }
                when '<'  { $!mem-pointer-- }
                when '+'  { @!memory[$!mem-pointer]++ }
                when '-'  { @!memory[$!mem-pointer]-- }
                when '.'  { print @!memory[$!mem-pointer].chr }
                when ','  { @!memory[$!mem-pointer] = $*IN.getc.ord }
                when '/'  { @!direction = @!direction.reverse «*» -1 }
                when '\\' { @!direction = @!direction.reverse }
                when '!'  { nexti() }
                when '?'  { nexti() unless @!memory[$!mem-pointer] }
                when '@'  { @!call-stack.push: @!inst-pointer.Array }
                when '#'  {
                    last unless +@!call-stack;
                    @!inst-pointer = |@!call-stack.pop;
                    nexti();
                }
            }
            nexti();
            last if @!inst-pointer[0] > +@code or
                    @!inst-pointer[1] > +@code[0];
        }

        sub init () {
            @!inst-pointer = 0, 0;
            @!direction    = 0, 1;
            $!mem-pointer  = 0;
            @!memory       = ()
        }

        sub nexti () { @!inst-pointer Z+= @!direction }

        sub pad ( *@lines ) {
            my $max = max @lines».chars;
            my @pad = @lines.map: $max - *.chars;
            map -> $i { flat @lines[$i].comb, ' ' xx @pad[$i] }, ^@lines;
        }
    }
}

# TESTING
my $hw = q:to/END/;
    /++++!/===========?\>++.>+.+++++++..+++\
    \+++\ | /+>+++++++>/ /++++++++++<<.++>./
    $+++/ | \+++++++++>\ \+++++.>.+++.-----\
          \==-<<<<+>+++/ /=.>.+>.--------.-/
    END

my $snusp = SNUSP.new;
$snusp.run($hw)
Output:
Hello World!

Ruby

See RCSNUSP/Ruby.

Seed7

The interpreter below implements Core SNUSP:

$ include "seed7_05.s7i";

const proc: snusp (in string: sourceCode, in integer: memSize, inout file: input, inout file: output) is func
  local
    var array string: instructions is 0 times "";
    var array char: memory is 0 times ' ';
    var integer: dataPointer is 1;
    var integer: instrPtrRow is 0;
    var integer: instrPtrColumn is 0;
    var integer: rowDir is 0;
    var integer: columnDir is 1;
    var integer: helpDir is 0;
    var integer: row is 0;
  begin
    instructions := split(sourceCode, "\n");
    memory := memSize times '\0;';

    for key row range instructions do
      if pos(instructions[row], '$') <> 0 then
        instrPtrRow := row;
        instrPtrColumn := pos(instructions[row], '$');
      end if;
    end for;

    while instrPtrRow >= 1 and instrPtrRow <= length(instructions) and
        instrPtrColumn >= 1 and instrPtrColumn <= length(instructions[instrPtrRow]) do
      case instructions[instrPtrRow][instrPtrColumn] of
        when {'>'}:  incr(dataPointer);
        when {'<'}:  decr(dataPointer);
        when {'+'}:  incr(memory[dataPointer]);
        when {'-'}:  decr(memory[dataPointer]);
        when {'.'}:  write(output, memory[dataPointer]);
        when {','}:  memory[dataPointer] := getc(input);
        when {'/'}:  helpDir := rowDir;
                     rowDir := -columnDir;
                     columnDir := -helpDir;
        when {'\\'}: helpDir := rowDir;
                     rowDir := columnDir;
                     columnDir := helpDir;
        when {'!'}:  instrPtrRow +:= rowDir;
                     instrPtrColumn +:= columnDir;
        when {'?'}:  if memory[dataPointer] = '\0;' then
                       instrPtrRow +:= rowDir;
                       instrPtrColumn +:= columnDir;
                     end if;
      end case;
      instrPtrRow +:= rowDir;
      instrPtrColumn +:= columnDir;
    end while;
  end func;

# SNUSP implementation of Hello World.
const string: helloWorld is "/++++!/===========?\\>++.>+.+++++++..+++\\\n\
                            \\\+++\\ | /+>+++++++>/ /++++++++++<<.++>./\n\
                            \$+++/ | \\+++++++++>\\ \\+++++.>.+++.-----\\\n\
                            \      \\==-<<<<+>+++/ /=.>.+>.--------.-/";

const proc: main is func
  begin
    snusp(helloWorld, 5, IN, OUT);
  end func;
Output:
Hello World!

Tcl

See RCSNUSP/Tcl.

Wren

Translation of: Go
import "io" for Stdin

// 'raw' is a multi-line string
var snusp = Fn.new { |dlen, raw|
    var ds = List.filled(dlen, 0) // data store
    var dp = 0 // data pointer

    // remove leading \n if it's there
    if (raw[0] == "\n") raw = raw[1..-1]

    // make 2 dimensional instruction store & declare instruction pointers
    var s = raw.split("\n")
    var ipr = 0
    var ipc = 0

    // look for starting instruction
    for (r in 0...s.count) {
        var row = s[r]
        var outer = false
        for (c in 0...row.count) {
            var i = row[c]
            if (i == "$") {
                ipr = r
                ipc = c
                outer = true
                break
            }
        }
        if (outer) break
    }

    var id = 0
    var step = Fn.new {
        if (id&1 == 0) {
            ipc = ipc + 1 - (id&2)
        } else {
            ipr = ipr + 1 - (id&2)
        }
    }

    // execute
    while (ipr >= 0 && ipr < s.count && ipc >= 0 && ipc < s[ipr].count) {
        var c = s[ipr][ipc]
        if (c == ">") {
            dp = dp + 1
        } else if (c == "<") {
            dp = dp - 1
        } else if (c == "+") {
            ds[dp] = ds[dp] + 1
        } else if (c == "-") {
            ds[dp] = ds[dp] - 1
        } else if (c == ".") {
            System.write(String.fromByte(ds[dp]))
        } else if (c == ",") {
            ds[dp] = Stdin.readByte()
        } else if (c == "/") {
            id = ~id
        } else if (c == "\\") {
            id = id ^ 1
        } else if (c == "!") {
            step.call()
        } else if (c == "?") {
            if (ds[dp] == 0) step.call()
        }
        step.call()
    }
}

var hw =
    "/++++!/===========?\\>++.>+.+++++++..+++\\\n" +
    "\\+++\\ | /+>+++++++>/ /++++++++++<<.++>./\n" +
    "$+++/ | \\+++++++++>\\ \\+++++.>.+++.-----\\\n" +
    "      \\==-<<<<+>+++/ /=.>.+>.--------.-/"
snusp.call(5, hw)
Output:
Hello World!