]> git.donarmstrong.com Git - lilypond.git/blobdiff - scm/lily.scm
* Documentation/topdocs/INSTALL.texi (Top): Remove information
[lilypond.git] / scm / lily.scm
index df687d0f6b579bdf4fa6eb2238c3058c2741e7a7..0fe8fbfb22512e515f7d5fbcb89aa48be4c03188 100644 (file)
-;;; lily.scm -- implement Scheme output routines for TeX and PostScript
+;;;; lily.scm -- toplevel Scheme stuff
 ;;;;
 ;;;;  source file of the GNU LilyPond music typesetter
 ;;;; 
-;;;; (c) 1998--2002 Jan Nieuwenhuizen <janneke@gnu.org>
+;;;; (c) 1998--2005 Jan Nieuwenhuizen <janneke@gnu.org>
 ;;;; Han-Wen Nienhuys <hanwen@cs.uu.nl>
 
-;;; Library functions
 
+(if (defined? 'set-debug-cell-accesses!)
+    (set-debug-cell-accesses! #f))
 
-(use-modules (ice-9 regex))
+;;(set-debug-cell-accesses! 5000)
 
+(use-modules (ice-9 regex)
+            (ice-9 safe)
+             (ice-9 optargs)
+            (oop goops)
+            (srfi srfi-1)  ; lists
+            (srfi srfi-13)) ; strings
 
-;;; General settings
-;; debugging evaluator is slower.
 
-(debug-enable 'debug)
-;(debug-enable 'backtrace)
-(read-enable 'positions)
+;; my display
+(define-public (myd k v) (display k) (display ": ") (display v) (display ", "))
+
+(define-public (print . args)
+  (apply format (cons (current-output-port) args)))
 
 
-(define-public (line-column-location line col file)
+;;; General settings
+;;; debugging evaluator is slower.  This should
+;;; have a more sensible default.
+
+(if (ly:get-option 'verbose)
+    (begin
+      (debug-enable 'debug)
+      (debug-enable 'backtrace)
+      (read-enable 'positions)))
+
+(define-public (line-column-location file line col)
   "Print an input location, including column number ."
   (string-append (number->string line) ":"
-                (number->string col) " " file)
-  )
+                (number->string col) " " file))
 
-(define-public (line-location line col file)
+(define-public (line-location  file line col)
   "Print an input location, without column number ."
-  (string-append (number->string line) " " file)
-  )
+  (string-append (number->string line) " " file))
 
 (define-public point-and-click #f)
 
+(define-public tex-backend?
+  (member (ly:output-backend) '("texstr" "tex")))
+
+(define-public parser #f)
+
+(define-public (lilypond-version)
+  (string-join
+   (map (lambda (x) (if (symbol? x)
+                       (symbol->string x)
+                       (number->string x)))
+       (ly:version))
+   "."))
+
+
+
 ;; cpp hack to get useful error message
 (define ifdef "First run this through cpp.")
 (define ifndef "First run this through cpp.")
 
+;; gettext wrapper for guile < 1.7.2
+(if (defined? 'gettext)
+    (define-public _ gettext)
+    (define-public _ ly:gettext))
 
+(define-public (ly:load x)
+  (let* ((fn (%search-load-path x)))
+    (if (ly:get-option 'verbose)
+       (format (current-error-port) "[~A]" fn))
+    (primitive-load fn)))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-public X 0)
-(define-public Y 1)
-(define-public START -1)
-(define-public STOP 1)
-(define-public LEFT -1)
-(define-public RIGHT 1)
-(define-public UP 1)
-(define-public DOWN -1)
-(define-public CENTER 0)
+(define-public TEX_STRING_HASHLIMIT 10000000)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; lily specific variables.
-(define-public default-script-alist '())
 
-(define-public security-paranoia #f)
+(define (type-check-list location signature arguments)
+  "Typecheck a list of arguments against a list of type
+predicates. Print a message at LOCATION if any predicate failed."
+  (define (recursion-helper signature arguments count) 
+    (define (helper pred? arg count) 
+      (if (not (pred? arg))
+
+         (begin
+           (ly:input-message
+            location
+            (format
+             #f (_ "wrong type for argument ~a.  Expecting ~a, found ~s")
+             count (type-name pred?) arg))
+           #f)
+         #t))
+
+    (if (null? signature)
+       #t
+       (and (helper (car signature) (car arguments) count)
+            (recursion-helper (cdr signature) (cdr arguments) (1+ count)))))
+  (recursion-helper signature arguments 1))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Unassorted utility functions.
-
-
-;;;;;;;;;;;;;;;;
-; alist
-(define (uniqued-alist  alist acc)
-  (if (null? alist) acc
-      (if (assoc (caar alist) acc)
-         (uniqued-alist (cdr alist) acc)
-         (uniqued-alist (cdr alist) (cons (car alist) acc)))))
-
-
-(define (assoc-get key alist)
-  "Return value if KEY in ALIST, else #f."
-  (let ((entry (assoc key alist)))
-    (if entry (cdr entry) #f)))
-  
-(define (assoc-get-default key alist default)
-  "Return value if KEY in ALIST, else DEFAULT."
-  (let ((entry (assoc key alist)))
-    (if entry (cdr entry) default)))
-
-
-(define-public (uniqued-alist  alist acc)
-  (if (null? alist) acc
-      (if (assoc (caar alist) acc)
-         (uniqued-alist (cdr alist) acc)
-         (uniqued-alist (cdr alist) (cons (car alist) acc)))))
-
-(define-public (alist<? x y)
-  (string<? (symbol->string (car x))
-           (symbol->string (car y))))
-
-;;;;;;;;;;;;;;;;
-; list
-(define (tail lst)
-  "Return tail element of LST."
-  (car (last-pair lst)))
-
-
-(define (flatten-list lst)
-  "Unnest LST" 
-  (if (null? lst)
-      '()
-      (if (pair? (car lst))
-         (append (flatten-list (car lst)) (flatten-list  (cdr lst)))
-         (cons (car lst) (flatten-list (cdr lst))))
-  ))
-
-(define (list-minus a b)
-  "Return list of elements in A that are not in B."
-  (if (pair? a)
-      (if (pair? b)
-         (if (member (car a) b)
-             (list-minus (cdr a) b)
-             (cons (car a) (list-minus (cdr a) b)))
-         a)
-      '()))
-
-;; why -list suffix (see reduce-list)
-(define-public (filter-list pred? list)
-  "return that part of LIST for which PRED is true.
-
- TODO: rewrite using accumulator. Now it takes O(n) stack. "
-  
-  (if (null? list) '()
-      (let* ((rest (filter-list pred? (cdr list))))
-       (if (pred? (car list))
-           (cons (car list)  rest)
-           rest))))
-
-(define-public (filter-out-list pred? list)
-  "return that part of LIST for which PRED is false."
-  (if (null? list) '()
-      (let* ((rest (filter-out-list pred? (cdr list))))
-       (if (not (pred? (car list)))
-           (cons (car list)  rest)
-           rest))))
-
-
-(define (first-n n lst)
-  "Return first N elements of LST"
-  (if (and (pair? lst)
-          (> n 0))
-      (cons (car lst) (first-n (- n 1) (cdr lst)))
-      '()))
-
-(define-public (uniq-list list)
-  (if (null? list) '()
-      (if (null? (cdr list))
-         list
-         (if (equal? (car list) (cadr list))
-             (uniq-list (cdr list))
-             (cons (car list) (uniq-list (cdr list)))))))
-
-(define (butfirst-n n lst)
-  "Return all but first N entries of LST"
-  (if (pair? lst)
-      (if (> n 0)
-         (butfirst-n (- n 1) (cdr lst))
-         lst)
-      '()))
-  
-(define (split-at predicate l)
- "Split L = (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 (lambda (x y) (= (- y x) 2))  '(1 3 5 9 11) (cons '() '()))"
-;; "
-
-;; KUT EMACS MODE.
-
-  (define (inner-split predicate l acc)
-  (cond
-   ((null? l) acc)
-   ((null? (cdr l))
-    (set-car! acc (cons (car l) (car acc)))
-    acc)
-   ((predicate (car l) (cadr l))
-    (set-car! acc (cons (car l) (car acc)))
-    (inner-split predicate (cdr l) acc))
-   (else
-    (set-car! acc (cons (car l) (car acc)))
-    (set-cdr! acc (cdr l))
-    acc)
-
-  ))
- (let*
-    ((c (cons '() '()))
-     )
-  (inner-split predicate l  c)
-  (set-car! c (reverse! (car c))) 
-  c)
-)
-
-
-(define-public (split-list l sep?)
-  "
-
-(display (split-list '(a b c / d e f / g) (lambda (x) (equal? x '/))) )
-=>
-((a b c) (d e f) (g))
-
-"
-
-(define (split-one sep?  l acc)
-  "Split off the first parts before separator and return both parts.
-
-"
-  ;; " KUT EMACS
-  (if (null? l)
-      (cons acc '())
-      (if (sep? (car l))
-         (cons acc (cdr l))
-         (split-one sep? (cdr l) (cons (car l) acc))
-         )
-      ))
-
-(if (null? l)
-    '()
-    (let* ((c (split-one sep? l '())))
-      (cons (reverse! (car c) '()) (split-list (cdr c) sep?))
-      )
-    )
-)
-
-
-(define (other-axis a)
-  (remainder (+ a 1) 2))
-  
-
-(define-public (widen-interval iv amount)
-   (cons (- (car iv) amount)
-         (+ (cdr iv) amount))
-)
-
-(define-public (write-me message x)
-  "Return X.  Display MESSAGE and write X.  Handy for debugging, possibly turned off."
-  (display message) (write x) (newline) x)
-;;  x)
-
-(define (index-cell cell dir)
-  (if (equal? dir 1)
-      (cdr cell)
-      (car cell)))
-
-(define (cons-map f x)
-  "map F to contents of X"
-  (cons (f (car x)) (f (cdr x))))
-
-;; used where?
-(define-public (reduce operator list)
-  "reduce OP [A, B, C, D, ... ] =
-   A op (B op (C ... ))
-"
-      (if (null? (cdr list)) (car list)
-         (operator (car list) (reduce operator (cdr list)))))
-
-(define (take-from-list-until todo gathered crit?)
-  "return (G, T), where (reverse G) + T = GATHERED + TODO, and the last of G
-is the  first to satisfy CRIT
-
- (take-from-list-until '(1 2 3  4 5) '() (lambda (x) (eq? x 3)))
-=>
- ((3 2 1) 4 5)
-
-"
-  (if (null? todo)
-      (cons gathered todo)
-      (if (crit? (car todo))
-         (cons (cons (car todo) gathered) (cdr todo))
-         (take-from-list-until (cdr todo) (cons (car todo) gathered) crit?)
-      )
-  ))
-
-(define-public (list-insert-separator list between)
-  "Create new list, inserting BETWEEN between elements of LIST"
-  (if (null? list)
-      '()
-      (if (null? (cdr list))
-         list
-         (cons (car list)
-               (cons between (list-insert-separator (cdr list) between)))
-  
-  )))
-
-;;;;;;;;;;;;;;;;
-; strings.
-
-(define-public (string-join str-list sep)
-  "append the list of strings in STR-LIST, joining them with SEP"
-  (apply string-append (list-insert-separator str-list sep))
-  )
-
-(define-public (pad-string-to str wid)
-  (string-append str (make-string (max (- wid (string-length str)) 0) #\ ))
-  )
-
-;;;;;;;;;;;;;;;;
-; other
-(define (sign x)
-  (if (= x 0)
-      0
-      (if (< x 0) -1 1)))
-
-(define-public (!= l r)
-  (not (= l r)))
+;;  output
 
-(define-public (ly:load x)
-  (let* (
-        (fn (%search-load-path x))
-
-        )
-    (if (ly:verbose)
-       (format (current-error-port) "[~A]" fn))
-    (primitive-load fn)))
 
+;;(define-public (output-framework) (write "hello\n"))
+
+(define output-tex-module
+  (make-module 1021 (list (resolve-interface '(scm output-tex)))))
+(define output-ps-module
+  (make-module 1021 (list (resolve-interface '(scm output-ps)))))
+
+(define-public (ps-output-expression expr port)
+  (display (eval expr output-ps-module) port))
+
+;; TODO: generate this list by registering the stencil expressions
+;;       stencil expressions should have docstrings.
+(define-public (ly:all-stencil-expressions)
+  "Return list of stencil expressions."
+  '(beam
+    bezier-sandwich
+    blank
+    bracket
+    char
+    dashed-line
+    dashed-slur
+    dot
+    draw-line
+    ez-ball
+    filledbox
+    glyph-string
+    horizontal-line
+    named-glyph
+    polygon
+    repeat-slash
+    round-filled-box
+    text
+    url-link
+    white-dot
+    white-text
+    embedded-ps
+    zigzag-line))
+
+;; TODO:
+;;  - generate this list by registering the output-backend-commands
+;;    output-backend-commands should have docstrings.
+;;  - remove hard copies in output-ps output-tex
+(define-public (ly:all-output-backend-commands)
+  "Return list of output backend commands."
+  '(
+    comment
+    grob-cause
+    no-origin
+    placebox
+    unknown))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;  output
-(use-modules (scm tex)
-            (scm ps)
-            (scm pysk)
-            (scm ascii-script)
-            (scm sketch)
-            (scm sodipodi)
-            (scm pdftex)
-            )
-
-(define output-alist
-  `(
-    ("tex" . ("TeX output. The default output form." ,tex-output-expression))
-    ("ps" . ("Direct postscript. Requires setting GS_LIB and GS_FONTPATH" ,ps-output-expression))
-    ("scm" . ("Scheme dump: debug scheme molecule expressions" ,write))
-    ("as" . ("Asci-script. Postprocess with as2txt to get ascii art"  ,as-output-expression))
-    ("sketch" . ("Bare bones Sketch output." ,sketch-output-expression))
-    ("sodipodi" . ("Bare bones Sodipodi output." ,sodipodi-output-expression))
-    ("pdftex" . ("PDFTeX output. Was last seen nonfunctioning." ,pdftex-output-expression))
-    ))
-
-
-(define (document-format-dumpers)
-  (map
-   (lambda (x)
-     (display (string-append  (pad-string-to 5 (car x)) (cadr x) "\n"))
-     output-alist)
-   ))
-
-(define-public (find-dumper format )
-  (let*
-      ((d (assoc format output-alist)))
-    
-    (if (pair? d)
-       (caddr d)
-       (scm-error "Could not find dumper for format ~s" format))
-    ))
+;; Safe definitions utility
+(define safe-objects (list))
+
+(define-macro (define-safe-public arglist . body)
+  "Define a variable, export it, and mark it as safe, ie usable in LilyPond safe mode.
+The syntax is the same as `define*-public'."
+  (define (get-symbol arg)
+    (if (pair? arg)
+        (get-symbol (car arg))
+        arg))
+  (let ((safe-symbol (get-symbol arglist)))
+    `(begin
+       (define*-public ,arglist
+         ,@body)
+       (set! safe-objects (cons (cons ',safe-symbol ,safe-symbol)
+                                safe-objects))
+       ,safe-symbol)))
+
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; other files.
 
-(map ly:load
-                                       ; load-from-path
-     '("music-types.scm"
-       "output-lib.scm"
-       "c++.scm"
-       "chords-ignatzek.scm"
-       "chord-entry.scm"
-       "double-plus-new-chord-name.scm"
-       "molecule.scm"
-       "new-markup.scm"
-       "bass-figure.scm"
-       "music-functions.scm"
-       "music-property-description.scm"
-       "auto-beam.scm"
-       "basic-properties.scm"
-       "chord-name.scm"
-       "translator-property-description.scm"
-       "script.scm"
-       "drums.scm"
-       "midi.scm"
-
-       "beam.scm"
-       "clef.scm"
-       "slur.scm"
-       "font.scm"
-       
-       "grob-property-description.scm"
-       "grob-description.scm"
-       "context-description.scm"
-       "interface-description.scm"
-       ))
-
-
-       
+(for-each ly:load
+         ;; load-from-path
+         '("lily-library.scm"
+           "file-cache.scm"
+           "define-music-types.scm"
+           "output-lib.scm"
+           "c++.scm"
+           "chord-ignatzek-names.scm"
+           "chord-entry.scm"
+           "chord-generic-names.scm"
+           "stencil.scm"
+           "new-markup.scm"
+           "bass-figure.scm"
+           "music-functions.scm"
+           "part-combiner.scm"
+           "define-music-properties.scm"
+           "auto-beam.scm"
+           "chord-name.scm"
+
+           "ly-from-scheme.scm"
+           
+           "define-context-properties.scm"
+           "translation-functions.scm"
+           "script.scm"
+           "midi.scm"
+           "beam.scm"
+           "clef.scm"
+           "slur.scm"
+           "font.scm"
+           "encoding.scm"
+           
+           "fret-diagrams.scm"
+           "define-markup-commands.scm"
+           "define-grob-properties.scm"
+           "define-grobs.scm"
+           "define-grob-interfaces.scm"
+           "page-layout.scm"
+           "titling.scm"
+           
+           "paper.scm"
+           "backend-library.scm"
+                                       ; last:
+           "safe-lily.scm"))
 
 
 (set! type-p-name-alist
-  `(
-   (,ly:dir? . "direction")
-   (,scheme? . "any type")
-   (,number-pair? . "pair of numbers")
-   (,ly:input-location? . "input location")   
-   (,ly:grob? . "grob (GRaphical OBject)")
-   (,grob-list? . "list of grobs")
-   (,ly:duration? . "duration")
-   (,pair? . "pair")
-   (,integer? . "integer")
-   (,list? . "list")
-   (,symbol? . "symbol")
-   (,string? . "string")
-   (,boolean? . "boolean")
-   (,ly:pitch? . "pitch")
-   (,ly:moment? . "moment")
-   (,ly:dimension? . "dimension, in staff space")
-   (,ly:input-location? . "input location")
-   (,music-list? . "list of music")
-   (,ly:music? . "music")
-   (,number? . "number")
-   (,char? . "char")
-   (,input-port? . "input port")
-   (,output-port? . "output port")   
-   (,vector? . "vector")
-   (,procedure? . "procedure") 
-   (,boolean-or-symbol? . "boolean or symbol")
-   (,number-or-string? . "number or string")
-   (,markup? . "markup")
-   (,markup-list? . "list of markups")
-   (,number-or-grob? . "number or grob")
-   ))
+      `(
+       (,boolean-or-symbol? . "boolean or symbol")
+       (,boolean? . "boolean")
+       (,char? . "char")
+       (,grob-list? . "list of grobs")
+       (,hash-table? . "hash table")
+       (,input-port? . "input port")
+       (,integer? . "integer")
+       (,list? . "list")
+       (,ly:context? . "context")
+       (,ly:dimension? . "dimension, in staff space")
+       (,ly:dir? . "direction")
+       (,ly:duration? . "duration")
+       (,ly:grob? . "layout object")
+       (,ly:input-location? . "input location")
+       (,ly:moment? . "moment")
+       (,ly:music? . "music")
+       (,ly:pitch? . "pitch")
+       (,ly:translator? . "translator")
+       (,ly:font-metric? . "font metric")
+       (,markup-list? . "list of markups")
+       (,markup? . "markup")
+       (,ly:music-list? . "list of music")
+       (,number-or-grob? . "number or grob")
+       (,number-or-string? . "number or string")
+       (,number-pair? . "pair of numbers")
+       (,number? . "number")
+       (,output-port? . "output port")   
+       (,pair? . "pair")
+       (,procedure? . "procedure") 
+       (,scheme? . "any type")
+       (,string? . "string")
+       (,symbol? . "symbol")
+       (,vector? . "vector")))
+
+
+;; debug mem leaks
+
+(define gc-protect-stat-count 0)
+(define-public (dump-gc-protects)
+  (set! gc-protect-stat-count (1+ gc-protect-stat-count))
+  (let* ((protects (sort
+                   (hash-table->alist (ly:protects))
+                   (lambda (a b)
+                     (< (object-address (car a))
+                        (object-address (car b))))))
+        (out-file-name (string-append
+                        "gcstat-" (number->string gc-protect-stat-count)
+                        ".scm"))
+        (outfile    (open-file  out-file-name  "w")))
+
+    (display "Dumping gc protected objs to ...\n")
+    (display
+     (filter
+      (lambda (x) (not (symbol? x))) 
+      (map (lambda (y)
+            (let ((x (car y))
+                  (c (cdr y)))
+
+              (string-append
+               (string-join
+                (map object->string (list (object-address x) c x))
+                " ")
+               "\n")))
+          protects))
+     outfile)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-public (lilypond-main files)
+  "Entry point for LilyPond."
+  (let* ((failed '())
+        (handler (lambda (key . arg) (set! failed (append arg failed)))))
+    (for-each
+     (lambda (f)
+       (catch 'ly-file-failed (lambda () (ly:parse-file f))
+             (lambda (x) (handler x f)))
+       (if #f
+          (dump-gc-protects)))
+     files)
+    
+    (if (pair? failed)
+       (begin
+         (newline (current-error-port))
+         (display (_ "error: failed files: ") (current-error-port))
+         (display (string-join failed) (current-error-port))
+         (newline (current-error-port))
+         (newline (current-error-port))
+         (exit 1))
+       (exit 0))))
+
+(define-public (tweak-grob-property grob sym val)
+  (set! (ly:grob-property grob sym) val))