Formas Normais

Gramáticas Adaptadas para Análise Sintática por Pesquisa Geral.

Introdução

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). Nenhuma destas estratégias gerais é eficiente.

Nesta secção é apresentado um processo (a normalização de uma gramática) que, numa sequência de passos, transforma numa gramática noutra, equivalente, e adaptada (tanto quanto possível) aos algoritmos de pesquisa: uma forma normal.

Conteúdo

O bom funcionamento (mínimo número de passos) da pesquisa no grafo de uma GIC depende da própria gramática.

Problemas como:

  • Pesquisas infinitas quando a palavra não é derivada pela gramática.
  • Exploração repetida de ramos quando a gramática é ambígua.

reduzem significativamente o desempenho da pesquisa.

Em certos caso é possível transformar a gramática dada noutra equivalente mas que "favorece" a pesquisa, reduzindo o número de ramificações, desambiguando, e/ou detetando "cedo" se a palavra não é gerada pela gramática.

Esta secção descreve um processo algorítmico de transformação de gramáticas. No fim a gramática que se obtém proporciona pesquisas gerais mais eficientes.

Todo este processo vai ser ilustrado com um exemplo de uma GIC (especialmente desenhada) para ilustrar a aplicação de cada passo:

1. Símbolo Inicial Não Recursivo

A recursividade é a "vantagem" das GIC sobre as ER, mas é também, uma fonte de dificuldades. Embora não seja desejável, nem sequer possível, remover completamente a recursividade das produções de uma GIC esta pode ser, em certos casos, controlada.

Símbolo Inicial Não Recursivo. Seja uma GIC. Existe uma GIC equivalente a e onde o símbolo inicial não é recursivo.

Porque:

  • Se o símbolo inicial de não for recursivo então .
  • Se o símbolo inicial de for recursivo então, fazendo uma variável nova:

Exemplo. Em o símbolo inicial é recursivo porque é uma produção de . Portanto esta gramática é transformada em

Em o símbolo inicial já não é recursivo. Além disso, .

2. Eliminação de Produções Vazias

Antes de tratar do segundo passo na normalização de uma gramática é necessário definir alguns conceitos e técnicas.


Quase todas as produções "aumentam" o tamanho da palavra. Por exemplo faz com que passe para , isto é de comprimento três para quatro. E esta é uma propriedade desejável pois permite cortar "filhos demasiado grandes" durante a pesquisa.

Há duas exceções de nota: as produções vazias e as produções unitárias . Formalmente:

Produções Vazias. Produções Unitárias. Gramática Contraível. Seja uma GIC.

  • Uma produção vazia tem a forma .
  • Uma produção unitária tem a forma .
  • O conjunto dos geradores da palavra vazia é
  • Numa gramática não contraível não existem geradores da palavra vazia. Isto é .
  • Numa gramática essencialmente não contraível o único gerador de vazio é o símbolo inicial. Isto é .

O interesse das gramáticas essencialmente não contraíveis é que:

Numa gramática essencialmente não contraível os passos intermédios de uma derivação só podem diminuir de tamanho por aplicação de .

Não é possível, nem desejável, descartar completamente os geradores de vazio. Por exemplo, se então em qualquer gramática equivalente a tem de existir pelo menos uma produção vazia. Neste caso, o melhor que se pode fazer é "concentrar" todas as produções vazias em .


A transformação de uma gramática noutra equivalente envolve a introdução de novas produções e a substituição de uma produção por outras, de forma a manter a linguagem gerada.

Introdução de Produções. Substituição de Produções. Seja uma CIG.

  • Se então é equivalente a .
  • Se e são todas as produções de então a gramática em que é equivalente a .

Intuitivamente, estas duas regras permitem "saltar" passos intermédios numa derivação.


O primeiro passo para retirar as produções que reduzem o comprimento das palavras trata das produções vazias.

Eliminação de Produções Vazias. Sejam uma GIC em que o símbolo inicial não é recursivo. e em que as produções são, em conjunto:

  • As produções de que não são produções da palavra vazia.
  • A produção se .
  • Todas as produções que se obtêm eliminando um ou mais símbolos de do corpo de cada produção , desde que o resultado, , tenha pelo menos um símbolo.

Então é equivalente a e essencialmente não contraível.

Exemplo. Na gramática o símbolo inicial não é recursivo. Além disso, os seus geradores da palavra vazia são

A gramática , que resulta de por eliminação das produções vazias é:

Note-se que:

  1. Foram retiradas as produções e .
  2. Como , foi acrescentada .
  3. De cada produção em cujo corpo ocorrem geradores da palavra vazia foram acrescentadas variantes sem esses símbolos. Por exemplo:
    1. Em no corpo, , ocorre . Portanto é acrescentada a produção , que resulta de removendo .
    2. Mais interessante, no corpo da produção tanto como são geradores da palavra vazia. As combinações possíveis sem algum desses elementos são .

Sem os cálculos auxiliares:

3. Eliminação de Produções Unitárias

As produções unitárias substituem uma variável por outra variável e podem ser eliminadas usando cadeias.

Cadeia. Seja uma GIC essencialmente não contraível.

Para cada , a cadeia de é o conjunto .

Qualquer gramática cujas produções são tais que:

  1. .
  2. Existe tal que .

é equivalente a .

Este enunciado pode ser difícil de interpretar. O primeiro ponto afirma que na gramática não existem produções unitárias. O segundo ponto permite substituir a produção unitária por para cada .

Exemplo. As cadeias de são

e as produções unitária são e . As respetivas substituições são:

  • de , dado que : .
  • de , dado que : .

A gramática que se obtém, equivalente a , é:

4. Eliminação de Símbolos Inúteis

A definição de linguagem gerada pela GIC é

Uma inspeção rápida a mostra que a recursividade desta variável impede-a de gerar palavras só de terminais. Portanto, em termos de linguagem gerada, este é um símbolo que pode ser retirado da gramática. Também podem ser retirados os símbolos que não podem ser atingidos a partir do símbolo inicial.

Símbolo Útil. Símbolo Acessível. Gramática Limpa. Seja uma GIC.

Um símbolo é:

  • Útil se existe uma derivação .
  • Inútil se não é útil.
  • Acessível se existe uma derivação .
  • Inacessível se não é acessível.

Uma variável é:

  • Produtiva se .
  • Improdutiva se não é produtiva.

Uma variável é útil se e só se for acessível e produtiva.

Uma gramática sem símbolos inúteis e inacessíveis diz-se limpa (ou reduzida).

Para limpar uma gramática de símbolos inúteis e inacessíveis:

  1. Eliminar as variáveis improdutivas:
    1. Encontrar as variáveis produtivas (por exemplo, derivando delas uma palavra só de terminais).
    2. Remover as produções onde ocorrem as variáveis improdutivas.
  2. Eliminar as variáveis inacessíveis:
    1. Encontrar os símbolos acessíveis (por exemplo, fazendo uma derivação a partir do símbolo inicial onde o símbolo ocorra).
    2. Remover as produções das variáveis inacessíveis.

Exemplo. Como foi observado acima, é inútil. Portanto as produções onde ocorre podem ser removidas. As novas produções são:

Como resultado destas remoções deixou de ser acessível: Nenhuma derivação "contém" . Portanto, também as produções de podem ser removidas e o resultado é:

Note-se que, removendo as produções de , o terminal ficou inacessível, pelo que foi retirado de , tal como , que nunca constou nas produções.

Finalmente, é fácil de verificar que e são produtivos e acessíveis. Também e são acessíveis.

Numa gramática limpa todos os símbolos "contam". Além disso, e de se ter tratado das produções vazias e unitárias, pouco se fez para melhorar o processo de pesquisa no grafo da gramática. Para esse efeito é preciso "controlar a forma" das produções.

5. Forma Normal de Chomsky

O corpo das produções de uma CIG é uma palavra de , pelo que pode ter vários terminais e variáveis misturados. Aqui pretende-se "arrumar" as formas possíveis que as produções podem ter.

Forma Normal de Chomsky (FNC). Uma GIC está na forma normal de Chomsky se cada uma das suas produções tem uma das formas seguintes:

  1. com .
  2. com .
  3. .

A forma normal de Chomsky é bastante fácil de obter, substituindo terminais "inconvenientes" por variáveis e usando novas variáveis para "agrupar" cadeias três ou mais símbolos.

Exemplo. Na gramática

acrescentam-se e para retirar estes terminais das outras produções:

Para se obter a FNC ainda falta tratar das produções e . Para tal acrescenta-se a produção e substitui-se . O resultado final é:

6. Forma Normal de Greibach

Uma forma de reduzir o número de ramificações durante a pesquisa consiste em "olhar para o próximo símbolo" da palavra e escolher apenas produções que colocam esse símbolo no início.

Por exemplo, dadas a GIC e a palavra , é simples de ver que a única derivação possível é porque em cada passo há só um candidato possível.

Neste caso, em cada passo da pesquisa é muito simples descartar todos os candidatos que não começam pelo terminal correto. Esta observação motiva a seguinte definição:

Forma Normal de Greibach (FNG). Uma GIC está na forma normal de Greibach se cada uma das suas produções tem uma das formas seguintes:

  1. com .
  2. com .
  3. .

Note-se que, em relação à forma normal de Chomsky, a única diferença na primeira forma das produções: em vez de .

As gramáticas na FNG são úteis para:

  • evitar recursões infinitas nos algoritmos de pesquisa.
  • guiar (com o primeiro símbolo) a escolha das regras a expandir.
  • produzir AP equivalente à gramática dada.

Construção de uma GIC na FNG

Ao contrário da construção da FNC, que é simples de obter, a construção de uma GIC na FNG requer alguma orientação.

Construção da FNG. Dada uma GIC na FNC:

  1. Ordenar as variáveis. Define-se uma ordem total nas variáveis de com uma única condição: é o primeiro elemento.
  2. Passo Descendente. Segue-se essa ordem para transformar todas as produções de modo que, se então .
  3. Passo Ascendente. Segue-se essa ordem invertida para substituir cada produção por para cada produção .

Exemplo. Continuando com , que está na FNC:

Ordenar as variáveis. A única condição é " é o primeiro elemento". Por exemplo

Passo Descendente. Ordenar as produções seguindo essa ordem.

As produções de e de não verificam a condição do passo descendente. No caso de esse problema resolve-se facilmente substituindo o no início da produções de por (porque ).

Resta o problema com a produção . Segue-se também o método anterior:

Só que desta vez o problema não ficou resolvido e resultou numa Recursão Direta à Esquerda da variável .


Eliminação da Recursividade Direta à Esquerda. Se

são as produções de , organizadas de forma que:

  • Cada começa por e
  • Nenhuma começa por .

Então obtém-se uma gramática equivalente:

  1. Acrescentando uma nova variável, por exemplo .
  2. Substituindo as produções de por

Uma mnemónica para este processo é a seguinte:

Substituir por e .

Intuitivamente percebe-se porque esta substituição é válida observando que a linguagem gerada por é:

Agora, outra forma de descrever esta linguagem é "Um seguido de zero ou mais ." Neste caso:

  • gera um ou mais .
  • gera um seguido de zero ou um .

A eliminação da RDE de

define as produções

pelo que a ordem inicialmente definida para as variáveis tem de ser estendida de forma que . Por exemplo:

A nova gramática equivalente fica:

No fim do passo descendente cada produção ou é vazia ou começa por um terminal ou começa por uma variável "mais abaixo".

Passo Ascendente. Substituir, "de baixo para cima" a primeira variável de cada produção:

Sem anotações, a GIC final, está na Forma Normal de Greibach e é equivalente à GIC inicial

Embora tenha muito mais produções do que e ainda por cima aparentemente redundantes (mas não o são) o facto de estar na FNG torna-a mais adequada e eficiente do que em termos de pesquisa. Por exemplo:

  • A derivação de uma palavra com símbolos tem, no máximo, passos porque cada produção "produz" um terminal.
  • O primeiro símbolo "filtra" as possíveis produções aplicáveis.
  • A pesquisa de uma palavra não gerada pela GIC, , "falha cedo", no máximo em passos.

Ver o exemplo no código deste capítulo.

Processo de Normalização de uma Gramática

Resumindo todos os passos, a normalização de uma GIC consiste em:

  1. Eliminar a recursividade do símbolo inicial.
  2. Eliminar as produções vazias exceto, se necessário, do símbolo inicial.
  3. Eliminar as produções unitárias.
  4. Eliminar os símbolos inúteis.
  5. Transformar na Forma Normal de Chomsky.
  6. Transformar na Forma Normal de Greibach.

Conclusão

O problema da Análise Sintática, que se está a tentar resolver, é uma reformulação do Problema Principal de ALP para as GIC e pergunta "como determinar se " quando é uma palavra/programa e é uma linguagem gerada por uma gramática independente de contexto.

Inicialmente tentou-se formalizar as linguagens usando expressões regulares mas o Pumping Lemma mostrou a necessidade de uma abordagem mais geral — as gramáticas independentes de contexto.

Embora as GIC resolvam a adequação (com as expressões algébricas) e os autómatos de pilha proporcionem um modelo computacional, este não é eficiente.

A Normalização de uma GIC é um processo que começa com uma GIC e que, ao longo de vários passos, produz outras GIC equivalentes, de forma a obter-se uma gramática "adequada" aos algoritmos de pesquisa geral. A Forma Normal de Greibach impõe restrições fortes às produções, que podem ser bem aproveitadas para reduzir a complexidade/ramificação da pesquisa.

Mesmo com a FNG a ajudar na pesquisa, a resposta a "" ainda permite ramificações ao pesquisar a árvore das derivações. Portanto o problema do não determinismo persiste e, com ele, a eficiência fica por resolver.

Os algoritmos deterministas são o próximo assunto e assentam, tanto quanto possível, nas propriedades das gramáticas e das derivações.