LSR: Update.
[lilypond.git] / scm / parser-ly-from-scheme.scm
blob31f04444378af967be319ecc1c475997405aa8b8
1 ;;;; ly-from-scheme.scm -- parsing LilyPond music expressions from scheme
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;; 
5 ;;;; (c) 2004--2007  Nicolas Sceaux  <nicolas.sceaux@free.fr>
6 ;;;;           Jan Nieuwenhuizen <janneke@gnu.org>
8 (define gen-lily-sym
9   ;; Generate a lilyvartmpXX symbol, that may be (hopefully) unique.
10   (let ((var-idx -1))
11     (lambda ()
12       (set! var-idx (1+ var-idx))
13       (string->symbol (format #f "lilyvartmp~a"
14                               (list->string (map (lambda (chr)
15                                                    (integer->char (+ (char->integer #\a)
16                                                                      (- (char->integer chr)
17                                                                         (char->integer #\0)))))
18                                                  (string->list (number->string var-idx)))))))))
20 (define-public (parse-string-result str parser)
21   "Parse `str', which is supposed to contain a music expression."
23   (ly:parser-parse-string
24    parser
25    (format #f "parseStringResult = \\notemode { ~a }" str))
26   (ly:parser-lookup parser 'parseStringResult))
28 (define-public (read-lily-expression chr port)
29   "Read a #{ lily music expression #} from port and return
30 the scheme music expression. The $ character may be used to introduce
31 scheme forms, typically symbols. $$ may be used to simply write a `$'
32 character."
33   (let ((bindings '()))
35     (define (create-binding! val)
36       "Create a new symbol, bind it to `val' and return it."
37       (let ((tmp-symbol (gen-lily-sym)))
38         (set! bindings (cons (cons tmp-symbol val) bindings))
39         tmp-symbol))
40     
41     (define (remove-dollars! form)
42       "Generate a form where `$variable' and `$ value' mottos are replaced
43       by new symbols, which are binded to the adequate values."
44       (cond (;; $variable
45              (and (symbol? form)
46                   (string=? (substring (symbol->string form) 0 1) "$")
47                   (not (and (<= 2 (string-length (symbol->string form)))
48                             (string=? (substring (symbol->string form) 1 2) "$"))))
49              (create-binding! (string->symbol (substring (symbol->string form) 1))))
50             (;; atom
51              (not (pair? form)) form)
52             (;; ($ value ...)
53              (eqv? (car form) '$)
54              (cons (create-binding! (cadr form)) (remove-dollars! (cddr form))))
55             (else ;; (something ...)
56              (cons (remove-dollars! (car form)) (remove-dollars! (cdr form))))))
57     
58     (let ((lily-string (call-with-output-string
59                         (lambda (out)
60                           (do ((c (read-char port) (read-char port)))
61                               ((and (char=? c #\#)
62                                     (char=? (peek-char port) #\})) ;; we stop when #} is encoutered
63                                (read-char port))
64                             (cond
65                              ;; a $form expression
66                              ((and (char=? c #\$) (not (char=? (peek-char port) #\$)))
67                               (format out "\\~a" (create-binding! (read port))))
68                              ;; just a $ character
69                              ((and (char=? c #\$) (char=? (peek-char port) #\$))
70                               ;; pop the second $
71                               (display (read-char port) out))
72                              ;; a #scheme expression
73                              ((char=? c #\#)
74                               (let ((expr (read port)))
75                                 (format out "#~s" (if (eq? '$ expr)
76                                                       (create-binding! (read port))
77                                                       (remove-dollars! expr)))))
78                              ;; other caracters
79                              (else
80                               (display c out))))))))
81       `(let ((parser-clone (ly:parser-clone parser)))
82          ,@(map (lambda (binding)
83                   `(ly:parser-define! parser-clone ',(car binding) ,(cdr binding)))
84                 (reverse bindings))
85          (parse-string-result ,lily-string parser-clone)))))
87 (read-hash-extend #\{ read-lily-expression)