Scala, 3,123 2,844 2,626 2,540 octets
En plus des contraintes décrites dans la question, cet interprète a été écrit pour se pencher autant que possible sur les principes de PF. Plus précisément:
- Seules les structures immuables
- Toutes les fonctions sont pures
Cela a été accompli, à l'exception des quatre lignes de code qui pilotent la boucle principale de l'interpréteur. Les structures immuables étaient très difficiles à utiliser ici car l'état des registres entraîne le flux de contrôle de la boucle (en particulier les deux instructions GOTO). Je réfléchis toujours à la façon de le convertir pour utiliser des structures pures et immuables, mais cela n'a rien à voir avec le défi du code golf.
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}
Je publierai la version non golfée sur Github et fournirai un lien lorsque je le ferai. Pour l'instant, je posterai la version originale ici:
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()
}
~
et~
dans vos commandes "roll registres"? Avez-vous un exemple de programme que les gens peuvent utiliser pour tester?