X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scm%2Flily-library.scm;h=335c345c3c0e345f6fd7b2b7b493be9f421759d5;hb=b3f0c2f6c352a850f03dc44a947776199eb3fa0b;hp=ae13bbebf0ec748142485952847b75a2d1273587;hpb=8bb707c295338bb85ec28ebc8a6d9465fa43a69e;p=lilypond.git diff --git a/scm/lily-library.scm b/scm/lily-library.scm index ae13bbebf0..335c345c3c 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -2,10 +2,13 @@ ;;;; lily-library.scm -- utilities ;;;; ;;;; source file of the GNU LilyPond music typesetter -;;;; -;;;; (c) 1998--2007 Jan Nieuwenhuizen +;;;; +;;;; (c) 1998--2009 Jan Nieuwenhuizen ;;;; Han-Wen Nienhuys +; for take, drop, take-while, list-index, and find-tail: +(use-modules (srfi srfi-1)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; constants. @@ -44,11 +47,15 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; moments -(define-public ZERO-MOMENT (ly:make-moment 0 1)) +(define-public ZERO-MOMENT (ly:make-moment 0 1)) (define-public (moment-min a b) (if (ly:moment output hooks. - +(define-public (collect-bookpart-for-book parser book-part) + "Toplevel book-part handler" + (define (add-bookpart book-part) + (ly:parser-define! + parser 'toplevel-bookparts + (cons book-part (ly:parser-lookup parser 'toplevel-bookparts)))) + ;; If toplevel scores have been found before this \bookpart, + ;; add them first to a dedicated bookpart + (if (pair? (ly:parser-lookup parser 'toplevel-scores)) + (begin + (add-bookpart (ly:make-book-part + (ly:parser-lookup parser 'toplevel-scores))) + (ly:parser-define! parser 'toplevel-scores (list)))) + (add-bookpart book-part)) + (define-public (collect-scores-for-book parser score) (ly:parser-define! parser 'toplevel-scores (cons score (ly:parser-lookup parser 'toplevel-scores)))) -(define (collect-music-aux score-handler parser music) +(define-public (collect-music-aux score-handler parser music) (define (music-property symbol) (let ((value (ly:music-property music symbol))) (if (not (null? value)) @@ -106,34 +127,42 @@ (define-public (scorify-music music parser) "Preprocess MUSIC." - + (for-each (lambda (func) (set! music (func music parser))) toplevel-music-functions) (ly:make-score music)) -(define (print-book-with parser book process-procedure) - (let* - ((paper (ly:parser-lookup parser '$defaultpaper)) - (layout (ly:parser-lookup parser '$defaultlayout)) - (count (ly:parser-lookup parser 'output-count)) - (base (ly:parser-output-name parser)) - (output-suffix (ly:parser-lookup parser 'output-suffix)) ) +(define (get-outfile-name parser base) + (let* ((output-suffix (ly:parser-lookup parser 'output-suffix)) + (counter-alist (ly:parser-lookup parser 'counter-alist)) + (output-count (assoc-get output-suffix counter-alist 0)) + (result base)) + ;; Allow all ASCII alphanumerics, including accents (if (string? output-suffix) - (set! base (format "~a-~a" base (string-regexp-substitute - "[^a-zA-Z0-9-]" "_" output-suffix)))) + (set! result (format "~a-~a" + base (string-regexp-substitute + "[^-[:alnum:]]" "_" output-suffix)))) + + ;; assoc-get call will always have returned a number + (if (> output-count 0) + (set! result (format #f "~a-~a" result output-count))) - ;; must be careful: output-count is under user control. - (if (not (integer? count)) - (set! count 0)) + (ly:parser-define! + parser 'counter-alist + (assoc-set! counter-alist output-suffix (1+ output-count))) + result)) + +(define (print-book-with parser book process-procedure) + (let* ((paper (ly:parser-lookup parser '$defaultpaper)) + (layout (ly:parser-lookup parser '$defaultlayout)) + (count (ly:parser-lookup parser 'output-count)) + (base (ly:parser-output-name parser)) + (outfile-name (get-outfile-name parser base))) - (if (> count 0) - (set! base (format #f "~a-~a" base count))) - (ly:parser-define! parser 'output-count (1+ count)) - (process-procedure book paper layout base) - )) + (process-procedure book paper layout outfile-name))) (define-public (print-book-with-defaults parser book) (print-book-with parser book ly:book-process)) @@ -141,6 +170,28 @@ (define-public (print-book-with-defaults-as-systems parser book) (print-book-with parser book ly:book-process-to-systems)) +;; Add a score to the current bookpart, book or toplevel +(define-public (add-score parser score) + (cond + ((ly:parser-lookup parser '$current-bookpart) + ((ly:parser-lookup parser 'bookpart-score-handler) + (ly:parser-lookup parser '$current-bookpart) score)) + ((ly:parser-lookup parser '$current-book) + ((ly:parser-lookup parser 'book-score-handler) + (ly:parser-lookup parser '$current-book) score)) + (else + ((ly:parser-lookup parser 'toplevel-score-handler) parser score)))) + +(define-public (add-text parser text) + (add-score parser (list text))) + +(define-public (add-music parser music) + (collect-music-aux (lambda (score) + (add-score parser score)) + parser + music)) + + ;;;;;;;;;;;;;;;; ;; alist @@ -222,7 +273,7 @@ found." (hash-fold (lambda (k v acc) (acons k v acc)) '() t)) -;; todo: code dup with C++. +;; todo: code dup with C++. (define-safe-public (alist->hash-table lst) "Convert alist to table" (let ((m (make-hash-table (length lst)))) @@ -246,14 +297,14 @@ found." (define (split-list lst n) "Split LST in N equal sized parts" - + (define (helper todo acc-vector k) (if (null? todo) acc-vector (begin (if (< k 0) (set! k (+ n k))) - + (vector-set! acc-vector k (cons (car todo) (vector-ref acc-vector k))) (helper (cdr todo) acc-vector (1- k))))) @@ -279,7 +330,7 @@ found." (reverse (helper lst '() 1))) - + (define-public (list-join lst intermediate) "put INTERMEDIATE between all elts of LST." @@ -297,7 +348,7 @@ found." (define (flatten-list lst) - "Unnest LST" + "Unnest LST" (if (null? lst) '() (if (pair? (car lst)) @@ -311,7 +362,7 @@ found." (define-public (uniq-list lst) "Uniq LST, assuming that it is sorted. Uses equal? for comparisons." - (reverse! + (reverse! (fold (lambda (x acc) (if (null? acc) (list x) @@ -320,57 +371,33 @@ found." (cons x acc)))) '() lst) '())) -(define (split-at-predicate predicate lst) - "Split LST = (a_1 a_2 ... a_k b_1 ... b_k) - into L1 = (a_1 ... a_k ) and L2 =(b_1 .. b_k) - Such that (PREDICATE a_i a_{i+1}) and not (PREDICATE a_k b_1). - L1 is copied, L2 not. - - (split-at-predicate (lambda (x y) (= (- y x) 2)) '(1 3 5 9 11) (cons '() '()))" - - ;; " Emacs is broken - - (define (inner-split predicate lst acc) - (cond - ((null? lst) acc) - ((null? (cdr lst)) - (set-car! acc (cons (car lst) (car acc))) - acc) - ((predicate (car lst) (cadr lst)) - (set-car! acc (cons (car lst) (car acc))) - (inner-split predicate (cdr lst) acc)) - (else - (set-car! acc (cons (car lst) (car acc))) - (set-cdr! acc (cdr lst)) - acc))) - - (let* ((c (cons '() '()))) - (inner-split predicate lst c) - (set-car! c (reverse! (car c))) - c)) - -(define-public (split-list-by-separator lst sep?) - "(display (split-list-by-separator '(a b c / d e f / g) (lambda (x) (equal? x '/)))) - => - ((a b c) (d e f) (g)) - " - ;; " Emacs is broken - (define (split-one sep? lst acc) - "Split off the first parts before separator and return both parts." - (if (null? lst) - (cons acc '()) - (if (sep? (car lst)) - (cons acc (cdr lst)) - (split-one sep? (cdr lst) (cons (car lst) acc))))) - - (if (null? lst) - '() - (let* ((c (split-one sep? lst '()))) - (cons (reverse! (car c) '()) (split-list-by-separator (cdr c) sep?))))) +(define (split-at-predicate pred lst) + "Split LST into two lists at the first element that returns #f for + (PRED previous_element element). Return the two parts as a pair. + Example: (split-at-predicate < '(1 2 3 2 1)) ==> ((1 2 3) . (2 1))" + (if (null? lst) + (list lst) + (let ((i (list-index pred (cdr lst) lst))) + (if i + (cons (take lst (1+ i)) (drop lst (1+ i))) + (list lst))))) + +(define-public (split-list-by-separator lst pred) + "Split LST at each element that satisfies PRED, and return the parts + (with the separators removed) as a list of lists. Example: + (split-list-by-separator '(a 0 b c 1 d) number?) ==> ((a) (b c) (d))" + (let loop ((result '()) (lst lst)) + (if (and lst (not (null? lst))) + (loop + (append result + (list (take-while (lambda (x) (not (pred x))) lst))) + (let ((tail (find-tail pred lst))) + (if tail (cdr tail) #f))) + result))) (define-public (offset-add a b) (cons (+ (car a) (car b)) - (+ (cdr a) (cdr b)))) + (+ (cdr a) (cdr b)))) (define-public (offset-flip-y o) (cons (car o) (- (cdr o)))) @@ -399,23 +426,25 @@ found." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; intervals +(define-public empty-interval '(+inf.0 . -inf.0)) + +(define-public (symmetric-interval expr) + (cons (- expr) expr)) + (define-public (interval-length x) "Length of the number-pair X, when an interval" (max 0 (- (cdr x) (car x)))) -(define-public interval-start car) (define-public (ordered-cons a b) (cons (min a b) (max a b))) -(define-public interval-end cdr) - (define-public (interval-bound interval dir) ((if (= dir RIGHT) cdr car) interval)) (define-public (interval-index interval dir) "Interpolate INTERVAL between between left (DIR=-1) and right (DIR=+1)" - + (* (+ (interval-start interval) (interval-end interval) (* dir (- (interval-end interval) (interval-start interval)))) 0.5)) @@ -427,7 +456,9 @@ found." (/ (+ (car x) (cdr x)) 2))) (define-public interval-start car) + (define-public interval-end cdr) + (define-public (interval-translate iv amount) (cons (+ amount (car iv)) (+ amount (cdr iv)))) @@ -439,7 +470,6 @@ found." (cons (- (car iv) amount) (+ (cdr iv) amount))) - (define-public (interval-empty? iv) (> (car iv) (cdr iv))) @@ -447,6 +477,10 @@ found." (cons (min (car i1) (car i2)) (max (cdr i1) (cdr i2)))) +(define-public (interval-intersection i1 i2) + (cons (max (car i1) (car i2)) + (min (cdr i1) (cdr i2)))) + (define-public (interval-sane? i) (not (or (nan? (car i)) (inf? (car i)) @@ -454,6 +488,9 @@ found." (inf? (cdr i)) (> (car i) (cdr i))))) +(define-public (add-point interval p) + (cons (min (interval-start interval) p) + (max (interval-end interval) p))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; string @@ -462,10 +499,10 @@ found." (equal? suffix (substring s (max 0 (- (string-length s) (string-length suffix))) (string-length s)))) - + (define-public (string-startswith s prefix) (equal? prefix (substring s 0 (min (string-length s) (string-length prefix))))) - + (define-public (string-encode-integer i) (cond ((= i 0) "o") @@ -491,6 +528,11 @@ found." (string-append (ly:number->string (car c)) " " (ly:number->string (cdr c)))) +(define-public (dir-basename file . rest) + "Strip suffixes in REST, but leave directory component for FILE." + (define (inverse-basename x y) (basename y x)) + (simple-format #f "~a/~a" (dirname file) + (fold inverse-basename file rest))) (define-public (write-me message x) "Return X. Display MESSAGE and write X. Handy for debugging, @@ -524,7 +566,7 @@ possibly turned off." (fold-right conc #f lst)) (define-public (string-regexp-substitute a b str) - (regexp-substitute/global #f a str 'pre b 'post)) + (regexp-substitute/global #f a str 'pre b 'post)) (define (regexp-split str regex) (define matches '()) @@ -547,12 +589,27 @@ possibly turned off." (reverse matches)) ;;;;;;;;;;;;;;;; -; other +;; other + (define (sign x) (if (= x 0) 0 (if (< x 0) -1 1))) +(define-public (binary-search start end getter target-val) + (_i "Find the index between @var{start} and @var{end} (an integer) +which will produce the closest match to @var{target-val} when +applied to function @var{getter}.") + (if (<= end start) + start + (let* ((compare (quotient (+ start end) 2)) + (get-val (getter compare))) + (cond + ((< target-val get-val) + (set! end (1- compare))) + ((< get-val target-val) + (set! start (1+ compare)))) + (binary-search start end getter target-val)))) (define-public (car< a b) (< (car a) (car b))) @@ -564,14 +621,22 @@ possibly turned off." (stringstring (car lst)) (symbol->string (car r)))) ;; -;; don't confuse users with # syntax. -;; +;; don't confuse users with # syntax. +;; (define-public (scm->string val) - (if (and (procedure? val) (symbol? (procedure-name val))) + (if (and (procedure? val) + (symbol? (procedure-name val))) (symbol->string (procedure-name val)) (string-append - (if (self-evaluating? val) "" "'") - (call-with-output-string (lambda (port) (display val port)))))) + (if (self-evaluating? val) + (if (string? val) + "\"" + "") + "'") + (call-with-output-string (lambda (port) (display val port))) + (if (string? val) + "\"" + "")))) (define-public (!= lst r) (not (= lst r))) @@ -587,17 +652,19 @@ possibly turned off." ;;; FONT may be font smob, or pango font string... (define-public (font-name-style font) - ;; FIXME: ughr, (ly:font-name) sometimes also has Style appended. + ;; FIXME: ughr, barf: feta-alphabet is actually emmentaler + (if (and (string? font) + (string-prefix? "feta-alphabet" font)) + (string-append "emmentaler" + "-" + (substring font + (string-length "feta-alphabet") + (string-length font))) (let* ((font-name (ly:font-name font)) - (full-name (if font-name font-name (ly:font-file-name font))) - (name-style (string-split full-name #\-))) - ;; FIXME: ughr, barf: feta-alphabet is actually emmentaler - (if (string-prefix? "feta-alphabet" full-name) - (list "emmentaler" - (substring full-name (string-length "feta-alphabet"))) - (if (not (null? (cdr name-style))) - name-style - (append name-style '("Regular")))))) + (full-name (if font-name font-name (ly:font-file-name font)))) + (if (string-prefix? "Aybabtu" full-name) + "aybabtu" + (string-downcase full-name))))) (define-public (modified-font-metric-font-scaling font) (let* ((designsize (ly:font-design-size font)) @@ -610,16 +677,16 @@ possibly turned off." (define-public (version-not-seen-message input-file-name) (ly:message - "~a:0: ~a: ~a" + "~a:0: ~a ~a" input-file-name - (_ "warning: ") + (_ "warning:") (format #f (_ "no \\version statement found, please add~afor future compatibility") (format #f "\n\n\\version ~s\n\n" (lilypond-version))))) (define-public (old-relative-not-used-message input-file-name) (ly:message - "~a:0: ~a: ~a" + "~a:0: ~a ~a" input-file-name - (_ "warning: ") + (_ "warning:") (_ "old relative compatibility not used")))