1. Especificação da Gramática da Linguagem
A gramática formal define as regras sintáticas que determinam como os tokens produzidos pelo analisador léxico podem ser combinados para formar construções válidas da linguagem C. Esta especificação serve como base para a implementação do analisador sintático e como referência para a resolução de ambiguidades.
A notação adotada é a EBNF (Extended Backus-Naur Form), escolhida por sua legibilidade e capacidade de expressar repetição e opcionalidade de forma compacta.
A especificação está organizada em duas partes. A Seção 2 documenta a gramática
atualmente implementada no parser.y, refletindo o estado validado do analisador
sintático. A Seção 3 documenta a especificação completa da linguagem alvo —
todos os tokens reconhecidos pelo léxico e as construções que o compilador suportará
ao final do desenvolvimento.
1.1 Convenções da Notação EBNF
| Notação | Significado |
|---|---|
A ::= B |
A é definido como B |
A B |
A seguido de B (concatenação) |
A \| B |
A ou B (alternativa) |
[ A ] |
A é opcional (zero ou uma vez) |
{ A } |
A se repete zero ou mais vezes |
( A ) |
Agrupamento |
'token' |
Terminal literal |
TOKEN |
Terminal nomeado (retornado pelo léxico) |
2. Estado Atual da Implementação
Esta seção descreve as regras gramaticais atualmente implementadas no analisador
sintático. Toda construção aqui documentada possui regra ativa no parser.y e
pode ser validada pelo compilador.
2.1 Estrutura do Programa
O ponto de entrada da gramática é programa. Um arquivo-fonte válido é composto
por uma sequência de elementos. Atualmente, um elemento pode ser uma função ou
uma declaração global simples.
programa ::= lista_elementos
lista_elementos ::= lista_elementos elemento
| elemento
elemento ::= funcao
| declaracao
2.2 Tipos
O parser reconhece os seguintes tipos primitivos:
tipo ::= 'int'
| 'float'
| 'void'
2.3 Funções
Uma função é formada por um tipo de retorno, um nome, uma lista de parâmetros entre parênteses e um bloco de comandos.
funcao ::= tipo IDENT '(' parametros ')' bloco
parametros ::= lista_parametros
| /* vazio */
lista_parametros ::= lista_parametros ',' parametro
| parametro
parametro ::= tipo IDENT
Exemplos válidos:
int soma(int a, int b) { ... }
void imprime() { ... }
2.4 Bloco e Lista de Comandos
Um bloco agrupa zero ou mais comandos entre chaves. É a unidade estrutural utilizada nos corpos de funções e nas estruturas de controle.
bloco ::= '{' lista_comandos '}'
lista_comandos ::= lista_comandos comando
| /* vazio */
2.5 Comandos
Um comando é a unidade de execução do programa. O parser reconhece os seguintes tipos de comando:
comando ::= declaracao
| atribuicao
| retorno
| comando_if
| comando_while
| comando_for
| incremento
| chamada_funcao ';'
| bloco
2.5.1 Declaração de Variável
declaracao ::= tipo IDENT ';'
| tipo IDENT '=' expressao ';'
| tipo IDENT '[' NUMBER ']' ';'
Exemplos válidos:
int x;
float y = 3.14;
int v[10];
2.5.2 Atribuição
Em comandos gerais, apenas a atribuição simples está implementada:
atribuicao ::= IDENT '=' expressao ';'
2.5.3 Incremento e Decremento
Aceita as formas pré-fixada e pós-fixada como comandos isolados:
incremento ::= IDENT '++' ';'
| IDENT '--' ';'
| '++' IDENT ';'
| '--' IDENT ';'
2.5.4 Retorno
retorno ::= 'return' expressao ';'
| 'return' ';'
2.5.5 Condicional — if / else
O corpo do if e do else exige obrigatoriamente um bloco com chaves.
A ambiguidade do dangling else é resolvida via %prec LOWER_THAN_ELSE,
associando o else sempre ao if mais interno.
comando_if ::= 'if' '(' expressao_booleana ')' bloco
| 'if' '(' expressao_booleana ')' bloco 'else' bloco
2.5.6 Repetição — while
comando_while ::= 'while' '(' expressao_booleana ')' bloco
2.5.7 Repetição — for
As três partes do for são independentes e opcionais:
comando_for ::= 'for' '(' for_init ';' for_condicao ';' for_atualizacao ')' bloco
for_init ::= declaracao_for
| atribuicao_for
| /* vazio */
for_condicao ::= expressao_booleana
| /* vazio */
for_atualizacao ::= atribuicao_for
| incremento_for
| /* vazio */
declaracao_for ::= tipo IDENT
| tipo IDENT '=' expressao
atribuicao_for ::= IDENT '=' expressao
| IDENT '+=' expressao
| IDENT '-=' expressao
incremento_for ::= IDENT '++'
| IDENT '--'
| '++' IDENT
| '--' IDENT
Exemplos válidos:
for (int i = 0; i < 10; i++) { ... }
for (i = 0; i != n; i += 2) { ... }
for ( ; ; ) { ... }
2.5.8 Chamada de Função como Comando
Chamadas simples de função podem aparecer como comando isolado:
chamada_funcao ::= IDENT '(' argumentos ')'
argumentos ::= lista_argumentos
| /* vazio */
lista_argumentos ::= lista_argumentos ',' expressao
| expressao
Exemplos válidos:
foo();
soma(1, 2);
2.6 Expressões
2.6.1 Expressão Booleana
Utilizada nas condições de if, while e for. Aceita comparações simples,
combinadas com && e ||, e agrupadas com parênteses:
expressao_booleana ::= expressao operador_relacional expressao
| expressao_booleana '&&' expressao_booleana
| expressao_booleana '||' expressao_booleana
| '(' expressao_booleana ')'
operador_relacional ::= '<' | '>' | '<=' | '>=' | '==' | '!='
2.6.2 Expressão Aritmética
A hierarquia em três níveis garante a precedência correta entre os operadores.
Operadores de maior precedência estão em níveis mais profundos da gramática,
garantindo que 2 + 3 * 4 seja interpretado como 2 + (3 * 4):
expressao ::= expressao '+' termo
| expressao '-' termo
| termo
termo ::= termo '*' fator
| termo '/' fator
| termo '%' fator
| fator
fator ::= NUMBER
| IDENT
| acesso_vetor
| chamada_funcao
| '(' expressao ')'
2.6.3 Acesso a Vetor
acesso_vetor ::= IDENT '[' expressao ']'
2.6.4 Chamada de Função em Expressão
Chamadas simples de função também são aceitas como fator de expressão, permitindo atribuições como:
int r = soma(2, 3);
2.7 Tabela de Precedência
A tabela reflete exatamente as declarações %left, %right e %nonassoc
presentes no parser.y, do menor para o maior nível:
| Nível | Operadores | Associatividade |
|---|---|---|
| 1 | \|\| |
Esquerda |
| 2 | && |
Esquerda |
| 3 | == != |
Esquerda |
| 4 | < > <= >= |
Esquerda |
| 5 | + - |
Esquerda |
| 6 | * / % |
Esquerda |
| 7 | ! (unário) |
Direita |
| 8 | = (atribuição) |
Direita |
| 9 | else (via LOWER_THAN_ELSE / KW_ELSE) |
Não-associativo |
2.8 Ambiguidades Conhecidas e Resoluções
2.8.1 Dangling Else
Dado um if aninhado sem else, o parser precisa decidir a qual if o
else pertence:
if (a)
if (b)
x = 1;
else /* pertence ao if (b), não ao if (a) */
x = 2;
Solução adotada: o else é sempre associado ao if mais interno.
Implementada no Bison declarando KW_ELSE com maior precedência que
LOWER_THAN_ELSE:
%nonassoc LOWER_THAN_ELSE
%nonassoc KW_ELSE
2.8.2 typedef-name vs IDENT
Nomes criados com typedef podem aparecer em posições de tipo, criando
ambiguidade com identificadores comuns:
typedef int Inteiro;
Inteiro x; /* "Inteiro" é um tipo ou um identificador? */
O parser não consegue distinguir os dois casos apenas com a gramática — a
decisão depende de saber se Inteiro foi previamente declarado como typedef.
Solução adotada: typedef está fora do subconjunto atual. A resolução
completa exigiria uma tabela de símbolos específica para nomes de tipos, mas o
parser hoje aceita apenas int, float e void na regra tipo.
2.9 Validações Semânticas Atuais
Além de validar a gramática, o compilador executa uma passada semântica sobre a AST. Essa etapa cobre:
- função declarada mais de uma vez;
- chamada para função não declarada;
- quantidade incorreta de argumentos;
- conversão básica entre
intefloatem argumentos, atribuições e retornos; returncom valor em funçãovoid;returnsem valor em função não-void;- função não-
voidsem retorno com valor; - divisão literal por zero.
Essas validações ainda são intencionalmente simples e seguem o escopo acadêmico do subconjunto suportado.
3. Especificação da Linguagem Alvo
Esta seção define o escopo completo da linguagem que o compilador suportará.
As construções aqui descritas têm seus tokens já reconhecidos pelo léxico e
declarados no parser, e serão integradas ao parser.y nas próximas iterações
do desenvolvimento.
3.1 Tipos
3.1.1 Tipos Primitivos Completos
A regra tipo será expandida para cobrir todos os primitivos do padrão ANSI C:
tipo ::= 'int' | 'float' | 'void'
| 'char' | 'double' | 'long'
| 'short' | 'signed' | 'unsigned'
3.1.2 Especificadores de Armazenamento
especificador_armazenamento ::= 'auto'
| 'extern'
| 'register'
| 'static'
3.1.3 Qualificadores de Tipo
qualificador_tipo ::= 'const'
| 'volatile'
3.2 Comandos
3.2.1 Atribuições Compostas
A regra atribuicao será expandida para cobrir todos os operadores compostos
fora do contexto do for:
atribuicao ::= IDENT '=' expressao ';'
| IDENT '+=' expressao ';'
| IDENT '-=' expressao ';'
| IDENT '*=' expressao ';'
| IDENT '/=' expressao ';'
| IDENT '%=' expressao ';'
3.2.2 do...while
comando_do_while ::= 'do' bloco 'while' '(' expressao_booleana ')' ';'
3.2.3 switch
comando_switch ::= 'switch' '(' expressao ')' '{' { clausula_case } '}'
clausula_case ::= 'case' expressao_constante ':' { comando }
| 'default' ':' { comando }
expressao_constante ::= NUMBER | IDENT
3.2.4 Desvios
comando_break ::= 'break' ';'
comando_continue ::= 'continue' ';'
comando_goto ::= 'goto' IDENT ';'
comando_rotulo ::= IDENT ':' comando
3.3 Tipos Compostos
3.3.1 Struct
especificador_struct ::= 'struct' IDENT '{' { membro_struct } '}'
| 'struct' IDENT
membro_struct ::= tipo lista_membros ';'
lista_membros ::= IDENT { ',' IDENT }
3.3.2 Union
especificador_union ::= 'union' IDENT '{' { membro_struct } '}'
| 'union' IDENT
3.3.3 Enum
especificador_enum ::= 'enum' IDENT '{' lista_enumeradores '}'
| 'enum' IDENT
lista_enumeradores ::= enumerador { ',' enumerador }
enumerador ::= IDENT
| IDENT '=' expressao_constante
3.3.4 Typedef
declaracao_typedef ::= 'typedef' tipo IDENT ';'
| 'typedef' especificador_struct IDENT ';'
| 'typedef' especificador_union IDENT ';'
| 'typedef' especificador_enum IDENT ';'
3.4 Expressões
3.4.1 Operadores Unários
expressao_unaria ::= '!' expressao_unaria
| '~' expressao_unaria
| '-' expressao_unaria
| '+' expressao_unaria
| '++' IDENT
| '--' IDENT
| 'sizeof' '(' tipo ')'
| 'sizeof' '(' IDENT ')'
3.4.2 Operadores Bit a Bit e Deslocamento
expressao_bit ::= expressao '&' expressao
| expressao '|' expressao
| expressao '^' expressao
| '~' expressao
expressao_deslocamento ::= expressao '<<' expressao
| expressao '>>' expressao
3.4.3 Operador Ternário
expressao_ternaria ::= expressao_booleana '?' expressao ':' expressao
3.4.4 Acesso a Membros
acesso_membro ::= IDENT '.' IDENT
| IDENT '->' IDENT
3.5 Tabela de Precedência Completa
Expansão da tabela atual incorporando todos os operadores da linguagem alvo, do menor para o maior nível:
| Nível | Operadores | Associatividade |
|---|---|---|
| 1 | \|\| |
Esquerda |
| 2 | && |
Esquerda |
| 3 | \| |
Esquerda |
| 4 | ^ |
Esquerda |
| 5 | & |
Esquerda |
| 6 | == != |
Esquerda |
| 7 | < > <= >= |
Esquerda |
| 8 | << >> |
Esquerda |
| 9 | + - |
Esquerda |
| 10 | * / % |
Esquerda |
| 11 | ! ~ - + (unários) |
Direita |
| 12 | = += -= *= /= %= |
Direita |
| 13 | ? : (ternário) |
Direita |
| 14 | else (via LOWER_THAN_ELSE / KW_ELSE) |
Não-associativo |