1.2.1 LilyPond Scheme-Syntax

Der Guile-Auswerter ist ein Teil von LilyPond, sodass Scheme also auch in normale LilyPond-Eingabedateien eingefügt werden kann. Es gibt mehrere Methoden, um Scheme in LilyPond zu integrieren.

Die einfachste Weise ist es, ein Rautenzeichen # vor einem Scheme-Ausdruck zu benutzen.

Die Eingabe von LilyPond ist in Zeichen und Ausdrücke gegliedert, so etwa wie die menschliche Sprache sich in Wörter und Sätze gliedert. LilyPond hat einen Lexer, der Zeichen erkennt (Zahlen, Zeichenketten, Scheme-Elemente, Tonhöhen usw.) und einen Parser, der die Syntax versteht, LilyPond-Grammatik. Wenn dann eine bestimmte Syntaxregel als zuständig erkannt wurde, werden die damit verknüpften Aktionen ausgeführt.

Die Rautenzeichenmethode (#), mit der Scheme eingebettet werden kann, passt sehr gut in dieses System. Wenn der Lexer ein Rautenzeichen sieht, ruft er den Scheme-reader auf, um den ganzen Scheme-Ausdruck zu lesen (das kann eine Variable, ein Ausdruck in Klammern oder verschiedene andere Sachen sein). Nachdem der Scheme-Ausdruck gelesen wurde, wird er als Wert eines SCM_TOKEN in der Grammatik gespeichert. Wenn der Parser weiß, wie er diesen Wert benutzen kann, ruft er Guile auf, um den Scheme-Ausdruck auszuwerten. Weil der Parser normalerweise dem Lexer etwas voraus sein muss, ist die Trennung von Lesen und Auswerten zwischen Lexer und Parser genau das richtige, um die Auswertung von LilyPond- und Scheme-Ausdrücken synchron zu halten. Aus diesem Grund sollte das Rautenzeichen zum Einbinden von Scheme immer benutzt werden, wenn es möglich ist.

Eine andere Möglichkeit, Scheme aufzurufen, ist die Benutzung des Dollarzeichens ($) anstelle der Raute. In diesem Fall wertet LilyPond den Code sofort aus, nachdem der Lexer ihn gelesen hat. Dabei wird der resultierende Scheme-Ausdruckstyp geprüft und eine Tokentyp dafür ausgesucht (einer von mehreren xxx_IDENTIFIER in der Syntax). Wenn der Wert des Ausdrucks gültig ist (der Guilde-Wert für *unspecified*), dann wird nichts an den Parser übergeben.

Das ist auch der gleiche Mechanismus, nach dem LilyPond funktioniert, wenn man eine Variable oder musikalische Funktion mit ihrer Bezeichnung ausruft, wie in \Bezeichnung, mit dem einzigen Unterschied, dass ihre Bezeichnung durch den LilyPond-Lexer bestimmt wird, ohne den Scheme-reader einzubeziehen, und also nur Variablen akzeptiert werden, die im aktuellen LilyPond-Modus gültig sind.

Die direkte Auswirkung von $ kann zu Überraschungen führen, siehe Eingabe-Variablen und Scheme. Es bietet sich daher an, # immer zu benützen, wenn der Parser es unterstützt. Innerhalb von musikalischen Ausdrücken werden Ausdrücke, die mit # erstellt werden, tatsächlich als Noten interpretiert. Sie werden jedoch nicht vor der Benutzung kopiert. Wenn Sie Teil einer Struktur sind, die noch einmal benutzt werden soll, muss man eventuell ly:music-deep-copy explizit einsetzen.

Es gibt auch die Operatoren $@ und #@, die eine „listentrennende“ Funktion aufweisen, indem sie alle Elemente einer Liste in den umgebenden Kontext einfügen.

Jetzt wollen wir uns tatsächlichen Scheme-Code anschauen. Scheme-Prozeduren können in LilyPond-Eingabedateien definiert werden:

#(define (average a b c) (/ (+ a b c) 3))

LilyPond-Kommentare (% oder %{ %}) können innerhalb von Scheme-Code nicht benutzt werden, nicht einmal in einer LilyPond-Eingabedatei, weil der Guile-Interpreter und nicht der LilyPond-Parser den Scheme-Ausdruck liest. Kommentare in Guile Scheme werden wie folgt notiert:

; Einzeiliges Kommentar

#!
  Guile-Stil Blockkommentar (nicht schachtelbar)
  Diese Kommentare werden von Scheme-Programmierern
  selten benutzt und nie im Quellcode
  von LilyPond
!#

Für den Rest dieses Abschnitts soll angenommen werden, dass die Daten in einer LilyPond-Eingabedatei notiert werden sollen, sodass immer # vor die Scheme-Ausdrücke gestellt wird.

Alle Scheme-Ausdrücke auf oberster Ebene in einer LilyPond-Eingabedatei können in einen einzigen Scheme-Ausdruck zusammengefasst werden mit begin:

#(begin
  (define foo 0)
  (define bar 1))

LilyPond erweitern v2.25.22 (development-branch).