brainfuck
Algum tempo atrás escrevi um artigo sobre como escrever código Javascript com apenas seis caracteres. A ideia por trás disso era abusar da conversão implícita do Javascript para criação de componentes da linguagem, assim executando o código.
brainfuck
O brainfuck é uma outra linguagem de programação considerada minimalista já que contém apenas oito comandos, cada um com um caractere específico.
A ideia básica desta linguagem é poder mover entre ponteiros de memórias e incrementar ou decrementar os valores desses ponteiros, com a possibilidade ainda de realizar loops. Essas características fazem com que ela seja considerada uma linguagem Turing completa.
O brainfuck possui os seguintes comandos:
comando | funcionalidade |
---|---|
> | incrementa o ponteiro de célula selecionada |
< | decrementa o ponteiro de célula selecionada |
+ | incrementa em 1 o valor da célula selecionada |
- | decrementa em 1 o valor da célula selecionada |
. | imprime na tela o caractere da célula selecionada |
, | salva na célula selecionada o input do usuário |
[ | início da estrutura de controle para loops (enquanto a célula selecionada for zero) |
] | fim da estrutura de loop |
Criando um interpretador
Por conta da sua simplicidade, o desenvolvimento de um interpretador para programas brainfuck não é muito difícil. Para provar isso, vamos desenvolver em Javascript um interpretador para esta linguagem.
O primeiro passo para criarmos um interpretador é definirmos quais as variáveis de controle precisaremos. Para o nosso caso vamos utilizar as seguintes:
- Memória: Esta será a memória do nosso programa;
- Ponteiro de instrução: Este ponteiro irá indicar qual instrução do programa estamos executando;
- Ponteiro de memória: Este ponteiro indica qual posição de memória o programa está atualmente. Como a linguagem se baseia em navegar em pontos de memórias para atualizar os valores, essa é uma das partes essenciais do interpretador;
- Pilha de loop: Esta pilha será utilizada para saber quando um loop começa para podermos voltar para a instrução quando o fim do loop é encontrado. Utilizamos a estrutura de pilha pois a linguagem permite agregação de loops, ou seja, um loop dentro do outro;
- Programa: Onde o programa ficará armazenado;
- Entrada: Onde serão armazenados os dados de entrada;
- Saída: Onde serão armazenados os dados de saída, exibidos ao usuário ao final da execução.
1 | class Brainfuck { |
Após isso precisamos implementar as funcionalidades de cada instrução válida. As instruções de entrada e saída, “,“ e “.“ respectivamente, irão manipular o valor da posição de memória corrente para inserir/buscar o valor.
1 | // A função "imprimir" deve pegar o valor do ponteiro de memória atual e |
Na sequência, implementamos as funcionalidades de avançar e voltar no ponteiro de memória. Essas são as funcionalidades que serão disparadas com as instruções “>“ e “<“.
1 | // A função "avançar" deve adicionar novos espaços de memória (caso o ponteiro já esteja no último) |
As instruções mais simples são as de incrementar e decrementar a posição de memória corrente (respectivamente “+“ e “-“).
1 | // A função "incrementar" deve incrementar o valor na posição de memória |
Por fim as funcionalidades de iniciar loop (“[“) e finalizar loop (“]“) são as mais complexas. O loop funciona verificando se o ponteiro de memória está apontando para uma posição de memória com valor zero ou diferente de zero.
Caso o valor seja diferente de zero, indica que um loop iniciou. As instruções desse loop serão executadas até o fim e o ponteiro de instrução volta para o início. Caso o valor continue diferente de zero, o loop continua.
Quando a instrução de início de loop for executada em um ponteiro de memória igual a zero, o loop termina e o ponteiro de instrução vai para o fim do loop.
1 | // A função "iniciarLoop" deve adicionar na pilha de loop o ponteiro que |
Para concluir a nossa classe interpretadora de brainfuck adicionamos um construtor para receber o programa, um objeto para englobar nossas instruções e um método para executar a aplicação.
1 | constructor(programa) { |
Para executarmos um programa podemos fazer, por exemplo, da seguinte forma, onde executamos o famoso “Hello World!”:
1 | const programaBrainfuck = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; |
Quer testar o programa? Você pode utilizar a caixa de texto abaixo:
Conclusão
Como vimos, um interpretador brainfuck é bem simples de ser desenvolvido e é uma ótima brincadeira para se fazer quando deseja aprender uma nova linguagem.
Caso queira ver o código completo criado, você pode verificar abaixo.
1 | class Brainfuck { |