Um Tradutor Dirigido por Sintaxe Simples Apresenta de

  • Slides: 81
Download presentation
Um Tradutor Dirigido por Sintaxe Simples • Apresenta de forma resumida boa parte do

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 posfixas 9– 5+2 95– 2+

Um compilador simples • Objetivo: traduzir expressões infixas em posfixas 9– 5+2 95– 2+ • Aplicabilidade: computação baseda em pilha (por exemplo, gerador de código para. NET e JVM)

Front-end de um compilador Programa fonte Analisador léxico tokens Parser árvore sintática Ger. de

Front-end de um compilador Programa fonte Analisador léxico tokens Parser árvore sintática Ger. de código intermediário Three-address code Tabela de Símbolos

Definição de uma linguagem • Linguagem = sintaxe + semântica • Sintaxe = forma

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

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

Exemplo: if-else if ( expression ) statement else statement stmt g if ( expr

Exemplo: if-else if ( expression ) statement else statement stmt g if ( expr ) stmt else stmt

Gramática Livre de Contexto • Um conjunto de tokens, símbolos terminais • Um conjunto

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 g list + digit list g list - digit list g

Exemplo 1 list g list + digit list g list - digit list g digit g 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo 1 a list g list + digit | list - digit | digit

Exemplo 1 a list g list + digit | list - digit | digit g 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo 2 call g id ( optparams ) optparams g params | params g

Exemplo 2 call g id ( optparams ) optparams g params | params g params , param | param

Derivações • Uma gramática deriva strings a partir do seu símbolo inicial e repetidamente

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 formam a linguagem definida pela gramática • Exemplos: 9, 9 - 5 + 2

Parsing • Problema de pegar uma string de terminais e verificar como derivá-la a

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

Parse Trees • Mostra graficamente como o símbolo inicial de uma gramática deriva uma string da linguagem. • Para uma produção A g XYZ A X Y Z

Parse Trees • • A raiz é o símbolo inicial Cada folha é um

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 X 1, X 2, . . . , Xn são labels de filhos deste nó, tem que haver uma produção A g X 1 X 2. . . Xn

Exemplo • 9 – 5 + 2 list digit 9 - 5 + 2

Exemplo • 9 – 5 + 2 list digit 9 - 5 + 2

Ambiguidade • Parse-tree gera uma string, mas uma string pode possuir várias parse-trees, se

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ãoambíguas, ou gramáticas ambíguas com informações adicionais sobre como resolver ambiguidades.

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

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

Exemplo: Duas parse trees • 9 – 5 + 2 string 9 - string

Exemplo: Duas parse trees • 9 – 5 + 2 string 9 - string + string 5 2 string 9 - string 5 + string 2

Associatividade de Operadores • +, –, * e / são associativos à esquerda, na

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=c 2**3**4

Associatividade à direita right g letter = right | letter g a | b

Associatividade à direita right g letter = right | letter g a | b | … | z

Precedência de operadores • 9+5*2 • * tem maior precedência do que + •

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 g expr + term | expr – term |

Precedência de operadores • expr g expr + term | expr – term | term g term * factor | term / factor | factor g digit | ( expr ) digit g 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Exemplo • stmt g id = expression ; | if ( expression ) stmt

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

Tradução dirigida por sintaxe • Tradução dirigida associa regras ou trechos de programa às

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

Exemplo expr g expr 1 + term traduza expr 1; traduza term; trate +

Exemplo expr g expr 1 + term traduza expr 1; traduza term; trate + ;

Conceito: Atributo • Um valor associado a um construtor do programa. • Exemplos: –

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

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 é

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 E 1 op E 2, então sua notação pós fixada é E 1’ E 2’ op, onde E 1’ e E 2’ são as notações pós-fixadas de E 1 e E 2 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 E 1.

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

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

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

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 nós de uma parse tree expr. t = 95

Exemplo – valores dos atributos nós de uma parse tree expr. t = 95 -2+ expr. t = 95 expr. t = 9 term. t = 2 term. t = 5 term. t = 9 9 - 5 + 2

Tipos de Atributos • Atributos sintetizados: seus valores são obtidos a partir dos filhos

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 expr g expr 1 + term

Exemplo – Definição dirigida por sintaxe • Produção expr g expr 1 + term expr g expr 1 – term expr g term g 0 term g 1 … term g 9 Regra semântica expr. t = expr 1. t || term. t || ‘+’ expr. t = expr 1. t || term. t || ‘-’ expr. t = term. t = ‘ 0’ term. t = ‘ 1’ term. t = ‘ 9’

Travessias • Travessia em profundidade (depth-first) • procedure visit (node N) { for (each

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)

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 g + term {print (‘+’) } rest 1 rest +

Exemplo 1 • rest g + term {print (‘+’) } rest 1 rest + term {print(‘+’)} rest 1

Exemplo 2 • expr g expr + term { print (‘+’) } expr g

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

Exemplo 2 expr - + term 2 term 5 9

Exemplo 2 expr - + term 2 term 5 9

Ações traduzindo 9=5+2 em 95 -2+ expr + term {print(‘-’)} - 2 term 5

Ações traduzindo 9=5+2 em 95 -2+ expr + term {print(‘-’)} - 2 term 5 9 {print(‘+’)} {print(‘ 9’)} {print(‘ 5’)} {print(‘ 2’)}

Parsing • Processo de determinar como uma string de terminais pode ser gerada por

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 complexidade

Parsing • Para gramáticas livres de contexto sempre é possível construir um parser complexidade O(n 3) 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

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 g expr ; | if (expr ) stmt | for (

Exemplo • stmt g expr ; | if (expr ) stmt | for ( optexpr ; optexpr ) stmt | other optexpr g expr |

Exemplo stmt for ( optexpr ; expr optexpr for ( ; expr ) other

Exemplo stmt for ( optexpr ; expr optexpr for ( ; expr ) other stmt

Construindo um parser top-down 1. Para cada nó n, com um não-terminal A, selecione

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

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 ) other

Backtracking • • A escolha de uma produção pode exigir tentativa-e-erro, voltando para tentar

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

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 g for ( optexpr ; optexpr ) stmt match

Exemplo – predictive parsing stmt g for ( optexpr ; optexpr ) stmt match (for); match (‘(‘); optexpr (); match (‘; ‘); optexpr (); match (‘)‘); stmt ();

Exemplo – predictive parsing void match (terminal t) { if (lookahead == t) lookahead

Exemplo – predictive parsing void match (terminal t) { if (lookahead == t) lookahead = next. Terminal else report (“syntax error”); }

Exemplo – predictive parsing (cont. ) void optexpr( ) { if (lookahead == expr)

Exemplo – predictive parsing (cont. ) void optexpr( ) { if (lookahead == expr) match (expr) ; }

Exemplo – predictive parsing (cont. ) void stmt( ) { switch (lookahead) { case

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(‘)‘); stmt(); break; case other: match(other); break; default: report(“syntax error”); } }

Predictive parsing - Problema • Recursão à esquerda leva a loop em predictive parsers:

Predictive parsing - Problema • Recursão à esquerda leva a loop em predictive parsers: expr g expr + term | term A g Aa |

Predictive parsing - Solução • Reescrever produções tornando-as recursivas à direita: A g Aa

Predictive parsing - Solução • Reescrever produções tornando-as recursivas à direita: A g Aa | b reescrever para A g b. R R g a. R | • Exemplo: expressão “baaaa”

Sintaxe abstrata x Sintaxe concreta • Sintaxe abstrata: ignora distinções superficiais de forma, irrelevantes

Sintaxe abstrata x Sintaxe concreta • Sintaxe abstrata: ignora distinções superficiais de forma, irrelevantes para o processo de tradução. list + digit 9 - - digit 5 + 2 2 5

Sintaxe abstrata x Sintaxe concreta (cont. ) • Na árvore sintática abstrata os nós

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 g expr + term expr g expr

Reescrevendo a gramática de expressões • expr g expr + term expr g expr - term expr g term g 0 … term g 9 {print (‘+’) } {print (‘-’) } {print (‘ 0’) } {print (‘ 9’) }

Reescrevendo a gramática de expressões A g Aa | Ab | c A g

Reescrevendo a gramática de expressões A g Aa | Ab | c A g c. R R g a. R | b. R |

Reescrevendo a gramática de expressões (cont. ) A = expr a = + term

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 g term rest g + term

Reescrevendo a gramática de expressões (cont. ) expr g term rest g + term {print (‘+’) } rest | - term {print (‘-’) } rest | term g 0 … term g 9 {print (‘ 0’) } {print (‘ 9’) }

Tradutor em C void expr () { term(); rest(); }

Tradutor em C void expr () { term(); rest(); }

Tradutor em C void term () { if (isdigit(lookahead)) { t = lookahead; match(lookahead);

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(‘+’);

Tradutor em C (cont. ) void rest() { if (lookahead == ‘+’) { match(‘+’); term(); print(‘+’); rest(); } else if (lookahead ==‘-’) { match(‘-’); term(); print(‘-’); rest(); } else { }; }

Otimizações void rest () { while (true) { if (lookahead == ‘+’) { match(‘+’);

Otimizações void 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:

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 palavraschave.

Estendendo para identificadore e números maiores que 9 expr g expr + term {print

Estendendo para identificadore e números maiores que 9 expr g expr + term {print (‘+’) } | expr – term {print (‘-’) } | term g term * factor {print (‘*’) } | term / factor {print (‘/’) } | factor g ( expr ) | num {print (num. value) } | id {print (id. lexeme) }

Class Token class Token int tag class Num class Word int value string lexeme

Class Token class Token int tag class Num class Word int value string lexeme

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

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:

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

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:

Construção da Syntax tree • Classes para representar a hierarquia de construções da Linguagem: Statements, If. Statements, While. Statements; Expressions, Binary. Expressions, etc.

class AST class Statement class If. Stmt Expr E; Stmt S; class Assignment. Stmt

class AST class Statement class If. Stmt Expr E; Stmt S; class Assignment. Stmt Identifier I; Expr E; class Expression class While. Stmt Expr E; Stmt;

class AST class Statement class Int. Literal Int IL; class Binary. Expr Operator Op;

class AST class Statement class Int. Literal Int IL; class Binary. Expr Operator Op; Expr E 1, E 2; class Expression class Unary. Expr Operator Op; Expr E;

Exemplo stmt g expr ; {stmt. n = new Eval(expr. n); } | if

Exemplo stmt g 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

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 • if. False x goto L • if. True

Fluxo de controle - instruções • if. False x goto L • if. True x goto L • goto L

Fluxo de controle – exemplo 1 class If. Stmt extends Stmt { Expr E;

Fluxo de controle – exemplo 1 class If. Stmt 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(“if. False ”+n. to. String()+“goto ”+after); S. gen() emit(after +”: “); } }

if. False x goto after Código para calcular expr em x if. False x

if. False x goto after Código para calcular expr em x if. False x goto after Código para stmt 1 after

Resumo If ( peek == ‘n’ ) line = line + 1; Lexical Analyser

Resumo If ( peek == ‘n’ ) line = line + 1; Lexical Analyser <if> <(> <id, ”peek”> <eq> <const, ’n’> <)> <id, ”line”> <assign> <id, ”line”> <+> <num, 1> <; > Syntax Directed Translator 1: t 1 = (int) ‘n’ 2: if. False peek == t 1 goto 4 3: line = line + 1 4: if eq peek assign (int) ‘n’ line + 1