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.

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 de v em G — a pesquisa é descendente, com a = inicial e b = final.
  • Se expande(G, v) segue contra a direção das arestas — devolve os pais de v em G — a pesquisa é ascendente, com a = final e b = inicial.
  • Se junta(x, y) devolve x + y (x seguido de y) — a pesquisa é em largura.
  • Se junta(x, y) devolve y + x (y seguido de x) — a pesquisa é em profundidade.

Resumidamente:

abexpandejunta(x, y)Pesquisa
filhosy + xDescendente Profundidade
filhosx + yDescendente Largura
paisy + xAscendente Profundidade
paisx + yAscendente 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)
Grafo Esquerdo Exemplo 1

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 EASGrafo Direito de EAS
Grafo Esquerdo EASGrafo Direito EAS

Portanto, para já, a análise sintática pode ser feita em oito tipos de pesquisa:

DireçãoEstratégiaGrafo
DescendenteLarguraGrafo da GIC
DescendenteLarguraÁrvore das Computações do AP
DescendenteProfundidadeGrafo da GIC
DescendenteProfundidadeÁrvore das Computações do AP
AscendenteLarguraGrafo da GIC
AscendenteLarguraÁrvore das Computações do AP
AscendenteProfundidadeGrafo da GIC
AscendenteProfundidadeÁ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:

  1. O candidato é o alvo ou não há candidatos. A pesquisa termina.
  2. O candidato é incompatível com o alvo. Passa-se ao próximo candidato.
  3. 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:

  1. O candidato é o alvo ou não há candidatos. A pesquisa termina.
  2. O candidato é incompatível com o alvo. Passa-se ao próximo candidato.
  3. 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:

SubpalavraProduçãoNova 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:

  1. O candidato é a produção inicial ou não há candidatos. A pesquisa termina.
  2. 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:

  1. O candidato é a produção inicial ou não há candidatos. A pesquisa termina.
  2. 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.