Scheme dans LilyPond#
La manière la plus courante d’insérer du code Scheme dans un fichier LilyPond est le caractère #
(croisillon), qui introduit une expression Scheme. Cette expression est évaluée et sa valeur renvoyée à LilyPond. La valeur spéciale *unspecified*
, qui est renvoyée par les expressions sans valeur utile comme les define
, est ignorée.
Portée lexicales#
Les variables définies en LilyPond ou en Scheme sont stockées exactement de la même manière. Ainsi, on peut accéder à une variable définie en syntaxe LilyPond depuis une expression Scheme et vice-versa. De plus, certains blocs possèdent leur propre portée lexicale isolée de l’extérieur. C’est notamment le cas des blocs \layout
et \paper
.
\version "2.25.8"
\layout {
#(define maVariable 5)
indent = \maVariable
}
{ c' }
% #myVar % Erreur
Résultat
Croisillon ou dollar#
Il existe un second moyen d’insérer une expression Scheme, le caractère $
. Il se comporte différemment du #
, et ceci sur deux aspects :
Il ne renvoie pas la valeur elle-même, mais une copie (profonde) de cette valeur,
Il prend effet plus tôt dans l’analyse lexicale.
Le second point est un peu délicat. Pour comprendre la syntaxe du fichier .ly
, LilyPond utiliser un analyseur lexical et un analyseur syntaxique. Pour prendre une analogie, ce principe est similaire à la manière dont un humain comprend une phrase : la suite de caractères se divise en mots, et ces mots s’agencent pour former des groupes nominaux, compléments d’objet, etc. L’analyseur lexical découpe l’entrée en jetons (les mots) et l’analyseur syntaxique reconnaît la structure (la grammaire). Tous deux travaillent en synchronisation. Il arrive que l’analyseur syntaxique ait besoin de demander un jeton supplémentaire à l’analyseur lexical. Imaginez-vous que vous devez interpréter le début de phrase « Il vit … » En fonction de ce qu’il y a ensuite, le sens de « vit » change : comparer « Il vit un renard. » et « Il vit à Paris. » Il est nécessaire d’avoir le mot suivant pour pouvoir analyser « vit ».
Avec $
, le code Scheme est interprété dès qu’il est lu par l’analyseur lexical. Il s’agit d’un moyen pratique de construire certains éléments de base comme les notes avec Scheme. Exemple :
\version "2.25.8"
hauteur = c
{ $hauteur 4 }
Résultat
Ce code fonctionne ! La variable hauteur
est immédiatement remplacée par la note « do » par l’analyseur lexical, et l’analyseur syntaxique n’y voit que du feu, puisqu’il n’y a aucun différence du point de vue de l’analyseur syntaxique entre { $hauteur 4 }
et { c 4 }
.
C’est un cas où le $
est nécessaire. En le remplaçant par un #
, on obtient une erreur :
\version "2.25.8"
hauteur = c
{ #hauteur 4 } % Error: ignoring non-music expression
Résultat
Cette fois, ce que voit l’analyseur syntaxique est une expression Scheme pas encore évaluée suivie d’une hauteur. Il n’a pas de moyen de déterminer que cette expression va renvoyer une durée au moment où il doit prendre la décision de l’interpréter comme une durée ou non.
D’un autre côté, le dollar peut avoir des effets inattendus en raison du moment auquel l’expression est évaluée. Le code suivant fonctionne :
\version "2.25.8"
hauteur = c
#(display hauteur)
Résultat
alors que le même code où #
est remplacé par $
ne fonctionne pas :
hauteur = c
$(display hauteur) % Error: unbound variable: hauteur
La raison est que l’analyseur syntaxique demande à l’analyseur lexical un jeton supplémentaire après la définition de variable hauteur = c
– en effet, s’il recevait « 8 » par exemple, l’affectation hauteur = c 8
donnerait à hauteur
une valeur qui n’est pas une hauteur mais une note avec une durée. Or, le $
fait que l’expression est évaluée par l’analyseur lexical dès ce moment où le jeton supplémentaire est demandé, donc avant que l’affectation hauteur = c
n’ait eu lieu.
Pour conclure en termes plus simples, le $
permet de faire comprendre à LilyPond des constructions où l’interprétation de la syntaxe dépend du type de valeur que l’expression Scheme renvoie. Cela comporte le désavantage que ces expressions doivent être évaluées plus tôt que d’ordinaire.
En général, il est conseillé d’utiliser le #
par défaut, avec une exception occasionnelle pour le $
dans des cas avancés ou bien pour copier une valeur de manière pratique.