Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
0815 Interpreter

Hello World

The Esolang wiki version

<:48:x<:65:=<:6C:$=$=$$~<:03:+$~<:ffffffffffffffbd:+$<:ffffffffffffffb1:
+$<:57:~$~<:18:x+$~<:03:+$~<:06:x-$x<:0e:x-$=x<:43:x-$

Explanation

Store "H" and "e". Print them. Use addition and subtraction to get the characters "l", "o", comma, and space. Print them (twice for the "l"). Store "W". Print it. Print "o" (which is still in a register). Use addition to get the characters "r", "l", "d", and "!". Print them.

Problems

Well, it takes a few inefficient approaches to various numbers. And the language specification says nothing about negative numbers having to be entered in two's complement; if anything, its insistence on signed arithmetic implies that negative numbers ought to be accepted with a minus sign instead.

(Note, however, that the author's implementation does insist on two's complement, rather than minus signs.)

Cat

The Esolang wiki version

}:_t:!~$^:_t:

Explanation

Read a character. Print it. If it wasn't zero, go back to the start (i.e. input is null-terminated).

Problems

EOF might have been a better choice of terminator.

Truth Machine

The Esolang wiki version

|~}:i:%^:i:

Explanation

Read in a number. Print it. If it's non-zero, print it again, and again, and...

Odd or Even

The Esolang wiki version

}:s:|=<:2:x~#:e:=/~%~<:20:~$=<:73:x<:69:~$~$~<:20:~$=^:o:<:65:
x<:76:=$=$~$<:6E:~$<:a:~$^:s:}:o:<:6F:x<:64:x~$~$$<:a:~$^:s:

Explanation

Read in a number. Divide it by 2. Print the number followed by " is ". If the remainder of division is non-zero, print "odd\n" and jump back to the beginning; otherwise print "even\n" and jump back to the beginning.

Problems

The seventh instruction (#:e:) is a jump to a non-existent label :e:. This is probably deliberate, because jumping to non-existent labels is a way to terminate the program (the only way, short of killing the process, when using the author's own implementation, because it has no way to quit when the program is waiting for input).

A version that instead outputs "0 is even" should simply remove that jump.

Fibonacci Sequence

The Esolang wiki version

%<:0D:>~$<:01:~%>=<:a94fad42221f2702:>~>}:_s:{x{={~$x+%{=>~>x~-x<:0D:~>~>~
^:_s:?

Explanation

Print 0, then "\r" (CR), then 1. Put 0, 1, "\r", and the 93rd Fibonacci number (0xa94fad42221f2702) into the queue, in that order. While the second number in the queue is not equal to F(93), read the numbers out, add them up, and print "\r" and their sum; then store the second number, the sum, "\r", and F(93) in the queue.

Problems

CR? Really? What is this, Mac OS 9?

Also, "\r" is enqueued, dequeued, printed, and discarded. Then another copy is loaded and the cycle repeats.

Finally, F(93) is too large to be stored as a signed 64-bit value, so it overflows and becomes negative. This would not be a problem if it was checked before being printed.

Fixed version:

%<:0A:>~$<:01:~%>=<:68a3dd8e61eccfbd:>~>}:_s:{x{={~$x+%{=>~>x~-x<:0A:~>~>~
^:_s:?
#!/usr/bin/env python3
"""Interpreter for the 0815 esolang."""
# Standard library imports.
from enum import Enum
from sys import stdin, argv
# A signed 64-bit register.
FULL_BITWIDTH = 0xffffffffffffffff
SIGNED_BITWIDTH = 0x7fffffffffffffff
class Register:
def __init__(self, val):
self.value = val
@property
def value(self):
return (self._val if 0 <= self._val <= SIGNED_BITWIDTH else
self._val - FULL_BITWIDTH - 1)
@value.setter
def value(self, val):
self._val = int(val) & FULL_BITWIDTH
# Interpreter states.
InterpreterStates = Enum('InterpreterStates',
'AWAITING_INSTRUCTION '
'GOT_INSTRUCTION '
'INSIDE_PARAMETER '
'DONE')
# The interpreter itself.
NEED_PARAM = '<}^#'
OPTIONAL_PARAM = '@&'
NO_PARAM = 'xX|!%$~=?>{+-*/'
INSTRUCTIONS = NEED_PARAM + OPTIONAL_PARAM + NO_PARAM
DELIMIT_PARAM = ':'
class Interpreter0815:
def __init__(self, program):
self.program = program
self.pos = 0
self.queue = []
self.x = Register(0)
self.y = Register(0)
self.z = Register(0)
self.labels = {}
self.state = InterpreterStates.AWAITING_INSTRUCTION
self.instructions = {'<': self.move,
'x': self.swap,
'X': self.swap,
'}': self.label,
'|': self.input_number,
'!': self.input_ascii,
'%': self.print_number,
'$': self.print_ascii,
'~': self.roll_left,
'=': self.roll_right,
'^': self.jump_if_not_zero,
'#': self.jump_if_zero,
'?': self.clear,
'>': self.enqueue,
'{': self.dequeue,
'@': self.roll_queue_left,
'&': self.roll_queue_right,
'+': self.add,
'-': self.subtract,
'*': self.multiply,
'/': self.divide}
def run(self):
"""Run the program until it terminates."""
while self.state != InterpreterStates.DONE:
try:
self.step()
except KeyboardInterrupt:
self.state = InterpreterStates.DONE
print()
def step(self):
"""Read and execute a single instruction."""
if self.state == InterpreterStates.DONE:
return
elif self.pos >= len(self.program):
self.state = InterpreterStates.DONE
return
assert self.state == InterpreterStates.AWAITING_INSTRUCTION
previous_state = self.state
self.state = instruction = param = None
param_tokens = []
while self.state not in (InterpreterStates.AWAITING_INSTRUCTION,
InterpreterStates.DONE):
try:
token = self.program[self.pos]
except IndexError:
self.state = InterpreterStates.DONE
continue
if self.state == InterpreterStates.INSIDE_PARAMETER:
if token in DELIMIT_PARAM:
param = ''.join(param_tokens)
if previous_state == InterpreterStates.GOT_INSTRUCTION:
assert instruction in NEED_PARAM + OPTIONAL_PARAM
self.instructions[instruction](param, self.pos)
self.state = InterpreterStates.AWAITING_INSTRUCTION
else:
param_tokens.append(token)
elif token in DELIMIT_PARAM:
previous_state = self.state
self.state = InterpreterStates.INSIDE_PARAMETER
elif token in INSTRUCTIONS:
instruction = token
if ((token in NEED_PARAM or token in OPTIONAL_PARAM) and
self.program[self.pos + 1] in DELIMIT_PARAM):
self.state = InterpreterStates.GOT_INSTRUCTION
elif (token in NO_PARAM or
(token in OPTIONAL_PARAM and
self.program[self.pos + 1] not in DELIMIT_PARAM)):
self.instructions[instruction]()
else:
# Comment, or invalid instruction (parameter required but not
# supplied).
pass
self.pos += 1
def move(self, value, pos):
"""Load a value into register X."""
self.x.value = int(value, base=16)
def swap(self):
"""Swap values of registers X and Y."""
self.x, self.y = self.y, self.x
def label(self, label, pos):
"""Store a new label."""
self.labels[label] = pos
def input_number(self):
"""Read a line into register X as a base-16 integer."""
hex_input = []
while stdin.buffer.peek()[:1] in b'0123456789ABCDEFabcdef':
hex_input.append(stdin.buffer.read(1))
self.x.value = int(b''.join(hex_input).decode('ASCII'), base=16)
def input_ascii(self):
"""Read a character into register X as an ASCII character."""
self.x.value = ord(stdin.read(1))
def print_number(self):
"""Print register Z as a base-16 integer."""
print(format(self.z.value, 'x'), end='')
def print_ascii(self):
"""Print register Z as an ASCII character."""
print(chr(self.z.value), end='')
def roll_left(self):
"""Roll registers to the left: Z to Y to X to Z."""
self.x, self.y, self.z = self.y, self.z, self.x
def roll_right(self):
"""Rolle registers to the right: X to Y to Z to X."""
self.x, self.y, self.z = self.z, self.x, self.y
def _jump(self, label):
"""Jump to a label unconditionally."""
try:
self.pos = self.labels[label]
except KeyError:
# Is this a nonexistent label (in which case the effect is to exit
# the program), or is it just a jump ahead?
label_decl = '}:' + label + ':'
ahead_pos = self.program.find(label_decl, self.pos)
if ahead_pos == -1:
self.state = InterpreterStates.DONE
else:
# Point at the start of the label declaration, so that it will
# be read as the next instruction and the label recorded.
self.pos = ahead_pos
def jump_if_not_zero(self, label, pos):
"""Jump to a label if Z is not zero."""
if self.z.value != 0:
self._jump(label)
def jump_if_zero(self, label, pos):
"""Jump to a label if register Z is zero."""
if self.z.value == 0:
self._jump(label)
def clear(self):
"""Clear the queue."""
self.queue = []
def enqueue(self):
"""Enqueue the value in register Z."""
self.queue.append(self.z.value)
def dequeue(self):
"""Dequeue a value into register X."""
self.x.value = self.queue.pop(0)
def roll_queue_left(self, distance=1, pos=None):
"""Roll the queue left a given number of places."""
if type(distance) is str:
distance = int(distance, base=16)
self.queue = self.queue[distance:] + self.queue[:distance]
def roll_queue_right(self, distance=1, pos=None):
"""Roll the queue right a given number of places."""
if type(distance) is str:
distance = int(distance, base=16)
self.queue = self.queue[-distance:] + self.queue[:-distance]
def add(self):
"""Add registers X and Y, storing the result in Z."""
self.z.value = self.x.value + self.y.value
def subtract(self):
"""Subtract register Y from X, storing the result in Z."""
self.z.value = self.x.value - self.y.value
def multiply(self):
"""Multiply registers X and Y, storing the result in Z."""
self.z.value = self.x.value * self.y.value
def divide(self):
"""Divide register X by Y, storing the quotient in Z.
The remainder is also stored, in register Y.
"""
self.z.value, self.y.value = divmod(self.x.value, self.y.value)
if __name__ == '__main__':
with open(argv[1]) as program:
Interpreter0815(program.read()).run()
import sys as y
b=y.stdin.buffer
I=int
g=getattr
X=2**64
R=lambda a:property(lambda s:g(s,a)-[0,X][0<=g(s,a)<X/2],lambda s,v:setattr(s,a,I(v)&X-1))
M,*Q=':<}^#@&xX|!%$~=?>{+-*/'
class A:
x,y,z=map(R,'uvw')
def R(s,p):
s.p,s.l,s.s,*s.q=p,{},3,;s.x=s.y=s.z=s.i=0
while s.s<4:
p,t=s.s,'';s.s=i=0
while s.s<3:
k=s.p[s.i]
s.i+=1
if s.s==1:
if k==M:
if p==2:s.t=s.i;g(s,s.I[i])(t);s.s=3
else:t+=k
elif k==M:p,s.s=s.s,1
elif k in Q:
i=k;m=M==s.p[s.i]
if k in Q[:6]and m:s.s=3
elif k in Q[6:]or k in'@&'and m<1:g(s,s.I[i])()
def B(s,v):s.x=I(v,16)
def C(s):s.x,s.y=s.y,s.x
def D(s,l):s.l[l]=s.t
def E(s):
c=b''
while b.peek()[0]in b'0123456789ABCDEFabcdef':
c+=b.read(1)
s.x=I(c.decode(),16)
def F(s):s.x=y.stdin.read(1)
def G(s):print(format(s.z,'x'),end='')
def H(s):print(chr(s.z),end='')
def J(s):s.x,s.y,s.z=s.y,s.z,s.x
def K(s):s.J();s.J()
def j(s,l):
try:s.i=s.l[l]
except:
a=s.p.find('}:'+l+M)
if a<0:s.s=4
else:s.i=a
def L(s,l):
if s.z:s.j(l)
def T(s,l):
if s.z==0:s.j(l)
def U(s):s.q=[]
def V(s):s.q+=s.z,
def W(s):s.x,*s.q=s.q
def Y(s,d='1'):d=I(d,16);s.q=s.q[d:]+s.q[:d]
def Z(s,d='1'):s.Y('-'+d)
def a(s):s.z=s.x+s.y
def b(s):s.z=s.x-s.y
def c(s):s.z=s.x*s.y
def d(s):s.z,s.y=divmod(s.x,s.y)
I=dict(zip(Q,'BDLTYZCCEFGHJKUVWabcd'))
try:A().R(open(y.argv[1]).read())
except:0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.