⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

{{implementation|SNUSP}}{{collection|RCSNUSP}}

This [[D]] implementation supports commands from all the three SNUSP variants, as described on the [[eso:SNUSP|Esolang SNUSP page]], plus an extended mode, '''SUPERNATURAL'''.

'''SUPERNATURAL Mode''': '''~ : code input''' (materialization): #Read the char in current code pointer as input, assign it to memory currently pointed to by memory pointer. ''' : join and wait tasks''' (telepathy): #A task has a state property of free/join/wait; #A task is by default initialized as free-state; #The splited new thread inherites free/join/wait-state from the old thread; #When a free-state task first executes this '''''' command, the task enters into a join-state; #Then if a join-state task executes another '''*''' command, the task enters into a wait-state; #A wait-state task stops its execution, and waits until all alive join-state tasks are turning into a wait-state, which then all these wait-state tasks are return to free-state; #This command enables global synchronization of the tasks. #Difference to original specification: ::The original specification does not require threads execution in a specific order. For this command to be useful, the order of execution of tasks(threads) becomes important; ::In this implementation, the order of execution is first-created-first-executed; ::The original specification specifies that ''(&)SPLIT''-ed old thread skips the immediate code (see below a->b->c->d example), which may lead to anti-intuition codes (which is good for an esoteric language :). This implementation retain old-thread-skips-immediate-code behavior in ''BLOATED'' mode, but new-thread-skips-immediate-code in ''SUPERNATURAL'' mode ( A->B->C->D example). 1->2->...->8 is thread creation order in ''SUPERNATURAL'' mode. :::$&==&:&:&:=
; ~ ~ ~ ~ &=> new(B) ; 2A 3B 4C 5D => old(A) ; =!=!=!===
..=.# ; /=!/=!/=!/===
.=.=.# ; 1b 6c 7d 8a &=> old(a) ; ~ ~ ~ ~ => new(b) ==&/;&/;&/;=/
*'''^ : warp''' (teleport): #The code pointer will bounce back to the code space boundary in its reverse direction; #then forward and stop after the first '''^''' it encounter in normal direction. #Example : :::==a^A<=cp==B^==<=C^== ::when the code pointer cp heading into A's^, next turn, the code pointer will be in C. if no such ^ in the reverse direction, the code pointer will be in ''a'' next turn.

module snud ;
private import std.string, std.random ;

// io interface, which has to be defined in another module
interface IIO {
  void setDebugInput(string s) ;
  void output(int v) ;
  bool inputReady() ;
  int input() ;
}

int rnd(int b)  { return b < 0 ? (-rand()) % (-b + 1) : rand() % (b + 1) ; }
// simple stack template
void push(T)(inout T[] stk, T value) { stk ~= value ; }
T pop(T)(inout T[] stk, bool discard = true) {
  T top = stk[$-1] ;
  if(discard) stk.length = stk.length - 1 ;
  return top ;
}

// a 2x tuple type
struct X(U,V = U) {
    U x ; V y ;
    void to(ref U a, ref V b) { a = x ; b = y ; }
    void from(U a, V b) { x = a ; y = b ; }
}
alias X!(int) I2 ; // intxint, used as code pointer, memory pointer & direction
alias X!(I2)  ST ; // (intxint)x(intxint), used as cpu state [cp,dp]

enum Mode : uint {CORE = 1, MODULAR, BLOATED, SUPERNATURAL } ;

// used to locate '$' and debugInput
string pfind(I2* loc, string[] c, string sl, string sr = null) {
  int rx, dir =  1 , start = 0 , end = c.length - 1 ;
  if(sr){ dir = -1 ; start = c.length - 1 ; end = 0 ; }
  with(*loc)
    for(x = -1, y = start ; y*dir <= end*dir ; y += dir)
      if((x = std.string.find(c[y], sl)) >= 0) {
        if(sr && (rx = std.string.rfind(c[y], sr)) >= 0)
          if(rx > x + sl.length)
            return c[y][x + sl.length..rx] ;
        break ;
      }
  return null ;
}

// a 2d memory space
final class Memory {
  private int[I2] cells ;
  void reset() { foreach(k, v ; cells) cells[k] = 0 ; }
  void opIndexAssign(int value, I2 key) { cells[key] = value ; }
  int opIndex(I2 key) {
    int* vp = key in cells ;  // get value pointer of the key, null if no such key
    if(vp is null) { cells[key] = 0 ; return 0 ; } // initialize the value/key pair
    return *vp ;              // return the already existed value
  }
}

final class CPU {
  final class Task {
    enum {FREE, JOINED, WAITING }
    const int id ;
    I2 cp, dp, mp ;
    int join = FREE ;
    bool quit = false ;
    private ST[] stack ;
    private char curCode ;

    this(I2 Cp, I2 Dp, I2 Mp, int joinstate = FREE) { 
      cp = Cp ; dp = Dp ; mp = Mp ; id = Id++ ; 
      if((join = joinstate) == JOINED) joinwait++ ;
    }

    private void fwd(int step = 1) { with(cp) from(x + dp.x*step, y + dp.y*step) ; }
    private void rfx(int dir) { with(dp) from(dir*y, dir*x) ; }
    // _outer_ is D keyword for an inner class to ref outer class
    private bool hasCode() { return this.outer.hasCode(cp) ; }
    char getCode() { return this.outer.getCode(cp) ; }
    Task execute() {
      curCode = getCode ;
      if(curCode in acceptCmd) // this control which SNUSP variants is used
      switch(curCode) {
        case '<' : mp.x-- ; break ;
        case '>' : mp.x++ ; break ;
        case ':' : mp.y-- ; break ;
        case ';' : mp.y++ ; break ;
        case '+' : m[mp] = m[mp] + 1 ; break ;
        case '-' : m[mp] = m[mp] - 1 ; break ;
        case ',' :
          if(!io.inputReady()) goto RET ; // wait input
          else m[mp] = io.input() ; break ;
        case '.' : io.output(m[mp]) ; break ;
        case '!' : fwd ; break ;
        case '?' : if(m[mp] == 0) fwd ; break ;
        case '%' : m[mp] = rnd(m[mp]) ; break ;
        case '\\': rfx( 1) ; break ;
        case '/' : rfx(-1) ; break ;
        case '@' : stack.push(ST(cp, dp)) ; break ; // save caller state
        case '#' :
          if(stack.length > 0) // return from subroutine
            { stack.pop().to(cp,dp) ; fwd ; break ; }
        case '\0':             // else process as \0, = quit
          quit = true ; goto RET ;
        case '&' :
          if(tasksMode == Mode.BLOATED) // old task skip immediate code
            { fwd    ; queued ~= new Task(cp, dp, mp, join) ; break ; }
          else                            // new task skip immediate code
            { fwd(2) ; queued ~= new Task(cp, dp, mp, join) ; fwd(-2) ; break ; }
        case '~' : fwd ; m[mp] = getCode ; break ; // read next code as input
        case '*' : // join/wait threads
          switch(join) {
            case FREE    : join = JOINED ;  joinwait++ ; break ;    // schedule to join
            case JOINED  : join = WAITING ; joinwait-- ; goto RET ; // start waiting join
            case WAITING :
              if(joinwait <= 0) { join = FREE ; break ; } // all joined, release all
              else goto RET ; // keep waiting
          }
          break ;
        case '^' : // wrap to the boundary in reverse direction then stop after the 1st '^'
          while(hasCode) fwd(-1) ; while(getCode != '^') fwd ; break ;
        default: //other char is a error command, since it should be seived out
          debug throw new Exception("unknown command") ;
      }
      fwd ;      // next code
  RET:           // directly go here, if waiting input/join, or quit
      lastcp = cp ; lastmp = mp ;
      if(quit && join == JOINED) joinwait-- ;
      return this ;
    }
  }

  this(IIO inputoutput) { m = new Memory ; io = inputoutput ; }

  bool hasCode(I2 codePtr)
    { with(codePtr) return !(x < 0 || y < 0 || x >= width || y  >= lines) ; }
  char getCode(I2 codePtr)
    { with(codePtr) return hasCode(codePtr) ? src[y][x] : '\0' ; }
  string program() { if(prog is null) prog = join(src,"\n") ; return prog ; }
  CPU load(string codes) {
    src = splitlines(codes) ; width = 0 ; lines = src.length ; prog = null ;
    foreach(k,l; src)
      { if ((src[k] = stripr(tr(l,"\0"," "))).length > width) width = src[k].length ; }
    foreach(k,l; src) src[k] = l ~ repeat(" ", width - l.length) ;
    debugInput = pfind(&start, src, "debug[", "]debug") ;
    pfind(&start, src, "$") ;
    if(start.x < 0) start = I2(0,0) ; else start.x++ ;
    return this ;
  }
  CPU initialize(Mode mode = Mode.SUPERNATURAL, bool useDebugInput = true) {
    if(useDebugInput) io.setDebugInput(debugInput) ; else io.setDebugInput("") ;
    tasks = [ new Task(start, I2(1,0), I2(0,0)) ] ;    // 1st task
    tick = 0 ; Id = 0 ; joinwait = 0 ; nTaskLeft = 1 ;
    queued = null ; m.reset() ; tasksMode = mode ; acceptCmd = null ;
    foreach(c ; join(command[0..mode],"")) acceptCmd[c] = c ;
    return this ;
  }
  int run(string codes,Mode mode = Mode.SUPERNATURAL, bool useDebugInput = true)
    { return load(codes).initialize(mode, useDebugInput).run() ; }
  int run() { while(nTaskLeft) run1Tick ; return m[lastmp] ; }
  bool run1Tick() {
    if(nTaskLeft > 0) {
      nTaskLeft = 0 ; tick++ ;
      foreach(tsk ; tasks) // execute & update task
        if((tasks[nTaskLeft] = tsk.execute).quit == false)
          nTaskLeft++ ;
      tasks.length = nTaskLeft ;
      if(queued) { tasks ~= queued ; queued = null ; } // join queued tasks
      nTaskLeft = tasks.length ;
    }
    return nTaskLeft > 0 ;
  }

  static const string[] command = ["<>+-,.!?/\\\0","@#",":;&%","~*^"] ;

  Memory m ;
  IIO io ;

  string prog, debugInput ;
  Mode tasksMode ;
  Task[] tasks, queued ;
  I2 start, lastcp, lastmp ;
  uint width, lines, tick, joinwait, nTaskLeft ;
  private string[] src ;
  private char[char] acceptCmd ;
  private uint Id = 0 ;
}

Sample SNUSP using a console io :

module rcsnusp ;
import snud ;
import std.stdio, std.file, std.conv ;

extern(C) {
  int kbhit() ;
  int getch() ;
  int printf(in char*,...);
}

final class CIO : IIO {
  private string debugInput ;
  void setDebugInput(string s) { debugInput = cast(string)s.dup.reverse ; }
  void output(int v){ printf("%1c", cast(char) v) ; }
  bool inputReady() { return debugInput.length || kbhit() ; }
  int input()
    { return cast(int)(debugInput.length ? debugInput.pop() : getch()) ; }
}

int main(string[] args) {
  CPU cpu = new CPU(new CIO) ;

  int result ;
  Mode mode = Mode.SUPERNATURAL ;
  bool useDebug = true ;
  if(args.length <= 1) { // from stdin
    string[] p ;
    string b ;
    while((b = readln()) != null) p ~= std.string.chomp(b)  ;
    cpu.load(std.string.join(p,"\n")).initialize(mode,useDebug) ;
    for(int i = 0 ; i < 10 ; i++) { // do some debug action
      cpu.run1Tick ;
      with (cpu.tasks[0])
      writefln( // debug state output 
        "m(%3d,%3d)=[%4d][%1s] c(%3d,%3d,%3d,%3d)=[%1s](%3d) id<%3d> jc(%1d) quit[%3s]", 
        mp.x, mp.y, cpu.m[mp], cast(char) (cpu.m[mp] >= 32 && cpu.m[mp]<128 ? cpu.m[mp] : '?'), 
        cp.x, cp.y, dp.x, dp.y, getCode, getCode, id, join, quit ? "YES" : "NO") ;
    }
    result = cpu.run(std.string.join(p,"\n"), mode, useDebug) ;
  } else {               // from file names
    if(args.length > 2 && std.string.isNumeric(args[2])) 
      mode = cast(Mode) toInt(args[2]) ;
    if(mode < Mode.CORE || mode > Mode.SUPERNATURAL) 
      mode = Mode.SUPERNATURAL ;
    if(args.length > 3) 
      useDebug = std.string.tolower(args[3]) == "no" ? false : true ;
    result = cpu.run(cast(string) std.file.read(args[1]), mode, useDebug) ;
  }
  writefln("\ntick:%d", cpu.tick) ;

  return result ;
}