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