Programa de exemplo para os programas lex e yacc
Esta seção contém programas de exemplo para os comandos lex e yacc .
Juntos, esses programas de exemplo criam um programa simples, de calculadora de mesa que executa operações de adição, subtração, multiplicação e divisão. Este programa de calculadora também permite atribuir valores às variáveis (cada uma designada por uma letra única, minúscula) e, em seguida, utilizar as variáveis em cálculos. Os arquivos que contêm os programas de exemplo lex e yacc são os seguintes:
Arquivo | Conteúdo |
---|---|
calc.lex | Especifica o arquivo de especificação de comandos lex que define as regras de análise lexical. |
calc.yacc | Especifica o arquivo de gramática de comando yacc que define as regras de análise, e chama a subroutine yylex criada pelo comando lex para fornecer entrada. |
As descrições a seguir assumem que os programas de exemplo calc.lex e calc.yacc estão localizados em seu diretório atual.
Compilando o programa de exemplo
Para criar o programa de exemplo de calculadora de escrivaninha, faça o seguinte:
- Processe o arquivo de gramática yacc usando a sinalização opcional -d (que informa o comando yacc para criar um arquivo que define os tokens usados além do código fonte de linguagem C):
yacc -d calc.yacc
- Use o comando ls para verificar se os seguintes arquivos foram criados:
- y.tab.c
- O arquivo de origem de linguagem C que o comando yacc criou para o analisador
- y.tab.h
- Um arquivo de cabeçalho contendo instruções de definição para os tokens utilizados pelo analisador
- Processe o arquivo de especificação lex :
lex calc.lex
- Use o comando ls para verificar se o seguinte arquivo foi criado:
- lex.yy.c
- O arquivo de origem de linguagem C que o comando lex criou para o analisador lexical
- Compile e vincule os dois arquivos de origem do idioma C:
cc y.tab.c lex.yy.c
- Use o comando ls para verificar se os seguintes arquivos foram criados:
- y.tab.o
- O arquivo objeto para o arquivo de origem y.tab.c
- lex.yy.o
- O arquivo de objeto para o arquivo de origem lex.yy.c
- a.out
- O arquivo do programa executável
$ a.out
OU
$ mv a.out calculate
$ calculate
m=4 <enter>
_
m+5 <enter>
9
_
Código de origem do Parser
O exemplo a seguir mostra o conteúdo do arquivo calc.yacc Este arquivo possui entradas em todas as três seções de um arquivo de gramática yacc : declarações, regras e programas.
%{
#include<stdio.h>
int regs[26];
int base;
%}
%start list
%union { int a; }
%token DIGIT LETTER
%left '|'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%left UMINUS /*supplies precedence for unary minus */
%% /* beginning of rules section */
list: /*empty */
|
list stat '\n'
|
list error '\n'
{
yyerrok;
}
;
stat: expr
{
printf("%d\n",$1);
}
|
LETTER '=' expr
{
regs[$1.a] = $3.a;
}
;
expr: '(' expr ')'
{
$$ = $2;
}
|
expr '*' expr
{
$$.a = $1.a * $3.a;
}
|
expr '/' expr
{
$$.a = $1.a / $3.a;
}
|
expr '%' expr
{
$$.a = $1.a % $3.a;
}
|
expr '+' expr
{
$$.a = $1.a + $3.a;
}
|
expr '-' expr
{
$$.a = $1.a - $3.a;
}
|
expr '&' expr
{
$$.a = $1.a & $3.a;
}
|
expr '|' expr
{
$$.a = $1.a | $3.a;
}
|
'-' expr %prec UMINUS
{
$$.a = -$2.a;
}
|
LETTER
{
$$.a = regs[$1.a];
}
|
number
;
number: DIGIT
{
$$ = $1;
base = ($1.a==0) ? 8 : 10;
} |
number DIGIT
{
$$.a = base * $1.a + $2.a;
}
;
%%
main()
{
return(yyparse());
}
yyerror(s)
char *s;
{
fprintf(stderr, "%s\n",s);
}
yywrap()
{
return(1);
}
- Seção de Declarações. Esta seção contém entradas que:
- Incluir arquivo de cabeçalho de E/S padrão
- Definir variáveis globais
- Defina olistregra como o lugar para iniciar o processamento
- Definir os tokens utilizados pelo analisador
- Definir as operadoras e sua precedência
- Seção de regras. A seção de regras define as regras que partem o fluxo de entrada.
- %start -Especifica que a entrada inteira deve combinar com stat.
- %union -Por padrão, os valores retornados por ações e o analisador lexical são inteiros. yacc também pode suportar valores de outros tipos, incluindo estruturas. Além disso, yacc mantém a faixa dos tipos, e insere nomes de membros do sindicato apropriados para que o analisador resultante seja rigorosamente tipo verificado. A pilha de valor yacc é declarada como uma união dos vários tipos de valores desejados. O usuário declara o sindicato, e associa nomes de membros do sindicato a cada token e símbolo não terminal tendo um valor. Quando o valor é referenciado através de uma construção $$ ou $n, yacc irá inserir automaticamente o nome do sindicato apropriado, de modo que não ocorrerão conversões indesejadas.
- %type -Makes uso dos membros da declaração %union e dá um tipo individual para os valores associados a cada parte da gramática.
- %toksn -Listas os tokens que vêm da ferramenta lex com seu tipo.
- Seção de Programas. A seção de programas contém as subroutines a seguir. Como essas subroutines estão incluídas neste arquivo, você não precisa usar a biblioteca yacc ao processar este arquivo.
Sub-rotina Descrição Principa O programa principal necessário que chama a subroutine yyparse para iniciar o programa. yyerror (s) Esta subroutinha de manipulação de erros imprime apenas uma mensagem de erro de sintaxe. yywrap A subroutine de embrulho que retorna um valor de 1 quando ocorre o fim da entrada.
Código de origem do analisador Lexical
%{
#include <stdio.h>
#include "y.tab.h"
int c;
%}
%%
" " ;
[a-z] {
c = yytext[0];
yylval.a = c - 'a';
return(LETTER);
}
[0-9] {
c = yytext[0];
yylval.a = c - '0';
return(DIGIT);
}
[^a-z0-9\b] {
c = yytext[0];
return(c);
}
%%