Análise Sintática
Pesquisar o Grafo de uma GIC ou uma Árvore das Computações de um AP de forma a encontrar a derivação de uma palavra e construir uma árvore de sintaxe abstrata.
- Análise Sintática
- Introdução
- Conteúdo
- Conclusão
Introdução
A eficiência computacional da análise sintática tem um problema, manifesto no não determinismo dos autómatos de pilha (que, ao contrário dos AFND, deles não se conhece nenhuma simulação determinista, geral e eficiente) e, equivalentemente, nas múltiplas produções das gramáticas independentes do contexto. Ambos estes casos têm representações gráficas: o Grafo da Gramática nas GIC e as Árvores das Computações nos AP.
Nesta situação é necessário fazerem-se pesquisas, abrangendo (no pior cenário) todas as possibilidades. Este é um processo inerentemente (demasiado) demorado pelo que importa encontrar formas de o melhorar.
Conteúdo
Do Problema Principal de ALP — Dada uma linguagem e uma palavra , determinar se . resta resolver a questão da eficiência computacional, já que a adequação ficou resolvida com as GIC e a computação com os AP.
Intuitivamente, para determinar se usando um AP, , é necessário explorar a árvore das computações de com entrada . Especificamente:
O problema de determinar se pode ser resolvido por pesquisa na árvore das computações do AP .
Além disso, como os AP e as GIG são equivalentes, esta observação também sugere que
As derivações de uma GIC podem ser representadas por um grafo orientado.
Mais abaixo vai ser definida rigorosamente a representação das derivações da GIC por um grafo e como é que o problema pode ser resolvido por pesquisa quer no grafo (das derivações) da GIC quer no grafo (das computações) do AP.
Entretanto, interessa fazer uma breve revisão sobre pesquisa em grafos orientados. O problema pode ser definido como:
Pesquisa em Grafos Orientados. Dado um grafo orientado, descobrir se existe algum caminho desde um vértice inicial, , até um vértice final, .
A resolução geral deste problema segue o seguinte código:
def pesquisa(grafo, a, b):
candidatos = [ a ]
visitados = []
while len(candidatos) > 0:
candidato = candidatos.pop(0)
visitados.append(candidato)
if candidato == b:
return True
else:
proximos = expande(grafo, candidato)
proximos_novos = remove(proximos, visitados)
candidatos = junta(candidatos, proximos_novos)
return False
N.B. Este código é muito geral e, portanto, não otimizado. Melhorias significativas podem incluir, por exemplo, um resuldado mais descritivo, com o caminho de a
até b
.
Há quatro variantes da resolução geral deste problema, que correspondem às combinações da seguintes opções para expande
e para junta
:
- Se
expande(G, v)
segue a direção das arestas — devolve os filhos dev
emG
— a pesquisa é descendente, coma = inicial
eb = final
. - Se
expande(G, v)
segue contra a direção das arestas — devolve os pais dev
emG
— a pesquisa é ascendente, coma = final
eb = inicial
. - Se
junta(x, y)
devolvex + y
(x
seguido dey
) — a pesquisa é em largura. - Se
junta(x, y)
devolvey + x
(y
seguido dex
) — a pesquisa é em profundidade.
Resumidamente:
a | b | expande | junta(x, y) | Pesquisa |
---|---|---|---|---|
filhos | y + x | Descendente Profundidade | ||
filhos | x + y | Descendente Largura | ||
pais | y + x | Ascendente Profundidade | ||
pais | x + y | Ascendente Largura |
A função remove
exclui dos candidatos os vértices já visitados e não depende do tipo de pesquisa.
Grafo de uma Gramática
Todas as possíveis derivações de uma gramática podem ser organizadas num grafo orientado, que começa no símbolo inicial e "percorre" sucessivamente as produções aplicadas durante as derivações. Neste grafo "caminhos" diferentes representam derivações diferentes. Os vértices "são" as palavras intermédias e as arestas as produções aplicadas:
Grafo Esquerdo (resp. Direito) de uma GIC. Seja uma GIC. O grafo esquerdo de (resp. direito) é o digrafo com:
- Vértices: as palavras do conjunto (resp. ).
- Arestas: os elementos de (resp. ).
Por exemplo, para a GIC parte do grafo esquerdo é
Grafo Esquerdo de (parcial) |
---|
O grafo (esquerdo/direito) de uma GIC representa todas as possíveis derivações (esquerdas/direitas) dessa GIC. Este grafo não tem de ser uma árvore porque no grafo de uma gramática ambígua há vários "caminhos" para o mesmo vértice.
Exemplo/Exercício: Desenhe o grafo de .
As árvores e as gramáticas não ambíguas estão relacionadas:
O grafo esquerdo (resp. direito) de uma GIC não ambígua é uma árvore. Recíprocamente, se o grafo esquerdo (resp. direito) de uma GIC for uma árvore, então a GIC é não ambígua.
O grafo da GIC não deve ser confundido com as árvores de derivação, que ilustram uma possível derivação de uma palavra.
Qualquer palavra de terminais gerada pela GIC é um vértice sem filhos. Isto é, se, e só se, é um vértice sem filhos no grafo de .
Isto é a reformulação do Problema Principal de ALP como um problema de pesquisa em grafos: Dada uma GIC e uma palavra , existe algum caminho no grafo (esquerdo) de ? Nesse caso ; caso contrário .
Exemplos com Expressões Algébricas Simplificadas
Para ilustrar melhor os grafos esquerdos e direitos, assim como os algoritmos de pesquisa, considere-se a seguinte gramática, para expressões algébricas simplificadas (EAS):
Os inícios dos grafos (esquerdo e direito) são
Grafo Esquerdo de EAS | Grafo Direito de EAS |
---|---|
Portanto, para já, a análise sintática pode ser feita em oito tipos de pesquisa:
Direção | Estratégia | Grafo |
---|---|---|
Descendente | Largura | Grafo da GIC |
Descendente | Largura | Árvore das Computações do AP |
Descendente | Profundidade | Grafo da GIC |
Descendente | Profundidade | Árvore das Computações do AP |
Ascendente | Largura | Grafo da GIC |
Ascendente | Largura | Árvore das Computações do AP |
Ascendente | Profundidade | Grafo da GIC |
Ascendente | Profundidade | Árvore das Computações do AP |
Análise Sintática Descendente em Largura no Grafo da GIC
Intuitivamente, mantém-se uma lista de candidatos, iniciada com e aplicam-se sucessivamente produções, sempre ao primeiro elemento dessa lista, o "candidato". Há três hipóteses:
- O candidato é o alvo ou não há candidatos. A pesquisa termina.
- O candidato é incompatível com o alvo. Passa-se ao próximo candidato.
- O candidato é compatível com o alvo, mas diferente. Os filhos do candidato atual são acrescentados ao fim da lista.
Compatível significa que o prefixo terminal do candidato é um prefixo do alvo.
Por exemplo, nas EAS, a pesquisa DL de é:
Análise Sintática Descendente em Profundidade no Grafo da GIC
Intuitivamente, mantém-se uma lista de candidatos, iniciada com e aplicam-se sucessivamente produções, sempre ao primeiro elemento dessa lista, o "candidato". Há três hipóteses:
- O candidato é o alvo ou não há candidatos. A pesquisa termina.
- O candidato é incompatível com o alvo. Passa-se ao próximo candidato.
- O candidato é compatível com o alvo, mas diferente. Os filhos do candidato atual são acrescentados ao início da lista.
Por exemplo, nas EAS, a pesquisa DP de é:
Análise Sintática Ascendente e as Computações do AP
As pesquisas anteriores percorrem o grafo da gramática, gerado pelas suas produções. Por outro lado, os autómatos de pilha, sendo equivalentes às GIC, também podem ser usados para a análise sintática.
No caso dos AP o não determinismo ocorre sempre que uma configuração tem dois ou mais sucessores. Por outro lado a própria palavra proporciona informação que pode ser usada para guiar a pesquisa.
As computações nos AP "consomem" a palavra na entrada e este processo é semelhante às pesquisas ascendentes, que começam no "alvo" e "andam para trás" até chegarem a um vértice "inicial".
O processo de pesquisa nas computações é essencialmente descrito pelas operações de transferência e de redução de um certo AP derivado da GIC.
N.B. que este AP não foi ainda formalmente definido.
Intuitivamente, a palavra da entrada é passada para a pilha, transferindo um símbolo de cada vez. Também a palavra que está na pilha pode ser reduzida, isto é, "desfeito" o resultado de aplicar uma produção. No fim, a entrada deve ficar vazia e a pilha só com o símbolo inicial.
Redução é o inverso da aplicação de uma produção. Por exemplo, se for uma produção então uma possível redução da palavra será porque .
Por exemplo, escolhendo convenientemente as operações "transferência", , da entrada para a pilha e "redução", , duma subpalavra na pilha, uma computação possível de é
Um resultado interessante deste método é que as reduções aplicadas mostram uma derivação direita da palavra, lida "de baixo para cima" na coluna "Produção":
Este esquema não só determina se a palavra é gerada pela gramática como, nesse caso, encontra uma derivação da palavra.
No entanto, tem algumas fontes de não determinismo:
- Quando fazer uma redução ou uma transferência?
- Numa redução podem ser escolhidos várias subpalavras da pilha e várias produções da gramática.
Por exemplo, dada a GIC como reduzir na pilha? Pode escolher-se:
Subpalavra | Produção | Nova Pilha |
---|---|---|
Análise Sintática Ascendente em Largura
Intuitivamente, mantém-se uma lista de candidatos, iniciada com e aplicam-se todas as reduções possíveis, sempre ao primeiro elemento dessa lista, o "candidato". Há duas hipóteses:
- O candidato é a produção inicial ou não há candidatos. A pesquisa termina.
- O candidato admite reduções. Os pais desse candidato são acrescentados ao fim da lista.
Por exemplo, nas EAS, a pesquisa AL de é:
Análise Sintática Ascendente em Profundidade
Intuitivamente, mantém-se uma lista de candidatos, iniciada com e aplicam-se todas as reduções possíveis, sempre ao primeiro elemento dessa lista, o "candidato". Há duas hipóteses:
- O candidato é a produção inicial ou não há candidatos. A pesquisa termina.
- O candidato admite reduções. Os pais do candidato atual são acrescentados ao início da lista.
Por exemplo, nas EAS, a pesquisa AP de é:
Conclusão
Depois de resolvidas as questões da adequação e da computação, está-se a tratar da eficiência no Problema Principal de ALP/Análise Sintática.
A análise sintática é representada como um problema de pesquisa em grafos orientados que, em geral, pode ser resolvido por quatro estratégias diferentes:
(ascendente ou descendente) x (largura ou profundidade)
. Por outro lado a pesquisa tanto pode ser feita no grafo da gramática como na árvore das computações do autómato de pilha.
Nenhuma destas estratégias gerais é eficiente. Para melhorar este problema e guiar as pesquisas na análise sintática colocam-se duas possibilidades: ou se condiciona a gramática, de forma reduzir as produções aplicáveis em cada passo ou, então, consultam-se os símbolos por processar para guiar as transições dos autómatos de pilha.