Golfe um intérprete roxo

13

Golfe um intérprete roxo

Roxa é um esolang que é projetado com dois objetivos principais:

  • Para ser uma minimização de Aubergine , já que simplesmente não existem linguagens de instrução única modificáveis ​​o suficiente.
  • Admitir a possibilidade de terrivelmente pequena intérpretes de golfe. Minha primeira passagem em um intérprete razoavelmente completo de Python 2 tem apenas 702 bytes, e tenho certeza de que um jogador mais experiente pode se barbear um pouco com isso.

Seu objetivo é escrever um intérprete para esse idioma.

Informação sobre Estados Unidos da América:

Um programa Purple é uma sequência de caracteres inseridos em uma matriz de memória infinita e endereçável, de modo que o primeiro caractere do programa seja colocado no endereço zero. O restante da matriz (antes e depois de onde o programa Purple está armazenado) é inicializado como zero.

Há três registros em roxo, chamados a e b e i , cada um dos quais pode conter um inteiro assinado e é inicializado para zero. i também é o ponteiro da instrução e sempre aponta para a instrução Purple em execução no momento.

A cada ciclo, o intérprete lê uma sequência de três caracteres contíguos, começando na localização da memória indicada pelo ponteiro da instrução e tenta executar essa sequência como a instrução Roxa. Depois, o ponteiro da instrução é sempre incrementado em 3.

Sintaticamente, a instrução Purple consiste em três caracteres (ou codificações) em uma linha, como " xyz ".

O primeiro caractere x pode ser um dos seguintes:

abABio

Esses símbolos têm o seguinte significado:

a - Place the result in register a.
b - Place the result in register b.
A - Place the result in the location in memory referred to by register a.
B - Place the result in the location in memory referred to by register b.
i - Set the instruction pointer to the result.
o - Output the result to stdout.

Os outros dois bytes y e z podem ser qualquer um dos seguintes:

abABio1

Cada um desses símbolos tem o seguinte significado:

a - Return the contents of register a.
b - Return the contents of register b.
A - Return the contents of the memory array at the address stored in register a.
B - Return the contents of the memory array at the address stored in register b.
i - Return the contents of register i (the instruction pointer).
o - Return the value of a single character read from stdin.
1 - Return the literal numeric value 1.

Após buscar a instrução, o intérprete Purple avaliará ye depois z , subtrairá o resultado de z do resultado de y e executará a ação indicada por x na diferença.

Se a sequência de três caracteres (ou suas codificações) não for uma instrução roxa válida, o intérprete será interrompido imediatamente sem erros.

Seu intérprete deve:

  • Seja um programa completo, não uma função.
  • Nunca envie para stderr, a menos que EOF seja lido .
  • Comporte-se de maneira idêntica à implementação de referência em todas as entradas bem formadas que não envolvam números muito grandes, incluindo os programas de teste fornecidos abaixo. (Bem, identicamente até o momento - ele pode ser mais lento, mas não muito!)

Você pode fornecer o programa ao intérprete da forma que desejar: leia-o de um arquivo, incorpore-o ao programa como uma string ou leia-o de stdin.

Casos de teste:

O programa

ooo

quando executado com entrada

z!

deve render

Y

O programa

bbboobiii

quando executado com entrada

It's a cat program.

(ou qualquer outra entrada) deve render

It's a cat program.

(ou qualquer outra entrada recebida) e, em seguida, comece novamente e faça a mesma coisa novamente .


O programa

Aoab11bi1bABoAaiba

quando executado com entrada

0

deve render

0

e depois parar, mas quando executado com entrada

1

deve continuar produzindo

1

para sempre.


O programa

b1bbb1oAbabaa1ab1Ab1Bi1b

deve render

b1bbb1oAbabaa1ab1Ab1Bi1b

O programa

aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG

deve render

Hello, World!

Pontuação:

Esse é o , e a fonte mais curta em bytes, potencialmente modificada pelo bônus a seguir, vence.

Bônus:

  • -10% se o seu intérprete lê um nome de arquivo do stdin ou de um argumento da linha de comando e carrega o programa a partir do arquivo.
quintopia
fonte
1
Qual é o tamanho das células da memória? bytes, caracteres (unicode?), inteiros grandes (arbitrários)? Parece que você está usando "caractere" e "byte" com o mesmo significado.
Paŭlo Ebermann 01/12/15
@ PaŭloEbermann, meu palpite é que é específico da implementação; por exemplo, eu preciso usar uint32caracteres e MAXINT para ints
cat
2
@sysreq Isso é realmente um bloqueador? Sua implementação pode simplesmente ter duas fitas, uma para índices negativos e outra para índices positivos. (Sim, isso vai levar um código mais pouco, mas não muito, eu acho.)
Paulo Ebermann
1
@ sysreq basicamente, um auto-intérprete Purple seria um programa que lê um programa Purple a partir de stdin e depois faz o que esse programa faria. O primeiro programa Purple (o intérprete) pode ser executado em qualquer intérprete que você desejar. Um programa que sobrescreve completamente os endereços de memória mais baixos com a entrada e se exclui antes de qualificar de alguma forma o salto para o código de leitura (embora eu ache que isso não seja possível).
quintopia 03/12/2015
2
Eu cheguei tão perto de ter um tempo de execução capaz de auto-interpretação, mas era tarde demais.
gato

Respostas:

7

Pyth, 148 128 121 bytes (ou 124 * .9 = 111.6, veja abaixo)

J,00=kjb.z .eXHkCbz#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Suíte de teste

Código fornecido na primeira linha do STDIN, entrada para o programa Purple no restante do STDIN. Para usar código com novas linhas, use a versão alternativa na parte inferior.

Razoavelmente golfe. Aqui está isso com quebras de linha e recuo para maior clareza:

J,00
=kjb.z
 .eXHkCbz
#
  =b-Fm
    ?=zx"oabABi1"C@H+Zd
      @
        s[0Jm.x@Hk0JZ1H)
        z
      Ch~tk
    S2
   ?hKx"abAB"=YC@HZ
    ?PK
      XH@JKb
      XJKb
  ?qY\i=Zb
  ?qY\opCb
  vN
  =+Z3

Basicamente, um #loop executa e interrompe via quebra de erro.

ae bsão combinados em uma única variável J,. Zé o ponteiro da instrução. ké inserido no programa Purple. Hé a fita, representada como um dicionário. bé o resultado atual.Yé o primeiro byte atual da instrução.

Lendo do arquivo:

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

Dê o nome do arquivo como primeira linha de STDIN. Execução de teste:

$ cat purple-final.pyth 
J,00=kjb.z .eXHkCbjb'z#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
$ cat purple-code.txt 
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG
$ pyth purple-final.pyth <<< 'purple-code.txt' 
Hello, World!
isaacg
fonte
5

JavaScript (ES6), 292 bytes

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=="))

Explicação

As respostas JavaScript são sempre estranhas quando STDINe STDOUTsão necessárias ...

O primeiro prompt é a entrada para a sequência de programas. Cada prompt que resulta de umo instrução lerá apenas o primeiro caractere.

evalé usado para substituir uma frase comum que salva alguns bytes. Ungolfed e sem o evalprograma é assim:

// Initialisation
a=b=i=                            // initialise registers to 0
  d=0;                            // d is set to true when the program should die

// Gets the result of Y or Z
v=n=>                             // n = offset from i
  (x=m[i+n])==97?a:               // x = value of instruction
  x==98?b:
  x==65?m[a]:
  x==66?m[b]:
  x==105?i:
  x==111?p()[c]():
  x==49?1:
  d=1;                            // if it was none of the valid values, die

// Execution loop
for(
  m=                              // m = memory array
    [...(p=prompt)()]             // receive the program
    .map(b=>b[c="charCodeAt"]()); // initialise m to the ASCII values of the program
  !d;                             // finish if an error occured
  i+=3                            // increment i
)
  (y=v(1),d)||                    // get the value of Y and check for errors
  (z=v(2),d)?1:                   // get the value of Z and check for errors

    // Get the result of X
    (x=m[r=y-z,i])==97?a=r:       // r = result of y - z
    x==98?b=r:
    x==65?m[a]=r:
    x==66?m[b]=r:
    x==105?i=r-3:
    x==111?alert(String.fromCharCode(r)):
    d=1
user81655
fonte
2
O segundo pode c="charCodeAt"ser substituído por apenas c?
Dendrobium
O acesso ao array com índices negativos funciona em JavaScript?
nimi
@Dendrobium Uau, eu não sei como eu senti falta disso haha! Obrigado.
User81655
2
@nimi Funciona. As próprias matrizes não suportam índices negativos, mas isso tira vantagem do fato de que elas também se comportam como objetos. array[-1] = 1é o mesmo que array = { "-1": 1 }. Ambos podem ser acessados ​​com array[-1].
user81655
@ user81655: Ah legal, não sabia disso.
nimi
3

Ceilão, 827 792 671 bytes

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){}}

Ele se comporta de maneira um pouco diferente da implementação de referência quando o programa tenta ler a entrada no EOF - a implementação de referência trava com um TypeError, que é muito caro para reproduzir aqui (e provavelmente um bug), portanto, retornará -1 ( repetidamente, se necessário).

(Ao tentar gravar este -1 no stdout, o intérprete terminará com um OverflowError. Semelhante acontecerá se um número inteiro fora do intervalo Unicode for gerado.)

O intérprete toma o programa como seu primeiro argumento de linha de comando (lembre-se de citá-lo para seu shell quando ele contiver espaço em branco ou outras coisas interessantes).

No Ceilão, podemos ler facilmente as entradas de maneira fácil (acho que isso mudará em uma das próximas versões); portanto, quando oé usado para leitura, estou lendo uma linha inteira e armazenando as peças em buffer para usos futuros. Eu acho que funciona de maneira semelhante na implementação do Python quando conectado a um terminal.


Ao tentar executar um comando (parte) que não é um dos caracteres válidos, isso nothingfará com que um AssertionError seja lançado, que então capturamos no bloco catch ao redor do loop principal.

Eu acho que esse preferencialmente deve ser um tipo de exceção personalizado (como AssertionError também pode ocorrer em outros lugares, se eu tiver um bug), mas isso exigiria muito mais espaço, consumindo a maioria das melhorias que fiz da primeira versão.

Alguns truques usados ​​para jogar golfe:

  • As versões anteriores usavam um ceylon.collection.HashMap - agora usamos um mapa imutável criado pela mapfunção e criamos um novo a cada vez AouB é usado como x .
  • Eu uso alias-imports para todos os identificadores de ceylon.language que são usados ​​mais de uma vez (incluindo o variable anotação, que é agora l).
  • Eu me livrei da classe E(para o meio ambiente) e dos método (step) - tudo agora acontece dentro da runfunção.
  • Em vez de usar .integerpara obter o ponto de código de um caractere,.hash obtém o mesmo resultado. Assim string*.hashé o mesmo que string.map(Character.integer)(fornece uma iterável dos pontos de código de uma string).
  • Quando um tipo é importado por alias, is I ... é menor que exists ....
  • Ao converter algo (por exemplo x) em uma string,"``t``" é mais curto que t.string(ou, o que eu usei para um caractere String{t}).
  • as funções usadas apenas uma vez podem ser incorporadas.

Aqui está a versão formatada (e comentada):

// Purple – a self-modifying, "one-instruction" language.
//
// Question:  http://codegolf.stackexchange.com/q/65411/2338
// My answer: http://codegolf.stackexchange.com/a/65492/2338

import ceylon.language {
    l=variable,
    I=Integer,
    x=nothing,
    p=process,
    m=map
}

shared void run() {
    try {
        // Reading code from file certainly takes more than 73 characters,
        // this isn't worth the 10% bonus.
        if (exists d = p.arguments[0]) {

            // The memory tape, as a Map<Integer, Integer>.
            // We can't modify the map itself, but we
            // can replace it by a new map when update is needed.
            l value t = m {
                // It is initialized with the code converted to Integers.
                // We use `.hash` instead of `.integer` because it is shorter.
                *d*.hash.indexed };

            // three registers
            l I a = 0;
            l I b = 0;
            l I i = 0;

            // get value from memory
            I g(I j) =>
                    t[j] else 0;

            // cached input which is still to be read
            l {I*} c = [];

            // get value from stdin.
            // we can only comfortably access stdin by line, so we read a whole line
            // and cache the rest for later.
            I o {
                if (c == []) {
                    if (exists e = p.readLine()) {
                        c = e*.hash.chain { 10 }; // convert string into ints, append \n
                    } else {
                        // EOF – return just -1 from now on.
                        c = { -1 }.cycled;
                    }
                }
                assert (is I r = c.first);
                c = c.rest;
                return r;
            }


            // Map of "functions" for fetching values.
            // We wrap the values in iterable constructors for lazy evaluation
            //  – this is shorter than using (() => ...).
            // The keys are the (Unicode/ASCII) code points of the mapped
            // source code characters.
            value f = m {
                // a
                97 -> { a },
                // b
                98 -> { b },
                // A
                65 -> { g(a) },
                // B
                66 -> { g(b) },
                // i
                105 -> { i },
                // o
                111 -> { o },
                // 1
                49 -> { 1 }
            };

            // Map of functions for "storing" results.
            // The values are void functions taking an Integer,
            // the keys are the ASCII/Unicode code points of the corresponding
            // source code characters.
            value s = m {
                // a
                97 -> ((I v) => a = v),
                // b
                98 -> ((I v) => b = v),
                // Modification of the memory works by replacing the map with a new one.
                // This is certainly not runtime-efficient, but shorter than importing
                // ceylon.collections.HashMap.
                // A
                65 -> ((I v) => t = m { a->v, *t }),
                // B
                66 -> ((I v) => t = m { b->v, *t }),
                // i
                105 -> ((I v) => i = v),
                // o – output as a character.
                111 -> ((I v) => p.write("``v.character``"))
            };

            // accessor function for the f map
            I h(I v) =>
                    f[v]?.first else x;

            // the main loop, can only be left by exception
            while (0 < 1) {
                (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
                i += 3;
            }
        }
    } catch (AssertionError e) {
        // abort silently
    }
}
Paŭlo Ebermann
fonte
Eu reutilizei parte desse código para um "intérprete paralelo" tentando encontrar todos os programas interrompidos. (Existem muitos deles.) (Lá eu usei uma versão não-I / O do Purple, pois a E / S ocupa muito espaço e não é usada nessa tarefa.)
Pa Elo Ebermann