@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,243 @@
1
+ #include "json_parse.h"
2
+
3
+ #include "jserror.h"
4
+ #include "number.h"
5
+
6
+ namespace lucent {
7
+
8
+ namespace {
9
+
10
+ constexpr int kMaxDepth = 1000;
11
+
12
+ class Parser {
13
+ public:
14
+ explicit Parser(const String& s) : s_(s), n_(s.length()) {}
15
+
16
+ JsonValue parse() {
17
+ ws();
18
+ JsonValue v = value(0);
19
+ ws();
20
+ if (i_ < n_) unexpected();
21
+ return v;
22
+ }
23
+
24
+ private:
25
+ char16_t at(size_t i) const { return s_.unit(i); }
26
+
27
+ [[noreturn]] void fail(const std::string& what) {
28
+ throwError(String::fromLatin1("SyntaxError"), String::fromUtf8("JSON Parse error: " + what));
29
+ }
30
+ [[noreturn]] void unexpected() {
31
+ if (i_ >= n_) fail("Unexpected end of input");
32
+ char16_t c = at(i_);
33
+ std::string tok = c < 0x80 ? std::string(1, static_cast<char>(c)) : "\\u" + std::to_string(c);
34
+ fail("Unexpected token: " + tok);
35
+ }
36
+
37
+ void ws() {
38
+ while (i_ < n_) {
39
+ char16_t c = at(i_);
40
+ if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
41
+ i_++;
42
+ }
43
+ }
44
+ bool eat(char16_t c) {
45
+ if (i_ < n_ && at(i_) == c) {
46
+ i_++;
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ void literal(const char* word) {
52
+ for (const char* w = word; *w; w++) {
53
+ if (!eat(static_cast<char16_t>(*w))) unexpected();
54
+ }
55
+ }
56
+
57
+ JsonValue value(int depth) {
58
+ if (depth > kMaxDepth) throwRangeError("JSON nesting is too deep");
59
+ if (i_ >= n_) unexpected();
60
+ JsonValue v;
61
+ switch (at(i_)) {
62
+ case '{': {
63
+ i_++;
64
+ v.kind = JsonValue::Kind::Object;
65
+ ws();
66
+ if (eat('}')) return v;
67
+ for (;;) {
68
+ ws();
69
+ if (i_ >= n_ || at(i_) != '"') unexpected();
70
+ String key = string();
71
+ ws();
72
+ if (!eat(':')) unexpected();
73
+ ws();
74
+ v.members.push_back(JsonMember{std::move(key), value(depth + 1)});
75
+ ws();
76
+ if (eat('}')) return v;
77
+ if (!eat(',')) unexpected();
78
+ }
79
+ }
80
+ case '[': {
81
+ i_++;
82
+ v.kind = JsonValue::Kind::Array;
83
+ ws();
84
+ if (eat(']')) return v;
85
+ for (;;) {
86
+ ws();
87
+ v.items.push_back(value(depth + 1));
88
+ ws();
89
+ if (eat(']')) return v;
90
+ if (!eat(',')) unexpected();
91
+ }
92
+ }
93
+ case '"':
94
+ v.kind = JsonValue::Kind::String;
95
+ v.string = string();
96
+ return v;
97
+ case 't':
98
+ literal("true");
99
+ v.kind = JsonValue::Kind::Bool;
100
+ v.boolean = true;
101
+ return v;
102
+ case 'f':
103
+ literal("false");
104
+ v.kind = JsonValue::Kind::Bool;
105
+ return v;
106
+ case 'n':
107
+ literal("null");
108
+ return v;
109
+ default:
110
+ v.kind = JsonValue::Kind::Number;
111
+ v.number = number();
112
+ return v;
113
+ }
114
+ }
115
+
116
+ bool digit() const { return i_ < n_ && at(i_) >= '0' && at(i_) <= '9'; }
117
+
118
+ double number() {
119
+ size_t start = i_;
120
+ eat('-');
121
+ if (eat('0')) {
122
+ // No leading zeros.
123
+ } else if (digit()) {
124
+ while (digit()) i_++;
125
+ } else {
126
+ unexpected();
127
+ }
128
+ if (eat('.')) {
129
+ if (!digit()) unexpected();
130
+ while (digit()) i_++;
131
+ }
132
+ if (eat('e') || eat('E')) {
133
+ if (!eat('+')) eat('-');
134
+ if (!digit()) unexpected();
135
+ while (digit()) i_++;
136
+ }
137
+ return stringToNumber(s_.substring(static_cast<double>(start), static_cast<double>(i_)));
138
+ }
139
+
140
+ int hex(char16_t c) {
141
+ if (c >= '0' && c <= '9') return c - '0';
142
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
143
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
144
+ return -1;
145
+ }
146
+
147
+ String string() {
148
+ i_++; // opening quote
149
+ std::u16string out;
150
+ for (;;) {
151
+ if (i_ >= n_) fail("Unterminated string");
152
+ char16_t c = at(i_++);
153
+ if (c == '"') break;
154
+ if (c < 0x20) {
155
+ i_--;
156
+ unexpected();
157
+ }
158
+ if (c != '\\') {
159
+ out.push_back(c);
160
+ continue;
161
+ }
162
+ if (i_ >= n_) fail("Unterminated string");
163
+ char16_t e = at(i_++);
164
+ switch (e) {
165
+ case '"':
166
+ case '\\':
167
+ case '/':
168
+ out.push_back(e);
169
+ break;
170
+ case 'b':
171
+ out.push_back(u'\b');
172
+ break;
173
+ case 'f':
174
+ out.push_back(u'\f');
175
+ break;
176
+ case 'n':
177
+ out.push_back(u'\n');
178
+ break;
179
+ case 'r':
180
+ out.push_back(u'\r');
181
+ break;
182
+ case 't':
183
+ out.push_back(u'\t');
184
+ break;
185
+ case 'u': {
186
+ int code = 0;
187
+ for (int k = 0; k < 4; k++) {
188
+ int h = i_ < n_ ? hex(at(i_)) : -1;
189
+ if (h < 0) fail("Invalid unicode escape");
190
+ code = code * 16 + h;
191
+ i_++;
192
+ }
193
+ out.push_back(static_cast<char16_t>(code));
194
+ break;
195
+ }
196
+ default:
197
+ i_ -= 2;
198
+ fail("Invalid escape sequence");
199
+ }
200
+ }
201
+ return String::fromUtf16(out);
202
+ }
203
+
204
+ const String& s_;
205
+ size_t n_;
206
+ size_t i_ = 0;
207
+ };
208
+
209
+ } // namespace
210
+
211
+ const JsonValue* JsonValue::find(const String& key) const {
212
+ for (auto it = members.rbegin(); it != members.rend(); ++it) {
213
+ if (it->key == key) return &it->value;
214
+ }
215
+ return nullptr;
216
+ }
217
+
218
+ const char* JsonValue::describe() const {
219
+ switch (kind) {
220
+ case Kind::Null:
221
+ return "null";
222
+ case Kind::Bool:
223
+ return "a boolean";
224
+ case Kind::Number:
225
+ return "a number";
226
+ case Kind::String:
227
+ return "a string";
228
+ case Kind::Array:
229
+ return "an array";
230
+ case Kind::Object:
231
+ return "an object";
232
+ }
233
+ return "a value";
234
+ }
235
+
236
+ JsonValue jsonParseTree(const String& text) { return Parser(text).parse(); }
237
+
238
+ void jsonShapeError(const std::string& path, const char* expected, const char* got) {
239
+ std::string where = path.empty() ? "" : " at " + path;
240
+ throwTypeError(("JSON.parse: expected " + std::string(expected) + where + ", got " + got).c_str());
241
+ }
242
+
243
+ } // namespace lucent
@@ -0,0 +1,164 @@
1
+ // Lucent runtime — JSON.stringify for Lucent values. Generated code adds
2
+ // `jsonWrite` overloads for each struct and class (found by ADL).
3
+ #pragma once
4
+
5
+ #include <cmath>
6
+ #include <string>
7
+ #include <tuple>
8
+ #include <variant>
9
+
10
+ #include "array.h"
11
+ #include "bytes.h"
12
+ #include "date.h"
13
+ #include "regexp.h"
14
+ #include "core.h"
15
+ #include "jserror.h"
16
+ #include "function.h"
17
+ #include "map.h"
18
+ #include "number.h"
19
+ #include "jsstring.h"
20
+
21
+ namespace lucent {
22
+
23
+ struct JsonWriter {
24
+ std::u16string out;
25
+ void raw(const char* s) {
26
+ while (*s) out.push_back(static_cast<unsigned char>(*s++));
27
+ }
28
+ void quote(const String& s);
29
+ };
30
+
31
+ /// Whether a value is omitted from objects (undefined, functions).
32
+ template <class T>
33
+ bool jsonOmitted(const T&) {
34
+ return false;
35
+ }
36
+ inline bool jsonOmitted(Undefined) { return true; }
37
+ template <class T>
38
+ bool jsonOmitted(const Opt<T>& v) {
39
+ return v.isUndefined();
40
+ }
41
+ template <class Sig>
42
+ bool jsonOmitted(const Fn<Sig>&) {
43
+ return true;
44
+ }
45
+
46
+ inline void jsonWrite(JsonWriter& w, double v) {
47
+ if (std::isfinite(v)) {
48
+ w.out.append(numberToString(v).toUtf16());
49
+ } else {
50
+ w.raw("null");
51
+ }
52
+ }
53
+ inline void jsonWrite(JsonWriter& w, bool v) { w.raw(v ? "true" : "false"); }
54
+ inline void jsonWrite(JsonWriter& w, const String& s) { w.quote(s); }
55
+ inline void jsonWrite(JsonWriter& w, Undefined) { w.raw("null"); }
56
+ inline void jsonWrite(JsonWriter& w, Null) { w.raw("null"); }
57
+ inline void jsonWrite(JsonWriter& w, const Error&) { w.raw("{}"); }
58
+ template <class Sig>
59
+ void jsonWrite(JsonWriter& w, const Fn<Sig>&) {
60
+ w.raw("null");
61
+ }
62
+ template <class T>
63
+ void jsonWrite(JsonWriter& w, const Opt<T>& v) {
64
+ if (v.has()) jsonWrite(w, v.get());
65
+ else w.raw("null");
66
+ }
67
+ template <class... Ts>
68
+ void jsonWrite(JsonWriter& w, const std::variant<Ts...>& v) {
69
+ std::visit([&](const auto& x) { jsonWrite(w, x); }, v);
70
+ }
71
+ template <class T>
72
+ void jsonWrite(JsonWriter& w, const Array<T>& a) {
73
+ w.raw("[");
74
+ for (size_t i = 0; i < a.size(); i++) {
75
+ if (i) w.raw(",");
76
+ T v = a.at(i);
77
+ if (jsonOmitted(v)) w.raw("null");
78
+ else jsonWrite(w, v);
79
+ }
80
+ w.raw("]");
81
+ }
82
+ template <class... Ts>
83
+ void jsonWrite(JsonWriter& w, const std::tuple<Ts...>& t) {
84
+ w.raw("[");
85
+ size_t i = 0;
86
+ std::apply(
87
+ [&](const auto&... x) {
88
+ ((w.raw(i++ ? "," : ""), jsonOmitted(x) ? w.raw("null") : jsonWrite(w, x)), ...);
89
+ },
90
+ t);
91
+ w.raw("]");
92
+ }
93
+ template <class V>
94
+ void jsonWrite(JsonWriter& w, const Dict<V>& d) {
95
+ w.raw("{");
96
+ bool first = true;
97
+ Array<String> ks = d.keys();
98
+ for (const auto& k : ks.items()) {
99
+ V v = d.get(k).get();
100
+ if (jsonOmitted(v)) continue;
101
+ if (!first) w.raw(",");
102
+ first = false;
103
+ w.quote(k);
104
+ w.raw(":");
105
+ jsonWrite(w, v);
106
+ }
107
+ w.raw("}");
108
+ }
109
+ template <class K, class V>
110
+ void jsonWrite(JsonWriter& w, const Map<K, V>&) {
111
+ w.raw("{}");
112
+ }
113
+ template <class T>
114
+ void jsonWrite(JsonWriter& w, const Set<T>&) {
115
+ w.raw("{}");
116
+ }
117
+ /// RegExp objects have no enumerable own properties; match results are arrays.
118
+ inline void jsonWrite(JsonWriter& w, const RegExp&) { w.raw("{}"); }
119
+ inline void jsonWrite(JsonWriter& w, const RegExpMatch& m) { jsonWrite(w, m->items); }
120
+
121
+ /// Date.prototype.toJSON: the ISO string, or null for an invalid date.
122
+ inline void jsonWrite(JsonWriter& w, const Date& d) {
123
+ if (std::isnan(d->getTime())) w.raw("null");
124
+ else w.quote(d->toISOString());
125
+ }
126
+ inline void jsonWrite(JsonWriter& w, const Bytes& b) {
127
+ w.raw("{");
128
+ for (size_t i = 0; i < b.size(); i++) {
129
+ if (i) w.raw(",");
130
+ w.quote(numberToString(static_cast<double>(i)));
131
+ w.raw(":");
132
+ jsonWrite(w, b.at(i));
133
+ }
134
+ w.raw("}");
135
+ }
136
+ /// Helper for generated struct writers: one `"name":value` member.
137
+ template <class T>
138
+ void jsonField(JsonWriter& w, bool& first, const char* name, const T& v) {
139
+ if (jsonOmitted(v)) return;
140
+ if (!first) w.raw(",");
141
+ first = false;
142
+ w.quote(String::fromUtf8(name));
143
+ w.raw(":");
144
+ jsonWrite(w, v);
145
+ }
146
+
147
+ namespace json {
148
+ template <class T>
149
+ Opt<String> stringifyOpt(const T& v) {
150
+ if (jsonOmitted(v)) return undefined;
151
+ JsonWriter w;
152
+ jsonWrite(w, v);
153
+ return String::fromUtf16(w.out);
154
+ }
155
+ template <class T>
156
+ String stringify(const T& v) {
157
+ JsonWriter w;
158
+ if (jsonOmitted(v)) return String::fromLatin1("undefined");
159
+ jsonWrite(w, v);
160
+ return String::fromUtf16(w.out);
161
+ }
162
+ } // namespace json
163
+
164
+ } // namespace lucent
@@ -0,0 +1,138 @@
1
+ // Lucent runtime — JSON.parse into typed values.
2
+ //
3
+ // The text is parsed into a JsonValue tree (SyntaxError on invalid JSON),
4
+ // then JsonRead<T> builds the typed value, throwing a TypeError that names
5
+ // the path when the shape does not match the type. The compiler generates
6
+ // JsonRead specializations for object types and unions.
7
+ #pragma once
8
+
9
+ #include <string>
10
+ #include <tuple>
11
+ #include <utility>
12
+ #include <vector>
13
+
14
+ #include "array.h"
15
+ #include "core.h"
16
+ #include "jsstring.h"
17
+ #include "map.h"
18
+
19
+ namespace lucent {
20
+
21
+ struct JsonMember;
22
+
23
+ struct JsonValue {
24
+ enum class Kind : uint8_t { Null, Bool, Number, String, Array, Object };
25
+ Kind kind = Kind::Null;
26
+ bool boolean = false;
27
+ double number = 0;
28
+ String string;
29
+ std::vector<JsonValue> items;
30
+ // A named struct, not std::pair: pair needs JsonValue complete here.
31
+ std::vector<JsonMember> members;
32
+
33
+ /// The member named `key`; the last one wins when a key repeats.
34
+ const JsonValue* find(const String& key) const;
35
+ /// "a number", "an object", … for error messages.
36
+ const char* describe() const;
37
+ };
38
+
39
+ struct JsonMember {
40
+ String key;
41
+ JsonValue value;
42
+ };
43
+
44
+ /// Parses JSON text; throws SyntaxError.
45
+ JsonValue jsonParseTree(const String& text);
46
+
47
+ /// TypeError: `JSON.parse: expected <expected> at <path>, got <what>`.
48
+ [[noreturn]] void jsonShapeError(const std::string& path, const char* expected, const char* got);
49
+
50
+ template <class T>
51
+ struct JsonRead;
52
+
53
+ template <>
54
+ struct JsonRead<double> {
55
+ static double read(const JsonValue& v, const std::string& p) {
56
+ if (v.kind != JsonValue::Kind::Number) jsonShapeError(p, "a number", v.describe());
57
+ return v.number;
58
+ }
59
+ };
60
+ template <>
61
+ struct JsonRead<bool> {
62
+ static bool read(const JsonValue& v, const std::string& p) {
63
+ if (v.kind != JsonValue::Kind::Bool) jsonShapeError(p, "a boolean", v.describe());
64
+ return v.boolean;
65
+ }
66
+ };
67
+ template <>
68
+ struct JsonRead<String> {
69
+ static String read(const JsonValue& v, const std::string& p) {
70
+ if (v.kind != JsonValue::Kind::String) jsonShapeError(p, "a string", v.describe());
71
+ return v.string;
72
+ }
73
+ };
74
+ template <>
75
+ struct JsonRead<Null> {
76
+ static Null read(const JsonValue& v, const std::string& p) {
77
+ if (v.kind != JsonValue::Kind::Null) jsonShapeError(p, "null", v.describe());
78
+ return null;
79
+ }
80
+ };
81
+ template <class T>
82
+ struct JsonRead<Opt<T>> {
83
+ static Opt<T> read(const JsonValue& v, const std::string& p) {
84
+ if (v.kind == JsonValue::Kind::Null) return Opt<T>(null);
85
+ return Opt<T>(JsonRead<T>::read(v, p));
86
+ }
87
+ };
88
+ template <class T>
89
+ struct JsonRead<Array<T>> {
90
+ static Array<T> read(const JsonValue& v, const std::string& p) {
91
+ if (v.kind != JsonValue::Kind::Array) jsonShapeError(p, "an array", v.describe());
92
+ Array<T> out;
93
+ for (size_t i = 0; i < v.items.size(); i++) out.push(JsonRead<T>::read(v.items[i], p + "[" + std::to_string(i) + "]"));
94
+ return out;
95
+ }
96
+ };
97
+ template <class V>
98
+ struct JsonRead<Dict<V>> {
99
+ static Dict<V> read(const JsonValue& v, const std::string& p) {
100
+ if (v.kind != JsonValue::Kind::Object) jsonShapeError(p, "an object", v.describe());
101
+ Dict<V> out;
102
+ for (const auto& [k, m] : v.members) out.set(k, JsonRead<V>::read(m, p + "." + k.toUtf8()));
103
+ return out;
104
+ }
105
+ };
106
+ template <class... Ts>
107
+ struct JsonRead<std::tuple<Ts...>> {
108
+ static std::tuple<Ts...> read(const JsonValue& v, const std::string& p) {
109
+ if (v.kind != JsonValue::Kind::Array || v.items.size() < sizeof...(Ts)) jsonShapeError(p, "a longer array", v.describe());
110
+ return build(v, p, std::index_sequence_for<Ts...>{});
111
+ }
112
+ template <size_t... I>
113
+ static std::tuple<Ts...> build(const JsonValue& v, const std::string& p, std::index_sequence<I...>) {
114
+ return std::tuple<Ts...>{JsonRead<Ts>::read(v.items[I], p + "[" + std::to_string(I) + "]")...};
115
+ }
116
+ };
117
+
118
+ /// One member of an object type (used by generated readers). A missing
119
+ /// member is undefined for optional types and an error otherwise.
120
+ template <class T>
121
+ T jsonMember(const JsonValue& obj, const char* key, const std::string& path) {
122
+ const JsonValue* m = obj.find(String::fromUtf8(key));
123
+ std::string at = path + "." + key;
124
+ if (!m) {
125
+ if constexpr (IsOpt<T>::value) return T(undefined);
126
+ else jsonShapeError(at, "a value", "undefined");
127
+ }
128
+ return JsonRead<T>::read(*m, at);
129
+ }
130
+
131
+ /// `JSON.parse(text) as T`.
132
+ template <class T>
133
+ T jsonParse(const String& text) {
134
+ JsonValue v = jsonParseTree(text);
135
+ return JsonRead<T>::read(v, "");
136
+ }
137
+
138
+ } // namespace lucent