Un mini langage de script pour une application
dimanche 1er février 2004, par Bech ()
Il y a quelque temps, j’avais besoin de permettre à l’utilisateur d’un logiciel de définir des critères pour le fonctionnement d’un questionnaire dans une application. L’utilisateur devait pouvoir définir l’équivalent d’un test booléen à partir de données issus d’une base de données.
J’ai donc utilisé Lex et Yacc pour créer un mini-langage intégré et interprété dans mon application.
Définition du langage / grammaire
Le langage doit permettre d’écrire facilement des test booléen. Pour cela, on utilise des mots simples :
Et,
Ou,
Vrai,
Faux,
Non,
Champ.
La syntaxe de champ est : champ[nom du champ]
Exemple de phrase :
Champ[TOTO] = "10"
(Champ[age] > 20) et (champ[age] < 40)
...
L’analyseur obtenu grâce à lex permet d’identifier les mots clefs et le type des données (chaîne alpha ou nombre).
Yacc permet quand à lui de vérifier la constitution des phrases et, tout en vérifiant la syntaxe, d’effectuer le test booléen.
Définition pour lex
[...]
[a-zA-Z]([a-zA-Z0-9_])* Begin
If est_mot_cle(yytext, m_cle) then
return(m_cle)
else
return(_Identificateur)
end;
[0-9]+("."[0-9]*)? begin
val(yytext, yylval.yyreal, Nbstatus);
if nbstatus = 0 then
return(_C_NUMERIQUE)
else yyerror('Nombre mal formé');
end;
\"([^"]|"")+\" return(_C_ALPHANUMERIQUE);
"=" return(_EGAL);
">="|"=>" return(_SUPERIEUR_EGAL);
"<="|"=<" return(_INFERIEUR_EGAL);
"<>"|"!=" return(_DIFFERENT);
[:,.;=>\[\]()<>\-+*/%\^] returnc(yytext[1]);
[ \n\t\f] ;
Définition pour Yacc
[...]
Expression_cond : Expression_Bool {Resultat := $1}
;
Expression_Bool :
_NON Expression_Bool
{
if $2 = False then
$$ := True
else
$$ := False;
} %prec _NON
| Expression_Bool _ET Expression_Bool
{
if (($1 = True) and ($3 = True)) then
$$ := True
else
$$ := False;
}
| Expression_Bool _OU Expression_Bool
{
if (($1 = True) or ($3 = True)) then
$$ := True
else
$$ := False;
}
| Expression_Bool _EGAL Expression_Bool
{
if ((($3 = True) and ($1 = True)) or (($3 = False) and ($1 = False))) then
$$ := True
else
$$ := False;
}
| '(' Expression_Bool ')' {$$ := $2}
| _VRAI {$$ := True}
| _FAUX {$$ := False}
| Champ _EGAL Chaine {$$:=Test_Valeur_ChampCh($1,_EGAL,$3);}
| Champ _SUPERIEUR_EGAL Chaine {$$:=Test_Valeur_ChampCh($1,_SUPERIEUR_EGAL,$3);}
| Champ _INFERIEUR_EGAL Chaine {$$:=Test_Valeur_ChampCh($1,_INFERIEUR_EGAL,$3);}
| Champ _DIFFERENT Chaine {$$:=Test_Valeur_ChampCh($1,_DIFFERENT,$3);}
| Champ _EGAL Nombre {$$:=Test_Valeur_Champ($1,_EGAL,$3);}
| Champ _SUPERIEUR_EGAL Nombre {$$:=Test_Valeur_Champ($1,_SUPERIEUR_EGAL,$3);}
| Champ _INFERIEUR_EGAL Nombre {$$:=Test_Valeur_Champ($1,_INFERIEUR_EGAL,$3);}
| Champ _DIFFERENT Nombre {$$:=Test_Valeur_Champ($1,_DIFFERENT,$3);}
;
Champ : _VAL_CHAMP '[' _Identificateur ']' {$$ := $3}
;
Chaine : _C_ALPHANUMERIQUE {$$ := yytext}
;
Nombre : _C_NUMERIQUE {$$ := yytext;}
[...]
Le programme de test
L’avantage du code obtenu avec lex et yacc est qu’il est utilisable dans une procédure ou un objet assez facilement. Le code de lex est encapsulé dans celui de yacc, celui de yacc est, dans l’exemple, encapsulé dans une fonction de la fenêtre principale. En fait, ce sont des fichiers inclus dans le programme.