comparison host/libcdl/expr.cxx @ 76:435cced73e2f ecos-v1_3_1-release

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