Compiladores  
Aula 05 Construção de Árvores Sintáticas  
(Implementação)  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
Processo de Compilação  
unidades léxicas  
Analisador  
Léxico  
Analisador  
Sintático  
Programa-fonte  
parse trees  
(opcional)  
Gerador de Código  
Intermediário e  
Analisador  
Tabela de Símbolos  
Otimização  
Semântico  
código  
intermediário  
linguagem de máquina  
Programa Objeto  
Gerador de Código  
Análise Sintática  
Os analisadores sintáticos constroem árvores de análise  
sintática (parse trees) para os programas dados.  
Em alguns casos, a parse tree é construída implicitamente (apenas o  
resultado de percorrer a árvore é gerado);  
Objetivos da Análise Sintática:  
Verificar o programa de entrada para determinar se ele está  
sintaticamente correto;  
Produzir árvores de análise (parse trees).  
Análise Sintática  
Saída:  
Token: 25, Lexema: (  
Enter <expr>: 25  
<expr>  
Enter <termo>: 25  
Enter <fator>: 25  
Token: 11, Lexema: soma  
Enter <expr>: 11  
<termo>  
Enter <termo>: 11  
Enter <fator>: 11  
Token: 21, Lexema: +  
Exit <fator>: 11  
<
fator>  
/
)
<
fator>  
Exit <termo>: 21  
Token: 10, Lexema: 47  
Enter <termo>: 10  
Enter <fator>: 10  
Token: 26, Lexema: )  
Exit <fator>: 10  
(
<expr>  
+
total  
<
termo>  
<termo>  
fator>  
Exit <termo>: 26  
Exit <expr>: 11  
Token: 24, Lexema: /  
Exit <fator>: 25  
Token: 11, Lexema: total  
Enter <fator>: 11  
Token: -1, Lexema: EOF  
Exit <fator>: 11  
<
fator>  
<
soma  
47  
Exit <termo>: -1  
Exit <expr>: 25  
Árvores  
Uma árvore é composta por um conjunto de nós tal que:  
Existe um nó r, denominado raiz, com zero ou mais sub-árvores, cujas  
raízes estão ligadas a r;  
Os nós raízes destas sub-árvores são os filhos de r;  
Os nós internos da árvore são os nós com filhos;  
As folhas ou nós externos da árvore são os nós sem filhos;  
Árvores  
Estrutura de uma Árvore:  
#define N 3  
struct noArvore  
{
int info;  
struct noArvore* filhos[N];  
};  
Árvores Implementação  
Representação de um nó da árvore:  
Estrutura em C contendo:  
A informação propriamente dita (exemplo: um caractere);  
Um vetor de ponteiros para as sub-árvores;  
#define MAX_CHILDREN 10  
typedef struct node {  
char* info;  
int type;  
struct node* children[MAX_CHILDREN];  
}
Node;  
Árvores Implementação  
Função CreateNode:  
Cria um nó raiz dadas a informação e inicializa as sub-árvores com  
NULL;  
Retorna o endereço do raiz criado;  
Node* CreateNode(char *value, int ntype)  
{
Node* p = (Node*)malloc(sizeof(Node));  
int i;  
p->info = value;  
p->type = ntype;  
for (i = 0; i < MAX_CHILDREN; i++)  
p->children[i] = NULL;  
return p;  
}
Árvores Implementação  
Função AddChild:  
Adiciona/cria um filho ao nó recebido por parâmetro;  
Retorna o endereço do criado;  
Node* AddChild(Node* node, char *value, int ntype)  
{
int i = 0;  
while (node->children[i] != NULL)  
i++;  
if (i < MAX_CHILDREN)  
node->children[i] = CreateNode(value, ntype);  
else  
printf("Error: MAX_CHILDREN!\n");  
return node->children[i];  
}
Árvores Implementação  
Função PrintTree:  
Imprime a árvore formatada na tela;  
Função recursiva;  
void PrintTree(Node *node, int tabs)  
{
int i = 0;  
int x = 0;  
for (x = 0; x < tabs; x++)  
printf("\t");  
printf("%s\n", node->info);  
while (node->children[i] != NULL)  
{
PrintTree(node->children[i], tabs + 1);  
i++;  
}
}
Construção da Árvore Sintática  
(Implementação)  
.
..  
int termo(FILE *code_file, int next_token, NextChar *next_char,  
Node *tree){  
printf("Enter <termo>\n");  
Node *termonode = AddChild(tree, "termo", -1);  
next_token = fator(code_file, next_token, next_char, termonode);  
while (next_token == MULT_OP || next_token == DIV_OP)  
{
if (next_token == MULT_OP)  
AddChild(termonode, "*", next_token);  
else if (next_token == DIV_OP)  
AddChild(termonode, "/", next_token);  
next_token = lex(code_file, next_char);  
next_token = fator(code_file, next_token, next_char, termonode);  
}
printf("Exit <termo>\n");  
return next_token;  
}
.
..  
Construção da Árvore Sintática  
Implementação)  
(
Entrada:  
int test;  
test = (45 + 100)/2;  
Exercício 01  
1
) Continue a implementação do analisador sintático,  
integrando-o com o com a gramatica desenvolvida nos  
exercícios anteriores. Ou seja, o analisador sintático deve ser  
capaz de construir árvores sintáticas para:  
a) Expressões de atribuição. Exemplo:  
resultado = (soma + 87) / total  
b) Declarações de variáveis (int e float). Exemplo:  
int a  
float b  
c) Programas compostos por mais de uma expressão de atribuição e  
declarações de variáveis (separadas por ponto e vírgula). Exemplo:  
int a;  
float b;  
a = 8 + 42;  
b = a / 3;  
Leitura Complementar  
Aho, A. V., Lam, M. S., Jeffrey, R. S.  
Compiladores: Princípios, Técnicas e  
Ferramentas. 2ª edição, Pearson, 2007.  
ISBN: 978-8588639249.  
Capítulo 3: Lexical Analysis  
Capítulo 4: Syntax Analysis  
Sebesta, R. W. Conceitos de Linguagens de  
Programação. 9ª edição Editora Bookman,  
2
011. ISBN: 978-8577807918.  
Capítulo 3: Descrevendo a Sintaxe e a Semântica  
Capítulo 4: Análise Léxica e Sintática