@c -*- coding: utf-8; mode: texinfo; documentlanguage: fr -*-

@ignore
   Translation of GIT committish: 606ac3c3b22c41d84aa5fa938d42da5125ac572a

   When revising a translation, copy the HEAD committish of the
   version that you are working on.  For details, see the Contributors'
   Guide, node Updating translation committishes..
@end ignore

@c \version "2.19.22"

@c Translators: Jean-Charles Malahieude

@node Scheme tutorial
@chapter Tutoriel Scheme

@cindex Scheme
@cindex Guile
@cindex Scheme, inclusion de code
@cindex accéder à Scheme
@cindex @'evaluation Scheme
@cindex LISP

LilyPond recourt abondamment au langage de programmation Scheme, tant au
niveau de la syntaxe de saisie que des mécanismes internes chargés de
combiner les différents modules du logiciel. Les lignes qui suivent
constituent un bref aperçu de la manière de saisir des données en
Scheme. Si vous désirez en apprendre plus sur Scheme, n'hésitez pas à
vous rendre sur @uref{https://@/www@/.Schemers@/.org}.

Le Scheme utilisé par LilyPond repose sur l'implémentation GNU Guile ;
celle-ci se base sur le standard Scheme « R5RS ». Si votre but est
d'apprendre Scheme au travers de LilyPond, sachez que l'utilisation
d'une autre implémentation ou d'un autre standard pourrait être source
de désagrément. Vous trouverez plus d'information sur Guile à la page
@uref{https://www.gnu.org/software/guile/} ; le standard Scheme « R5RS »
est quant à lui disponible à la page
@uref{https://www.Schemers.org/Documents/Standards/R5RS/}.


@node Introduction to Scheme
@section Introduction à Scheme

Nous commencerons par nous intéresser à Scheme et à son fonctionnement,
grâce à l'interpréteur Guile. Une fois plus à l'aise avec Scheme, nous
verrons comment ce langage peut s'intégrer à un fichier LilyPond.


@node Scheme sandbox
@subsection Le bac à sable de Scheme

@cindex Scheme, bac à sable
@cindex Scheme, @emph{sandbox}
@cindex @emph{sandbox} Scheme

L'installation de LilyPond comprend l'implémentation Guile de Scheme.
Tous les paquetages de LilyPond disposent d'un « bac à sable » Scheme,
accessible par la commande :

@example
lilypond scheme-sandbox
@end example

@noindent
Une fois le bac à sable actif, vous obtiendrez l'invite de Guile

@example
lily-guile@@()>
@end example

@noindent
qui permet de saisir des expressions Scheme. Dans ce qui suit, nous
utiliserons simplement @code{guile>} pour indiquer le prompt.

Le fichier contenant le code du bac à sable --
@file{ly/Scheme-sandbox.ly} -- essaie d'activer la bibliothèque GNU
@code{readline}, qui offre une ligne de commande plus élaborée, comme
par exemple l'utilisation des flèches de direction pour accéder aux
commandes précédentes. Ceci n'est fonctionnel, à ce jour, que lorsque le
binaire de LilyPond est lié dynamiquement à Guile, ce qui n'est pas le
cas avec les binaires « officiellement » distribués. Voici deux moyens
de contourner ce désagrément.

@itemize

@item
Utiliser @uref{https://github.com/hanslub42/rlwrap, @command{rlwrap}},
petit programme « enveloppant » la bibliothèque @code{readline}. Ce
programme est disponibles pour les systèmes de type Unix ; il est lancé
ainsi :

@example
rlwrap lilypond scheme-sandbox
@end example

@item
Utiliser l'éditeur Emacs auquel on ajoute le
@uref{https://github.com/shevvek/lilypond-ts-mode, LilyPond
Tree-sitter Mode}. Ceci fonctionne aussi sur Windows.
@end itemize


@node Scheme variables
@subsection Scheme et les variables

Une variable Scheme peut contenir n'importe quelle valeur valide en
Scheme, y compris une procédure Scheme.

Une variable Scheme se crée avec la fonction @code{define} :

@lisp
guile> (define a 2)
guile>
@end lisp

L'évaluation d'une variable Scheme se réalise en saisissant le nom de
cette variable à l'invite de Guile :

@lisp
guile> a
2
guile>
@end lisp

Une variable Scheme s'affiche à l'écran à l'aide de la fonction
@code{display} :

@lisp
guile> (display a)
2guile>
@end lisp

@noindent
Vous aurez remarqué que la valeur @code{2} et l'invite @code{guile}
apparaissent sur une même ligne. On peut améliorer la présentation à
l'aide de la procédure @code{newline} ou bien en affichant un caractère
« retour chariot ».

@lisp
guile> (display a)(newline)
2
guile> (display a)(display "\n")
2
guile>
@end lisp

Après avoir créé une variable, vous pouvez en modifier la valeur grâce à
un @code{set!} :

@lisp
guile> (set! a 12345)
guile> a
12345
guile>
@end lisp

Vous quitterez proprement le bac à sable à l'aide de l'instruction
@code{quit} :

@lisp
guile> (quit)
@end lisp


@node Scheme simple data types
@subsection Types de données Scheme simples

L'un des concepts de base de tout langage est la saisie de données,
qu'il s'agisse de nombres, de chaînes de caractères, de listes, etc.
Voici les différents types de données Scheme simples utilisées
couramment dans LilyPond.

@table @asis
@item Booléens
Les valeurs booléennes sont vrai ou faux.  En Scheme, ce sera @code{#t}
pour vrai, et @code{#f} pour faux.
@funindex #t
@funindex #f

@item Nombres
Les nombres se saisissent le plus communément : @code{1} est le
nombre (entier) un, alors que @w{@code{-1.5}} est un nombre à virgule
flottante (un nombre non entier).

@item Chaînes
Les chaînes de caractères sont bornées par des guillemets
informatiques :

@example
"ceci est une chaîne"
@end example

Une chaîne peut s'étendre sur plusieurs lignes :

@example
"ceci
est
une chaîne"
@end example

@noindent
auquel cas les retours à la ligne seront inclus dans la chaîne.

Un caractère de retour à la ligne peut s'ajouter dans la chaîne, sous la
forme d'un @code{\n}.

@example
"ceci\nest une\nchaîne multiligne"
@end example

Guillemets et obliques inverses dans une chaîne doivent être précédés
d'une oblique inverse. La chaîne @code{\a dit "b"} se saisit donc

@example
"\\a dit \"b\""
@end example

@end table

Il existe bien d'autres types de données Scheme, dont nous ne parlerons
pas ici. Vous en trouverez une liste exhaustive dans le guide de
référence de Guile, à la page
@uref{https://www.gnu.org/software/guile/manual/html_node/Data-Types.html}.


@node Scheme compound data types
@subsection Types de données Scheme composites

Scheme prend aussi en charge des types de données composites. LilyPond
utilise beaucoup les paires, listes, listes associatives et tables de
hachage.


@node Pairs
@unnumberedsubsubsec Paires

Le type de donnée composite fondamental est la paire (@code{pair}).
Comme son nom l'indique, il s'agit de lier deux valeurs, à l'aide de
l'opérateur @code{cons}.

@lisp
guile> (cons 4 5)
(4 . 5)
guile>
@end lisp

Vous aurez noté que la paire s'affiche sous la forme de deux éléments
bornés par des parenthèses et séparés par une espace, un point
(@code{.}) et une autre espace. Le point n'est en aucune manière un
séparateur décimal ; il s'agit de l'indicateur d'une paire.

Vous pouvez aussi saisir littéralement les valeurs d'une paire, en la
faisant précéder d'une apostrophe.

@lisp
guile> '(4 . 5)
(4 . 5)
guile>
@end lisp

Les deux éléments d'une paire peuvent être constitués de n'importe
quelle valeur Scheme valide :

@lisp
guile> (cons #t #f)
(#t . #f)
guile> '("blah-blah" . 3.1415926535)
("blah-blah" . 3.1415926535)
guile>
@end lisp

Les premier et second éléments de la paire sont accessibles à l'aide des
procédures Scheme @code{car} et @code{cdr}.

@lisp
guile> (define mypair (cons 123 "hello there")
@dots{} )
guile> (car mypair)
123
guile> (cdr mypair)
"hello there"
guile>
@end lisp

@noindent

Note : @code{cdr} se prononce « couldeur », comme l'indiquent Sussman et
Abelson -- voir
@uref{https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133}.


@node Lists
@unnumberedsubsubsec Listes

Autre structure de donnée commune en Scheme : la liste (@emph{list}).
Une liste « correcte » se définit comme étant vide (représentée par
@code{'()} et de longueur 0) ou une paire dont le @code{cdr} est une
liste.

Il existe plusieurs méthodes pour créer une liste, la plus courante
étant l'utilisation de la procédure @code{list} :

@lisp
guile> (list 1 2 3 "abc" 17.5)
(1 2 3 "abc" 17.5)
@end lisp

La représentation d'une liste par la succession de ses éléments, séparés
par des espaces, bornée par des parenthèses, n'est en fait qu'une vue
compacte des paires qui la constituent. Les paires sont ainsi dépourvues
du point de séparation et de la parenthèse ouvrante qui le suit et des
parenthèses fermantes. Sans ce « compactage », cette liste serait ainsi
présentée :

@lisp
(1 . (2 . (3 . ("abc" . (17.5 . ())))))
@end lisp

Vous pouvez donc saisir une liste comme elle serait présentée, en
entourant ses éléments par des parenthèses à la suite d'une apostrophe
(afin que ce qui suit ne soit pas interprété comme un appel à une
fonction) :

@lisp
guile> '(17 23 "foo" "bar" "bazzle")
(17 23 "foo" "bar" "bazzle")
@end lisp

Les listes ont une importance considérable en Scheme. Certains vont
d'ailleurs jusqu'à considérer Scheme comme un dialecte du lisp, où
« lisp » serait une abréviation de « List Processing ». Il est vrai que
toute expression Scheme est une liste.


@node Association lists (alists)
@unnumberedsubsubsec Listes associatives (alists)

Il existe un type particulier de liste : la @emph{liste associative} --
ou @emph{alist}.  Une @emph{alist} permet de stocker des données dans le
but de les réutiliser.

Une liste associative est une liste dont les éléments sont des paires.
Le @code{car} de chacun des éléments constitue une clé (@emph{key}) et
chaque @code{cdr} une valeur (@emph{value}). La procédure Scheme
@code{assoc} permet de retrouver une entrée de la liste associative ;
son @code{cdr} en fournira la valeur :

@lisp
guile> (define mon-alist '((1  . "A") (2 . "B") (3 . "C")))
guile> mon-alist
((1 . "A") (2 . "B") (3 . "C"))
guile> (assoc 2 mon-alist)
(2 . "B")
guile> (cdr (assoc 2 mon-alist))
"B"
guile>
@end lisp

LilyPond recourt abondamment aux @emph{alists} pour stocker des
propriétés ou autres données.


@node Hash tables
@unnumberedsubsubsec Tables de hachage

Il s'agit d'une structure de données à laquelle LilyPond fait parfois
appel. Une table de hachage (@emph{hash table}) peut se comparer à une
matrice ou un tableau dont l'index peut être n'importe quel type de
valeur Scheme et ne se limitant pas à des nombres entiers.

Les tables de hachage sont un moyen plus efficace que les listes
associatives lorsqu'il s'agit d'enregistrer de nombreuses données qui ne
changeront que peu fréquemment.

La syntaxe permettant de créer une table de hachage peut paraître
complexe, mais vous en trouverez de nombreux exemples dans les sources
de LilyPond.

@lisp
guile> (define h (make-hash-table 10))
guile> h
#<hash-table 0/31>
guile> (hashq-set! h 'cle1 "valeur1")
"valeur1"
guile> (hashq-set! h 'key2 "valeur2")
"valeur2"
guile> (hashq-set! h 3 "valeur3")
"valeur3"
@end lisp

La procédure @code{hashq-ref} permet de récupérer une valeur dans la
table de hachage.

@lisp
guile> (hashq-ref h 3)
"valeur3"
guile> (hashq-ref h 'cle2)
"valeur2"
guile>
@end lisp

La procédure @code{hashq-get-handle} permet de retrouver à la fois une
clé et sa valeur. Cette procédure a l'avantage de renvoyer @code{#f}
lorsque la clé n'existe pas.

@lisp
guile> (hashq-get-handle h 'cle1)
(cle1 . "valeur1")
guile> (hashq-get-handle h 'zut)
#f
guile>
@end lisp


@node Calculations in Scheme
@subsection Scheme et les calculs

@ignore
We have been using lists all along.  A calculation, like @code{(+ 1 2)}
is also a list (containing the symbol @code{+} and the numbers 1
and@tie{}2).  Normally lists are interpreted as calculations, and the
Scheme interpreter substitutes the outcome of the calculation.  To enter a
list, we stop the evaluation.  This is done by quoting the list with a
quote @code{'} symbol.  So, for calculations do not use a quote.

Inside a quoted list or pair, there is no need to quote anymore.  The
following is a pair of symbols, a list of symbols and a list of lists
respectively,

@example
#'(stem . head)
#'(staff clef key-signature)
#'((1) (2))
@end example
@end ignore

Scheme permet aussi d'effectuer des calculs. Il utilise alors un
@emph{préfixe}. Additionner 1 et 2 s'écrira @code{(+ 1 2)} et non
@math{1+2} comme on aurait pu s'y attendre.

@lisp
guile> (+ 1 2)
3
@end lisp

Les calculs peuvent s'imbriquer ; le résultat d'une fonction peut
servir pour un autre calcul.

@lisp
guile> (+ 1 (* 3 4))
13
@end lisp

Ces calculs sont un exemple d'évaluation : une expression telle que
@code{(* 3 4)} est remplacée par sa valeur, soit @code{12}.

En matière de calcul, Scheme fait la différence entre des nombres
entiers ou non. Les calculs sur des nombres entiers seront exacts, alors
que s'il s'agit de nombres non entiers, les calculs tiendront compte de
la précision mentionnée :

@lisp
guile> (/ 7 3)
7/3
guile> (/ 7.0 3.0)
2.33333333333333
@end lisp

Lorsque l'interpréteur Scheme rencontre une expression sous forme de
liste, le premier élément de cette liste est considéré en tant que
procédure qui prendra en argument le restant de la liste. C'est la
raison pour laquelle, en Scheme, tous les opérateurs sont en préfixe.

Le fait que le premier élément d'une expression Scheme sous forme de
liste ne soit pas un opérateur ou une procédure déclenchera une erreur
de la part de l'interpréteur :

@lisp
guile> (1 2 3)

Backtrace:
In current input:
  52: 0* [1 2 3]

<unnamed port>:52:1: In expression (1 2 3):
<unnamed port>:52:1: Wrong type to apply: 1
ABORT: (misc-error)
guile>
@end lisp

Vous pouvez constater que l'interpréteur a tenté de considérer @code{1}
comme étant un opérateur ou une procédure, ce qu'il n'a pu réaliser. Il
a donc renvoyé l'erreur « Wrong type to apply: 1 » (@emph{Application
d'un type erroné : 1}).

C'est pourquoi il est impératif, pour créer une liste, soit d'utiliser
l'opérateur consacré (@code{list}), soit de faire précéder la liste
d'une apostrophe, de telle sorte que l'interpréteur ne tente pas de
l'évaluer.

@lisp
guile> (list 1 2 3)
(1 2 3)
guile> '(1 2 3)
(1 2 3)
guile>
@end lisp

Vous pourrez être confronté à cette erreur lorsque vous intégrerez
Scheme à LilyPond.

@ignore
The same assignment can be done in completely in Scheme as well,

@example
#(define twentyFour (* 2 twelve))
@end example

@c this next section is confusing -- need to rewrite

The @emph{name} of a variable is also an expression, similar to a
number or a string.  It is entered as

@example
#'twentyFour
@end example

@funindex #'symbol
@cindex quoting in Scheme

The quote mark @code{'} prevents the Scheme interpreter from substituting
@code{24} for the @code{twentyFour}.  Instead, we get the name
@code{twentyFour}.
@end ignore


@node Scheme procedures
@subsection Scheme et les procédures

Une procédure Scheme est une expression Scheme qui renverra une valeur
issue de son exécution. Les procédures Scheme sont capables de manipuler
des variables qui ne sont pas définies en leur sein.


@node Defining procedures
@unnumberedsubsubsec Définition de procédures

En Scheme, on définit une procédure à l'aide de l'instruction
@code{define} :

@example
(define (nom-fonction argument1 argument2 @dots{} argumentn)
 expression-Scheme-qui-donnera-une-valeur-en-retour)
@end example

Nous pourrions, par exemple, définir une procédure calculant la moyenne
de deux nombres :

@lisp
guile> (define (moyenne x y) (/ (+ x y) 2))
guile> moyenne
#<procedure moyenne (x y)>
@end lisp

Une fois la procédure définie, on l'appelle en la faisant suivre, dans
une liste, des arguments qui doivent l'accompagner. Calculons maintenant
la moyenne de 3 et 12 :

@lisp
guile> (moyenne 3 12)
15/2
@end lisp


@node Predicates
@unnumberedsubsubsec Prédicats

Une procédure Scheme chargée de retourner une valeur booléenne s'appelle
un « prédicat » (@emph{predicate}). Par convention, plutôt que par
nécessité, le nom d'un prédicat se termine par un point
d'interrogation :

@lisp
guile> (define (moins-de-dix? x) (< x 10))
guile> (moins-de-dix? 9)
#t
guile> (moins-de-dix? 15)
#f
@end lisp


@node Return values
@unnumberedsubsubsec Valeurs de retour

Une procédure Scheme doit toujours renvoyer une valeur de retour, en
l'occurrence la valeur de la dernière expression exécutée par cette
procédure. La valeur de retour sera une valeur Scheme valide, y compris
une structure de donnée complexe ou une procédure.

On peut avoir besoin de regrouper plusieurs expressions Scheme dans une
même procédure. Deux méthodes permettent de combiner des expressions
multiples. La première consiste à utiliser la procédure @code{begin},
qui permet l'évaluation de plusieurs expressions et renvoie la valeur de
la dernière expression.

@lisp
guile> (begin (+ 1 2) (- 5 8) (* 2 2))
4
@end lisp

Une deuxième méthode consiste à combiner les expressions dans un bloc
@code{let}. Ceci aura pour effet de créer une série de liens, puis
d'évaluer en séquence les expressions susceptibles d'inclure ces liens.
La valeur renvoyée par un bloc @emph{let} est la valeur de retour de la
dernière clause de ce bloc :

@lisp
guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4))
@dots{} (+ (* x y) (/ z x)))
508
@end lisp


@need 1500
@node Scheme conditionals
@subsection Scheme et les conditions


@node if
@unnumberedsubsubsec if

Scheme dispose d'une procédure @code{if} :

@example
(if expression-test expression-affirmative expression-négative)
@end example

@var{expression-test} est une expression qui renverra une valeur
booléenne. Dans le cas où @var{expression-test} retourne @code{#t}, la
procédure @code{if} renvoie la valeur de @var{expression-affirmative},
et celle de @var{expression-négative} dans le cas contraire.

@lisp
guile> (define a 3)
guile> (define b 5)
guile> (if (> a b) "a est plus grand que b" "a n'est pas plus grand que b")
"a n'est pas plus grand que b"
@end lisp


@node cond
@unnumberedsubsubsec cond

Une autre manière d'introduire une condition en Scheme est d'utiliser
l'instruction @code{cond} :

@example
(cond (expression-test-1 expression-résultat-séquence-1)
      (expression-test-2 expression-résultat-séquence-2)
      @dots{}
      (expression-test-n expression-résultat-séquence-n))
@end example

Comme par exemple ici :

@lisp
guile> (define a 6)
guile> (define b 8)
guile> (cond ((< a b) "a est plus petit que b")
@dots{}          ((= a b) "a égale b")
@dots{}          ((> a b) "a est plus grand que b"))
"a est plus petit que b"
@end lisp


@need 1500
@node Scheme in LilyPond
@section Scheme et LilyPond


@node LilyPond Scheme syntax
@subsection Syntaxe Scheme dans LilyPond

@funindex $
@funindex #

L'installation de LilyPond comprenant l'interpréteur Guile, les fichiers
source LilyPond peuvent contenir du Scheme.  Vous disposez de plusieurs
méthodes pour inclure du Scheme dans vos fichiers LilyPond.

La méthode la plus simple consiste à insérer un @emph{hash} (le
caractère @code{#}, improprement appelé dièse) avant l'expression
Scheme.

Rappelons-nous qu'un fichier source LilyPond est structuré en jetons et
expressions, tout comme le langage humain est structuré en mots et
phrases. LilyPond dispose d'un analyseur lexical (appelé @emph{lexer})
qui sait identifier les jetons -- nombres, chaînes, éléments Scheme,
hauteurs, etc. -- ainsi que d'un analyseur syntaxique (appelé
@emph{parser}). Dès lors que le programme sait quelle règle grammaticale
particulière doit s'appliquer, il exécute les consignes qui lui sont
associées.

Le recours à un @emph{hash} pour mettre en exergue du Scheme est tout à
fait approprié. Dès qu'il rencontre un @code{#}, l'analyseur lexical
passe le relais au lecteur Scheme qui va alors déchiffrer l'intégralité
de l'expression Scheme -- ce peut être un identifiant, une expression
bornée par des parenthèses ou bien d'autres choses encore. Une fois
cette expression lue, elle est enregistrée en tant que valeur d'un
élément grammatical @code{SCM_TOKEN}. Puisque l'analyseur syntaxique
sait comment traiter ce jeton, il charge Guile d'évaluer l'expression
Scheme. Dans la mesure où le @emph{parser} requiert une lecture en
avance de la part du @emph{lexer} pour prendre une décision, cette
distinction entre lecture et évaluation -- @emph{lexer} et @emph{parser}
-- révèle toute sa pertinence lorsqu'il s'agit d'exécuter conjointement
des expressions LilyPond et des expressions Scheme. C'est la raison pour
laquelle nous vous recommandons, dans toute la mesure du possible,
d'utiliser un signe @emph{hash} lorsque vous faites appel à Scheme.

Une autre manière de faire appel à l'interpréteur Scheme à partir de
LilyPond consiste à introduire une expression Scheme par un caractère
dollar au lieu d'un caractère dièse -- un @code{$} au lieu
d'un @code{#}. En pareil cas, LilyPond évalue le code dès sa lecture par
l'analyseur lexical, vérifie le type d'expression Scheme qui en résulte
et détermine un type de jeton (l'un des @code{xxx_IDENTIFIER} de la
grammaire) qui lui correspond, puis en fait une copie qui servira à
traiter la valeur de ce jeton. Lorsque la valeur de l'expression est
@emph{void}, autrement dit une valeur Guile @code{*unspecified*} (pour
@emph{non spécifiée}), aucune information n'est transmise à l'analyseur
grammatical.

C'est, en réalité, la manière dont LilyPond opère lorsque vous rappelez
une variable ou une fonction par son nom -- au travers d'un @code{\nom}
--, à la seule différence que sa finalité est déterminée par l'analyseur
lexical de LilyPond sans consultation du lecteur Scheme ; le nom de la
variable rappelée doit donc être en corrélation avec le mode LilyPond
actif à ce moment là.

L'immédiateté de l'opérateur @code{$} peut entraîner des effets
indésirables dont nous reparlerons à la rubrique
@ref{Input variables and Scheme} ; aussi est-il préférable d'utiliser
un @code{#} dès que l'analyseur grammatical le supporte. Dans le cadre
d'une expression musicale, une expression qui aura été créée à l'aide
d'un @code{#} sera interprétée comme étant de la musique. Elle ne sera
cependant pas recopiée avant utilisation. Si la structure qui l'abrite
devait être réutilisée, un appel explicite à @code{ly:music-deep-copy}
pourrait être requis.

@funindex $@@
@funindex #@@

Les opérateurs @code{$@@} et @code{#@@} agissent comme des « colleurs de
liste » : leur fonction consiste à insérer tous les éléments d'une liste
dans le contexte environnant.

Examinons à présent du vrai code Scheme. Nous pouvons définir des
procédures Scheme au milieu d'un fichier source LilyPond :

@example
#(define (moyenne a b c) (/ (+ a b c) 3))
@end example

Pour mémoire, vous noterez que les commentaires LilyPond (@code{%} ou
@code{%@{…%@}}) ne peuvent s'utiliser dans du code Scheme, même si
celui-ci se trouve au sein d'un fichier LilyPond. Ceci tient au fait que
l'expression Scheme est lue par l'interpréteur Guile, et en aucune façon
par l'analyseur lexical de LilyPond. Voici comment introduire des
commentaires dans votre code Scheme :

@example
; ceci n'est qu'une simple ligne de commentaire

#!
  Ceci constitue un bloc de commentaire (non imbricable)
  dans le style Guile.
  En fait, les Schemeurs les utilisent très rarement,
  et vous n'en trouverez jamais dans le code source
  de LilyPond.
!#
@end example

Dans la suite de notre propos, nous partons du principe que les données
sont incluses dans un fichier musical, aussi toutes les expressions
Scheme seront introduites par un @code{#}.

Toutes les expressions Scheme de haut niveau incluses dans un fichier
LilyPond peuvent se combiner en une expression Scheme unique à l'aide de
la clause @code{begin} :

@example
#(begin
  (define foo 0)
  (define bar 1))
@end example


@node LilyPond variables
@subsection Variables LilyPond

Les variables LilyPond sont enregistrées en interne sous la forme de
variables Scheme.  Ainsi,

@example
douze = 12
@end example

@noindent
est équivalant à

@example
#(define douze 12)
@end example

Ceci a pour conséquence que toute variable LilyPond peut être utilisée
dans une expression Scheme.  Par exemple, nous pourrions dire

@example
vingtQuatre = #(* 2 douze)
@end example

@noindent
ce qui aurait pour conséquence que le nombre @emph{24} sera stocké dans
la variable LilyPond (et Scheme) @code{vingtQuatre}.

Scheme autorise la modification d'expressions complexes au fil de l'eau,
ce que réalise LilyPond dans le cadre des fonctions musicales.
Toutefois, lorsqu'une expression musicale est stockée dans une variable
plutôt que saisie au fur et à mesure, on s'attend, alors qu'elle est
passée à une fonction musicale, à ce que sa valeur originale ne soit en
rien modifiée. C'est la raison pour laquelle faire référence à une
variable à l'aide d'une oblique inverse -- autrement dit saisir
@code{\vingtQuatre} -- aura pour effet que LilyPond créera une copie de
la valeur musicale de cette variable aux fins de l'utiliser au sein de
l'expression musicale au lieu d'utiliser directement la valeur de cette
variable.

Par voie de conséquence, une expression musicale introduite par @code{#}
ne devrait pas contenir de matériau inexistant auparavant ou bien
littéralement recopié, mais plutôt une référence explicite.

@morerefs
Manuel d'extension :
@ref{LilyPond Scheme syntax}.
@endmorerefs


@node Debugging Scheme code
@subsection Débogage de code Scheme

@cindex débogage, code Scheme
@cindex Scheme, débogage
@cindex localisation du code, message d'erreur Scheme
@cindex erreur Scheme, localisation du code
@cindex ligne, localisation, message d'erreur Scheme

Lorsque l'on débogue de larges portions de code Scheme, il est bien
appréciable que soit mise en évidence la ligne du programme à la source
même de l'erreur. LilyPond permet la localisation dans les sources pour
les erreurs Scheme dès lors qu'est ajoutée à la ligne de commande
l'option @option{-dcompile-@/Scheme-@/code}. L'ajout d'une ligne
@code{#(ly:set-@/option 'compile-@/Scheme-@/code)} en tête d'un fichier
LilyPond aura le même effet.

Par ailleurs, il est possible d'obtenir encore plus d'informations au
sujet de l'erreur grâce à l'option @option{-ddebug-@/eval} -- ou une
ligne @code{#(debug-@/enable 'backtrace)} dans le fichier. Grâce à ce
mode, et dès la survenance d'une erreur, s'affiche en mode verbeux une
« trace inverse » contenant des informations sur tous les appels de
fonction, ce qui permet de remonter le fil jusqu'à la pierre
d'achoppement.

@knownissues
En raison d'une limitation dans l'implémentation de Guile, l'option
@option{-dcompile-@/Scheme-@/code} ne permet pas de compiler des
fichiers LilyPond comportant au-delà de quelques milliers d'expressions
Scheme.


@node Input variables and Scheme
@subsection Saisie de variables et Scheme

Le format de saisie prend en charge la notion de variable -- ou
identifiant. Dans l'exemple suivant, une expression musicale se voit
attribuer un identifiant qui portera le nom de @code{traLaLa}.

@example
traLaLa = @{ c'4 d'4 @}
@end example

@noindent

Une variable a aussi une portée. Dans l'exemple suivant, le bloc
@code{\layout} contient une variable @code{traLaLa} tout à fait
indépendante de l'autre @code{\traLaLa}.

@example
traLaLa = @{ c'4 d'4 @}
\layout @{ traLaLa = 1.0 @}
@end example

Dans les faits, chaque fichier a un domaine de compétence, et les
différents blocs @code{\header}, @code{\midi} et @code{\layout} ont leur
propre champ de compétence, imbriqué dans ce domaine principal.

Variables et champs de compétence sont implémentés par le système de
modules de Guile. Un module anonyme Scheme est attaché à chacun de ces
domaines. Une assertion telle que

@example
traLaLa = @{ c'4 d'4 @}
@end example

@noindent
est convertie, en interne, en une définition Scheme :

@example
(define traLaLa @var{valeur Scheme de '@code{@dots{}}'})
@end example

Cela signifie que variables LilyPond et variables Scheme peuvent tout à
fait se mélanger. Dans l'exemple suivant, un fragment musical est stocké
dans la variable @code{traLaLa} puis dupliqué à l'aide de Scheme. Le
résultat est alors importé dans un bloc @code{\score} au moyen d'une
seconde variable @code{twice}.

@lilypond[verbatim]
traLaLa = { c'4 d'4 }

#(define newLa (map ly:music-deep-copy
  (list traLaLa traLaLa)))
#(define twice
  (make-sequential-music newLa))

\twice
@end lilypond

@c Due to parser lookahead

Cet exemple est particulièrement intéressant. L'assignation
n'interviendra qu'une fois que l'analyseur grammatical aura l'assurance
que rien du type de @code{\addlyrics} ne suit ; il doit donc vérifier ce
qui vient après. Le @emph{parser} lit le @code{#} et l'expression Scheme
qui le suit @strong{sans} l'évaluer, de telle sorte qu'il peut procéder
à l'assignation, et @strong{ensuite} exécuter le code Scheme sans
problème.


@node Importing Scheme in LilyPond
@subsection Import de code Scheme dans LilyPond

@funindex $
@funindex #

L'exemple précédent illustre la manière « d'exporter » une expression
musicale à partir des saisies et à destination de l'interpréteur Scheme.
L'inverse est aussi réalisable : en la plaçant derrière un @code{$}, une
valeur Scheme sera interprétée comme si elle avait été saisie en syntaxe
LilyPond. Au lieu de définir @code{\twice}, nous aurions tout aussi bien
pu écrire

@example
@dots{}
$(make-sequential-music newLa)
@end example

Vous pouvez utiliser @code{$} suivi d'une expression Scheme partout où
vous auriez utilisé @code{\@var{nom}}, dès lors que vous aurez assigné à
cette expression Scheme le nom de variable @var{nom}. La substitution
intervenant au niveau de l'analyseur lexical (le @emph{lexer}), LilyPond
ne saurait faire la différence.

Cette manière de procéder comporte cependant un inconvénient au niveau
de la temporisation. Si nous avions défini @code{newLa} avec un @code{$}
plutôt qu'un @code{#}, la définition Scheme suivante aurait échoué du
fait que @code{traLaLa} n'était pas encore défini. Pour plus
d'information quant au problème de synchronisation, voir la rubrique
@ref{LilyPond Scheme syntax}.

@funindex $@@
@funindex #@@

Une autre façon de procéder serait de recourir aux « colleurs de liste »
@code{$@@} et @code{#@@} dont la fonction est d'insérer les éléments
d'une liste dans le contexte environnant. Grâce à ces opérateurs, la
dernière partie de notre fonction pourrait s'écrire ainsi :

@example
@dots{}
@{ #@@newLa @}
@end example

Ici, chaque élément de la liste stockée dans @code{newLa} est pris à
son tour et inséré dans la liste, tout comme si nous avions écrit

@example
@{ #(premier newLa) #(deuxième newLa) @}
@end example

Dans ces deux dernières formes, le code Scheme est évalué alors même que
le code initial est en cours de traitement, que ce soit par le
@emph{lexer} ou par le @emph{parser}.  Si le code Scheme ne doit être
exécuté que plus tard, consultez la rubrique
@ref{Void Scheme functions}, ou stockez-le dans une procédure comme
ici :

@example
#(define (nopc)
  (ly:set-option 'point-and-click #f))

@dots{}
#(nopc)
@{ c'4 @}
@end example


@node Object properties
@subsection Propriétés des objets

Les propriétés des objets sont stockées dans LilyPond sous la forme
d'enchaînements de listes associatives, autrement dit des listes de
listes associatives. Une propriété se détermine par l'ajout de valeurs
en début de liste de cette propriété. Les caractéristiques d'une
propriété s'ajustent donc à la lecture des différentes valeurs des
listes associatives.

La modification d'une valeur pour une propriété donnée requiert
l'assignation d'une valeur de la liste associative, tant pour la clé que
pour la valeur associée. Voici comment procéder selon la syntaxe de
LilyPond :

@example
\override Stem.thickness = #2.6
@end example

Cette instruction ajuste l'apparence des hampes. Une entrée
@w{@code{'(thickness . 2.6)}} de la @emph{alist} est ajoutée à la liste
de la propriété de l'objet @code{Stem}. @code{thickness} devant
s'exprimer en unité d'épaisseur de ligne, les hampes auront donc une
épaisseur de 2,6 lignes de portée, et à peu près le double de leur
épaisseur normale. Afin de faire la distinction entre les variables que
vous définissez au fil de vos fichiers -- tel le @code{vingtQuatre} que
nous avons vu plus haut -- et les variables internes des objets, nous
parlerons de « propriétés » pour ces dernières, et de « variables » pour
les autres. Ainsi, l'objet hampe possède une propriété @code{thickness},
alors que @code{vingtQuatre} est une variable.

@cindex propriétés ou variables
@cindex variables ou propriétés


@c  todo -- here we're getting interesting.  We're now introducing
@c  LilyPond variable types.  I think this deserves a section all
@c  its own


@need 1500
@node LilyPond compound variables
@subsection Variables LilyPond composites


@node Offsets
@unnumberedsubsubsec Décalages (@emph{offsets})

Les décalages (@emph{offset}) sur deux axes (coordonnées X et Y) sont
stockés sous forme de @emph{paires}. Le @code{car} de l'offset
correspond à l'abscisse (coordonnée X) et le @code{cdr} à l'ordonnée
(coordonnée Y).

@example
\override TextScript.extra-offset = #'(1 . 2)
@end example

Cette clause affecte la paire @code{(1 . 2)} à la propriété
@code{extra-offset} de l'objet @code{TextScript}. Ces nombres sont
exprimés en espace de portée. La commande aura donc pour effet de
déplacer l'objet d'un espace de portée vers la droite, et de deux
espaces vers le haut.

Les procédures permettant de manipuler les offsets sont regroupées dans
le fichier @file{scm/lily-library.scm}.


@node Fractions
@unnumberedsubsubsec Fractions

Les fractions, telles que LilyPond les utilise, sont aussi stockées sous
forme de @emph{paire}. Alors que Scheme est tout à fait capable de
représenter des nombres rationnels, vous conviendrez que, musicalement
parlant, @samp{2/4} et @samp{1/2} ne se valent pas ; nous devrons donc
pouvoir les distinguer. Dans le même ordre d'idée, LilyPond ne connaît
pas les « fractions » négatives. Pour ces raisons, @code{2/4} en
LilyPond correspond à @code{(2 . 4)} en Scheme, et @code{#2/4} en
LilyPond correspond à @code{1/2} en Scheme.


@node Extents
@unnumberedsubsubsec Étendues (@emph{extents})

Les paires permettent aussi de stocker des intervalles qui représentent
un ensemble de nombres compris entre un minimum (le @code{car}) et un
maximum (le @code{cdr}). Ces intervalles stockent l'étendue, tant au
niveau horizontal (X) que vertical (Y) des objets imprimables. En
matière d'étendue sur les X, le @code{car} correspond à la coordonnée de
l'extrémité gauche, et le @code{cdr} à la coordonnée de l'extrémité
droite. En matière d'étendue sur les Y, le @code{car} correspond à la
coordonnée de l'extrémité basse, et le @code{cdr} à la coordonnée de
l'extrémité haute.

Les procédures permettant de manipuler les offsets sont regroupées dans
le fichier @file{scm/lily-library.scm}. Nous vous recommandons
l'utilisation de ces procédures dans toute la mesure du possible afin
d'assurer la cohérence du code.


@node Property alists
@unnumberedsubsubsec Propriété en @emph{alist}

Les propriétés en @emph{alist} sont des structures de données
particulières à LilyPond. Il s'agit de listes associatives dont les clés
sont des propriétés et les valeurs des expressions Scheme fournissant la
valeur requise pour cette propriété.

Les propriétés LilyPond sont des symboles Scheme, à l'instar de
@code{'thickness}.


@node Alist chains
@unnumberedsubsubsec Chaînes d'@emph{alist}

Une chaîne d'@emph{alist} est une liste contenant les listes
associatives d'une propriété.

L'intégralité du jeu de propriétés qui doivent s'appliquer à un objet
graphique est en fait stocké en tant que chaîne d'@emph{alist}. Afin
d'obtenir la valeur d'une propriété particulière qu'un objet graphique
devrait avoir, on examinera chacune des listes associatives de la
chaîne, à la recherche d'une entrée contenant la clé de cette propriété.
Est renvoyée la première entrée d'@emph{alist} trouvée, sa valeur étant
la valeur de la propriété.

L'obtention des valeurs de propriété des objets graphiques se réalise en
principe à l'aide de la procédure Scheme @code{chain-assoc-get}.


@node LilyPond's box model
@subsection Le modèle de boîte de LilyPond

Le modèle de boîte « classique » pour du texte -- tel qu'en vigueur par
exemple chez @TeX{} -- comporte les trois dimensions @dfn{largeur},
@dfn{hauteur} et @dfn{profondeur} : les glyphes sont positionnés sur une
ligne de base horizontale de texte ; la hauteur d'un glyphe est l'empan
vertical au-dessus de cette ligne, la profondeur l'empan au-dessous de
cette ligne et la largeur représente l'étendue horizontale de ce glyphe.
LilyPond y ajoute une quatrième dimension, @dfn{breapth},@footnote{Il
s'agit d'un terme inventé, dérivé du mot anglais @emph{« breadth »}
(ampleur, laize) et qui est un proche synonyme de « largeur ».} pour
étendre la taille horizontale quelque peu sur la gauche du « point de
référence » du glyphe, noté « (0, 0) » dans l'image ci-dessous. La
raison principale à cette extension tient au fait que de nombreux
glyphes, tel que le point d'orgue, ont une symétrie horizontale (et sont
aussi attachés symétriquement à une tête de note) et que le meilleur
point de référence de tels glyphes est par conséquent le centre
horizontal. D'autres glyphes, comme par exemple l'altération de quart de
ton présent sur la gauche de l'image, disposent quant à eux d'un
@emph{breapth} considérable.

@lilypond[quote]
#(define-markup-command (point layout props x y xoff yoff)
   (number? number? number? number?)
   (interpret-markup
    layout props
    (make-overlay-markup
     (list
      (make-translate-markup
       (cons x y)
       (make-draw-circle-markup 0.4 0 #t))
      (make-translate-markup
       (cons (+ x xoff) (+ y yoff))
       (make-concat-markup
        (list "(" (number->string x) ", " (number->string y) ")")))))))

\markup \scale #'(0.8 . 0.8) {
  \overlay {
    \scale #'(18 . 16) \with-color #(x11-color "gray80")
      \musicglyph "accidentals.flatflat.slash"

    \override #'((filled . #f)
                 (thickness . 2))
      \polygon #'((-9 . -10) (-9 . 29) (26 . 29) (26 . -10))

    \translate #'(-15 . 0) \draw-line #'(47 . 0)
    \translate #'(32 . 0) \arrow-head #X #RIGHT ##t
    \translate #'(31 . -2) \italic "x"
    \translate #'(0 . -15) \draw-line #'(0 . 50)
    \translate #'(0 . 35) \arrow-head #Y #UP ##t
    \translate #'(-2 . 34) \italic "y"

    \point #0 #0 #0.5 #0.8
    \point #-9 #-10 #0.5 #0.8
    \point #26 #29 #-3 #1

    \translate #'(-13.5 . -5.5) \rotate #90 \italic "profondeur"
    \translate #'(-13.5 . 13) \rotate #90 \italic "hauteur"
    \translate #'(-8.5 . -12.2) \italic "breapth"
    \translate #'(9 . -12.2) \italic "largeur"
  }

  \hspace #1

  \overlay {
    \scale #'(16 . 16) \with-color #(x11-color "gray80")
      \musicglyph "custodes.mensural.u0"

    \override #'((filled . #f)
                 (thickness . 2))
      \polygon #'((0 . -5.8) (0 . 32) (21 . 32) (21 . -5.8))

    \translate #'(-6 . 0) \draw-line #'(50 . 0)
    \translate #'(44 . 0) \arrow-head #X #RIGHT ##t
    \translate #'(43 . -2) \italic "x"
    \translate #'(0 . -11) \draw-line #'(0 . 49)
    \translate #'(0 . 39) \arrow-head #Y #UP ##t
    \translate #'(-2 . 38) \italic "y"

    \point #0 #0 #0.5 #0.8
    \point #0 #-5.8 #0.5 #0.8
    \point #21 #32 #-3 #1

    \translate #'(-4.5 . -3.7) \rotate #90 \italic "profondeur"
    \translate #'(-4.5 . 15) \rotate #90 \italic "hauteur"
    \translate #'(7 . -8) \italic "largeur"
  }
}
@end lilypond

Comme l'indique le glyphe de guidon sur la droite de l'image, la boîte
définie pour un glyphe diffère souvent et de façon considérable avec ce
qui est en fait imprimé : LilyPond positionne le côté droit du glyphe de
guidon à la fin d'une portée, faisant dépasser la ligne d'extension à
droite dans la marge.

Ce modèle de boîte sert non seulement aux glyphes musicaux de LilyPond,
mais aussi pour les @emph{markups} quelle qu'en soit la nature. Les
dimensions, tant horizontale que verticale, sont spécifiées en terme
d'étendue ; utilisant la notation Scheme, le glyphe de gauche dans
l'image ci-dessus a une étendue horizontale de @code{#'(-9 . 26)} et une
étendue verticale de @code{#'(-10 . 29)}.

LilyPond utilise aussi quelques extensions au modèle de boîte afin de
couvrir certains cas particuliers.

@itemize
@item
Lorsqu'une boîte de devrait pas intervenir dans l'algorithme
d'espacement de la musique sur un axe donné, l'étendue sur cet axe est
fixé à la valeur de @code{#'(+inf.0 . -inf.0)} qui ailleurs serait
absurde, aussi disponible en tant que symbole @code{empty-interval}.

@item
En matière de @emph{markup}, l'@dfn{espacement}, qui normalement
déclenche un déplacement horizontal ou vertical de l'objet qui suit dans
le @emph{markup}, est aussi fixé de manière interne en tant que boîte :
l'étendue sur l'axe orthogonal est définie à @code{empty-interval} en
tant que marqueur, et une valeur @var{s} d'espacement est représentée
par l'étendue @code{#'(0 . @var{s})}. Contrairement aux boîtes
« normales », l'espacement peut aussi être négatif : par exemple,
l'étendue horizontale de la commande de @emph{markup} @code{\hspace #-4}
est @code{#'(0 . -4)}.
@end itemize


@node Internal music representation
@subsection Représentation interne de la musique

Dans les entrailles du programme, la musique se présente comme une liste
Scheme. Cette liste comporte les différents éléments qui affecteront la
sortie imprimable. L'analyse grammaticale (l'opération @emph{parsing})
est le processus chargé de convertir la musique représentée par le code
LilyPond en présentation interne Scheme.

L'analyse d'une expression musicale se traduit par un jeu d'objets
musicaux en Scheme. Une objet musical est déterminé par le temps qu'il
occupe, que l'on appelle @emph{durée}. Les durées s'expriment par des
nombres rationnels représentant la longueur d'un objet musical par
rapport à la ronde.

Un objet musical dispose de trois types :
@itemize
@item
un nom de musique : toute expression musicale a un nom. Par exemple, une
note amène à un @rinternals{NoteEvent}, un @code{\simultaneous} à un
@rinternals{SimultaneousMusic}. Une liste exhaustive des différentes
expressions est disponible dans la référence des propriétés internes, à
la rubrique @rinternals{Music expressions}.

@item
un « type » ou interface : tout nom de musique dispose de plusieurs
types ou interfaces. Ainsi, une note est tout à la fois un @code{event},
un @code{note-event}, un @code{rhythmic-event} et un
@code{melodic-event}. Les différentes classes musicales sont
répertoriées à la rubrique @rinternals{Music classes} de la référence
des propriétés internes.

@item
un objet C++ : tout objet musical est représenté par un objet de la
classe C++ @code{Music}.
@end itemize

L'information réelle d'une expression musicale est enregistrée sous
forme de propriétés. Par exemple, un @rinternals{NoteEvent} dispose des
propriétés @code{pitch} et @code{duration}, respectivement chargées de
stocker la hauteur et la durée de cette note. Les différentes propriétés
sont répertoriées à la rubrique @rinternals{Music properties} de la
référence des propriétés internes.

Une expression composite est un objet musical dont les propriétés
contiennent d'autres objets musicaux. S'il s'agit d'une liste d'objets,
elle sera stockée dans la propriété @code{elements} d'un objet musical ;
s'il n'y a qu'un seul objet « enfant », il sera stocké dans la propriété
@code{element}. Ainsi, par exemple, les enfants de
@rinternals{SequentialMusic} iront dans @code{elements}, alors que
l'argument unique de @rinternals{GraceMusic} ira dans @code{element}. De
même, le corps d'une répétition ira dans la propriété @code{element}
d'un @rinternals{VoltaRepeatedMusic}, les alternatives quant à elles dans la
propriété @code{elements}.


@node Building complicated functions
@section Construction de fonctions complexes

Nous allons voir dans cette partie les moyens dont vous disposez pour
obtenir les informations qui vous permettront de créer vos propres
fonctions musicales complexes.


@node A first approach (example)
@subsection Première approche (exemple)

@enumerate
@item @b{La problématique}

Partant du code suivant, l'indication « espressivo » (les deux petits
soufflets) devrait être plus large, mais pas gagner en hauteur.

@lilypond[verbatim,quote]
{ f'\espressivo }
@end lilypond

@item @b{Les solutions envisageables}

@enumerate A
@item créer un nouveau stencil
@item redimensionner le stencil par défaut sur l'axe des X
@end enumerate

@item @b{Réflexions sur le point précédent}

La solution A est réalisable, mais demandera plus d'effort de codage.
Quant à la solution B, bien que moins exigeante, elle a le désavantage
de ne pas donner de résultats acceptables dans tous les cas -- mais on
ne le saura pas avant d'avoir essayé.

@result{} opter pour la solution B.

@item @b{Codage}

@itemize
@item
Si l'on veut manipuler le stencil par défaut, il faut s'informer à son
sujet.

L'indication d'espressivo est un objet graphique @code{Script}. En
consultant la référence des propriétés internes, on lit

@example
‘stencil’ (stencil):
     ‘ly:script-interface::print’
@end example

Voyons voir si tout cela marche. Pour être assurés d'affecter le bon
@emph{grob}, nous utiliserons des couleurs. Il existe une fonction
prédéfinie à cet effet -- voir @rinternals{Scheme functions}.

@funindex stencil-with-color

@example
-- Fonction: stencil-with-color stencil couleur
    Renvoie une version modifiée du stencil donné, teint
    dans la couleur donnée. Voir ‘normalize-color’ pour
    les formats de couleur possibles.
@end example

@funindex normalize-color

@example
-- Fonction: normalize-color couleur
    Convertit une couleur donnée dans l'un des formats pris en
    charge en une liste de 4 nombres : R, G, B, A. Les formats
    possibles sont : une liste de 4 nombres ; une liste de 3
    nombres (transparence par défaut à 1.0) ; une chaîne CSS
    (nom de couleur, ou “#RRGGBB”, “#RRGGBBAA”, “#RGB” ou
    “#RGBA”).
@end example

Utilisant un nom de couleur (@samp{red}), nous obtenons le code
suivant :

@lilypond[verbatim,quote]
colorDefaultStencil =
\once \override Script.stencil =
  % `lambda` starts a procedure; its argument is `grob`.
  #(lambda (grob)
     ;; With `let`, local variables are defined.
     (let ((stil (ly:script-interface::print grob)))
       ;; The procedure returns the colored default stencil.
       (stencil-with-color stil red)))

{ \colorDefaultStencil f'\espressivo }
@end lilypond

Parfait, cela fonctionne.

@item
Ce type d'opération -- prendre  un stencil ou une propriété par défaut
et en retourner une version modifiée -- est très courante, et LilyPond
dispose d'une fonction dédiée à cet effet : @code{grob-transformer}.

@funindex grob-transformer

@example
-- Function: grob-transformer property func
    Create an override value good for applying FUNC to either
    pure or unpure values.  FUNC is called with the
    respective grob as first argument and the default value
    (after resolving all callbacks) as the second.
@end example

Laissons-nous tenter. Les concepts de pur ou impur seront vus plus
avant, dans @ref{Unpure-pure containers} ; ignorons-les pour l'instant.

@lilypond[verbatim,quote]
colorDefaultStencil =
\once \override Script.stencil =
  #(grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color orig red)))

{ \colorDefaultStencil f'\espressivo }
@end lilypond

Excellent !

@item
Pour obtenir un stencil redimensionné, il faut utiliser

@funindex ly:stencil-scale

@example
-- Function: ly:stencil-scale stil x y
    Scale stencil STIL using the horizontal and vertical
    scaling factors X and optional Y (defaulting to X).
    Negative values flip or mirror STIL without changing its
    origin; this may result in collisions unless it is
    repositioned.
@end example

@noindent
à partir du même chapitre de la référence des propriétés internes.

Résultat :

@lilypond[verbatim,quote]
scaleColorDefaultStencil =
\once \override Script.stencil =
  #(grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color
        (ly:stencil-scale
         orig
         ;; 1 is the neutral element with ly:stencil-scale,
         ;; i.e., scaling with '1 1' (for x- and y-axis)
         ;; returns a visibly unchanged stencil.
         2 1)
        red)))

{ \scaleColorDefaultStencil f'\espressivo }
@end lilypond

Mis à part la couleur, cela ressemble fort à ce que l'on voulait.

@item
Certains problèmes subsistent cependant, comme le montre l'exemple
suivant :

@example
@{ \scaleColorDefaultStencil f'_\espressivo ^\fermata @}
@end example

@lilypond[quote]
scaleColorDefaultStencil =
\once \override Script.stencil =
  #(grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color
        (ly:stencil-scale orig 2 1)
        red)))

{ \scaleColorDefaultStencil f'_\espressivo ^\fermata }
@end lilypond

@itemize @minus
@item
Le point d'orgue est redimensionné lui aussi.
@item
Devoir taper la commande avant les notes et @code{\espressivo}
après ces notes n'est pas pratique.
@end itemize

Solution aux deux inconvénients : utilisons @code{\tweak} !

@lilypond[verbatim,quote]
{
  f'-\tweak stencil
      #(grob-transformer 'stencil
         (lambda (grob orig)
           (stencil-with-color
            (ly:stencil-scale orig 2 1)
            red)))
      _\espressivo
    ^\fermata
}
@end lilypond

Cela fait ce qui est demandé, mais autant de saisie n'est pas la
panacée ; pourquoi ne pas définir une variable que l'on utiliserait dans
le @emph{tweak} ? Après un peu de copier coller, nous obtenons

@lilypond[verbatim,quote]
#(define longer-script
   (grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color
        (ly:stencil-scale orig 2 1)
        red))))

{
  f'-\tweak stencil #longer-script _\espressivo
    ^\fermata
}
@end lilypond

Cela fonctionne correctement, bien que les valeurs soient codées en dur
et que taper @code{-\tweak stencil #longer-script} sans arrêt soit
rébarbatif.

Pour régler cela, commençons par introduire des variables dans
@code{longer-script} pour les valeurs de redimensionnement x et y.

@example
#(define (longer-script x y)
   (grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color
        (ly:stencil-scale orig x y)
        red))))
@end example

Ensuite, et pour réduire la frappe, nous définissons une fonction
événementielle -- voir @ref{Event functions}. Les éléments du premier
groupe entre parenthèses -- @code{(x-val y-val)} -- sont les variables
de la fonction, et les éléments du second groupe entre parenthèses sont
les types respectifs de variable (on cherche deux fois un nombre -- voir
@rnotationnamed{Predefined type predicates, Types de prédicat
prédéfinis} pour les possibilités de test).

@funindex #@{ @dots{} #@}

@example
scaleEspr =
#(define-event-function (x-val y-val) (number? number?)
  #@{
    \tweak stencil #(longer-script x-val y-val)
    \espressivo
  #@})

@{ f'_\scaleEspr 2 1
     ^\fermata @}
@end example

@lilypond[quote]
#(define (longer-script x y)
   (grob-transformer 'stencil
     (lambda (grob orig)
       (stencil-with-color
        (ly:stencil-scale orig x y)
        red))))

scaleEspr =
#(define-event-function (x-val y-val) (number? number?)
  #{
    \tweak stencil #(longer-script x-val y-val)
    \espressivo
  #})

{ f'_\scaleEspr 2 1
    ^\fermata }
@end lilypond

Cela fonctionne à merveille ! Notez l'utilisation de @code{#@{ @dots{}
#@}} : dans cette construction Scheme, il est tout à fait possible
d'utiliser la syntaxe de LilyPond, et les arguments de la fonction, tel
que @var{x-val}, sont correctement expansés -- voir
@ref{LilyPond code blocks}.

@end itemize

@item @b{Le code finalisé}

Pour terminer, faisons un peu de ménage.

@itemize @minus
@item
Nous souhaitons redimensionner uniquement dans l'horizontalité ; nous
pouvons donc forcer le redimensionnement vertical dans la fonction
événementielle.
@item
Supprimons la couleur.
@end itemize

Ceci nous amène au code suivant.

@lilypond[verbatim,quote]
#(define (longer-script x y)
   (grob-transformer 'stencil
     (lambda (grob orig)
       (ly:stencil-scale orig x y))))

longEspressivo =
#(define-event-function (x-val) (number?)
  #{
    \tweak stencil #(longer-script x-val 1)
    \espressivo
  #})

{ f'^\longEspressivo #2 _\fermata }
@end lilypond

Voilà !

Le principal bénéfice à l'utilisation de @code{grob-transformer} réside
dans le fait que nul n'est besoin de connaître le nom exact de la
fonction de stencil. Dans l'exemple qui suit, nous pivotons une liaison
et une ligature de 90 degrés -- que les fonction de stencil s'appellent
respectivement @code{ly:slur::print} et @code{ly:beam::print} n'est
d'aucun intérêt en l'occurrence.

@lilypond[verbatim,quote]
#(define rotate-90
   (grob-transformer 'stencil
     (lambda (grob orig)
       (ly:stencil-rotate orig 90 0 0))))

{ f'8( g')
  f'8-\tweak stencil #rotate-90 ( g')
  f'8[ g']
  f'8-\tweak stencil #rotate-90 [ g'] }
@end lilypond


@end enumerate


@node Displaying music expressions
@subsection Affichage d'expressions musicales

@cindex stockage interne
@cindex expression musicale, affichage
@cindex représentation interne, affichage
@cindex displayMusic
@funindex \displayMusic

Lorsque l'on veut écrire une fonction musicale, il est intéressant
d'examiner comment une expression musicale est représentée en interne.
Vous disposez à cet effet de la fonction musicale @code{\displayMusic}.

@example
@{
  \displayMusic @{ c'4\f @}
@}
@end example

@noindent
affichera

@example
(make-music
  'SequentialMusic
  'elements
  (list (make-music
          'NoteEvent
          'articulations
          (list (make-music
                  'AbsoluteDynamicEvent
                  'text
                  "f"))
          'duration
          (ly:make-duration 2 0 1/1)
          'pitch
          (ly:make-pitch 0 0 0))))
@end example

Par défaut, LilyPond affichera ces messages sur la console, parmi toutes
les autres informations. Vous pouvez, afin de les isoler et de garder le
résultat des commandes @code{\display@{TRUC@}}, spécifier un port
optionnel à utiliser pour la sortie :

@example
@{
  \displayMusic #(open-output-file "display.txt") @{ c'4\f @}
@}
@end example

Ceci aura pour effet d'écraser tout fichier précédemment généré. Lorsque
plusieurs expressions doivent être retranscrites, il suffit de faire
appel à une variable pour le port puis de la réutiliser :

@example
@{
  port = #(open-output-file "display.txt")
  \displayMusic \port @{ c'4\f @}
  \displayMusic \port @{ d'4 @}
  #(close-output-port port)
@}
@end example

La documentation de Guile fournit une description détaillée des ports.
Clôturer un port n'est requis que si vous désirez consulter le fichier
avant que LilyPond n'ait fini, ce dont nous ne nous sommes pas préoccupé
dans le premier exemple.

L'information sera encore plus lisible après un peu de mise en forme :

@example
(make-music 'SequentialMusic
  'elements (list
	     (make-music 'NoteEvent
               'articulations (list
			       (make-music 'AbsoluteDynamicEvent
				 'text
				 "f"))
	       'duration (ly:make-duration 2 0 1/1)
	       'pitch    (ly:make-pitch 0 0 0))))
@end example

Une séquence musicale @code{@{ @dots{} @}} se voit attribuer le nom de
@code{SequentialMusic}, et les expressions qu'elle contient sont
enregistrées en tant que liste dans sa propriété @code{'elements}. Une
note est représentée par un objet @code{NoteEvent} -- contenant les
propriétés de durée et hauteur -- ainsi que l'information qui lui est
attachée -- en l'occurrence un @code{AbsoluteDynamicEvent} ayant une
propriété @code{text} de valeur @code{"f"} -- et stockée dans sa
propriété @code{articulations}.

@funindex \void
La fonction @code{\displayMusic} renvoie la musique qu'elle affiche ;
celle-ci sera donc aussi interprétée. L'insertion d'une commande
@code{\void} avant le @code{\displayMusic} permet de s'affranchir de la
phase d'interprétation.


@node Music properties
@subsection Propriétés musicales

Nous abordons ici les propriétés @emph{music}, et non pas les propriétés
@emph{context} ou @emph{layout}.

Partons de cet exemple simple :

@example
someNote = c'
\displayMusic \someNote
===>
(make-music
  'NoteEvent
  'duration
  (ly:make-duration 2 0 1/1)
  'pitch
  (ly:make-pitch 0 0 0))
@end example

L'objet @code{NoteEvent} est la représentation brute de @code{someNote}.
Voyons ce qui se passe lorsque nous plaçons ce @notation{c'} dans une
construction d'accord :

@example
someNote = <c'>
\displayMusic \someNote
===>
(make-music
  'EventChord
  'elements
  (list (make-music
          'NoteEvent
          'duration
          (ly:make-duration 2 0 1/1)
          'pitch
          (ly:make-pitch 0 0 0))))
@end example

L'objet @code{NoteEvent} est maintenant le premier objet de la propriété
@code{'elements} de @code{someNote}.

@code{\displayMusic} utilise la fonction @code{display-Scheme-music}
pour afficher la représentation en Scheme d'une expression musicale :

@example
#(display-Scheme-music (first (ly:music-property someNote 'elements)))
===>
(make-music
  'NoteEvent
  'duration
  (ly:make-duration 2 0 1/1)
  'pitch
  (ly:make-pitch 0 0 0))
@end example

La hauteur de la note est accessible au travers de la propriété
@code{'pitch} de l'objet @code{NoteEvent} :

@example
#(display-Scheme-music
   (ly:music-property (first (ly:music-property someNote 'elements))
                      'pitch))
===>
(ly:make-pitch 0 0 0)
@end example

La hauteur de la note se modifie en définissant sa propriété
@code{'pitch} :

@funindex \displayLilyMusic

@example
#(set! (ly:music-property (first (ly:music-property someNote 'elements))
                          'pitch)
       (ly:make-pitch 0 1 0)) ;; set the pitch to d'.
\displayLilyMusic \someNote
===>
d'4
@end example


@node Doubling a note with slurs (example)
@subsection Doublement d'une note avec liaison (exemple)

Supposons que nous ayons besoin de créer une fonction transformant une
saisie @code{a} en @w{@code{@{ a( a) @}}}. Commençons par examiner
comment le résultat est représenté en interne.

@example
\displayMusic@{ a'( a') @}
===>
(make-music
  'SequentialMusic
  'elements
  (list (make-music
          'NoteEvent
          'articulations
          (list (make-music
                  'SlurEvent
                  'span-direction
                  -1))
          'duration
          (ly:make-duration 2 0 1/1)
          'pitch
          (ly:make-pitch 0 5 0))
        (make-music
          'NoteEvent
          'articulations
          (list (make-music
                  'SlurEvent
                  'span-direction
                  1))
          'duration
          (ly:make-duration 2 0 1/1)
          'pitch
          (ly:make-pitch 0 5 0))))
@end example

Mauvaise nouvelle ! Les expressions @code{SlurEvent} doivent s'ajouter
« à l'intérieur » de la note -- dans sa propriété @code{articulations}.

Examinons à présent la saisie :

@example
\displayMusic a'
===>
(make-music
  'NoteEvent
  'duration
  (ly:make-duration 2 0 1/1)
  'pitch
  (ly:make-pitch 0 5 0))))
@end example

Nous aurons donc besoin, dans notre fonction, de cloner cette expression
-- de telle sorte que les deux notes constituent la séquence -- puis
d'ajouter un @code{SlurEvent} à la propriété @code{'articulations} de
chacune d'elles, et enfin réaliser un @code{SequentialMusic} de ces deux
éléments @code{NoteEvent}. En tenant compte du fait que, dans le cadre
d'un ajout, une propriété non définie est lue @code{'()} (une liste
vide), aucune vérification n'est requise avant d'introduire un nouvel
élément en tête de la propriété @code{articulations}.

@example
doubleSlur = #(define-music-function (note) (ly:music?)
         "Renvoie : @{ note ( note ) @}.
         `note` est censé être une note unique."
         (let ((note2 (ly:music-deep-copy note)))
           (set! (ly:music-property note 'articulations)
                 (cons (make-music 'SlurEvent 'span-direction -1)
                       (ly:music-property note 'articulations)))
           (set! (ly:music-property note2 'articulations)
                 (cons (make-music 'SlurEvent 'span-direction 1)
                       (ly:music-property note2 'articulations)))
           (make-music 'SequentialMusic 'elements (list note note2))))
@end example


@node Adding articulation to notes (example)
@subsection Ajout d'articulation à des notes (exemple)

Le moyen d'ajouter une articulation à des notes consiste à juxtaposer
deux expressions musicales. L'option de réaliser nous-mêmes une fonction
musicale à cette fin.

Un @code{$variable} au milieu de la notation @code{#@{ @dots{} #@}} se
comporte exactement comme un banal @code{\variable} en notation LilyPond
traditionnelle. Nous pourrions écrire

@example
@{ \musique -. -> @}
@end example

@noindent
mais, pour les besoins de la démonstration, nous allons voir comment
réaliser ceci en Scheme. Commençons par examiner une saisie simple et le
résultat auquel nous désirons aboutir :

@example
%  saisie
\displayMusic c4
===>
(make-music
  'NoteEvent
  'duration
  (ly:make-duration 2 0 1/1)
  'pitch
  (ly:make-pitch -1 0 0))))
=====
%  résultat attendu
\displayMusic c4->
===>
(make-music
  'NoteEvent
  'articulations
  (list (make-music
          'ArticulationEvent
          'articulation-type 'accent))
  'duration
  (ly:make-duration 2 0 1/1)
  'pitch
  (ly:make-pitch -1 0 0))
@end example

Nous voyons qu'une note (@code{c4}) est représentée par une expression
@code{NoteEvent}. Si nous souhaitons ajouter une articulation
@notation{accent}, nous devrons ajouter une expression
@code{ArticulationEvent} à la propriété @code{articulations} de
l'expression @code{NoteEvent}.

Construisons notre fonction en commençant par

@example
(define (ajoute-accent note-event)
  "Ajoute un accent (ArticulationEvent) aux articulations de `note-event`,
  qui est censé être une expression NoteEvent."
  (set! (ly:music-property note-event 'articulations)
        (cons (make-music 'ArticulationEvent
                'articulation-type 'accent)
              (ly:music-property note-event 'articulations)))
  note-event)
@end example

La première ligne est la manière de définir une fonction en Scheme : la
fonction Scheme a pour nom @code{ajoute-accent} et elle comporte une
variable appelée @code{note-event}. En Scheme, le type d'une variable se
déduit la plupart de temps de par son nom -- c'est d'ailleurs une
excellente pratique que l'on retrouve dans de nombreux autres langages.

@example
"Ajoute un accent@dots{}"
@end example

@noindent
décrit ce que fait la fonction. Bien que ceci ne soit pas primordial,
tout comme des noms de variable évidents, tâchons néanmoins de prendre
de bonnes habitudes dès nos premiers pas.

Vous pouvez vous demander pourquoi nous modifions directement
l'événement note plutôt que d'en manipuler une copie -- on pourrait
utiliser @code{ly:music-deep-copy} à cette fin. La raison en est qu'il
existe un contrat tacite : les fonctions musicales sont autorisées à
modifier leurs arguments -- ils sont générés en partant de zéro (comme
les notes que vous saisissez) ou déjà recopiés (faire référence à une
variable musicale avec @samp{\nom} ou à de la musique issue
d'expressions Scheme @samp{$(@dots{})} aboutit à une copie). Dans la
mesure où surmultiplier les copies serait contre productif, la valeur de
retour d'une fonction musicale n'est @strong{pas} recopiée. Afin de
respecter ce contrat, n'utilisez pas un même argument à plusieurs
reprises, et n'oubliez pas que le retourner compte pour une utilisation.

Dans un exemple précédent, nous avons construit de la musique en
répétant un certain argument musical. Dans ce cas là, l'une des
répétitions se devait d'être une copie. Dans le cas contraire, certaines
bizarreries auraient pu survenir. Par exemple, la présence d'un
@code{\relative} ou d'un @code{\transpose}, après plusieurs répétitions
du même élément, entraînerait des « relativisations » ou transpositions
en cascade. Si nous les assignons à une variable musicale,
l'enchaînement est rompu puisque la référence à @samp{\nom} créera une
nouvelle copie sans toutefois prendre en considération l'identité des
éléments répétés.

Cette fonction n'étant pas une fonction musicale à part entière, elle
peut s'utiliser dans d'autres fonctions musicales. Il est donc sensé de
respecter le même contrat que pour les fonctions musicales : l'entrée
peut être modifiée pour arriver à une sortie, et il est de la
responsabilité de l'appelant d'effectuer des copies s'il a réellement
besoin de l'argument dans son état originel. Vous constaterez, à la
lecture des fonctions propres à LilyPond, comme @code{music-map}, que ce
principe est toujours respecté.

Revenons à nos moutons… Nous disposons maintenant d'un @code{note-event}
que nous pouvons modifier, non pas grâce à un @code{ly:music-deep-copy},
mais plutôt en raison de notre précédente réflexion. Nous ajoutons
@notation{l'accent} à la liste de ses propriétés @code{'articulations}.

@example
(set! emplacement nouvelle-valeur)
@end example

L'emplacement est ce que nous voulons ici définir. Il s'agit de la
propriété @code{'articulations} de l'expression @code{note-event}.

@example
(ly:music-property note-event 'articulations)
@end example

La fonction @code{ly:music-property} permet d'accéder aux propriétés
musicales -- les @code{'articulations}, @code{'duration},
@code{'pitch}, etc. que @code{\displayMusic} nous a indiquées. La
nouvelle valeur sera l'ancienne propriété @code{'articulations},
augmentée d'un élément : l'expression @code{ArticulationEvent}, que nous
recopions à partir des informations de @code{\displayMusic}.

@example
(cons (make-music 'ArticulationEvent
        'articulation-type 'accent)
      (ly:music-property result-event-chord 'articulations))
@end example

@code{cons} permet d'ajouter un élément en tête de liste sans pour
autant modifier la liste originale. C'est exactement ce que nous
recherchons : la même liste qu'auparavant, plus la nouvelle expression
@code{ArticulationEvent}. L'ordre au sein de la propriété
@code{'articulations} n'a ici aucune importance.

Enfin, après avoir ajouté l'articulation @notation{accent} à sa
propriété @code{articulations}, nous pouvons renvoyer le
@code{note-event}, ce que réalise la dernière ligne de notre fonction.

Nous pouvons à présent transformer la fonction @code{ajoute-accent} en
fonction musicale, à l'aide d'un peu d'enrobage syntaxique et mention du
type de son argument.

@example
ajouteAccent = #(define-music-function (note-event) (ly:music?)
  "Ajoute un accent (ArticulationEvent) aux articulations de `note-event`,
  qui est censé être une expression NoteEvent."
  (set! (ly:music-property note-event 'articulations)
        (cons (make-music 'ArticulationEvent
                'articulation-type 'accent)
              (ly:music-property note-event 'articulations)))
  note-event)
@end example

Par acquis de conscience, vérifions que tout ceci fonctione :

@example
\displayMusic \ajouteAccent c4
@end example
