TAPEX

~by micnap929b
== About ==
TAPEX is an esoteric programming language heavily inspired by brainfuck (knowledge about it is advisable to better understand this article).
TAPEX, same as brainfuck, stores data on an infinite tape rather than in single variables.
Despite these similarities, TAPEX programs can look much different compared to ones written in brainfuck.
== Learn by examples! ==
 1  # I'm an inline comment. I come after a hash so I'm ignored by the interpreter.
 2
 3  /*
 4  I'm block comment.
 5  I can span multiple lines.
 6  See how to open and close me.
 7  */
 8
 9  ! # This command outputs each cell.
10    # Output: [0,0,0,0,0,...]. An array of 16 zeroes - this is what do you start with.
11  + # This command increments the current cell (so the first zero in our case).
12    # Now we have: [1,0,0,0,0,...].
13  - # This is how decrementation looks.
14    # Now we have [0,0,0,0,0,...] again.
15  = # This is how to reset a cell to zero.
16    # In our case nothing changed: [0,0,0,0,0,...].
17  > # This command moves the pointer to the next cell. Now we can operate on the second zero.
18  < # But let's go back to the first one by moving the pointer to the previous cell.
19  : # This command moves the pointer to cell #0 regardless of where it is. We could have used it instead of <.
20  . # This command outputs the current cell.
21    # Output: 0.
22
23  +5 # You can specify by how much do you want to increment.
24  -3 # Same with decrementation.
25  >2 # You can also specify by how many cells do you want to move the pointer...
26  :7 # ...or which cell should it move to.
27  =4 # While setting a cell to a given value...
28  4  # ... you can omit the equality symbol.
29  *3 # Multiply by 3.
30  /4 # Divide by 4
31
32  >+ # Increment the next cell. Equivalent executing >, +, and <
33  <+ # Increment the previous cell.
34  <- # Decrement the previous cell.
35  >. # Output the next cell.
36  @+ # Increment all cells.
37
38  >+5 # Increment next cell by 5.
39  >*2 # Double next cell.
40  </3 # Divide previous cell by 3.
41  <=3 # While setting a cell to a given value in such type of commands, you mustn't omit the equality symbol.
42  <3  # Otherwise your command can be interpreted in a different way. This moves the pointer 3 cells to the left.
43  @+2 # Increment all cells by 2
44
45  [
46  ...  # Commands between square brackets will be executed while the current cell in not 0.
47  ]
48
49  (
50  ...  # Commands between parentheses will be executed if the current cell in not 0.
51  )
== Future updates ==
=== What will be fixed ===
* Currently multi-digit numbers throw errors. I must fix it in the beta version.
* Currently block comments' opening and closing lines mustn't contain anything else than /* or */, and spaces. In the future that will probably change.
=== What will be added ===
* Exponentation, roots, and binary/logical expressions are planned to be added in v.Alpha.0.1
== References ==
# I'll post a JS interpreter, here or on GitHub, soon. Ok here it is:
	document.querySelector('button').onclick=()=>{
		console.clear();
		let a=document.querySelector('textarea').value;
		a=a.replace(/\/\*[^]*?\*\//g,'\n');
		a=a.split('\n');
		a=a.map(e=>e.replace(/\s/g,).replace(/#.*$/,));
		for(let i=0; i<a.length; ++i){
			if(a[i]=='('){
				a[i]={cmd:[],type:'if',cond:'!==0'};
				while(a[i+1]!=')'){
					a[i].cmd.push(a.splice(i+1,1)[0]);
				}
				a.splice(i+1,1);
			}else if(a[i]=='['){
				a[i]={cmd:[],type:'while',cond:'!==0'};
				while(a[i+1]!=']'){
					a[i].cmd.push(a.splice(i+1,1)[0]);
				}
				a.splice(i+1,1);
			}
		}
		let s=Array(16).fill(0);
		let p=0;
		[s,p]=tapex(s,p,a);
	}
	function tapex(s,p,a){
		for(let i=0; i<a.length; ++i){
			let ai=a[i];
			if(typeof ai=='object'){
				if(ai.type=='while'){
					let func=new Function('x','return x'+ai.cond);
					while(func(s[p])){
						[s,p]=tapex(s,p,ai.cmd);
					}
				}else if(ai.type=='if'){
					let func=new Function('x','return x'+ai.cond);
					if(func(s[p])){
						[s,p]=tapex(s,p,ai.cmd);
					}
				}
			}else{
				switch(ai[0]){
					case '?': [s,p]=logi(s,p,ai.slice(1)); break;
					case 'b': [s,p]=bin(s,p,ai.slice(1)); break;
					default: switch(ai.length){
						case 1: [s,p]=mono(s,p,ai); break;
						case 2: [s,p]=duo(s,p,ai); break;
						case 3: [s,p]=trio(s,p,ai); break;
					}
				}
			}
		}
		return [s,p];
	}
	function mono(s,p,ai){
		if(+ai===+ai){
			s[p]=parseInt(ai);
		}else{
			switch(ai){
				case '+': ++s[p]; break;
				case '=': s[p]=0; break;
				case '-': --s[p]; break;
				case '>': ++p; break;
				case ':': p=0; break;
				case '<': --p; break;
				case '.': console.log(s[p]); break;
				case '!': console.log(s); break;
				default: console.warn(`Mono command ${ai} is not recognized.`);
			}
		}
		return [s,p];
	}
	function duo(s,p,ai){
		let aia=ai[0];
		let aib=ai[1];
		if(+aib===+aib){
			aib=parseInt(aib);
			switch(aia){
				case '+': s[p]+=aib; break;
				case '=': s[p]=aib; break;
				case '-': s[p]-=aib; break;
				case '*': s[p]*=aib; break;
				case '/': s[p]/=aib; break;
				case '>': p+=aib; break;
				case ':': p=aib; break;
				case '<': p-=aib; break;
				case '.': console.log(s[aib]); break;
				default: console.warn(`Param command ${ai} is not recognized.`);
			}
		}else{
			switch(aia){
				case '>': ++p; [s,p]=mono(s,p,aib); --p; break;
				case '<': --p; [s,p]=mono(s,p,aib); ++p; break;
				case '@': for(let j=0; j': ++p; [s,p]=duo(s,p,aib); --p; break;
			case '<': --p; [s,p]=duo(s,p,aib); ++p; break;
			case '@': for(let j=0; j<s.length; ++j){
				[s,j]=duo(s,j,aib);
			} break;
			default: console.warn(`Trio command ${ai} is not recognized.`);
		}
		return [s,p];
	}
	function bin(s,p,ai){
		switch(ai){
			case '!': s[p]=~s[p]; break;
			case '&': s[p]&=s[p-1]; break;
			case '|': s[p]|=s[p-1]; break;
			case '^': s[p]^=s[p-1]; break;
			case '!&': s[p]=!(s[p-1]&s[p]); break;
			case '!|': s[p]=!(s[p-1]|s[p]); break;
			case '!^': s[p]=!(s[p-1]^s[p]); break;
			default: console.warn(`Binary command ${ai} is not recognized.`);
		}
		s[p]=s[p]?1:0;
		return [s,p];
	}
	function logi(s,p,ai){
		switch(ai){
			case '!': s[p]=!s[p]; break;
			case '&': s[p]=s[p-1]&&s[p]; break;
			case '|': s[p]=s[p-1]||s[p]; break;
			case '!&': s[p]=!(s[p-1]&&s[p]); break;
			case '!|': s[p]=!(s[p-1]||s[p]); break;
			default: console.warn(`Logical command ${ai} is not recognized.`);
		}
		s[p]=s[p]?1:0;
		return [s,p];
	}