Skip to content

1. Integração Léxico-Sintático

1.1 Introdução

O documento de integração descreve como os tokens produzidos pelo analisador léxico serão consumidos pelo analisador sintático (parser). Essa integração é o ponto de contato entre as duas primeiras fases do compilador e define o contrato formal pelo qual o léxico entrega informações estruturadas ao parser.

No modelo adotado por este projeto, o léxico é implementado com Flex e o parser será implementado com Bison — ferramentas que se comunicam por meio de uma interface padronizada: a função yylex(), a variável global yylval e o arquivo de cabeçalho gerado pelo Bison (parser.tab.h).


2. Tabela Completa de Tokens

A tabela a seguir lista todos os tokens reconhecidos pelo analisador léxico (scanner.l), seus códigos numéricos (iniciando em 258, padrão Flex/Bison), os padrões que os originam e o papel semântico que cada um cumpre no parser.

2.1 Palavras-Chave (KW_*)

Token Código Lexema Papel no Parser
KW_AUTO 258 auto Especificador de classe de armazenamento
KW_BREAK 259 break Instrução de desvio
KW_CASE 260 case Rótulo de switch
KW_CHAR 261 char Especificador de tipo
KW_CONST 262 const Qualificador de tipo
KW_CONTINUE 263 continue Instrução de desvio
KW_DEFAULT 264 default Rótulo padrão de switch
KW_DO 265 do Estrutura de repetição
KW_DOUBLE 266 double Especificador de tipo
KW_ELSE 267 else Ramificação condicional
KW_ENUM 268 enum Declaração de enumeração
KW_EXTERN 269 extern Especificador de armazenamento
KW_FLOAT 270 float Especificador de tipo
KW_FOR 271 for Estrutura de repetição
KW_GOTO 272 goto Instrução de desvio incondicional
KW_IF 273 if Estrutura condicional
KW_INT 274 int Especificador de tipo
KW_LONG 275 long Modificador de tipo
KW_REGISTER 276 register Especificador de armazenamento
KW_RETURN 277 return Instrução de retorno
KW_SHORT 278 short Modificador de tipo
KW_SIGNED 279 signed Modificador de tipo
KW_SIZEOF 280 sizeof Operador de tamanho
KW_STATIC 281 static Especificador de armazenamento
KW_STRUCT 282 struct Declaração de estrutura
KW_SWITCH 283 switch Estrutura de seleção
KW_TYPEDEF 284 typedef Criação de alias de tipo
KW_UNION 285 union Declaração de união
KW_UNSIGNED 286 unsigned Modificador de tipo
KW_VOID 287 void Especificador de tipo/retorno
KW_VOLATILE 288 volatile Qualificador de tipo
KW_WHILE 289 while Estrutura de repetição

2.2 Identificadores e Literais

Token Código Padrão Carrega em yylval
IDENT 290 [a-zA-Z_][a-zA-Z0-9_]* str — cópia de yytext
NUMBER 291 [0-9]+(\.[0-9]+)? str — número preservado como texto

2.3 Operadores de Dois Caracteres (TK_OP_*)

Token Código Lexema Papel no Parser
TK_OP_IGUAL_COMPARACAO 292 == Operador relacional de igualdade
TK_OP_DIFERENTE 293 != Operador relacional de diferença
TK_OP_MENOR_IGUAL 294 <= Operador relacional
TK_OP_MAIOR_IGUAL 295 >= Operador relacional
TK_OP_AND 296 && Operador lógico E
TK_OP_OR 297 \|\| Operador lógico OU
TK_OP_INCREMENTO 298 ++ Operador unário de incremento
TK_OP_DECREMENTO 299 -- Operador unário de decremento
TK_OP_MAIS_IGUAL 300 += Atribuição composta
TK_OP_MENOS_IGUAL 301 -= Atribuição composta
TK_OP_MULTIPLICACAO_IGUAL 302 *= Atribuição composta
TK_OP_DIVISAO_IGUAL 303 /= Atribuição composta
TK_OP_MODULO_IGUAL 304 %= Atribuição composta
TK_OP_DESLOCAMENTO_ESQUERDA 305 << Deslocamento de bits à esquerda
TK_OP_DESLOCAMENTO_DIREITA 306 >> Deslocamento de bits à direita
TK_OP_PONTEIRO_ACESSO 307 -> Acesso a membro via ponteiro

2.4 Operadores de Um Caractere e Delimitadores

Token Código Lexema Papel no Parser
TK_OP_SOMA 308 + Operador aritmético / unário
TK_OP_SUBTRACAO 309 - Operador aritmético / unário
TK_OP_MULTIPLICACAO 310 * Operador aritmético / ponteiro
TK_OP_DIVISAO 311 / Operador aritmético
TK_OP_MODULO 312 % Operador aritmético
TK_OP_IGUAL 313 = Operador de atribuição
TK_OP_MENOR 314 < Operador relacional
TK_OP_MAIOR 315 > Operador relacional
TK_OP_NAO 316 ! Operador lógico de negação
TK_OP_E_BIT 317 & Bit a bit E / endereço
TK_OP_OU_BIT 318 \| Bit a bit OU
TK_OP_XOR_BIT 319 ^ Bit a bit XOR
TK_OP_NOT_BIT 320 ~ Bit a bit complemento
TK_OP_TERNARIO 321 ? Operador ternário (parte 1)
TK_OP_DOIS_PONTOS 322 : Operador ternário / rótulo
TK_OP_PONTO 323 . Acesso a membro de struct
TK_OP_VIRGULA 324 , Separador de argumentos
TK_OP_PONTO_VIRGULA 325 ; Terminador de instrução
TK_ABRE_PARENTESE 326 ( Agrupamento / chamada
TK_FECHA_PARENTESE 327 ) Agrupamento / chamada
TK_ABRE_CHAVE 328 { Início de bloco
TK_FECHA_CHAVE 329 } Fim de bloco
TK_ABRE_COLCHETE 330 [ Acesso a array
TK_FECHA_COLCHETE 331 ] Acesso a array

3. A Interface yylex() — Como o Parser Consome Tokens

No modelo Flex/Bison, o parser não lê o código-fonte diretamente. Em vez disso, ele invoca repetidamente a função yylex(), gerada pelo Flex, que:

  1. Avança o cursor de leitura sobre o arquivo-fonte;
  2. Reconhece o próximo lexema com base nas regras do scanner.l;
  3. Preenche a variável global yylval com o valor semântico do token;
  4. Retorna o código numérico do token reconhecido.

O fluxo pode ser representado da seguinte forma:

Fase / Componente Ação Principal O que é transmitido?
1. Arquivo .c Fornece o código-fonte bruto. Fluxo de caracteres (texto).
2. yylex() (Léxico) Lê o texto, identifica padrões e gera tokens. Código do Token: ex: IDENT = 290
Valor Semântico: ex: yylval.str = "total"
3. Parser (Sintático) Consome a saída do léxico para validar a gramática. Decisão: Usa o código numérico.
Árvore (AST): Usa o conteúdo de yylval.

4. Estrutura de Valores — yylval

A variável yylval é o mecanismo pelo qual o léxico transmite o valor semântico de cada token ao parser. Ela é declarada como uma union no arquivo Bison, de modo que cada campo corresponde a um tipo de dado que um token pode carregar.

4.1 Declaração da Union

%union {
    char *str;
    NoAST *no;
}

4.2 Associação Token → Campo da Union

Token Campo usado Como é preenchido no scanner
IDENT str yylval.str = copiar_yytext(yytext);
NUMBER str yylval.str = copiar_yytext(yytext);
Palavras-chave e operadores Não carregam valor; o código do token já é suficiente

4.3 Implementação Atual do Scanner

As regras de IDENT e NUMBER já estão integradas ao Bison. O léxico preserva o lexema como texto para que a AST e a análise semântica decidam depois como interpretar o valor.

[a-zA-Z_][a-zA-Z0-9_]* {
    yylval.str = copiar_yytext(yytext);
    return IDENT;
}

[0-9]+(\\.[0-9]+)? {
    yylval.str = copiar_yytext(yytext);
    return NUMBER;
}

Observação: copiar_yytext aloca uma cópia do lexema na heap. O parser libera essa memória após criar o nó da AST correspondente.


5. Exemplos de Entrada e Fluxo de Tokens

Os exemplos a seguir são extraídos diretamente dos testes automatizados do projeto (scanner/inputs/), garantindo correspondência exata com o comportamento real do scanner.l.


5.1 Identificadores e Literais Numéricos (02_identifiers_numbers.c)

Entrada:

_tmp var1 intx 0 42 3.14 10.

Sequência de tokens produzida:

Lexema Token yylval
_tmp IDENT (290) str = "_tmp"
var1 IDENT (290) str = "var1"
intx IDENT (290) str = "intx"
0 NUMBER (291) str = "0"
42 NUMBER (291) str = "42"
3.14 NUMBER (291) str = "3.14"
10. NUMBER (291) str = "10" + TK_OP_PONTO

Nota: O lexema intx é reconhecido como IDENT e não como a palavra-chave int, pois o padrão de keywords exige correspondência exata do lexema completo. O caso 10. demonstra que o ponto flutuante exige ao menos um dígito após o .; o scanner tokeniza como NUMBER(10) seguido de TK_OP_PONTO.


5.2 Comentários e Espaços em Branco (05_comments_whitespace.c)

Entrada:

// this line is a comment
int value /* ignored block comment */ = 12;

Sequência de tokens produzida:

Lexema Token yylval
// this line is a comment (descartado)
int KW_INT (274)
value IDENT (290) str = "value"
/* ignored block comment */ (descartado)
= TK_OP_IGUAL (313)
12 NUMBER (291) str = "12"
; TK_OP_PONTO_VIRGULA (325)

Nota: Comentários de linha (//) e de bloco (/* */) são consumidos pelo léxico mas não geram nenhum token para o parser. O espaço em branco entre tokens também é descartado silenciosamente.


5.3 Trecho Misto — Programa Completo (07_mixed_snippet.c)

Este caso de teste é o mais abrangente da suíte, combinando declaração de função, declaração de variável com inicialização, atribuição composta, estrutura condicional e operadores de dois caracteres.

Entrada:

int main() {
    int a = 10;
    a += 2;
    if (a >= 12 && a != 0) {
        a--;
    }
}

Sequência de tokens produzida:

Lexema Token yylval
int KW_INT (274)
main IDENT (290) str = "main"
( TK_ABRE_PARENTESE (326)
) TK_FECHA_PARENTESE (327)
{ TK_ABRE_CHAVE (328)
int KW_INT (274)
a IDENT (290) str = "a"
= TK_OP_IGUAL (313)
10 NUMBER (291) str = "10"
; TK_OP_PONTO_VIRGULA (325)
a IDENT (290) str = "a"
+= TK_OP_MAIS_IGUAL (300)
2 NUMBER (291) str = "2"
; TK_OP_PONTO_VIRGULA (325)
if KW_IF (273)
( TK_ABRE_PARENTESE (326)
a IDENT (290) str = "a"
>= TK_OP_MAIOR_IGUAL (295)
12 NUMBER (291) str = "12"
&& TK_OP_AND (296)
a IDENT (290) str = "a"
!= TK_OP_DIFERENTE (293)
0 NUMBER (291) str = "0"
) TK_FECHA_PARENTESE (327)
{ TK_ABRE_CHAVE (328)
a IDENT (290) str = "a"
-- TK_OP_DECREMENTO (299)
; TK_OP_PONTO_VIRGULA (325)
} TK_FECHA_CHAVE (329)
} TK_FECHA_CHAVE (329)

6. Tokens Ignorados pelo Parser

Os padrões a seguir são consumidos pelo léxico mas não geram tokens para o parser, pois não possuem relevância gramatical:

Padrão Tratamento
[ \\t\\n]+ Espaços, tabulações e quebras de linha — descartados silenciosamente
// ... Comentário de linha — descartado
/* ... */ Comentário de bloco — descartado
. (caractere desconhecido) Emite UNKNOWN no terminal; não retorna token válido ao parser