From eb98dcb8ece687486640a5dc3ec3aa2395ec31cf Mon Sep 17 00:00:00 2001 From: Mark Polesky Date: Fri, 7 May 2010 16:56:26 -0700 Subject: [PATCH] Doc & scm/: Music-functions and type-predicates. - Clean up: NR 5.6 "Using music functions" EL 2.1 "Music functions" - In Extending 2.1, move `Void functions' to end of section, so `Functions without arguments' comes first. - Use a consistent indentation format for music functions. - Make some minor formatting/wording changes. - Categorize predicates in type-p-name-alist. - Create type-predicates-doc-string to document type-p-name-alist automatically. - Add notation appendix `Predefined type predicates' to include type-predicates-doc-string. - Show predicate name instead of "unknown" in type-check error message (provided by Neil Puttock). - Make `color?' predicate more specific. --- .../extending/programming-interface.itely | 251 ++++++++---------- .../notation/changing-defaults.itely | 163 ++++++------ .../notation/notation-appendices.itely | 7 + scm/c++.scm | 6 +- scm/document-type-predicates.scm | 69 +++++ scm/documentation-generate.scm | 9 +- scm/lily.scm | 159 +++++++---- scm/output-lib.scm | 7 +- 8 files changed, 399 insertions(+), 272 deletions(-) create mode 100644 scm/document-type-predicates.scm diff --git a/Documentation/extending/programming-interface.itely b/Documentation/extending/programming-interface.itely index 7045b49daf..7c8b287552 100644 --- a/Documentation/extending/programming-interface.itely +++ b/Documentation/extending/programming-interface.itely @@ -30,137 +30,85 @@ not familiar with Scheme, you may wish to read our @node Music functions @section Music functions -Music functions are scheme functions that are used to -automatically create music expressions. They can be used to -greatly simplify the input file. +@emph{Music functions} are scheme procedures that can create music +expressions automatically, and can be used to greatly simplify the +input file. @menu * Music function syntax:: * Simple substitution functions:: * Intermediate substitution functions:: * Mathematics in functions:: -* Void functions:: * Functions without arguments:: +* Void functions:: @end menu + @node Music function syntax @subsection Music function syntax -The general syntax of a music function is: +The general form for music functions is: @example -myFunction = -#(define-music-function (parser location @var{var_1} @var{var_2}...@var{var_n}) - (@var{var_1-type?} @var{var_2-type?}...@var{var_n-type?}) - @var{...valid music expression...}) +function = +#(define-music-function + (parser location @var{arg1} @var{arg2} @dots{}) + (@var{type1?} @var{type2?} @dots{}) + @var{music}) @end example @noindent where @multitable @columnfractions .33 .66 -@item @var{var_i} @tab @var{i}th variable -@item @var{var_i-type?} @tab type of @var{i}th variable -@item @var{...valid music expression...} @tab expression that returns -valid music, generally in the form of a Scheme expression. There is -also special syntax that allows LilyPond input code in this music -expression. -@end multitable +@item @code{@var{argN}} +@tab @var{n}th argument -The variable type checkers are scheme procedures that will return -@code{#t} if a variable is of a given type. Some common types -are shown in the table below. Other types can be found in the files -@file{lily/music-scheme.cc} and @file{scm/c++.scm}. The complete -list of named type checkers for LilyPond is found in the -@var{type-p-name-alist} of @file{scm/lily.scm}. +@item @code{@var{typeN?}} +@tab a scheme @emph{type predicate} for which @code{@var{argN}} +must return @code{#t}. -@c TODO -- automatically document type-p-name-alist +@item @code{@var{music}} +@tab A music expression, optionally written in scheme, with any +LilyPond code enclosed in hashed braces +(@tie{}@w{@code{#@{@dots{}#@}}}@tie{}). Within LilyPond code +blocks, use @code{$} to reference function arguments (eg., +@samp{$arg1}) or to start an inline scheme expression containing +function arguments (eg., @w{@samp{$(cons arg1 arg2)}}). -@multitable @columnfractions .33 .66 -@headitem Input type @tab @var{vari-type?} notation -@item Integer @tab @code{integer?} -@item Float (decimal number) @tab @code{number?} -@item Text string @tab @code{string?} -@item Markup @tab @code{markup?} -@item Music expression @tab @code{ly:music?} -@item A pair of variables @tab @code{pair?} @end multitable -The @code{parser} and @code{location} arguments are mandatory. -The @code{parser} argument is used in the body of the function -to gain access to the value of another LilyPond variable. -The @code{location} argument is used to set the @q{origin} -of the music expression that is built by the music function, -so that in case of a syntax error LilyPond -can tell the user an appropriate place to look in the input file. - -@node Simple substitution functions -@subsection Simple substitution functions +@noindent +For a list of available type predicates, see +@ruser{Predefined type predicates}. User-defined type predicates +are also allowed. -A simple substitution function is a music function whose output music -expression is written in LilyPond code, but with an input variable -substituted into the LilyPond code. The general form of these functions is -@example -myFunction = -#(define-music-function (parser location @var{var1}) - (@var{var1-type?}) - #@{ - @emph{... LilyPond input code with} @code{#$var1} @emph{for substition ...} - #@}) -@end example +@seealso -Note that the special characters @code{#@{} and @code{#@}} surround the -LilyPond music. +Notation Reference: +@ruser{Predefined type predicates}. -@multitable @columnfractions .33 .66 -@item @var{vari} @tab @var{i}th variable -@item @var{vari-type?} @tab type of @var{i}th variable -@item @var{...music...} @tab normal LilyPond input, using - variables as @code{#$var1}, etc. -@end multitable +Installed Files: +@file{lily/music-scheme.cc}, +@file{scm/c++.scm}, +@file{scm/lily.scm}. -For example, a function can be defined that simplifies -setting the padding of a TextScript: -@lilypond[quote,verbatim,ragged-right] -padText = #(define-music-function (parser location padding) (number?) - #{ - \once \override TextScript #'padding = #$padding - #}) - -\relative c''' { - c4^"piu mosso" b a b - \padText #1.8 - c4^"piu mosso" d e f - \padText #2.6 - c4^"piu mosso" fis a g -} -@end lilypond +@node Simple substitution functions +@subsection Simple substitution functions -In addition to numbers, we can use music expressions such -as notes for arguments to music functions: +Simple substitution functions are music functions whose output +music expression is written in LilyPond format and contains +function arguments in the output expression. They are described +in @ruser{Substitution function examples}. -@lilypond[quote,verbatim,ragged-right] -custosNote = #(define-music-function (parser location note) - (ly:music?) - #{ - \once \override Voice.NoteHead #'stencil = - #ly:text-interface::print - \once \override Voice.NoteHead #'text = - \markup \musicglyph #"custodes.mensural.u0" - \once \override Voice.Stem #'stencil = ##f - $note - #}) -@end lilypond @node Intermediate substitution functions @subsection Intermediate substitution functions -Slightly more complicated than simple substitution function, -intermediate substitution functions involve a mix of Scheme code and -LilyPond code in the music expression to be -returned. +Intermediate substitution functions involve a mix of Scheme code +and LilyPond code in the music expression to be returned. Some @code{\override} commands require an argument consisting of a pair of numbers (called a @code{cons cell} in Scheme). @@ -168,20 +116,19 @@ a pair of numbers (called a @code{cons cell} in Scheme). The pair can be directly passed into the music function, using a @code{pair?} variable: -@quotation @example manualBeam = -#(define-music-function (parser location beg-end) - (pair?) -#@{ - \once \override Beam #'positions = #$beg-end -#@}) +#(define-music-function + (parser location beg-end) + (pair?) + #@{ + \once \override Beam #'positions = $beg-end + #@}) \relative c' @{ \manualBeam #'(3 . 6) c8 d e f @} @end example -@end quotation Alternatively, the numbers making up the pair can be passed as separate arguments, and the Scheme code @@ -190,11 +137,12 @@ music expression: @lilypond[quote,verbatim,ragged-right] manualBeam = -#(define-music-function (parser location beg end) - (number? number?) -#{ - \once \override Beam #'positions = #(cons $beg $end) -#}) +#(define-music-function + (parser location beg end) + (number? number?) + #{ + \once \override Beam #'positions = $(cons beg end) + #}) \relative c' { \manualBeam #3 #6 c8 d e f @@ -209,61 +157,50 @@ Music functions can involve Scheme programming in addition to simple substitution, @lilypond[quote,verbatim,ragged-right] -AltOn = #(define-music-function (parser location mag) (number?) - #{ \override Stem #'length = #$(* 7.0 mag) +AltOn = +#(define-music-function + (parser location mag) + (number?) + #{ + \override Stem #'length = $(* 7.0 mag) \override NoteHead #'font-size = - #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) #}) + $(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) + #}) AltOff = { \revert Stem #'length \revert NoteHead #'font-size } -{ c'2 \AltOn #0.5 c'4 c' - \AltOn #1.5 c' c' \AltOff c'2 } +\relative c' { + c2 \AltOn #0.5 c4 c + \AltOn #1.5 c c \AltOff c2 +} @end lilypond @noindent This example may be rewritten to pass in music expressions, @lilypond[quote,verbatim,ragged-right] -withAlt = #(define-music-function (parser location mag music) (number? ly:music?) - #{ \override Stem #'length = #$(* 7.0 mag) +withAlt = +#(define-music-function + (parser location mag music) + (number? ly:music?) + #{ + \override Stem #'length = $(* 7.0 mag) \override NoteHead #'font-size = - #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) + $(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) $music \revert Stem #'length - \revert NoteHead #'font-size #}) + \revert NoteHead #'font-size + #}) -{ c'2 \withAlt #0.5 {c'4 c'} - \withAlt #1.5 {c' c'} c'2 } +\relative c' { + c2 \withAlt #0.5 { c4 c } + \withAlt #1.5 { c c } c2 +} @end lilypond -@node Void functions -@subsection Void functions - -A music function must return a music expression, but sometimes we -may want to have a function that does not involve music (such as -turning off Point and Click). To do this, we return a @code{void} -music expression. - -That is why the form -that is returned is the @code{(make-music ...)}. With the -@code{'void} property set to @code{#t}, the parser is told to -actually disregard this returned music -expression. Thus the important part of the void music function is the -processing done by the function, not the music expression that is -returned. - -@example -noPointAndClick = -#(define-music-function (parser location) () - (ly:set-option 'point-and-click #f) - (make-music 'SequentialMusic 'void #t)) -... -\noPointAndClick % disable point and click -@end example - @node Functions without arguments @subsection Functions without arguments @@ -280,7 +217,9 @@ without arguments, @example displayBarNum = -#(define-music-function (parser location) () +#(define-music-function + (parser location) + () (if (eq? #t (ly:get-option 'display-bar-numbers)) #@{ \once \override Score.BarNumber #'break-visibility = ##f #@} #@{#@})) @@ -294,6 +233,32 @@ lilypond -d display-bar-numbers FILENAME.ly @end example +@node Void functions +@subsection Void functions + +A music function must return a music expression, but sometimes we +may want to have a function that does not involve music (such as +turning off Point and Click). To do this, we return a @code{void} +music expression. + +That is why the form that is returned is the +@w{@code{(make-music @dots{})}}. With the @code{'void} property +set to @code{#t}, the parser is told to actually disregard this +returned music expression. Thus the important part of the void +music function is the processing done by the function, not the +music expression that is returned. + +@example +noPointAndClick = +#(define-music-function + (parser location) + () + (ly:set-option 'point-and-click #f) + (make-music 'SequentialMusic 'void #t)) +... +\noPointAndClick % disable point and click +@end example + @node Markup functions @section Markup functions diff --git a/Documentation/notation/changing-defaults.itely b/Documentation/notation/changing-defaults.itely index e743ac1683..bbf6a4e0b2 100644 --- a/Documentation/notation/changing-defaults.itely +++ b/Documentation/notation/changing-defaults.itely @@ -3572,16 +3572,15 @@ of ties as required. @c TODO -- add @seealso, etc. to these subsections -Where tweaks need to be reused with different music expressions, it -is often convenient to make the tweak part of a music function. -In this section, we discuss only @emph{substitution} functions, where -the object is to substitute a variable into a piece of LilyPond -input code. Other more complex functions are described in -@rextend{Music functions}. +Where tweaks need to be reused with different music expressions, +it is often convenient to make the tweak part of a @emph{music +function}. In this section, we discuss only @emph{substitution} +functions, where the object is to substitute a variable into a +piece of LilyPond input code. Other more complex functions are +described in @rextend{Music functions}. @menu * Substitution function syntax:: -* Common argument types:: * Substitution function examples:: @end menu @@ -3593,91 +3592,88 @@ code is easy. The general form of these functions is @example function = -#(define-music-function (parser location @var{var1} @var{var2}...@var{vari}... ) - (@var{var1-type?} @var{var2-type?}...@var{vari-type?}...) - #@{ - @emph{...music...} - #@}) +#(define-music-function + (parser location @var{arg1} @var{arg2} @dots{}) + (@var{type1?} @var{type2?} @dots{}) + #@{ + @var{@dots{}music@dots{}} + #@}) @end example @noindent where @multitable @columnfractions .33 .66 -@item @var{vari} @tab @var{i}th variable -@item @var{vari-type?} @tab type of @var{i}th variable -@item @var{...music...} @tab normal LilyPond input, using - variables as @code{#$var1}, etc. -@end multitable - -Common variable types are described in @ref{Common argument types}. -A more complete description of variable types is found in -@rextend{Music function syntax}. The complete list of defined variable -types is found in the @var{type-p-name-alist} entry of -@file{scm/lily.scm}. - -@c TODO -- find an automatic way of documenting the type-p-name-alist +@item @code{@var{argN}} +@tab @var{n}th argument -The @code{parser} and @code{location} arguments are mandatory, -and are used in some advanced situations as described in -@rextend{Music function syntax}. For substitution functions, just be sure -to include them. +@item @code{@var{typeN?}} +@tab a scheme @emph{type predicate} for which @code{@var{argN}} +must return @code{#t}. -@seealso +@item @code{@var{@dots{}music@dots{}}} +@tab normal LilyPond input, using @code{$} to reference arguments +(eg. @samp{$arg1}). +@end multitable -Notation Reference: -@ref{Common argument types}. -Extending LilyPond: -@rextend{Music function syntax}. +The @code{parser} and @code{location} arguments are mandatory, and +are used in some advanced situations as described in the +@q{Extending} manual (see @rextend{Music functions}). For +substitution functions, just be sure to include them. -@node Common argument types -@subsection Common argument types +The list of type predicates is also required. Some of the most +common type predicates used in music functions are: -In order to allow for error checking, the type of each argument -that is passed to a music function must be defined. Some of the -common types of variables are shown in the table below. +@example +boolean? +cheap-list? @emph{(use instead of }@q{list?}@emph{ for faster processing)} +ly:music? +markup? +number? +pair? +string? +symbol? +@end example -The following input types may be used as variables in a music -function. This list is not exhaustive; -more information about possible variable types -can be found in @rextend{Music function syntax}. +@noindent +For a list of available type predicates, see +@ref{Predefined type predicates}. User-defined type predicates +are also allowed. -@multitable @columnfractions .33 .66 -@headitem Input type @tab @var{vari-type?} notation -@item Integer @tab @code{integer?} -@item Float (decimal number) @tab @code{number?} -@item Text string @tab @code{string?} -@item Markup @tab @code{markup?} -@item Music expression @tab @code{ly:music?} -@item A Scheme pair @tab @code{pair?} -@end multitable @seealso -Extending LilyPond: -@rextend {Music function syntax}. +Notation Reference: +@ref{Predefined type predicates}. + +Extending: +@rextend{Music functions}. Installed Files: @file{lily/music-scheme.cc}, -@file{scm/c++.scm}. +@file{scm/c++.scm}, +@file{scm/lily.scm}. @node Substitution function examples @subsection Substitution function examples -This section introduces some substitution function examples. These -are not intended to be exhaustive, but rather to demonstrate some -of the possibilities of simple substitution functions. +This section introduces some substitution function examples. +These are not intended to be exhaustive, but rather to demonstrate +some of the possibilities of simple substitution functions. In the first example, a function is defined that simplifies setting the padding of a TextScript: @lilypond[quote,verbatim,ragged-right] -padText = #(define-music-function (parser location padding) (number?) - #{ - \once \override TextScript #'padding = #$padding - #}) +padText = +#(define-music-function + (parser location padding) + (number?) + #{ + \once \override TextScript #'padding = $padding + #}) \relative c''' { c4^"piu mosso" b a b @@ -3691,30 +3687,36 @@ padText = #(define-music-function (parser location padding) (number?) In addition to numbers, we can use music expressions such as notes for arguments to music functions: -@lilypond[quote,verbatim,ragged-right] -custosNote = #(define-music-function (parser location note) - (ly:music?) - #{ - \once \override Voice.NoteHead #'stencil = - #ly:text-interface::print - \once \override Voice.NoteHead #'text = - \markup \musicglyph #"custodes.mensural.u0" - \once \override Voice.Stem #'stencil = ##f - $note - #}) +@c TODO: use a better example (the music argument is redundant). -{ c' d' e' f' \custosNote g' } +@lilypond[quote,verbatim,ragged-right] +custosNote = +#(define-music-function + (parser location note) + (ly:music?) + #{ + \once \override Voice.NoteHead #'stencil = + #ly:text-interface::print + \once \override Voice.NoteHead #'text = + \markup \musicglyph #"custodes.mensural.u0" + \once \override Voice.Stem #'stencil = ##f + $note + #}) + +\relative c' { c4 d e f \custosNote g } @end lilypond Substitution functions with multiple arguments can be defined: @lilypond[quote,verbatim,ragged-right] -tempoPadded = #(define-music-function (parser location padding tempotext) - (number? string?) -#{ - \once \override Score.MetronomeMark #'padding = $padding - \tempo \markup { \bold $tempotext } -#}) +tempoPadded = +#(define-music-function + (parser location padding tempotext) + (number? string?) + #{ + \once \override Score.MetronomeMark #'padding = $padding + \tempo \markup { \bold $tempotext } + #}) \relative c'' { \tempo \markup { "Low tempo" } @@ -3726,3 +3728,4 @@ tempoPadded = #(define-music-function (parser location padding tempotext) @seealso +TODO: add missing @@ref's here. diff --git a/Documentation/notation/notation-appendices.itely b/Documentation/notation/notation-appendices.itely index b18810e872..6b58d17b65 100644 --- a/Documentation/notation/notation-appendices.itely +++ b/Documentation/notation/notation-appendices.itely @@ -47,6 +47,7 @@ and just before * All context properties:: * Layout properties:: * Available music functions:: +* Predefined type predicates:: * Scheme functions:: @end menu @@ -1343,6 +1344,12 @@ Internals Reference: @include identifiers.tely +@node Predefined type predicates +@appendixsec Predefined type predicates + +@include type-predicates.tely + + @node Scheme functions @appendixsec Scheme functions diff --git a/scm/c++.scm b/scm/c++.scm index 362e358b34..bc518030ae 100644 --- a/scm/c++.scm +++ b/scm/c++.scm @@ -74,4 +74,8 @@ (type-name (match-predicate obj type-p-name-alist))) (define-public (type-name predicate) - (assoc-get predicate type-p-name-alist "unknown")) + (let ((entry (assoc predicate type-p-name-alist))) + (if (pair? entry) (cdr entry) + (string-trim-right + (symbol->string (procedure-name predicate)) + #\?)))) diff --git a/scm/document-type-predicates.scm b/scm/document-type-predicates.scm new file mode 100644 index 0000000000..aff4034553 --- /dev/null +++ b/scm/document-type-predicates.scm @@ -0,0 +1,69 @@ +;;;; This file is part of LilyPond, the GNU music typesetter. +;;;; +;;;; Copyright (C) 2010 Mark Polesky +;;;; +;;;; LilyPond is free software: you can redistribute it and/or modify +;;;; it under the terms of the GNU General Public License as published by +;;;; the Free Software Foundation, either version 3 of the License, or +;;;; (at your option) any later version. +;;;; +;;;; LilyPond is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with LilyPond. If not, see . + +(define (document-type-predicate entry) + (let ((pred (procedure-name (car entry)))) + (string-append + "@item @code{" + (case pred + ;; don't print "cheap-markup?" + ((cheap-markup?) "markup?") + (else (symbol->string pred))) + "} @tab \n" + (case pred + ;; clarify `list?' vs. `cheap-list?' + ((list?) "list @emph{(use} @code{cheap-list?} + @emph{for faster processing)}") + ((cheap-list?) "list @emph{(use this instead of} + @code{list?} @emph{for faster processing)}") + (else (cdr entry))) + "\n"))) + +(define (document-type-predicate-category alist nodename) + (string-append + "@node " nodename "\n" + "@unnumberedsubsec " nodename "\n" + "\n" + "@multitable @columnfractions .33 .66\n" + "@headitem Type predicate @tab Description\n" + (apply string-append + (sort (map document-type-predicate alist) + ly:string-ci