]> git.donarmstrong.com Git - lilypond.git/blob - scm/lily-library.scm
Improve error checking in ly:assoc-get and ly:chain-assoc-get.
[lilypond.git] / scm / lily-library.scm
1 ;;;;
2 ;;;; lily-library.scm -- utilities
3 ;;;;
4 ;;;;  source file of the GNU LilyPond music typesetter
5 ;;;;
6 ;;;; (c) 1998--2009 Jan Nieuwenhuizen <janneke@gnu.org>
7 ;;;; Han-Wen Nienhuys <hanwen@xs4all.nl>
8
9 ; for take, drop, take-while, list-index, and find-tail:
10 (use-modules (srfi srfi-1))
11
12 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 ;; constants.
14
15 (define-public X 0)
16 (define-public Y 1)
17 (define-safe-public START -1)
18 (define-safe-public STOP 1)
19 (define-public LEFT -1)
20 (define-public RIGHT 1)
21 (define-public UP 1)
22 (define-public DOWN -1)
23 (define-public CENTER 0)
24
25 (define-safe-public DOUBLE-FLAT-QTS -4)
26 (define-safe-public THREE-Q-FLAT-QTS -3)
27 (define-safe-public FLAT-QTS -2)
28 (define-safe-public SEMI-FLAT-QTS -1)
29 (define-safe-public NATURAL-QTS 0)
30 (define-safe-public SEMI-SHARP-QTS 1)
31 (define-safe-public SHARP-QTS 2)
32 (define-safe-public THREE-Q-SHARP-QTS 3)
33 (define-safe-public DOUBLE-SHARP-QTS 4)
34 (define-safe-public SEMI-TONE-QTS 2)
35
36 (define-safe-public DOUBLE-FLAT  -1)
37 (define-safe-public THREE-Q-FLAT -3/4)
38 (define-safe-public FLAT -1/2)
39 (define-safe-public SEMI-FLAT -1/4)
40 (define-safe-public NATURAL 0)
41 (define-safe-public SEMI-SHARP 1/4)
42 (define-safe-public SHARP 1/2)
43 (define-safe-public THREE-Q-SHARP 3/4)
44 (define-safe-public DOUBLE-SHARP 1)
45 (define-safe-public SEMI-TONE 1/2)
46
47 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48 ;; moments
49
50 (define-public ZERO-MOMENT (ly:make-moment 0 1))
51
52 (define-public (moment-min a b)
53   (if (ly:moment<? a b) a b))
54
55 (define-public (moment<=? a b)
56   (or (equal? a b)
57       (ly:moment<? a b)))
58
59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60 ;; arithmetic
61 (define-public (average x . lst)
62   (/ (+ x (apply + lst)) (1+ (length lst))))
63
64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
65 ;; parser <-> output hooks.
66
67 (define-public (collect-bookpart-for-book parser book-part)
68   "Toplevel book-part handler"
69   (define (add-bookpart book-part)
70     (ly:parser-define!
71        parser 'toplevel-bookparts
72        (cons book-part (ly:parser-lookup parser 'toplevel-bookparts))))
73   ;; If toplevel scores have been found before this \bookpart,
74   ;; add them first to a dedicated bookpart
75   (if (pair? (ly:parser-lookup parser 'toplevel-scores))
76       (begin
77         (add-bookpart (ly:make-book-part
78                        (ly:parser-lookup parser 'toplevel-scores)))
79         (ly:parser-define! parser 'toplevel-scores (list))))
80   (add-bookpart book-part))
81
82 (define-public (collect-scores-for-book parser score)
83   (ly:parser-define!
84    parser 'toplevel-scores
85    (cons score (ly:parser-lookup parser 'toplevel-scores))))
86
87 (define-public (collect-music-aux score-handler parser music)
88   (define (music-property symbol)
89     (let ((value (ly:music-property music symbol)))
90       (if (not (null? value))
91           value
92           #f)))
93   (cond ((music-property 'page-marker)
94          ;; a page marker: set page break/turn permissions or label
95          (begin
96            (let ((label (music-property 'page-label)))
97              (if (symbol? label)
98                  (score-handler (ly:make-page-label-marker label))))
99            (for-each (lambda (symbol)
100                        (let ((permission (music-property symbol)))
101                          (if (symbol? permission)
102                              (score-handler
103                               (ly:make-page-permission-marker symbol
104                                                               (if (eqv? 'forbid permission)
105                                                                   '()
106                                                                   permission))))))
107                      (list 'line-break-permission 'page-break-permission
108                            'page-turn-permission))))
109         ((not (music-property 'void))
110          ;; a regular music expression: make a score with this music
111          ;; void music is discarded
112          (score-handler (scorify-music music parser)))))
113
114 (define-public (collect-music-for-book parser music)
115   "Top-level music handler"
116   (collect-music-aux (lambda (score)
117                        (collect-scores-for-book parser score))
118                      parser
119                      music))
120
121 (define-public (collect-book-music-for-book parser book music)
122   "Book music handler"
123   (collect-music-aux (lambda (score)
124                        (ly:book-add-score! book score))
125                      parser
126                      music))
127
128 (define-public (scorify-music music parser)
129   "Preprocess MUSIC."
130
131   (for-each (lambda (func)
132               (set! music (func music parser)))
133             toplevel-music-functions)
134
135   (ly:make-score music))
136
137
138 (define (get-outfile-name parser base)
139   (let* ((output-suffix (ly:parser-lookup parser 'output-suffix))
140          (counter-alist (ly:parser-lookup parser 'counter-alist))
141          (output-count (assoc-get output-suffix counter-alist 0))
142          (result base))
143     ;; Allow all ASCII alphanumerics, including accents
144     (if (string? output-suffix)
145         (set! result (format "~a-~a"
146                              base (string-regexp-substitute
147                                     "[^-[:alnum:]]" "_" output-suffix))))
148
149     ;; assoc-get call will always have returned a number
150     (if (> output-count 0)
151         (set! result (format #f "~a-~a" result output-count)))
152
153     (ly:parser-define!
154       parser 'counter-alist
155       (assoc-set! counter-alist output-suffix (1+ output-count)))
156     result))
157
158 (define (print-book-with parser book process-procedure)
159   (let* ((paper (ly:parser-lookup parser '$defaultpaper))
160          (layout (ly:parser-lookup parser '$defaultlayout))
161          (count (ly:parser-lookup parser 'output-count))
162          (base (ly:parser-output-name parser))
163          (outfile-name (get-outfile-name parser base)))
164
165     (process-procedure book paper layout outfile-name)))
166
167 (define-public (print-book-with-defaults parser book)
168   (print-book-with parser book ly:book-process))
169
170 (define-public (print-book-with-defaults-as-systems parser book)
171   (print-book-with parser book ly:book-process-to-systems))
172
173 ;; Add a score to the current bookpart, book or toplevel
174 (define-public (add-score parser score)
175     (cond
176       ((ly:parser-lookup parser '$current-bookpart)
177           ((ly:parser-lookup parser 'bookpart-score-handler)
178                 (ly:parser-lookup parser '$current-bookpart) score))
179       ((ly:parser-lookup parser '$current-book)
180           ((ly:parser-lookup parser 'book-score-handler)
181                 (ly:parser-lookup parser '$current-book) score))
182       (else
183           ((ly:parser-lookup parser 'toplevel-score-handler) parser score))))
184
185 (define-public (add-text parser text)
186   (add-score parser (list text)))
187
188 (define-public (add-music parser music)
189   (collect-music-aux (lambda (score)
190                        (add-score parser score))
191                      parser
192                      music))
193
194
195 ;;;;;;;;;;;;;;;;
196 ;; alist
197
198 (define-public assoc-get ly:assoc-get)
199
200 (define-public chain-assoc-get ly:chain-assoc-get)
201
202 (define-public (uniqued-alist alist acc)
203   (if (null? alist) acc
204       (if (assoc (caar alist) acc)
205           (uniqued-alist (cdr alist) acc)
206           (uniqued-alist (cdr alist) (cons (car alist) acc)))))
207
208 (define-public (alist<? x y)
209   (string<? (symbol->string (car x))
210             (symbol->string (car y))))
211
212 (define (map-alist-vals func list)
213   "map FUNC over the vals of  LIST, leaving the keys."
214   (if (null?  list)
215       '()
216       (cons (cons  (caar list) (func (cdar list)))
217             (map-alist-vals func (cdr list)))))
218
219 (define (map-alist-keys func list)
220   "map FUNC over the keys of an alist LIST, leaving the vals. "
221   (if (null?  list)
222       '()
223       (cons (cons (func (caar list)) (cdar list))
224             (map-alist-keys func (cdr list)))))
225
226 (define-public (first-member members lst)
227   "Return first successful MEMBER of member from MEMBERS in LST."
228   (if (null? members)
229       #f
230       (let ((m (member (car members) lst)))
231         (if m m (first-member (cdr members) lst)))))
232
233 (define-public (first-assoc keys lst)
234   "Return first successful ASSOC of key from KEYS in LST."
235   (if (null? keys)
236       #f
237       (let ((k (assoc (car keys) lst)))
238         (if k k (first-assoc (cdr keys) lst)))))
239
240 (define-public (flatten-alist alist)
241   (if (null? alist)
242       '()
243       (cons (caar alist)
244             (cons (cdar alist)
245                   (flatten-alist (cdr alist))))))
246
247 ;;;;;;;;;;;;;;;;
248 ;; vector
249
250 (define-public (vector-for-each proc vec)
251   (do
252       ((i 0 (1+ i)))
253       ((>= i (vector-length vec)) vec)
254     (vector-set! vec i (proc (vector-ref vec i)))))
255
256 ;;;;;;;;;;;;;;;;
257 ;; hash
258
259 (define-public (hash-table->alist t)
260   (hash-fold (lambda (k v acc) (acons  k v  acc))
261              '() t))
262
263 ;; todo: code dup with C++.
264 (define-safe-public (alist->hash-table lst)
265   "Convert alist to table"
266   (let ((m (make-hash-table (length lst))))
267     (map (lambda (k-v) (hashq-set! m (car k-v) (cdr k-v))) lst)
268     m))
269
270 ;;;;;;;;;;;;;;;;
271 ;; list
272
273 (define (functional-or . rest)
274   (if (pair? rest)
275       (or (car rest)
276            (apply functional-and (cdr rest)))
277       #f))
278
279 (define (functional-and . rest)
280   (if (pair? rest)
281       (and (car rest)
282            (apply functional-and (cdr rest)))
283       #t))
284
285 (define (split-list lst n)
286   "Split LST in N equal sized parts"
287
288   (define (helper todo acc-vector k)
289     (if (null? todo)
290         acc-vector
291         (begin
292           (if (< k 0)
293               (set! k (+ n k)))
294
295           (vector-set! acc-vector k (cons (car todo) (vector-ref acc-vector k)))
296           (helper (cdr todo) acc-vector (1- k)))))
297
298   (helper lst (make-vector n '()) (1- n)))
299
300 (define (list-element-index lst x)
301   (define (helper todo k)
302     (cond
303      ((null? todo) #f)
304      ((equal? (car todo) x) k)
305      (else
306       (helper (cdr todo) (1+ k)))))
307
308   (helper lst 0))
309
310 (define-public (count-list lst)
311   "Given lst (E1 E2 .. ) return ((E1 . 1) (E2 . 2) ... )  "
312
313   (define (helper l acc count)
314     (if (pair? l)
315         (helper (cdr l) (cons (cons (car l) count) acc) (1+ count))
316         acc))
317
318
319   (reverse (helper lst '() 1)))
320
321 (define-public (list-join lst intermediate)
322   "put INTERMEDIATE  between all elts of LST."
323
324   (fold-right
325    (lambda (elem prev)
326             (if (pair? prev)
327                 (cons  elem (cons intermediate prev))
328                 (list elem)))
329           '() lst))
330
331 (define-public (filtered-map proc lst)
332   (filter
333    (lambda (x) x)
334    (map proc lst)))
335
336
337 (define (flatten-list lst)
338   "Unnest LST"
339   (if (null? lst)
340       '()
341       (if (pair? (car lst))
342           (append (flatten-list (car lst)) (flatten-list  (cdr lst)))
343           (cons (car lst) (flatten-list (cdr lst))))))
344
345 (define (list-minus a b)
346   "Return list of elements in A that are not in B."
347   (lset-difference eq? a b))
348
349 (define-public (uniq-list lst)
350   "Uniq LST, assuming that it is sorted. Uses equal? for comparisons."
351
352   (reverse!
353    (fold (lambda (x acc)
354            (if (null? acc)
355                (list x)
356                (if (equal? x (car acc))
357                    acc
358                    (cons x acc))))
359          '() lst) '()))
360
361 (define (split-at-predicate pred lst)
362   "Split LST into two lists at the first element that returns #f for
363   (PRED previous_element element). Return the two parts as a pair.
364   Example: (split-at-predicate < '(1 2 3 2 1)) ==> ((1 2 3) . (2 1))"
365   (if (null? lst)
366       (list lst)
367       (let ((i (list-index pred (cdr lst) lst)))
368         (if i
369             (cons (take lst (1+ i)) (drop lst (1+ i)))
370             (list lst)))))
371
372 (define-public (split-list-by-separator lst pred)
373   "Split LST at each element that satisfies PRED, and return the parts
374   (with the separators removed) as a list of lists. Example:
375   (split-list-by-separator '(a 0 b c 1 d) number?) ==> ((a) (b c) (d))"
376   (let loop ((result '()) (lst lst))
377     (if (and lst (not (null? lst)))
378         (loop
379           (append result
380                   (list (take-while (lambda (x) (not (pred x))) lst)))
381           (let ((tail (find-tail pred lst)))
382             (if tail (cdr tail) #f)))
383        result)))
384
385 (define-public (offset-add a b)
386   (cons (+ (car a) (car b))
387         (+ (cdr a) (cdr b))))
388
389 (define-public (offset-flip-y o)
390   (cons (car o) (- (cdr o))))
391
392 (define-public (offset-scale o scale)
393   (cons (* (car o) scale)
394         (* (cdr o) scale)))
395
396 (define-public (ly:list->offsets accum coords)
397   (if (null? coords)
398       accum
399       (cons (cons (car coords) (cadr coords))
400             (ly:list->offsets accum (cddr coords)))))
401
402 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
403 ;; numbers
404
405 (if (not (defined? 'nan?)) ;; guile 1.6 compat
406     (define-public (nan? x) (not (or (< 0.0 x)
407                                      (> 0.0 x)
408                                      (= 0.0 x)))))
409
410 (if (not (defined? 'inf?))
411     (define-public (inf? x) (= (/ 1.0 x) 0.0)))
412
413 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
414 ;; intervals
415
416 (define-public empty-interval '(+inf.0 . -inf.0))
417
418 (define-public (symmetric-interval expr)
419   (cons (- expr) expr))
420
421 (define-public (interval-length x)
422   "Length of the number-pair X, when an interval"
423   (max 0 (- (cdr x) (car x))))
424
425 (define-public (ordered-cons a b)
426   (cons (min a b)
427         (max a b)))
428
429 (define-public (interval-bound interval dir)
430   ((if (= dir RIGHT) cdr car) interval))
431
432 (define-public (interval-index interval dir)
433   "Interpolate INTERVAL between between left (DIR=-1) and right (DIR=+1)"
434
435   (* (+  (interval-start interval) (interval-end interval)
436          (* dir (- (interval-end interval) (interval-start interval))))
437      0.5))
438
439 (define-public (interval-center x)
440   "Center the number-pair X, when an interval"
441   (if (interval-empty? x)
442       0.0
443       (/ (+ (car x) (cdr x)) 2)))
444
445 (define-public interval-start car)
446
447 (define-public interval-end cdr)
448
449 (define-public (interval-translate iv amount)
450   (cons (+ amount (car iv))
451         (+ amount (cdr iv))))
452
453 (define (other-axis a)
454   (remainder (+ a 1) 2))
455
456 (define-public (interval-widen iv amount)
457    (cons (- (car iv) amount)
458          (+ (cdr iv) amount)))
459
460 (define-public (interval-empty? iv)
461    (> (car iv) (cdr iv)))
462
463 (define-public (interval-union i1 i2)
464    (cons (min (car i1) (car i2))
465          (max (cdr i1) (cdr i2))))
466
467 (define-public (interval-intersection i1 i2)
468    (cons (max (car i1) (car i2))
469          (min (cdr i1) (cdr i2))))
470
471 (define-public (interval-sane? i)
472   (not (or  (nan? (car i))
473             (inf? (car i))
474             (nan? (cdr i))
475             (inf? (cdr i))
476             (> (car i) (cdr i)))))
477
478 (define-public (add-point interval p)
479   (cons (min (interval-start interval) p)
480         (max (interval-end interval) p)))
481
482 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
483 ;; string
484
485 (define-public (string-endswith s suffix)
486   (equal? suffix (substring s
487                             (max 0 (- (string-length s) (string-length suffix)))
488                             (string-length s))))
489
490 (define-public (string-startswith s prefix)
491   (equal? prefix (substring s 0 (min (string-length s) (string-length prefix)))))
492
493 (define-public (string-encode-integer i)
494   (cond
495    ((= i  0) "o")
496    ((< i 0)   (string-append "n" (string-encode-integer (- i))))
497    (else (string-append
498           (make-string 1 (integer->char (+ 65 (modulo i 26))))
499           (string-encode-integer (quotient i 26))))))
500
501 (define (number->octal-string x)
502   (let* ((n (inexact->exact x))
503          (n64 (quotient n 64))
504          (n8 (quotient (- n (* n64 64)) 8)))
505     (string-append
506      (number->string n64)
507      (number->string n8)
508      (number->string (remainder (- n (+ (* n64 64) (* n8 8))) 8)))))
509
510 (define-public (ly:inexact->string x radix)
511   (let ((n (inexact->exact x)))
512     (number->string n radix)))
513
514 (define-public (ly:number-pair->string c)
515   (string-append (ly:number->string (car c)) " "
516                  (ly:number->string (cdr c))))
517
518 (define-public (dir-basename file . rest)
519   "Strip suffixes in REST, but leave directory component for FILE."
520   (define (inverse-basename x y) (basename y x))
521   (simple-format #f "~a/~a" (dirname file)
522                  (fold inverse-basename file rest)))
523
524 (define-public (write-me message x)
525   "Return X.  Display MESSAGE and write X.  Handy for debugging,
526 possibly turned off."
527   (display message) (write x) (newline) x)
528 ;;  x)
529
530 (define-public (stderr string . rest)
531   (apply format (cons (current-error-port) (cons string rest)))
532   (force-output (current-error-port)))
533
534 (define-public (debugf string . rest)
535   (if #f
536       (apply stderr (cons string rest))))
537
538 (define (index-cell cell dir)
539   (if (equal? dir 1)
540       (cdr cell)
541       (car cell)))
542
543 (define (cons-map f x)
544   "map F to contents of X"
545   (cons (f (car x)) (f (cdr x))))
546
547 (define-public (list-insert-separator lst between)
548   "Create new list, inserting BETWEEN between elements of LIST"
549   (define (conc x y )
550     (if (eq? y #f)
551         (list x)
552         (cons x  (cons between y))))
553   (fold-right conc #f lst))
554
555 (define-public (string-regexp-substitute a b str)
556   (regexp-substitute/global #f a str 'pre b 'post))
557
558 (define (regexp-split str regex)
559   (define matches '())
560   (define end-of-prev-match 0)
561   (define (notice match)
562
563     (set! matches (cons (substring (match:string match)
564                                    end-of-prev-match
565                                    (match:start match))
566                         matches))
567     (set! end-of-prev-match (match:end match)))
568
569   (regexp-substitute/global #f regex str notice 'post)
570
571   (if (< end-of-prev-match (string-length str))
572       (set!
573        matches
574        (cons (substring str end-of-prev-match (string-length str)) matches)))
575
576    (reverse matches))
577
578 ;;;;;;;;;;;;;;;;
579 ;; other
580
581 (define (sign x)
582   (if (= x 0)
583       0
584       (if (< x 0) -1 1)))
585
586 (define-public (binary-search start end getter target-val)
587   (_i "Find the index between @var{start} and @var{end} (an integer)
588 which will produce the closest match to @var{target-val} when
589 applied to function @var{getter}.")
590   (if (<= end start)
591       start
592       (let* ((compare (quotient (+ start end) 2))
593              (get-val (getter compare)))
594         (cond
595          ((< target-val get-val)
596           (set! end (1- compare)))
597          ((< get-val target-val)
598           (set! start (1+ compare))))
599         (binary-search start end getter target-val))))
600
601 (define-public (car< a b)
602   (< (car a) (car b)))
603
604 (define-public (symbol<? lst r)
605   (string<? (symbol->string lst) (symbol->string r)))
606
607 (define-public (symbol-key<? lst r)
608   (string<? (symbol->string (car lst)) (symbol->string (car r))))
609
610 ;;
611 ;; don't confuse users with #<procedure .. > syntax.
612 ;;
613 (define-public (scm->string val)
614   (if (and (procedure? val)
615            (symbol? (procedure-name val)))
616       (symbol->string (procedure-name val))
617       (string-append
618        (if (self-evaluating? val)
619            (if (string? val)
620                "\""
621                "")
622            "'")
623        (call-with-output-string (lambda (port) (display val port)))
624        (if (string? val)
625            "\""
626            ""))))
627
628 (define-public (!= lst r)
629   (not (= lst r)))
630
631 (define-public lily-unit->bigpoint-factor
632   (cond
633    ((equal? (ly:unit) "mm") (/ 72.0 25.4))
634    ((equal? (ly:unit) "pt") (/ 72.0 72.27))
635    (else (ly:error (_ "unknown unit: ~S") (ly:unit)))))
636
637 (define-public lily-unit->mm-factor
638   (* 25.4 (/ lily-unit->bigpoint-factor 72)))
639
640 ;;; FONT may be font smob, or pango font string...
641 (define-public (font-name-style font)
642   ;; FIXME: ughr, barf: feta-alphabet is actually emmentaler
643   (if (and (string? font)
644            (string-prefix? "feta-alphabet" font))
645       (string-append "emmentaler"
646                      "-"
647                      (substring font
648                                 (string-length "feta-alphabet")
649                                 (string-length font)))
650       (let* ((font-name (ly:font-name font))
651              (full-name (if font-name font-name (ly:font-file-name font))))
652         (if (string-prefix? "Aybabtu" full-name)
653             "aybabtu"
654             (string-downcase full-name)))))
655
656 (define-public (modified-font-metric-font-scaling font)
657   (let* ((designsize (ly:font-design-size font))
658          (magnification (* (ly:font-magnification font)))
659          (scaling (* magnification designsize)))
660     (debugf "scaling:~S\n" scaling)
661     (debugf "magnification:~S\n" magnification)
662     (debugf "design:~S\n" designsize)
663     scaling))
664
665 (define-public (version-not-seen-message input-file-name)
666   (ly:message
667    "~a:0: ~a ~a"
668     input-file-name
669     (_ "warning:")
670     (format #f
671             (_ "no \\version statement found, please add~afor future compatibility")
672             (format #f "\n\n\\version ~s\n\n" (lilypond-version)))))
673
674 (define-public (old-relative-not-used-message input-file-name)
675   (ly:message
676    "~a:0: ~a ~a"
677     input-file-name
678     (_ "warning:")
679     (_ "old relative compatibility not used")))