Project

General

Profile

Statistics
| Branch: | Revision:

root / src / sim / cdynamicexpression.cc @ 3e29b8a0

History | View | Annotate | Download (29.7 KB)

1
//==========================================================================
2
//   CDYNAMICEXPRESSION.CC  - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//  Author: Andras Varga
7
//
8
//==========================================================================
9

    
10
/*--------------------------------------------------------------*
11
  Copyright (C) 1992-2008 Andras Varga
12
  Copyright (C) 2006-2008 OpenSim Ltd.
13

14
  This file is distributed WITHOUT ANY WARRANTY. See the file
15
  `license' for details on this and other legal matters.
16
*--------------------------------------------------------------*/
17

    
18
#include "cdynamicexpression.h"
19
#include "cxmlelement.h"
20
#include "cmathfunction.h"
21
#include "cnedfunction.h"
22
#include "cexception.h"
23
#include "expryydefs.h"
24
#include "cpar.h"
25
#include "cenvir.h"
26
#include "cmodule.h"
27
#include "stringutil.h"
28
#include "unitconversion.h"
29

    
30
USING_NAMESPACE
31

    
32
cDummyStringPool cDynamicExpression::Elem::stringPool("cDynamicExpression::Elem::stringPool");
33

    
34
void cDynamicExpression::Elem::operator=(const Elem& other)
35
{
36
    deleteOld();
37

    
38
    memcpy(this, &other, sizeof(Elem));
39

    
40
    if (type==STR)
41
        s = stringPool.get(s);
42
    else if (type==DBL)
43
        d.unit = stringPool.get(d.unit);
44
    else if (type==FUNCTOR)
45
        fu = (Functor *) fu->dup();
46
    else if (type==CONSTSUBEXPR)
47
        constExpr = (cExpression *) constExpr->dup();
48
}
49

    
50
cDynamicExpression::Elem::~Elem()
51
{
52
    deleteOld();
53
}
54

    
55
void cDynamicExpression::Elem::deleteOld()
56
{
57
    if (type==STR)
58
        stringPool.release(s);
59
    else if (type==DBL)
60
        stringPool.release(d.unit);
61
    else if (type==FUNCTOR)
62
        delete fu;
63
    else if (type==CONSTSUBEXPR)
64
        delete constExpr;
65
}
66

    
67
int cDynamicExpression::Elem::compare(const Elem& other) const
68
{
69
    if (type!=other.type)
70
        return type - other.type;
71

    
72
    switch (type)
73
    {
74
#define CMP(x) (x==other.x ? 0 : x<other.x ? -1 : 1)
75
      case BOOL:     return (int)other.b - (int)b;
76
      case DBL:      return d.d==other.d.d ? opp_strcmp(d.unit,other.d.unit) : d.d<other.d.d ? -1 : 1;
77
      case STR:      return CMP(s);
78
      case XML:      return CMP(x);
79
      case MATHFUNC: return CMP(f);
80
      case NEDFUNC:  return nf.argc==other.nf.argc ? CMP(nf.f) : (other.nf.argc-nf.argc);
81
      case FUNCTOR:  return CMP(fu);
82
      case OP:       return other.op - op;
83
      case CONSTSUBEXPR: return constExpr->compare(other.constExpr);
84
      default:   throw cRuntimeError("internal error: bad Elem type");
85
#undef CMP
86
    }
87
}
88

    
89
void cDynamicExpression::Value::operator=(const cPar& par)
90
{
91
    switch (par.getType())
92
    {
93
      case cPar::BOOL: *this = par.boolValue(); break;
94
      case cPar::DOUBLE: *this = par.doubleValue(); dblunit = par.getUnit(); break;
95
      case cPar::LONG: *this = par.doubleValue(); dblunit = par.getUnit(); break;
96
      case cPar::STRING: *this = par.stdstringValue(); break;
97
      case cPar::XML: *this = par.xmlValue(); break;
98
      default: throw cRuntimeError("internal error: bad cPar type: %s", par.getFullPath().c_str());
99
    }
100
}
101

    
102
std::string cDynamicExpression::Value::str() const
103
{
104
    char buf[32];
105
    switch (type)
106
    {
107
      case BOOL: return bl ? "true" : "false";
108
      case DBL:  sprintf(buf, "%g%s", dbl, opp_nulltoempty(dblunit)); return buf;
109
      case STR:  return opp_quotestr(s.c_str());
110
      case XML:  return xml->detailedInfo();
111
                 //or: return std::string("<")+xml->getTagName()+" ... >, " + opp_nulltoempty(xml->getSourceLocation());
112
      default:   throw cRuntimeError("internal error: bad Value type");
113
    }
114
}
115

    
116
cDynamicExpression::cDynamicExpression()
117
{
118
    elems = NULL;
119
    size = 0;
120
}
121

    
122
cDynamicExpression::~cDynamicExpression()
123
{
124
    delete [] elems;
125
}
126

    
127
cDynamicExpression& cDynamicExpression::operator=(const cDynamicExpression& other)
128
{
129
    if (this==&other) return *this;
130

    
131
    delete [] elems;
132

    
133
    size = other.size;
134
    elems = new Elem[size];
135
    for (int i=0; i<size; i++)
136
        elems[i] = other.elems[i];
137
    return *this;
138
}
139

    
140
std::string cDynamicExpression::info() const
141
{
142
    return str();
143
}
144

    
145
void cDynamicExpression::setExpression(Elem e[], int n)
146
{
147
    delete [] elems;
148
    elems = e;
149
    size = n;
150
}
151

    
152

    
153
void cDynamicExpression::parse(const char *text)
154
{
155
    // throws exception if something goes wrong
156
    ::doParseExpression(text, elems, size);
157
}
158

    
159
int cDynamicExpression::compare(const cExpression *other) const
160
{
161
    const cDynamicExpression *o = dynamic_cast<const cDynamicExpression *>(other);
162
    if (!o)
163
        return 1; // for lack of a better option
164

    
165
    if (size != o->size)
166
        return o->size - size;
167

    
168
    for (int i=0; i<size; i++)
169
    {
170
        int c = elems[i].compare(o->elems[i]);
171
        if (c != 0)
172
            return c;
173
    }
174
    return 0;
175
}
176

    
177
double cDynamicExpression::convertUnit(double d, const char *unit, const char *targetUnit)
178
{
179
    // not inline because simkernel header files cannot refer to common/ headers (unitconversion.h)
180
    return UnitConversion::convertUnit(d, unit, targetUnit);
181
}
182

    
183
bool cDynamicExpression::isAConstant() const
184
{
185
    for (int i=0; i<size; i++)
186
    {
187
        switch(elems[i].type)
188
        {
189
            // literals and anything calculated from them are OK
190
            case Elem::BOOL:
191
            case Elem::DBL:
192
            case Elem::STR:
193
            case Elem::XML:
194
            case Elem::OP:
195
                continue; //OK
196
            default:
197
                return false;
198
        }
199
    }
200
    return true;
201
}
202

    
203
bool cDynamicExpression::containsConstSubexpressions() const
204
{
205
    for (int i = 0; i < size; i++)
206
        if (elems[i].type == Elem::CONSTSUBEXPR)
207
            return true;
208
    return false;
209
}
210

    
211
void cDynamicExpression::evaluateConstSubexpressions(cComponent *context)
212
{
213
    throw cRuntimeError(this, "const subexpressions not yet implemented"); //TODO implement
214
}
215

    
216
bool cDynamicExpression::boolValue(cComponent *context)
217
{
218
    Value v = evaluate(context);
219
    if (v.type!=Value::BOOL)
220
        throw cRuntimeError(eECANTCAST, "bool");
221
    return v.bl;
222
}
223

    
224
long cDynamicExpression::longValue(cComponent *context, const char *expectedUnit)
225
{
226
    Value v = evaluate(context);
227
    if (v.type!=Value::DBL)
228
        throw cRuntimeError(eECANTCAST, "long");
229
    return double_to_long(UnitConversion::convertUnit(v.dbl, v.dblunit, expectedUnit));
230
}
231

    
232
double cDynamicExpression::doubleValue(cComponent *context, const char *expectedUnit)
233
{
234
    Value v = evaluate(context);
235
    if (v.type!=Value::DBL)
236
        throw cRuntimeError(eECANTCAST, "double");
237
    return UnitConversion::convertUnit(v.dbl, v.dblunit, expectedUnit);
238
}
239

    
240
std::string cDynamicExpression::stringValue(cComponent *context)
241
{
242
    Value v = evaluate(context);
243
    if (v.type!=Value::STR)
244
        throw cRuntimeError(eECANTCAST, "string");
245
    return v.s;
246
}
247

    
248
cXMLElement *cDynamicExpression::xmlValue(cComponent *context)
249
{
250
    Value v = evaluate(context);
251
    if (v.type!=Value::XML)
252
        throw cRuntimeError(eECANTCAST, "XML element");
253
    return v.xml;
254
}
255

    
256
#define ulong(x) ((unsigned long)(x))
257

    
258

    
259
inline double trunc(double x)
260
{
261
   return x < 0.0 ? ceil(x) : floor(x);
262
}
263

    
264
static const int stksize = 20;
265

    
266
// we have a static stack to avoid new[] and call to Value ctor stksize times
267
static cDynamicExpression::Value _stk[stksize];
268
static bool _stkinuse = false;
269

    
270
cDynamicExpression::Value cDynamicExpression::evaluate(cComponent *context) const
271
{
272
    // use static _stk[] if possible, or allocate another one if that's in use.
273
    // Note: this will be reentrant but NOT thread safe
274
    Value *stk;
275
    if (_stkinuse)
276
        stk = new Value[stksize];
277
    else {
278
        _stkinuse = true;
279
        stk = _stk;
280
    }
281
    struct Finally {
282
        Value *stk;
283
        Finally(Value *stk) { this->stk = stk; }
284
        ~Finally() { if (stk==_stk) _stkinuse = false; else delete[] stk; }
285
    } f(stk);
286

    
287
    //printf("    evaluating: %s\n", str().c_str()); //XXX
288
    int tos = -1;
289
    for (int i = 0; i < size; i++)
290
    {
291
       Elem& e = elems[i];
292
       switch (e.type)
293
       {
294
           case Elem::BOOL:
295
             if (tos>=stksize-1)
296
                 throw cRuntimeError(eESTKOFLOW);
297
             stk[++tos] = e.b;
298
             break;
299

    
300
           case Elem::DBL:
301
             if (tos>=stksize-1)
302
                 throw cRuntimeError(eESTKOFLOW);
303
             stk[++tos].set(e.d.d, e.d.unit);
304
             break;
305

    
306
           case Elem::STR:
307
             if (tos>=stksize-1)
308
                 throw cRuntimeError(eESTKOFLOW);
309
             stk[++tos] = e.s;
310
             break;
311

    
312
           case Elem::XML:
313
             if (tos>=stksize-1)
314
                 throw cRuntimeError(eESTKOFLOW);
315
             stk[++tos] = e.x;
316
             break;
317

    
318
           case Elem::MATHFUNC:
319
             switch (e.f->getNumArgs())
320
             {
321
               case 0:
322
                   stk[++tos] = e.f->getMathFuncNoArg()();
323
                   break;
324
               case 1:
325
                   if (tos<0)
326
                       throw cRuntimeError(eESTKUFLOW);
327
                   if (stk[tos].type!=Value::DBL)
328
                       throw cRuntimeError(eEBADARGS,e.f->getName());
329
                   if (!opp_isempty(stk[tos].dblunit))
330
                       throw cRuntimeError(eDIMLESS,e.f->getName());
331
                   stk[tos] = e.f->getMathFunc1Arg()(stk[tos].dbl);
332
                   break;
333
               case 2:
334
                   if (tos<1)
335
                       throw cRuntimeError(eESTKUFLOW);
336
                   if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
337
                       throw cRuntimeError(eEBADARGS,e.f->getName());
338
                   if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
339
                       throw cRuntimeError(eDIMLESS,e.f->getName());
340
                   stk[tos-1] = e.f->getMathFunc2Args()(stk[tos-1].dbl, stk[tos].dbl);
341
                   tos--;
342
                   break;
343
               case 3:
344
                   if (tos<2)
345
                       throw cRuntimeError(eESTKUFLOW);
346
                   if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL || stk[tos-2].type!=Value::DBL)
347
                       throw cRuntimeError(eEBADARGS,e.f->getName());
348
                   if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit) || !opp_isempty(stk[tos-2].dblunit))
349
                       throw cRuntimeError(eDIMLESS,e.f->getName());
350
                   stk[tos-2] = e.f->getMathFunc3Args()(stk[tos-2].dbl, stk[tos-1].dbl, stk[tos].dbl);
351
                   tos-=2;
352
                   break;
353
               case 4:
354
                   if (tos<3)
355
                       throw cRuntimeError(eESTKUFLOW);
356
                   if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL || stk[tos-2].type!=Value::DBL || stk[tos-3].type!=Value::DBL)
357
                       throw cRuntimeError(eEBADARGS,e.f->getName());
358
                   if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit) || !opp_isempty(stk[tos-2].dblunit) || !opp_isempty(stk[tos-3].dblunit))
359
                       throw cRuntimeError(eDIMLESS,e.f->getName());
360
                   stk[tos-3] = e.f->getMathFunc4Args()(stk[tos-3].dbl, stk[tos-2].dbl, stk[tos-1].dbl, stk[tos].dbl);
361
                   tos-=3;
362
                   break;
363
               default:
364
                   throw cRuntimeError(eBADEXP);
365
             }
366
             break;
367

    
368
           case Elem::NEDFUNC:
369
             {
370
             int numargs = e.nf.argc;
371
             int argpos = tos-numargs+1; // stk[] index of 1st arg to pass
372
             if (argpos<0)
373
                 throw cRuntimeError(eESTKUFLOW);
374
             // note: args checking is left to the function itself
375
             stk[argpos] = e.nf.f->invoke(context, stk+argpos, numargs);
376
             tos = argpos;
377
             break;
378
             }
379

    
380
           case Elem::FUNCTOR:
381
             {
382
             int numargs = e.fu->getNumArgs();
383
             int argpos = tos-numargs+1; // stk[] index of 1st arg to pass
384
             if (argpos<0)
385
                 throw cRuntimeError(eESTKUFLOW);
386
             const char *argtypes = e.fu->getArgTypes();
387
             for (int i=0; i<numargs; i++)
388
                 if (argtypes[i] != '*' && stk[argpos+i].type != (argtypes[i]=='L' ? 'D' : argtypes[i]))
389
                     throw cRuntimeError(eEBADARGS,e.fu->getFullName());
390
             // note: unit checking is left to the function itself
391
             stk[argpos] = e.fu->evaluate(context, stk+argpos, numargs);
392
             tos = argpos;
393
             break;
394
             }
395

    
396
           case Elem::CONSTSUBEXPR:
397
             // this should not occur
398
             throw cRuntimeError("evaluate: constant subexpressions must have already been evaluated");
399

    
400
           case Elem::OP:
401
             if (e.op==NEG || e.op==NOT || e.op==BIN_NOT)
402
             {
403
                 // unary
404
                 if (tos<0)
405
                     throw cRuntimeError(eESTKUFLOW);
406
                 switch (e.op)
407
                 {
408
                     case NEG:
409
                         if (stk[tos].type!=Value::DBL)
410
                             throw cRuntimeError(eEBADARGS,"-");
411
                         stk[tos].dbl = -stk[tos].dbl;
412
                         break;
413
                     case NOT:
414
                         if (stk[tos].type!=Value::BOOL)
415
                             throw cRuntimeError(eEBADARGS,"!");
416
                         stk[tos].bl = !stk[tos].bl;
417
                         break;
418
                     case BIN_NOT:
419
                         if (stk[tos].type!=Value::DBL)
420
                             throw cRuntimeError(eEBADARGS,"~");
421
                         if (!opp_isempty(stk[tos].dblunit))
422
                             throw cRuntimeError(eDIMLESS,"~");
423
                         stk[tos].dbl = ~ulong(stk[tos].dbl);
424
                         break;
425
                     default: ASSERT(false);
426
                 }
427
             }
428
             else if (e.op==IIF)
429
             {
430
                 // tertiary
431
                 if (tos<2)
432
                     throw cRuntimeError(eESTKUFLOW);
433
                 // 1st arg must be bool, others 2nd and 3rd can be anything
434
                 if (stk[tos-2].type!=Value::BOOL)
435
                     throw cRuntimeError(eEBADARGS,"?:");
436
                 stk[tos-2] = (stk[tos-2].bl ? stk[tos-1] : stk[tos]);
437
                 tos-=2;
438
             }
439
             else
440
             {
441
                 // binary
442
                 if (tos<1)
443
                     throw cRuntimeError(eESTKUFLOW);
444
                 switch(e.op)
445
                 {
446
                   case ADD:
447
                       // double addition or string concatenation
448
                       if (stk[tos-1].type==Value::DBL && stk[tos].type==Value::DBL) {
449
                           stk[tos].dbl = UnitConversion::convertUnit(stk[tos].dbl, stk[tos].dblunit, stk[tos-1].dblunit);
450
                           stk[tos-1].dbl = stk[tos-1].dbl + stk[tos].dbl;
451
                       }
452
                       else if (stk[tos-1].type==Value::STR && stk[tos].type==Value::STR)
453
                           stk[tos-1].s = stk[tos-1].s + stk[tos].s;
454
                       else
455
                           throw cRuntimeError(eEBADARGS,"+");
456
                       tos--;
457
                       break;
458
                   case SUB:
459
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
460
                           throw cRuntimeError(eEBADARGS,"-");
461
                       stk[tos].dbl = UnitConversion::convertUnit(stk[tos].dbl, stk[tos].dblunit, stk[tos-1].dblunit);
462
                       stk[tos-1].dbl = stk[tos-1].dbl - stk[tos].dbl;
463
                       tos--;
464
                       break;
465
                   case MUL:
466
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
467
                           throw cRuntimeError(eEBADARGS,"*");
468
                       if (!opp_isempty(stk[tos].dblunit) && !opp_isempty(stk[tos-1].dblunit))
469
                           throw cRuntimeError("Multiplying two quantities with units is not supported");
470
                       stk[tos-1].dbl = stk[tos-1].dbl * stk[tos].dbl;
471
                       if (opp_isempty(stk[tos-1].dblunit))
472
                           stk[tos-1].dblunit = stk[tos].dblunit;
473
                       tos--;
474
                       break;
475
                   case DIV:
476
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
477
                           throw cRuntimeError(eEBADARGS,"/");
478
                       // for now we only support num/num, unit/num, and unit/unit if the two units are convertible
479
                       if (!opp_isempty(stk[tos].dblunit))
480
                           stk[tos].dbl = UnitConversion::convertUnit(stk[tos].dbl, stk[tos].dblunit, stk[tos-1].dblunit);
481
                       stk[tos-1].dbl = stk[tos-1].dbl / stk[tos].dbl;
482
                       if (!opp_isempty(stk[tos].dblunit))
483
                           stk[tos-1].dblunit = NULL;
484
                       tos--;
485
                       break;
486
                   case MOD:
487
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
488
                           throw cRuntimeError(eEBADARGS,"%");
489
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
490
                           throw cRuntimeError(eDIMLESS,"%");
491
                       stk[tos-1].dbl = fmod(trunc(stk[tos-1].dbl), trunc(stk[tos].dbl));
492
                       tos--;
493
                       break;
494
                   case POW:
495
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
496
                           throw cRuntimeError(eEBADARGS,"^");
497
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
498
                           throw cRuntimeError(eDIMLESS,"^");
499
                       stk[tos-1].dbl = pow(stk[tos-1].dbl, stk[tos].dbl);
500
                       tos--;
501
                       break;
502
                   case AND:
503
                       if (stk[tos].type!=Value::BOOL || stk[tos-1].type!=Value::BOOL)
504
                           throw cRuntimeError(eEBADARGS,"&&");
505
                       stk[tos-1].bl = stk[tos-1].bl && stk[tos].bl;
506
                       tos--;
507
                       break;
508
                   case OR:
509
                       if (stk[tos].type!=Value::BOOL || stk[tos-1].type!=Value::BOOL)
510
                           throw cRuntimeError(eEBADARGS,"||");
511
                       stk[tos-1].bl = stk[tos-1].bl || stk[tos].bl;
512
                       tos--;
513
                       break;
514
                   case XOR:
515
                       if (stk[tos].type!=Value::BOOL || stk[tos-1].type!=Value::BOOL)
516
                           throw cRuntimeError(eEBADARGS,"##");
517
                       stk[tos-1].bl = stk[tos-1].bl != stk[tos].bl;
518
                       tos--;
519
                       break;
520
                   case BIN_AND:
521
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
522
                           throw cRuntimeError(eEBADARGS,"&");
523
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
524
                           throw cRuntimeError(eDIMLESS,"&");
525
                       stk[tos-1].dbl = (double)(ulong(stk[tos-1].dbl) & ulong(stk[tos].dbl));
526
                       tos--;
527
                       break;
528
                   case BIN_OR:
529
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
530
                           throw cRuntimeError(eEBADARGS,"|");
531
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
532
                           throw cRuntimeError(eDIMLESS,"|");
533
                       stk[tos-1].dbl = (double)(ulong(stk[tos-1].dbl) | ulong(stk[tos].dbl));
534
                       tos--;
535
                       break;
536
                   case BIN_XOR:
537
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
538
                           throw cRuntimeError(eEBADARGS,"#");
539
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
540
                           throw cRuntimeError(eDIMLESS,"#");
541
                       stk[tos-1].dbl = (double)(ulong(stk[tos-1].dbl) ^ ulong(stk[tos].dbl));
542
                       tos--;
543
                       break;
544
                   case LSHIFT:
545
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
546
                           throw cRuntimeError(eEBADARGS,"<<");
547
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
548
                           throw cRuntimeError(eDIMLESS,"<<");
549
                       stk[tos-1].dbl = (double)(ulong(stk[tos-1].dbl) << ulong(stk[tos].dbl));
550
                       tos--;
551
                       break;
552
                   case RSHIFT:
553
                       if (stk[tos].type!=Value::DBL || stk[tos-1].type!=Value::DBL)
554
                           throw cRuntimeError(eEBADARGS,">>");
555
                       if (!opp_isempty(stk[tos].dblunit) || !opp_isempty(stk[tos-1].dblunit))
556
                           throw cRuntimeError(eDIMLESS,">>");
557
                       stk[tos-1].dbl = (double)(ulong(stk[tos-1].dbl) >> ulong(stk[tos].dbl));
558
                       tos--;
559
                       break;
560
#define COMPARISON(RELATION) \
561
                                 if (stk[tos-1].type==Value::DBL && stk[tos].type==Value::DBL) { \
562
                                     stk[tos].dbl = UnitConversion::convertUnit(stk[tos].dbl, stk[tos].dblunit, stk[tos-1].dblunit); \
563
                                     stk[tos-1] = (stk[tos-1].dbl RELATION stk[tos].dbl); \
564
                                 } else if (stk[tos-1].type==Value::STR && stk[tos].type==Value::STR) \
565
                                     stk[tos-1] = (stk[tos-1].s RELATION stk[tos].s); \
566
                                 else if (stk[tos-1].type==Value::BOOL && stk[tos].type==Value::BOOL) \
567
                                     stk[tos-1] = (stk[tos-1].bl RELATION stk[tos].bl); \
568
                                 else \
569
                                     throw cRuntimeError(eEBADARGS,#RELATION); \
570
                                 tos--;
571
                   case EQ:
572
                       COMPARISON(==);
573
                       break;
574
                   case NE:
575
                       COMPARISON(!=);
576
                       break;
577
                   case LT:
578
                       COMPARISON(<);
579
                       break;
580
                   case LE:
581
                       COMPARISON(<=);
582
                       break;
583
                   case GT:
584
                       COMPARISON(>);
585
                       break;
586
                   case GE:
587
                       COMPARISON(>=);
588
                       break;
589
#undef COMPARISON
590
                   default:
591
                       throw cRuntimeError(eBADEXP);
592
                 }
593
             }
594
             break;
595
           default:
596
             throw cRuntimeError(eBADEXP);
597
       }
598
    }
599
    if (tos!=0)
600
        throw cRuntimeError(eBADEXP);
601

    
602
    //printf("        ==> returning %s\n", stk[tos].str().c_str()); //XXX
603

    
604
    return stk[tos];
605
}
606

    
607

    
608
std::string cDynamicExpression::str() const
609
{
610
    // We perform the same algorithm as during evaluation (i.e. stack machine),
611
    // only instead of actual calculations we store the result as string.
612
    // We need to keep track of operator precendences to be able to add parens where needed.
613

    
614
    try
615
    {
616
        const int stksize = 20;
617
        std::string strstk[stksize];
618
        int pristk[stksize];
619

    
620
        int tos = -1;
621
        for (int i = 0; i < size; i++)
622
        {
623
           Elem& e = elems[i];
624
           switch (e.type)
625
           {
626
               case Elem::BOOL:
627
                 if (tos>=stksize-1)
628
                     throw cRuntimeError(eESTKOFLOW);
629
                 strstk[++tos] = (e.b ? "true" : "false");
630
                 pristk[tos] = 0;
631
                 break;
632
               case Elem::DBL:
633
                 {
634
                 if (tos>=stksize-1)
635
                     throw cRuntimeError(eESTKOFLOW);
636
                 char buf[32];
637
                 sprintf(buf, "%g%s", e.d.d, opp_nulltoempty(e.d.unit));
638
                 strstk[++tos] = buf;
639
                 pristk[tos] = 0;
640
                 }
641
                 break;
642
               case Elem::STR:
643
                 if (tos>=stksize-1)
644
                     throw cRuntimeError(eESTKOFLOW);
645
                 strstk[++tos] = opp_quotestr(e.s ? e.s : "");
646
                 pristk[tos] = 0;
647
                 break;
648
               case Elem::XML:
649
                 if (tos>=stksize-1)
650
                     throw cRuntimeError(eESTKOFLOW);
651
                 strstk[++tos] = std::string("<") + (e.x ? e.x->getTagName() : "null") +">"; //FIXME plus location info?
652
                 pristk[tos] = 0;
653
                 break;
654
               case Elem::MATHFUNC:
655
               case Elem::NEDFUNC:
656
                 {
657
                 int numargs = (e.type==Elem::MATHFUNC) ? e.f->getNumArgs() : e.nf.argc;
658
                 std::string name = (e.type==Elem::MATHFUNC) ? e.f->getName() : e.nf.f->getName();
659
                 int argpos = tos-numargs+1; // strstk[] index of 1st arg to pass
660
                 if (argpos<0)
661
                     throw cRuntimeError(eESTKUFLOW);
662
                 std::string tmp = name+"(";
663
                 for (int i=0; i<numargs; i++)
664
                     tmp += (i==0 ? "" : ", ") + strstk[argpos+i];
665
                 tmp += ")";
666
                 strstk[argpos] = tmp;
667
                 tos = argpos;
668
                 pristk[tos] = 0;
669
                 break;
670
                 }
671
               case Elem::FUNCTOR:
672
                 {
673
                 int numargs = e.fu->getNumArgs();
674
                 int argpos = tos-numargs+1; // strstk[] index of 1st arg to pass
675
                 if (argpos<0)
676
                     throw cRuntimeError(eESTKUFLOW);
677
                 strstk[argpos] = e.fu->str(strstk+argpos, numargs);
678
                 tos = argpos;
679
                 break;
680
                 }
681
               case Elem::CONSTSUBEXPR:
682
                 strstk[++tos] = std::string("const(")+e.constExpr->str()+")";
683
                 break;
684
               case Elem::OP:
685
                 if (e.op==NEG || e.op==NOT || e.op==BIN_NOT)
686
                 {
687
                     if (tos<0)
688
                         throw cRuntimeError(eESTKUFLOW);
689
                     const char *op;
690
                     switch (e.op)
691
                     {
692
                         case NEG: op=" -"; break;
693
                         case NOT: op=" !"; break;
694
                         case BIN_NOT: op=" ~"; break;
695
                         default:  op=" ???";
696
                     }
697
                     strstk[tos] = std::string(op) + strstk[tos]; // pri=0: never needs parens
698
                     pristk[tos] = 0;
699
                 }
700
                 else if (e.op==IIF)  // conditional (tertiary)
701
                 {
702
                     if (tos<2)
703
                         throw cRuntimeError(eESTKUFLOW);
704
                     strstk[tos-2] = strstk[tos-2] + " ? " + strstk[tos-1] + " : " + strstk[tos];
705
                     tos-=2;
706
                     pristk[tos] = 8;
707
                 }
708
                 else
709
                 {
710
                     // binary
711
                     if (tos<1)
712
                         throw cRuntimeError(eESTKUFLOW);
713
                     int pri;
714
                     const char *op;
715
                     switch(e.op)
716
                     {
717
                         //
718
                         // Precedences, based on expr.y:
719
                         //   prec=7: && || ##
720
                         //   prec=6: == != > >= < <=
721
                         //   prec=5: & | #
722
                         //   prec=4: << >>
723
                         //   prec=3: + -
724
                         //   prec=2: * / %
725
                         //   prec=1: ^
726
                         //   prec=0: UMIN ! ~
727
                         //
728
                         case ADD: op=" + "; pri=3; break;
729
                         case SUB: op=" - "; pri=3; break;
730
                         case MUL: op=" * "; pri=2; break;
731
                         case DIV: op=" / "; pri=2; break;
732
                         case MOD: op=" % "; pri=2; break;
733
                         case POW: op=" ^ "; pri=1; break;
734
                         case EQ:  op=" == "; pri=6; break;
735
                         case NE:  op=" != "; pri=6; break;
736
                         case LT:  op=" < "; pri=6; break;
737
                         case GT:  op=" > "; pri=6; break;
738
                         case LE:  op=" <= "; pri=6; break;
739
                         case GE:  op=" >= "; pri=6; break;
740
                         case AND: op=" && "; pri=7; break;
741
                         case OR:  op=" || "; pri=7; break;
742
                         case XOR: op=" ## "; pri=7; break;
743
                         case BIN_AND: op=" & "; pri=5; break;
744
                         case BIN_OR:  op=" | "; pri=5; break;
745
                         case BIN_XOR: op=" # "; pri=5; break;
746
                         case LSHIFT:  op=" << "; pri=4; break;
747
                         case RSHIFT:  op=" >> "; pri=4; break;
748
                         default:  op=" ??? "; pri=10; break;
749
                     }
750

    
751
                     // add parens, depending on the operator precedences
752
                     std::string tmp;
753
                     if (pri < pristk[tos-1])
754
                         tmp = std::string("(") + strstk[tos-1] + ")";
755
                     else
756
                         tmp = strstk[tos-1];
757
                     tmp += op;
758
                     if (pri < pristk[tos])
759
                         tmp += std::string("(") + strstk[tos] + ")";
760
                     else
761
                         tmp += strstk[tos];
762
                     strstk[tos-1] = tmp;
763
                     tos--;
764
                     pristk[tos] = pri;
765
                     break;
766
                 }
767
                 break;
768
               default:
769
                 throw cRuntimeError(eBADEXP);
770
           }
771
        }
772
        if (tos!=0)
773
            throw cRuntimeError(eBADEXP);
774
        return strstk[tos];
775
    }
776
    catch (std::exception& e)
777
    {
778
        std::string ret = std::string("[[ ") + e.what() + " ]]";
779
        return ret;
780
    }
781
}
782

    
783