Análise Sintáctica em C
Para o C existem duas ferramentas que possibilitam a construção de
analisadores sintácticos com alguma facilidade: o gerador de
analisadores lexicográficos (tokenisers) flex e o
gerador de analisadores sintácticos (parsers) bison.
A entrada da ambas as aplicações são ficheiros com três secções
separadas por linhas com os símbolos %%:
%{
declarações em C
}%
definições/declarações (a usar na secção seguinte)
%%
regras de reconhecimento dos tokens ou produções da
gramática
%%
código C
Definição do analisador lexicográfico
Consideremos um pequeno exemplo para somas de inteiros. Para o
flex teremos o ficheiro calculadora.lex com o seguinte
conteúdo:
%{
#include "calculadora.tab.h" /* ficheiro produzido pelo bison */
%}
%%
[0-9]+ yylval = atoi(yytext); return INTEIRO;
"+" return '+';
[ \t] ; /* ignora espaços e tabs (\t) */
"\n" return FIM_LINHA;
Observações:
- [0-9]+ é uma expressão regular que representa todas as
sequências não vazias de caracteres no intervalo de '0' a
'9' (i.e., números naturais)
- "+" representa a palavra com o carácter '+'
(que tem de estar entre aspas devido a ser um operador para as
expressões regulares.
- A variável yytext guarda o texto correspondente ao
símbolo terminal (token) corrente.
- A variável yylval serve para passar o valor de um
símbolo terminal (token) ao analisador sintáctico. (Para a
calculadora basta passar valores inteiros.)
- A constante INTEIRO corresponde a um símbolo terminal
(token) e será definida pelo bison. Essa definição
estará no ficheiro calculadora.tab.h, sendo essa a razão
para fazer #include dele.
- A última secção pode omitir-se.
Definição do analisador sintáctico
Para o bison teremos o ficheiro calculadora.y com:
%{
#include <stdio.h>
void yyerror(char *); /* ver abaixo */
%}
%token INTEIRO
%token FIM_LINHA
%start linha
%%
linha: expressao FIM_LINHA { printf("valor: %d\n", $1); }
;
expressao: expressao '+' termo { $$ = $1 + $3; }
| termo { $$ = $1; }
;
termo: INTEIRO { $$ = $1; }
;
%%
int main(int argc, char **argv)
{
return yyparse();
}
/* função usada pelo bison para dar mensagens de erro */
void yyerror(char *msg)
{
fprintf(stderr, "erro: %s\n", msg);
}
Observações:
- %token INTEIRO declara a constante INTEIRO,
para poder ser usada no corpo das produções e para a comunicação
entre o analisador lexicográfico e o analisador sintáctico.
- %start linha diz que linha é o símbolo inicial
da gramática.
- Nas produções, as chavetas delimitam as acções semânticas da
gramática (consistindo em código C).
- $n refere-se ao valor (inteiro, neste caso) do
n-ésimo símbolo da produção (incluindo as acções semânticas).
$$ é o valor da cabeça da produção.
- A barra vertical | pode ser usada para separar as
produções de um não terminal, que são terminadas por ponto e
vírgula.
- Na última produção, $1 contém o valor do número lido por
se ter feito yylval = atoi(yytext).
- A função yyparse realiza a análise sintáctica e devolve 0
em caso de sucesso, e um inteiro diferente de 0 se falhar.
Compilação
O executável calculadora é criado através da sequência de
comandos
flex calculadora.lex # cria o ficheiro lex.yy.c
bison -d calculadora.y # cria os ficheiros calculadora.tab.{c,h}
gcc -o calculadora calculadora.tab.c lex.yy.c -lfl
Mais informação sobre estas ferramentas pode ser obtida executando os
comandos info flex e info bison ou nos manuais
online do flex
e do bison.