lilypond-devel
[Top][All Lists]
Advanced

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

Re: music macros?


From: Erik Sandberg
Subject: Re: music macros?
Date: Sat, 4 Feb 2006 00:07:04 +0100
User-agent: KMail/1.8.3

On Thursday 02 February 2006 02.35, Han-Wen Nienhuys wrote:
> Erik Sandberg wrote:
> > Yes, just represent those commands as music functions internally. E.g.,
> > \transpose creates a MusicFunction expression, with a list of two pitches
> > and one Music as its 'elements (or 'arguments, I haven't decided yet),
> > and 'to-music-callback is set to ly:music-transpose (or rather, to a
> > wrapper around that function).
>
> OK, so we create all music expressions/events as "Music promises", which
> expand into Music objects via some function as soon as they are
> inspected, but have \relative deal with music-promises directly?

How about this: The parser spits out a big evaluatable Scheme expression, 
which simply is eval:ed after parsing. A bit like the return value of 
music->make-music, but with music functions & macros written as real Scheme 
functions.

The good thing about this solution, is the simplicity and the powerfulness (I 
suppose that lots of things would be handled automatically by Scheme, perhaps 
we will even get local variables for free). The bad thing, is that the 
\relative macro will be rather ugly (it will need to traverse (make-music ..) 
calls and modify pitches).

The approach requires quite large changes to the parser: All identifiers (both 
functions and variables) need to be treated equal, which means that:
{ c \partcombine f g }
is parsed into
(eval-list ((make-music ...) partcombine (make-music ...) (make-music ...)))
which, when evaluated, becomes the same as:
(list 'c (partcombine 'f 'g))
I.e., parentheses are added where appropriate, using our knowledge of 
partcombine's arity. The reason why this must be done after parsing, is that 
variables can be assigned functions during "runtime".

/* The eval-list function would look something like:
(define (eval-list ls)
 (if (pair? ls)
  (let ((a (car ls))
        (d (cdr ls)))
   (if (procedure? a)
    (let ((arity (car (procedure-property a 'arity))))
     (cons (primitive-eval (cons a (list-head d arity)))
           (eval-list (list-tail d arity))))
    (cons a (eval-list d))))
 ls))
.. plus a special case for macros.
*/

This is the basic idea. The structure can be cleaned up further: E.g.,
we write our own list evaluation function, so we can just as well store Music 
objects directly, instead of make-music calls, and make a special case in 
eval-list. This would make the \relative macro a bit cleaner.

Given the mechanisms described above, I think it's pretty straightforward to 
write a .ly file which defines a full functional language, that uses 
lilypond-style syntax. (e.g. fun = \lambda {\x \y} {c8 d \x e4 \y \y })

-- 
Erik




reply via email to

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