@lucent-lang/lucent 0.0.4

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 (125) hide show
  1. package/app.plugin.js +92 -0
  2. package/bin/lucent.cjs +16 -0
  3. package/core/index.cjs +11 -0
  4. package/dist/bench-CR3E7e2e.js +304 -0
  5. package/dist/build-CAIrRSYA.js +7 -0
  6. package/dist/build-check-QozuNJm5.js +92 -0
  7. package/dist/check-1SCmYlqA.js +7 -0
  8. package/dist/clean-BVwpbJ5K.js +35 -0
  9. package/dist/cli.js +406 -0
  10. package/dist/codes-CIuwXt1S.js +260 -0
  11. package/dist/compiler.js +4 -0
  12. package/dist/confirm-BYF5eY3q.js +34 -0
  13. package/dist/dashboard-B-NzjjaN.js +91 -0
  14. package/dist/dev-BYWMP7oC.js +221 -0
  15. package/dist/diagnostic-BM9sXmwl.js +30 -0
  16. package/dist/diff-BTDo3Sro.js +44 -0
  17. package/dist/doctor-C0PrfWT6.js +230 -0
  18. package/dist/doctor-DdR4JnVc.js +30 -0
  19. package/dist/explain--vV1oaRl.js +35 -0
  20. package/dist/format-D9fnIMEx.js +53 -0
  21. package/dist/init-gIabu1S_.js +207 -0
  22. package/dist/live-steps-BGcWFM9M.js +79 -0
  23. package/dist/new-module-C2mtb0op.js +66 -0
  24. package/dist/package-manager-BnxwCySi.js +30 -0
  25. package/dist/package.json +3 -0
  26. package/dist/packages-HyskKIsY.js +150 -0
  27. package/dist/pipeline-CH7-2cxu.js +232 -0
  28. package/dist/project-D8tEet0k.js +150 -0
  29. package/dist/sdk-coverage-BoW_3Efd.js +64 -0
  30. package/dist/sdk-prefetch-mo04JDZV.js +89 -0
  31. package/dist/sdk-search-Iv4K3-8a.js +124 -0
  32. package/dist/sdk-show-CkrdlqlP.js +74 -0
  33. package/dist/sdks-Xoyiy2E4.js +28 -0
  34. package/dist/src-BD7f1_aZ.js +11493 -0
  35. package/dist/steps--_h-FFS7.js +24 -0
  36. package/dist/theme-uv5bc0aK.js +134 -0
  37. package/dist/tsconfig-S4uyLNzf.js +59 -0
  38. package/dist/version-bqpIO5sP.js +26 -0
  39. package/gradle/lucent-classpath.gradle +38 -0
  40. package/gradle/lucent-classpath.init.gradle +6 -0
  41. package/gradle/lucent.gradle +24 -0
  42. package/lib/core.cjs +106 -0
  43. package/lib/globals.d.ts +36 -0
  44. package/lib/sdk/android.d.ts +8 -0
  45. package/lib/sdk/core.d.ts +23 -0
  46. package/lib/sdk/ios.d.ts +33 -0
  47. package/lib/sdk/platform.d.ts +8 -0
  48. package/lib/sdk/thread.d.ts +8 -0
  49. package/metro/index.cjs +72 -0
  50. package/metro/index.d.ts +6 -0
  51. package/metro/transformer.cjs +70 -0
  52. package/package.json +61 -0
  53. package/runtime/cpp/lucent/abort.cpp +61 -0
  54. package/runtime/cpp/lucent/abort.h +54 -0
  55. package/runtime/cpp/lucent/array.h +442 -0
  56. package/runtime/cpp/lucent/async.h +236 -0
  57. package/runtime/cpp/lucent/builtins.cpp +163 -0
  58. package/runtime/cpp/lucent/bytes.h +129 -0
  59. package/runtime/cpp/lucent/console.h +17 -0
  60. package/runtime/cpp/lucent/core.h +129 -0
  61. package/runtime/cpp/lucent/date.cpp +441 -0
  62. package/runtime/cpp/lucent/date.h +82 -0
  63. package/runtime/cpp/lucent/equality.h +87 -0
  64. package/runtime/cpp/lucent/function.h +61 -0
  65. package/runtime/cpp/lucent/generator.h +244 -0
  66. package/runtime/cpp/lucent/helpers.h +226 -0
  67. package/runtime/cpp/lucent/jserror.cpp +51 -0
  68. package/runtime/cpp/lucent/jserror.h +66 -0
  69. package/runtime/cpp/lucent/jsi/convert.cpp +202 -0
  70. package/runtime/cpp/lucent/jsi/convert.h +612 -0
  71. package/runtime/cpp/lucent/jsi/host.cpp +322 -0
  72. package/runtime/cpp/lucent/jsi/host.h +166 -0
  73. package/runtime/cpp/lucent/json.cpp +243 -0
  74. package/runtime/cpp/lucent/json.h +164 -0
  75. package/runtime/cpp/lucent/json_parse.h +138 -0
  76. package/runtime/cpp/lucent/jsstring.cpp +874 -0
  77. package/runtime/cpp/lucent/jsstring.h +152 -0
  78. package/runtime/cpp/lucent/lucent.h +24 -0
  79. package/runtime/cpp/lucent/map.h +344 -0
  80. package/runtime/cpp/lucent/native.cpp +76 -0
  81. package/runtime/cpp/lucent/native.h +116 -0
  82. package/runtime/cpp/lucent/number.cpp +545 -0
  83. package/runtime/cpp/lucent/number.h +190 -0
  84. package/runtime/cpp/lucent/ops.h +165 -0
  85. package/runtime/cpp/lucent/platform/android.cpp +437 -0
  86. package/runtime/cpp/lucent/platform/android.h +117 -0
  87. package/runtime/cpp/lucent/platform/ios.h +297 -0
  88. package/runtime/cpp/lucent/platform/ios.mm +31 -0
  89. package/runtime/cpp/lucent/regexp.cpp +440 -0
  90. package/runtime/cpp/lucent/regexp.h +107 -0
  91. package/runtime/cpp/lucent/scheduler.cpp +153 -0
  92. package/runtime/cpp/lucent/scheduler.h +160 -0
  93. package/runtime/cpp/lucent/unicode_data.inc +4051 -0
  94. package/runtime/cpp/rn/LucentModule.cpp +35 -0
  95. package/runtime/cpp/rn/LucentModule.h +37 -0
  96. package/runtime/cpp/third_party/quickjs/LICENSE +22 -0
  97. package/runtime/cpp/third_party/quickjs/README.md +11 -0
  98. package/runtime/cpp/third_party/quickjs/cutils.c +641 -0
  99. package/runtime/cpp/third_party/quickjs/cutils.h +457 -0
  100. package/runtime/cpp/third_party/quickjs/libregexp-opcode.h +73 -0
  101. package/runtime/cpp/third_party/quickjs/libregexp.c +3448 -0
  102. package/runtime/cpp/third_party/quickjs/libregexp.h +65 -0
  103. package/runtime/cpp/third_party/quickjs/libunicode-table.h +5206 -0
  104. package/runtime/cpp/third_party/quickjs/libunicode.c +2124 -0
  105. package/runtime/cpp/third_party/quickjs/libunicode.h +196 -0
  106. package/runtime/js/index.d.ts +7 -0
  107. package/runtime/js/index.js +60 -0
  108. package/runtime/native/LucentNative.podspec +28 -0
  109. package/runtime/native/android/CMakeLists.txt +51 -0
  110. package/runtime/native/android/build.gradle +22 -0
  111. package/runtime/native/android/include/lucentnative.h +16 -0
  112. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +25 -0
  113. package/runtime/native/android/src/main/java/dev/lucent/NativeProxy.java +132 -0
  114. package/runtime/native/ios/LucentRegistration.mm +23 -0
  115. package/runtime/native/package.json +6 -0
  116. package/runtime/native/react-native.config.js +19 -0
  117. package/runtime/test/jsi/harness.cpp +181 -0
  118. package/schemas/build.schema.json +152 -0
  119. package/schemas/check.schema.json +74 -0
  120. package/schemas/doctor.schema.json +40 -0
  121. package/schemas/lucent.schema.json +43 -0
  122. package/schemas/sdk-prefetch.schema.json +37 -0
  123. package/schemas/sdk-search.schema.json +54 -0
  124. package/ts-plugin/index.d.ts +4 -0
  125. package/ts-plugin/index.js +99 -0
@@ -0,0 +1,116 @@
1
+ // Lucent runtime — platform objects and the main thread.
2
+ //
3
+ // A NativeRef holds an Objective-C object (retained) or a JNI global
4
+ // reference. It is opaque here, so generated headers stay plain C++; the
5
+ // glue in *.ios.lucent.ts (.mm) and *.android.lucent.ts units wraps and
6
+ // unwraps it (lucent/platform/ios.h, lucent/platform/android.h).
7
+ #pragma once
8
+
9
+ #include <functional>
10
+ #include <memory>
11
+ #include <type_traits>
12
+
13
+ #include "async.h"
14
+ #include "jserror.h"
15
+ #include "scheduler.h"
16
+
17
+ namespace lucent {
18
+
19
+ /// How many platform objects Lucent holds (NativeRefs, not copies): debug
20
+ /// builds report what is left when the module goes.
21
+ long liveNativeRefs();
22
+
23
+ namespace detail {
24
+ void countNativeRef(int delta);
25
+ }
26
+
27
+ class NativeRef {
28
+ public:
29
+ NativeRef() = default;
30
+ /// Takes ownership of `handle`; `release` runs when the last copy goes.
31
+ NativeRef(void* handle, void (*release)(void*), bool (*same)(void*, void*))
32
+ : p_(handle, Release{release}), same_(same) {
33
+ if (handle) detail::countNativeRef(1);
34
+ }
35
+
36
+ void* get() const { return p_.get(); }
37
+ explicit operator bool() const { return p_ != nullptr; }
38
+ const void* identity() const { return p_.get(); }
39
+
40
+ /// The same platform object: pointer identity on iOS, IsSameObject on Android.
41
+ bool same(const NativeRef& o) const {
42
+ if (p_ == o.p_) return true;
43
+ if (!p_ || !o.p_) return false;
44
+ return same_ ? same_(get(), o.get()) : get() == o.get();
45
+ }
46
+
47
+ private:
48
+ struct Release {
49
+ void (*release)(void*);
50
+ void operator()(void* p) const {
51
+ if (!p) return;
52
+ detail::countNativeRef(-1);
53
+ release(p);
54
+ }
55
+ };
56
+ std::shared_ptr<void> p_;
57
+ bool (*same_)(void*, void*) = nullptr;
58
+ };
59
+
60
+ // Namespace-scope, not hidden friends: generated code calls them qualified.
61
+ inline bool strictEquals(const NativeRef& a, const NativeRef& b) { return a.same(b); }
62
+ inline String toJsString(const NativeRef&) { return String::fromLatin1("[object NativeObject]"); }
63
+
64
+ /// Runs `job` on the platform's main thread (main queue, main Looper; a
65
+ /// dedicated thread standing in for it on other hosts).
66
+ void postToMain(std::function<void()> job);
67
+ bool onMainThread();
68
+
69
+ /// A platform callback into Lucent code that returns nothing and outlives
70
+ /// the call: queued on the Lucent thread, so the platform never waits for
71
+ /// Lucent code. `f` owns what it captured and is released after it runs.
72
+ template <class F>
73
+ void postCallback(F f) {
74
+ Scheduler::instance().post(std::move(f));
75
+ }
76
+
77
+ /// A platform callback the platform waits for (for its result, or because
78
+ /// it runs during the call or on the main thread): runs on the calling
79
+ /// thread, holding the Lucent lock. A Lucent error is reported and the
80
+ /// platform gets a default result.
81
+ template <class F>
82
+ auto callNow(F f) -> std::invoke_result_t<F&> {
83
+ using R = std::invoke_result_t<F&>;
84
+ LucentScope scope;
85
+ try {
86
+ return f();
87
+ } catch (...) {
88
+ reportUncaught(std::current_exception(), "callback");
89
+ if constexpr (!std::is_void_v<R>) return R{};
90
+ }
91
+ }
92
+
93
+ /// `main(f)` from lucent:thread: runs `f` on the main thread holding the
94
+ /// Lucent lock, and settles the promise with its result. The caller never
95
+ /// waits for the main thread, so the lock order stays deadlock-free.
96
+ template <class F>
97
+ auto runOnMain(F f) -> Promise<std::invoke_result_t<F>> {
98
+ using R = std::invoke_result_t<F>;
99
+ Promise<R> p;
100
+ postToMain([p, f = std::move(f)]() mutable {
101
+ LucentScope scope;
102
+ try {
103
+ if constexpr (std::is_void_v<R>) {
104
+ f();
105
+ p.resolve(undefined);
106
+ } else {
107
+ p.resolve(f());
108
+ }
109
+ } catch (...) {
110
+ p.reject(currentError(std::current_exception()));
111
+ }
112
+ });
113
+ return p;
114
+ }
115
+
116
+ } // namespace lucent
@@ -0,0 +1,545 @@
1
+ #include "number.h"
2
+
3
+ #include <algorithm>
4
+ #include <cstdio>
5
+ #include <vector>
6
+ #include <cstdlib>
7
+ #include <cstring>
8
+ #include <random>
9
+ #include <string>
10
+
11
+ namespace lucent {
12
+
13
+ namespace {
14
+
15
+ // Decimal digits and exponent of a finite, positive double: v = 0.d1d2...dk * 10^n.
16
+ struct Decimal {
17
+ std::string digits;
18
+ int n = 0;
19
+ };
20
+
21
+ // Parses the output of printf("%.*e"), tolerating any locale decimal mark.
22
+ Decimal parseScientific(const char* buf) {
23
+ Decimal d;
24
+ const char* p = buf;
25
+ while (*p && *p != 'e' && *p != 'E') {
26
+ if (*p >= '0' && *p <= '9') d.digits.push_back(*p);
27
+ p++;
28
+ }
29
+ int exp = (*p) ? std::atoi(p + 1) : 0;
30
+ while (d.digits.size() > 1 && d.digits.back() == '0') d.digits.pop_back();
31
+ d.n = exp + 1;
32
+ return d;
33
+ }
34
+
35
+ /// `a` (positive, finite) rounded to `significant` digits, with exact ties
36
+ /// rounded up, as Number.prototype.toPrecision/toExponential specify. printf
37
+ /// with 800 digits prints the exact binary value (at most 767 significant
38
+ /// digits), so the rounding decision sees every digit.
39
+ Decimal roundHalfUp(double a, int significant) {
40
+ static thread_local char buf[1024];
41
+ std::snprintf(buf, sizeof buf, "%.800e", a);
42
+ Decimal exact = parseScientific(buf);
43
+ Decimal d{exact.digits, exact.n};
44
+ d.digits.resize(std::max<size_t>(d.digits.size(), static_cast<size_t>(significant) + 1), '0');
45
+ bool up = d.digits[static_cast<size_t>(significant)] >= '5';
46
+ d.digits.resize(static_cast<size_t>(significant));
47
+ if (up) {
48
+ int i = significant - 1;
49
+ while (i >= 0 && d.digits[static_cast<size_t>(i)] == '9') d.digits[static_cast<size_t>(i--)] = '0';
50
+ if (i >= 0) {
51
+ d.digits[static_cast<size_t>(i)]++;
52
+ } else {
53
+ d.digits.insert(d.digits.begin(), '1');
54
+ d.digits.pop_back();
55
+ d.n++;
56
+ }
57
+ }
58
+ return d;
59
+ }
60
+
61
+ Decimal withPrecision(double v, int precision) {
62
+ char buf[64];
63
+ std::snprintf(buf, sizeof buf, "%.*e", precision - 1, v);
64
+ return parseScientific(buf);
65
+ }
66
+
67
+ bool roundTrips(double v, int precision) {
68
+ char buf[64];
69
+ std::snprintf(buf, sizeof buf, "%.*e", precision - 1, v);
70
+ // strtod needs '.', which is what the C locale prints.
71
+ for (char* c = buf; *c; c++) {
72
+ if (*c == ',') *c = '.';
73
+ }
74
+ return std::strtod(buf, nullptr) == v;
75
+ }
76
+
77
+ // Shortest digit string that round-trips (ECMA-262 Number::toString step 5).
78
+ Decimal shortest(double v) {
79
+ int lo = 1, hi = 17;
80
+ while (lo < hi) {
81
+ int mid = (lo + hi) / 2;
82
+ if (roundTrips(v, mid)) hi = mid;
83
+ else lo = mid + 1;
84
+ }
85
+ return withPrecision(v, lo);
86
+ }
87
+
88
+ std::string formatDecimal(const Decimal& d) {
89
+ const std::string& s = d.digits;
90
+ int k = static_cast<int>(s.size());
91
+ int n = d.n;
92
+ std::string out;
93
+ if (k <= n && n <= 21) {
94
+ out = s;
95
+ out.append(static_cast<size_t>(n - k), '0');
96
+ } else if (0 < n && n <= 21) {
97
+ out = s.substr(0, static_cast<size_t>(n)) + "." + s.substr(static_cast<size_t>(n));
98
+ } else if (-6 < n && n <= 0) {
99
+ out = "0.";
100
+ out.append(static_cast<size_t>(-n), '0');
101
+ out += s;
102
+ } else {
103
+ int e = n - 1;
104
+ out = s.substr(0, 1);
105
+ if (k > 1) out += "." + s.substr(1);
106
+ out += e >= 0 ? "e+" : "e-";
107
+ out += std::to_string(e >= 0 ? e : -e);
108
+ }
109
+ return out;
110
+ }
111
+
112
+ bool isDigitIn(char16_t c, int radix, int& value) {
113
+ int v;
114
+ if (c >= '0' && c <= '9') v = c - '0';
115
+ else if (c >= 'a' && c <= 'z') v = c - 'a' + 10;
116
+ else if (c >= 'A' && c <= 'Z') v = c - 'A' + 10;
117
+ else return false;
118
+ if (v >= radix) return false;
119
+ value = v;
120
+ return true;
121
+ }
122
+
123
+ // Exact decimal expansion of |v| with `fraction` digits after the point,
124
+ // rounded half away from zero on the exact binary value (Number::toFixed).
125
+ std::string fixedDigits(double v, int fraction) {
126
+ // printf prints the exact binary value when given enough precision; 1100
127
+ // digits covers every double's fractional expansion.
128
+ static thread_local std::vector<char> buf(1600);
129
+ int len = std::snprintf(buf.data(), buf.size(), "%.1100f", std::fabs(v));
130
+ std::string s(buf.data(), static_cast<size_t>(len));
131
+ for (auto& c : s) {
132
+ if (c == ',') c = '.';
133
+ }
134
+ size_t dot = s.find('.');
135
+ std::string intPart = s.substr(0, dot);
136
+ std::string frac = s.substr(dot + 1);
137
+ std::string kept = intPart + frac.substr(0, static_cast<size_t>(fraction));
138
+ bool roundUp = frac.size() > static_cast<size_t>(fraction) && frac[static_cast<size_t>(fraction)] >= '5';
139
+ if (roundUp) {
140
+ int i = static_cast<int>(kept.size()) - 1;
141
+ while (i >= 0) {
142
+ if (kept[static_cast<size_t>(i)] == '9') {
143
+ kept[static_cast<size_t>(i)] = '0';
144
+ i--;
145
+ } else {
146
+ kept[static_cast<size_t>(i)]++;
147
+ break;
148
+ }
149
+ }
150
+ if (i < 0) kept.insert(kept.begin(), '1');
151
+ }
152
+ size_t intLen = kept.size() - static_cast<size_t>(fraction);
153
+ std::string out = kept.substr(0, intLen);
154
+ if (out.empty()) out = "0";
155
+ if (fraction > 0) out += "." + kept.substr(intLen);
156
+ return out;
157
+ }
158
+
159
+ } // namespace
160
+
161
+ namespace detail {
162
+ void formatNumber(double v, ConcatPiece& out) {
163
+ if (std::fabs(v) < 9007199254740992.0 && std::trunc(v) == v) {
164
+ // Integers, including -0 (printed "0").
165
+ auto r = std::to_chars(out.digits, out.digits + sizeof out.digits, static_cast<int64_t>(v));
166
+ out.n = static_cast<uint8_t>(r.ptr - out.digits);
167
+ return;
168
+ }
169
+ out.formatted = numberToString(v);
170
+ }
171
+ } // namespace detail
172
+
173
+ String numberToString(double v) {
174
+ if (std::isnan(v)) return String::fromLatin1("NaN");
175
+ if (v == 0) return String::fromLatin1("0");
176
+ if (std::isinf(v)) return String::fromLatin1(v > 0 ? "Infinity" : "-Infinity");
177
+ if (std::fabs(v) < 9007199254740992.0 && std::trunc(v) == v) {
178
+ return String::fromLatin1(std::to_string(static_cast<int64_t>(v)));
179
+ }
180
+ std::string out = v < 0 ? "-" : "";
181
+ out += formatDecimal(shortest(std::fabs(v)));
182
+ return String::fromLatin1(out);
183
+ }
184
+
185
+ String numberToString(double v, double radixValue) {
186
+ double r = std::trunc(radixValue);
187
+ if (std::isnan(radixValue)) r = 10;
188
+ if (r < 2 || r > 36) throwRangeError("toString() radix must be between 2 and 36");
189
+ int radix = static_cast<int>(r);
190
+ if (radix == 10 || std::isnan(v) || std::isinf(v) || v == 0) return numberToString(v);
191
+ static const char* chars = "0123456789abcdefghijklmnopqrstuvwxyz";
192
+ bool negative = v < 0;
193
+ double value = std::fabs(v);
194
+ double integer = std::floor(value);
195
+ double fraction = value - integer;
196
+ // Fractional digits: emit while they still distinguish the value, the way
197
+ // V8's DoubleToRadixCString does.
198
+ double delta = 0.5 * (std::nextafter(value, kInfinity) - value);
199
+ delta = std::max(std::nextafter(0.0, 1.0), delta);
200
+ std::string fracDigits;
201
+ if (fraction >= delta) {
202
+ do {
203
+ fraction *= radix;
204
+ delta *= radix;
205
+ int digit = static_cast<int>(fraction);
206
+ fracDigits.push_back(chars[digit]);
207
+ fraction -= digit;
208
+ if (fraction > 0.5 || (fraction == 0.5 && (digit & 1))) {
209
+ if (fraction + delta > 1) {
210
+ // Round up and propagate.
211
+ for (;;) {
212
+ if (fracDigits.empty()) {
213
+ integer += 1;
214
+ break;
215
+ }
216
+ char& last = fracDigits.back();
217
+ int d = static_cast<int>(std::strchr(chars, last) - chars) + 1;
218
+ if (d < radix) {
219
+ last = chars[d];
220
+ break;
221
+ }
222
+ fracDigits.pop_back();
223
+ }
224
+ break;
225
+ }
226
+ }
227
+ } while (fraction >= delta);
228
+ }
229
+ std::string intDigits;
230
+ if (integer == 0) {
231
+ intDigits = "0";
232
+ } else {
233
+ while (integer >= 1) {
234
+ double q = std::floor(integer / radix);
235
+ int digit = static_cast<int>(integer - q * radix);
236
+ intDigits.push_back(chars[digit]);
237
+ integer = q;
238
+ }
239
+ std::reverse(intDigits.begin(), intDigits.end());
240
+ }
241
+ std::string out = negative ? "-" : "";
242
+ out += intDigits;
243
+ if (!fracDigits.empty()) out += "." + fracDigits;
244
+ return String::fromLatin1(out);
245
+ }
246
+
247
+ String numberToFixed(double v, double digitsValue) {
248
+ double f = std::isnan(digitsValue) ? 0 : std::trunc(digitsValue);
249
+ if (f < 0 || f > 100) throwRangeError("toFixed() digits argument must be between 0 and 100");
250
+ if (std::isnan(v)) return String::fromLatin1("NaN");
251
+ if (std::fabs(v) >= 1e21 || std::isinf(v)) return numberToString(v);
252
+ std::string digits = fixedDigits(v, static_cast<int>(f));
253
+ bool allZero = digits.find_first_not_of("0.") == std::string::npos;
254
+ std::string out = (v < 0 && !allZero) ? "-" : "";
255
+ return String::fromLatin1(out + digits);
256
+ }
257
+
258
+ String numberToExponential(double v, double digitsValue) {
259
+ if (std::isnan(v)) return String::fromLatin1("NaN");
260
+ if (std::isinf(v)) return numberToString(v);
261
+ double f = std::isnan(digitsValue) ? 0 : std::trunc(digitsValue);
262
+ if (f < 0 || f > 100) throwRangeError("toExponential() argument must be between 0 and 100");
263
+ std::string out = v < 0 ? "-" : "";
264
+ double a = std::fabs(v);
265
+ Decimal d;
266
+ if (a == 0) {
267
+ d.digits = std::string(static_cast<size_t>(f) + 1, '0');
268
+ d.n = 1;
269
+ } else {
270
+ d = roundHalfUp(a, static_cast<int>(f) + 1);
271
+ }
272
+ out += d.digits.substr(0, 1);
273
+ if (f > 0) out += "." + d.digits.substr(1);
274
+ int e = a == 0 ? 0 : d.n - 1;
275
+ out += e >= 0 ? "e+" : "e-";
276
+ out += std::to_string(e >= 0 ? e : -e);
277
+ return String::fromLatin1(out);
278
+ }
279
+
280
+ String numberToExponential(double v) {
281
+ if (std::isnan(v)) return String::fromLatin1("NaN");
282
+ if (std::isinf(v)) return numberToString(v);
283
+ std::string out = v < 0 ? "-" : "";
284
+ double a = std::fabs(v);
285
+ Decimal d = a == 0 ? Decimal{"0", 1} : shortest(a);
286
+ out += d.digits.substr(0, 1);
287
+ if (d.digits.size() > 1) out += "." + d.digits.substr(1);
288
+ int e = a == 0 ? 0 : d.n - 1;
289
+ out += e >= 0 ? "e+" : "e-";
290
+ out += std::to_string(e >= 0 ? e : -e);
291
+ return String::fromLatin1(out);
292
+ }
293
+
294
+ String numberToPrecision(double v, double precisionValue) {
295
+ if (std::isnan(v)) return String::fromLatin1("NaN");
296
+ if (std::isinf(v)) return numberToString(v);
297
+ double pv = std::isnan(precisionValue) ? 0 : std::trunc(precisionValue);
298
+ if (pv < 1 || pv > 100) throwRangeError("toPrecision() argument must be between 1 and 100");
299
+ int p = static_cast<int>(pv);
300
+ std::string out = v < 0 ? "-" : "";
301
+ double a = std::fabs(v);
302
+ Decimal d;
303
+ int e;
304
+ if (a == 0) {
305
+ d.digits = std::string(static_cast<size_t>(p), '0');
306
+ e = 0;
307
+ } else {
308
+ d = roundHalfUp(a, p);
309
+ e = d.n - 1;
310
+ }
311
+ if (e < -6 || e >= p) {
312
+ out += d.digits.substr(0, 1);
313
+ if (p > 1) out += "." + d.digits.substr(1);
314
+ out += e >= 0 ? "e+" : "e-";
315
+ out += std::to_string(e >= 0 ? e : -e);
316
+ } else if (e == p - 1) {
317
+ out += d.digits;
318
+ } else if (e >= 0) {
319
+ out += d.digits.substr(0, static_cast<size_t>(e) + 1) + "." + d.digits.substr(static_cast<size_t>(e) + 1);
320
+ } else {
321
+ out += "0.";
322
+ out.append(static_cast<size_t>(-(e + 1)), '0');
323
+ out += d.digits;
324
+ }
325
+ return String::fromLatin1(out);
326
+ }
327
+
328
+ double stringToNumber(const String& input) {
329
+ String s = input.trim();
330
+ size_t n = s.length();
331
+ if (n == 0) return 0;
332
+ std::string a;
333
+ a.reserve(n);
334
+ for (size_t i = 0; i < n; i++) {
335
+ char16_t c = s.unit(i);
336
+ if (c >= 0x80) return kNaN;
337
+ a.push_back(static_cast<char>(c));
338
+ }
339
+ if (a == "Infinity" || a == "+Infinity") return kInfinity;
340
+ if (a == "-Infinity") return -kInfinity;
341
+ if (n > 2 && a[0] == '0' && (a[1] == 'x' || a[1] == 'X' || a[1] == 'o' || a[1] == 'O' || a[1] == 'b' || a[1] == 'B')) {
342
+ int radix = (a[1] == 'x' || a[1] == 'X') ? 16 : (a[1] == 'o' || a[1] == 'O') ? 8 : 2;
343
+ double r = 0;
344
+ for (size_t i = 2; i < n; i++) {
345
+ int d;
346
+ if (!isDigitIn(static_cast<char16_t>(a[i]), radix, d)) return kNaN;
347
+ r = r * radix + d;
348
+ }
349
+ return r;
350
+ }
351
+ // StrDecimalLiteral: [+-] (digits [. digits] | . digits) [(e|E) [+-] digits]
352
+ size_t i = 0;
353
+ if (a[i] == '+' || a[i] == '-') i++;
354
+ size_t intDigits = 0, fracDigits = 0;
355
+ while (i < n && a[i] >= '0' && a[i] <= '9') {
356
+ i++;
357
+ intDigits++;
358
+ }
359
+ if (i < n && a[i] == '.') {
360
+ i++;
361
+ while (i < n && a[i] >= '0' && a[i] <= '9') {
362
+ i++;
363
+ fracDigits++;
364
+ }
365
+ }
366
+ if (intDigits + fracDigits == 0) return kNaN;
367
+ if (i < n && (a[i] == 'e' || a[i] == 'E')) {
368
+ i++;
369
+ if (i < n && (a[i] == '+' || a[i] == '-')) i++;
370
+ size_t expDigits = 0;
371
+ while (i < n && a[i] >= '0' && a[i] <= '9') {
372
+ i++;
373
+ expDigits++;
374
+ }
375
+ if (expDigits == 0) return kNaN;
376
+ }
377
+ if (i != n) return kNaN;
378
+ return std::strtod(a.c_str(), nullptr);
379
+ }
380
+
381
+ double parseFloat(const String& input) {
382
+ String s = input.trimStart();
383
+ size_t n = s.length();
384
+ std::string a;
385
+ for (size_t i = 0; i < n && s.unit(i) < 0x80; i++) a.push_back(static_cast<char>(s.unit(i)));
386
+ size_t i = 0;
387
+ bool negative = false;
388
+ if (i < a.size() && (a[i] == '+' || a[i] == '-')) {
389
+ negative = a[i] == '-';
390
+ i++;
391
+ }
392
+ if (a.compare(i, 8, "Infinity") == 0) return negative ? -kInfinity : kInfinity;
393
+ size_t start = 0, digits = 0;
394
+ while (i < a.size() && a[i] >= '0' && a[i] <= '9') {
395
+ i++;
396
+ digits++;
397
+ }
398
+ if (i < a.size() && a[i] == '.') {
399
+ i++;
400
+ while (i < a.size() && a[i] >= '0' && a[i] <= '9') {
401
+ i++;
402
+ digits++;
403
+ }
404
+ }
405
+ if (digits == 0) return kNaN;
406
+ size_t end = i;
407
+ if (i < a.size() && (a[i] == 'e' || a[i] == 'E')) {
408
+ size_t j = i + 1;
409
+ if (j < a.size() && (a[j] == '+' || a[j] == '-')) j++;
410
+ size_t e = 0;
411
+ while (j < a.size() && a[j] >= '0' && a[j] <= '9') {
412
+ j++;
413
+ e++;
414
+ }
415
+ if (e > 0) end = j;
416
+ }
417
+ return std::strtod(a.substr(start, end).c_str(), nullptr);
418
+ }
419
+
420
+ double parseInt(const String& s) { return parseInt(s, 0); }
421
+
422
+ double parseInt(const String& input, double radixValue) {
423
+ String s = input.trimStart();
424
+ size_t n = s.length(), i = 0;
425
+ bool negative = false;
426
+ if (i < n && (s.unit(i) == '+' || s.unit(i) == '-')) {
427
+ negative = s.unit(i) == '-';
428
+ i++;
429
+ }
430
+ int radix = std::isnan(radixValue) ? 0 : static_cast<int>(toInt32(radixValue));
431
+ bool stripPrefix = true;
432
+ if (radix != 0) {
433
+ if (radix < 2 || radix > 36) return kNaN;
434
+ if (radix != 16) stripPrefix = false;
435
+ } else {
436
+ radix = 10;
437
+ }
438
+ if (stripPrefix && i + 1 < n && s.unit(i) == '0' && (s.unit(i + 1) == 'x' || s.unit(i + 1) == 'X')) {
439
+ i += 2;
440
+ radix = 16;
441
+ }
442
+ double r = 0;
443
+ size_t start = i;
444
+ std::string decimal;
445
+ while (i < n) {
446
+ int d;
447
+ if (!isDigitIn(s.unit(i), radix, d)) break;
448
+ if (radix == 10) decimal.push_back(static_cast<char>(s.unit(i)));
449
+ r = r * radix + d;
450
+ i++;
451
+ }
452
+ if (i == start) return kNaN;
453
+ // Base 10 goes through strtod so large values round like JavaScript engines.
454
+ if (radix == 10) r = std::strtod(decimal.c_str(), nullptr);
455
+ return negative ? -r : r;
456
+ }
457
+
458
+ double jsPow(double a, double b) {
459
+ if (std::isnan(b)) return kNaN;
460
+ if (b == 0) return 1;
461
+ if ((a == 1 || a == -1) && std::isinf(b)) return kNaN;
462
+ return std::pow(a, b);
463
+ }
464
+
465
+ namespace math {
466
+
467
+ double round(double v) {
468
+ if (!std::isfinite(v) || v == 0) return v;
469
+ if (v > 0 && v < 0.5) return 0.0;
470
+ if (v < 0 && v >= -0.5) return -0.0;
471
+ double f = std::floor(v);
472
+ return (v - f >= 0.5) ? f + 1 : f;
473
+ }
474
+
475
+ double cbrt(double x) {
476
+ // Port of fdlibm s_cbrt.c (as used by V8).
477
+ static const uint32_t B1 = 715094163, B2 = 696219795;
478
+ static const double P0 = 1.87595182427177009643, P1 = -1.88497979543377169875, P2 = 1.621429720105354466140,
479
+ P3 = -0.758397934778766047437, P4 = 0.145996192886612446982;
480
+ uint64_t bits;
481
+ std::memcpy(&bits, &x, sizeof bits);
482
+ uint32_t hx = static_cast<uint32_t>(bits >> 32) & 0x7fffffff;
483
+ uint32_t sign = static_cast<uint32_t>(bits >> 32) & 0x80000000;
484
+ if (hx >= 0x7ff00000) return x + x;
485
+ double t;
486
+ if (hx < 0x00100000) {
487
+ if ((hx | static_cast<uint32_t>(bits)) == 0) return x;
488
+ t = 18014398509481984.0; // 2^54
489
+ t *= x;
490
+ uint64_t tb;
491
+ std::memcpy(&tb, &t, sizeof tb);
492
+ uint32_t high = static_cast<uint32_t>(tb >> 32) & 0x7fffffff;
493
+ tb = static_cast<uint64_t>(sign | (high / 3 + B2)) << 32;
494
+ std::memcpy(&t, &tb, sizeof t);
495
+ } else {
496
+ uint64_t tb = static_cast<uint64_t>(sign | (hx / 3 + B1)) << 32;
497
+ std::memcpy(&t, &tb, sizeof t);
498
+ }
499
+ double r = (t * t) * (t / x);
500
+ t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
501
+ uint64_t tb;
502
+ std::memcpy(&tb, &t, sizeof tb);
503
+ tb = (tb + 0x80000000ULL) & 0xffffffffc0000000ULL;
504
+ std::memcpy(&t, &tb, sizeof t);
505
+ double s = t * t;
506
+ r = x / s;
507
+ double w = t + t;
508
+ r = (r - t) / (w + r);
509
+ t = t + t * r;
510
+ return t;
511
+ }
512
+
513
+ double hypot(double a, double b) {
514
+ if (std::isinf(a) || std::isinf(b)) return kInfinity;
515
+ return std::hypot(a, b);
516
+ }
517
+
518
+ double hypot(double a, double b, double c) {
519
+ if (std::isinf(a) || std::isinf(b) || std::isinf(c)) return kInfinity;
520
+ return std::sqrt(a * a + b * b + c * c);
521
+ }
522
+
523
+ double random() {
524
+ static thread_local std::mt19937_64 engine{std::random_device{}()};
525
+ return static_cast<double>(engine() >> 11) * (1.0 / 9007199254740992.0);
526
+ }
527
+
528
+ double min() { return kInfinity; }
529
+ double max() { return -kInfinity; }
530
+ double min(double a) { return a; }
531
+ double max(double a) { return a; }
532
+ double min(double a, double b) {
533
+ if (std::isnan(a) || std::isnan(b)) return kNaN;
534
+ if (a == 0 && b == 0) return std::signbit(a) ? a : b;
535
+ return a < b ? a : b;
536
+ }
537
+ double max(double a, double b) {
538
+ if (std::isnan(a) || std::isnan(b)) return kNaN;
539
+ if (a == 0 && b == 0) return std::signbit(a) ? b : a;
540
+ return a > b ? a : b;
541
+ }
542
+
543
+ } // namespace math
544
+
545
+ } // namespace lucent