Execute SNUSP/D: Difference between revisions

From Rosetta Code
Content added Content deleted
m (added tags)
m (Grammar)
Line 1: Line 1:
{{implementation|SNUSP}}{{collection|RCSNUSP}}[[Category:D]]
{{implementation|SNUSP}}{{collection|RCSNUSP}}[[Category:D]]
This implementation includes all '''Bloated SNUSP''' commands, plus a minor custom feature : ''debugInput''. ''debugInput'' is a pattern to setup a pre-defined string for (I/O) input. That is, just before reading I/O input operation, if this string is not empty yet, a char is ''pop/taken'' from the string, instaed of actually read from I/O. The pattern is '''debug['''<''string''>''']debug'''. Similar to the staring point '$', this pattern is searched inside the source code before execution, but from bottom lines first.<br> <br>
This implementation includes all '''Bloated SNUSP''' commands, plus a minor custom feature : ''debugInput''. ''debugInput'' is a pattern to setup a predefined string for (I/O) input. That is, just before reading I/O input operation, if this string is not empty yet, a char is ''popped/taken'' from the string, instead of actually reading from I/O. The pattern is '''debug['''<''string''>''']debug'''. Similar to the starting point '$', this pattern is searched inside the source code before execution, but from bottom lines first.<br> <br>
The following are some notes.
The following are some notes.


*'''Memory'''
*'''Memory'''
**The memory space is represented by a class ''Mem''. Since Bloated SNUSP allows memory pointer to move up and down, the memory space is a 2-dimensional space;
**The memory space is represented by a class ''Mem''. Since Bloated SNUSP allows memory pointer to move up and down, the memory space is a 2-dimensional space;
**Mem is internally represented as an associative array, with ''cfloat'' as key and ''int'' as value. Externally, the ''keys'' is a integer 2-dimensional coordinates tuple (x,y), which acts as a memory pointer. The tuple will convert to cfloat before accessing the associative array. The conversion is unique as long as the magnitude of x and y is less than 0x7fffff. The type of the memory cells is ''int'', it will be only interpreted as ''char''/''string'' during IO operation;
**Mem is internally represented as an associative array, with ''cfloat'' as key and ''int'' as value. Externally, the ''keys'' are integer 2-dimensional coordinates tuple (x,y), which act as a memory pointer. The tuple will convert to cfloat before accessing the associative array. The conversion is unique as long as the magnitude of x and y is less than 0x7fffff. The type of the memory cells is ''int'', it will be only interpreted as ''char/string'' during IO operation;
*'''Code Pointer'''
*'''Code Pointer'''
**This again is in a 2-dimensional space. The Code pointer CP structure contain the pointer position and moving direction. It also includes function to get the ''command code'' in the SNUSP program source under the pointer position, and to manipulate its own position and direction;
**This again is in a 2-dimensional space. The code pointer CP structure contains the pointer position and moving direction. It also includes functions to get the ''command code'' in the SNUSP program source under the pointer position, and to manipulate its own position and direction;
**If CP go out of the code space, no error is threw. In this implementation, such situation is treated as a sub-routine return (#);
**If CP goes out of the code space, no error is thrown. In this implementation, such a situation is treated as a sub-routine return (#);
*'''Core/Thread/Execution Unit''''
*'''Core/Thread/Execution Unit''''
**This class is which actually execute the command. In fact, ''execute'' is the only non-trivial function (beside the ''fwd'' short hand ) inside this class. Each Core owns a code pointer, a memory pointer (actually 2 int) and a cp call stack, and shared with the same ''CPU''. Via the CPU, each Core can accessed the common resources like IO, Memory Space and Code Space(program) ;
**This class is what actually executes the command. In fact, ''execute'' is the only non-trivial function (besides the ''fwd'' short hand ) inside this class. Each Core owns a code pointer, a memory pointer (actually 2 int) and a cp call stack, and shares the same ''CPU''. Via the CPU, each Core can access common resources like IO, Memory Space and Code Space(program) ;
**Since this implementation supported ''Bloated'' feature, aka thread execution, this class is a result of separating execution unit from ''CPU''.
**Since this implementation supports ''Bloated'' features, aka thread execution, this class is a result of separating execution unit from ''CPU''.
*'''CPU'''
*'''CPU'''
**CPU acts as a resource manager and thread execution scheduler(trivially).
**CPU acts as a resource manager and thread execution scheduler(trivially).
Line 152: Line 152:
private Core[] newcores ;
private Core[] newcores ;


static int rnd(int b) { // return a random number between and include 0 and b
static int rnd(int b) { // return a random number between and including 0 and b
int a = 0 ;
int a = 0 ;
if(a > b) { a = b ; b = 0 ; }
if(a > b) { a = b ; b = 0 ; }

Revision as of 23:25, 7 May 2008

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.

This implementation includes all Bloated SNUSP commands, plus a minor custom feature : debugInput. debugInput is a pattern to setup a predefined string for (I/O) input. That is, just before reading I/O input operation, if this string is not empty yet, a char is popped/taken from the string, instead of actually reading from I/O. The pattern is debug[<string>]debug. Similar to the starting point '$', this pattern is searched inside the source code before execution, but from bottom lines first.

The following are some notes.

  • Memory
    • The memory space is represented by a class Mem. Since Bloated SNUSP allows memory pointer to move up and down, the memory space is a 2-dimensional space;
    • Mem is internally represented as an associative array, with cfloat as key and int as value. Externally, the keys are integer 2-dimensional coordinates tuple (x,y), which act as a memory pointer. The tuple will convert to cfloat before accessing the associative array. The conversion is unique as long as the magnitude of x and y is less than 0x7fffff. The type of the memory cells is int, it will be only interpreted as char/string during IO operation;
  • Code Pointer
    • This again is in a 2-dimensional space. The code pointer CP structure contains the pointer position and moving direction. It also includes functions to get the command code in the SNUSP program source under the pointer position, and to manipulate its own position and direction;
    • If CP goes out of the code space, no error is thrown. In this implementation, such a situation is treated as a sub-routine return (#);
  • Core/Thread/Execution Unit'
    • This class is what actually executes the command. In fact, execute is the only non-trivial function (besides the fwd short hand ) inside this class. Each Core owns a code pointer, a memory pointer (actually 2 int) and a cp call stack, and shares the same CPU. Via the CPU, each Core can access common resources like IO, Memory Space and Code Space(program) ;
    • Since this implementation supports Bloated features, aka thread execution, this class is a result of separating execution unit from CPU.
  • CPU
    • CPU acts as a resource manager and thread execution scheduler(trivially).
  • IO
    • This may be the simplest class.


Note :This implementation should run fine in both D1 & D2. <d>module rcsnusp ; import std.stdio, std.c.stdio, std.string, std.random, std.file ;

// array stack template T[] push(T)(inout T[] stk, T value) { stk ~= value ; return stk ; } T pop(T)(inout T[] stk, bool discard = true)

 { T top = stk[$-1] ; if(discard) stk.length = stk.length - 1 ; return top ; }

bool empty(T)(T[] stk) { return stk.length <= 0 ; }

// quick element remove for non-ordered array void rem(T)(inout T[] stk, int i) {

 if(i + 1 < stk.length) stk[i] = stk[$-1] ;
 stk.length = stk.length - 1 ;

}

class Mem { // Memory

 private int[cfloat] cells ;
 static const int MASK = 0xffffff ;
 static cfloat i2cf(int x, int y) { return (x & MASK) + (y & MASK)*1fi ; }
 int opIndex(int x, int y) {
   auto key = i2cf(x,y) ; int res ;
   if(key in cells) res = cells[key] ; else cells[key] = res = 0 ; 
   return res ;
 }
 void opIndexAssign(int value, int x, int y) { cells[i2cf(x,y)] = value ; }
 void reset() { foreach(k, v ; cells) cells[k] = 0 ; }

}

struct CP { // Codes Pointer

 int x, y, dx, dy ;
 static CP opCall(int h, int v, int hd, int vd) 
   { CP c ; with(c) { x = h ; y = v ; dx = hd ; dy = vd ; } ; return c ; } 
 static CP opCall(string[] codes) {  
   int v, h = -1 /* _find_ return -1 if not found */;
   for(v = 0 ; v < codes.length ; v++) // find location of 1st '$'
     if((h = find(codes[v], '$')) != -1) break ;
   if (h == -1) { h = 0 ; v = 0 ; }    // default 1st char of 1st line
   else h++ ; // advanced pass the '$' char found
   return CP(h, v, 1, 0) ; // default direction is (1,0)     
 }
 CP dup() { return CP(x,y,dx,dy) ; }
 bool hasCode(string[] codes) 
   { return !(y < 0 || x < 0 || y >= codes.length || x >= codes[y].length) ; }
 string getCode(string[] codes) { return hasCode(codes) ? codes[y][x..x+1] : null ; }
 void fwd(int step = 1) { x += dx*step ; y += dy*step ; }
 void turn(string t) {
   if(t == "\\" || t == "/") {
     int sign = t == "/" ? -1 : 1 ;
     int tx = dy*sign , ty = dx*sign ; 
     dx = tx ; dy = ty ;
   }
 }

}

class Core { // this is _thread_ :)

 bool quit = false ;
 
 this(CPU c, CP p, int xm, int ym) 
   { cpu = c ; cp = p ; mx = xm ; my = ym ; }
 void fwd(int step = 1) { cp.fwd(step) ; } // short hand
 void execute() { 
   if(quit) throw new Exception("Execute a dead core") ;
   string code = cp.getCode(cpu.prog) ; 
   if(code is null) code = "#" ; // treat as _return_ if out of code space
   Mem m = cpu.mem ;
   switch(code) {
     case "<" : mx-- ; fwd ; break ;
     case ">" : mx++ ; fwd ; break ;
     case ":" : my-- ; fwd ; break ;
     case ";" : my++ ; fwd ; break ;
     case "+" : m[mx,my] = m[mx,my] + 1 ; fwd ; break ;
     case "-" : m[mx,my] = m[mx,my] - 1 ; fwd ; break ;
     case "," : 
       string ss = cpu.io.get() ;
       if (ss.length > 0) { 
         m[mx,my] = cast(int) ss[0] ;
         fwd ;
       } // else do nothing and not forward cp, simulate waiting input 
       break ;
     case ".": cpu.io.put(m[mx,my]) ; fwd ; break ;
     case "!": fwd(2) ; break ; // skip one step == forward x 2
     case "?": fwd ; if(m[mx,my] == 0) fwd ; break ;       
     case "%": m[mx,my] = cpu.rnd(m[mx,my]) ; fwd ; break ;
     case "&": // split core/thread 
       fwd ;
       // will run following new core in next turn, with cp, mx, my dulplicated
       cpu.addCore(new Core(cpu, cp.dup, mx, my)) ; 
       fwd ; // old core skip one more step
       break ;
     case "@": stack.push(cp.dup) ; fwd ; break ;
     case "#": 
       if(stack.empty()) quit = true ;       // ready to quit
       else { cp = stack.pop() ; fwd(2) ; }  // return from subroutine
       break ;
     case "\\","/": cp.turn(code) ; fwd ; break ;
     default: fwd ;
   }
 }
 private :
   CPU cpu ;
   CP[] stack ; // call stack ;
   CP cp ;      // current code pointer
   int mx, my ; // memory pointer 

}

class IO {

 private char[] debugInput ;
 string get() { 
   if(debugInput.empty())
     return kbhit() ? [cast(char)getch()] : null ;
   return [debugInput.pop()] ;
 }
 void put(int c) { printf("%c", cast(char)c) ; }
 void getDebugInput(string[] codes) {
   for(int i = codes.length - 1 ; i >= 0 ; i--) {
     int left = std.string.find(codes[i], "debug[") ;
     int right = std.string.rfind(codes[i],"]debug") ;      
     if(right > left + 6) {        
       (debugInput = codes[i][left+6..right].dup).reverse ;
       break ;
     }
   }     
 }

}

class CPU {

 private string[] program ;
 private Mem memory ;
 private IO inOut ;
 private Core[] cores ;  
 private Core[] newcores ;   
 static int rnd(int b) { // return a random number between and including 0 and b
   int a = 0 ;
   if(a > b) { a = b ; b = 0 ; }
   return a + cast(int) (rand() % (b - a + 1)) ;
 } 
 static string[] normalize(string[] codes) {
   string[] c = splitlines(join(codes,"\n")) ;
   int lenmax = 0 ;
   foreach(l; c) if (l.length > lenmax) lenmax = l.length ;
   foreach(inout l ; c) l ~= repeat(" ", lenmax - l.length) ;
   return c ;
 }
 
 this() { memory = new Mem ; inOut = new IO ; inOut.getDebugInput(program) ; }
 this(string codes) {program = normalize(splitlines(codes)) ; this() ; }
 this(string[] codes) { program = normalize(codes) ; this() ; }
 string[] prog() { return program ; }  
 Mem mem() { return memory ; }
 IO io() { return inOut ; }
 
 void addCore(Core c) { newcores ~= c ; }
 void run() {
   if(program is null) throw new Exception("No program") ;
   cores = [new Core(this, CP(program), 0, 0)] ; // first core
   while(cores.length + newcores.length > 0) { // execute one command for each active core
     cores ~= newcores ; newcores = null ; // join and clear newcores 
     // reverse order so _rem_ will not affect those core not yet executed
     for(int i = cores.length - 1 ; i >= 0 ; i--) 
       if(cores[i].quit == true) cores.rem(i) ; else cores[i].execute ;
   }
   memory.reset ; 
 }

}

void main(string[] args) {

 if(args.length <= 1) { // from stdin
   string[] p ;
   string b ;
   while((b = readln()) != null) p ~= chomp(b)  ;      
   (new CPU(p)).run ;
 } else {               // from file names 
   foreach(f ; args[1..$])
     try {
       (new CPU(cast(string) std.file.read(f))).run ;  
     } 
     catch(Exception e) writefln(e.msg) ;
 }

}</d>