Notes - MIECT
Compiladores
Notes - MIECT
Compiladores
  • Compiladores
  • Compiladores, Linguagens e Gramáticas
    • Enquadramento
    • Compiladores
      • Interpretadores
      • Estrutura de um Compilador
    • Implementação de um Compilador
    • Linguagens: Definição como Conjunto
      • Conceito básicos e terminologia
      • Operações sobre palavras
      • Operações sobre linguagens
    • Introdução às gramáticas
      • Hierarquia de Chomsky
      • Autómatos
  • Introdução, Estrutura, Aplicação
    • Exemplos
      • Expr
      • Exemplo figuras
      • Exemplo visitor
      • Exemplo listener
    • Construção de gramáticas
      • Especificação de gramáticas
    • Estrutura léxica
    • Regras léxicas
      • Padrões léxicos típicos
      • Operador léxico “não ganancioso”
    • Estrutura sintática
      • Secção de Tokens
      • Acções no preâmbulo da gramática
    • Regras sintácticas
      • Padrões sintácticos típicos
      • Precedência
      • Associatividade
      • Herança de gramáticas
    • Outras funcionalidades
      • Tabelas CSV
      • Gramáticas ambíguas
      • Predicados semânticos
      • Separar analisador léxico do analisador sintáctico
      • “Ilhas” lexicais
      • Enviar tokens para canais diferentes
      • Reescrever a entrada
      • Desacoplar código da gramática - ParseTreeProperty
  • Análise Semântica
    • Estrutura de um Compilador
    • Sistema de Tipos
    • Gramáticas de Atributos
    • Tabela de símbolos
    • Instruções restringidas por contexto
    • ANTLR4: gestão de erros
  • Síntese
    • Síntese: Geração de código
    • String Template
    • Síntese: geração de código intermédio
  • Análise Lexical
    • Análise Lexical: Estrutura de um Compilador
    • Linguagens regulares
    • Gramáticas regulares
    • Expressões regulares
    • Conversão entre ER e GR
    • Reconhecimento de tokens
    • Autómatos finitos
    • Autómato finito não determinista
    • Autómato finito determinista
      • Projecto de autómato finito determinista
    • Conversão de AFND em AFD
    • Conversão de uma expressão regular num AFND
    • Autómato finito generalizado (AFG)
  • Gramática de Atributos
    • Conteúdo semântico
    • Gramática de atributos
    • Avaliação Dirigida pela Sintaxe
  • Análise Sintática Descendente
    • Análise Sintática
    • Análise Sintática Descendente
    • Analisador (parser) Recursivo-Descendente Preditivo
    • Questões a resolver
    • Fatorização à Esquerda
    • Eliminação de Recursividade á Esquerda
    • Conjuntos predict, first e follow
      • Conjunto first
      • Conjunto follow
    • Reconhecedor Descendente Preditivo
  • Análise Sintática Ascendente
    • Análise Sintática Ascendente
    • Construção de um reconhecedor ascendente
    • Tabela de decisão de um reconhecedor ascendente
    • Reconhecedor Ascendente
    • Tabela de Decisão de um Reconhecedor Ascendente
Powered by GitBook
On this page
  • Dependência local: classificação de atributos
  • Exemplo dependência local: expressão aritmética
  • Exemplo dependência local: declaração
  • Declaração de atributos associados à árvore sintáctica
  • Declaração de atributos ParseTreeProperty
  • Gramáticas de atributos em ANTLR4: síntese
  1. Análise Semântica

Gramáticas de Atributos

PreviousSistema de TiposNextTabela de símbolos

Last updated 2 years ago

Já vimos que atribuir sentido ao código fonte de uma linguagem requer, não só, correcção sintáctica (assegurada por gramáticas independentes de contexto) como também correcção semântica.

Nesse sentido, é de toda a conveniência ter acesso a toda a informação gerada pela análise sintáctica, i.e. à árvore sintáctica, e poder associar nova informação aos respectivos nós.

Este é o objectivo da gramática de atributos:

  • Cada símbolo da gramática da linguagem (terminal ou não terminal) pode ter a si associado um conjunto de zero ao mais atributos.

  • Um atributo pode ser um número, uma palavra, um tipo, ...

  • O cálculo de cada atributo tem de ser feito tendo em consideração a dependência da informação necessária para o seu valor.

Entre os diferentes tipos de atributos, existem alguns cujo valor depende apenas da sua vizinhança sintáctica.

  • Um desses exemplos é o valor de uma expressão aritmética (que para além disso, depende apenas do próprio nó e, eventualmente, de nós descendentes).

Existem também atributos que (podem) depender de informação remota.

  • É o caso, por exemplo, do tipo de dados de uma expressão que envolva a utilização de uma variável ou invocação de um método.

Dependência local: classificação de atributos

Os atributos podem ser classificados duas formas, consoante as dependências que lhes são aplicáveis:

  1. Dizem-se sintetizados, se o seu valor depende apenas de nós descendentes (i.e. se o seu valor depende apenas dos símbolos existentes no respectivo corpo da produção).

  2. Dizem-se herdados, se depende de nós "irmãos" ou de nós ascendentes.

Formalmente podem-se designar os atributos anotando com uma seta no sentido da dependência (para cima, nos atributos sintetizados; e para baixo nos herdados).

Exemplo dependência local: expressão aritmética

Considere a seguinte gramática:

  • E -> P + E | P P -> T * P | T T -> (E) | int

Se quisermos definir um atributo v para o valor da expressão, temos um exemplo de um atributo sintetizado.

Por exemplo, para a entrada — 1 + 2 ∗ 3 — temos a seguinte árvore sintáctica anotada:

Produção
Regra Semântica

E1 -> P + E2

E1y = Py + E2.v

E -> P

E.v = P.v

P1 -> T * P2

P1.v = T.v * P2.v

P -> T

P.v = T.v

T -> (E)

T.v = E.v

T -> int

T.v = int.value

Exemplo dependência local: declaração

Considere a seguinte gramática:

  • D -> TL T -> int | real L -> id L | id

Se quisermos definir um atributo t para indicar o tipo de cada variável id, temos um exemplo de um atributo herdado.

Por exemplo, para a entrada — int a, b — temos a seguinte árvore sintáctica anotada:

Produção
Regra semântica

D -> TL

D.t = T.t L.t = T.t

T -> int

T.t = int

T -> real

T.t = real

L1 -> id, L2

id.t = L1.t L2.t = L1.t

L -> id

id.t = L.t

Declaração de atributos associados à árvore sintáctica

Podemos declarar atributos de formas distintas:

  • Directamente na gramática independente de contexto recorrendo a argumentos e resultados de regras sintáctica.

expr[String type] returns[int value]: // type not used
    e1 = expr ’+’ e2=expr
    {$value = $e1.value + $e2.value; }    #Add
    | INT
        {$value = Integer.parseInt($INT.text);}    #Int
    ;
  • Indirectamente fazendo uso do array associativo ParseTreeProperty:

protected ParseTreeProperty<Integer> value=
    new ParseTreeProperty<>();
    
@Override public void exitInt(ExprParser.IntContext ctx){
    value.put(ctx , Integer.parseInt(ctx.INT().getText()));
}

@Override public void exitAdd(ExprParser.AddContext ctx){
    int left = value.get(ctx.e1);
    int right = value.get(ctx.e2);
    value.put(ctx, left+right);
}

Podemos ainda utilizar o resultados dos métodos visit.

Declaração de atributos ParseTreeProperty

Este array tem como chave nós da árvore sintáctica, e permite simular quer argumentos, quer resultados, de regras.

A diferença está nos locais onde o seu valor é atribuído e acedido.

Para simular a passagem de argumentos basta atribuir-lhe o valor antes de percorrer o respectivo nó (nos listeners usualmente nos métodos enter...), sendo o acesso feito no próprio nó.

Para simular resultados, faz-se como no exemplo dado (i.e. atribui-se o valor no próprio nó, e acede-se nos nós ascendentes).

Gramáticas de atributos em ANTLR4: síntese

Podemos associar três tipos de informação a regras sintácticas:

  1. Informação com origem em regras utilizadas no corpo da regra (atributos sintetizados);

  2. Informação com origem em regras que utilizam esta regra no seu corpo (atributos herdados);

  3. Informação local à regra.

Em ANTLR4 a utilização directa de todos estes tipos de atributos é muito simples e intuitiva:

  1. Atributos sintetizados: resultado de regras;

  2. Atributos herdados: argumentos de regras;

  3. Atributos locais.

Alternativamente, podemos utilizar o array associativo ParseTreeProperty (que se justifica apenas para as duas primeiras, já que para a terceira podemos utilizar variáveis locais ao método respectivo); ou o resultado dos métodos visit (no caso de se utilizar visitors) para atributos sintetizados.