|
3 | 3 | #pragma once |
4 | 4 |
|
5 | 5 | #include <string> |
6 | | -#include <variant> |
7 | 6 | #include <algorithm> |
8 | 7 | #include <limits> |
9 | 8 | #include <ctgmath> |
| 9 | +#include <cassert> |
10 | 10 | #include <utf8.h> |
11 | 11 |
|
12 | 12 | #include "global.h" |
@@ -548,63 +548,75 @@ class LIBSCRATCHCPP_EXPORT Value |
548 | 548 | } |
549 | 549 | } |
550 | 550 |
|
| 551 | + double getNumber(bool *ok = nullptr) const |
| 552 | + { |
| 553 | + // Equivalent to JavaScript Number(), *ok == false means NaN |
| 554 | + if (ok) |
| 555 | + *ok = true; |
| 556 | + |
| 557 | + switch (m_type) { |
| 558 | + case Type::Integer: |
| 559 | + return m_intValue; |
| 560 | + |
| 561 | + case Type::Double: |
| 562 | + return m_doubleValue; |
| 563 | + |
| 564 | + case Type::Bool: |
| 565 | + return m_boolValue; |
| 566 | + |
| 567 | + case Type::String: |
| 568 | + return stringToDouble(m_stringValue, ok); |
| 569 | + |
| 570 | + case Type::Infinity: |
| 571 | + return std::numeric_limits<double>::infinity(); |
| 572 | + |
| 573 | + case Type::NegativeInfinity: |
| 574 | + return -std::numeric_limits<double>::infinity(); |
| 575 | + |
| 576 | + case Type::NaN: |
| 577 | + if (ok) |
| 578 | + *ok = false; |
| 579 | + |
| 580 | + return 0; |
| 581 | + |
| 582 | + default: |
| 583 | + assert(false); // this should never happen |
| 584 | + if (ok) |
| 585 | + *ok = false; |
| 586 | + |
| 587 | + return 0; |
| 588 | + } |
| 589 | + } |
| 590 | + |
551 | 591 | void initString(const char *str) { initString(std::string(str)); } |
552 | 592 |
|
553 | 593 | Type m_type; |
554 | 594 |
|
555 | 595 | friend bool operator==(const Value &v1, const Value &v2) |
556 | 596 | { |
557 | | - if (v1.m_type == v2.m_type) { |
558 | | - auto type = v1.m_type; |
559 | | - switch (type) { |
560 | | - case Type::Integer: |
561 | | - return v1.toLong() == v2.toLong(); |
562 | | - case Type::Double: |
563 | | - return v1.toDouble() == v2.toDouble(); |
564 | | - case Type::Bool: |
565 | | - return v1.toBool() == v2.toBool(); |
566 | | - case Type::String: |
567 | | - return stringsEqual(v1.toUtf16(), v2.toUtf16()); |
568 | | - default: |
569 | | - if ((static_cast<int>(v1.m_type) < 0) && (static_cast<int>(v2.m_type) < 0)) { |
570 | | - return v1.m_type == v2.m_type; |
571 | | - } |
572 | | - } |
573 | | - } else { |
574 | | - if (v1.isString() || v2.isString()) { |
575 | | - if (static_cast<int>(v1.m_type) < 0 || static_cast<int>(v2.m_type) < 0) |
576 | | - return stringsEqual(v1.toUtf16(), v2.toUtf16()); |
577 | | - |
578 | | - long l1, l2; |
579 | | - double d1, d2; |
580 | | - int type1 = checkString(v1.toString(), &l1, &d1); |
581 | | - int type2 = checkString(v2.toString(), &l2, &d2); |
582 | | - |
583 | | - if (type1 == 1) |
584 | | - d1 = l1; |
585 | | - |
586 | | - if (type2 == 1) |
587 | | - d2 = l2; |
588 | | - |
589 | | - if (type1 > 0 && type2 > 0) |
590 | | - return d1 == d2; |
591 | | - else if (type2 > 0) |
592 | | - return v1.toString() == doubleToString(d2); |
593 | | - else if (type1 > 0) |
594 | | - return doubleToString(d1) == v2.toString(); |
595 | | - else |
596 | | - return stringsEqual(v1.toUtf16(), v2.toUtf16()); |
597 | | - } else if (v1.isNumber() || v2.isNumber()) { |
598 | | - if (static_cast<int>(v1.m_type) < 0 || static_cast<int>(v2.m_type) < 0) |
599 | | - return false; |
600 | | - else |
601 | | - return v1.toDouble() == v2.toDouble(); |
602 | | - } else if (v1.isBool() || v2.isBool()) |
603 | | - return ((v1.m_type != Type::NaN && v2.m_type != Type::NaN) && (v1.toBool() == v2.toBool())); |
604 | | - else |
605 | | - return false; |
| 597 | + // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150 |
| 598 | + bool ok; |
| 599 | + double n1 = v1.getNumber(&ok); |
| 600 | + double n2; |
| 601 | + |
| 602 | + if (ok) |
| 603 | + n2 = v2.getNumber(&ok); |
| 604 | + |
| 605 | + if (!ok) { |
| 606 | + // At least one argument can't be converted to a number |
| 607 | + // Scratch compares strings as case insensitive |
| 608 | + return stringsEqual(v1.toUtf16(), v2.toUtf16()); |
606 | 609 | } |
607 | | - return false; |
| 610 | + |
| 611 | + // Handle the special case of Infinity |
| 612 | + if ((static_cast<int>(v1.m_type) < 0) && (static_cast<int>(v2.m_type) < 0)) { |
| 613 | + assert(v1.m_type != Type::NaN); |
| 614 | + assert(v2.m_type != Type::NaN); |
| 615 | + return v1.m_type == v2.m_type; |
| 616 | + } |
| 617 | + |
| 618 | + // Compare as numbers |
| 619 | + return n1 == n2; |
608 | 620 | } |
609 | 621 |
|
610 | 622 | friend bool operator!=(const Value &v1, const Value &v2) { return !(v1 == v2); } |
|
0 commit comments