3 //============================================================================
7 // Implementation of the various CDL expression classes.
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
15 // This file is part of the eCos host tools.
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)
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
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.
31 // ----------------------------------------------------------------------------
33 //####COPYRIGHTEND####
34 //============================================================================
35 //#####DESCRIPTIONBEGIN####
42 //####DESCRIPTIONEND####
43 //============================================================================
48 // ----------------------------------------------------------------------------
49 #include "cdlconfig.h"
51 // Get the infrastructure types, assertions, tracing and similar
53 #include <cyg/infra/cyg_ass.h>
54 #include <cyg/infra/cyg_trac.h>
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>
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);
74 // ----------------------------------------------------------------------------
75 // A utility class to keep track of the context in which expression
76 // evaluation is happening.
78 CdlEvalContext::CdlEvalContext(CdlTransaction transaction_arg, CdlNode node_arg, CdlProperty property_arg,
79 CdlToplevel toplevel_arg)
81 CYG_REPORT_FUNCNAME("CdlEvalContext::constructor");
82 CYG_REPORT_FUNCARG4XV(this, transaction_arg, node_arg, property_arg);
84 transaction = transaction_arg;
86 if ((0 == property_arg) && (0 != transaction)) {
87 CdlConflict conflict = transaction->get_conflict();
89 property_arg = conflict->get_property();
92 property = property_arg;
94 if ((0 == node_arg) && (0 != transaction)) {
95 CdlConflict conflict = transaction->get_conflict();
97 node_arg = conflict->get_node();
102 if (0 == toplevel_arg) {
103 if (0 != transaction) {
104 toplevel_arg = transaction->get_toplevel();
105 } else if (0 != node) {
106 toplevel_arg = node->get_toplevel();
109 toplevel = toplevel_arg;
111 cdlevalcontext_cookie = CdlEvalContext_Magic;
112 CYGDBG_MEMLEAK_CONSTRUCTOR();
114 CYG_POSTCONDITION_THISC();
118 CdlEvalContext::~CdlEvalContext()
120 CYG_REPORT_FUNCNAME("CdlEvalContext::destructor");
121 CYG_PRECONDITION_THISC();
123 cdlevalcontext_cookie = CdlEvalContext_Invalid;
128 CYGDBG_MEMLEAK_DESTRUCTOR();
133 // Given a context and a reference inside an expression, obtain the node
134 // being referenced - if it is loaded.
136 CdlEvalContext::resolve_reference(CdlExpression expr, int index)
138 CYG_REPORT_FUNCNAMETYPE("CdlEvalContext::resolve_reference", "result %");
139 CYG_REPORT_FUNCARG2XV(expr, index);
140 CYG_PRECONDITION_THISC();
141 CYG_PRECONDITION_CLASSC(expr);
142 CYG_PRECONDITIONC((0 <= index) && (index <= (int)expr->references.size()));
144 // This expression may be happening in the context of a particular
145 // property. If so then the destination may or may not be
146 // resolved, which will have been handled when the containing package
147 // was loaded. Alternatively this expression may be evaluated inside
148 // some arbitrary Tcl code, in which case references remain unbound
149 // and need to be resolved the hard way.
151 if (0 != this->property) {
152 // There is a property, use the bound/unbound reference.
153 result = expr->references[index].get_destination();
155 // The destination name can be retrieved, but we still need some
156 // way of resolving it.
157 if (0 != this->toplevel) {
158 std::string destination_name = expr->references[index].get_destination_name();
159 result = this->toplevel->lookup(destination_name);
163 CYG_REPORT_RETVAL(result);
167 // Ditto, but also check that the result is a valuable.
169 CdlEvalContext::resolve_valuable_reference(CdlExpression expr, int index)
171 CYG_REPORT_FUNCNAMETYPE("CdlEvalContext::resolve_reference", "result %");
172 CYG_REPORT_FUNCARG2XV(expr, index);
174 CdlValuable result = 0;
175 CdlNode node = this->resolve_reference(expr, index);
177 result = dynamic_cast<CdlValuable>(node);
179 CYG_REPORT_RETVAL(result);
184 CdlEvalContext::check_this(cyg_assert_class_zeal zeal) const
186 if (CdlEvalContext_Magic != cdlevalcontext_cookie) {
189 CYGDBG_MEMLEAK_CHECKTHIS();
191 if ((0 != transaction) && !transaction->check_this(zeal)) {
194 if ((0 != toplevel) && !toplevel->check_this(zeal)) {
197 if ((0 != node) && !node->check_this(zeal)) {
200 if ((0 != property) && !property->check_this(zeal)) {
207 //{{{ Expression parsing
211 // ----------------------------------------------------------------------------
212 // There are a number of different entry points related to expression parsing,
213 // largely to support list and goal expressions. All of these eventually
214 // end up calling the function
215 // continue_parse(expr, data, index, token, token_end)
217 // The expr argument holds an existing expression object that needs to be
218 // updated. If token is Invalid then we are at the start of an expression
219 // (but not necessarily at the start of the string).
221 // The data string holds all of the expression that should be parsed.
222 // It is formed by concatenating all non-option arguments to the
223 // appropriate property command, with spaces between them.
225 // index is an input/output variable. On input it indicates where in
226 // the string parsing should continue. On output it indicates the
227 // location within the string where the terminating token began.
229 // token is an input/output variable. On input it can have the values
230 // Invalid or And. The former means that we are parsing a completely
231 // new expression. The latter is used for goal expressions: it is
232 // necessary to parse a new expression and then combine it with the
235 // token_end is an output variable. It indicates the location within
236 // the string where the terminating token ended. This is useful for
237 // e.g. ranges in a list expression.
239 // A conventional recursive descent parser is used.
244 // ----------------------------------------------------------------------------
249 // A separate token enum is necessary, rather than re-using the CdlExprOp
250 // enum. Some tokens may correspond to several operators, and some tokens
251 // such as close-bracket do not correspond directly to an operator at all.
256 T_Reference = 1, // CYGPKG_HAL
257 T_String = 2, // "hello"
258 T_Integer = 3, // 123
259 T_Double = 4, // 3.1415
261 T_OpenBracket = 6, // (
262 T_CloseBracket = 7, // )
267 T_Exclamation = 12, // !
269 T_Questionmark = 14, // ?
270 T_Remainder = 15, // %
271 T_LeftShift = 16, // <<
272 T_RightShift = 17, // >>
273 T_LessThan = 18, // <
274 T_LessEqual = 19, // <=
275 T_GreaterThan = 20, // >
276 T_GreaterEqual = 21, // >=
278 T_NotEqual = 23, // !=
284 T_Colon = 29, // : (in a conditional)
285 T_StringConcat = 30, // .
286 T_Function = 31, // is_substr etc.
287 T_Comma = 32, // , (inside a function)
288 T_Implies = 33, // implies
297 // Statics to keep track of the current state.
298 static std::string current_data = "";
299 static unsigned int current_index = 0;
300 static unsigned int token_start = 0;
301 static int current_char = EOF;
302 static token current_token = T_Invalid;
303 static std::string current_string = "";
304 static std::string current_reference = "";
305 static std::string current_special = "";
306 static cdl_int current_int = 0;
307 static double current_double = 0.0;
308 static CdlValueFormat current_format = CdlValueFormat_Default;
309 static int current_function_id = 0;
312 //{{{ Character access
314 // ----------------------------------------------------------------------------
315 // Individual character access.
316 // Note that current_index is one character past current_char.
318 // Return the next character in the string, or EOF
322 if (current_index >= current_data.size()) {
325 current_char = current_data[current_index++];
329 // Go back a character. This is useful when parsing
330 // strings. It is the responsibility of the calling code
331 // to make sure that we are not at the start of the buffer.
335 CYG_ASSERTC(((EOF == current_char) && (0 < current_index)) || (1 < current_index));
336 if (EOF != current_char) {
339 current_char = current_data[current_index - 1];
343 //{{{ get_error_location()
345 // ----------------------------------------------------------------------------
346 // Construct part of a diagnostic message, indicating the
347 // area in the data where the error occurred. This string
348 // is of the form {...data} ^char^ {data...}. Ideally
349 // the ^ markers would be on a subsequent line, eliminating
350 // the need for braces, but there is insufficient control
351 // of how the message gets presented to the user.
353 // Care has to be taken with EOD.
357 CYG_REPORT_FUNCNAME("get_error_location");
358 std::string result = "";
360 // token_start is probably the best place for centering the error.
361 // current_index is past the point where the error has occurred.
362 if (token_start > 1) {
363 if (token_start > 16) {
364 result = "{..." + current_data.substr(token_start - 13, 13) + "} ";
366 result = "{" + current_data.substr(0, token_start) + "}";
370 if (current_char == EOF) {
371 result += " <end of data>";
373 result += " ^" + std::string(1, current_data[token_start]) + "^ ";
376 if (token_start < current_data.size()) {
377 if ((token_start + 16) < current_data.size()) {
378 result += "{" + current_data.substr(token_start + 1, current_data.size() - (token_start+1)) + "}";
380 result += "{" + current_data.substr(token_start, 13) + "...}";
388 // Export this functionality available to other modules, especially func.cxx and its
389 // argument checking routines.
391 CdlParse::get_expression_error_location(void)
393 return get_error_location();
397 //{{{ Token translation
399 // ----------------------------------------------------------------------------
401 // Convert a token into a binary expression operator
403 token_to_binary_expr_op()
405 CYG_REPORT_FUNCNAMETYPE("token_to_expr_op", "op %d");
406 CdlExprOp result = CdlExprOp_Invalid;
408 switch(current_token) {
409 case T_Minus: result = CdlExprOp_Subtract; break;
410 case T_Plus: result = CdlExprOp_Add; break;
411 case T_Times: result = CdlExprOp_Multiply; break;
412 case T_Divide: result = CdlExprOp_Divide; break;
413 case T_Remainder: result = CdlExprOp_Remainder; break;
414 case T_LeftShift: result = CdlExprOp_LeftShift; break;
415 case T_RightShift: result = CdlExprOp_RightShift; break;
416 case T_LessThan: result = CdlExprOp_LessThan; break;
417 case T_LessEqual: result = CdlExprOp_LessEqual; break;
418 case T_GreaterThan: result = CdlExprOp_GreaterThan; break;
419 case T_GreaterEqual: result = CdlExprOp_GreaterEqual; break;
420 case T_Equal: result = CdlExprOp_Equal; break;
421 case T_NotEqual: result = CdlExprOp_NotEqual; break;
422 case T_BitAnd: result = CdlExprOp_BitAnd; break;
423 case T_BitXor: result = CdlExprOp_BitXor; break;
424 case T_BitOr: result = CdlExprOp_BitOr; break;
425 case T_And: result = CdlExprOp_And; break;
426 case T_Or: result = CdlExprOp_Or; break;
427 case T_StringConcat: result = CdlExprOp_StringConcat; break;
428 case T_Implies: result = CdlExprOp_Implies; break;
429 case T_Xor: result = CdlExprOp_Xor; break;
430 case T_Eqv: result = CdlExprOp_Eqv; break;
431 default: result = CdlExprOp_Invalid; break;
434 CYG_REPORT_RETVAL(result);
438 // Convert a token into an ExprOp. This way the internal token enum does
439 // not need to be exported in order to define the interface.
441 // In practice the higher level code will only look for a handful of
442 // cases, mainly EOD and the range operator, but we might as well
443 // do the job property.
447 CYG_REPORT_FUNCNAMETYPE("token_to_expr_op", "expr op %d");
450 // Many of the tokens are already handled for binary operators.
451 result = token_to_binary_expr_op();
452 if (CdlExprOp_Invalid == result) {
453 switch(current_token) {
454 case T_EOD: result = CdlExprOp_EOD; break;
455 case T_Reference: result = CdlExprOp_Reference; break;
456 case T_String: result = CdlExprOp_StringConstant; break;
457 case T_Integer: result = CdlExprOp_IntegerConstant; break;
458 case T_Double: result = CdlExprOp_DoubleConstant; break;
459 case T_Range: result = CdlExprOp_Range; break;
460 case T_Exclamation: result = CdlExprOp_LogicalNot; break;
461 case T_Tilde: result = CdlExprOp_BitNot; break;
463 case T_Colon: result = CdlExprOp_Cond; break; // best guess
464 case T_Function: result = CdlExprOp_Function; break;
468 default: result = CdlExprOp_Invalid; break;
471 CYG_REPORT_RETVAL(result);
475 // A utility routine to turn the current token back into a string
476 // This is used for diagnostics.
480 CYG_REPORT_FUNCNAME("token_to_string");
481 std::string result = "";
483 switch(current_token) {
484 case T_EOD: result = "<end of data>"; break;
485 case T_Reference: result = "reference to " + current_reference; break;
486 case T_String: result = "string \"" + current_string + "\""; break;
490 Cdl::integer_to_string(current_int, tmp, current_format);
491 result = "integer constant " + tmp;
497 Cdl::double_to_string(current_double, tmp, current_format);
498 result = "double constant " + tmp;
501 case T_Range: result = "range operator \"to\""; break;
502 case T_OpenBracket: result = "open bracket ("; break;
503 case T_CloseBracket: result = "close bracket )"; break;
504 case T_Minus: result = "minus sign -"; break;
505 case T_Plus: result = "plus sign +"; break;
506 case T_Times: result = "multiply operator *"; break;
507 case T_Divide: result = "divide operator /"; break;
508 case T_Exclamation: result = "not operator !"; break;
509 case T_Tilde: result = "bitwise not operator ~"; break;
510 case T_Questionmark: result = "question mark ?"; break;
511 case T_Remainder: result = "remainder operator %"; break;
512 case T_LeftShift: result = "left shift operator <<"; break;
513 case T_RightShift: result = "right shift operator >>"; break;
514 case T_LessThan: result = "less-than operator <"; break;
515 case T_LessEqual: result = "less-or-equal operator <="; break;
516 case T_GreaterThan: result = "greater-than operator >"; break;
517 case T_GreaterEqual: result = "greater-or-equal operator >="; break;
518 case T_Equal: result = "equality operator =="; break;
519 case T_NotEqual: result = "not-equal operator !="; break;
520 case T_BitAnd: result = "bitwise and operator &"; break;
521 case T_BitXor: result = "bitwise xor operator ^"; break;
522 case T_BitOr: result = "bitwise or operator |"; break;
523 case T_And: result = "and operator &&"; break;
524 case T_Or: result = "or operator ||"; break;
525 case T_Colon: result = "colon"; break;
526 case T_StringConcat: result = "string concatenation operator ."; break;
527 case T_Implies: result = "implies operator"; break;
528 case T_Xor: result = "logical xor operator"; break;
529 case T_Eqv: result = "logical equivalence operator eqv"; break;
530 case T_Function: result = std::string("function call ") + CdlFunction::get_name(current_function_id); break;
532 default: result = "<invalid token>"; break;
542 // ----------------------------------------------------------------------------
543 //{{{ process_string()
545 // The start of a string has been detected. Work out the entire string,
546 // allowing for backslash escapes.
550 CYG_REPORT_FUNCNAME("process_string");
551 CYG_ASSERTC('"' == current_char);
552 CYG_ASSERTC("" == current_string);
554 std::string result = "";
556 // Move past the leading quote mark.
558 while ('"' != current_char) {
559 if (EOF == current_char) {
560 throw CdlParseException("Premature end of data in string constant.\n" + get_error_location());
561 } else if ('\\' == current_char) {
562 // Allow \a, \b, \f, \n, \r, \t, \v, \ddd and \xhh.
563 // Also copy with \newline space.
564 // Any other character gets passed through unchanged.
566 switch(current_char) {
568 throw CdlParseException("Premature end of data after backslash in string constant.\n" + get_error_location());
594 if (!isxdigit(current_char)) {
595 throw CdlParseException("Non-hexadecimal digit detected in string \\x escape sequence.\n" +
596 get_error_location());
598 // NOTE: there is no overflow detection here.
601 if (('0' <= current_char) && (current_char <= '9')) {
602 tmp += (current_char - '0');
603 } else if (('a' <= current_char) && (current_char <= 'f')) {
604 tmp += 10 + (current_char - 'a');
605 } else if (('A' <= current_char) && (current_char <= 'F')) {
606 tmp += 10 + (current_char - 'A');
608 CYG_FAIL("C library error, isxdigit() succeeded on non-hexadecimal character");
611 } while(isxdigit(current_char));
613 result += (char) tmp;
618 while ((EOF != current_char) && isspace(current_char)) {
621 // We have gone one too far, back up.
627 if (('0' <= current_char) && (current_char <= '7')) {
628 // A sequence of octal digits.
631 tmp = (8 * tmp) + (current_char - '0');
633 } while (('0' <= current_char) && (current_char <= '7'));
635 result += (char) tmp;
637 // For all other backslash sequences, just add the second character
638 result += (char) current_char;
642 result += (char) current_char;
646 // The closing quote has been reached, move past it.
650 current_token = T_String;
651 current_string = result;
657 //{{{ process_number()
659 // The start of a number has been detected. This number may be an
660 // integer or a double. It is necessary to figure out where the number
661 // ends and invoke the appropriate Cdl:: conversion utility.
663 // Care has to be taken with termination. Consider a token such as
664 // 134_5. This is not a string because there are no quote marks, nor
665 // is it a valid reference, and because it begins with a digit it
666 // should be interpreted as a number. The 134 bit works fine, then
667 // number processing stops leaving current_char as '_'. If we are
668 // parsing a list expression then the following _5 will actually
669 // be interpreted as a reference. To avoid this, here is a utility
670 // which checks number completion and throws an exception if
672 static void check_number_termination()
674 CYG_REPORT_FUNCNAME("check_number_termination");
676 // End-of-data or any whitespace is ok.
677 if ((EOF != current_char) && !isspace(current_char)) {
678 // Any valid operator is ok as well, or brackets for that matter.
679 if (('-' != current_char) && ('+' != current_char) && ('*' != current_char) &&
680 ('/' != current_char) && ('!' != current_char) && ('~' != current_char) &&
681 ('?' != current_char) && ('%' != current_char) && ('<' != current_char) &&
682 ('>' != current_char) && ('=' != current_char) && ('&' != current_char) &&
683 ('^' != current_char) && ('|' != current_char) && (':' != current_char) &&
684 ('(' != current_char) && (')' != current_char)) {
687 Cdl::integer_to_string(current_int, tmp);
688 throw CdlParseException("Invalid character detected after number " + tmp + "\n" + get_error_location());
698 CYG_REPORT_FUNCNAME("process_number");
700 std::string tmp = "";
701 bool is_float = false;
703 // Detect the special cases of 0x and octal numbers.
704 if ('0' == current_char) {
706 if (('x' == current_char) || ('X' == current_char)) {
709 if (!isxdigit(current_char)) {
710 throw CdlParseException("Invalid hexadecimal number, expected at least one hexadecimal digit after 0x.\n"
711 + get_error_location());
716 if (('0' <= current_char) && (current_char <= '9')) {
717 current_int += (current_char - '0');
718 } else if (('a' <= current_char) && (current_char <= 'f')) {
719 current_int += 10 + (current_char - 'a');
721 current_int += 10 + (current_char - 'A');
724 } while(isxdigit(current_char));
725 current_token = T_Integer;
726 current_format = CdlValueFormat_Hex;
727 check_number_termination();
731 } else if (('0' <= current_char) && (current_char <= '7')) {
736 current_int += (current_char - '0');
738 } while (('0' <= current_char) && (current_char <= '7'));
739 current_token = T_Integer;
740 current_format = CdlValueFormat_Octal;
741 check_number_termination();
745 } else if (('8' == current_char) || ('9' == current_char)) {
746 throw CdlParseException("08... and 09... are not valid octal numbers.\n" + get_error_location());
748 // This could be plain 0, or 0.123
749 // Backup, and let the rest of the code take care of things
755 tmp += (char) current_char;
757 } while(isdigit(current_char));
759 // If we have found a . then we have a floating point number with a fraction.
760 if ('.' == current_char) {
763 if (!isdigit(current_char)) {
764 throw CdlParseException("Invalid floating point constant, expected a digit for the fractional part.\n" +
765 get_error_location());
769 tmp += (char) current_char;
771 } while(isdigit(current_char));
774 // If we have found e or E then we have a floating point number with an exponent
775 if (('e' == current_char) || ('E' == current_char)) {
778 if (('+' == current_char) || ('-' == current_char)) {
782 if (!isdigit(current_char)) {
783 throw CdlParseException("Invalid floating point constant, expected a digit for the exponent.\n" +
784 get_error_location());
788 tmp += (char) current_char;
790 } while(isdigit(current_char));
794 if (!Cdl::string_to_double(tmp, current_double)) {
795 throw CdlParseException("Invalid floating point constant `" + tmp + "'.\n" + get_error_location());
797 current_token = T_Double;
800 if (!Cdl::string_to_integer(tmp, current_int)) {
801 throw CdlParseException("Invalid integer constant `" + tmp + "'.\n" + get_error_location());
803 current_token = T_Integer;
807 check_number_termination();
812 //{{{ process_alphanumeric()
814 // The start of an alphanumeric sequence has been detected. This may
815 // be a reference, a function call, or an operator like eq or to. All
816 // such sequences must be a valid C preprocessor name, so the only
817 // characters allowed are underscore, upper and lower case characters,
818 // and digits. The first character cannot be a digit, but that has
819 // been checked already.
821 // Some care has to be taken with locale's, the C library may decide
822 // that a character is a letter even though the same character is not
823 // valid as far as the preprocessor is concerned.
825 process_alphanumeric()
827 CYG_REPORT_FUNCNAME("process_alphanumeric");
830 current_reference += (char) current_char;
832 } while (('_' == current_char) || isdigit(current_char) ||
833 (('a' <= current_char) && (current_char <= 'z')) ||
834 (('A' <= current_char) && (current_char <= 'Z')));
840 //{{{ process_special()
842 // Usually an alphanumeric sequence of characters is a reference, e.g.
843 // CYGPKG_KERNEL. However there are only so many special characters
844 // available so some operators are implemented as a sequence, e.g.
845 // "to". CDL also supports functions like is_substr().
847 // The data will have been collected into the current_reference string
848 // by a call to process_alphanumeric().
853 CYG_REPORT_FUNCNAMETYPE("process_special", "special %d");
856 if ("to" == current_reference) {
857 current_token = T_Range;
859 } else if ("implies" == current_reference) {
860 current_token = T_Implies;
862 } else if ("xor" == current_reference) {
863 current_token = T_Xor;
865 } else if ("eqv" == current_reference) {
866 current_token = T_Eqv;
868 } else if (CdlFunction::is_function(current_reference.c_str(), current_function_id)) {
869 current_token = T_Function;
874 current_special = current_reference;
875 current_reference = "";
877 CYG_REPORT_RETVAL(result);
886 // ----------------------------------------------------------------------------
887 // Work out what the next token is. This includes the handling of
888 // strings, integers, doubles, and references.
892 CYG_REPORT_FUNCNAMETYPE("next_token", "token %d");
894 // Make sure there is no dross left lying around from the previous call.
895 current_token = T_Invalid;
897 current_reference = "";
898 current_special = "";
900 current_double = 0.0;
901 current_format = CdlValueFormat_Default;
902 current_function_id = 0;
904 // Skip leading white space. This includes newlines, tabs, etc,
905 // consider the case of:
914 // which is perfectly legitimate. White space inside strings
915 // is handled by the string literal code, and does not get filtered
918 // Exactly which characters are white-space is implementation-defined,
919 // so a special check for EOF is in order.
920 while ((EOF != current_char) && isspace(current_char)) {
924 // Remember the token starting point. next_char() has actually moved
925 // the index on by one.
926 token_start = current_index - 1;
928 // The simple cases can be handled inline, the more complicated cases
929 // involve other functions
930 switch(current_char) {
933 current_token = T_EOD;
941 current_token = T_OpenBracket;
946 current_token = T_CloseBracket;
950 // At this level it is not possible to distinguish between
951 // unary and binary operators, so no attempt is made to
952 // turn - and + into part of a number.
954 current_token = T_Minus;
959 current_token = T_Plus;
964 current_token = T_Times;
969 current_token = T_Divide;
975 if ('=' == current_char) {
976 current_token = T_NotEqual;
979 current_token = T_Exclamation;
984 current_token = T_Tilde;
989 current_token = T_Questionmark;
994 current_token = T_Remainder;
1000 if ('<' == current_char) {
1001 current_token = T_LeftShift;
1003 } else if ('=' == current_char) {
1004 current_token = T_LessEqual;
1007 current_token = T_LessThan;
1013 if ('>' == current_char) {
1014 current_token = T_RightShift;
1016 } else if ('=' == current_char) {
1017 current_token = T_GreaterEqual;
1020 current_token = T_GreaterThan;
1026 if ('=' != current_char) {
1027 throw CdlParseException(std::string("Incomplete == operator in expression.\n") + get_error_location());
1029 current_token = T_Equal;
1036 if ('&' == current_char) {
1037 current_token = T_And;
1040 current_token = T_BitAnd;
1045 current_token = T_BitXor;
1051 if ('|' == current_char) {
1052 current_token = T_Or;
1055 current_token = T_BitOr;
1060 current_token = T_Colon;
1065 current_token = T_StringConcat;
1070 current_token = T_Comma;
1075 // String constants have been handled already. The only
1076 // valid tokens that are left are numbers, references,
1077 // "specials" such as the range and string equality
1078 // operators, and functions.
1080 // Numbers should begin with a digit (plus and minus are
1081 // tokenized separately).
1083 // References must be valid C preprocessor symbols, i.e.
1084 // they must begin with either a letter or an underscore.
1085 // The range operator is handled most conveniently as
1086 // a special case of a reference.
1087 if (isdigit(current_char)) {
1089 } else if (('_' == current_char) ||
1090 (('a' <= current_char) && (current_char <= 'z')) ||
1091 (('A' <= current_char) && (current_char <= 'Z'))) {
1092 process_alphanumeric();
1093 if (!process_special()) {
1094 current_token = T_Reference;
1097 std::string msg = "Unexpected character '";
1098 msg += (char) current_char;
1099 msg += "' in expression.\n";
1100 msg += get_error_location();
1101 throw CdlParseException(msg);
1106 CYG_REPORT_RETVAL(current_token);
1110 //{{{ initialise_tokenisation()
1112 // ----------------------------------------------------------------------------
1113 // This is called at the start of expression parsing. It
1114 // sets up the appropriate statics, and provides initial
1115 // values for current_char and current_token.
1117 initialise_tokenisation(std::string data, int index)
1119 CYG_REPORT_FUNCNAME("initialise_tokenization");
1121 current_data = data;
1122 current_index = static_cast<unsigned int>(index);
1123 token_start = current_index;
1127 CYG_REPORT_RETURN();
1133 //{{{ Syntactic analysis
1135 // ----------------------------------------------------------------------------
1136 // Syntactic analysis.
1138 // The BNF of CDL expressions is something like this:
1140 // <expression> ::= <conditional>
1141 // <conditional> ::= <implies> ? <conditional> : <conditional> | <implies>
1142 // <implies> ::= <eqv> [<implies op> <implies>] implies
1143 // <eqv> ::= <or> [<eqv op> <eqv>] xor, eqv
1144 // <or> ::= <and> [<or op> <or>] ||
1145 // <and> ::= <bitor> [<and op> <and>] &&
1146 // <bitor> ::= <bitxor> [<bitor op> <bitor>] |
1147 // <bitxor> ::= <bitand> [<bitxor op> <bitxor>] ^
1148 // <bitand> ::= <eq> [<bitand op> <and>] &
1149 // <eq> ::= <comp> [<eq op> <eq>] == !=
1150 // <comp> ::= <shift> [<comp op> <comp>] < <= > >=
1151 // <shift> ::= <add> [<shift op> <shift>] << >>
1152 // <add> ::= <mult> [<add op> <add>] + - .
1153 // <mult> ::= <unary> [<mult op> <mult>] * / %
1154 // <unary> ::= -<unary> | +<unary> | !<unary> | *<unary> | ?<unary> |
1156 // <string constant> | <integer constant> |
1157 // <double constant> | <reference> |
1158 // ( <expression> ) | <function>
1160 // There are separate functions for each of these terms.
1162 // A forward declaration, needed for bracketed subexpressions.
1163 static void parse_expression(CdlExpression);
1165 // A utility to add a reference to the current expression, returning
1168 push_reference(CdlExpression expr, const std::string& reference)
1170 CYG_REPORT_FUNCNAMETYPE("push_reference", "new index %d");
1171 CYG_PRECONDITION_CLASSC(expr);
1173 CdlReference ref(reference);
1174 expr->references.push_back(ref);
1175 int result = (int) expr->references.size() - 1;
1177 CYG_REPORT_RETVAL(result);
1181 // A utility to add a subexpression, returning its index.
1183 push_subexpression(CdlExpression expr, const CdlSubexpression& subexpr)
1185 CYG_REPORT_FUNCNAME("push_subexpression");
1186 CYG_PRECONDITION_CLASSC(expr);
1188 expr->sub_expressions.push_back(subexpr);
1189 expr->first_subexpression = ((int) expr->sub_expressions.size()) - 1;
1191 CYG_REPORT_RETURN();
1194 // Another utility to hold of the most recent subexpression
1195 static CdlSubexpression&
1196 current_subexpression(CdlExpression expr)
1198 CYG_REPORT_FUNCNAME("current_subexpression");
1200 CdlSubexpression& result = expr->sub_expressions[expr->first_subexpression];
1202 CYG_REPORT_RETURN();
1207 parse_function(CdlExpression expr)
1209 CYG_REPORT_FUNCNAME("parse_function");
1210 CYG_REPORT_FUNCARG1XV(expr);
1211 CYG_PRECONDITION_CLASSC(expr);
1213 CdlSubexpression subexpr;
1214 subexpr.op = CdlExprOp_Function;
1215 subexpr.func = current_function_id;
1217 int number_of_args = CdlFunction::get_args_count(current_function_id);
1218 CYG_ASSERTC((0 < number_of_args) && (number_of_args <= CdlFunction_MaxArgs));
1219 std::string name = current_special;
1221 // check for the opening bracket: xyzzy(arg1, arg2)
1223 if (T_OpenBracket != current_token) {
1224 throw CdlParseException(std::string("Expected opening bracket after function ") + name + "\n" + get_error_location());
1229 for (i = 0; i < number_of_args; i++) {
1230 parse_expression(expr);
1231 subexpr.args[i] = expr->first_subexpression;
1232 if (i < (number_of_args - 1)) {
1233 if (T_Comma != current_token) {
1234 throw CdlParseException(std::string("Expected comma between arguments in function ") +
1235 name + "\n" + get_error_location());
1240 if (T_Comma == current_token) {
1241 throw CdlParseException(std::string("Too many arguments passed to function ") + name + "\n" + get_error_location());
1243 if (T_CloseBracket != current_token) {
1244 throw CdlParseException(std::string("Expected closing bracket after function ") + name + "\n" + get_error_location());
1248 // Allow the function implementation to check its arguments if it is so inclined.
1249 CdlFunction::check(expr, subexpr);
1251 push_subexpression(expr, subexpr);
1252 CYG_REPORT_RETURN();
1256 parse_unary(CdlExpression expr)
1258 CYG_REPORT_FUNCNAME("parse_operand");
1259 CYG_REPORT_FUNCARG1XV(expr);
1260 CYG_PRECONDITION_CLASSC(expr);
1262 CdlSubexpression subexpr;
1264 switch(current_token) {
1267 // This warrants a special case
1268 throw CdlParseException("End of expression reached when expecting an operand.\n" + get_error_location());
1273 parse_function(expr);
1279 subexpr.op = CdlExprOp_Reference;
1280 subexpr.reference_index = push_reference(expr, current_reference);
1281 push_subexpression(expr, subexpr);
1288 subexpr.op = CdlExprOp_StringConstant;
1289 subexpr.constants = current_string;
1290 push_subexpression(expr, subexpr);
1297 subexpr.op = CdlExprOp_IntegerConstant;
1298 subexpr.constants.set_integer_value(current_int, current_format);
1299 push_subexpression(expr, subexpr);
1306 subexpr.op = CdlExprOp_DoubleConstant;
1307 subexpr.constants.set_double_value(current_double, current_format);
1308 push_subexpression(expr, subexpr);
1313 case T_OpenBracket :
1316 parse_expression(expr);
1317 if (T_CloseBracket != current_token) {
1318 throw CdlParseException("Missing close bracket after subexpression.\n" + get_error_location());
1328 CdlSubexpression& last_sub = current_subexpression(expr);
1329 if (CdlExprOp_IntegerConstant == last_sub.op) {
1330 // Do the negating inline, no need for another subexpression.
1331 last_sub.constants = last_sub.constants.get_integer_value() * -1;
1332 } else if (CdlExprOp_DoubleConstant == last_sub.op) {
1333 last_sub.constants = last_sub.constants.get_double_value() * -1;
1335 // We could detect certain cases such as string constants etc.
1336 // For now don't bother.
1337 subexpr.op = CdlExprOp_Negate;
1338 subexpr.lhs_index = expr->first_subexpression;
1339 push_subexpression(expr, subexpr);
1348 CdlSubexpression& last_sub = current_subexpression(expr);
1349 if ((CdlExprOp_IntegerConstant == last_sub.op) || (CdlExprOp_DoubleConstant == last_sub.op)) {
1350 // No need to do anything here.
1352 subexpr.op = CdlExprOp_Plus;
1353 subexpr.lhs_index = expr->first_subexpression;
1354 push_subexpression(expr, subexpr);
1363 subexpr.op = CdlExprOp_Indirect;
1364 subexpr.lhs_index = expr->first_subexpression;
1365 push_subexpression(expr, subexpr);
1369 case T_Exclamation :
1373 subexpr.op = CdlExprOp_LogicalNot;
1374 subexpr.lhs_index = expr->first_subexpression;
1375 push_subexpression(expr, subexpr);
1383 subexpr.op = CdlExprOp_BitNot;
1384 subexpr.lhs_index = expr->first_subexpression;
1385 push_subexpression(expr, subexpr);
1389 case T_Questionmark:
1391 // This is the `active' operator, it can only be applied directly to a reference.
1394 CdlSubexpression& last_sub = current_subexpression(expr);
1395 if (CdlExprOp_Reference != last_sub.op) {
1396 throw CdlParseException("The active operator ? can only be applied directly to a reference.\n" +
1397 get_error_location());
1399 // There is no point in creating a new subexpression object, just modify
1400 // the existing one. This has the useful side effect of avoiding
1401 // reference substitution in the eval code.
1402 last_sub.op = CdlExprOp_Active;
1407 throw CdlParseException("Unexpected token `" + token_to_string() + "', expecting an operand.\n" +
1408 get_error_location());
1412 CYG_REPORT_RETURN();
1416 parse_multiply(CdlExpression expr)
1418 CYG_REPORT_FUNCNAME("parse_multiply");
1421 while ((T_Times == current_token) || (T_Divide == current_token) || (T_Remainder == current_token)) {
1423 CdlSubexpression subexpr;
1425 (T_Times == current_token) ? CdlExprOp_Multiply :
1426 (T_Divide == current_token) ? CdlExprOp_Divide : CdlExprOp_Remainder;
1427 subexpr.lhs_index = expr->first_subexpression;
1432 subexpr.rhs_index = expr->first_subexpression;
1433 push_subexpression(expr, subexpr);
1436 CYG_REPORT_RETURN();
1440 parse_add(CdlExpression expr)
1442 CYG_REPORT_FUNCNAME("parse_add");
1444 parse_multiply(expr);
1445 while ((T_Plus == current_token) ||
1446 (T_Minus == current_token) ||
1447 (T_StringConcat == current_token)) {
1449 CdlSubexpression subexpr;
1450 subexpr.op = (T_Plus == current_token) ? CdlExprOp_Add :
1451 (T_Minus == current_token) ? CdlExprOp_Subtract :
1452 CdlExprOp_StringConcat;
1453 subexpr.lhs_index = expr->first_subexpression;
1456 parse_multiply(expr);
1458 subexpr.rhs_index = expr->first_subexpression;
1459 push_subexpression(expr, subexpr);
1462 CYG_REPORT_RETURN();
1466 parse_shift(CdlExpression expr)
1468 CYG_REPORT_FUNCNAME("parse_shift");
1471 while ((T_LeftShift == current_token) || (T_RightShift == current_token)) {
1473 CdlSubexpression subexpr;
1474 subexpr.op = (T_LeftShift == current_token) ? CdlExprOp_LeftShift : CdlExprOp_RightShift;
1475 subexpr.lhs_index = expr->first_subexpression;
1480 subexpr.rhs_index = expr->first_subexpression;
1481 push_subexpression(expr, subexpr);
1484 CYG_REPORT_RETURN();
1488 parse_comparison(CdlExpression expr)
1490 CYG_REPORT_FUNCNAME("parse_comparison");
1493 while ((T_LessThan == current_token) || (T_LessEqual == current_token) ||
1494 (T_GreaterThan == current_token) || (T_GreaterEqual == current_token)) {
1496 CdlSubexpression subexpr;
1498 (T_LessThan == current_token) ? CdlExprOp_LessThan :
1499 (T_LessEqual == current_token) ? CdlExprOp_LessEqual :
1500 (T_GreaterThan == current_token) ? CdlExprOp_GreaterThan : CdlExprOp_GreaterEqual;
1501 subexpr.lhs_index = expr->first_subexpression;
1506 subexpr.rhs_index = expr->first_subexpression;
1507 push_subexpression(expr, subexpr);
1510 CYG_REPORT_RETURN();
1514 parse_equals(CdlExpression expr)
1516 CYG_REPORT_FUNCNAME("parse_equals");
1518 parse_comparison(expr);
1519 while ((T_Equal == current_token) ||
1520 (T_NotEqual == current_token)) {
1522 CdlSubexpression subexpr;
1523 subexpr.op = (T_Equal == current_token) ? CdlExprOp_Equal : CdlExprOp_NotEqual;
1524 subexpr.lhs_index = expr->first_subexpression;
1527 parse_comparison(expr);
1529 subexpr.rhs_index = expr->first_subexpression;
1530 push_subexpression(expr, subexpr);
1533 CYG_REPORT_RETURN();
1537 parse_bitand(CdlExpression expr)
1539 CYG_REPORT_FUNCNAME("parse_bitand");
1542 while (T_BitAnd == current_token) {
1544 CdlSubexpression subexpr;
1545 subexpr.op = CdlExprOp_BitAnd;
1546 subexpr.lhs_index = expr->first_subexpression;
1551 subexpr.rhs_index = expr->first_subexpression;
1552 push_subexpression(expr, subexpr);
1555 CYG_REPORT_RETURN();
1559 parse_bitxor(CdlExpression expr)
1561 CYG_REPORT_FUNCNAME("parse_bitxor");
1564 while (T_BitXor == current_token) {
1566 CdlSubexpression subexpr;
1567 subexpr.op = CdlExprOp_BitXor;
1568 subexpr.lhs_index = expr->first_subexpression;
1573 subexpr.rhs_index = expr->first_subexpression;
1574 push_subexpression(expr, subexpr);
1577 CYG_REPORT_RETURN();
1581 parse_bitor(CdlExpression expr)
1583 CYG_REPORT_FUNCNAME("parse_bitor");
1586 while (T_BitOr == current_token) {
1588 CdlSubexpression subexpr;
1589 subexpr.op = CdlExprOp_BitOr;
1590 subexpr.lhs_index = expr->first_subexpression;
1595 subexpr.rhs_index = expr->first_subexpression;
1596 push_subexpression(expr, subexpr);
1599 CYG_REPORT_RETURN();
1603 parse_and(CdlExpression expr)
1605 CYG_REPORT_FUNCNAME("parse_and");
1607 while (T_And == current_token) {
1609 CdlSubexpression subexpr;
1610 subexpr.op = CdlExprOp_And;
1611 subexpr.lhs_index = expr->first_subexpression;
1616 subexpr.rhs_index = expr->first_subexpression;
1617 push_subexpression(expr, subexpr);
1620 CYG_REPORT_RETURN();
1624 parse_or(CdlExpression expr)
1626 CYG_REPORT_FUNCNAME("parse_or");
1629 while (T_Or == current_token) {
1631 CdlSubexpression subexpr;
1632 subexpr.op = CdlExprOp_Or;
1633 subexpr.lhs_index = expr->first_subexpression;
1638 subexpr.rhs_index = expr->first_subexpression;
1639 push_subexpression(expr, subexpr);
1642 CYG_REPORT_RETURN();
1646 parse_eqv(CdlExpression expr)
1648 CYG_REPORT_FUNCNAME("parse_eqv");
1651 while ((T_Xor == current_token) || (T_Eqv == current_token)) {
1653 CdlSubexpression subexpr;
1654 subexpr.op = (T_Xor == current_token) ? CdlExprOp_Xor : CdlExprOp_Eqv;
1655 subexpr.lhs_index = expr->first_subexpression;
1660 subexpr.rhs_index = expr->first_subexpression;
1661 push_subexpression(expr, subexpr);
1664 CYG_REPORT_RETURN();
1668 parse_implies(CdlExpression expr)
1670 CYG_REPORT_FUNCNAME("parse_implies");
1673 while (T_Implies == current_token) {
1675 CdlSubexpression subexpr;
1676 subexpr.op = CdlExprOp_Implies;
1677 subexpr.lhs_index = expr->first_subexpression;
1682 subexpr.rhs_index = expr->first_subexpression;
1683 push_subexpression(expr, subexpr);
1686 CYG_REPORT_RETURN();
1690 parse_conditional(CdlExpression expr)
1692 CYG_REPORT_FUNCNAME("parse_conditional");
1694 parse_implies(expr);
1695 if (T_Questionmark == current_token) {
1696 CdlSubexpression subexpr;
1697 subexpr.op = CdlExprOp_Cond;
1698 subexpr.lhs_index = expr->first_subexpression;
1701 parse_conditional(expr);
1702 subexpr.rhs_index = expr->first_subexpression;
1704 if (T_Colon != current_token) {
1705 throw CdlParseException("Expected colon in conditional expression.\n" + get_error_location());
1709 parse_conditional(expr);
1710 subexpr.rrhs_index = expr->first_subexpression;
1712 push_subexpression(expr, subexpr);
1715 CYG_REPORT_RETURN();
1719 parse_expression(CdlExpression expr)
1721 CYG_REPORT_FUNCNAME("parse_expression");
1723 parse_conditional(expr);
1725 CYG_REPORT_RETURN();
1728 // ----------------------------------------------------------------------------
1731 CdlExpressionBody::continue_parse(CdlExpression expr, std::string data, int& index, CdlExprOp& token, int& token_end)
1733 CYG_REPORT_FUNCNAME("CdlExpression::continue_parse");
1734 CYG_REPORT_FUNCARG1XV(expr);
1735 CYG_PRECONDITION_CLASSC(expr);
1736 CYG_PRECONDITIONC((CdlExprOp_Invalid == token) || (CdlExprOp_And == token));
1738 int current_subexpr = expr->first_subexpression;
1739 initialise_tokenisation(data, index);
1740 parse_expression(expr);
1741 if (CdlExprOp_And == token) {
1742 CdlSubexpression subexpr;
1743 subexpr.op = CdlExprOp_And;
1744 subexpr.lhs_index = current_subexpr;
1745 subexpr.rhs_index = expr->first_subexpression;
1746 push_subexpression(expr, subexpr);
1748 token = token_to_expr_op();
1749 index = token_start;
1750 token_end = current_index;
1752 CYG_REPORT_RETURN();
1758 //{{{ Expression Evaluation
1760 // ----------------------------------------------------------------------------
1761 // Expression evaluation. This always happens in the context of a
1762 // particular toplevel. The parsed expression is held in what amounts
1763 // to a simple tree, so evaluation involves some recursion and a big
1764 // switch statement.
1767 evaluate_subexpr(CdlEvalContext& context, CdlExpression expr, int subexpr_index, CdlSimpleValue& result)
1769 CYG_REPORT_FUNCNAME("evaluate_subexpr");
1770 CYG_REPORT_FUNCARG2XV(expr, subexpr_index);
1771 CYG_ASSERTC((subexpr_index >= 0) && ((unsigned int)subexpr_index < expr->sub_expressions.size()));
1773 const CdlSubexpression& subexpr = expr->sub_expressions[subexpr_index];
1774 switch(subexpr.op) {
1775 case CdlExprOp_StringConstant :
1776 case CdlExprOp_IntegerConstant :
1777 case CdlExprOp_DoubleConstant :
1779 result = subexpr.constants;
1782 case CdlExprOp_Function :
1784 CdlFunction::eval(context, expr, subexpr, result);
1787 case CdlExprOp_Reference :
1789 // This expression may be happening in the context of a particular
1790 // property. If so then the destination may or may not be resolved,
1791 // and this is significant in the context of loading and unloading.
1792 // Alternatively this expression may be being evaluated inside
1793 // some Tcl code, with no particular context.
1794 CdlNode destination = 0;
1795 if (0 != context.property) {
1796 // There is a property, use the bound/unbound reference.
1797 destination = expr->references[subexpr.reference_index].get_destination();
1799 // The destination name can be retrieved, but we still need some
1800 // way of resolving it.
1801 if (0 != context.toplevel) {
1802 std::string destination_name = expr->references[subexpr.reference_index].get_destination_name();
1803 destination = context.toplevel->lookup(destination_name);
1806 if (0 == destination) {
1807 // There are two ways of handling this.
1808 // 1) throw an eval exception, which will usually result
1809 // in a new conflict object
1810 // 2) substitute a value of 0.
1811 // There should already be a conflict object for an
1812 // unresolved reference, and having two conflicts for
1813 // essentially the same error is not useful. Using a value
1814 // of 0 allows things to continue for a bit longer. It is
1815 // consistent with active vs. inactive values, gives
1816 // basically the right result for "requires" properties,
1819 // For now option (2) has it, but this decision may be
1820 // reversed in future.
1823 CdlValuable valuable = dynamic_cast<CdlValuable>(destination);
1824 if (0 == valuable) {
1825 // This is a serious problem, an exception is warranted.
1826 throw CdlEvalException("The expression references `" + destination->get_class_name() + " " +
1827 destination->get_name() + "' which does not have a value.");
1829 CdlSimpleValue::eval_valuable(context, valuable, result);
1834 case CdlExprOp_Negate :
1836 // Unary -. Evaluate the target. If it is numeric, fine. Otherwise
1837 // an error is warranted.
1838 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1839 if (result.has_integer_value()) {
1840 result.set_integer_value(-1 * result.get_integer_value());
1841 } else if (result.has_double_value()) {
1842 result.set_double_value(-1.0 * result.get_double_value());
1844 throw CdlEvalException("Attempt to negate non-numeric value `" + result.get_value() + "'.");
1848 case CdlExprOp_Plus :
1850 // Unary +. Essentially this just checks that the current value is numeric.
1851 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1852 if ((!result.has_integer_value()) && (!result.has_double_value())) {
1853 throw CdlEvalException("Attempt to apply unary + operator to non-numeric value `" + result.get_value() + "'.");
1857 case CdlExprOp_LogicalNot :
1860 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1861 if (result.get_bool_value()) {
1866 result.set_value_format(CdlValueFormat_Default);
1869 case CdlExprOp_BitNot :
1871 // ~x. The operand must be an integer value.
1872 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1873 if (result.has_integer_value()) {
1874 cdl_int tmp = result.get_integer_value();
1877 throw CdlEvalException("Attempt to apply unary ~ operator to non-integer value `" + result.get_value() + "'.");
1881 case CdlExprOp_Indirect :
1883 // *x. The operand must evaluate to a string, and that string should be
1884 // the name of a CdlValuable object.
1885 CdlNode destination = 0;
1886 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
1887 std::string name = result.get_value();
1889 if (0 != context.toplevel) {
1890 destination = context.toplevel->lookup(name);
1892 CYG_FAIL("This situation should probably never happen.");
1895 if (0 == destination) {
1896 throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name +
1897 "', which is not the name of a known CDL entity.");
1899 CdlValuable valuable = dynamic_cast<CdlValuable>(destination);
1900 if (0 == valuable) {
1901 throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name +
1902 "', which does not have a value.");
1904 CdlSimpleValue::eval_valuable(context, valuable, result);
1909 case CdlExprOp_Active :
1911 // ?x. If x is currently unresolved then default to 0.
1912 // See the CdlExprOp_Reference code above for a similar case.
1913 CdlNode destination = 0;
1914 if (0 != context.property) {
1915 destination = expr->references[subexpr.reference_index].get_destination();
1917 if (0 != context.toplevel) {
1918 std::string destination_name = expr->references[subexpr.reference_index].get_destination_name();
1919 destination = context.toplevel->lookup(destination_name);
1923 bool active = false;
1924 if ((0 != destination) && context.transaction->is_active(destination)) {
1934 case CdlExprOp_Multiply :
1936 // x * y. For now this only makes sense for numerical data,
1937 // but it is possible to mix and match integer and double
1940 // Strictly speaking the rhs need only be evaluated if it
1941 // is known that the lhs is numeric.
1944 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1945 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1946 if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
1947 (!(rhs.has_integer_value() || rhs.has_double_value()))) {
1948 throw CdlEvalException("Attempt to multiply non-numerical values: `" + lhs.get_value() + "' * `" +
1949 rhs.get_value() + "'.");
1951 if (lhs.has_integer_value() && rhs.has_integer_value()) {
1952 result = lhs.get_integer_value() * rhs.get_integer_value();
1954 result = lhs.get_double_value() * rhs.get_double_value();
1956 result.set_value_format(lhs, rhs);
1959 case CdlExprOp_Divide :
1961 // x / y. Basically the same as multiplication, apart from a check for
1962 // division by zero.
1965 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1966 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1967 if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
1968 (!(rhs.has_integer_value() || rhs.has_double_value()))) {
1969 throw CdlEvalException("Attempt to divide non-numerical values: `" + lhs.get_value() + "' / `" +
1970 rhs.get_value() + "'.");
1972 if (lhs.has_integer_value() && rhs.has_integer_value()) {
1973 cdl_int rhs_val = rhs.get_integer_value();
1975 throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'.");
1977 result = lhs.get_integer_value() / rhs_val;
1980 double rhs_val = rhs.get_double_value();
1981 if (0.0 == rhs_val) {
1982 throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'.");
1984 result = lhs.get_double_value() / rhs_val;
1986 result.set_value_format(lhs, rhs);
1989 case CdlExprOp_Remainder :
1991 // x % y. Both operands must be integral.
1994 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
1995 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
1996 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
1997 throw CdlEvalException("Attempt to use the remainder operator on non integral data: `" +
1998 lhs.get_value() + "' % `" + rhs.get_value() + "'.");
2000 cdl_int rhs_val = rhs.get_integer_value();
2002 throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' % `" + rhs.get_value() + "'.");
2004 result = lhs.get_integer_value() % rhs_val;
2005 result.set_value_format(lhs, rhs);
2008 case CdlExprOp_Add :
2010 // x + y. For now this only makes sense for numerical data,
2011 // but it is possible to mix and match integer and double
2012 // precision data. Arguably for string data this operator
2013 // should mean concatenation, but it would probably be
2014 // safer to have a separate operator for that.
2016 // Strictly speaking the rhs need only be evaluated if it
2017 // is known that the lhs is numeric.
2020 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2021 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2022 if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2023 (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2024 throw CdlEvalException("Attempt to add non-numerical values: `" + lhs.get_value() + "' + `" +
2025 rhs.get_value() + "'.");
2027 if (lhs.has_integer_value() && rhs.has_integer_value()) {
2028 result = lhs.get_integer_value() + rhs.get_integer_value();
2030 result = lhs.get_double_value() + rhs.get_double_value();
2032 result.set_value_format(lhs, rhs);
2035 case CdlExprOp_Subtract :
2037 // x - y. Again only numerical data is supported for now.
2040 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2041 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2042 if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2043 (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2044 throw CdlEvalException("Attempt to subtract non-numerical values: `" + lhs.get_value() + "' - `" +
2045 rhs.get_value() + "'.");
2047 if (lhs.has_integer_value() && rhs.has_integer_value()) {
2048 result = lhs.get_integer_value() - rhs.get_integer_value();
2050 result = lhs.get_double_value() - rhs.get_double_value();
2052 result.set_value_format(lhs, rhs);
2055 case CdlExprOp_LeftShift :
2057 // x << y. Both operands must be integral. For now there is no
2058 // check on the value of y.
2061 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2062 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2063 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2064 throw CdlEvalException("Attempt to use the left-shift operator on non integral data: `" +
2065 lhs.get_value() + "' << `" + rhs.get_value() + "'.");
2067 result = lhs.get_integer_value() << rhs.get_integer_value();
2068 result.set_value_format(lhs, rhs);
2071 case CdlExprOp_RightShift :
2073 // x >> y. Both operands must be integral. For now there is no
2074 // check on the value of y.
2077 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2078 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2079 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2080 throw CdlEvalException("Attempt to use the right-shift operator on non integral data: `" +
2081 lhs.get_value() + "' >> `" + rhs.get_value() + "'.");
2083 result = lhs.get_integer_value() >> rhs.get_integer_value();
2084 result.set_value_format(lhs, rhs);
2087 case CdlExprOp_LessThan :
2088 case CdlExprOp_LessEqual :
2089 case CdlExprOp_GreaterThan :
2090 case CdlExprOp_GreaterEqual :
2092 // x < y, and similar comparison operators. These share
2093 // sufficient code to warrant a common implementation. Only
2094 // numerical data is supported for now. These operator could
2095 // be interpreted as e.g. substring operations, but arguably
2096 // separate operators would be better for that.
2099 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2100 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2101 if ((!(lhs.has_integer_value() || lhs.has_double_value())) ||
2102 (!(rhs.has_integer_value() || rhs.has_double_value()))) {
2104 std::string op_str =
2105 (CdlExprOp_LessThan == subexpr.op) ? "<" :
2106 (CdlExprOp_LessEqual == subexpr.op) ? "<=" :
2107 (CdlExprOp_GreaterThan == subexpr.op) ? ">" : ">=";
2109 throw CdlEvalException("Attempt to compare non-numerical values: `" + lhs.get_value() +
2110 "' " + op_str + " `" + rhs.get_value() + "'.");
2113 if (lhs.has_integer_value() && rhs.has_integer_value()) {
2114 cdl_int lhs_val = lhs.get_integer_value();
2115 cdl_int rhs_val = rhs.get_integer_value();
2117 (CdlExprOp_LessThan == subexpr.op) ? (lhs_val < rhs_val) :
2118 (CdlExprOp_LessEqual == subexpr.op) ? (lhs_val <= rhs_val) :
2119 (CdlExprOp_GreaterThan == subexpr.op) ? (lhs_val > rhs_val) : (lhs_val >= rhs_val);
2121 double lhs_val = lhs.get_double_value();
2122 double rhs_val = rhs.get_double_value();
2124 (CdlExprOp_LessThan == subexpr.op) ? (lhs_val < rhs_val) :
2125 (CdlExprOp_LessEqual == subexpr.op) ? (lhs_val <= rhs_val) :
2126 (CdlExprOp_GreaterThan == subexpr.op) ? (lhs_val > rhs_val) : (lhs_val >= rhs_val);
2131 case CdlExprOp_Equal :
2133 // x == y. For numerical data this should be a numerical comparison.
2134 // Otherwise a string comparison has to be used.
2138 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2139 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2140 if ((lhs.has_integer_value() || lhs.has_double_value()) &&
2141 (rhs.has_integer_value() || rhs.has_double_value())) {
2143 if (lhs.has_integer_value() && rhs.has_integer_value()) {
2144 if (lhs.get_integer_value() == rhs.get_integer_value()) {
2150 if (lhs.get_double_value() == rhs.get_double_value()) {
2158 // At least one of the two sides is non-numerical. Do a string comparison.
2159 if (lhs.get_value() == rhs.get_value()) {
2168 case CdlExprOp_NotEqual :
2170 // x != y. For numerical data this should be a numerical comparison.
2171 // Otherwise a string comparison has to be used.
2175 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2176 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2177 if ((lhs.has_integer_value() || lhs.has_double_value()) &&
2178 (rhs.has_integer_value() || rhs.has_double_value())) {
2180 if (lhs.has_integer_value() && rhs.has_integer_value()) {
2181 if (lhs.get_integer_value() != rhs.get_integer_value()) {
2187 if (lhs.get_double_value() != rhs.get_double_value()) {
2195 // At least one of the two sides is non-numerical. Do a string comparison.
2196 if (lhs.get_value() != rhs.get_value()) {
2205 case CdlExprOp_BitAnd :
2207 // x & y. Only integer data is supported.
2210 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2211 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2212 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2213 throw CdlEvalException("Attempt to use the bitwise and operator on non integral data: `" +
2214 lhs.get_value() + "' & `" + rhs.get_value() + "'.");
2216 result = lhs.get_integer_value() & rhs.get_integer_value();
2217 result.set_value_format(lhs, rhs);
2220 case CdlExprOp_BitXor :
2222 // x ^ y. Only integer data is supported.
2225 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2226 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2227 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2228 throw CdlEvalException("Attempt to use the bitwise xor operator on non integral data: `" +
2229 lhs.get_value() + "' ^ `" + rhs.get_value() + "'.");
2231 result = lhs.get_integer_value() ^ rhs.get_integer_value();
2232 result.set_value_format(lhs, rhs);
2235 case CdlExprOp_BitOr :
2237 // x | y. Only integer data is supported.
2240 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2241 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2242 if (!(lhs.has_integer_value() && rhs.has_integer_value())) {
2243 throw CdlEvalException("Attempt to use the bitwise or operator on non integral data: `" +
2244 lhs.get_value() + "' | `" + rhs.get_value() + "'.");
2246 result = lhs.get_integer_value() | rhs.get_integer_value();
2247 result.set_value_format(lhs, rhs);
2250 case CdlExprOp_And :
2252 // x && y. Both sides should be interpreted as boolean values,
2253 // and "y" should only be evaluated if necessary.
2254 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2255 if (!result.get_bool_value()) {
2258 evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2259 if (result.get_bool_value()) {
2269 // x || y. Both sides should be interpreted as boolean values,
2270 // and "y" should only be evaluated if necessary.
2271 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2272 if (result.get_bool_value()) {
2275 evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2276 if (result.get_bool_value()) {
2284 case CdlExprOp_Xor :
2286 // x xor y. Both sides should be interpreted as boolean values.
2289 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2290 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2292 bool lhs_bool = lhs.get_bool_value();
2293 bool rhs_bool = rhs.get_bool_value();
2294 if ((lhs_bool && !rhs_bool) || (!lhs_bool && rhs_bool)) {
2302 case CdlExprOp_Eqv :
2304 // x eqv y. Both sides should be interpreted as boolean values.
2307 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2308 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2310 bool lhs_bool = lhs.get_bool_value();
2311 bool rhs_bool = rhs.get_bool_value();
2312 if ((!lhs_bool && !rhs_bool) || (lhs_bool && rhs_bool)) {
2320 case CdlExprOp_Implies :
2322 // x implies y. Both sides should be interpreted as boolean values.
2325 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2326 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2328 bool lhs_bool = lhs.get_bool_value();
2329 bool rhs_bool = rhs.get_bool_value();
2330 if (!lhs_bool || rhs_bool) {
2338 case CdlExprOp_Cond :
2341 // First evaluate the condition. Then evaluate either the second
2342 // or third argument, as appropriate.
2343 evaluate_subexpr(context, expr, subexpr.lhs_index, result);
2344 if (result.get_bool_value()) {
2345 evaluate_subexpr(context, expr, subexpr.rhs_index, result);
2347 evaluate_subexpr(context, expr, subexpr.rrhs_index, result);
2351 case CdlExprOp_StringConcat :
2356 evaluate_subexpr(context, expr, subexpr.lhs_index, lhs);
2357 evaluate_subexpr(context, expr, subexpr.rhs_index, rhs);
2358 result = lhs.get_value() + rhs.get_value();
2366 CYG_REPORT_RETURN();
2369 // ----------------------------------------------------------------------------
2371 CdlExpressionBody::eval_internal(CdlEvalContext& context, CdlSimpleValue& result)
2373 CYG_REPORT_FUNCNAME("CdlExpression::eval_internal)");
2374 CYG_REPORT_FUNCARG3XV(this, &context, &result);
2375 CYG_INVARIANT_THISC(CdlExpressionBody);
2376 CYG_PRECONDITION_CLASSOC(context);
2378 evaluate_subexpr(context, this, first_subexpression, result);
2380 CYG_REPORT_RETURN();
2384 CdlExpressionBody::eval_subexpression(CdlEvalContext& context, int index, CdlSimpleValue& result)
2386 CYG_REPORT_FUNCNAME("CdlExpression::eval_subexpression)");
2387 CYG_REPORT_FUNCARG4XV(this, &context, index, &result);
2388 CYG_INVARIANT_THISC(CdlExpressionBody);
2389 CYG_PRECONDITION_CLASSOC(context);
2391 evaluate_subexpr(context, this, index, result);
2393 CYG_REPORT_RETURN();
2402 // ----------------------------------------------------------------------------
2403 // Ordinary expressions.
2405 // The default constructor is private and does very little. Expressions
2406 // are created primarily by means of the parse() member function. There
2407 // is an argument for having constructors that take the same arguments
2408 // as the parse() member functions and relying on exception handling,
2409 // but that gets tricky for goal expressions and continue_parse().
2411 // The copy constructor is protected and is used when creating e.g.
2412 // a default_value property object, which inherits from the ordinary
2413 // expression class. Again it might be better to do the parsing in
2414 // the constructor itself.
2416 // The assignment operator is private and illegal.
2418 CdlExpressionBody::CdlExpressionBody()
2420 CYG_REPORT_FUNCNAME("CdlExpression:: default constructor");
2421 CYG_REPORT_FUNCARG1XV(this);
2423 expression_string = "";
2424 first_subexpression = -1;
2426 cdlexpressionbody_cookie = CdlExpressionBody_Magic;
2427 CYGDBG_MEMLEAK_CONSTRUCTOR();
2429 CYG_POSTCONDITION_THISC();
2430 CYG_REPORT_RETURN();
2433 CdlExpressionBody::CdlExpressionBody(const CdlExpressionBody& original)
2435 CYG_REPORT_FUNCNAME("CdlExpression:: copy constructor");
2436 CYG_REPORT_FUNCARG2XV(this, &original);
2437 CYG_INVARIANT_CLASSOC(CdlExpressionBody, original);
2439 // Sub-expressions are simple structs, so this should result in a bit-wise
2440 // copy of each vector element
2441 sub_expressions = original.sub_expressions;
2444 first_subexpression = original.first_subexpression;
2446 // The CdlReference class has a valid copy constructor and assignment
2447 // operator, provided that the reference is not yet bound. This should
2448 // be true when this copy constructor gets invoked, after parsing
2449 // and during the construction of a derived property object.
2450 references = original.references;
2451 expression_string = original.expression_string;
2453 cdlexpressionbody_cookie = CdlExpressionBody_Magic;
2454 CYGDBG_MEMLEAK_CONSTRUCTOR();
2456 CYG_POSTCONDITION_THISC();
2457 CYG_REPORT_RETURN();
2463 // ----------------------------------------------------------------------------
2464 // check_this(). Expression objects can exist before any parsing has
2465 // happened, not to mention in the middle of parsing. The
2466 // first_subexpression field can be used to detect this.
2469 CdlExpressionBody::check_this(cyg_assert_class_zeal zeal) const
2471 if (CdlExpressionBody_Magic != cdlexpressionbody_cookie) {
2474 CYGDBG_MEMLEAK_CHECKTHIS();
2476 if (-1 == first_subexpression) {
2481 case cyg_system_test :
2485 for (std::vector<CdlReference>::const_iterator i = references.begin(); i != references.end(); i++) {
2486 if (!i->check_this(cyg_quick)) {
2492 if ((unsigned)first_subexpression >= sub_expressions.size()) {
2506 CdlExpressionBody::~CdlExpressionBody()
2508 CYG_REPORT_FUNCNAME("CdlExpression::destructor");
2509 CYG_REPORT_FUNCARG1XV(this);
2510 CYG_PRECONDITION_THISC();
2512 cdlexpressionbody_cookie = CdlExpressionBody_Invalid;
2513 first_subexpression = -1;
2514 sub_expressions.clear();
2515 expression_string = "";
2517 // This assumes that all references have been unbound already by
2518 // higher-level destructors.
2521 CYGDBG_MEMLEAK_DESTRUCTOR();
2523 CYG_REPORT_RETURN();
2527 //{{{ Parsing - exported interface
2529 // ----------------------------------------------------------------------------
2530 // parse(string) invokes parse(string, ...) and checks that the expression
2531 // has terminated with EOD. Parsing of list expressions etc. can terminate
2532 // with some other token.
2534 // parse(string, ...) allocates the expression object and invokes
2535 // continue_parse().
2537 // continue_parse() is supposed to do all the hard work.
2540 CdlExpressionBody::parse(std::string data)
2542 CYG_REPORT_FUNCNAMETYPE("CdlExpression::parse", "result %p");
2544 CdlExpression result = 0;
2546 CdlExprOp next_op = CdlExprOp_Invalid;
2549 result = parse(data, index, next_op, end_index);
2551 // Either there has already been a parsing or out-of-memory
2552 // exception, or we should be at the end of the expression string.
2553 if (CdlExprOp_EOD != next_op) {
2555 throw CdlParseException("Unexpected data at end of expression.\n" + get_error_location());
2558 // Keep a copy of the original string for diagnostics purposes.
2559 result->expression_string = data;
2561 CYG_REPORT_RETVAL(result);
2566 CdlExpressionBody::parse(std::string data, int& index, CdlExprOp& next_token, int& token_end)
2568 CYG_REPORT_FUNCNAMETYPE("CdlExpression::parse", "result %d");
2570 CdlExpression result = new CdlExpressionBody;
2573 continue_parse(result, data, index, next_token, token_end);
2580 CYG_REPORT_RETVAL(result);
2587 // ----------------------------------------------------------------------------
2588 // There has been a change in the toplevel which involves entities being
2589 // created or destroyed, and reference resolution is required.
2592 CdlExpressionBody::update(CdlTransaction transaction, CdlNode source, CdlProperty source_prop, CdlNode dest, CdlUpdate change)
2594 CYG_REPORT_FUNCNAMETYPE("CdlExpression::update", "result %d");
2595 CYG_REPORT_FUNCARG6XV(this, transaction, source, source_prop, dest, change);
2596 CYG_PRECONDITION_THISC();
2597 CYG_PRECONDITION_CLASSC(source);
2598 CYG_PRECONDITION_CLASSC(source_prop);
2600 CdlToplevel toplevel = source->get_toplevel();
2601 bool result = false;
2602 std::vector<CdlReference>::iterator ref_i;
2605 case CdlUpdate_Loaded:
2607 // The source package has just been loaded. Try to resolve every
2608 // reference, creating CdlConflict objects where necessary.
2609 CYG_ASSERTC(0 == dest);
2610 for (ref_i = references.begin(); ref_i != references.end(); ref_i++) {
2611 dest = toplevel->lookup(ref_i->get_destination_name());
2613 CdlConflict_UnresolvedBody::make(transaction, source, source_prop, ref_i->get_destination_name());
2615 ref_i->bind(source, source_prop, dest);
2622 case CdlUpdate_Unloading:
2624 // The source package is being unloaded. Unbind all currently bound references.
2625 // Also destroy any unresolved conflicts.
2626 CYG_ASSERTC(0 == dest);
2627 for (ref_i = references.begin(); ref_i != references.end(); ref_i++) {
2628 dest = ref_i->get_destination();
2630 ref_i->unbind(source, source_prop);
2637 case CdlUpdate_Created :
2640 // A previously unresolved reference can now be resolved.
2641 // It is necessary to search the vector for an unresolved
2642 // reference with the desired name, and do the binding.
2643 // This search may fail in the case of list expressions.
2644 CYG_ASSERT_CLASSC(dest);
2645 std::string dest_name = dest->get_name();
2646 for (ref_i = references.begin(); !result && (ref_i != references.end()); ref_i++) {
2647 if ((dest_name == ref_i->get_destination_name()) && (0 == ref_i->get_destination())) {
2648 ref_i->bind(source, source_prop, dest);
2651 std::vector<CdlConflict> conflicts;
2652 std::vector<CdlConflict>::iterator conf_i;
2653 transaction->get_structural_conflicts(source, source_prop, &CdlConflict_UnresolvedBody::test, conflicts);
2654 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
2655 CdlConflict_Unresolved real_conf = dynamic_cast<CdlConflict_Unresolved>(*conf_i);
2656 CYG_ASSERTC(0 != real_conf);
2657 if (dest_name == real_conf->get_target_name()) {
2658 transaction->clear_conflict(real_conf);
2662 CYG_ASSERTC(conf_i != conflicts.end());
2668 case CdlUpdate_Destroyed :
2670 // A previously resolved reference is about to become illegal.
2671 // Search the vector for a resolved reference object matching
2672 // the destination, and unbind it. Also create a conflict
2673 // object. The search can fail in the case of list expressions
2674 CYG_ASSERT_CLASSC(dest);
2675 for (ref_i = references.begin(); !result && (ref_i != references.end()); ref_i++) {
2676 if (dest == ref_i->get_destination()) {
2677 ref_i->unbind(source, source_prop);
2678 CdlConflict_UnresolvedBody::make(transaction, source, source_prop, ref_i->get_destination_name());
2686 CYG_FAIL("Illegal change type passed to CdlExpression::update");
2690 CYG_REPORT_RETVAL(result);
2697 // ----------------------------------------------------------------------------
2698 // Expression evaluation. At the end of the day everything filters through
2699 // to eval_internal() which should all the hard work.
2701 // The eval() member function handles EvalException conflicts. The
2702 // eval_internal() member function does not, and is used for list
2703 // and goal expressions as well.
2706 CdlExpressionBody::eval(CdlEvalContext& context, CdlSimpleValue& result)
2708 CYG_REPORT_FUNCNAME("CdlExpression::eval");
2712 eval_internal(context, result);
2714 // Evaluation has succeeded, so if there was an EvalException
2715 // conflict get rid of it. This can only happen in the context
2716 // of a transaction.
2717 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
2718 context.transaction->clear_conflicts(context.node, context.property, &CdlConflict_EvalExceptionBody::test);
2721 } catch(CdlEvalException e) {
2723 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
2725 CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
2726 &CdlConflict_EvalExceptionBody::test);
2727 if (0 == conflict) {
2728 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
2731 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
2732 CYG_ASSERTC(0 != eval_conf);
2733 if (eval_conf->get_explanation() != e.get_message()) {
2735 // Replace the conflict object. That way higher level code gets informed
2736 // there has been a change.
2737 context.transaction->clear_conflict(conflict);
2738 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
2750 // ----------------------------------------------------------------------------
2753 CdlExpressionBody::get_original_string() const
2755 CYG_REPORT_FUNCNAME("CdlExpression::get_original_string");
2756 CYG_REPORT_FUNCARG1XV(this);
2757 CYG_PRECONDITION_THISC();
2759 CYG_REPORT_RETURN();
2760 return expression_string;
2766 //{{{ CdlListExpression
2770 // ----------------------------------------------------------------------------
2771 // The normal sequence of events is:
2773 // 1) higher level code calls CdlListExpressionbody::parse()
2774 // 2) this static member creates a new and empty list expression object.
2775 // The constructor need not do very much.
2776 // 3) the parse() member then fills in the newly created object
2777 // 4) the object is returned to higher-level code
2778 // 5) usually the list expression will now become part of
2779 // a property object by means of a copy constructor.
2781 // The only complication is that a list expression contains a vector
2782 // of CdlExpression pointers which must be freed during the destructor.
2783 // The copy constructor does not make duplicates of the individual
2784 // expression objects, instead ownership is transferred.
2786 CdlListExpressionBody::CdlListExpressionBody()
2788 CYG_REPORT_FUNCNAME("CdlListExpression:: default constructor");
2789 CYG_REPORT_FUNCARG1XV(this);
2791 expression_string = "";
2793 cdllistexpressionbody_cookie = CdlListExpressionBody_Magic;
2794 CYGDBG_MEMLEAK_CONSTRUCTOR();
2796 CYG_POSTCONDITION_THISC();
2797 CYG_REPORT_RETURN();
2800 CdlListExpressionBody::CdlListExpressionBody(const CdlListExpressionBody& original)
2802 CYG_REPORT_FUNCNAME("CdlListExpression:: copy constructor");
2803 CYG_REPORT_FUNCARG2XV(this, &original);
2804 CYG_INVARIANT_CLASSOC(CdlListExpressionBody, original);
2806 expression_string = original.expression_string;
2808 // These copy across the pointers
2809 data = original.data;
2810 ranges = original.ranges;
2812 // And this clears out the pointers, but leaves the expression objects lying around
2813 CdlListExpression tmp = const_cast<CdlListExpression>(&original);
2815 tmp->ranges.clear();
2817 cdllistexpressionbody_cookie = CdlListExpressionBody_Magic;
2818 CYGDBG_MEMLEAK_CONSTRUCTOR();
2820 CYG_POSTCONDITION_THISC();
2821 CYG_REPORT_RETURN();
2827 CdlListExpressionBody::~CdlListExpressionBody()
2829 CYG_REPORT_FUNCNAME("CdlListExpression:: destructor");
2830 CYG_REPORT_FUNCARG1XV(this);
2831 CYG_PRECONDITION_THISC();
2833 cdllistexpressionbody_cookie = CdlListExpressionBody_Invalid;
2834 expression_string = "";
2836 for (std::vector<CdlExpression>::iterator i = data.begin(); i != data.end(); i++) {
2840 for (std::vector<std::pair<CdlExpression, CdlExpression> >::iterator j = ranges.begin(); j != ranges.end(); j++) {
2848 CYGDBG_MEMLEAK_DESTRUCTOR();
2850 CYG_REPORT_RETURN();
2856 // ----------------------------------------------------------------------------
2858 CdlListExpressionBody::check_this(cyg_assert_class_zeal zeal) const
2860 if (CdlListExpressionBody_Magic != cdllistexpressionbody_cookie) {
2863 CYGDBG_MEMLEAK_CHECKTHIS();
2865 case cyg_system_test :
2869 for (std::vector<CdlExpression>::const_iterator i = data.begin(); i != data.end(); i++) {
2870 if (!(*i)->check_this(cyg_quick)) {
2874 for (std::vector<std::pair<CdlExpression,CdlExpression> >::const_iterator j = ranges.begin();
2877 if (!(j->first->check_this(cyg_quick)) || !(j->second->check_this(cyg_quick))) {
2895 // ----------------------------------------------------------------------------
2896 // Parsing a list expression involves repeated parsing of ordinary
2897 // expressions until an EOD token is reached.
2900 CdlListExpressionBody::parse(std::string data)
2902 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::parse", "result %p");
2904 // Allocate an expression object that can then be filled in.
2905 CdlListExpression result = new CdlListExpressionBody;
2907 // Do the parsing in a try/catch statement to make sure the
2908 // allocated expression gets freed on a parse error.
2912 CdlExprOp op = CdlExprOp_Invalid;
2913 CdlExpression expr1 = 0;
2916 // Try to parse the next expression in the list
2917 op = CdlExprOp_Invalid;
2918 expr1 = CdlExpressionBody::parse(data, index, op, end_index);
2920 // There should now be a valid expression, failure would have
2921 // resulted in an exception.
2922 CYG_ASSERT_CLASSC(expr1);
2924 // Allow for ranges.
2925 if (CdlExprOp_Range != op) {
2926 // A simple expression, just add it to the current data vector
2927 // "index" will contain the appropriate value.
2928 result->data.push_back(expr1);
2930 // A range expression. Get the other end of the range.
2931 // This requires manipulating index a bit.
2932 CdlExpression expr2 = 0;
2934 op = CdlExprOp_Invalid;
2936 expr2 = CdlExpressionBody::parse(data, index, op, end_index);
2942 result->ranges.push_back(std::make_pair(expr1, expr2));
2944 } while (CdlExprOp_EOD != op);
2951 // Keep track of the original string for diagnostics purposes
2952 result->expression_string = data;
2954 CYG_REPORT_RETVAL(result);
2961 // ----------------------------------------------------------------------------
2962 // This code is invoked when it is necessary to update the references
2963 // for the list expression. There are four situations in which this
2964 // can happen: the package has just been loaded; the package is being
2965 // unloaded; a referenced target is being created; a referenced target is
2968 // The first two cases simply involve processing every expression that
2969 // makes up the overall list expression. The last two cases involve
2970 // searching through the expressions until an applicable one is found.
2971 // Note that an expression may contain multiple references to another
2972 // object, resulting in multiple calls to this function.
2975 CdlListExpressionBody::update(CdlTransaction transact, CdlNode source, CdlProperty source_prop, CdlNode dest, CdlUpdate change)
2977 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::update", "result %d");
2978 CYG_REPORT_FUNCARG6XV(this, transact, source, source_prop, dest, change);
2979 CYG_PRECONDITION_THISC();
2980 CYG_PRECONDITION_CLASSC(source);
2981 CYG_PRECONDITION_CLASSC(source_prop);
2983 bool result = false;
2985 if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
2987 std::vector<CdlExpression>::const_iterator expr_i;
2988 std::vector<std::pair<CdlExpression, CdlExpression> >::const_iterator pair_i;
2990 for (expr_i = data.begin(); expr_i != data.end(); expr_i++) {
2991 bool handled = (*expr_i)->update(transact, source, source_prop, dest, change);
2992 CYG_ASSERTC(handled);
2993 CYG_UNUSED_PARAM(bool, handled);
2995 for (pair_i = ranges.begin(); pair_i != ranges.end(); pair_i++) {
2996 bool handled = pair_i->first->update(transact, source, source_prop, dest, change);
2997 CYG_ASSERTC(handled);
2998 handled = pair_i->second->update(transact, source, source_prop, dest, change);
2999 CYG_ASSERTC(handled);
3005 CYG_ASSERTC((CdlUpdate_Created == change) || (CdlUpdate_Destroyed == change));
3007 std::vector<CdlExpression>::const_iterator expr_i;
3008 std::vector<std::pair<CdlExpression, CdlExpression> >::const_iterator pair_i;
3010 for (expr_i = data.begin(); !result && (expr_i != data.end()); expr_i++) {
3011 result = (*expr_i)->update(transact, source, source_prop, dest, change);
3013 for (pair_i = ranges.begin(); !result && (pair_i != ranges.end()); pair_i++) {
3014 result = pair_i->first->update(transact, source, source_prop, dest, change);
3016 result = pair_i->second->update(transact, source, source_prop, dest, change);
3021 CYG_REPORT_RETVAL(result);
3028 // ----------------------------------------------------------------------------
3029 // Evaluation. The hard work is actually done in eval_internal()
3032 CdlListExpressionBody::eval(CdlEvalContext& context, CdlListValue& result)
3034 CYG_REPORT_FUNCNAME("CdlListExpression::eval");
3035 CYG_REPORT_FUNCARG3XV(this, &context, &result);
3036 CYG_PRECONDITION_THISC();
3037 CYG_PRECONDITION_CLASSOC(context);
3039 this->eval_internal(context, result);
3041 CYG_REPORT_RETURN();
3044 // ----------------------------------------------------------------------------
3045 // This requires evaluating each expression in the data and ranges
3046 // vectors and adding the result to the appropriate vector in result.
3047 // Various error conditions are possible.
3050 CdlListExpressionBody::eval_internal(CdlEvalContext& context, CdlListValue& result)
3052 CYG_REPORT_FUNCNAME("CdlListExpression::eval_internal");
3053 CYG_REPORT_FUNCARG2XV(this, &context);
3055 result.table.clear();
3056 result.integer_ranges.clear();
3057 result.double_ranges.clear();
3059 CdlSimpleValue val1;
3060 CdlSimpleValue val2;
3063 for (std::vector<CdlExpression>::const_iterator i = data.begin(); i != data.end(); i++) {
3064 (*i)->eval_internal(context, val1);
3065 if ("" != val1.get_value()) {
3066 result.table.push_back(val1);
3069 for (std::vector<std::pair<CdlExpression,CdlExpression> >::const_iterator j = ranges.begin(); j != ranges.end(); j++) {
3070 j->first->eval_internal(context, val1);
3071 j->second->eval_internal(context, val2);
3073 if (val1.has_integer_value() && val2.has_integer_value()) {
3074 cdl_int x1 = val1.get_integer_value();
3075 cdl_int x2 = val2.get_integer_value();
3081 result.integer_ranges.push_back(std::make_pair(x1, x2));
3082 } else if (val1.has_double_value() && val2.has_double_value()) {
3083 double x1 = val1.get_double_value();
3084 double x2 = val2.get_double_value();
3090 result.double_ranges.push_back(std::make_pair(x1, x2));
3092 throw CdlEvalException("Range expression involves non-numerical limits");
3096 // Any problems would have resulted in an exception. If there
3097 // was a previous EvalExeption for this property, it is no
3098 // longer applicable
3099 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3100 context.transaction->clear_conflicts(context.node, context.property, &CdlConflict_EvalExceptionBody::test);
3103 } catch(CdlEvalException e) {
3105 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3107 CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
3108 &CdlConflict_EvalExceptionBody::test);
3109 if (0 == conflict) {
3110 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3112 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
3113 CYG_ASSERTC(0 != eval_conf);
3114 if (eval_conf->get_explanation() != e.get_message()) {
3116 // Replace the conflict object. Higher level will be informed about this.
3117 context.transaction->clear_conflict(conflict);
3118 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3126 CYG_REPORT_RETURN();
3132 // ----------------------------------------------------------------------------
3135 CdlListExpressionBody::is_member(CdlEvalContext& context, CdlSimpleValue& val)
3137 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (value)", "result %d");
3138 CYG_REPORT_FUNCARG3XV(this, &context, &val);
3139 CYG_PRECONDITION_THISC();
3140 CYG_PRECONDITION_CLASSOC(context);
3142 bool result = false;
3143 CdlListValue list_val;
3144 eval_internal(context, list_val);
3145 result = list_val.is_member(val);
3147 CYG_REPORT_RETVAL(result);
3152 CdlListExpressionBody::is_member(CdlEvalContext& context, std::string val)
3154 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (string)", "result %d");
3155 CYG_REPORT_FUNCARG2XV(this, &context);
3156 CYG_PRECONDITION_THISC();
3157 CYG_PRECONDITION_CLASSOC(context);
3159 bool result = false;
3160 CdlListValue list_val;
3161 eval_internal(context, list_val);
3162 result = list_val.is_member(val);
3164 CYG_REPORT_RETVAL(result);
3169 CdlListExpressionBody::is_member(CdlEvalContext& context, cdl_int val)
3171 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (int)", "result %d");
3172 CYG_REPORT_FUNCARG3XV(this, &context, (int) val);
3173 CYG_PRECONDITION_THISC();
3174 CYG_PRECONDITION_CLASSOC(context);
3176 bool result = false;
3177 CdlListValue list_val;
3178 eval_internal(context, list_val);
3179 result = list_val.is_member(val);
3181 CYG_REPORT_RETVAL(result);
3186 CdlListExpressionBody::is_member(CdlEvalContext& context, double val)
3188 CYG_REPORT_FUNCNAMETYPE("CdlListExpression::is_member (double)", "result %d");
3189 CYG_REPORT_FUNCARG2XV(this, &context);
3190 CYG_PRECONDITION_THISC();
3191 CYG_PRECONDITION_CLASSOC(context);
3193 bool result = false;
3194 CdlListValue list_val;
3195 eval_internal(context, list_val);
3196 result = list_val.is_member(val);
3198 CYG_REPORT_RETVAL(result);
3205 // ----------------------------------------------------------------------------
3208 CdlListExpressionBody::get_original_string() const
3210 CYG_REPORT_FUNCNAME("CdlListExpression::get_original_string");
3211 CYG_REPORT_FUNCARG1XV(this);
3212 CYG_PRECONDITION_THISC();
3214 CYG_REPORT_RETURN();
3215 return expression_string;
3221 //{{{ CdlGoalExpression
3223 // ----------------------------------------------------------------------------
3224 // Constructors etc. are pretty much as per ordinary and list
3225 // expressions. Most of the work is done in the private base class.
3227 CdlGoalExpressionBody::CdlGoalExpressionBody()
3228 : CdlExpressionBody()
3230 CYG_REPORT_FUNCNAME("CdlGoalExpression::default_constructor");
3231 CYG_REPORT_FUNCARG1XV(this);
3233 expression_string = "";
3234 cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Magic;
3235 CYGDBG_MEMLEAK_CONSTRUCTOR();
3237 CYG_POSTCONDITION_THISC();
3238 CYG_REPORT_RETURN();
3241 CdlGoalExpressionBody::CdlGoalExpressionBody(const CdlGoalExpressionBody& original)
3242 : CdlExpressionBody(original)
3244 CYG_REPORT_FUNCNAME("CdlGoalExpression:: copy constructor");
3245 CYG_REPORT_FUNCARG2XV(this, &original);
3246 CYG_INVARIANT_CLASSOC(CdlGoalExpressionBody, original);
3248 expression_string = original.expression_string;
3249 cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Magic;
3250 CYGDBG_MEMLEAK_CONSTRUCTOR();
3252 CYG_POSTCONDITION_THISC();
3253 CYG_REPORT_RETURN();
3256 CdlGoalExpressionBody::~CdlGoalExpressionBody()
3258 CYG_REPORT_FUNCNAME("CdlGoalExpression:: destructor");
3259 CYG_REPORT_FUNCARG1XV(this);
3260 CYG_PRECONDITION_THISC();
3262 cdlgoalexpressionbody_cookie = CdlGoalExpressionBody_Invalid;
3263 expression_string = "";
3264 CYGDBG_MEMLEAK_DESTRUCTOR();
3266 CYG_REPORT_RETURN();
3269 // ----------------------------------------------------------------------------
3270 // Parsing. A goal expression acts a bit like a list expression with
3271 // implicit && operators between the various expressions. It could be
3272 // implemented as a vector of expressions (which might make diagnostics
3273 // easier) but it is almost as easy to derive a goal expression from
3277 CdlGoalExpressionBody::parse(std::string data)
3279 CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::parse", "result %p");
3281 CdlGoalExpression result = new CdlGoalExpressionBody;
3285 CdlExprOp op = CdlExprOp_Invalid;
3288 // Parse the first expression in the data.
3289 CdlExpressionBody::continue_parse(result, data, index, op, end_index);
3291 // At this stage we have reached end-of-data or we should be
3292 // at the start of another expression - any binary or ternary
3293 // operands would have been subsumed in the previous expression.
3294 // We need to keep adding && operators and new expressions until
3296 while (CdlExprOp_EOD != op) {
3298 CdlExpressionBody::continue_parse(result, data, index, op, end_index);
3306 // Keep track of the original expression string for diagnostics purposes
3307 result->expression_string = data;
3308 CYG_REPORT_RETVAL(result);
3312 // ----------------------------------------------------------------------------
3314 CdlGoalExpressionBody::eval(CdlEvalContext& context, bool& result)
3316 CYG_REPORT_FUNCNAME("CdlGoalExpression::eval");
3317 CYG_REPORT_FUNCARG2XV(this, &context);
3318 CYG_PRECONDITION_THISC();
3319 CYG_PRECONDITION_CLASSOC(context);
3321 eval_internal(context, result);
3323 CYG_REPORT_RETURN();
3327 CdlGoalExpressionBody::eval(CdlEvalContext& context)
3329 CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::eval", "result %d");
3330 CYG_REPORT_FUNCARG2XV(this, &context);
3331 CYG_PRECONDITION_THISC();
3332 CYG_PRECONDITION_CLASSOC(context);
3335 eval_internal(context, result);
3337 CYG_REPORT_RETVAL(result);
3341 // ----------------------------------------------------------------------------
3342 // Provide access to the underlying CdlExpression object. This allows the
3343 // inference engine etc. to work out why a goal expression is failing
3346 CdlGoalExpressionBody::get_expression()
3348 CYG_REPORT_FUNCNAMETYPE("CdlGoalExpression::get_expression", "result %p");
3349 CYG_REPORT_FUNCARG1XV(this);
3350 CYG_PRECONDITION_THISC();
3352 CdlExpression result = this;
3353 CYG_REPORT_RETVAL(result);
3357 // ----------------------------------------------------------------------------
3360 CdlGoalExpressionBody::check_this(cyg_assert_class_zeal zeal) const
3362 if (CdlGoalExpressionBody_Magic != cdlgoalexpressionbody_cookie) {
3365 CYGDBG_MEMLEAK_CHECKTHIS();
3367 // There is no data specific to a goal expression, just let the
3368 // underlying check_this() member do its stuff.
3370 return inherited::check_this(zeal);
3373 // ----------------------------------------------------------------------------
3376 CdlGoalExpressionBody::get_original_string() const
3378 CYG_REPORT_FUNCNAME("CdlGoalExpression::get_original_string");
3379 CYG_REPORT_FUNCARG1XV(this);
3380 CYG_PRECONDITION_THISC();
3382 CYG_REPORT_RETURN();
3383 return expression_string;
3386 // ----------------------------------------------------------------------------
3389 CdlGoalExpressionBody::eval_internal(CdlEvalContext& context, bool& result)
3391 CYG_REPORT_FUNCNAME("CdlGoalExpression::eval_internal");
3392 CYG_REPORT_FUNCARG2XV(this, &context);
3393 // The assertions are all done in the calling code
3395 // Start by evaluating the underlying expression
3398 inherited::eval_internal(context, val);
3400 // The evaluation succeeded. Do we have an integer, a string, ...?
3401 if (val.has_integer_value()) {
3402 result = (0 != val.get_integer_value());
3403 } else if (val.has_double_value()) {
3404 result = (0.0 != val.get_double_value());
3406 result = ("" != val.get_value());
3409 // If there is an EvalException conflict for this property, it is no longer applicable
3410 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3411 context.transaction->clear_conflicts(context.node, context.property,
3412 &CdlConflict_EvalExceptionBody::test);
3415 } catch(CdlEvalException e) {
3416 if ((0 != context.transaction) && (0 != context.node) && (0 != context.property)) {
3417 CdlConflict conflict = context.transaction->get_conflict(context.node, context.property,
3418 &CdlConflict_EvalExceptionBody::test);
3419 if (0 == conflict) {
3420 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3422 CdlConflict_EvalException eval_conf = dynamic_cast<CdlConflict_EvalException>(conflict);
3423 CYG_ASSERTC(0 != eval_conf);
3424 if (eval_conf->get_explanation() != e.get_message()) {
3425 // Replace the conflict object. Higher level can detect this.
3426 context.transaction->clear_conflict(conflict);
3427 CdlConflict_EvalExceptionBody::make(context.transaction, context.node, context.property, e.get_message());
3434 CYG_REPORT_RETURN();