]> git.donarmstrong.com Git - lilypond.git/blob - lily/lily-guile.cc
d490e50f60c7347f51aec5e74ba7811f243e9eee
[lilypond.git] / lily / lily-guile.cc
1 /*
2   lily-guile.cc -- implement assorted guile functions
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2002 Jan Nieuwenhuizen <janneke@gnu.org>
7
8   Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 */
10
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <math.h>   /* isinf */
15 #include <string.h> /* strdup, strchr */
16 #include "libc-extension.hh"
17 #include "lily-guile.hh"
18 #include "main.hh"
19 #include "simple-file-storage.hh"
20 #include "file-path.hh"
21 #include "debug.hh"
22 #include "direction.hh"
23 #include "offset.hh"
24 #include "interval.hh"
25 #include "pitch.hh"
26 #include "dimensions.hh"
27
28 SCM
29 ly_last (SCM list)
30 {
31   return ly_car (scm_last_pair (list));
32 }
33
34 SCM
35 ly_str02scm (char const*c)
36 {
37   // this all really sucks, guile should take char const* arguments!
38   return gh_str02scm ((char*)c);
39 }
40
41
42 SCM
43 ly_write2scm (SCM s)
44 {
45   SCM port = scm_mkstrport (SCM_INUM0, 
46                             scm_make_string (SCM_INUM0, SCM_UNDEFINED),
47                             SCM_OPN | SCM_WRTNG,
48                             "ly_write2string");
49   //  SCM write = scm_eval_3 (ly_symbol2scm ("write"), s, SCM_EOL);
50   SCM write = scm_primitive_eval (ly_symbol2scm ("write"));
51   
52   // scm_apply (write, port, SCM_EOL);
53   gh_call2 (write, s, port);
54   return scm_strport_to_string (port);
55 }
56
57
58 /*
59   Pass string to scm parser, evaluate one expression.
60   Return result value and #chars read.
61
62   Thanks to Gary Houston <ghouston@freewire.co.uk>
63
64   Need guile-1.3.4 (>1.3 anyway) for ftell on str ports -- jcn
65 */
66 SCM
67 ly_parse_scm (char const* s, int* n)
68 {
69   SCM str = ly_str02scm (s);
70   SCM port = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG,
71                             "ly_eval_scm_0str");
72   SCM from = scm_ftell (port);
73
74   SCM form;
75   SCM answer = SCM_UNSPECIFIED;
76
77   /* Read expression from port */
78   if (!SCM_EOF_OBJECT_P (form = scm_read (port)))
79     answer = scm_primitive_eval (form);
80  
81   /*
82    After parsing
83
84  (begin (foo 1 2))
85
86    all seems fine, but after parsing
87
88  (foo 1 2)
89
90    read_buf has been advanced to read_pos - 1,
91    so that scm_ftell returns 1, instead of #parsed chars
92    */
93   
94   /*
95     urg: reset read_buf for scm_ftell
96     shouldn't scm_read () do this for us?
97   */
98   scm_fill_input (port);
99   SCM to = scm_ftell (port);
100   *n = gh_scm2int (to) - gh_scm2int (from);
101
102   /* Don't close the port here; if we re-enter this function via a
103      continuation, then the next time we enter it, we'll get an error.
104      It's a string port anyway, so there's no advantage to closing it
105      early.
106
107      scm_close_port (port);
108   */
109
110   return answer;
111 }
112
113 SCM
114 ly_quote_scm (SCM s)
115 {
116   return scm_list_n (ly_symbol2scm ("quote"), s, SCM_UNDEFINED);
117 }
118
119
120
121 String
122 ly_symbol2string (SCM s)
123 {
124   assert (gh_symbol_p (s));
125   return String ((Byte*)SCM_STRING_CHARS (s), (int) SCM_STRING_LENGTH (s));
126 }
127
128
129 String
130 gulp_file_to_string (String fn)
131 {
132   String s = global_path.find (fn);
133   if (s == "")
134     {
135       String e = _f ("can't find file: `%s'", fn);
136       e += " ";
137       e += _f ("(load path: `%s')", global_path.str ());
138       error (e);
139     }
140   else if (verbose_global_b)
141     progress_indication ("[" + s);
142
143
144   Simple_file_storage f (s);
145   String result (f.ch_C ());
146   if (verbose_global_b)
147     progress_indication ("]");
148   return result;
149 }
150
151 LY_DEFINE(ly_gulp_file, "ly-gulp-file", 1,0, 0,
152           (SCM name),
153           "Read the file named @var{name}, and return its contents in a string. The
154 file is looked up using the lilypond search path.
155
156 ")
157 {
158   return ly_str02scm (gulp_file_to_string (ly_scm2string (name)).ch_C ());
159 }
160
161
162 /**
163    Read a file, and shove it down GUILE.  GUILE also has file read
164    functions, but you can't fiddle with the path of those.
165
166
167    TODO: JUNKME.
168 */
169 void
170 read_lily_scm_file (String fn)
171 {
172   gh_eval_str ((char *) gulp_file_to_string (fn).ch_C ());
173 }
174
175 extern "C" {
176   // maybe gdb 5.0 becomes quicker if it doesn't do fancy C++ typing?
177 void
178 ly_display_scm (SCM s)
179 {
180   gh_display (s);
181   gh_newline ();
182 }
183 };
184
185 String
186 ly_scm2string (SCM s)
187 {
188   assert (gh_string_p (s));
189
190   size_t len; 
191   char *p = gh_scm2newstr (s , &len);
192   
193   String r (p);
194
195   free (p);
196   return r;
197 }
198
199 SCM
200 index_cell (SCM s, Direction d)
201 {
202   assert (d);
203   return (d == LEFT) ? ly_car (s) : ly_cdr (s);
204 }
205
206 SCM
207 index_set_cell (SCM s, Direction d, SCM v)
208 {
209   if (d == LEFT)
210     gh_set_car_x (s, v);
211   else if (d == RIGHT)
212     gh_set_cdr_x (s, v);
213   return s;
214 }
215   
216 LY_DEFINE(ly_warning,"ly-warn", 1, 0, 0,
217   (SCM str),"Scheme callable function to issue the warning @code{msg}.
218 ")
219 {
220   assert (gh_string_p (str));
221   warning ("lily-guile: " + ly_scm2string (str));
222   return SCM_BOOL_T;
223 }
224
225 LY_DEFINE(ly_isdir_p,  "dir?", 1,0, 0,  (SCM s),
226           "type predicate. A direction is a -1, 0 or 1, where -1 represents left or
227 down and 1 represents right or up.
228 ")
229 {
230   if (gh_number_p (s))
231     {
232       int i = gh_scm2int (s);
233       return (i>= -1 && i <= 1)  ? SCM_BOOL_T : SCM_BOOL_F; 
234     }
235   return SCM_BOOL_F;
236 }
237
238 bool
239 ly_number_pair_p (SCM p)
240 {
241   return gh_pair_p (p) && gh_number_p (ly_car (p)) && gh_number_p (ly_cdr (p));
242 }
243
244 typedef void (*Void_fptr) ();
245 Array<Void_fptr> *scm_init_funcs_;
246
247 void add_scm_init_func (void (*f) ())
248 {
249   if (!scm_init_funcs_)
250     scm_init_funcs_ = new Array<Void_fptr>;
251
252   scm_init_funcs_->push (f);
253 }
254
255 extern  void init_cxx_function_smobs ();
256
257 void
258 prepend_load_path (String p )
259 {
260   char s[1024];
261   sprintf (s, 
262            "(set! %%load-path (cons \"%s\" %%load-path))", p.ch_C());
263
264   scm_c_eval_string (s);
265 }
266
267 void
268 init_lily_guile (String p )
269 {
270   prepend_load_path (p);
271
272   // todo: junk this. We should make real modules iso. just loading files.
273   prepend_load_path (p + "/scm/");
274
275   SCM last_mod = scm_current_module ();
276   scm_set_current_module (scm_c_resolve_module ("guile"));
277   
278   init_cxx_function_smobs ();
279   for (int i=scm_init_funcs_->size () ; i--;)
280     (scm_init_funcs_->elem (i)) ();
281
282   if (verbose_global_b)
283     progress_indication ("\n");
284   read_lily_scm_file ("lily.scm");
285
286   scm_set_current_module (last_mod);
287 }
288
289 unsigned int ly_scm_hash (SCM s)
290 {
291   return scm_ihashv (s, ~1u);
292 }
293
294
295
296 bool
297 ly_dir_p (SCM s)
298 {
299   if (gh_number_p (s))
300     {
301       int i = gh_scm2int (s);
302       return i>= -1 && i <= 1; 
303     }
304   return false;
305 }
306
307
308 bool
309 ly_axis_p (SCM s)
310 {
311   if (gh_number_p (s))
312     {
313       int i = gh_scm2int (s);
314       return i== 0 || i == 1;
315     }
316   return false;
317 }
318
319
320 Direction
321 to_dir (SCM s)
322 {
323   return (Direction) gh_scm2int (s);
324 }
325
326 Interval
327 ly_scm2interval (SCM p)
328 {
329   return  Interval (gh_scm2double (ly_car (p)),
330                     gh_scm2double (ly_cdr (p)));
331 }
332
333 SCM
334 ly_interval2scm (Drul_array<Real> i)
335 {
336   return gh_cons (gh_double2scm (i[LEFT]),
337                   gh_double2scm (i[RIGHT]));
338 }
339
340
341
342
343 bool
344 to_boolean (SCM s)
345 {
346   return gh_boolean_p (s) && gh_scm2bool (s);
347 }
348
349 /*
350   Appendable list L: the cdr contains the list, the car the last cons
351   in the list.
352  */
353 SCM
354 appendable_list ()
355 {
356   SCM s = gh_cons (SCM_EOL, SCM_EOL);
357   gh_set_car_x (s, s);
358   
359   return s;
360 }
361
362 void
363 appendable_list_append (SCM l, SCM elt)
364 {
365   SCM newcons = gh_cons (elt, SCM_EOL);
366   
367   gh_set_cdr_x (ly_car (l), newcons);      
368   gh_set_car_x (l, newcons);
369 }
370
371
372 SCM
373 ly_offset2scm (Offset o)
374 {
375   return gh_cons (gh_double2scm (o[X_AXIS]), gh_double2scm (o[Y_AXIS]));
376 }
377
378 Offset
379 ly_scm2offset (SCM s)
380 {
381   return Offset (gh_scm2double (ly_car (s)),
382                  gh_scm2double (ly_cdr (s)));
383 }
384
385 SCM
386 ly_type (SCM exp)
387 {
388   char const  * cp = "unknown";
389   if (gh_number_p (exp))
390     {
391       cp = "number";
392     }
393   else if (gh_string_p (exp))
394     {
395       cp = "string";
396     }
397   else if (gh_procedure_p (exp))
398     {
399       cp = "procedure";
400     }
401   else if (gh_boolean_p (exp))
402     {
403       cp = "boolean";
404     }
405   else if (gh_pair_p (exp))
406     {
407       cp = "list";
408     }
409
410   return ly_str02scm (cp);
411 }
412
413 /*
414   convert without too many decimals, and leave  a space at the end.
415  */
416    
417    
418 LY_DEFINE(ly_number2string,  "ly-number->string", 1, 0,0,
419           (SCM s),
420           " converts @var{num} to a string without generating many decimals. It
421 leaves a space at the end.
422 ")
423 {
424   assert (gh_number_p (s));
425
426   char str[400];                        // ugh.
427
428   if (scm_exact_p (s) == SCM_BOOL_F)
429     {
430       Real r (gh_scm2double (s));
431
432       if (isinf (r) || isnan (r))
433         {
434           programming_error ("Infinity or NaN encountered while converting Real number; setting to zero.");
435           r = 0.0;
436         }
437
438       sprintf (str, "%8.4f ", r);
439     }
440   else
441     {
442       sprintf (str, "%d ", gh_scm2int (s));
443     }
444
445   return ly_str02scm (str);
446 }
447
448 /*
449   Undef this to see if GUILE GC is causing too many swaps.
450  */
451
452 // #define TEST_GC
453
454 #ifdef TEST_GC
455 #include <libguile/gc.h>
456
457 static void *
458 greet_sweep (void *dummy1, void *dummy2, void *dummy3)
459 {
460    fprintf (stderr, "entering sweep\n");
461 }
462
463 static void *
464 wave_sweep_goodbye (void *dummy1, void *dummy2, void *dummy3)
465 {
466    fprintf (stderr, "leaving sweep\n");
467 }
468 #endif
469
470
471 #include "version.hh"
472 LY_DEFINE(ly_version,  "ly-version", 0, 0, 0, (),
473           "Return the current lilypond version as a list, e.g.
474 @code{(1 3 127 uu1)}. 
475 ")
476 {
477   char const* vs =  "\' (" MAJOR_VERSION " " MINOR_VERSION " "  PATCH_LEVEL " " MY_PATCH_LEVEL ")" ;
478   
479   return gh_eval_str ((char*)vs);
480 }
481
482 LY_DEFINE(ly_unit,  "ly-unit", 0, 0, 0, (),
483           "Return the unit used for lengths as a string.")
484 {
485   return ly_str02scm (INTERNAL_UNIT);
486 }
487
488 LY_DEFINE(ly_verbose,  "ly-verbose", 0, 0, 0, (),
489   "Return whether lilypond is being run in verbose mode.")
490 {
491   return gh_bool2scm (verbose_global_b);
492 }
493
494 static void
495 init_functions ()
496 {
497 #ifdef TEST_GC 
498   scm_c_hook_add (&scm_before_mark_c_hook, greet_sweep, 0, 0);
499   scm_c_hook_add (&scm_before_sweep_c_hook, wave_sweep_goodbye, 0, 0);
500 #endif
501 }
502
503 ADD_SCM_INIT_FUNC (funcs, init_functions);
504
505 SCM
506 ly_deep_copy (SCM l)
507 {
508   if (gh_pair_p (l))
509     {
510       return gh_cons (ly_deep_copy (ly_car (l)), ly_deep_copy (ly_cdr (l)));
511     }
512   else
513     return l;
514 }
515
516
517
518
519 SCM
520 ly_assoc_chain (SCM key, SCM achain)
521 {
522   if (gh_pair_p (achain))
523     {
524       SCM handle = scm_assoc (key, ly_car (achain));
525       if (gh_pair_p (handle))
526         return handle;
527       else
528         return ly_assoc_chain (key, ly_cdr (achain));
529     }
530   else
531     return SCM_BOOL_F;
532 }
533
534 /* looks the key up in the cdrs of the alist-keys
535    - ignoring the car and ignoring non-pair keys.
536    Returns first match found, i.e.
537
538    alist = ((1 . 10)
539                    ((1 . 2) . 11)
540                    ((2 . 1) . 12)
541                    ((3 . 0) . 13)
542                    ((4 . 1) . 14) )
543
544 I would like (ly_assoc_cdr 1) to return 12 - because it's the first
545 element with the cdr of the key = 1.  In other words (alloc_cdr key)
546 corresponds to call
547
548 (alloc (anything . key))
549
550
551
552 */
553 SCM
554 ly_assoc_cdr (SCM key, SCM alist)
555 {
556   if (gh_pair_p (alist)) {
557     SCM trykey = ly_caar(alist);
558     if(gh_pair_p(trykey) && to_boolean(scm_equal_p(key,ly_cdr(trykey))))
559       return ly_car(alist);
560     else
561       return ly_assoc_cdr (key, ly_cdr (alist));
562   }
563   else
564     return SCM_BOOL_F;
565 }
566
567 /*
568   LIST has the form "sym1 sym2 sym3\nsym4\nsym5"
569
570   i.e. \n and ' ' can be used interchangeably as separators.
571  */
572 SCM
573 parse_symbol_list (const char * list)
574 {
575   char * s = strdup (list);
576   char *orig = s;
577   SCM create_list = SCM_EOL;
578
579   for (char * p = s; *p; p++)
580     {
581       if (*p == '\n')
582         *p = ' ' ;
583     }
584   
585   if (!s[0] )
586     s = 0;
587
588
589   
590   while (s)
591     {
592       char *next = strchr (s, ' ');
593       if (next)
594         *next++ = 0;
595
596       create_list = gh_cons (ly_symbol2scm (s), create_list);
597       s = next;
598     }
599
600   free (orig);
601   return create_list;
602 }
603
604
605 SCM
606 ly_truncate_list (int k, SCM l )
607 {
608   if (k == 0)
609     {
610       l = SCM_EOL;
611     }
612   else
613     {
614       SCM s = l;
615       k--;
616       for (; gh_pair_p (s) && k--; s = ly_cdr (s))
617         ;
618
619       if (gh_pair_p (s))
620         {
621           gh_set_cdr_x (s, SCM_EOL);
622         }
623     }
624   return l;
625 }
626
627 SCM my_gh_symbol2scm (const char* x)
628 {
629   return gh_symbol2scm ((char*)x);
630 }
631
632 String
633 print_scm_val (SCM val)
634 {
635   String realval = ly_scm2string (ly_write2scm (val));
636   if (realval.length_i () > 200)
637     realval = realval.left_str (100) + "\n :\n :\n" + realval.right_str (100);
638   
639   return realval;        
640 }
641
642 bool
643 type_check_assignment (SCM sym, SCM val,  SCM type_symbol) 
644 {
645   bool ok = true;
646
647   /*
648     Always succeeds.
649
650
651     TODO: should remove #f from allowed vals?
652    */
653   if (val == SCM_EOL || val == SCM_BOOL_F)
654     return ok;
655
656   
657   SCM type_p = SCM_EOL;
658
659   if (gh_symbol_p (sym))
660     type_p = scm_object_property (sym, type_symbol);
661
662   if (type_p != SCM_EOL && !gh_procedure_p (type_p))
663       {
664         warning (_f ("Can't find property type-check for `%s' (%s).  Perhaps you made a typing error? Doing assignment anyway.",
665                      ly_symbol2string (sym).ch_C (),
666                      ly_symbol2string (type_symbol).ch_C ()
667
668                      ));
669       }
670   else
671     {
672       if (val != SCM_EOL
673           && gh_procedure_p (type_p)
674           && gh_call1 (type_p, val) == SCM_BOOL_F)
675         {
676           SCM errport = scm_current_error_port ();
677           ok = false;
678           SCM typefunc = scm_primitive_eval (ly_symbol2scm ("type-name"));
679           SCM type_name = gh_call1 (typefunc, type_p);
680
681          
682           scm_puts (_f ("Type check for `%s' failed; value `%s' must be of type `%s'",
683                         ly_symbol2string (sym).ch_C (),
684                         print_scm_val (val),
685                         ly_scm2string (type_name).ch_C ()).ch_C (),
686                     errport);
687           scm_puts ("\n", errport);                   
688         }
689     }
690   return ok;
691 }
692
693
694 /* some SCM abbrevs
695
696    zijn deze nou handig?
697    zijn ze er al in scheme, maar heten ze anders? */
698
699
700 /* Remove doubles from (sorted) list */
701 SCM
702 ly_unique (SCM list)
703 {
704   SCM unique = SCM_EOL;
705   for (SCM i = list; gh_pair_p (i); i = ly_cdr (i))
706     {
707       if (!gh_pair_p (ly_cdr (i))
708           || !gh_equal_p (ly_car (i), ly_cadr (i)))
709         unique = gh_cons (ly_car (i), unique);
710     }
711   return scm_reverse_x (unique, SCM_EOL);
712 }
713
714 /* tail add */
715 SCM
716 ly_snoc (SCM s, SCM list)
717 {
718   return gh_append2 (list, scm_list_n (s, SCM_UNDEFINED));
719 }
720
721
722 /* Split list at member s, removing s.
723    Return (BEFORE . AFTER) */
724 SCM
725 ly_split_list (SCM s, SCM list)
726 {
727   SCM before = SCM_EOL;
728   SCM after = list;
729   for (; gh_pair_p (after);)
730     {
731       SCM i = ly_car (after);
732       after = ly_cdr (after);
733       if (gh_equal_p (i, s))
734         break;
735       before = gh_cons (i, before);
736     }
737   return gh_cons ( scm_reverse_x (before, SCM_EOL),  after);
738   
739 }
740