Escolar Documentos
Profissional Documentos
Cultura Documentos
Tutorial Criando Um Pequeno Compilador
Tutorial Criando Um Pequeno Compilador
Neste tutorial ser criado, com instrues passo a passo, um pequeno compilador, ou
para ser mais preciso um interpretador, para expresses numricas.
Neste interpretador sero aceitas espresses com nmeros, as quatro operaes bsicas e
parnteses. Multiplicao e diviso devero ter prioridade maior que adio e subtrao.
Sero detalhadas as especificaes lxica e sinttica, ser implementado o analisador
semntico e por fim ser feito um programa para testar os analisadores.
Para este tutorial sero feitos o analisador lxico e o sinttico, e os exemplos de cdigo
sero em Java, mas em alguns casos sero tambm dados exemplos em C++ e em
Delphi para mostrar as difernas.
Especificao Lxica
A primeira coisa a se fazer em uma especificao lxica e saber quais os tokens que
devero ser reconhecidos pelo analisador.
Como neste exemplo vo ser precisos nmeros, operadores e parnteses, j possvel
ter uma idia de quais tokens sero precisos:
NUMERO
+
*
/
(
)
Definies Regulares
Pare este exemplo ser feira apenas a seguinte definio:
D : [0-9]
Tokens
Expresses Regulares
Esta tabela ilustra as possbilidades de expresses regulares. Quaisquer combinaes
entre estes padres possvel. Espaos em branco so ignorados (exceto entre " e ").
a
reconhece a
ab
reconhece a seguido de b
a|b
reconhece a ou b
[abc]
recohece a, b ou c
reconhece a, b, c, ... ou z
a*
a+
a?
reconhece um a ou nenhum a.
\123
Caracteres especiais
Os caracteres " \ | * + ? ( ) [ ] { } . ^ - possuem significado especial. Para
utiliz-los como caracteres normais deve-se preced-los por \, ou coloc-los entre " e ".
Qualquer sequancia de caractateres entre " e " tratada como caracteres ordinrios.
\+
reconhece +
"+*"
reconhece + seguido de *
reconhece "
Line Feed
\r
Carriage Return
\s
Espao
\t
Tabulao
\b
Backspace
\e
Esc
Assim define-se que BEGIN, END e WHILE so casos especiais de ID. Sempre que o
analisador encontrar um ID ele procura na lista de casos especiais para ver se este ID
no um BEGIN ou um WHILE.
Especificao Sinttica
A especificao sinttica feita de produes. As produes para uma gramtica para
expresses nmericas da forma especificada ficam da seguinte forma:
<E> ::= <E> "+" <T>
| <E> "-" <T>
| <T>;
<T> ::= <T> "*" <F>
| <T> "/" <F>
| <F>;
<F> ::= "(" <E> ")" | NUMERO;
Podem ser utilizados na gramtica qualquer token j declarado como smbolo terminal.
Os smbolos no-terminais precisam ser previamente declarados em sua rea especfica.
Esta gramtica possui recurses esquerda e no est fatorada. No possvel processla com um analisador preditivo sem que antes a gramtica seja transformada. Neste
exemplo ser feito um analisador SLR, portanto a gramtica j est pronta.
Inserindo as aes semnticas na gramtica ela fica assim:
<E> ::= <E> "+" <T> #2
| <E> "-" <T> #3
| <T>;
<T> ::= <T> "*" <F> #4
| <T> "/" <F> #5
| <F>;
<F> ::= "(" <E> ")" | NUMERO #1;
As aes so distribuidas j pensando-se na anlise semntica. A ao 1 acontece aps
um NUMERO ser encontrado. Sua implementao ir calcular o valor numrico do
token NUMERO e empilh-lo.
As demais aes iro desempilhar dois valores, efetuar uma operao sobre eles e
empilhar o resultado.
Implementao do Semntico
gerada uma classe para o analisador semntico. Sua implementao porm por conta
do usurio.
O nico mtodo que o analisador semntico possu o mtodo executeAction(). O
analisador sinttico chama ele sempre que uma ao semntica encontrada. So
passados de pametro para este mtodo o nmero da ao semntica que o disparou, e o
ltimo token reconhecido antes da ao.
Para este exemplo, o analisador semntico vai precisar apenas de uma pilha para avaliar
as expresses. Em casos mais complexos, como um compilador, ser preciso uma tabela
de smbolos tambm. E o gerador de cdigo deve ser acionado por aes semnticas
tambm.
import java.util.Stack;
public class Semantico implements Constants
{
Stack stack = new Stack();
public int getResult()
{
return ((Integer)stack.peek()).intValue();
}
public void executeAction(int action, Token token) throws
SemanticError
{
Integer a, b;
switch (action)
{
case 1:
String tmp = currentToken.getLexeme();
if (tmp.charAt(0) == '0')
throw new SemanticError("Nmeros comeados por 0 no so
permitidos", token.getPosition());
stack.push(Integer.valueOf(tmp));
break;
case 2:
b = (Integer) stack.pop();
a = (Integer) stack.pop();
stack.push(new Integer(a.intValue() + b.intValue()));
break;
case 3:
b = (Integer) stack.pop();
a = (Integer) stack.pop();
stack.push(new Integer(a.intValue() - b.intValue()));
break;
case 4:
b = (Integer) stack.pop();
a = (Integer) stack.pop();
stack.push(new Integer(a.intValue() * b.intValue()));
break;
case 5:
}
}
b = (Integer) stack.pop();
a = (Integer) stack.pop();
stack.push(new Integer(a.intValue() / b.intValue()));
break;
C++
catch ( SintaticError e )
{
e.printStackTrace();
}
catch ( SemanticError e )
{
e.printStackTrace();
}
}
}
Para utilizar os analisadores gerados pelo GALS, deve seguir os seguintes passos:
Em Java
Lexico lexico = new Lexico();
Sintatico sintatico = new Sintatico();
Semantico semantico = new Semantico();
...
lexico.setInput( /* entrada */ );
try
{
sintatico.parse(lexico, semantico);
}
catch ( LexicalError e )
{
//Trada erros lxicos
}
catch ( SintaticError e )
{
//Trada erros sintticos
}
catch ( SemanticError e )
{
//Trada erros semnticos
}
Em C++
Lexico lexico;
Sintatico sintatico;
Semantico semantico;
...
lexico.setInput( /* entrada */ );
try
{
}
sintatico.parse(&lexico, &semantico);
Em Delphi
lexico : TLexico;
sintatico : TSintatico;
semantico : TSemantico;
...
lexico := TLexico.create;
sintatico := TSintatico.create;
semantico := TSemantico.create;
...
lexico.setInput( /* entrada */ );
try
sintatico.parse(lexico, semantico);
except
on e : ELexicalError do
//Trada erros lxicos
on e : ESintaticError do
//Trada erros sintticos
on e : ESemanticError do
//Trada erros semnticos
end;
...
lexico.destroy;
sintatico.destroy;
semantico.destroy;
Mesagens de Erro
So geradas mensagens de erro default para os possvies erros. Em alguns casos elas
podem ser apropriadas, mas em geral voc vai querer alter-las para informar ao usurio
uma mensagem mais adequada. As tabelas de erro esto nos arquivos com as constantes.