OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [expr.cxx] - Blame information for rev 609

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.