;;;; You should have received a copy of the GNU General Public License
;;;; along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
-(define ly-syntax-module (resolve-module '(ly-syntax)))
+(define-module (scm ly-syntax-constructors)
+ #:use-module (lily)
+ #:use-module (srfi srfi-1))
-(defmacro define-ly-syntax (args . body)
- (if (pair? args)
- `(module-define! ,ly-syntax-module ',(car args)
- (lambda ,(cdr args) ,@body))
- `(module-define! ,ly-syntax-module ',args ,@body)))
-
-;; A ly-syntax constructor can access location data as (*location*).
-;; This is mainly used for reporting errors and warnings. This
-;; function is a syntactic sugar which uses (*location*) to set the
-;; origin of the returned music object; this behaviour is usually
-;; desired.
-(defmacro define-ly-syntax-loc (args . body)
- `(define-ly-syntax ,args
- (let ((m ,(cons 'begin body)))
- (set! (ly:music-property m 'origin) (*location*))
- m)))
-
-(define (music-function-call-error fun m)
- (let* ((sig (ly:music-function-signature fun))
- (pred (if (pair? (car sig)) (caar sig) (car sig))))
+(define-public (music-function-call-error fun m)
+ (let* ((sigcar (car (ly:music-function-signature fun)))
+ (pred? (if (pair? sigcar) (car sigcar) sigcar)))
(ly:parser-error
(format #f (_ "~a function cannot return ~a")
- (type-name pred)
+ (type-name pred?)
(value->lily-string m))
(*location*))
- (and (pair? (car sig)) (cdar sig))))
-
-(define-ly-syntax music-function-call-error music-function-call-error)
+ (and (pair? sigcar)
+ (if (ly:music? (cdr sigcar))
+ (ly:music-deep-copy (cdr sigcar) (*location*))
+ (cdr sigcar)))))
;; Music function: Apply function and check return value.
-;; args are in reverse order, rest may specify additional ones
+;; args are in reverse order.
;;
;; If args is not a proper list, an error has been flagged earlier
;; and no fallback value had been available. In this case,
;; we don't call the function but rather return the general
;; fallback.
-(define-ly-syntax (music-function fun args . rest)
- (let* ((sig (ly:music-function-signature fun))
- (pred (if (pair? (car sig)) (caar sig) (car sig)))
- (good (proper-list? args))
- (m (and good (apply (ly:music-function-extract fun)
- (reverse! args rest)))))
- (if (and good (pred m))
- (begin
- (if (ly:music? m)
- (set! (ly:music-property m 'origin) (*location*)))
- m)
- (if good
- (music-function-call-error fun m)
- (and (pair? (car sig)) (cdar sig))))))
+(define-public (music-function fun args)
+ (let* ((sigcar (car (ly:music-function-signature fun)))
+ (pred? (if (pair? sigcar) (car sigcar) sigcar))
+ (good (list? args))
+ (m (and good (apply (ly:music-function-extract fun) (reverse! args)))))
+ (if good
+ (if (pred? m)
+ (if (ly:music? m) (ly:set-origin! m) m)
+ (music-function-call-error fun m))
+ (and (pair? sigcar)
+ (if (ly:music (cdr sigcar))
+ (ly:music-deep-copy (cdr sigcar) (*location*))
+ (cdr sigcar))))))
-(define-ly-syntax (argument-error n pred arg)
+(define-public (argument-error n pred arg)
(ly:parser-error
(format #f
(_ "wrong type for argument ~a. Expecting ~a, found ~s")
n (type-name pred) (music->make-music arg))
(*location*)))
-(define-ly-syntax-loc (void-music)
- (make-music 'Music))
+;; Used for chaining several music functions together. `final'
+;; contains the last argument and still needs typechecking.
+(define (music-function-chain fun args final)
+ (let* ((siglast (last (ly:music-function-signature fun)))
+ (pred? (if (pair? siglast) (car siglast) siglast)))
+ (if (pred? final)
+ (music-function fun (cons final args))
+ (begin
+ (argument-error (1+ (length args)) pred? final)
+ ;; call music function just for the error return value
+ (music-function fun #f)))))
+
+(define-public (partial-music-function fun-list arg-list)
+ (let* ((good (every list? arg-list))
+ (sig (ly:music-function-signature (car fun-list))))
+ (and good
+ (ly:make-music-function
+ (cons (car sig) (list-tail (cdr sig) (length (car arg-list))))
+ (lambda rest
+ ;; Every time we use music-function, it destructively
+ ;; reverses its list of arguments. Changing the calling
+ ;; convention would be non-trivial since we do error
+ ;; propagation to the reversed argument list by making it
+ ;; a non-proper list. So we just create a fresh copy of
+ ;; all argument lists for each call. We also want to
+ ;; avoid reusing any music expressions without copying and
+ ;; want to let them point to the location of the music
+ ;; function call rather than its definition.
+ (let ((arg-list (ly:music-deep-copy arg-list (*location*))))
+ (fold music-function-chain
+ (music-function (car fun-list)
+ (reverse! rest (car arg-list)))
+ (cdr fun-list) (cdr arg-list))))))))
-(define-ly-syntax-loc (sequential-music mlist)
- (make-sequential-music mlist))
+(define-public (void-music)
+ (ly:set-origin! (make-music 'Music)))
-(define-ly-syntax-loc (simultaneous-music mlist)
- (make-simultaneous-music mlist))
+(define-public (sequential-music mlist)
+ (ly:set-origin! (make-sequential-music mlist)))
-(define-ly-syntax-loc (event-chord mlist)
- (make-music 'EventChord
- 'elements mlist))
+(define-public (simultaneous-music mlist)
+ (ly:set-origin! (make-simultaneous-music mlist)))
-(define-ly-syntax-loc (unrelativable-music mus)
- (make-music 'UnrelativableMusic
- 'element mus))
+(define-public (event-chord mlist)
+ (ly:set-origin! (make-music 'EventChord
+ 'elements mlist)))
-(define-ly-syntax-loc (context-change type id)
- (make-music 'ContextChange
- 'change-to-type type
- 'change-to-id id))
+(define-public (unrelativable-music mus)
+ (ly:set-origin! (make-music 'UnrelativableMusic
+ 'element mus)))
-(define-ly-syntax (tempo text . rest)
+(define-public (context-change type id)
+ (ly:set-origin! (make-music 'ContextChange
+ 'change-to-type type
+ 'change-to-id id)))
+
+(define-public (tempo text . rest)
(let* ((unit (and (pair? rest)
(car rest)))
(count (and unit
(cadr rest)))
(range-tempo? (pair? count))
- (tempo-change (make-music 'TempoChangeEvent
- 'origin (*location*)
- 'text text
- 'tempo-unit unit
- 'metronome-count count))
+ (tempo-change (ly:set-origin! (make-music 'TempoChangeEvent
+ 'text text
+ 'tempo-unit unit
+ 'metronome-count count)))
(tempo-set
(and unit
(context-spec-music
(make-sequential-music (list tempo-change tempo-set))
tempo-change)))
-(define-ly-syntax-loc (repeat type num body alts)
- (make-repeat type num body alts))
+(define-public (repeat type num body alts)
+ (ly:set-origin! (make-repeat type num body alts)))
(define (script-to-mmrest-text music)
"Extract @code{'direction} and @code{'text} from @var{music}, and transform
(make-music 'MultiMeasureTextEvent music)
music))
-(define-ly-syntax-loc (multi-measure-rest duration articulations)
- (make-music 'MultiMeasureRestMusic
- 'articulations (map script-to-mmrest-text articulations)
- 'duration duration))
+(define-public (multi-measure-rest duration articulations)
+ (ly:set-origin! (make-music 'MultiMeasureRestMusic
+ 'articulations (map script-to-mmrest-text articulations)
+ 'duration duration)))
-(define-ly-syntax-loc (repetition-chord duration articulations)
- (make-music 'EventChord
- 'duration duration
- 'elements articulations))
+(define-public (repetition-chord duration articulations)
+ (ly:set-origin! (make-music 'EventChord
+ 'duration duration
+ 'elements articulations)))
-(define-ly-syntax-loc (context-specification type id ops create-new mus)
+(define-public (context-specification type id ops create-new mus)
(let ((csm (context-spec-music mus type id)))
(set! (ly:music-property csm 'property-operations) ops)
(if create-new (set! (ly:music-property csm 'create-new) #t))
- csm))
+ (ly:set-origin! csm)))
-(define-ly-syntax (composed-markup-list commands markups)
+(define-public (composed-markup-list commands markups)
;; `markups' being a list of markups, eg (markup1 markup2 markup3),
;; and `commands' a list of commands with their scheme arguments, in reverse order,
;; eg: ((italic) (raise 4) (bold)), maps the commands on each markup argument, eg:
(make-map-markup-commands-markup-list
compose complex) completed))))))))
-(define-ly-syntax (property-operation ctx music-type symbol . args)
+(define-public (property-operation ctx music-type symbol . args)
(let* ((props (case music-type
((PropertySet) (list 'value (car args)))
((PropertyUnset) '())
(list 'grob-property-path (car args))
(list 'grob-property-path args)))
(else (ly:error (_ "Invalid property operation ~a") music-type))))
- (m (apply make-music music-type
- 'symbol symbol
- 'origin (*location*)
- props)))
- (make-music 'ContextSpeccedMusic
- 'element m
- 'context-type ctx
- 'origin (*location*))))
+ (m (ly:set-origin! (apply make-music music-type
+ 'symbol symbol
+ props))))
+ (ly:set-origin! (make-music 'ContextSpeccedMusic
+ 'element m
+ 'context-type ctx))))
(define (get-first-context-id! mus)
"Find the name of a ContextSpeccedMusic, possibly naming it"
'()))
'())))
-(define-ly-syntax-loc (lyric-event text duration)
- (make-lyric-event text duration))
+(define-public (lyric-event text duration)
+ (ly:set-origin! (make-lyric-event text duration)))
-(define (lyric-combine-music sync sync-type music loc)
+(define-public (lyric-combine sync sync-type music)
;; CompletizeExtenderEvent is added following the last lyric in MUSIC
;; to signal to the Extender_engraver that any pending extender should
;; be completed if the lyrics end before the associated voice.
(append! (ly:music-property music 'elements)
(list (make-music 'CompletizeExtenderEvent)))
- (make-music 'LyricCombineMusic
- 'element music
- 'associated-context sync
- 'associated-context-type sync-type
- 'origin loc))
-
-(define-ly-syntax (lyric-combine voice typ music)
- (lyric-combine-music voice typ music (*location*)))
+ (ly:set-origin!
+ (make-music 'LyricCombineMusic
+ 'element music
+ 'associated-context sync
+ 'associated-context-type sync-type)))
-(define-ly-syntax (add-lyrics music addlyrics-list)
+(define-public (add-lyrics music addlyrics-list)
(let* ((existing-voice-name (get-first-context-id! music))
(voice-name (if (string? existing-voice-name)
existing-voice-name
'context-id voice-name
'origin (ly:music-property music 'origin))))
(voice-type (ly:music-property voice 'context-type))
- (lyricstos (map (lambda (mus)
- (let* ((loc (ly:music-property mus 'origin))
- (lyr (lyric-combine-music
- voice-name voice-type mus loc)))
- (make-music 'ContextSpeccedMusic
- 'create-new #t
- 'context-type 'Lyrics
- 'element lyr
- 'origin loc)))
- addlyrics-list)))
+ (lyricstos (map
+ (lambda (mus)
+ (with-location
+ (ly:music-property mus 'origin)
+ (ly:set-origin! (make-music 'ContextSpeccedMusic
+ 'create-new #t
+ 'context-type 'Lyrics
+ 'element
+ (lyric-combine
+ voice-name voice-type mus)))))
+ addlyrics-list)))
(make-simultaneous-music (cons voice lyricstos))))