lilypond-devel
[Top][All Lists]
Advanced

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

parser variables persist beyond { } scope


From: Mark Polesky
Subject: parser variables persist beyond { } scope
Date: Thu, 30 Jul 2009 12:19:32 -0700 (PDT)

I remember being confused and frustrated about this long ago. Now I've
hit upon it again. Parser variables -- I don't know if this is the
official term, but it's what Neil used to describe variables that are
accessible with ly:parser-lookup. He gave the example of
afterGraceFraction, which can be defined outside of a music block,
LilyPond-style (see music-functions-init.ly), or within a music block,
scheme-style (see LM 1.2.6 Grace notes).

The problem with this approach is that if one uses "define" (or
preferably "set!" as Neil mentioned) to change the variable within a
music block, the new value persists beyond the closing curly-brace of
the music block. It will even persist into any subsequent score blocks,
which can lead to unexpected results. Even worse, the source of these
unexpected results may be difficult to track down, especially in large
projects with multiple score blocks.

One reason that I found this particularly frustrating was the implicit
(and apparently incorrect?) belief that curly braces in LilyPond define
boundaries for a local scope (as in many programming languages). Please
clarify this for me if my understanding is inaccurate.

Here's a demonstration of the problem:

*********************
\version "2.13.3"

\score {
  \relative {
    <<
    { c1 \afterGrace d1 { c16[ d] } c1 }
    #(set! afterGraceFraction (cons 15 16))
    { c1 \afterGrace d1 { c16[ d] } c1 }
    >>
  }
}

\score {
  \relative {
    <<
    %% value of "afterGraceFraction" inherited from previous score!
    { c1 \afterGrace d1 { c16[ d] } c1 }
    #(set! afterGraceFraction (cons 15 16))
    { c1 \afterGrace d1 { c16[ d] } c1 }
    >>
  }
}
*********************

I experimented a little bit with one possible solution, but I don't know
how practical the rest of you will find it. For each parser variable, it
is possible to define a separate "default" value. The existing variable
would be initialized to the default value, and it would be reset to the
default value after each call to the relevant command (eg. \afterGrace).
So if the user changes the existing variable, the new value will only be
in effect for that call. This way subsequent calls wouldn't accidentally
inherit the new value. If a user wants to set the same value for all
scores, without having to keep resetting it, s/he could simply reset the
*default* value.

Using afterGraceFraction as an example, the relevant lines in
ly/music-functions-init.ly would be changed to this:

*********************
afterGraceFractionDefault = #(cons 6 8)
afterGraceFraction = #afterGraceFractionDefault

afterGrace =
#(define-music-function
  (parser location main grace)
  (ly:music? ly:music?)
  (_i "Create @var{grace} note(s) after a @var{main} music expression.")
  (let*
      ((main-length (ly:music-length main))
       (fraction  (ly:parser-lookup parser 'afterGraceFraction)))

    ; reset afterGraceFraction to default so later \afterGrace blocks
    ; don't inherit the value set by user for this block.
    (set! afterGraceFraction
          (ly:parser-lookup parser 'afterGraceFractionDefault))

    (make-simultaneous-music
     (list
      main
      (make-sequential-music
       (list

        (make-music 'SkipMusic
                    'duration (ly:make-duration
                               0 0
                               (* (ly:moment-main-numerator main-length)
                                  (car fraction))
                               (* (ly:moment-main-denominator main-length)
                                  (cdr fraction)) ))
        (make-music 'GraceMusic
                    'element grace)))))))

*********************

And in the ly file...

*********************
\version "2.13.3"

\score {
  \relative {
    <<
      %% uses default value (cons 6 8)
      { c4 \afterGrace d4 { c16[ d] } c2 }
      
      %% this changes the value for the next block only:
      #(set! afterGraceFraction (cons 15 16))
      { c4 \afterGrace d4 { c16[ d] } c2 }
      
      %% automatically reverts to default value (cons 6 8)
      { c4 \afterGrace d4 { c16[ d] } c2 }
      >>
  }
}

%% To properly reset the default value, both values must be set;
%% otherwise the new value won't be used until *after* the next call.
#(set! afterGraceFractionDefault (cons 15 16))
#(set! afterGraceFraction (cons 15 16))
      
\score {
  \relative {
    <<
      %% now the new default value is (cons 15 16)
      { c4 \afterGrace d4 { c16[ d] } c2 }
      
      %% this changes the value for the next block only:
      #(set! afterGraceFraction (cons 6 8))
      { c4 \afterGrace d4 { c16[ d] } c2 }
      
      %% automatically reverts to the new default value (cons 15 16)
      { c4 \afterGrace d4 { c16[ d] } c2 }
      >>
  }
}
*********************

What do you guys think? Is this a good idea or is it totally
unnecessary? Would you rather just add a warning in the docs? Or
(ideally) is there some way to actually limit the scope of these
parser variables?

Let me know.
- Mark


      




reply via email to

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