1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3 
4   Copyright (C) 1997--2020 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10 
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "music-iterator.hh"
21 
22 #include "warn.hh"
23 #include "music.hh"
24 #include "context.hh"
25 #include "event-iterator.hh"
26 #include "input.hh"
27 #include "international.hh"
28 #include "ly-smob-list.hh"
29 #include "music-wrapper.hh"
30 #include "music-wrapper-iterator.hh"
31 #include "simple-music-iterator.hh"
32 
33 #include <cstdio>
34 
Music_iterator()35 Music_iterator::Music_iterator ()
36 {
37   music_ = 0;
38   smobify_self ();
39 }
40 
~Music_iterator()41 Music_iterator::~Music_iterator ()
42 {
43 }
44 
45 Moment
pending_moment() const46 Music_iterator::pending_moment () const
47 {
48   return Moment (Rational::infinity ());
49 }
50 
51 void
process(Moment)52 Music_iterator::process (Moment)
53 {
54 }
55 
56 SCM
get_static_get_iterator(Music * m)57 Music_iterator::get_static_get_iterator (Music *m)
58 {
59   Music_iterator *p = 0;
60 
61   SCM ctor = get_property (m, "iterator-ctor");
62   SCM iter = SCM_EOL;
63   if (ly_is_procedure (ctor))
64     {
65       iter = scm_call_0 (ctor);
66       p = unsmob<Music_iterator> (iter);
67     }
68   else
69     {
70       if (dynamic_cast<Music_wrapper *> (m))
71         p = new Music_wrapper_iterator;
72       else if (m->is_mus_type ("event"))
73         p = new Event_iterator;
74       else
75         p = new Simple_music_iterator;
76 
77       iter = p->self_scm ();
78       p->unprotect ();
79     }
80 
81   p->music_ = m;
82   assert (m);
83   p->music_length_ = m->get_length ();
84   p->start_mom_ = m->start_mom ();
85 
86   p->create_children ();
87 
88   return iter;
89 }
90 
91 void
init_context(Context * report)92 Music_iterator::init_context (Context *report)
93 {
94   if (!get_own_context ())
95     {
96       set_own_context (report);
97       create_contexts ();
98     }
99   else
100     {
101       programming_error ("context already initialized; skipping");
102     }
103 }
104 
105 void
substitute_context(Context * f,Context * t)106 Music_iterator::substitute_context (Context *f, Context *t)
107 {
108   if (f != t)
109     {
110       if (get_own_context () == f)
111         set_own_context (t);
112       derived_substitute (f, t);
113     }
114 }
115 
116 void
derived_substitute(Context *,Context *)117 Music_iterator::derived_substitute (Context *, Context *)
118 {
119 }
120 
121 /* Descend to a bottom context; implicitly create a new one if necessary */
122 void
descend_to_bottom_context()123 Music_iterator::descend_to_bottom_context ()
124 {
125   assert (get_context ());
126   if (!get_context ()->is_bottom_context ())
127     set_context (get_context ()->get_default_interpreter ());
128 }
129 
130 /* Concretely: If the current context is Global, descend to Score. */
131 void
descend_to_user_accessible_context()132 Music_iterator::descend_to_user_accessible_context ()
133 {
134   auto c = get_context ();
135   assert (c);
136   if (!c->is_accessible_to_user ())
137     {
138       c = c->get_user_accessible_interpreter ();
139       if (c)
140         set_context (c);
141       else
142         programming_error ("cannot find an accessible context");
143     }
144 }
145 
146 void
report_event(Music * m)147 Music_iterator::report_event (Music *m)
148 {
149   descend_to_bottom_context ();
150 
151   /*
152     FIXME: then don't do it.
153   */
154   if (!m->is_mus_type ("event"))
155     m->programming_error ("Sending non-event to context");
156 
157   m->send_to_context (get_context ());
158 }
159 
160 IMPLEMENT_CTOR_CALLBACK (Music_iterator);
161 
162 Music *
get_music() const163 Music_iterator::get_music () const
164 {
165   return music_;
166 }
167 
168 Input *
origin() const169 Music_iterator::origin () const
170 {
171   if (Music *m = get_music ())
172     return m->origin ();
173   return nullptr;
174 }
175 
176 /****************************************************************/
177 
178 const char *const Music_iterator::type_p_name_ = "ly:iterator?";
179 
180 SCM
mark_smob() const181 Music_iterator::mark_smob () const
182 {
183   derived_mark ();
184   /*
185     Careful with GC, although we intend the following as pointers
186     only, we _must_ mark them.
187   */
188   if (get_own_context ())
189     scm_gc_mark (get_own_context ()->self_scm ());
190   if (music_)
191     scm_gc_mark (music_->self_scm ());
192 
193   return SCM_EOL;
194 }
195 
196 int
print_smob(SCM port,scm_print_state *) const197 Music_iterator::print_smob (SCM port, scm_print_state *) const
198 {
199   char s[1000];
200 
201   sprintf (s, "#<%s>", class_name ());
202   scm_puts (s, port);
203   return 1;
204 }
205 
206 void
derived_mark() const207 Music_iterator::derived_mark ()const
208 {
209 }
210 
211 void
quit()212 Music_iterator::quit ()
213 {
214   do_quit ();
215   set_own_context (nullptr);
216 }
217 
218 void
do_quit()219 Music_iterator::do_quit ()
220 {
221 }
222 
223 bool
run_always() const224 Music_iterator::run_always ()const
225 {
226   return false;
227 }
228 
229 bool
is_child_context(Context * me,Context * child)230 is_child_context (Context *me, Context *child)
231 {
232   while (child && child != me)
233     child = child->get_parent ();
234 
235   return child == me;
236 }
237 
238 /*
239   move to context of child iterator if it is deeper down in the
240   hierarchy.
241 */
242 void
descend_to_child(Context * child_report)243 Music_iterator::descend_to_child (Context *child_report)
244 {
245   Context *me_report = get_context ();
246   if (is_child_context (me_report, child_report))
247     set_context (child_report);
248 }
249