Bonjour,
je débute avec Flex/Bison.
J'ai utilisé le tuto sur Lex/Yacc suivant
http://ds9a.nl/lex-yacc/cvs/lex-yacc-howto.html
Mon programme connait désormais le langage suivant
heat on
-> heat is on !
heat off
-> heat is off !
get heat
-> heat is off !
set temperature 10
-> temperature set to 10
get temperature
-> temperature is 10
voir le code sur
Accès web
http://svn.berlios.de/viewcvs/openphysic/compilation/2_yacc/(...)
Accès anonyme
svn checkout svn://svn.berlios.de/openphysic/compilation/2_yacc/thermostat3/
Rem : il faudra que j'améliore le programme pour pouvoir définir des températures en nombre à virgule flottante au lieu d'être en entier
Le problème c'est que j'obtiens un programme qui parse les informations envoyées sur stdin... je sais que l'on peut modifier ce comportement
il suffit de redéfinir la variable yyin juste avant d'appeler yyparse
mais moi je ne veux pas parser les informations en provenance d'un fichier...
je veux juste parser les informations stockées dans un char*
Comment procéder ?
Merci d'avance
# Bourrin
Posté par Octabrain . Évalué à 1.
Créer un pipe(2) (man 2 pipe), passer une extrémité à bison, et dans l'autre, écrire ta chaine de caractères.
[^] # Re: Bourrin
Posté par scls19fr (site web personnel) . Évalué à 2.
donc j'imagine que c'est pas gagné ! (il n'y a pas d'OS...)
# YY_INPUT
Posté par Étienne . Évalué à 1.
Tu dois pouvoir faire quelque comme ceci :
%{
#under YY_INPUT
#define YY_INPUT(b,r,ms) (r = my_yyinput(b, ms))
%}
int my_yyinput(char* buf, int max_size)
{
//on peut copier dans buf au maximum max_size octets et renvoyer le nombre d'octets copiés
}
Étienne
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
peux-tu préciser... on va dire que la chaine à parser est
stockée dans une variable globale char* g_str;
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
http://flex.sourceforge.net/manual/Multiple-Input-Buffers.ht(...)
mais ça ne m'avance guère
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
extern char* g_str;
extern size_t g_str_size; /* taille de g_str */
extern int g_str_read; /* nombre de caractère déjà envoyés à lex, initializé au départ à 0 */
int my_yyinput(char* buf, int max_size)
{
/* on calcule le nombre de caractères à copier dans buf,
* soit on a assez de caractères pour remplir, buf, soit on prend le reste de la chaine
*/
int num = min(max_size, g_str_size-g_str_read);
/* on copie notre chaine dans buf */
if (num > 0)
{
memcpy(buf, g_str+g_str_read, num);
g_str_read += num;
}
return num;
}
La macro YY_INPUT est appelée par flex pour reremplir le buffer interne sur lequel il travail. Ce qui permet d'optimiser la lecture en ne lisant pas caractère par caractère comme c'était le cas de lex d'AT&T. Donc ta fonction my_yyinput va être appelée à chaque fois que le buffer interne de flex est vide et tu lui donne la suite de ta chaîne.
Étienne
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
En particulier les lignes 90 à 108 et la fonction sun_map_input ligne 205 qui fait exactement ce que tu cherche.
Étienne
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
par contre on ne peux pas simplement avoir une lecture caractère par caractère depuis la chaîne de caractère dans mon cas ça me semble plus adapté. non ?
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
Étienne
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
main.l
%{
#include <string.h>
#include "main.tab.h"
#undef YY_INPUT
#define YY_INPUT(b,r,ms) (r = my_yyinput(b, ms))
extern char* g_str;
extern size_t g_str_size; /* taille de g_str */
extern int g_str_read; /* nombre de caractère déjà envoyés à lex, initializé au départ à 0 */
int my_yyinput(char* buf, int max_size)
{
/* on calcule le nombre de caractères à copier dans buf,
* soit on a assez de caractères pour remplir, buf, soit on prend le reste de la chaine
*/
int num = fmin(max_size, g_str_size-g_str_read);
/* on copie notre chaine dans buf */
if (num > 0)
{
memcpy(buf, g_str+g_str_read, num);
g_str_read += num;
}
return num;
}
%}
%option case-insensitive
INTEGER [0-9]+
FLOATING [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
%%
{INTEGER} {yylval=atoi(yytext); return TOK_NUMBER;}
heat return TOK_HEAT;
on|off {yylval=!strcmp(yytext,"on"); return TOK_STATE;}
set return TOK_SET;
temperature return TOK_TEMPERATURE;
get return TOK_GET;
\n return TOK_EOL; /* end of line */;
[ \t]+ /* ignore whitespace */;
%%
main.y
%{
#include <stdio.h>
#include <string.h>
#define STR_SIZE 50
char g_str[STR_SIZE];
/*char* g_str;*/
size_t g_str_size; /* taille de g_str */
int g_str_read; /* nombre de caractère déjà envoyés à lex, initializé au départ à 0 */
/*
#define STR_SIZE 50
char str[STR_SIZE];
int myinput(char *buf, int max){
memcpy(buf, str, STR_SIZE);
}
*/
/*
int n = min(max, gStringLimit-gStringPtr);
if(n>0){
memcpy(buf, gStringPtr, n);
gStringPtr +=n;
}
return n;
}
*/
int value; /* temperature value */
enum _state {
off,
on
};
enum _state state;
void show_heat_state() {
if (state==off) {
printf("\tHeat is off\n");
} else {
printf("\tHeat is on\n");
}
}
%}
%token TOK_NUMBER TOK_HEAT TOK_STATE TOK_SET TOK_TEMPERATURE TOK_GET TOK_EOL
%%
commands: /* empty */
| commands command TOK_EOL
;
command:
heat_switch
|
heat_get
|
target_set
|
target_get
;
heat_switch:
TOK_HEAT TOK_STATE
{
if ($2==0) {
state=off;
} else if ($2==1) {
state=on;
}
show_heat_state();
}
;
heat_get:
TOK_GET TOK_HEAT
{
show_heat_state();
}
;
target_set:
TOK_SET TOK_TEMPERATURE TOK_NUMBER
{
value=$3;
printf("\tTemperature set to %d\n",value);
}
;
target_get:
TOK_GET TOK_TEMPERATURE
{
printf("\tTemperature is %d\n",value);
}
;
%%
void yyerror(const char *str) {
fprintf(stderr,"yyerror: %s\n",str);
}
int yywrap() {
return 1;
}
int main(void) {
strcpy(g_str,"get temperature");
/* printf(g_str); */
value=0;
state=off;
yyparse();
return 0;
}
Makefile
all:
flex main.l
bison -d main.y
gcc lex.yy.c main.tab.c -lfl -o main
./main
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
Sinon dans ton programme, j'ai l'impression que tu n'initialise jamais g_str_size et g_str_read, le mieux est sans doute d'avoir une fonction set_parse_string() qui va positionner correctement g_str, g_str_size et g_str_read.
%{
#include <string.h>
#undef YY_INPUT
#define YY_INPUT(b,r,ms) { r = my_yyinput(b,ms); }
char g_str[] = "toto tata tutu\n";
size_t g_str_size = sizeof(g_str);
int g_str_read = 0;
int my_yyinput(char* buf, int max_size)
{
int reste = g_str_size-g_str_read;
int num = (max_size < reste ? max_size : reste);
if (num > 0)
{
memcpy(buf, g_str+g_str_read, num);
g_str_read += num;
}
return num;
}
%}
%%
%.|\n ECHO;
%%
Étienne
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
- je ne pense pas que tu puisse faire un "extern char*" et déclarer un "char[]", il faut transformer dans main.y ton g_str en char*
- tu ne calcule pas g_str_size et g_str_read
- il me semble que ton fichier doit finir par une fin de ligne (cf commands dans main.y), et donc soit tu supprime le TOK_EOL de la fin de commands, soit tu rajoute un \n à la fin de ta chaine de caractères.
Étienne
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
ok pour le \n .... je n'y avais vraiment pas pensé !!!!!
sinon je crois qu'il existe une fonction nommé yy_scan_string...
ne peut-elle pas servir à cet usage ?
http://flex.sourceforge.net/manual/Multiple-Input-Buffers.ht(...)
Function: YY_BUFFER_STATE yy_scan_buffer (char *base, yy_size_t size)
which scans in place the buffer starting at base, consisting of size bytes, the last two bytes of which must be YY_END_OF_BUFFER_CHAR (ASCII NUL). These last two bytes are not scanned; thus, scanning consists of base[0] through base[size-2], inclusive.
If you fail to set up base in this manner (i.e., forget the final two YY_END_OF_BUFFER_CHAR bytes), then yy_scan_buffer() returns a NULL pointer instead of creating a new input buffer.
[^] # Re: YY_INPUT
Posté par Étienne . Évalué à 1.
si je mets un char* j'ai un bus error lorsque j'initialise ma chaine de caractère !
Il faut quand même allouer ton buffer.
Je te propose de faire quelquechose du genre (dans main.y) :
char* g_str = NULL;
size_t g_str_size;
size_t g_str_read;
int set_parse_string(char* str)
{
size_t length = strlen(str);
if (g_str != NULL) free(g_str);
g_str = (char*) malloc(length+1);
if (g_str == NULL) return 0;
strcpy(g_str, str);
g_str_size = length;
g_str_read = 0;
return 1;
}
et dans ton main :
int main(void) {
value = 0;
state = off;
/* première suite de commandes à parser (ici il n'y en a qu'une) */
set_parse_string("set temperature 12");
yyparse();
/* deuxième suite de commandes à parser */
set_parse_string("get temperature heat");
yyparse();
return 0;
}
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
Ah ce fichu malloc/free !!! je sais pourquoi j'avais abandonné le C pendant
quelques années pour me mettre à Python ! par contre Python sur un uC 8 bits ça doit pas le faire ;-)
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 1.
Il faut juste que je gère le problème des températures en nombre à virgule flottante
[^] # Re: YY_INPUT
Posté par scls19fr (site web personnel) . Évalué à 2.
Il y a juste besoin d'utiliser
yy_scan_string("*IDN?\n");
yyparse();
et il est nullement nécessaire de redéfinir la macro YY_INPUT !!!
main.l
%{
#include <string.h>
#include "main.tab.h"
%}
%option case-insensitive
INTEGER [0-9]+
FLOATING [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
%%
{INTEGER} {yylval=atoi(yytext); return TOK_NUMBER;}
"*IDN?" return TOK_IDENT;
heat return TOK_HEAT;
on|off {yylval=!strcmp(yytext,"on"); return TOK_STATE;}
set return TOK_SET;
temperature return TOK_TEMPERATURE;
get return TOK_GET;
\n return TOK_EOL; /* end of line */;
[ \t]+ /* ignore whitespace */;
%%
main.y
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int value; /* temperature value */
enum _state {
off,
on
};
enum _state state;
void show_heat_state() {
if (state==off) {
printf("\tHeat is off\n");
} else {
printf("\tHeat is on\n");
}
}
/* Si l'on compile avec l'option --verbose et yydebug mis a 1, permet
d'obtenir le fichier data.output, qui contient la liste des
differents states possibles pour la machine d'etat. */
int yydebug=1;
%}
%token TOK_IDENT TOK_NUMBER TOK_HEAT TOK_STATE TOK_SET TOK_TEMPERATURE TOK_GET TOK_EOL
/*
%union
{
double dbl;
int integer;
}
*/
%%
commands: /* empty */
| commands command TOK_EOL
;
command:
identification
| heat_switch
| heat_get
| target_set
| target_get
;
identification:
TOK_IDENT
{
printf("\tMyDevice\n",value);
}
;
heat_switch:
TOK_HEAT TOK_STATE
{
if ($2==0) {
state=off;
} else if ($2==1) {
state=on;
}
show_heat_state();
}
;
heat_get:
TOK_GET TOK_HEAT
{
show_heat_state();
}
;
target_set:
TOK_SET TOK_TEMPERATURE TOK_NUMBER
{
value=$3;
printf("\tTemperature set to %d\n",value);
}
;
target_get:
TOK_GET TOK_TEMPERATURE
{
printf("\tTemperature is %d\n",value);
}
;
%%
void yyerror(const char *str) {
fprintf(stderr,"yyerror: %s\n",str);
}
int yywrap() {
return 1;
}
int main(void) {
value=0;
state=off;
/* yyparse(); */ /* parser les commandes envoyées sur stdin */
/* suite de commandes à parser */
yy_scan_string("*idn?\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("*IDN?\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("heat on\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("get heat\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("heat off\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("get heat\n");
yyparse();
/* suite de commandes à parser */
yy_scan_string("set temperature 21\n");
yyparse();
/* suite commandes à parser */
yy_scan_string("get temperature\n");
yyparse();
return 0;
}
Makefile
all:
flex main.l
bison -d --verbose main.y
gcc lex.yy.c main.tab.c -lfl -o main
./main
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.