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
  • Exemplo 1
  • Análise
  • Exemplo 2
  • Exemplo 2a
  • Exemplo 2b
  • Exemplo 3
  • Exemplo 3
  • Exemplo 4
  1. Análise Sintática Descendente

Analisador (parser) Recursivo-Descendente Preditivo

PreviousAnálise Sintática DescendenteNextQuestões a resolver

Last updated 2 years ago

Exemplo 1

Sobre o alfabeto {a,b}, considere linguagem

  • L = {a^n * b^n : n >= 0}

descrita pela gramática

  • S -> a S b | end

Construa-se um programa com lookahead de 1, em que o símbolo não terminal S seja uma função recursiva, que reconheça a linguagem L

Análise

No programa anterior:

  • lookahead é uma variável que representa o próximo à entrada.

  • adv() é uma função que avança na entrada, colocando em lookahead o próximo símbolo.

  • eat(c) é uma função que verifica se no lookahead está o símblo c, gerando erro se não estiver, e avança para o próximo.

  • Há duas produções da gramática com cabeça S, sendo a decisão central do programa a escolha de qual usar face ao valor do lookahead.

    • deve escolher-se S -> a S b se o lookahead for a.

    • e S -> end se o lookahead for $ ou b

No programa anterior, o símbolo $, marcador de fim de entrada, corresponde ao \n

  • Uma palavra é aceite pelo programa se e só se

    • S(); eat($), não der erro.

Exemplo 2

Sobre o alfabeto {a; b}, considere linguagem

descrita pela gramática

  • S -> end | a B S | b A S A -> a | b A A B -> a B B | b

Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.

  • O programa terá 3 funções recursivas, A, B, S, semelhantes à função S do exemplo anterior.

  • Em A, deve escolher-se A -> a se lookahead for a e A -> b AA se for b.

  • Em B, deve escolher-se B -> b se lookahead for b e B -> a B B se for a.

  • Em S, deve escolher-se S -> a B S se lookahead for a, S -> b A S se for b e S -> end se for $ (este último, mais tarde saber-se-á porquê).

Exemplo 2a

Sobre o alfabeto {a; b}, considere linguagem

descrita pela gramática

  • S -> end | a B | b A A -> a S | b A A B -> a B B | b S

Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.

  • O programa terá 3 funções recursivas, A, B e S, semelhantes á funcão S do exemplo anterior, exceto no critério de escolha da produção S -> end.

  • Escolhar S -> end quando lookahead for $ pode não resolver.

  • Por exemplo, com o lookahead igual a a, há situações em que se tem de escolher S -> a B e outras S -> end.

  • É o que acontece com a entrada bbaa

    • S => b A => bb AA => bba S A => ... momento em que o S tem de ser expandido para o end e o lookahead é a.

Exemplo 2b

Sobre o alfabeto {a; b}, considere linguagem

descrita pela gramática

  • S -> end | a B | b A A -> a S | b A A B -> a B B | b S

Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.

  • Tal como no caso anterior, escolher S -> end quando lookahead for $ pode não resolver.

Exemplo 3

Sobre o alfabeto {a,b}, considere linguagem

descrita pela gramática

  • S -> a S b | a b

Construa um programa em que o símbolo não terminal S seja uma função recursiva que reconheça a linguagem L.

  • Como escolher entre as duas produções se ambas começam pelo mesmo símbolo ?

  • Há duas abordagens:

    • Pôr em evidência o a à esquerda, transformando a gramática para

      • S -> a X X -> S b | b

    • Aumentar o número do símbolos de lookahead para 2

      • se for aa, escolhe-se S -> a S b

      • se for ab, escolhe-se S -> a b

Exemplo 3

Sobre o alfabeto {a, b}, considere linguagem

descrita pela gramática

  • S -> a S b | a b

Construa um programa em que o símbolo não terminal S seja uma função recursiva quem reconheça a linguagem L.

  • Há duas abordagens:

    • Pôr em evidência o a à esquerda, transformando a gramática para

      • S -> a X X -> S b | b

    • Aumentar o número de símbolo de lookahead para 2

      • se for aa, escolhe-se S -> a S b

      • se for aa, escolhe-se S -> a b

Exemplo 4

Sobre o alfabeto {a, b}, considere linguagem

descrita pela gramática

  • S -> S a b | a b

Construa um programa em que o símbolo não terminal S seja uma função recursiva que reconheça a linguagem L.

  • Escolher a primeira produção cria um ciclo infinito, por causa da recursividade à esquerda

    • O ANTLR consegue lidar com este tipo (simples) de recursividade à esquerda, mas falha com outros tipos.

    • Mas, em geral os reconhecedores descendetes não lidam com recursividades à esquerda.

Solução geral: eliminar a recursividade à esquerda