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:
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)
Pergunta
Marcelo Utikawa da Fonseca
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 é:
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); #endifinterpretador.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: 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: maisum.txt:É 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 FonsecaLink para o comentário
Compartilhar em outros sites
6 respostass a esta questão
Posts Recomendados
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.