From 196bdae43d1d4f13a3080dbb5e5b4dd07f92cb51 Mon Sep 17 00:00:00 2001
From: Han-Wen Nienhuys <hanwen@xs4all.nl>
Date: Sat, 30 Aug 2003 21:35:56 +0000
Subject: [PATCH] * scm/music-functions.scm (remove-tag): filter \tagged music
 expressions.

* input/regression/tag-filter.ly (texidoc): new file.

* lily/parser.yy (post_event): add \tag #'symbol / \tag #'(symbol1
symbol2 .. ) etc.

* Documentation/user/refman.itely (Fingering instructions): adjust manual.
---
 ChangeLog                       |  8 +++++
 Documentation/topdocs/NEWS.texi | 29 ++++++++++++++++
 Documentation/user/refman.itely | 59 +++++++++++++++++++++++++++++++++
 input/regression/tag-filter.ly  | 53 +++++++++++++++++++++++++++++
 lily/my-lily-lexer.cc           |  1 +
 lily/parser.yy                  | 44 +++++++++++++++++++++---
 scm/define-music-properties.scm |  4 +++
 scm/music-functions.scm         | 43 +++++++++++++++++++++++-
 8 files changed, 235 insertions(+), 6 deletions(-)
 create mode 100644 input/regression/tag-filter.ly

diff --git a/ChangeLog b/ChangeLog
index 26df67e1a8..26bb357369 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2003-08-30  Han-Wen Nienhuys  <hanwen@cs.uu.nl>
 
+	* scm/music-functions.scm (remove-tag): filter \tagged music
+	expressions.
+
+	* input/regression/tag-filter.ly (texidoc): new file.
+
+	* lily/parser.yy (post_event): add \tag #'symbol / \tag #'(symbol1
+	symbol2 .. ) etc. 
+
 	* scripts/convert-ly.py (FatalConversionError.conv): fingering
 	convert rule.
 
diff --git a/Documentation/topdocs/NEWS.texi b/Documentation/topdocs/NEWS.texi
index b0b4ec04c6..bf4aacc5c6 100644
--- a/Documentation/topdocs/NEWS.texi
+++ b/Documentation/topdocs/NEWS.texi
@@ -8,6 +8,35 @@
 @chapter New features in 1.9 since 1.8
 
 @itemize
+
+@item
+Each music expression can now be tagged, to make different printed
+versions from the same music expression.  In the following example,
+we see two versions of a piece of music, one for the full score, and
+one with cue notes for the instrumental part:
+@example
+< \tag #'part <
+  @{ c4 f2 g @}      % in the part, we have cue-notes  
+  \\ R1 >
+  \tag #'score R1  % in the score: only a rest
+>
+@end example
+ 
+The same can be applied to articulations, texts, etc.: they are
+made by prepending
+@example
+        -\tag #@var{your-tag(s)} 
+@end example
+to an articulation, for example, 
+@example
+        c4-\tag #'with-fingerings -4 -\tag #'with-strings \6
+@end example
+
+This defines a note, which has a fingering and a string-number
+indication. 
+
+
+
 @item
 Markup text (ie. general text formatting) may now be used for lyrics too. 
 
diff --git a/Documentation/user/refman.itely b/Documentation/user/refman.itely
index 8423e5c64e..94bb40d6c3 100644
--- a/Documentation/user/refman.itely
+++ b/Documentation/user/refman.itely
@@ -4432,6 +4432,65 @@ in this example disappears in the second line:
 @end lilypond
 
 
+@node Different editions from one source
+@subsection Different editions from one source
+
+The @code{\\tag} command marks music expressions with a name. These
+tagged expressions can be filtered out later.  With this mechanism it
+is possible to make different versions of the same music source.
+
+In the following example, we see two versions of a piece of music, one
+for the full score, and one with cue notes for the instrumental part:
+
+@example
+ c1
+    \relative c' <
+	\tag #'part <
+	  R1 \\
+	  {
+	      \property Voice.fontSize = #-1
+	      c4_"cue" f2 g4 } 
+        >
+	\tag #'score R1
+     >
+@end example
+
+The same can be applied to articulations, texts, etc.: they are
+made by prepending
+@example
+        -\tag #@var{your-tag} 
+@end example
+to an articulation, for example, 
+@example
+    c1-\tag #'part ^4
+@end example
+
+This defines a note with a conditional fingering indication.
+
+By applying the @code{remove-tag} function, tagged expressions can be
+filtered. For example,
+@example
+\simultaneous {
+        @var{the music}
+	\apply #(remove-tag 'score) @var{the music}
+	\apply #(remove-tag 'part) @var{the music}
+}
+@end example
+would yield
+
+@lilypondfile[notexidoc]{tag-filter.ly}
+
+The argument of the @code{\tag} command should be a symbol, or a list
+of symbols, for example,
+@example
+  \tag #'(original-part transposed-part) @dots{}
+@end example
+
+@seealso
+
+@inputfileref{input/regression,tag-filter.ly}
+
+
 @node Sound output for transposing instruments
 @subsection Sound output for transposing instruments
 
diff --git a/input/regression/tag-filter.ly b/input/regression/tag-filter.ly
new file mode 100644
index 0000000000..e7d5915276
--- /dev/null
+++ b/input/regression/tag-filter.ly
@@ -0,0 +1,53 @@
+
+\version "1.9.3"
+\header {
+
+texidoc = "The @code{\\tag} command marks music expressions with a
+name. These tagged expressions can be filtered out later. This
+mechanism can be used to make different versions of the same music. In
+this example, the top stave displays the music expression with all
+tags included. The bottom two staves are filtered: the part has cue
+notes and fingerings, but the score has not."
+
+}
+
+\paper { raggedright= ##t }
+
+common =
+\notes \relative c''  {
+
+    c1
+    \relative c' <
+	\tag #'part <
+	  R1 \\
+	  {
+	      \property Voice.fontSize = #-1
+	      c4_"cue" f2 g4 } 
+        >
+	\tag #'score R1
+     >
+    c1-\tag #'part ^4
+}
+
+
+\score {
+    \notes \simultaneous { 
+    \new Staff {
+	\property Staff.instrument = #"both"
+	\common
+	}
+    \new Staff {
+	\property Staff.instrument = #"part"
+	\apply #(remove-tag 'score) \common
+	}
+    \new Staff {
+	\property Staff.instrument = #"score"
+	\apply #(remove-tag 'part) \common
+	}
+    }
+}
+
+
+
+
+
diff --git a/lily/my-lily-lexer.cc b/lily/my-lily-lexer.cc
index 1e63016cbf..b2f154f92c 100644
--- a/lily/my-lily-lexer.cc
+++ b/lily/my-lily-lexer.cc
@@ -80,6 +80,7 @@ static Keyword_ent the_key_tab[]={
   {"set", SET},
   {"simultaneous", SIMULTANEOUS},
   {"skip", SKIP},
+  {"tag", TAG},
   {"tempo", TEMPO},
   {"time", TIME_T},
   {"times", TIMES},
diff --git a/lily/parser.yy b/lily/parser.yy
index 2b8ec7198c..e8c710b8ac 100644
--- a/lily/parser.yy
+++ b/lily/parser.yy
@@ -96,7 +96,23 @@ My_lily_parser* my_lily_parser;
 
 #define yyerror THIS->parser_error
 
+/*
+  Add symbols to the  TAGS field of a music object. 
+*/
 
+void
+tag_music (Music*m,  SCM tag, Input ip)
+{
+	SCM tags = m->get_mus_property ("tags");
+	if (gh_symbol_p (tag))
+		tags = scm_cons (tag, tags);
+	else if (gh_list_p (tag))
+		tags = gh_append2 (tag, tags);
+	else
+		ip.warning (_("Tag must be symbol or list of symbols."));
+
+	m->set_mus_property ("tags", tags);
+}
 
 
 
@@ -278,6 +294,7 @@ yylex (YYSTYPE *s,  void * v)
 %token SIMULTANEOUS
 %token SKIP
 %token SPANREQUEST
+%token TAG
 %token TEMPO
 %token TIMES
 %token TIME_T
@@ -352,7 +369,7 @@ yylex (YYSTYPE *s,  void * v)
 %type <scm> steno_duration optional_notemode_duration multiplied_duration
 %type <scm>  verbose_duration
 	
-%type <scm>   post_events
+%type <scm>   post_events 
 %type <music> gen_text_def direction_less_event direction_reqd_event
 %type <scm>   steno_pitch pitch absolute_pitch pitch_also_in_chords
 %type <scm>   explicit_pitch steno_tonic_pitch
@@ -367,7 +384,7 @@ yylex (YYSTYPE *s,  void * v)
 %type <scm> Music_list
 %type <outputdef>  music_output_def_body
 %type <music> shorthand_command_req
-%type <music>	post_event 
+%type <music>	post_event tagged_post_event
 %type <music> command_req verbose_command_req
 %type <music>	extender_req
 %type <music> hyphen_req
@@ -1092,7 +1109,11 @@ basic music objects too, since the meaning is different.
 	}
 	| relative_music	{ $$ = $1; }
 	| re_rhythmed_music	{ $$ = $1; } 
-	| part_combined_music	{ $$ = $1; } 
+	| part_combined_music	{ $$ = $1; }
+	| TAG embedded_scm Music {
+		tag_music ($3, $2, THIS->here_input ());
+		$$ = $3;
+	}
 	;
 
 relative_music:
@@ -1510,20 +1531,33 @@ post_events:
 		$$ = gh_cons ($2->self_scm(), $$);
 		scm_gc_unprotect_object ($2->self_scm());
 	}
+	| post_events tagged_post_event {
+		$2 -> set_spot (THIS->here_input ());
+		$$ = scm_cons ($2->self_scm(), $$);
+		scm_gc_unprotect_object ($2->self_scm());
+	}
 	;
 
 
+tagged_post_event:
+	'-' TAG embedded_scm post_event {
+		tag_music ($4, $3, THIS->here_input ());
+		$$ = $4;
+	}
+	;
 
 post_event:
 	direction_less_event {
 		$$ = $1;
 	}
 	| script_dir direction_reqd_event {
-		$2->set_mus_property ("direction", gh_int2scm ($1));
+		if ($1)
+			$2->set_mus_property ("direction", gh_int2scm ($1));
 		$$ = $2;
 	}
 	| script_dir direction_less_event {
-		$2->set_mus_property ("direction", gh_int2scm ($1));
+		if ($1)
+			$2->set_mus_property ("direction", gh_int2scm ($1));
 		$$ = $2;
 	}
 	| string_number_event
diff --git a/scm/define-music-properties.scm b/scm/define-music-properties.scm
index e705f24b59..705706a735 100644
--- a/scm/define-music-properties.scm
+++ b/scm/define-music-properties.scm
@@ -42,6 +42,10 @@ TODO: consider making type into symbol ")
 (music-property-description 'denominator integer? "denominator in a time signature")
 (music-property-description 'digit integer? "digit for fingering")
 (music-property-description 'direction ly:dir? "Print this up or down?")
+(music-property-description 'tags list? "List of symbols that for denoting extra details,
+eg. @code{\\tag #'part ...} could tag a piece of music as only being active in a part.")
+
+
 (music-property-description 'text-type symbol? "Particular type of text script (eg. finger, dynamic).")
 (music-property-description 'tempo-unit ly:duration? "The unit for the metronome count.")
 (music-property-description 'tonic ly:pitch? "Base of the scale")
diff --git a/scm/music-functions.scm b/scm/music-functions.scm
index 431b9bdc30..591468c510 100644
--- a/scm/music-functions.scm
+++ b/scm/music-functions.scm
@@ -1,4 +1,3 @@
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define-public (music-map function music)
@@ -14,6 +13,48 @@
 	(function music)
 	))
 
+(define-public (music-filter pred? music)
+  "Filter out music expressions that do not satisfy PRED."
+  
+  (define (inner-music-filter pred? music)
+    "Recursive function."
+    (let* ((es (ly:get-mus-property music 'elements))
+	   (e (ly:get-mus-property music 'element))
+	   (as (ly:get-mus-property music 'articulations))
+	   (filtered-as (filter ly:music? (map (lambda (y) (inner-music-filter pred? y)) as)))
+	   (filtered-e (if (ly:music? e)
+			   (inner-music-filter pred? e)
+			   e))
+	   (filtered-es (filter ly:music? (map (lambda (y) (inner-music-filter pred? y)) es)))
+	   )
+
+      (ly:set-mus-property! music 'element filtered-e)
+      (ly:set-mus-property! music 'elements filtered-es)
+      (ly:set-mus-property! music 'articulations filtered-as)
+
+      ;; if filtering emptied the expression, we remove it completely.
+      (if (or (pred? music)
+	      (and (eq? filtered-es '()) (not (ly:music? e))
+		   (or (not (eq? es '()))
+		       (ly:music? e))))
+	  (set! music '()))
+      
+      music))
+
+  (set! music (inner-music-filter pred? music))
+  (if (ly:music? music)
+      music
+      (make-music-by-name 'Music)	;must return music.
+      ))
+
+(define-public (remove-tag tag)
+  (lambda (mus)
+    (music-filter
+     (lambda (m)
+       (let* ((tags (ly:get-mus-property m 'tags))
+	      (res (memq tag tags)))
+       res)) mus)))
+
 (define-public (display-music music)
   "Display music, not done with music-map for clarity of presentation."
   (display music)
-- 
2.39.5