firefly-compiler 0.5.39 → 0.5.40

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 (129) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +157 -157
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +275 -275
  6. package/compiler/Compiler.ff +234 -234
  7. package/compiler/Dependencies.ff +186 -186
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/JsEmitter.ff +1437 -1437
  10. package/compiler/LspHook.ff +202 -202
  11. package/compiler/ModuleCache.ff +178 -178
  12. package/compiler/Workspace.ff +88 -88
  13. package/core/.firefly/include/package.json +5 -5
  14. package/core/.firefly/package.ff +2 -2
  15. package/core/Any.ff +25 -25
  16. package/core/Array.ff +298 -298
  17. package/core/Atomic.ff +63 -63
  18. package/core/Box.ff +7 -7
  19. package/core/BrowserSystem.ff +40 -40
  20. package/core/BuildSystem.ff +156 -156
  21. package/core/Crypto.ff +94 -94
  22. package/core/Equal.ff +41 -41
  23. package/core/Error.ff +25 -25
  24. package/core/HttpClient.ff +142 -142
  25. package/core/Instant.ff +24 -24
  26. package/core/Js.ff +305 -305
  27. package/core/JsSystem.ff +135 -135
  28. package/core/Json.ff +423 -423
  29. package/core/List.ff +482 -482
  30. package/core/Lock.ff +108 -108
  31. package/core/NodeSystem.ff +198 -198
  32. package/core/Ordering.ff +160 -160
  33. package/core/Path.ff +377 -378
  34. package/core/Queue.ff +90 -90
  35. package/core/Random.ff +140 -140
  36. package/core/RbMap.ff +216 -216
  37. package/core/Show.ff +44 -44
  38. package/core/SourceLocation.ff +68 -68
  39. package/core/Task.ff +165 -165
  40. package/experimental/benchmarks/ListGrab.ff +23 -23
  41. package/experimental/benchmarks/ListGrab.java +55 -55
  42. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  43. package/experimental/benchmarks/Pyrotek45.java +64 -64
  44. package/experimental/bidirectional/Bidi.ff +88 -88
  45. package/experimental/lines/Main.ff +40 -40
  46. package/experimental/random/Index.ff +53 -53
  47. package/experimental/random/Process.ff +120 -120
  48. package/experimental/random/RunLength.ff +65 -65
  49. package/experimental/random/Scrape.ff +51 -51
  50. package/experimental/random/Symbols.ff +73 -73
  51. package/experimental/random/Tensor.ff +52 -52
  52. package/experimental/random/Units.ff +36 -36
  53. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  54. package/experimental/s3/S3TestPut.ff +16 -16
  55. package/experimental/tests/TestJson.ff +26 -26
  56. package/firefly.sh +0 -0
  57. package/fireflysite/.firefly/package.ff +4 -4
  58. package/fireflysite/CommunityOverview.ff +20 -20
  59. package/fireflysite/CountingButtonDemo.ff +58 -58
  60. package/fireflysite/DocumentParser.ff +325 -325
  61. package/fireflysite/ExamplesOverview.ff +40 -40
  62. package/fireflysite/FrontPage.ff +344 -344
  63. package/fireflysite/GettingStarted.ff +45 -45
  64. package/fireflysite/Guide.ff +456 -456
  65. package/fireflysite/Main.ff +163 -163
  66. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  67. package/fireflysite/PackagesOverview.ff +49 -49
  68. package/fireflysite/PostgresqlDemo.ff +34 -34
  69. package/fireflysite/ReferenceAll.ff +18 -18
  70. package/fireflysite/ReferenceIntroduction.ff +11 -11
  71. package/fireflysite/Styles.ff +567 -567
  72. package/fireflysite/Test.ff +121 -121
  73. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
  74. package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
  75. package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
  76. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
  77. package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -235
  78. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
  79. package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
  80. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
  81. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
  82. package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
  83. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
  84. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
  85. package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
  86. package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
  87. package/lsp/.firefly/package.ff +1 -1
  88. package/lsp/CompletionHandler.ff +827 -827
  89. package/lsp/Handler.ff +714 -714
  90. package/lsp/HoverHandler.ff +79 -79
  91. package/lsp/LanguageServer.ff +272 -272
  92. package/lsp/SignatureHelpHandler.ff +55 -55
  93. package/lsp/SymbolHandler.ff +181 -181
  94. package/lsp/TestReferences.ff +17 -17
  95. package/lsp/TestReferencesCase.ff +7 -7
  96. package/lsp/stderr.txt +1 -1
  97. package/lsp/stdout.txt +34 -34
  98. package/lux/.firefly/package.ff +1 -1
  99. package/lux/Css.ff +648 -648
  100. package/lux/CssTest.ff +48 -48
  101. package/lux/Lux.ff +608 -608
  102. package/lux/LuxEvent.ff +79 -79
  103. package/lux/Main.ff +123 -123
  104. package/lux/Main2.ff +143 -143
  105. package/lux/TestDry.ff +28 -28
  106. package/output/js/ff/compiler/Builder.mjs +36 -36
  107. package/output/js/ff/core/Path.mjs +0 -2
  108. package/package.json +1 -1
  109. package/rpc/.firefly/package.ff +1 -1
  110. package/rpc/Rpc.ff +70 -70
  111. package/s3/.firefly/package.ff +1 -1
  112. package/s3/S3.ff +92 -92
  113. package/vscode/LICENSE.txt +21 -21
  114. package/vscode/Prepublish.ff +15 -15
  115. package/vscode/README.md +16 -16
  116. package/vscode/client/package-lock.json +544 -544
  117. package/vscode/client/package.json +22 -22
  118. package/vscode/client/src/extension.ts +104 -104
  119. package/vscode/icons/firefly-icon.svg +10 -10
  120. package/vscode/language-configuration.json +61 -61
  121. package/vscode/package-lock.json +3623 -3623
  122. package/vscode/package.json +1 -1
  123. package/vscode/snippets.json +241 -241
  124. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  125. package/webserver/.firefly/include/package.json +5 -5
  126. package/webserver/.firefly/package.ff +2 -2
  127. package/webserver/WebServer.ff +647 -647
  128. package/websocket/.firefly/package.ff +1 -1
  129. package/websocket/WebSocket.ff +100 -100
package/core/Json.ff CHANGED
@@ -1,423 +1,423 @@
1
- newtype Json(jsValue: JsValue)
2
-
3
- read(json: String): Option[Json] {
4
- try {
5
- Some(Json(Js->JSON->parse(json)))
6
- } catchAny {_ =>
7
- None
8
- }
9
- }
10
-
11
- string(json: String): Json {
12
- Json(json!)
13
- }
14
-
15
- int(json: Int): Json {
16
- Json(json!)
17
- }
18
-
19
- float(json: Float): Json {
20
- Json(json!)
21
- }
22
-
23
- bool(json: Bool): Json {
24
- Json(json!)
25
- }
26
-
27
- null(): Json {
28
- Json(Js.null())
29
- }
30
-
31
- array(json: List[Json]): Json {
32
- Json(json!)
33
- }
34
-
35
- object(): Json {
36
- Json(Js->())
37
- }
38
-
39
- new0(): Json {
40
- Json(Js->())
41
- }
42
-
43
- fields(body: ((String, Json) => Unit) => Unit): Json {
44
- let result = Js->()
45
- body {k, v => result.set(k, v.jsValue)}
46
- Json(result)
47
- }
48
-
49
- extend self: Json {
50
-
51
- write(indentation: Option[String] = None): String {
52
- Js->JSON->stringify(self.jsValue, Js.null(), Js.orUndefined(indentation))?
53
- }
54
-
55
- with[T: JsonLike](field: String, value: T): Json {
56
- if(!self.isObject()) {
57
- Js.throw(Js->Error->("Not an object: " + self.write()))
58
- }
59
- Json(self.jsValue.with(field, toJson(value).jsValue))
60
- }
61
-
62
- merge(that: Json): Json {
63
- if(!self.isObject()) {
64
- Js.throw(Js->Error->("Not an object: " + self.write()))
65
- }
66
- if(!that.isObject()) {
67
- Js.throw(Js->Error->("Not an object: " + that.write()))
68
- }
69
- Json(Js.object().assign(self.jsValue, that.jsValue))
70
- }
71
-
72
- grabString(): String {
73
- if(!self.isString()) {
74
- Js.throw(Js->Error->("Not a String: " + self.write()))
75
- }
76
- self.jsValue?
77
- }
78
-
79
- grabInt(): Int {
80
- if(!self.isInt()) {
81
- Js.throw(Js->Error->("Not an Int: " + self.write()))
82
- }
83
- self.jsValue?
84
- }
85
-
86
- grabFloat(): Float {
87
- if(!self.isFloat()) {
88
- Js.throw(Js->Error->("Not a Float: " + self.write()))
89
- }
90
- self.jsValue?
91
- }
92
-
93
- grabBool(): Bool {
94
- if(!self.isBool()) {
95
- Js.throw(Js->Error->("Not a Bool: " + self.write()))
96
- }
97
- self.jsValue?
98
- }
99
-
100
- grabArray(): List[Json] {
101
- if(!self.isArray()) {
102
- Js.throw(Js->Error->("Not an array: " + self.write()))
103
- }
104
- self.jsValue?
105
- }
106
-
107
- grabMap(): Map[String, Json] {
108
- mutable map = Map.new()
109
- self.each {key, value =>
110
- map = map.add(key, value)
111
- }
112
- map
113
- }
114
-
115
- getString(): Option[String] {
116
- if(self.isString()) {self.grabString()}
117
- }
118
-
119
- getInt(): Option[Int] {
120
- if(self.isInt()) {self.grabInt()}
121
- }
122
-
123
- getFloat(): Option[Float] {
124
- if(self.isFloat()) {self.grabFloat()}
125
- }
126
-
127
- getBool(): Option[Bool] {
128
- if(self.isBool()) {self.grabBool()}
129
- }
130
-
131
- getArray(): Option[List[Json]] {
132
- if(self.isArray()) {self.grabArray()}
133
- }
134
-
135
- getMap(): Option[Map[String, Json]] {
136
- if(self.isObject()) {self.grabMap()}
137
- }
138
-
139
- isString(): Bool {
140
- self.jsValue.typeof() == "string"
141
- }
142
-
143
- isInt(): Bool {
144
- Js->Number->isSafeInteger(self.jsValue)?
145
- }
146
-
147
- isFloat(): Bool {
148
- self.jsValue.typeof() == "number"
149
- }
150
-
151
- isBool(): Bool {
152
- self.jsValue.typeof() == "boolean"
153
- }
154
-
155
- isArray(): Bool {
156
- Js->Array->isArray(self.jsValue)?
157
- }
158
-
159
- isObject(): Bool {
160
- self.jsValue.typeof() == "object" && !self.jsValue.isNull() && !self.jsValue.isArray()
161
- }
162
-
163
- isNull(): Bool {
164
- self.jsValue.isNull()
165
- }
166
-
167
- get(key: String): Json {
168
- self.field(key)
169
- }
170
-
171
- field(key: String): Json {
172
- if(self.isObject() && self.jsValue.hasOwn(key)) {
173
- Json(self.jsValue.get(key))
174
- } else {
175
- Json(Js.null())
176
- }
177
- }
178
-
179
- index(key: Int): Json {
180
- if(self.isArray()) {
181
- Json(self.jsValue.get(key).coalesce(Js.null()))
182
- } else {
183
- Json(Js.null())
184
- }
185
- }
186
-
187
- hasField(key: String): Bool {
188
- self.isObject() && self.jsValue.hasOwn(key)
189
- }
190
-
191
- getField(key: String): Option[Json] {
192
- if(self.hasField(key)) {self.get(key)}
193
- }
194
-
195
- getIndex(key: Int): Option[Json] {
196
- if(self.isArray() && !self.jsValue.get(key).isUndefined()) {
197
- Json(self.jsValue.get(key))
198
- }
199
- }
200
-
201
- getFields(): Option[List[String]] {
202
- if(self.isObject()) {
203
- Js->Object->keys(self.jsValue)?
204
- }
205
- }
206
-
207
- grabField(key: String): Json {
208
- self.getField(key).else {
209
- throw(GrabException())
210
- }
211
- }
212
-
213
- grabIndex(key: Int): Json {
214
- self.getIndex(key).else {
215
- throw(GrabException())
216
- }
217
- }
218
-
219
- grabFields(): List[String] {
220
- self.getFields().else {
221
- throw(GrabException())
222
- }
223
- }
224
-
225
- map[T](body: (String, Json) => T): List[T] {
226
- let array = Array.new()
227
- self.each {field, value => array.push(body(field, value))}
228
- array.drain()
229
- }
230
-
231
- flatMap[T](body: (String, Json) => List[T]): List[T] {
232
- let array = Array.new()
233
- self.each {field, value => array.pushList(body(field, value))}
234
- array.drain()
235
- }
236
-
237
- each(body: (String, Json) => Unit) {
238
- if(!self.isObject()) {throw(GrabException())}
239
- Js->Object->entries(self.jsValue).each {p =>
240
- body(p.get(0)?, p.get(1)?)
241
- }
242
- }
243
-
244
- eachWhile(body: (String, Json) => Bool) {
245
- if(!self.isObject()) {throw(GrabException())}
246
- Js->Object->entries(self.jsValue).eachWhile {p =>
247
- body(p.get(0)?, p.get(1)?)
248
- }
249
- }
250
-
251
- }
252
-
253
- trait T: JsonLike {
254
- toJson(value: T): Json
255
- fromJson(json: Json): Option[T]
256
- }
257
-
258
- instance Json: JsonLike {
259
- toJson(value: Json): Json {value}
260
- fromJson(json: Json): Option[Json] {Some(json)}
261
- }
262
-
263
- instance String: JsonLike {
264
- toJson(value: String): Json {string(value)}
265
- fromJson(json: Json): Option[String] {_.getString()}
266
- }
267
-
268
- instance Int: JsonLike {
269
- toJson(value: Int): Json {int(value)}
270
- fromJson(json: Json): Option[Int] {_.getInt()}
271
- }
272
-
273
- instance Float: JsonLike {
274
- toJson(value: Float): Json {float(value)}
275
- fromJson(json: Json): Option[Float] {_.getFloat()}
276
- }
277
-
278
- instance Bool: JsonLike {
279
- toJson(value: Bool): Json {bool(value)}
280
- fromJson(json: Json): Option[Bool] {_.getBool()}
281
- }
282
-
283
- instance List[T: JsonLike]: JsonLike {
284
- toJson(value: List[T]): Json {array(value.map(toJson))}
285
- fromJson(json: Json): Option[List[T]] {
286
- json.getArray().flatMap {array =>
287
- mutable convertible = True
288
- let result = Array.new()
289
- array.eachWhile {item =>
290
- fromJson(item).{
291
- | None => convertible = False
292
- | Some(value) => result.push(value)
293
- }
294
- convertible
295
- }
296
- if(convertible) {result.drain()}
297
- }
298
- }
299
- }
300
-
301
- instance StringMap[T: JsonLike]: JsonLike {
302
- toJson(value: StringMap[T]): Json {
303
- Json.fields {setField =>
304
- value.each {key, value => setField(key, toJson(value))}
305
- }
306
- }
307
- fromJson(json: Json): Option[StringMap[T]] {
308
- if(json.isObject()) {
309
- let map = StringMap.new()
310
- mutable convertible = True
311
- json.eachWhile {key, value =>
312
- fromJson(value).{
313
- | None => convertible = False
314
- | Some(v) => map.set(key, v)
315
- }
316
- convertible
317
- }
318
- if(convertible) {map}
319
- }.flatten()
320
- }
321
- }
322
-
323
-
324
- instance Json: HasAnyTag {
325
- anyTag(): AnyTag[Json] {Any.internalAnyTag("ff:core/Json.Json[]")}
326
- }
327
-
328
- instance Json: Show {
329
- show(value: Json): String {value.write(Some(" "))}
330
- }
331
-
332
- instance Json: Equal {
333
- equals(a: Json, b: Json): Bool {
334
- internalEquals(a, b)
335
- }
336
- }
337
-
338
- instance Json: Order {
339
- compare(a: Json, b: Json): Ordering {
340
- Ordering.fromInt(internalCompare(a, b))
341
- }
342
- }
343
-
344
- instance Json: Serializable {
345
- serializeUsing(serialization: Serialization, value: Json): Unit {
346
- Serializable.serializeUsing(serialization, value.write())
347
- }
348
- deserializeUsing(serialization: Serialization): Json {
349
- Json.read(Serializable.deserializeUsing[String](serialization)).grab()
350
- }
351
- }
352
-
353
-
354
- internalEquals(a: Json, b: Json): Bool {
355
- if(a.jsValue === b.jsValue) {True} else:
356
- if(a.jsValue.isArray() || b.jsValue.isArray()) {
357
- if(!a.jsValue.isArray() || !b.jsValue.isArray()) {False} else:
358
- if(a.jsValue->length !== b.jsValue->length) {False} else:
359
- mutable equal = True
360
- 0.until(a.jsValue->length?).eachWhile {i =>
361
- equal = internalEquals(Json(a.jsValue.get(i)), Json(b.jsValue.get(i)))
362
- equal
363
- }
364
- equal
365
- } else:
366
- if(a.isObject() && b.isObject()) {
367
- let aKeys = Js->Object->keys(a.jsValue)
368
- let bKeys = Js->Object->keys(b.jsValue)
369
- if(aKeys->length !== bKeys->length) {False} else:
370
- mutable equal = True
371
- aKeys.eachWhile {key =>
372
- equal = b.jsValue.hasOwn(key?) && internalEquals(Json(a.jsValue.get(key)), Json(b.jsValue.get(key)))
373
- equal
374
- }
375
- equal
376
- } else:
377
- False
378
- }
379
-
380
- internalCompare(a: Json, b: Json): Int {
381
- if(a.jsValue === b.jsValue) {0} else:
382
- if(a.jsValue === Js.null() || b.jsValue === Js.null()) {
383
- if(a.jsValue === Js.null()) {-1} else {1}
384
- } else:
385
- if(a.isBool() || b.isBool()) {
386
- if(!b.isBool() || b.jsValue?) {-1} else {1}
387
- } else:
388
- if(a.isFloat() || b.isFloat()) {
389
- if(!b.isFloat()) {-1} else:
390
- if(!a.isFloat()) {1} else:
391
- if(a.jsValue.isNan()) {if(b.jsValue.isNan()) {0} else {-1}} else:
392
- if(b.jsValue.isNan()) {1} else:
393
- if(a.jsValue? < b.jsValue?) {-1} else {1}
394
- } else:
395
- if(a.isString() || b.isString()) {
396
- if(!b.isString()) {-1} else:
397
- if(!a.isString()) {1} else:
398
- a.jsValue->localeCompare(b.jsValue, "en")?
399
- } else:
400
- if(a.isArray() || b.isArray()) {
401
- if(!b.isArray()) {-1} else:
402
- if(!a.isArray()) {1} else:
403
- let aLength: Int = a.jsValue->length?
404
- let bLength: Int = b.jsValue->length?
405
- mutable result = 0
406
- 0.until(aLength.min(bLength)).eachWhile {i =>
407
- result = internalCompare(Json(a.jsValue.get(i)), Json(b.jsValue.get(i)))
408
- result == 0
409
- }
410
- if(result != 0) {result} else:
411
- aLength - bLength
412
- } else:
413
- let aKeys = Js->Object->keys(a.jsValue)->sort()
414
- let bKeys = Js->Object->keys(b.jsValue)->sort()
415
- let keyResult = internalCompare(Json(aKeys), Json(bKeys))
416
- if(keyResult != 0) {keyResult} else:
417
- mutable result = 0
418
- aKeys.eachWhile {key =>
419
- result = internalCompare(Json(a.jsValue.get(key)), Json(b.jsValue.get(key)))
420
- result == 0
421
- }
422
- result
423
- }
1
+ newtype Json(jsValue: JsValue)
2
+
3
+ read(json: String): Option[Json] {
4
+ try {
5
+ Some(Json(Js->JSON->parse(json)))
6
+ } catchAny {_ =>
7
+ None
8
+ }
9
+ }
10
+
11
+ string(json: String): Json {
12
+ Json(json!)
13
+ }
14
+
15
+ int(json: Int): Json {
16
+ Json(json!)
17
+ }
18
+
19
+ float(json: Float): Json {
20
+ Json(json!)
21
+ }
22
+
23
+ bool(json: Bool): Json {
24
+ Json(json!)
25
+ }
26
+
27
+ null(): Json {
28
+ Json(Js.null())
29
+ }
30
+
31
+ array(json: List[Json]): Json {
32
+ Json(json!)
33
+ }
34
+
35
+ object(): Json {
36
+ Json(Js->())
37
+ }
38
+
39
+ new0(): Json {
40
+ Json(Js->())
41
+ }
42
+
43
+ fields(body: ((String, Json) => Unit) => Unit): Json {
44
+ let result = Js->()
45
+ body {k, v => result.set(k, v.jsValue)}
46
+ Json(result)
47
+ }
48
+
49
+ extend self: Json {
50
+
51
+ write(indentation: Option[String] = None): String {
52
+ Js->JSON->stringify(self.jsValue, Js.null(), Js.orUndefined(indentation))?
53
+ }
54
+
55
+ with[T: JsonLike](field: String, value: T): Json {
56
+ if(!self.isObject()) {
57
+ Js.throw(Js->Error->("Not an object: " + self.write()))
58
+ }
59
+ Json(self.jsValue.with(field, toJson(value).jsValue))
60
+ }
61
+
62
+ merge(that: Json): Json {
63
+ if(!self.isObject()) {
64
+ Js.throw(Js->Error->("Not an object: " + self.write()))
65
+ }
66
+ if(!that.isObject()) {
67
+ Js.throw(Js->Error->("Not an object: " + that.write()))
68
+ }
69
+ Json(Js.object().assign(self.jsValue, that.jsValue))
70
+ }
71
+
72
+ grabString(): String {
73
+ if(!self.isString()) {
74
+ Js.throw(Js->Error->("Not a String: " + self.write()))
75
+ }
76
+ self.jsValue?
77
+ }
78
+
79
+ grabInt(): Int {
80
+ if(!self.isInt()) {
81
+ Js.throw(Js->Error->("Not an Int: " + self.write()))
82
+ }
83
+ self.jsValue?
84
+ }
85
+
86
+ grabFloat(): Float {
87
+ if(!self.isFloat()) {
88
+ Js.throw(Js->Error->("Not a Float: " + self.write()))
89
+ }
90
+ self.jsValue?
91
+ }
92
+
93
+ grabBool(): Bool {
94
+ if(!self.isBool()) {
95
+ Js.throw(Js->Error->("Not a Bool: " + self.write()))
96
+ }
97
+ self.jsValue?
98
+ }
99
+
100
+ grabArray(): List[Json] {
101
+ if(!self.isArray()) {
102
+ Js.throw(Js->Error->("Not an array: " + self.write()))
103
+ }
104
+ self.jsValue?
105
+ }
106
+
107
+ grabMap(): Map[String, Json] {
108
+ mutable map = Map.new()
109
+ self.each {key, value =>
110
+ map = map.add(key, value)
111
+ }
112
+ map
113
+ }
114
+
115
+ getString(): Option[String] {
116
+ if(self.isString()) {self.grabString()}
117
+ }
118
+
119
+ getInt(): Option[Int] {
120
+ if(self.isInt()) {self.grabInt()}
121
+ }
122
+
123
+ getFloat(): Option[Float] {
124
+ if(self.isFloat()) {self.grabFloat()}
125
+ }
126
+
127
+ getBool(): Option[Bool] {
128
+ if(self.isBool()) {self.grabBool()}
129
+ }
130
+
131
+ getArray(): Option[List[Json]] {
132
+ if(self.isArray()) {self.grabArray()}
133
+ }
134
+
135
+ getMap(): Option[Map[String, Json]] {
136
+ if(self.isObject()) {self.grabMap()}
137
+ }
138
+
139
+ isString(): Bool {
140
+ self.jsValue.typeof() == "string"
141
+ }
142
+
143
+ isInt(): Bool {
144
+ Js->Number->isSafeInteger(self.jsValue)?
145
+ }
146
+
147
+ isFloat(): Bool {
148
+ self.jsValue.typeof() == "number"
149
+ }
150
+
151
+ isBool(): Bool {
152
+ self.jsValue.typeof() == "boolean"
153
+ }
154
+
155
+ isArray(): Bool {
156
+ Js->Array->isArray(self.jsValue)?
157
+ }
158
+
159
+ isObject(): Bool {
160
+ self.jsValue.typeof() == "object" && !self.jsValue.isNull() && !self.jsValue.isArray()
161
+ }
162
+
163
+ isNull(): Bool {
164
+ self.jsValue.isNull()
165
+ }
166
+
167
+ get(key: String): Json {
168
+ self.field(key)
169
+ }
170
+
171
+ field(key: String): Json {
172
+ if(self.isObject() && self.jsValue.hasOwn(key)) {
173
+ Json(self.jsValue.get(key))
174
+ } else {
175
+ Json(Js.null())
176
+ }
177
+ }
178
+
179
+ index(key: Int): Json {
180
+ if(self.isArray()) {
181
+ Json(self.jsValue.get(key).coalesce(Js.null()))
182
+ } else {
183
+ Json(Js.null())
184
+ }
185
+ }
186
+
187
+ hasField(key: String): Bool {
188
+ self.isObject() && self.jsValue.hasOwn(key)
189
+ }
190
+
191
+ getField(key: String): Option[Json] {
192
+ if(self.hasField(key)) {self.get(key)}
193
+ }
194
+
195
+ getIndex(key: Int): Option[Json] {
196
+ if(self.isArray() && !self.jsValue.get(key).isUndefined()) {
197
+ Json(self.jsValue.get(key))
198
+ }
199
+ }
200
+
201
+ getFields(): Option[List[String]] {
202
+ if(self.isObject()) {
203
+ Js->Object->keys(self.jsValue)?
204
+ }
205
+ }
206
+
207
+ grabField(key: String): Json {
208
+ self.getField(key).else {
209
+ throw(GrabException())
210
+ }
211
+ }
212
+
213
+ grabIndex(key: Int): Json {
214
+ self.getIndex(key).else {
215
+ throw(GrabException())
216
+ }
217
+ }
218
+
219
+ grabFields(): List[String] {
220
+ self.getFields().else {
221
+ throw(GrabException())
222
+ }
223
+ }
224
+
225
+ map[T](body: (String, Json) => T): List[T] {
226
+ let array = Array.new()
227
+ self.each {field, value => array.push(body(field, value))}
228
+ array.drain()
229
+ }
230
+
231
+ flatMap[T](body: (String, Json) => List[T]): List[T] {
232
+ let array = Array.new()
233
+ self.each {field, value => array.pushList(body(field, value))}
234
+ array.drain()
235
+ }
236
+
237
+ each(body: (String, Json) => Unit) {
238
+ if(!self.isObject()) {throw(GrabException())}
239
+ Js->Object->entries(self.jsValue).each {p =>
240
+ body(p.get(0)?, p.get(1)?)
241
+ }
242
+ }
243
+
244
+ eachWhile(body: (String, Json) => Bool) {
245
+ if(!self.isObject()) {throw(GrabException())}
246
+ Js->Object->entries(self.jsValue).eachWhile {p =>
247
+ body(p.get(0)?, p.get(1)?)
248
+ }
249
+ }
250
+
251
+ }
252
+
253
+ trait T: JsonLike {
254
+ toJson(value: T): Json
255
+ fromJson(json: Json): Option[T]
256
+ }
257
+
258
+ instance Json: JsonLike {
259
+ toJson(value: Json): Json {value}
260
+ fromJson(json: Json): Option[Json] {Some(json)}
261
+ }
262
+
263
+ instance String: JsonLike {
264
+ toJson(value: String): Json {string(value)}
265
+ fromJson(json: Json): Option[String] {_.getString()}
266
+ }
267
+
268
+ instance Int: JsonLike {
269
+ toJson(value: Int): Json {int(value)}
270
+ fromJson(json: Json): Option[Int] {_.getInt()}
271
+ }
272
+
273
+ instance Float: JsonLike {
274
+ toJson(value: Float): Json {float(value)}
275
+ fromJson(json: Json): Option[Float] {_.getFloat()}
276
+ }
277
+
278
+ instance Bool: JsonLike {
279
+ toJson(value: Bool): Json {bool(value)}
280
+ fromJson(json: Json): Option[Bool] {_.getBool()}
281
+ }
282
+
283
+ instance List[T: JsonLike]: JsonLike {
284
+ toJson(value: List[T]): Json {array(value.map(toJson))}
285
+ fromJson(json: Json): Option[List[T]] {
286
+ json.getArray().flatMap {array =>
287
+ mutable convertible = True
288
+ let result = Array.new()
289
+ array.eachWhile {item =>
290
+ fromJson(item).{
291
+ | None => convertible = False
292
+ | Some(value) => result.push(value)
293
+ }
294
+ convertible
295
+ }
296
+ if(convertible) {result.drain()}
297
+ }
298
+ }
299
+ }
300
+
301
+ instance StringMap[T: JsonLike]: JsonLike {
302
+ toJson(value: StringMap[T]): Json {
303
+ Json.fields {setField =>
304
+ value.each {key, value => setField(key, toJson(value))}
305
+ }
306
+ }
307
+ fromJson(json: Json): Option[StringMap[T]] {
308
+ if(json.isObject()) {
309
+ let map = StringMap.new()
310
+ mutable convertible = True
311
+ json.eachWhile {key, value =>
312
+ fromJson(value).{
313
+ | None => convertible = False
314
+ | Some(v) => map.set(key, v)
315
+ }
316
+ convertible
317
+ }
318
+ if(convertible) {map}
319
+ }.flatten()
320
+ }
321
+ }
322
+
323
+
324
+ instance Json: HasAnyTag {
325
+ anyTag(): AnyTag[Json] {Any.internalAnyTag("ff:core/Json.Json[]")}
326
+ }
327
+
328
+ instance Json: Show {
329
+ show(value: Json): String {value.write(Some(" "))}
330
+ }
331
+
332
+ instance Json: Equal {
333
+ equals(a: Json, b: Json): Bool {
334
+ internalEquals(a, b)
335
+ }
336
+ }
337
+
338
+ instance Json: Order {
339
+ compare(a: Json, b: Json): Ordering {
340
+ Ordering.fromInt(internalCompare(a, b))
341
+ }
342
+ }
343
+
344
+ instance Json: Serializable {
345
+ serializeUsing(serialization: Serialization, value: Json): Unit {
346
+ Serializable.serializeUsing(serialization, value.write())
347
+ }
348
+ deserializeUsing(serialization: Serialization): Json {
349
+ Json.read(Serializable.deserializeUsing[String](serialization)).grab()
350
+ }
351
+ }
352
+
353
+
354
+ internalEquals(a: Json, b: Json): Bool {
355
+ if(a.jsValue === b.jsValue) {True} else:
356
+ if(a.jsValue.isArray() || b.jsValue.isArray()) {
357
+ if(!a.jsValue.isArray() || !b.jsValue.isArray()) {False} else:
358
+ if(a.jsValue->length !== b.jsValue->length) {False} else:
359
+ mutable equal = True
360
+ 0.until(a.jsValue->length?).eachWhile {i =>
361
+ equal = internalEquals(Json(a.jsValue.get(i)), Json(b.jsValue.get(i)))
362
+ equal
363
+ }
364
+ equal
365
+ } else:
366
+ if(a.isObject() && b.isObject()) {
367
+ let aKeys = Js->Object->keys(a.jsValue)
368
+ let bKeys = Js->Object->keys(b.jsValue)
369
+ if(aKeys->length !== bKeys->length) {False} else:
370
+ mutable equal = True
371
+ aKeys.eachWhile {key =>
372
+ equal = b.jsValue.hasOwn(key?) && internalEquals(Json(a.jsValue.get(key)), Json(b.jsValue.get(key)))
373
+ equal
374
+ }
375
+ equal
376
+ } else:
377
+ False
378
+ }
379
+
380
+ internalCompare(a: Json, b: Json): Int {
381
+ if(a.jsValue === b.jsValue) {0} else:
382
+ if(a.jsValue === Js.null() || b.jsValue === Js.null()) {
383
+ if(a.jsValue === Js.null()) {-1} else {1}
384
+ } else:
385
+ if(a.isBool() || b.isBool()) {
386
+ if(!b.isBool() || b.jsValue?) {-1} else {1}
387
+ } else:
388
+ if(a.isFloat() || b.isFloat()) {
389
+ if(!b.isFloat()) {-1} else:
390
+ if(!a.isFloat()) {1} else:
391
+ if(a.jsValue.isNan()) {if(b.jsValue.isNan()) {0} else {-1}} else:
392
+ if(b.jsValue.isNan()) {1} else:
393
+ if(a.jsValue? < b.jsValue?) {-1} else {1}
394
+ } else:
395
+ if(a.isString() || b.isString()) {
396
+ if(!b.isString()) {-1} else:
397
+ if(!a.isString()) {1} else:
398
+ a.jsValue->localeCompare(b.jsValue, "en")?
399
+ } else:
400
+ if(a.isArray() || b.isArray()) {
401
+ if(!b.isArray()) {-1} else:
402
+ if(!a.isArray()) {1} else:
403
+ let aLength: Int = a.jsValue->length?
404
+ let bLength: Int = b.jsValue->length?
405
+ mutable result = 0
406
+ 0.until(aLength.min(bLength)).eachWhile {i =>
407
+ result = internalCompare(Json(a.jsValue.get(i)), Json(b.jsValue.get(i)))
408
+ result == 0
409
+ }
410
+ if(result != 0) {result} else:
411
+ aLength - bLength
412
+ } else:
413
+ let aKeys = Js->Object->keys(a.jsValue)->sort()
414
+ let bKeys = Js->Object->keys(b.jsValue)->sort()
415
+ let keyResult = internalCompare(Json(aKeys), Json(bKeys))
416
+ if(keyResult != 0) {keyResult} else:
417
+ mutable result = 0
418
+ aKeys.eachWhile {key =>
419
+ result = internalCompare(Json(a.jsValue.get(key)), Json(b.jsValue.get(key)))
420
+ result == 0
421
+ }
422
+ result
423
+ }