File indexing completed on 2024-04-28 15:29:53

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kapplicationtrader.h"
0009 #include "ktraderparsetree_p.h"
0010 
0011 namespace KTraderParse
0012 {
0013 QVariant ParseContext::property(const QString &_key) const
0014 {
0015     if (service) {
0016         return service->property(_key);
0017     }
0018 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90)
0019     if (info.isValid()) {
0020         return info.property(_key);
0021     }
0022 #endif
0023     return QVariant();
0024 }
0025 
0026 bool ParseTreeOR::eval(ParseContext *_context) const
0027 {
0028     ParseContext c1(_context);
0029     ParseContext c2(_context);
0030 
0031     // don't evaluate both expressions but return immediately
0032     // if the first one of them succeeds. Otherwise queries like
0033     // ((not exist Blah) or (Blah == 'Foo')) do not work, because
0034     // the evaluation of the second term ends up in a fatal error
0035     // (Simon)
0036 
0037     if (!m_pLeft->eval(&c1)) {
0038         return false;
0039     }
0040 
0041     if (c1.type != ParseContext::T_BOOL) {
0042         return false;
0043     }
0044 
0045     _context->b = c1.b;
0046     _context->type = ParseContext::T_BOOL;
0047     if (c1.b) {
0048         return true;
0049     }
0050 
0051     if (!m_pRight->eval(&c2)) {
0052         return false;
0053     }
0054 
0055     if (c2.type != ParseContext::T_BOOL) {
0056         return false;
0057     }
0058 
0059     _context->b = (c1.b || c2.b);
0060     _context->type = ParseContext::T_BOOL;
0061 
0062     return true;
0063 }
0064 
0065 bool ParseTreeAND::eval(ParseContext *_context) const
0066 {
0067     _context->type = ParseContext::T_BOOL;
0068 
0069     ParseContext c1(_context);
0070     ParseContext c2(_context);
0071     if (!m_pLeft->eval(&c1)) {
0072         return false;
0073     }
0074     if (c1.type != ParseContext::T_BOOL) {
0075         return false;
0076     }
0077     if (!c1.b) {
0078         _context->b = false;
0079         return true;
0080     }
0081 
0082     if (!m_pRight->eval(&c2)) {
0083         return false;
0084     }
0085     if (c2.type != ParseContext::T_BOOL) {
0086         return false;
0087     }
0088 
0089     _context->b = (c1.b && c2.b);
0090 
0091     return true;
0092 }
0093 
0094 bool ParseTreeCALC::eval(ParseContext *_context) const
0095 {
0096     ParseContext c1(_context);
0097     ParseContext c2(_context);
0098     if (!m_pLeft->eval(&c1)) {
0099         return false;
0100     }
0101     if (!m_pRight->eval(&c2)) {
0102         return false;
0103     }
0104 
0105     // Bool extension
0106     if (c1.type != ParseContext::T_NUM && c1.type != ParseContext::T_DOUBLE && c1.type != ParseContext::T_BOOL) {
0107         return false;
0108     }
0109     // Bool extension
0110     if (c2.type != ParseContext::T_NUM && c2.type != ParseContext::T_DOUBLE && c2.type != ParseContext::T_BOOL) {
0111         return false;
0112     }
0113     // Bool extension
0114     if (c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_BOOL) {
0115         return false;
0116     }
0117 
0118     /**
0119      * Make types compatible
0120      */
0121     if (c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE) {
0122         c1.type = ParseContext::T_DOUBLE;
0123         c1.f = c1.i;
0124     } else if (c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM) {
0125         c2.type = ParseContext::T_DOUBLE;
0126         c2.f = c2.i;
0127     }
0128     // Bool extension
0129     else if (c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_NUM) {
0130         c1.type = ParseContext::T_NUM;
0131         if (c1.b) {
0132             c1.i = 1;
0133         } else {
0134             c1.i = -1;
0135         }
0136     }
0137     // Bool extension
0138     else if (c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_DOUBLE) {
0139         c1.type = ParseContext::T_DOUBLE;
0140         if (c1.b) {
0141             c1.f = 1.0;
0142         } else {
0143             c1.f = -1.0;
0144         }
0145     }
0146     // Bool extension
0147     else if (c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_BOOL) {
0148         c2.type = ParseContext::T_NUM;
0149         if (c2.b) {
0150             c2.i = 1;
0151         } else {
0152             c2.i = -1;
0153         }
0154     }
0155     // Bool extension
0156     else if (c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_BOOL) {
0157         c2.type = ParseContext::T_DOUBLE;
0158         if (c2.b) {
0159             c2.f = 1.0;
0160         } else {
0161             c2.f = -1.0;
0162         }
0163     }
0164 
0165     _context->type = c1.type;
0166 
0167     /**
0168      * Calculate
0169      */
0170     switch (m_cmd) {
0171     case 1: /* Add */
0172         if (c1.type == ParseContext::T_DOUBLE) {
0173             _context->f = (c1.f + c2.f);
0174             return true;
0175         }
0176         if (c1.type == ParseContext::T_NUM) {
0177             _context->i = (c1.i + c2.i);
0178             return true;
0179         }
0180         break;
0181     case 2: /* Sub */
0182         if (c1.type == ParseContext::T_DOUBLE) {
0183             _context->f = (c1.f - c2.f);
0184             return true;
0185         }
0186         if (c1.type == ParseContext::T_NUM) {
0187             _context->i = (c1.i - c2.i);
0188             return true;
0189         }
0190         break;
0191     case 3: /* Mul */
0192         if (c1.type == ParseContext::T_DOUBLE) {
0193             // cout << "Double Mult" << endl;
0194             _context->f = (c1.f * c2.f);
0195             return true;
0196         }
0197         if (c1.type == ParseContext::T_NUM) {
0198             _context->i = (c1.i * c2.i);
0199             return true;
0200         }
0201         break;
0202     case 4: /* Div */
0203         if (c1.type == ParseContext::T_DOUBLE) {
0204             _context->f = (c1.f / c2.f);
0205             return true;
0206         }
0207         if (c1.type == ParseContext::T_NUM) {
0208             _context->i = (c1.i / c2.i);
0209             return true;
0210         }
0211         break;
0212     }
0213 
0214     return false;
0215 }
0216 
0217 bool ParseTreeCMP::eval(ParseContext *_context) const
0218 {
0219     // cout << "CMP 1 cmd=" << m_cmd << endl;
0220     ParseContext c1(_context);
0221     ParseContext c2(_context);
0222     if (!m_pLeft->eval(&c1)) {
0223         return false;
0224     }
0225 
0226     if (!m_pRight->eval(&c2)) {
0227         return false;
0228     }
0229 
0230     /**
0231      * Make types compatible
0232      */
0233     if (c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE) {
0234         c1.type = ParseContext::T_DOUBLE;
0235         c1.f = c1.i;
0236     } else if (c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM) {
0237         c2.type = ParseContext::T_DOUBLE;
0238         c2.f = c2.i;
0239     }
0240 
0241     /**
0242      * Compare
0243      */
0244     _context->type = ParseContext::T_BOOL;
0245 
0246     switch (m_cmd) {
0247     case 1: /* EQ */
0248     case 7: /* EQI */
0249         if (c1.type != c2.type) {
0250             _context->b = false;
0251             return true;
0252         }
0253         if (c1.type == ParseContext::T_STRING) {
0254             if (m_cmd == 7) {
0255                 _context->b = QString::compare(c1.str, c2.str, Qt::CaseInsensitive) == 0;
0256             } else {
0257                 _context->b = (c1.str == c2.str);
0258             }
0259             return true;
0260         }
0261         if (c1.type == ParseContext::T_BOOL) {
0262             _context->b = (c1.b == c2.b);
0263             return true;
0264         }
0265         if (c1.type == ParseContext::T_DOUBLE) {
0266             _context->b = qFuzzyCompare(c1.f, c2.f);
0267             return true;
0268         }
0269         if (c1.type == ParseContext::T_NUM) {
0270             _context->b = (c1.i == c2.i);
0271             return true;
0272         }
0273         break;
0274     case 2: /* NEQ */
0275     case 8: /* NEQI */
0276         if (c1.type != c2.type) {
0277             _context->b = true;
0278             return true;
0279         }
0280         if (c1.type == ParseContext::T_STRING) {
0281             if (m_cmd == 8) {
0282                 _context->b = QString::compare(c1.str, c2.str, Qt::CaseInsensitive) != 0;
0283             } else {
0284                 _context->b = (c1.str != c2.str);
0285             }
0286             return true;
0287         }
0288         if (c1.type == ParseContext::T_BOOL) {
0289             _context->b = (c1.b != c2.b);
0290             return true;
0291         }
0292         if (c1.type == ParseContext::T_DOUBLE) {
0293             _context->b = !qFuzzyCompare(c1.f, c2.f);
0294             return true;
0295         }
0296         if (c1.type == ParseContext::T_NUM) {
0297             _context->b = (c1.i != c2.i);
0298             return true;
0299         }
0300         break;
0301     case 3: /* GEQ */
0302         if (c1.type != c2.type) {
0303             _context->b = false;
0304             return true;
0305         }
0306         if (c1.type == ParseContext::T_DOUBLE) {
0307             _context->b = (c1.f >= c2.f);
0308             return true;
0309         }
0310         if (c1.type == ParseContext::T_NUM) {
0311             _context->b = (c1.i >= c2.i);
0312             return true;
0313         }
0314         _context->b = false;
0315         return true;
0316 
0317     case 4: /* LEQ */
0318         if (c1.type != c2.type) {
0319             _context->b = false;
0320             return true;
0321         }
0322         if (c1.type == ParseContext::T_DOUBLE) {
0323             _context->b = (c1.f <= c2.f);
0324             return true;
0325         }
0326         if (c1.type == ParseContext::T_NUM) {
0327             _context->b = (c1.i <= c2.i);
0328             return true;
0329         }
0330         _context->b = false;
0331         return true;
0332 
0333     case 5: /* < */
0334         if (c1.type != c2.type) {
0335             _context->b = false;
0336             return true;
0337         }
0338         if (c1.type == ParseContext::T_DOUBLE) {
0339             _context->b = (c1.f < c2.f);
0340             return true;
0341         }
0342         if (c1.type == ParseContext::T_NUM) {
0343             _context->b = (c1.i < c2.i);
0344             return true;
0345         }
0346         _context->b = false;
0347         return true;
0348 
0349     case 6: /* > */
0350         if (c1.type != c2.type) {
0351             _context->b = false;
0352             return true;
0353         }
0354         if (c1.type == ParseContext::T_DOUBLE) {
0355             _context->b = (c1.f > c2.f);
0356             return true;
0357         }
0358         if (c1.type == ParseContext::T_NUM) {
0359             _context->b = (c1.i > c2.i);
0360             return true;
0361         }
0362         _context->b = false;
0363         return true;
0364     }
0365 
0366     return false;
0367 }
0368 
0369 bool ParseTreeNOT::eval(ParseContext *_context) const
0370 {
0371     ParseContext c1(_context);
0372     if (!m_pLeft->eval(&c1)) {
0373         return false;
0374     }
0375     if (c1.type != ParseContext::T_BOOL) {
0376         return false;
0377     }
0378 
0379     _context->b = !c1.b;
0380     _context->type = ParseContext::T_BOOL;
0381 
0382     return true;
0383 }
0384 
0385 bool ParseTreeEXIST::eval(ParseContext *_context) const
0386 {
0387     _context->type = ParseContext::T_BOOL;
0388 
0389     QVariant prop = _context->property(m_id);
0390     _context->b = prop.isValid();
0391 
0392     return true;
0393 }
0394 
0395 bool ParseTreeMATCH::eval(ParseContext *_context) const
0396 {
0397     _context->type = ParseContext::T_BOOL;
0398 
0399     ParseContext c1(_context);
0400     ParseContext c2(_context);
0401     if (!m_pLeft->eval(&c1)) {
0402         return false;
0403     }
0404     if (!m_pRight->eval(&c2)) {
0405         return false;
0406     }
0407     if (c1.type != ParseContext::T_STRING || c2.type != ParseContext::T_STRING) {
0408         return false;
0409     }
0410 
0411     _context->b = c2.str.contains(c1.str, m_cs);
0412 
0413     return true;
0414 }
0415 
0416 bool ParseTreeSubsequenceMATCH::eval(ParseContext *_context) const
0417 {
0418     _context->type = ParseContext::T_BOOL;
0419 
0420     ParseContext c1(_context);
0421     ParseContext c2(_context);
0422     if (!m_pLeft->eval(&c1)) {
0423         return false;
0424     }
0425     if (!m_pRight->eval(&c2)) {
0426         return false;
0427     }
0428     if (c1.type != ParseContext::T_STRING || c2.type != ParseContext::T_STRING) {
0429         return false;
0430     }
0431     _context->b = KApplicationTrader::isSubsequence(c1.str, c2.str, m_cs);
0432     return true;
0433 }
0434 
0435 bool ParseTreeIN::eval(ParseContext *_context) const
0436 {
0437     _context->type = ParseContext::T_BOOL;
0438 
0439     ParseContext c1(_context);
0440     ParseContext c2(_context);
0441     if (!m_pLeft->eval(&c1)) {
0442         return false;
0443     }
0444     if (!m_pRight->eval(&c2)) {
0445         return false;
0446     }
0447 
0448     if ((c1.type == ParseContext::T_NUM) && (c2.type == ParseContext::T_SEQ) && ((*(c2.seq.begin())).type() == QVariant::Int)) {
0449         _context->b = std::any_of(c2.seq.cbegin(), c2.seq.cend(), [&c1](const QVariant &variant) {
0450             return variant.type() == QVariant::Int && variant.toInt() == c1.i;
0451         });
0452 
0453         return true;
0454     }
0455 
0456     if (c1.type == ParseContext::T_DOUBLE //
0457         && c2.type == ParseContext::T_SEQ //
0458         && (*(c2.seq.begin())).type() == QVariant::Double) {
0459         _context->b = std::any_of(c2.seq.cbegin(), c2.seq.cend(), [&c1](const QVariant &variant) {
0460             return variant.type() == QVariant::Double && qFuzzyCompare(variant.toDouble(), c1.i);
0461         });
0462 
0463         return true;
0464     }
0465 
0466     if (c1.type == ParseContext::T_STRING && c2.type == ParseContext::T_STR_SEQ) {
0467         if (false && m_substring) {
0468             _context->b = false;
0469             for (const QString &string : std::as_const(c2.strSeq)) {
0470                 if (string.contains(c1.str, m_cs)) {
0471                     _context->b = true;
0472                     break;
0473                 }
0474             }
0475         } else {
0476             _context->b = c2.strSeq.contains(c1.str, m_cs);
0477         }
0478 
0479         return true;
0480     }
0481 
0482     return false;
0483 }
0484 
0485 bool ParseTreeID::eval(ParseContext *_context) const
0486 {
0487     QVariant prop = _context->property(m_str);
0488 
0489     if (!prop.isValid()) {
0490         return false;
0491     }
0492 
0493     if (prop.type() == QVariant::String) {
0494         _context->str = prop.toString();
0495         _context->type = ParseContext::T_STRING;
0496         return true;
0497     }
0498 
0499     if (prop.type() == QVariant::Int) {
0500         _context->i = prop.toInt();
0501         _context->type = ParseContext::T_NUM;
0502         return true;
0503     }
0504 
0505     if (prop.type() == QVariant::Bool) {
0506         _context->b = prop.toBool();
0507         _context->type = ParseContext::T_BOOL;
0508         return true;
0509     }
0510 
0511     if (prop.type() == QVariant::Double) {
0512         _context->f = prop.toDouble();
0513         _context->type = ParseContext::T_DOUBLE;
0514         return true;
0515     }
0516 
0517     if (prop.type() == QVariant::List) {
0518         _context->seq = prop.toList();
0519         _context->type = ParseContext::T_SEQ;
0520         return true;
0521     }
0522 
0523     if (prop.type() == QVariant::StringList) {
0524         _context->strSeq = prop.toStringList();
0525         _context->type = ParseContext::T_STR_SEQ;
0526         return true;
0527     }
0528 
0529     // Value has unknown type
0530     return false;
0531 }
0532 
0533 bool ParseTreeMIN2::eval(ParseContext *_context) const
0534 {
0535     _context->type = ParseContext::T_DOUBLE;
0536 
0537     QVariant prop = _context->property(m_strId);
0538 
0539     if (!prop.isValid()) {
0540         return false;
0541     }
0542 
0543     if (!_context->initMaxima(m_strId)) {
0544         return false;
0545     }
0546 
0547     auto it = _context->maxima.constFind(m_strId);
0548     if (it == _context->maxima.cend()) {
0549         return false;
0550     }
0551 
0552     if (prop.type() == QVariant::Int && it.value().type == PreferencesMaxima::PM_INT) {
0553         _context->f = double(prop.toInt() - it.value().iMin) / double(it.value().iMax - it.value().iMin) * (-2.0) + 1.0;
0554         return true;
0555     } else if (prop.type() == QVariant::Double && it.value().type == PreferencesMaxima::PM_DOUBLE) {
0556         _context->f = (prop.toDouble() - it.value().fMin) / (it.value().fMax - it.value().fMin) * (-2.0) + 1.0;
0557         return true;
0558     }
0559 
0560     return false;
0561 }
0562 
0563 bool ParseTreeMAX2::eval(ParseContext *_context) const
0564 {
0565     _context->type = ParseContext::T_DOUBLE;
0566 
0567     QVariant prop = _context->property(m_strId);
0568 
0569     if (!prop.isValid()) {
0570         return false;
0571     }
0572 
0573     // Create extrema
0574     if (!_context->initMaxima(m_strId)) {
0575         return false;
0576     }
0577 
0578     // Find extrema
0579     auto it = _context->maxima.constFind(m_strId);
0580     if (it == _context->maxima.cend()) {
0581         return false;
0582     }
0583 
0584     if (prop.type() == QVariant::Int && it.value().type == PreferencesMaxima::PM_INT) {
0585         _context->f = double(prop.toInt() - it.value().iMin) / double(it.value().iMax - it.value().iMin) * 2.0 - 1.0;
0586         return true;
0587     } else if (prop.type() == QVariant::Double && it.value().type == PreferencesMaxima::PM_DOUBLE) {
0588         _context->f = (prop.toDouble() - it.value().fMin) / (it.value().fMax - it.value().fMin) * 2.0 - 1.0;
0589         return true;
0590     }
0591 
0592     return false;
0593 }
0594 
0595 int matchConstraint(const ParseTreeBase *_tree, const KService::Ptr &_service, const KService::List &_list)
0596 {
0597     // Empty tree matches always
0598     if (!_tree) {
0599         return 1;
0600     }
0601 
0602     QMap<QString, PreferencesMaxima> maxima;
0603     ParseContext c(_service, _list, maxima);
0604 
0605     // Error during evaluation ?
0606     if (!_tree->eval(&c)) {
0607         return -1;
0608     }
0609 
0610     // Did we get a bool ?
0611     if (c.type != ParseContext::T_BOOL) {
0612         return -1;
0613     }
0614 
0615     return (c.b ? 1 : 0);
0616 }
0617 
0618 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90)
0619 int matchConstraintPlugin(const ParseTreeBase *_tree, const KPluginInfo &_info, const KPluginInfo::List &_list)
0620 {
0621     // Empty tree matches always
0622     if (!_tree) {
0623         return 1;
0624     }
0625 
0626     QMap<QString, PreferencesMaxima> maxima;
0627     ParseContext c(_info, _list, maxima);
0628 
0629     // Error during evaluation ?
0630     if (!_tree->eval(&c)) {
0631         return -1;
0632     }
0633 
0634     // Did we get a bool ?
0635     if (c.type != ParseContext::T_BOOL) {
0636         return -1;
0637     }
0638 
0639     return (c.b ? 1 : 0);
0640 }
0641 #endif
0642 
0643 bool ParseContext::initMaxima(const QString &_prop)
0644 {
0645     // Is the property known ?
0646     QVariant prop = property(_prop);
0647 
0648     if (!prop.isValid()) {
0649         return false;
0650     }
0651 
0652     // Numeric ?
0653     if (prop.type() != QVariant::Int && prop.type() != QVariant::Double) {
0654         return false;
0655     }
0656 
0657     // Did we cache the result ?
0658     auto it = maxima.constFind(_prop);
0659     if (it != maxima.cend()) {
0660         return (it.value().type == PreferencesMaxima::PM_DOUBLE || it.value().type == PreferencesMaxima::PM_INT);
0661     }
0662 
0663     // Double or Int ?
0664     PreferencesMaxima extrema;
0665     if (prop.type() == QVariant::Int) {
0666         extrema.type = PreferencesMaxima::PM_INVALID_INT;
0667     } else {
0668         extrema.type = PreferencesMaxima::PM_INVALID_DOUBLE;
0669     }
0670 
0671     // Iterate over all offers
0672     QVariantList offerValues;
0673     if (service) {
0674         offerValues.reserve(offers.size());
0675 
0676         std::transform(offers.cbegin(), offers.cend(), std::back_inserter(offerValues), [&_prop](const KService::Ptr &servicePtr) {
0677             return servicePtr->property(_prop);
0678         });
0679     }
0680 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90)
0681     else if (info.isValid()) {
0682         offerValues.reserve(pluginOffers.size());
0683 
0684         std::transform(pluginOffers.cbegin(), pluginOffers.cend(), std::back_inserter(offerValues), [&_prop](const KPluginInfo &plugin) {
0685             return plugin.property(_prop);
0686         });
0687     }
0688 #endif
0689 
0690     for (const QVariant &p : std::as_const(offerValues)) {
0691         if (p.isValid()) {
0692             // Determine new maximum/minimum
0693             if (extrema.type == PreferencesMaxima::PM_INVALID_INT) {
0694                 extrema.type = PreferencesMaxima::PM_INT;
0695                 extrema.iMin = p.toInt();
0696                 extrema.iMax = p.toInt();
0697             }
0698             // Correct existing extrema
0699             else if (extrema.type == PreferencesMaxima::PM_INT) {
0700                 if (p.toInt() < extrema.iMin) {
0701                     extrema.iMin = p.toInt();
0702                 }
0703                 if (p.toInt() > extrema.iMax) {
0704                     extrema.iMax = p.toInt();
0705                 }
0706             }
0707             // Determine new maximum/minimum
0708             else if (extrema.type == PreferencesMaxima::PM_INVALID_DOUBLE) {
0709                 extrema.type = PreferencesMaxima::PM_DOUBLE;
0710                 extrema.fMin = p.toDouble();
0711                 extrema.fMax = p.toDouble();
0712             }
0713             // Correct existing extrema
0714             else if (extrema.type == PreferencesMaxima::PM_DOUBLE) {
0715                 if (p.toDouble() < it.value().fMin) {
0716                     extrema.fMin = p.toDouble();
0717                 }
0718                 if (p.toDouble() > it.value().fMax) {
0719                     extrema.fMax = p.toDouble();
0720                 }
0721             }
0722         }
0723     }
0724 
0725     // Cache the result
0726     maxima.insert(_prop, extrema);
0727 
0728     // Did we succeed ?
0729     return (extrema.type == PreferencesMaxima::PM_DOUBLE || extrema.type == PreferencesMaxima::PM_INT);
0730 }
0731 
0732 ParseTreeBase::~ParseTreeBase()
0733 {
0734 }
0735 
0736 bool ParseTreeSTRING::eval(ParseContext *_context) const
0737 {
0738     _context->type = ParseContext::T_STRING;
0739     _context->str = m_str;
0740     return true;
0741 }
0742 
0743 bool ParseTreeNUM::eval(ParseContext *_context) const
0744 {
0745     _context->type = ParseContext::T_NUM;
0746     _context->i = m_int;
0747     return true;
0748 }
0749 
0750 bool ParseTreeBRACKETS::eval(ParseContext *_context) const
0751 {
0752     return m_pLeft->eval(_context);
0753 }
0754 
0755 bool ParseTreeDOUBLE::eval(ParseContext *_context) const
0756 {
0757     _context->type = ParseContext::T_DOUBLE;
0758     _context->f = m_double;
0759     return true;
0760 }
0761 
0762 bool ParseTreeBOOL::eval(ParseContext *_context) const
0763 {
0764     _context->type = ParseContext::T_BOOL;
0765     _context->b = m_bool;
0766     return true;
0767 }
0768 
0769 }