Criando um parser reentrant (pure parser) e brincando com actions

Olá!
Mais uma vez brincando com criação de parser, depois de apanhar bastante para fazer o Flex usar yylval, notei que o meu problema era atualizar a versão do mesmo! :D
Dessa vez trabalhei com actions, que são os códigos entre colchetes nas regras do parser (no arquivo .y) e atribuindo valores ao tokens, que é usando a tal yylval, sendo ela, do tipo de uma estrutura que criei.

Enfim, para demonstração criei um operador chamado SQRT, que retorna a raiz quadrada do operando fornecido. Aceitando inclusive recursividade, veja abaixo:

Arquivo usado pelo Flex para gerar o tokenizer:

%{
#include "common.h"
#include "parser.h"
%}
 
%option bison-bridge
%option case-insensitive
 
%%
 
<<EOF>> {
	return 0;
}
 
"SQRT" {
	return OP_SQRT;
}
 
[a-zA-Z]+ {
	yylval->nome = strdup(yytext);
	return VAR;
}
 
[0-9]+ {
	yylval->valor = atoi(yytext);
	return NUM; 
}
 
[ \n\t]+ { 
	; /* Ignorar */
}
 
[()=] {
	return yytext[0];
}
 
%%
 
int main(int argc, char** argv)
{
	YY_BUFFER_STATE buffer;
 
	if (argc < 2) {
		return 0;
	}
 
	buffer = yy_scan_string(argv[1]);
 
	yyparse();
 
	yy_delete_buffer(buffer);	
 
	return 0;
}

Note que acima utilizei yy_scan_string() para passar uma string diretamente como entrada, ao invés de usar stdin, como fiz em outro post, ou passando um FILE * para yyin.
Repare também que aqui yylval é um variavel*. Por que no common.h eu defino a YYSTYPE.

Arquivo usado pelo Bison:

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
%}
 
%error-verbose
%pure_parser
 
%destructor { free($$.nome); } VAR
 
%token NUM
%token VAR
%token OP_SQRT
 
%%
 
expr:
		/* empty */
	|	expr VAR '=' expr_sqrt { mostrar_atribuicao(&$2, &$4); }
;
 
expr_sqrt:
		OP_SQRT sqrt { $$ = $2; }
	|	OP_SQRT '(' expr_sqrt ')' { $$ = $3; raiz_quadrada(&$$); }
;
 
sqrt:
		NUM { $$ = $1; raiz_quadrada(&$$); }
;
 
%%

O %destrutor é interessante, o código definido será executado quando um símbolo for descartado. [1]

Arquivo com as funções usadas no parser:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
 
void yyerror(const char* errmsg)
{
	printf("\nErro: %s\n", errmsg);
}
 
int yywrap()
{
    return 1;
}
 
void raiz_quadrada(variavel *var)
{
	var->valor = (int) sqrt((double)var->valor);
}
 
void mostrar_atribuicao(variavel *lval, variavel *rval)
{
	printf("Variavel: '%s' | Valor: %d\n", lval->nome, rval->valor);
	free(lval->nome);	
}

Header usado nos três arquivos:

#ifndef COMMON_H
#define COMMON_H
 
typedef struct {
	char *nome;
	int valor;
} variavel;
 
#define YYSTYPE variavel
 
extern int yylex(); 
extern int yyparse(); 
extern void yyerror(const char* s); 
extern int yywrap(void);
 
 
void raiz_quadrada(variavel *var);
void mostrar_atribuicao(variavel *lval, variavel *rval);
 
#endif

E claro, nosso Makefile:

CFLAGS=-g
BISON=bison
FLEX=flex
 
parser: parser.o scanner.o funcs.o
	$(CC) $(CFLAGS) -o parser scanner.o parser.o funcs.o -lm
 
parser.c: parser.y funcs.c
	$(BISON) -d -oparser.c parser.y
 
scanner.c: scanner.l
	$(FLEX) -oscanner.c scanner.l
 
funcs.c:
	$(CC) $(CFLAGS) -o funcs funcs.c 
 
clean:
	rm -f scanner.c scanner.o parser.c parser.o parser parser.h

Testando nós teremos:

$ ./parser "a = SQRT 81"
Variavel: 'a' | Valor: 9
 
$ ./parser "a = SQRT (SQRT 81)"
Variavel: 'a' | Valor: 3
 
$ ./parser "a = SQRT (SQRT (SQRT 81))"
Variavel: 'a' | Valor: 1

Referências
- http://www.gnu.org/software/bison/manual/html_mono/bison.html#Pure-Decl
- http://flex.sourceforge.net/manual/Bison-Bridge.html#Bison-Bridge
[1] - http://www.gnu.org/software/bison/manual/html_mono/bison.html#Destructor...

Olhando assim eu consigo

Olhando assim eu consigo assimilar tudo :)

Mas só olhando heheh

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options