Presentation is loading. Please wait.

Presentation is loading. Please wait.

IFT359 – Programmation fonctionnelle Thème 10 Extension syntaxique II pattern  motif template  gabarit 1.

Similar presentations


Presentation on theme: "IFT359 – Programmation fonctionnelle Thème 10 Extension syntaxique II pattern  motif template  gabarit 1."— Presentation transcript:

1 IFT359 – Programmation fonctionnelle Thème 10 Extension syntaxique II pattern  motif template  gabarit 1

2 Pourquoi des extensions syntaxiques Une extension syntaxique est une forme spéciale créée par le programmeur Les extensions syntaxiques sont mieux connues sous le nom de macro Les extensions syntaxiques sont utilisées –pour simplifier et uniformiser les patterns répétitifs dans un programme –pour introduire de nouvelles formes syntaxiques avec un mode d’évaluation qui leur sont propres –pour permettre de programmer plus efficacement 2

3 Fonction vs Formes spéciales Il est important de distinguer entre forme spéciale et fonction Le concept de fonction vous est maintenant très familier –L’évaluation d’une fonction a été décrite dans les sections traitant de l’évaluation normale, applicatif et par environnement –Nous avons utilisé à plusieurs reprise le constructeur λ pour créer des fonctions Le concept de forme spéciale –L’évaluation d’une forme spéciale varie d’une forme spéciale à l’autre define, if, let … sont des formes spéciales fournies avec le langage –Pour créer une nouvelle forme spéciale, il faut utiliser define-syntax, let-syntax ou letrec-syntax –define-syntax, let-syntax ou letrec-syntax sont des formes syntaxiques fournies par le langage créent des procédures spéciales appelés syntax-transformer –votre macro compilée est un syntax-transformer 3

4 La syntaxe d’une extension syntaxique Chaque extension syntaxique a sa propre syntaxe. Un extension syntaxique se présente généralement sous la forme (keyword subform...), –keyword est l’identificateur nommant la forme syntaxique –la syntaxe de chaque sous forme varie d’une extension syntaxique à l’autre Une extension syntaxique peut –se réduire à un identificateur, voir syntax-id-rules et identifier macro (section 16.1.5 du guide) –être une liste impropre, un vecteur … voir syntax-case et syntax-rules dans le manuel de référence 4

5 Évaluation vs Expansion Reader (parsing) –( read-syntax )  objets syntaxiques Expansion –application des syntax-transformer –(cond (and …))  (if (if …)) –On obtient donc de nouveaux objets syntaxiques –le processus se répète tant qu’il existe des syntax-transformer applicable Évaluation (runtime) –Définitions / applications / affectation / etc. tel que vu depuis le début du cours 5

6 Le processus d’expansion Les formes représentant des extensions syntaxiques sont transformés en formes spéciales de base et en fonctions avant l’exécution du programme, i.e. avant la compilation ou l’interprétation du code. La fonction effectuant l’expansion est appelée syntax-expander. Le syntax-expander analyse le code produit par la fonction read- syntax et à chaque fois qu’il rencontre une extension syntaxique, il évoque le syntax-transformer associé à cette extension. La forme syntaxique originale est remplacé par le code produit par le transformer. Le processus d’expansion est repris avec ce nouveau code. Si le syntax-expander rencontre une extension syntaxique primitive alors il procède à l’expansion de ses parties. L’information lexicale est préservée durant l’expansion afin que le respect de la portée lexicale soit maintenu pour les identificateurs et les mots-clefs. 6

7 define-syntax (define-syntax keyword expr) –keyword est le nom de l’extension syntaxique –l’évaluation de expr doit résulter en un transformer (syntax-rules (litt-id …) clauses …) retourne un syntax- transformer déjà vu (syntax-cases (litt-id …) clauses …) doit être encapsulé dans une fonction qui sera le transformer –sera vu dans les prochaines diapositives On peut créer nous même le transformer –Ce n’est qu’une fonction qui prend un objet syntaxique et qui retourne un autre objet syntaxique remplaçant le précédent. 7

8 le tranformer swap 8 (require (for-syntax syntax/stx)) (define-syntax swap (λ (stx) (let ([var1 (stx-car (stx-cdr stx))] [var2 (stx-car (stx-cdr (stx-cdr stx)))] [temp (stx-car (generate-temporaries (syntax (a))))]) #`(let ([#,temp #,(syntax->datum var1)]) (set! #,var1 #,var2) (set! #,var2 #,temp) )))) (define-syntax swap (λ (stx) (syntax-case stx () [(_ a b) #'(let ([t a]) (set! a b) (set! b t))]))) (define-syntax swap (syntax-rules () [(_ a b) (let ([t a]) (set! a b) (set! b t))]))

9 syntax-case Un appel normal de syntax-case a la forme (define-syntax keyword (λ (stx) (syntax-case stx () …) La visibilité des identificateurs introduit par define-syntax est similaire à celles des identificateurs définis avec define. 9 (define even? (λ (x) (or (= x 0) (odd? (- x 1))))) (define-syntax odd? (λ (stx) (syntax-case stx () [(_ x) #'(not (even? x))]))) (even? 10)  #t

10 Rappel sur syntax-rules avec syntax-rules, on fournit pour chaque pattern, un template –On peut utiliser …, _ et une liste de littéraux. syntax-rules crée automatiquement un transformer 1 transformer –une procédure qui reçoit un objet syntaxique et qui produit un objet syntaxique en retour. 10 (syntax-rules () [(_ a b) (cons a b)]) 1) le vrai nom est syntax-transformer

11 syntax-case (introduction) 11 (define-syntax when (λ (stx) (syntax-case stx () [(_ test e0 e1...) #'(if test (begin e0 e1...) (void))]))) (define-syntax id (λ (stx) (syntax-case stx (literal-id...) [pattern 1 expression-résultante 1 ] ou [pattern 1 fender-expr 1 result-expr] … )

12 syntax-case 12 (define-syntax id (λ (stx) (syntax-case stx (literal-id...) [pattern 1 result-expr 1 ] ou [pattern 1 fender-expr 1 result-expr 1 ] … ) stx est la forme syntaxique qui doit être transformée on dit aussi l’objet syntaxique qui doit être transformé litteral-id doit être un identificateur autre que _ et … Les clauses peuvent prendre deux formes 1.[ pattern result-expr ] a déjà été présentée 2.[ pattern fender-expr result-expr ] fender-expr doit être une expression qui s’évalue à vraie pour que la clause soit considérée fender-expr est une condition qui s’ajoute à celui de respecter le pattern

13 syntax-case 13 (define-syntax id (λ (stx) (syntax-case stx (literal-id...) [pattern 1 result-expr 1 ] ou [pattern 1 fender-expr 1 result-expr 1 ] … ) La mise en correspondance entre stx et pattern est décrites dans la prochaine diapositive (c’est la même que pour syntax-rules ) syntaxe-case doit retourner un objet syntaxique et pour retourner un objet syntaxique, il faut utiliser #’ ou #` avec #, et #@,

14 correspondance input--pattern An input form F matches a pattern P if and only if –P is an underscore or pattern variable, le souligné seul correspond au stx (stx est l’input global reçu) –P is a literal identifier and F is an identifier with the same binding as determined by the predicate free-identifier=? une variable de patron peut correspondre à un input qui est un littéral ; un littéral comme patron ne peut correspondre qu’à un input qui est le même littéral –P is of the form (P 1... P n ) and F is a list of n elements that match P1 through Pn, –P is of the form (P1... Pk Pe ellipsis Pm+1... Pn), where ellipsis is the identifier... and F is a proper list of n elements whose first k elements match P1 through Pk, whose next m - k elements each match Pe, and whose remaining n - m elements match Pm+1 through Pn, –P is a pattern datum (any nonlist, nonvector, nonsymbol object) and F is equal to P in the sense of the equal? procedure. par exemple un nombre dans un patron doit correspondre au même nombre dans l’input –… (non pertinent pour ce cours) 14 - = ou

15 correspondance input--pattern 15 An input form F matches a pattern P if and only if P is an underscore (i.e. _) or pattern variable (define-syntax tata (λ (stx) (syntax-case stx () [(_ x) #'(apply + x)]))) (tata '(456 789)) ; retourne 1245 (define-syntax tata (λ (stx) (syntax-case stx () [_ #'"_ est tout l'input"]))) (tata xxx yyy) ; retourne "_ est tout l'input" (define-syntax tata (λ (stx) (syntax-case stx () [(tata _) #' _]))) (tata xxx) ; _ est xxx mais ;_: wildcard not allowed as an expression in: _

16 correspondance input--pattern 16 An input form F matches a pattern P if and only if P is a literal identifier and F is an identifier with the same binding as determined by the predicate free-identifier=? (define-syntax tata4 (λ (stx) (syntax-case stx (xxx) [(_ xxx) #'"le littéral xxx matche l'input xxx"] [(_ else) #'"le littéral xxx ne matche pas l'input xxx"]))) (let ([yyy #t]) (tata4 xxx))  "le littéral xxx matche l'input xxx" (let ([xxx #t]) (tata4 xxx))  "le littéral xxx ne matche pas l'input xxx" ; la première clause échoue parce que le xxx de l’appel n’est pas un identificateur libre

17 correspondance input--pattern 17 An input form F matches a pattern P if and only if P is of the form (P 1... P n ) and F is a list of n elements that match P 1 through P n. (define-syntax tata (λ (stx) (syntax-case stx () [(_ _ x y) #'(begin (+ x y) 'bonjour)]))) (tata zzz 3 4) ; le premier _ est tata et le deuxième _ est zzz ; -> 7 ; -> bonjour ; le begin est au top level, ça explique les deux retours

18 correspondance input--pattern 18 An input form F matches a pattern P if and only if P is of the form (P 1... P k P e ellipsis P m+1... P n ), where ellipsis is the identifier... and F is a proper list of n elements whose first k elements match P 1 through P k, whose next m - k elements each match P e ellipsis, and whose remaining n - m elements match P m+1 through P n, (define-syntax toto (λ (stx) (syntax-case stx () [(_ x1 x2 x3 z... q) #'(begin (displayln (x1 x2 x3)) (displayln q) '(z...))]))) (toto + 1 2 e f g h '(a b c d)) 3 (+ 1 2) (a b c d) '(e f g h) ellipsis et … représentent les … de DrRacket et non la convention d’abbréviation. Attention : une liste comme pattern ne peut avoir plus d’un élément ellipsis

19 correspondance input--pattern 19 An input form F matches a pattern P if and only if P is a pattern datum (any nonlist, nonvector, nonsymbol object) and F is equal to P in the sense of the equal? procedure. (define-syntax tata (λ (stx) (syntax-case stx () [(_ x1 2 x2 "abcd") #'(list x1 x2)]))) (tata (+ 1 2) 2 'elt3 "abcd") ; -> (3 elt3)

20 Retour sur l’hygiène dans les macros 20 (define-syntax my-unless1 (syntax-rules () [(_ condition body) (if (not condition) body (void))])) (let ([not (lambda (x) x)]) (my-unless1 #t (displayln "This should not be printed!")) (not 3)) ;  3 ; la définition du not n'est pas dans l'espace lexical ; de la macro, il n'y donc pas interférence entre le not ; de la macro et le not du let contenant l'appel de la macro

21 Retour sur l’hygiène dans les macros 21 (let ([not (λ (x) x)]) (define-syntax my-unless2 (syntax-rules () [(_ condition body) (if (not condition) body (void))])) ; ici le not est redéfini localement et ;c'est la version locale qui prime (my-unless2 #t (displayln "This should not be printed!")) (not 3)) ;; "This should not be printed!" ;;  3

22 Retour sur l’hygiène dans les macros 22 (define not (λ (x) x)) (define-syntax my-unless2 (syntax-rules () [(_ condition body) (if (not condition) body (void))])) ; Le not de DrRacket est redéfini pour le module ; et c'est la version du module qui est utilisé (my-unless2 #t (displayln "This should not be printed!")) (not 3) ;; "This should not be printed!" ;;  3

23 Retour sur l’hygiène dans les macros 23 (define-syntax my-unless2 (syntax-rules () [(_ condition body) (if (not condition) body (void))])) (define not (λ (x) x)) ; Le not de DrRacket est redéfini pour le module ; et c'est la version du module qui est utilisé (my-unless2 #t (displayln "This should not be printed!")) (not 3) ;; "This should not be printed!" ;;  3

24 Retour sur l’hygiène dans les macros 24 (define-syntax my-unless2 (syntax-rules () [(_ condition body) (if (not condition) body (void))])) (my-unless2 #t (displayln "This should not be printed!")) (define not (λ (x) x)) ;; not: undefined; ;; cannot reference an identifier before its definition ; Ceci s'explique par le fait que dans une première passe, ; DrRacket ajoute à son environnement courant tous les ; identificateurs introduit par les define, define-value, ; define-syntanx mais leur valeurs sont et c'est ; cette valeur qui est utilisé dans l'appel du my-unless.3

25 syntax ou #’ (syntax template) ou #’template –sont résultat est un objet syntaxique L’objet syntaxique est identique au template sauf que les variables de pattern sont remplacés par les objets syntaxiques qui leurs correspondent et que tous les autres identificateurs conservent leur valeurs contextuelles. –dans (+ 3 4) le + reste l’opérateur +, et 3 et 4 restent des nombres, mais l’évaluation n’est pas faite (i.e. comme si c’était un quote) –ne pas confondre avec quote-syntax qui ressemble vraiment à un quote –les variables de pattern sont créés uniquement avec syntax-case ou syntax-rules ou with-syntax 25

26 objet syntaxique un objet syntaxique associé à une expression est cette expression augmenté des méta-informations suivantes : –position dans le code source (#ligne, #colonne, etc.) –un pointeur sur l’environnent englobant (lexical context, i.e. permet de retrouver les variables libres de l’objet) si l’expression encapsulée est une liste, on peut accéder aux objets syntaxiques qui le composent à l’aide de stx-car et stx-cdr Pour utiliser ces fonctions, il faut utiliser les expressions require suivante : –(require (for-syntax syntax/stx)) Pour les utiliser au moment de l’expansion (ce qui est le cas usuel) –(require syntax/stx) Pour les utiliser au moment de l’exécution. 26

27 Exemple 1 illustrant #’ (syntaxe) 27 ( module A racket (provide test) (define x 10) (define y 20) (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) #'(begin (set! a 300) (displayln (list a b)) (list x y))]))) ) (require 'A) (define x 8) (test x 5) x ; "(300 5)" ; (10 20) ; 300 ; Les clauses peuvent avoir comme les fonctions des variables ; libres. Les valeurs des variables libres sont prises dans ; l’environnement lexical qui contient la définition de la macro. clause == [pattern template]

28 Exemple 2 illustrant #’ (syntaxe) 28 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) (begin (define u #'b) u)]))) ; Un clause induit un espace lexical et par conséquent les liaisons ; de variables introduites dans cet espace lexical ne sont pas ; visibles à l'extérieur. ; Ici, le u n'est pas visible à l'extérieur de l'espace ; lexical de la clause. (test a 2);  2 ; ce qui est retourné au moment de l'expansion est la valeur ; de u qui est #'b, et #'b est une variable de pattern (un objet syntaxique qui conserve ; l’information syntaxique de l’appel de la macro) et au moment de l'exécution b s’évalue à 2. u ;  l'erreur "u: unbound identifier in module"

29 Exemple 3 illustrant #’ (syntaxe) 29 (define x 10) (define y 20) (provide test) (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) (begin (displayln x) #'(begin (set! a 300) (displayln (list a b)) (list x (+ x y))))]))) (test x y) ; Attention : l'espace lexical d'une clause est celui qui est accessible à l'expansion, ; Dans cet exemple, la valeur de x ne sera pas connu avant l'exécution. ; x: undefined; ; cannot reference an identifier before its definition ; phase: 1 ; explanation: cannot access the run-time definition

30 Exemple 4 illustrant #’ (syntaxe) 30 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) #'(define a b)]))) (test a 2) ; retourne void a ;  2 Ce qui est retourné est l'objet syntaxique #'(define a b). Les variables de pattern a et b sont remplacés par les identificateurs provenant de l'appel de la macro, i.e. de l'environnement de l'appel. (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) #'(begin (define a b) a)]))) (test a 2) ;  2 parce que ce qui est retourné c'est tout le #’(begin …) et les variables de pattern sont remplacés par leur identificateurs respectifs, i.e. ceux provenant de l'appel de la macro. a ;  2

31 Exemple 5 illustrant #’ (syntaxe) 31 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) #'(begin (+ 3 4) (define a b) a)]))) (test a 2) ;  7 ;  2 C'est comme précédemment, l'exemple est tout simplement pour faire remarquer que le begin est à top-level et que son contenu est inséré dans le code comme s'il n'y avait pas de begin a ;  2

32 Exemple 6 illustrant #’ (syntaxe) 32 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) #'(let ([c 'bidon]) (define a b) a)]))) (test a 2) ;  2 comme précédemment a ; a: unbound identifier in module in: a Le a n’est pas visible à l’extérieur du let.

33 Exemple 7 illustrant #’ (syntaxe) 33 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) (let ([b 15]) #'(begin (define a b) a))]))) (test a 2) La clause forme un espace lexical A et le let introduit un sous-espace lexical C. Dans C l’identificateur b qui ombrage celui en provenance de l'appel. Cet identificateur b n'a de sens que dans l'espace lexical C. Le b du template n’est plus la variable de pattern reçu mais un identificateur de l’espace lexical C. Lorsque ce template remplacera l’appel de macro alors le b ne se retrouvera pas le bon espace lexical. ; b: identifier used out of context in: b

34 Exemple 8 illustrant #’ (syntaxe) 34 (define-syntax test (λ (stx) (syntax-case stx () [(_ a b) (list #'a #'b)]))) (test c 2) ; on a le message d'erreur que ce qui est retourné n'est pas un objet syntaxique

35 Exemple 9 illustrant #’ (syntaxe) 35 (require (for-syntax syntax/stx)) (define-syntax test-brut (λ (stx) (let ([var1 (stx-car (stx-cdr stx))] [var2 (stx-car (stx-cdr (stx-cdr stx)))]) #`(define #,var1 #,var2)))) (test-brut c 2) ;  void c ;  2 #` similaire à backquote mais pour les objets syntaxiques #, joue le même rôle que, dans les backquote #,@ joue le même rôle que,@ dans les bacquote Nous devons utiliser #` et #, parce que var1 et var2 ne sont pas des variables de pattern.

36 Exemple 10 illustrant #’ (syntaxe) 36 (require (for-syntax syntax/stx)) (define-syntax test-brut (λ (stx) (let ([var1 (stx-car (stx-cdr stx))] [var2 (stx-car (stx-cdr (stx-cdr stx)))]) (define var1 var2) (displayln (syntax->datum var1)) #'(void) ))) (test-brut c 2) ; affiche 2 c ; retourne le message d'erreur ; " c: unbound identifier in module in: c" ; ATTENTION ; Le var1 du define n'est pas celui du let parce que les define internes ; provoquent un letrec-values comme le montre l'expansion du code ; à la diapositive suivante

37 Exemple 10 (suite) illustrant #’ (syntaxe) 37 ; (let ([a 2]) ; (define a 3) ; a) ; se développe comme (module |...| racket (#%module-begin (#%app call-with-values (lambda () (let-values (((a) '2)) (let-values (((a) '3)) a))) print-values)))

38 Un syntax-case peut retourner un syntax-case syntax-rules avec syntax-case (define-syntax syntax-rules* (λ (stx) (syntax-case stx () [(_ (k... ) [pattern template]...) #'(λ (x) (syntax-case x (k...) [pattern #'template]...))]))) 38 Ne répond pas tout à fait aux normes sur le premier id des patterns. Pour la version exacte voir la section 11.1 du manuel de référence.11.1

39 manipulation des objets syntaxiques Il est souvent utile de manipuler les objets syntaxiques représentant un identificateur –au moment de l’expansion nous ne connaissons pas la valeur de ces identificateurs –nous avons vu stx-car et stx-cdr pour manipuler les objets syntaxiques encapsulant des listes. –il n’y a pas d’intérêt à avoir des outils pour manipuler les objets syntaxiques encapsulant des nombres, des caractères, des strings … Plusieurs fonctions utilitaires : –identifier? –generate-temporaries –bound-identifier=? –free-identifier=? 39

40 identifier? procédure: ( identifier? obj) –retourne : #t si obj est un objet syntaxique encapsulant un identificateur #f autrement –est souvent utilisé à l’intérieur des fenders pour vérifier si une sous- forme de l’input est un identificateur. 40 (define symb 'ag) (identifier? 'ag)  #f (identifier? symb)  #f (identifier? #'ag)  #t #'ag  #

41 identifier? et pattern réduit à un keyword 41 (define-syntax set-car! (syntax-rules () [(_ p x) (set! p (cons x (cdr p)))])) (define p1 (cons 0 #f)) (define-syntax pcar1 (λ (x) (syntax-case x () [_ #'(car p1)] [(_ e) #'(set-car! p1 e)]))) (let ([a1 pcar1]) (pcar1 1) (list a1 pcar1))  ‘(0 0) (define-syntax set-car! (syntax-rules () [(_ p x) (set! p (cons x (cdr p)))])) (define p1 (cons 0 #f)) (define-syntax pcar1 (λ (x) (syntax-case x () [_ (identifier? x) #'(car p1)] [(_ e) #'(set-car! p1 e)]))) (let ([a1 pcar1]) (pcar1 1) (list a1 pcar1))  ‘(0 1) Expliquer la différence des résultats

42 define-for-syntax 42 (define-for-syntax ids? (λ (ls) (or (stx-null? ls) (and (identifier? (stx-car ls)) (ids? (stx-cdr ls)))))) ids? vérifie si une liste ne contient que des identificateurs. define-for-syntax est comme define sauf que l’identificateur défini peut être utilisé au moment de l’expansion. (define-syntax my-let ; ne vérifie pas que tous les identificateurs sont différents ; à suivre (λ (x) (syntax-case x () [(_ ((i e)...) b1 b2...) (ids? (syntax->list #'(i...))) #'((λ (i...) b1 b2...) e...)])))

43 Comparaison des identificateurs 43

44 free-identifier=? 44

45 free-identifier=? 45 (define-syntax my-cond (lambda (x) (syntax-case x () [(_ (e0 e1 e2...)) (and (identifier? #'e0) (free-identifier=? #'e0 #'else)) #'(begin e1 e2...)] [(_ (e0 e1 e2...)) #'(if e0 (begin e1 e2...))] [(_ (e0 e1 e2...) c1 c2...) #'(if e0 (begin e1 e2...) (my-cond c1 c2...))]))) fender

46 bound-identifier=? ( bound-identifier=? id1 id2 ) –retourne #t que s’ils encapsulent des identificateurs ayant par la syntaxe des liaisons équivalentes (i.e. ce serait le même pointeur) –détermine si deux identificateurs sont associés à la même liaison lorsqu’ils apparaissent dans le résultat d’un transformer. Une liaison est le lien entre un identificateur et un endroit dans la mémoire habituellement un seul identificateur est associé à la liaison mais –cet identificateur peut être renommé lorsqu’il est importé dans un module, –un identificateur dans un programme peut être associé à deux liaisons, lorsque ce cas arrive, l’expander traite à l’interne ces deux identificateurs comme s’ils étaient différents. –la liaison est déterminée par la structure du programme et non par la valeur des identificateurs, i.e. c’est la même liaison si la modification de la valeur de l’un des identificateurs modifie la valeur de l’autre. –est utilisé pour détecter les variables en double ou pour détecter la présence d’un identificateur donné dans un input. 46

47 bound-identifier=? 47 (define-syntax (check stx) (syntax-case stx () [(_ x y) (if (bound-identifier=? #'x #'y) #'(let ([y 'wrong]) (let ([x 'binds]) y)) #'(let ([y 'no-binds]) (let ([x 'wrong]) y)))])) (check a a)  ‘binds (check a b)  ‘no-binds (define-syntax check-a (syntax-rules () [(check-a y) (check a y)])) (check-a a)  ‘no-binds

48 exemple de capture d’une liaison (check a a)  x et y même liaison  x = binds  y = binds  #t (define-syntax check-a (syntax-rules () [(check-a y) (check a y)])) (check-a a)  x et y différente liaison  x = wrong  y = no-binds  #f les deux identificateurs encapsulant « a » sont traités par le même transformer Le transformer associé à check-a n’étant pas le même que celui de check. Le « a » à l’intérieur de check-a sera celui de l’espace lexical encapsulant la définition de check- a. Le « a » au moment de l’appel peut- être différent de celui au moment de la création de check-a. Par exemple, l’appel peut-être fait à partir d’un autre module. 48

49 bound-identifier=? 49 (define-syntax my-let (λ (x) (syntax-case x () [(_ ((i e)...) b1 b2...) (and (ids? (syntax->list #'(i...))) (not (check-duplicate-identifier (syntax->list #'(i...))))) #'((λ (i...) b1 b2...) e...)]))) (define check-duplicate-identifier (λ (ls) (cond [(or (stx-null? ls) (stx-null? (stx-cdr ls))) #t] [(ormap (λ (x) (bound-identifier=? x (stx-car ls))) (stx-cdr ls)) #f] [else (check-duplicate-identifier (stx-cdr ls))])))

50 letrec 50 (define-syntax letrec (λ (x) (syntax-case x () [(([i v]...) e1 e2...) #'(let ([i #f]...) (set! i v)... e1 e2...)])) Cette implémentation du letrec est incorrecte pour des raisons non évidentes. Plus précisément, dans la spécification de scheme et de drRacket, les v … doivent tous être évalués avant d’être affectés aux i … Pour solutionner ces problèmes il faut être en mesure d’évaluer les v … et conserver les valeurs obtenues dans des variables temporaires. Un sous problème est que ces variables temporaires doivent être associé aux v…, elles doivent donc être des variables de motifs (pattern). with-syntax et generate-temporaries permettent de solutionner problème

51 with-syntax (with-syntax ((pattern expr)...) body1 body2...) –retourne la valeur de la dernière expression de son corps –construit des identificateurs et les utilise pour former des pattern –fait un pattern matching entre les patterns et les expr et les identificateurs de pattern prennent les valeurs des sous-expressions correspondantes dans expr les p... sont construits par pattern-matching avec les e… et les variables de motifs définis dans p… sont utilisées dans les b1 b2 … 51 (define-syntax with-syntax (lambda (x) (syntax-case x () [(_ ((p e)...) b1 b2...) #'(syntax-case (list e...) () [(p...) (let () b1 b2...)])])))

52 generate-temporaries (generate-temporaries list) –retourne une liste de nouveaux identificateurs distincts –le nombre de nouveaux identificateurs correspond à la longueur de list –le contenu de list n’a aucun impact. 52

53 with-syntax et generate-temporaries 53 (define-syntax my-letrec (λ (x) (syntax-case x () [(_ ([i v]...) e1 e2...) (with-syntax ([(t-id...) (generate-temporaries #'(i...))]) #'(let ((i #f)...) (let ([t-id v]...) (set! i t-id)... e1 e2...)))]))) (generate-temporaries #'(i...)) est une liste d’identificateurs. with-syntax associe à chaque t-id un des identificateurs qui vient d’être créés par generate-temporaries. Chaque t-id est une variable de motif qui est insérée dans l’ objet syntaxique retourné

54 accessibilité des identificateurs Les identificateurs utilisés dans un template peuvent appartenir à l’espace lexical encapsulant la macro et le template en conserve l’accès. En particulier tous les identificateurs prédéfinis (define, car …) sont accessibles. Les nombres, les symboles qui s’auto-évalue sont toujours accessibles. Par contre les identificateurs qui appartiennent spécificiquement à la l’expression résultante d’une clause de syntax-case ne sont accessibles dans le template retourné par la macro. –datum->syntax permet de créer un objet sytaxique appartenant à un espace lexical choisi 54

55 datum->syntax 55

56 créer des liaisons dans un espace lexical 56 Voir fichier make-id-accesseur.rkt

57 si stx un objet syntaxique représentant (a (b c)) (syntax? stx)  #t (syntax->source stx)  "c:\xxx\yyy\zzz.ss" (syntax->line stx)  10 (syntax->column stx)  0 (stx-null? stx)  #f (stx-list? stx)  #t ;; s-o == syntax-object (stx-car stx)  (stx-cdr stx)  ( ) (syntax->list stx)  ( ) (syntax->datum stx)  ‘(a (b c)) Voir aussi le fichier stx-util 57

58 Retour sur syntax-rules (define-syntax syntax-rules* (λ (stx) (syntax-case stx () [(_ (k...) [(kw. pattern) template]...) (with-syntax ([(dummy...) (gen-temps #'(kw...)]) #'(λ (x) (syntax-case x (k...) [(dummy. pattern) #'template]...))]))) L’introduction des dummy est pour tenir compte de la spécification de syntax-rules affirmant que le premier terme n’est jamais pris en compte, 58

59 macro retournant macro 59 (define-syntax macro-créant-macro-output (λ (stx) (syntax-case stx () [(_ name (args...)) #'(define-syntax name (syntax-rules () [(_ body (......)) (list body (......) args...)]))]))) (macro-créant-macro-output toto (1 2 3)) (toto (+ 3 4 5) 'bonjour)  (12 bonjour 1 2 3) Le « define-syntax name » fait partie d’un template du (define-syntax macro-créant … Il s’ensuit que si j’écrit simplement (_ body …) alors DrRacket interprète body … comme une variable de pattern de (_ name (args …)) mais ne le trouve pas et retourne une erreur. Pour faire ce que l’on veut, il faut alors utiliser un pattern spécial de la forme (… …). Voir la section 11.1 du manuel de référence le pattern (ellipsis stat-pattern)

60 (require (for-syntax "util_syntax.rkt")) ; si les construits importés sont à la phase 0 ; et que nous voulons les utiliser à la phase 1 ; alors (for-syntax...) est nécessaire ; si les construits importés sont à la phase 1 ; et que nous voulons les utiliser à la phase 1 ; alors (for-syntax...) ne doit pas être utiliser 60


Download ppt "IFT359 – Programmation fonctionnelle Thème 10 Extension syntaxique II pattern  motif template  gabarit 1."

Similar presentations


Ads by Google