]> git.donarmstrong.com Git - lilypond.git/blob - lily/my-lily-parser.cc
* lily/my-lily-parser.cc (ly:parser-add-book-and-score): 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   SCM nn = lexer_->lookup_identifier ("pitchnames");
104   lexer_->push_note_state (alist_to_hashq (nn));
105
106   set_yydebug (0);
107   lexer_->new_input (lexer_->main_input_name_, ly_code, sources_);
108   do_yyparse ();
109   
110   if (!define_spots_.is_empty ())
111     {
112       define_spots_.top ().warning (_ ("Braces don't match"));
113       error_level_ = 1;
114     }
115
116   error_level_ = error_level_ | lexer_->error_level_;
117 }
118
119 void
120 My_lily_parser::push_spot ()
121 {
122   define_spots_.push (here_input ());
123 }
124
125 char const *
126 My_lily_parser::here_str0 () const
127 {
128   return lexer_->here_str0 ();
129 }
130
131 void
132 My_lily_parser::parser_error (String s)
133 {
134   here_input ().error (s);
135   error_level_ = 1;
136 }
137
138
139
140 Input
141 My_lily_parser::pop_spot ()
142 {
143   return define_spots_.pop ();
144 }
145
146 Input
147 My_lily_parser::here_input () const
148 {
149   /*
150     Parsing looks ahead , so we really want the previous location of the
151     lexer, not lexer_->here_input ().
152   */
153   /*
154     Actually, that gets very icky when there are white space, because
155     the line-numbers are all wrong.  Let's try the character before
156     the current token. That gets the right result for
157     note/duration stuff, but doesn't mess up for errors in the 1st token of the line. 
158     
159   */
160   Input hi (lexer_->here_input ());
161
162   char const * bla = hi.defined_str0_;
163   if (hi.line_number () > 1
164       || hi.column_number () > 1)
165     bla --;
166   
167   return Input (hi.source_file_, bla);
168 }
169
170
171 /****************************************************************/
172
173
174 /*
175   junkme?
176  */
177 bool store_locations_global_b;
178
179 /*
180   no ! suffix since it doesn't modify 1st argument.
181  */
182 LY_DEFINE (ly_set_point_and_click, "ly:set-point-and-click", 1, 0, 0,
183           (SCM what),
184           "Set the options for Point-and-click source specials output. The\n"
185 "argument is a symbol.  Possible options are @code{none} (no source specials),\n"
186 "@code{line} and @code{line-column}")
187 {
188   /*
189     UGH.
190    */
191   SCM val = SCM_BOOL_F;
192   if (ly_symbol2scm ("line-column") == what)
193     val = scm_c_eval_string ("line-column-location");
194   else if (what == ly_symbol2scm ("line"))
195     val = scm_c_eval_string ("line-location");
196
197   scm_module_define (global_lily_module, ly_symbol2scm ("point-and-click"), val);
198   store_locations_global_b = ly_c_procedure_p (val);
199   return SCM_UNSPECIFIED;
200 }
201
202
203 /* Distill full input file name from command argument.  PATH describes
204    file name with added default extension, ".ly" if none.  "-" is
205    STDIN.  */
206 Path
207 distill_inname (String str)
208 {
209   Path p = split_path (str);
210   if (str.is_empty () || str == "-")
211     p.base = "-";
212   else
213     {
214       String orig_ext = p.ext;
215       char const *extensions[] = {"ly", "", 0};
216       for (int i = 0; extensions[i]; i++)
217         {
218           p.ext = orig_ext;
219           if (*extensions[i] && !p.ext.is_empty ())
220             p.ext += ".";
221           p.ext += extensions[i];
222           if (!global_path.find (p.to_string ()).is_empty ())
223               break;
224         }
225       /* Reshuffle extension */
226       p = split_path (p.to_string ());
227     }
228   return p;
229 }
230
231 LY_DEFINE (ly_parse_file, "ly:parse-file",
232            1, 0, 0,
233            (SCM name),
234            "Parse a single @code{.ly} file.  "
235            "Upon failure, throw @code{ly-file-failed} key.")
236 {
237   SCM_ASSERT_TYPE (ly_c_string_p (name), name, SCM_ARG1, __FUNCTION__, "string");
238   char const *file = SCM_STRING_CHARS (name);
239   
240   String infile (file);
241   Path inpath = distill_inname (infile);
242   
243   /* By default, use base name of input file for output file name */
244   Path outpath = inpath;
245   if (inpath.to_string () != "-")
246     outpath.ext = output_format_global;
247   
248   /* By default, write output to cwd; do not copy directory part
249      of input file name */
250   outpath.root = "";
251   outpath.dir = "";
252   
253   if (!output_name_global.is_empty ())
254     outpath = split_path (output_name_global);
255   
256   String init;
257   if (!init_name_global.is_empty ())
258     init = init_name_global;
259   else
260     init = "init.ly";
261   
262   String in_file = inpath.to_string ();
263   String out_file = outpath.to_string ();
264
265
266   if (init.length () && global_path.find (init).is_empty ())
267     {
268       warning (_f ("can't find init file: `%s'", init));
269       warning (_f ("(search path: `%s')", global_path.to_string ().to_str0 ()));
270       exit (2);
271     }
272
273   if ((in_file != "-") && global_path.find (in_file).is_empty ())
274     {
275       warning (_f ("can't find file: `%s'", in_file));
276       scm_throw (ly_symbol2scm ("ly-file-failed"), scm_list_1 (scm_makfrom0str (in_file.to_str0 ())));
277     }
278   else
279     {
280       Sources sources;
281       sources.set_path (&global_path);
282   
283       progress_indication (_f ("Now processing `%s'", in_file.to_str0 ()));
284       progress_indication ("\n");
285
286       My_lily_parser *parser = new My_lily_parser (&sources);
287       parser->parse_file (init, in_file, out_file);
288
289       bool error = parser->error_level_;
290       parser = 0;
291       if (error)
292         /* TODO: pass renamed input file too.  */
293         scm_throw (ly_symbol2scm ("ly-file-failed"),
294                    scm_list_1 (scm_makfrom0str (in_file.to_str0 ())));
295     }
296   return SCM_UNSPECIFIED;
297 }
298
299 LY_DEFINE (ly_parse_string, "ly:parse-string",
300            1, 0, 0,
301            (SCM ly_code),
302            "Parse the string LY_CODE.  "
303            "Upon failure, throw @code{ly-file-failed} key.")
304 {
305   SCM_ASSERT_TYPE (ly_c_string_p (ly_code), ly_code, SCM_ARG1, __FUNCTION__, "string");
306   
307   Sources sources;
308   sources.set_path (&global_path);
309   My_lily_parser *parser = new My_lily_parser (&sources);
310   parser->parse_string (ly_scm2string (ly_code));
311   parser = 0;
312   
313   return SCM_UNSPECIFIED;
314 }
315
316
317 LY_DEFINE (ly_parser_add_book_and_score, "ly:parser-add-book-and-score",
318            2, 0, 0,
319            (SCM purple, SCM music),
320            "Handle the toplevel-music MUSIC by adding BOOK and SCORE.")
321 {
322 #if 0
323   SCM_ASSERT_TYPE (ly_c_parser_p (parser), music, SCM_ARG1, __FUNCTION__, "parser");
324   SCM_ASSERT_TYPE (ly_c_music_p (music), music, SCM_ARG1, __FUNCTION__, "music");
325 #endif
326   My_lily_parser *parser = unsmob_my_lily_parser (purple);
327   Score *score = new Score;
328   
329   /* URG? */
330   SCM check_funcs = ly_scheme_function ("toplevel-music-functions");
331   for (; ly_c_pair_p (check_funcs); check_funcs = ly_cdr (check_funcs))
332     music = scm_call_1 (ly_car (check_funcs), music);
333   score->music_ = music;
334   Book *book = new Book;
335   book->scores_.push (score);
336   scm_gc_unprotect_object (score->self_scm ());
337   
338   SCM header = parser->header_;
339   Path outname = split_path (parser->output_basename_);
340   int *c = &parser->book_count_;
341   if (*c)
342     outname.base += "-" + to_string (*c);
343   (*c)++;
344   My_lily_lexer *lexer = parser->lexer_;
345   Music_output_def *dp
346     = unsmob_music_output_def (lexer->lookup_identifier ("$defaultpaper"));
347   book->process (outname.to_string (),
348                  dp ? dp->clone () : new Paper_def, header);
349   scm_gc_unprotect_object (book->self_scm ());
350   return SCM_UNDEFINED;
351 }