1 CIn / UFPE Parsing Tarciana Dias / Gustavo Carvalho [email protected]@cin.ufpe.br /...
-
Upload
rayssa-morado -
Category
Documents
-
view
215 -
download
0
Transcript of 1 CIn / UFPE Parsing Tarciana Dias / Gustavo Carvalho [email protected]@cin.ufpe.br /...
2
Roteiro• Processo de Compilação
• Conceitos Básicos
• Estratégias de Parsing
• Gramáticas LL / Recursive Descent Algorithm
• Referências
3
Processo de Compilação
Análise Léxica (Scanning)
Análise Sintática (Parsing)
Análise Semântica
Ger. Código Intermediário
Otimização Cód. Interm. Geração de Código Otimização do Cód. Gerado
Tokens
AST
AST decorada
Cód. Interm.
Cód. Interm. Otimizado Cód. Objeto Cód. Objeto Otimizado
Front-end
Back-end
Compilador(1 ou N passos)
Erro
Erro
Erro
4
Processo de Interpretação
Análise Léxica (Scanning)
Análise Sintática (Parsing)
Análise Semântica
Ger. Código Intermediário
Otimização Cód. Interm. Geração de Código Otimização do Cód. Gerado
Tokens
Front-end
Back-end
Interpretador
Erro
Erro
Erro
• Fetch => Analyze => Execute
• Saídas de forma imediata
• Não traduz programa fonte para código objeto (a priori)
• Ideal (melhor desempenho): instruções com formato simplificado (bytecode)
Execução
5
Roteiro• Processo de Compilação
• Conceitos Básicos
• Estratégias de Parsing
• Gramáticas LL / Recursive Descent Algorithm
• Referências
6
Conceitos Básicos• Gramáticas livres de contexto (GLC)
– Conjunto finito de símbolos não-terminais (V)• Uma classe particular de frases de uma linguagem• Ex.: Programa, Expressao, Valor
– Conjunto finito de símbolos terminais (Σ), disjunto de V• Símbolos atômicos• Ex.: ‘23’, ‘+’, ‘-‘, ‘and’
– Conjunto finito de regras de produção (R)– Símbolo inicial (um dos símbolos de V) (S)
• Ex.: Programa
7
Conceitos Básicos• Exemplo
– Terminais• +, -, not, length, and, or, ==, ++, 0, …, 9, a, …, z, A, …, Z
– Não-terminais• Programa, Expressao, Valor, ExpUnaria, ExpBinaria, ValorConcreto,
ValorInteiro, ValorBooleano, ValorString– Produções Programa ::= Expressao
Expressao ::= Valor | ExpUnaria | ExpBinariaValor ::= ValorConcretoValorConcreto ::= ValorInteiro | ValorBooleano | ValorStringExpUnaria ::= "-" Expressao | "not" Expressao | "length" ExpressaoExpBinaria ::= Expressao "+" Expressao
| Expressao "-" Expressao | Expressao "and" Expressao | Expressao "or" Expressao | Expressao "==" Expressao | Expressao "++" ExpressaoValorInteiro ::= [1-9] [0-9]*ValorBooleano ::= "true" | "false"ValorString ::= ([A-Za-z] | [0-9])*
8
Conceitos Básicos• Uma árvore sintática para uma gramática G
– Árvore com labels em que:• As folhas são símbolos terminais• Os nós são símbolos não-terminais
• Uma frase de G – Seqüência de terminais de uma árvore sintática (esquerda p/ direita)
• Exemplo: 2 + 3 (onde o todo é 2 + 3 + 5)• Uma sentença
– Frase cuja árvore começa a partir do símbolo inicial• Exemplo: 2 + 3 (onde o todo é 2 + 3)
• Linguagem gerada por G: todas as sentenças de G
9
Conceitos Básicos• Gramáticas ambíguas
– Podem gerar 2 árvores distintas para a mesma expressão– Exemplo:
• Suponha que usemos um único não-terminal string e que não façamos distinção entre dígitos e listas:
• Expressão: 9 – 5 + 2
string
string string
string string2
59-
+
string
string
string string
59
+
-
2
string
string → string + string | string – string | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
(9 – 5 ) + 2 9 – (5 + 2)
10
Conceitos Básicos• Expressões Regulares (REs)
– Notação conveniente para expressar um conjunto de strings de símbolos terminais.
• ‘|’ separa alternativas;• ‘*’ indica que o item anterior pode ser repetido 0 ou mais vezes;• ‘(’ e ‘)’ são parênteses agrupadores.
• Exemplos: – Mr | Ms gera {Mr, Ms}– M(r | s) gera {Mr, Ms}– ps*t gera {pt, pst, psst, pssst, ...}– ba(na)* gera {ba, bana, banana, bananana, ...}– M(r | s)* gera {M, Mr, Ms, Mrr, Mrs, Msr, Mss, Mrrr, ...}
• Assim RE são capazes de gerar linguagens simples, chamadas de linguagens regulares
11
Conceitos Básicos• No entanto, linguagens de programação completas são self-
embedding– Por exemplo, as expressões representadas por LE1 ou LE2 (vistas em sala de
aula), ex.: a * (b+c) / d contém outra subexpressão embedded, (b + c).– O comando if x > y then m := x else m := y contém o subcomando embedded,
‘m := x’
• Portanto, self-embedding nos permitem escrever expressões complexas, comandos, etc;
• Uma linguagem regular, ou seja, que não exibe self-embedding pode ser representada através de REs.
• Já uma linguagem que exiba self-embedding não pode ser gerada por REs. Para isso, nós escrevemos regras de produção recursivas usando ou BNF ou EBNF.
12
Conceitos Básicos• EBNF é uma combinação de BNF com REs.
• Uma produção EBNF é da forma N := X, onde N é um símbolo não-terminal e X é uma RE estendida, ou seja, uma RE construída a partir de ambos os símbolos terminais e não-terminais.
– Diferentemente de uma BNF, o lado direito de uma produção EBNF pode usar não somente ‘|’ mas também ‘*’ além de ‘(‘ e ‘)’.
– Diferentemente de uma RE ordinária , o lado direito pode conter símbolos não-terminais como também símbolos terminais.
• Logo , podemos escrever regras de produção recursivas, e uma EBNF é capaz de gerar uma linguagem com self-embedding.
13
Conceitos Básicos• Exemplo de gramática expressa em EBNF: Expression ::= primary-Expression (Operator primary-Expression)*primary-Expression ::= Identifier | (Expression)Identifier ::= a | b | c | d | eOperator ::= + | - | * | /
• Esta gramática gera expressões como:
• a + b• a – b – c• a + (b * c)• a * (b + c) / d• a – (b – (c – (d - e)))
14
Conceitos Básicos• Transformações de Gramática
– Fatoração à esquerda– Ex;: XY | XZ equivale à X (Y | Z)
single-Command ::= V-name := Expression | if Expression then single-Command | if Expression then single-Command else single-Command
single-Command ::= V-name := Expression | if Expression then single-Command ( ε | else single-Command)
15
Conceitos Básicos• Transformações de Gramática
– Eliminação de recursão à esquerda
N ::= X | NY, onde N é um símbolo não-terminal e X e Y são REs estendidas. Esta produção é recursiva à esquerda.
N ::= X(Y)*
Substituindo por uma regra EBNF equivalente
16
Conceitos Básicos• Transformações de Gramática Identifier ::= Letter
| Identifier Letter | Identifier Digit
Identifier ::= Letter | Identifier (Letter | Digit)
Identifier ::= Letter (Letter | Digit)*
Fatorando à
esquerda
Eliminando recursão
à esquerda
17
Conceitos Básicos
• A necessidade da eliminação de recursão à esquerda é melhor entendida depois que se vai usar a abordagem top-down;
18
Roteiro• Processo de Compilação
• Conceitos Básicos
• Estratégias de Parsing
• Gramáticas LL / Recursive Descent Algorithm
• Referências
19
Estratégias de Parsing• Objetivo
– Reconhecimento de uma string de entrada (seqüência de tokens) e decisão se esta é ou não uma sentença de G
– Determinação de sua estrutura de frases (pode ser representada por uma árvore sintática)
– Gramática não ambígua: cada sentença tem exatamente uma syntax tree• Top-Down
– Examina os símbolos terminais da esquerda para a direita– Forma a ST (syntax tree) de cima para baixo– Parsing ok: string de entrada totalmente conectada à ST
• L(eft-to-right) L(eft-most-derivation) => LL• Bottom-Up
– Examina os símbolos terminais da esquerda para a direita– Forma a ST (syntax tree) de baixo (nós terminais) para cima(nó raiz)– Parsing ok: string de entrada reduzida a uma S-tree
• S(imple) L(eft-to-right) R(ight-most-derivation) => SLR• L(eft-to-right) R(ight-most-derivation) => LR• L(ook) A(head) L(eft-to-right) R(ight-most-derivation) => LALR
20
Estratégia Bottom-Up• Exemplo: Sentence ::= Subject Verb Object Subject ::= I | a Noun | the Noun Object ::= me | a Noun | the Noun Noun ::= cat | mat | rat Verb ::= like | is | see | sees
the cat sees a rat .
Noun
Subject
Verb Noun
Object
SentenceAqui ele
não poderia ter escolhido
um Subject?
21
Estratégia Top-Down• Exemplo: Sentence ::= Subject Verb Object Subject ::= I | a Noun | the Noun Object ::= me | a Noun | the Noun Noun ::= cat | mat | rat Verb ::= like | is | see | sees
the cat sees a rat .
Noun
Subject
Verb Noun
Object
Sentence
22
Estratégias de Parsing• Qual estratégia de parsing devemos usar?
– Isso vai depender do tipo de gramática !
– Sempre é necessário escolher qual regra de produção aplicar• Isto é feito de acordo com o algoritmo de Parsing
– Recursive Descent é um algoritmo de parsing top-down
– Consiste de um grupo de métodos parseN, um para cada símbolo não-terminal N de G. Estes métodos cooperam para fazer o parse das sentenças completas
parseSubject parseVerb parseObjectparseSentence
the cat sees a rat .
parseNoun parseNoun
23
Roteiro• Processo de Compilação
• Conceitos Básicos
• Estratégias de Parsing
• Gramáticas LL / Recursive Descent Algorithm
• Referências
24
Gramáticas LL(1)
• Deve-se expressar a gramática em EBNF, com uma regra de produção simples para cada símbolo não-terminal, e realizar as transformações de gramática necessárias, por exemplo, sempre eliminar recursão à esquerda e fatorizar à esquerda sempre que possível
• Gramáticas LL(1):– Se a gramática contém X | Y, starters[[ X ]] e starters[[ Y ]] devem ser disjuntos– Se a gramática contém X* , starters[[ X ]] deve ser disjunto do conjunto de tokens que
podem seguir X*
• Na prática quase toda gramática de uma linguagem de programação pode ser transformada em LL(1), sem mudar a linguagem que a gramática gera
• Recursive-descent parsing é somente adequado para gramáticas LL(1)– Em geral, o projetista da linguagem projeta a sua sintaxe para ser adequada à um parsing
recursive-descent.
25
Gramáticas não-LL(1)
• Exemplo de uma Gramáticas não LL(1):
Program ::= single-CommandCommand ::= single-Command (; single-Command)*V-name ::= Identifiersingle-Command ::= V-name := Expression | Identifier ( Expression ) | if Expression then single-Command | if Expression then single-Command else single-Command …Expression ::= primary-Expression (Operator primary-Expression)*primary-Expression ::= Integer-Literal | Identifier …
26
Gramáticas não-LL(1)• Desenvolvimento do método parseSingleCommand:
private void parseSingleCommand(){switch(currentToken.kind){
case Token.IDENTIFIER: { parseVname();
accept(Token.BECOMES); parseExpression(); } break;
case Token.IDENTIFIER: { parseIdentifier ();
accept(Token.LPAREN); parseExpression(); accept(Token.RPAREN); } break;
}}
Uma simples fatoração à
esquerda resolve o problema!
…V-name ::= Identifiersingle-Command ::= V-name := Expression | Identifier ( Expression ) | …
…single-Command ::= Identifier (:= Expression | ( Expression ) )
27
Gramáticas não-LL(1)• Exemplo de uma Gramáticas não LL(1):
• Aqui, starters[[ ;Declaration ]] = {;} e o conjunto de terminais que seguem (; Declaration)* neste contexto também é {;}
– Como não são disjuntos, então a gramática não é LL(1)
…Block::= begin Declaration (; Declaration)* ; Command endDeclaration ::= integer Identifier (, Identifier)*…
private void parseBlock(){ accept(Token.BEGIN){ parseDeclaration(); while (currentToken.kind == Token.SEMICOLON) acceptIt(); parseDeclaration(); } accept(Token.SEMICOLON); parseCommand(); accept(Token.END);}
…Block::= begin Declaration ; (Declaration;)* Command end…
Como resolver?
28
Recursive Descent Parser• Recursive Descent Parser
– Algoritmo de parsing para gramáticas LL– Visão geral
• Para cada produção N, crie um método parseN• Crie uma classe parser com um atributo currentToken
– E os métodos parseN– E os métodos auxiliares: accept e acceptIt– E um método público parse que chama parseS
• O código de cada método parseN depende da produção N• A árvore é dada implicitamente pela chamada dos métodos
– Pode ser criada explicitamente
29
Recursive Descent ParserPrograma ::= ExpressaoExpressao ::= ExpCondicionalOrExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)?ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)?ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)?ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)?ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)?ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcretoValorConcreto ::= ValorInteiro | ValorBooleano | ValorString
accept(int type) { if ( currentToken.getType() == type ) { currentToken = scanner.getNextToken(); } else { // ERRO }}
acceptIt() { currentToken = scanner.getNextToken();}
Métodos auxiliares
30
Recursive Descent Parser
parsePrograma() parseExpressao();}
parseExpressao() { parseExpCondicionalOr();}
parseExpCondicionalOr() { parseExpCondicionalAnd(); while ( currentToken.getType() == Token.OR ) { acceptIt(); parseExpCondicionalAnd(); }}
parseExpIgualdade() { parseExpAritmetica(); if ( currentToken.getType() == Token.EQUAL ) { acceptIt(); parseExpAritmetica(); }}
Programa ::= ExpressaoExpressao ::= ExpCondicionalOrExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)?ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)?ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)?ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)?ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)?ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcretoValorConcreto ::= ValorInteiro | ValorBooleano | ValorString
parse () { parsePrograma(); if ( currentToken.getType() != Token.EOT ) { // ERRO }}
31
Recursive Descent Parser
parseExpUnaria() { if ( currentToken.getType() == Token.MINUS ) { acceptIt(); parseExpressao(); } else if ( currentToken.getType() == Token.NOT ) { acceptIt(); parseExpressao(); } else if ( currentToken.getType() == Token.LENGTH ) { acceptIt(); parseExpressao(); } else { parseValorConcreto(); }}
Programa ::= ExpressaoExpressao ::= ExpCondicionalOrExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)?ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)?ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)?ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)?ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)?ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcretoValorConcreto ::= ValorInteiro | ValorBooleano | ValorString
parseValorConcreto() { if ( currentToken.getType() == Token.INT ) { acceptIt(); } else if ( currentToken.getType() == Token.BOOLEAN ) { acceptIt(); } else { accept(Token.STRING); }}
32
Roteiro• Processo de Compilação
• Conceitos Básicos
• Estratégias de Parsing
• Gramáticas LL / Recursive Descent Algorithm
• Referências
33
Referências• WATT, D.; BROWN, D.
Programming Language Processors in Java.– Capítulo 4
• Foco maior na abordagem LL
• AHO, A.; LAM, M.; SETHI, R.; ULLMAN, J.Compilers: Principles, Techniques & Tools.– Capítulo 4
• Foco maior na abordagem LR