um tradutor dirigido por sintaxe simples apresenta de forma resumida boa parte do assunto que...

Post on 07-Apr-2016

224 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Um Tradutor Dirigido por Sintaxe Simples

• Apresenta de forma resumida boa parte do assunto que veremos em detalhes no restante do curso

• Dá uma visão geral do processo de compilação

Um compilador simples

• Objetivo: traduzir expressões infixas em posfixas9 – 5 + 2 9 5 – 2 +

• Aplicabilidade: computação baseda em pilha (por exemplo, gerador de código para .NET e JVM)

Front-end de um compilador

Analisador léxico

Programa fonte

Parser

Ger. de código intermediário

Tabela de Símbolos

tokens

árvore sintática

Three-address code

Definição de uma linguagem

• Linguagem = sintaxe + semântica• Sintaxe = forma• Semântica = significado

Definição de uma linguagem

• Especificação da sintaxe: – gramática livre de contexto, BNF

(Backus-Naur Form)• Especificação da Semântica:

– normalmente informal (textual)– formal: uso de semântica operacional,

denotacional, de ações, etc.

Exemplo: if-else

if ( expression ) statement else statement

Exemplo: if-else

if ( expression ) statement else statement

stmt if ( expr ) stmt else stmt

Gramática Livre de Contexto

• Um conjunto de tokens, símbolos terminais• Um conjunto de símbolos não-terminais• Um conjunto de produções, cada produção

consiste de um não-terminal, uma seta, e uma sequencia de tokens e/ou não terminais

• Um não terminal designado como símbolo inicial

Exemplo 1

list list + digitlist list - digitlist digit

digit 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo 1a

list list + digit | list - digit | digit

digit 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo 2

call id ( optparams )optparams params | params params , param | param

Derivações

• Uma gramática deriva strings a partir do seu símbolo inicial e repetidamente substituindo não-terminais pelo corpo de sua produção

• As sequencias de terminais produzidas desta forma formam a linguagem definida pela gramática

• Exemplos: 9, 9 - 5, 9 - 5 + 2

Parsing

• Problema de pegar uma string de terminais e verificar como derivá-la a partir do símbolo inicial da gramática; caso não seja possível, reportar erros de sintaxe.

• Processo de procurar uma parse-tree para uma dada sequencia de terminais.

Parse Trees

• Mostra graficamente como o símbolo inicial de uma gramática deriva uma string da linguagem.

• Para uma produção A XYZ

Z

A

X Y

Parse Trees

• A raiz é o símbolo inicial• Cada folha é um terminal ou • Cada nó interior é um não-terminal• Se A é um não-terminal e X1, X2,...,Xn são

labels de filhos deste nó, tem que haver uma produção A X1 X2 ... Xn

Exemplo

• 9 – 5 + 2

digit

digit

list

list

list

- + 9 5 2

digit

Ambiguidade

• Parse-tree gera uma string, mas uma string pode possuir várias parse-trees, se a gramática for ambígua.

• Solução: usar sempre gramáticas não-ambíguas, ou gramáticas ambíguas com informações adicionais sobre como resolver ambiguidades.

Ambiguidade - Exemplo

• string string + string | string - string | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo: Duas parse trees

• 9 – 5 + 2

string

string

string

string

string -

+

9 5

2string string

string

string

string -

+9

5 2

Associatividade de Operadores

• +, –, * e / são associativos à esquerda, na maioria das linguagens de programação:9 – 5 + 2 é equivalente a (9-5)+2

• Atribuição em C e exponenciação são associativos à direita: a = b = c2**3**4

Associatividade à direita

right letter = right | letterletter a | b | … | z

Precedência de operadores

• 9 + 5 * 2• * tem maior precedência do que +• Usaremos dois não-terminais para

representar os dois níveis de precedência em expressões e um outro para gerar as unidades básicas de expressões.

Precedência de operadores

• expr expr + term | expr – term | termterm term * factor | term / factor | factorfactor digit | ( expr )digit 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo

• stmt id = expression ; | if ( expression ) stmt | if ( expression ) stmt else stmt | while ( expression ) stmt | do stmt while ( expression ) ; | { stmt }stmts stmts stmt |

Tradução dirigida por sintaxe

• Tradução dirigida associa regras ou trechos de programa às produções de uma gramática.

Exemplo

expr expr1 + term

traduza expr1;traduza term;trate + ;

Conceito: Atributo

• Um valor associado a um construtor do programa.

• Exemplos:– Tipo em uma expressão;– Número de instruções geradas;– Localização da primeira instrução gerada por

um construtor;

Conceito: Esquema de tradução (dirigida pela sintaxe)

• Notação para associar trechos de programa a produções de uma gramática

• Os trechos de programa são executados quando a produção é usada durante a análise sintática

• O resultado da execução desses trechos de programa, na ordem criada pela análise sintática, produz a tradução desejada do programa fonte

Notação pós-fixada

• Se E é uma variável ou constante, sua notação pós-fixada é ela mesma;

• Se E é uma expressão da forma E1 op E2, então sua notação pós fixada é E1

’ E2’ op, onde E1

’ e E2

’ são as notações pós-fixadas de E1 e E2 respectivamente;

• Se E é uma expressão com parênteses ( E ), então sua notação pós-fixada é a mesma notação pós-fixada de E1.

Exemplo

• (9-5)+2 95-2+

• 9-(5+2) 952+-

• 9-(5+2)*3 952+-3*

Definição dirigida por sintaxe

• Definição dirigida por sintaxe associa a cada símbolo um conjunto de atributos;

• e a cada produção, regras semânticas para computar os valores dos atributos

(Gramática de atributos)

Definição dirigida por sintaxe

1 Dada uma string x, construa uma parse tree para ela;

2 Aplique as regras semânticas para avaliar os atributos em cada nó.

Exemplo – valores dos atributos nos nós de uma parse tree

9 - 2+5

expr.t = 95-2+

expr.t = 95- term.t = 2

expr.t = 9

term.t = 9

term.t = 5

Tipos de Atributos

• Atributos sintetizados: seus valores são obtidos a partir dos filhos de um determinado nó;Podem ser calculados através de uma travessia bottom-up;

• Atributos herdados: têm seus valores definidos a partir do próprio nó, de seus pais ou seus irmãos.

Exemplo – Definição dirigida por sintaxe

• Produção Regra semânticaexpr expr1 + term expr.t = expr1.t || term.t || ‘+’expr expr1 – term expr.t = expr1.t || term.t || ‘-’expr term expr.t = term.tterm 0 term.t = ‘0’term 1 term.t = ‘1’…term 9 term.t = ‘9’

Travessias

• Travessia em profundidade (depth-first)

• procedure visit (node N) { for (each child C of N, from left to right) { visit(C); } evaluate semantic rules at node N;}

Esquemas de tradução

• Gramática livre de contexto com fragmentos de programas (ações semânticas) embutidos no lado direito das produções.

• Semelhante à definição dirigida por sintaxe, mas com a ordem de avaliação das regras semânticas explicitada.

Exemplo 1

• rest + term {print (‘+’) } rest1

term

rest

+ {print(‘+’)} rest1

Exemplo 2

• expr expr + term { print (‘+’) } expr expr - term { print (‘-’) }expr termterm 0 { print (‘0’) }term 1 { print (‘1’) }…term 9 { print (‘9’) }

Exemplo 2

9

-2

+

5

expr

expr term

expr

term

term

Ações traduzindo 9=5+2 em 95-2+

9

-2

+

5

expr

expr term

expr

term

term{print(‘2’)}

{print(‘9’)}

{print(‘5’)}

{print(‘-’)}

{print(‘+’)}

Parsing

• Processo de determinar como uma string de terminais pode ser gerada por uma gramática

• Conceitualmente é a construção da parse tree

• Mas a parse tree pode não ser efetivamente construída durante a compilação.

Parsing

• Para gramáticas livres de contexto sempre é possível construir um parser com complexidade O(n3) para fazer o parsing de n tokens.

• Na prática, o parsing de linguagens de programação normalmente pode ser feito linearmente.

• Travessia linear da esquerda para a direita, olhando um token de cada vez.

Top-down ou bottom-up parsers

• Refere-se à ordem em que os nós da parse tree são criados.

• Top-down: mais fáceis de escrever “à mão”• Bottom-up: suportam uma classe maior de

gramáticas e de esquemas de tradução;são frequentemente usados/gerados pelas ferramentas de geração automática de parsers.

Exemplo

• stmt expr ; | if (expr ) stmt | for ( optexpr ; optexpr ; optexpr ) stmt | otheroptexpr expr |

Exemplo

;(

expr

optexpr

stmt

for otheroptexpr ;

expr

optexpr )

stmt

for ( ; expr ; expr ) other

Construindo um parser top-down

1. Para cada nó n, com um não-terminal A, selecione uma das produções de A e construa os filhos de n para os símbolos à direita da produção.

2. Encontre o próximo nó para o qual uma sub-árvore deve ser construída.

Construindo um parser top-down

• Para algumas gramáticas basta uma única travessia da esquerda para a direita da string de entrada.

• Token corrente é chamado de lookahead symbol.

• Exemplo: for ( ; expr ; expr ) other

Backtracking

• A escolha de uma produção pode exigir tentativa-e-erro, voltando para tentar novas alternativas possíveis.

• Predictive-parsing: parsing em que não ocorre backtracking.

Recursive descent parsing

• Método de análise sintática top-down em que um conjunto de procedimentos recursivos é usado para processar a entrada.

• Cada procedimento está associado a um símbolo não-terminal da gramática.

• Predictive parsing é um caso especial de recursive descent parsing em que o símbolo lookahead determina sem ambiguidades o procedimento a ser chamado para cada não-terminal.

Exemplo – predictive parsing

stmt for ( optexpr ; optexpr ; optexpr ) stmt

match (for); match (‘(‘); optexpr (); match (‘;‘);optexpr (); match (‘;‘);optexpr (); match (‘)‘); stmt ();

Exemplo – predictive parsing

void match (terminal t) { if (lookahead == t) lookahead = nextTerminal else report (“syntax error”);

}

Exemplo – predictive parsing (cont.)

void optexpr( ) { if (lookahead == expr) match (expr) ;

}

Exemplo – predictive parsing (cont.)void stmt( ) {

switch (lookahead) { case expr: match(expr); match(‘;’); break; case if: match(if); match(‘(‘); match(expr);match(‘)’); stmt(); break;

case for: match(for); match(‘(‘); optexpr(); match(‘;‘); optexpr(); match(‘;‘); optexpr(); match(‘)‘); stmt(); break; case other: match(other); break; default: report(“syntax error”);}

}

Predictive parsing - Problema

• Recursão à esquerda leva a loop em predictive parsers:

expr expr + term | term

A A |

Predictive parsing - Solução

• Reescrever produções tornando-as recursivas à direita:A A | reescrever paraA RR R |

• Exemplo: expressão “”

Sintaxe abstrata x Sintaxe concreta

• Sintaxe abstrata: ignora distinções superficiais de forma, irrelevantes para o processo de tradução.

digit

digit

list

list

list

- +

digit

5 29

5

2

+

-

9

Sintaxe abstrata x Sintaxe concreta (cont.)

• Na árvore sintática abstrata os nós são construções do programa, enquanto que na parse tree os nós são não-terminais.

• Idealmente as duas árvores devem ser próximas.

Reescrevendo a gramática de expressões

• expr expr + term {print (‘+’) } expr expr - term {print (‘-’) }expr termterm 0 {print (‘0’) }…term 9 {print (‘9’) }

Reescrevendo a gramática de expressões

A Aa | Ab | c

A cR

R aR | bR |

Reescrevendo a gramática de expressões (cont.)

A = expr a = + term {print(‘+’)}b = - term {print(‘-’)}c = term

Reescrevendo a gramática de expressões (cont.)

expr term rest

rest + term {print (‘+’) } rest | - term {print (‘-’) } rest

|

term 0 {print (‘0’) }…term 9 {print (‘9’) }

Tradutor em C

void expr () { term(); rest();

}

Tradutor em C

void term () { if (isdigit(lookahead)) { t = lookahead; match(lookahead); print(t);

} else report(“syntax error”);

}

Tradutor em C (cont.)

void rest() { if (lookahead == ‘+’) { match(‘+’); term(); print(‘+’); rest(); } else if (lookahead ==‘-’) { match(‘-’); term(); print(‘-’); rest(); } else { };

}

Otimizaçõesvoid rest ()

{ while (true) { if (lookahead == ‘+’) { match(‘+’); term(); print(‘+’); continue; } else if (lookahead ==‘-’) { match(‘-’); term(); print(‘-’); continue; } break;}

}

Usando um analisador léxico

• Facilita o tratamento de lexemas ao invés de caracteres:– Tratamento de espaços em branco– Tratamento de número maiores que 9

(constantes)– Reconhecimento de identificadores e palavras-

chave.

Estendendo para identificadore e números maiores que 9

expr expr + term {print (‘+’) } | expr – term {print (‘-’) } | termterm term * factor {print (‘*’) } | term / factor {print (‘/’) } | factorfactor ( expr ) | num {print (num.value) }

| id {print (id.lexeme) }

Class Token

int tag

class Token

int value

class Num

string lexeme

class Word

Código do analisador léxico• token scan() { char t;

while(true) { t = getchar(); if (t == ‘ ’ || t ==‘\t’) { } ; else if (t ==‘\n’) line++;

else if (isdigit(t)) { tokenval = t – ‘0’; t = getchar(); while (isdigit(t)) { { tokenval = tokenval * 10 + t – ‘0’; t = getchar(); } ungetc(t,stdin); return token<num,tokenval>; } else …

A tabela de símbolos

• Interface:– insert(s,t)– lookup(s)

• Palavras reservadas:insert(“div”,div)insert(“mod”, mod)

Geração de código intermediário

• Árvores: e.g. parse trees ou (abstract) syntax trees

• Representações lineares:e.g. three-address code

Construção da Syntax tree

• Classes para representar a hierarquia de construções da Linguagem:

Statements, IfStatements, WhileStatements; Expressions, BinaryExpressions, etc.

class AST

class Statement class Expression

Expr E;Stmt S;

class IfStmtIdentifier I;Expr E;

class AssignmentStmt

Expr E;Stmt;

class WhileStmt

class AST

class Statement class Expression

Int IL;

class IntLiteralOperator Op;Expr E1, E2;

class BinaryExprOperator Op;Expr E;

class UnaryExpr

Exemplo

stmt expr ; {stmt.n = new Eval(expr.n); } | if (expr ) stmt {stmt.n = new If(expr.n, stmt.n}

Fluxo de controle

• Desvios condicionais ou incondicionais• o destino pode ser especificado

– pelo operando da instrução– o operando pode especificar um desvio relativo– o destino pode ser especificado simbolicamente, através

de labels

Fluxo de controle - instruções

• ifFalse x goto L• ifTrue x goto L• goto L

Fluxo de controle – exemplo 1class IfStmt extends Stmt {

Expr E; Stmt S;

public If (Expr x, Stmt y) { E = x; S = y; after = newlabel(); }

public void gen() { Expr n = E.rvalue(); emit(“ifFalse ”+n.toString()+“goto ”+after); S.gen() emit(after +”:“); }

}

Código para calcular expr em x

ifFalse x goto after

after

Código para stmt1

ifFalse x goto after

If ( peek == ‘\n’ ) line = line + 1;

Lexical Analyser

Resumo

<if> <(> <id,”peek”> <eq> <const,’\n’> <)> <id,”line”> <assign> <id,”line”> <+> <num,1>

<;>

Syntax Directed Translator

1: t1 = (int) ‘\n’2: ifFalse peek == t1 goto 43: line = line + 14:

assign

if

eq

peek line

line

+

1

(int)

‘\n’

top related