Statistics
| Branch: | Revision:

root / src / sim / cdynamicexpression.cc @ 7ff3c017

History | View | Annotate | Download (29.8 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 __thread cDynamicExpression::Value* _stk = NULL;
268
static __thread bool _stkinuse = false;
269

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

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

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

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

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

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

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

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

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

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

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

    
605
    return stk[tos];
606
}
607

    
608

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

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

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

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

    
784