Ir para conteúdo
Fórum Script Brasil
  • 0

Interpretador de arquivos


Marcelo Utikawa da Fonseca

Pergunta

Olá a todos!

Eu fiz esse programa para poder mostrar alguns conceitos como: Lista Ligada, Ponteiros para Funções e Número Variável de Argumentos.

É um programa simples e que pode ser expandido facilmente. Qualquer dúvida é só responder a esse tópico que eu explico!

O objetivo desse programa foi criar um interpretador de comandos em um arquivo .txt. Você chama o programa passando como parâmetro um arquivo que será interpretado.

O formado do arquivo é:

COMANDO P1 P2 P3 ... Pn
Sendo P1 a Pn os parâmetros para o comando. O programa foi desenvolvido em dois arquivos: interpretador.c e main.c. Eu fiz dessa forma para que o interpretador fosse independente do programa principal, podendo ser usado em outros programas sem necessidade de se altérá-lo. O main.c usa as funções criadas no interpretador.c para interpretar o arquivo. Existe ainda o arquivo interpretador.h que contém as definições necessárias para usar o arquivo interpretador.c. Lá existe a definição das estruturas e as funções que existem em interpretador.c Segue agora a listagem dos arquivos que citei: interpretador.h:
#ifndef INTERPRETADOR_H
#define INTERPRETADOR_H

// Tipos de parametros suportados
#define IC_TYPE_NULL       0
#define IC_TYPE_INT        1
#define IC_TYPE_FLOAT      2
#define IC_TYPE_CHAR       3
#define IC_TYPE_STRING     4
#define IC_TYPE_FULLSTRING 5

// Tipos de retorno dos handlers
#define IC_RETURN_ERR  -1
#define IC_RETURN_OK    0
#define IC_RETURN_NEXT  1

// Macros para criar Handlers
#define IC_HANDLER(fnc)     int   fnc (IntCodeList *pList, ParamList *pParamList)
#define IC_HANDLER_PTR(fnc) int (*fnc)(IntCodeList *pList, ParamList *pParamList)

// Configuracoes
#define IC_CONFIG_INTCODE_SIZE   20
#define IC_CONFIG_BUF_SIZE     1024

// Typedefs
typedef struct strParamList ParamList;
typedef struct strIntCode IntCode;
typedef struct strIntCodeList IntCodeList;

// Estrutura que define um parametro e ponteiro para lista ligada
struct strParamList {
    union {
        int    iInt;
        float  fFloat;
        char   cChar;
        char   szString[IC_CONFIG_BUF_SIZE];
        void  *pAny;
    } Value;
    int iType;
        struct strParamList *pNext;
};

// Estrutura que contem lista de parametros, handler e codigo associado
struct strIntCode {
    char       szCode[IC_CONFIG_INTCODE_SIZE+1];
    IC_HANDLER_PTR(pHandler);
    ParamList *pParamList;
};

// Estrutura base que contem a lista de todos os interpretadores adicionados
struct strIntCodeList {
    IntCode                IC;
    struct strIntCodeList *pNext;
};

// Funcoes para trabalhar com os interpretadores
void         AddIntCode       (IntCodeList *pList, char *szCode, IC_HANDLER_PTR(pHandler), ...);
int          ExecuteFile      (IntCodeList *pList, char *szFile);
IntCodeList *CreateIntCodeList(void);

#endif
interpretador.c:
#include <stdarg.h>
#include <string.h>
#include <malloc.h>

#include "interpretador.h"

/*** Handlers Pre-Definidos ***/

IC_HANDLER(ICHandlerInclude)
{
    // Executa o arquivo passado como parametro
    return ExecuteFile(pList, pParamList->Value.szString);
}

/*** Fim dos Handlers ***/

// Funcao para alocar uma variavel do tipo IntCodeList
static IntCodeList *AllocIntCodeList(void)
{
    IntCodeList *pNewList = (IntCodeList *)malloc(sizeof(IntCodeList));
    memset(pNewList, 0, sizeof(IntCodeList));
    return pNewList;
}

// Funcao que cria uma IntCodeList, adiciona os handlers padrão e retorna esta lista criada.
IntCodeList *CreateIntCodeList(void)
{
    IntCodeList *pList = AllocIntCodeList();
    AddIntCode(pList, "INCLUDE", ICHandlerInclude, IC_TYPE_FULLSTRING, IC_TYPE_NULL);
    return pList;
}

// Funcao para adicionar um novo handler para determinado codigo.
// Podem ser adicionados qualquer numero de handlers para cada codigo
void AddIntCode(IntCodeList *pList, char *szCode, IC_HANDLER_PTR(pHandler), ...)
{
    int iType;
    va_list args;
    IntCode *pNewIC;
    ParamList *pNewParamList, *pNextParamList;

    if(pList == NULL)
        return;

    if(pList->IC.szCode == NULL) { // Primeiro IntCode
        pNewIC = &(pList->IC);
    } else { // já existe o primeiro, alocar um novo
        // Primeiro procura o final da lista
        while(pList->pNext != NULL) {
            pList = pList->pNext;
        }
        // Aloca um novo IntCodeList e associa seu IC para pNewIC
        pList->pNext = AllocIntCodeList();
        pNewIC = &(pList->pNext->IC);
    }

    // Carrega o campo code e handler do IntCode
    strncpy(pNewIC->szCode, szCode, IC_CONFIG_INTCODE_SIZE);
    pNewIC->szCode[IC_CONFIG_INTCODE_SIZE] = 0;
    pNewIC->pHandler = pHandler;

    // Inicia loop para montar lista de parametros, terminando em IC_TYPE_NULL
    va_start(args, pHandler);
    while((iType = va_arg(args, int)) != IC_TYPE_NULL) {
        // Aloca novo parametro e carrega tipo de variavel
        pNewParamList = (ParamList *)malloc(sizeof(ParamList));
        memset(pNewParamList, 0, sizeof(ParamList));
        pNewParamList->iType = iType;

        // Associa novo parametro a lista de parametros do IntCode
        if(pNewIC->pParamList == NULL) {
            pNewIC->pParamList = pNewParamList;
        } else {
            pNextParamList->pNext = pNewParamList;
        }
        pNextParamList = pNewParamList;
    }
}

// Executa o arquivo passado como parametro, interpretando conforme a lista de IntCode passada como parametro
int ExecuteFile(IntCodeList *pList, char *szFile)
{
    FILE        *pFile;
    IntCodeList *pCurrent;
    int          iRet = IC_RETURN_ERR;
    char         szCode[IC_CONFIG_INTCODE_SIZE+1], szFormatCode[10], *szFormatParam;
    char         szLine[IC_CONFIG_BUF_SIZE], szParam[IC_CONFIG_BUF_SIZE];

    // Verifica se a lista é valida
    if(pList == NULL || pList->IC.szCode == NULL) {
        return iRet;
    }

    if(szFile == NULL) {
        pFile = stdin;
    } else {
        // Abre o arquivo e retorna erro se não conseguir
        pFile = fopen(szFile, "r");
        if(pFile == NULL) {
        return iRet;
        }
    }

    /*** Lista valida e arquivo aberto. Agora comecamos a interpretar os codigos! ***/

    // Primeiro criamos a string de formato, limitando a leitura a IC_CONFIG_INTCODE_SIZE caracteres
    sprintf(szFormatCode, "%%%02ds", IC_CONFIG_INTCODE_SIZE);
    iRet = IC_RETURN_OK;

    // Enquanto não houver erro, le nova linha
    while(iRet != IC_RETURN_ERR && fgets(szLine, IC_CONFIG_BUF_SIZE, pFile) != NULL) {
        int iLineEnd;
        // Limpa o final da string lida
        for(iLineEnd = strlen(szLine)-1; iLineEnd >= 0; iLineEnd--) {
            switch(szLine[iLineEnd]) {
                case '\r':
                case '\n':
                case ' ':
                    szLine[iLineEnd] = '';
            default: // Terminou. Devemos interroper o loop
                iLineEnd = 0;
            }
        }

        // Agora carregamos o codigo da string
        if(sscanf(szLine, szFormatCode, szCode) == 1) {
            pCurrent = pList;
            iRet = IC_RETURN_OK;
            // Codigo lido corretamente. Inicia loop de busca dos handlers
            while(pCurrent != NULL) {
                if(!strcmp(pCurrent->IC.szCode, szCode)) { // handler encontrado!
                    int iStart = strlen(szCode) + 1;
                    ParamList *pParamList = pCurrent->IC.pParamList;
                    // Loop para carregar os parametros da string
                    while(pParamList != NULL) {
                        switch(pParamList->iType) {
                            case IC_TYPE_INT       : szFormatParam = "%d"; break;
                            case IC_TYPE_FLOAT     : szFormatParam = "%f"; break;
                            case IC_TYPE_CHAR      : szFormatParam = "%c"; break;
                            case IC_TYPE_STRING    : szFormatParam = "%s"; break;
                            default:
                            case IC_TYPE_FULLSTRING: szFormatParam = NULL; break;
                        }
                        // Carrega parametro conforme formato definido
                        if(szFormatParam == NULL) {
                            strcpy(pParamList->Value.szString, szLine+iStart);
                            break;
                        } else {
                            // Primeiro carrega sem formato, avancando na string
                            sscanf(szLine + iStart, "%s", szParam);
                            iStart += strlen(szParam)+1;
                            // Converte conforme formato especificado
                            sscanf(szParam, szFormatParam, &pParamList->Value);
                            pParamList = pParamList->pNext;
                        }
                    }
                    // Executa o handler e checa o retorno
                    iRet = (pCurrent->IC.pHandler)(pList, pCurrent->IC.pParamList);
                    if(iRet == IC_RETURN_OK) {
                        // OK, interromper busca por novos handlers para linha atual.
                        break;
                    } else if(iRet != IC_RETURN_NEXT) {
                        // Erro! Interromper execucao.
                        iRet = IC_RETURN_ERR;
                        break;
                    }
                }
                // Avanca para o proximo item na lista
                pCurrent = pCurrent->pNext;
            }
        }
    }

    // Terminou. Fecha o arquivo e retorna.
    fclose(pFile);
    return iRet;
}
main.c:
#include <stdio.h>

#include "interpretador.h"

// Handler que trabalha com o codigo PESSOA
IC_HANDLER(hPessoa)
{
    char  *szNome;
    float  fAltura;
    int    iPeso, iIdade;

    // Carregamento dos parametros recebidos
    szNome = pParamList->Value.szString;
    pParamList = pParamList->pNext;

    fAltura = pParamList->Value.fFloat;
    pParamList = pParamList->pNext;

    iPeso = pParamList->Value.iInt;
    pParamList = pParamList->pNext;

    iIdade = pParamList->Value.iInt;

    // Exibe os dados recebidos na tela
    printf("%s tem %.02f m, %d Kg e %d anos.\n", szNome, fAltura, iPeso, iIdade);

    // Sempre retorna OK
    return IC_RETURN_OK;
}

int main(int argc, char **argv)
{
    IntCodeList *pList;

    // Checa se foram passados os parametros corretamente.
    if(argc > 2) {
        printf("Sintaxe:\n\t%s [arquivo]\nSe não fornecido o parametro arquivo, utiliza-se stdin\n", argv[0]);
        return 1;
    }

    // Cria a lista e adiciona um handler para PESSOA
    pList = CreateIntCodeList();
    AddIntCode(pList, "PESSOA", hPessoa, IC_TYPE_STRING, IC_TYPE_FLOAT, IC_TYPE_INT, IC_TYPE_INT, IC_TYPE_NULL);

    // Interpreta o arquivo fornecido como parametro
    if(ExecuteFile(pList, argc==2 ? argv[1] : NULL) == IC_RETURN_ERR) {
        printf("Erro interpretando arquivo!\n");
        return 2;
    } else {
        printf("Arquivo interpretado com sucesso!\n");
    }

    // Fim do programa
    return 0;
}
Eu criei ainda um script para compilar já que são dois arquivos. O conceito é gerar o arquivo objeto do interpretador.c e depois compilar o main.c, agregando o arquivo objeto criado. compila.sh:
#/bin/sh

rm *.o interpretador 2>/dev/null

gcc -Wall -c -o interpretador.o interpretador.c
gcc -Wall -o interpretador main.c interpretador.o
Abaixo estão dois arquivos para testar o programa. São dois pois no teste.txt eu uso um comando para incluir o arquivo maisum.txt teste.txt:
INCLUDE maisum.txt
PESSOA João 1.75 80 37
PESSOA Maria 1.68 56 35
maisum.txt:
PESSOA Fulano 1.48 39 11

É isso. Vejam como o arquivo main.c é simples. Essa é a vantagem de separar o código em mais de um arquivo. Assim é possível reaproveitar a lógica facilmente!

Mais uma vez, fiquem à vontade para perguntar...

Comentários e críticas construtivas também serão muito bem vindos! B)

Editado por Marcelo Utikawa da Fonseca
Link para o comentário
Compartilhar em outros sites

6 respostass a esta questão

Posts Recomendados

  • 0
Bom dia e bom trabalho!

Ideia dwe expansao: use o interpretador para interpretar os parametros do argv =)

Tou sem tempo agora, depois dou uma brincada.

Abraco!

Obrigado pelo comentário!

Só não entendi sobre a parte de interpretar o argv... Pode explicar melhor? :rolleyes:

Abraços,

Marcelo Utikawa da Fonseca

Link para o comentário
Compartilhar em outros sites

  • 0

Daria certo fazer o que eu falei se a função ExecuteFile() recebesse um ponteiro para FILE ou um descritor de arquivo ao invés de receber uma string com o caminho do arquivo.

Isso não é uma má idéia, desse modo você pode usar esse código que você criou para interpretar mensagens vindas de sockets, por exemplo, ou então da stdin.

Editado por == Douplus ==
Link para o comentário
Compartilhar em outros sites

  • 0

Entendi, ótima idéia!!! :)

Já editei o código lá em cima para incluir a sua idéia. O que fiz foi mudar a função ExecuteFile para aceitar nulo como nome de arquivo. Nesse caso ele usa stdin.

O que alterei foi o main.c para passar nulo quando não receber o argumento com o nome do arquivo e um if no interpretador, conforme a seguir:

if(szFile == NULL) {
        pFile = stdin;
    } else {
        // Abre o arquivo e retorna erro se não conseguir
        pFile = fopen(szFile, "r");
        if(pFile == NULL) {
            return iRet;
        }
    }
Agora é possível executar o programa assim:
cat teste.txt | ./interpretador

Obrigado!

Abraços,

Marcelo Utikawa da Fonseca

Editado por Marcelo Utikawa da Fonseca
Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,1k
    • Posts
      651,8k
×
×
  • Criar Novo...