]> git.donarmstrong.com Git - lilypond.git/blob - lily/lily-guile.cc
release: 1.3.106
[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--2000 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
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
26 SCM
27 ly_str02scm (char const*c)
28 {
29   // this all really sucks, guile should take char const* arguments!
30   return gh_str02scm ((char*)c);
31 }
32
33
34
35 /*
36   Pass string to scm parser, evaluate one expression.
37   Return result value and #chars read.
38
39   Thanks to Gary Houston <ghouston@freewire.co.uk>
40
41   Need guile-1.3.4 (>1.3 anyway) for ftell on str ports -- jcn
42 */
43 SCM
44 ly_parse_scm (char const* s, int* n)
45 {
46   SCM str = gh_str02scm ((char*)s);
47   SCM port = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG,
48                             "ly_eval_scm_0str");
49   SCM from = scm_ftell (port);
50
51   SCM form;
52   SCM answer = SCM_UNSPECIFIED;
53
54   /* Read expression from port */
55   if (!SCM_EOF_OBJECT_P (form = scm_read (port)))
56     answer = scm_eval_3 (form, 1, SCM_EOL); // guh?
57
58   /*
59    After parsing
60
61        (begin (foo 1 2))
62
63    all seems fine, but after parsing
64
65        (foo 1 2)
66
67    read_buf has been advanced to read_pos - 1,
68    so that scm_ftell returns 1, instead of #parsed chars
69    */
70   
71   /*
72     urg: reset read_buf for scm_ftell
73     shouldn't scm_read () do this for us?
74   */
75   scm_fill_input (port);
76   SCM to = scm_ftell (port);
77   *n = gh_scm2int (to) - gh_scm2int (from);
78
79   /* Don't close the port here; if we re-enter this function via a
80      continuation, then the next time we enter it, we'll get an error.
81      It's a string port anyway, so there's no advantage to closing it
82      early.
83
84      scm_close_port (port);
85   */
86
87   return answer;
88 }
89
90 SCM
91 ly_quote_scm (SCM s)
92 {
93   return gh_list (ly_symbol2scm ("quote"), s, SCM_UNDEFINED);
94 }
95
96
97 SCM
98 ly_symbol2scm(const char *s)
99 {
100   return gh_symbol2scm ((char *)s);
101 }
102
103
104 String
105 ly_symbol2string (SCM s)
106 {
107   assert (gh_symbol_p (s));
108   return String((Byte*)SCM_CHARS (s), (int) SCM_LENGTH(s));
109 }
110
111
112 String
113 gulp_file_to_string (String fn)
114 {
115   String s = global_path.find (fn);
116   if (s == "")
117     {
118       String e = _f ("can't find file: `%s'", fn);
119       e += " ";
120       e += _f ("(load path: `%s')", global_path.str ());
121       error (e);
122     }
123   else if (verbose_global_b)
124     progress_indication ("[" + s );
125
126
127   Simple_file_storage f(s);
128   String result (f.ch_C());
129   if (verbose_global_b)
130     progress_indication ("]");
131   return result;
132 }
133
134 SCM
135 ly_gulp_file (SCM fn)
136 {
137   return ly_str02scm (gulp_file_to_string (ly_scm2string (fn)).ch_C());
138 }
139
140
141 /**
142    Read a file, and shove it down GUILE.  GUILE also has file read
143    functions, but you can't fiddle with the path of those.
144  */
145 void
146 read_lily_scm_file (String fn)
147 {
148   gh_eval_str ((char *) gulp_file_to_string (fn).ch_C());
149 }
150
151 extern "C" {
152   // maybe gdb 5.0 becomes quicker if it doesn't do fancy C++ typing?
153 void
154 ly_display_scm (SCM s)
155 {
156   gh_display (s);
157   gh_newline ();
158 }
159 };
160
161 String
162 ly_scm2string (SCM s)
163 {
164   assert (gh_string_p (s));
165   int len; 
166   char * p = gh_scm2newstr (s , &len);
167   
168   String r (p);
169
170   free (p);
171   return r;
172 }
173
174 SCM
175 index_cell (SCM s, Direction d)
176 {
177   assert (d);
178   return (d == LEFT) ? gh_car  (s) : gh_cdr (s);
179 }
180
181 SCM
182 index_set_cell (SCM s, Direction d, SCM v)
183 {
184   if (d == LEFT)
185     gh_set_car_x (s, v);
186   else if (d == RIGHT)
187     gh_set_cdr_x (s, v);
188   return s;
189 }
190   
191 SCM
192 ly_warning (SCM str)
193 {
194   assert (gh_string_p (str));
195   warning ("lily-guile: " + ly_scm2string (str));
196   return SCM_BOOL_T;
197 }
198
199 SCM
200 ly_isdir_p (SCM s)
201 {
202   if (gh_number_p (s))
203     {
204       int i = gh_scm2int (s);
205       return (i>= -1 && i <= 1)  ? SCM_BOOL_T : SCM_BOOL_F; 
206     }
207   return SCM_BOOL_F;
208 }
209
210
211
212 typedef void (*Void_fptr)();
213 Array<Void_fptr> *scm_init_funcs_;
214
215 void add_scm_init_func (void (*f)())
216 {
217   if (!scm_init_funcs_)
218     scm_init_funcs_ = new Array<Void_fptr>;
219
220   scm_init_funcs_->push (f);
221 }
222
223 void
224 init_lily_guile ()
225 {
226   for (int i=scm_init_funcs_->size() ; i--;)
227     (scm_init_funcs_->elem (i)) ();
228 }
229
230 unsigned int ly_scm_hash (SCM s)
231 {
232   return scm_ihashv (s, ~1u);
233 }
234
235
236
237 bool
238 isdir_b (SCM s)
239 {
240   if (gh_number_p (s))
241     {
242       int i = gh_scm2int (s);
243       return i>= -1 && i <= 1; 
244     }
245   return false;
246 }
247
248 Direction
249 to_dir (SCM s)
250 {
251   return (Direction) gh_scm2int (s);
252 }
253
254 Interval
255 ly_scm2interval (SCM p)
256 {
257   return  Interval (gh_scm2double (gh_car (p)),
258                     gh_scm2double (gh_cdr (p)));
259 }
260
261 SCM
262 ly_interval2scm (Interval i)
263 {
264   return gh_cons (gh_double2scm (i[LEFT]),
265                   gh_double2scm (i[RIGHT]));
266 }
267
268
269 bool
270 to_boolean (SCM s)
271 {
272   return gh_boolean_p (s) && gh_scm2bool (s);
273 }
274
275 /*
276   Appendable list L: the cdr contains the list, the car the last cons
277   in the list.
278  */
279 SCM
280 appendable_list ()
281 {
282   SCM s = gh_cons (SCM_EOL, SCM_EOL);
283   gh_set_car_x (s, s);
284   
285   return s;
286 }
287
288 void
289 appendable_list_append (SCM l, SCM elt)
290 {
291   SCM newcons = gh_cons (elt, SCM_EOL);
292   
293   gh_set_cdr_x (gh_car (l), newcons);      
294   gh_set_car_x (l, newcons);
295 }
296
297
298 SCM
299 ly_offset2scm (Offset o)
300 {
301   return gh_cons (gh_double2scm (o[X_AXIS]), gh_double2scm(o[Y_AXIS]));
302 }
303
304 Offset
305 ly_scm2offset (SCM s)
306 {
307   return Offset (gh_scm2double (gh_car (s)),
308                  gh_scm2double (gh_cdr (s)));
309 }
310
311 SCM
312 ly_type (SCM exp)
313 {
314   char const  * cp = "unknown";
315   if (gh_number_p (exp))
316     {
317       cp = "number";
318     }
319   else if (gh_string_p (exp))
320     {
321       cp = "string";
322     }
323   else if (gh_procedure_p (exp))
324     {
325       cp = "procedure";
326     }
327   else if (gh_boolean_p (exp))
328     {
329       cp = "boolean";
330     }
331   else if (gh_pair_p (exp))
332     {
333       cp = "list";
334     }
335
336   return ly_str02scm (cp);
337 }
338
339 /*
340   convert without too many decimals, and leave  a space at the end.
341  */
342    
343    
344 SCM
345 ly_number2string (SCM s)
346 {
347   assert (gh_number_p (s));
348
349   char str[400];                        // ugh.
350
351   if (scm_integer_p (s) == SCM_BOOL_F)
352     {
353       Real r (gh_scm2double (s));
354
355       if (isinf (r) || isnan (r))
356         {
357           programming_error ("Infinity or NaN encountered while converting Real number; setting to zero.");
358           r = 0.0;
359         }
360
361       sprintf (str, "%8.4f ", r);
362     }
363   else
364     {
365       sprintf (str, "%d ", gh_scm2int (s));
366     }
367
368   return gh_str02scm (str);
369 }
370
371 /*
372   Undef this to see if GUILE GC is causing too many swaps.
373  */
374
375 // #define TEST_GC
376
377 #ifdef TEST_GC
378 #include <libguile/gc.h>
379
380 static void *
381 greet_sweep (void *dummy1, void *dummy2, void *dummy3)
382 {
383    fprintf(stderr, "entering sweep\n");
384 }
385
386 static void *
387 wave_sweep_goodbye (void *dummy1, void *dummy2, void *dummy3)
388 {
389    fprintf(stderr, "leaving sweep\n");
390 }
391 #endif
392
393
394 #include "version.hh"
395 SCM
396 ly_version ()
397 {
398   char const* vs =  "\'(" MAJOR_VERSION " " MINOR_VERSION " "  PATCH_LEVEL " " MY_PATCH_LEVEL ")" ;
399
400   
401   return gh_eval_str ((char*)vs);
402 }
403
404 static void
405 init_functions ()
406 {
407   scm_make_gsubr ("ly-warn", 1, 0, 0, (Scheme_function_unknown)ly_warning);
408   scm_make_gsubr ("ly-version", 0, 0, 0, (Scheme_function_unknown)ly_warning);  
409   scm_make_gsubr ("ly-gulp-file", 1,0, 0, (Scheme_function_unknown)ly_gulp_file);
410   scm_make_gsubr ("dir?", 1,0, 0, (Scheme_function_unknown)ly_isdir_p);
411
412   scm_make_gsubr ("ly-number->string", 1, 0,0, (Scheme_function_unknown) ly_number2string);
413
414
415 #ifdef TEST_GC 
416   scm_c_hook_add (&scm_before_mark_c_hook, greet_sweep, 0, 0);
417   scm_c_hook_add (&scm_before_sweep_c_hook, wave_sweep_goodbye, 0, 0);
418 #endif
419 }
420
421 ADD_SCM_INIT_FUNC(funcs, init_functions);
422
423 SCM
424 ly_deep_copy (SCM l)
425 {
426   if (gh_pair_p (l))
427     {
428       return gh_cons (ly_deep_copy (gh_car (l)), ly_deep_copy (gh_cdr (l)));
429     }
430   else
431     return l;
432 }
433
434
435
436
437 SCM
438 ly_assoc_chain (SCM key, SCM achain)
439 {
440   if (gh_pair_p (achain))
441     {
442       SCM handle = scm_assoc (key, gh_car (achain));
443       if (gh_pair_p (handle))
444         return handle;
445       else
446         return ly_assoc_chain (key, gh_cdr (achain));
447     }
448   else
449     return SCM_BOOL_F;
450 }