HashedPoem
HashedPoem is an esolang invented by User:None1, inspired by the hash function. Something interesting is that this esolang is the 24th esolang invented in the year 2024 (on this wiki).
Programs
HashedPoem programs consists of words separated by whitespaces (spaces, tabs, linefeeds). Each word has a hash value, the hash of a lowercase string a0a1a2...al-1 can be computed using this expression:
If one can't see math tags, there is the expression in image form:
To be more clear, a Python function computing the hash value is as follows:
computehash=lambda x:sum((ord(j)*pow(256,i,7))%7 for i,j in enumerate(x))%7
When computing the hash value of a non-lowercase string, it converts to lowercase first.
For convenience, the following table lists some words with every hash value:
Hash | Words |
---|---|
0 | or, fuck, with, can, to |
1 | no, its, of, at |
2 | oh, it, let, hello, word |
3 | this, down, thanks |
4 | then, as, for, get |
5 | is, on, brain, brainfuck |
6 | yes, esolang, there, the, a |
If two words have the same hash value, they are treated equally in HashedPoem.
For convenience, let's call the words with hash value i i-hashed words.
A program doesn't have to make sense (you can even include words that are not in English), but it has to be valid, that means you can even write:
is is is is is is is or
but not:
is is is is is is is
but writing a valid HashedPoem program that makes sense can be a challenge, because it's hard.
Non-letters are ignored, that means:
*#?_
has a hash of 0.
Memory
It uses an unbounded tape. Every cell has a unique nonnegative address, address 0 is the first cell, address 1 is the second, address 2 is the third, etc. The cells contain unbounded signed intergers. Initiall, address 0 contains 1, all the other addresses are 0.
Integer representation
Since HashedPoem programs consist entirely of words and words have only 7 possible hash values, HashedPoem cannot use decimal to represent integers, instead, it uses base-6. A number is represented by a sequence of words ending with a 0-hashed word, all the words except the last one are nonzero-hashed. A digit is represented by a nonzero-hashed word, its value is the word's hash minus 1.
For example, the sequence of words:
this is because
when hashed, becomes:
3 5 0
which becomes 24 in base-6, which is 16 in decimal.
When there is no digit in an integer, it represents 0, so:
because
represents 0.
There is no way to represent negative integers directly in code, but you can use arithmetic operations to calculate one.
Commands
Commands are assembly-like.
Hash | Command | Meaning |
---|---|---|
0 | ADD <integer1> <integer2> | Add the cells with addresses <integer1> and <integer2> and store the result in <integer1> |
1 | SUB <integer1> <integer2> | Subtract the cells with addresses <integer1> and <integer2> and store the result in <integer1> |
2 | MUL <integer1> <integer2> | Multiply the cells with addresses <integer1> and <integer2> and store the result in <integer1> |
3 | IO <word> <integer> | If the hash of the word is even, print the cell with address <integer> as Unicode, otherwise input the cell with address <integer2> as Unicode |
4 | REF <integer> | Set the cell with address <integer> to the cell addressed the absolute value of the value of the cell with address <integer>. |
5 | LABEL <integer> | Defines a label with an ID of <integer>, if a label with this ID already exists, the program errors. |
6 | JMP <integer1> <integer2> | If the cell with address <integer1> is nonzero, jump to the label with the ID <integer2>, if there isn't a label with the ID <integer2>, the program errors. |
Code
Assemblers
Currently, there exists a low-level assembler that assembles to hashed HashedPoem in Python. (Note, this assembler automatically converts decimal to base-6, so write in decimal)
import sys code=map(lambda x:x.split(),sys.stdin.read().split('\n')) res='' def dec2base6(x): r='0' while x: r=str(x%6+1)+r x//=6 return r for i in code: try: c=i[0].upper() if c=='ADD': res+='0'+dec2base6(int(i[1]))+dec2base6(int(i[2])) if c=='SUB': res+='1'+dec2base6(int(i[1]))+dec2base6(int(i[2])) if c=='MUL': res+='2'+dec2base6(int(i[1]))+dec2base6(int(i[2])) if c=='IO': res+='3'+i[1]+dec2base6(int(i[2])) if c=='REF': res+='4'+dec2base6(int(i[1])) if c=='LABEL': res+='5'+dec2base6(int(i[1])) if c=='JMP': res+='6'+dec2base6(int(i[1]))+dec2base6(int(i[2])) except: pass print(res)
Example:
LABEL 0 JMP 0 0
Assembles to:
50600
Interpreters
In Python:
import sys class Tape: def __init__(self): self.data={0:1} def __getitem__(self,item): if item in self.data: return self.data[item] else: return 0 def __setitem__(self,item,x): self.data[item]=x _computehash=lambda x:sum((ord(j)*pow(256,i,7))%7 for i,j in enumerate(x))%7 computehash=lambda x:_computehash(''.join(filter(lambda y:y.isalpha(),x.lower()))) hashall=lambda x:''.join(map(str,map(computehash,x.split()))) def nextword(x): # separate the first word return (int(x[0]),x[1:]) def _nextnum(x): p=x.find('0') if p==-1: raise SyntaxError('Missing ending zero-hashed word in number') return (x[:p],x[p+1:]) def nextnum(x): # separate the next integer ns,nx=_nextnum(x) r=0 for i in ns: r=r*6+int(i)-1 return (r,nx) def disasm(x): # Disassemble code to a list of tuples of integers ha=hashall(x) res=[] ha=''.join(filter(lambda x:x in '0123456',ha)) while ha: i,ha=nextword(ha) i=str(i) if i in '0126': a,ha=nextnum(ha) b,ha=nextnum(ha) res.append((int(i),a,b)) elif i in '45': a,ha=nextnum(ha) res.append((int(i),a)) else: a,ha=nextword(ha) b,ha=nextnum(ha) res.append((int(i),a,b)) return res def getchar(): c=sys.stdin.read(1) return (ord(c) if c else 0)%256 def interpret_asm(x): # Interpret the disassembled code ip=0 tape=Tape() labels={} for j,i in enumerate(x): if i[0]==5: # LABEL if i[1] in labels: raise SyntaxError("Duplicate label ID") labels[i[1]]=j while ip<len(x): c=x[ip] opcode=c[0] if opcode==0: # ADD tape[c[1]]+=tape[c[2]] if opcode==1: # SUB tape[c[1]]-=tape[c[2]] if opcode==2: # MUL tape[c[1]]*=tape[c[2]] if opcode==6: # JMP if tape[c[1]]: if c[2] not in labels: raise SyntaxError("Label ID does not exist") ip=labels[c[2]] continue if opcode==4: # REF tape[c[1]]=tape[abs(tape[c[1]])] if opcode==3: # IO if not (c[1]&1): # even: print print(chr(tape[c[2]]),end='') else: # odd: input tape[c[2]]=getchar() ip+=1 def interpret(x): return interpret_asm(disasm(x)) if __name__=='__main__': interpret(sys.stdin.read())
Unhashers
This is a random unhasher in Python, it unhashes to meaningless code, because, as the previous text claimed, writing a program that makes sense is hard:
from random import choice _computehash=lambda x:sum((ord(j)*pow(256,i,7))%7 for i,j in enumerate(x))%7 computehash=lambda x:_computehash(''.join(filter(lambda y:y.isalpha(),x.lower()))) def rndgen(): return ''.join(choice('QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm') for i in range(10)) def gen(x): r=rndgen() while computehash(r)!=x: r=rndgen() return r c=input() d=[gen(int(i)) for i in filter(lambda x:x in '0123456',c.strip())] for i in range(len(d)//5): print(d[i*5],d[i*5+1],d[i*5+2],d[i*5+3],d[i*5+4]) for i in range(len(d)%5): print(d[len(d)//5*5+i],end=' ')
An improvement on the above, generates meaningless "words".
from random import choice _computehash=lambda x:sum((ord(j)*pow(256,i,7))%7 for i,j in enumerate(x))%7 computehash=lambda x:_computehash(''.join(filter(lambda y:y.isalpha(),x.lower()))) def rndgen(): return choice('im in il un dis ad re de inter mis '.split(' '))+choice('act add substract switch hash unhash jump label multiply program research create implement interpret prove and or xor not'.split())+choice('ed tion ation ion er or ist '.split(' ')) def gen(x): r=rndgen() while computehash(r)!=x: r=rndgen() return r c=input() d=[gen(int(i)) for i in filter(lambda x:x in '0123456',c.strip())] for i in range(len(d)//5): print(d[i*5],d[i*5+1],d[i*5+2],d[i*5+3],d[i*5+4]) print(*d[len(d)//5*5:])
Examples
Infinite loop
is to yes can can
but actually, it can make more sense:
Brainfuck or esolang? Fuck !
The space between Fuck
and !
is mandatory.
Hashed:
50600
Disassembled:
LABEL 0 JMP 0 0
Truth Machine
Currently, the code is only available in hashed form:
00000000000002000200020031003001020503030600
and meaningless form:
interlabelion ilnotist disimplementist rehashor or ilxortion resubstraction programtion labelor substraction label or deortion improgram imresearch reortion or discreate oror injumpion misinterpretation deadder adsubstractation ilproveation misprogramor dejumpist imnoted ortion jumper adsubstractist ilmultiplyist dishash dehash dissubstract iladded renoted adoror disandor unxored disnottion uncreateed adprogramist ilandist delabel
And a form that makes a bit of sense, but not much (Written by User:None1):
I love cows . I love oxen . I love bad bees , That can make food. However, bees beat cows, Sting their back. Whip you . Hit your back . You make me eat feet. Fuck them! Fuck them! Make the people fuck!
It takes a lot of time to write that.
Disassembled:
ADD 0 0 ADD 0 0 ADD 0 0 ADD 0 0 ADD 1 0 ADD 1 0 ADD 1 0 IO 1 0 ADD 2 0 SUB 0 1 LABEL 0 IO 1 2 JMP 0 0
If you unhashed it into more meaningful code (code that makes sense), please put it here.
Computational class
Turing complete, because the two register minsky machine can be compiled to it (l is the position of the command in the minsky machine program, first command is 1, second command is 2, third is 3, etc. n is the sum of the number of commands in the minsky machine program and l):
Increment A: LABEL l ADD 1 0 Increment B: LABEL l ADD 2 0 If A=0, jump to the i-th minsky machine command, otherwise decrement A: LABEL l JMP 1 n JMP 0 i LABEL n SUB 1 0 If B=0, jump to the i-th minsky machine command, otherwise decrement B: LABEL l JMP 2 n JMP 0 i LABEL n SUB 2 0