@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.
- package/app.plugin.js +92 -0
- package/bin/lucent.cjs +16 -0
- package/core/index.cjs +11 -0
- package/dist/bench-CR3E7e2e.js +304 -0
- package/dist/build-CAIrRSYA.js +7 -0
- package/dist/build-check-QozuNJm5.js +92 -0
- package/dist/check-1SCmYlqA.js +7 -0
- package/dist/clean-BVwpbJ5K.js +35 -0
- package/dist/cli.js +406 -0
- package/dist/codes-CIuwXt1S.js +260 -0
- package/dist/compiler.js +4 -0
- package/dist/confirm-BYF5eY3q.js +34 -0
- package/dist/dashboard-B-NzjjaN.js +91 -0
- package/dist/dev-BYWMP7oC.js +221 -0
- package/dist/diagnostic-BM9sXmwl.js +30 -0
- package/dist/diff-BTDo3Sro.js +44 -0
- package/dist/doctor-C0PrfWT6.js +230 -0
- package/dist/doctor-DdR4JnVc.js +30 -0
- package/dist/explain--vV1oaRl.js +35 -0
- package/dist/format-D9fnIMEx.js +53 -0
- package/dist/init-gIabu1S_.js +207 -0
- package/dist/live-steps-BGcWFM9M.js +79 -0
- package/dist/new-module-C2mtb0op.js +66 -0
- package/dist/package-manager-BnxwCySi.js +30 -0
- package/dist/package.json +3 -0
- package/dist/packages-HyskKIsY.js +150 -0
- package/dist/pipeline-CH7-2cxu.js +232 -0
- package/dist/project-D8tEet0k.js +150 -0
- package/dist/sdk-coverage-BoW_3Efd.js +64 -0
- package/dist/sdk-prefetch-mo04JDZV.js +89 -0
- package/dist/sdk-search-Iv4K3-8a.js +124 -0
- package/dist/sdk-show-CkrdlqlP.js +74 -0
- package/dist/sdks-Xoyiy2E4.js +28 -0
- package/dist/src-BD7f1_aZ.js +11493 -0
- package/dist/steps--_h-FFS7.js +24 -0
- package/dist/theme-uv5bc0aK.js +134 -0
- package/dist/tsconfig-S4uyLNzf.js +59 -0
- package/dist/version-bqpIO5sP.js +26 -0
- package/gradle/lucent-classpath.gradle +38 -0
- package/gradle/lucent-classpath.init.gradle +6 -0
- package/gradle/lucent.gradle +24 -0
- package/lib/core.cjs +106 -0
- package/lib/globals.d.ts +36 -0
- package/lib/sdk/android.d.ts +8 -0
- package/lib/sdk/core.d.ts +23 -0
- package/lib/sdk/ios.d.ts +33 -0
- package/lib/sdk/platform.d.ts +8 -0
- package/lib/sdk/thread.d.ts +8 -0
- package/metro/index.cjs +72 -0
- package/metro/index.d.ts +6 -0
- package/metro/transformer.cjs +70 -0
- package/package.json +61 -0
- package/runtime/cpp/lucent/abort.cpp +61 -0
- package/runtime/cpp/lucent/abort.h +54 -0
- package/runtime/cpp/lucent/array.h +442 -0
- package/runtime/cpp/lucent/async.h +236 -0
- package/runtime/cpp/lucent/builtins.cpp +163 -0
- package/runtime/cpp/lucent/bytes.h +129 -0
- package/runtime/cpp/lucent/console.h +17 -0
- package/runtime/cpp/lucent/core.h +129 -0
- package/runtime/cpp/lucent/date.cpp +441 -0
- package/runtime/cpp/lucent/date.h +82 -0
- package/runtime/cpp/lucent/equality.h +87 -0
- package/runtime/cpp/lucent/function.h +61 -0
- package/runtime/cpp/lucent/generator.h +244 -0
- package/runtime/cpp/lucent/helpers.h +226 -0
- package/runtime/cpp/lucent/jserror.cpp +51 -0
- package/runtime/cpp/lucent/jserror.h +66 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +202 -0
- package/runtime/cpp/lucent/jsi/convert.h +612 -0
- package/runtime/cpp/lucent/jsi/host.cpp +322 -0
- package/runtime/cpp/lucent/jsi/host.h +166 -0
- package/runtime/cpp/lucent/json.cpp +243 -0
- package/runtime/cpp/lucent/json.h +164 -0
- package/runtime/cpp/lucent/json_parse.h +138 -0
- package/runtime/cpp/lucent/jsstring.cpp +874 -0
- package/runtime/cpp/lucent/jsstring.h +152 -0
- package/runtime/cpp/lucent/lucent.h +24 -0
- package/runtime/cpp/lucent/map.h +344 -0
- package/runtime/cpp/lucent/native.cpp +76 -0
- package/runtime/cpp/lucent/native.h +116 -0
- package/runtime/cpp/lucent/number.cpp +545 -0
- package/runtime/cpp/lucent/number.h +190 -0
- package/runtime/cpp/lucent/ops.h +165 -0
- package/runtime/cpp/lucent/platform/android.cpp +437 -0
- package/runtime/cpp/lucent/platform/android.h +117 -0
- package/runtime/cpp/lucent/platform/ios.h +297 -0
- package/runtime/cpp/lucent/platform/ios.mm +31 -0
- package/runtime/cpp/lucent/regexp.cpp +440 -0
- package/runtime/cpp/lucent/regexp.h +107 -0
- package/runtime/cpp/lucent/scheduler.cpp +153 -0
- package/runtime/cpp/lucent/scheduler.h +160 -0
- package/runtime/cpp/lucent/unicode_data.inc +4051 -0
- package/runtime/cpp/rn/LucentModule.cpp +35 -0
- package/runtime/cpp/rn/LucentModule.h +37 -0
- package/runtime/cpp/third_party/quickjs/LICENSE +22 -0
- package/runtime/cpp/third_party/quickjs/README.md +11 -0
- package/runtime/cpp/third_party/quickjs/cutils.c +641 -0
- package/runtime/cpp/third_party/quickjs/cutils.h +457 -0
- package/runtime/cpp/third_party/quickjs/libregexp-opcode.h +73 -0
- package/runtime/cpp/third_party/quickjs/libregexp.c +3448 -0
- package/runtime/cpp/third_party/quickjs/libregexp.h +65 -0
- package/runtime/cpp/third_party/quickjs/libunicode-table.h +5206 -0
- package/runtime/cpp/third_party/quickjs/libunicode.c +2124 -0
- package/runtime/cpp/third_party/quickjs/libunicode.h +196 -0
- package/runtime/js/index.d.ts +7 -0
- package/runtime/js/index.js +60 -0
- package/runtime/native/LucentNative.podspec +28 -0
- package/runtime/native/android/CMakeLists.txt +51 -0
- package/runtime/native/android/build.gradle +22 -0
- package/runtime/native/android/include/lucentnative.h +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +25 -0
- package/runtime/native/android/src/main/java/dev/lucent/NativeProxy.java +132 -0
- package/runtime/native/ios/LucentRegistration.mm +23 -0
- package/runtime/native/package.json +6 -0
- package/runtime/native/react-native.config.js +19 -0
- package/runtime/test/jsi/harness.cpp +181 -0
- package/schemas/build.schema.json +152 -0
- package/schemas/check.schema.json +74 -0
- package/schemas/doctor.schema.json +40 -0
- package/schemas/lucent.schema.json +43 -0
- package/schemas/sdk-prefetch.schema.json +37 -0
- package/schemas/sdk-search.schema.json +54 -0
- package/ts-plugin/index.d.ts +4 -0
- package/ts-plugin/index.js +99 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Metro babel transformer: swaps each *.lucent.ts module for its JS proxy.
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const crypto = require("node:crypto");
|
|
6
|
+
|
|
7
|
+
const upstream = require(process.env.LUCENT_UPSTREAM_TRANSFORMER);
|
|
8
|
+
const LUCENT = /\.lucent\.tsx?$/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The module's name, as the compiler's moduleNameOf gives it: its file's, or
|
|
12
|
+
* in a Lucent package (the nearest package.json has a `lucent` field)
|
|
13
|
+
* `<package>/<path under its sources>`.
|
|
14
|
+
*/
|
|
15
|
+
function moduleName(filename) {
|
|
16
|
+
const base = (f) => f.replace(/\.(ios|android)(?=\.lucent\.tsx?$)/, "").replace(LUCENT, "");
|
|
17
|
+
for (let dir = path.dirname(path.resolve(filename)); ; dir = path.dirname(dir)) {
|
|
18
|
+
const file = path.join(dir, "package.json");
|
|
19
|
+
if (fs.existsSync(file)) {
|
|
20
|
+
const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
21
|
+
if (!pkg.lucent || !pkg.name) break;
|
|
22
|
+
const rel = path.relative(path.join(dir, pkg.lucent.sources || "."), path.resolve(filename));
|
|
23
|
+
return `${pkg.name}/${base(rel).split(path.sep).join("/")}`;
|
|
24
|
+
}
|
|
25
|
+
if (path.dirname(dir) === dir) break;
|
|
26
|
+
}
|
|
27
|
+
return base(path.basename(filename));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function proxyFor(filename, projectRoot) {
|
|
31
|
+
const name = moduleName(filename);
|
|
32
|
+
const generated = path.join(projectRoot, ".lucent", "native", "js", `${name}.js`);
|
|
33
|
+
if (fs.existsSync(generated))
|
|
34
|
+
return rebase(fs.readFileSync(generated, "utf8"), generated, filename);
|
|
35
|
+
return `throw new Error(${JSON.stringify(`Lucent: ${path.basename(filename)} has not been compiled. Run \`lucent build\` and rebuild the app.`)});\n`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The proxy's relative requires (the JS loader), which are relative to the
|
|
40
|
+
* generated file, made relative to the source file Metro bundles it as.
|
|
41
|
+
*/
|
|
42
|
+
function rebase(proxy, generated, filename) {
|
|
43
|
+
return proxy.replace(/require\("(\.\.?\/[^"]+)"\)/g, (_, spec) => {
|
|
44
|
+
const rel = path
|
|
45
|
+
.relative(path.dirname(filename), path.resolve(path.dirname(generated), spec))
|
|
46
|
+
.split(path.sep)
|
|
47
|
+
.join("/");
|
|
48
|
+
return `require(${JSON.stringify(rel.startsWith(".") ? rel : `./${rel}`)})`;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
...upstream,
|
|
54
|
+
transform(args) {
|
|
55
|
+
if (LUCENT.test(args.filename)) {
|
|
56
|
+
const root = (args.options && args.options.projectRoot) || process.cwd();
|
|
57
|
+
const src = proxyFor(args.filename, root);
|
|
58
|
+
// The proxy is plain JavaScript, which the TypeScript pipeline accepts.
|
|
59
|
+
return upstream.transform({ ...args, src });
|
|
60
|
+
}
|
|
61
|
+
return upstream.transform(args);
|
|
62
|
+
},
|
|
63
|
+
getCacheKey(...args) {
|
|
64
|
+
const base = typeof upstream.getCacheKey === "function" ? upstream.getCacheKey(...args) : "";
|
|
65
|
+
// Proxies change when `lucent build` runs: include the manifest in the key.
|
|
66
|
+
const manifest = path.join(process.cwd(), ".lucent", "native", "manifest.json");
|
|
67
|
+
const stamp = fs.existsSync(manifest) ? fs.readFileSync(manifest, "utf8") : "";
|
|
68
|
+
return crypto.createHash("sha1").update(base).update(stamp).update("lucent-1").digest("hex");
|
|
69
|
+
},
|
|
70
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lucent-lang/lucent",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Write native React Native modules in TypeScript: the Lucent compiler, CLI, Metro integration, Expo config plugin and editor plugin",
|
|
5
|
+
"homepage": "https://lucent-lang.dev",
|
|
6
|
+
"bugs": "https://github.com/Fausto95/lucent/issues",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Fausto95/lucent.git",
|
|
11
|
+
"directory": "packages/lucent"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"lucent": "./bin/lucent.cjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"app.plugin.js",
|
|
18
|
+
"bin",
|
|
19
|
+
"core",
|
|
20
|
+
"dist",
|
|
21
|
+
"gradle",
|
|
22
|
+
"lib",
|
|
23
|
+
"metro",
|
|
24
|
+
"runtime",
|
|
25
|
+
"schemas",
|
|
26
|
+
"ts-plugin"
|
|
27
|
+
],
|
|
28
|
+
"main": "./app.plugin.js",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": "./app.plugin.js",
|
|
31
|
+
"./metro": {
|
|
32
|
+
"types": "./metro/index.d.ts",
|
|
33
|
+
"default": "./metro/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./ts-plugin": {
|
|
36
|
+
"types": "./ts-plugin/index.d.ts",
|
|
37
|
+
"default": "./ts-plugin/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./core": {
|
|
40
|
+
"types": "./lib/sdk/core.d.ts",
|
|
41
|
+
"default": "./core/index.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./app.plugin.js": "./app.plugin.js",
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"ink": "^7.1.1",
|
|
48
|
+
"picocolors": "^1.1.1",
|
|
49
|
+
"react": "19.3.0",
|
|
50
|
+
"typescript": "~5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/react": "^19.2.0",
|
|
54
|
+
"ajv": "^8",
|
|
55
|
+
"ink-testing-library": "^4.0.0",
|
|
56
|
+
"@lucent-lang/bindgen": "0.0.3",
|
|
57
|
+
"@lucent-lang/compiler": "0.0.3",
|
|
58
|
+
"@lucent-lang/runtime": "0.0.3"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {}
|
|
61
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#include "abort.h"
|
|
2
|
+
|
|
3
|
+
#include <cstdio>
|
|
4
|
+
#include <exception>
|
|
5
|
+
|
|
6
|
+
#include "scheduler.h"
|
|
7
|
+
|
|
8
|
+
namespace lucent {
|
|
9
|
+
|
|
10
|
+
void AbortSignalObject::abort(Error why) {
|
|
11
|
+
if (aborted.load()) return;
|
|
12
|
+
reason = std::move(why);
|
|
13
|
+
aborted.store(true);
|
|
14
|
+
auto listeners = std::move(listeners_);
|
|
15
|
+
listeners_.clear();
|
|
16
|
+
for (auto& [id, f] : listeners) {
|
|
17
|
+
// Like an event listener: a throwing listener does not stop the others.
|
|
18
|
+
try {
|
|
19
|
+
f();
|
|
20
|
+
} catch (const std::exception& e) {
|
|
21
|
+
std::fprintf(stderr, "[lucent] uncaught exception in abort listener: %s\n", e.what());
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void AbortSignalObject::throwIfAborted() const {
|
|
27
|
+
if (aborted.load()) throw Exception(reason);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
uint64_t AbortSignalObject::add(std::function<void()> f) {
|
|
31
|
+
uint64_t id = nextId_++;
|
|
32
|
+
if (!aborted.load()) listeners_.emplace_back(id, std::move(f));
|
|
33
|
+
return id;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
void AbortSignalObject::remove(uint64_t id) {
|
|
37
|
+
std::erase_if(listeners_, [id](const auto& l) { return l.first == id; });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void AbortControllerObject::abort(Opt<Error> why) {
|
|
41
|
+
signal->abort(why.has() ? why.get() : abortError());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Error abortError() {
|
|
45
|
+
return makeError(String::fromLatin1("AbortError"), String::fromLatin1("signal is aborted without reason"));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Promise<void> delay(double ms, Opt<AbortSignal> signal) {
|
|
49
|
+
if (!signal.has()) return delay(ms);
|
|
50
|
+
AbortSignal s = signal.get();
|
|
51
|
+
if (s->aborted.load()) return Promise<void>::rejected(s->reason);
|
|
52
|
+
Promise<void> p;
|
|
53
|
+
uint64_t id = s->add([p, s] { p.reject(s->reason); });
|
|
54
|
+
Scheduler::instance().postDelayed(ms, [p, s, id] {
|
|
55
|
+
s->remove(id);
|
|
56
|
+
p.resolve(undefined);
|
|
57
|
+
});
|
|
58
|
+
return p;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
} // namespace lucent
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Lucent runtime — AbortController and AbortSignal.
|
|
2
|
+
//
|
|
3
|
+
// `aborted` may be read from any thread. Everything else, including abort
|
|
4
|
+
// itself, runs under the Lucent lock. A signal that came from JavaScript is
|
|
5
|
+
// aborted from the JS thread by a listener on the JS signal (lucent/jsi).
|
|
6
|
+
#pragma once
|
|
7
|
+
|
|
8
|
+
#include <atomic>
|
|
9
|
+
#include <cstdint>
|
|
10
|
+
#include <functional>
|
|
11
|
+
#include <utility>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
#include "async.h"
|
|
15
|
+
#include "core.h"
|
|
16
|
+
#include "jserror.h"
|
|
17
|
+
|
|
18
|
+
namespace lucent {
|
|
19
|
+
|
|
20
|
+
class AbortSignalObject : public Object {
|
|
21
|
+
public:
|
|
22
|
+
std::atomic<bool> aborted{false};
|
|
23
|
+
/// Set once, when the signal aborts.
|
|
24
|
+
Error reason;
|
|
25
|
+
|
|
26
|
+
/// Aborts once: records `why` and runs the listeners in the order they
|
|
27
|
+
/// were added, synchronously, like dispatching the `abort` event.
|
|
28
|
+
void abort(Error why);
|
|
29
|
+
void throwIfAborted() const;
|
|
30
|
+
/// `addEventListener("abort", f)`.
|
|
31
|
+
void addEventListener(std::function<void()> f) { add(std::move(f)); }
|
|
32
|
+
uint64_t add(std::function<void()> f);
|
|
33
|
+
void remove(uint64_t id);
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
uint64_t nextId_ = 1;
|
|
37
|
+
std::vector<std::pair<uint64_t, std::function<void()>>> listeners_;
|
|
38
|
+
};
|
|
39
|
+
using AbortSignal = Ref<AbortSignalObject>;
|
|
40
|
+
|
|
41
|
+
struct AbortControllerObject : Object {
|
|
42
|
+
AbortSignal signal = std::make_shared<AbortSignalObject>();
|
|
43
|
+
/// `abort()` uses an AbortError, like React Native and the DOM.
|
|
44
|
+
void abort(Opt<Error> reason);
|
|
45
|
+
};
|
|
46
|
+
using AbortController = Ref<AbortControllerObject>;
|
|
47
|
+
|
|
48
|
+
/// The default abort reason.
|
|
49
|
+
Error abortError();
|
|
50
|
+
|
|
51
|
+
/// `delay(ms, signal)`: rejects with the signal's reason if it aborts first.
|
|
52
|
+
Promise<void> delay(double ms, Opt<AbortSignal> signal);
|
|
53
|
+
|
|
54
|
+
} // namespace lucent
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
// Lucent runtime — Array<T> with JavaScript semantics.
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <initializer_list>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <tuple>
|
|
9
|
+
#include <variant>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
#include "core.h"
|
|
13
|
+
#include "equality.h"
|
|
14
|
+
#include "jserror.h"
|
|
15
|
+
#include "function.h"
|
|
16
|
+
#include "number.h"
|
|
17
|
+
#include "jsstring.h"
|
|
18
|
+
|
|
19
|
+
namespace lucent {
|
|
20
|
+
|
|
21
|
+
template <class T>
|
|
22
|
+
class Array;
|
|
23
|
+
|
|
24
|
+
template <class T>
|
|
25
|
+
String toJsString(const Opt<T>& v);
|
|
26
|
+
// Defined in ops.h / helpers.h; declared here so element types that are
|
|
27
|
+
// unions or tuples resolve inside Array's templates (ADL only searches std).
|
|
28
|
+
template <class... Ts>
|
|
29
|
+
String toJsString(const std::variant<Ts...>& v);
|
|
30
|
+
template <class... Ts>
|
|
31
|
+
String toJsString(const std::tuple<Ts...>& t);
|
|
32
|
+
|
|
33
|
+
namespace detail {
|
|
34
|
+
// JS relative index (used by slice, splice, fill, at...).
|
|
35
|
+
inline size_t relativeIndex(double v, size_t len) {
|
|
36
|
+
if (std::isnan(v)) return 0;
|
|
37
|
+
v = std::trunc(v);
|
|
38
|
+
if (v < 0) {
|
|
39
|
+
v += static_cast<double>(len);
|
|
40
|
+
return v < 0 ? 0 : static_cast<size_t>(v);
|
|
41
|
+
}
|
|
42
|
+
return v > static_cast<double>(len) ? len : static_cast<size_t>(v);
|
|
43
|
+
}
|
|
44
|
+
// Result of a comparator, with NaN treated as 0.
|
|
45
|
+
inline double compareResult(double v) { return std::isnan(v) ? 0 : v; }
|
|
46
|
+
} // namespace detail
|
|
47
|
+
|
|
48
|
+
template <class T>
|
|
49
|
+
class Array {
|
|
50
|
+
public:
|
|
51
|
+
/// std::vector<bool> is a bit-packed proxy container; store bytes instead.
|
|
52
|
+
using Elem = std::conditional_t<std::is_same_v<T, bool>, uint8_t, T>;
|
|
53
|
+
using value_type = T;
|
|
54
|
+
|
|
55
|
+
Array() : d_(std::make_shared<std::vector<Elem>>()) {}
|
|
56
|
+
Array(std::initializer_list<T> items) : d_(std::make_shared<std::vector<Elem>>()) {
|
|
57
|
+
d_->reserve(items.size());
|
|
58
|
+
for (const auto& v : items) d_->push_back(static_cast<Elem>(v));
|
|
59
|
+
}
|
|
60
|
+
explicit Array(std::vector<Elem> items) : d_(std::make_shared<std::vector<Elem>>(std::move(items))) {}
|
|
61
|
+
|
|
62
|
+
/// `new Array(n).fill(v)` style construction.
|
|
63
|
+
static Array filled(size_t n, const T& v) { return Array(std::vector<Elem>(n, static_cast<Elem>(v))); }
|
|
64
|
+
/// `Array.from({ length: n }, (_, i) => f(i))`.
|
|
65
|
+
template <class F>
|
|
66
|
+
static Array generate(double n, F&& f) {
|
|
67
|
+
if (!(n >= 0) || std::trunc(n) != n || n > 4294967295.0) throwRangeError("Invalid array length");
|
|
68
|
+
Array out;
|
|
69
|
+
size_t count = static_cast<size_t>(n);
|
|
70
|
+
out.d_->reserve(count);
|
|
71
|
+
for (size_t i = 0; i < count; i++) out.d_->push_back(static_cast<Elem>(f(static_cast<double>(i))));
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
size_t size() const { return d_->size(); }
|
|
76
|
+
double length() const { return static_cast<double>(d_->size()); }
|
|
77
|
+
void setLength(double n) {
|
|
78
|
+
if (!(n >= 0) || std::trunc(n) != n || n > 4294967295.0) throwRangeError("Invalid array length");
|
|
79
|
+
size_t len = static_cast<size_t>(n);
|
|
80
|
+
if (len > d_->size()) {
|
|
81
|
+
if constexpr (std::is_default_constructible_v<Elem> && IsOpt<T>::value) {
|
|
82
|
+
d_->resize(len);
|
|
83
|
+
} else {
|
|
84
|
+
throwRangeError("Cannot grow an array of non-optional elements by setting length");
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
d_->resize(len);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Unchecked element read for compiler-proven in-bounds indexes.
|
|
92
|
+
T at(size_t i) const { return static_cast<T>((*d_)[i]); }
|
|
93
|
+
/// `a[i]` for an index the compiler knows is an integer.
|
|
94
|
+
Opt<T> getIndex(int64_t index) const {
|
|
95
|
+
if (index >= 0 && static_cast<uint64_t>(index) < d_->size()) return static_cast<T>((*d_)[static_cast<size_t>(index)]);
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/// `a[i]`: undefined when out of bounds.
|
|
99
|
+
Opt<T> get(double index) const {
|
|
100
|
+
if (index >= 0 && index < static_cast<double>(d_->size()) && std::trunc(index) == index) {
|
|
101
|
+
return static_cast<T>((*d_)[static_cast<size_t>(index)]);
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
/// `a[i] = v`. Writing at `length` appends; writing further would create
|
|
106
|
+
/// holes, which Lucent arrays do not have.
|
|
107
|
+
void set(double index, T value) {
|
|
108
|
+
if (!(index >= 0) || std::trunc(index) != index) throwRangeError("Invalid array index");
|
|
109
|
+
size_t i = static_cast<size_t>(index);
|
|
110
|
+
if (i < d_->size()) {
|
|
111
|
+
(*d_)[i] = static_cast<Elem>(std::move(value));
|
|
112
|
+
} else if (i == d_->size()) {
|
|
113
|
+
d_->push_back(static_cast<Elem>(std::move(value)));
|
|
114
|
+
} else {
|
|
115
|
+
if constexpr (IsOpt<T>::value) {
|
|
116
|
+
d_->resize(i);
|
|
117
|
+
d_->push_back(static_cast<Elem>(std::move(value)));
|
|
118
|
+
} else {
|
|
119
|
+
throwRangeError("Array index out of bounds (Lucent arrays cannot have holes)");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
double push(T v) {
|
|
125
|
+
d_->push_back(static_cast<Elem>(std::move(v)));
|
|
126
|
+
return length();
|
|
127
|
+
}
|
|
128
|
+
template <class... Rest>
|
|
129
|
+
double push(T v, Rest&&... rest) {
|
|
130
|
+
push(std::move(v));
|
|
131
|
+
return push(std::forward<Rest>(rest)...);
|
|
132
|
+
}
|
|
133
|
+
Opt<T> pop() {
|
|
134
|
+
if (d_->empty()) return undefined;
|
|
135
|
+
T v = static_cast<T>(std::move(d_->back()));
|
|
136
|
+
d_->pop_back();
|
|
137
|
+
return v;
|
|
138
|
+
}
|
|
139
|
+
Opt<T> shift() {
|
|
140
|
+
if (d_->empty()) return undefined;
|
|
141
|
+
T v = static_cast<T>(std::move(d_->front()));
|
|
142
|
+
d_->erase(d_->begin());
|
|
143
|
+
return v;
|
|
144
|
+
}
|
|
145
|
+
double unshift(T v) {
|
|
146
|
+
d_->insert(d_->begin(), static_cast<Elem>(std::move(v)));
|
|
147
|
+
return length();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Opt<T> atIndex(double index) const {
|
|
151
|
+
if (std::isnan(index)) index = 0;
|
|
152
|
+
index = std::trunc(index);
|
|
153
|
+
double n = length();
|
|
154
|
+
if (index < 0) index += n;
|
|
155
|
+
if (index < 0 || index >= n) return undefined;
|
|
156
|
+
return at(static_cast<size_t>(index));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Array slice() const { return Array(*d_); }
|
|
160
|
+
Array slice(double start) const { return slice(start, length()); }
|
|
161
|
+
Array slice(double start, double end) const {
|
|
162
|
+
size_t n = d_->size();
|
|
163
|
+
size_t a = detail::relativeIndex(start, n), b = detail::relativeIndex(end, n);
|
|
164
|
+
if (a >= b) return Array();
|
|
165
|
+
return Array(std::vector<Elem>(d_->begin() + static_cast<std::ptrdiff_t>(a), d_->begin() + static_cast<std::ptrdiff_t>(b)));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Array splice(double start) { return splice(start, length()); }
|
|
169
|
+
template <class... Items>
|
|
170
|
+
Array splice(double start, double deleteCount, Items&&... items) {
|
|
171
|
+
size_t n = d_->size();
|
|
172
|
+
size_t a = detail::relativeIndex(start, n);
|
|
173
|
+
double dc = std::isnan(deleteCount) ? 0 : std::trunc(deleteCount);
|
|
174
|
+
size_t count = dc <= 0 ? 0 : std::min(static_cast<size_t>(std::min(dc, 4294967295.0)), n - a);
|
|
175
|
+
std::vector<Elem> removed(d_->begin() + static_cast<std::ptrdiff_t>(a), d_->begin() + static_cast<std::ptrdiff_t>(a + count));
|
|
176
|
+
d_->erase(d_->begin() + static_cast<std::ptrdiff_t>(a), d_->begin() + static_cast<std::ptrdiff_t>(a + count));
|
|
177
|
+
std::vector<Elem> inserted{static_cast<Elem>(T(std::forward<Items>(items)))...};
|
|
178
|
+
d_->insert(d_->begin() + static_cast<std::ptrdiff_t>(a), inserted.begin(), inserted.end());
|
|
179
|
+
return Array(std::move(removed));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
Array concat() const { return slice(); }
|
|
183
|
+
template <class... Rest>
|
|
184
|
+
Array concat(const Array& other, const Rest&... rest) const {
|
|
185
|
+
Array out = slice();
|
|
186
|
+
out.d_->insert(out.d_->end(), other.d_->begin(), other.d_->end());
|
|
187
|
+
if constexpr (sizeof...(rest) > 0) return out.concat(rest...);
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
/// Appends every element of `other` (spread).
|
|
191
|
+
void append(const Array& other) {
|
|
192
|
+
if (other.d_ == d_) {
|
|
193
|
+
std::vector<Elem> copy = *d_;
|
|
194
|
+
d_->insert(d_->end(), copy.begin(), copy.end());
|
|
195
|
+
} else {
|
|
196
|
+
d_->insert(d_->end(), other.d_->begin(), other.d_->end());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
String join() const { return join(String::fromLatin1(",")); }
|
|
201
|
+
String join(const String& sep) const {
|
|
202
|
+
String out;
|
|
203
|
+
for (size_t i = 0; i < d_->size(); i++) {
|
|
204
|
+
if (i > 0) out += sep;
|
|
205
|
+
if constexpr (IsOpt<T>::value) {
|
|
206
|
+
const T& v = (*d_)[i];
|
|
207
|
+
if (v.has()) out += toJsString(v.get());
|
|
208
|
+
} else {
|
|
209
|
+
out += toJsString(at(i));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
double indexOf(const T& v, double from = 0) const {
|
|
216
|
+
for (size_t i = detail::relativeIndex(from, d_->size()); i < d_->size(); i++) {
|
|
217
|
+
if (strictEquals(at(i), v)) return static_cast<double>(i);
|
|
218
|
+
}
|
|
219
|
+
return -1;
|
|
220
|
+
}
|
|
221
|
+
double lastIndexOf(const T& v) const {
|
|
222
|
+
for (size_t i = d_->size(); i-- > 0;) {
|
|
223
|
+
if (strictEquals(at(i), v)) return static_cast<double>(i);
|
|
224
|
+
}
|
|
225
|
+
return -1;
|
|
226
|
+
}
|
|
227
|
+
bool includes(const T& v) const {
|
|
228
|
+
for (size_t i = 0; i < d_->size(); i++) {
|
|
229
|
+
if (sameValueZero(at(i), v)) return true;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
template <class F>
|
|
235
|
+
Opt<T> find(F&& f) const {
|
|
236
|
+
for (size_t i = 0; i < d_->size(); i++) {
|
|
237
|
+
T v = at(i);
|
|
238
|
+
if (invokeCallback(f, v, static_cast<double>(i), *this)) return v;
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
template <class F>
|
|
243
|
+
double findIndex(F&& f) const {
|
|
244
|
+
for (size_t i = 0; i < d_->size(); i++) {
|
|
245
|
+
if (invokeCallback(f, at(i), static_cast<double>(i), *this)) return static_cast<double>(i);
|
|
246
|
+
}
|
|
247
|
+
return -1;
|
|
248
|
+
}
|
|
249
|
+
template <class F>
|
|
250
|
+
Opt<T> findLast(F&& f) const {
|
|
251
|
+
for (size_t i = d_->size(); i-- > 0;) {
|
|
252
|
+
T v = at(i);
|
|
253
|
+
if (invokeCallback(f, v, static_cast<double>(i), *this)) return v;
|
|
254
|
+
}
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
template <class F>
|
|
258
|
+
double findLastIndex(F&& f) const {
|
|
259
|
+
for (size_t i = d_->size(); i-- > 0;) {
|
|
260
|
+
if (invokeCallback(f, at(i), static_cast<double>(i), *this)) return static_cast<double>(i);
|
|
261
|
+
}
|
|
262
|
+
return -1;
|
|
263
|
+
}
|
|
264
|
+
template <class F>
|
|
265
|
+
Array filter(F&& f) const {
|
|
266
|
+
Array out;
|
|
267
|
+
size_t n = d_->size();
|
|
268
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) {
|
|
269
|
+
T v = at(i);
|
|
270
|
+
if (invokeCallback(f, v, static_cast<double>(i), *this)) out.d_->push_back(static_cast<Elem>(v));
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
template <class U, class F>
|
|
275
|
+
Array<U> map(F&& f) const {
|
|
276
|
+
std::vector<typename Array<U>::Elem> out;
|
|
277
|
+
size_t n = d_->size();
|
|
278
|
+
out.reserve(n);
|
|
279
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) {
|
|
280
|
+
out.push_back(static_cast<typename Array<U>::Elem>(U(invokeCallback(f, at(i), static_cast<double>(i), *this))));
|
|
281
|
+
}
|
|
282
|
+
return Array<U>(std::move(out));
|
|
283
|
+
}
|
|
284
|
+
template <class U, class F>
|
|
285
|
+
Array<U> flatMap(F&& f) const {
|
|
286
|
+
Array<U> out;
|
|
287
|
+
size_t n = d_->size();
|
|
288
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) {
|
|
289
|
+
out.append(invokeCallback(f, at(i), static_cast<double>(i), *this));
|
|
290
|
+
}
|
|
291
|
+
return out;
|
|
292
|
+
}
|
|
293
|
+
template <class F>
|
|
294
|
+
void forEach(F&& f) const {
|
|
295
|
+
size_t n = d_->size();
|
|
296
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) invokeCallback(f, at(i), static_cast<double>(i), *this);
|
|
297
|
+
}
|
|
298
|
+
template <class F>
|
|
299
|
+
bool some(F&& f) const {
|
|
300
|
+
size_t n = d_->size();
|
|
301
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) {
|
|
302
|
+
if (invokeCallback(f, at(i), static_cast<double>(i), *this)) return true;
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
template <class F>
|
|
307
|
+
bool every(F&& f) const {
|
|
308
|
+
size_t n = d_->size();
|
|
309
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) {
|
|
310
|
+
if (!invokeCallback(f, at(i), static_cast<double>(i), *this)) return false;
|
|
311
|
+
}
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
/// reduce without an initial value: the accumulator is an element.
|
|
315
|
+
template <class F>
|
|
316
|
+
T reduce(F&& f) const {
|
|
317
|
+
if (d_->empty()) throwTypeError("Reduce of empty array with no initial value");
|
|
318
|
+
T acc = at(0);
|
|
319
|
+
size_t n = d_->size();
|
|
320
|
+
for (size_t i = 1; i < n && i < d_->size(); i++) acc = T(invokeCallback(f, acc, at(i), static_cast<double>(i), *this));
|
|
321
|
+
return acc;
|
|
322
|
+
}
|
|
323
|
+
template <class U, class F>
|
|
324
|
+
U reduce(F&& f, U init) const {
|
|
325
|
+
U acc = std::move(init);
|
|
326
|
+
size_t n = d_->size();
|
|
327
|
+
for (size_t i = 0; i < n && i < d_->size(); i++) acc = U(invokeCallback(f, acc, at(i), static_cast<double>(i), *this));
|
|
328
|
+
return acc;
|
|
329
|
+
}
|
|
330
|
+
template <class F>
|
|
331
|
+
T reduceRight(F&& f) const {
|
|
332
|
+
if (d_->empty()) throwTypeError("Reduce of empty array with no initial value");
|
|
333
|
+
size_t i = d_->size() - 1;
|
|
334
|
+
T acc = at(i);
|
|
335
|
+
while (i-- > 0) {
|
|
336
|
+
if (i < d_->size()) acc = T(invokeCallback(f, acc, at(i), static_cast<double>(i), *this));
|
|
337
|
+
}
|
|
338
|
+
return acc;
|
|
339
|
+
}
|
|
340
|
+
template <class U, class F>
|
|
341
|
+
U reduceRight(F&& f, U init) const {
|
|
342
|
+
U acc = std::move(init);
|
|
343
|
+
for (size_t i = d_->size(); i-- > 0;) {
|
|
344
|
+
if (i < d_->size()) acc = U(invokeCallback(f, acc, at(i), static_cast<double>(i), *this));
|
|
345
|
+
}
|
|
346
|
+
return acc;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/// Default sort: by string value, as JavaScript does; absent values last.
|
|
350
|
+
Array& sort() {
|
|
351
|
+
mergeSort([](const T& a, const T& b) -> double {
|
|
352
|
+
if constexpr (IsOpt<T>::value) {
|
|
353
|
+
if (!a.has() || !b.has()) return (a.has() ? -1.0 : 0.0) + (b.has() ? 1.0 : 0.0);
|
|
354
|
+
return static_cast<double>(String::compare(toJsString(a.get()), toJsString(b.get())));
|
|
355
|
+
} else {
|
|
356
|
+
return static_cast<double>(String::compare(toJsString(a), toJsString(b)));
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
return *this;
|
|
360
|
+
}
|
|
361
|
+
template <class F>
|
|
362
|
+
Array& sort(F&& cmp) {
|
|
363
|
+
mergeSort([&](const T& a, const T& b) -> double { return detail::compareResult(cmp(a, b)); });
|
|
364
|
+
return *this;
|
|
365
|
+
}
|
|
366
|
+
Array toSorted() const { return slice().sort(); }
|
|
367
|
+
template <class F>
|
|
368
|
+
Array toSorted(F&& cmp) const {
|
|
369
|
+
Array out = slice();
|
|
370
|
+
out.sort(std::forward<F>(cmp));
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
Array& reverse() {
|
|
374
|
+
std::reverse(d_->begin(), d_->end());
|
|
375
|
+
return *this;
|
|
376
|
+
}
|
|
377
|
+
Array toReversed() const { return slice().reverse(); }
|
|
378
|
+
Array& fill(const T& v) { return fill(v, 0, length()); }
|
|
379
|
+
Array& fill(const T& v, double start) { return fill(v, start, length()); }
|
|
380
|
+
Array& fill(const T& v, double start, double end) {
|
|
381
|
+
size_t n = d_->size();
|
|
382
|
+
size_t a = detail::relativeIndex(start, n), b = detail::relativeIndex(end, n);
|
|
383
|
+
for (size_t i = a; i < b; i++) (*d_)[i] = static_cast<Elem>(v);
|
|
384
|
+
return *this;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const void* identity() const { return d_.get(); }
|
|
388
|
+
friend bool strictEquals(const Array& a, const Array& b) { return a.d_ == b.d_; }
|
|
389
|
+
friend String toJsString(const Array& a) { return a.join(); }
|
|
390
|
+
|
|
391
|
+
/// Raw storage, for the JSI boundary and for-of loops.
|
|
392
|
+
const std::vector<Elem>& items() const { return *d_; }
|
|
393
|
+
std::vector<Elem>& items() { return *d_; }
|
|
394
|
+
|
|
395
|
+
private:
|
|
396
|
+
template <class Cmp>
|
|
397
|
+
void mergeSort(Cmp&& cmp) {
|
|
398
|
+
// Stable, and safe against inconsistent comparators (never reads out of
|
|
399
|
+
// bounds, unlike std::sort).
|
|
400
|
+
std::vector<T> a;
|
|
401
|
+
a.reserve(d_->size());
|
|
402
|
+
for (auto& e : *d_) a.push_back(static_cast<T>(e));
|
|
403
|
+
std::vector<T> tmp(a.size());
|
|
404
|
+
for (size_t width = 1; width < a.size(); width *= 2) {
|
|
405
|
+
for (size_t lo = 0; lo < a.size(); lo += 2 * width) {
|
|
406
|
+
size_t mid = std::min(lo + width, a.size()), hi = std::min(lo + 2 * width, a.size());
|
|
407
|
+
size_t i = lo, j = mid, k = lo;
|
|
408
|
+
while (i < mid && j < hi) {
|
|
409
|
+
if (cmp(a[i], a[j]) > 0) tmp[k++] = a[j++];
|
|
410
|
+
else tmp[k++] = a[i++];
|
|
411
|
+
}
|
|
412
|
+
while (i < mid) tmp[k++] = a[i++];
|
|
413
|
+
while (j < hi) tmp[k++] = a[j++];
|
|
414
|
+
}
|
|
415
|
+
a.swap(tmp);
|
|
416
|
+
}
|
|
417
|
+
d_->clear();
|
|
418
|
+
for (auto& v : a) d_->push_back(static_cast<Elem>(std::move(v)));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
std::shared_ptr<std::vector<Elem>> d_;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
template <class T>
|
|
425
|
+
struct IsArray : std::false_type {};
|
|
426
|
+
template <class T>
|
|
427
|
+
struct IsArray<Array<T>> : std::true_type {};
|
|
428
|
+
|
|
429
|
+
template <class T>
|
|
430
|
+
String toJsString(const Opt<T>& v) {
|
|
431
|
+
if (v.isUndefined()) return String::fromLatin1("undefined");
|
|
432
|
+
if (v.isNull()) return String::fromLatin1("null");
|
|
433
|
+
return toJsString(v.get());
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/// String.prototype.split with a string separator.
|
|
437
|
+
Array<String> split(const String& s, const String& separator);
|
|
438
|
+
Array<String> split(const String& s, const String& separator, double limit);
|
|
439
|
+
/// `s.split("")` and `[...s]`-style splitting into code units / code points.
|
|
440
|
+
Array<String> splitCodePoints(const String& s);
|
|
441
|
+
|
|
442
|
+
} // namespace lucent
|