Trabalho Prático de Processamento de Linguagens I

Universidade do Minho 13/04/2000

Ernesto Miranda Pedrosa da Silva

E-mail: a24814@correio.ci.uminho.pt

Lu´is Filipe Gonçalves de Carvalho

E-mail: a24842@correio.ci.uminho.pt

Samuel Oliveira Santos Conceição

E-mail: a24879@correio.ci.uminho.pt

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


Índice

  1. Introdução
  2. Primeira Fase
    1. Etapa 1
    2. Etapa 2
    3. Etapa 3
  3. Conclusão
  4. C´odigo do Trabalho
    1. LEX
    2. YACC
    3. GROVE.c

Introdução

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.


Primeira Fase

Etapa 1

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			

Etapa 2

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;}

Etapa 3

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);

Conclusão

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.


C´odigo do Trabalho

LEX


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);}
%%

YACC


%{
#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);
}

GROVE.c


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;

Agradecimentos: Bibliografia: