Trabalho Prático de Processamento de Linguagens I - Primeira fase

Universidade do Minho, Campus de Gaultar 10/04/2000

Assis Ricardo Lima Oliveira

E-mail: a17824@ci.uminho.pt

Bruno da Silva do Casal Martins

E-mail: a22645@ci.uminho.pt

Resumo:

Este é o relatório referente à primeira fase do trabalho de Processamento de Linguagens I, em que produzimos um analizador léxico, um analisador semântico, e um grove para a analise de um texto estruturado, como por exemplo o XML , passando para o formato ESIS .


Índice

  1. Introdução
  2. Análise Léxica
    1. Objectivo
    2. Implementação
  3. Análise Sintática
    1. Objectivo
    2. Implementação
  4. Armazenamento da Informação
    1. Objectivo
    2. Implementação
  5. Código dos ficheiros
    1. XML.L
    2. XML.Y
    3. GROVE.H
    4. GROVE.C
  6. Conclusão

Introdução

Inicia-mos esta fase do trabalho com o objctivo de reconhecer num certo ficheiro de texto uma certa estrutura e implementa-la num grove, fazendo no fim uma travessia exortando esse grove num formato ESIS .

Ao dividir esta fase em 3 etapas, fez-nos presenciar com bastante pormenor a todas as evoluções referentes. A ánalise léxica feita no lex para reconhecer todos os caracteres possiveis, a ánalise sintática no yacc para verificar a construção correcta do documento, e a implementação de um grove contendo a informação toda sobre o documento para "tradução" para o formato ESIS .

Assim comecamos a explicação das nossas ideias e decisões.


Análise Léxica

Objectivo

Temos por objectivo nesta análise léxica reconhecer certos itens pré acordados, para exportar para a ánalise sintática.

Implementação

Decidimos de inicio criar estados exclusivos para facilitar o tratamento do documento. Esses estados são :

Repare-se que a utilização de estados exclusivos é muito importante para diminuir a dificuldade da ánalise léxica. Por exemplo, para quando se reconhece texto, saber se este texto é uma ID ou se é texto do utilizador. Verifica-se a mesma situação na análise dos atributos.


Análise Sintática

Objectivo

O objectivo desta análise é verificar se o documento é reconhecido pela gramática dada no enunciado.

Implementação

A gramática podia ser alterada não podendo fugir muito da inicial, pois correriamos o risco de reconhecer um documento num formato completamente diferente do pedido. Apesar do docente ter alterado a gramática, decidimos ainda fazer outra alteração, esta verificou-se apenas numa linha da gramática, e serviu-nos só para a certificação do inicio e do fim do documento ser <doc> e </doc> respectivamente.

O analisador sintático yacc recebe TOKEN's do lex contendo toda a informação reconhecida no documento, passando a fazer a análise sintática.


Armazenamento da Informação

Objectivo

O objectivo desta etapa do trabalho é armazenar toda a informação do documento de maneira a que seja fácil percorre-la e imprimi-la noutro formato.

Implementação

A maneira como a informação está organizada permite-nos expo-la no formato que quisermos bastando para isso alterar a funcao de travessia .

Neste trabalho a função foi feita de maneira a que imprimisse os dados no formato ESIS . A estrutura utilizada para isso foi a dada pelo docente.

Para a ajuda do armazenamento dos dados utilizamos um array de apontadores de todas as TAG's , para sabermos se os dados analizados são "irmãos" ou "conteudo" dos dados anteriores. O indice do array é modificado conforme a abertura ou fecho de TAG's . A chamada das funções de inserção de dados no grove é colocada no ficheiro contendo a gramática, em locais estratégicos de maneira a que os dados se encaixem na maneira pretendida.

Em relação á travessia: esta é feita de maneira recursiva. A função vai até ao "fundo" do conteudo das TAG's verificando no fim a existência ou não de irmãos. No caso afirmativo a função procura o conteudo desses irmãos e assim sucessivamente.

O formato ESIS só é apresentado no caso do documento inicial estar bem estruturado.


Código dos ficheiros

XML.L


%{
#include <string.h>
%}
text ([a-zA-Z]*)
alfa ([a-zA-Z]*[0-9]*[\.\n\ \t\,\:\(\)\[\]\{\}\#\$\%\!\?\+]*)

%x TAG ATTR SMB
%%
\n ;

{alfa} {yylval.car=strdup(yytext);return(TEXTO);}
[\<] {BEGIN TAG;return(*yytext);}
<TAG>{text} {if(strcmp(yytext,"doc")==0) return(DOC); else
{yylval.car=strdup(yytext);return(ID);}}
<TAG>[\ ] {BEGIN ATTR;}
<TAG>\/ {return(*yytext);}
<TAG>\> {BEGIN 0;return(*yytext);}

<ATTR>{text} {yylval.car=strdup(yytext);return(ID);}
<ATTR>\> {BEGIN 0;return(*yytext);}
<ATTR>\/ {return(*yytext);}
<ATTR>= {return(*yytext);}
<ATTR>\"{alfa}\" {yylval.car=strdup(yytext);return(VALOR);}

[&] {BEGIN SMB;return(*yytext);}
<SMB>{text} {yylval.car=strdup(yytext);return(ID);}
<SMB>; {BEGIN 0;return(*yytext);}

. {return(ERRO);}
%%

XML.Y


%token VALOR ERRO TEXTO DOC ID
%union {
        char *car;
}
%type <car> TEXTO ID VALOR
%start Documento
%%
Documento: '<' DOC '>'{insere_1_id("doc");} ElemList '<' '/' DOC '>'{travessia();}

ElemList : ElemList Elem
         | Elem;

Elem : TEXTO  {insere_texto($1);}
     | '&' ID ';' {insere_texto("&");insere_texto($2);insere_texto(";");}
     | '<' ID AttrList '>' {insere_id($2);} ElemList '<' '/' ID '>'{decresce();}
     | '<' ID AttrList {insere_id($2);} '/' '>'; {decresce();}

AttrList : AttrList Attr
         |;

Attr : ID '=' VALOR; {insere_atr($1,$3);}

%%
#include "lex.yy.c"
#include <stdio.h>
#include <string.h>
int main() {
        yyparse();
        return 0;
}
void yyerror (char *s){}

GROVE.H


#include <string.h>


typedef struct Anodo {
        char* nome;
        char* valor;
        struct Anodo* seg;
}atributo;

typedef struct Gnodo {
        char* id;
        union C {
                char* texto;
                struct X {
                        struct Gnodo* conteudo;
                        atributo* atribs;
                }normal;
        }cont;
        struct Gnodo* irmaos;
}grove;

void insere_texto(char* s);
void insere_1_id(char* id);
void insere_id(char* id);
void insere_atr(char* id,char* vl);
void decresce();
void travessia();
void travessia_aux(grove* gaux);
void travessia_atr(atributo* a);

GROVE.C


#include "grove.h"
#include <glib.h>
#include <stdio.h>
#include <string.h>


grove* g=NULL;
atributo* a=NULL;
atributo* a1=NULL;
grove* array[1000];
int i=0;

void insere_texto(char* s){
        char* str="";
        if (strcmp(g->;id,"TEXTO")==0){
                str=g->cont.texto;
                str=(char*)realloc(str,strlen(str)+strlen(s)+1);
                str=strcat(str,s);
                g->cont.texto=str;
        }
        else {
                if(strcmp(array[i-1]->id,g->id)!=0){
                        g->irmaos=(grove*)malloc(sizeof(grove));
                        g=g->irmaos;
                } else {
			g->cont.normal.conteudo=(grove*)malloc(sizeof(grove));
                        g=g->cont.normal.conteudo;
                }
                g->id="TEXTO";
                g->cont.texto=s;
        }
}

void insere_1_id(char* id){
        g=(grove*)malloc(sizeof(grove));
        g->id=id;
        array[0]=g;
        i=1;
}

void insere_id(char* id){
        if(strcmp(g->id,"TEXTO")==0){
                g->irmaos=(grove*)malloc(sizeof(grove));
                g=g->irmaos;
        } else {
                if(strcmp(array[i-1]->id,g->id)==0){
		g->cont.normal.conteudo=(grove*)malloc(sizeof(grove));
                        g=g->cont.normal.conteudo;
                } else {
                        g->irmaos=(grove*)malloc(sizeof(grove));
                        g=g->irmaos;
                }
        }
        g->id=id;
        if (a1!=NULL) {
                g->cont.normal.atribs=a1;
                a1=NULL;
        }
        array[i]=g;
        i++;
}

void insere_atr(char* id,char* vl){
        char* v_aux;
        v_aux=strtok(vl,"\"");
        if(a1==NULL){
                a=(atributo*)malloc(sizeof(atributo));
                a1=a;
                a->nome=id;
                a->valor=v_aux;
                a->seg=NULL;
        } else {
                a->seg=(atributo*)malloc(sizeof(atributo));
                a=a->seg;
                a->nome=id;
                a->valor=v_aux;
                a->seg=NULL;
        }
}

void decresce(){
        array[i]=NULL;
        i--;
        g=array[i]; }

void travessia(){
        grove* gaux=NULL;
        g=array[0];
        travessia_aux(g);
}

void travessia_aux(grove* gc){
        if(strcmp(gc->id,"TEXTO")!=0){
                travessia_atr(gc->cont.normal.atribs);

                printf("( %s\n",gc->id);
                if(gc->cont.normal.conteudo!=NULL)
                        travessia_aux(gc->cont.normal.conteudo);
                printf(") %s\n",gc->id);
        } else {
                        printf("- %s\n",gc->cont.texto);
        }
        if(gc->irmaos!=NULL)
                travessia_aux(gc->irmaos);
}

void travessia_atr(atributo* a){
        while(a!=NULL){
                printf("A %s=%s\n",a->nome,a->valor);
                a=a->seg;
        }
}

Conclusão

Chegou a hora de fazer uma avalição sobre esta primeira fase do nosso trabalho prático. Achamos que foi bastante positivo, não só pelo conhecimento a fundo de toda a matéria que envolve este trabalho, mas também pelo conhecimento de todas as ferramentas que utilizamos.

Estavamos de sobre aviso que esta primeira fase tinha que ser bem contruida pois vamos passar a utilizar-la daqui em diante. Achamos que conseguimos atingir esse objectivo, o que não quer dizer que mais tarde não precisamos de aplicar melhoramentos ao nosso código.

Bem que nos resta agora dizer, apenas que esperamos pela próxima fase.


Agradecimentos:

Agradecemos ao professor José Carlos Ramalho pelos esclarecimentos sobre as duvidas que nos ocorreram durante a realização deste trabalho.

Bibliografia: