firefly-compiler 0.5.35 → 0.5.37

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