Resumo:
Este é o relatório da primeira fase do trabalho prático de Processamento de Linguagens I, fase esta que consistia na implementação de um analisador léxico e sintático de um subconjunto da linguagem XML e na criação de um grove para armazenar a informação do texto para subsequente geração do ESIS.
Nesta fase, começámos por testar a gramática definida na aula prática, que foi sendo modificada ao longo da implementação, para corrigir os erros que iam sendo encontrados.
O analisador léxico foi implementado em flex . Inicialmente era bastante simples e não reconhecia os textos de teste na perfeição, dando por vezes erro com certos caracteres e tendo certos problemas com alguns espaços em branco. Estes problemas foram resolvidos quando passámos a utilizar start-conditions no flex , que simplificaram, e muito, o desenvolvimento do analisador. Devemos mencionar que, as start-conditions utilizadas são exclusivas, embora neste caso isso seja indiferente, pois não existe nenhuma produção no analisador léxico que não esteja sujeita a uma start-condition .
Para a implementação do analisador sintático utilizámos o yacc . A gramática foi modificada várias vezes, até à versão final que, em virtude da simplificação operada no analisador léxico aquando da introdução de start-conditions , ficou praticamente igual à versão criada na aula prática em que foi apresentado o trabalho. As únicas diferenças residem na produção inicial, que foi modificada para garantir que o documento é iniciado com uma tag, e no facto de estarmos a utilizar recursividade à esquerda nas produções recursivas.
A estrutura criada para o grove consiste numa lista ligada de estruturas com apontadores para nome do nodo, filhos (ou no caso de ser um nodo de texto ou de macro, um apontador para o texto ou para o nome da macro, respectivamente), irmãos, e lista de atributos, tal como foi pedido na aula prática. Temos no entanto de referir que, devido à maneira como os nodos do grove são criados, e ao facto de estarmos a utilizar recursividade à esquerda, a lista de nodos irmãos de um nodo qualquer fica ligada pela ordem inversa à que é introduzida no texto, o mesmo acontecendo com a lista de atributos de uma tag. Quanto ao ESIS, criamos uma função travessia para o gerar, função esta que leva em conta a inversão da lista de nodos referida anteriormente, mas não a inversão da lista de atributos, que pensamos não afectar o reconhecimento, pelo menos nesta fase do trabalho.
ESPACO [\ \t\n]+ PALAVRA [a-zA-Z][a-zA-Z0-9]* %x INTAG INAND %% <INITIAL>\< { BEGIN INTAG; return *yytext; } <INITIAL>\<\/ { BEGIN INTAG; return ETAGOPEN; } <INTAG>\> { BEGIN INITIAL; return *yytext; } <INTAG>\/\> { BEGIN INITIAL; return CLOSETAG; } <INTAG>\= { return *yytext; } <INTAG>{PALAVRA} { yylval.t=strdup(yytext); return(ID); } <INTAG>\"[^\"]*\" { yytext[strlen(yytext)-1]=0; yylval.t=yytext+1; return(VALOR); } <INTAG>{ESPACO} ; <INITIAL>\& {BEGIN INAND; return AND;} <INITIAL>[^&<]+ { yylval.t=strdup(yytext); return(CHAR); } <INAND>{PALAVRA} { yylval.t=strdup(yytext); return ID; } <INAND>\; { BEGIN INITIAL; return ENDAND; }
%{ #include<string.h> typedef struct attrNode { char *name; char *value; struct attrNode *next; } aNode; typedef struct groveNode { char *name; union { char *text; struct groveNode *son; } content; struct groveNode *brother; struct attrNode *attrList; } gNode; typedef enum{NODO,TEXTO,AND} gNodeType; gNode *grove, *gtest; aNode *atest; void travessia(gNode *grv); gNode *addGNode(gNodeType tp, char *name, void *cont, gNode *bro, aNode *at); %} %token ID ETAGOPEN CLOSETAG VALOR ESPACO AND ENDAND CHAR %union{ char *t; aNode *att; gNode *grv; } %type <t> ID ESPACO VALOR CHAR %type <att> Attr AttrList %type <grv> Elem ElemList Doc %% Doc: '<' ID AttrList '>' ElemList ETAGOPEN ID '>' { if (!strcmp($2,$7)) { printf("*******AXIOMA RECONHECIDO!!!!*******\n"); grove=$$=addGNode(NODO, $2, $5, NULL, $3); printf("\n"); return; } else exit(-1); } ; ElemList: /*empty*/ { $$=NULL; } | ElemList Elem { $2->brother=$1; $$=$2; } ; Elem: CHAR { $$=addGNode(TEXTO, NULL, $1, NULL, NULL); } | AND ID ENDAND { $$=addGNode(AND, NULL, $2, NULL, NULL); } | '<' ID AttrList '>' ElemList ETAGOPEN ID '>' { if (strcmp($2,$7) != 0) { printf("TAGS NAO COINCIDEM!!!!\n"); return; } $$=addGNode(NODO, $2, $5, NULL, $3); } | '<' ID AttrList CLOSETAG { $$=addGNode(NODO, $2, NULL, NULL, $3); } ; AttrList: /*empty*/ { $$=NULL; } | AttrList Attr { $2->next=$1; $$=$2; } ; Attr: ID '=' VALOR { if ( ($$=(aNode*)malloc(sizeof(struct attrNode)))==NULL) exit(-1); $$->name=strdup($1); $$->value=strdup($3); $$->next=NULL; } ; %% #include "scanner.c" int main() { yyparse(); travessia(grove); printf("\n"); return 0; } void yyerror(char *text) { printf("Erro: %s\n",text); } void travessia(gNode *grv) { if (strcmp(grv->name,"TEXTO")==0) { if (grv->brother!=NULL) travessia(grv->brother); printf("- %s\n",grv->content.text); } else if (strcmp(grv->name,"AND")==0) { if (grv->brother!=NULL) travessia(grv->brother); printf("&(%s)\n",grv->content.text); } else { aNode *attrAux=grv->attrList; if (grv->brother!=NULL) travessia(grv->brother); while(attrAux!=NULL) { printf("A %s %s\n", attrAux->name, attrAux->value); attrAux=attrAux->next; } printf("(%s\n",grv->name); if (grv->content.son!=NULL) travessia(grv->content.son); printf(")%s\n",grv->name); } } gNode *addGNode(gNodeType tp, char *name, void *cont, gNode *bro, aNode *at) { gNode *grv; if ( (grv=(gNode*)malloc(sizeof(struct groveNode)))==NULL) { printf("Erro na aloca,c~ao dum nodo do grove.\n"); exit(-1); } if (tp==AND) { grv->name=strdup("AND"); grv->content.text=strdup((char *)cont); } else if (tp==TEXTO) { grv->name=strdup("TEXTO"); grv->content.text=strdup((char *)cont); } else { grv->name=strdup(name); grv->content.son=(gNode *)cont; } grv->brother=bro; grv->attrList=at; return(grv); }
PROCDEPS= y.tab.c YACCDEPS= fase1.y scanner.c FLEXDEPS= fase1.l fase1: $(PROCDEPS) gcc -o fase1 $< -lfl -ggdb y.tab.c: $(YACCDEPS) yacc -v fase1.y scanner.c: $(FLEXDEPS) lex -oscanner.c $< clean: scanner.c y.tab.c y.output rm scanner.c rm y.output rm y.tab.c
Devemos agradecer aos professores Pedro Henriques e José Ramalho pela ajuda dispensada, sempre que solicitada. Agradecemos ainda ao nosso colega Marco Monteiro pelas dicas que nos deu, principalmente por nos ensinar a usar start-conditions no flex .
Bibliografia: