Resumo:
Este trabalho surge-nos na disciplina de Processamento de Linguagens I, e consiste em "infiltrar-nos" no manuseamento de linguagens geradas por uma determinada gramática, com a utilização das ferramentas Lex & Yacc .
Este trabalho foi feito em três etapas, etapas essas que foram criados pelo nosso prof. José Ramalho para melhor nos orientarmos em relação ao tempo que deveriamos empreender em cada etapa .
A intenção final destas 3 etapas é criar um processador genérico de documentos estruturados (XML) que validará a boa formação dos documentos e que guardará a sua informação num grove .
Este relato´rio tem apenas documentado a parte relativa a´s 3 primeiras etapas desta primeira fase. Vamos ter entaõ o relat´orio dividido nas 3 etapas, em que cada uma delas temos em conta dois aspectos, aspectos esses que são :
"O que nos e´ pedido"
"Soluçao encontrada"
Nesta etapa foi-nos pedido para construir um analisador l´exico e um analisador sint´actico, usando as ferramentas Lex + Yacc para uma gram´atica dada.
Esta etapa, foi talvez a etapa que obrigasse mais a puxar pela cabeça ja´ que na parte le´xica obrigava-nos a fazer um reconhecimento de um conjunto de expressões que tanto podiam ser texto do utilizador como conjunto de comandos( a que chamamos Tags ), que permitem formatar o texto. Uma Tag é composta por vários símbolos terminais : q <, >, = com ou não identificador e valor.
Exemplo:<Tag_exemplo atr="VALOR" > bla, bla ... </Tag_exemplo>
Podemos ver que < ...> e < ...> são exempo de duas Tags.
Um problema que tivemos foi o de, na análise léxica , o de como reconhecer o texto do utilizador. Para isso tentamos então encontrar uma expressão regular que contivesse o maior numero possívél de caracteres. Esta exressão regular não podia então fazer matching às tags. Utilizamos então as seguintes expressões (apenas numeramos as mais importantes) :
ID [a-zA-Z][a-zA-Z0-9_\-]*
Esta expressão regular e' a que nos "dá" o identificador, ou seja o que pode estar a seguir ao nome de uma Tag ,de referir ainda que não aceitamos um identificador a começar um um número.
VALOR \"[^\"]+\"
Esta expressão regular dá-nos o valor, ou seja, o que vem depois do identificador numa Tag. De notar que um valor é '"' seguido de um ou mais caracteres excepto '"', seguido outra vez de '"'
TEXTO [^\ <& ]+
Esta expressão é a que nos dá o texto propriamente dito, ou seja um ou mais caracteres, excepto o < e o & .
Nesta primeira etapa, a nivél léxico era apenas necessário então enviar o 'texto', o íd', e o 'valor', não sendo se quer guardar nenhum destes dados como iremos precisar em diante.
Neste aspecto nesta primeira etapa foi pouco problemática, já que foi quase um Copy Paste da gramática dada, apenas com a alteração da recursividade, passada para a esquerda, para no final, enviar uma mensagem a dizer que reconheceu o documento, caso tenha acontecido esse mesmo reconhecimento.
Nesta etapa foram nos pedidas dois objectivos. Ou seja, acrescentar ao analisador construído na etapa anterior a análise semântica que faça as seguintes validações :
todas as anotações correspondentes a elementos com conteúdo são abertas e fechadas correctamente
O documento tem que obrigatoriamente começar com a abertura dum elemento (que ir´a englobar todo o documento),sugeriram-nos para isso alterar a gramática
Nesta etapa já tivemos mais trabalho e tivemos então de utilizar algumas librarias do C extra, para nos ajudar na melhor resolução de um ou outro problema.
Neste etapa foi necessário então algumas ajudas já que tinhas alguns problemas para corrigir, problemas esses, por exemplo:
Como separar o texto de tudo o resto
Tivemos então de recorrer a ajuda de Start Conditions , estas ajudaram-nos, já que funcionam como um meta-autómato, ou seja recebe algo, e passa para um novo estado, foi neste contexto que as usamos, por exemplo, quando nos aparece num texto o caracter '<' , ja sabemos que passamos para um estado , em que estamos dentro de uma Tag.
Como guardar a informação gerada
Neste problema tivemos então que guardar a informação á medida que a iamos trabalhando, para isso guardamos nas variaveis da Union criada no Yacc (falaremos mais á frente) para isso utilizamos a função simples char *strdup(const char *s) .
Ao nivél do Yacc tivemos também nesta etapa que fazer algumas alterações, criamos uma Union para guardar as variavéis que nos fazem a passagem dos dados relativos a gramática.
Nesta etapa ainda alteramos a gramática de modo que obrigasse um texto a conter no inicio e no fim do mesmo, Tags que o englobassem , da seguinte maneira : <doc> .... <doc> .
Essas mesmas Tags teriam que ter o mesmo ID , ou seja temos que fazer uma comparação dos ID s para ver se são iguais, caso não sejam dá-nos uma mensagem de erro do género :
Erro nas Tags, na linha -> X
Documento Invalido!!
O mesmo acontece no caso das Tags de cada elemento da nossa grámatica, não tenha a mesma descrição quer ao abrir, quer ao fechar .
Ainda nesta etapa decidimos incluir uma variavél global, que nos controla o tipo de erro, ou seja se e' como já vimos anteriormente, erro de Tags, ou se e' outro tipo, erro sintáctico, com a seguinte mensagem de erro:
Erro Sintactico, na linha -> X
Documento Invalido!!
Para terminar quando um documento é válido dá-nos a seguinte mensagem :
Documento Valido!!
Nesta fase e'-nos pedido o armazenamento da informação, ou seja :
criar um grove e armazenar a informação do documento nessa estrutura ;
criar uma função de travessia que será invocada a seguir ao yyparse() e gerará um documento no formato ESIS .
Nesta etapa, a única alteração que tivemos de efectuar, foi ao nivél do Yacc , por isso só iremos falar desse nivél , e não no léxico.
Nesta etapa veio, então talvez, a parte mais dificil deste trabalho, não querendo com isto dizer, que tivessemos muita, ou alguma dificuldade.
Tivemos então de fazer um maior uso do C , essencialmente na construção do grove que exige um tratamento, com o uso de diversos apontadores.
Para criarmos então uma grove, tivemos que criar estruturas válidas para o nosso trabalho, a estrutura que decidimos usar, foi a estrutura aconselhada, na própria etapa, esta estrutura, tem o seguinte código :
Exemplo:typdedef struct Anodo { char *nome; char *valor; struct Anodo *seg; } atributo; typedef struct Gnodo { char *id; union c {char *texto; /*para nodos texto*/ struct X /* para os outros nodos */ { struct Gnodo *conteudo; atributo *atribs; }atr; }corpo; struct Gnodo *irmaos; }Grove ;
Como podemos ver, a estrutura Anodo é a estrutura que guarda os atributos de uma determinada Tag, como podemos ver tem o nome , e o valor, exemplo :
<REALCADO ESTILO="italico">
Como podemos verificar o ESTILO corresponde ao nome; e o "italico" corresponde ao valor, desse mesmo atributo de notar, que podemos ter uma sequência de atributos, responsav´el por isto é a seguinte linha struct Anodo *seg; que corresponde a um apontador para o próximo atributo.
Quanto a estrutura Gnodo que contém em si toda a informaç~ ao relevante de um documento, ou seja, é um Grove própriamente dito, contém um ID que é o identificador da Tag,no mesmo exemplo anterior, seria REALCADO pode ainda ser um texto, ou uma estrutura que contenha dentro de si, outro grove com os seus respectivos atributos. De repara que esta estrutura tem ainda um apontador para outra estrutura, igual a esta, caso exista, caso não, este aponta dor, tal com o anterior, apontam para NULL .
Ainda nesta parte tivemos que utilizar funções auxiliares, que nos ajudassem melhor a inserir a informação no Grove já que quando faziamos, a travessia, devido á recursividade á direita, o documento aparecia-nos como se tivesse sido lido de baixo para cima e, da direita para a esquerda.
As funções são as seguintes Grove *concGrv (Grove *g1, Grove *g2) e atributo *concAtr( atributo *a1 , atributo *a2) cada uma delas recebe duas variaveis, e vai inserir uma como o ultimo elemento da outra (que é uma lista), ou seja percorre a lista ate ao fim e depois insere o elemento na cauda, assim resolve-nos então o problema que tinhamos.Estas funções ambas devolvem as listas já com o elemento inserido .
A outra função que tambem utilizamos foi a função ,
void Percorre_e_GeraESIS(Grove *g)
Esta função o que faz, é percorre um grove e vai imprimindo o nome das Tags , e chama então outras funções auxiliares que se limitam quer os atributos, quer o texto, caso seja essa a sit uação, de notar que essa função chama-se recursivamente, quer para o seu conteudo quer para os seus irmaos .As funções chamadas por esta função :
void imprimeAttr(Grove *g)
void imprimeTexto(Grove *g)
Pretende-se criar um interpretador para a seguinte lista de comandos:
LOAD "path para o documento" id - este comando deverá criar uma entrada na estrutura de dados interna em que o identificador id fique associado ao documento cuja path foi passada como primeiro argumento
LIST - mostra no écran a lista de documentos carregados e respectivos ids.
SHOW id - mostra no écran o documento associado ao identificador id em formato ESIS (aplicação do conversor implementado na primeira fase).
EXIT - sai do programa.
HELP - imprime no écran um texto parecido com esta lista de comandos.
Foi-nos proposta nas aulas uma determinada gramática, gramática essa que nós decidimos utilizar, que é a seguinte:
Exemplo:Interp --> ComList ComList --> Comando | ComList Comando Comando --> LOAD fich-id id |SHOW id |LIST |EXIT |HELP
Decidimos a ainda acrescentar a esta lista de comandos alguns comandos que achamos serem também uteís a este mini-interpretador de comandos, esses comandos são :
LS - este comando lista no ecrán os ficheiros existentes na directoria actual com extensão esis.
CLEAN - limpa a nossa estrutura de dados toda, para poder ser inserido tudo de novo.
CHANGE id1 / id2 - muda o nome do id1 para um novo que será o id2.
CREATE id - faz algo parecido com o comando SHOW, só que em vez de imprimir para o ecrán, cria um ficheiro íd'.esis, em que ' id' é o nome de identificador(ficheiros que podem ser consultados com o comando LS)
Para esta fase tivemos uma maior facilidade em fazê-la, já que era algo muito idêntico ao que tinhamos feito na primeira fase, só que um pouco menos complexa.
Neste capitulo (léxico) tivemos que criar algo muito parecido com o que já tinhamos feito na primeira fase.
Criamos duas expressões regulares necessárias para apanhar os identificadores e os ficheiros. Estas expressões são as mesmas utilizadas na primeira fase do trabalho.
De resto foi também utilizado Star Conditions bastante utéis neste tipo de problemas, a única contrariedade que encontramos foi no comando CHANGE id1 id2 que exigia receber dois identificadores logo tivemos que criar uma nova etapa, que s erá iniciada, quando encontrar o primeiro id onde irá aguardar um outro identificador. Esta solução foi necessária, já que ao executarmos o comando da seguinte maneira : CHANGE exemplo_id ele ficaria á espera do segundo id e não envia para o ecrãn uma mensagem de erro, como seria o desejado. E quando fosse executado outro comando aí sim daria a tal mensagem de erro. Foi então a maneira que achamos para resolver este problema .
Tivemos ainda outro problema que foi, a maneira que nós concebemos a nossa solução obrigou-nos a criar um ciclo para resolver o caso em que tinhamos diversos caracteres e apenas dar uma mensagem de erro, senão quando executavamos algo como: ksd daria-nos 3 mensagens de erro, ou seja tantas, quanto o número de caracteres.
Para resolver esse problema fizemos então :
Exemplo:. {BEGIN(abr_LX);} <abr_LX>. {BEGIN(abr_LX);} <abr_LX>\n {BEGIN(0);return(*yytext);}
Nesta parte tinhamos apenas que usar a gramática aconselhada e identificar qual o comando executado, e fazer propriamente essa execução.
Mas tivemos que fazer algumas alterações, tivemos que introduzir uma nova derivação para sabermos que concluimos um comando, então a gramática, passou a ser
Exemplo:Interpretador --> ListaComandos ListaComandos --> Comando | ListComandos Comando Comando --> LOAD fich id ENTER |SHOW id ENTER |LIST |EXIT |LS |CLEAN |CREATE id ENTER |CHANGE id id ENTER |HELP |ENTER
Desta maneira podemos carregar várias vezes na tecla ENTER sem que dê alguma mensagem de erro.
Nesta parte ainda temos que executar as acções semânticas respectivas a cada comando.
Por exemplo quando executamos o comando LOAD fich id tinhamos que inserir numa estrutura de dados o ficheiro e o respecti vo id, a estrutura usada para guardar a informação são as árvores binárias de procura:
Exemplo:typedef struct Arv { char *id; char *fich; struct Arv *esq, *dir; }AB;
Para executar então esta função (acima referida) usamos a função:
AB *insArvB (AB *arv , char *id , char *fich)
Esta função recebe uma árvore um apontador para um identificador, e um outro para um ficheiro, e devolve a árvore já com os novos elementos inseridos.
Foram ainda utilizadas as seguintes funções para outros comandos
void mostraESIS (AB *a , char *id , char *sitio )
Esta função tanto é usada para o comando SHOW como o comando CREATE .
Esta função recebe uma árvore um identificador, e um sítio, este sitio, ou é o ecrãn ou o disco, onde executa o trabalho feito na primeira fase para o ficheir o correspondente ao identificador recebido como parâmetro. Caso o sitio seja o disco ele cria um ficheiro com extensão .esis (ou seja do tipo ESIS) com o nome do identificador.
void consArvB(AB *arv)
Esta função gera uma tabela com todos os IDS e respectivos ficheiros, esta função é executada pelo comando LIST
void troca_ids(AB *a , char *id , char *id2)
Esta função recebe uma árvore binária um ID e substitui-o pelo novo id, esta função devolve uma mensagem de erro se o identificador que queremos alterar não existe, ou se o identificador para o qua l queremos alterar já existe, existe então uma mensagem de erro para cada um destes erros.
Todas estas funções, e outras chamam diversas funções, todas estas estão num ficheiro á parte chamado arvores.h quem vem tambem no apêndice deste relatório.
Quanto á nossa conclusão desta fase podemos dizer que foi um trabalho que reuniu algum trabalho e algum gosto pela matéria. Achamos que de facto este método de trabalhar documentos e textos, para além de ser algo u'til, quanto a nos é algo que nos motivou bastante.
Nós neste curso até agora, a linguagem C era a linguagem, mais usada nos nossos trabalhos, se formos ver como faríamos este mesmo trabalho usando apenas a linguagem C era algo um pouco/muito difícil de fazer até mesmo para alguem que trabalhe bem em C .
Se formos então analisar o trabalho de ferramentas, até agora desconhecidas para nós, iremos ver que essas ferramentas simples e utéis facilitam-nos bastante o nosso trabalho, reduzindo o nosso número de linhas do código, á insignificância . As ferramentas que que falamos são o Lex e Yacc , de facto estas duas ajudaram-nos bastante no nosso trabalho.
Verificando agora o facto de ter que guardar uma estrutura bastante complicada , teoricamente, concluimos que foi um trabalho bastante simples. Tivemos que defenir uma estrutura, e depois foi só guardar facilmente nessa mesma estrutura o documento que é dado na entrada, de facto foi algo que fizemos com poucas linhas de código.
Falando na travessia desse grove foi algo também bastante simples, com uma simples funçaõ que imprimia, quer o texto, quer os atributos, depois era só ser chamada recursivamente, tinhamos o nosso problema resolvido .
A maior prova que tivemos do trabalho enorme e exaustivo que o Yacc tem, foi dado pelas últimas aulas que tivemos, onde tinhamos que verificar o funcionamento do parser LR(0) , o que é algo bastante chato e por vezes complexo, só que o Yacc faz também outros parsers , bastante mais complexos quando este não nos permite ainda chegar á próxima produção, que é por exemplo o LR(1) .
Fazendo referência a algo, que já nos íamos esquecendo, o nosso executavél permite, quando invocado c/ -esis imprimir para o ecrãn o documento ESIS, Sem esta mesma opção ele apenas imprime se é válido ou não caso não seja diz que tipo de erro, e em que linha.
-
Na segunda fase do nosso trabalho, foi algo que de facto nos deixou bastante espantados, já que nunca nos tinha passado pela cabeça que poderiamos implementar uma mini-shell com o uso do LEX & YACC . A gramática utilizada, é também algo muito simples e pouco complexa.
Se analisarmos bem a questão podemos ir construíndo uma mini-shell com alguns comandos, e ao longo do tempo podemos acrescentar diversos comandos.
Este segunda fase foi algo que de facto deixou-nos a pensar, iremos com certeza fazer algo muito parecido com este trabalho, mas para nós próprios...
ID [_a-zA-Z][a-zA-Z0-9_\-]* VALOR \"[^\"]+\" OUTROS [=\/] LIXO [ \t\n\r]+ TEXTO [^\<&]+ %option yylineno %x abr_ST e_Comercial %% & {BEGIN(e_Comercial);return('&');} <e_Comercial>{ID} {yylval.id=(char *)strdup(yytext);return(ID);} <e_Comercial>; {BEGIN(0);return(';');} <e_Comercial>{LIXO} ; \< {BEGIN(abr_ST);return('<');} <abr_ST>{ID} {yylval.id=(char *)strdup(yytext);return(ID);} <abr_ST>{VALOR} {yylval.valor=(char *)strdup(yytext);return(VALOR);} <abr_ST>{OUTROS} return(*yytext); <abr_ST>{LIXO} ; <abr_ST>> {BEGIN(0);return('>');} <abr_ST>\< erro=2; <abr_ST>. return(*yytext); {LIXO} ; {TEXTO} { yylval.txt=(char *)strdup(yytext);return(TEXTO); }
%{#include "groves.h" Grove *grove; int erro=0; %} %token TEXTO ID VALOR %union { char *txt; char *id; char *valor; Grove *grv; atributo *atrib; } %type <txt> TEXTO %type <id> ID %type <valor> VALOR %type <grv> Doc ElemList Elem %type <atrib> AttrList Attr %start Doc %% Doc : '<' ID AttrList '>' ElemList '<' '/' ID '>' { Grove *new=malloc(sizeof(struct Gnodo)); new->id=$2; new->corpo.atr.conteudo=$5; new->corpo.atr.atribs=$3; new->irmaos=NULL; grove=new; if( strcmp($2,$8) ) {erro=erroTag;controlo();} }; ElemList : ElemList Elem { $$=concGrv($1,$2); } |{$$=NULL;}; Elem : TEXTO { Grove *new=(Grove *)malloc(sizeof(struct Gnodo)); new->id="Texto"; new->corpo.texto=$1; $$=new; } | '&' ID ';' { Grove *new=malloc(sizeof(struct Gnodo)); new->id=$2; new->irmaos=NULL; $$=new; } | '<' ID AttrList '>' ElemList '<' '/' ID '>' { Grove *new=(Grove *)malloc(sizeof(struct Gnodo)); new->id=$2; new->corpo.atr.conteudo=$5; new->corpo.atr.atribs=$3; new->irmaos=NULL; $$=new; if(strcmp($2,$8)) {erro=erroTag;controlo();} } | '<' ID AttrList '/' '>' { Grove *new=(Grove *)malloc(sizeof(struct Gnodo)); new->id=$2; new->corpo.atr.conteudo=NULL; new->corpo.atr.atribs=$3; new->irmaos=NULL; $$=new; }; AttrList : AttrList Attr { $$=concAttr($1,$2); } |{$$=NULL;}; Attr : ID '=' VALOR { atributo *new=malloc(sizeof(struct Anodo)); new->nome=$1; new->valor=$3; new->seg=NULL; $$=new; }; %% #include<stdio.h> #include<stdlib.h> #include "lex.yy.c" #define Serro 0 #define erroTag 1 #define erroSin 2 int yyerror(char *s) { erro=erroSin; return 1; } void controlo() { switch(erro) { case Serro:printf("\nDocumento Valido!!\n");break; case erroSin:printf("\nErro Sintatico, na linha -> %d\n",yylineno); printf("\nDocumento Invalido!!\n");exit(0); case erroTag:printf("\nErro nas Tags, na linha -> %d\n",yylineno); printf("\nDocumento Invalido!!\n");exit(0); default:break; } } int main(int argc ,char *argv[]) { yyparse(); if(argc>1) { controlo(); if(!strcmp(argv[1],"-esis")) { printf("\n\n\t\t**** Travessia ESIS do Grove ****\n\n"); Percorre_e_GeraESIS(grove); return 1; } } else controlo(); }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <glib.h> typedef struct Anodo { char *nome; char *valor; struct Anodo *seg; }atributo; typedef struct Gnodo { char *id; union c { char *texto; /* para nodos texto */ struct X /* para os outros nodos */ { struct Gnodo *conteudo; atributo *atribs; }atr; }corpo; struct Gnodo *irmaos; }Grove; void imprimeAttr(Grove *g); void imprimeTexto(Grove *g); void Percorre_e_GeraESIS(Grove *g) { if(g!=NULL) { if(!strcmp(g->id,"Texto")) imprimeTexto(g); else { printf("(%s\n",g->id); /* Abrir Tag */ imprimeAttr(g); Percorre_e_GeraESIS(g->corpo.atr.conteudo); printf(")%s\n",g->id); /* Fechar Tag */ } Percorre_e_GeraESIS(g->irmaos); } } void imprimeAttr(Grove *g) { atributo *a=malloc(sizeof(struct Anodo)); a=g->corpo.atr.atribs; while(a!=NULL) { printf(" A %s %s\n",a->nome,a->valor); a=a->seg; } } void imprimeTexto(Grove *g) { if(!isprint(g->corpo.texto[0]) && !isspace(g->corpo.texto[0])) printf(" -%s\n",(g->corpo.texto)+1); else printf(" -%s\n",g->corpo.texto); } Grove *concGrv ( Grove *g1 , Grove *g2 ) { while(g1!=NULL) { g1->irmaos=concGrv(g1->irmaos,g2); return g1; } g1=g2; return g2; } atributo *concAttr ( atributo *a1 , atributo *a2 ) { while(a1!=NULL) { a1->seg=concAttr(a1->seg,a2); return a1; } a1=a2; return a2; }
ID [a-zA-Z][_a-zA-Z0-9\-]* FICH \"[^\"\n\r]+\" ESPACOS [ \t]* %x abr_LD abr_SH abr_CHG abr_LX abr_ID %% LOAD {BEGIN(abr_LD);return(LOAD);} <abr_LD>{FICH} {yylval.fich=(char *)strdup(yytext);return (FICH);} <abr_LD>{ID} {yylval.id=(char *)strdup(yytext);BEGIN(0);return(ID);} <abr_LD>{ESPACOS} ; <abr_LD>. ; <abr_LD>\n {BEGIN(0);return(*yytext);} SHOW {BEGIN(abr_SH);return(SHOW);} <abr_SH>{ID} {yylval.id=(char *)strdup(yytext);BEGIN(0);return(ID);} <abr_SH>{ESPACOS} ; <abr_SH>. ; <abr_SH>\n {BEGIN(0);return(*yytext);} CREATE {BEGIN(abr_SH);return(CREATE);} CHANGE {BEGIN(abr_CHG);return(CHANGE);} <abr_CHG>{ID} {yylval.id=(char *)strdup(yytext);BEGIN(abr_ID);return(ID);} <abr_CHG>{ESPACOS} ; <abr_CHG>. ; <abr_CHG>\n {BEGIN(0);return(ENTER);} <abr_ID>{ID} {yylval.id=(char *)strdup(yytext);BEGIN(0);return(ID);} <abr_ID>{ESPACOS} ; <abr_ID>. ; <abr_ID>\n {BEGIN(0);return(*yytext);} LS{ESPACOS}\n {return(LS);} CLEAN{ESPACOS}\n {return(CLEAN);} LIST{ESPACOS}\n {return (LIST);} EXIT{ESPACOS}\n {return (EXIT);} HELP{ESPACOS}\n {return (HELP);} {ESPACOS} ; \n {return(ENTER);} . {BEGIN(abr_LX);} <abr_LX>. {BEGIN(abr_LX);} <abr_LX>\n {BEGIN(0);return (*yytext);}
%{ #include "arvores.h" AB *arvB=NULL; %} %token LOAD SHOW LIST EXIT HELP FICH ID CREATE CHANGE CLEAN LS ENTER %union{ char *fich; char *id; } %type <id> ID %type <fich> FICH %start Interpretador %% Interpretador : ListaComandos ListaComandos : Comando |ListaComandos Comando ; Comando : LOAD FICH ID ENTER { arvB=insArvB(arvB,$3,$2); return 1; } |SHOW ID ENTER { mostraESIS(arvB,$2,"ecran"); return 1; } |LIST { consArvB(arvB); return 1; } |EXIT {exit(0);} |LS { system("ls -la *.esis"); return 1; } |CLEAN { arvB=NULL; return 1; } |CREATE ID ENTER { mostraESIS(arvB,$2,"disco"); return 1; } |CHANGE ID ID ENTER { troca_ids(arvB,$2,$3); return 1; } |HELP { TabHELP(); return 1; } |ENTER {return 1;} ; %% #include <stdio.h> #include <stdlib.h> #include "lex.yy.c" int main(void) { printf("\t\t\n ***** Command's Mini-Interpretor ***** \n\n"); printf("\nTrabFase2 @ PLI-Shell#> "); while(yyparse()) printf("\nTrabFase2 @ PLI-Shell#> "); return 1; } int yyerror(char *s) { printf(" Invalid Command, or too few arguments !!\n"); printf(" Print HELP for more information \n"); return 1; }
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> typedef struct Arv { char *id; char *fich; struct Arv *esq, *dir; }AB; char *tiraAspas(char *file) { int i; for(i=1 ; file[i] != '\"' ; i++) *(file + i-1) = *(file + i); *(file + i-1)='\0'; return file; } AB *insArvB (AB *arv,char *id,char *fich) { AB *new; int comp; if ( arv != NULL ) { comp=strcmp(arv->id,id); if ( comp ==0 ) printf("This identifier exist !!\n"); if ( comp > 0 ) { arv->esq=insArvB(arv->esq,id,fich); } if ( comp < 0 ) { arv->dir=insArvB(arv->dir,id,fich); } } else { new=(struct Arv *)malloc(sizeof (struct Arv)); new->id=id; new->fich=tiraAspas(fich); new->esq=NULL; new->dir=NULL; return new; } return arv; } void da_espacos(int esp) { if(esp > 0) while(esp > 0){ printf(" ");esp--; } else printf(" "); } void escreve(char *id , char *fich) { printf("\t\t¦ %s",id); da_espacos(20-strlen(id)); printf("¦ %s",fich); da_espacos(27-strlen(fich)); printf("¦\n"); } void consArvB_aux(AB *arv) { if( arv != NULL) { escreve(arv->id,arv->fich); consArvB_aux(arv->esq); consArvB_aux(arv->dir); } } void consArvB(AB *arv) { printf("\t\t/--------------------------------------------------------\\\n"); printf("\t\t| IDENTIFIER ¦ FILE |\n"); printf("\t\t|--------------------------------------------------------|\n"); consArvB_aux(arv); printf("\t\t\\--------------------------------------------------------/\n"); } void TabHELP() { printf("\n\n\t\t/---------------------- HELP -------------------------\\\n"); printf("\t\t| LOAD <fich> <id> -> Loads the file <fich> to |\n"); printf("\t\t| memory with the <id> identifier |\n"); printf("\t\t| SHOW <id> -> Shows the ESIS ,if possible , for the |\n"); printf("\t\t| file with <id> as identifier |\n"); printf("\t\t| LIST -> Shows the table with the files and the |\n"); printf("\t\t| respectives identifiers |\n"); printf("\t\t| HELP -> Displays this help !! |\n"); printf("\t\t| LS -> Shows the files in the current directory, |\n"); printf("\t\t| you can only see .esis files |\n"); printf("\t\t| CLEAN -> Resets from table all files and respective |\n"); printf("\t\t| identifier |\n"); printf("\t\t| CHANGE <id1> <id2> -> Changes the name of the <id1> |\n"); printf("\t\t| to <id2> |\n"); printf("\t\t| CREATE <id> -> Creates a ESIS's file |\n"); printf("\t\t| EXIT -> Exits the Mini-Interpretor |\n"); printf("\t\t|-----------------------------------------------------|\n"); printf("\t\t| <fich> -> Name of file between \" |\n"); printf("\t\t| <id> -> Name of the identifier |\n"); printf("\t\t\\-----------------------------------------------------/\n"); } int existe_in_AB( AB *a , char *id ) { int comp=0; if(a !=NULL ) { comp=strcmp(a->id,id); if( comp == 0 ) return 1; if( comp > 0 ) return existe_in_AB( a->esq , id ); if( comp < 0 ) return existe_in_AB( a->dir , id ); } return 0; } void troca_ids(AB *a , char *id , char *id2) {int comp=0; if(a !=NULL ) { if(!existe_in_AB(a,id)) { printf("The identifier doesn't exist!!"); return ; } if(!existe_in_AB(a,id2)) { comp=strcmp(a->id,id); if( comp == 0 ) a->id=id2; if( comp > 0 ) troca_ids( a->esq , id , id2 ); if( comp < 0 ) troca_ids( a->dir , id , id2 ); } else printf(" This identifier belongs to another file!!"); } } char *dafich(AB *a , char *id) { int comp; comp=strcmp(a->id,id); if( comp == 0 ) return(char *)strdup( a->fich ); if( comp > 0 ) return dafich( a->esq , id ); if( comp < 0 ) return dafich( a->dir , id ); } void mostraESIS (AB *a , char *id , char *sitio ) { if (a != NULL) { if(existe_in_AB(a,id)) { char *com; com=strdup("analisador -esis <"); strcat(com,dafich(a,id)); if(!strcmp(sitio,"disco")) { strcat(com," >"); strcat(com,id); strcat(com,".esis"); printf("\nIf everything is ok,file saved as %s.esis\n", id); printf("execute LS to verify!!\n"); } system (com); } else printf(" Identifer not found!!\n"); } else printf(" The table is Empty!!\n"); }
Queremos apenas agradecer aos professores que nos ajudaram nas dúvidas que lhes iámos pondo acerca do nosso trabalho,que por exemplo ao fim das aulas não se importavam de tirar essas dúvidas.
Queremos também agradecer a alguns colegas nossos que em conjunto chegamos á resolução dos nossos problemas .
Não revelamos os nomes, porque não achamos elegante da nossa parte.
Queremos ainda agradecer aos nossos pais por nos terem lembrado, ou não, de nos conceberem, provavelmente não tinham ainda televisão , Obrigado!!
Bibliografia: