Rain

Rain is a two-dimensional fungeoidal esoteric programming language invented in 2017 by dok with the goal of being difficult to write while remaining possible to compile.
==Concepts==
Rain is a two-dimensional language, the code can be read in only two directions either up or down, left and right direction are used for function call.
It is a stack-based language, so all operations are performed on a stack. In addition to the arithmetic stack a function call stack can be used to store information as one value can be stored and retrieved per function context.
In a way each column can be considered as a method, thus libraries can be created and used with the necessity of doing the proper "placing and routing".
===Code execution===
Rain can be executed in two directions, from top to bottom or from bottom to top.
The main program start out at (0,0), i.e. the top-left of the file. It initially moves down.
Execution will not stop until a ; instruction is met (on the main program), or an error occurs.
Trying to execute a space instruction will raise an error, trying to execute an instruction outside of the program bounds will raise an error too.
In the following example, the Instruction Pointer moves downwards :, make a jump to the right >, upwards | and return ;to the main program and stops ;.
 : ;
 > |
 ; 
==Instructions==
Rain has the following commands:
Cmd 	Description
: 	NOP: do nothing.
0 – 9 	Push corresponding number onto the stack
Addition: Pop two values a and b, then push the result of a + b 	
Subtraction: Pop two values a and b, then push the result of b - a	
* 	Multiplication: Pop two values a and b, then push the result of a * b
/ 	Integer division: Pop two values a and b, then push the result of b / a, rounded down.
% 	Modulo: Pop two values a and b, then push the remainder of the integer division of b/a.
) 	Greater than: Pop two values a and b, then push 1 if b > a, otherwise 0.
( 	Lesser than: Pop two values a and b, then push 1 if b < a, otherwise 0.
= 	Equals: Pop two values a and b, then push 1 if y = x, otherwise 0.
 	Intercept: only for left and right jump, else act as a NOP.
> 	Jump right: from the current position to the right, find the first intercept symbol, invert the direction and continue on the next instruction after the found intercept.
< 	Jump left: from the current position to the left, find the first intercept symbol, invert the direction and continue on the next instruction after the found intercept.
] and [ 	Tunnel: A jump outside of a tunnel will pass through the tunnel ignoring all intercept in it.
! 	Trampoline: jump over the following instruction in the current direction.
? 	Conditional trampoline: pop one value off the stack. The next instruction is only executed if the popped value is 0.
^ 	Duplicate top stack value
\ 	Swap top stack values
_ 	Over: push on the stack the value under the top element of the stack.
~ 	Push on the stack the length of the stack.
` 	Pop (remove) top stack value and discard
. 	Store: Pop one value off the stack to the local variable.
, 	Fetch: Push the value of the local variable.
# 	Pop top of stack and output as integer
$ 	Pop top of stack and output as ASCII character
" or ' 	Toggle stringmode (push each character's ASCII value all the way up respectively to the next " or ')
& 	Get integer from user and push it
@ 	Get character from user and push it
; 	End function call, return to cell next to last call (restore previous direction)
==Examples==
===Hello, world!===
 : 
 : ; 
 : > |
 : " $
 : H $
 : e $
 : l $
 : l $
 : o $
 : , $
 : $
 : W $
 : o $
 : r $
 : l $
 : d $
 : ! $
 : " $
 : + ;
 : 5 
 : 5 
 : 0 
 > | 
 ;
===Cat program===
 : ;
 | <
 @ $
 ^ ;
 0 ?
 ) )
 ? 0
 ; ^
 $ @
 > |
 ;
===Factorial===
Asks for a number n, and outputs it's factorial. Assumes the input is a non-negative integer.
 & ;
 ^ > |
 ? 1 )
 ! ^ ?
 > | ;
 # : ^
 5 : 1
 5 : -
 + | <
 $ *
 ; ;
===Fibonacci sequence===
Asks for a number n, and outputs all the Fibonacci numbers up to fib(n), with n starting at 0 with fib(0) = 0 and fib(1) = 1.
 0 ; ; 
 1 > | < 
 & : . , 
 1 : : + 
 + : , _ 
 > | 0 > |
 5 ) \ ^
 5 ? . #
 + ; - "
 $ , 1 
 ; > | "
 ; $
 ;
===Ackermann function===
Ask for two numbers an return the result of the Ackermann function.
 & ; ;
 & > | < ;
 > | _ : +
 # : . : 1
 5 : , : `
 5 : ? : \
 + : > : |
 $ : , :
 ; : ? :
 : ; :
 1 ^ :
 - . :
 1 , :
 ` ? :
 | < :
 , :
 ? :
 ; :
 _ :
 \ :
 1 :
 - :
 > |
 \ :
 1 :
 - :
 \ :
 > |
 ;
===Pascal's Triangle===
Ask for a number n and print the Pascal's Triangle up to n row
 1 
 1 ;
 & ; ; > |
 | < , ; .
 ^ : > | ? ,
 0 : + . ~ #
 ) : 5 > | 8
 ? : 5 , : 4
 ! , . $ : *
 > 1 | ; : $
 1 > | < | <
 - . _ | ,
 ^ : + : ;
 0 : . :
 ) : ~ :
 ^ : 1 :
 ? : = :
 ; : ? :
 ? : > |
 ! : , 
 > | ; 
 ;
===print string===
This method can be used to print the stack as characters up to the first null value.
 ;
 call-> | <
 ^ :
 ? $
 ; :
 > |
 ; 
===stack reverse===
This method can be used to reverse the entire the stack.
 ; ;
 call-> | < ,
 . | <
 ~ : ?
 1 : =
 = : 1
 ? : ~
 > | .
 , : \
 > > |
 ; ;
which is the same as :
 public void reverse(Stack st) {
 int m = (int)st.Pop();
 if (st.Count != 1)
 reverse(st);
 Push(st , m);
 }
 public void Push(Stack st , int a) {
 int m = (int)st.Pop();
 if (st.Count != 0)
 Push(st , a);
 else
 st.Push(a);
 st.Push(m);
 }
==Implementations==
In Python3 by User:Quintopia
 import sys
 import functools
 
 class Rain:
 class IdentityDict(dict):
 def __init__(self,rain):
 self.rain = rain
 super(rain.IdentityDict,self).__init__()
 def __missing__(self,key):
 def default(stack):
 if rain.stringmode:
 return stack+[ord(key)]
 else:
 print("%c is not a valid command at (%d,%d)"%(rain.prog[rain.row][rain.col],rain.row,rain.col))
 sys.exit(1)
 return default
 def errors(self,func):
 def wrapped(stack):
 try:
 return func(stack)
 except IndexError:
 print("Tried to pop an empty stack at (%d,%d)"%(self.col,self.row))
 sys.exit(1)
 except ZeroDivisionError:
 print("Tried to divide by zero at (%d,%d)"%(self.col,self.row))
 sys.exit(1)
 return wrapped
 def stringwrapper(self,c,func):
 def wrapped(stack):
 if self.stringmode:
 return stack+[ord(c)]
 else:
 return func(stack)
 return wrapped
 def seek(self,dir):
 def wrapped(stack):
 try:
 self.callstack.append((self.row,self.col,self.dir))
 self.variables.append(0)
 tunnelinglevel = 0
 while not self.prog[self.row][self.col]=='|' or tunnelinglevel:
 self.col+=dir
 if self.prog[self.row][self.col]=='?]['[dir]:
 tunnelinglevel += 1
 elif self.prog[self.row][self.col]=='?[]'[dir]:
 tunnelinglevel -= 1
 self.dir*=-1
 return stack
 except IndexError:
 print("Tried to jump out of bounds at (%d,%d)"%(self.col,self.row))
 sys.exit(1)
 return wrapped
 def jump(self,stack):
 self.row+=self.dir
 return stack
 def conditionally(self,func):
 def wrapped(stack):
 if stack.pop():
 return func(stack)
 else:
 return stack
 return wrapped
 def store(self,stack):
 self.variables[-1]=stack.pop()
 return stack
 def output(self,type):
 def wrapped(stack):
 c = stack.pop()
 if type:
 sys.stdout.write(chr(c))
 else:
 sys.stdout.write(str(c))
 return stack
 return wrapped
 def toggle(self,delimiter):
 def wrapped(stack):
 if self.stringmode==delimiter:
 self.stringmode = False
 elif not self.stringmode:
 self.stringmode = delimiter
 else:
 return stack+ord("?'\""[delimiter])
 return stack
 return wrapped
 def inputs(self,type):
 def wrapped(stack):
 if type:
 if len(self.inputbuffer)==0:
 try:
 self.inputbuffer = list(map(ord,input()))+[10] #add the stripped newline back in
 except KeyboardInterrupt:
 sys.exit(0)
 except EOFError:
 return stack+[-1]
 return stack + [self.inputbuffer.pop(0)]
 else:
 try:
 return stack + [eval(input())]
 except (NameError,SyntaxError,EOFError):
 return stack+[-1]
 return wrapped
 def comeback(self,stack):
 if len(self.callstack):
 (self.row,self.col,self.dir)=self.callstack.pop()
 self.variables.pop()
 return stack
 else:
 sys.exit(0)
 def __init__(self,prog):
 self.prog = prog
 self.variables = [0]
 self.stack = []
 self.callstack = []
 self.row=0
 self.col=0
 self.dir=1
 self.stringmode=False
 self.inputbuffer=[]
 self.commands = self.IdentityDict(self)
 for c in range(10):
 self.commands[chr(c+48)]=functools.partial(lambda k,x:x + [k],c)
 self.commands[':']=self.commands['|']=lambda x: x
 self.commands['+']=self.errors(lambda x: x[:-2]+[x[-2]+x[-1]])
 self.commands['-']=self.errors(lambda x: x[:-2]+[x[-2]-x[-1]])
 self.commands['*']=self.errors(lambda x: x[:-2]+[x[-2]*x[-1]])
 self.commands['/']=self.errors(lambda x: x[:-2]+[x[-2]/x[-1]])
 self.commands['%']=self.errors(lambda x: x[:-2]+[x[-2]%x[-1]])
 self.commands[')']=self.errors(lambda x: x[:-2]+[int(x[-2]>x[-1])])
 self.commands['(']=self.errors(lambda x: x[:-2]+[int(x[-2]<x[-1])])
 self.commands['=']=self.errors(lambda x: x[:-2]+[int(x[-2]==x[-1])])
 self.commands['>']=self.seek(1)
 self.commands['<']=self.seek(-1)
 self.commands['!']=self.jump
 self.commands['?']=self.conditionally(self.jump)
 self.commands['^']=self.errors(lambda x:x+[x[-1]])
 self.commands['\\']=self.errors(lambda x:x[:-2]+[x[-1]]+[x[-2]])
 self.commands['_']=self.errors(lambda x:x+[x[-2]])
 self.commands['~']=lambda x:x+[len(x)]
 self.commands['`']=self.errors(lambda x:x[:-1])
 self.commands['.']=self.errors(self.store)
 self.commands[',']=lambda x:x+[self.variables[-1]]
 self.commands['#']=self.errors(self.output(0))
 self.commands['$']=self.errors(self.output(1))
 self.commands['"']=self.toggle(2)
 self.commands["'"]=self.toggle(1)
 self.commands['&']=self.inputs(0)
 self.commands['@']=self.inputs(1)
 self.commands[';']=self.comeback
 for c,f in list(self.commands.items()):
 if c not in '"\:
 self.commands[c]=self.stringwrapper(c,f)
 def run(self):
 while True:
 try:
 self.stack = self.commands[self.prog[self.row][self.col]](self.stack)
 #print "(%d,%d,%c)"%(self.row,self.col,self.prog[self.row][self.col])
 #print self.stack
 except IndexError:
 print("Try to execute out of bounds at (%d,%d)"%(self.col,self.row))
 sys.exit(1)
 self.row+=self.dir
 if __name__=="__main__":
 if len(sys.argv)<2:
 print("Please provide a program filename to execute.")
 sys.exit(1)
 try:
 with open(sys.argv[1],'r') as f:
 prog = [line for line in f]
 except IOError:
 print("File not found or unreadable: %s"%(sys.argv[1]))
 sys.exit(1)
 rain = Rain(prog)
 rain.run()
Category:Two-dimensional languages
Category:Languages
Category:Implemented
Category:Turing complete
Category: 2017