Esta é a etapa final, e o programa já interpreta seis comandos, faltando apenas mais dois.
Adicione uma variável chamada colchetes, a qual guardará quantas estruturas de repetição (a estrutura “[” é o início e “]” é o fim, em brainfuck) há no arquivo. Em seguida, adicione uma estrutura de repetição antes da declaração da pilha, de preferência uma do tipo for
, com a condição de repetir até o índice apontar para o último elemento da variável fonte e, dentro dela coloque uma condição, a qual se o elemento examinado for igual “[”, deverá aumentar o valor da variável colchetes. Isso será feito para contar as estruturas de repetição, para depois criar uma variável do tamanho exato, para guardar as localizações das estruturas de repetição. Também, a informação será utilizada para determinar se não há erros em relação as estruturas (o programa não pode fechar uma estrutura que não foi aberta, nem o programa pode terminar com uma estrutura aberta).
O código a seguir possui comentários que, acredito que sejam auto-explicativos:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int c;
FILE *arquivo; // Deve-se usar o asterisco aqui, por ser ponteiro
if (argc >= 2) // Se tiver 2 ou mais argumentos
{
arquivo = fopen(argv[1], "rb"); // Abre o arquivo especificado
if (arquivo == NULL) // Se não conseguir abrir o arquivo
{
puts("ERRO: Não foi possível abrir o arquivo!");
do { c = getchar(); } while ((c != '\n') && (c != EOF));
return 1; // Encerra o programa
}
}
else
{
return 0; // Encerra o programa
}
fseek(arquivo, 0, SEEK_END);
int tamanho = ftell(arquivo); // Offset do fim do arquivo, que equivale ao tamanho do arquivo
rewind(arquivo); // Rebobina o arquivo
// Aloca memória, com um byte adicional para o nulo (usado em strings em C)
char *fonte = (char *) malloc(tamanho + 1);
if (fonte == NULL) // Verifica se consegui alocar memoria
{
puts("ERRO: Não há memória o suficiente!");
do { c = getchar(); } while ((c != '\n') && (c != EOF));
return 1;
}
memset(fonte, 0, tamanho * sizeof(char)); // Preencherá a variável fonte com zeros (para "limpar")
size_t size = fread(fonte, 1, tamanho, arquivo);
fonte[tamanho] = 0; // Põe o sinal de fim (nulo)
if (size != tamanho * sizeof(char))
{
puts("ERRO: Não foi possível ler o arquivo!");
do { c = getchar(); } while ((c != '\n') && (c != EOF));
return 1;
}
int colchetes = 0, i;
for (i = 0; i < tamanho; i++)
{
if (fonte[i] == '[')
{
colchetes++;
}
else if (fonte[i] == ']')
{
if (colchetes == 0) // Se nenhuma estrutura '[' está aberta
{
puts("ERRO: A estrutura de repetição ']' não foi aberta!");
do { c = getchar(); } while ((c != '\n') && (c != EOF));
return 1;
}
else
{
colchetes--;
}
}
}
if (colchetes > 0) // Se alguma estrutura '[' não foi fechada
{
puts("ERRO: A estrutura de repetição '[' não foi fechada!");
do { c = getchar(); } while ((c != '\n') && (c != EOF));
return 1;
}
unsigned char pilha[30000]; // Equivalente a aproximadamente 30 KB
memset(pilha, 0, 30000 * sizeof(unsigned char));
int backup = 0, ptr = 0, pos[tamanho];
colchetes = 0;
for (i = 0; i < tamanho; i++)
{
if (fonte[i] == '[')
{
backup = i;
while (i < tamanho)
{
if (fonte[i] == '[')
{
colchetes++;
}
else if (fonte[i] == ']')
{
colchetes--;
}
if (colchetes == 0)
{
break;
}
i++;
}
pos[i] = backup;
pos[backup] = i;
i = backup;
}
}
for (i = 0; i < tamanho; i++)
{
switch (fonte[i])
{
case '+':
pilha[ptr]++;
break;
case '.':
putchar(pilha[ptr]);
break;
case '-':
pilha[ptr]--;
break;
case ',':
backup = getchar();
if (backup != -1)
{
pilha[ptr] = backup;
}
break;
case '>':
if (ptr == 30000 - 1) // Célula 30000
{
ptr = 0; // Célula 1
}
else
{
ptr++;
}
break;
case '<':
if (ptr == 0)
{
ptr = 30000 - 1; // Célula 30000
}
else
{
ptr--;
}
break;
case '[':
if (pilha[ptr] == 0)
{
i = pos[i];
}
break;
case ']':
i = pos[i] - 1; // Voltar ao início da repetição
break;
}
}
free(fonte);
return 0;
}
Conclusão
O interpretador de brainfuck está completo e funcional e, deverá ser capaz de interpretar qualquer programa em brainfuck. Ele é simples e você pode melhorá-lo, adicionando novas funcionalidades. Experimente interpretar o código em brainfuck abaixo:
Programa Olá Mundo
++++++++++[>++++++++>+++++++++++>----
------>+++>++++++++>++++++++++++>++++
+++++++>++++++++++>+++++++++++>+++<<<
<<<<<<<-]>-.>--.>++++.>++++++++++++++
.------------.>---.>---.>.>.>+.>+++.,
Em um tutorial futuro, irei instruir o leitor a desenvolver um interpretador de uma linguagem mais complexa, de comandos de mais de um byte de comprimento, com suporte a compilação e interpretação de bytecode.
« parte anterior página principal »
0 comentários:
Postar um comentário