]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
e3f8cba3e8c7393b6a0ed493164c231842fb2541
[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           is_scheme = true;
174         }
175       else if (ly_is_procedure (definition))
176         {
177           // `definition' is a procedure, which takes the context as
178           // an argument and evaluates to an a-list scheme engraver
179           // definition.
180           definition = scm_call_1 (definition, cs);
181           is_scheme = true;
182         }
183
184       if (!is_scheme && !type)
185         warning (_f ("cannot find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
186       else
187         {
188           Translator *instance = is_scheme ? new Scheme_engraver (definition)
189             : type->clone ();
190
191           SCM str = instance->self_scm ();
192
193           if (instance->must_be_last ())
194             {
195               SCM cons = scm_cons (str, SCM_EOL);
196               if (scm_is_pair (trans_list))
197                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
198               else
199                 trans_list = cons;
200             }
201           else
202             trans_list = scm_cons (str, trans_list);
203
204           instance->daddy_context_ = new_context;
205           instance->unprotect ();
206         }
207     }
208
209   /* Filter unwanted translator types. Required to make
210      \with { \consists "..." } work. */
211   if (dynamic_cast<Engraver_group *> (g))
212     g->simple_trans_list_ = filter_performers (trans_list);
213   else if (dynamic_cast<Performer_group *> (g))
214     g->simple_trans_list_ = filter_engravers (trans_list);
215
216   // TODO: scrap Context::implementation
217   new_context->implementation_ = g;
218
219   g->connect_to_context (new_context);
220   g->unprotect ();
221
222   recurse_over_translators (new_context,
223                             &Translator::initialize,
224                             &Translator_group::initialize,
225                             DOWN);
226 }
227
228 SCM
229 Translator_group::get_simple_trans_list ()
230 {
231   return simple_trans_list_;
232 }
233
234 void
235 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
236 {
237   Translator_group *tg
238     = dynamic_cast<Translator_group *> (c->implementation ());
239
240   if (tg && dir == DOWN)
241     {
242       tg->precomputed_translator_foreach (idx);
243       tg->call_precomputed_self_method (idx);
244     }
245
246   for (SCM s = c->children_contexts (); scm_is_pair (s);
247        s = scm_cdr (s))
248     precomputed_recurse_over_translators (unsmob<Context> (scm_car (s)), idx, dir);
249
250   if (tg && dir == UP)
251     {
252       tg->precomputed_translator_foreach (idx);
253       tg->call_precomputed_self_method (idx);
254     }
255 }
256
257 void
258 recurse_over_translators (Context *c, Translator_method ptr,
259                           Translator_group_method tg_ptr, Direction dir)
260 {
261   Translator_group *tg
262     = dynamic_cast<Translator_group *> (c->implementation ());
263
264   if (tg && dir == DOWN)
265     {
266       (tg->*tg_ptr) ();
267       translator_each (tg->get_simple_trans_list (), ptr);
268     }
269
270   for (SCM s = c->children_contexts (); scm_is_pair (s);
271        s = scm_cdr (s))
272     recurse_over_translators (unsmob<Context> (scm_car (s)), ptr, tg_ptr, dir);
273
274   if (tg && dir == UP)
275     {
276       translator_each (tg->get_simple_trans_list (),
277                        ptr);
278
279       (tg->*tg_ptr) ();
280     }
281 }
282
283 Translator_group::Translator_group ()
284 {
285   simple_trans_list_ = SCM_EOL;
286   protected_events_ = SCM_EOL;
287   context_ = 0;
288   smobify_self ();
289 }
290
291 void
292 Translator_group::derived_mark () const
293 {
294 }
295
296 void
297 Translator_group::precompute_method_bindings ()
298 {
299   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
300     {
301       Translator *tr = unsmob<Translator> (scm_car (s));
302       Translator::Callback ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
303       tr->fetch_precomputable_methods (ptrs);
304
305       assert (tr);
306       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
307         {
308           if (ptrs[i])
309             precomputed_method_bindings_[i].push_back (Translator_method_binding (tr, ptrs[i]));
310         }
311     }
312
313   fetch_precomputable_methods (precomputed_self_method_bindings_);
314 }
315
316 void
317 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
318 {
319   vector<Translator_method_binding> &bindings (precomputed_method_bindings_[idx]);
320   for (vsize i = 0; i < bindings.size (); i++)
321     bindings[i].invoke ();
322 }
323
324 void
325 Translator_group::fetch_precomputable_methods (Translator_group_void_method ptrs[])
326 {
327   for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
328     ptrs[i] = 0;
329 }
330
331 void
332 Translator_group::call_precomputed_self_method (Translator_precompute_index idx)
333 {
334   if (precomputed_self_method_bindings_[idx])
335     (*precomputed_self_method_bindings_[idx]) (this);
336 }
337
338 Translator_group::~Translator_group ()
339 {
340 }
341
342
343 const char Translator_group::type_p_name_[] = "ly:translator-group?";
344
345 int
346 Translator_group::print_smob (SCM port, scm_print_state *) const
347 {
348   scm_puts ("#<Translator_group ", port);
349   scm_puts (class_name (), port);
350   scm_display (simple_trans_list_, port);
351   scm_puts (" >", port);
352   return 1;
353 }
354
355 SCM
356 Translator_group::mark_smob () const
357 {
358   derived_mark ();
359   scm_gc_mark (protected_events_);
360   return simple_trans_list_;
361 }