Escreva um intérprete de frango!

8

Você precisa escrever um intérprete para um idioma legal chamado Chicken !

Você deve ler um programa Chicken a partir de um arquivo, entrada padrão, argumentos de programa ou função, ou o que for mais conveniente para o seu idioma, bem como a entrada para o programa.

Você deve imprimir ou retornar o resultado da interpretação do programa de acordo com a especificação do idioma Chicken.

Mais descrição sobre o idioma .


Visão Geral do Programa Chicken

O frango opera em uma única pilha, que compõe todo o seu modelo de memória. À medida que as instruções são executadas, o programa pressiona e pop valores da pilha, mas também existem instruções que permitem ao programa modificar outras partes da pilha à vontade.

Existem três segmentos na pilha:

  1. Os registros, nos índices 0 e 1. O índice 0 é uma referência à própria pilha e o índice 1 é uma referência à entrada do usuário. Usado principalmente para a instrução 6 (veja abaixo).
  2. O código carregado: para cada linha de código, há célula neste segmento que contém o número de "galinhas" na linha. Isso é preenchido com um 0 (opcode para encerrar o programa) no final.
  3. A pilha real do programa, na qual os valores são enviados / atualizados à medida que o programa é executado. Observe que os segmentos não são isolados, o que significa que é possível criar código auto-modificável ou executar código a partir desse segmento do espaço de pilha.

The Chicken ISA

O conjunto de instruções do Chicken é baseado no número de vezes que a palavra "chicken" aparece em cada linha do programa. Uma linha vazia finaliza o programa e imprime o valor mais alto na pilha.

O conjunto de instruções Chicken, pelo número de "chicken" s por linha:

  1. Empurre a string literal "chicken" para a pilha
  2. Adicione os dois principais valores da pilha como números naturais e pressione o resultado.
  3. Subtraia os dois principais valores como números naturais e empurre o resultado.
  4. Multiplique os dois principais valores como números naturais e pressione o resultado.
  5. Compare dois valores principais para igualdade, pressione 1 se forem iguais e 0 caso contrário.
  6. Veja a próxima instrução para determinar de qual fonte carregar: 0 carga da pilha, 1 carga da entrada do usuário. Pontos principais da pilha para endereçar / indexar para carregar da fonte especificada; carregue esse valor e empurre-o para a pilha. Como esta é uma instrução de largura dupla, o ponteiro da instrução ignora a instrução usada para determinar a fonte.
  7. Pontos principais da pilha para endereçar / indexar para armazenar. O valor abaixo disso será exibido e armazenado na pilha no índice especificado.
  8. O topo da pilha é um deslocamento relativo para o qual pular. Se o valor abaixo desse valor for verdadeiro, o programa passará pelo deslocamento.
  9. Interpreta o topo da pilha como ascii e pressiona o caractere correspondente.
  10. (10 + N) Coloca o número literal n-10 na pilha.

Exemplo

Suponha que o programa seja:

chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
(an empty line)

(Um programa para gatos. Observe que a linha vazia é necessária devido à linha anterior ter 6 "galinhas".)

Entrada fornecida ao programa Chicken

Chicken

Resultado

Chicken

A implementação de referência Chicken.js .


Detecção de erro

O intérprete deve deixar um erro e terminar quando qualquer palavra que não seja "frango" estiver presente na fonte.


Boa sorte!

Value Ink
fonte
3
Você precisa copiar as especificações de idioma para a pergunta. As perguntas não devem depender de links externos.
mbomb007
Bem, por que você também não faz isso?
1
É a sua pergunta. Você determina as especificações.
mbomb007
5
A entrada do arquivo restringe esse desafio a idiomas específicos. Por exemplo, torna impossível produzir uma resposta de frango, o que tenho certeza que você concorda que é decepcionante.
Aaron

Respostas:

1

Ruby, 335 bytes

Pega o nome do arquivo de entrada como argumento da linha de comando e recebe a entrada do usuário (para a instrução nº 6) de STDIN.

Devido ao fato de Ruby "truthy" (tudo, exceto falsee nil) ser diferente do Javascript "truthy" (Ruby truthy plus 0, strings vazias etc.), pode haver alguns casos extremos em que os programas que funcionam bem em um interpretador JS falham nesse caso. por causa da instrução 8, como se estivesse ""na pilha. Corrigi o maior caso, porém, que é falso 0.

Funciona com o programa de teste e o programa Hello World no site do Chicken.

+(/^(#{c='chicken'}|\s)*$/m=~f=$<.read+"

")
s=[0,STDIN.read]+f.lines.map{|l|l.split.size};s[0]=s;i=1
s<<(s[i]<10?[->{c},->{x,y=s.pop 2;x+y},->{x,y=s.pop 2;x-y},->{s.pop*s.pop},->{s.pop==s.pop},->{s[s[i+=1]][s.pop]},->{s[s.pop]=s.pop;s.pop},->{l,k,j=s.pop 3;i+=j if k&&k!=0;l},->{s.pop.chr}][s[i]-1][]:s[i]-10)while s[i+=1]>0
$><<s.pop

Explicação

O intérprete começa imediatamente executando uma correspondência de expressão regular em /^(chicken|\s)*$/mtodo o arquivo ( $<.read), o que garante que o arquivo contenha nada além de chickenespaço em branco. No Ruby, esse operador retorna o índice para a correspondência ou nilse não foi encontrado.

Dois truques de economia de bytes são usados ​​aqui: em vez de corresponder diretamente chicken, o operador de substituição de string #{}é usado para atribuir essa string a uma variável para mais tarde (salva 1 byte) e ao armazenar o conteúdo do arquivo em uma variável para processamento , ele anexa duas novas linhas para permitir que a linesfunção posteriormente adicione naturalmente um extra 0ao final do conjunto de instruções. (São necessários dois por causa da nova linha à direita ignorada, necessária para o programa Chicken).

O erro usado é o NoMethodError: undefined method '+@' for nil:NilClassque é feito envolvendo a correspondência de expressões regulares em parênteses e colocando uma +na frente. Se o arquivo corresponder ao padrão, você obtém +0, que avalia 0e prossegue normalmente.

Em seguida, a pilha é montada. A lista inicial deve ser criada antes que a referência própria à pilha possa ser atribuída, para que um espaço reservado seja usado e depois substituído. O ponteiro de instrução está definido como em 1vez de, 2porque não existem operadores pós-incremento no Ruby.

Por fim, ele usa o truque lambda de @BassdropCumberwubwubwub para determinar o que colocar na pilha a seguir. Se uma operação não coloca nada na pilha, o intérprete simplesmente gera um valor extra para que a pilha permaneça a mesma. (Isso economiza bytes ao adicionar uma operação de envio a cada lambda.)

Código não destruído:

f = $<.read + "\n\n"
+(/^(chicken|\s)*$/m =~ f)
s = [0, STDIN.read] + f.lines.map{|l|l.split.size}
s[0] = s
i = 1

while s[i += 1] > 0
    if s[i] < 10
        s.push [
            ->{'chicken'},
            ->{
                x,y = s.pop 2
                x+y
                },
            ->{
                x,y = s.pop 2
                x-y
                },
            ->{s.pop*s.pop},
            ->{s.pop==s.pop},
            ->{s[s[i+=1]][s.pop]},
            ->{s[s.pop]=s.pop;s.pop},
            ->{
                l,k,j=s.pop 3
                i+=j if k&&k!=0
                l
                },
            ->{s.pop.chr}
        ][s[i] - 1][]
    else
        s.push(s[i] - 10)
    end
end

print s.pop
Value Ink
fonte
Na verdade, acho que não posso fazer isso curto. (+1)
4

Javascript ES6, 398 bytes

De longe, o golfe mais longo que já fiz, tenho certeza de que isso pode ser melhorado, mas meu cérebro não reconhece outra coisa senão chickenneste momento.

(a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

Vou editar a explicação quando meu cérebro começar a funcionar novamente. Aqui está uma versão um pouco não destruída por enquanto.
Emite um valor de falsey (0) para tudo o que não échicken

(a,b)=>{
    for(c='chicken',s=[j=0,b,...A=a.split`
    `.map(m=>m.split(c).length-1)],i=A.length+2; // loop init
    j<A.length; // loop condition
    ( // everything else
        [
            _=>s[++i]=c,
            _=>s[--i]=s[i]+s[i+1],
            _=>s[--i]=s[i]-s[i+1],
            _=>s[--i]=s[i]*s[i+1],
            _=>s[--i]=s[i]==s[i+1],
            _=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],
            _=>s[s[i--]]=s[i--],
            _=>j+=s[--i]?s[--i+2]:0,
            _=>s[i]=String.fromCharCode(s[i])
        ][s[j+2]-1]
        ||(_=>s[++i]=s[j+1]-10)
    )(j++)
);
return /[^chicken \n]\w/g.test(a)?0:s[i]}

Experimente aqui

f=
  (a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

i.innerHTML = f(`chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
`, 'Hello world!')
<pre id=i>

Bassdrop Cumberwubwubwub
fonte
Bem, deixe-me esperar por mais algumas respostas e descobrir quem é o vencedor.
Isso falha na "detecção de erro". Você pode fazer isso adicionando if(!/^(chicken\s?)+$/.test(a))throw'There are any words except "chicken".';logo no início do seu intérprete.
Ismael Miguel
@ Matthew, o que você acha disso? Existem certos idiomas que não têm um tipo de erro; geralmente, eles podem gerar um valor falsey. É meio vago no OP, então eu assumi que estava tudo bem.
Bassdrop Cumberwubwubwub
Você pode emitir o erro, dizendo que algo estava errado.
1
@BassdropCumberwubwubwub O que o OP significava é, por exemplo, lançar uma exceção ou gerar algo para stderrou sair do programa com um código diferente de zero. Algo que mostra que algo não está certo. Em Javascript, você pode lançar uma exceção, retornar um objeto Error, mostrar um alerta, gravar no console usando console.erro()ou algo semelhante.
Ismael Miguel