Including only definitions from a Lilypond file

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Including only definitions from a Lilypond file

Jérôme Plût-2
Hello everybody,

I have some lilypond files that contain both music variables and
typesetting instructions, like this:

soprano = \relative {
  % music here...
}

\bookpart {
  % typesetting here...
}

I would like to include this file while importing the definitions and
ignoring the typesetting. (The long-term goal is to display some
statistics (range, etc.) about the various voices in the piece. So the
including file will want to know what \soprano contains, but to
replace all typesetting instructions by its own).

I *could* do this by writing a separate (e.g. Perl) parser that looks
only for music definitions, but it would be cleaner (and also more
robust) if there existed a way to do this from inside Lilypond or
Scheme. Ideally I would need the parser to obtain both the list of
variables defined in the file and the contents of those variables.

Before I start hacking something: does there already exist a simpler
way to do this?

Thanks,

--
        Jérôme
 

Reply | Threaded
Open this post in threaded view
|

Re: Including only definitions from a Lilypond file

Hans Aikema-2

> On 13 Aug 2020, at 20:19, Jérôme Plût <[hidden email]> wrote:
>
> Hello everybody,
>
> I have some lilypond files that contain both music variables and
> typesetting instructions, like this:
>
> soprano = \relative {
>  % music here...
> }
>
> \bookpart {
>  % typesetting here...
> }
>
> I would like to include this file while importing the definitions and
> ignoring the typesetting. (The long-term goal is to display some
> statistics (range, etc.) about the various voices in the piece. So the
> including file will want to know what \soprano contains, but to
> replace all typesetting instructions by its own).
>
> I *could* do this by writing a separate (e.g. Perl) parser that looks
> only for music definitions, but it would be cleaner (and also more
> robust) if there existed a way to do this from inside Lilypond or
> Scheme. Ideally I would need the parser to obtain both the list of
> variables defined in the file and the contents of those variables.
>
> Before I start hacking something: does there already exist a simpler
> way to do this?
>
> Thanks,
>
> --
>        Jérôme
>

Hi Jérôme,

Another way you might approach it would be splitting the file in two files and including the file that holds the soprano definition in both your 'layout' file (the other half of your current file) and the new 'statistics computation' file.

It's in general similar to the way that I currently setup my choral music scores - one file for each voice, a series of includes for various standard layouts and a 'linking them all' file that \include s both the various voice definition files (holding standard named variables for a voice's music and the associated lyrics) and the applicable standard layout file(s).

kind regards,
Hans


Reply | Threaded
Open this post in threaded view
|

Re: Including only definitions from a Lilypond file

David Kastrup
In reply to this post by Jérôme Plût-2
Jérôme Plût <[hidden email]> writes:

> Hello everybody,
>
> I have some lilypond files that contain both music variables and
> typesetting instructions, like this:
>
> soprano = \relative {
>   % music here...
> }
>
> \bookpart {
>   % typesetting here...
> }
>
> I would like to include this file while importing the definitions and
> ignoring the typesetting. (The long-term goal is to display some
> statistics (range, etc.) about the various voices in the piece. So the
> including file will want to know what \soprano contains, but to
> replace all typesetting instructions by its own).
>
> I *could* do this by writing a separate (e.g. Perl) parser that looks
> only for music definitions, but it would be cleaner (and also more
> robust) if there existed a way to do this from inside Lilypond or
> Scheme. Ideally I would need the parser to obtain both the list of
> variables defined in the file and the contents of those variables.
>
> Before I start hacking something: does there already exist a simpler
> way to do this?

All of the elements in a score are routed through hooks you can
redefine.  So you can just redefine your various hooks to do nothing and
then include the file.

The hooks all end in "-handler".  I see

dak@lola:/usr/local/tmp/lilypond$ git grep -e -handler lily/parser.yy
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-book-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-bookpart-handler");
lily/parser.yy:                  ? "toplevel-book-handler"
lily/parser.yy:                  : "toplevel-bookpart-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-score-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-music-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-text-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("toplevel-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("toplevel-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("toplevel-score-handler");
lily/parser.yy:                         SCM proc = parser->lexer_->lookup_identifier ("context-mod-music-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("book-bookpart-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("book-score-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("book-music-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("book-text-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("book-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("book-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("book-score-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("bookpart-score-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("bookpart-music-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("bookpart-text-handler");
lily/parser.yy:         SCM proc = parser->lexer_->lookup_identifier ("bookpart-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("bookpart-text-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("bookpart-score-handler");
lily/parser.yy:                         ("output-def-music-handler");
lily/parser.yy:                              ("output-def-music-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("context-mod-music-handler");
lily/parser.yy:                 SCM proc = parser->lexer_->lookup_identifier ("context-mod-music-handler");


--
David Kastrup

Reply | Threaded
Open this post in threaded view
|

Re: Including only definitions from a Lilypond file

Aaron Hill
On 2020-08-14 4:48 am, David Kastrup wrote:
> All of the elements in a score are routed through hooks you can
> redefine.  So you can just redefine your various hooks to do nothing
> and
> then include the file.

Would something like this work?

%%%%
\version "2.20.0"

#(begin
   (use-modules (ice-9 regex))
   (let* ((symbols
            (map (lambda (m) (string->symbol (match:substring m 1)))
                 (list-matches "define ([a-z-]+-handler)"
                   (ly:gulp-file "declarations-init.ly"))))
          (procs (map primitive-eval symbols))
          (null-proc (lambda args #f)))
     (ly:parser-define! 'disableHandlers
       (define-void-function () ()
         (for-each
           (lambda (sym) (primitive-eval `(set! ,sym ,null-proc)))
           symbols)))
     (ly:parser-define! 'restoreHandlers
       (define-void-function () ()
         (for-each
           (lambda (sym proc) (primitive-eval `(set! ,sym ,proc)))
           symbols procs)))))

assert =
#(define-void-function
   (expr)
   (scheme?)
   (or (primitive-eval expr)
       (ly:error "Assertion failed: ~s" expr)))

\disableHandlers

foo = { f'4 4 2 }
\assert #'(defined? 'foo)

\markup "Hidden"
{ \foo }
\assert #'(eq? 0 (length toplevel-scores))

\restoreHandlers

\markup "Visible"
{ \foo }
\assert #'(eq? 2 (length toplevel-scores))
%%%%


-- Aaron Hill

Reply | Threaded
Open this post in threaded view
|

Re: Including only definitions from a Lilypond file

Jérôme Plût-2
> On 2020-08-14 4:48 am, David Kastrup wrote:
> > All of the elements in a score are routed through hooks you can
> > redefine.  So you can just redefine your various hooks to do nothing and
> > then include the file.
>
> Would something like this work?
>
> %%%%
> \version "2.20.0"
>
> #(begin
>   (use-modules (ice-9 regex))
>   (let* ((symbols
>            (map (lambda (m) (string->symbol (match:substring m 1)))
>                 (list-matches "define ([a-z-]+-handler)"
>                   (ly:gulp-file "declarations-init.ly"))))
>          (procs (map primitive-eval symbols))
>          (null-proc (lambda args #f)))
>     (ly:parser-define! 'disableHandlers
>       (define-void-function () ()
>         (for-each
>           (lambda (sym) (primitive-eval `(set! ,sym ,null-proc)))
>           symbols)))
>     (ly:parser-define! 'restoreHandlers
>       (define-void-function () ()
>         (for-each
>           (lambda (sym proc) (primitive-eval `(set! ,sym ,proc)))
>           symbols procs)))))

This mostly works (except that I only needed to replace
"toplevel-.*-handler" functions), thanks!

Now I would like to use this to programmatically include a given file,
e.g. defining a Scheme function #(my-function "filename.ly") that would do
something equivalent to

#(compute something with "filename.ly")
\include "filename.ly"
#(compute something else with "filename.ly")

I tried to call (ly:parser-include-string (string-concatenate
`("\\disableHandlers \\include \"" ,filename "\""))) inside
my-function, but this produces
"fatal error: call-after-session used after session start".
(Some variants using ly:parser-parse-string, current-parser,
ly:parser-clone did not fare better).

Currently I see one (poor man's) way of doing that, which is calling

lilypond -e '(include "myfile.scm")' filename.ly

where "myfile.scm" does the first part of computation and sets up one
of the toplevel-.*-handler functions to do the second part of the
computation. (I've not tried it yet, though!). However this is quite
inelegant and, in particular:
 - likely not too robust w.r.t the content of "filename.ly",
 - does not allow including more than one file in sequence.

Is there a better way of doing this?

Thanks,

--
        Jérôme Plût