]> git.donarmstrong.com Git - lilypond.git/blob - lily/my-lily-parser.cc
* lily/my-lily-parser.cc:
[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-path.hh"
12 #include "lily-version.hh"
13 #include "ly-module.hh"
14 #include "ly-smobs.icc"
15 #include "main.hh"
16 #include "my-lily-lexer.hh"
17 #include "my-lily-parser.hh"
18 #include "paper-def.hh"
19 #include "parray.hh"
20 #include "parser.hh"
21 #include "scm-hash.hh"
22 #include "score.hh"
23 #include "source.hh"
24 #include "string.hh"
25 #include "warn.hh"
26
27 My_lily_parser::My_lily_parser (Sources *sources)
28 {
29   book_count_ = 0;
30   score_count_ = 0;
31   lexer_ = 0;
32   sources_ = sources;
33   default_duration_ = Duration (2,0);
34   error_level_ = 0;
35   last_beam_start_ = SCM_EOL;
36   header_ = SCM_EOL;
37
38   header_ = ly_make_anonymous_module ();
39   smobify_self ();
40 }
41
42 My_lily_parser::My_lily_parser (My_lily_parser const &src)
43 {
44   book_count_ = src.book_count_;
45   score_count_ = src.score_count_;
46   lexer_ = src.lexer_;
47   sources_ = src.sources_;
48   default_duration_ = src.default_duration_;
49   error_level_ = src.error_level_;
50   last_beam_start_ = src.last_beam_start_;
51   header_ = src.header_;
52
53   smobify_self ();
54 }
55
56 My_lily_parser::~My_lily_parser ()
57 {
58   delete lexer_;
59 }
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   return parser->header_;
70 }
71
72 int
73 My_lily_parser::print_smob (SCM s, SCM port, scm_print_state*)
74 {
75   scm_puts ("#<my_lily_parser ", port);
76   My_lily_parser *parser = (My_lily_parser*) ly_cdr (s);
77   (void) parser;
78   scm_puts (" >", port);
79   return 1;
80 }
81
82
83 /* Process one .ly file, or book.  */
84 void
85 My_lily_parser::parse_file (String init, String name, String out_name)
86 {
87   lexer_ = new My_lily_lexer (sources_);
88   output_basename_ = out_name;
89   
90   lexer_->main_input_name_ = name;
91
92   progress_indication (_ ("Parsing..."));
93   progress_indication ("\n");
94
95   set_yydebug (0);
96   lexer_->new_input (init, sources_);
97
98   /* Read .ly IN_FILE, lex, parse, write \score blocks from IN_FILE to
99      OUT_FILE (unless IN_FILE redefines output file name).  */
100   do_yyparse ();
101   
102   if (!define_spots_.is_empty ())
103     {
104       define_spots_.top ().warning (_ ("Braces don't match"));
105       error_level_ = 1;
106     }
107
108   error_level_ = error_level_ | lexer_->error_level_;
109 }
110
111 void
112 My_lily_parser::parse_string (String ly_code)
113 {
114   My_lily_lexer *parent = lexer_;
115   lexer_ = (parent == 0 ? new My_lily_lexer (sources_)
116             : new My_lily_lexer (*parent));
117
118   lexer_->main_input_name_ = "<string>";
119   lexer_->main_input_b_ = true;
120
121   set_yydebug (0);
122   lexer_->new_input (lexer_->main_input_name_, ly_code, sources_);
123   do_yyparse ();
124   
125   if (!define_spots_.is_empty ())
126     {
127       define_spots_.top ().warning (_ ("Braces don't match"));
128       error_level_ = 1;
129     }
130
131   error_level_ = error_level_ | lexer_->error_level_;
132
133   if (parent != 0)
134     {
135       parent->keytable_ = lexer_->keytable_;
136       parent->encoding_ = lexer_->encoding_;
137       parent->chordmodifier_tab_ = lexer_->chordmodifier_tab_;
138       parent->pitchname_tab_stack_ = lexer_->pitchname_tab_stack_;
139       parent->sources_ = lexer_->sources_;
140       parent->scopes_ = lexer_->scopes_;
141       parent->error_level_ = lexer_->error_level_; 
142       parent->main_input_b_ = lexer_->main_input_b_;
143     }
144 }
145
146 void
147 My_lily_parser::push_spot ()
148 {
149   define_spots_.push (here_input ());
150 }
151
152 char const *
153 My_lily_parser::here_str0 () const
154 {
155   return lexer_->here_str0 ();
156 }
157
158 void
159 My_lily_parser::parser_error (String s)
160 {
161   here_input ().error (s);
162   error_level_ = 1;
163 }
164
165 Input
166 My_lily_parser::pop_spot ()
167 {
168   return define_spots_.pop ();
169 }
170
171 Input
172 My_lily_parser::here_input () const
173 {
174   /*
175     Parsing looks ahead , so we really want the previous location of the
176     lexer, not lexer_->here_input ().
177   */
178   /*
179     Actually, that gets very icky when there are white space, because
180     the line-numbers are all wrong.  Let's try the character before
181     the current token. That gets the right result for
182     note/duration stuff, but doesn't mess up for errors in the 1st token of the line. 
183     
184   */
185   Input hi (lexer_->here_input ());
186
187   char const * bla = hi.defined_str0_;
188   if (hi.line_number () > 1
189       || hi.column_number () > 1)
190     bla --;
191   
192   return Input (hi.source_file_, bla);
193 }
194
195
196 /****************************************************************/
197
198
199 /*
200   junkme?
201  */
202 bool store_locations_global_b;
203
204 /* Do not append `!' suffix, since 1st argument is not modified. */
205 LY_DEFINE (ly_set_point_and_click, "ly:set-point-and-click", 1, 0, 0,
206           (SCM what),
207           "Set the options for Point-and-click source specials output. The\n"
208 "argument is a symbol.  Possible options are @code{none} (no source specials),\n"
209 "@code{line} and @code{line-column}")
210 {
211   /* UGH. */
212   SCM val = SCM_BOOL_F;
213   if (ly_symbol2scm ("line-column") == what)
214     val = scm_c_eval_string ("line-column-location");
215   else if (what == ly_symbol2scm ("line"))
216     val = scm_c_eval_string ("line-location");
217
218   scm_module_define (global_lily_module, ly_symbol2scm ("point-and-click"),
219                      val);
220   store_locations_global_b = ly_c_procedure_p (val);
221   return SCM_UNSPECIFIED;
222 }
223
224
225 /* Distill full input file name from command argument.  PATH describes
226    file name with added default extension, ".ly" if none.  "-" is
227    STDIN.  */
228 Path
229 distill_inname (String str)
230 {
231   Path p = split_path (str);
232   if (str.is_empty () || str == "-")
233     p.base = "-";
234   else
235     {
236       String orig_ext = p.ext;
237       char const *extensions[] = {"ly", "", 0};
238       for (int i = 0; extensions[i]; i++)
239         {
240           p.ext = orig_ext;
241           if (*extensions[i] && !p.ext.is_empty ())
242             p.ext += ".";
243           p.ext += extensions[i];
244           if (!global_path.find (p.to_string ()).is_empty ())
245               break;
246         }
247       /* Reshuffle extension */
248       p = split_path (p.to_string ());
249     }
250   return p;
251 }
252
253 LY_DEFINE (ly_parse_file, "ly:parse-file",
254            1, 0, 0,
255            (SCM name),
256            "Parse a single @code{.ly} file.  "
257            "Upon failure, throw @code{ly-file-failed} key.")
258 {
259   SCM_ASSERT_TYPE (ly_c_string_p (name), name, SCM_ARG1, __FUNCTION__, "string");
260   char const *file = SCM_STRING_CHARS (name);
261   
262   String infile (file);
263   Path inpath = distill_inname (infile);
264   
265   /* By default, use base name of input file for output file name */
266   Path outpath = inpath;
267   if (inpath.to_string () != "-")
268     outpath.ext = output_format_global;
269   
270   /* By default, write output to cwd; do not copy directory part
271      of input file name */
272   outpath.root = "";
273   outpath.dir = "";
274   
275   if (!output_name_global.is_empty ())
276     outpath = split_path (output_name_global);
277   
278   String init;
279   if (!init_name_global.is_empty ())
280     init = init_name_global;
281   else
282     init = "init.ly";
283   
284   String in_file = inpath.to_string ();
285   String out_file = outpath.to_string ();
286
287
288   if (init.length () && global_path.find (init).is_empty ())
289     {
290       warning (_f ("can't find init file: `%s'", init));
291       warning (_f ("(search path: `%s')", global_path.to_string ().to_str0 ()));
292       exit (2);
293     }
294
295   if ((in_file != "-") && global_path.find (in_file).is_empty ())
296     {
297       warning (_f ("can't find file: `%s'", in_file));
298       scm_throw (ly_symbol2scm ("ly-file-failed"), scm_list_1 (scm_makfrom0str (in_file.to_str0 ())));
299     }
300   else
301     {
302       Sources sources;
303       sources.set_path (&global_path);
304   
305       progress_indication (_f ("Now processing `%s'", in_file.to_str0 ()));
306       progress_indication ("\n");
307
308       My_lily_parser *parser = new My_lily_parser (&sources);
309       scm_module_define (global_lily_module, ly_symbol2scm ("parser"),
310                          parser->self_scm ());
311       parser->parse_file (init, in_file, out_file);
312
313       bool error = parser->error_level_;
314       parser = 0;
315       if (error)
316         /* TODO: pass renamed input file too.  */
317         scm_throw (ly_symbol2scm ("ly-file-failed"),
318                    scm_list_1 (scm_makfrom0str (in_file.to_str0 ())));
319     }
320   return SCM_UNSPECIFIED;
321 }
322
323 LY_DEFINE (ly_parse_string, "ly:parse-string",
324            1, 0, 0,
325            (SCM ly_code),
326            "Parse the string LY_CODE.  "
327            "Upon failure, throw @code{ly-file-failed} key.")
328 {
329   SCM_ASSERT_TYPE (ly_c_string_p (ly_code), ly_code, SCM_ARG1, __FUNCTION__, "string");
330   
331   Sources sources;
332   sources.set_path (&global_path);
333   My_lily_parser *parser = new My_lily_parser (&sources);
334   scm_module_define (global_lily_module, ly_symbol2scm ("parser"),
335                      parser->self_scm ());
336   parser->parse_string (ly_scm2string (ly_code));
337   parser = 0;
338   
339   return SCM_UNSPECIFIED;
340 }
341
342 LY_DEFINE (ly_parser_parse_string, "ly:parser-parse-string",
343            2, 0, 0,
344            (SCM parser_smob, SCM ly_code),
345            "Parse the string LY_CODE with PARSER_SMOB."
346            "Upon failure, throw @code{ly-file-failed} key.")
347 {
348 #if 0
349   SCM_ASSERT_TYPE (ly_c_parser_p (parser), music, SCM_ARG1, __FUNCTION__, "parser");
350 #endif
351   SCM_ASSERT_TYPE (ly_c_string_p (ly_code), ly_code, SCM_ARG1, __FUNCTION__, "string");
352
353 #if 0
354   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
355   parser->parse_string (ly_scm2string (ly_code));
356 #else
357   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
358   My_lily_parser *clone = new My_lily_parser (*parser);
359   clone->parse_string (ly_scm2string (ly_code));
360   clone = 0;
361 #endif
362   
363   return SCM_UNSPECIFIED;
364 }
365
366 static Music_output_def*
367 get_paper (My_lily_parser *parser)
368 {
369   SCM id = parser->lexer_->lookup_identifier ("$defaultpaper");
370   Music_output_def *paper = unsmob_music_output_def (id);
371   return paper ? paper->clone () : new Paper_def;
372 }
373
374 LY_DEFINE (ly_parser_print_score, "ly:parser-print-score",
375            2, 0, 0,
376            (SCM parser_smob, SCM score_smob),
377            "Print score, i.e., the classic way.")
378 {
379 #if 0
380   SCM_ASSERT_TYPE (ly_c_parser_p (parser), music, SCM_ARG1, __FUNCTION__, "parser");
381   SCM_ASSERT_TYPE (ly_c_music_p (music), music, SCM_ARG1, __FUNCTION__, "music");
382 #endif
383   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
384   Score *score = unsmob_score (score_smob);
385
386   SCM header = is_module (score->header_) ? score->header_
387     : parser->header_.to_SCM ();
388   
389   Path outname = split_path (parser->output_basename_);
390   int *c = &parser->book_count_;
391   if (*c)
392     outname.base += "-" + to_string (*c);
393   (*c)++;
394
395   SCM os = scm_makfrom0str (outname.to_string ().to_str0 ());
396   for (int i = 0; i < score->defs_.size (); i++)
397     default_rendering (score->music_, score->defs_[i]->self_scm (), header,
398                        os);
399
400   if (score->defs_.is_empty ())
401     {
402       Music_output_def *paper = get_paper (parser);
403       default_rendering (score->music_, paper->self_scm (), header, os);
404       scm_gc_unprotect_object (paper->self_scm ());
405     }
406   return SCM_UNDEFINED;
407 }
408
409 LY_DEFINE (ly_parser_print_book, "ly:parser-print-book",
410            2, 0, 0,
411            (SCM parser_smob, SCM book_smob),
412            "Print book.")
413 {
414 #if 0
415   SCM_ASSERT_TYPE (ly_c_parser_p (parser_smob), parser_smob, SCM_ARG1, __FUNCTION__, "parser_smob");
416   SCM_ASSERT_TYPE (ly_c_music_p (book_smob), book_smob, SCM_ARG1, __FUNCTION__, "book_smob");
417 #endif
418   My_lily_parser *parser = unsmob_my_lily_parser (parser_smob);
419   Book *book = unsmob_book (book_smob);
420   
421   SCM header = parser->header_;
422   Path outname = split_path (parser->output_basename_);
423   int *c = &parser->book_count_;
424   if (*c)
425     outname.base += "-" + to_string (*c);
426   (*c)++;
427   Music_output_def *paper = get_paper (parser);
428   book->process (outname.to_string (), paper, header);
429   scm_gc_unprotect_object (paper->self_scm ());
430   return SCM_UNDEFINED;
431 }