File indexing completed on 2025-04-20 09:46:08
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 }