[ << Scheme tutorial ] | [Top][Contents][Index] | [ Interfaces for programmers >> ] |
[ < Scheme in LilyPond ] | [ Up : Scheme in LilyPond ] | [ LilyPond variables > ] |
1.2.1 LilyPond Scheme syntax
The Guile interpreter is part of LilyPond, which means that Scheme can be included in LilyPond input files. There are several methods for including Scheme in LilyPond.
The simplest way is to use a hash mark #
before a Scheme
expression.
Now LilyPond’s input is structured into tokens and expressions, much like human language is structured into words and sentences. LilyPond has a lexer that recognizes tokens (literal numbers, strings, Scheme elements, pitches and so on), and a parser that understands the syntax. Once it knows that a particular syntax rule applies, it executes actions associated with it.
The hash mark #
method of embedding Scheme is a natural fit
for this system. Once the lexer sees a hash mark, it calls the Scheme
reader to read one full Scheme expression (this can be an identifier, an
expression enclosed in parentheses, or several other things). After the
Scheme expression is read, it is stored away as the value for an
SCM_TOKEN
in the grammar. Once the parser knows how to make use
of this token, it calls Guile for evaluating the Scheme expression.
Since the parser usually requires a bit of lookahead from the lexer to
make its parsing decisions, this separation of reading and evaluation
between lexer and parser is exactly what is needed to keep the execution
of LilyPond and Scheme expressions in sync. For this reason, you should
use the hash mark #
for calling Scheme whenever this is
feasible.
Another way to call the Scheme interpreter from LilyPond is the use of
dollar $
instead of a hash mark for introducing Scheme
expressions. In this case, LilyPond evaluates the code right after the
lexer has read it. It checks the resulting type of the Scheme
expression and then picks a token type (one of several
xxx_IDENTIFIER
in the syntax) for it. It creates a copy
of the value and uses that for the value of the token. If the value of
the expression is void (Guile’s value of *unspecified*
), nothing
at all is passed to the parser.
This is, in fact, exactly the same mechanism that LilyPond employs when
you call any variable or music function by name, as \name
, with
the only difference that the name is determined by the LilyPond lexer
without consulting the Scheme reader, and thus only variable names
consistent with the current LilyPond mode are accepted.
The immediate action of $
can lead to surprises, see
Importing Scheme in LilyPond. Using #
where the
parser supports it is usually preferable. Inside of music expressions,
expressions created using #
are interpreted as
music. However, they are not copied before use. If they are
part of some structure that might still get used, you may need to use
ly:music-deep-copy
explicitly.
There are also ‘list splicing’ operators $@
and #@
that insert all elements of a list in the surrounding context.
Now let’s take a look at some actual Scheme code. Scheme procedures can be defined in LilyPond input files:
#(define (average a b c) (/ (+ a b c) 3))
Note that LilyPond comments (%
and %{ %}
) cannot
be used within Scheme code, even in a LilyPond input file, because
the Guile interpreter, not the LilyPond lexer, is reading
the Scheme expression. Comments in Guile Scheme are entered
as follows:
; this is a single-line comment #! This a (non-nestable) Guile-style block comment But these are rarely used by Schemers and never in LilyPond source code !#
For the rest of this section, we will assume that the data is entered
in a music file, so we add a #
at the beginning of each Scheme
expression.
All of the top-level Scheme expressions in a LilyPond input file can
be combined into a single Scheme expression by use of the
begin
statement:
#(begin (define foo 0) (define bar 1))
[ << Scheme tutorial ] | [Top][Contents][Index] | [ Interfaces for programmers >> ] |
[ < Scheme in LilyPond ] | [ Up : Scheme in LilyPond ] | [ LilyPond variables > ] |