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 		&gtype2name_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