]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
73a9c602e031ebd3b17bf332b8001ac18d58c163
[lilypond.git] / lily / translator-group.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>,
5                  Erik Sandberg <mandolaerik@gmail.com>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "translator-group.hh"
22
23 #include "context-def.hh"
24 #include "context.hh"
25 #include "dispatcher.hh"
26 #include "engraver.hh"
27 #include "engraver-group.hh"
28 #include "international.hh"
29 #include "main.hh"
30 #include "music.hh"
31 #include "output-def.hh"
32 #include "performer.hh"
33 #include "performer-group.hh"
34 #include "scheme-engraver.hh"
35 #include "scm-hash.hh"
36 #include "warn.hh"
37
38 void
39 translator_each (SCM list, Translator_method method)
40 {
41   for (SCM p = list; scm_is_pair (p); p = scm_cdr (p))
42     (unsmob<Translator> (scm_car (p))->*method) ();
43 }
44
45 void
46 Translator_group::initialize ()
47 {
48   precompute_method_bindings ();
49 }
50
51 void
52 Translator_group::connect_to_context (Context *c)
53 {
54   if (context_)
55     {
56       programming_error ("translator group is already connected to context "
57                          + context_->context_name ());
58     }
59
60   context_ = c;
61   c->event_source ()->add_listener (GET_LISTENER (Translator_group, create_child_translator),
62                                     ly_symbol2scm ("AnnounceNewContext"));
63   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
64     {
65       Translator *tr = unsmob<Translator> (scm_car (tr_list));
66       tr->connect_to_context (c);
67     }
68 }
69
70 void
71 Translator_group::disconnect_from_context ()
72 {
73   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
74     {
75       Translator *tr = unsmob<Translator> (scm_car (tr_list));
76       tr->disconnect_from_context (context_);
77     }
78   context_->event_source ()->remove_listener (GET_LISTENER (Translator_group, create_child_translator),
79                                               ly_symbol2scm ("AnnounceNewContext"));
80   context_ = 0;
81   protected_events_ = SCM_EOL;
82 }
83
84 void
85 Translator_group::finalize ()
86 {
87 }
88
89 /*
90   Both filter_performers and filter_engravers used to use a direct dynamic_cast
91   on the unsmobbed translator to be filtered, i.e.,
92
93   if (unsmob<Performer> (scm_car (*tail)))
94
95   but this caused mysterious optimisation issues in several GUB builds.  See
96   issue #818 for the background to this change.
97 */
98 SCM
99 filter_performers (SCM ell)
100 {
101   SCM *tail = &ell;
102   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
103     {
104       if (unsmob<Performer> (scm_car (*tail)))
105         *tail = scm_cdr (*tail);
106       else
107         tail = SCM_CDRLOC (*tail);
108     }
109   return ell;
110 }
111
112 SCM
113 filter_engravers (SCM ell)
114 {
115   SCM *tail = &ell;
116   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
117     {
118       if (unsmob<Engraver> (scm_car (*tail)))
119         *tail = scm_cdr (*tail);
120       else
121         tail = SCM_CDRLOC (*tail);
122     }
123   return ell;
124 }
125
126 /*
127   Protects the parameter from being garbage collected. The object is
128   protected until the next disconnect_from_context call.
129
130   Whenever a child translator hears an event, the event is added to
131   this list. This eliminates the need for derived_mark methods in most
132   translators; all incoming events are instead protected by the
133   translator group.
134
135   TODO: Should the list also be flushed at the beginning of each new
136   moment?
137  */
138 void
139 Translator_group::protect_event (SCM ev)
140 {
141   protected_events_ = scm_cons (ev, protected_events_);
142 }
143
144 /*
145   Create a new translator for a newly created child context. Triggered
146   by AnnounceNewContext events.
147  */
148 void
149 Translator_group::create_child_translator (SCM sev)
150 {
151   Stream_event *ev = unsmob<Stream_event> (sev);
152   // get from AnnounceNewContext
153   SCM cs = ev->get_property ("context");
154   Context *new_context = unsmob<Context> (cs);
155   Context_def *def = unsmob<Context_def> (new_context->get_definition ());
156   SCM ops = new_context->get_definition_mods ();
157
158   SCM trans_names = def->get_translator_names (ops);
159
160   Translator_group *g = get_translator_group (def->get_translator_group_type ());
161   SCM trans_list = SCM_EOL;
162
163   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
164     {
165       SCM definition = scm_car (s);
166       bool is_scheme = false;
167
168       Translator *type = 0;
169       if (ly_is_symbol (definition))
170         type = get_translator (definition);
171       else if (ly_is_pair (definition))
172         {
173           type = get_translator (ly_symbol2scm ("Scheme_engraver"));
174           is_scheme = true;
175         }
176       else if (ly_is_procedure (definition))
177         {
178           // `definition' is a procedure, which takes the context as
179           // an argument and evaluates to an a-list scheme engraver
180           // definition.
181           definition = scm_call_1 (definition, cs);
182           type = get_translator (ly_symbol2scm ("Scheme_engraver"));
183           is_scheme = true;
184         }
185
186       if (!type)
187         warning (_f ("cannot find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
188       else
189         {
190           Translator *instance = type->clone ();
191           if (is_scheme)
192             dynamic_cast<Scheme_engraver *> (instance)->init_from_scheme (definition);
193
194           SCM str = instance->self_scm ();
195
196           if (instance->must_be_last ())
197             {
198               SCM cons = scm_cons (str, SCM_EOL);
199               if (scm_is_pair (trans_list))
200                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
201               else
202                 trans_list = cons;
203             }
204           else
205             trans_list = scm_cons (str, trans_list);
206
207           instance->daddy_context_ = new_context;
208           instance->unprotect ();
209         }
210     }
211
212   /* Filter unwanted translator types. Required to make
213      \with { \consists "..." } work. */
214   if (dynamic_cast<Engraver_group *> (g))
215     g->simple_trans_list_ = filter_performers (trans_list);
216   else if (dynamic_cast<Performer_group *> (g))
217     g->simple_trans_list_ = filter_engravers (trans_list);
218
219   // TODO: scrap Context::implementation
220   new_context->implementation_ = g;
221
222   g->connect_to_context (new_context);
223   g->unprotect ();
224
225   recurse_over_translators (new_context,
226                             &Translator::initialize,
227                             &Translator_group::initialize,
228                             DOWN);
229 }
230
231 SCM
232 Translator_group::get_simple_trans_list ()
233 {
234   return simple_trans_list_;
235 }
236
237 void
238 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
239 {
240   Translator_group *tg
241     = dynamic_cast<Translator_group *> (c->implementation ());
242
243   if (tg && dir == DOWN)
244     {
245       tg->precomputed_translator_foreach (idx);
246       tg->call_precomputed_self_method (idx);
247     }
248
249   for (SCM s = c->children_contexts (); scm_is_pair (s);
250        s = scm_cdr (s))
251     precomputed_recurse_over_translators (unsmob<Context> (scm_car (s)), idx, dir);
252
253   if (tg && dir == UP)
254     {
255       tg->precomputed_translator_foreach (idx);
256       tg->call_precomputed_self_method (idx);
257     }
258 }
259
260 void
261 recurse_over_translators (Context *c, Translator_method ptr,
262                           Translator_group_method tg_ptr, Direction dir)
263 {
264   Translator_group *tg
265     = dynamic_cast<Translator_group *> (c->implementation ());
266
267   if (tg && dir == DOWN)
268     {
269       (tg->*tg_ptr) ();
270       translator_each (tg->get_simple_trans_list (), ptr);
271     }
272
273   for (SCM s = c->children_contexts (); scm_is_pair (s);
274        s = scm_cdr (s))
275     recurse_over_translators (unsmob<Context> (scm_car (s)), ptr, tg_ptr, dir);
276
277   if (tg && dir == UP)
278     {
279       translator_each (tg->get_simple_trans_list (),
280                        ptr);
281
282       (tg->*tg_ptr) ();
283     }
284 }
285
286 Translator_group::Translator_group ()
287 {
288   simple_trans_list_ = SCM_EOL;
289   protected_events_ = SCM_EOL;
290   context_ = 0;
291   smobify_self ();
292 }
293
294 void
295 Translator_group::derived_mark () const
296 {
297 }
298
299 void
300 Translator_group::precompute_method_bindings ()
301 {
302   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
303     {
304       Translator *tr = unsmob<Translator> (scm_car (s));
305       Translator::Callback ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
306       tr->fetch_precomputable_methods (ptrs);
307
308       assert (tr);
309       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
310         {
311           if (ptrs[i])
312             precomputed_method_bindings_[i].push_back (Translator_method_binding (tr, ptrs[i]));
313         }
314     }
315
316   fetch_precomputable_methods (precomputed_self_method_bindings_);
317 }
318
319 void
320 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
321 {
322   vector<Translator_method_binding> &bindings (precomputed_method_bindings_[idx]);
323   for (vsize i = 0; i < bindings.size (); i++)
324     bindings[i].invoke ();
325 }
326
327 void
328 Translator_group::fetch_precomputable_methods (Translator_group_void_method ptrs[])
329 {
330   for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
331     ptrs[i] = 0;
332 }
333
334 void
335 Translator_group::call_precomputed_self_method (Translator_precompute_index idx)
336 {
337   if (precomputed_self_method_bindings_[idx])
338     (*precomputed_self_method_bindings_[idx]) (this);
339 }
340
341 Translator_group::~Translator_group ()
342 {
343 }
344
345
346 const char Translator_group::type_p_name_[] = "ly:translator-group?";
347
348 int
349 Translator_group::print_smob (SCM port, scm_print_state *)
350 {
351   scm_puts ("#<Translator_group ", port);
352   scm_puts (class_name (), port);
353   scm_display (simple_trans_list_, port);
354   scm_puts (" >", port);
355   return 1;
356 }
357
358 SCM
359 Translator_group::mark_smob ()
360 {
361   derived_mark ();
362   scm_gc_mark (protected_events_);
363   return simple_trans_list_;
364 }