#:use-module (srfi srfi-1))
(define-public (music-function-call-error fun m)
- (let* ((sig (ly:music-function-signature fun))
- (pred (if (pair? (car sig)) (caar sig) (car sig))))
+ (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))))
+ (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-public (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))
- (if (ly:music? m) (ly:set-origin! m) 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-public (argument-error n pred arg)
(ly:parser-error
n (type-name pred) (music->make-music arg))
(*location*)))
+;; 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-public (void-music)
(ly:set-origin! (make-music 'Music)))