Inclusions automatiques en Scheme (was: Re: Bonjour)

Cet article décrit l'idée :

   <http://nicolas.sceaux.free.fr/index.php/2006/07/01/9&gt;

J'ai eu beau parcourir ton site un certain nombre de fois, jamais je
n'avais trouvé cet article ! Tu ne pourrais pas mettre un lien
"Archives" ou quelque chose dans ton menu de droite ? En l'état, ce
post est quasiment introuvable à moins de connaître son existence...
(et au fait, c'est pour quand la "part 2" ? :slight_smile:

Mon propos était aussi de te demander ton avis sur la question des
includes enchâssés, et notamment de la commande \includeRelative que
proposait Graham, et que j'avais proposé de sponsoriser.

J'ai eu beau parcourir le code source, je n'arrive pas à trouver où se
situent les définitions de la commande \include (et comme tu t'en
doutes, lancer une recherche pour le terme "include" n'est pas
vraiment d'un grand secours dans du c++... :slight_smile:

Je t'invite à jeter un coup d'œil si le cœœur t'en dit (c'est pas
forcément
super digeste) aux parties instrumentales séparées que j'ai faite pour
Armide de Lully.

Oui, j'ai consulté tout ça pendant un certain temps, avec un sentiment
mêlé d'admiration, de perplexité, et de terreur :slight_smile:

Grâce à ton article, je vais pouvoir comprendre tout cela un peu
mieux. Mon idée est justement de pouvoir me passer de paramètres en
ligne de commande : que LilyPond cherche un fichier dans un
répertoire, s'il ne le trouve pas aille le chercher dans un autre
répertoire, et enfin s'il ne le trouve pas du tout, crée à la place
une variable vide (de façon à ce que le code puisse être compilé quand
même).

Je suis très bien tes explications, jusqu'au gros bloc de Scheme
suivant, que tu pourrais peut-être expliquer un peu davantage :

;;; Pourquoi est-il nécessaire d'inclure cette srfi précisément ?
;;; Qu'apporte le ice-9 ?
#(use-modules (srfi srfi-39) (ice-9 format))

;;; Et ça, c'est pour vider la variable current-piece ? Ou quoi ?
#(define *current-piece* (make-parameter ""))

;;; Là je comprends à peu près. Pourquoi le
;;; if string-null? est-il nécessaire ? Dans quel cas
;;; *current-piece* est-il nul ?
#(define (include-pathname name)
   "Build a pathname string for file `name', based
    on the value of *current-piece*."
   (string-append (if (string-null? (*current-piece*))
                      ""
                      (string-append (*current-piece*) "/"))
                  name ".ily"))

;;; format #f : c'est quoi, au juste ?
;;; le 'void #t sert à quoi ? tu indiques :
;;; "the music expression can be disregarded",
;;; c'est-à-dire... ?
includeScore =
#(define-music-function (parser location name) (string?)
   (parameterize ( (*current-piece* name))
     (ly:parser-parse-string
       (ly:clone-parser parser)
       (format #f "\\include \"~a\""
               (include-pathname "score"))))
   (make-music 'SequentialMusic 'void #t))

;;; Deux questions :
;;; -pourquoi Voice = "" ? Sans les guillemets, tu risquerais quoi ?
;;; -pourquoi appeler explicitement le \notemode ? Je croyais
;;; que la construction #{...#} l'appelait implicitement.
includeNotes =
#(define-music-function (parser location pathname) (string?)
  (let ( (include-file (include-pathname pathname)))
   #{ \context Voice = "" \notemode {
        \include $include-file } #}))

Voilà. Merci beaucoup pour toutes les réponses (tu n'es pas au bout de
tes peines)...

Cordialement,
Valentin

···

Le 01/12/07, Nicolas Sceaux<****@****> a écrit :

J'ai eu beau parcourir ton site un certain nombre de fois, jamais je
n'avais trouvé cet article ! Tu ne pourrais pas mettre un lien
"Archives" ou quelque chose dans ton menu de droite ? En l'état, ce
post est quasiment introuvable à moins de connaître son existence...
(et au fait, c'est pour quand la "part 2" ? :slight_smile:

En fait j'avais passé "hors ligne" ces articles. Il y avait donc un
"part 2", sur la façon de faire des dacapo et compagnie. Puis j'ai
décidé de retirer les articles de ce genre, n'ayant pas trop l'énergie
pour en faire davantage, ni maintenir les précédents. J'ai repassé
"en ligne" l'article sur les inclusions juste avant d'envoyer mon post.

Mon propos était aussi de te demander ton avis sur la question des
includes enchâssés, et notamment de la commande \includeRelative que
proposait Graham, et que j'avais proposé de sponsoriser.

A une époque j'ai aussi regretté de ne pas avoir d'include relatif,
puis je me suis débrouillé autrement, d'abord en ajoutant des
répertoires pour la recherche des fichiers à inclure avec -I sur la
ligne de commande, enfin avec les commandes que j'utilise maintenant.

J'ai eu beau parcourir le code source, je n'arrive pas à trouver où se
situent les définitions de la commande \include (et comme tu t'en
doutes, lancer une recherche pour le terme "include" n'est pas
vraiment d'un grand secours dans du c++... :slight_smile:

Le code relatif à la commande \include se trouve dans le lexer:
lily/lexer.ll. Rechercher "\\include".

Je suis très bien tes explications, jusqu'au gros bloc de Scheme
suivant, que tu pourrais peut-être expliquer un peu davantage :

;;; Pourquoi est-il nécessaire d'inclure cette srfi précisément ?
;;; Qu'apporte le ice-9 ?
#(use-modules (srfi srfi-39) (ice-9 format))

(ice-9 format) sert à utiliser une fonction format (pour construire
des chaînes de caractères) qui ressemble à celle de Common Lisp et
avec laquelle je suis familié.
(srfi srfi-39) sert à ajouter une fonctionnalité : des variables
"spéciales", ce sont les variables entourées d'étoiles dans le reste
du code, et qui ne fonctionne pas de la même façon que les autres
variables en Scheme.

;;; Et ça, c'est pour vider la variable current-piece ? Ou quoi ?
#(define *current-piece* (make-parameter ""))

C'est pour déclarer une variable spéciale. Par défaut, elle vaudra "".
Cette variable sert à sauvegarder le nom du répertoire où se situe
la pièce en cours de traitement.

;;; Là je comprends à peu près. Pourquoi le
;;; if string-null? est-il nécessaire ? Dans quel cas
;;; *current-piece* est-il nul ?
#(define (include-pathname name)
  "Build a pathname string for file `name', based
   on the value of *current-piece*."
  (string-append (if (string-null? (*current-piece*))
                     ""
                     (string-append (*current-piece*) "/"))
                 name ".ily"))

Si *current-piece* vaut "", on ne veut pas ajouter de "/", sinon on va
partir de la racine du système de fichier.

Avec *current-piece* valant "" :
   (include-pathname "violon") ==> "violon.ily"
Avec *current-piece* valant "AAAouverture" :
   (include-pathname "violon") ==> "AAAouverture/violon.ily"

;;; format #f : c'est quoi, au juste ?

C'est pour retourner la chaîne construite avec format. Avec #t
c'est pour écrire la chaîne sur (current-output).

;;; le 'void #t sert à quoi ? tu indiques :
;;; "the music expression can be disregarded",
;;; c'est-à-dire... ?
includeScore =
#(define-music-function (parser location name) (string?)
  (parameterize ( (*current-piece* name))
    (ly:parser-parse-string
      (ly:clone-parser parser)
      (format #f "\\include \"~a\""
              (include-pathname "score"))))
  (make-music 'SequentialMusic 'void #t))

Toute expression musicale située au "top-level" va être traitée
de façon à générer une partition. Ici, ce n'est pas ce qu'on veut,
puisque le fichier contenant la partition aura déjà été parsé.
L'expression musicale returnée par cette fonction peut être ignorée,
puisqu'elle ne sert à rien. C'est l'effet de bord créé par la
fonction qui est intéressant, pas sa valeur de retour.

;;; Deux questions :
;;; -pourquoi Voice = "" ? Sans les guillemets, tu risquerais quoi ?

sans doute rien :slight_smile:

;;; -pourquoi appeler explicitement le \notemode ? Je croyais
;;; que la construction #{...#} l'appelait implicitement.

C'est vrai, mais au début ce n'était pas le cas. C'est une survivance
de ces anciens temps.

nicolas

···

Le 2 déc. 07 à 11:46, Valentin Villenave a écrit :

En fait j'avais passé "hors ligne" ces articles. Il y avait donc un
"part 2", sur la façon de faire des dacapo et compagnie. Puis j'ai
décidé de retirer les articles de ce genre, n'ayant pas trop l'énergie
pour en faire davantage, ni maintenir les précédents. J'ai repassé
"en ligne" l'article sur les inclusions juste avant d'envoyer mon post.

C'est fort dommage. Je comprends fort bien le problème vis-à-vis de
ton blog, fait avec soin et constamment mis à jour ; cependant tu
pourrais peut-être les poster sur cette mailinglist, ou encore les
mettre sur http://lilypondwiki.tuxfamily.org/ afin que ceux qui
cherchent un peu puissent tomber dessus.

A une époque j'ai aussi regretté de ne pas avoir d'include relatif,
puis je me suis débrouillé autrement, d'abord en ajoutant des
répertoires pour la recherche des fichiers à inclure avec -I sur la
ligne de commande, enfin avec les commandes que j'utilise maintenant.

Nous avions une discussion avec Han-Wen sur la légitimité des includes
relatifs ; je persiste à me sentir "choqué" quand je vois, par exemple
dans ton fichier common/common.ily, des lignes telles que

\include "common/include-commands.ily"

qui se rapportent pourtant à des fichiers se trouvant dans le *même* répertoire.
Je ne suis pas le seul à trouver ça bizarre, c'est aussi le cas de
Graham et quelques autres.

(srfi srfi-39) sert à ajouter une fonctionnalité : des variables
"spéciales", ce sont les variables entourées d'étoiles dans le reste
du code, et qui ne fonctionne pas de la même façon que les autres
variables en Scheme.

Très bien. Est-ce que, à ta connaissance, les prochaines versions de
Guile (avec notamment la R6RS) vont changer quelque chose ? Aura-t-on
toujours besoin d'inclure explicitement cette srfi ?

Toute expression musicale située au "top-level" va être traitée
de façon à générer une partition. Ici, ce n'est pas ce qu'on veut,
puisque le fichier contenant la partition aura déjà été parsé.
L'expression musicale returnée par cette fonction peut être ignorée,
puisqu'elle ne sert à rien. C'est l'effet de bord créé par la
fonction qui est intéressant, pas sa valeur de retour.

Je crains de n'avoir pas fait les études adéquates pour comprendre ces
explications :frowning:
Je ne comprends pas en quoi l'expression serait parsée deux fois, et
même si je le comprenais, alors pourquoi utiliser un
"define-music-function (parser location quoiquecesoit)"
si c'est pour justement ne pas parser l'expression musicale ?

Pour poser ma question plus simplement : qu'est-ce qui différencie ton
code d'un truc tout bête tel que :

includeScore =
#(define-music-function (parser location name) (string?)
    #{ \include " $name/score.ily" #})

(en dehors du fait que ce code ne marche pas :wink:

Merci pour ces explications et pour ta patience.

Cordialement,
Valentin

···

Le 02/12/07, Nicolas Sceaux<****@****> a écrit :

(srfi srfi-39) sert à ajouter une fonctionnalité : des variables
"spéciales", ce sont les variables entourées d'étoiles dans le reste
du code, et qui ne fonctionne pas de la même façon que les autres
variables en Scheme.

Très bien. Est-ce que, à ta connaissance, les prochaines versions de
Guile (avec notamment la R6RS) vont changer quelque chose ? Aura-t-on
toujours besoin d'inclure explicitement cette srfi ?

aucune idée, a priori il faudra toujours.

Toute expression musicale située au "top-level" va être traitée
de façon à générer une partition. Ici, ce n'est pas ce qu'on veut,
puisque le fichier contenant la partition aura déjà été parsé.
L'expression musicale returnée par cette fonction peut être ignorée,
puisqu'elle ne sert à rien. C'est l'effet de bord créé par la
fonction qui est intéressant, pas sa valeur de retour.

Je crains de n'avoir pas fait les études adéquates pour comprendre ces
explications :frowning:
Je ne comprends pas en quoi l'expression serait parsée deux fois, et
même si je le comprenais, alors pourquoi utiliser un
"define-music-function (parser location quoiquecesoit)"
si c'est pour justement ne pas parser l'expression musicale ?

Pour poser ma question plus simplement : qu'est-ce qui différencie ton
code d'un truc tout bête tel que :

includeScore =
#(define-music-function (parser location name) (string?)
   #{ \include " $name/score.ily" #})

(en dehors du fait que ce code ne marche pas :wink:

hm, en écrivant mon message je me doutais que ce n'était pas très clair.
Tout d'abord, il faut comprendre que l'utilisation de define-music-function
pour faire une fonction qui va inclure une partition est une bidouille.
En effet, une fonction musicale sert à fabriquer une expression musicale,
et à la retourner (l'expression musicale fabriquée est la valeur de retour
de la fonction).
Ici, on veut insérer une partition. Un block \score n'est pas une expression
musicale donc une fonction retournant une fonction musicale n'est a priori
pas ce qu'on veut... Si j'en utilise une ici, c'est pour avoir un aspect
plus cohérent du reste. Je trouve plus élégant d'avoir :
   \includeScore "partition"
que :
   #(include-score "partition")

Dans la définition de \includeScore il y a deux parties :

includeScore =
#(define-music-function (parser location name) (string?)
   ;; 1ere partie :
   (parameterize ( (*current-piece* name))
     (ly:parser-parse-string
       (ly:clone-parser parser)
       (format #f "\\include \"~a\""
               (include-pathname "score"))))
   ;; 2eme partie :
   (make-music 'SequentialMusic 'void #t))

En scheme, la valeur de retour de fonction est sa dernière expression.
Dans le cas d'une function musicale, cette expression doit être de type
expression musicale. Donc includeScore doit retourner une expression
musicale. Comme on l'a vu, on block \score n'est pas une expression
musicale, on retourne donc une expression musicale bidon (2ème partie).
La propriété 'void #t est là pour indiquer au parser d'ignorer cette
expression musicale bidon.

La 1ère partie s'occupe de faire le vrai boulot de cette fonction, à
savoir réaliser l'équivalent de :
   \include "score.ily"
en invoquant la fonction ly:parser-parse-string.

···

Le 2 déc. 07 à 16:58, Valentin Villenave a écrit :

Effectivement. Je n'avais pas réalisé que \include "score.ily" n'était
pas une expression musicale en soi.
Merci du tuyau.

J'aurai sûrement d'autres questions d'ici quelque temps quant aux
commandes de manipulation du texte ; dans l'immédiat, merci pour tout
et encore bienvenue sur la liste !

Valentin

···

Le 02/12/07, Nicolas Sceaux<****@****> a écrit :

Tout d'abord, il faut comprendre que l'utilisation de define-music-
function
pour faire une fonction qui va inclure une partition est une bidouille.
En effet, une fonction musicale sert à fabriquer une expression
musicale,
et à la retourner (l'expression musicale fabriquée est la valeur de
retour
de la fonction).
Ici, on veut insérer une partition. Un block \score n'est pas une
expression
musicale donc une fonction retournant une fonction musicale n'est a
priori
pas ce qu'on veut... Si j'en utilise une ici, c'est pour avoir un aspect
plus cohérent du reste.