]> git.donarmstrong.com Git - lilypond.git/blob - lily/my-lily-parser.cc
44c858171990d1942f33e7aaf6ab6aca26a53729
[lilypond.git] / lily / my-lily-parser.cc
1 /*
2   my-lily-parser.cc -- implement My_lily_parser
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7        Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 #include "book.hh"
11 #include "file-name.hh"
12 #include "file-path.hh"
13 #include "lily-version.hh"
14 #include "ly-module.hh"
15 #include "main.hh"
16 #include "my-lily-lexer.hh"
17 #include "my-lily-parser.hh"
18 #include "output-def.hh"
19 #include "paper-book.hh"
20 #include "parser.hh"
21 #include "score.hh"
22 #include "source.hh"
23 #include "warn.hh"
24
25
26 My_lily_parser::My_lily_parser (Sources *sources)
27 {
28   book_count_ = 0;
29   score_count_ = 0;
30   lexer_ = 0;
31   sources_ = sources;
32   default_duration_ = Duration (2,0);
33   error_level_ = 0;
34   last_beam_start_ = SCM_EOL;
35
36   smobify_self ();
37 }
38
39 My_lily_parser::My_lily_parser (My_lily_parser const &src)
40 {
41   book_count_ = src.book_count_;
42   score_count_ = src.score_count_;
43   lexer_ = 0;
44   sources_ = src.sources_;
45   default_duration_ = src.default_duration_;
46   error_level_ = src.error_level_;
47   last_beam_start_ = src.last_beam_start_;
48
49   smobify_self ();
50   if (src.lexer_)
51     lexer_ = new My_lily_lexer (*src.lexer_);
52 }
53
54 My_lily_parser::~My_lily_parser ()
55 {
56   // FIXME: Memleak: del lexer
57 }
58
59 #include "ly-smobs.icc"
60
61 IMPLEMENT_SMOBS (My_lily_parser);
62 IMPLEMENT_TYPE_P (My_lily_parser, "ly:my-lily-parser?");
63 IMPLEMENT_DEFAULT_EQUAL_P (My_lily_parser);
64
65 SCM
66 My_lily_parser::mark_smob (SCM s)
67 {
68   My_lily_parser *parser = (My_lily_parser*) ly_cdr (s);
69   (void) parser;
70   return SCM_EOL;
71 }
72
73 int
74 My_lily_parser::print_smob (SCM s, SCM port, scm_print_state*)
75 {
76   scm_puts ("#<my_lily_parser ", port);
77   My_lily_parser *parser = (My_lily_parser*) ly_cdr (s);
78   (void) parser;
79   scm_puts (" >", port);
80   return 1;
81 }
82
83
84 /* Process one .ly file, or book.  */
85 void
86 My_lily_parser::parse_file (String init, String name, String out_name)
87 {
88   lexer_ = new My_lily_lexer (sources_);
89   // TODO: use $parser 
90   lexer_->set_identifier (ly_symbol2scm ("parser"),
91                           self_scm ());
92   output_basename_ = out_name;
93   
94   lexer_->main_input_name_ = name;
95
96   progress_indication (_ ("Parsing..."));
97   progress_indication ("\n");
98
99   set_yydebug (0);
100   lexer_->new_input (init, sources_);
101
102   /* Read .ly IN_FILE, lex, parse, write \score blocks from IN_FILE to
103      OUT_FILE (unless IN_FILE redefines output file name).  */
104   do_yyparse ();
105   
106   if (!define_spots_.is_empty ())
107     {
108       define_spots_.top ().warning (_ ("Braces don't match"));
109       error_level_ = 1;
110     }
111
112   error_level_ = error_level_ | lexer_->error_level_;
113   delete lexer_;
114   lexer_ = 0;
115 }
116
117 void
118 My_lily_parser::parse_string (String ly_code)
119 {
120   My_lily_lexer *parent = lexer_;
121   lexer_ = (parent == 0 ? new My_lily_lexer (sources_)
122             : new My_lily_lexer (*parent));
123
124   // TODO: use $parser 
125   lexer_->set_identifier (ly_symbol2scm ("parser"),
126                           self_scm ());
127   
128   lexer_->main_input_name_ = "<string>";
129   lexer_->main_input_b_ = true;
130
131   set_yydebug (0);
132   lexer_->new_input (lexer_->main_input_name_, ly_code, sources_);
133   do_yyparse ();
134   
135   if (!define_spots_.is_empty ())
136     {
137       define_spots_.top ().warning (_ ("Braces don't match"));
138       error_level_ = 1;
139     }
140
141   error_level_ = error_level_ | lexer_->error_level_;
142
143   if (parent != 0)
144     {
145       parent->keytable_ = lexer_->keytable_;
146       parent->encoding_ = lexer_->encoding_;
147       parent->chordmodifier_tab_ = lexer_->chordmodifier_tab_;
148       parent->pitchname_tab_stack_ = lexer_->pitchname_tab_stack_;
149       parent->sources_ = lexer_->sources_;
150       parent->scopes_ = lexer_->scopes_;
151       parent->error_level_ = lexer_->error_level_; 
152       parent->main_input_b_ = lexer_->main_input_b_;
153     }
154
155   delete lexer_;
156   lexer_ = 0;
157 }
158
159 void
160 My_lily_parser::push_spot ()
161 {
162   define_spots_.push (here_input ());
163 }
164
165 char const *
166 My_lily_parser::here_str0 () const
167 {
168   return lexer_->here_str0 ();
169 }
170
171 void
172 My_lily_parser::parser_error (String s)
173 {
174   here_input ().error (s);
175   error_level_ = 1;
176 }
177
178 Input
179 My_lily_parser::pop_spot ()
180 {
181   return define_spots_.pop ();
182 }
183
184 Input
185 My_lily_parser::here_input () const
186 {
187   /*
188     Parsing looks ahead , so we really want the previous location of the
189     lexer, not lexer_->here_input ().
190   */
191   
192   /*
193     Actually, that gets very icky when there are white space, because
194     the line-numbers are all wrong.  Let's try the character before
195     the current token. That gets the right result for note/duration
196     stuff, but doesn't mess up for errors in the 1st token of the
197     line.
198   */
199   Input hi (lexer_->here_input ());
200
201   char const * bla = hi.defined_str0_;
202   if (hi.line_number () > 1
203       || hi.column_number () > 1)
204     bla --;
205   
206   return Input (hi.source_file_, bla);
207 }
208
209
210 /****************************************************************/
211
212
213 /*
214   junkme?
215  */
216 bool store_locations_global_b;
217
218 /* Do not append `!' suffix, since 1st argument is not modified. */
219 LY_DEFINE (ly_set_point_and_click, "ly:set-point-and-click",
220            1, 0, 0, (SCM what),
221           "Set the options for Point-and-click source specials output. The\n"
222 "argument is a symbol.  Possible options are @code{none} (no source specials),\n"
223 "@code{line} and @code{line-column}")
224 {
225   /* UGH. */
226   SCM val = SCM_BOOL_F;
227   if (ly_symbol2scm ("line-column") == what)
228     val = ly_scheme_function ("line-column-location");
229   else if (what == ly_symbol2scm ("line"))
230     val = ly_scheme_function ("line-location");
231
232   scm_module_define (global_lily_module, ly_symbol2scm ("point-and-click"),
233                      val);
234   store_locations_global_b = ly_c_procedure_p (val);
235   return SCM_UNSPECIFIED;
236 }
237
238 LY_DEFINE (ly_parse_file, "ly:parse-file",
239            1, 0, 0, (SCM name),
240            "Parse a single @code{.ly} file.  "
241            "Upon failure, throw @code{ly-file-failed} key.")
242 {
243   SCM_ASSERT_TYPE (ly_c_string_p (name), name, SCM_ARG1, __FUNCTION__, "string");
244   char const *file = SCM_STRING_CHARS (name);
245   char const *extensions[] = {"ly", "", 0};
246   String file_name = global_path.find (file, extensions);
247   
248   /* By default, use base name of input file for output file name,
249      write output to cwd; do not use root and directory parts of input
250      file name.  */
251   File_name out_file_name (file_name);
252   out_file_name.ext_ = "";
253   out_file_name.root_ = "";
254   out_file_name.dir_ = "";
255
256   if (!output_name_global.is_empty ())
257     out_file_name = File_name (output_name_global);
258   
259   String init;
260   if (!init_name_global.is_empty ())
261     init = init_name_global;
262   else
263     init = "init.ly";
264
265   String out_file = out_file_name.to_string ();
266
267   if (init.length () && global_path.find (init).is_empty ())
268     {
269       warning (_f ("can't find init file: `%s'", init));
270       warning (_f ("(search path: `%s')",
271                    global_path.to_string ().to_str0 ()));
272       exit (2);
273     }
274
275   if ((file_name != "-") && global_path.find (file_name).is_empty ())
276     {
277       warning (_f ("can't find file: `%s'", file_name));
278       scm_throw (ly_symbol2scm ("ly-file-failed"),
279                  scm_list_1 (scm_makfrom0str (file_name.to_str0 ())));
280     }
281   else
282     {
283       Sources sources;
284       sources.set_path (&global_path);
285   
286       progress_indication (_f ("Now processing `%s'", file_name.to_str0 ()));
287       progress_indication ("\n");
288
289       My_lily_parser *parser = new My_lily_parser (&sources);
290
291       parser->parse_file (init, file_name, out_file);
292
293       bool error = parser->error_level_;
294       parser = 0;
295       if (error)
296         /* TODO: pass renamed input file too.  */
297         scm_throw (ly_symbol2scm ("ly-file-failed"),
298                    scm_list_1 (scm_makfrom0str (file_name.to_str0 ())));
299     }
300   return SCM_UNSPECIFIED;
301 }
302
303 LY_DEFINE (ly_parse_string, "ly:parse-string",
304            1, 0, 0, (SCM ly_code),
305            "Parse the string LY_CODE.  "
306            "Upon failure, throw @code{ly-file-failed} key.")
307 {
308   SCM_ASSERT_TYPE (ly_c_string_p (ly_code), ly_code, SCM_ARG1, __FUNCTION__, "string");
309   
310   Sources sources;
311   sources.set_path (&global_path);
312   My_lily_parser *parser = new My_lily_parser (&sources);
313   scm_module_define (global_lily_module, ly_symbol2scm ("parser"),
314                      parser->self_scm ());
315   parser->parse_string (ly_scm2string (ly_code));
316   parser = 0;
317   
318   return SCM_UNSPECIFIED;
319 }
320
321 LY_DEFINE (ly_clone_parser, "ly:clone-parser",
322            1, 0, 0, (SCM parser_smob),
323            "Return a clone of PARSER_SMOB.")
324 {
325   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
326   My_lily_parser *clone = new My_lily_parser (*parser);
327
328   /* FIXME: should copy scopes too. */
329   return scm_gc_unprotect_object (clone->self_scm ());
330 }
331
332 LY_DEFINE (ly_parser_define, "ly:parser-define",
333            3, 0, 0, (SCM parser_smob, SCM symbol, SCM val),
334            "Bind SYMBOL to VAL in PARSER_SMOB's module.")
335 {
336   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
337   SCM_ASSERT_TYPE (ly_c_symbol_p (symbol), symbol, SCM_ARG2, __FUNCTION__, "symbol");
338   SCM_ASSERT_TYPE (parser, parser_smob, SCM_ARG2, __FUNCTION__, "parser");  
339
340
341   parser->lexer_->set_identifier (scm_symbol_to_string (symbol), val);
342   return SCM_UNSPECIFIED;
343 }
344
345 LY_DEFINE (ly_parser_lookup, "ly:parser-lookup",
346            2, 0, 0, (SCM parser_smob, SCM symbol),
347            "Lookup @var{symbol} in @var{parser_smob}'s module.  "
348            "Undefined is '().")
349 {
350   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
351
352   SCM_ASSERT_TYPE (ly_c_symbol_p (symbol), symbol, SCM_ARG2, __FUNCTION__, "symbol");
353   SCM_ASSERT_TYPE (parser, parser_smob, SCM_ARG2, __FUNCTION__, "parser");  
354
355   SCM val= parser->lexer_->lookup_identifier (ly_scm2string (scm_symbol_to_string (symbol)));
356   if (val != SCM_UNDEFINED)
357     return val;
358   else
359     return SCM_EOL;
360 }
361
362 LY_DEFINE (ly_parser_parse_string, "ly:parser-parse-string",
363            2, 0, 0, (SCM parser_smob, SCM ly_code),
364            "Parse the string LY_CODE with PARSER_SMOB."
365            "Upon failure, throw @code{ly-file-failed} key.")
366 {
367   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
368
369   SCM_ASSERT_TYPE (parser, parser_smob, SCM_ARG1, __FUNCTION__, "parser");
370   SCM_ASSERT_TYPE (ly_c_string_p (ly_code), ly_code, SCM_ARG2, __FUNCTION__, "string");
371   
372   parser->parse_string (ly_scm2string (ly_code));
373   
374   return SCM_UNSPECIFIED;
375 }
376
377 Output_def*
378 get_paper (My_lily_parser *parser)
379 {
380   SCM id = parser->lexer_->lookup_identifier ("$defaultpaper");
381   Output_def *paper = unsmob_output_def (id);
382   paper = paper ? paper->clone () : new Output_def;
383   paper->set_variable (ly_symbol2scm ("is-paper"), SCM_BOOL_T);
384
385   paper->parent_ = unsmob_output_def (parser->lexer_->lookup_identifier ("$defaultbookpaper"));
386   return paper;
387 }
388
389
390 Output_def*
391 get_midi (My_lily_parser *parser)
392 {
393   SCM id = parser->lexer_->lookup_identifier ("$defaultmidi");
394   Output_def *paper = unsmob_output_def (id);
395   paper = paper ? paper->clone () : new Output_def;
396   paper->set_variable (ly_symbol2scm ("is-midi"), SCM_BOOL_T);
397   return paper;
398 }
399
400
401 Output_def*
402 get_bookpaper (My_lily_parser *parser)
403 {
404   SCM id = parser->lexer_->lookup_identifier ("$defaultbookpaper");
405   Output_def *paper = unsmob_output_def (id);
406
407   paper = paper ? dynamic_cast<Output_def*> (paper->clone ()) : new Output_def;
408   paper->set_variable (ly_symbol2scm ("is-bookpaper"), SCM_BOOL_T);
409   return paper;
410 }
411
412
413 /* TODO: move this to Scheme?  Why take the parser arg, and all the back
414    & forth between scm and c++? */
415 LY_DEFINE (ly_parser_print_score, "ly:parser-print-score",
416            2, 0, 0,
417            (SCM parser_smob, SCM score_smob),
418            "Print score, i.e., the classic way.")
419 {
420   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
421   Score *score = unsmob_score (score_smob);
422
423   SCM_ASSERT_TYPE (parser, parser_smob, SCM_ARG1, __FUNCTION__, "parser");
424   SCM_ASSERT_TYPE (score, score_smob, SCM_ARG2, __FUNCTION__, "score");
425
426   SCM header = ly_c_module_p (score->header_) ? score->header_
427     : parser->lexer_->lookup_identifier ("$globalheader");
428   
429   File_name outname (parser->output_basename_);
430   int *c = &parser->book_count_;
431   if (*c)
432     outname.base_ += "-" + to_string (*c);
433   (*c)++;
434
435   SCM os = scm_makfrom0str (outname.to_string ().to_str0 ());
436   SCM bookpaper = get_bookpaper (parser)->self_scm ();
437   for (int i = 0; i < score->defs_.size (); i++)
438     default_rendering (score->music_, score->defs_[i]->self_scm (),
439                        bookpaper,
440                        header, os);
441
442   if (score->defs_.is_empty ())
443     {
444       Output_def *paper = get_paper (parser);
445       default_rendering (score->music_, paper->self_scm (),
446                          get_bookpaper (parser)->self_scm (),
447                          header, os);
448       scm_gc_unprotect_object (paper->self_scm ());
449     }
450   return SCM_UNSPECIFIED;
451 }
452
453
454 LY_DEFINE (ly_parser_set_note_names, "ly:parser-set-note-names",
455            2, 0, 0, (SCM parser, SCM names),
456            "Replace current note names in @var{parser}. "
457            "@var{names} is an alist of symbols.  "
458            "This only has effect if the current mode is notes.")
459 {
460   My_lily_parser *p = unsmob_my_lily_parser (parser);
461   SCM_ASSERT_TYPE(p, parser, SCM_ARG1, __FUNCTION__, "Lilypond parser");
462
463   if (p->lexer_->is_note_state ())
464     {
465       p->lexer_->pop_state ();
466       p->lexer_->push_note_state (alist_to_hashq (names));
467     }
468
469   return SCM_UNSPECIFIED;
470 }
471
472 LY_DEFINE (ly_parser_print_book, "ly:parser-print-book",
473            2, 0, 0, (SCM parser_smob, SCM book_smob),
474            "Print book.")
475 {
476   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
477   Book *book = unsmob_book (book_smob);
478   Output_def *bp = unsmob_output_def (parser->lexer_->lookup_identifier ("$defaultbookpaper"));
479   
480   SCM_ASSERT_TYPE (parser, parser_smob, SCM_ARG1, __FUNCTION__, "Lilypond parser");
481   SCM_ASSERT_TYPE (book, book_smob, SCM_ARG2, __FUNCTION__, "Book");
482   
483   /*  ugh. changing argument.*/
484   book->bookpaper_ = bp;
485   
486   File_name outname (parser->output_basename_);
487   int *c = &parser->book_count_;
488   if (*c)
489     outname.base_ += "-" + to_string (*c);
490   (*c)++;
491
492   Output_def *paper = get_paper (parser);
493
494   Paper_book* pb = book->process (outname.to_string (), paper);
495
496   pb->output (outname.to_string ());
497   
498   scm_gc_unprotect_object (paper->self_scm ());
499   scm_gc_unprotect_object (pb->self_scm ());
500
501   return SCM_UNSPECIFIED;
502 }
503