Este documento fornece instruções para implementar um compilador dividido em 4 etapas: 1) Análise léxica e sintática com inserção na tabela de símbolos, 2) Geração de código intermediário, 3) Otimizações simples, 4) Geração de código assembly. A análise léxica reconhece tokens como palavras-chave e operadores, enquanto a análise sintática define regras gramaticais. Um conflito de precedência no if/else é resolvido atribuindo maior precedência
1. Trabalho de Compiladores (v.2)
Prof. Dr. Erwin Doescher
Análise Léxica: Deve reconhecer os seguintes elementos da linguagem
Palavras reservadas: else if int char return void while puts gets
Símbolos especiais: + – * / < <= > >= == != = ; , { } [ ] ( )
tokens: ID NUM string else if int char return void while puts gets relop
Definições regulares:
ID → letra ( letra | digito | _ )*
NUM → digito digito*
relop → <= | < | > | >= | == | !=
letra → [ a – z ]
digito → [ 0 – 9 ]
escape → " | ' | n | t
simbolos → [][@!#$%&()_+={}~?/;:.>,<|] | "-" | ' | ^
interno → letra | digito| escape | simbolos | espaço
string → "(interno|*)* "
comentarios → / *(/ * | interno| n)**/ | //.*n
Análise Sintática:
programa → lista_decl
lista_decl → lista_decl decl | decl
decl → var_decl | fun_decl
var_decl → tipo_var lista_id_var ;
lista_id_var → lista_id_var, id_var | id_var
id_var → ID | ID [ NUM ] | *ID | *ID [ NUM ]
tipo_var → int | char
fun_decl → tipo_var ID ( params ) composto_decl | void ID ( params ) composto_decl
params → param_lista | void
param_lista → param_lista, param | param
param → tipo_var id_param
id_param → ID | ID [ ] | *ID | *ID [ ]
tipo_fun → tipo_var | void
composto_decl → { local_decl statement_lista }
local_decl → local_decl var_decl |
statement_lista → statement_lista statement |
statement → puts_decl | gets_decl |expr_decl | composto_decl | sel_decl | iter_decl | retorno_decl
puts_decl → puts (ID); | puts (string);
gets_decl → gets (ID);
expr_decl → expr ; | ;
sel_decl → if ( expr ) statement
| if ( expr ) statement else statement
iter_decl → while ( expr ) statement
2. retorno_decl → return ; | return expr ;
expr → var = expr | simples_expr
var → ID | ID [ expr ]
simples_expr → soma_expr relop soma_expr | soma_expr
relacional → <= | < | > | >= | == | !=
soma_expr → soma_expr soma termo | termo
soma → + | –
termo → termo mult fator | fator
mult → * | /
fator → ( expr ) | var | chamada | NUM
chamada → ID ( args )
args → arg_lista |
arg_lista → arg_lista, expr | expr
Sugestão de roteiro de implementação:
1. Defina o atributo dos tokens a serem enviados do analisador léxico para o sintático
2. Defina os atributos dos símbolos gramaticais para o analisador sintático, definindo-os em
uma estrutura
3. Defina o atributo para o analisador implementado com o Yacc como sendo a união dos tipos
definidos nos itens 1) e 2)
4. Antes de implementar o arquivo do analisador léxico, defina os tokens necessários no
arquivo do analisador sintático
5. Faça o analisador léxico
6. Defina as regras de produção no arquivo do analisador sintático, deixando provisoriamente
as regras semânticas em branco
7. Defina as regras semânticas usadas para inserir variáveis na tabela de símbolos
8. Faça a verificação de tipos das regras de produção que necessitarem
9. Defina o tipo de armazenamento a ser usado para o código intermediário: vetor fixo, vetor
dinâmico ou lista ligada
10. Crie as funções auxiliares necessárias para concatenar dois fragmentos de código
intermediário em um único
11. Crie as funções para gerar temporários e rótulos únicos
12. Defina as regras semânticas para gerar o código de 3 endereços de cada produção.
13. Implemente as otimizações básicas simples
14. Implemente a rotina que leia o vetor de código intermediário e escreva o programa em
linguagem de montagem correspondente (código alvo).
Prazos:
04/11/2010 – Analisador Léxico e Sintático com a inserção de dados na tabela de símbolos e
Verificação de tipos
18/11/2010 – Geração do código intermediário
25/11/2010 – Otimizações simples de código
02/12/2010 – (Final) Geração de código alvo
3. Obs.: A produção do if gera um conflito empilha/reduz:
sel_decl → if ( expr ) statement
| if ( expr ) statement else statement
Para resolver o conflito é preciso dar uma precedência maior para o if … else.
Isto pode ser feito no yacc definindo as precedências:
%nonassoc menor_que_else
%nonassoc eles
no começo do arquivo e definindo a produção como:
sel_decl : if '(' expr ')' statement %prec menor_que_else
| if '(' expr ')' statement else statement
;