Purple
Paradigm(s) | imperative |
---|---|
Designed by | User:Quintopia |
Appeared in | 2015 |
Computational class | Turing complete |
Reference implementation | See below |
Influenced by | Aubergine |
File extension(s) | .pur |
Purple is a self-modifying tarpit intended as a minimization of Aubergine created by User:Quintopia in 2015, as a program of repeatedly staying up late at night to create Aubergine derivatives. It was created after noticing that Aubergine's conditional jump was unnecessary for Turing-completeness, and that the functionality of the remaining instructions could be captured in a single instruction with one additional argument, and that said instruction would no longer require an identifying character, so that each instruction would continue to occupy three bytes.
Specification
A Purple program lives in an infinite memory array, its first byte at address zero. Instructions may modify this array, and in so doing, also modify the program as it executes. Each instruction is 3 bytes long. A single instruction in Purple looks like:
xyz
where x is any of a, A, b, B, i, or o, and y and z are any of these or 1.
When this instruction is executed, y and z are evaluated in that order, and the difference y-z is sent to a location as specified by x
The meanings of a, A, b, B, i, and 1 are as in Aubergine. Putting o in the x position causes the value y-z to be output to stdout as a character when possible. o in either of the other positions evaluates to the value of a single byte from stdin.
After each instruction is executed the instruction pointer is incremented by 3 as in Aubergine. Execution halts when any of x, y, or z is not one of the valid choices listed above. a, b, the instruction pointer i, and any element of the memory array may be any integer (without bound), though how interpreters choose to output large or negative integers is implementation-dependent. (The reference interpreter simply fails quietly when attempting to output numbers greater than 255 or less than zero.) The unbounded memory array in which the program source code lies is initialized to zero everywhere it does not contain the initial program source, and any address in it may be read or written to (including negative addresses).
Comparison to Aubergine
Purple is not a "true" minimization of Aubergine, since Aubergine has 4*6*7=168 different valid commands, while Purple has 6*7*7=294. However, it is much simpler implementation-wise, as there are only a handful of special cases. And the language is more primitive as well, inasmuch as it is more difficult to program in successfully. Succinct Aubergine programs will tend to be slightly shorter than their Purple equivalents, as a certain number of instructions must be used in finding or making ones and zeros, although the abilities to index and address beyond the initial memory array and store results in locations not involved in the calculation do make certain optimizations and tricks possible. The Purple truth-machine, for instance, is two instructions shorter than Aubergine's, as it can dereference -1 to get a zero to jump to, as well as compare the contents of two addresses and store them in a register using a single instruction.
Since each instruction no longer begins with a symbol character, Purple is much more difficult to read than Aubergine, as it is very difficult to tell where one instruction ends and the next begins. It would be well served by an editing program that marked every third character in some way.
Examples
Subtract
The simplest program in Purple reads in two bytes and outputs their difference:
ooo
Cat
bbboobiii
Quine
b1bbb1oAbabaa1ab1Ab1Bi1b
Truth-machine
Aoab11bi1bABoAaiba
Hello, World!
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet? !dlroW ,olleG
Shorter version:
AA1AA1AA1bA1b1Bo1bb1bbibb1Bi1b ! d l r o W , o l l e H
Implementations
As behavior upon reading EOF is undefined by this specification, these implementations may do anything in that case, including break.
Python 2
It does all the normal error checking an interpreter should, but weighs in at only 665 bytes (Unix). It can also be safely imported as a module.
A=__import__ s=A("sys") d=A("collections").defaultdict class Purple: def __init__(m,f): try:o=open(f);m.c=d(int,enumerate(map(ord,list(o.read()))));o.close();m.v=d(int);m.g=map(ord,"abi1oAB");m.v[49]=1 except IOError:print"File '%s' not found."%f;s.exit(1) g=lambda h,y:ord(s.stdin.read(1))if y==111 else[h.v[y],h.c[h.v[y+32]]][0<y-64<3] def run(h): while all([h.c[h.v[105]+x]in h.g for x in 0,1,2]):i=h.v[105];x,v=h.c[i],g(h,h.c[i+1])-g(h,h.c[i+2]);exec(["s.exit(0)","h.c[h.v[x+32]]=v","h.v[x]=v","if 0<=v<256:s.stdout.write(chr(v))"][x/27-1]);h.v[105]+=3 if __name__=="__main__": if len(s.argv)<2:print"No filename to execute." else:run(Purple(s.argv[1]))
Python 3
Updated to 3, now with helpful pretty print functionality
A=__import__ s=A("sys") d=A("collections").defaultdict class Purple: def __repr__(h): return f""" command: {''.join([chr(h.c[h.v[105]+x])if 0<h.c[h.v[105]+x]<256else"_"for x in(0,1,2)])}; a: {h.v[97]}; b: {h.v[98]}; A: {h.c[h.v[97]]}; B: {h.c[h.v[98]]} {[h.c[k]for k in sorted(h.c.keys())]}""" def __init__(m,f): try:o=open(f);m.c=d(int,enumerate(map(ord,list(o.read()))));o.close();m.v=d(int);m.g=list(map(ord,"abi1oAB"));m.v[49]=1 except IOError:print(f"File '{f}' not found.");s.exit(1) g=lambda h,y:ord(s.stdin.read(1))if y==111 else[h.v[y],h.c[h.v[y+32]]][64<y<67] def run(h): while all([h.c[h.v[105]+x]in h.g for x in(0,1,2)]):i=h.v[105];x,v=h.c[i],g(h,h.c[i+1])-g(h,h.c[i+2]);exec(["s.exit(0)","h.c[h.v[x+32]]=v","h.v[x]=v","if 0<=v<256:s.stdout.write(chr(v))"][x//27-1]);h.v[105]+=3 if __name__=="__main__": if len(s.argv)<2:print("No filename to execute.") else:run(Purple(s.argv[1]))
Ceylon
import ceylon.language{l=variable,I=Integer,x=nothing,p=process,m=map}shared void run(){try{if(exists d=p.arguments[0]){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;l{I*}c=[];I o{if(c==[]){if(exists e=p.readLine()){c=e*.hash.chain{10};}else{c={-1}.cycled;}}assert(is I r=c.first);c=c.rest;return r;}value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},111->{o},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v),111->((I v)=>p.write("``v.character``"))};I h(I v)=>f[v]?.first else x;while(0<1){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}}catch(AssertionError e){}}
Pyth
By isaacg
J,00=kjb.z .eXHkCbjb'z#=b-Fm?q\o=zC@H+ZdCh~tk@s[Jm.x@Hk0JZ1H)x"abABi1"zS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
Javascript (ES6)
By Anonymous
eval(`a=b=i=d=0;v=n=>(x=m[i+n])==97?a_98?b_65?m[a]_66?m[b]_105?i_111?p()[c]()_49?1:d=1;for(m=[...(p=prompt)()].map(b=>b[c="charCodeAt"]());!d;i+=3)(y=v(1),d)||(z=v(2),d)?1:(x=m[r=y-z,i])==97?a=r_98?b=r_65?m[a]=r_66?m[b]=r_105?i=r-3_111?alert(String.fromCharCode(r)):d=1`.replace(/_/g,":x=="))