sábado, 1 de agosto de 2009

Como implementar um interpretador de brainfuck - parte 4

Até aqui, o programa já consegue interpretar dois dos oito comandos de brainfuck. Se o valor da célula selecionada for igual a 82, e for mandado para a tela, será exibido um “R”, pois 82 equivale a essa letra, de acordo com o ASCII.

Adicione novas condicionais para interpretar os outros quatro comandos, deixando os dois últimos para mais tarde. O comando “-” deve decrementar o valor da célula selecionada, “,” deve pegar um valor do usuário através do teclado e guardá-lo na célula selecionada, “>” deve selecionar a próxima célula e, “<” deve selecionar a célula anterior. Ficará assim:

#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;
      }
   }

   free(fonte);
   return 0;
}

Na verificação do comando “,”, eu usei uma condição para verificar o valor recebido. Isso porque por padrão, os interpretadores de brainfuck identificam a tecla Enter pelo valor ASCII 10, mas o Windows identifica como 13, causando incompatibilidade com a maioria dos códigos brainfuck espalhados pela internet. Se nosso programa receber o valor 13, guardará 10 e, se receber 10, guardará 13.

Um pouco mais a frente, eu usei uma condicional para verificar qual célula estava selecionada. Caso esteja na última célula (pilha[29999] é a célula 30000), deverá pular para a primeira célula (pilha[0] é a célula 1) e vice-e-versa. Se não houver a verificação, e o programa instruir selecionar a próxima célula, sendo que já estiver na última, houverá um problema e o programa encerrará de forma abrupta.

« parte anterior próxima parte »

Postagens relacionadas

0 comentários:

Postar um comentário