• Aucun résultat trouvé

Les deux principaux outils utilisés pour réaliser ce traducteur sont Flex et Bison.

Flex (pour "Fast Lexical Analyzer"), réécriture de Lex [Lesk et Schmidt,1975], permet de générer des analyseurs lexicaux à partir d’un fichier de description fourni par l’utili- sateur. Un analyseur lexical permet de traduire un programme source en unités lexicales (tokens en anglais), constituées de suites de caractères représentées par des expressions régulières. Par exemple, la suite de caractères (’i’,’f’) dans le fichier source fera émettre à l’analyseur lexical le token IF.

Bison, basé sur yacc [Johnson, 1975], permet de générer un analyseur syntaxique à partir d’un fichier de description de grammaire non contextuelle LALR(1). Nous n’entre- rons pas dans les détails de l’utilisation de ce programme ni dans l’explication de ce qu’est une grammaire LALR : le lecteur est invité à lire Aho, Lam, Sethi, et Ullman [2007]. La seule chose à savoir est qu’un langage de programmation peut s’écrire sous forme de règles de production qui permettent de le décrire (ie. décrire l’ensemble des programmes valides pour ce langage) : c’est ce qu’on appelle une grammaire. Par exemple, un programme S peut être une suite d’instructions. On peut donc écrire que S −→ I IS où I est une ins- truction et IS une suite d’instructions. Ensuite I peut être par exemple une expression E donc on aura la règle de production I −→ E. E peut être un chiffre donc E −→ chif f re. chif f re est un token reconnu par l’analyseur lexical avec cette règle (sous forme d’expres- sion régulière) : ’[0-9]’. L’analyseur syntaxique permet de reconnaitre la suite des règles (tirées de la grammaire) permettant de décrire le programme analysé, et pour chaque règle reconnue, d’effectuer une action spécifiée par l’utilisateur (de Bison). Cette action peut être de n’importe quel type. Dans notre cas, il s’agit de faire les opérations nécessaires à la génération du code Java.

Le langage MATLAB a quelques particularités tant au niveau lexical qu’au niveau syntaxique qui font qu’il peut être difficile à spécifier de manière simple pour les outils précités. Afin de nous aider dans cette tâche nous nous sommes inspirés des travaux de Abhay, Joisha, et Kanhere [1999]. La documentation MATLAB est aussi un passage obligé pour rendre compte exactement de la sémantique du langage, en particulier la page défi- nissant les opérateurs et leurs priorités a été d’une grande aide [Hanselman et Littlefield,

1997].

C.3.2 Analyseur lexical pour le langage MATLAB

L’analyse lexicale d’un programme MATLAB peut poser plusieurs problèmes. Leur résolu- tion peut avoir pour conséquence que le traducteur MC et MATLAB n’ont pas exactement le même comportement dans certains cas limites, mais, cela n’a pas trop de conséquences sur l’utilisateur. Nous décortiquons ici deux problèmes nous paraissant intéressants. C.3.2.1 Le caractère ’

En MATLAB le caractère ’ peut avoir deux significations : il peut déterminer le début et la fin d’une chaîne de caractères ou bien définir l’opération de transposition :

B = ’A’ %i c i l a v a r i a b l e B c o n t i e n d r a l a c h a i n e de c a r a c t e r e s ’A’ B = A’ %i c i B c o n t i e n d r a l a t r a n s p o s e de l a m a t r i c e A

La règle qui permet de différentier ces 2 formes est la suivante : ’ sera un opérateur de transposition si, et seulement si, ce caractère suit immédiatement un entier, un flottant,

un imaginaire, un identifiant, un opérateur de transposition, une parenthèse fermante ou un crochet fermant.

Pour pouvoir repérer ce genre de situations dans Flex, une variable d’état S est dispo- nible. Il est possible d’associer à une règle permettant de reconnaître un élément lexical, un pré-requis, basé sur l’état de S : la règle ne peut alors être utilisée dans l’analyse lexicale qu’aux moments où S est dans un état attendu. Ainsi il est possible d’avoir le compor- tement voulu, si chaque fois que l’on reconnaît un entier, un flottant, un imaginaire, un identifiant, un opérateur de transposition, une parenthèse fermante ou un crochet fermant, l’on met la variable S dans un état particulier qui sera nécessaire à l’application de la règle reconnaissant l’opérateur de transposition. Si S n’est pas dans le bon état, cela signifie que le ’ rencontré commence une chaîne de caractère.

C.3.2.2 Les matrices

La syntaxe MATLAB permet de définir les matrices par une suite de valeurs entre crochets où les lignes sont séparées par un ’ ;’ ou par un retour à la ligne, et les éléments de chaque ligne par une ’,’ ou un espace. Or, en dehors des matrices, l’espace est en général ignoré. Il faut donc, si l’on est dans une matrice émettre un token associé au caractère ’ ’ alors que ce n’est pas nécessaire dans les autres cas. Pour ce faire, on utilise l’analyseur syntaxique qui, si l’on entre dans une matrice, active un drapeau qui est utilisé par l’analyseur lexical pour ignorer les espaces ou au contraire émettre un token de séparation de colonne.

Une deuxième ambiguïté en ce qui concerne la syntaxe des matrices est celle-ci : A = [ 1 , 2 , 3 ] ’

[ A, B, C ] = f u n c ( i n _ a r g s )

Dans le premier cas une matrice est définie et affectée à A, dans le deuxième cas trois valeurs de sortie d’une fonction f unc sont affectées chacune à une variable. Dans le premier cas les crochets délimitent une matrice et dans le second cas une liste d’arguments de sortie dont la sémantique est totalement différente.

Pour résoudre ce problème, on demande à l’analyseur lexical d’émettre des tokens différents pour les crochets ouvrants et fermants dans chacun des deux cas. Dans Flex, il existe une fonctionnalité permettant de gérer ces cas. En effet, pour chaque token, Flex permet non seulement de donner une expression régulière de celui-ci pour la reconnaissance, mais aussi de donner une expression régulière de ce qui suit. Ainsi, on peut différencier les deux cas en spécifiant ce qui doit suivre. Dans le second cas, le crochet ouvrant sera toujours suivi de n’importe quoi (à quelques exceptions près), d’un crochet fermant, d’un nombre quelconque d’espaces et d’un signe égal. Quant au crochet fermant, il sera suivi d’un nombre quelconque d’espaces et d’un signe égal. Cela permet de résoudre cette ambiguïté dans une majorité de cas.

C.3.3 Analyseur syntaxique

La syntaxe MATLAB étant très permissive, l’analyse syntaxique n’en est que plus compli- quée. Par ailleurs, la sémantique associée peut être elle aussi complexe à mettre en œuvre à cause de cette permissivité. Nous montrons deux cas particuliers que sont l’opération ’ :’ et l’utilisation du mot clé ’end’.

C.3.3.1 Exemple d’ambiguïté

Les expressions basées sur ’ :’ (’colon expressions’ en anglais) permettent de définir de manière synthétique des vecteurs lignes dont les éléments suivent une progression arith-

métique. Par exemple, A = 1 : 4 est équivalent à A = [1, 2, 3, 4]. De même, A = 1 : 2 : 4 produit A = [1, 3]. On remarque donc qu’il y a deux types d’expressions ’ :’ : debut : f in et debut : pas : f in. Si debut > f in un vecteur vide est produit (de taille 1 × 0 !).

Étrangement, il est aussi possible d’empiler l’opérateur ’ :’, au prix de la lisibilité. On peut par exemple écrire 2 : 5 : 10 : 10 et 2 : 5 : 10 : 3 : 10 : ’ :’ étant associatif à gauche, ces expressions reviennent à (2 : 5 : 10) : 10 et (2 : 5 : 10) : 3 : 10. Par ailleurs, si un vecteur est utilisé comme argument de cette opérateur, alors son premier élément est pris en compte. Ainsi, dans le premier cas on obtient [2, 3, 4, 5, 6, 7, 8, 9, 10] et dans le second [2, 5, 8].

On peut obtenir ce comportement en utilisant ces règles de production :

e x p r : c o l o n _ e x p r { $$ . s o u r c e = " c o l o n ("+ $1 . s t a r t +" , matrixFromDouble ( 1 ) , " + $1 . s t o p + " ) " ; } | c o l o n _ e x p r 1 { $$ . s o u r c e = " c o l o n ("+ $1 . s t a r t +","+ $1 . s t r i d e +","+ $1 . s t o p + " ) " ; } ; c o l o n _ e x p r : e x p r ’ : ’ e x p r { $$ . s t a r t = $1 . s o u r c e ; $$ . s t r i d e = $3 . s o u r c e ; $$ . s t o p = $3 . s o u r c e ; } ; c o l o n _ e x p r 1 : c o l o n _ e x p r ’ : ’ e x p r { $$ . s t a r t = $1 . s t a r t ; $$ . s t r i d e = $1 . s t r i d e ; $$ . s t o p = $3 . s o u r c e ; } ;

Dans ce code, $$ désigne l’élément à gauche dans les règles de production (−→ y étant représenté par un ’ :’). $n désigne le neélément à droite de ’ :’. Ces symboles commençant

par $ désignent des variables pouvant contenir des champs que l’on définit comme l’on veut. Le champ source contient le code généré en Java. le champ start contient la valeur de départ de l’expression colonne, stride son pas et stop sa fin. Le code Java généré fait appel à une fonction colon définie dans MCJavaCore qui génère le vecteur désiré.

Ces règles sont ambigües : l’analyseur syntaxique a deux façons d’interpréter 1 : 2 : 10 en tenant compte de l’associativité à gauche (sinon il y en aurait plus) :

expr colon_expr1 expr integer 10 : : colon_expr expr integer 2 : : expr integer 1 expr colon_expr expr integer 10 : : expr colon_expr expr integer 2 : : expr integer 1

On remarque qu’une fois le premier colon_expr réduit, on a le choix entre décaler en utilisant la règle de production de colon_expr1 (arbre de gauche) et réduire colon_expr en expr en utilisant la règle de production expr −→ colon_expr (arbre de droite). C’est ce qu’on appelle un conflit décalage/réduction. Pour résoudre ce genre de conflit, Bison favorise le décalage par défaut : ce qui dans notre cas, et dans la plupart des cas, est la bonne solution.

C.3.3.2 Cas particulier du end

Le end est un élément lexical utilisé dans deux cas. Il est d’abord utilisé pour signifier la fin d’un bloc (bloc conditionnel, boucle, fonction). Il peut aussi désigner, lorsqu’il est utilisé dans une référence, le dernier élément de la variable à laquelle on fait référence. Par exemple, on peut écrire A(5 : end − 1) qui signifie que l’on veut accéder à une sous matrice de A de son 5e à son avant-dernier élément. Pire, on peut aussi écrire A(1 : end, 4 : end).

Dans ce cas, le premier end correspond au nombre de lignes de A et le deuxième end correspond au nombre de colonnes. Différencier ces deux types end en utilisant l’analyseur lexical est assez facile, il suffit de chercher s’il est entouré de parenthèses. Par contre comment rendre la signification du deuxième end en Java ?

Pour gérer correctement ces expressions, end est d’abord placé dans le code généré tel quel. Puis au moment de la réduction d’une règle correspondant à une référence, une fonction est appliquée au code source généré de cette référence, qui a pour but de remplacer ces end par leur véritable valeur :

v o i d r e p l a c e E n d s ( c o n s t s t r i n g &var_name , i n t o u t _ r e f , s t r i n g &s o u r c e ) { i f ( o u t _ r e f == 2 ) { // r e m p l a c e r end par A. l e n t g t h e t A [ 0 ] . l e n g t h i n t pos = s o u r c e . f i n d ( " end " ) ; i f ( pos != −1){ s o u r c e . e r a s e ( pos , 3 ) ; s t r i n g r e p = " matrixFromDouble ("+var_name +". l e n g t h ) " ; s o u r c e . i n s e r t ( pos , r e p ) ; } pos = s o u r c e . f i n d ( " end " ) ; i f ( pos != −1){ s o u r c e . e r a s e ( pos , 3 ) ; s t r i n g r e p = " matrixFromDouble ("+var_name + " [ 0 ] . l e n g t h ) " ;

s o u r c e . i n s e r t ( pos , r e p ) ; } } e l s e i f ( o u t _ r e f == 1 ) { i n t pos =1; w h i l e ( pos != −1){ pos = s o u r c e . f i n d ( " end " ) ; i f ( pos != −1){ s o u r c e . e r a s e ( pos , 3 ) ; s t r i n g r e p = " numel("+var_name + " ) " ; s o u r c e . i n s e r t ( pos , r e p ) ; } } } }

Cette fonction cherche d’abord combien d’arguments sont passés à la référence. S’il n’y en a qu’un seul, end est remplacé par numel(A) (qui renvoie le nombre d’éléments de A). Si deux arguments sont passés à la référence, end est remplacé par A.length (qui renvoie le nombre de lignes de A) dans le premier argument, et A[0].length (le nombre colonne de A) dans le deuxième argument (les matrices dans MCJavaCore sont représentées par des double[][]).

C.3.4 Conclusion

Nous voyons donc que le langage MATLAB voulant apporter des facilités à l’utilisateur, et voulant à tout prix minimiser le nombre d’erreurs de syntaxe, nous force à gérer des cas complexes. Pourquoi autoriser plusieurs séparateurs lors de la définition des matrices ? Pourquoi autoriser des expression contenant plus de 2 ’ :’ à la suite ?