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: interpretador.c: main.c: 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.