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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [host/] [libcdl/] [expr.cxx] - Blame information for rev 830

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

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

powered by: WebSVN 2.1.0

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