Scheme expressions inside of embedded Lilypond (@code{#@{@dots{}#@}})
are now executed in lexical closure of the surrounding Scheme code.
@code{$} is no longer special in embedded Lilypond. It can be used
-unconditionally in Lilypond code for immediate evaluation, similar to
-how @code{ly:export} could previously be used. @code{ly:export} has
-been removed. As a consequence, @code{#} is now free to delay
-evaluation of its argument until the parser actually reduces the
-containing expression, greatly reducing the potential for premature
-evaluation.
+unconditionally in Lilypond code for immediate evaluation of Scheme
+expressions, similar to how @code{ly:export} could previously be used.
+@code{ly:export} has been removed. As a consequence, @code{#} is now
+free to delay evaluation of its argument until the parser actually
+reduces the containing expression, greatly reducing the potential for
+premature evaluation. There are also @q{splicing} operators @code{$@@}
+and @code{#@@} for interpreting the members of a list individually.
@item
Support for jazz-like chords has been improved: Lydian and altered
* LilyPond Scheme syntax::
* LilyPond variables::
* Input variables and Scheme::
+* Importing Scheme in LilyPond::
* Object properties::
* LilyPond compound variables::
* Internal music representation::
This is, in fact, exactly the same mechanism that Lilypond employs when
you call any variable or music function by name, as @code{\name}, with
-the only difference that its end is determined by the Lilypond lexer
+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.
variables and Scheme}. Using @code{#} where the parser supports it is
usually preferable.
+@funindex $@@
+@funindex #@@
+There are also @q{list splicing} operators @code{$@@} and @code{#@@}
+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 twice
(make-sequential-music newLa))
-{ \twice }
+\twice
@end lilypond
@c Due to parser lookahead
evaluating it, so it can go ahead with the assignment, and
@emph{afterwards} execute the Scheme code without problem.
+@node Importing Scheme in LilyPond
+@subsection Importing Scheme in LilyPond
+@funindex $
+@funindex #
+
The above example shows how to @q{export} music expressions from the
input to the Scheme interpreter. The opposite is also possible. By
placing it after @code{$}, a Scheme
@example
...
-@{ $(make-sequential-music (list newLa)) @}
+$(make-sequential-music newLa)
@end example
You can use @code{$} with a Scheme expression anywhere you could use
would not yet have been defined. For an explanation of this timing
problem, @ref{LilyPond Scheme syntax}.
-In any case, evaluation of Scheme code happens in the parser at latest.
-If you need it to be executed at a later point of time, @ref{Void scheme
-functions}, or store it in a macro:
+@funindex $@@
+@funindex #@@
+A further convenience can be the @q{list splicing} operators @code{$@@}
+and @code{#@@} for inserting the elements of a list in the surrounding
+context. Using those, the last part of the example could have been
+written as
+
+@example
+...
+@{ $@@newLa @}
+@end example
+
+Here, every element of the list stored in @code{newLa} is taken in
+sequence and inserted into the list, as if we had written
+
+@example
+@{ $(first newLa) $(second newLa) @}
+@end example
+
+Now in all of these forms, the Scheme code is evaluated while the
+input is still being consumed, either in the lexer or in the parser.
+If you need it to be executed at a later point of time, check out
+@ref{Void scheme functions}, or store it in a procedure:
@example
#(define (nopc)
SCM scopes_;
SCM start_module_;
int hidden_state_;
+ SCM eval_scm (SCM, char extra_token = 0);
public:
- SCM eval_scm (SCM);
+ SCM eval_scm_token (SCM sval) { return eval_scm (sval, '#'); }
SCM extra_tokens_;
YYSTYPE *lexval_;
Input *lexloc_;
}
-<sourcefilename>\"[^"]*\" {
+<sourcefilename>\"[^""]*\" {
string s (YYText_utf8 () + 1);
s = s.substr (0, s.rfind ('\"'));
}
char_count_stack_.back () += n;
- sval = eval_scm (sval);
-
+ sval = eval_scm (sval, '$');
+
int token = scan_scm_id (sval);
if (!scm_is_eq (yylval.scm, SCM_UNSPECIFIED))
- return token;
+ return token;
}
<INITIAL,notes,lyrics>{
return get_state () == figures;
}
+// The extra_token parameter specifies how to convert multiple values
+// into additional tokens. For '#', additional values get pushed as
+// SCM_IDENTIFIER. For '$', they get checked for their type and get
+// pushed as a corresponding *_IDENTIFIER token. Since the latter
+// tampers with yylval, it can only be done from the lexer itself, so
+// this function is private.
+
SCM
-Lily_lexer::eval_scm (SCM readerdata)
+Lily_lexer::eval_scm (SCM readerdata, char extra_token)
{
SCM sval = SCM_UNDEFINED;
error_level_ = 1;
return SCM_UNSPECIFIED;
}
+
+ if (extra_token && SCM_VALUESP (sval))
+ {
+ sval = scm_struct_ref (sval, SCM_INUM0);
+
+ if (scm_is_pair (sval)) {
+ for (SCM v = scm_reverse (scm_cdr (sval));
+ scm_is_pair (v);
+ v = scm_cdr (v))
+ {
+ int token;
+ switch (extra_token) {
+ case '$':
+ token = scan_scm_id (scm_car (v));
+ if (!scm_is_eq (yylval.scm, SCM_UNSPECIFIED))
+ push_extra_token (token, yylval.scm);
+ break;
+ case '#':
+ push_extra_token (SCM_IDENTIFIER, scm_car (v));
+ break;
+ }
+ }
+ sval = scm_car (sval);
+ } else
+ sval = SCM_UNSPECIFIED;
+ }
+
return sval;
}
scm_set_port_line_x (port, scm_from_int (ps->start_location_.line_number () - 1));
scm_set_port_column_x (port, scm_from_int (ps->start_location_.column_number () - 1));
- SCM form = scm_read (port);
+ bool multiple = ly_is_equal (scm_peek_char (port), SCM_MAKE_CHAR ('@'));
+
+ if (multiple)
+ (void) scm_read_char (port);
+ SCM form = scm_read (port);
SCM to = scm_ftell (port);
+
ps->nchars = scm_to_int (to) - scm_to_int (from);
if (!SCM_EOF_OBJECT_P (form))
// Replace form with a call to previously compiled closure
form = scm_list_1 (c);
}
+ if (multiple)
+ form = scm_list_3 (ly_symbol2scm ("apply"),
+ ly_symbol2scm ("values"),
+ form);
return scm_cons (form, make_input (ps->start_location_));
}
scm_remember_upto_here_1 (form);
return ans;
}
-
embedded_scm_bare:
SCM_TOKEN
{
- $$ = parser->lexer_->eval_scm ($1);
+ $$ = parser->lexer_->eval_scm_token ($1);
}
| SCM_IDENTIFIER
;
(port-line port))
(set-port-column! copycat
(port-column port))
+ (if (char=? (peek-char port) #\@)
+ (read-char copycat))
(read copycat))))
;; kill unused lookahead, it has been
;; written out already