Introdução

Procura-se ilustrar uma das principais aplicações da análise sintática: A implementação de linguagens de programação.

Antes de se definir a própria linguagem de programação e considerar os aspetos da implementação do respetivo analisador sintático e do interpretador faz-se uma revisão dos termos e processos envolvidos.

Alguns termos relevantes, alguns já familiares, são:

  • Símbolos: "entidades" indivisíveis e distintas entre si. Um conjunto de símbolos é um alfabeto.
  • Palavra: sequência finita de símbolos.
  • Linguagem: conjunto de palavras.
  • Gramática: regras que permitem especificar certas linguagens.
  • Representação: descrição da estrutura duma palavra, de acordo com uma gramática.

Também é importante recapitular alguns processos:

  • A análise sintática consiste em decidir de uma palavra está, ou não, numa linguagem : "?". No caso da linguagem ser gerada por uma certa gramática , isto é, se , a questão da análise sintática passa a ser "?" em que é o símbolo inicial de .
  • Para o processamento de programas, em particular para a interpretação e/ou compilação, não basta saber que ; É mais importante representar a estrutura de de acordo com as regras de . Essa representação pode ser, por exemplo, a árvore da derivação de ou a sua própria derivação, isto é a sequência de regras aplicadas passo-a-passo em .
  • A representação do programa é processada pelo interpretador, que mantém uma configuração interna, atualizada conforme a computação avança nas instruções.
  • A evolução da computação depende, por um lado, dos tipos das instruções e, por outro, dos valores das expressões.

Representação de Programas e/ou Expressões

É necessária uma estrutura de dados adequada à representação e ao processamento de programas.

Começando pelas expressões algébricas:

Árvore de DerivaçãoÁrvore SimplificadaValoração da Expressão
Figura Exemplo Árvore DerivaçãoFigura Árvore Derivação SimplificadasFigura Árvore Derivação Simplificadas
  • A palavra 2 + (3 * 4) representa a expressão algébrica mas, como "simples" sequência de símbolos, perde-se a estrutura da expressão que representa.

  • As sequências (isto é, listas) não são adequadas para processar expressões ou programas. Por exemplo, para calcular o valor de primeiro avalia-se a parte e depois esse resultado é somado a .

  • A estrutura da expressão algébrica resulta de uma certa gramática, implícita na respetiva definição formal.

  • A linguagem das expressões algébricas resulta, por exemplo, da seguinte GIC:

    E → E + T | E - T | T
    T → T * U | T / U | U
    U → N | V | ( E )
    

    em que N define números (-1, 2.45E-5, 42.021, etc) e V variáveis (a, Largura, etc).

  • De acordo com esta GIC a palavra 2 + (3 * 4) tem a Árvore de Derivação acima.

  • Na Árvore Simplificada a representação em árvore permite calcular o valor da expressão, propagando os valores das folhas para a raíz e, em cada nó, aplicando a operação adequada conforme o tipo do nó.

  • A implementação de árvores por uma estrutura de dados é assunto doutras disciplinas. Aqui estamos interessados numa representação simples de árvores, de preferência sem a introdução de novos tipos, operações, etc

  • A árvore simplificada fica representada (em Python) pelo tuplo ('+', 2, ('*', 3, 4)).

  • Em geral, cada nó tem um conteúdo X e descendentes D1, ..., Dn. Este nó é representado pelo tuplo (X, D1, ..., Dn) com a convenção de que as folhas (Z) são representadas apenas por Z.

  • Subindo a árvore de 2 + (3 * 4) temos:

    1. Folhas 2, 3 e 4 (e não (2) por exemplo).
    2. A multiplicação é o tuplo ('*', 3, 4) porque o respetivo conteúdo é '*' (uma string Python) e tem dois descendentes, 3 e 4.
    3. O nó da soma tem conteúdo '+' e dois descendentes, a folha 2 e a sub-árvore ('*', 3, 4). Fica representado por ('+', 2, ('*', 3, 4)).

Esta forma de representar as (estruturas das) expressões vai ser aplicada à representação (das estruturas) dos programas.

Também o exemplo do cálculo do valor da expressão algébrica orienta a implementação do interpretador.