]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/expr.cxx
Initial revision
[karo-tx-redboot.git] / tools / src / libcdl / expr.cxx
1 //{{{  Banner                           
2
3 //============================================================================
4 //
5 //      expr.cxx
6 //
7 //      Implementation of the various CDL expression classes.
8 //
9 //============================================================================
10 //####COPYRIGHTBEGIN####
11 //                                                                          
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
14 //
15 // This file is part of the eCos host tools.
16 //
17 // This program is free software; you can redistribute it and/or modify it 
18 // under the terms of the GNU General Public License as published by the Free 
19 // Software Foundation; either version 2 of the License, or (at your option) 
20 // any later version.
21 // 
22 // This program is distributed in the hope that it will be useful, but WITHOUT 
23 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
24 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
25 // more details.
26 // 
27 // You should have received a copy of the GNU General Public License along with
28 // this program; if not, write to the Free Software Foundation, Inc., 
29 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30 //
31 // ----------------------------------------------------------------------------
32 //                                                                          
33 //####COPYRIGHTEND####
34 //============================================================================
35 //#####DESCRIPTIONBEGIN####
36 //
37 // Author(s):   bartv
38 // Contact(s):  bartv
39 // Date:        1999/02/02
40 // Version:     0.02
41 //
42 //####DESCRIPTIONEND####
43 //============================================================================
44
45 //}}}
46 //{{{  #include's                       
47
48 // ----------------------------------------------------------------------------
49 #include "cdlconfig.h"
50
51 // Get the infrastructure types, assertions, tracing and similar
52 // facilities.
53 #include <cyg/infra/cyg_ass.h>
54 #include <cyg/infra/cyg_trac.h>
55
56 // <cdlcore.hxx> defines everything implemented in this module.
57 // It implicitly supplies <string>, <vector> and <map> because
58 // the class definitions rely on these headers.
59 #include <cdlcore.hxx>
60
61 //}}}
62
63 //{{{  Statics                          
64
65 // ----------------------------------------------------------------------------
66 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlEvalContext);
67 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlExpressionBody);
68 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlListExpressionBody);
69 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlGoalExpressionBody);
70
71 //}}}
72 //{{{  CdlEvalContext                   
73
74 // ----------------------------------------------------------------------------
75 // A utility class to keep track of the context in which expression
76 // evaluation is happening.
77
78 CdlEvalContext::CdlEvalContext(CdlTransaction transaction_arg, CdlNode node_arg, CdlProperty property_arg,
79                                CdlToplevel toplevel_arg)
80 {
81     CYG_REPORT_FUNCNAME("CdlEvalContext::constructor");
82     CYG_REPORT_FUNCARG4XV(this, transaction_arg, node_arg, property_arg);
83
84     transaction = transaction_arg;
85     
86     if ((0 == property_arg) && (0 != transaction)) {
87         CdlConflict conflict = transaction->get_conflict();
88         if (0 != conflict) {
89             property_arg = conflict->get_property();
90         }
91     }
92     property    = property_arg;
93     
94     if ((0 == node_arg) && (0 != transaction)) {
95         CdlConflict conflict = transaction->get_conflict();
96         if (0 != conflict) {
97             node_arg = conflict->get_node();
98         }
99     }
100     node        = node_arg;
101     
102     if (0 == toplevel_arg) {
103         if (0 != transaction) {
104             toplevel_arg = transaction->get_toplevel();
105         } else if (0 != node) {
106             toplevel_arg = node->get_toplevel();
107         }
108     }
109     toplevel = toplevel_arg;
110     
111     cdlevalcontext_cookie = CdlEvalContext_Magic;
112     CYGDBG_MEMLEAK_CONSTRUCTOR();
113
114     CYG_POSTCONDITION_THISC();
115     CYG_REPORT_RETURN();
116 }
117
118 CdlEvalContext::~CdlEvalContext()
119 {
120     CYG_REPORT_FUNCNAME("CdlEvalContext::destructor");
121     CYG_PRECONDITION_THISC();
122
123     cdlevalcontext_cookie       = CdlEvalContext_Invalid;
124     transaction = 0;
125     node        = 0;
126     property    = 0;
127     toplevel    = 0;
128     CYGDBG_MEMLEAK_DESTRUCTOR();
129     
130     CYG_REPORT_RETURN();
131 }
132
133 // Given a context and a reference inside an expression, obtain the node
134 // being referenced - if it is loaded.
135 CdlNode
136 CdlEvalContext::resolve_reference(CdlExpression expr, int index)
137 {
138     CYG_REPORT_FUNCNAMETYPE("CdlEvalContext::resolve_reference", "result %");
139     CYG_REPORT_FUNCARG2XV(expr, index);
140     CYG_PRECONDITION_THISC();
141     CYG_PRECONDITION_CLASSC(expr);
142     CYG_PRECONDITIONC((0 <= index) && (index <= (int)expr->references.size()));
143
144     // This expression may be happening in the context of a particular
145     // property. If so then the destination may or may not be
146     // resolved, which will have been handled when the containing package
147     // was loaded. Alternatively this expression may be evaluated inside
148     // some arbitrary Tcl code, in which case references remain unbound
149     // and need to be resolved the hard way.
150     CdlNode result = 0;
151     if (0 != this->property) {
152         // There is a property, use the bound/unbound reference.
153         result = expr->references[index].get_destination();
154     } else {
155         // The destination name can be retrieved, but we still need some
156         // way of resolving it.
157         if (0 != this->toplevel) {
158             std::string destination_name = expr->references[index].get_destination_name();
159             result = this->toplevel->lookup(destination_name);
160         }
161     }
162     
163     CYG_REPORT_RETVAL(result);
164     return result;
165 }
166
167 // Ditto, but also check that the result is a valuable.
168 CdlValuable
169 CdlEvalContext::resolve_valuable_reference(CdlExpression expr, int index)
170 {
171     CYG_REPORT_FUNCNAMETYPE("CdlEvalContext::resolve_reference", "result %");
172     CYG_REPORT_FUNCARG2XV(expr, index);
173
174     CdlValuable result  = 0;
175     CdlNode     node = this->resolve_reference(expr, index);
176     if (0 != node) {
177         result = dynamic_cast<CdlValuable>(node);
178     }
179     CYG_REPORT_RETVAL(result);
180     return result;
181 }
182
183 bool
184 CdlEvalContext::check_this(cyg_assert_class_zeal zeal) const
185 {
186     if (CdlEvalContext_Magic != cdlevalcontext_cookie) {
187         return false;
188     }
189     CYGDBG_MEMLEAK_CHECKTHIS();
190     
191     if ((0 != transaction) && !transaction->check_this(zeal)) {
192         return false;
193     }
194     if ((0 != toplevel) && !toplevel->check_this(zeal)) {
195         return false;
196     }
197     if ((0 != node) && !node->check_this(zeal)) {
198         return false;
199     }
200     if ((0 != property) && !property->check_this(zeal)) {
201         return false;
202     }
203     return true;
204 }
205
206 //}}}
207 //{{{  Expression parsing               
208
209 //{{{  Description                      
210
211 // ----------------------------------------------------------------------------
212 // There are a number of different entry points related to expression parsing,
213 // largely to support list and goal expressions. All of these eventually
214 // end up calling the function
215 //    continue_parse(expr, data, index, token, token_end)
216 //
217 // The expr argument holds an existing expression object that needs to be
218 // updated. If token is Invalid then we are at the start of an expression
219 // (but not necessarily at the start of the string).
220 //
221 // The data string holds all of the expression that should be parsed.
222 // It is formed by concatenating all non-option arguments to the
223 // appropriate property command, with spaces between them.
224 //
225 // index is an input/output variable. On input it indicates where in
226 // the string parsing should continue. On output it indicates the
227 // location within the string where the terminating token began.
228 //
229 // token is an input/output variable. On input it can have the values
230 // Invalid or And. The former means that we are parsing a completely
231 // new expression. The latter is used for goal expressions: it is
232 // necessary to parse a new expression and then combine it with the
233 // existing one.
234 //
235 // token_end is an output variable. It indicates the location within
236 // the string where the terminating token ended. This is useful for
237 // e.g. ranges in a list expression.
238 //
239 // A conventional recursive descent parser is used.
240
241 //}}}
242 //{{{  Tokenization                     
243
244 // ----------------------------------------------------------------------------
245 // Tokenization.
246
247 //{{{  token enum                       
248
249 // A separate token enum is necessary, rather than re-using the CdlExprOp
250 // enum. Some tokens may correspond to several operators, and some tokens
251 // such as close-bracket do not correspond directly to an operator at all.
252 enum token {
253     T_Invalid           = -2,
254
255     T_EOD               = -1,
256     T_Reference         =  1,   // CYGPKG_HAL
257     T_String            =  2,   // "hello"
258     T_Integer           =  3,   // 123
259     T_Double            =  4,   // 3.1415
260     T_Range             =  5,   // to
261     T_OpenBracket       =  6,   // (
262     T_CloseBracket      =  7,   // )
263     T_Minus             =  8,   // -
264     T_Plus              =  9,   // +
265     T_Times             = 10,   // *
266     T_Divide            = 11,   // /
267     T_Exclamation       = 12,   // !
268     T_Tilde             = 13,   // ~
269     T_Questionmark      = 14,   // ?
270     T_Remainder         = 15,   // %
271     T_LeftShift         = 16,   // <<
272     T_RightShift        = 17,   // >>
273     T_LessThan          = 18,   // <
274     T_LessEqual         = 19,   // <=
275     T_GreaterThan       = 20,   // >
276     T_GreaterEqual      = 21,   // >=
277     T_Equal             = 22,   // ==
278     T_NotEqual          = 23,   // !=
279     T_BitAnd            = 24,   // &
280     T_BitXor            = 25,   // ^
281     T_BitOr             = 26,   // |
282     T_And               = 27,   // &&
283     T_Or                = 28,   // ||
284     T_Colon             = 29,   // : (in a conditional)
285     T_StringConcat      = 30,   // .
286     T_Function          = 31,   // is_substr etc.
287     T_Comma             = 32,   // , (inside a function)
288     T_Implies           = 33,   // implies
289     T_Xor               = 34,   // xor
290     T_Eqv               = 35    // eqv
291     
292 };
293
294 //}}}
295 //{{{  Statics                          
296
297 // Statics to keep track of the current state.
298 static std::string      current_data            = "";
299 static unsigned int     current_index           = 0;
300 static unsigned int     token_start             = 0;
301 static int              current_char            = EOF;
302 static token            current_token           = T_Invalid;
303 static std::string      current_string          = "";
304 static std::string      current_reference       = "";
305 static std::string      current_special         = "";
306 static cdl_int          current_int             = 0;
307 static double           current_double          = 0.0;
308 static CdlValueFormat   current_format          = CdlValueFormat_Default;
309 static int              current_function_id     = 0;
310
311 //}}}
312 //{{{  Character access                 
313
314 // ----------------------------------------------------------------------------
315 // Individual character access.
316 // Note that current_index is one character past current_char.
317
318 // Return the next character in the string, or EOF
319 static void
320 next_char()
321 {
322     if (current_index >= current_data.size()) {
323         current_char = EOF;
324     } else {
325         current_char = current_data[current_index++];
326     }
327 }
328
329 // Go back a character. This is useful when parsing
330 // strings. It is the responsibility of the calling code
331 // to make sure that we are not at the start of the buffer.
332 static void
333 backup_char()
334 {
335     CYG_ASSERTC(((EOF == current_char) && (0 < current_index)) || (1 < current_index));
336     if (EOF != current_char) {
337         current_index--;
338     }
339     current_char = current_data[current_index - 1];
340 }
341
342 //}}}
343 //{{{  get_error_location()             
344
345 // ----------------------------------------------------------------------------
346 // Construct part of a diagnostic message, indicating the
347 // area in the data where the error occurred. This string
348 // is of the form {...data} ^char^ {data...}. Ideally
349 // the ^ markers would be on a subsequent line, eliminating
350 // the need for braces, but there is insufficient control
351 // of how the message gets presented to the user.
352 //
353 // Care has to be taken with EOD.
354 static std::string
355 get_error_location()
356 {
357     CYG_REPORT_FUNCNAME("get_error_location");
358     std::string result = "";
359
360     // token_start is probably the best place for centering the error.
361     // current_index is past the point where the error has occurred.
362     if (token_start > 1) {
363         if (token_start > 16) {
364             result = "{..." + current_data.substr(token_start - 13, 13) + "} ";
365         } else {
366             result = "{" + current_data.substr(0, token_start) + "}";
367         }
368     }
369
370     if (current_char == EOF) {
371         result += " <end of data>";
372     } else {
373         result += " ^" + std::string(1, current_data[token_start]) + "^ ";
374     }
375
376     if (token_start < current_data.size()) {
377         if ((token_start + 16) < current_data.size()) {
378             result += "{" + current_data.substr(token_start + 1, current_data.size() - (token_start+1)) + "}";
379         } else {
380             result += "{" + current_data.substr(token_start, 13) + "...}";
381         }
382     }
383
384     CYG_REPORT_RETURN();
385     return result;
386 }
387
388 // Export this functionality available to other modules, especially func.cxx and its
389 // argument checking routines.
390 std::string
391 CdlParse::get_expression_error_location(void)
392 {
393     return get_error_location();
394 }
395
396 //}}}
397 //{{{  Token translation                
398
399 // ----------------------------------------------------------------------------
400
401 // Convert a token into a binary expression operator
402 static CdlExprOp
403 token_to_binary_expr_op()
404 {
405     CYG_REPORT_FUNCNAMETYPE("token_to_expr_op", "op %d");
406     CdlExprOp result = CdlExprOp_Invalid;
407
408     switch(current_token) {
409       case T_Minus:             result = CdlExprOp_Subtract; break;
410       case T_Plus:              result = CdlExprOp_Add; break;
411       case T_Times:             result = CdlExprOp_Multiply; break;
412       case T_Divide:            result = CdlExprOp_Divide; break;
413       case T_Remainder:         result = CdlExprOp_Remainder; break;
414       case T_LeftShift:         result = CdlExprOp_LeftShift; break;
415       case T_RightShift:        result = CdlExprOp_RightShift; break;
416       case T_LessThan:          result = CdlExprOp_LessThan; break;
417       case T_LessEqual:         result = CdlExprOp_LessEqual; break;
418       case T_GreaterThan:       result = CdlExprOp_GreaterThan; break;
419       case T_GreaterEqual:      result = CdlExprOp_GreaterEqual; break;
420       case T_Equal:             result = CdlExprOp_Equal; break;
421       case T_NotEqual:          result = CdlExprOp_NotEqual; break;
422       case T_BitAnd:            result = CdlExprOp_BitAnd; break;
423       case T_BitXor:            result = CdlExprOp_BitXor; break;
424       case T_BitOr:             result = CdlExprOp_BitOr; break;
425       case T_And:               result = CdlExprOp_And; break;
426       case T_Or:                result = CdlExprOp_Or; break;
427       case T_StringConcat:      result = CdlExprOp_StringConcat; break;
428       case T_Implies:           result = CdlExprOp_Implies; break;
429       case T_Xor:               result = CdlExprOp_Xor; break;
430       case T_Eqv:               result = CdlExprOp_Eqv; break;
431       default:                  result = CdlExprOp_Invalid; break;
432     }
433     
434     CYG_REPORT_RETVAL(result);
435     return result;
436 }
437
438 // Convert a token into an ExprOp. This way the internal token enum does
439 // not need to be exported in order to define the interface.
440 //
441 // In practice the higher level code will only look for a handful of
442 // cases, mainly EOD and the range operator, but we might as well
443 // do the job property.
444 static CdlExprOp
445 token_to_expr_op()
446 {
447     CYG_REPORT_FUNCNAMETYPE("token_to_expr_op", "expr op %d");
448     CdlExprOp result;
449
450     // Many of the tokens are already handled for binary operators.
451     result = token_to_binary_expr_op();
452     if (CdlExprOp_Invalid == result) {
453         switch(current_token) {
454         case T_EOD:             result = CdlExprOp_EOD; break;
455         case T_Reference:       result = CdlExprOp_Reference; break;
456         case T_String:          result = CdlExprOp_StringConstant; break;
457         case T_Integer:         result = CdlExprOp_IntegerConstant; break;
458         case T_Double:          result = CdlExprOp_DoubleConstant; break;
459         case T_Range:           result = CdlExprOp_Range; break;
460         case T_Exclamation:     result = CdlExprOp_LogicalNot; break;
461         case T_Tilde:           result = CdlExprOp_BitNot; break;
462         case T_Questionmark:
463         case T_Colon:           result = CdlExprOp_Cond; break; // best guess
464         case T_Function:        result = CdlExprOp_Function; break;
465         case T_OpenBracket:
466         case T_CloseBracket:
467         case T_Invalid:
468         default:                result = CdlExprOp_Invalid; break;
469         }
470     }
471     CYG_REPORT_RETVAL(result);
472     return result;
473 }
474
475 // A utility routine to turn the current token back into a string
476 // This is used for diagnostics.
477 static std::string
478 token_to_string()
479 {
480     CYG_REPORT_FUNCNAME("token_to_string");
481     std::string result = "";
482
483     switch(current_token) {
484       case T_EOD:               result = "<end of data>"; break;
485       case T_Reference:         result = "reference to " + current_reference; break;
486       case T_String:            result = "string \"" + current_string + "\""; break;
487       case T_Integer:
488       {
489           std::string tmp;
490           Cdl::integer_to_string(current_int, tmp, current_format);
491           result = "integer constant " + tmp;
492           break;
493       }
494       case T_Double:
495       {
496           std::string tmp;
497           Cdl::double_to_string(current_double, tmp, current_format);
498           result = "double constant " + tmp;
499           break;
500       }
501       case T_Range:             result = "range operator \"to\""; break;
502       case T_OpenBracket:       result = "open bracket ("; break;
503       case T_CloseBracket:      result = "close bracket )"; break;
504       case T_Minus:             result = "minus sign -"; break;
505       case T_Plus:              result = "plus sign +"; break;
506       case T_Times:             result = "multiply operator *"; break;
507       case T_Divide:            result = "divide operator /"; break;
508       case T_Exclamation:       result = "not operator !"; break;
509       case T_Tilde:             result = "bitwise not operator ~"; break;
510       case T_Questionmark:      result = "question mark ?"; break;
511       case T_Remainder:         result = "remainder operator %"; break;
512       case T_LeftShift:         result = "left shift operator <<"; break;
513       case T_RightShift:        result = "right shift operator >>"; break;
514       case T_LessThan:          result = "less-than operator <"; break;
515       case T_LessEqual:         result = "less-or-equal operator <="; break;
516       case T_GreaterThan:       result = "greater-than operator >"; break;
517       case T_GreaterEqual:      result = "greater-or-equal operator >="; break;
518       case T_Equal:             result = "equality operator =="; break;
519       case T_NotEqual:          result = "not-equal operator !="; break;
520       case T_BitAnd:            result = "bitwise and operator &"; break;
521       case T_BitXor:            result = "bitwise xor operator ^"; break;
522       case T_BitOr:             result = "bitwise or operator |"; break;
523       case T_And:               result = "and operator &&"; break;
524       case T_Or:                result = "or operator ||"; break;
525       case T_Colon:             result = "colon"; break;
526       case T_StringConcat:      result = "string concatenation operator ."; break;
527       case T_Implies:           result = "implies operator"; break;
528       case T_Xor:               result = "logical xor operator"; break;
529       case T_Eqv:               result = "logical equivalence operator eqv"; break;
530       case T_Function:          result = std::string("function call ") + CdlFunction::get_name(current_function_id); break;
531       case T_Invalid:
532       default:                  result = "<invalid token>"; break;
533     }
534
535     CYG_REPORT_RETURN();
536     return result;
537 }
538
539 //}}}
540 //{{{  Literals                         
541
542 // ----------------------------------------------------------------------------
543 //{{{  process_string()                 
544
545 // The start of a string has been detected. Work out the entire string,
546 // allowing for backslash escapes.
547 static void
548 process_string()
549 {
550     CYG_REPORT_FUNCNAME("process_string");
551     CYG_ASSERTC('"' == current_char);
552     CYG_ASSERTC("" == current_string);
553
554     std::string result = "";
555
556     // Move past the leading quote mark.
557     next_char();
558     while ('"' != current_char) {
559         if (EOF == current_char) {
560             throw CdlParseException("Premature end of data in string constant.\n" + get_error_location());
561         } else if ('\\' == current_char) {
562             // Allow \a, \b, \f, \n, \r, \t, \v, \ddd and \xhh.
563             // Also copy with \newline space.
564             // Any other character gets passed through unchanged.
565             next_char();
566             switch(current_char) {
567               case EOF:
568                 throw CdlParseException("Premature end of data after backslash in string constant.\n" + get_error_location());
569               case 'a':
570                 result += '\a';
571                 break;
572               case 'b':
573                 result += '\b';
574                 break;
575               case 'f':
576                 result += '\f';
577                 break;
578               case 'n':
579                 result += '\n';
580                 break;
581               case 'r':
582                 result += '\r';
583                 break;
584               case 't':
585                 result += '\t';
586                 break;
587               case 'v':
588                 result += '\v';
589                 break;
590               case 'x':
591               {
592                 cdl_int tmp = 0;
593                 next_char();
594                 if (!isxdigit(current_char)) {
595                     throw CdlParseException("Non-hexadecimal digit detected in string \\x escape sequence.\n" +
596                         get_error_location());
597                 }
598                 // NOTE: there is no overflow detection here.
599                 do {
600                     tmp *= 16;
601                     if (('0' <= current_char) && (current_char <= '9')) {
602                         tmp += (current_char - '0');
603                     } else if (('a' <= current_char) && (current_char <= 'f')) {
604                         tmp += 10 + (current_char - 'a');
605                     } else if (('A' <= current_char) && (current_char <= 'F')) {
606                         tmp += 10 + (current_char - 'A');
607                     } else {
608                         CYG_FAIL("C library error, isxdigit() succeeded on non-hexadecimal character");
609                     }
610                     next_char();
611                 } while(isxdigit(current_char));
612                 backup_char();
613                 result += (char) tmp;
614               }
615
616               case '\n':
617                 next_char();
618                 while ((EOF != current_char) && isspace(current_char)) {
619                     next_char();
620                 }
621                 // We have gone one too far, back up.
622                 backup_char();
623                 result += " ";
624                 break;
625
626               default:
627                 if (('0' <= current_char) && (current_char <= '7')) {
628                     // A sequence of octal digits.
629                     cdl_int tmp = 0;
630                     do {
631                         tmp = (8 * tmp) + (current_char - '0');
632                         next_char();
633                     } while (('0' <= current_char) && (current_char <= '7'));
634                     backup_char();
635                     result += (char) tmp;
636                 } else {
637                     // For all other backslash sequences, just add the second character
638                     result += (char) current_char;
639                 }
640             }
641         } else {
642             result += (char) current_char;
643         }
644         next_char();
645     }
646     // The closing quote has been reached, move past it.
647     next_char();
648
649     // And all done.
650     current_token  = T_String;
651     current_string = result;
652
653     CYG_REPORT_RETURN();
654 }
655
656 //}}}
657 //{{{  process_number()                 
658
659 // The start of a number has been detected. This number may be an
660 // integer or a double. It is necessary to figure out where the number
661 // ends and invoke the appropriate Cdl:: conversion utility.
662 //
663 // Care has to be taken with termination. Consider a token such as
664 // 134_5. This is not a string because there are no quote marks, nor
665 // is it a valid reference, and because it begins with a digit it
666 // should be interpreted as a number. The 134 bit works fine, then
667 // number processing stops leaving current_char as '_'. If we are
668 // parsing a list expression then the following _5 will actually
669 // be interpreted as a reference. To avoid this, here is a utility
670 // which checks number completion and throws an exception if
671 // necessary.
672 static void check_number_termination()
673 {
674     CYG_REPORT_FUNCNAME("check_number_termination");
675
676     // End-of-data or any whitespace is ok.
677     if ((EOF != current_char) && !isspace(current_char)) {
678         // Any valid operator is ok as well, or brackets for that matter.
679         if (('-' != current_char) && ('+' != current_char) && ('*' != current_char) &&
680             ('/' != current_char) && ('!' != current_char) && ('~' != current_char) &&
681             ('?' != current_char) && ('%' != current_char) && ('<' != current_char) &&
682             ('>' != current_char) && ('=' != current_char) && ('&' != current_char) &&
683             ('^' != current_char) && ('|' != current_char) && (':' != current_char) &&
684             ('(' != current_char) && (')' != current_char)) {
685
686             std::string tmp;
687             Cdl::integer_to_string(current_int, tmp);
688             throw CdlParseException("Invalid character detected after number " + tmp + "\n" + get_error_location());
689         }
690     }
691     
692     CYG_REPORT_RETURN();
693 }
694
695 static void
696 process_number()
697 {
698     CYG_REPORT_FUNCNAME("process_number");
699
700     std::string tmp      = "";
701     bool        is_float = false;
702
703     // Detect the special cases of 0x and octal numbers.
704     if ('0' == current_char) {
705         next_char();
706         if (('x' == current_char) || ('X' == current_char)) {
707             
708             next_char();
709             if (!isxdigit(current_char)) {
710                 throw CdlParseException("Invalid hexadecimal number, expected at least one hexadecimal digit after 0x.\n"
711                                         + get_error_location());
712             }
713             current_int = 0;
714             do {
715                 current_int *= 16;
716                 if (('0' <= current_char) && (current_char <= '9')) {
717                     current_int += (current_char - '0');
718                 } else if (('a' <= current_char) && (current_char <= 'f')) {
719                     current_int += 10 + (current_char - 'a');
720                 } else {
721                     current_int += 10 + (current_char - 'A');
722                 }
723                 next_char();
724             } while(isxdigit(current_char));
725             current_token  = T_Integer;
726             current_format = CdlValueFormat_Hex;
727             check_number_termination();
728             CYG_REPORT_RETURN();
729             return;
730                 
731         } else if (('0' <= current_char) && (current_char <= '7')) {
732
733             current_int = 0;
734             do {
735                 current_int *= 8;
736                 current_int += (current_char - '0');
737                 next_char();
738             } while (('0' <= current_char) && (current_char <= '7'));
739             current_token  = T_Integer;
740             current_format = CdlValueFormat_Octal;
741             check_number_termination();
742             CYG_REPORT_RETURN();
743             return;
744             
745         } else if (('8' == current_char) || ('9' == current_char)) {
746             throw CdlParseException("08... and 09... are not valid  octal numbers.\n" + get_error_location());
747         } else {
748             // This could be plain 0, or 0.123
749             // Backup, and let the rest of the code take care of things
750             backup_char();
751         }
752     }
753     
754     do {
755         tmp += (char) current_char;
756         next_char();
757     } while(isdigit(current_char));
758
759     // If we have found a . then we have a floating point number with a fraction.
760     if ('.' == current_char) {
761         tmp += '.';
762         next_char();
763         if (!isdigit(current_char)) {
764             throw CdlParseException("Invalid floating point constant, expected a digit for the fractional part.\n" +
765                                     get_error_location());
766         }
767         is_float = true;
768         do {
769             tmp += (char) current_char;
770             next_char();
771         } while(isdigit(current_char));
772     }
773
774     // If we have found e or E then we have a floating point number with an exponent
775     if (('e' == current_char) || ('E' == current_char)) {
776         tmp += 'E';
777         next_char();
778         if (('+' == current_char) || ('-' == current_char)) {
779             tmp += current_char;
780             next_char();
781         }
782         if (!isdigit(current_char)) {
783             throw CdlParseException("Invalid floating point constant, expected a digit for the exponent.\n" +
784                                     get_error_location());
785         }
786         is_float = true;
787         do {
788             tmp += (char) current_char;
789             next_char();
790         } while(isdigit(current_char));
791     }
792
793     if (is_float) {
794         if (!Cdl::string_to_double(tmp, current_double)) {
795             throw CdlParseException("Invalid floating point constant `" + tmp + "'.\n" + get_error_location());
796         } else {
797             current_token = T_Double;
798         }
799     } else {
800         if (!Cdl::string_to_integer(tmp, current_int)) {
801             throw CdlParseException("Invalid integer constant `" + tmp + "'.\n" + get_error_location());
802         } else {
803             current_token = T_Integer;
804         }
805     }
806     
807     check_number_termination();
808     CYG_REPORT_RETURN();
809 }
810
811 //}}}
812 //{{{  process_alphanumeric()           
813
814 // The start of an alphanumeric sequence has been detected. This may
815 // be a reference, a function call, or an operator like eq or to. All
816 // such sequences must be a valid C preprocessor name, so the only
817 // characters allowed are underscore, upper and lower case characters,
818 // and digits. The first character cannot be a digit, but that has
819 // been checked already.
820 //
821 // Some care has to be taken with locale's, the C library may decide
822 // that a character is a letter even though the same character is not
823 // valid as far as the preprocessor is concerned.
824 static void
825 process_alphanumeric()
826 {
827     CYG_REPORT_FUNCNAME("process_alphanumeric");
828
829     do {
830        current_reference += (char) current_char;
831        next_char();
832     } while (('_' == current_char) || isdigit(current_char) ||
833              (('a' <= current_char) && (current_char <= 'z')) ||
834              (('A' <= current_char) && (current_char <= 'Z')));
835
836     CYG_REPORT_RETURN();
837 }
838
839 //}}}
840 //{{{  process_special()                
841
842 // Usually an alphanumeric sequence of characters is a reference, e.g.
843 // CYGPKG_KERNEL. However there are only so many special characters
844 // available so some operators are implemented as a sequence, e.g. 
845 // "to". CDL also supports functions like is_substr().
846 //
847 // The data will have been collected into the current_reference string
848 // by a call to process_alphanumeric().
849
850 static bool
851 process_special()
852 {
853     CYG_REPORT_FUNCNAMETYPE("process_special", "special %d");
854     bool result = false;
855     
856     if ("to" == current_reference) {
857         current_token  = T_Range;
858         result = true;
859     } else if ("implies" == current_reference) {
860         current_token  = T_Implies;
861         result = true;
862     } else if ("xor" == current_reference) {
863         current_token  = T_Xor;
864         result = true;
865     } else if ("eqv" == current_reference) {
866         current_token  = T_Eqv;
867         result = true;
868     } else if (CdlFunction::is_function(current_reference.c_str(), current_function_id)) {
869         current_token  = T_Function;
870         result = true;
871     }
872
873     if (result) {
874         current_special     = current_reference;
875         current_reference   = "";
876     }
877     CYG_REPORT_RETVAL(result);
878     return result;
879 }
880
881 //}}}
882
883 //}}}
884 //{{{  next_token()                     
885
886 // ----------------------------------------------------------------------------
887 // Work out what the next token is. This includes the handling of
888 // strings, integers, doubles, and references.
889 static void
890 next_token()
891 {
892     CYG_REPORT_FUNCNAMETYPE("next_token", "token %d");
893
894     // Make sure there is no dross left lying around from the previous call.
895     current_token       = T_Invalid;
896     current_string      = "";
897     current_reference   = "";
898     current_special     = "";
899     current_int         = 0;
900     current_double      = 0.0;
901     current_format      = CdlValueFormat_Default;
902     current_function_id = 0;
903
904     // Skip leading white space. This includes newlines, tabs, etc,
905     // consider the case of:
906     //    ...
907     //    legal_values {
908     //        1
909     //        2
910     //        4
911     //        ..
912     //    }
913     //    ...
914     // which is perfectly legitimate. White space inside strings
915     // is handled by the string literal code, and does not get filtered
916     // out here.
917     //
918     // Exactly which characters are white-space is implementation-defined,
919     // so a special check for EOF is in order.
920     while ((EOF != current_char) && isspace(current_char)) {
921         next_char();
922     }
923
924     // Remember the token starting point. next_char() has actually moved
925     // the index on by one.
926     token_start = current_index - 1;
927
928     // The simple cases can be handled inline, the more complicated cases
929     // involve other functions
930     switch(current_char) {
931
932       case EOF:
933           current_token = T_EOD;
934           break;
935
936       case '"':
937           process_string();
938           break;
939
940       case '(':
941           current_token = T_OpenBracket;
942           next_char();
943           break;
944
945       case ')':
946           current_token = T_CloseBracket;
947           next_char();
948           break;
949
950           // At this level it is not possible to distinguish between
951           // unary and binary operators, so no attempt is made to
952           // turn - and + into part of a number.
953       case '-':
954           current_token = T_Minus;
955           next_char();
956           break;
957
958       case '+':
959           current_token = T_Plus;
960           next_char();
961           break;
962
963       case '*':
964           current_token = T_Times;
965           next_char();
966           break;
967
968       case '/':
969           current_token = T_Divide;
970           next_char();
971           break;
972
973       case '!':
974           next_char();
975           if ('=' == current_char) {
976               current_token = T_NotEqual;
977               next_char();
978           } else {
979               current_token = T_Exclamation;
980           }
981           break;
982
983       case '~':
984           current_token = T_Tilde;
985           next_char();
986           break;
987
988       case '?':
989           current_token = T_Questionmark;
990           next_char();
991           break;
992
993       case '%':
994           current_token = T_Remainder;
995           next_char();
996           break;
997
998       case '<':
999           next_char();
1000           if ('<' == current_char) {
1001               current_token = T_LeftShift;
1002               next_char();
1003           } else if ('=' == current_char) {
1004               current_token = T_LessEqual;
1005               next_char();
1006           } else {
1007               current_token = T_LessThan;
1008           }
1009           break;
1010
1011       case '>':
1012           next_char();
1013           if ('>' == current_char) {
1014               current_token = T_RightShift;
1015               next_char();
1016           } else if ('=' == current_char) {
1017               current_token = T_GreaterEqual;
1018               next_char();
1019           } else {
1020               current_token = T_GreaterThan;
1021           }
1022           break;
1023
1024       case '=':
1025           next_char();
1026           if ('=' != current_char) {
1027               throw CdlParseException(std::string("Incomplete == operator in expression.\n") + get_error_location());
1028           } else {
1029               current_token = T_Equal;
1030               next_char();
1031           }
1032           break;
1033
1034       case '&':
1035           next_char();
1036           if ('&' == current_char) {
1037               current_token = T_And;
1038               next_char();
1039           } else {
1040               current_token = T_BitAnd;
1041           }
1042           break;
1043
1044       case '^':
1045           current_token = T_BitXor;
1046           next_char();
1047           break;
1048
1049       case '|':
1050           next_char();
1051           if ('|' == current_char) {
1052               current_token = T_Or;
1053               next_char();
1054           } else {
1055               current_token = T_BitOr;
1056           }
1057           break;
1058
1059       case ':':
1060           current_token = T_Colon;
1061           next_char();
1062           break;
1063
1064       case '.':
1065           current_token = T_StringConcat;
1066           next_char();
1067           break;
1068
1069       case ',':
1070           current_token = T_Comma;
1071           next_char();
1072           break;
1073         
1074       default:
1075           // String constants have been handled already. The only
1076           // valid tokens that are left are numbers, references,
1077           // "specials" such as the range and string equality
1078           // operators, and functions.
1079           //
1080           // Numbers should begin with a digit (plus and minus are
1081           // tokenized separately).
1082           //
1083           // References must be valid C preprocessor symbols, i.e.
1084           // they must begin with either a letter or an underscore.
1085           // The range operator is handled most conveniently as
1086           // a special case of a reference.
1087           if (isdigit(current_char)) {
1088               process_number();
1089           } else if (('_' == current_char) ||
1090                      (('a' <= current_char) && (current_char <= 'z')) ||
1091                      (('A' <= current_char) && (current_char <= 'Z'))) {
1092               process_alphanumeric();
1093               if (!process_special()) {
1094                   current_token = T_Reference;
1095               }
1096           } else {
1097               std::string msg = "Unexpected character '";
1098               msg += (char) current_char;
1099               msg += "' in expression.\n";
1100               msg += get_error_location();
1101               throw CdlParseException(msg);
1102           }
1103           break;
1104     }
1105
1106     CYG_REPORT_RETVAL(current_token);
1107 }
1108
1109 //}}}
1110 //{{{  initialise_tokenisation()        
1111
1112 // ----------------------------------------------------------------------------
1113 // This is called at the start of expression parsing. It
1114 // sets up the appropriate statics, and provides initial
1115 // values for current_char and current_token.
1116 static void
1117 initialise_tokenisation(std::string data, int index)
1118 {
1119     CYG_REPORT_FUNCNAME("initialise_tokenization");
1120
1121     current_data        = data;
1122     current_index       = static_cast<unsigned int>(index);
1123     token_start         = current_index;
1124     next_char();
1125     next_token();
1126
1127     CYG_REPORT_RETURN();
1128 }
1129
1130 //}}}
1131
1132 //}}}
1133 //{{{  Syntactic analysis               
1134
1135 // ----------------------------------------------------------------------------
1136 // Syntactic analysis.
1137 //
1138 // The BNF of CDL expressions is something like this:
1139 //
1140 //   <expression>   ::= <conditional>
1141 //   <conditional>  ::= <implies> ? <conditional> : <conditional> | <implies>
1142 //   <implies>      ::= <eqv>    [<implies op>  <implies>]      implies
1143 //   <eqv>          ::= <or>     [<eqv op>      <eqv>]          xor, eqv        
1144 //   <or>           ::= <and>    [<or op>       <or>]           ||
1145 //   <and>          ::= <bitor>  [<and op>      <and>]          &&
1146 //   <bitor>        ::= <bitxor> [<bitor op>    <bitor>]        |
1147 //   <bitxor>       ::= <bitand> [<bitxor op>   <bitxor>]       ^
1148 //   <bitand>       ::= <eq>     [<bitand op>   <and>]          &
1149 //   <eq>           ::= <comp>   [<eq op>       <eq>]           == !=
1150 //   <comp>         ::= <shift>  [<comp op>     <comp>]         < <= > >=
1151 //   <shift>        ::= <add>    [<shift op>    <shift>]        << >>
1152 //   <add>          ::= <mult>   [<add op>      <add>]          + - .
1153 //   <mult>         ::= <unary>  [<mult op>     <mult>]         * / %
1154 //   <unary>        ::= -<unary> | +<unary> | !<unary> | *<unary> | ?<unary> |
1155 //                      ~<unary> |
1156 //                      <string constant> | <integer constant> |
1157 //                      <double constant> | <reference> |
1158 //                      ( <expression> ) | <function>
1159 //
1160 // There are separate functions for each of these terms.
1161
1162 // A forward declaration, needed for bracketed subexpressions.
1163 static void parse_expression(CdlExpression);
1164
1165 // A utility to add a reference to the current expression, returning
1166 // the index.
1167 static int
1168 push_reference(CdlExpression expr, const std::string& reference)
1169 {
1170     CYG_REPORT_FUNCNAMETYPE("push_reference", "new index %d");
1171     CYG_PRECONDITION_CLASSC(expr);
1172
1173     CdlReference ref(reference);
1174     expr->references.push_back(ref);
1175     int result = (int) expr->references.size() - 1;
1176
1177     CYG_REPORT_RETVAL(result);
1178     return result;
1179 }
1180
1181 // A utility to add a subexpression, returning its index.
1182 static void
1183 push_subexpression(CdlExpression expr, const CdlSubexpression& subexpr)
1184 {
1185     CYG_REPORT_FUNCNAME("push_subexpression");
1186     CYG_PRECONDITION_CLASSC(expr);
1187
1188     expr->sub_expressions.push_back(subexpr);
1189     expr->first_subexpression = ((int) expr->sub_expressions.size()) - 1;
1190
1191     CYG_REPORT_RETURN();
1192 }
1193
1194 // Another utility to hold of the most recent subexpression
1195 static CdlSubexpression&
1196 current_subexpression(CdlExpression expr)
1197 {
1198     CYG_REPORT_FUNCNAME("current_subexpression");
1199
1200     CdlSubexpression& result = expr->sub_expressions[expr->first_subexpression];
1201
1202     CYG_REPORT_RETURN();
1203     return result;
1204 }
1205
1206 static void
1207 parse_function(CdlExpression expr)
1208 {
1209     CYG_REPORT_FUNCNAME("parse_function");
1210     CYG_REPORT_FUNCARG1XV(expr);
1211     CYG_PRECONDITION_CLASSC(expr);
1212
1213     CdlSubexpression subexpr;
1214     subexpr.op          = CdlExprOp_Function;
1215     subexpr.func        = current_function_id;
1216
1217     int number_of_args  = CdlFunction::get_args_count(current_function_id);
1218     CYG_ASSERTC((0 < number_of_args) && (number_of_args <= CdlFunction_MaxArgs));
1219     std::string name    = current_special;
1220
1221     // check for the opening bracket: xyzzy(arg1, arg2)
1222     next_token();
1223     if (T_OpenBracket != current_token) {
1224         throw CdlParseException(std::string("Expected opening bracket after function ") + name + "\n" + get_error_location());
1225     }
1226     next_token();
1227
1228     int i;
1229     for (i = 0; i < number_of_args; i++) {
1230         parse_expression(expr);
1231         subexpr.args[i] = expr->first_subexpression;
1232         if (i < (number_of_args - 1)) {
1233             if (T_Comma != current_token) {
1234                 throw CdlParseException(std::string("Expected comma between arguments in function ") +
1235                                         name + "\n" + get_error_location());
1236             }
1237             next_token();
1238         }
1239     }
1240     if (T_Comma == current_token) {
1241         throw CdlParseException(std::string("Too many arguments passed to function ") + name + "\n" + get_error_location());
1242     }
1243     if (T_CloseBracket != current_token) {
1244         throw CdlParseException(std::string("Expected closing bracket after function ") + name + "\n" + get_error_location());
1245     }
1246     next_token();
1247     
1248     // Allow the function implementation to check its arguments if it is so inclined.
1249     CdlFunction::check(expr, subexpr);
1250     
1251     push_subexpression(expr, subexpr);
1252     CYG_REPORT_RETURN();
1253 }
1254
1255 static void
1256 parse_unary(CdlExpression expr)
1257 {
1258     CYG_REPORT_FUNCNAME("parse_operand");
1259     CYG_REPORT_FUNCARG1XV(expr);
1260     CYG_PRECONDITION_CLASSC(expr);
1261
1262     CdlSubexpression subexpr;
1263
1264     switch(current_token) {
1265       case T_EOD :
1266       {
1267         // This warrants a special case
1268         throw CdlParseException("End of expression reached when expecting an operand.\n" + get_error_location());
1269       }
1270
1271       case T_Function :
1272       {
1273           parse_function(expr);
1274           break;
1275       }
1276       
1277       case T_Reference :
1278       {
1279         subexpr.op              = CdlExprOp_Reference;
1280         subexpr.reference_index = push_reference(expr, current_reference);
1281         push_subexpression(expr, subexpr);
1282         next_token();
1283         break;
1284       }
1285       
1286       case T_String :
1287       {
1288         subexpr.op              = CdlExprOp_StringConstant;
1289         subexpr.constants       = current_string;
1290         push_subexpression(expr, subexpr);
1291         next_token();
1292         break;
1293       }
1294       
1295       case T_Integer :
1296       {
1297         subexpr.op               = CdlExprOp_IntegerConstant;
1298         subexpr.constants.set_integer_value(current_int, current_format);
1299         push_subexpression(expr, subexpr);
1300         next_token();
1301         break;
1302       }
1303       
1304       case T_Double :
1305       {
1306         subexpr.op              = CdlExprOp_DoubleConstant;
1307         subexpr.constants.set_double_value(current_double, current_format);
1308         push_subexpression(expr, subexpr);
1309         next_token();
1310         break;
1311       }
1312       
1313       case T_OpenBracket :
1314       {
1315         next_token();
1316         parse_expression(expr);
1317         if (T_CloseBracket != current_token) {
1318             throw CdlParseException("Missing close bracket after subexpression.\n" + get_error_location());
1319         }
1320         next_token();
1321         break;
1322       }
1323       
1324       case T_Minus :
1325       {
1326         next_token();
1327         parse_unary(expr);
1328         CdlSubexpression& last_sub      = current_subexpression(expr);
1329         if (CdlExprOp_IntegerConstant == last_sub.op) {
1330             // Do the negating inline, no need for another subexpression.
1331             last_sub.constants = last_sub.constants.get_integer_value() * -1;
1332         } else if (CdlExprOp_DoubleConstant == last_sub.op) {
1333             last_sub.constants = last_sub.constants.get_double_value() * -1;
1334         } else {
1335             // We could detect certain cases such as string constants etc.
1336             // For now don't bother.
1337             subexpr.op          = CdlExprOp_Negate;
1338             subexpr.lhs_index   = expr->first_subexpression;
1339             push_subexpression(expr, subexpr);
1340         }
1341         break;
1342       }
1343       
1344       case T_Plus :
1345       {
1346         next_token();
1347         parse_unary(expr);
1348         CdlSubexpression& last_sub      = current_subexpression(expr);
1349         if ((CdlExprOp_IntegerConstant == last_sub.op) || (CdlExprOp_DoubleConstant == last_sub.op)) {
1350             // No need to do anything here.
1351         } else {
1352             subexpr.op          = CdlExprOp_Plus;
1353             subexpr.lhs_index   = expr->first_subexpression;
1354             push_subexpression(expr, subexpr);
1355         }
1356         break;
1357       }
1358
1359       case T_Times :
1360       {
1361           next_token();
1362           parse_unary(expr);
1363           subexpr.op            = CdlExprOp_Indirect;
1364           subexpr.lhs_index     = expr->first_subexpression;
1365           push_subexpression(expr, subexpr);
1366           break;
1367       }
1368       
1369       case T_Exclamation :
1370       {
1371           next_token();
1372           parse_unary(expr);
1373           subexpr.op            = CdlExprOp_LogicalNot;
1374           subexpr.lhs_index     = expr->first_subexpression;
1375           push_subexpression(expr, subexpr);
1376           break;
1377       }
1378
1379       case T_Tilde :
1380       {
1381           next_token();
1382           parse_unary(expr);
1383           subexpr.op            = CdlExprOp_BitNot;
1384           subexpr.lhs_index     = expr->first_subexpression;
1385           push_subexpression(expr, subexpr);
1386           break;
1387       }
1388
1389       case T_Questionmark:
1390       {
1391           // This is the `active' operator, it can only be applied directly to a reference.
1392           next_token();
1393           parse_unary(expr);
1394           CdlSubexpression& last_sub = current_subexpression(expr);
1395           if (CdlExprOp_Reference != last_sub.op) {
1396               throw CdlParseException("The active operator ? can only be applied directly to a reference.\n" +
1397                                       get_error_location());
1398           }
1399           // There is no point in creating a new subexpression object, just modify
1400           // the existing one. This has the useful side effect of avoiding
1401           // reference substitution in the eval code.
1402           last_sub.op           = CdlExprOp_Active;
1403           break;
1404       }
1405       default:
1406       {
1407         throw CdlParseException("Unexpected token `" + token_to_string() + "', expecting an operand.\n" +
1408                                 get_error_location());
1409       }
1410     }
1411
1412     CYG_REPORT_RETURN();
1413 }
1414
1415 static void
1416 parse_multiply(CdlExpression expr)
1417 {
1418     CYG_REPORT_FUNCNAME("parse_multiply");
1419     
1420     parse_unary(expr);
1421     while ((T_Times == current_token) || (T_Divide == current_token) || (T_Remainder == current_token)) {
1422
1423         CdlSubexpression subexpr;
1424         subexpr.op      =
1425             (T_Times  == current_token) ? CdlExprOp_Multiply :
1426             (T_Divide == current_token) ? CdlExprOp_Divide : CdlExprOp_Remainder;
1427         subexpr.lhs_index = expr->first_subexpression;
1428         
1429         next_token();
1430         parse_unary(expr);
1431
1432         subexpr.rhs_index = expr->first_subexpression;
1433         push_subexpression(expr, subexpr);
1434     }
1435     
1436     CYG_REPORT_RETURN();
1437 }
1438
1439 static void
1440 parse_add(CdlExpression expr)
1441 {
1442     CYG_REPORT_FUNCNAME("parse_add");
1443     
1444     parse_multiply(expr);
1445     while ((T_Plus == current_token)  ||
1446            (T_Minus == current_token) ||
1447            (T_StringConcat == current_token)) {
1448
1449         CdlSubexpression subexpr;
1450         subexpr.op = (T_Plus == current_token) ? CdlExprOp_Add :
1451                      (T_Minus == current_token) ? CdlExprOp_Subtract :
1452                      CdlExprOp_StringConcat;
1453         subexpr.lhs_index = expr->first_subexpression;
1454         
1455         next_token();
1456         parse_multiply(expr);
1457
1458         subexpr.rhs_index = expr->first_subexpression;
1459         push_subexpression(expr, subexpr);
1460     }
1461     
1462     CYG_REPORT_RETURN();
1463 }
1464
1465 static void
1466 parse_shift(CdlExpression expr)
1467 {
1468     CYG_REPORT_FUNCNAME("parse_shift");
1469     
1470     parse_add(expr);
1471     while ((T_LeftShift == current_token) || (T_RightShift == current_token)) {
1472
1473         CdlSubexpression subexpr;
1474         subexpr.op = (T_LeftShift == current_token) ? CdlExprOp_LeftShift : CdlExprOp_RightShift;
1475         subexpr.lhs_index = expr->first_subexpression;
1476         
1477         next_token();
1478         parse_add(expr);
1479
1480         subexpr.rhs_index = expr->first_subexpression;
1481         push_subexpression(expr, subexpr);
1482     }
1483     
1484     CYG_REPORT_RETURN();
1485 }
1486
1487 static void
1488 parse_comparison(CdlExpression expr)
1489 {
1490     CYG_REPORT_FUNCNAME("parse_comparison");
1491     
1492     parse_shift(expr);
1493     while ((T_LessThan == current_token)    || (T_LessEqual    == current_token) ||
1494            (T_GreaterThan == current_token) || (T_GreaterEqual == current_token))  {
1495
1496         CdlSubexpression subexpr;
1497         subexpr.op =
1498             (T_LessThan    == current_token) ? CdlExprOp_LessThan : 
1499             (T_LessEqual   == current_token) ? CdlExprOp_LessEqual :
1500             (T_GreaterThan == current_token) ? CdlExprOp_GreaterThan : CdlExprOp_GreaterEqual;
1501         subexpr.lhs_index = expr->first_subexpression;
1502         
1503         next_token();
1504         parse_shift(expr);
1505
1506         subexpr.rhs_index = expr->first_subexpression;
1507         push_subexpression(expr, subexpr);
1508     }
1509     
1510     CYG_REPORT_RETURN();
1511 }
1512
1513 static void
1514 parse_equals(CdlExpression expr)
1515 {
1516     CYG_REPORT_FUNCNAME("parse_equals");
1517     
1518     parse_comparison(expr);
1519     while ((T_Equal == current_token) ||
1520            (T_NotEqual == current_token)) {
1521
1522         CdlSubexpression subexpr;
1523         subexpr.op = (T_Equal == current_token) ? CdlExprOp_Equal : CdlExprOp_NotEqual;
1524         subexpr.lhs_index = expr->first_subexpression;
1525         
1526         next_token();
1527         parse_comparison(expr);
1528
1529         subexpr.rhs_index = expr->first_subexpression;
1530         push_subexpression(expr, subexpr);
1531     }
1532     
1533     CYG_REPORT_RETURN();
1534 }
1535
1536 static void
1537 parse_bitand(CdlExpression expr)
1538 {
1539     CYG_REPORT_FUNCNAME("parse_bitand");
1540     
1541     parse_equals(expr);
1542     while (T_BitAnd == current_token) {
1543
1544         CdlSubexpression subexpr;
1545         subexpr.op = CdlExprOp_BitAnd;
1546         subexpr.lhs_index = expr->first_subexpression;
1547         
1548         next_token();
1549         parse_equals(expr);
1550
1551         subexpr.rhs_index = expr->first_subexpression;
1552         push_subexpression(expr, subexpr);
1553     }
1554     
1555     CYG_REPORT_RETURN();
1556 }
1557
1558 static void
1559 parse_bitxor(CdlExpression expr)
1560 {
1561     CYG_REPORT_FUNCNAME("parse_bitxor");
1562     
1563     parse_bitand(expr);
1564     while (T_BitXor == current_token) {
1565
1566         CdlSubexpression subexpr;
1567         subexpr.op = CdlExprOp_BitXor;
1568         subexpr.lhs_index = expr->first_subexpression;
1569         
1570         next_token();
1571         parse_bitand(expr);
1572
1573         subexpr.rhs_index = expr->first_subexpression;
1574         push_subexpression(expr, subexpr);
1575     }
1576     
1577     CYG_REPORT_RETURN();
1578 }
1579
1580 static void
1581 parse_bitor(CdlExpression expr)
1582 {
1583     CYG_REPORT_FUNCNAME("parse_bitor");
1584     
1585     parse_bitxor(expr);
1586     while (T_BitOr == current_token) {
1587
1588         CdlSubexpression subexpr;
1589         subexpr.op = CdlExprOp_BitOr;
1590         subexpr.lhs_index = expr->first_subexpression;
1591         
1592         next_token();
1593         parse_bitxor(expr);
1594
1595         subexpr.rhs_index = expr->first_subexpression;
1596         push_subexpression(expr, subexpr);
1597     }
1598     
1599     CYG_REPORT_RETURN();
1600 }
1601
1602 static void
1603 parse_and(CdlExpression expr)
1604 {
1605     CYG_REPORT_FUNCNAME("parse_and");
1606     parse_bitor(expr);
1607     while (T_And == current_token) {
1608
1609         CdlSubexpression subexpr;
1610         subexpr.op = CdlExprOp_And;
1611         subexpr.lhs_index = expr->first_subexpression;
1612         
1613         next_token();
1614         parse_bitor(expr);
1615
1616         subexpr.rhs_index = expr->first_subexpression;
1617         push_subexpression(expr, subexpr);
1618     }
1619     
1620     CYG_REPORT_RETURN();
1621 }
1622
1623 static void
1624 parse_or(CdlExpression expr)
1625 {
1626     CYG_REPORT_FUNCNAME("parse_or");
1627
1628     parse_and(expr);
1629     while (T_Or == current_token) {
1630
1631         CdlSubexpression subexpr;
1632         subexpr.op = CdlExprOp_Or;
1633         subexpr.lhs_index = expr->first_subexpression;
1634         
1635         next_token();
1636         parse_and(expr);
1637
1638         subexpr.rhs_index = expr->first_subexpression;
1639         push_subexpression(expr, subexpr);
1640     }
1641     
1642     CYG_REPORT_RETURN();
1643 }
1644
1645 static void
1646 parse_eqv(CdlExpression expr)
1647 {
1648     CYG_REPORT_FUNCNAME("parse_eqv");
1649
1650     parse_or(expr);
1651     while ((T_Xor == current_token) || (T_Eqv == current_token)) {
1652         
1653         CdlSubexpression subexpr;
1654         subexpr.op = (T_Xor == current_token) ? CdlExprOp_Xor : CdlExprOp_Eqv;
1655         subexpr.lhs_index = expr->first_subexpression;
1656         
1657         next_token();
1658         parse_or(expr);
1659
1660         subexpr.rhs_index = expr->first_subexpression;
1661         push_subexpression(expr, subexpr);
1662     }
1663     
1664     CYG_REPORT_RETURN();
1665 }
1666
1667 static void
1668 parse_implies(CdlExpression expr)
1669 {
1670     CYG_REPORT_FUNCNAME("parse_implies");
1671
1672     parse_eqv(expr);
1673     while (T_Implies == current_token) {
1674         
1675         CdlSubexpression subexpr;
1676         subexpr.op = CdlExprOp_Implies;
1677         subexpr.lhs_index = expr->first_subexpression;
1678         
1679         next_token();
1680         parse_eqv(expr);
1681
1682         subexpr.rhs_index = expr->first_subexpression;
1683         push_subexpression(expr, subexpr);
1684     }
1685     
1686     CYG_REPORT_RETURN();
1687 }
1688
1689 static void
1690 parse_conditional(CdlExpression expr)
1691 {
1692     CYG_REPORT_FUNCNAME("parse_conditional");
1693
1694     parse_implies(expr);
1695     if (T_Questionmark == current_token) {
1696         CdlSubexpression subexpr;
1697         subexpr.op = CdlExprOp_Cond;
1698         subexpr.lhs_index = expr->first_subexpression;
1699
1700         next_token();
1701         parse_conditional(expr);
1702         subexpr.rhs_index = expr->first_subexpression;
1703
1704         if (T_Colon != current_token) {
1705             throw CdlParseException("Expected colon in conditional expression.\n" + get_error_location());
1706         }
1707
1708         next_token();
1709         parse_conditional(expr);
1710         subexpr.rrhs_index = expr->first_subexpression;
1711
1712         push_subexpression(expr, subexpr);
1713     }
1714     
1715     CYG_REPORT_RETURN();
1716 }
1717
1718 static void
1719 parse_expression(CdlExpression expr)
1720 {
1721     CYG_REPORT_FUNCNAME("parse_expression");
1722
1723     parse_conditional(expr);
1724     
1725     CYG_REPORT_RETURN();
1726 }
1727
1728 // ----------------------------------------------------------------------------
1729 // The entry point.
1730 void
1731 CdlExpressionBody::continue_parse(CdlExpression expr, std::string data, int& index, CdlExprOp& token, int& token_end)
1732 {
1733     CYG_REPORT_FUNCNAME("CdlExpression::continue_parse");
1734     CYG_REPORT_FUNCARG1XV(expr);
1735     CYG_PRECONDITION_CLASSC(expr);
1736     CYG_PRECONDITIONC((CdlExprOp_Invalid == token) || (CdlExprOp_And == token));
1737
1738     int current_subexpr = expr->first_subexpression;
1739     initialise_tokenisation(data, index);
1740     parse_expression(expr);
1741     if (CdlExprOp_And == token) {
1742         CdlSubexpression subexpr;
1743         subexpr.op        = CdlExprOp_And;
1744         subexpr.lhs_index = current_subexpr;
1745         subexpr.rhs_index = expr->first_subexpression;
1746         push_subexpression(expr, subexpr);
1747     }
1748     token       = token_to_expr_op();
1749     index       = token_start;
1750     token_end   = current_index;
1751
1752     CYG_REPORT_RETURN();
1753 }
1754
1755 //}}}
1756
1757 //}}}
1758 //{{{  Expression Evaluation            
1759
1760 // ----------------------------------------------------------------------------
1761 // Expression evaluation. This always happens in the context of a
1762 // particular toplevel. The parsed expression is held in what amounts
1763 // to a simple tree, so evaluation involves some recursion and a big
1764 // switch statement.
1765
1766 static void
1767 evaluate_subexpr(CdlEvalContext& context, CdlExpression expr, int subexpr_index, CdlSimpleValue& result)
1768 {
1769     CYG_REPORT_FUNCNAME("evaluate_subexpr");
1770     CYG_REPORT_FUNCARG2XV(expr, subexpr_index);
1771     CYG_ASSERTC((subexpr_index >= 0) && ((unsigned int)subexpr_index < expr->sub_expressions.size()));
1772
1773     const CdlSubexpression& subexpr = expr->sub_expressions[subexpr_index];
1774     switch(subexpr.op) {
1775     case CdlExprOp_StringConstant :
1776     case CdlExprOp_IntegerConstant :
1777     case CdlExprOp_DoubleConstant :
1778     {
1779         result = subexpr.constants;
1780         break;
1781     }
1782     case CdlExprOp_Function :
1783     {
1784         CdlFunction::eval(context, expr, subexpr, result);
1785         break;
1786     }
1787     case CdlExprOp_Reference :
1788     {
1789         // This expression may be happening in the context of a particular
1790         // property. If so then the destination may or may not be resolved,
1791         // and this is significant in the context of loading and unloading.
1792         // Alternatively this expression may be being evaluated inside
1793         // some Tcl code, with no particular context.
1794         CdlNode destination = 0;
1795         if (0 != context.property) {
1796             // There is a property, use the bound/unbound reference.
1797             destination = expr->references[subexpr.reference_index].get_destination();
1798         } else {
1799             // The destination name can be retrieved, but we still need some
1800             // way of resolving it.
1801             if (0 != context.toplevel) {
1802                 std::string destination_name = expr->references[subexpr.reference_index].get_destination_name();
1803                 destination = context.toplevel->lookup(destination_name);
1804             }
1805         }
1806         if (0 == destination) {
1807             // There are two ways of handling this.
1808             //   1) throw an eval exception, which will usually result
1809             //      in a new conflict object
1810             //   2) substitute a value of 0.
1811             // There should already be a conflict object for an
1812             // unresolved reference, and having two conflicts for
1813             // essentially the same error is not useful. Using a value
1814             // of 0 allows things to continue for a bit longer. It is
1815             // consistent with active vs. inactive values, gives
1816             // basically the right result for "requires" properties,
1817             // and so on.
1818             //
1819             // For now option (2) has it, but this decision may be
1820             // reversed in future.
1821             result = false;
1822         } else {
1823             CdlValuable valuable = dynamic_cast<CdlValuable>(destination);
1824             if (0 == valuable) {
1825                 // This is a serious problem, an exception is warranted.
1826                 throw CdlEvalException("The expression references `" + destination->get_class_name() + " " +
1827                                        destination->get_name() + "' which does not have a value.");
1828             } else {
1829                 CdlSimpleValue::eval_valuable(context, valuable, result);
1830             }
1831         }
1832         break;
1833     }
1834     case CdlExprOp_Negate :
1835     {
1836         // Unary -. Evaluate the target. If it is numeric, fine. Otherwise
1837         // an error is warranted.
1838         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1839         if (result.has_integer_value()) {
1840             result.set_integer_value(-1 * result.get_integer_value());
1841         } else if (result.has_double_value()) {
1842             result.set_double_value(-1.0 * result.get_double_value());
1843         } else {
1844             throw CdlEvalException("Attempt to negate non-numeric value `" + result.get_value() + "'.");
1845         }
1846         break;
1847     }
1848     case CdlExprOp_Plus :
1849     {
1850         // Unary +. Essentially this just checks that the current value is numeric.
1851         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1852         if ((!result.has_integer_value()) && (!result.has_double_value())) {
1853             throw CdlEvalException("Attempt to apply unary + operator to non-numeric value `" + result.get_value() + "'.");
1854         }
1855         break;
1856     }
1857     case CdlExprOp_LogicalNot :
1858     {
1859         // !x
1860         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1861         if (result.get_bool_value()) {
1862             result = false;;
1863         } else {
1864             result = true;
1865         }
1866         result.set_value_format(CdlValueFormat_Default);
1867         break;
1868     }
1869     case CdlExprOp_BitNot :
1870     {
1871         // ~x. The operand must be an integer value.
1872         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1873         if (result.has_integer_value()) {
1874             cdl_int tmp = result.get_integer_value();
1875             result = ~tmp;
1876         } else {
1877             throw CdlEvalException("Attempt to apply unary ~ operator to non-integer value `" + result.get_value() + "'.");
1878         }
1879         break;
1880     }
1881     case CdlExprOp_Indirect :
1882     {
1883         // *x. The operand must evaluate to a string, and that string should be
1884         // the name of a CdlValuable object.
1885         CdlNode destination = 0;
1886         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1887         std::string name = result.get_value();
1888         
1889         if (0 != context.toplevel) {
1890             destination = context.toplevel->lookup(name);
1891         } else {
1892             CYG_FAIL("This situation should probably never happen.");
1893         }
1894         
1895         if (0 == destination) {
1896             throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name +
1897                                    "', which is not the name of a known CDL entity.");
1898         } else {
1899             CdlValuable valuable = dynamic_cast<CdlValuable>(destination);
1900             if (0 == valuable) {
1901                 throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name +
1902                                        "', which does not have a value.");
1903             } else {
1904                 CdlSimpleValue::eval_valuable(context, valuable, result);
1905             }
1906         }
1907         break;
1908     }
1909     case CdlExprOp_Active :
1910     {
1911         // ?x. If x is currently unresolved then default to 0.
1912         // See the CdlExprOp_Reference code above for a similar case.
1913         CdlNode destination = 0;
1914         if (0 != context.property) {
1915             destination =  expr->references[subexpr.reference_index].get_destination();
1916         } else {
1917             if (0 != context.toplevel) {
1918                 std::string destination_name = expr->references[subexpr.reference_index].get_destination_name();
1919                 destination = context.toplevel->lookup(destination_name);
1920             }
1921         }
1922
1923         bool active = false;
1924         if ((0 != destination) && context.transaction->is_active(destination)) {
1925             active = true;
1926         }
1927         if (active) {
1928             result = true;
1929         } else {
1930             result = false;
1931         }
1932         break;
1933     }
1934     case CdlExprOp_Multiply :
1935     {
1936         // x * y. For now this only makes sense for numerical data,
1937         // but it is possible to mix and match integer and double
1938         // precision data.
1939         //
1940         // Strictly speaking the rhs need only be evaluated if it
1941         // is known that the lhs is numeric.
1942         CdlSimpleValue lhs;
1943         CdlSimpleValue rhs;
1944         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1945         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1946         if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
1947             (!(rhs.has_integer_value() || rhs.has_double_value()))) {
1948             throw CdlEvalException("Attempt to multiply non-numerical values: `" + lhs.get_value() + "' * `" +
1949                                    rhs.get_value() + "'.");
1950         }
1951         if (lhs.has_integer_value() && rhs.has_integer_value()) {
1952             result = lhs.get_integer_value() * rhs.get_integer_value();
1953         } else {
1954             result = lhs.get_double_value() * rhs.get_double_value();
1955         }
1956         result.set_value_format(lhs, rhs);
1957         break;
1958     }
1959     case CdlExprOp_Divide :
1960     {
1961         // x / y. Basically the same as multiplication, apart from a check for
1962         // division by zero.
1963         CdlSimpleValue lhs;
1964         CdlSimpleValue rhs;
1965         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1966         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1967         if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
1968             (!(rhs.has_integer_value() || rhs.has_double_value()))) {
1969             throw CdlEvalException("Attempt to divide non-numerical values: `" + lhs.get_value() + "' / `" +
1970                                    rhs.get_value() + "'.");
1971         }
1972         if (lhs.has_integer_value() && rhs.has_integer_value()) {
1973             cdl_int rhs_val = rhs.get_integer_value();
1974             if (0 == rhs_val) {
1975                 throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'.");
1976             } else {
1977                 result = lhs.get_integer_value() / rhs_val;
1978             }
1979         } else {
1980             double rhs_val = rhs.get_double_value();
1981             if (0.0 == rhs_val) {
1982                 throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'.");
1983             }
1984             result = lhs.get_double_value() / rhs_val;
1985         }
1986         result.set_value_format(lhs, rhs);
1987         break;
1988     }
1989     case CdlExprOp_Remainder :
1990     {
1991         // x % y. Both operands must be integral.
1992         CdlSimpleValue lhs;
1993         CdlSimpleValue rhs;
1994         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1995         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1996         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
1997             throw CdlEvalException("Attempt to use the remainder operator on non integral data: `" +
1998                                    lhs.get_value() + "' % `" + rhs.get_value() + "'.");
1999         }
2000         cdl_int rhs_val = rhs.get_integer_value();
2001         if (0 == rhs_val) {
2002             throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' % `" + rhs.get_value() + "'.");
2003         }
2004         result = lhs.get_integer_value() % rhs_val;
2005         result.set_value_format(lhs, rhs);
2006         break;
2007     }
2008     case CdlExprOp_Add :
2009     {
2010         // x + y. For now this only makes sense for numerical data,
2011         // but it is possible to mix and match integer and double
2012         // precision data. Arguably for string data this operator
2013         // should mean concatenation, but it would probably be
2014         // safer to have a separate operator for that.
2015         //
2016         // Strictly speaking the rhs need only be evaluated if it
2017         // is known that the lhs is numeric.
2018         CdlSimpleValue lhs;
2019         CdlSimpleValue rhs;
2020         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2021         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2022         if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2023             (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2024             throw CdlEvalException("Attempt to add non-numerical values: `" + lhs.get_value() + "' + `" +
2025                                    rhs.get_value() + "'.");
2026         }
2027         if (lhs.has_integer_value() && rhs.has_integer_value()) {
2028             result = lhs.get_integer_value() + rhs.get_integer_value();
2029         } else {
2030             result = lhs.get_double_value() + rhs.get_double_value();
2031         }
2032         result.set_value_format(lhs, rhs);
2033         break;
2034     }
2035     case CdlExprOp_Subtract :
2036     {
2037         // x - y. Again only numerical data is supported for now.
2038         CdlSimpleValue lhs;
2039         CdlSimpleValue rhs;
2040         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2041         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2042         if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2043             (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2044             throw CdlEvalException("Attempt to subtract non-numerical values: `" + lhs.get_value() + "' - `" +
2045                                    rhs.get_value() + "'.");
2046         }
2047         if (lhs.has_integer_value() && rhs.has_integer_value()) {
2048             result = lhs.get_integer_value() - rhs.get_integer_value();
2049         } else {
2050             result = lhs.get_double_value() - rhs.get_double_value();
2051         }
2052         result.set_value_format(lhs, rhs);
2053         break;
2054     }
2055     case CdlExprOp_LeftShift :
2056     {
2057         // x << y. Both operands must be integral. For now there is no
2058         // check on the value of y.
2059         CdlSimpleValue lhs;
2060         CdlSimpleValue rhs;
2061         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2062         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2063         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2064             throw CdlEvalException("Attempt to use the left-shift operator on non integral data: `" +
2065                                    lhs.get_value() + "' << `" + rhs.get_value() + "'.");
2066         }
2067         result = lhs.get_integer_value() << rhs.get_integer_value();
2068         result.set_value_format(lhs, rhs);
2069         break;
2070     }
2071     case CdlExprOp_RightShift :
2072     {
2073         // x >> y. Both operands must be integral. For now there is no
2074         // check on the value of y.
2075         CdlSimpleValue lhs;
2076         CdlSimpleValue rhs;
2077         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2078         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2079         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2080             throw CdlEvalException("Attempt to use the right-shift operator on non integral data: `" +
2081                                    lhs.get_value() + "' >> `" + rhs.get_value() + "'.");
2082         }
2083         result = lhs.get_integer_value() >> rhs.get_integer_value();
2084         result.set_value_format(lhs, rhs);
2085         break;
2086     }
2087     case CdlExprOp_LessThan :
2088     case CdlExprOp_LessEqual :
2089     case CdlExprOp_GreaterThan :
2090     case CdlExprOp_GreaterEqual :
2091     {
2092         // x < y, and similar comparison operators. These share
2093         // sufficient code to warrant a common implementation. Only
2094         // numerical data is supported for now. These operator could
2095         // be interpreted as e.g. substring operations, but arguably
2096         // separate operators would be better for that.
2097         CdlSimpleValue lhs;
2098         CdlSimpleValue rhs;
2099         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2100         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2101         if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2102             (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2103             
2104             std::string op_str =
2105                 (CdlExprOp_LessThan    == subexpr.op) ? "<" :
2106                 (CdlExprOp_LessEqual   == subexpr.op) ? "<=" :
2107                 (CdlExprOp_GreaterThan == subexpr.op) ? ">" : ">=";
2108
2109             throw CdlEvalException("Attempt to compare non-numerical values: `" + lhs.get_value() +
2110                                    "' " + op_str + " `" + rhs.get_value() + "'.");
2111         }
2112         bool val = false;
2113         if (lhs.has_integer_value() && rhs.has_integer_value()) {
2114             cdl_int lhs_val = lhs.get_integer_value();
2115             cdl_int rhs_val = rhs.get_integer_value();
2116             val =
2117                 (CdlExprOp_LessThan    == subexpr.op) ? (lhs_val <  rhs_val) :
2118                 (CdlExprOp_LessEqual   == subexpr.op) ? (lhs_val <= rhs_val) :
2119                 (CdlExprOp_GreaterThan == subexpr.op) ? (lhs_val >  rhs_val) : (lhs_val >= rhs_val);
2120         } else {
2121             double lhs_val = lhs.get_double_value();
2122             double rhs_val = rhs.get_double_value();
2123             val =
2124                 (CdlExprOp_LessThan    == subexpr.op) ? (lhs_val <  rhs_val) :
2125                 (CdlExprOp_LessEqual   == subexpr.op) ? (lhs_val <= rhs_val) :
2126                 (CdlExprOp_GreaterThan == subexpr.op) ? (lhs_val >  rhs_val) : (lhs_val >= rhs_val);
2127         }
2128         result = val;
2129         break;
2130     }
2131     case CdlExprOp_Equal :
2132     {
2133         // x == y. For numerical data this should be a numerical comparison.
2134         // Otherwise a string comparison has to be used.
2135         bool val = false;
2136         CdlSimpleValue lhs;
2137         CdlSimpleValue rhs;
2138         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2139         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2140         if ((lhs.has_integer_value() || lhs.has_double_value()) &&
2141             (rhs.has_integer_value() || rhs.has_double_value())) {
2142
2143             if (lhs.has_integer_value() && rhs.has_integer_value()) {
2144                 if (lhs.get_integer_value() == rhs.get_integer_value()) {
2145                     val = true;
2146                 } else {
2147                     val = false;
2148                 }
2149             } else {
2150                 if (lhs.get_double_value() == rhs.get_double_value()) {
2151                     val = true;
2152                 } else {
2153                     val = false;
2154                 }
2155                   
2156             }
2157         } else {
2158             // At least one of the two sides is non-numerical. Do a string comparison.
2159             if (lhs.get_value() == rhs.get_value()) {
2160                 val = true;
2161             } else {
2162                 val = false;
2163             }
2164         }
2165         result = val;
2166         break;
2167     }
2168     case CdlExprOp_NotEqual :
2169     {
2170         // x != y. For numerical data this should be a numerical comparison.
2171         // Otherwise a string comparison has to be used.
2172         bool val = false;
2173         CdlSimpleValue lhs;
2174         CdlSimpleValue rhs;
2175         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2176         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2177         if ((lhs.has_integer_value() || lhs.has_double_value()) &&
2178             (rhs.has_integer_value() || rhs.has_double_value())) {
2179
2180             if (lhs.has_integer_value() && rhs.has_integer_value()) {
2181                 if (lhs.get_integer_value() != rhs.get_integer_value()) {
2182                     val = true;
2183                 } else {
2184                     val = false;
2185                 }
2186             } else {
2187                 if (lhs.get_double_value() != rhs.get_double_value()) {
2188                     val = true;
2189                 } else {
2190                     val = false;
2191                 }
2192                   
2193             }
2194         } else {
2195             // At least one of the two sides is non-numerical. Do a string comparison.
2196             if (lhs.get_value() != rhs.get_value()) {
2197                 val = true;
2198             } else {
2199                 val = false;
2200             }
2201         }
2202         result = val;
2203         break;
2204     }
2205     case CdlExprOp_BitAnd :
2206     {
2207         // x & y. Only integer data is supported.
2208         CdlSimpleValue lhs;
2209         CdlSimpleValue rhs;
2210         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2211         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2212         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2213             throw CdlEvalException("Attempt to use the bitwise and operator on non integral data: `" +
2214                                    lhs.get_value() + "' & `" + rhs.get_value() + "'.");
2215         }
2216         result = lhs.get_integer_value() & rhs.get_integer_value();
2217         result.set_value_format(lhs, rhs);
2218         break;
2219     }
2220     case CdlExprOp_BitXor :
2221     {
2222         // x ^ y. Only integer data is supported.
2223         CdlSimpleValue lhs;
2224         CdlSimpleValue rhs;
2225         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2226         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2227         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2228             throw CdlEvalException("Attempt to use the bitwise xor operator on non integral data: `" +
2229                                    lhs.get_value() + "' ^ `" + rhs.get_value() + "'.");
2230         }
2231         result = lhs.get_integer_value() ^ rhs.get_integer_value();
2232         result.set_value_format(lhs, rhs);
2233         break;
2234     }
2235     case CdlExprOp_BitOr :
2236     {
2237         // x | y. Only integer data is supported.
2238         CdlSimpleValue lhs;
2239         CdlSimpleValue rhs;
2240         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2241         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2242         if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2243             throw CdlEvalException("Attempt to use the bitwise or operator on non integral data: `" +
2244                                    lhs.get_value() + "' | `" + rhs.get_value() + "'.");
2245         }
2246         result = lhs.get_integer_value() | rhs.get_integer_value();
2247         result.set_value_format(lhs, rhs);
2248         break;
2249     }
2250     case CdlExprOp_And :
2251     {
2252         // x && y. Both sides should be interpreted as boolean values,
2253         // and "y" should only be evaluated if necessary.
2254         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2255         if (!result.get_bool_value()) {
2256             result = false;
2257         } else {
2258             evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2259             if (result.get_bool_value()) {
2260                 result = true;
2261             } else {
2262                 result = false;
2263             }
2264         }
2265         break;
2266     }
2267     case CdlExprOp_Or :
2268     {
2269         // x || y. Both sides should be interpreted as boolean values,
2270         // and "y" should only be evaluated if necessary.
2271         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2272         if (result.get_bool_value()) {
2273             result = true;
2274         } else {
2275             evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2276             if (result.get_bool_value()) {
2277                 result = true;
2278             } else {
2279                 result = false;
2280             }
2281         }
2282         break;
2283     }
2284     case CdlExprOp_Xor :
2285     {
2286         // x xor y. Both sides should be interpreted as boolean values.
2287         CdlSimpleValue lhs;
2288         CdlSimpleValue rhs;
2289         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2290         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2291
2292         bool lhs_bool = lhs.get_bool_value();
2293         bool rhs_bool = rhs.get_bool_value();
2294         if ((lhs_bool && !rhs_bool) || (!lhs_bool && rhs_bool)) {
2295             result = true;
2296         } else {
2297             result = false;
2298         }
2299         
2300         break;
2301     }
2302     case CdlExprOp_Eqv :
2303     {
2304         // x eqv y. Both sides should be interpreted as boolean values.
2305         CdlSimpleValue lhs;
2306         CdlSimpleValue rhs;
2307         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2308         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2309
2310         bool lhs_bool = lhs.get_bool_value();
2311         bool rhs_bool = rhs.get_bool_value();
2312         if ((!lhs_bool && !rhs_bool) || (lhs_bool && rhs_bool)) {
2313             result = true;
2314         } else {
2315             result = false;
2316         }
2317         
2318         break;
2319     }
2320     case CdlExprOp_Implies :
2321     {
2322         // x implies y. Both sides should be interpreted as boolean values.
2323         CdlSimpleValue lhs;
2324         CdlSimpleValue rhs;
2325         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2326         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2327
2328         bool lhs_bool = lhs.get_bool_value();
2329         bool rhs_bool = rhs.get_bool_value();
2330         if (!lhs_bool || rhs_bool) {
2331             result = true;
2332         } else {
2333             result = false;
2334         }
2335         
2336         break;
2337     }
2338     case CdlExprOp_Cond :
2339     {
2340         // x ? a : b.
2341         // First evaluate the condition. Then evaluate either the second
2342         // or third argument, as appropriate.
2343         evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2344         if (result.get_bool_value()) {
2345             evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2346         } else {
2347             evaluate_subexpr(context, expr, subexpr.rrhs_index, result);
2348         }
2349         break;
2350     }
2351     case CdlExprOp_StringConcat :
2352     {
2353         // a . b
2354         CdlSimpleValue lhs;
2355         CdlSimpleValue rhs;
2356         evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2357         evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2358         result = lhs.get_value() + rhs.get_value();
2359         break;
2360     }
2361
2362     default:
2363         break;
2364     }
2365
2366     CYG_REPORT_RETURN();
2367 }
2368
2369 // ----------------------------------------------------------------------------
2370 void
2371 CdlExpressionBody::eval_internal(CdlEvalContext& context, CdlSimpleValue& result)
2372 {
2373     CYG_REPORT_FUNCNAME("CdlExpression::eval_internal)");
2374     CYG_REPORT_FUNCARG3XV(this, &context, &result);
2375     CYG_INVARIANT_THISC(CdlExpressionBody);
2376     CYG_PRECONDITION_CLASSOC(context);
2377
2378     evaluate_subexpr(context, this, first_subexpression, result);
2379     
2380     CYG_REPORT_RETURN();
2381 }
2382
2383 void
2384 CdlExpressionBody::eval_subexpression(CdlEvalContext& context, int index, CdlSimpleValue& result)
2385 {
2386     CYG_REPORT_FUNCNAME("CdlExpression::eval_subexpression)");
2387     CYG_REPORT_FUNCARG4XV(this, &context, index, &result);
2388     CYG_INVARIANT_THISC(CdlExpressionBody);
2389     CYG_PRECONDITION_CLASSOC(context);
2390
2391     evaluate_subexpr(context, this, index, result);
2392     
2393     CYG_REPORT_RETURN();
2394 }
2395
2396 //}}}
2397
2398 //{{{  CdlExpression                    
2399
2400 //{{{  Construction                                     
2401
2402 // ----------------------------------------------------------------------------
2403 // Ordinary expressions.
2404 //
2405 // The default constructor is private and does very little. Expressions
2406 // are created primarily by means of the parse() member function. There
2407 // is an argument for having constructors that take the same arguments
2408 // as the parse() member functions and relying on exception handling,
2409 // but that gets tricky for goal expressions and continue_parse().
2410 //
2411 // The copy constructor is protected and is used when creating e.g.
2412 // a default_value property object, which inherits from the ordinary
2413 // expression class. Again it might be better to do the parsing in
2414 // the constructor itself.
2415 //
2416 // The assignment operator is private and illegal.
2417
2418 CdlExpressionBody::CdlExpressionBody()
2419 {
2420     CYG_REPORT_FUNCNAME("CdlExpression:: default constructor");
2421     CYG_REPORT_FUNCARG1XV(this);
2422
2423     expression_string           = "";
2424     first_subexpression         = -1;
2425
2426     cdlexpressionbody_cookie    = CdlExpressionBody_Magic;
2427     CYGDBG_MEMLEAK_CONSTRUCTOR();
2428     
2429     CYG_POSTCONDITION_THISC();
2430     CYG_REPORT_RETURN();
2431 }
2432
2433 CdlExpressionBody::CdlExpressionBody(const CdlExpressionBody& original)
2434 {
2435     CYG_REPORT_FUNCNAME("CdlExpression:: copy constructor");
2436     CYG_REPORT_FUNCARG2XV(this, &original);
2437     CYG_INVARIANT_CLASSOC(CdlExpressionBody, original);
2438
2439     // Sub-expressions are simple structs, so this should result in a bit-wise
2440     // copy of each vector element
2441     sub_expressions     = original.sub_expressions;
2442
2443     // Simple scalar
2444     first_subexpression = original.first_subexpression;
2445
2446     // The CdlReference class has a valid copy constructor and assignment
2447     // operator, provided that the reference is not yet bound. This should
2448     // be true when this copy constructor gets invoked, after parsing
2449     // and during the construction of a derived property object.
2450     references          = original.references;
2451     expression_string   = original.expression_string;
2452
2453     cdlexpressionbody_cookie    = CdlExpressionBody_Magic;
2454     CYGDBG_MEMLEAK_CONSTRUCTOR();
2455     
2456     CYG_POSTCONDITION_THISC();
2457     CYG_REPORT_RETURN();
2458 }
2459
2460 //}}}
2461 //{{{  check_this()                                     
2462
2463 // ----------------------------------------------------------------------------
2464 // check_this(). Expression objects can exist before any parsing has
2465 // happened, not to mention in the middle of parsing. The
2466 // first_subexpression field can be used to detect this.
2467
2468 bool
2469 CdlExpressionBody::check_this(cyg_assert_class_zeal zeal) const
2470 {
2471     if (CdlExpressionBody_Magic != cdlexpressionbody_cookie) {
2472         return false;
2473     }
2474     CYGDBG_MEMLEAK_CHECKTHIS();
2475
2476     if (-1 == first_subexpression) {
2477         return true;
2478     }
2479
2480     switch(zeal) {
2481       case cyg_system_test    :
2482       case cyg_extreme        :
2483       case cyg_thorough       :
2484       {
2485           for (std::vector<CdlReference>::const_iterator i = references.begin(); i != references.end(); i++) {
2486               if (!i->check_this(cyg_quick)) {
2487                   return false;
2488               }
2489           }
2490       }
2491       case cyg_quick          :
2492           if ((unsigned)first_subexpression >= sub_expressions.size()) {
2493               return false;
2494           }
2495       case cyg_trivial        :
2496       case cyg_none           :
2497         break;
2498     }
2499
2500     return true;
2501 }
2502
2503 //}}}
2504 //{{{  Destruction                                      
2505
2506 CdlExpressionBody::~CdlExpressionBody()
2507 {
2508     CYG_REPORT_FUNCNAME("CdlExpression::destructor");
2509     CYG_REPORT_FUNCARG1XV(this);
2510     CYG_PRECONDITION_THISC();
2511
2512     cdlexpressionbody_cookie    = CdlExpressionBody_Invalid;
2513     first_subexpression         = -1;
2514     sub_expressions.clear();
2515     expression_string           = "";
2516
2517     // This assumes that all references have been unbound already by
2518     // higher-level destructors.
2519     references.clear();
2520
2521     CYGDBG_MEMLEAK_DESTRUCTOR();
2522
2523     CYG_REPORT_RETURN();
2524 }
2525
2526 //}}}
2527 //{{{  Parsing - exported interface                     
2528
2529 // ----------------------------------------------------------------------------
2530 // parse(string) invokes parse(string, ...) and checks that the expression
2531 // has terminated with EOD. Parsing of list expressions etc. can terminate
2532 // with some other token.
2533 //
2534 // parse(string, ...) allocates the expression object and invokes
2535 // continue_parse().
2536 //
2537 // continue_parse() is supposed to do all the hard work.
2538
2539 CdlExpression
2540 CdlExpressionBody::parse(std::string data)
2541 {
2542     CYG_REPORT_FUNCNAMETYPE("CdlExpression::parse", "result %p");
2543
2544     CdlExpression       result  = 0;
2545     int                 index   = 0;
2546     CdlExprOp           next_op = CdlExprOp_Invalid;
2547     int                 end_index;
2548
2549     result = parse(data, index, next_op, end_index);
2550     
2551     // Either there has already been a parsing or out-of-memory
2552     // exception, or we should be at the end of the expression string.
2553     if (CdlExprOp_EOD != next_op) {
2554         delete result;
2555         throw CdlParseException("Unexpected data at end of expression.\n" + get_error_location());
2556     }
2557
2558     // Keep a copy of the original string for diagnostics purposes.
2559     result->expression_string = data;
2560
2561     CYG_REPORT_RETVAL(result);
2562     return result;
2563 }
2564
2565 CdlExpression
2566 CdlExpressionBody::parse(std::string data, int& index, CdlExprOp& next_token, int& token_end)
2567 {
2568     CYG_REPORT_FUNCNAMETYPE("CdlExpression::parse", "result %d");
2569
2570     CdlExpression result = new CdlExpressionBody;
2571
2572     try {
2573         continue_parse(result, data, index, next_token, token_end);
2574     }
2575     catch (...) {
2576         delete result;
2577         throw;
2578     }
2579
2580     CYG_REPORT_RETVAL(result);
2581     return result;
2582 }
2583
2584 //}}}
2585 //{{{  update()                                         
2586
2587 // ----------------------------------------------------------------------------
2588 // There has been a change in the toplevel which involves entities being
2589 // created or destroyed, and reference resolution is required.
2590
2591 bool
2592 CdlExpressionBody::update(CdlTransaction transaction, CdlNode source, CdlProperty source_prop, CdlNode dest, CdlUpdate change)
2593 {
2594     CYG_REPORT_FUNCNAMETYPE("CdlExpression::update", "result %d");
2595     CYG_REPORT_FUNCARG6XV(this, transaction, source, source_prop, dest, change);
2596     CYG_PRECONDITION_THISC();
2597     CYG_PRECONDITION_CLASSC(source);
2598     CYG_PRECONDITION_CLASSC(source_prop);
2599
2600     CdlToplevel toplevel        = source->get_toplevel();
2601     bool        result          = false;
2602     std::vector<CdlReference>::iterator ref_i;
2603
2604     switch(change) {
2605       case CdlUpdate_Loaded:
2606       {
2607         // The source package has just been loaded. Try to resolve every
2608         // reference, creating CdlConflict objects where necessary.
2609         CYG_ASSERTC(0 == dest);
2610         for (ref_i = references.begin(); ref_i != references.end(); ref_i++) {
2611             dest = toplevel->lookup(ref_i->get_destination_name());
2612             if (0 == dest) {
2613                 CdlConflict_UnresolvedBody::make(transaction, source, source_prop, ref_i->get_destination_name());
2614             } else {
2615                 ref_i->bind(source, source_prop, dest);
2616             }
2617         }
2618         result = true;
2619         break;
2620       }
2621
2622       case CdlUpdate_Unloading:
2623       {
2624         // The source package is being unloaded. Unbind all currently bound references.
2625         // Also destroy any unresolved conflicts.
2626         CYG_ASSERTC(0 == dest);
2627         for (ref_i = references.begin(); ref_i != references.end(); ref_i++) {
2628             dest = ref_i->get_destination();
2629             if (0 != dest) {
2630                 ref_i->unbind(source, source_prop);
2631             }
2632         }
2633         result = true;
2634         break;
2635       }
2636
2637       case CdlUpdate_Created :
2638       {
2639         
2640         // A previously unresolved reference can now be resolved.
2641         // It is necessary to search the vector for an unresolved
2642         // reference with the desired name, and do the binding.
2643         // This search may fail in the case of list expressions.
2644         CYG_ASSERT_CLASSC(dest);
2645         std::string dest_name = dest->get_name();
2646         for (ref_i = references.begin(); !result && (ref_i != references.end()); ref_i++) {
2647             if ((dest_name == ref_i->get_destination_name()) && (0 == ref_i->get_destination())) {
2648                 ref_i->bind(source, source_prop, dest);
2649                 result = true;
2650                 
2651                 std::vector<CdlConflict> conflicts;
2652                 std::vector<CdlConflict>::iterator conf_i;
2653                 transaction->get_structural_conflicts(source, source_prop, &CdlConflict_UnresolvedBody::test, conflicts);
2654                 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
2655                     CdlConflict_Unresolved real_conf = dynamic_cast<CdlConflict_Unresolved>(*conf_i);
2656                     CYG_ASSERTC(0 != real_conf);
2657                     if (dest_name == real_conf->get_target_name()) {
2658                         transaction->clear_conflict(real_conf);
2659                         break;
2660                     }
2661                 }
2662                 CYG_ASSERTC(conf_i != conflicts.end());
2663             }
2664         }
2665         break;
2666       }
2667
2668       case CdlUpdate_Destroyed :
2669       {
2670         // A previously resolved reference is about to become illegal.
2671         // Search the vector for a resolved reference object matching
2672         // the destination, and unbind it. Also create a conflict
2673         // object. The search can fail in the case of list expressions
2674         CYG_ASSERT_CLASSC(dest);
2675         for (ref_i = references.begin(); !result && (ref_i != references.end()); ref_i++) {
2676             if (dest == ref_i->get_destination()) {
2677                 ref_i->unbind(source, source_prop);
2678                 CdlConflict_UnresolvedBody::make(transaction, source, source_prop, ref_i->get_destination_name());
2679                 result = true;
2680             }
2681         }
2682         break;
2683       }
2684
2685       default :
2686           CYG_FAIL("Illegal change type passed to CdlExpression::update");
2687           break;
2688     }
2689
2690     CYG_REPORT_RETVAL(result);
2691     return result;
2692 }
2693
2694 //}}}
2695 //{{{  Evaluation                                       
2696
2697 // ----------------------------------------------------------------------------
2698 // Expression evaluation. At the end of the day everything filters through
2699 // to eval_internal() which should all the hard work.
2700 //
2701 // The eval() member function handles EvalException conflicts. The
2702 // eval_internal() member function does not, and is used for list
2703 // and goal expressions as well.
2704
2705 void
2706 CdlExpressionBody::eval(CdlEvalContext& context, CdlSimpleValue& result)
2707 {
2708     CYG_REPORT_FUNCNAME("CdlExpression::eval");
2709
2710     try {
2711         
2712         eval_internal(context, result);
2713         
2714         // Evaluation has succeeded, so if there was an EvalException
2715         // conflict get rid of it. This can only happen in the context
2716         // of a transaction.
2717         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
2718             context.transaction->clear_conflicts(context.node, context.property, &CdlConflict_EvalExceptionBody::test);
2719         }
2720         
2721     } catch(CdlEvalException e) {
2722
2723         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
2724
2725             CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
2726                                                                               &CdlConflict_EvalExceptionBody::test);
2727             if (0 == conflict) {
2728                 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
2729             } else {
2730                 
2731                 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
2732                 CYG_ASSERTC(0 != eval_conf);
2733                 if (eval_conf->get_explanation() != e.get_message()) {
2734                     
2735                     // Replace the conflict object. That way higher level code gets informed
2736                     // there has been a change.
2737                     context.transaction->clear_conflict(conflict);
2738                     CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
2739                 }
2740             }
2741         }
2742
2743         throw;
2744     }
2745 }
2746
2747 //}}}
2748 //{{{  Misc                                             
2749
2750 // ----------------------------------------------------------------------------
2751
2752 std::string
2753 CdlExpressionBody::get_original_string() const
2754 {
2755     CYG_REPORT_FUNCNAME("CdlExpression::get_original_string");
2756     CYG_REPORT_FUNCARG1XV(this);
2757     CYG_PRECONDITION_THISC();
2758
2759     CYG_REPORT_RETURN();
2760     return expression_string;
2761 }
2762
2763 //}}}
2764
2765 //}}}
2766 //{{{  CdlListExpression                
2767
2768 //{{{  Construction                             
2769
2770 // ----------------------------------------------------------------------------
2771 // The normal sequence of events is:
2772 //
2773 // 1) higher level code calls CdlListExpressionbody::parse()
2774 // 2) this static member creates a new and empty list expression object.
2775 //    The constructor need not do very much.
2776 // 3) the parse() member then fills in the newly created object
2777 // 4) the object is returned to higher-level code
2778 // 5) usually the list expression will now become part of
2779 //    a property object by means of a copy constructor.
2780 //
2781 // The only complication is that a list expression contains a vector
2782 // of CdlExpression pointers which must be freed during the destructor.
2783 // The copy constructor does not make duplicates of the individual
2784 // expression objects, instead ownership is transferred.
2785
2786 CdlListExpressionBody::CdlListExpressionBody()
2787 {
2788     CYG_REPORT_FUNCNAME("CdlListExpression:: default constructor");
2789     CYG_REPORT_FUNCARG1XV(this);
2790
2791     expression_string           = "";
2792
2793     cdllistexpressionbody_cookie = CdlListExpressionBody_Magic;
2794     CYGDBG_MEMLEAK_CONSTRUCTOR();
2795     
2796     CYG_POSTCONDITION_THISC();
2797     CYG_REPORT_RETURN();
2798 }
2799
2800 CdlListExpressionBody::CdlListExpressionBody(const CdlListExpressionBody& original)
2801 {
2802     CYG_REPORT_FUNCNAME("CdlListExpression:: copy constructor");
2803     CYG_REPORT_FUNCARG2XV(this, &original);
2804     CYG_INVARIANT_CLASSOC(CdlListExpressionBody, original);
2805
2806     expression_string           = original.expression_string;
2807
2808     // These copy across the pointers
2809     data        = original.data;
2810     ranges      = original.ranges;
2811
2812     // And this clears out the pointers, but leaves the expression objects lying around
2813     CdlListExpression tmp = const_cast<CdlListExpression>(&original);
2814     tmp->data.clear();
2815     tmp->ranges.clear();
2816
2817     cdllistexpressionbody_cookie = CdlListExpressionBody_Magic;
2818     CYGDBG_MEMLEAK_CONSTRUCTOR();
2819     
2820     CYG_POSTCONDITION_THISC();
2821     CYG_REPORT_RETURN();
2822 }
2823
2824 //}}}
2825 //{{{  Destruction                              
2826
2827 CdlListExpressionBody::~CdlListExpressionBody()
2828 {
2829     CYG_REPORT_FUNCNAME("CdlListExpression:: destructor");
2830     CYG_REPORT_FUNCARG1XV(this);
2831     CYG_PRECONDITION_THISC();
2832
2833     cdllistexpressionbody_cookie        = CdlListExpressionBody_Invalid;
2834     expression_string                   = "";
2835
2836     for (std::vector<CdlExpression>::iterator i = data.begin(); i != data.end(); i++) {
2837         delete *i;
2838         *i = 0;
2839     }
2840     for (std::vector<std::pair<CdlExpression, CdlExpression> >::iterator j = ranges.begin(); j != ranges.end(); j++) {
2841         delete j->first;
2842         delete j->second;
2843         j->first = 0;
2844         j->second = 0;
2845     }
2846     data.clear();
2847     ranges.clear();
2848     CYGDBG_MEMLEAK_DESTRUCTOR();
2849     
2850     CYG_REPORT_RETURN();
2851 }
2852
2853 //}}}
2854 //{{{  check_this()                             
2855
2856 // ----------------------------------------------------------------------------
2857 bool
2858 CdlListExpressionBody::check_this(cyg_assert_class_zeal zeal) const
2859 {
2860     if (CdlListExpressionBody_Magic != cdllistexpressionbody_cookie) {
2861         return false;
2862     }
2863     CYGDBG_MEMLEAK_CHECKTHIS();
2864     switch(zeal) {
2865       case cyg_system_test      :
2866       case cyg_extreme          :
2867       case cyg_thorough         :
2868       {
2869           for (std::vector<CdlExpression>::const_iterator i = data.begin(); i != data.end(); i++) {
2870               if (!(*i)->check_this(cyg_quick)) {
2871                   return false;
2872               }
2873           }
2874           for (std::vector<std::pair<CdlExpression,CdlExpression> >::const_iterator j = ranges.begin();
2875                j != ranges.end();
2876                j++) {
2877               if (!(j->first->check_this(cyg_quick)) || !(j->second->check_this(cyg_quick))) {
2878                   return false;
2879               }
2880           }
2881       }
2882       case cyg_quick            :
2883       case cyg_trivial          :
2884       case cyg_none             :
2885       default                   :
2886           break;
2887     }
2888
2889     return true;
2890 }
2891
2892 //}}}
2893 //{{{  Parsing                                  
2894
2895 // ----------------------------------------------------------------------------
2896 // Parsing a list expression involves repeated parsing of ordinary
2897 // expressions until an EOD token is reached.
2898
2899 CdlListExpression
2900 CdlListExpressionBody::parse(std::string data)
2901 {
2902     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::parse", "result %p");
2903
2904     // Allocate an expression object that can then be filled in.
2905     CdlListExpression result = new CdlListExpressionBody;
2906
2907     // Do the parsing in a try/catch statement to make sure the
2908     // allocated expression gets freed on a parse error.
2909     try {
2910         int             index           = 0;
2911         int             end_index       = 0;
2912         CdlExprOp       op              = CdlExprOp_Invalid;
2913         CdlExpression   expr1           = 0;
2914
2915         do {
2916             // Try to parse the next expression in the list
2917             op    = CdlExprOp_Invalid;
2918             expr1 = CdlExpressionBody::parse(data, index, op, end_index);
2919
2920             // There should now be a valid expression, failure would have
2921             // resulted in an exception.
2922             CYG_ASSERT_CLASSC(expr1);
2923
2924             // Allow for ranges.
2925             if (CdlExprOp_Range != op) {
2926                 // A simple expression, just add it to the current data vector
2927                 // "index" will contain the appropriate value.
2928                 result->data.push_back(expr1);
2929             } else {
2930                 // A range expression. Get the other end of the range.
2931                 // This requires manipulating index a bit.
2932                 CdlExpression expr2 = 0;
2933                 index = end_index;
2934                 op    = CdlExprOp_Invalid;
2935                 try {
2936                     expr2 = CdlExpressionBody::parse(data, index, op, end_index);
2937                 }
2938                 catch (...) {
2939                     delete expr1;
2940                     throw;
2941                 }
2942                 result->ranges.push_back(std::make_pair(expr1, expr2));
2943             }
2944         } while (CdlExprOp_EOD != op);
2945     }
2946     catch (...) {
2947         delete result;
2948         throw;
2949     }
2950
2951     // Keep track of the original string for diagnostics purposes
2952     result->expression_string = data;
2953     
2954     CYG_REPORT_RETVAL(result);
2955     return result;
2956 }
2957
2958 //}}}
2959 //{{{  update()                                 
2960
2961 // ----------------------------------------------------------------------------
2962 // This code is invoked when it is necessary to update the references
2963 // for the list expression. There are four situations in which this
2964 // can happen: the package has just been loaded; the package is being
2965 // unloaded; a referenced target is being created; a referenced target is
2966 // being destroyed.
2967 //
2968 // The first two cases simply involve processing every expression that
2969 // makes up the overall list expression. The last two cases involve
2970 // searching through the expressions until an applicable one is found.
2971 // Note that an expression may contain multiple references to another
2972 // object, resulting in multiple calls to this function.
2973
2974 bool
2975 CdlListExpressionBody::update(CdlTransaction transact, CdlNode source, CdlProperty source_prop, CdlNode dest, CdlUpdate change)
2976 {
2977     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::update", "result %d");
2978     CYG_REPORT_FUNCARG6XV(this, transact, source, source_prop, dest, change);
2979     CYG_PRECONDITION_THISC();
2980     CYG_PRECONDITION_CLASSC(source);
2981     CYG_PRECONDITION_CLASSC(source_prop);
2982
2983     bool result = false;
2984
2985     if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
2986
2987         std::vector<CdlExpression>::const_iterator expr_i;
2988         std::vector<std::pair<CdlExpression, CdlExpression> >::const_iterator pair_i;
2989
2990         for (expr_i = data.begin(); expr_i != data.end(); expr_i++) {
2991             bool handled = (*expr_i)->update(transact, source, source_prop, dest, change);
2992             CYG_ASSERTC(handled);
2993             CYG_UNUSED_PARAM(bool, handled);
2994         }
2995         for (pair_i = ranges.begin(); pair_i != ranges.end(); pair_i++) {
2996             bool handled = pair_i->first->update(transact, source, source_prop, dest, change);
2997             CYG_ASSERTC(handled);
2998             handled = pair_i->second->update(transact, source, source_prop, dest, change);
2999             CYG_ASSERTC(handled);
3000         }
3001         
3002         result = true;
3003         
3004     } else {
3005         CYG_ASSERTC((CdlUpdate_Created == change) || (CdlUpdate_Destroyed == change));
3006
3007         std::vector<CdlExpression>::const_iterator expr_i;
3008         std::vector<std::pair<CdlExpression, CdlExpression> >::const_iterator pair_i;
3009
3010         for (expr_i = data.begin(); !result && (expr_i != data.end()); expr_i++) {
3011             result = (*expr_i)->update(transact, source, source_prop, dest, change);
3012         }
3013         for (pair_i = ranges.begin(); !result && (pair_i != ranges.end()); pair_i++) {
3014             result = pair_i->first->update(transact, source, source_prop, dest, change);
3015             if (!result) {
3016                 result = pair_i->second->update(transact, source, source_prop, dest, change);
3017             }
3018         }
3019     }
3020     
3021     CYG_REPORT_RETVAL(result);
3022     return result;
3023 }
3024
3025 //}}}
3026 //{{{  Evaluation                               
3027
3028 // ----------------------------------------------------------------------------
3029 // Evaluation. The hard work is actually done in eval_internal()
3030
3031 void
3032 CdlListExpressionBody::eval(CdlEvalContext& context, CdlListValue& result)
3033 {
3034     CYG_REPORT_FUNCNAME("CdlListExpression::eval");
3035     CYG_REPORT_FUNCARG3XV(this, &context, &result);
3036     CYG_PRECONDITION_THISC();
3037     CYG_PRECONDITION_CLASSOC(context);
3038
3039     this->eval_internal(context, result);
3040
3041     CYG_REPORT_RETURN();
3042 }
3043
3044 // ----------------------------------------------------------------------------
3045 // This requires evaluating each expression in the data and ranges
3046 // vectors and adding the result to the appropriate vector in result.
3047 // Various error conditions are possible.
3048
3049 void
3050 CdlListExpressionBody::eval_internal(CdlEvalContext& context, CdlListValue& result)
3051 {
3052     CYG_REPORT_FUNCNAME("CdlListExpression::eval_internal");
3053     CYG_REPORT_FUNCARG2XV(this, &context);
3054
3055     result.table.clear();
3056     result.integer_ranges.clear();
3057     result.double_ranges.clear();
3058
3059     CdlSimpleValue val1;
3060     CdlSimpleValue val2;
3061
3062     try {
3063         for (std::vector<CdlExpression>::const_iterator i = data.begin(); i != data.end(); i++) {
3064             (*i)->eval_internal(context, val1);
3065             if ("" != val1.get_value()) {
3066               result.table.push_back(val1);
3067             }
3068         }
3069         for (std::vector<std::pair<CdlExpression,CdlExpression> >::const_iterator j = ranges.begin(); j != ranges.end(); j++) {
3070             j->first->eval_internal(context, val1);
3071             j->second->eval_internal(context, val2);
3072
3073             if (val1.has_integer_value() && val2.has_integer_value()) {
3074                 cdl_int x1 = val1.get_integer_value();
3075                 cdl_int x2 = val2.get_integer_value();
3076                 if (x1 > x2) {
3077                     cdl_int tmp = x1;
3078                     x1 = x2;
3079                     x2 = tmp;
3080                 }
3081                 result.integer_ranges.push_back(std::make_pair(x1, x2));
3082             } else if (val1.has_double_value() && val2.has_double_value()) {
3083                 double x1 = val1.get_double_value();
3084                 double x2 = val2.get_double_value();
3085                 if (x1 > x2) {
3086                     double tmp = x1;
3087                     x1 = x2;
3088                     x2 = tmp;
3089                 }
3090                 result.double_ranges.push_back(std::make_pair(x1, x2));
3091             } else {
3092                 throw CdlEvalException("Range expression involves non-numerical limits");
3093             }
3094         }
3095         
3096         // Any problems would have resulted in an exception. If there
3097         // was a previous EvalExeption for this property, it is no
3098         // longer applicable
3099         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3100             context.transaction->clear_conflicts(context.node, context.property, &CdlConflict_EvalExceptionBody::test);
3101         }
3102         
3103     } catch(CdlEvalException e) {
3104
3105         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3106             
3107             CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
3108                                                                               &CdlConflict_EvalExceptionBody::test);
3109             if (0 == conflict) {
3110                 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3111             } else {
3112                 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
3113                 CYG_ASSERTC(0 != eval_conf);
3114                 if (eval_conf->get_explanation() != e.get_message()) {
3115                 
3116                     // Replace the conflict object. Higher level will be informed about this.
3117                     context.transaction->clear_conflict(conflict);
3118                     CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3119                 }
3120             }
3121         }
3122
3123         throw;
3124     }
3125     
3126     CYG_REPORT_RETURN();
3127 }
3128
3129 //}}}
3130 //{{{  is_member()                              
3131
3132 // ----------------------------------------------------------------------------
3133
3134 bool
3135 CdlListExpressionBody::is_member(CdlEvalContext& context, CdlSimpleValue& val)
3136 {
3137     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (value)", "result %d");
3138     CYG_REPORT_FUNCARG3XV(this, &context, &val);
3139     CYG_PRECONDITION_THISC();
3140     CYG_PRECONDITION_CLASSOC(context);
3141
3142     bool result = false;
3143     CdlListValue list_val;
3144     eval_internal(context, list_val);
3145     result = list_val.is_member(val);
3146
3147     CYG_REPORT_RETVAL(result);
3148     return result;
3149 }
3150
3151 bool
3152 CdlListExpressionBody::is_member(CdlEvalContext& context, std::string val)
3153 {
3154     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (string)", "result %d");
3155     CYG_REPORT_FUNCARG2XV(this, &context);
3156     CYG_PRECONDITION_THISC();
3157     CYG_PRECONDITION_CLASSOC(context);
3158
3159     bool result = false;
3160     CdlListValue list_val;
3161     eval_internal(context, list_val);
3162     result = list_val.is_member(val);
3163
3164     CYG_REPORT_RETVAL(result);
3165     return result;
3166 }
3167
3168 bool
3169 CdlListExpressionBody::is_member(CdlEvalContext& context, cdl_int val)
3170 {
3171     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (int)", "result %d");
3172     CYG_REPORT_FUNCARG3XV(this, &context, (int) val);
3173     CYG_PRECONDITION_THISC();
3174     CYG_PRECONDITION_CLASSOC(context);
3175
3176     bool result = false;
3177     CdlListValue list_val;
3178     eval_internal(context, list_val);
3179     result = list_val.is_member(val);
3180
3181     CYG_REPORT_RETVAL(result);
3182     return result;
3183 }
3184
3185 bool
3186 CdlListExpressionBody::is_member(CdlEvalContext& context, double val)
3187 {
3188     CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (double)", "result %d");
3189     CYG_REPORT_FUNCARG2XV(this, &context);
3190     CYG_PRECONDITION_THISC();
3191     CYG_PRECONDITION_CLASSOC(context);
3192
3193     bool result = false;
3194     CdlListValue list_val;
3195     eval_internal(context, list_val);
3196     result = list_val.is_member(val);
3197
3198     CYG_REPORT_RETVAL(result);
3199     return result;
3200 }
3201
3202 //}}}
3203 //{{{  Misc                                     
3204
3205 // ----------------------------------------------------------------------------
3206
3207 std::string
3208 CdlListExpressionBody::get_original_string() const
3209 {
3210     CYG_REPORT_FUNCNAME("CdlListExpression::get_original_string");
3211     CYG_REPORT_FUNCARG1XV(this);
3212     CYG_PRECONDITION_THISC();
3213
3214     CYG_REPORT_RETURN();
3215     return expression_string;
3216 }
3217
3218 //}}}
3219
3220 //}}}
3221 //{{{  CdlGoalExpression                
3222
3223 // ----------------------------------------------------------------------------
3224 // Constructors etc. are pretty much as per ordinary and list
3225 // expressions. Most of the work is done in the private base class.
3226
3227 CdlGoalExpressionBody::CdlGoalExpressionBody()
3228     : CdlExpressionBody()
3229 {
3230     CYG_REPORT_FUNCNAME("CdlGoalExpression::default_constructor");
3231     CYG_REPORT_FUNCARG1XV(this);
3232
3233     expression_string           = "";
3234     cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Magic;
3235     CYGDBG_MEMLEAK_CONSTRUCTOR();
3236     
3237     CYG_POSTCONDITION_THISC();
3238     CYG_REPORT_RETURN();
3239 }
3240
3241 CdlGoalExpressionBody::CdlGoalExpressionBody(const CdlGoalExpressionBody& original)
3242     : CdlExpressionBody(original)
3243 {
3244     CYG_REPORT_FUNCNAME("CdlGoalExpression:: copy constructor");
3245     CYG_REPORT_FUNCARG2XV(this, &original);
3246     CYG_INVARIANT_CLASSOC(CdlGoalExpressionBody, original);
3247
3248     expression_string           = original.expression_string;
3249     cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Magic;
3250     CYGDBG_MEMLEAK_CONSTRUCTOR();
3251     
3252     CYG_POSTCONDITION_THISC();
3253     CYG_REPORT_RETURN();
3254 }
3255
3256 CdlGoalExpressionBody::~CdlGoalExpressionBody()
3257 {
3258     CYG_REPORT_FUNCNAME("CdlGoalExpression:: destructor");
3259     CYG_REPORT_FUNCARG1XV(this);
3260     CYG_PRECONDITION_THISC();
3261
3262     cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Invalid;
3263     expression_string            = "";
3264     CYGDBG_MEMLEAK_DESTRUCTOR();
3265
3266     CYG_REPORT_RETURN();
3267 }
3268
3269 // ----------------------------------------------------------------------------
3270 // Parsing. A goal expression acts a bit like a list expression with
3271 // implicit && operators between the various expressions. It could be
3272 // implemented as a vector of expressions (which might make diagnostics
3273 // easier) but it is almost as easy to derive a goal expression from
3274 // an ordinary one.
3275
3276 CdlGoalExpression
3277 CdlGoalExpressionBody::parse(std::string data)
3278 {
3279     CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::parse", "result %p");
3280
3281     CdlGoalExpression result = new CdlGoalExpressionBody;
3282
3283     try {
3284         int       index         = 0;
3285         CdlExprOp op            = CdlExprOp_Invalid;
3286         int       end_index     = 0;
3287
3288         // Parse the first expression in the data.
3289         CdlExpressionBody::continue_parse(result, data, index, op, end_index);
3290
3291         // At this stage we have reached end-of-data or we should be
3292         // at the start of another expression - any binary or ternary
3293         // operands would have been subsumed in the previous expression.
3294         // We need to keep adding && operators and new expressions until
3295         // end-of-data.
3296         while (CdlExprOp_EOD != op) {
3297             op = CdlExprOp_And;
3298             CdlExpressionBody::continue_parse(result, data, index, op, end_index);
3299         }
3300     }
3301     catch(...) {
3302         delete result;
3303         throw;
3304     }
3305
3306     // Keep track of the original expression string for diagnostics purposes
3307     result->expression_string = data;
3308     CYG_REPORT_RETVAL(result);
3309     return result;
3310 }
3311
3312 // ----------------------------------------------------------------------------
3313 void
3314 CdlGoalExpressionBody::eval(CdlEvalContext& context, bool& result)
3315 {
3316     CYG_REPORT_FUNCNAME("CdlGoalExpression::eval");
3317     CYG_REPORT_FUNCARG2XV(this, &context);
3318     CYG_PRECONDITION_THISC();
3319     CYG_PRECONDITION_CLASSOC(context);
3320
3321     eval_internal(context, result);
3322
3323     CYG_REPORT_RETURN();
3324 }
3325
3326 bool
3327 CdlGoalExpressionBody::eval(CdlEvalContext& context)
3328 {
3329     CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::eval", "result %d");
3330     CYG_REPORT_FUNCARG2XV(this, &context);
3331     CYG_PRECONDITION_THISC();
3332     CYG_PRECONDITION_CLASSOC(context);
3333
3334     bool result;
3335     eval_internal(context, result);
3336     
3337     CYG_REPORT_RETVAL(result);
3338     return result;
3339 }
3340
3341 // ----------------------------------------------------------------------------
3342 // Provide access to the underlying CdlExpression object. This allows the
3343 // inference engine etc. to work out why a goal expression is failing
3344
3345 CdlExpression
3346 CdlGoalExpressionBody::get_expression()
3347 {
3348     CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::get_expression", "result %p");
3349     CYG_REPORT_FUNCARG1XV(this);
3350     CYG_PRECONDITION_THISC();
3351
3352     CdlExpression result = this;
3353     CYG_REPORT_RETVAL(result);
3354     return result;
3355 }
3356
3357 // ----------------------------------------------------------------------------
3358
3359 bool
3360 CdlGoalExpressionBody::check_this(cyg_assert_class_zeal zeal) const
3361 {
3362     if (CdlGoalExpressionBody_Magic != cdlgoalexpressionbody_cookie) {
3363         return false;
3364     }
3365     CYGDBG_MEMLEAK_CHECKTHIS();
3366
3367     // There is no data specific to a goal expression, just let the
3368     // underlying check_this() member do its stuff.
3369     
3370     return inherited::check_this(zeal);
3371 }
3372
3373 // ----------------------------------------------------------------------------
3374
3375 std::string
3376 CdlGoalExpressionBody::get_original_string() const
3377 {
3378     CYG_REPORT_FUNCNAME("CdlGoalExpression::get_original_string");
3379     CYG_REPORT_FUNCARG1XV(this);
3380     CYG_PRECONDITION_THISC();
3381
3382     CYG_REPORT_RETURN();
3383     return expression_string;
3384 }
3385
3386 // ----------------------------------------------------------------------------
3387
3388 void
3389 CdlGoalExpressionBody::eval_internal(CdlEvalContext& context, bool& result)
3390 {
3391     CYG_REPORT_FUNCNAME("CdlGoalExpression::eval_internal");
3392     CYG_REPORT_FUNCARG2XV(this, &context);
3393     // The assertions are all done in the calling code
3394
3395     // Start by evaluating the underlying expression
3396     CdlSimpleValue      val;
3397     try {
3398         inherited::eval_internal(context, val);
3399
3400         // The evaluation succeeded. Do we have an integer, a string, ...?
3401         if (val.has_integer_value()) {
3402             result = (0 != val.get_integer_value());
3403         } else if (val.has_double_value()) {
3404             result = (0.0 != val.get_double_value());
3405         } else {
3406             result = ("" != val.get_value());
3407         }
3408
3409         // If there is an EvalException conflict for this property, it is no longer applicable
3410         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3411             context.transaction->clear_conflicts(context.node, context.property,
3412                                                           &CdlConflict_EvalExceptionBody::test);
3413         }
3414         
3415     } catch(CdlEvalException e) {
3416         if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3417             CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
3418                                                                           &CdlConflict_EvalExceptionBody::test);
3419             if (0 == conflict) {
3420                 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3421             } else {
3422                 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
3423                 CYG_ASSERTC(0 != eval_conf);
3424                 if (eval_conf->get_explanation() != e.get_message()) {
3425                     // Replace the conflict object. Higher level can detect this.
3426                     context.transaction->clear_conflict(conflict);
3427                     CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3428                 }
3429             }
3430             throw;
3431         }
3432     }
3433     
3434     CYG_REPORT_RETURN();
3435 }
3436
3437 //}}}