lilypond-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

markup syntax in scheme


From: Nicolas Sceaux
Subject: markup syntax in scheme
Date: Sat, 31 Jan 2004 17:09:13 +0100
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

Hello,

Here is a proposal for a lilypond-like markup notation in scheme, in
order to ease markup command definition by users.

For instance:
  \markup { foo
            \raise #0.2 \hbracket \bold bar
            \override #'(baseline-skip . 4)
                      \bracket \column < baz bazr bla >
  }
  === (proposed syntax)
  (markup "foo"
          #:raise 0.2 #:hbracket #:bold "bar"
          #:override '(baseline-skip . 4) 
                     #:bracket #:column ("baz" "bazr" "bla"))
  === (how it can be done today)
  (make-line-markup
    "foo"
    (make-raise-markup 0.2 (make-hbracket-markup (make-bold-markup "bar")))
    (make-override-markup '(baseline-skip . 4)
                          (make-bracket-markup (make-column-markup
                                                 (list "baz" "bazr" "bla")))))

The third expression may be less accessible than the second.

The following example shows how to translate LilyPond markup notation
into this scheme notation:

------------------------------------------------------
\score {
    \notes {
        \fatText
        f'1-\markup {
            foo
            \raise #0.2 \hbracket \bold bar
            \override #'(baseline-skip . 4)

            \bracket \column < baz bazr bla >
            \hspace #2.0
            \override #'(font-family . music) {
                \lookup #"noteheads-0"
                \char #53
            }
            \musicglyph #"accidentals--1"
            \combine "X" "+"   
            \combine "o" "/"
            \box \column < { "string 1" } { "string 2" } >
            "$\\emptyset$"
            \italic Norsk
            \super "2"
            \dynamic sfzp
            \huge { "A" \smaller "A" \smaller \smaller "A"
                    \smaller \smaller \smaller "A" }
            \sub "alike"
        }       
        \break
        f'1-#(markup* 
              "foo"
              #:raise 0.2 #:hbracket #:bold "bar"
              #:override '(baseline-skip . 4) 
              #:bracket #:column ( "baz" "bazr" "bla" )
              #:hspace 2.0
              #:override '(font-family . music) #:line (#:lookup "noteheads-0" 
                                                        #:char 53)
              #:musicglyph "accidentals--1"
              #:combine "X" "+"   
              #:combine "o" "/"
              #:box #:column ("string 1" "string 2")
              "$\\emptyset$"
              #:italic "Norsk"
              #:super "2"
              #:dynamic "sfzp"
              #:huge #:line ("A" #:smaller "A" #:smaller #:smaller "A" 
                             #:smaller #:smaller #:smaller "A")
              #:sub "alike")
    }
    \paper { 
        raggedright = ##t
        indent = #0
        \translator {
            \StaffContext
            \remove Time_signature_engraver 
        }
    }
}
------------------------------------------------------

This one shows how to use the `markup' macro in order to define a
markup command:

------------------------------------------------------
#(define-public (number-or-string? obj)
  (or (number? obj)
      (string? obj)))

#(def-markup-command (tempo paper props tempo1 tempo2) (string? 
number-or-string?)
  "Syntax: \\tempo duration-string number
or: \\tempo duration-string1 duration-string2
eg: \\tempo #\"4.\" #120  ==> quater = 120
or: \\tempo #\4.\" \"4\"  ==> dotted-quater = quater"
  (let ((markup1 (markup #:tiny #:note tempo1 0.7))
        (markup2 (if (number? tempo2)
                     (number->string tempo2)
                     (markup #:tiny #:note tempo2 0.7))))
    (interpret-markup paper props (markup markup1 "=" markup2))))

\score {
    \notes {
        \time 4/4
        c''1^\markup \tempo #"4" #120
        \time 6/8
        c''2.^\markup \tempo #"4." #"4"
    }
    \paper { raggedright = ##t }
}
------------------------------------------------------

PNG image

If you find that it might be interesting, here is a patch for
new-markup.scm

--- new-markup.scm.~1.63.~      2004-01-25 17:10:20.000000000 +0100
+++ new-markup.scm      2004-01-31 15:59:34.000000000 +0100
@@ -82,6 +82,123 @@
                    error-msg #f)
         (cons markup-function args))))
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; markup constructors
+;;; lilypond-like syntax for markup construction in scheme.
+
+(use-modules (ice-9 optargs)
+             (ice-9 receive))
+
+(defmacro*-public markup (#:rest body)
+  "The `markup' macro provides a lilypond-like syntax for building markups.
+ - #:COMMAND is used instead of \\COMMAND
+ - #:lines ( ... ) is used instead of { ... }
+ - #:center ( ... ) is used instead of \\center < ... >
+ - etc.
+Example:
+  \\markup { foo
+            \\raise #0.2 \\hbracket \\bold bar
+            \\override #'(baseline-skip . 4)
+            \\bracket \\column < baz bazr bla >
+  }
+         <==>
+  (markup \"foo\"
+          #:raise 0.2 #:hbracket #:bold \"bar\"
+          #:override '(baseline-skip . 4) 
+          #:bracket #:column (\"baz\" \"bazr\" \"bla\"))
+Use `markup*' in a \\notes block."
+  (car (compile-all-markup-expressions `(#:line ,body))))
+
+(defmacro*-public markup* (#:rest body)
+  "Same as `markup', for use in a \\notes block."
+  `(ly:export (markup ,@body)))
+  
+  
+(define (compile-all-markup-expressions expr)
+  "Return a list of canonical markups expressions, eg:
+  (#:COMMAND1 arg11 arg12 #:COMMAND2 arg21 arg22 arg23)
+  ===>
+  ((make-COMMAND1-markup arg11 arg12)
+   (make-COMMAND2-markup arg21 arg22 arg23) ...)"
+  (do ((rest expr rest)
+       (markps '() markps))
+      ((null? rest) (reverse markps))
+    (receive (m r) (compile-markup-expression rest)
+             (set! markps (cons m markps))
+             (set! rest r))))
+
+(define (keyword->make-markup key)
+  "Transform a keyword, eg. #:COMMAND, in a make-COMMAND-markup symbol."
+  (string->symbol (string-append "make-" (symbol->string (keyword->symbol 
key)) "-markup")))
+
+(define (compile-markup-expression expr)
+  "Return two values: the first complete canonical markup expression found in 
`expr',
+eg (make-COMMAND-markup arg1 arg2 ...), and the rest expression."
+  (cond ((and (pair? expr)
+              (keyword? (car expr)))
+         ;; expr === (#:COMMAND arg1 ...)
+         (let* ((command (symbol->string (keyword->symbol (car expr))))
+                (sig (markup-command-signature (car (lookup-markup-command 
command))))
+                (sig-len (length sig)))
+           (do ((i 0 (1+ i))
+                (args '() args)
+                (rest (cdr expr) rest))
+               ((>= i sig-len)
+                (values (cons (keyword->make-markup (car expr)) (reverse 
args)) rest))
+             (cond ((eqv? (list-ref sig i) markup-list?)
+                    ;; (car rest) is a markup list
+                    (set! args (cons `(list ,@(compile-all-markup-expressions 
(car rest))) args))
+                    (set! rest (cdr rest)))
+                   (else
+                    ;; pick up one arg in `rest'
+                    (receive (a r) (compile-markup-arg rest)
+                             (set! args (cons a args))
+                             (set! rest r)))))))
+        ((and (pair? expr)
+              (pair? (car expr))
+              (keyword? (caar expr)))
+         ;; expr === ((#:COMMAND arg1 ...) ...)
+         (receive (m r) (compile-markup-expression (car expr))
+                  (values m (cdr expr))))
+        (else
+         ;; expr === (symbol ...) or ("string" ...) or ((funcall ...) ...)
+         (values (car expr)
+                 (cdr expr)))))
+
+(define (compile-all-markup-args expr)
+  "Transform `expr' into markup arguments"
+  (do ((rest expr rest)
+       (args '() args))
+      ((null? rest) (reverse args))
+    (receive (a r) (compile-markup-arg rest)
+             (set! args (cons a args))
+             (set! rest r))))
+
+(define (compile-markup-arg expr)
+  "Return two values: the desired markup argument, and the rest arguments"
+  (cond ((null? expr)
+         ;; no more args
+         (values '() '()))
+        ((keyword? (car expr))
+         ;; expr === (#:COMMAND ...)
+         ;; ==> build and return the whole markup expression
+         (compile-markup-expression expr))
+        ((and (pair? (car expr))
+              (keyword? (caar expr)))
+         ;; expr === ((#:COMMAND ...) ...)
+         ;; ==> build and return the whole markup expression(s)
+         ;; found in (car expr)
+         (receive (markup-expr rest-expr) (compile-markup-expression (car 
expr))
+                  (if (null? rest-expr)
+                      (values markup-expr (cdr expr))
+                      (values `(list ,markup-expr ,@(compile-all-markup-args 
rest-expr))
+                              (cdr expr)))))
+        ((and (pair? (car expr))
+              (pair? (caar expr)))
+         ;; expr === (((foo ...) ...) ...)
+         (values (cons 'list (compile-all-markup-args (car expr))) (cdr expr)))
+        (else (values (car expr) (cdr expr)))))
+
 ;;;;;;;;;;;;;;;
 ;;; Utilities for storing and accessing markup commands signature
 ;;; and keyword.
Changes:
(markup) a macro that provides a LilyPond-like syntax in scheme for
building markups, in order to help markup command definition.

nicolas

reply via email to

[Prev in Thread] Current Thread [Next in Thread]