Projeto Integrador: Gramática Formal da Linguagem Didágica 📝

🎯 Desenvolvimento da Gramática para o Compilador Didágica

A linguagem Didágica apresenta desafios únicos: sintaxe em português, convenções de nomenclatura rígidas (CamelCase para palavras-chave, snake_case para identificadores), sistema de mutabilidade explícita, e integração de conceitos de engenharia de software diretamente na sintaxe.

📋 Gramática Formal Desenvolvida

Definição Completa G = (V, T, P, S)

Desenvolvi a gramática formal completa para a Didágica seguindo rigorosamente a teoria estudada:

Conjunto de Variáveis (V):

V = {Programa, BlocoDeclarações, Declaração, DeclaraçãoVariável,
     DeclaraçãoFuncao, DeclaraçãoClasse, DeclaraçãoContrato,
     BlocoComandos, Comando, ComandoCondicional, ComandoLaço,
     ComandoAtribuição, ComandoEscrita, Expressão, ExpressãoLógica,
     ExpressãoRelacional, ExpressãoAritmética, Termo, Fator,
     ListaParametros, ListaArgumentos, Tipo}

Conjunto de Terminais (T):

T = {Guarde, como, mutável, imutável, Escreva, Se, então, Senão, Fim,
     Enquanto, ParaCada, de, até, Repita, AtéQue, Função, retorne,
     Classe, herda, Construtor, Método, propriedade, Contrato, componente,
     Inteiro, Real, Texto, Booleano, Nulo, E, Ou, Não,
     ==, !=, >, <, >=, <=, +, -, *, /, =,
     (, ), {, }, ",", ;, identificador, literal_inteiro, literal_real,
     literal_texto, literal_booleano, leia_entrada, para_inteiro,
     para_real, para_texto, para_booleano}

Símbolo Inicial: S = Programa

Principais Regras de Produção Implementadas

🔧 Regras Fundamentais da Linguagem

  • Programa \rightarrow BlocoDeclarações BlocoComandosPrincipal
  • BlocoDeclarações \rightarrow Declaração BlocoDeclarações | \varepsilon
  • Declaração \rightarrow DeclaraçãoVariável | DeclaraçãoFuncao | DeclaraçãoClasse
  • DeclaraçãoVariável \rightarrow Guarde Tipo literal como identificador | Guarde mutável Tipo literal como identificador
  • Tipo \rightarrow Inteiro | Real | Texto | Booleano
  • DeclaraçãoFuncao \rightarrow Funcao Tipo identificador ( ListaParametros ) BlocoComandos Fim
  • DeclaraçãoClasse \rightarrow Classe identificador BlocoClasse Fim | Classe identificador herda identificador BlocoClasse Fim
  • BlocoComandos \rightarrow Comando BlocoComandos | \varepsilon
  • Comando \rightarrow ComandoAtribuição | ComandoCondicional | ComandoLaço | ComandoEscrita
  • BlocoComandosPrincipal \rightarrow Comando BlocoComandosPrincipal | \varepsilon
  • ComandoCondicional \rightarrow Se Expressão então BlocoComandos Fim | Se Expressão então BlocoComandos Senão BlocoComandos Fim
  • ComandoLaço \rightarrow Enquanto Expressão BlocoComandos Fim | ParaCada Tipo identificador de literal até literal BlocoComandos Fim
  • ComandoEscrita \rightarrow Escreva ListaArgumentos
  • Expressão \rightarrow ExpressãoLógica
  • ExpressãoLógica \rightarrow ExpressãoRelacional | ExpressãoLógica E ExpressãoRelacional | ExpressãoLógica Ou ExpressãoRelacional | Não ExpressãoLógica
  • ExpressãoRelacional \rightarrow ExpressãoAritmética | ExpressãoAritmética == ExpressãoAritmética | ExpressãoAritmética != ExpressãoAritmética | ExpressãoAritmética > ExpressãoAritmética | ExpressãoAritmética < ExpressãoAritmética | ExpressãoAritmética >= ExpressãoAritmética | ExpressãoAritmética <= ExpressãoAritmética
  • ExpressãoAritmética \rightarrow Termo | ExpressãoAritmética + Termo | ExpressãoAritmética - Termo
  • Termo \rightarrow Fator | Termo * Fator | Termo / Fator
  • Fator \rightarrow identificador | literal_inteiro | literal_real | literal_texto | literal_booleano | ( Expressão ) | identificador ( ListaArgumentos )

🏗️ Classificação na Hierarquia de Chomsky

📊 Análise Formal da Classificação

Analisei rigorosamente minha gramática e classifiquei-a como Tipo 2 (Livre de Contexto) na hierarquia de Chomsky:

Justificativa:

  • Todas as produções têm a forma A → α (lado esquerdo com apenas um não-terminal)
  • Não há dependências contextuais nas regras
  • A gramática gera linguagem que não é regular (devido a estruturas aninhadas)
  • Mas é reconhecível por autômato de pilha

Verificação:

  • ✅ Não é Tipo 3 (Regular): estruturas como Se...Fim aninhadas não são regulares
  • ✅ É Tipo 2 (Livre de Contexto): todas as regras seguem formato A → α
  • ❌ Não precisa ser Tipo 1 (Sensível ao Contexto): não há regras αAβ → αγβ
  • ❌ Não precisa ser Tipo 0 (Irrestrita): gramática é bem estruturada

🔍 Limitações da Gramática e Análise Semântica

A gramática livre de contexto que desenvolvi, embora poderosa para a análise sintática, possui uma limitação inerente: ela não é capaz de verificar restrições que dependem do contexto em que um símbolo é utilizado.

A regra fundamental de que uma variável deve ser declarada antes de ser usada é um exemplo clássico dessa limitação. A gramática, por si só, só consegue verificar se a estrutura de um comando de atribuição (ComandoAtribuição -> identificador = Expressão) é válida, mas não consegue “lembrar” se o identificador já foi visto em uma DeclaraçãoVariável em um ponto anterior do código.

Para resolver essa questão, a verificação é delegada a uma fase posterior do compilador, a Análise Semântica. Nesta etapa, o compilador percorre a Árvore de Sintaxe Abstrata (AST) construída pelo parser e utiliza uma tabela de símbolos para rastrear todas as variáveis declaradas. Somente após a declaração, o uso de um identificador é considerado válido.

Essa separação de tarefas é uma decisão de design que equilibra a expressividade da linguagem com a eficiência do compilador, permitindo que a análise sintática seja realizada com os algoritmos eficientes das linguagens livres de contexto, enquanto restrições mais complexas são tratadas de forma sistemática em uma etapa separada.

🔍 Resolução de Ambiguidades

Problemas Identificados e Soluções

⚠️ Ambiguidades Tratadas

1. Problema do Dangling-Else:

Se condicao1 então
    Se condicao2 então
        comando1
Senão  # A qual Se pertence?
    comando2
Fim

Solução: Regra de associação - Senão sempre se associa ao Se mais próximo não pareado.

2. Precedência de Operadores:

Estabeleci hierarquia clara: 1. () - Parênteses 2. Não - Negação lógica 3. * / - Multiplicação e divisão 4. + - - Adição e subtração 5. == != > < >= <= - Comparações 6. E - AND lógico 7. Ou - OR lógico

3. Associatividade:

  • Operadores aritméticos: associativos à esquerda
  • Operadores lógicos: associativos à esquerda
  • Comparações: não associativas (erro se encadeadas)

🧪 Exemplos de Derivação

Programa Simples com Estrutura Condicional

🔬 Derivação Completa

Programa:

Guarde Inteiro 18 como idade_minima
Se idade_minima >= 18 então
    Escreva "Maior de idade"
Fim

Derivação:

Programa
⇒ BlocoDeclarações
⇒ Declaração BlocoDeclarações
⇒ DeclaraçãoVariável BlocoDeclarações
⇒ Guarde Tipo literal como identificador BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Declaração BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Comando BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima ComandoCondicional BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se Expressão então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se ExpressãoLógica então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se ExpressãoRelacional então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se ExpressãoAritmética >= ExpressãoAritmética então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se Termo >= ExpressãoAritmética então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se Fator >= ExpressãoAritmética então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= ExpressãoAritmética então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= Termo então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= Fator então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Comando BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então ComandoEscrita BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva ListaArgumentos BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva literal_texto BlocoComandos Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva literal_texto ε Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva literal_texto Fim BlocoDeclarações
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva literal_texto Fim ε
⇒ Guarde Inteiro 18 como idade_minima Se identificador >= literal_inteiro então Escreva literal_texto Fim

Programa com Função

🔧 Exemplo de Estrutura Mais Complexa

Programa:

Funcao Inteiro somar(Inteiro a, Inteiro b)
    retorne a + b
Fim

Guarde Inteiro somar(5, 3) como resultado
Escreva "Resultado:", resultado

Árvore de Derivação (parcial):

Programa
├── BlocoDeclarações
│   ├── DeclaraçãoFuncao
│   │   └── Funcao Inteiro somar(...) Fim
│   └── BlocoDeclarações
│       ├── DeclaraçãoVariável
│       │   └── Guarde Inteiro somar(5, 3) como resultado
│       └── BlocoDeclarações
│           └── ε (A declaração da variável seria o último BlocoDeclarações)
└── BlocoComandosPrincipal (?)
    └── ComandoEscrita
        └── Escreva "Resultado:", resultado

🎯 Características Específicas da Didágica

Elementos Únicos Implementados

🌟 Funcionalidades Educacionais

1. Sintaxe Natural em Português:

  • Guarde Inteiro 25 como minha_idade em vez de int minha_idade = 25
  • Se...então...Senão...Fim em vez de if...else
  • Enquanto...Fim em vez de while

2. Sistema de Mutabilidade Explícita:

Guarde Inteiro 10 como constante        # imutável por padrão
Guarde mutável Inteiro 20 como Variável # explicitamente mutável

3. Convenções de Nomenclatura Enforçadas:

  • Palavras Reservadas: CamelCase (Guarde, Escreva, Funcao)
  • Identificadores: snake_case obrigatório (minha_Variável, idade_usuario)

4. Estruturas Verbosas mas Claras:

ParaCada Inteiro i de 1 até 10
    Escreva "Número:", i
Fim

5. Funções Nativas Educacionais:

  • leia_entrada() - entrada do usuário
  • para_inteiro(), para_real(), para_texto() - conversões explícitas

📊 Validação e Testes

Programas de Teste Criados

🧪 Suite de Testes Desenvolvida

Teste 1 - Programa Básico:

Escreva "Olá, mundo!"
Guarde Texto "Didágica" como nome_linguagem
Escreva "Linguagem:", nome_linguagem

Status: Deriva corretamente

Teste 2 - Estruturas de Controle:

Guarde Inteiro 10 como limite
ParaCada Inteiro i de 1 até limite
    Se i == 5 então
        Escreva "Meio do caminho"
    Senão
        Escreva "Número:", i
    Fim
Fim

Status: Deriva corretamente, sem ambiguidades

Teste 3 - Função com Parâmetros:

Funcao Real calcular_media(Real a, Real b)
    retorne (a + b) / 2.0
Fim

Guarde Real calcular_media(8.5, 9.2) como media
Escreva "Média:", media

Status: Deriva corretamente

Teste 4 - Classe com Herança:

Classe Pessoa
    propriedade Texto nome
    propriedade Inteiro idade
Fim

Classe Aluno herda Pessoa
    propriedade Texto matricula

    Metodo mostrar_info()
        Escreva "Aluno:", eu.nome
    Fim
Fim

Status: Deriva corretamente

Análise de Erros Sintáticos

⚠️ Casos de Erro Testados

Erro 1 - Convenção de Nomenclatura:

Guarde Inteiro 10 como MinhaIdade  # Erro: deveria ser minha_idade

Resultado: Rejeitado pela gramática (correto)

Erro 2 - Estrutura Incompleta:

Se idade > 18 então
    Escreva "Maior"
# Falta Fim

Resultado: Rejeitado pela gramática (correto)

Erro 3 - Operador Inexistente:

Guarde Inteiro a && b como resultado  # && não existe, deveria ser E

Resultado: Rejeitado pela gramática (correto)

🔄 Preparação para Próxima Semana

🚀 Interface com Linguagens Regulares

A gramática que desenvolvi esta semana estabelece o contexto completo para a próxima fase: especificação dos tokens como linguagens regulares.

Tokens que precisam ser especificados com expressões regulares:

  • identificador: [a-z][a-z0-9_]* (snake_case obrigatório)
  • literal_inteiro: [0-9]+
  • literal_real: [0-9]+.[0-9]+
  • literal_texto: “[^"]*”
  • Palavras Reservadas: strings exatas (Guarde, Escreva, Se, etc.)

Desafios identificados para próxima semana:

  1. Implementar reconhecimento de snake_case vs CamelCase
  2. Distinguir palavras-chave de identificadores
  3. Tratar caracteres acentuados em strings
  4. Implementar funções nativas (leia_entrada, para_inteiro, etc.)

A gramática não-ambígua que construí garante que posso implementar parser eficiente usando algoritmos LL(1) ou LR(1).

💭 Reflexões sobre o Desenvolvimento

🤔 Lições Aprendidas

Desafios Enfrentados:

  1. Balancear naturalidade e formalismo: Manter sintaxe próxima ao português sem comprometer precisão formal
  2. Eliminar ambiguidades: Especialmente com operadores lógicos em português (E, Ou, Não)
  3. Enforçar convenções: Garantir que gramática rejeite identificadores que não seguem snake_case

Decisões Importantes:

  1. Verbosidade educacional: Preferi clareza sobre concisão (ParaCada…de…até vs for)
  2. Mutabilidade explícita: Forçar programador a decidir conscientemente sobre mutabilidade
  3. Operadores híbridos: Misturar símbolos matémáticos com palavras portuguesas

Próximos Passos:

  • Implementar analisador léxico baseado nos tokens definidos
  • Testar gramática com parser recursivo descendente
  • Desenvolver mensagens de erro educacionalmente úteis
  • Preparar exemplos progressivos para demonstrar capacidades da linguagem