@ugo-studio/jspp 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/analysis/scope.js +22 -11
- package/dist/analysis/typeAnalyzer.js +4 -3
- package/dist/cli.js +7 -5
- package/dist/core/codegen/expression-handlers.js +10 -6
- package/dist/core/codegen/function-handlers.js +27 -6
- package/dist/core/codegen/helpers.js +12 -8
- package/dist/core/codegen/index.js +3 -1
- package/dist/core/codegen/statement-handlers.js +53 -16
- package/dist/core/codegen/visitor.js +7 -1
- package/package.json +2 -2
- package/src/prelude/access.hpp +8 -3
- package/src/prelude/any_value.hpp +141 -241
- package/src/prelude/any_value_helpers.hpp +225 -0
- package/src/prelude/error_helpers.hpp +3 -3
- package/src/prelude/index.hpp +17 -3
- package/src/prelude/library/console.hpp +7 -7
- package/src/prelude/library/performance.hpp +25 -0
- package/src/prelude/library/symbol.hpp +60 -3
- package/src/prelude/log_string.hpp +10 -6
- package/src/prelude/operators.hpp +2 -2
- package/src/prelude/types.hpp +24 -6
- package/src/prelude/values/array.hpp +2 -0
- package/src/prelude/values/function.hpp +33 -1
- package/src/prelude/values/{operators → helpers}/array.hpp +14 -7
- package/src/prelude/values/helpers/function.hpp +77 -0
- package/src/prelude/values/helpers/iterator.hpp +101 -0
- package/src/prelude/values/helpers/string.hpp +50 -0
- package/src/prelude/values/helpers/symbol.hpp +23 -0
- package/src/prelude/values/iterator.hpp +88 -0
- package/src/prelude/values/object.hpp +2 -1
- package/src/prelude/values/prototypes/array.hpp +18 -11
- package/src/prelude/values/prototypes/function.hpp +26 -0
- package/src/prelude/values/prototypes/iterator.hpp +57 -0
- package/src/prelude/values/prototypes/string.hpp +366 -357
- package/src/prelude/values/prototypes/symbol.hpp +39 -0
- package/src/prelude/values/string.hpp +25 -0
- package/src/prelude/values/symbol.hpp +102 -0
- package/src/prelude/well_known_symbols.hpp +7 -3
- package/src/prelude/values/operators/function.hpp +0 -34
- /package/src/prelude/values/{operators → helpers}/object.hpp +0 -0
|
@@ -11,18 +11,23 @@
|
|
|
11
11
|
#include <memory>
|
|
12
12
|
#include <utility>
|
|
13
13
|
#include <string>
|
|
14
|
-
#include <
|
|
14
|
+
#include <map>
|
|
15
15
|
#include <vector>
|
|
16
16
|
#include <functional>
|
|
17
17
|
#include <cmath>
|
|
18
|
+
#include <optional>
|
|
18
19
|
|
|
19
20
|
#include "types.hpp"
|
|
20
21
|
#include "values/non_values.hpp"
|
|
21
22
|
#include "values/object.hpp"
|
|
22
23
|
#include "values/array.hpp"
|
|
23
24
|
#include "values/function.hpp"
|
|
25
|
+
#include "values/iterator.hpp"
|
|
26
|
+
#include "values/symbol.hpp"
|
|
27
|
+
#include "values/string.hpp"
|
|
24
28
|
#include "error.hpp"
|
|
25
29
|
#include "descriptors.hpp"
|
|
30
|
+
#include "well_known_symbols.hpp"
|
|
26
31
|
|
|
27
32
|
namespace jspp
|
|
28
33
|
{
|
|
@@ -37,8 +42,10 @@ namespace jspp
|
|
|
37
42
|
Object = 6,
|
|
38
43
|
Array = 7,
|
|
39
44
|
Function = 8,
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
Iterator = 9,
|
|
46
|
+
Symbol = 10,
|
|
47
|
+
DataDescriptor = 11,
|
|
48
|
+
AccessorDescriptor = 12,
|
|
42
49
|
};
|
|
43
50
|
|
|
44
51
|
// Tagged storage with a union for payload
|
|
@@ -52,10 +59,12 @@ namespace jspp
|
|
|
52
59
|
JsUninitialized uninitialized;
|
|
53
60
|
bool boolean;
|
|
54
61
|
double number;
|
|
55
|
-
std::
|
|
62
|
+
std::shared_ptr<JsString> str;
|
|
56
63
|
std::shared_ptr<JsObject> object;
|
|
57
64
|
std::shared_ptr<JsArray> array;
|
|
58
65
|
std::shared_ptr<JsFunction> function;
|
|
66
|
+
std::shared_ptr<JsIterator<AnyValue>> iterator;
|
|
67
|
+
std::shared_ptr<JsSymbol> symbol;
|
|
59
68
|
std::shared_ptr<DataDescriptor> data_desc;
|
|
60
69
|
std::shared_ptr<AccessorDescriptor> accessor_desc;
|
|
61
70
|
};
|
|
@@ -74,7 +83,7 @@ namespace jspp
|
|
|
74
83
|
switch (storage.type)
|
|
75
84
|
{
|
|
76
85
|
case JsType::String:
|
|
77
|
-
storage.str.~
|
|
86
|
+
storage.str.~shared_ptr();
|
|
78
87
|
break;
|
|
79
88
|
case JsType::Object:
|
|
80
89
|
storage.object.~shared_ptr();
|
|
@@ -85,6 +94,12 @@ namespace jspp
|
|
|
85
94
|
case JsType::Function:
|
|
86
95
|
storage.function.~shared_ptr();
|
|
87
96
|
break;
|
|
97
|
+
case JsType::Iterator:
|
|
98
|
+
storage.iterator.~shared_ptr();
|
|
99
|
+
break;
|
|
100
|
+
case JsType::Symbol:
|
|
101
|
+
storage.symbol.~shared_ptr();
|
|
102
|
+
break;
|
|
88
103
|
case JsType::DataDescriptor:
|
|
89
104
|
storage.data_desc.~shared_ptr();
|
|
90
105
|
break;
|
|
@@ -124,7 +139,7 @@ namespace jspp
|
|
|
124
139
|
storage.number = other.storage.number;
|
|
125
140
|
break;
|
|
126
141
|
case JsType::String:
|
|
127
|
-
new (&storage.str) std::
|
|
142
|
+
new (&storage.str) std::shared_ptr<JsString>(std::move(other.storage.str));
|
|
128
143
|
break;
|
|
129
144
|
case JsType::Object:
|
|
130
145
|
new (&storage.object) std::shared_ptr<JsObject>(std::move(other.storage.object));
|
|
@@ -135,6 +150,12 @@ namespace jspp
|
|
|
135
150
|
case JsType::Function:
|
|
136
151
|
new (&storage.function) std::shared_ptr<JsFunction>(std::move(other.storage.function));
|
|
137
152
|
break;
|
|
153
|
+
case JsType::Iterator:
|
|
154
|
+
new (&storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(std::move(other.storage.iterator));
|
|
155
|
+
break;
|
|
156
|
+
case JsType::Symbol:
|
|
157
|
+
new (&storage.symbol) std::shared_ptr<JsSymbol>(std::move(other.storage.symbol));
|
|
158
|
+
break;
|
|
138
159
|
case JsType::DataDescriptor:
|
|
139
160
|
new (&storage.data_desc) std::shared_ptr<DataDescriptor>(std::move(other.storage.data_desc));
|
|
140
161
|
break;
|
|
@@ -165,7 +186,7 @@ namespace jspp
|
|
|
165
186
|
storage.number = other.storage.number;
|
|
166
187
|
break;
|
|
167
188
|
case JsType::String:
|
|
168
|
-
new (&storage.str) std::
|
|
189
|
+
new (&storage.str) std::shared_ptr<JsString>(std::make_shared<JsString>(*other.storage.str));
|
|
169
190
|
break;
|
|
170
191
|
case JsType::Object:
|
|
171
192
|
new (&storage.object) std::shared_ptr<JsObject>(other.storage.object); // shallow copy
|
|
@@ -176,6 +197,12 @@ namespace jspp
|
|
|
176
197
|
case JsType::Function:
|
|
177
198
|
new (&storage.function) std::shared_ptr<JsFunction>(other.storage.function); // shallow copy
|
|
178
199
|
break;
|
|
200
|
+
case JsType::Iterator:
|
|
201
|
+
new (&storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(other.storage.iterator); // shallow copy
|
|
202
|
+
break;
|
|
203
|
+
case JsType::Symbol:
|
|
204
|
+
new (&storage.symbol) std::shared_ptr<JsSymbol>(other.storage.symbol); // shallow copy (shared)
|
|
205
|
+
break;
|
|
179
206
|
case JsType::DataDescriptor:
|
|
180
207
|
new (&storage.data_desc) std::shared_ptr<DataDescriptor>(other.storage.data_desc); // shallow copy
|
|
181
208
|
break;
|
|
@@ -291,10 +318,10 @@ namespace jspp
|
|
|
291
318
|
{
|
|
292
319
|
AnyValue v;
|
|
293
320
|
v.storage.type = JsType::String;
|
|
294
|
-
new (&v.storage.str) std::
|
|
321
|
+
new (&v.storage.str) std::shared_ptr<JsString>(std::make_shared<JsString>(raw_s));
|
|
295
322
|
return v;
|
|
296
323
|
}
|
|
297
|
-
static AnyValue make_object(const std::
|
|
324
|
+
static AnyValue make_object(const std::map<std::string, AnyValue> &props) noexcept
|
|
298
325
|
{
|
|
299
326
|
AnyValue v;
|
|
300
327
|
v.storage.type = JsType::Object;
|
|
@@ -308,13 +335,27 @@ namespace jspp
|
|
|
308
335
|
new (&v.storage.array) std::shared_ptr<JsArray>(std::make_shared<JsArray>(dense));
|
|
309
336
|
return v;
|
|
310
337
|
}
|
|
311
|
-
static AnyValue make_function(const
|
|
338
|
+
static AnyValue make_function(const JsFunctionCallable &call, const std::string &name) noexcept
|
|
312
339
|
{
|
|
313
340
|
AnyValue v;
|
|
314
341
|
v.storage.type = JsType::Function;
|
|
315
342
|
new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, name));
|
|
316
343
|
return v;
|
|
317
344
|
}
|
|
345
|
+
static AnyValue make_generator(const JsFunctionCallable &call, const std::string &name) noexcept
|
|
346
|
+
{
|
|
347
|
+
AnyValue v;
|
|
348
|
+
v.storage.type = JsType::Function;
|
|
349
|
+
new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, true, name));
|
|
350
|
+
return v;
|
|
351
|
+
}
|
|
352
|
+
static AnyValue make_symbol(const std::string &description = "") noexcept
|
|
353
|
+
{
|
|
354
|
+
AnyValue v;
|
|
355
|
+
v.storage.type = JsType::Symbol;
|
|
356
|
+
new (&v.storage.symbol) std::shared_ptr<JsSymbol>(std::make_shared<JsSymbol>(description));
|
|
357
|
+
return v;
|
|
358
|
+
}
|
|
318
359
|
static AnyValue make_data_descriptor(const AnyValue &value, bool writable, bool enumerable, bool configurable) noexcept
|
|
319
360
|
{
|
|
320
361
|
AnyValue v;
|
|
@@ -333,6 +374,35 @@ namespace jspp
|
|
|
333
374
|
return v;
|
|
334
375
|
}
|
|
335
376
|
|
|
377
|
+
static AnyValue from_symbol(std::shared_ptr<JsSymbol> sym) noexcept
|
|
378
|
+
{
|
|
379
|
+
AnyValue v;
|
|
380
|
+
v.storage.type = JsType::Symbol;
|
|
381
|
+
new (&v.storage.symbol) std::shared_ptr<JsSymbol>(std::move(sym));
|
|
382
|
+
return v;
|
|
383
|
+
}
|
|
384
|
+
static AnyValue from_string(std::shared_ptr<JsString> str) noexcept
|
|
385
|
+
{
|
|
386
|
+
AnyValue v;
|
|
387
|
+
v.storage.type = JsType::String;
|
|
388
|
+
new (&v.storage.str) std::shared_ptr<JsString>(std::move(str));
|
|
389
|
+
return v;
|
|
390
|
+
}
|
|
391
|
+
static AnyValue from_iterator(JsIterator<AnyValue> &&iterator) noexcept
|
|
392
|
+
{
|
|
393
|
+
AnyValue v;
|
|
394
|
+
v.storage.type = JsType::Iterator;
|
|
395
|
+
new (&v.storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(std::make_shared<JsIterator<AnyValue>>(std::move(iterator)));
|
|
396
|
+
return v;
|
|
397
|
+
}
|
|
398
|
+
static AnyValue from_iterator_ref(JsIterator<AnyValue> *iterator) noexcept
|
|
399
|
+
{
|
|
400
|
+
AnyValue v;
|
|
401
|
+
v.storage.type = JsType::Iterator;
|
|
402
|
+
new (&v.storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(iterator, [](JsIterator<AnyValue> *) {});
|
|
403
|
+
return v;
|
|
404
|
+
}
|
|
405
|
+
|
|
336
406
|
// property resolution helpers ---------------------------------------
|
|
337
407
|
static AnyValue resolve_property_for_read(const AnyValue &val) noexcept
|
|
338
408
|
{
|
|
@@ -393,12 +463,15 @@ namespace jspp
|
|
|
393
463
|
bool is_object() const noexcept { return storage.type == JsType::Object; }
|
|
394
464
|
bool is_array() const noexcept { return storage.type == JsType::Array; }
|
|
395
465
|
bool is_function() const noexcept { return storage.type == JsType::Function; }
|
|
466
|
+
bool is_iterator() const noexcept { return storage.type == JsType::Iterator; }
|
|
396
467
|
bool is_boolean() const noexcept { return storage.type == JsType::Boolean; }
|
|
468
|
+
bool is_symbol() const noexcept { return storage.type == JsType::Symbol; }
|
|
397
469
|
bool is_null() const noexcept { return storage.type == JsType::Null; }
|
|
398
470
|
bool is_undefined() const noexcept { return storage.type == JsType::Undefined; }
|
|
399
471
|
bool is_uninitialized() const noexcept { return storage.type == JsType::Uninitialized; }
|
|
400
472
|
bool is_data_descriptor() const noexcept { return storage.type == JsType::DataDescriptor; }
|
|
401
473
|
bool is_accessor_descriptor() const noexcept { return storage.type == JsType::AccessorDescriptor; }
|
|
474
|
+
bool is_generator() const noexcept { return storage.type == JsType::Function && storage.function->is_generator; }
|
|
402
475
|
|
|
403
476
|
// --- TYPE CASTERS
|
|
404
477
|
double as_double() const noexcept
|
|
@@ -411,7 +484,7 @@ namespace jspp
|
|
|
411
484
|
assert(is_boolean());
|
|
412
485
|
return storage.boolean;
|
|
413
486
|
}
|
|
414
|
-
|
|
487
|
+
JsString *as_string() const noexcept
|
|
415
488
|
{
|
|
416
489
|
assert(is_string());
|
|
417
490
|
return storage.str.get();
|
|
@@ -426,11 +499,21 @@ namespace jspp
|
|
|
426
499
|
assert(is_array());
|
|
427
500
|
return storage.array.get();
|
|
428
501
|
}
|
|
429
|
-
JsFunction *as_function(const std::string &expression =
|
|
502
|
+
JsFunction *as_function(const std::optional<std::string> &expression = std::nullopt) const
|
|
430
503
|
{
|
|
431
504
|
if (is_function())
|
|
432
505
|
return storage.function.get();
|
|
433
|
-
throw RuntimeError::make_error(expression + " is not a function", "TypeError");
|
|
506
|
+
throw RuntimeError::make_error(expression.value_or(to_std_string()) + " is not a function", "TypeError");
|
|
507
|
+
}
|
|
508
|
+
JsSymbol *as_symbol() const noexcept
|
|
509
|
+
{
|
|
510
|
+
assert(is_symbol());
|
|
511
|
+
return storage.symbol.get();
|
|
512
|
+
}
|
|
513
|
+
std::shared_ptr<JsIterator<AnyValue>> as_iterator() const
|
|
514
|
+
{
|
|
515
|
+
assert(is_iterator());
|
|
516
|
+
return storage.iterator; // Returns the shared_ptr, incrementing ref count
|
|
434
517
|
}
|
|
435
518
|
DataDescriptor *as_data_descriptor() const noexcept
|
|
436
519
|
{
|
|
@@ -444,31 +527,22 @@ namespace jspp
|
|
|
444
527
|
}
|
|
445
528
|
|
|
446
529
|
// --- PROPERTY ACCESS OPERATORS
|
|
447
|
-
AnyValue get_own_property(const std::string &key)
|
|
530
|
+
AnyValue get_own_property(const std::string &key) const
|
|
448
531
|
{
|
|
449
532
|
switch (storage.type)
|
|
450
533
|
{
|
|
451
534
|
case JsType::Object:
|
|
452
|
-
return
|
|
535
|
+
return storage.object->get_property(key);
|
|
453
536
|
case JsType::Array:
|
|
454
|
-
return
|
|
537
|
+
return storage.array->get_property(key);
|
|
455
538
|
case JsType::Function:
|
|
456
|
-
return
|
|
539
|
+
return storage.function->get_property(key);
|
|
540
|
+
case JsType::Iterator:
|
|
541
|
+
return storage.iterator->get_property(key);
|
|
542
|
+
case JsType::Symbol:
|
|
543
|
+
return storage.symbol->get_property(key);
|
|
457
544
|
case JsType::String:
|
|
458
|
-
|
|
459
|
-
// Check for prototype methods
|
|
460
|
-
auto proto_fn = StringPrototypes::get(key, this->storage.str);
|
|
461
|
-
if (proto_fn.has_value())
|
|
462
|
-
{
|
|
463
|
-
return resolve_property_for_read(proto_fn.value());
|
|
464
|
-
}
|
|
465
|
-
// Handle character access by string index (e.g., "abc"["1"])
|
|
466
|
-
if (JsArray::is_array_index(key))
|
|
467
|
-
{
|
|
468
|
-
uint32_t idx = static_cast<uint32_t>(std::stoull(key));
|
|
469
|
-
return get_own_property(idx);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
545
|
+
return storage.str->get_property(key);
|
|
472
546
|
case JsType::Undefined:
|
|
473
547
|
throw RuntimeError::make_error("Cannot read properties of undefined (reading '" + key + "')", "TypeError");
|
|
474
548
|
case JsType::Null:
|
|
@@ -477,41 +551,42 @@ namespace jspp
|
|
|
477
551
|
return AnyValue::make_undefined();
|
|
478
552
|
}
|
|
479
553
|
}
|
|
480
|
-
AnyValue get_own_property(uint32_t idx) noexcept
|
|
554
|
+
AnyValue get_own_property(uint32_t idx) const noexcept
|
|
481
555
|
{
|
|
482
556
|
switch (storage.type)
|
|
483
557
|
{
|
|
484
558
|
case JsType::Array:
|
|
485
|
-
return
|
|
486
|
-
case JsType::String:
|
|
487
|
-
|
|
488
|
-
if (idx < storage.str->length())
|
|
489
|
-
{
|
|
490
|
-
return AnyValue::make_string(std::string(1, (*storage.str)[idx]));
|
|
491
|
-
}
|
|
492
|
-
return AnyValue::make_undefined();
|
|
493
|
-
}
|
|
559
|
+
return storage.array->get_property(idx);
|
|
560
|
+
case JsType::String:
|
|
561
|
+
return storage.str->get_property(idx);
|
|
494
562
|
default:
|
|
495
563
|
return get_own_property(std::to_string(idx));
|
|
496
564
|
}
|
|
497
565
|
}
|
|
498
|
-
AnyValue get_own_property(const AnyValue &key) noexcept
|
|
566
|
+
AnyValue get_own_property(const AnyValue &key) const noexcept
|
|
499
567
|
{
|
|
500
568
|
if (key.storage.type == JsType::Number && storage.type == JsType::Array)
|
|
501
569
|
return storage.array->get_property(key.storage.number);
|
|
570
|
+
if (key.storage.type == JsType::Number && storage.type == JsType::String)
|
|
571
|
+
return storage.str->get_property(key.storage.number);
|
|
572
|
+
|
|
573
|
+
// If the key is a Symbol, use its internal key string
|
|
574
|
+
if (key.storage.type == JsType::Symbol)
|
|
575
|
+
return get_own_property(key.storage.symbol->key);
|
|
576
|
+
|
|
502
577
|
return get_own_property(key.to_std_string());
|
|
503
578
|
}
|
|
504
579
|
// for setting values
|
|
505
|
-
AnyValue set_own_property(const std::string &key, const AnyValue &value)
|
|
580
|
+
AnyValue set_own_property(const std::string &key, const AnyValue &value) const
|
|
506
581
|
{
|
|
507
582
|
switch (storage.type)
|
|
508
583
|
{
|
|
509
584
|
case JsType::Object:
|
|
510
|
-
return
|
|
585
|
+
return storage.object->set_property(key, value);
|
|
511
586
|
case JsType::Array:
|
|
512
|
-
return
|
|
587
|
+
return storage.array->set_property(key, value);
|
|
513
588
|
case JsType::Function:
|
|
514
|
-
return
|
|
589
|
+
return storage.function->set_property(key, value);
|
|
515
590
|
case JsType::Undefined:
|
|
516
591
|
throw RuntimeError::make_error("Cannot set properties of undefined (setting '" + key + "')", "TypeError");
|
|
517
592
|
case JsType::Null:
|
|
@@ -520,215 +595,40 @@ namespace jspp
|
|
|
520
595
|
return value;
|
|
521
596
|
}
|
|
522
597
|
}
|
|
523
|
-
AnyValue set_own_property(uint32_t idx, const AnyValue &value)
|
|
598
|
+
AnyValue set_own_property(uint32_t idx, const AnyValue &value) const
|
|
524
599
|
{
|
|
525
600
|
if (storage.type == JsType::Array)
|
|
526
601
|
{
|
|
527
|
-
return
|
|
602
|
+
return storage.array->set_property(idx, value);
|
|
528
603
|
}
|
|
529
604
|
return set_own_property(std::to_string(idx), value);
|
|
530
605
|
}
|
|
531
|
-
AnyValue set_own_property(const AnyValue &key, const AnyValue &value)
|
|
606
|
+
AnyValue set_own_property(const AnyValue &key, const AnyValue &value) const
|
|
532
607
|
{
|
|
533
608
|
if (key.storage.type == JsType::Number && storage.type == JsType::Array)
|
|
534
609
|
{
|
|
535
610
|
return storage.array->set_property(key.storage.number, value);
|
|
536
611
|
}
|
|
612
|
+
|
|
613
|
+
// If the key is a Symbol, use its internal key string
|
|
614
|
+
if (key.storage.type == JsType::Symbol)
|
|
615
|
+
return set_own_property(key.storage.symbol->key, value);
|
|
616
|
+
|
|
537
617
|
return set_own_property(key.to_std_string(), value);
|
|
538
618
|
}
|
|
539
619
|
|
|
540
620
|
// --- HELPERS
|
|
541
|
-
const bool is_truthy() const noexcept
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
const bool is_strictly_equal_to(const AnyValue &other) const noexcept
|
|
556
|
-
{
|
|
557
|
-
if (storage.type == other.storage.type)
|
|
558
|
-
{
|
|
559
|
-
switch (storage.type)
|
|
560
|
-
{
|
|
561
|
-
case JsType::Boolean:
|
|
562
|
-
return storage.boolean == other.storage.boolean;
|
|
563
|
-
case JsType::Number:
|
|
564
|
-
return storage.number == other.storage.number;
|
|
565
|
-
case JsType::String:
|
|
566
|
-
return (*storage.str.get() == *other.storage.str.get());
|
|
567
|
-
case JsType::Array:
|
|
568
|
-
return (storage.array == other.storage.array);
|
|
569
|
-
case JsType::Object:
|
|
570
|
-
return (storage.object == other.storage.object);
|
|
571
|
-
case JsType::Function:
|
|
572
|
-
return (storage.function == other.storage.function);
|
|
573
|
-
default:
|
|
574
|
-
return true;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return false;
|
|
578
|
-
}
|
|
579
|
-
const bool is_equal_to(const AnyValue &other) const noexcept
|
|
580
|
-
{
|
|
581
|
-
// Implements JavaScript's Abstract Equality Comparison Algorithm (==)
|
|
582
|
-
// Step 1: If types are the same, use strict equality (===)
|
|
583
|
-
if (storage.type == other.storage.type)
|
|
584
|
-
{
|
|
585
|
-
return is_strictly_equal_to(other);
|
|
586
|
-
}
|
|
587
|
-
// Steps 2 & 3: null == undefined
|
|
588
|
-
if ((is_null() && other.is_undefined()) || (is_undefined() && other.is_null()))
|
|
589
|
-
{
|
|
590
|
-
return true;
|
|
591
|
-
}
|
|
592
|
-
// Step 4 & 5: number == string
|
|
593
|
-
if (is_number() && other.is_string())
|
|
594
|
-
{
|
|
595
|
-
double num_this = this->as_double();
|
|
596
|
-
double num_other;
|
|
597
|
-
try
|
|
598
|
-
{
|
|
599
|
-
const std::string &s = *other.as_string();
|
|
600
|
-
// JS considers empty string or whitespace-only string to be 0
|
|
601
|
-
if (s.empty() || std::all_of(s.begin(), s.end(), [](unsigned char c)
|
|
602
|
-
{ return std::isspace(c); }))
|
|
603
|
-
{
|
|
604
|
-
num_other = 0.0;
|
|
605
|
-
}
|
|
606
|
-
else
|
|
607
|
-
{
|
|
608
|
-
size_t pos;
|
|
609
|
-
num_other = std::stod(s, &pos);
|
|
610
|
-
// Check if the entire string was consumed, allowing for trailing whitespace
|
|
611
|
-
while (pos < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
|
|
612
|
-
{
|
|
613
|
-
pos++;
|
|
614
|
-
}
|
|
615
|
-
if (pos != s.length())
|
|
616
|
-
{
|
|
617
|
-
num_other = std::numeric_limits<double>::quiet_NaN();
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
catch (...)
|
|
622
|
-
{
|
|
623
|
-
num_other = std::numeric_limits<double>::quiet_NaN();
|
|
624
|
-
}
|
|
625
|
-
return num_this == num_other;
|
|
626
|
-
}
|
|
627
|
-
if (is_string() && other.is_number())
|
|
628
|
-
{
|
|
629
|
-
// Delegate to the other operand to avoid code duplication
|
|
630
|
-
return other.is_equal_to(*this);
|
|
631
|
-
}
|
|
632
|
-
// Step 6 & 7: boolean == any
|
|
633
|
-
if (is_boolean())
|
|
634
|
-
{
|
|
635
|
-
// Convert boolean to number and re-compare
|
|
636
|
-
return AnyValue::make_number(as_boolean() ? 1.0 : 0.0).is_equal_to(other);
|
|
637
|
-
}
|
|
638
|
-
if (other.is_boolean())
|
|
639
|
-
{
|
|
640
|
-
// Convert boolean to number and re-compare
|
|
641
|
-
return is_equal_to(AnyValue::make_number(other.as_boolean() ? 1.0 : 0.0));
|
|
642
|
-
}
|
|
643
|
-
// Step 8 & 9: object == (string or number)
|
|
644
|
-
if ((other.is_object() || other.is_array() || other.is_function()) && (is_string() || is_number()))
|
|
645
|
-
{
|
|
646
|
-
// Delegate to the other operand to avoid code duplication
|
|
647
|
-
return other.is_equal_to(*this);
|
|
648
|
-
}
|
|
649
|
-
if ((is_object() || is_array() || is_function()) && (other.is_string() || other.is_number()))
|
|
650
|
-
{
|
|
651
|
-
// Convert object to primitive (string) and re-compare.
|
|
652
|
-
// This is a simplification of JS's ToPrimitive which would try valueOf() first.
|
|
653
|
-
return AnyValue::make_string(to_std_string()).is_equal_to(other);
|
|
654
|
-
}
|
|
655
|
-
// Step 10: All other cases (e.g., object == null) are false.
|
|
656
|
-
return false;
|
|
657
|
-
}
|
|
658
|
-
const AnyValue is_strictly_equal_to_primitive(const AnyValue &other) const noexcept
|
|
659
|
-
{
|
|
660
|
-
return AnyValue::make_boolean(is_strictly_equal_to(other));
|
|
661
|
-
}
|
|
662
|
-
const AnyValue is_equal_to_primitive(const AnyValue &other) const noexcept
|
|
663
|
-
{
|
|
664
|
-
return AnyValue::make_boolean(is_equal_to(other));
|
|
665
|
-
}
|
|
666
|
-
const AnyValue not_strictly_equal_to_primitive(const AnyValue &other) const noexcept
|
|
667
|
-
{
|
|
668
|
-
return AnyValue::make_boolean(!is_strictly_equal_to(other));
|
|
669
|
-
}
|
|
670
|
-
const AnyValue not_equal_to_primitive(const AnyValue &other) const noexcept
|
|
671
|
-
{
|
|
672
|
-
return AnyValue::make_boolean(!is_equal_to(other));
|
|
673
|
-
}
|
|
674
|
-
const std::string to_std_string() const noexcept
|
|
675
|
-
{
|
|
676
|
-
switch (storage.type)
|
|
677
|
-
{
|
|
678
|
-
case JsType::Undefined:
|
|
679
|
-
return "undefined";
|
|
680
|
-
case JsType::Null:
|
|
681
|
-
return "null";
|
|
682
|
-
case JsType::Boolean:
|
|
683
|
-
return storage.boolean ? "true" : "false";
|
|
684
|
-
case JsType::String:
|
|
685
|
-
return *storage.str.get();
|
|
686
|
-
case JsType::Object:
|
|
687
|
-
return storage.object->to_std_string();
|
|
688
|
-
case JsType::Array:
|
|
689
|
-
return storage.array->to_std_string();
|
|
690
|
-
case JsType::Function:
|
|
691
|
-
return storage.function->to_std_string();
|
|
692
|
-
case JsType::DataDescriptor:
|
|
693
|
-
return storage.data_desc->value->to_std_string();
|
|
694
|
-
case JsType::AccessorDescriptor:
|
|
695
|
-
{
|
|
696
|
-
if (storage.accessor_desc->get.has_value())
|
|
697
|
-
return storage.accessor_desc->get.value()({}).to_std_string();
|
|
698
|
-
else
|
|
699
|
-
return "undefined";
|
|
700
|
-
}
|
|
701
|
-
case JsType::Number:
|
|
702
|
-
{
|
|
703
|
-
if (std::isnan(storage.number))
|
|
704
|
-
{
|
|
705
|
-
return "NaN";
|
|
706
|
-
}
|
|
707
|
-
if (std::abs(storage.number) >= 1e21 || (std::abs(storage.number) > 0 && std::abs(storage.number) < 1e-6))
|
|
708
|
-
{
|
|
709
|
-
std::ostringstream oss;
|
|
710
|
-
oss << std::scientific << std::setprecision(4) << storage.number;
|
|
711
|
-
return oss.str();
|
|
712
|
-
}
|
|
713
|
-
else
|
|
714
|
-
{
|
|
715
|
-
std::ostringstream oss;
|
|
716
|
-
oss << std::setprecision(6) << std::fixed << storage.number;
|
|
717
|
-
std::string s = oss.str();
|
|
718
|
-
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
|
719
|
-
if (!s.empty() && s.back() == '.')
|
|
720
|
-
{
|
|
721
|
-
s.pop_back();
|
|
722
|
-
}
|
|
723
|
-
return s;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
// Uninitialized and default should not be reached under normal circumstances
|
|
727
|
-
case JsType::Uninitialized:
|
|
728
|
-
return "<uninitialized>";
|
|
729
|
-
default:
|
|
730
|
-
return "";
|
|
731
|
-
}
|
|
732
|
-
}
|
|
621
|
+
const bool is_truthy() const noexcept;
|
|
622
|
+
|
|
623
|
+
const bool is_strictly_equal_to(const AnyValue &other) const noexcept;
|
|
624
|
+
const bool is_equal_to(const AnyValue &other) const noexcept;
|
|
625
|
+
|
|
626
|
+
const AnyValue is_strictly_equal_to_primitive(const AnyValue &other) const noexcept;
|
|
627
|
+
const AnyValue is_equal_to_primitive(const AnyValue &other) const noexcept;
|
|
628
|
+
|
|
629
|
+
const AnyValue not_strictly_equal_to_primitive(const AnyValue &other) const noexcept;
|
|
630
|
+
const AnyValue not_equal_to_primitive(const AnyValue &other) const noexcept;
|
|
631
|
+
|
|
632
|
+
const std::string to_std_string() const noexcept;
|
|
733
633
|
};
|
|
734
634
|
}
|