Execute SNUSP/D

Revision as of 09:01, 4 June 2008 by rosettacode>Badmadevil (changed, _join_ state of new thread thread should be inherited from original thread)

This implementation supports commands from all the three SNUSP variants, as described on the Esolang SNUSP page, plus an extended mode, SUPERNATURAL.

Execute SNUSP/D is an implementation of SNUSP. Other implementations of SNUSP.
Execute SNUSP/D is part of RCSNUSP. You may find other members of RCSNUSP at Category:RCSNUSP.

SUPERNATURAL Mode:

  • ~ : code input (materialization):
  1. Read the char in current code pointer as input, assign it to memory currently pointed to by memory pointer.
  • * : join and wait tasks (telepathy):
  1. A task is initialized as free-state;
  2. When a free-state task first executes this * command, the task enters into a join-state;
  3. Then if a join-state task executes another * command, the task enters into a wait-state;
  4. A wait-state task stops its execution, and waits until all alive tasks are in a wait-state, which then all tasks are return to free-state;
  5. This command enables global synchronization of the tasks.
  6. 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)
   \==&/;&/;&/;=/  
  • The splited new thread inherites free/join/wait-state from the old thread.
  • ^ : wrap (teleport):
  1. The code pointer will bounce back to the code space boundary in its reverse direction;
  2. then forward and stop after the first ^ it encounter in normal direction.

<d>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 ;

}</d> Sample SNUSP using a console io : <d>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 ;

}</d>