Hi Kieren, James and all,
I extracted my edition-engraver from lalily. The example compiles here
with 2.18.
You can try this and comment ... I will add more comments to the code,
if this is of interest ;)
The compilation creates a file example.edition.log containing all
edition-engraver paths.
I use this inside lalily. There is always a current "music-path", which
is used to create the path for the engraver. Then the edition-engraver
paths are made by the template in use, so I don't have to deal with them.
HTH
Best, Jan-Peter
On 14.01.2014 14:46, James wrote:
> On 14/01/14 07:17, Jan-Peter Voigt wrote:
>> Hi Kieren and all,
>>
>> I use an engraver for this task, that looks for overrides, sets and
>> clefs at the current measure (of the context consisting this engraver)
>> and at the current moment inside this measure. So this is a little bit
>> different from your example in that it listens during compilation, while
>> your example modifies a music expression /before/ compilation.
>>
>> My intention was the ability to use a pen and mark all grobs to modify
>> on a printed sheet of music - without any links to the source.
>> Now I just write
>>
>> \editionMod <the edition name> <measure> <moment> <engraver-id> {...}
>>
>> and I can switch on and off the edition-overrides as needed.
>> This approach should be able to produce any grob, but beside overrides
>> and sets I have only implemented inserting clefs and TextScript-grobs.
>>
>> To implement a function, that modifies the music expression without
>> compiling means, that you have to know the run of time-signatures. If
>> you don't know that, you don't know when measure X starts.
>> If you have a global variable for all time signatures, you might look
>> for all time-signature-events and accumulate them to produce a
>> skip-expression.
>>
>> addAt =
>> #(define-music-function (parser location music measure moment
>> addition)(ly:music? integer? ly:moment ly:music?)
>> ; ... create myAdd = { \skip #to-measure-moment $addition }
>> ; and return
>> (make-music 'SimultaneousMusic 'elements (list music myAdd))
>> )
>>
>> HTHOIALI (hth or is at least interesting)
>>
>> Cheers,
>> Jan-Peter
>
> Could you give a working .ly example?
>
> It might prove helpful for other users that cannot necessarily
> understand scheme (like me :) )
>
> Also if it compiles on 2.14 then we could add it to the LSR for other
> users.
>
> James
\version "2.18.0"
\include "util.ily"
#(use-modules (oop goops))
% custom string representation of a moment
#(define-public (moment->string mom)
(if (ly:moment? mom)
(let ((num (ly:moment-main-numerator mom))
(den (ly:moment-main-denominator mom))
(gnum (ly:moment-grace-numerator mom))
(gden (ly:moment-grace-denominator mom)))
(format "(~A/~A~A)" num den
(cond
((> gnum 0)(format "+~A/~A" gnum gden))
((< gnum 0)(format "~A/~A" gnum gden))
(else "")
))
)
"(?:?)"
))
%%%%%%%%%%%%%
% class to store for example \set stanza = "1."
#(define-class <propset> ()
(once #:init-value #t #:accessor is-once #:setter set-once! #:init-keyword
#:once)
(symbol #:accessor get-symbol #:setter set-symbol! #:init-keyword #:symbol)
(value #:accessor get-value #:setter set-value! #:init-keyword #:value)
(previous #:accessor get-previous #:setter set-previous! #:init-value #f)
(context #:accessor get-context #:setter set-context! #:init-keyword
#:context)
)
% apply set to context
#(define-method (do-propset context (prop <propset>))
(if (get-context prop)
(let ((parctx (ly:context-find context (get-context prop))))
(if (ly:context? parctx) (set! context parctx))))
(set-previous! prop (ly:context-property context (get-symbol prop)))
(ly:context-set-property! context (get-symbol prop) (get-value prop))
)
%(export do-propset)
% apply unset to context
#(define-method (reset-prop context (prop <propset>))
(if (get-context prop)
(let ((parctx (ly:context-find context (get-context prop))))
(if (ly:context? parctx) (set! context parctx))))
(ly:context-set-property! context (get-symbol prop) (get-previous prop))
)
%(export reset-prop)
% predicate
#(define-public (propset? p)(is-a? p <propset>))
% serialize to string
#(define-method (propset->string (ps <propset>))
(format "~A\\set ~A = ~A" (if (is-once ps) "once " "") (string-append (if
(get-context ps) (format "~A." (get-context ps)) "") (format "~A" (get-symbol
ps))) (get-value ps)))
%(export propset->string)
% implement display
#(define-method (display (o <propset>) port) (display (propset->string o) port))
%%%%%%%%%%%%%
% store applyContext
#(define-class <apply-context> ()
(proc #:accessor procedure #:setter set-procedure! #:init-keyword #:proc)
)
% apply stored function to context
#(define-method (do-apply ctx (a <apply-context>))
((procedure a) ctx))
%(export do-apply)
% predicate
#(define-public (apply-context? a)(is-a? a <apply-context>))
% store overrides
#(define-class <override> ()
(once #:init-value #t #:accessor is-once #:setter set-once! #:init-keyword
#:once)
(revert #:init-value #f #:accessor is-revert #:setter set-revert!
#:init-keyword #:revert)
(grob #:accessor get-grob #:setter set-grob! #:init-keyword #:grob)
(prop #:accessor get-prop #:setter set-prop! #:init-keyword #:prop)
(value #:accessor get-value #:setter set-value! #:init-keyword #:value)
(context #:accessor get-context #:setter set-context! #:init-keyword
#:context)
)
% serialize to string
#(define-method (oop->string (o <override>))
(let* ((ctxn (get-context o))
(ctxp (if ctxn (format "~A." ctxn) "")))
(if (is-revert o)
(string-append "\\revert " ctxp (format "~A " (get-grob o)) (format
"#'~A" (get-prop o)))
(string-append (if (is-once o) "\\once " "") "\\override " ctxp (format
"~A " (get-grob o)) (format "#'~A" (get-prop o)) " = " (format "~A" (get-value
o)))
)))
%(export oop->string)
% implement display
#(define-method (display (o <override>) port) (display (oop->string o) port))
% predicate
#(define-public (override? o)(is-a? o <override>))
% apply stored override to context
#(define-method (do-override ctx (mod <override>))
(if (get-context mod)
(let ((parctx (ly:context-find ctx (get-context mod))))
(if (ly:context? parctx) (set! ctx parctx))))
(ly:context-pushpop-property ctx (get-grob mod) (get-prop mod) (get-value
mod)))
%(export do-override)
% apply revert to context
#(define-method (do-revert ctx (mod <override>))
(if (get-context mod)
(let ((parctx (ly:context-find ctx (get-context mod))))
(if (ly:context? parctx) (set! ctx parctx))))
(ly:context-pushpop-property ctx (get-grob mod) (get-prop mod)))
%(export do-revert)
%%%%%%%%%%%%%
% stored edition tags
#(define-public (editions) #f)
% set edition tags
#(define-public (set-editions! ed) #f)
% add edition modification
#(define-public (add-edmod edition takt pos path mod) #f)
% create edition engraver with path
#(define-public (edition-engraver tag-path) #f)
% call proc with arg edition-engraver for all active
#(define-public (walk-edition-engravers proc) #f)
% display all stored modifications
#(define-public (display-mods) #f)
% display all registered edition-engraver paths
#(define-public (display-edition) #f)
% find edition-engraver in this or any parent context
#(define-public (context-find-edition-engraver context) #f)
#(define lalily:edition-tags 'lalily:edition-tags)
% now actually implement the needed functions
#(let ((mod-tree (tree-create 'mods))
(edition-list '())
(edition-tree (tree-create 'edition))
(context-count (tree-create 'context)))
(define (o->sym o) (cond ((symbol? o) o) ((string? o) (string->symbol o))
(else (string->symbol (format "~A" o)))))
(set! editions (lambda () (if (list? edition-list) edition-list '())))
(set! set-editions! (lambda (eds) (if (list? eds) (set! edition-list eds)
(ly:error "list expected: ~A" eds))))
(set! add-edmod
(lambda (edition takt pos path modm)
(let* ((edition (if (string? edition) (string->symbol edition)
edition))
(path `(,edition ,takt ,pos ,@path))
(mods (tree-get mod-tree path)))
(if (not (list? mods)) (set! mods '()))
(cond
((ly:music? modm)
(let ((x 0))
(define (add-mods modmus ctx)
(for-some-music
(lambda (m)
(cond
((eq? 'ContextSpeccedMusic (ly:music-property m 'name))
(let* ((ct (ly:music-property m 'context-type))
(elm (ly:music-property m 'element)))
(if (eq? 'Bottom ct)
#f
(begin
(add-mods elm ct)
#t)
)
))
((eq? 'OverrideProperty (ly:music-property m 'name))
(let* ((once (ly:music-property m 'once #f))
(grob (ly:music-property m 'symbol))
(prop (ly:music-property m 'grob-property))
(prop (if (symbol? prop)
prop
(car (ly:music-property m
'grob-property-path))))
(value (ly:music-property m 'grob-value))
(mod (make <override> #:once once #:grob grob
#:prop prop #:value value #:context ctx)))
(set! mods `(,@mods ,mod))
#t
))
((eq? 'RevertProperty (ly:music-property m 'name))
(let* ((grob (ly:music-property m 'symbol))
(prop (ly:music-property m 'grob-property))
(prop (if (symbol? prop)
prop
(car (ly:music-property m
'grob-property-path))))
(mod (make <override> #:once #f #:revert #t
#:grob grob #:prop prop #:value #f #:context ctx)))
(set! mods `(,@mods ,mod))
#t
))
((eq? 'PropertySet (ly:music-property m 'name))
(let* ((once (ly:music-property m 'once #f))
(symbol (ly:music-property m 'symbol))
(value (ly:music-property m 'value))
(mod (make <propset> #:once once #:symbol symbol
#:value value #:context ctx)))
(set! mods `(,@mods ,mod))
#t
))
((eq? 'ApplyContext (ly:music-property m 'name))
(let* ((proc (ly:music-property m 'procedure))
(mod (make <apply-context> #:proc proc)))
(set! mods `(,@mods ,mod))
))
((or
(eq? 'TextScriptEvent (ly:music-property m 'name))
(eq? 'LineBreakEvent (ly:music-property m 'name))
(eq? 'PageBreakEvent (ly:music-property m 'name))
(eq? 'PageTurnEvent (ly:music-property m 'name))
(eq? 'OttavaMusic (ly:music-property m 'name))
(eq? 'PartCombineForceEvent (ly:music-property m 'name))
(eq? 'ExtenderEvent (ly:music-property m 'name))
(eq? 'HyphenEvent (ly:music-property m 'name))
)
(set! mods `(,@mods ,m))
#t
)
(else #f)
)
)
modmus))
(add-mods modm #f)))
((ly:context-mod? modm)(set! mods `(,@mods ,modm)))
)
(tree-set! mod-tree path mods)
#f
)))
(set! edition-engraver
(lambda (tag-path . props)
(let ((eng #f)
(cmf (if (eq? #t tag-path) (get-music-folder)))) ; current
music folder
(define (get-sym c)(string->symbol (base26 c)))
(set! eng (lambda (context)
(let* ((tag-path tag-path)
(tag '())
(barnum 0)
(measurepos (ly:make-moment 0 1))
(get-path (lambda (edition takt pos) `(,edition
,takt ,pos ,@tag)))
(initialize
(lambda (trans)
(if (procedure? tag-path) (set! tag-path
(tag-path)))
(if (not (list? tag-path))
(let ((parent (ly:context-parent context))
(peng #f))
(define (search-peng path eng)
(if (eqv? (object-property eng
'context) parent)
(set! peng eng)))
(if (ly:context? parent)
(walk-edition-engravers search-peng))
(if peng (set! tag-path
(object-property peng 'tag-path)))
(if (not (list? tag-path))
(set! tag-path (if (list? cmf) cmf
(get-music-folder))))
))
(let* ((cn (ly:context-name context))
(path `(,@tag-path ,(o->sym cn)))
(ccid (tree-get context-count path)))
(define (topctx context)
(let ((par (ly:context-find context
'Score)))
(if (ly:context? par) (topctx par)
context)))
(if (not (integer? ccid))(set! ccid 0))
(tree-set! context-count path (+ ccid 1))
; (ly:message "~A ~A" ccid path)
(set! path `(,@path ,(get-sym ccid)))
(set! tag path)
(tree-set! edition-tree path
(cons eng
(let* ((c context)
(takt (ly:context-property c
'currentBarNumber))
(mpos (ly:context-property c
'measurePosition)))
(cons takt mpos) )))
(set-object-property! eng 'context context)
(set-object-property! eng 'tag-path
tag-path)
(set-object-property! eng 'path path)
; (if (lalily:verbose) (ly:message "looking
for editions in ~A" (glue-list path "/")))
)))
; paper column interface
(paper-column-interface (lambda (engraver grob
source-engraver)
(let ((takt (ly:context-property context
'currentBarNumber))
(pos (ly:context-property context
'measurePosition)))
(if (eq? #t (ly:grob-property grob
'non-musical))
(for-each
(lambda (edition)
(let* ((path (get-path edition
takt pos))
(mods (tree-get mod-tree
path)))
(if (list? mods)
(for-each
(lambda (mod)
(cond
((and (ly:music? mod)
(eq? 'LineBreakEvent (ly:music-property mod 'name)))
(set!
(ly:grob-property grob 'line-break-permission) (ly:music-property mod
'break-permission)))
((and (ly:music? mod)
(eq? 'PageBreakEvent (ly:music-property mod 'name)))
(set!
(ly:grob-property grob 'page-break-permission) (ly:music-property mod
'break-permission)))
((and (ly:music? mod)
(eq? 'PageTurnEvent (ly:music-property mod 'name)))
(set!
(ly:grob-property grob 'page-turn-permission) (ly:music-property mod
'break-permission)))
)) mods))))
(editions)))
)))
(start-translation-timestep
(lambda (trans . recall) ; recall from
process-music
(let ((takt (ly:context-property context
'currentBarNumber))
(pos (ly:context-property context
'measurePosition))
(modc '()))
(define (modc+ mod)(set! modc `(,@modc
,mod)))
(set! barnum takt)(set! measurepos pos)
(for-each (lambda (edition)
(let* ((path (get-path edition
takt pos))
(mods (tree-get mod-tree
path)))
;(display path)(display
mods)(newline)
(if (list? mods)
(for-each (lambda (mod)
(cond
((override?
mod)
(if
(is-revert mod)
(do-revert context mod)
(do-override context mod))
(modc+ mod))
((propset?
mod)
(do-propset
context mod)
(modc+ mod))
((apply-context? mod)
(do-apply
context mod))
((ly:context-mod? mod)
(ly:context-mod-apply! context mod)
(modc+ mod))
)) mods)
)
)) (editions))
; warning if start-translation-timestep is
not called in first place
(if (and (> (length modc) 0)(> (length
recall) 0) (eq? #t (car recall)))
(begin
(ly:warning "missing @ ~A ~A ~A" takt
pos (glue-list tag "/"))
(for-each (lambda (mod) (ly:warning
"---> ~A" mod)) modc)
))
)))
(stop-translation-timestep
(lambda (trans)
(let ((takt (ly:context-property context
'currentBarNumber))
(pos (ly:context-property context
'measurePosition)))
(for-each (lambda (edition)
(let* ((path (get-path edition
takt pos))
(mods (tree-get mod-tree
path)))
(if (list? mods)
(for-each (lambda (mod)
(cond
((and
(override? mod)(is-once mod))
(do-revert
context mod))
((and
(propset? mod)(is-once mod))
(reset-prop
context mod))
))
mods))
)) (editions))
)))
(process-music
(lambda (trans)
(let ((takt (ly:context-property context
'currentBarNumber))
(pos (ly:context-property context
'measurePosition)))
; recall start-translation-timestep, if it
is not called already
(if (or (not (equal? takt barnum))(not
(equal? measurepos pos)))
(start-translation-timestep trans #t))
(for-each (lambda (edition)
(let* ((path (get-path edition
takt pos))
(mods (tree-get mod-tree
path)))
(if (list? mods)
(for-each (lambda (mod)
(cond
((and
(ly:music? mod) (eq? 'TextScriptEvent (ly:music-property mod 'name)))
(let ((grob
(ly:engraver-make-grob trans 'TextScript '()))
(text
(ly:music-property mod 'text))
(direction (ly:music-property mod 'direction #f)))
(ly:grob-set-property! grob 'text text)
(if
direction (ly:grob-set-property! grob 'direction direction))
))
))
mods))
)) (editions))
)))
(finalize
(lambda (trans)
(if (eq? 'Score (ly:context-name context))
(let* ((takt (ly:context-property context
'currentBarNumber))
(pos (ly:context-property context
'measurePosition))
(parser (ly:assoc-get 'parser
props #f #f)))
(ly:message "(~A) finalize ~A (~A
~A)"
(glue-list (editions) ", ")
(glue-list tag "/")
takt (if (ly:moment? pos)
(moment->string pos) pos))
(if parser
(let* ((outname
(ly:parser-output-name parser))
(logfile (format
"~A.edition.log" outname)))
(ly:message "writing '~A' ..."
logfile)
(with-output-to-file logfile
(lambda()
(display-edition)
(display "<--- mods
--->")(newline)
(display-mods)
))
))
(set! context-count (tree-create
'context))
))))
)
`(
(initialize . ,initialize)
(acknowledgers
(paper-column-interface . ,paper-column-interface)
)
(start-translation-timestep .
,start-translation-timestep)
(stop-translation-timestep .
,stop-translation-timestep)
(process-music . ,process-music)
(finalize . ,finalize)
))))
eng)))
(set! walk-edition-engravers
(lambda (proc)
(tree-walk edition-tree '() ; walk all
(lambda (path key value)
(proc path (if (pair? value) (car value) value))
) '(empty . #f) '(sort . #f))
))
(set! context-find-edition-engraver
(lambda (context)
(let ((peng #f))
(define (search-peng path eng)
(if (eqv? (object-property eng 'context) context)
(set! peng eng)))
(if (ly:context? context) (walk-edition-engravers search-peng))
peng
)))
(set! display-edition (lambda () (tree-display edition-tree
'(pathsep . " ")
`(vformat . ,(lambda (p) (let ((m (if
(pair? p) (cdr p) p)))
(if (and (pair?
m)(ly:moment? (cdr m)))
(format
"(~A . ~A)" (car m)(moment->string (cdr m)))
(format
"~A" m))
)))
)))
(set! display-mods
(lambda ()
(tree-display mod-tree
'(pathsep . " ")
`(pformat . ,(lambda (v) (cond
((ly:moment? v) (moment->string v))
(else (format "~A" v))
)))
`(vformat . ,(lambda (v)
(if (list? v)
(glue-list (map (lambda (e)
(cond
((ly:music? e)
(format "[M] ~A"
(ly:music-property e 'name))
)
(else (format "~A" e)))) v)
"\n") (format "~A" v)))))))
)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define (frac-or-mom? v) (or (fraction? v)(ly:moment? v)))
#(define (music-or-contextmod? v) (or (ly:music? v)(ly:context-mod? v)))
#(define-public editionMod
(define-music-function (parser location edition takt pos path mod)
(string-or-symbol? integer? frac-or-mom? list? music-or-contextmod?)
"Add modification to edition @ measure moment"
(if (fraction? pos)(set! pos (ly:make-moment (car pos)(cdr pos))))
(add-edmod edition takt pos path mod)
(make-music 'SequentialMusic 'void #t))
)
#(define (list-or-boolean? v) (or (boolean? v)(list? v)(procedure? v)))
#(define-public editionEngraver
(define-scheme-function (parser location tag)(list-or-boolean?)
(edition-engraver tag `(parser . ,parser))))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%;;; music functions
% activate edition
#(define-public addEdition
(define-music-function (parser location edition)(string-or-symbol?)
"Add edition to edition-list.
Every edition from the global edition-list will be listened for by the
edition-engraver."
(if (string? edition) (set! edition (string->symbol edition)))
(if (not (memq edition (editions))) (set-editions! `(,@(editions)
,edition)))
(make-music 'SequentialMusic 'void #t)
))
% deactivate edition
#(define-public removeEdition
(define-music-function (parser location edition)(string-or-symbol?)
"Remove edition from edition-list.
Every edition from the global edition-list will be listened for by the
edition-engraver."
(if (string? edition) (set! edition (string->symbol edition)))
(set-editions! (delete edition (editions)))
(make-music 'SequentialMusic 'void #t)
))
% set editions
#(define-public setEditions
(define-void-function (parser location editions)(list?)
"Set edition-list to editions.
Every edition from the global edition-list will be listened for by the
edition-engraver.
This will override the previously set list."
(set-editions! (map (lambda (edition)
(cond
((symbol? edition) edition)
((string? edition) (string->symbol edition))
(else (string->symbol (format "~A" edition)))
)) editions))
))
\version "2.18.0"
\include "edition-engraver.ily"
\addEdition test
% color the notehead red on the second quarter in the second measure
\editionMod test 2 1/4 my.test.Staff.A \once \override NoteHead #'color = #red
% destroy the slur starting on the second quarter in the first measure
\editionMod test 1 2/4 my.test.Staff.A \shape #'((0 . 0)(0 . 1)(0 . -1)(0 . 0)) Slur
\layout {
\context {
\Score
\consists \editionEngraver my.test
}
}
\new Staff \with {
\consists \editionEngraver my.test
} <<
\new Voice \with {
\consists \editionEngraver ##f
} \relative c'' { c4 bes a( g) f e d c }
>>
\version "2.18.0"
#(use-modules (oop goops))
#(define-public (base26 i)
"produce a string A, B, ..., Z, AA, AB, ... for numbers
usable to allow 2.17+ list input like in: \\editionMod notes.sop.Voice.A
ATTENTION: there will be no ZZ but YZ -> AAA and YZZ -> AAAA"
(let ((A (char->integer (if (< i 0) #\a #\A)))
(i (if (< i 0) (- -1 i) i)))
(define (baseX x i)
(let ((q (quotient i x))
(r (remainder i x)))
(if (and (> q 0) (< q x))
(list (- q 1) r)
(let ((ret '()))
(if (> q 0) (set! ret (baseX x q)))
`(,@ret ,r))
)))
(list->string
(map
(lambda (d) (integer->char (+ A d)))
(baseX 26 i)))
))
#(define-public (glue-list lst glue)
"create string from list containing arbitrary objects"
(string-join (map (lambda (s) (format "~A" s)) lst) glue 'infix))
#(define-public (glue-symbol lst . glue)
"create symbol from list containig arbitrary objects"
(string->symbol (string-join (map (lambda (s) (format "~A" s)) lst) (if (>
(length glue) 0)(car glue) ":") 'infix)))
%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%;;; stack
%; a stack implementation with methods push, pop and get
#(define-class <stack> ()
(name #:accessor name #:setter set-name! #:init-value "stack")
(store #:accessor store #:setter set-store! #:init-value '())
)
#(define-method (push (stack <stack>) val)
(set! (store stack) (cons val (store stack))))
#(define-method (get (stack <stack>))
(let ((st (store stack)))
(if (> (length st) 0)
(car st)
#f)))
#(define-method (pop (stack <stack>))
(let ((st (store stack)))
(if (> (length st) 0)
(let ((ret (car st)))
(set! (store stack) (cdr st))
ret)
#f)))
#(define-method (display (stack <stack>) port)
(for-each (lambda (e)
(format #t "~A> " (name stack))(display e)(newline)) (store
stack)))
#(define-public (stack-create)(make <stack>))
%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%;;; tree
%; a tree implementation
%; every tree-node has a hashtable of children and a value
%; main methods are:
%; tree-set! <tree> path-list val: set a value in the tree
%; tree-get <tree> path-list: get a value from the tree or #f if not present
#(define-class <tree> ()
(children #:accessor children #:init-thunk make-hash-table)
(key #:accessor key #:init-keyword #:key #:init-value 'node)
(value #:accessor value #:setter set-value! #:init-value #f)
)
#(define-method (tree-set! (tree <tree>) (path <list>) val)
(if (= (length path) 0)
(set! (value tree) val)
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (not (is-a? child <tree>))
(begin (set! child (make <tree> #:key ckey))
(hash-set! (children tree) ckey child)
))
(tree-set! child cpath val)
))
val)
#(define-method (tree-merge! (tree <tree>) (path <list>) (proc <procedure>) val)
(let ((ctree (tree-get-tree tree path)))
(if (is-a? ctree <tree>)
(set! (value ctree) (proc (value ctree) val))
(tree-set! tree path (proc #f val)))
))
#(define-method (tree-get-tree (tree <tree>) (path <list>))
(if (= (length path) 0)
tree
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (is-a? child <tree>)
(tree-get-tree child cpath)
#f)
)))
#(define-method (tree-get (tree <tree>) (path <list>))
(let ((ctree (tree-get-tree tree path)))
(if (is-a? ctree <tree>) (value ctree) #f)))
#(define-method (tree-get-from-path (tree <tree>) (path <list>) skey val)
(if (equal? skey (key tree))(set! val (value tree)))
(let ((child (hash-ref (children tree) skey)))
(if (is-a? child <tree>)(set! val (value child))))
(if (= (length path) 0)
val
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (is-a? child <tree>)
(tree-get-from-path child cpath skey val)
val)
)))
#(define-method (tree-get-keys (tree <tree>) (path <list>))
(if (= (length path) 0)
(hash-map->list (lambda (key value) key) (children tree))
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (is-a? child <tree>)
(tree-get-keys child cpath)
#f)
)))
#(define-method (tree-dispatch (tree <tree>) (path <list>) (relative <list>)
def)
(let ((val (value tree)))
(if (= (length path) 0)
(if val (cons '() val)(cons relative def))
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (or val (not (list? relative))) (set! relative '()))
(if val (set! def (value tree)))
(if (is-a? child <tree>)
(tree-dispatch child cpath `(,@relative ,ckey) def)
`((,@relative ,@path) . ,def))
))))
#(define-method (tree-collect (tree <tree>) (path <list>) (vals <stack>))
(let ((val (value tree)))
(if (> (length path) 0)
(let* ((ckey (car path))
(cpath (cdr path))
(child (hash-ref (children tree) ckey))
)
(if (is-a? child <tree>) (tree-collect child cpath vals))
))
(if val (push vals val))
(reverse (store vals))
))
#(define (stdsort p1 p2)
(let ((v1 (car p1))
(v2 (car p2)))
(cond
((and (number? v1) (number? v2)) (< v1 v2))
((and (ly:moment? v1) (ly:moment? v2)) (ly:moment<? v1 v2))
(else (string-ci<? (format "~A" v1) (format "~A" v2)))
)))
#(define-method (tree-walk (tree <tree>) (path <list>) (callback <procedure>) .
opts)
(let ((dosort (assoc-get 'sort opts))
(sortby (assoc-get 'sortby opts stdsort))
(doempty (assoc-get 'empty opts)))
(if (or doempty (value tree))
(callback path (key tree) (value tree)))
(for-each (lambda (p)
(tree-walk (cdr p) `(,@path ,(car p)) callback `(sort .
,dosort) `(sortby . ,sortby) `(empty . ,doempty)))
(if dosort (sort (hash-table->alist (children tree)) sortby)
(hash-table->alist (children tree)) ))
))
#(define-method (tree-walk-branch (tree <tree>) (path <list>) (callback
<procedure>) . opts)
(let ((dosort (assoc-get 'sort opts))
(sortby (assoc-get 'sortby opts stdsort))
(doempty (assoc-get 'empty opts))
(ctree (tree-get-tree tree path)))
(if (is-a? ctree <tree>)
(tree-walk ctree path callback `(sort . ,dosort) `(sortby . ,sortby)
`(empty . ,doempty)))
))
#(define-public (tree-display tree . opt)
(let ((path (ly:assoc-get 'path opt '() #f))
(dosort (ly:assoc-get 'sort opt #t #f))
(sortby (assoc-get 'sortby opt stdsort))
(empty (ly:assoc-get 'empty opt #f #f))
(dval (ly:assoc-get 'value opt #t #f))
(vformat (ly:assoc-get 'vformat opt (lambda (v)(format "~A" v)) #f))
(pformat (ly:assoc-get 'pformat opt (lambda (v)(format "~A" v)) #f))
(pathsep (ly:assoc-get 'pathsep opt "/" #f))
(port (ly:assoc-get 'port opt (current-output-port))))
(tree-walk-branch tree path
(lambda (path k val)
(format #t "[~A] ~A" (key tree) (string-join (map pformat path) pathsep
'infix) port)
(if (and dval val) (begin
(display ": " port)
(display (vformat val) port)
))
(newline port)
) `(sort . ,dosort) `(sortby . ,sortby) `(empty . ,empty) )
))
#(define-public (tree->string tree . opt)
(call-with-output-string
(lambda (port)
(apply tree-display tree (assoc-set! opt 'port port))
)))
#(define-method (display (tree <tree>) port)
(let ((tkey (key tree)))
(tree-display tree)))
#(define-public (tree? tree)(is-a? tree <tree>))
#(define-public (tree-create . key)
(let ((k (if (> (length key) 0)(car key) 'node)))
(make <tree> #:key k)
))
_______________________________________________
lilypond-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-devel