1 /* Run builtin functions ... sin/error etc.
2 */
3
4 /*
5
6 Copyright (C) 1991-2003 The National Gallery
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 */
23
24 /*
25
26 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27
28 */
29
30 #include "ip.h"
31
32 #ifdef HAVE_GSL
33 #include <gsl/gsl_sf_gamma.h>
34 #include <gsl/gsl_errno.h>
35 #endif /*HAVE_GSL*/
36
37 /* Trace builtin calls.
38 #define DEBUG
39 */
40
41 /* Spot something that might be an arg to sin/cos/tan etc.
42 */
43 static gboolean
ismatharg(Reduce * rc,PElement * base)44 ismatharg( Reduce *rc, PElement *base )
45 {
46 return( PEISIMAGE( base ) || PEISREAL( base ) || PEISCOMPLEX( base ) );
47 }
48
49 /* Spot something that might be an arg to re/im etc.
50 */
51 static gboolean
iscomplexarg(Reduce * rc,PElement * base)52 iscomplexarg( Reduce *rc, PElement *base )
53 {
54 return( PEISIMAGE( base ) || PEISCOMPLEX( base ) );
55 }
56
57 /* Spot anything.
58 */
isany(Reduce * rc,PElement * base)59 static gboolean isany( Reduce *rc, PElement *base ) { return( TRUE ); }
60
61 /* Other PEIS as functions.
62 */
pe_is_image(Reduce * rc,PElement * base)63 static gboolean pe_is_image( Reduce *rc, PElement *base )
64 { return( PEISIMAGE( base ) ); }
pe_is_real(Reduce * rc,PElement * base)65 static gboolean pe_is_real( Reduce *rc, PElement *base )
66 { return( PEISREAL( base ) ); }
pe_is_complex(Reduce * rc,PElement * base)67 static gboolean pe_is_complex( Reduce *rc, PElement *base )
68 { return( PEISCOMPLEX( base ) ); }
pe_is_bool(Reduce * rc,PElement * base)69 static gboolean pe_is_bool( Reduce *rc, PElement *base )
70 { return( PEISBOOL( base ) ); }
pe_is_char(Reduce * rc,PElement * base)71 static gboolean pe_is_char( Reduce *rc, PElement *base )
72 { return( PEISCHAR( base ) ); }
pe_is_list(Reduce * rc,PElement * base)73 static gboolean pe_is_list( Reduce *rc, PElement *base )
74 { return( PEISLIST( base ) ); }
pe_is_flist(Reduce * rc,PElement * base)75 static gboolean pe_is_flist( Reduce *rc, PElement *base )
76 { return( PEISFLIST( base ) ); }
pe_is_class(Reduce * rc,PElement * base)77 static gboolean pe_is_class( Reduce *rc, PElement *base )
78 { return( PEISCLASS( base ) ); }
79
80
81 /* The types we might want to spot for builtins.
82 *
83 * Others, eg.:
84 *
85 static BuiltinTypeSpot vimage_spot = { "vips_image", pe_is_image };
86 static BuiltinTypeSpot bool_spot = { "bool", pe_is_bool };
87 static BuiltinTypeSpot realvec_spot = { "[real]", reduce_is_realvec };
88 static BuiltinTypeSpot matrix_spot = { "[[real]]", reduce_is_matrix };
89 static BuiltinTypeSpot instance_spot = { "class instance", pe_is_class };
90 static gboolean pe_is_gobject( Reduce *rc, PElement *base )
91 { return( PEISMANAGEDGOBJECT( base ) ); }
92 static BuiltinTypeSpot gobject_spot = { "GObject", pe_is_gobject };
93 *
94 */
95
96 static BuiltinTypeSpot real_spot = { "real", pe_is_real };
97 static BuiltinTypeSpot complex_spot = { "complex|image", iscomplexarg };
98 static BuiltinTypeSpot flist_spot = { "non-empty list", pe_is_flist };
99 static BuiltinTypeSpot string_spot = { "[char]", reduce_is_finitestring };
100 static BuiltinTypeSpot list_spot = { "[*]", reduce_is_list };
101 static BuiltinTypeSpot math_spot = { "image|real|complex", ismatharg };
102 static BuiltinTypeSpot any_spot = { "any", isany };
103
104 /* Args for "_".
105 */
106 static BuiltinTypeSpot *underscore_args[] = {
107 &string_spot
108 };
109
110 /* Do a _ call. Args already spotted.
111 */
112 static void
apply_underscore_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)113 apply_underscore_call( Reduce *rc,
114 const char *name, HeapNode **arg, PElement *out )
115 {
116 PElement rhs;
117 char text[MAX_STRSIZE];
118
119 PEPOINTRIGHT( arg[0], &rhs );
120 (void) reduce_get_string( rc, &rhs, text, MAX_STRSIZE );
121
122 /* Pump though gettext.
123 */
124 if( !heap_managedstring_new( rc->heap, _( text ), out ) )
125 reduce_throw( rc );
126 }
127
128 /* Args for "has_member".
129 */
130 static BuiltinTypeSpot *has_member_args[] = {
131 &string_spot,
132 &any_spot
133 };
134
135 /* Do a has_member call. Args already spotted.
136 */
137 static void
apply_has_member_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)138 apply_has_member_call( Reduce *rc,
139 const char *name, HeapNode **arg, PElement *out )
140 {
141 PElement rhs;
142 char mname[MAX_STRSIZE];
143 PElement member;
144
145 PEPOINTRIGHT( arg[1], &rhs );
146 (void) reduce_get_string( rc, &rhs, mname, MAX_STRSIZE );
147 PEPOINTRIGHT( arg[0], &rhs );
148 PEPUTP( out, ELEMENT_BOOL,
149 class_get_member( &rhs, mname, NULL, &member ) );
150 }
151
152 /* Args for "is_instanceof".
153 */
154 static BuiltinTypeSpot *is_instanceof_args[] = {
155 &string_spot,
156 &any_spot
157 };
158
159 /* Do an is_instance call. Args already spotted.
160 */
161 static void
apply_is_instanceof_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)162 apply_is_instanceof_call( Reduce *rc,
163 const char *name, HeapNode **arg, PElement *out )
164 {
165 PElement rhs;
166 char kname[MAX_STRSIZE];
167
168 PEPOINTRIGHT( arg[1], &rhs );
169 (void) reduce_get_string( rc, &rhs, kname, MAX_STRSIZE );
170 PEPOINTRIGHT( arg[0], &rhs );
171 PEPUTP( out, ELEMENT_BOOL, reduce_is_instanceof( rc, kname, &rhs ) );
172 }
173
174 /* Args for builtin on complex.
175 */
176 static BuiltinTypeSpot *complex_args[] = {
177 &complex_spot
178 };
179
180 /* Do a complex op. Args already spotted.
181 */
182 static void
apply_complex_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)183 apply_complex_call( Reduce *rc,
184 const char *name, HeapNode **arg, PElement *out )
185 {
186 PElement rhs;
187
188 PEPOINTRIGHT( arg[0], &rhs );
189
190 if( PEISIMAGE( &rhs ) ) {
191 if( strcmp( name, "re" ) == 0 )
192 call_spine( rc, "im_c2real", arg, out );
193 else if( strcmp( name, "im" ) == 0 )
194 call_spine( rc, "im_c2imag", arg, out );
195 }
196 else if( PEISCOMPLEX( &rhs ) ) {
197 if( strcmp( name, "re" ) == 0 ) {
198 PEPUTP( out,
199 ELEMENT_NODE, GETLEFT( PEGETVAL( &rhs ) ) );
200 }
201 else if( strcmp( name, "im" ) == 0 ) {
202 PEPUTP( out,
203 ELEMENT_NODE, GETRIGHT( PEGETVAL( &rhs ) ) );
204 }
205 }
206 else
207 error( "internal error #98743698437639487" );
208 }
209
210 /* Args for builtin on list.
211 */
212 static BuiltinTypeSpot *flist_args[] = {
213 &flist_spot
214 };
215
216 /* Do a list op. Args already spotted.
217 */
218 static void
apply_list_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)219 apply_list_call( Reduce *rc,
220 const char *name, HeapNode **arg, PElement *out )
221 {
222 PElement rhs;
223 PElement a;
224
225 PEPOINTRIGHT( arg[0], &rhs );
226 g_assert( PEISFLIST( &rhs ) );
227
228 reduce_get_list( rc, &rhs );
229
230 if( strcmp( name, "hd" ) == 0 ) {
231 PEGETHD( &a, &rhs );
232 PEPUTPE( out, &a );
233 }
234 else if( strcmp( name, "tl" ) == 0 ) {
235 PEGETTL( &a, &rhs );
236 PEPUTPE( out, &a );
237 }
238 else
239 error( "internal error #098734953" );
240 }
241
242 /* "gammq"
243 */
244 static BuiltinTypeSpot *gammq_args[] = {
245 &real_spot,
246 &real_spot
247 };
248
249 static void
apply_gammq_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)250 apply_gammq_call( Reduce *rc,
251 const char *name, HeapNode **arg, PElement *out )
252 {
253 PElement rhs;
254 double a, x, Q;
255
256 PEPOINTRIGHT( arg[1], &rhs );
257 a = PEGETREAL( &rhs );
258 PEPOINTRIGHT( arg[0], &rhs );
259 x = PEGETREAL( &rhs );
260
261 if( a <= 0 || x < 0 ) {
262 error_top( _( "Out of range." ) );
263 error_sub( _( "gammq arguments must be a > 0, x >= 0." ) );
264 reduce_throw( rc );
265 }
266
267 #ifdef HAVE_GSL
268 Q = gsl_sf_gamma_inc_Q( a, x );
269 #else /*!HAVE_GSL*/
270 error_top( _( "Not available." ) );
271 error_sub( _( "No GSL library available for gammq." ) );
272 reduce_throw( rc );
273 #endif /*HAVE_GSL*/
274
275 if( !heap_real_new( rc->heap, Q, out ) )
276 reduce_throw( rc );
277 }
278
279 /* Args for "vips_image".
280 */
281 static BuiltinTypeSpot *image_args[] = {
282 &string_spot
283 };
284
285 /* Do a image call.
286 */
287 static void
apply_image_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)288 apply_image_call( Reduce *rc,
289 const char *name, HeapNode **arg, PElement *out )
290 {
291 Heap *heap = rc->heap;
292
293 PElement rhs;
294 char buf[FILENAME_MAX];
295 char filename[FILENAME_MAX];
296 char mode[FILENAME_MAX];
297 char *fn;
298 Imageinfo *ii;
299
300 /* Get string.
301 */
302 PEPOINTRIGHT( arg[0], &rhs );
303 (void) reduce_get_string( rc, &rhs, buf, FILENAME_MAX );
304
305 /* The buf might be something like n3862.pyr.tif:1, ie. contain some
306 * load options. Split and search just for the filename component.
307 */
308 im_filename_split( buf, filename, mode );
309
310 /* Try to load image from given string.
311 */
312 if( !(fn = path_find_file( filename )) )
313 reduce_throw( rc );
314
315 /* Reattach the mode and load.
316 */
317 im_snprintf( buf, FILENAME_MAX, "%s:%s", fn, mode );
318 if( !(ii = imageinfo_new_input(
319 main_imageinfogroup, NULL, heap, buf )) ) {
320 IM_FREE( fn );
321 reduce_throw( rc );
322 }
323 IM_FREE( fn );
324
325 PEPUTP( out, ELEMENT_MANAGED, ii );
326 MANAGED_UNREF( ii );
327 }
328
329 /* Args for "read".
330 */
331 static BuiltinTypeSpot *read_args[] = {
332 &string_spot
333 };
334
335 /* Do a read call.
336 */
337 static void
apply_read_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)338 apply_read_call( Reduce *rc,
339 const char *name, HeapNode **arg, PElement *out )
340 {
341 PElement rhs;
342 char buf[FILENAME_MAX];
343
344 /* Get string.
345 */
346 PEPOINTRIGHT( arg[0], &rhs );
347 (void) reduce_get_string( rc, &rhs, buf, FILENAME_MAX );
348
349 if( !heap_file_new( rc->heap, buf, out ) )
350 reduce_throw( rc );
351 }
352
353 /* Args for "graph_export_image".
354 */
355 static BuiltinTypeSpot *graph_export_image_args[] = {
356 &real_spot,
357 &any_spot
358 };
359
360 /* Do a graph_export_image call.
361 */
362 static void
apply_graph_export_image_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)363 apply_graph_export_image_call( Reduce *rc,
364 const char *name, HeapNode **arg, PElement *out )
365 {
366 #ifdef HAVE_LIBGOFFICE
367 PElement rhs;
368 double dpi;
369 Plot *plot;
370 Imageinfo *ii;
371
372 PEPOINTRIGHT( arg[1], &rhs );
373 dpi = PEGETREAL( &rhs );
374
375 PEPOINTRIGHT( arg[0], &rhs );
376 if( !reduce_is_instanceof( rc, CLASS_PLOT, &rhs ) ) {
377 char txt[100];
378 VipsBuf buf = VIPS_BUF_STATIC( txt );
379
380 itext_value_ev( rc, &buf, &rhs );
381 error_top( _( "Bad argument." ) );
382 error_sub( _( "Argument 2 to \"%s\" should "
383 "be instance of \"%s\", you passed:\n %s" ),
384 name, CLASS_PLOT,
385 vips_buf_all( &buf ) );
386 reduce_throw( rc );
387 }
388
389 plot = g_object_new( TYPE_PLOT, NULL );
390
391 if( !classmodel_update_members( CLASSMODEL( plot ), &rhs ) ) {
392 UNREF( plot );
393 reduce_throw( rc );
394 }
395
396 if( !(ii = plot_to_image( plot, rc, dpi )) ) {
397 UNREF( plot );
398 reduce_throw( rc );
399 }
400 UNREF( plot );
401
402 PEPUTP( out, ELEMENT_MANAGED, ii );
403 #else /*!HAVE_LIBGOFFICE*/
404 PEPUTP( out, ELEMENT_BOOL, TRUE );
405 #endif /*HAVE_LIBGOFFICE*/
406 }
407
408 /* Args for "math".
409 */
410 static BuiltinTypeSpot *math_args[] = {
411 &math_spot
412 };
413
414 /* A math function ... name, number implementation, image implementation.
415 */
416 typedef struct {
417 const char *name; /* ip name */
418 double (*rfn)( double ); /* Number implementation */
419 const char *ifn; /* VIPS name */
420 } MathFn;
421
ip_sin(double a)422 static double ip_sin( double a ) { return( sin( IM_RAD( a ) ) ); }
ip_cos(double a)423 static double ip_cos( double a ) { return( cos( IM_RAD( a ) ) ); }
ip_tan(double a)424 static double ip_tan( double a ) { return( tan( IM_RAD( a ) ) ); }
ip_asin(double a)425 static double ip_asin( double a ) { return( IM_DEG( asin( a ) ) ); }
ip_acos(double a)426 static double ip_acos( double a ) { return( IM_DEG( acos( a ) ) ); }
ip_atan(double a)427 static double ip_atan( double a ) { return( IM_DEG( atan( a ) ) ); }
ip_exp10(double a)428 static double ip_exp10( double a ) { return( pow( 10.0, a ) ); }
ip_ceil(double a)429 static double ip_ceil( double a ) { return( ceil( a ) ); }
ip_floor(double a)430 static double ip_floor( double a ) { return( floor( a ) ); }
431
432 /* Table of math functions ... number implementations, image implementations.
433 */
434 static MathFn math_fn[] = {
435 { "sin", &ip_sin, "im_sintra" },
436 { "cos", &ip_cos, "im_costra" },
437 { "tan", &ip_tan, "im_tantra" },
438 { "asin", &ip_asin, "im_asintra" },
439 { "acos", &ip_acos, "im_acostra" },
440 { "atan", &ip_atan, "im_atantra" },
441 { "log", &log, "im_logtra" },
442 { "log10", &log10, "im_log10tra" },
443 { "exp", &exp, "im_exptra" },
444 { "exp10", &ip_exp10, "im_exp10tra" },
445 { "ceil", &ip_ceil, "im_ceil" },
446 { "floor", &ip_floor, "im_floor" }
447 };
448
449 /* Do a math function (eg. sin, cos, tan).
450 */
451 static void
apply_math_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)452 apply_math_call( Reduce *rc,
453 const char *name, HeapNode **arg, PElement *out )
454 {
455 PElement rhs;
456 int i;
457
458 /* Find implementation.
459 */
460 for( i = 0; i < IM_NUMBER( math_fn ); i++ )
461 if( strcmp( name, math_fn[i].name ) == 0 )
462 break;
463 if( i == IM_NUMBER( math_fn ) )
464 error( "internal error #928456936" );
465
466 /* Get arg type ... real/complex/image
467 */
468 PEPOINTRIGHT( arg[0], &rhs );
469 if( PEISIMAGE( &rhs ) ) {
470 /* Easy ... pass to VIPS.
471 */
472 call_spine( rc, math_fn[i].ifn, arg, out );
473 }
474 else if( PEISREAL( &rhs ) ) {
475 double a = PEGETREAL( &rhs );
476 double b = math_fn[i].rfn( a );
477
478 if( !heap_real_new( rc->heap, b, out ) )
479 reduce_throw( rc );
480 }
481 else if( PEISCOMPLEX( &rhs ) ) {
482 error_top( _( "Not implemented." ) );
483 error_sub( _( "Complex math ops not implemented." ) );
484 reduce_throw( rc );
485 }
486 else
487 error( "internal error #92870653" );
488 }
489
490 /* Args for "predicate".
491 */
492 static BuiltinTypeSpot *pred_args[] = {
493 &any_spot
494 };
495
496 /* A predicate function ... name, implementation.
497 */
498 typedef struct {
499 const char *name; /* ip name */
500 gboolean (*fn)( Reduce *, PElement * ); /* Implementation */
501 } PredicateFn;
502
503 /* Table of predicate functions ... name and implementation.
504 */
505 static PredicateFn predicate_fn[] = {
506 { "is_image", &pe_is_image },
507 { "is_bool", &pe_is_bool },
508 { "is_real", &pe_is_real },
509 { "is_char", &pe_is_char },
510 { "is_class", &pe_is_class },
511 { "is_list", &pe_is_list },
512 { "is_complex", &pe_is_complex }
513 };
514
515 /* Do a predicate function (eg. is_bool)
516 */
517 static void
apply_pred_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)518 apply_pred_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
519 {
520 PElement rhs;
521 gboolean res;
522 int i;
523
524 /* Find implementation.
525 */
526 for( i = 0; i < IM_NUMBER( predicate_fn ); i++ )
527 if( strcmp( name, predicate_fn[i].name ) == 0 )
528 break;
529 if( i == IM_NUMBER( predicate_fn ) )
530 error( "internal error #928456936" );
531
532 /* Call!
533 */
534 PEPOINTRIGHT( arg[0], &rhs );
535 res = predicate_fn[i].fn( rc, &rhs );
536 PEPUTP( out, ELEMENT_BOOL, res );
537 }
538
539 /* Args for "error".
540 */
541 static BuiltinTypeSpot *error_args[] = {
542 &string_spot
543 };
544
545 /* Do "error".
546 */
547 static void
apply_error_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)548 apply_error_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
549 {
550 char buf[MAX_STRSIZE];
551 PElement rhs;
552
553 /* Get string.
554 */
555 PEPOINTRIGHT( arg[0], &rhs );
556 (void) reduce_get_string( rc, &rhs, buf, MAX_STRSIZE );
557
558 error_top( _( "Macro error." ) );
559 error_sub( "%s", buf );
560 reduce_throw( rc );
561 }
562
563 /* Args for "search".
564 */
565 static BuiltinTypeSpot *search_args[] = {
566 &string_spot
567 };
568
569 /* Do "search".
570 */
571 static void
apply_search_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)572 apply_search_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
573 {
574 char buf[MAX_STRSIZE];
575 PElement rhs;
576 char *fn;
577
578 /* Get string.
579 */
580 PEPOINTRIGHT( arg[0], &rhs );
581 (void) reduce_get_string( rc, &rhs, buf, MAX_STRSIZE );
582
583 if( !(fn = path_find_file( buf )) )
584 /* If not found, return [].
585 */
586 fn = im_strdup( NULL, "" );
587
588 if( !heap_managedstring_new( rc->heap, fn, out ) ) {
589 IM_FREE( fn );
590 reduce_throw( rc );
591 }
592 IM_FREE( fn );
593 }
594
595 /* Args for "print".
596 */
597 static BuiltinTypeSpot *print_args[] = {
598 &any_spot
599 };
600
601 /* Do "print".
602 */
603 static void
apply_print_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)604 apply_print_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
605 {
606 PElement rhs;
607 char txt[MAX_STRSIZE];
608 VipsBuf buf = VIPS_BUF_STATIC( txt );
609
610 PEPOINTRIGHT( arg[0], &rhs );
611 itext_value_ev( rc, &buf, &rhs );
612
613 if( !heap_managedstring_new( rc->heap, vips_buf_all( &buf ), out ) )
614 reduce_throw( rc );
615 }
616
617 /* Args for "dir".
618 */
619 static BuiltinTypeSpot *dir_args[] = {
620 &any_spot
621 };
622
623 static void *
dir_object_member(Symbol * sym,PElement * value,Reduce * rc,PElement * list)624 dir_object_member( Symbol *sym, PElement *value,
625 Reduce *rc, PElement *list )
626 {
627 PElement t;
628
629 if( !heap_list_add( rc->heap, list, &t ) ||
630 !heap_managedstring_new( rc->heap, IOBJECT( sym )->name, &t ) )
631 reduce_throw( rc );
632 (void) heap_list_next( list );
633
634 return( NULL );
635 }
636
637 static void *
dir_object(Reduce * rc,PElement * list,PElement * instance,PElement * out)638 dir_object( Reduce *rc, PElement *list, PElement *instance, PElement *out )
639 {
640 PElement p;
641
642 /* p walks down the list as we build it, list stays pointing at the
643 * head ready to be written to out.
644 */
645 p = *list;
646 heap_list_init( &p );
647 class_map( instance, (class_map_fn) dir_object_member, rc, &p );
648 PEPUTPE( out, list );
649
650 return( NULL );
651 }
652
653 static void *
dir_scope(Symbol * sym,Reduce * rc,PElement * list)654 dir_scope( Symbol *sym, Reduce *rc, PElement *list )
655 {
656 PElement t;
657
658 if( !heap_list_add( rc->heap, list, &t ) ||
659 !heap_managedstring_new( rc->heap, IOBJECT( sym )->name, &t ) )
660 reduce_throw( rc );
661 (void) heap_list_next( list );
662
663 return( NULL );
664 }
665
666 static void *
dir_gtype(GType type,void * a,void * b)667 dir_gtype( GType type, void *a, void *b )
668 {
669 Reduce *rc = (Reduce *) a;
670 PElement *list = (PElement *) b;
671 PElement t;
672
673 if( !heap_list_add( rc->heap, list, &t ) ||
674 !heap_real_new( rc->heap, type, &t ) )
675 return( rc );
676 (void) heap_list_next( list );
677
678 return( NULL );
679 }
680
681 static void
dir_gobject(Reduce * rc,GParamSpec ** properties,guint n_properties,PElement * out)682 dir_gobject( Reduce *rc,
683 GParamSpec **properties, guint n_properties, PElement *out )
684 {
685 int i;
686 PElement list;
687
688 list = *out;
689 heap_list_init( &list );
690
691 for( i = 0; i < n_properties; i++ ) {
692 PElement t;
693
694 if( !heap_list_add( rc->heap, &list, &t ) ||
695 !heap_managedstring_new( rc->heap,
696 properties[i]->name, &t ) )
697 reduce_throw( rc );
698 (void) heap_list_next( &list );
699 }
700 }
701
702 /* Do "dir".
703 */
704 static void
apply_dir_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)705 apply_dir_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
706 {
707 PElement rhs;
708
709 PEPOINTRIGHT( arg[0], &rhs );
710
711 if( PEISCLASS( &rhs ) )
712 /* This is more complex than it looks. We have to walk a class
713 * instance generating a list of member names, while not
714 * destroying the instance as we go, in the case that out will
715 * overwrite (rhs) arg[0].
716 */
717 reduce_safe_pointer( rc, (reduce_safe_pointer_fn) dir_object,
718 &rhs, out, NULL, NULL );
719 else if( PEISSYMREF( &rhs ) ) {
720 Symbol *sym = PEGETSYMREF( &rhs );
721
722 if( is_scope( sym ) && sym->expr && sym->expr->compile ) {
723 PElement list;
724
725 list = *out;
726 heap_list_init( &list );
727
728 icontainer_map( ICONTAINER( sym->expr->compile ),
729 (icontainer_map_fn) dir_scope, rc, &list );
730 }
731 }
732 else if( PEISREAL( &rhs ) ) {
733 /* Assume this is a gtype and try to get the children of that
734 * type.
735 */
736 GType type = PEGETREAL( &rhs );
737 PElement list;
738
739 list = *out;
740 heap_list_init( &list );
741
742 if( !g_type_name( type ) ) {
743 error_top( _( "No such type" ) );
744 error_sub( _( "GType %u not found." ),
745 (unsigned int) type );
746 reduce_throw( rc );
747 }
748
749 if( vips_type_map( type, dir_gtype, rc, &list ) )
750 reduce_throw( rc );
751 }
752 else if( PEISMANAGEDGOBJECT( &rhs ) ) {
753 guint n_properties;
754 ManagedgobjectClass *class =
755 MANAGEDGOBJECT_GET_CLASS( PEGETMANAGEDGOBJECT( &rhs ) );
756 GParamSpec **properties;
757
758 properties = g_object_class_list_properties(
759 G_OBJECT_CLASS( class ), &n_properties );
760 dir_gobject( rc, properties, n_properties, out );
761 g_free( properties);
762 }
763 else
764 /* Just [], ie. no names possible.
765 */
766 heap_list_init( out );
767 }
768
769 /* Args for "expand".
770 */
771 static BuiltinTypeSpot *expand_args[] = {
772 &string_spot
773 };
774
775 /* Do "expand".
776 */
777 static void
apply_expand_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)778 apply_expand_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out )
779 {
780 PElement rhs;
781 char txt[FILENAME_MAX];
782 char txt2[FILENAME_MAX];
783
784 PEPOINTRIGHT( arg[0], &rhs );
785 (void) reduce_get_string( rc, &rhs, txt, FILENAME_MAX );
786 expand_variables( txt, txt2 );
787
788 if( !heap_managedstring_new( rc->heap, txt2, out ) )
789 reduce_throw( rc );
790 }
791
792 /* Args for "name2gtype".
793 */
794 static BuiltinTypeSpot *name2gtype_args[] = {
795 &string_spot
796 };
797
798 /* Do "name2gtype".
799 */
800 static void
apply_name2gtype_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)801 apply_name2gtype_call( Reduce *rc, const char *name,
802 HeapNode **arg, PElement *out )
803 {
804 PElement rhs;
805 char txt[FILENAME_MAX];
806 int gtype;
807
808 PEPOINTRIGHT( arg[0], &rhs );
809 (void) reduce_get_string( rc, &rhs, txt, FILENAME_MAX );
810
811 gtype = g_type_from_name( txt );
812
813 if( !heap_real_new( rc->heap, gtype, out ) )
814 reduce_throw( rc );
815 }
816
817 /* Args for "gtype2name".
818 */
819 static BuiltinTypeSpot *gtype2name_args[] = {
820 &real_spot
821 };
822
823 /* Do "gtype2name".
824 */
825 static void
apply_gtype2name_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)826 apply_gtype2name_call( Reduce *rc, const char *name,
827 HeapNode **arg, PElement *out )
828 {
829 PElement rhs;
830 int gtype;
831
832 PEPOINTRIGHT( arg[0], &rhs );
833 gtype = PEGETREAL( &rhs );
834
835 if( !heap_managedstring_new( rc->heap, g_type_name( gtype ), out ) )
836 reduce_throw( rc );
837 }
838
839 /* Args for "vips_object_new".
840 */
841 static BuiltinTypeSpot *vo_new_args[] = {
842 &string_spot,
843 &list_spot,
844 &list_spot
845 };
846
847 /* Do a vips_object_new call.
848 */
849 static void
apply_vo_new_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)850 apply_vo_new_call( Reduce *rc,
851 const char *name, HeapNode **arg, PElement *out )
852 {
853 PElement rhs;
854 char buf[256];
855 PElement required;
856 PElement optional;
857
858 PEPOINTRIGHT( arg[2], &rhs );
859 reduce_get_string( rc, &rhs, buf, 256 );
860 PEPOINTRIGHT( arg[1], &required );
861 PEPOINTRIGHT( arg[0], &optional );
862
863 vo_object_new( rc, buf, &required, &optional, out );
864 }
865
866 /* Args for "vips_call".
867 */
868 static BuiltinTypeSpot *vo_call_args[] = {
869 &string_spot,
870 &list_spot,
871 &list_spot
872 };
873
874 /* Do a vips_call call.
875 */
876 static void
apply_vo_call_call(Reduce * rc,const char * name,HeapNode ** arg,PElement * out)877 apply_vo_call_call( Reduce *rc,
878 const char *name, HeapNode **arg, PElement *out )
879 {
880 PElement rhs;
881 char buf[256];
882 PElement required;
883 PElement optional;
884
885 PEPOINTRIGHT( arg[2], &rhs );
886 reduce_get_string( rc, &rhs, buf, 256 );
887 PEPOINTRIGHT( arg[1], &required );
888 PEPOINTRIGHT( arg[0], &optional );
889
890 vo_call( rc, buf, &required, &optional, out );
891 }
892
893 /* All ip's builtin functions.
894 */
895 static BuiltinInfo builtin_table[] = {
896 /* Other.
897 */
898 { "dir", N_( "return list of names of members" ),
899 FALSE, IM_NUMBER( dir_args ),
900 &dir_args[0], &apply_dir_call },
901 { "search", N_( "search for file" ),
902 FALSE, IM_NUMBER( search_args ),
903 &search_args[0], &apply_search_call },
904 { "error", N_( "raise error" ),
905 FALSE, IM_NUMBER( error_args ),
906 &error_args[0], &apply_error_call },
907 { "print", N_( "convert to [char]" ),
908 FALSE, IM_NUMBER( print_args ),
909 &print_args[0], &apply_print_call },
910 { "expand", N_( "expand environment variables" ),
911 FALSE, IM_NUMBER( expand_args ),
912 &expand_args[0], &apply_expand_call },
913 { "name2gtype", N_( "convert [char] to GType" ),
914 FALSE, IM_NUMBER( name2gtype_args ),
915 &name2gtype_args[0], &apply_name2gtype_call },
916 { "gtype2name", N_( "convert GType to [char]" ),
917 FALSE, IM_NUMBER( gtype2name_args ),
918 >ype2name_args[0], &apply_gtype2name_call },
919 { "_", N_( "look up localised string" ),
920 FALSE, IM_NUMBER( underscore_args ),
921 &underscore_args[0], &apply_underscore_call },
922
923 /* vips8 wrapper.
924 */
925 { "vips_object_new", N_( "create new vips8 object" ),
926 FALSE, IM_NUMBER( vo_new_args ),
927 &vo_new_args[0], apply_vo_new_call },
928 { "vips_call", N_( "call vips8 operator" ),
929 FALSE, IM_NUMBER( vo_call_args ),
930 &vo_call_args[0], apply_vo_call_call },
931
932 /* Predicates.
933 */
934 { "is_image", N_( "true if argument is primitive image" ),
935 FALSE, IM_NUMBER( pred_args ),
936 &pred_args[0], apply_pred_call },
937 { "is_bool", N_( "true if argument is primitive bool" ),
938 FALSE, IM_NUMBER( pred_args ),
939 &pred_args[0], apply_pred_call },
940 { "is_real", N_( "true if argument is primitive real number" ),
941 FALSE, IM_NUMBER( pred_args ),
942 &pred_args[0], apply_pred_call },
943 { "is_class", N_( "true if argument is class" ),
944 FALSE, IM_NUMBER( pred_args ),
945 &pred_args[0], apply_pred_call },
946 { "is_char", N_( "true if argument is primitive char" ),
947 FALSE, IM_NUMBER( pred_args ),
948 &pred_args[0], apply_pred_call },
949 { "is_list", N_( "true if argument is primitive list" ),
950 FALSE, IM_NUMBER( pred_args ),
951 &pred_args[0], apply_pred_call },
952 { "is_complex", N_( "true if argument is primitive complex" ),
953 FALSE, IM_NUMBER( pred_args ),
954 &pred_args[0], apply_pred_call },
955 { "is_instanceof", N_( "true if argument class instance of type" ),
956 FALSE, IM_NUMBER( is_instanceof_args ),
957 &is_instanceof_args[0], apply_is_instanceof_call },
958 { "has_member", N_( "true if class has named member" ),
959 FALSE, IM_NUMBER( has_member_args ),
960 &has_member_args[0], apply_has_member_call },
961
962 /* List and complex projections.
963 */
964 { "re", N_( "real part of complex" ),
965 TRUE, IM_NUMBER( complex_args ),
966 &complex_args[0], apply_complex_call },
967 { "im", N_( "imaginary part of complex" ),
968 TRUE, IM_NUMBER( complex_args ),
969 &complex_args[0], apply_complex_call },
970 { "hd", N_( "head of list" ),
971 TRUE, IM_NUMBER( flist_args ),
972 &flist_args[0], apply_list_call },
973 { "tl", N_( "tail of list" ),
974 TRUE, IM_NUMBER( flist_args ),
975 &flist_args[0], apply_list_call },
976
977 /* Math.
978 */
979 { "sin", N_( "sine of real number" ),
980 TRUE, IM_NUMBER( math_args ),
981 &math_args[0], apply_math_call },
982 { "cos", N_( "cosine of real number" ),
983 TRUE, IM_NUMBER( math_args ),
984 &math_args[0], apply_math_call },
985 { "tan", N_( "tangent of real number" ),
986 TRUE, IM_NUMBER( math_args ),
987 &math_args[0], apply_math_call },
988 { "asin", N_( "arc sine of real number" ),
989 TRUE, IM_NUMBER( math_args ),
990 &math_args[0], apply_math_call },
991 { "acos", N_( "arc cosine of real number" ),
992 TRUE, IM_NUMBER( math_args ),
993 &math_args[0], apply_math_call },
994 { "atan", N_( "arc tangent of real number" ),
995 TRUE, IM_NUMBER( math_args ),
996 &math_args[0], apply_math_call },
997 { "log", N_( "log base e of real number" ),
998 TRUE, IM_NUMBER( math_args ),
999 &math_args[0], apply_math_call },
1000 { "log10", N_( "log base 10 of real number" ),
1001 TRUE, IM_NUMBER( math_args ),
1002 &math_args[0], apply_math_call },
1003 { "exp", N_( "e to the power of real number" ),
1004 TRUE, IM_NUMBER( math_args ),
1005 &math_args[0], apply_math_call },
1006 { "exp10", N_( "10 to the power of real number" ),
1007 TRUE, IM_NUMBER( math_args ),
1008 &math_args[0], apply_math_call },
1009 { "ceil", N_( "real to int, rounding up" ),
1010 TRUE, IM_NUMBER( math_args ),
1011 &math_args[0], apply_math_call },
1012 { "floor", N_( "real to int, rounding down" ),
1013 TRUE, IM_NUMBER( math_args ),
1014 &math_args[0], apply_math_call },
1015
1016 /* Optional GSL funcs.
1017 */
1018 { "gammq", N_( "gamma function" ),
1019 TRUE, IM_NUMBER( gammq_args ),
1020 &gammq_args[0], apply_gammq_call },
1021
1022 /* Constructors.
1023 */
1024 { "vips_image", N_( "load vips image" ),
1025 FALSE, IM_NUMBER( image_args ),
1026 &image_args[0], apply_image_call },
1027 { "read", N_( "load text file" ),
1028 FALSE, IM_NUMBER( read_args ),
1029 &read_args[0], apply_read_call },
1030 { "graph_export_image", N_( "generate image from Plot object" ),
1031 FALSE, IM_NUMBER( graph_export_image_args ),
1032 &graph_export_image_args[0], apply_graph_export_image_call },
1033
1034 };
1035
1036 #ifdef HAVE_GSL
1037 static void
builtin_gsl_error(const char * reason,const char * file,int line,int gsl_errno)1038 builtin_gsl_error( const char *reason, const char *file,
1039 int line, int gsl_errno )
1040 {
1041 error_top( _( "GSL library error." ) );
1042 error_sub( "%s - (%s:%d) - %s",
1043 reason, file, line, gsl_strerror( gsl_errno ) );
1044
1045 reduce_throw( reduce_context );
1046 }
1047 #endif /*HAVE_GSL*/
1048
1049 void
builtin_init(void)1050 builtin_init( void )
1051 {
1052 Toolkit *kit;
1053 int i;
1054
1055 /* Make the _builtin toolkit and populate.
1056 */
1057 kit = toolkit_new( main_toolkitgroup, "_builtin" );
1058
1059 for( i = 0; i < IM_NUMBER( builtin_table ); i++ ) {
1060 Symbol *sym;
1061
1062 sym = symbol_new( symbol_root->expr->compile,
1063 builtin_table[i].name );
1064 g_assert( sym->type == SYM_ZOMBIE );
1065 sym->type = SYM_BUILTIN;
1066 sym->builtin = &builtin_table[i];
1067 (void) tool_new_sym( kit, -1, sym );
1068 symbol_made( sym );
1069 }
1070
1071 filemodel_set_auto_load( FILEMODEL( kit ) );
1072 filemodel_set_modified( FILEMODEL( kit ), FALSE );
1073 kit->pseudo = TRUE;
1074
1075 /* Start up GSL, if we have it.
1076 */
1077 #ifdef HAVE_GSL
1078 gsl_set_error_handler( builtin_gsl_error );
1079 #endif /*HAVE_GSL*/
1080 }
1081
1082 /* Make a usage error.
1083 */
1084 void
builtin_usage(VipsBuf * buf,BuiltinInfo * builtin)1085 builtin_usage( VipsBuf *buf, BuiltinInfo *builtin )
1086 {
1087 int i;
1088
1089 vips_buf_appendf( buf,
1090 ngettext( "Builtin \"%s\" takes %d argument.",
1091 "Builtin \"%s\" takes %d arguments.",
1092 builtin->nargs ),
1093 builtin->name, builtin->nargs );
1094 vips_buf_appends( buf, "\n" );
1095
1096 for( i = 0; i < builtin->nargs; i++ )
1097 vips_buf_appendf( buf, " %d - %s\n",
1098 i + 1,
1099 builtin->args[i]->name );
1100 }
1101
1102 #ifdef DEBUG
1103 static void
builtin_trace_args(Heap * heap,const char * name,int n,HeapNode ** arg)1104 builtin_trace_args( Heap *heap, const char *name, int n, HeapNode **arg )
1105 {
1106 int i;
1107 char txt[100];
1108 VipsBuf buf = VIPS_BUF_STATIC( txt );
1109
1110 for( i = 0; i < n; i++ ) {
1111 PElement t;
1112
1113 PEPOINTRIGHT( arg[n - i - 1], &t );
1114 vips_buf_appends( &buf, "(" );
1115 graph_pelement( heap, &buf, &t, FALSE );
1116 vips_buf_appends( &buf, ") " );
1117 }
1118
1119 printf( "builtin: %s %s\n", name, vips_buf_all( &buf ) );
1120 }
1121 #endif /*DEBUG*/
1122
1123 /* Execute the internal implementation of a builtin function.
1124 */
1125 void
builtin_run(Reduce * rc,Compile * compile,int op,const char * name,HeapNode ** arg,PElement * out,BuiltinInfo * builtin)1126 builtin_run( Reduce *rc, Compile *compile,
1127 int op, const char *name, HeapNode **arg, PElement *out,
1128 BuiltinInfo *builtin )
1129 {
1130 int i;
1131
1132 /* Typecheck args.
1133 */
1134 for( i = 0; i < builtin->nargs; i++ ) {
1135 BuiltinTypeSpot *ts = builtin->args[i];
1136 PElement base;
1137
1138 PEPOINTRIGHT( arg[builtin->nargs - i - 1], &base );
1139 if( !ts->pred( rc, &base ) ) {
1140 char txt[100];
1141 VipsBuf buf = VIPS_BUF_STATIC( txt );
1142
1143 itext_value_ev( rc, &buf, &base );
1144 error_top( _( "Bad argument." ) );
1145 error_sub( _( "Argument %d to builtin \"%s\" should "
1146 "be \"%s\", you passed:\n %s" ),
1147 i + 1, name, ts->name,
1148 vips_buf_all( &buf ) );
1149 reduce_throw( rc );
1150 }
1151 }
1152
1153 #ifdef DEBUG
1154 builtin_trace_args( rc->heap, name, builtin->nargs, arg );
1155 #endif /*DEBUG*/
1156
1157 builtin->fn( rc, name, arg, out );
1158 }
1159