1 /* Expressions!
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 /* Trace error_set()/_clear().
31 #define DEBUG_ERROR
32 */
33
34 /* Trace expr_clone()
35 #define DEBUG_CLONE
36 */
37
38 /*
39 #define DEBUG
40 */
41
42 #include "ip.h"
43
44 /* Our signals.
45 */
46 enum {
47 SIG_NEW_VALUE, /* new value for root */
48 SIG_LAST
49 };
50
51 static iContainerClass *parent_class = NULL;
52
53 static guint expr_signals[SIG_LAST] = { 0 };
54
55 /* Set of expressions containing errors.
56 */
57 GSList *expr_error_all = NULL;
58
59 void *
expr_error_print(Expr * expr,VipsBuf * buf)60 expr_error_print( Expr *expr, VipsBuf *buf )
61 {
62 g_assert( expr->err );
63
64 vips_buf_appendf( buf, _( "error in \"%s\"" ),
65 IOBJECT( expr->sym )->name );
66 if( expr->sym->tool )
67 tool_error( expr->sym->tool, buf );
68 else if( expr->row ) {
69 Workspace *ws = expr->row->ws;
70 Workspacegroup *wsg = workspace_get_workspacegroup( ws );
71
72 vips_buf_appendf( buf, " (" );
73 row_qualified_name( expr->row, buf );
74 if( FILEMODEL( wsg )->filename )
75 vips_buf_appendf( buf, " - %s",
76 FILEMODEL( wsg )->filename );
77 vips_buf_appendf( buf, ")" );
78 }
79
80 /* Don't show error_top, it's just a summary of error_sub.
81 */
82 vips_buf_appendf( buf, ": %s\n", expr->error_sub );
83
84 return( NULL );
85 }
86
87 static Expr *
expr_map_all_sub(Symbol * sym,map_expr_fn fn,void * a)88 expr_map_all_sub( Symbol *sym, map_expr_fn fn, void *a )
89 {
90 if( !sym->expr )
91 return( NULL );
92 else
93 return( expr_map_all( sym->expr, fn, a ) );
94 }
95
96 /* Apply a function to a expr ... and any local exprs.
97 */
98 Expr *
expr_map_all(Expr * expr,map_expr_fn fn,void * a)99 expr_map_all( Expr *expr, map_expr_fn fn, void *a )
100 {
101 Expr *res;
102
103 /* Apply to this expr.
104 */
105 if( (res = fn( expr, a, NULL )) )
106 return( res );
107
108 /* And over any locals.
109 */
110 if( expr->compile && (res = (Expr *)
111 icontainer_map( ICONTAINER( expr->compile ),
112 (icontainer_map_fn) expr_map_all_sub,
113 (void *) fn, a )) )
114 return( res );
115
116 return( NULL );
117 }
118
119 void *
expr_name_print(Expr * expr)120 expr_name_print( Expr *expr )
121 {
122 printf( "expr(%p) ", expr );
123 symbol_name_print( expr->sym );
124
125 if( expr->row ) {
126 printf( "(row " );
127 row_name_print( expr->row );
128 printf( ") " );
129 }
130
131 return( NULL );
132 }
133
134 void
expr_name(Expr * expr,VipsBuf * buf)135 expr_name( Expr *expr, VipsBuf *buf )
136 {
137 if( expr->row )
138 row_qualified_name( expr->row, buf );
139 else
140 symbol_qualified_name( expr->sym, buf );
141 }
142
143 Expr *
expr_get_parent(Expr * expr)144 expr_get_parent( Expr *expr )
145 {
146 Symbol *sym_parent = symbol_get_parent( expr->sym );
147
148 if( !sym_parent )
149 return( NULL );
150
151 return( sym_parent->expr );
152 }
153
154 /* Find the enclosing expr in the dynamic scope hierarchy.
155 */
156 static Expr *
expr_get_parent_dynamic(Expr * expr)157 expr_get_parent_dynamic( Expr *expr )
158 {
159 Row *row;
160
161 if( !expr->row )
162 return( expr_get_parent( expr ) );
163 else if( (row = HEAPMODEL( expr->row )->row) )
164 /* Enclosing row expr.
165 */
166 return( row->expr );
167 else {
168 /* Enclosing workspace expr.
169 */
170 Workspace *ws = expr->row->top_col->ws;
171
172 return( ws->sym->expr );
173 }
174 }
175
176 /* Look back up to find the root expr.
177 */
178 Expr *
expr_get_root(Expr * expr)179 expr_get_root( Expr *expr )
180 {
181 if( is_top( expr->sym ) )
182 return( expr );
183 else
184 return( expr_get_root( expr_get_parent( expr ) ) );
185 }
186
187 /* Look back up to find the root expr using the dynamic hierarchy (if it's
188 * there).
189 */
190 Expr *
expr_get_root_dynamic(Expr * expr)191 expr_get_root_dynamic( Expr *expr )
192 {
193 Expr *parent;
194
195 if( is_top( expr->sym ) )
196 return( expr );
197 else if( expr->row && expr->row->top_row && expr->row->top_row->expr )
198 return( expr->row->top_row->expr );
199 else if( (parent = expr_get_parent_dynamic( expr )) )
200 return( expr_get_root_dynamic( parent ) );
201 else
202 return( NULL );
203 }
204
205 /* Is an expr part of a row, including enclosing exprs.
206 *
207 * For example, row A1 could be "[x::x<-A2]", that would be expanded to
208 * something like
209 * "$lcomp0 {$lcomp0 = foldr $f0 [] A2 {$f0 x $sofar = x : $sofar}}"
210 * Now, row A1 depends on A2, but expr A1 will not ... it's $lcomp0, the local
211 * expr of A1, that will get called for expr_dirty.
212 *
213 * Return NULL for expr is not a row and has no enclosing rows.
214 */
215 static Row *
expr_get_row(Expr * expr)216 expr_get_row( Expr *expr )
217 {
218 if( expr->row )
219 return( expr->row );
220 else if( is_top( expr->sym ) )
221 return( NULL );
222 else
223 return( expr_get_row( expr_get_parent( expr ) ) );
224 }
225
226 void
expr_new_value(Expr * expr)227 expr_new_value( Expr *expr )
228 {
229 #ifdef DEBUG
230 {
231 PElement *root = &expr->root;
232
233 printf( "expr_new_value: " );
234 symbol_name_print( expr->sym );
235 printf( ": " );
236 graph_pointer( root );
237 }
238 #endif /*DEBUG*/
239
240 g_signal_emit( G_OBJECT( expr ), expr_signals[SIG_NEW_VALUE], 0 );
241 }
242
243 /* An expr has lost a value.
244 */
245 void
expr_value_destroy(Expr * expr)246 expr_value_destroy( Expr *expr )
247 {
248 /* Break ImageInfo link (if any).
249 */
250 if( expr->imageinfo )
251 imageinfo_expr_remove( expr, expr->imageinfo );
252 }
253
254 /* Clean up an expr, ready to have a new def parsed into it.
255 */
256 void *
expr_strip(Expr * expr)257 expr_strip( Expr *expr )
258 {
259 expr_error_clear( expr );
260
261 /* Break top links we're part of.
262 */
263 if( slist_map( expr->static_links,
264 (SListMapFn) link_expr_destroy, NULL ) )
265 return( expr );
266 if( slist_map( expr->dynamic_links,
267 (SListMapFn) link_expr_destroy, NULL ) )
268 return( expr );
269 g_assert( !expr->static_links );
270 g_assert( !expr->dynamic_links );
271
272 /* Junk error stuff.
273 */
274 IM_FREE( expr->error_top );
275 IM_FREE( expr->error_sub );
276
277 /* Unref the compile.
278 */
279 if( expr->compile )
280 (void) compile_expr_link_break( expr->compile, expr );
281
282 return( NULL );
283 }
284
285 static void
expr_dispose(GObject * gobject)286 expr_dispose( GObject *gobject )
287 {
288 Expr *expr = EXPR( gobject );
289 Symbol *sym = expr->sym;
290
291 #ifdef DEBUG
292 printf( "expr_dispose: " );
293 expr_name_print( expr );
294 printf( "\n" );
295 #endif /*DEBUG*/
296
297 expr_strip( expr );
298
299 /* Break the value link.
300 */
301 expr_value_destroy( expr );
302
303 /* Unlink from symbol.
304 */
305 if( sym->expr == expr )
306 sym->expr = NULL;
307
308 if( expr->row ) {
309 Row *row = expr->row;
310
311 /* If this is the sym for a top row, kill the row too.
312 * Otherwise just break the link and wait for the next row
313 * refresh to do the kill for us.
314 */
315 if( row == row->top_row ) {
316 IDESTROY( row );
317 }
318 else {
319 row->expr = NULL;
320 row->sym = NULL;
321
322 expr->row = NULL;
323
324 /* Make sure we will re-parse and compile any text
325 * with this sym that might have been modified from
326 * the default.
327 */
328 if( row->child_rhs && row->child_rhs->itext ) {
329 iText *itext = ITEXT( row->child_rhs->itext );
330
331 if( itext->edited )
332 heapmodel_set_modified(
333 HEAPMODEL( itext ), TRUE );
334 }
335 }
336 }
337
338 G_OBJECT_CLASS( parent_class )->dispose( gobject );
339 }
340
341 static void
expr_info(iObject * iobject,VipsBuf * buf)342 expr_info( iObject *iobject, VipsBuf *buf )
343 {
344 Expr *expr = EXPR( iobject );
345
346 if( expr->err ) {
347 vips_buf_appends( buf, _( "Error" ) );
348 vips_buf_appendf( buf, ": %s\n%s\n",
349 expr->error_top, expr->error_sub );
350 }
351 }
352
353 static void
expr_real_new_value(Expr * expr)354 expr_real_new_value( Expr *expr )
355 {
356 PElement *root = &expr->root;
357
358 expr_value_destroy( expr );
359 if( PEISIMAGE( root ) && PEGETII( root ) )
360 imageinfo_expr_add( PEGETII( root ), expr );
361
362 /* If this is the main expr for this symbol, signal new value there
363 * too.
364 */
365 if( expr->sym->expr == expr )
366 symbol_new_value( expr->sym );
367 }
368
369 static void
expr_class_init(ExprClass * class)370 expr_class_init( ExprClass *class )
371 {
372 GObjectClass *gobject_class = (GObjectClass *) class;
373 iObjectClass *iobject_class = (iObjectClass *) class;
374
375 parent_class = g_type_class_peek_parent( class );
376
377 /* Create signals.
378 */
379 expr_signals[SIG_NEW_VALUE] = g_signal_new( "new_value",
380 G_OBJECT_CLASS_TYPE( gobject_class ),
381 G_SIGNAL_RUN_FIRST,
382 G_STRUCT_OFFSET( ExprClass, new_value ),
383 NULL, NULL,
384 g_cclosure_marshal_VOID__VOID,
385 G_TYPE_NONE, 0 );
386
387 /* Init methods.
388 */
389 gobject_class->dispose = expr_dispose;
390
391 iobject_class->info = expr_info;
392
393 class->new_value = expr_real_new_value;
394
395 /* Static init.
396 */
397 }
398
399 static void
expr_init(Expr * expr)400 expr_init( Expr *expr )
401 {
402 expr->sym = NULL;
403 expr->row = NULL;
404 expr->compile = NULL;
405
406 expr->static_links = NULL;
407 expr->dynamic_links = NULL;
408
409 expr->imageinfo = NULL;
410
411 expr->err = FALSE;
412 expr->error_top = NULL;
413 expr->error_sub = NULL;
414 }
415
416 GType
expr_get_type(void)417 expr_get_type( void )
418 {
419 static GType type = 0;
420
421 if( !type ) {
422 static const GTypeInfo info = {
423 sizeof( ExprClass ),
424 NULL, /* base_init */
425 NULL, /* base_finalize */
426 (GClassInitFunc) expr_class_init,
427 NULL, /* class_finalize */
428 NULL, /* class_data */
429 sizeof( Expr ),
430 32, /* n_preallocs */
431 (GInstanceInitFunc) expr_init,
432 };
433
434 type = g_type_register_static( TYPE_ICONTAINER,
435 "Expr", &info, 0 );
436 }
437
438 return( type );
439 }
440
441 Expr *
expr_new(Symbol * sym)442 expr_new( Symbol *sym )
443 {
444 Expr *expr;
445
446 expr = EXPR( g_object_new( TYPE_EXPR, NULL ) );
447
448 expr->sym = sym;
449 PEPOINTE( &expr->root, &sym->base );
450 icontainer_child_add( ICONTAINER( sym ), ICONTAINER( expr ), -1 );
451
452 #ifdef DEBUG
453 printf( "expr_new: " );
454 expr_name_print( expr );
455 printf( "\n" );
456 #endif /*DEBUG*/
457
458 return( expr );
459 }
460
461
462 /* Clone an existing expr.
463 */
464 Expr *
expr_clone(Symbol * sym)465 expr_clone( Symbol *sym )
466 {
467 Expr *expr;
468
469 if( sym->expr && sym->expr->compile ) {
470 /* Make a new expr, share the compile.
471 */
472 expr = expr_new( sym );
473 compile_expr_link_make( sym->expr->compile, expr );
474 }
475 else {
476 /* No existing expr to copy, make a bare one for the
477 * row, at the same scope level as sym.
478 */
479 expr = expr_new( sym );
480 }
481
482 return( expr );
483 }
484
485 /* Mark an expression as containing an error, save the error buffers.
486 */
487 void *
expr_error_set(Expr * expr)488 expr_error_set( Expr *expr )
489 {
490 /* Was not in error? Add to error set.
491 */
492 if( !expr->err ) {
493 #ifdef DEBUG_ERROR
494 printf( "expr_error_set: error in " );
495 symbol_name_print( expr->sym );
496 printf( ": %s %s\n", error_get_top(), error_get_sub() );
497 #endif /*DEBUG_ERROR*/
498
499 IM_SETSTR( expr->error_top, error_get_top() );
500 IM_SETSTR( expr->error_sub, error_get_sub() );
501
502 /* Zap the value of the expr ... it may contain pointers to
503 * dead stuff.
504 */
505 PEPUTP( &expr->root, ELEMENT_NOVAL, (void *) 99 );
506
507 expr_error_all = g_slist_prepend( expr_error_all, expr );
508 expr->err = TRUE;
509 if( expr->row )
510 row_error_set( expr->row );
511
512 /* If this is the value of a top-level sym, note state
513 * change on symbol.
514 */
515 if( is_top( expr->sym ) && expr->sym->expr == expr )
516 symbol_state_change( expr->sym );
517 }
518
519 return( NULL );
520 }
521
522 /* Extract the error from an expression.
523 */
524 void
expr_error_get(Expr * expr)525 expr_error_get( Expr *expr )
526 {
527 if( !expr->err )
528 error_clear();
529 else {
530 g_assert( expr->error_top );
531 g_assert( expr->error_sub );
532
533 error_top( "%s", expr->error_top );
534 error_sub( "%s", expr->error_sub );
535 }
536 }
537
538 /* Clear error state.
539 */
540 void
expr_error_clear(Expr * expr)541 expr_error_clear( Expr *expr )
542 {
543 if( expr->err ) {
544 #ifdef DEBUG_ERROR
545 printf( "expr_error_clear: " );
546 symbol_name_print( expr->sym );
547 printf( "\n" );
548 #endif /*DEBUG_ERROR*/
549
550 expr->err = FALSE;
551 expr_error_all = g_slist_remove( expr_error_all, expr );
552 if( expr->row )
553 row_error_clear( expr->row );
554
555 if( is_top( expr->sym ) && expr->sym->expr == expr )
556 symbol_state_change( expr->sym );
557 }
558 }
559
560 /* Mark an expr dirty.
561 *
562 * Two cases: if expr has a row, this is part of a display. Use the row
563 * stuff to mark this expr dirty. Then use symbol_dirty() to mark on from the
564 * root of this row.
565 *
566 * Case two: this must be an expr inside a top-level ... just
567 * symbol_dirty() on from that top level.
568 *
569 * FIXME ... we should be able to scrap this expr_get_root() ... we want the
570 * 'parent' field in the Link we are probably being called from.
571 */
572 void *
expr_dirty(Expr * expr,int serial)573 expr_dirty( Expr *expr, int serial )
574 {
575 Row *row;
576
577 #ifdef DEBUG
578 printf( "expr_dirty: " );
579 symbol_name_print( expr->sym );
580 printf( "\n" );
581 #endif /*DEBUG*/
582
583 if( (row = expr_get_row( expr )) &&
584 row->top_row->sym ) {
585 Symbol *top_sym = row->top_row->sym;
586
587 row_dirty( row, TRUE );
588 symbol_dirty( top_sym, serial );
589 }
590 else
591 symbol_dirty( expr_get_root( expr )->sym, serial );
592
593 return( NULL );
594 }
595
596 void *
expr_dirty_intrans(Expr * expr,int serial)597 expr_dirty_intrans( Expr *expr, int serial )
598 {
599 if( expr->row &&
600 expr->row->top_row->sym ) {
601 row_dirty_intrans( expr->row, TRUE );
602 symbol_dirty( expr->row->top_row->sym, serial );
603 }
604 else
605 symbol_dirty_intrans( expr->sym, serial );
606
607 return( NULL );
608 }
609
610 void
expr_tip_sub(Expr * expr,VipsBuf * buf)611 expr_tip_sub( Expr *expr, VipsBuf *buf )
612 {
613 Compile *compile = expr->compile;
614
615 if( is_top( expr->sym ) ) {
616 vips_buf_appends( buf, _( "top level" ) );
617 vips_buf_appends( buf, " " );
618 }
619
620 if( compile &&
621 is_class( compile ) ) {
622 vips_buf_appends( buf, _( "class" ) );
623 vips_buf_appends( buf, " " );
624 if( compile->nparam == 0 ) {
625 vips_buf_appends( buf, _( "instance" ) );
626 vips_buf_appends( buf, " " );
627 }
628 else {
629 vips_buf_appends( buf, _( "definition" ) );
630 vips_buf_appends( buf, " " );
631 }
632
633 vips_buf_appendf( buf, "\"%s\"", IOBJECT( expr->sym )->name );
634 }
635 else if( expr->sym->type == SYM_PARAM )
636 vips_buf_appendf( buf, _( "parameter \"%s\"" ),
637 IOBJECT( expr->sym )->name );
638 else if( compile ) {
639 if( is_member( expr->sym ) ) {
640 vips_buf_appends( buf, _( "member" ) );
641 vips_buf_appends( buf, " " );
642 }
643
644 if( compile->nparam == 0 ) {
645 vips_buf_appends( buf, _( "value" ) );
646 vips_buf_appends( buf, " " );
647 }
648 else {
649 vips_buf_appends( buf, _( "function" ) );
650 vips_buf_appends( buf, " " );
651 }
652
653 vips_buf_appendf( buf, "\"%s\"", IOBJECT( expr->sym )->name );
654 }
655
656 if( !is_top( expr->sym ) ) {
657 vips_buf_appends( buf, " " );
658 vips_buf_appends( buf, _( "of" ) );
659 vips_buf_appends( buf, " " );
660 expr_tip_sub( expr_get_parent( expr ), buf );
661 }
662 }
663
664 /* Look at an expr, make a tooltip.
665 */
666 void
expr_tip(Expr * expr,VipsBuf * buf)667 expr_tip( Expr *expr, VipsBuf *buf )
668 {
669 expr_name( expr, buf );
670 vips_buf_appends( buf, ": " );
671 expr_tip_sub( expr, buf );
672 }
673
674 /* Bind unresolved refs in an expr. Bind for every enclosing dynamic scope.
675 */
676 void
expr_resolve(Expr * expr)677 expr_resolve( Expr *expr )
678 {
679 Expr *top = symbol_root->expr;
680 Expr *i;
681
682 #ifdef DEBUG
683 printf( "expr_resolve: " );
684 expr_name_print( expr );
685 printf( "\n" );
686 #endif /*DEBUG*/
687
688 for( i = expr; i != top; i = expr_get_parent_dynamic( i ) )
689 /* May try to resolve out through a parameter.
690 */
691 if( i->compile )
692 compile_resolve_dynamic( expr->compile, i->compile );
693 }
694