@ugo-studio/jspp 0.1.3 → 0.1.5

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.
Files changed (83) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/scope.js +33 -4
  3. package/dist/analysis/typeAnalyzer.js +260 -21
  4. package/dist/ast/symbols.js +29 -0
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -31
  10. package/dist/core/codegen/class-handlers.js +131 -0
  11. package/dist/core/codegen/control-flow-handlers.js +474 -0
  12. package/dist/core/codegen/declaration-handlers.js +36 -15
  13. package/dist/core/codegen/expression-handlers.js +579 -125
  14. package/dist/core/codegen/function-handlers.js +222 -37
  15. package/dist/core/codegen/helpers.js +158 -4
  16. package/dist/core/codegen/index.js +20 -8
  17. package/dist/core/codegen/literal-handlers.js +18 -6
  18. package/dist/core/codegen/statement-handlers.js +171 -228
  19. package/dist/core/codegen/visitor.js +31 -3
  20. package/package.json +3 -3
  21. package/src/prelude/any_value.hpp +510 -633
  22. package/src/prelude/any_value_access.hpp +151 -0
  23. package/src/prelude/any_value_defines.hpp +190 -0
  24. package/src/prelude/any_value_helpers.hpp +139 -225
  25. package/src/prelude/exception.hpp +32 -0
  26. package/src/prelude/exception_helpers.hpp +49 -0
  27. package/src/prelude/index.hpp +25 -9
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +14 -13
  30. package/src/prelude/library/error.hpp +113 -0
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +35 -4
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +2 -2
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +131 -0
  38. package/src/prelude/library/symbol.hpp +46 -59
  39. package/src/prelude/library/timer.hpp +92 -0
  40. package/src/prelude/scheduler.hpp +145 -0
  41. package/src/prelude/types.hpp +58 -1
  42. package/src/prelude/utils/access.hpp +345 -0
  43. package/src/prelude/utils/assignment_operators.hpp +99 -0
  44. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  45. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  46. package/src/prelude/utils/log_any_value/function.hpp +39 -0
  47. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  48. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  49. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  50. package/src/prelude/utils/log_any_value/object.hpp +136 -0
  51. package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
  52. package/src/prelude/utils/operators.hpp +751 -0
  53. package/src/prelude/utils/well_known_symbols.hpp +25 -0
  54. package/src/prelude/values/array.hpp +10 -7
  55. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  56. package/src/prelude/values/function.hpp +85 -51
  57. package/src/prelude/values/helpers/array.hpp +80 -35
  58. package/src/prelude/values/helpers/function.hpp +110 -77
  59. package/src/prelude/values/helpers/iterator.hpp +16 -10
  60. package/src/prelude/values/helpers/object.hpp +85 -10
  61. package/src/prelude/values/helpers/promise.hpp +181 -0
  62. package/src/prelude/values/helpers/string.hpp +3 -3
  63. package/src/prelude/values/helpers/symbol.hpp +2 -2
  64. package/src/prelude/values/iterator.hpp +14 -6
  65. package/src/prelude/values/object.hpp +14 -3
  66. package/src/prelude/values/promise.hpp +73 -0
  67. package/src/prelude/values/prototypes/array.hpp +855 -16
  68. package/src/prelude/values/prototypes/function.hpp +4 -4
  69. package/src/prelude/values/prototypes/iterator.hpp +11 -10
  70. package/src/prelude/values/prototypes/number.hpp +153 -0
  71. package/src/prelude/values/prototypes/object.hpp +26 -0
  72. package/src/prelude/values/prototypes/promise.hpp +134 -0
  73. package/src/prelude/values/prototypes/string.hpp +29 -29
  74. package/src/prelude/values/prototypes/symbol.hpp +22 -3
  75. package/src/prelude/values/shape.hpp +52 -0
  76. package/src/prelude/values/string.hpp +1 -1
  77. package/src/prelude/values/symbol.hpp +1 -1
  78. package/src/prelude/access.hpp +0 -91
  79. package/src/prelude/error.hpp +0 -31
  80. package/src/prelude/error_helpers.hpp +0 -59
  81. package/src/prelude/log_string.hpp +0 -407
  82. package/src/prelude/operators.hpp +0 -256
  83. package/src/prelude/well_known_symbols.hpp +0 -14
@@ -0,0 +1,131 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/promise.hpp"
5
+ #include "any_value.hpp"
6
+ #include "exception.hpp"
7
+
8
+ inline auto Promise = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
9
+ {
10
+ if (args.empty() || !args[0].is_function())
11
+ {
12
+ throw jspp::Exception::make_exception("Promise resolver undefined is not a function", "TypeError");
13
+ }
14
+ auto executor = args[0].as_function();
15
+
16
+ jspp::JsPromise promise;
17
+ auto state = promise.state; // Share state
18
+
19
+ // resolve function
20
+ auto resolveFn = jspp::AnyValue::make_function([state](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
21
+ {
22
+ jspp::JsPromise p; p.state = state;
23
+ p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
24
+ return jspp::AnyValue::make_undefined(); }, "resolve");
25
+
26
+ // reject function
27
+ auto rejectFn = jspp::AnyValue::make_function([state](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
28
+ {
29
+ jspp::JsPromise p; p.state = state;
30
+ p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
31
+ return jspp::AnyValue::make_undefined(); }, "reject");
32
+
33
+ try
34
+ {
35
+ const jspp::AnyValue executorArgs[] = {resolveFn, rejectFn};
36
+ executor->call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(executorArgs, 2));
37
+ }
38
+ catch (const jspp::Exception &e)
39
+ {
40
+ promise.reject(*e.data);
41
+ }
42
+ catch (...)
43
+ {
44
+ promise.reject(jspp::AnyValue::make_string("Unknown error during Promise execution"));
45
+ }
46
+
47
+ return jspp::AnyValue::make_promise(promise); },
48
+ "Promise");
49
+
50
+ struct PromiseInit
51
+ {
52
+ PromiseInit()
53
+ {
54
+ // Promise.resolve(value)
55
+ Promise.define_data_property("resolve", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
56
+ {
57
+ jspp::JsPromise p;
58
+ p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
59
+ return jspp::AnyValue::make_promise(p); }, "resolve"));
60
+
61
+ // Promise.reject(reason)
62
+ Promise.define_data_property("reject", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
63
+ {
64
+ jspp::JsPromise p;
65
+ p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
66
+ return jspp::AnyValue::make_promise(p); }, "reject"));
67
+
68
+ // Promise.all(iterable)
69
+ Promise.define_data_property("all", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
70
+ {
71
+ // Basic implementation for arrays
72
+ if (args.empty() || !args[0].is_array()) {
73
+ // If not array, reject? Or treat as non-iterable?
74
+ // Should throw TypeError if not iterable. For now assume array.
75
+ // If empty array, return resolved empty array.
76
+ // TODO: Strict iterable check
77
+ }
78
+
79
+ // Handle non-array iterable or empty args
80
+ if (args.empty() || !args[0].is_array()) {
81
+ jspp::JsPromise p; p.reject(jspp::AnyValue::make_string("Promise.all argument must be an array"));
82
+ return jspp::AnyValue::make_promise(p);
83
+ }
84
+
85
+ auto arr = args[0].as_array();
86
+ size_t len = static_cast<size_t>(arr->length);
87
+ if (len == 0) {
88
+ jspp::JsPromise p; p.resolve(jspp::AnyValue::make_array(std::vector<jspp::AnyValue>()));
89
+ return jspp::AnyValue::make_promise(p);
90
+ }
91
+
92
+ jspp::JsPromise masterPromise;
93
+ auto masterState = masterPromise.state;
94
+
95
+ auto results = std::make_shared<std::vector<jspp::AnyValue>>(len, jspp::Constants::UNDEFINED);
96
+ auto count = std::make_shared<size_t>(len);
97
+
98
+ // Check if already rejected to avoid further processing
99
+ auto rejected = std::make_shared<bool>(false);
100
+
101
+ for (size_t i = 0; i < len; ++i) {
102
+ jspp::AnyValue item = arr->get_property(static_cast<uint32_t>(i));
103
+
104
+ auto handleResult = [masterState, results, count, i, rejected](const jspp::AnyValue& res) {
105
+ if (*rejected) return;
106
+ (*results)[i] = res;
107
+ (*count)--;
108
+ if (*count == 0) {
109
+ jspp::JsPromise p; p.state = masterState;
110
+ p.resolve(jspp::AnyValue::make_array(std::move(*results)));
111
+ }
112
+ };
113
+
114
+ auto handleReject = [masterState, rejected](const jspp::AnyValue& reason) {
115
+ if (*rejected) return;
116
+ *rejected = true;
117
+ jspp::JsPromise p; p.state = masterState;
118
+ masterState->status =jspp::PromiseStatus::Rejected; // ensure master state updated
119
+ p.reject(reason);
120
+ };
121
+
122
+ if (item.is_promise()) {
123
+ item.as_promise()->then(handleResult, handleReject);
124
+ } else {
125
+ handleResult(item);
126
+ }
127
+ }
128
+
129
+ return jspp::AnyValue::make_promise(masterPromise); }, "all"));
130
+ }
131
+ } promiseInit;
@@ -1,65 +1,52 @@
1
1
  #pragma once
2
2
 
3
3
  #include "types.hpp"
4
- #include "well_known_symbols.hpp"
5
-
4
+ #include "utils/well_known_symbols.hpp"
6
5
  #include "values/object.hpp"
7
6
  #include "any_value.hpp"
8
7
 
9
- inline auto Symbol = jspp::AnyValue::make_object({
10
- {"iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator)},
11
- {"toString", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toString)},
12
- });
13
-
14
- // #pragma once
15
-
16
- // #include "types.hpp"
17
- // #include "well_known_symbols.hpp"
18
- // #include "values/object.hpp"
19
- // #include "any_value.hpp"
20
-
21
- // // We define the Symbol constructor function
22
- // inline auto symbolConstructor = [](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
23
- // std::string desc = "";
24
- // if (!args.empty() && !args[0].is_undefined()) {
25
- // desc = args[0].to_std_string();
26
- // }
27
- // return jspp::AnyValue::make_symbol(desc);
28
- // };
29
-
30
- // inline auto Symbol = jspp::AnyValue::make_function(symbolConstructor, "Symbol");
31
-
32
- // // Implementation of Symbol.for(key)
33
- // inline auto forFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
34
- // std::string key = "";
35
- // if (!args.empty()) {
36
- // key = args[0].to_std_string();
37
- // }
38
- // auto symPtr = jspp::JsSymbol::for_global(key);
39
- // return jspp::AnyValue::from_symbol(symPtr);
40
- // }, "for");
41
-
42
- // // Implementation of Symbol.keyFor(sym)
43
- // inline auto keyForFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
44
- // if (args.empty() || !args[0].is_symbol()) {
45
- // throw jspp::RuntimeError::make_error("Symbol.keyFor requires a symbol argument", "TypeError");
46
- // }
47
-
48
- // jspp::JsSymbol* sym = args[0].as_symbol();
49
- // auto result = jspp::JsSymbol::key_for(sym);
50
-
51
- // if (result.has_value()) {
52
- // return jspp::AnyValue::make_string(result.value());
53
- // } else {
54
- // return jspp::AnyValue::make_undefined();
55
- // }
56
- // }, "keyFor");
57
-
58
- // // Attach static properties/methods to the Symbol object
59
- // struct SymbolInit {
60
- // SymbolInit() {
61
- // Symbol.set_own_property("iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator));
62
- // Symbol.set_own_property("for", forFn);
63
- // Symbol.set_own_property("keyFor", keyForFn);
64
- // }
65
- // } symbolInit;
8
+ // Define Symbol as a function
9
+ inline auto Symbol = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
10
+ {
11
+ std::string description = "";
12
+ if (!args.empty() && !args[0].is_undefined()) {
13
+ description = args[0].to_std_string();
14
+ }
15
+ return jspp::AnyValue::make_symbol(description); }, "Symbol", false);
16
+
17
+ // Initialize Symbol properties
18
+ struct SymbolInit
19
+ {
20
+ SymbolInit()
21
+ {
22
+ // Static methods
23
+ Symbol.define_data_property("for", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
24
+ {
25
+ std::string key = "";
26
+ if (!args.empty()) key = args[0].to_std_string();
27
+ return jspp::AnyValue::from_symbol(jspp::JsSymbol::for_global(key)); }, "for"));
28
+
29
+ Symbol.define_data_property("keyFor", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
30
+ {
31
+ if (args.empty() || !args[0].is_symbol()) throw jspp::Exception::make_exception("Symbol.keyFor requires a symbol", "TypeError");
32
+ auto sym = args[0].as_symbol();
33
+ auto key = jspp::JsSymbol::key_for(sym);
34
+ if (key.has_value()) return jspp::AnyValue::make_string(key.value());
35
+ return jspp::AnyValue::make_undefined(); }, "keyFor"));
36
+
37
+ // Well-known symbols
38
+ Symbol.define_data_property("iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator), false, false, false);
39
+ Symbol.define_data_property("asyncIterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::asyncIterator), false, false, false);
40
+ Symbol.define_data_property("hasInstance", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::hasInstance), false, false, false);
41
+ Symbol.define_data_property("isConcatSpreadable", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::isConcatSpreadable), false, false, false);
42
+ Symbol.define_data_property("match", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::match), false, false, false);
43
+ Symbol.define_data_property("matchAll", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::matchAll), false, false, false);
44
+ Symbol.define_data_property("replace", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::replace), false, false, false);
45
+ Symbol.define_data_property("search", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::search), false, false, false);
46
+ Symbol.define_data_property("species", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::species), false, false, false);
47
+ Symbol.define_data_property("split", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::split), false, false, false);
48
+ Symbol.define_data_property("toPrimitive", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toPrimitive), false, false, false);
49
+ Symbol.define_data_property("toStringTag", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toStringTag), false, false, false);
50
+ Symbol.define_data_property("unscopables", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::unscopables), false, false, false);
51
+ }
52
+ } symbolInit;
@@ -0,0 +1,92 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "any_value.hpp"
5
+ #include "scheduler.hpp"
6
+ #include "values/function.hpp"
7
+ #include "exception.hpp"
8
+
9
+ // setTimeout(callback, delay, ...args)
10
+ inline auto setTimeout = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
11
+ if (args.empty() || !args[0].is_function()) {
12
+ throw jspp::Exception::make_exception("Callback must be a function", "TypeError");
13
+ }
14
+
15
+ auto callback = args[0];
16
+ double delay = 0;
17
+ if (args.size() > 1 && args[1].is_number()) {
18
+ delay = args[1].as_double();
19
+ }
20
+
21
+ // Capture arguments
22
+ std::vector<jspp::AnyValue> callArgs;
23
+ for (size_t i = 2; i < args.size(); ++i) {
24
+ callArgs.push_back(args[i]);
25
+ }
26
+
27
+ auto task = [callback, callArgs]() {
28
+ try {
29
+ callback.call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(callArgs));
30
+ } catch (const jspp::Exception& e) {
31
+ std::cerr << "Uncaught exception in setTimeout: " << e.what() << "\n";
32
+ } catch (const std::exception& e) {
33
+ std::cerr << "Uncaught exception in setTimeout: " << e.what() << "\n";
34
+ } catch (...) {
35
+ std::cerr << "Uncaught unknown exception in setTimeout\n";
36
+ }
37
+ };
38
+
39
+ size_t id = jspp::Scheduler::instance().set_timeout(task, static_cast<size_t>(delay));
40
+ return jspp::AnyValue::make_number(static_cast<double>(id));
41
+ }, "setTimeout");
42
+
43
+ // clearTimeout(id)
44
+ inline auto clearTimeout = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
45
+ if (!args.empty() && args[0].is_number()) {
46
+ size_t id = static_cast<size_t>(args[0].as_double());
47
+ jspp::Scheduler::instance().clear_timer(id);
48
+ }
49
+ return jspp::AnyValue::make_undefined();
50
+ }, "clearTimeout");
51
+
52
+ // setInterval(callback, delay, ...args)
53
+ inline auto setInterval = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
54
+ if (args.empty() || !args[0].is_function()) {
55
+ throw jspp::Exception::make_exception("Callback must be a function", "TypeError");
56
+ }
57
+
58
+ auto callback = args[0];
59
+ double delay = 0;
60
+ if (args.size() > 1 && args[1].is_number()) {
61
+ delay = args[1].as_double();
62
+ }
63
+
64
+ std::vector<jspp::AnyValue> callArgs;
65
+ for (size_t i = 2; i < args.size(); ++i) {
66
+ callArgs.push_back(args[i]);
67
+ }
68
+
69
+ auto task = [callback, callArgs]() {
70
+ try {
71
+ callback.call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(callArgs));
72
+ } catch (const jspp::Exception& e) {
73
+ std::cerr << "Uncaught exception in setInterval: " << e.what() << "\n";
74
+ } catch (const std::exception& e) {
75
+ std::cerr << "Uncaught exception in setInterval: " << e.what() << "\n";
76
+ } catch (...) {
77
+ std::cerr << "Uncaught unknown exception in setInterval\n";
78
+ }
79
+ };
80
+
81
+ size_t id = jspp::Scheduler::instance().set_interval(task, static_cast<size_t>(delay));
82
+ return jspp::AnyValue::make_number(static_cast<double>(id));
83
+ }, "setInterval");
84
+
85
+ // clearInterval(id)
86
+ inline auto clearInterval = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
87
+ if (!args.empty() && args[0].is_number()) {
88
+ size_t id = static_cast<size_t>(args[0].as_double());
89
+ jspp::Scheduler::instance().clear_timer(id);
90
+ }
91
+ return jspp::AnyValue::make_undefined();
92
+ }, "clearInterval");
@@ -0,0 +1,145 @@
1
+ #pragma once
2
+ #include <deque>
3
+ #include <functional>
4
+ #include <chrono>
5
+ #include <queue>
6
+ #include <unordered_set>
7
+ #include <vector>
8
+ #include <thread>
9
+ #include <iostream>
10
+
11
+ namespace jspp {
12
+ class Scheduler {
13
+ public:
14
+ using Task = std::function<void()>;
15
+ using TimePoint = std::chrono::steady_clock::time_point;
16
+
17
+ struct Timer {
18
+ size_t id;
19
+ TimePoint next_run;
20
+ std::chrono::milliseconds interval; // 0 if not repeating
21
+ Task task;
22
+
23
+ // Min-heap priority queue (smallest time at top)
24
+ bool operator>(const Timer& other) const {
25
+ return next_run > other.next_run;
26
+ }
27
+ };
28
+
29
+ static Scheduler& instance() {
30
+ static Scheduler s;
31
+ return s;
32
+ }
33
+
34
+ void enqueue(Task task) {
35
+ tasks.push_back(std::move(task));
36
+ }
37
+
38
+ size_t set_timeout(Task task, size_t delay_ms) {
39
+ return schedule_timer(std::move(task), delay_ms, false);
40
+ }
41
+
42
+ size_t set_interval(Task task, size_t delay_ms) {
43
+ return schedule_timer(std::move(task), delay_ms, true);
44
+ }
45
+
46
+ void clear_timer(size_t id) {
47
+ cancelled_timers.insert(id);
48
+ }
49
+
50
+ void run() {
51
+ while (true) {
52
+ bool has_work = false;
53
+
54
+ // 1. Process all immediate tasks (microtask/task queue)
55
+ while (!tasks.empty()) {
56
+ Task task = tasks.front();
57
+ tasks.pop_front();
58
+ task();
59
+ has_work = true;
60
+ }
61
+
62
+ // 2. Process timers
63
+ auto now = std::chrono::steady_clock::now();
64
+ while (!timers.empty()) {
65
+ // Peek top
66
+ const auto& top = timers.top();
67
+
68
+ // Cleanup cancelled timers lazily
69
+ if (cancelled_timers.count(top.id)) {
70
+ cancelled_timers.erase(top.id);
71
+ timers.pop();
72
+ continue;
73
+ }
74
+
75
+ if (top.next_run <= now) {
76
+ // Timer is ready
77
+ Timer t = top;
78
+ timers.pop();
79
+
80
+ // Execute task
81
+ t.task();
82
+ has_work = true;
83
+
84
+ // Reschedule if interval and not cancelled during execution
85
+ if (t.interval.count() > 0 && cancelled_timers.find(t.id) == cancelled_timers.end()) {
86
+ // Drift-safe(ish) scheduling: run next interval relative to now
87
+ // Note: JS allows drift.
88
+ t.next_run = std::chrono::steady_clock::now() + t.interval;
89
+ timers.push(t);
90
+ }
91
+ } else {
92
+ break; // Next timer is in the future
93
+ }
94
+ }
95
+
96
+ // 3. Exit or Wait
97
+ if (!has_work) {
98
+ if (tasks.empty() && timers.empty()) {
99
+ break; // No pending work, exit event loop
100
+ }
101
+
102
+ if (!timers.empty()) {
103
+ auto next_time = timers.top().next_run;
104
+ std::this_thread::sleep_until(next_time);
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ bool has_tasks() const {
111
+ return !tasks.empty() || !timers.empty();
112
+ }
113
+
114
+ private:
115
+ std::deque<Task> tasks;
116
+ std::priority_queue<Timer, std::vector<Timer>, std::greater<Timer>> timers;
117
+ std::unordered_set<size_t> cancelled_timers;
118
+ size_t next_timer_id = 1;
119
+ const size_t MAX_TIMER_ID = 2147483647; // 2^31 - 1
120
+
121
+ size_t schedule_timer(Task task, size_t delay_ms, bool repeat) {
122
+ size_t id = next_timer_id++;
123
+ if (next_timer_id > MAX_TIMER_ID) {
124
+ next_timer_id = 1;
125
+ }
126
+
127
+ // If we wrap around, ensure we don't accidentally treat this new ID as cancelled
128
+ // (in case an old timer with this ID is still lingering in the 'cancelled' set)
129
+ if (cancelled_timers.count(id)) {
130
+ cancelled_timers.erase(id);
131
+ }
132
+
133
+ auto now = std::chrono::steady_clock::now();
134
+
135
+ Timer t;
136
+ t.id = id;
137
+ t.next_run = now + std::chrono::milliseconds(delay_ms);
138
+ t.interval = repeat ? std::chrono::milliseconds(delay_ms) : std::chrono::milliseconds(0);
139
+ t.task = std::move(task);
140
+
141
+ timers.push(t);
142
+ return id;
143
+ }
144
+ };
145
+ }
@@ -14,6 +14,7 @@
14
14
  #include <set>
15
15
  #include <cmath>
16
16
  #include <optional>
17
+ #include <span>
17
18
 
18
19
  // JSPP standard library
19
20
  namespace jspp
@@ -26,6 +27,7 @@ namespace jspp
26
27
  struct JsObject; // can set property
27
28
  struct JsArray; // can set property
28
29
  struct JsFunction; // can set property
30
+ struct JsPromise; // can set property
29
31
  struct JsSymbol; // can set property (but usually doesn't have own props)
30
32
 
31
33
  template <typename T>
@@ -36,19 +38,70 @@ namespace jspp
36
38
  struct AccessorDescriptor;
37
39
 
38
40
  // Custom runtime exception
39
- struct RuntimeError;
41
+ struct Exception;
40
42
 
41
43
  // Dynamic AnyValue
42
44
  class AnyValue;
43
45
 
46
+ // Truthiness checker
47
+ const bool is_truthy(const double &val) noexcept;
48
+ const bool is_truthy(const std::string &val) noexcept;
49
+ const bool is_truthy(const AnyValue &val) noexcept;
50
+
51
+ // Basic equality operators
52
+ inline const bool is_strictly_equal_to_primitive(const AnyValue &lhs, const double &rhs) noexcept;
53
+ inline const bool is_strictly_equal_to_primitive(const double &lhs, const AnyValue &rhs) noexcept;
54
+ inline const bool is_strictly_equal_to_primitive(const double &lhs, const double &rhs) noexcept;
55
+ inline const bool is_strictly_equal_to_primitive(const AnyValue &lhs, const AnyValue &rhs) noexcept;
56
+
57
+ inline const bool is_equal_to_primitive(const AnyValue &lhs, const double &rhs) noexcept;
58
+ inline const bool is_equal_to_primitive(const double &lhs, const AnyValue &rhs) noexcept;
59
+ inline const bool is_equal_to_primitive(const double &lhs, const double &rhs) noexcept;
60
+ inline const bool is_equal_to_primitive(const AnyValue &lhs, const AnyValue &rhs) noexcept;
61
+
62
+ inline const AnyValue is_strictly_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
63
+ inline const AnyValue is_strictly_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
64
+ inline const AnyValue is_strictly_equal_to(const double &lhs, const double &rhs) noexcept;
65
+ inline const AnyValue is_strictly_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
66
+
67
+ inline const AnyValue is_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
68
+ inline const AnyValue is_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
69
+ inline const AnyValue is_equal_to(const double &lhs, const double &rhs) noexcept;
70
+ inline const AnyValue is_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
71
+
72
+ inline const AnyValue not_strictly_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
73
+ inline const AnyValue not_strictly_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
74
+ inline const AnyValue not_strictly_equal_to(const double &lhs, const double &rhs) noexcept;
75
+ inline const AnyValue not_strictly_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
76
+
77
+ inline const AnyValue not_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
78
+ inline const AnyValue not_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
79
+ inline const AnyValue not_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
80
+
81
+ // Bitwise operators
82
+ inline AnyValue unsigned_right_shift(const AnyValue &lhs, const AnyValue &rhs);
83
+ inline AnyValue unsigned_right_shift(const AnyValue &lhs, const double &rhs);
84
+ inline AnyValue unsigned_right_shift(const double &lhs, const AnyValue &rhs);
85
+
44
86
  // Arithemetic operators
87
+
88
+ inline AnyValue pow(const double &lhs, const double &rhs);
89
+ inline AnyValue pow(const AnyValue &lhs, const double &rhs);
45
90
  inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
46
91
 
47
92
  // AnyValue prototypes
93
+ namespace ObjectPrototypes
94
+ {
95
+ inline std::optional<AnyValue> get(const std::string &key, JsObject *self);
96
+ }
48
97
  namespace StringPrototypes
49
98
  {
50
99
  inline std::optional<AnyValue> get(const std::string &key, JsString *self);
51
100
  }
101
+ namespace NumberPrototypes
102
+ {
103
+ inline std::optional<AnyValue> get(const std::string &key, double self);
104
+ }
52
105
  namespace ArrayPrototypes
53
106
  {
54
107
  inline std::optional<AnyValue> get(const std::string &key, JsArray *self);
@@ -57,6 +110,10 @@ namespace jspp
57
110
  {
58
111
  inline std::optional<AnyValue> get(const std::string &key, JsFunction *self);
59
112
  }
113
+ namespace PromisePrototypes
114
+ {
115
+ inline std::optional<AnyValue> get(const std::string &key, JsPromise *self);
116
+ }
60
117
  namespace IteratorPrototypes
61
118
  {
62
119
  inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self);