Resumo:
Este relat´orio corresponde à primeira fase do trabalho da disciplina de Processamento de Linguagens I, que consiste na implementação de um Processador Gen´erico de Documentos Estruturados
O objectivo desta primeira fase é o de implementar uma ferramenta que analise documentos estruturados e construa uma estrutura de dados( Grove ) com toda a informação do mesmo. Esta fase estava dividida em 3 etapas, as quais descreveremos detalhadamente posteriormente.
Nesta etapa era-nos pedido a contrução de uma analisador l´exico e sint´actico, usando as ferramentas LEX + YACC para a seguinte gram´atica:
Documento : ElemList '$' ElemList : ElemList Elem | Elem Elem : char | '&' id ';' | '<' id AttrList '>' ElemList '<' '/' id '>' | '<' id AttrList '/' '>' AttrList : Attr AttrList | Attr : id '=' valor
Fizemos as alterações à gram´atica que achamos necess´arias para tornar o nosso programa mais funcional resultando em:
Documento : ElemList '$' ElemList : ElemList Elem | Elem : Texto | '&' ID ';' | '<' ID AttrList Composto Texto : Texto CHAR |CHAR |E Composto : '>' ElemList '<' '/' ID '>' | '/' '>' AttrList : AttrList Attr | Attr : ID '=' VALOR
Nesta etapa t´inhamos que fazer algumas validçõ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á englobar todo o documento).
Para a primeira validação fizemos:
| '<' ID AttrList Composto {if($4 != NULL && strcasecmp($2,$4->id) != 0){ printf("\nIdentificador %s aberto e %s a termina-lo\n",$2,$4->id); exit(0);} ...
Para a segunda criamos um estado em Lex que apenas aceita que os documentos comecem por uma Tag:
\<{id}\> {yyless(0);BEGIN B;}
Nesta etapa t´inhamos que fazer o armazenamento de toda a informação num grove. Decidimos algumas alterações na estrutura de dados oferecida pelo professor. Criamos apontadores para a ´ultima posição da lista de atributos e para a ´ultima posição do grove, de modo a facilitar a concatenação dos v´arios elementos. Sendo assim ficamos com a seguinte estrutura:
typedef struct Anodo{ char* nome; char* valor; struct Anodo* seg; struct Anodo* ult; }atributo; typedef struct Gnodo{ char* id; union C{ char* texto; /* para nodos _texto */ struct X{ /* para os outros nodos */ struct Gnodo* conteudo; atributo* atribs; }normal; }cont; struct Gnodo* irmaos; struct Gnodo* ultimo; }grove;
Fizemos tb as seguintes funções para percorrer todo o grove e gerar um documento no formato ESIS. Temos tb uma função que retira tamb´em alguns caracteres indesejados antes de inserir no grove(ex. \n, espaços,...).
void percorreaux(atributo *a); void corta_espacos_dir( char *command ); void percorre(grove *g1);
De uma maneira geral fic´amos satisfeitos com a nossa resolução, pois pensamos que cumprimos os objectivos pedidos pelo docente na totalidade.
Apesar de nos termos deparado com algumas dificuldades na parte inicial, com alguma investigação conseguimos ultrapassa-las e construir um trabalho que nos satisfaz plenamente.
Só queriamos pedir desculpa pela simplicidade deste relat´orio mas ela justifica-se por se tratar de um relat´orio interm´edio e não do relat´orio final.
Aguard´amos ansiosamente a segunda fase do trabalho.
char [^\&\<] id [a-zA-Z][a-zA-Z0-9\-]* valor \"[^\"]*\" %x A B C %% \<{id}\> {yyless(0);BEGIN B;} <B>\&\& {printf("%s",yytext);yylval.string=(char*)strdup(yytext);return(E);} <B>\\\& {printf("%s",yytext);yylval.string=(char*)strdup(yytext);return( E);} <B>[\<\&] {printf("%c",*yytext);BEGIN A;return((char)*yytext);} <A>{id} {printf("%s",yytext);yylval.string=(char *)strdup(yytext);return(ID);} <A>[\>\;] {printf("%c",*yytext);BEGIN B;return((char)*yytext);} <A>[\=\/] {printf("%c",*yytext);return((char)*yytext);} <A>{valor} {yylval.string=(char*)strdup(yytext);printf("%s",yytext);return(VALOR);} <B><<eof>> {return('$');exit(0);} <B>{char} {yylval.car=(char)*yytext;printf("%c",*yytext);return(CHAR);} . {printf("O Documento tem que comecar com uma tag\n");exit(0);} %%
%{ #include "grove.c" grove *g; char *s,c; %} %union { char car; char *string; grove *gr; atributo *at; } %token <car> CHAR %token <string> ID VALOR E %type <gr> ElemList Elem Composto %type <at> Attr AttrList %type <string> Texto %start Documento %% Documento : ElemList '$' {g = $1;printf("CHEGOU AO FIM\n");} ElemList : ElemList Elem {//printf("\nElemList Elem\n"); if($1 != NULL && $2 != NULL){ $1->ultimo->irmaos = $2; $1->ultimo = $2; $$ = $1; }else if($2 != NULL){ $$ = $2; }else{ $$ = $1; } } | {//printf("\nElemList\n"); $$ = NULL;} Elem : Texto {//printf("\nElem Texto\n"); $$ = (grove *)malloc(sizeof(struct Gnodo)); $$->id = NULL; $$->cont.texto = (char *)strdup($1); corta_espacos_dir($$->cont.texto); $$->irmaos = NULL; $$->ultimo = $$; if($$->cont.texto[0] == '\0'){$$ = NULL;} } | '&' ID ';' {//printf("\nElemId\n"); $$=(grove *)malloc(sizeof(struct Gnodo)); $$->id=(char *)strdup($2); $$->cont.normal.conteudo = NULL; $$->cont.normal.atribs = NULL; $$->irmaos = NULL; $$->ultimo = $$;} | '<' ID AttrList Composto {//printf("\nElemAttCom\n"); if($4 != NULL && strcasecmp($2,$4->id) != 0){ printf("\nIdentificador %s aberto e %s a termina-lo\n",$2,$4->id); exit(0); }else{ if($4 == NULL){ $$=(grove *)malloc(sizeof(struct Gnodo)); $$->id=(char *)strdup($2); $$->cont.normal.conteudo = NULL; $$->cont.normal.atribs = $3; $$->irmaos = NULL; $$->ultimo = $$; }else{ $4->cont.normal.atribs = $3; $$ = $4; } } } Texto : Texto CHAR {//printf("\nTexto Char\n"); s = (char *)strdup("a"); s[0] = $2; $$ = (char *)strcat($1,s); //printf("\t%s\n",$$); } |CHAR {//printf("\nChar\n"); $$ = (char *)strdup("a"); $$[0] = $1; //printf("%s\n",$$); } |E { $$ = (char *)strdup($1); } Composto : '>' ElemList '<' '/' ID '>' {//printf("\nComp ElemL\n"); $$=(grove *)malloc(sizeof(struct Gnodo)); $$->id=(char *)strdup($5); $$->cont.normal.conteudo = $2; $$->cont.normal.atribs = NULL; $$->irmaos = NULL; $$->ultimo = $$;} | '/' '>' {//printf("\nComp \n"); $$=NULL;} AttrList : AttrList Attr {//printf("\nAttrLAtt\n"); if($1 != NULL){ $1->ult->seg = $2; $1->ult = $2; $$ = $1; }else{ $$ = $2; } } | {//printf("\nAttrNull\n"); $$ = NULL;} Attr : ID '=' VALOR {//printf("\nAttr Id V\n"); $$ = (atributo *)malloc(sizeof(struct Anodo)); $$->nome = (char *)strdup($1); $$->valor = (char *)strdup($3); $$->ult = $$; $$->seg = NULL;} %% #include "lex.yy.c" grove *g; void percorreaux(atributo *a){ if(a != NULL){ printf("A %s %s\n",a->nome,a->valor); percorreaux(a->seg); } } void corta_espacos_dir( char *command ) { int i, encontrou = 0 ; i = ( strlen( command ) - 1 ) ; while( ( i >= 0 ) && ( encontrou == 0 ) ) { if( ( command[i] != ' ' ) && ( command[i] != '\n' ) && (command[i] != '\t')) { command[i + 1] = '\0' ; encontrou = 1 ; } else if( i == 0 ) // Neste caso, o comando e formado apenas por "\s" e // "\n". command[i] = '\0' ; i-- ; } } void percorre(grove *g1){ if(g1 != NULL){ if(g1->id == NULL){ printf("-%s\n",g1->cont.texto); percorre(g1->irmaos); }else{ printf("(%s\n",g1->id); percorre(g1->cont.normal.conteudo); percorreaux(g1->cont.normal.atribs); printf(")%s\n",g1->id); percorre(g1->irmaos); } } } main(){ yyparse(); printf("\n\n"); percorre(g); } yyerror(char *s){ printf("%s\n",s); }
typedef struct Anodo{ char* nome; char* valor; struct Anodo* seg; struct Anodo* ult; }atributo; typedef struct Gnodo{ char* id; union C{ char* texto; /* para nodos _texto */ struct X{ /* para os outros nodos */ struct Gnodo* conteudo; atributo* atribs; }normal; }cont; struct Gnodo* irmaos; struct Gnodo* ultimo; }grove;