Write a 0815 interpreter

I like the concept of 0815, except the interpreter on the creator's website returns an Error 404. So I decided to ask you all to help!

The Basics

0815 is based around three (3) registers and a queue. The registers are named X, Y, and Z, with X being write-only, Z being read-only, and Y being a "helper" register that can't be directly accessed. All registers start out set to 0. All numbers are in hexadecimal.

Instructions

Note: Some instructions take parameters which are formatted like }:hello: where the :hello: is the parameter. Other note: Some of the instruction descriptions are unclear, so I took some liberties with them. (Original instructions here)

~ means the parameter is required

----------|---|--------------------------------------------
~Move   | < | <:8: will write 8 to X (parameter required)
----------|---|--------------------------------------------
Swap   | x | Swap X and Y
----------|---|--------------------------------------------
~Label  | } | }:hello: makes a label called 'hello'
----------|---|--------------------------------------------
Input   | | | Set X to an integer from input in
Number   |   | hexadecimal greedily (regex: [0-9A-F]+)
----------|---|--------------------------------------------
Input   | ! | Set X to the ASCII code of one ASCII
ASCII   |   | character in the input
----------|---|--------------------------------------------
Print   | % | Prints an integer stored in Z
number  |   | (hexadecimal base)
----------|---|--------------------------------------------
Print   | $| Prints an ASCII char stored in Z ASCII | | ----------|---|-------------------------------------------- Roll | | Rolls all registers to the left Registers | ~ | X <- Y <- Z <- X Left | | After roll: X = Y, Y = Z and Z = X ----------|---|-------------------------------------------- Roll | | Rolls all registers to the right Registers | = | X -> Y -> Z -> X Right | | After roll: Y = X, Z = Y and X = Z ----------|---|-------------------------------------------- ~Jump if | ^ | ^:loop: Jumps to the label 'loop' if Z is not zero | | not zero ----------|---|-------------------------------------------- ~Jump if | # | #:loop: Jumps to the label 'loop' if Z is zero | | zero ----------|---|--------------------------------------------  Queue Instructions ----------|---|-------------------------------------------- Clear | ? | Clears the queue ----------|---|-------------------------------------------- Enqueue | > | Enqueue the number stored in Z ----------|---|-------------------------------------------- Dequeue | { | Dequeue a number into X ----------|---|-------------------------------------------- | | Rolls the queue to the left, wrapping as Roll | | necessary. The first number becomes the Queue | @ | last; second becomes first and so on. Can Left | | take a parameter to roll a certain number | | of times. @:b: rolls 11 times. ----------|---|-------------------------------------------- Roll | | Queue | & | The opposite of @ Right | | ----------|---|--------------------------------------------  Arithmetic instructions ----------|---|-------------------------------------------- Add | + | Z = X + Y ----------|---|-------------------------------------------- Subtract | - | Z = X - Y ----------|---|-------------------------------------------- Multiply | * | Z = X * Y ----------|---|-------------------------------------------- Divide | / | Z = X / Y | | Y = remainder ----------|---|--------------------------------------------  Example programs Hello world: (Slightly golfed) <:48:~$<:65:~$<:6C:~$$><:6F:~$>@<:2C:~$<:20:~$<:57:~${~$<:72:~${~$<:64:~$ Cat: }:_t:!~$^:_t:


Truth Machine:

|~}:i:%^:i:


This is , so the submission with the least bytes wins!

• What's the distinction between ~ and ~ in your "roll registers" commands? Do you have an example program that people can use to test? Commented Dec 7, 2015 at 18:54
• Any examples you could give us? Commented Dec 7, 2015 at 18:59
• @DanTheMan I am seeing "Hello, Wlrod" when I run your Hello World example using my (still-WIP) implementation... Not sure whether that's a bug in my code or the example itself Commented Dec 8, 2015 at 12:10
• @DanTheMan Yes, but changing the link gets you the interpreter. :-) Commented Dec 8, 2015 at 13:56
• @usandfriends Fixed! Commented Dec 9, 2015 at 2:21

Pip, 321262 248 bytes

Greatly shortened using eval and some Kolmogorov-complexity tricks.

a@:.(:.*?:)?z:Y0w:2**64W++v<#a{UnpWa@v^':;V("Yp57Syi7UybWb^@(b@?[^0-9A-F]|$)Yy57Y APOb7OzTB167O Cz7SyzSyi7SyiSyz7I2I!2l:[]7lPBz7Y3B3UDQl7z:(y+4-4*4//i)i:y%i"R2zv:a@?"}:".p.':7R3"POl7Lp?p5olP"R4"i)%w7z:(y"R5"FB16%w"^7"<x|!%$~=^#?>{@&+-*/"@?n)}


Try it online! (The example is the Collatz/hailstone numbers program from the language author's website.)

Notes:

• Takes the 0815 code as the first command-line argument and the input as the second. Yes, it's a bit awkward (much less so on TIO, where you get separate text boxes), but Pip isn't that great at reading all of stdin.
• Numbers in input must use uppercase hex digits (as per OP: matching the regex [0-9A-F]+). If, at a | instruction, the next character of input is not 0-9A-F, the read fails and causes potentially weird behavior. This means the author's even-odd program doesn't work as written... I'm not sure how he expected multiple number reads to work, but I just implemented according to what the spec said.
• If a label is duplicated, a jump goes to the first one. If a label doesn't exist, a jump terminates the program.

My ungolfed version with comments:

;;; INITIALIZATION ;;;

; a is the code, analyzed with regex into a list of instructions
a @: .(:.*?:)?
; b is the input
; x, y, z are registers (initially three 0s)
x:y:z:0
; l is the queue (initially empty list)
; v is the instruction pointer (initially -1, but gets incremented at top of loop)
; w represents the width of the registers
w:2**64

;;; MAIN LOOP ;;;

; Loop while IP hasn't gone past last instruction
W ++v<#a {
; Unify n with first char of current iNstruction, p with the parameter (or nil)
U np W a@v ^ ':

; If n eQuals "<": move p value into x
InQ'<
; Convert p from hex and truncate to register width
x : pFB16%w
; Swap x and y
InQ'x
Sxy
; Input number (in hex, uppercase only)
InQ'| {
; Find index of first non-hex character in b and split at that index
; Unify left half with x and right with b
U xb W b^@(b @? [^0-9A-F]|$) ; Convert x from hex and truncate to register width x : xFB16%w } ; Input ASCII InQ'! ; Pop 1st char of b and get ASCII code x : A POb ; Print number (in hex) InQ'% ; Output z converted to hex O zTB16 ; Print ASCII InQ'$
; Output chr(z)
O Cz
; Roll registers left
InQ'~ {
; With three registers, a roll is just two swaps
Sxz
Sxy
}
; Roll registers right
InQ'= {
Sxy
Sxz
}
; Jump (combines if-nonzero and if-zero cases)
I nQ'^ & z | nQ'# & !z
; Construct the corresponding label, find its index in a, and set IP to that
v : a @? "}:".p.':
; If the label doesn't exist, v becomes nil, which makes the loop condition nil,
; which (being falsy) exits the loop and terminates the program

; Clear queue
InQ'?
l:[]
; Enqueue
InQ'>
; Push z to back of l
lPBz
; Dequeue
InQ'{
; Pop l and assign to x
x:POl
; Roll queue left
; NOTE: doesn't work if 0 is a valid parameter for roll operation
; If we want to handle that case, use #p?... instead of p?... for +1 byte
InQ'@
; Loop specified number of times (converted from hex), or 1 if not specified
L p ? pFB16 1
; Pop (front of) l and push that value to the back of l
l PB POl
; Roll queue right
InQ'&
L p ? pFB16 1
; Dequeue from back of l and push that value to (the front of) l
l PU DQl

; Add (truncating as needed)
InQ'+
z:(x+y)%w
; Subtract (truncating as needed)
InQ'-
z:(x-y)%w
; Multiply (truncating as needed)
InQ'*
z:x*y%w
; Divide (truncating not needed)
InQ'/ {
z:x//y
y:x%y
}
}


haxe, 987 bytes

class Main{static function main(){var c=sys.io.File.getContent(Sys.args()[0]).split(""),i=0,x=0,y=0,z=0,t=0,m=0,g=Sys.getChar.bind(false),f=String.fromCharCode,b="",B="",l="",A=[];while(i<c.length)switch(l=c[i++]){case"<"|"}"|"^"|"#":b=l=="<"?"0x":"";if(c[i++]==":")while((B=c[i++])!=":")b+=B;if(l=="<")x=Std.parseInt(b);else if(l!="}"&&(z==0)==(l=="#")){t=0;while(t!=(m=(i+t++)%c.length)){if(c[m]==":")t+=c.indexOf(":",m+1)-m;else if(c[m]=="}"){l="";if(c[++m]==":")while((B=c[++m])!=":")l+=B;if(b==l){i=m;break;}}}return;}case"x":t=x;x=y;y=t;case"|":b="0x";while(((t=g())>47&&t<58)||(t>96&&t<103))b+=f(t);x=Std.parseInt(b);case"!":x=g();x=x<0?0:x;case"%"|"$":Sys.print(l=="%"?[for(j in 0...8){"0123456789abcdef".split("")[z>>((7-j)*4)&15];}].join(""):f(z));case"?":A=[];case">":A.push(z);case"{":x=A.shift();case"@":A.push(A.shift());case"&":A.unshift(A.pop());case"+":z=x+y;case"-":z=x-y;case"*":z=x*y;case"~":t=x;x=y;y=z;z=t;case"=":t=x;x=z;z=y;y=t;case"/":z=Math.floor(x/y);y=x%y;}}}  or with some whitespace: class Main{static function main(){ var c=sys.io.File.getContent(Sys.args()[0]).split(""), i=0, x=0, y=0, z=0, t=0, m=0, g=Sys.getChar.bind(false), f=String.fromCharCode, b="", B="", l="", A=[]; while(i<c.length)switch(l=c[i++]){ case"<"|"}"|"^"|"#": b=l=="<"?"0x":""; if(c[i++]==":") while((B=c[i++])!=":") b+=B; if(l=="<") x=Std.parseInt(b); else if(l!="}"&&(z==0)==(l=="#")){ t=0; while(t!=(m=(i+t++)%c.length)){ if(c[m]==":") t+=c.indexOf(":",m+1)-m; else if(c[m]=="}"){ l=""; if(c[++m]==":") while((B=c[++m])!=":") l+=B; if(b==l){ i=m; break; } } } return; } case"x": t=x;x=y;y=t; case"|": b="0x"; while(((t=g())>47&&t<58)||(t>96&&t<103)) b+=f(t); x=Std.parseInt(b); case"!": x=g(); x=x<0?0:x; case"%"|"$":
Sys.print(l=="%"?[
for(j in 0...8){
"0123456789abcdef".split("")[z>>((7-j)*4)&15];
}
].join(""):f(z));
case"?":
A=[];
case">":
A.push(z);
case"{":
x=A.shift();
case"@":
A.push(A.shift());
case"&":
A.unshift(A.pop());
case"+":
z=x+y;
case"-":
z=x-y;
case"*":
z=x*y;
case"~":
t=x;x=y;y=z;z=t;
case"=":
t=x;x=z;z=y;y=t;
case"/":
z=Math.floor(x/y);
y=x%y;
}}}


Successfully golfed with haxe? Wow.

• +1 for Haxe, the shortest answer in one of the least golfy langs
– cat
Commented Dec 12, 2015 at 14:20
• Do you know, if you compiled this to JS, would it be shorter? then you could make it ES6/ES7
– cat
Commented Dec 12, 2015 at 14:20

Python 3, 1320 bytes

The ungolfed version, as well as analysis of (and where necessary, corrected versions of) the test programs, can be found in this gist.

Design decisions:

• Multiple definitions of labels are ignored. Only the first definition of a label is used. (Formerly the most recently seen definition would be used, but that got golfed out.)
• Input complies with the OP's spec: numeric input reads hex characters greedily. (Originally it would read until newline, and error when non-hex characters were read.)
• "ASCII" input is taken to mean "byte-oriented" input. (Originally it had full Unicode support, which took less code thanks to being Python 3... but meeting the next bullet point meant reading byte by byte was now shorter.)
• Parameters where no parameters are expected are ignored; hence, they are usable as comments.

There are issues with the example programs using CR alone as their newline (sometimes), which makes my shell overwrite the current line rather than starting a new one. This affects the Fibonacci Sequence and 99 Bottles of Beer programs. The last verse of 99 Bottles of Beer also isn't printing correctly, and I'm still investigating why.

import sys as y
import sys as y
b=y.stdin.buffer
I=int
g=getattr
X=2**64
R=lambda a:property(lambda s:g(s,a)-[X,0][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.s,*s.q=p,3,;s.x=s.y=s.z=s.i=0 while s.s<4: t='';p=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=max(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=2 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):0 def E(s): s.x=0 try: while 1:s.x=s.x*16+I(b.peek()[:1],16);b.read(1) except:0 def F(s):s.x=b.read(1)[0] 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): 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'):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  • Love these long answers :-) Commented Mar 24, 2021 at 16:34 Scala, 3,1232,8442,626 2,540 bytes In addition to the constraints outlined in the question, this interpreter was written to lean as much as possible towards FP principles. Specifically: • Only immutable stuctures • All functions are pure This was accomplished, with the exception of the four lines of code that drive the main loop of the interpreter. Immutable structures were very difficult to utilize here because the state of the registers drives the control flow of the loop (specifically the two GOTO statements). I am still thinking on how to convert it to use pure & immutable structures, but that is irrelevant to the code golf challenge. import java.util.Scanner import scala.util._ object Z extends App{type K=Long type G=List[K] type I=String type E=Boolean val(f,t,ф,д,б)=(false,true,(z:G)=>z(0),(z:G)=>z.tail,(q:K,w:K,e:K)=>q::w::e::Nil) trait O{def h(z:I)=BigInt(z,16).longValue()} trait S extends O trait B extends S with P{def apply(r:G):E} trait R extends O{def a(r:G):G} trait T extends O{def a(r:G,s:G):(G,G)} trait P{def p:I} case class L(p:I)extends S with P case class U(p:I)extends B{def apply(r:G):E=r(2)==0} case class J(p:I)extends B{def apply(r:G):E=r(2)!=0} case class M(p:I)extends R with P{def a(r:G):G=h(p)::д(r)} class Y extends R{def a(r:G):G=б(r(0),r(0)%r(1),r(0)/r(1))} case class Q(p:I)extends T with P{def r(q:G,i:Int):G={val s=д(q):+ф(q) if(i>0)r(s,i-1)else s} def a(e:G,t:G)=e->r(t,Try(p.toInt).getOrElse(1))} case class N(p:I)extends T with P{def r(q:G,i:Int):G={ val s=q.last::q.iterator.sliding(2).map(_(0)).toList if(i>0)r(s,i-1)else s} def a(e:G,t:G)=e->r(t,Try(p.toInt).getOrElse(1))} case class A(n:Array[O], l:Map[I,Int]){def e={var (r,t,x)=(List(0L,0,0),List[K](),0) while(x<n.length){x=n(x)match{case i:B=>if(i(r))l(i.p)else x+1 case i:R=>r=i.a(r);x+1 case i:T=>val(y,u)=i.a(r,t);r=y;t=u;x+1 case _=>x+1}}}} object A{def apply(i:Seq[O]):A={A(n=i.toArray,l=Map(i.zipWithIndex.flatMap{case(e:L,i)=>Some(e.p->i) case _=>None}.toList:_*))}} object X{def v(y:(Char, Option[Z.I]))=y._2.getOrElse("");val F=Map('x->new R{def a(t:G)=б(t(1),t(0),t(2))},'|'->new R{def a(r:G)=h(new Scanner(System.in).next("[0-9a-fA-F]+"))::д(r)},'!'->new R{def a(r:G)=(System.in.read match{case i if i== -1=>0;case i=>i})::д(r)},'%'->new R{def a(r:G)={print(Integer.toHexString(r(2).toInt));r}},'$'->new R{def a(r:G)={print(r(2).toChar);r}},'~'->new R{def a(r:G)=д(r):+ф(r)},'='->new R{def a(r:G)=б(r(2),r(0),r(1))},'?'->new T{def a(r:G,s:G)=r->List()},'>'->new T{def a(r:G,s:G)=r->(r(2)::s)},'{'->new T{def a(r:G, s:G)=(ф(s)::д(r))->д(s)},'+'->new R{def a(r:G)=б(r(0),r(1),r(0)+r(1))},'-'->new R{def a(r:G)=б(r(0),r(1),r(0)-r(1))},'*'->new R{def a(r:G)=б(r(0),r(1),r(0)*r(1))},'/'->new Y);def apply(i:I)={(i+" ").foldLeft((List[(Char,Option[I])](),None:Option[Char],"",f))((a,n)=>{n match{case i if i==':'=>if(a._4)(a._1:+(a._2.get->Some(a._3)),None,"",f)else(a._1,a._2,"",t)
case i if a._4=>(a._1,a._2,a._3+i,t)
case i if a._2.isEmpty=>(a._1,Some(i),"",f)
case i=>(a._1:+(a._2.get->None),Some(i),"",f)}})._1.map(x=>x._1 match{
case'<'=>M(v(x))
case'}'=>L(v(x))
case'^'=>J(v(x))
case'#'=>U(v(x))
case'@'=>Q(v(x))
case'&'=>N(v(x))
case c=>F(c)})}}
A(X(args(0))).e}


I will post the ungolfed version on Github and will provide a link when I do so. For now, I will post the original version here:

import java.util.Scanner
import scala.util.Try

trait Operation {
def hexToLong(hex:String):Long = BigInt(hex, 16).longValue()
}

trait Parameter {
def param:String
}

trait RegisterOperation extends Operation { def apply(registers:List[Long]):List[Long] }
trait StackOperation extends Operation { def apply(registers:List[Long], stack:List[Long]):(List[Long], List[Long]) }
trait SpecialOperation extends Operation
trait SpecialRegisterOperation extends SpecialOperation with Parameter  { def apply(registers:List[Long]):Boolean }

class Move(val param:String) extends RegisterOperation with Parameter { override def apply(registers:List[Long]): List[Long] = hexToLong(param) :: registers.tail }
class Swap extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = registers(1) :: registers(0) :: registers(2) :: Nil }
class InputNumber extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = hexToLong(new Scanner(System.in).next("[0-9a-fA-F]+")) :: registers.tail }
class InputAscii extends RegisterOperation {
override def apply(registers:List[Long]): List[Long] = (System.in.read() match {
case i if i == -1 => 0
case i => i}) :: registers.tail }

class PrintNumber extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = { print(Integer.toHexString(registers(2).toInt)); registers } }
class PrintAscii extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = { print(registers(2).toChar); registers } }
class RegisterRollLeft extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = registers.tail :+ registers.head }
class RegisterRollRight extends RegisterOperation { override def apply(registers:List[Long]): List[Long] = registers(2) :: registers(0) :: registers(1) :: Nil }

class Add extends RegisterOperation { override def apply(registers:List[Long]): List[Long] =  registers(0) :: registers(1) :: (registers(0) + registers(1)) :: Nil }
class Subtract extends RegisterOperation { override def apply(registers:List[Long]): List[Long] =  registers(0) :: registers(1) :: (registers(0) - registers(1)) :: Nil }
class Multiply extends RegisterOperation { override def apply(registers:List[Long]): List[Long] =  registers(0) :: registers(1) :: (registers(0) * registers(1)) :: Nil }
class Divide extends RegisterOperation { override def apply(registers:List[Long]): List[Long] =  registers(0) :: (registers(0) % registers(1)) :: (registers(0) / registers(1)) :: Nil }

class Clear extends StackOperation { override def apply(registers:List[Long], stack:List[Long]) = registers -> List() }
class Enqueue extends StackOperation { override def apply(registers:List[Long], stack:List[Long]) = registers -> (registers(2) :: stack) }
class Dequeue extends StackOperation { override def apply(registers:List[Long], stack:List[Long]) = (stack.head :: registers.tail) -> stack.tail }
class QueueRollLeft(val param:String) extends StackOperation with Parameter {
def roll(stack:List[Long], i:Int):List[Long] = {
val s = stack.tail :+ stack.head
if (i > 0) roll(s, i-1) else s
}

override def apply(registers:List[Long], stack:List[Long]) = registers -> roll(stack, Try(param.toInt).toOption.getOrElse(1))
}

class QueueRollRight(val param:String) extends StackOperation with Parameter {
def roll(stack:List[Long], i:Int):List[Long] = {
val s = stack.last :: stack.iterator.sliding(2).map(_.head).toList
if (i > 0) roll(s, i-1) else s
}

override def apply(registers:List[Long], stack:List[Long]) = registers -> roll(stack, Try(param.toInt).toOption.getOrElse(1))
}

class SetLabel(val param:String) extends SpecialOperation with Parameter
class JumpLabelIfZero(val param:String) extends SpecialRegisterOperation { override def apply(registers: List[Long]): Boolean = registers(2) == 0 }
class JumpLabelIfNotZero(val param:String) extends SpecialRegisterOperation { override def apply(registers: List[Long]): Boolean = registers(2) != 0 }

class Script(val instructions:Array[Operation],
val labels:Map[String, Int]) {
def execute() = {
var registers = List[Long](0, 0, 0)
var stack = List[Long]()
var idx = 0;

while(idx < instructions.length) {
idx = instructions(idx) match {
case i: SpecialRegisterOperation => if(i(registers)) labels(i.param) else idx + 1
case i: RegisterOperation => { registers = i(registers); idx + 1 }
case i: StackOperation => { val (zregisters, zstack) = i(registers, stack); registers = zregisters; stack = zstack; idx + 1 }
case _ => idx + 1
}
}
}
}

object Script {
def apply(instructions: Seq[Operation]):Script = {
new Script(instructions = instructions.toArray, labels = Map(instructions.zipWithIndex.flatMap {
case (e:SetLabel, i) => Some(e.param -> i)
case _ => None
}.toList:_*))
}
}

object Parser {
def apply(input:String): Seq[Operation] = {
case class Accumulator(val list: List[(Char, Option[String])] = List(), val char:Option[Char] = None, val str:String = "", val parsingVar:Boolean = false)

(input + " ").foldLeft(Accumulator())((acc, next) => {
next match {
case i if i == ':' => if(acc.parsingVar) Accumulator(acc.list :+ (acc.char.get -> Some(acc.str)), None, "", false) else Accumulator(acc.list, acc.char, "", true)
case i if acc.parsingVar => Accumulator(acc.list, acc.char, acc.str + i, true)
case i if !acc.char.isDefined => Accumulator(acc.list, Some(i), "", false)
case i => Accumulator(acc.list :+ (acc.char.get -> None), Some(i), "", false)
}
}).list.map(x => x._1 match {
case '<' => new Move(x._2.getOrElse(""))
case 'x' => new Swap
case '}' => new SetLabel(x._2.getOrElse(""))
case '|' => new InputNumber
case '!' => new InputAscii
case '%' => new PrintNumber
case '$' => new PrintAscii case '~' => new RegisterRollLeft case '=' => new RegisterRollRight case '^' => new JumpLabelIfNotZero(x._2.getOrElse("")) case '#' => new JumpLabelIfZero(x._2.getOrElse("")) case '?' => new Clear case '>' => new Enqueue case '{' => new Dequeue case '@' => new QueueRollLeft(x._2.getOrElse("")) case '&' => new QueueRollRight(x._2.getOrElse("")) case '+' => new Add case '-' => new Subtract case '*' => new Multiply case '/' => new Divide }) } } object Go extends App { Script(Parser(args(0))).execute() }  Python 3, 1573,1499 bytes Runs the example programs at http://paulo-jorente.de/poncho/esolang/0815/ and implements all the instructions (even write ones, according to the original spec). Executes the source file passed on the command line. import sys from collections import deque class w: q=deque() X=Y=Z=c=0 def __init__(k):k.s=open(sys.argv[1],'r').read() def a(k,l): i=k.s.find("}:"+l+":") if (i<0):sys.exit() return i def l(k): k.c+=1 if k.s[k.c]!=":":sys.exit(":") k.c+=1 return k.s[k.c:k.c+k.s[k.c:].find(":")] def u(k): k.c+=1 if k.s[k.c]!=":":sys.exit(":") k.c+=1 h=k.s[k.c:k.c+k.s[k.c:].find(":")] k.c+=len(h) return h def b(k): g=range j=input z=len w=sys.stdout.write o=k.s[k.c] y=k.q.rotate if o=='<':k.X=int(k.u(),16) elif o=='x':t=k.X;k.X=k.Y;k.Y=t elif o=='=':t=k.X;k.X=k.Z;v=k.Y;k.Y=t;k.Z=v; elif o=='$':
if (k.Z==0x0d):w('\n')
else:w(str(chr(k.Z%256)))
elif o=='%':w(str(k.Z))
elif o=='~':t=k.X;v=k.Y;k.X=v;k.Y=k.Z;k.Z=t;
elif o=='?':k.q.clear()
elif o=='>':k.q.append(k.Z)
elif o=='{':k.X=k.q.popleft()
elif o=='@':
if k.s[k.c+1]==':':
t=k.u()
for i in g(0,int(t,16)):y(-1)
else:
y(-1)
elif o=='&':
if k.s[k.c+1]==':':
t=k.u()
for i in g(0,int(t,16)):y(1)
else:
y(1)
elif o=='}':
k.c+=z(k.l())
elif o=='#':
l=k.l()
if (k.Z==0):
k.c=k.a(l)-1
else:
k.c+=z(l)
elif o=='^':
l=k.l()
if (k.Z!=0):
k.c=k.a(l)-1
else:
k.c+=z(l)
elif o=='|':k.X=int(j("?"),16)
elif o=='!':k.X=ord(j(">")[0])
elif o=='+':k.Z=k.X+k.Y
elif o=='-':k.Z=k.X-k.Y
elif o=='*':k.Z=k.X*k.Y
elif o=='/':
k.Z=int(k.X/k.Y)
k.Y=k.X%k.Y
k.c+=1
def x(k):
while k.c<len(k.s):k.b()
w().x()

• "Runs with some bugs" is not usually an acceptable answer.
– jado
Commented Dec 8, 2015 at 0:40
• @phase: it's fixed now. Commented Dec 8, 2015 at 1:46
• This is code golf, man! why all the whitespace? why all the 2-byte variable names? Commented Dec 9, 2015 at 3:38
• This might be because I'm running it in Python 3 (which works, by the way, after changing j=raw_input to j=input), but it doesn't work for me on the "99 Bottles of Beer" program. A sample ("//" for newline): "0x63//0x63tles of beer on the wall//0x62 one down and pass it around//0x62tles of beer on the wall//0x62tles of beer on the wall//0x61 one down and pass it around") Commented Dec 9, 2015 at 12:22
• @TimPederick: you are right. Did you manage to run the original interpreter (.exe)? What's the expected result? Commented Dec 9, 2015 at 18:59

Flex/C++, 848 838 bytes

%{
#include<map>
#include<deque>
using namespace std;typedef uint64_t I;I X,Y,Z,P,T,i;map<string,I>L;deque<I>Q;
#define YY_USER_ACTION P+=yyleng;
#define A for(i=0;i<(yyleng>2?stoull(yytext+2,0,16):1);++i
#define B fseek(yyin,L.at(yytext+1),0),yyrestart(yyin);
%}
H :[0-9A-F]+:
B :[0-9a-zA-Z_]+:
%x E
%%
<E>\}{B} L[yytext+1]=P;
<E>.|\n
\<{H} X=stoull(yytext+2,0,16);
x T=X;X=Y;Y=T;
\}{B}
\| scanf("%lx",&X);
! X=getchar();
% printf("%lx",Z);
$putchar(Z); ~ T=X;X=Y;Y=Z;Z=T; = T=Y;Y=X;X=Z;Z=T; \^{B} if(Z)B #{B} if(!Z)B \? Q.clear(); > Q.push_back(Z); \{ X=Q.front();Q.pop_front(); @{H}? A)T=Q.front(),Q.pop_front(),Q.push_back(T); &{H}? A)T=Q.back(),Q.pop_back(),Q.push_front(T); \+ Z=X+Y; - Z=X-Y; \* Z=X*Y; \/ Z=X/Y;Y=X%Y; .|\n %% main(int,char**v){yyin=fopen(v[1],"r");BEGIN(E);yylex();rewind(yyin);yyrestart(yyin);BEGIN(0);yylex();}  Compiled with: flex -o 0815.cpp 0815.ll g++ -std=c++11 0815.cpp -o 0815 -ll  It might compile with other lexes as well, but I didn't check. It works in two passes, so forward jumps are handled correctly. Label names are allowed to start with a digit, as it happens more than once in the test cases. Hex literals can only be upper case, in accordance with the specs. Passes all test cases, including those found on the Esolangs Wiki and the 0815 page. Error handling is non-existent and exit on an unknown label is not graceful, after all this is code-golf. I am preparing the ungolfed (and much nicer) interpreter for a release, so that OP can keep tinkering with 0815. Stay tuned :) • The 99 bottles of beer examples uses \r instead of \n as new line (old-school Mac OS?), so if you want to see something printed on screen use ./0815 99bb.0815 | tr "\r" "\n". Same for Fibonacci. Commented Dec 14, 2015 at 19:04 Common Lisp, 1088 bytes (progn(defun r(s)(parse-integer s :radix 16))(defun h(k)(coerce(loop for c =(#7=read-char()())while(funcall k c)collect c finally(unread-char c))'string))(defun w(c)(find c"0123456789ABCDEFabcdef"))(defmacro c(s)(with-input-from-string(*standard-input* s)(labels((L()(intern(p #'u)))(o()(case(peek-char()())(#\:(r(q))(#7#))(t 1)))(q()(p #'w))(p(k)(prog2(#7#)(h k)(#7#)))(u(c)(char/= c #\:)))(let((x 0)(y 0)(z 0)(q(list())))(labels((&(n)(q(nconc(last(car q)n)(butlast(car q)n))))(@(n)(q(nconc(nthcdr n(car q))(subseq(car q)0 n))))(q(x)(#3=setf q(cons x(last x)))))(prog(),@(loop for c =(#7#()())while c collect(case c(#\<(#3#x,(r(q))))(#\x(#5=rotatef x y))(#\}(L))(#\|(#3#x(r(h'w))))(#\!(#3#x(char-code(#7#))))(#\%(format t"~x"z))(#$(princ(code-char z)))(#\~(#5#x y z))(#\=(#5#z y x))(#\^(if(/= z 0)(go,(L))))(#\#(if(= 0 z)(go,(L))))(#\?(#3#q(q())))(#\>'(if(car q)(#3#(cddr q)(list z)(cdr q)(cddr q))(#3#(cdr q)(#3#(car q)(list z)))))(#\{'(#3#x(pop(car q))))(#\@(@,(o)))(#\&(&,(o)))(#\/'(#3#(values z y)(truncate x y)))(t(#3#z(,(elt'(+ * -)(position c"+*-"))x y))))))))))))


Ungolfed

With optional trace at runtime.

;;; Those auxiliary functions are needed both
;;; during macroexpansion and evaluation

(defun r(s)
(parse-integer s :radix 16))

(defun h(k)
(coerce (loop for c = (read-char nil nil)
while (funcall k c)
collect c
finally (unread-char c))
'string))

(defun w(c)
(find c "0123456789ABCDEFabcdef"))

;;; C is a macro, it takes a string, replaces it by
;;; code, which is then evaluated.

(defmacro C(s &optional tracep)
(with-input-from-string(*standard-input* s)
(labels((L()(intern(p #'u)))
(d()(assert(eql #\:(read-char))))
(o(d)(case (peek-char () () ())
(#\:(r(q))(d))
(t d)))
(q()(p #'w))
(p(k)(prog2(d)(h k)(d)))
(u(c)(not(eql c #\:))))
(let((x 0)(y 0)(z 0)(q(list())))
(labels((&(n)(q(append(last(car q)n)(butlast(car q)n))))
(@(n)(q(nconc(nthcdr n(car q))(subseq(car q)0 n))))
(q(x)(setf q(cons x(last x)))))
(tagbody
,@(loop for c =(read-char nil ())
while c
when tracep
collect '(fresh-line)
and collect (print (list :c ,c :x x :y y :z z :q q :s (map'string'code-char(car q))))
collect
(ecase c
(#\<(setf x ,(r(q))))
(#\x(rotatef x y))
(#\}(L))
(#\|(setf x(r(h'w))))
(#\!(setf x(char-code(read-char))))
(#\%(format t"~x"z))
(#\$(princ(code-char z))) (#\~(rotatef x y z)) (#\=(rotatef z y x)) (#\^(if(not(zerop z))(go,(L)))) (#\#(if(zerop z)(go,(L)))) (#\?(setf q(q()))) (#\>'(if(car q)(setf(cddr q)(list z)(cdr q)(cddr q))(setf(cdr q)(setf(car q)(list z))))) (#\{'(setf x(pop(car q)))) (#\@(@,(o 1))) (#\&(&,(o 1))) (#\/'(multiple-value-setq(z y)(truncate x y))) ((#\+ #\* #\-)(,(intern(string c)"CL")x y))) when tracep collect (print (list :c ,c :x x :y y :z z :q q :s (map'string'code-char(car q)))))))))))  Fibonnaci (c "%<:0A:>~$<:01:~%>=<:68a3dd8e61eccfbd:>~>}:_s:{x{={~\$x+%{=>~>x~-x<:0A:~>~>~^:_s:?")


Macroexpansion

(LET ((X 0) (Y 0) (Z 0) (Q (LIST NIL)))
(LABELS ((& (N)
(Q (NCONC (LAST (CAR Q) N) (BUTLAST (CAR Q) N))))
(@ (N)
(Q (NCONC (NTHCDR N (CAR Q)) (SUBSEQ (CAR Q) 0 N))))
(Q (X)
(SETF Q (CONS X (LAST X)))))
(PROG ()
(FORMAT T "~x" Z)
(SETF X 10)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y Z)
(PRINC (CODE-CHAR Z))
(SETF X 1)
(ROTATEF X Y Z)
(FORMAT T "~x" Z)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF Z Y X)
(SETF X 7540113804746346429)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y Z)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
|_s|
(SETF X (POP (CAR Q)))
(ROTATEF X Y)
(SETF X (POP (CAR Q)))
(ROTATEF Z Y X)
(SETF X (POP (CAR Q)))
(ROTATEF X Y Z)
(PRINC (CODE-CHAR Z))
(ROTATEF X Y)
(SETF Z (+ X Y))
(FORMAT T "~x" Z)
(SETF X (POP (CAR Q)))
(ROTATEF Z Y X)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y Z)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y)
(ROTATEF X Y Z)
(SETF Z (- X Y))
(ROTATEF X Y)
(SETF X 10)
(ROTATEF X Y Z)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y Z)
(IF (CAR Q)
(SETF (CDDR Q) (LIST Z)
(CDR Q) (CDDR Q))
(SETF (CDR Q) (SETF (CAR Q) (LIST Z))))
(ROTATEF X Y Z)
(IF (/= Z 0)
(GO |_s|))
(SETF Q (Q NIL)))))


Output

0
1
1
2
3
5
8
D
15
22
37
59
90
...
5D4D629E80D5489
96F75D79B354522
F444C01834299AB
18B3C1D91E77DECD
27F80DDAA1BA7878
40ABCFB3C0325745
68A3DD8E61ECCFBD


Maybe more explanations later