firefly-compiler 0.4.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 (221) hide show
  1. package/.firefly-workspace +1 -0
  2. package/.vscode/settings.json +5 -0
  3. package/LICENSE.txt +21 -0
  4. package/README.md +96 -0
  5. package/bin/firefly.mjs +2 -0
  6. package/compiler/.firefly/package.ff +1 -0
  7. package/compiler/Builder.ff +218 -0
  8. package/compiler/Compiler.ff +241 -0
  9. package/compiler/Dependencies.ff +179 -0
  10. package/compiler/Deriver.ff +647 -0
  11. package/compiler/Dictionaries.ff +205 -0
  12. package/compiler/Environment.ff +166 -0
  13. package/compiler/Inference.ff +1117 -0
  14. package/compiler/JsEmitter.ff +861 -0
  15. package/compiler/JsImporter.ff +56 -0
  16. package/compiler/LspHook.ff +188 -0
  17. package/compiler/Main.ff +237 -0
  18. package/compiler/Parser.ff +1383 -0
  19. package/compiler/Patterns.ff +111 -0
  20. package/compiler/Resolver.ff +620 -0
  21. package/compiler/Substitution.ff +178 -0
  22. package/compiler/Syntax.ff +299 -0
  23. package/compiler/Token.ff +180 -0
  24. package/compiler/Tokenizer.ff +278 -0
  25. package/compiler/Unification.ff +220 -0
  26. package/compiler/Wildcards.ff +50 -0
  27. package/compiler/Workspace.ff +88 -0
  28. package/core/.firefly/package.ff +2 -0
  29. package/core/Any.ff +30 -0
  30. package/core/Array.ff +249 -0
  31. package/core/AssetSystem.ff +61 -0
  32. package/core/Atomic.ff +64 -0
  33. package/core/Bool.ff +13 -0
  34. package/core/BrowserSystem.ff +14 -0
  35. package/core/Buffer.ff +211 -0
  36. package/core/BuildSystem.ff +144 -0
  37. package/core/Channel.ff +131 -0
  38. package/core/Char.ff +18 -0
  39. package/core/Core.ff +58 -0
  40. package/core/Duration.ff +15 -0
  41. package/core/Equal.ff +52 -0
  42. package/core/Error.ff +20 -0
  43. package/core/FileHandle.ff +41 -0
  44. package/core/Float.ff +41 -0
  45. package/core/HttpClient.ff +84 -0
  46. package/core/Instant.ff +9 -0
  47. package/core/Int.ff +61 -0
  48. package/core/IntMap.ff +85 -0
  49. package/core/JsSystem.ff +66 -0
  50. package/core/JsValue.ff +240 -0
  51. package/core/List.ff +440 -0
  52. package/core/Lock.ff +144 -0
  53. package/core/Log.ff +24 -0
  54. package/core/Map.ff +126 -0
  55. package/core/NodeSystem.ff +88 -0
  56. package/core/Nothing.ff +1 -0
  57. package/core/Option.ff +133 -0
  58. package/core/Ordering.ff +157 -0
  59. package/core/Pair.ff +55 -0
  60. package/core/Path.ff +393 -0
  61. package/core/RbMap.ff +216 -0
  62. package/core/Serializable.ff +173 -0
  63. package/core/Set.ff +38 -0
  64. package/core/Show.ff +43 -0
  65. package/core/Stack.ff +263 -0
  66. package/core/Stream.ff +406 -0
  67. package/core/String.ff +175 -0
  68. package/core/StringMap.ff +85 -0
  69. package/core/Task.ff +138 -0
  70. package/core/Try.ff +81 -0
  71. package/core/Unit.ff +3 -0
  72. package/experimental/random/AltGeneric.ff +44 -0
  73. package/experimental/random/Async.ff +68 -0
  74. package/experimental/random/Buffer2.ff +77 -0
  75. package/experimental/random/Cat.ff +12 -0
  76. package/experimental/random/Dictionary.ff +52 -0
  77. package/experimental/random/Example.ff +46 -0
  78. package/experimental/random/Generic.ff +102 -0
  79. package/experimental/random/HappyEyeballs.ff +40 -0
  80. package/experimental/random/HashMap.ff +72 -0
  81. package/experimental/random/IfElseUnit.ff +9 -0
  82. package/experimental/random/InputOutput.ff +23 -0
  83. package/experimental/random/ListVsArray.ff +45 -0
  84. package/experimental/random/Main.ff +44 -0
  85. package/experimental/random/MapTest.ff +67 -0
  86. package/experimental/random/OldTaskSystem.ff +210 -0
  87. package/experimental/random/PatternTest.ff +39 -0
  88. package/experimental/random/Patterns.ff +226 -0
  89. package/experimental/random/ReadBytesTest.ff +10 -0
  90. package/experimental/random/RunLength.ff +65 -0
  91. package/experimental/random/Scrape.ff +51 -0
  92. package/experimental/random/Serialization.ff +217 -0
  93. package/experimental/random/SerializationTest.ff +46 -0
  94. package/experimental/random/Serializer.ff +36 -0
  95. package/experimental/random/StdInOutErr.ff +4 -0
  96. package/experimental/random/Symbols.ff +74 -0
  97. package/experimental/random/Tag.ff +49 -0
  98. package/experimental/random/Tensor.ff +52 -0
  99. package/experimental/random/Try.ff +56 -0
  100. package/experimental/random/Tsv.ff +9 -0
  101. package/experimental/random/TypesAreModules.ff +87 -0
  102. package/experimental/random/blueprints/Blueprint.ff +52 -0
  103. package/experimental/random/blueprints/Main.ff +11 -0
  104. package/experimental/random/blueprints/Pretty.ff +58 -0
  105. package/experimental/random/blueprints/User.ff +64 -0
  106. package/experimental/random/blueprintsystem/BlueprintSystem.ff +48 -0
  107. package/experimental/random/blueprintsystem/Deserialize.ff +53 -0
  108. package/experimental/random/blueprintsystem/ReadJs.ff +13 -0
  109. package/experimental/random/blueprintsystem/User.ff +2 -0
  110. package/experimental/random/kahrs/Kahrs.ff +112 -0
  111. package/experimental/random/kahrs/TestKahrs.ff +22 -0
  112. package/experimental/random/kahrs/TestMap.ff +18 -0
  113. package/experimental/random/streaming/Gzip.ff +3 -0
  114. package/experimental/random/streaming/Main.ff +34 -0
  115. package/experimental/random/streaming/S3Bucket.ff +11 -0
  116. package/experimental/random/streaming/Tar.ff +5 -0
  117. package/experimental/rhymeapp/Main.ff +81 -0
  118. package/experimental/rhymeapp/index.html +14 -0
  119. package/firefly.sh +5 -0
  120. package/fireflysite/Main.ff +13 -0
  121. package/httpserver/.firefly/package.ff +1 -0
  122. package/httpserver/HttpServer.ff +184 -0
  123. package/lsp/.firefly/package.ff +1 -0
  124. package/lsp/CompletionHandler.ff +814 -0
  125. package/lsp/Handler.ff +551 -0
  126. package/lsp/HoverHandler.ff +82 -0
  127. package/lsp/LanguageServer.ff +229 -0
  128. package/lsp/SignatureHelpHandler.ff +55 -0
  129. package/lsp/SymbolHandler.ff +167 -0
  130. package/output/js/ff/compiler/Builder.mjs +483 -0
  131. package/output/js/ff/compiler/Compiler.mjs +410 -0
  132. package/output/js/ff/compiler/Dependencies.mjs +388 -0
  133. package/output/js/ff/compiler/Deriver.mjs +1166 -0
  134. package/output/js/ff/compiler/Dictionaries.mjs +1305 -0
  135. package/output/js/ff/compiler/Environment.mjs +1005 -0
  136. package/output/js/ff/compiler/Inference.mjs +4264 -0
  137. package/output/js/ff/compiler/JsEmitter.mjs +5353 -0
  138. package/output/js/ff/compiler/JsImporter.mjs +262 -0
  139. package/output/js/ff/compiler/LspHook.mjs +789 -0
  140. package/output/js/ff/compiler/Main.mjs +1695 -0
  141. package/output/js/ff/compiler/Parser.mjs +4004 -0
  142. package/output/js/ff/compiler/Patterns.mjs +923 -0
  143. package/output/js/ff/compiler/Resolver.mjs +2303 -0
  144. package/output/js/ff/compiler/Substitution.mjs +1146 -0
  145. package/output/js/ff/compiler/Syntax.mjs +12430 -0
  146. package/output/js/ff/compiler/Token.mjs +3092 -0
  147. package/output/js/ff/compiler/Tokenizer.mjs +589 -0
  148. package/output/js/ff/compiler/Unification.mjs +1748 -0
  149. package/output/js/ff/compiler/Wildcards.mjs +604 -0
  150. package/output/js/ff/compiler/Workspace.mjs +683 -0
  151. package/output/js/ff/core/Any.mjs +139 -0
  152. package/output/js/ff/core/Array.mjs +594 -0
  153. package/output/js/ff/core/AssetSystem.mjs +270 -0
  154. package/output/js/ff/core/Atomic.mjs +186 -0
  155. package/output/js/ff/core/Bool.mjs +141 -0
  156. package/output/js/ff/core/BrowserSystem.mjs +122 -0
  157. package/output/js/ff/core/Buffer.mjs +467 -0
  158. package/output/js/ff/core/BuildSystem.mjs +320 -0
  159. package/output/js/ff/core/Channel.mjs +268 -0
  160. package/output/js/ff/core/Char.mjs +145 -0
  161. package/output/js/ff/core/Core.mjs +300 -0
  162. package/output/js/ff/core/Duration.mjs +112 -0
  163. package/output/js/ff/core/Equal.mjs +175 -0
  164. package/output/js/ff/core/Error.mjs +138 -0
  165. package/output/js/ff/core/FileHandle.mjs +164 -0
  166. package/output/js/ff/core/Float.mjs +214 -0
  167. package/output/js/ff/core/HttpClient.mjs +210 -0
  168. package/output/js/ff/core/Instant.mjs +105 -0
  169. package/output/js/ff/core/Int.mjs +254 -0
  170. package/output/js/ff/core/IntMap.mjs +282 -0
  171. package/output/js/ff/core/JsSystem.mjs +234 -0
  172. package/output/js/ff/core/JsValue.mjs +678 -0
  173. package/output/js/ff/core/List.mjs +2335 -0
  174. package/output/js/ff/core/Lock.mjs +322 -0
  175. package/output/js/ff/core/Log.mjs +159 -0
  176. package/output/js/ff/core/Map.mjs +358 -0
  177. package/output/js/ff/core/NodeSystem.mjs +288 -0
  178. package/output/js/ff/core/Nothing.mjs +100 -0
  179. package/output/js/ff/core/Option.mjs +1002 -0
  180. package/output/js/ff/core/Ordering.mjs +734 -0
  181. package/output/js/ff/core/Pair.mjs +318 -0
  182. package/output/js/ff/core/Path.mjs +768 -0
  183. package/output/js/ff/core/RbMap.mjs +1936 -0
  184. package/output/js/ff/core/Serializable.mjs +434 -0
  185. package/output/js/ff/core/Set.mjs +250 -0
  186. package/output/js/ff/core/Show.mjs +201 -0
  187. package/output/js/ff/core/Stack.mjs +595 -0
  188. package/output/js/ff/core/Stream.mjs +1300 -0
  189. package/output/js/ff/core/String.mjs +433 -0
  190. package/output/js/ff/core/StringMap.mjs +282 -0
  191. package/output/js/ff/core/Task.mjs +345 -0
  192. package/output/js/ff/core/Try.mjs +503 -0
  193. package/output/js/ff/core/Unit.mjs +103 -0
  194. package/package.json +29 -0
  195. package/postgresql/.firefly/include/package-lock.json +250 -0
  196. package/postgresql/.firefly/include/package.json +5 -0
  197. package/postgresql/.firefly/include/prepare.sh +2 -0
  198. package/postgresql/.firefly/package.ff +3 -0
  199. package/postgresql/Pg.ff +530 -0
  200. package/unsafejs/.firefly/package.ff +1 -0
  201. package/unsafejs/UnsafeJs.ff +19 -0
  202. package/vscode/.vscode/launch.json +18 -0
  203. package/vscode/.vscode/tasks.json +33 -0
  204. package/vscode/LICENSE.txt +21 -0
  205. package/vscode/Prepublish.ff +15 -0
  206. package/vscode/README.md +17 -0
  207. package/vscode/client/package-lock.json +544 -0
  208. package/vscode/client/package.json +22 -0
  209. package/vscode/client/src/extension.ts +64 -0
  210. package/vscode/client/tsconfig.json +12 -0
  211. package/vscode/icons/firefly-icon.png +0 -0
  212. package/vscode/icons/firefly-icon.svg +10 -0
  213. package/vscode/icons/firefly-logo-notext.png +0 -0
  214. package/vscode/icons/firefly-logo.png +0 -0
  215. package/vscode/language-configuration.json +39 -0
  216. package/vscode/package-lock.json +3623 -0
  217. package/vscode/package.json +144 -0
  218. package/vscode/snippets-none.json +1 -0
  219. package/vscode/snippets.json +241 -0
  220. package/vscode/syntaxes/firefly.tmLanguage.json +294 -0
  221. package/vscode/tsconfig.json +20 -0
@@ -0,0 +1,144 @@
1
+ capability BuildSystem {}
2
+ capability BrowserCode(packageGroup: String, packageName: String, mainFile: Path, assetSystem: AssetSystem)
3
+ capability BrowserBundle(assetSystem: AssetSystem)
4
+
5
+ extend self: BuildSystem {
6
+
7
+ compileForBrowser(mainFile: String): BrowserCode {
8
+ // TODO: Check that the mainFile is in the current package directory
9
+ internalCompile(self, internalPath(self, mainFile), "browser")
10
+ let streams = internalListDirectory(internalPath(self, ".firefly/output/browser"))
11
+ let mainPackagePair = internalMainPackagePair(self)
12
+ BrowserCode(
13
+ packageGroup = mainPackagePair.first
14
+ packageName = mainPackagePair.second
15
+ mainFile = internalPath(self, mainFile)
16
+ assetSystem = AssetSystem(streams.toMap())
17
+ )
18
+ }
19
+
20
+ buildMode(): Bool
21
+ target node async "return !!self_.buildMode_"
22
+
23
+ setAssets(assetSystem: AssetSystem): Unit
24
+ target node async "self_.assets_ = assetSystem_"
25
+
26
+ packageAssets(): AssetSystem {
27
+ let streams = internalListDirectory(internalPath(self, "."))
28
+ AssetSystem(streams.toMap())
29
+ }
30
+
31
+ dependencyAssets(user: String, package: String): AssetSystem {
32
+ panic("dependencyAssets not yet implemented")
33
+ }
34
+
35
+ arguments(): Array[String]
36
+ target node async "return self_.array_"
37
+
38
+ mainTask(): Task
39
+ target js async "return self_.task_"
40
+
41
+ }
42
+
43
+ extend self: BrowserCode {
44
+
45
+ assets(): AssetSystem {
46
+ self.assetSystem
47
+ }
48
+
49
+ bundle(minify: Bool = True, sourceMap: Bool = False): BrowserBundle {
50
+ let prefix = ".firefly/output/browser"
51
+ let mainJsBaseFile = self.mainFile.absolute().removeLast(".ff").grab() + ".mjs"
52
+ let mainJsFile = prefix + "/" + self.packageGroup + "/" + self.packageName + "/" + mainJsBaseFile
53
+ let file = prefix + "/Main.bundle.js"
54
+ internalCallEsBuild(self, mainJsFile = mainJsFile, outputPath = file, minify = minify, sourceMap = sourceMap)
55
+ let assets = AssetSystem([
56
+ Pair(file.dropFirst(prefix.size()), {self.mainFile.path(file).readStream()})
57
+ ...if(sourceMap) {[
58
+ Pair(file.dropFirst(prefix.size()) + ".map", {self.mainFile.path(file + ".map").readStream()})
59
+ ]} else {[]}
60
+ ].toMap())
61
+ BrowserBundle(assets)
62
+ }
63
+
64
+ }
65
+
66
+ extend self: BrowserBundle {
67
+
68
+ assets(): AssetSystem {
69
+ self.assetSystem
70
+ }
71
+
72
+ }
73
+
74
+
75
+ internalCallEsBuild(
76
+ self: BrowserCode
77
+ mainJsFile: String
78
+ outputPath: String
79
+ minify: Bool
80
+ sourceMap: Bool
81
+ ): Unit
82
+ target node async """
83
+ import * as esbuild from 'esbuild'
84
+ return await esbuild.build({
85
+ entryPoints: [mainJsFile_],
86
+ bundle: true,
87
+ minify: minify_,
88
+ sourcemap: sourceMap_,
89
+ platform: 'browser',
90
+ target: 'es6',
91
+ external: ['../../../node_modules/*'], // TODO
92
+ outfile: outputPath_
93
+ })
94
+ """
95
+
96
+ internalNodeCallEsBuild(
97
+ self: NodeSystem
98
+ mainJsFile: String
99
+ outputPath: String
100
+ minify: Bool
101
+ ): Unit
102
+ target node async """
103
+ import * as esbuild from 'esbuild'
104
+ return await esbuild.build({
105
+ entryPoints: [mainJsFile_],
106
+ bundle: true,
107
+ minify: minify_,
108
+ sourcemap: true,
109
+ platform: 'node',
110
+ target: 'es6',
111
+ external: ['../../../node_modules/*'], // TODO
112
+ outfile: outputPath_
113
+ })
114
+ """
115
+
116
+ internalListDirectory(path: Path): List[Pair[String, () => Stream[Buffer]]] {
117
+ function go(currentPath: Path): Stream[Path] {
118
+ currentPath.entries().flatMap {file =>
119
+ if(file.isDirectory()) {
120
+ go(file.path())
121
+ } else {
122
+ [file.path()].toStream()
123
+ }
124
+ }
125
+ }
126
+ go(path).map {file =>
127
+ Pair("/" + file.relativeTo(path), {file.readStream()})
128
+ }.toList()
129
+ }
130
+
131
+ internalPath(buildSystem: BuildSystem, absoluteOrRelative: String): Path
132
+ target node async """
133
+ return absoluteOrRelative_
134
+ """
135
+
136
+ internalCompile(buildSystem: BuildSystem, mainFile: Path, target: String): Unit
137
+ target node async """
138
+ return await $firefly_compiler.buildViaBuildSystem_$(buildSystem_, buildSystem_.fireflyPath_, mainFile_, target_, $task)
139
+ """
140
+
141
+ internalMainPackagePair(buildSystem: BuildSystem): Pair[String, String]
142
+ target node async """
143
+ return {first_: buildSystem_.mainPackagePair_.group_, second_: buildSystem_.mainPackagePair_.name_}
144
+ """
@@ -0,0 +1,131 @@
1
+ capability Channel[T] {}
2
+
3
+ extend self[T]: Channel[T] {
4
+
5
+ read(): T {
6
+ readOr(self, {_}).wait()
7
+ }
8
+
9
+ write(message: T): Unit {
10
+ writeOr(self, message, {}).wait()
11
+ }
12
+
13
+ }
14
+
15
+
16
+ capability ChannelAction[T] {}
17
+
18
+ extend self[T]: ChannelAction[T] {
19
+
20
+ readOr[M](channel: Channel[M], body: M => T): ChannelAction[T]
21
+ target js async "return {channel: channel_, body: body_, previous: self_}"
22
+
23
+ writeOr[M](channel: Channel[M], message: M, body: () => T): ChannelAction[T]
24
+ target js async "return {channel: channel_, body: body_, message: message_, previous: self_}"
25
+
26
+ wait(): T {
27
+ internalRunChannelAction(self, None)
28
+ }
29
+
30
+ timeout(duration: Duration, body: () => T): T {
31
+ internalRunChannelAction(self, Some(Pair(body, Some(duration))))
32
+ }
33
+
34
+ immediately(body: () => T): T {
35
+ internalRunChannelAction(self, Some(Pair(body, None)))
36
+ }
37
+
38
+ }
39
+
40
+ readOr[M, T](channel: Channel[M], body: M => T): ChannelAction[T]
41
+ target js async "return {channel: channel_, body: body_, previous: null}"
42
+
43
+ writeOr[M, T](channel: Channel[M], message: M, body: () => T): ChannelAction[T]
44
+ target js async "return {channel: channel_, body: body_, message: message_, previous: null}"
45
+
46
+
47
+ internalRunChannelAction[T](action: ChannelAction[T], mode: Option[Pair[() => T, Option[Duration]]]): T
48
+ target js async """
49
+ ff_core_Task.Task_throwIfAborted($task)
50
+
51
+ // Convert the linked actions into an array.
52
+ let actions = []
53
+ while(action_ != null) {
54
+ actions.push(action_)
55
+ action_ = action_.previous
56
+ }
57
+ actions.reverse()
58
+
59
+ // If any reads or writes can be done immediately, do the first one and return.
60
+ for(let action of actions) {
61
+ if(action.hasOwnProperty("message")) {
62
+ if(action.channel.readers.size != 0) {
63
+ let reader = action.channel.readers.values().next().value
64
+ action.channel.readers.delete(reader)
65
+ reader.resolve(action.message)
66
+ return await action.body($task)
67
+ } else if(action.channel.buffer.length < action.channel.capacity) {
68
+ action.channel.buffer.push(action.message)
69
+ return await action.body($task)
70
+ }
71
+ } else {
72
+ if(action.channel.buffer.length != 0) {
73
+ return await action.body(action.channel.buffer.shift(), $task)
74
+ } else if(action.channel.writers.size != 0) {
75
+ let writer = action.channel.writers.values().next().value
76
+ action.channel.writers.delete(writer)
77
+ writer.resolve()
78
+ return await action.body(writer.message, $task)
79
+ }
80
+ }
81
+ }
82
+
83
+ // If there's an "immediately(body)" action, do that now.
84
+ if(mode_.value_ && mode_.value_.second_.value_ == null) return await mode_.value_.first_($task)
85
+
86
+ // Otherwise, start waiting for one of the readers or writers (or timeout(body), or cancellation) to happen.
87
+ let abort = null
88
+ let finish = null
89
+ let cleanups = []
90
+ function doCleanup() {
91
+ for(let cleanup of cleanups) cleanup()
92
+ }
93
+ let promise = new Promise((resolve, reject) => {
94
+ if(mode_.value_) finish = () => {doCleanup(); resolve(() => mode_.value_.first_($task))}
95
+ abort = () => {doCleanup(); reject($task.controller.signal.reason)}
96
+ for(let action of actions) {
97
+ if(action.hasOwnProperty("message")) {
98
+ let writer = {
99
+ resolve: () => {
100
+ doCleanup()
101
+ resolve(() => action.body($task))
102
+ },
103
+ message: action.message
104
+ }
105
+ cleanups.push(() => action.channel.writers.delete(writer))
106
+ action.channel.writers.add(writer)
107
+ } else {
108
+ let reader = {
109
+ resolve: m => {
110
+ doCleanup()
111
+ resolve(() => action.body(m, $task))
112
+ }
113
+ }
114
+ cleanups.push(() => action.channel.readers.delete(reader))
115
+ action.channel.readers.add(reader)
116
+ }
117
+ }
118
+ })
119
+ let timeout = null
120
+ try {
121
+ $task.controller.signal.addEventListener('abort', abort)
122
+ if(finish != null) timeout = setTimeout(finish, mode_.value_.second_.value_ * 1000)
123
+ let body = await promise
124
+ if(timeout != null) { clearTimeout(timeout); timeout = null }
125
+ return await body()
126
+ } finally {
127
+ if(timeout != null) clearTimeout(timeout)
128
+ $task.controller.signal.removeEventListener('abort', abort)
129
+ if($task.controller.signal.aborted) $task.controller = new AbortController()
130
+ }
131
+ """
package/core/Char.ff ADDED
@@ -0,0 +1,18 @@
1
+ newtype Char(codeUnit: Int)
2
+
3
+ extend self: Char {
4
+
5
+ isAsciiLetter(): Bool {self.isAsciiUpper() || self.isAsciiLower()}
6
+
7
+ isAsciiLetterOrDigit(): Bool {self.isAsciiLetter() || self.isAsciiDigit()}
8
+
9
+ isAsciiUpper(): Bool {self >= 'A' && self <= 'Z'}
10
+
11
+ isAsciiLower(): Bool {self >= 'a' && self <= 'z'}
12
+
13
+ isAsciiDigit(): Bool {self >= '0' && self <= '9'}
14
+
15
+ toString(): String
16
+ target js sync "return String.fromCharCode(self_)"
17
+
18
+ }
package/core/Core.ff ADDED
@@ -0,0 +1,58 @@
1
+ if[T](condition: Bool, body: () => T): Option[T] {
2
+ condition.{
3
+ | False => None
4
+ | True => Some(body())
5
+ }
6
+ }
7
+
8
+ while(condition: () => Bool, body: () => Unit): Unit {
9
+ condition().{
10
+ | False =>
11
+ | True =>
12
+ body()
13
+ tailcall while(condition, body)
14
+ }
15
+ }
16
+
17
+ doWhile(body: () => Bool): Unit {
18
+ while {body()} {}
19
+ }
20
+
21
+ doUntil[T](body: () => Option[T]): T {
22
+ body().{
23
+ | Some(v) => v
24
+ | None => tailcall doUntil(body)
25
+ }
26
+ }
27
+
28
+ do[T](body: () => T): T {
29
+ body()
30
+ }
31
+
32
+ try[T](body: () => T): Try[T]
33
+ target js sync """
34
+ try {
35
+ return {Success: true, value_: body_()}
36
+ } catch(e) {
37
+ return {Failure: true, error_: e}
38
+ }
39
+ """
40
+ target js async """
41
+ try {
42
+ return {Success: true, value_: await body_($task)}
43
+ } catch(e) {
44
+ return {Failure: true, error_: e}
45
+ }
46
+ """
47
+
48
+ throw[E: HasAnyTag, T](exception: E): T {
49
+ throwAny(Any.toAny(exception))
50
+ }
51
+
52
+ throwAny[T](exception: Any): T
53
+ target js sync "throw Object.assign(new Error(), {ffException: exception_})"
54
+
55
+ panic[T](message: String): T
56
+ target js sync "throw new Error(message_)"
57
+
58
+ data GrabException()
@@ -0,0 +1,15 @@
1
+ newtype Duration(seconds: Float)
2
+
3
+ extend self: Duration {
4
+
5
+ show(digits: Int = 3): String {
6
+ self.seconds.toFixed(digits) + " s"
7
+ }
8
+
9
+ }
10
+
11
+ instance Duration: Show {
12
+ show(value: Duration): String {
13
+ value.show()
14
+ }
15
+ }
package/core/Equal.ff ADDED
@@ -0,0 +1,52 @@
1
+ trait T: Equal {
2
+ equals(x: T, y: T): Bool
3
+ }
4
+
5
+ notEquals[T: Equal](x: T, y: T): Bool {
6
+ !equals(x, y)
7
+ }
8
+
9
+ instance Nothing: Equal {
10
+ equals(x: Nothing, y: Nothing): Bool {True}
11
+ }
12
+
13
+ instance Bool: Equal {
14
+ equals(x: Bool, y: Bool): Bool
15
+ target js sync """return x_ === y_"""
16
+ }
17
+
18
+ instance Char: Equal {
19
+ equals(x: Char, y: Char): Bool
20
+ target js sync """return x_ === y_"""
21
+ }
22
+
23
+ instance Int: Equal {
24
+ equals(x: Int, y: Int): Bool
25
+ target js sync """return x_ === y_"""
26
+ }
27
+
28
+ instance Float: Equal {
29
+ equals(x: Float, y: Float): Bool
30
+ target js sync """return x_ === y_"""
31
+ }
32
+
33
+ instance String: Equal {
34
+ equals(x: String, y: String): Bool
35
+ target js sync """return x_ === y_"""
36
+ }
37
+
38
+ instance List[T]: Equal {
39
+ equals(x: List[T], y: List[T]): Bool
40
+ target js sync """
41
+ let a = x_
42
+ let b = y_
43
+ if(a === b) return true
44
+ while(a.Link && b.Link) {
45
+ if(a === b) return true
46
+ if(!ff_core_Equal_Equal$T.equals_(a.head_, b.head_)) return false
47
+ a = a.tail_
48
+ b = b.tail_
49
+ }
50
+ return a.Empty && b.Empty
51
+ """
52
+ }
package/core/Error.ff ADDED
@@ -0,0 +1,20 @@
1
+ data Error {}
2
+
3
+ extend self: Error {
4
+
5
+ rethrow[T](): T
6
+ target js sync "throw self_"
7
+
8
+ name(): String
9
+ target js sync "return self_.name || ''"
10
+
11
+ message(): String
12
+ target js sync "return self_.message || ''"
13
+
14
+ stack(): String
15
+ target js sync "return self_.stack || ''"
16
+
17
+ exception(): Option[Any]
18
+ target js sync "return self_.ffException ? ff_core_Option.Some(self_.ffException) : ff_core_Option.None()"
19
+
20
+ }
@@ -0,0 +1,41 @@
1
+ capability FileHandle {}
2
+
3
+ extend self: FileHandle {
4
+
5
+ close(): Unit
6
+ target js async """
7
+ await self_.close()
8
+ """
9
+
10
+ read(buffer: Buffer, offset: Int = 0, length: Option[Int] = None, position: Option[Int] = None): Unit
11
+ target js async """
12
+ ff_core_Task.Task_throwIfAborted($task)
13
+ await self_.read(buffer_, {offset: offset_, length: length.value_, position: position.value_})
14
+ """
15
+
16
+ write(buffer: Buffer, offset: Int = 0, length: Option[Int] = None, position: Option[Int] = None): Unit
17
+ target js async """
18
+ ff_core_Task.Task_throwIfAborted($task)
19
+ await self_.write(buffer_, offset_, length.value_, position.value_)
20
+ """
21
+
22
+ writeText(text: String, position: Option[Int] = None, encoding: String = "utf8"): Unit
23
+ target js async """
24
+ ff_core_Task.Task_throwIfAborted($task)
25
+ await self_.write(text, position.value_, encoding_)
26
+ """
27
+
28
+ truncate(length: Int = 0): Unit
29
+ target js async """
30
+ ff_core_Task.Task_throwIfAborted($task)
31
+ await self_.truncate(length_)
32
+ """
33
+
34
+ sync(dataOnly: Bool = False): Unit
35
+ target js async """
36
+ ff_core_Task.Task_throwIfAborted($task)
37
+ if(dataOnly_) await self_.datasync()
38
+ else await self_.sync()
39
+ """
40
+
41
+ }
package/core/Float.ff ADDED
@@ -0,0 +1,41 @@
1
+ data Float {}
2
+
3
+ extend self: Float {
4
+
5
+ toInt(): Int
6
+ target js sync "return Math.trunc(Math.abs(self_))"
7
+
8
+ round(): Float
9
+ target js sync "return Math.round(self_)"
10
+
11
+ floor(): Float
12
+ target js sync "return Math.floor(self_)"
13
+
14
+ ceil(): Float
15
+ target js sync "return Math.ceil(self_)"
16
+
17
+ truncate(): Float
18
+ target js sync "return Math.trunc(self_)"
19
+
20
+ sign(): Float
21
+ target js sync "return Math.sign(self_)"
22
+
23
+ abs(): Float
24
+ target js sync "return Math.abs(self_)"
25
+
26
+ toFixed(digits: Int): String
27
+ target js sync "return self_.toFixed(digits_)"
28
+
29
+ min(that: Float): Float {
30
+ if(self < that) {self} else {that}
31
+ }
32
+
33
+ max(that: Float): Float {
34
+ if(self > that) {self} else {that}
35
+ }
36
+
37
+ clamp(from: Float, to: Float): Float {
38
+ if(self <= from) {from} elseIf {self >= to} {to} else {self}
39
+ }
40
+
41
+ }
@@ -0,0 +1,84 @@
1
+ capability HttpClient {}
2
+ capability FetchBody {}
3
+ capability FetchResponse {}
4
+ data FetchRedirect {
5
+ RedirectFollow
6
+ RedirectError
7
+ RedirectManual
8
+ }
9
+
10
+ extend self: HttpClient {
11
+
12
+ fetch(
13
+ url: String
14
+ method: String = "GET"
15
+ headers: List[Pair[String, String]] = emptyList
16
+ body: Option[FetchBody] = None
17
+ redirect: FetchRedirect = RedirectFollow
18
+ // The rest of the options are only respected by browsers - see the MDN fetch() documentation
19
+ referrer: Option[String] = None
20
+ integrity: Option[String] = None
21
+ mode: Option[String] = None
22
+ credentials: Option[String] = None
23
+ cache: Option[String] = None
24
+ throw: Bool = True
25
+ ): FetchResponse
26
+ target js async """
27
+ try {
28
+ const options = {headers: {}, signal: $task.controller.signal}
29
+ options.method = method_
30
+ ff_core_List.List_each(headers_, pair => {options.headers[pair.key_] = pair.value_})
31
+ if(body_.value_) options.body = body_.value_
32
+ if(redirect_.RedirectError) options.redirect = "error"
33
+ else if(redirect_.RedirectManual) options.redirect = "manual"
34
+ if(referrer_.value_) options.referrer = referrer_.value_
35
+ if(integrity_.value_) options.integrity = integrity_.value_
36
+ if(mode_.value_) options.mode = mode_.value_
37
+ if(credentials_.value_) options.credentials = credentials_.value_
38
+ if(cache_.value_) options.cache = cache_.value_
39
+ let result = await fetch(url_, options)
40
+ if(throw_ && !result.ok) throw new Error("Unexpected HTTP status code: " + result.status)
41
+ return result
42
+ } finally {
43
+ if($task.controller.signal.aborted) $task.controller = new AbortController()
44
+ }
45
+ """
46
+
47
+ }
48
+
49
+ emptyList: List[Pair[String, String]] = [] // TODO: Why won't this type check when inlined? Probably some dangling unification variable?
50
+
51
+ bodyText(body: String): FetchBody
52
+ target js sync "return body_"
53
+ target js async "return body_"
54
+
55
+ bodyBuffer(body: Buffer): FetchBody
56
+ target js sync "return body_"
57
+ target js async "return body_"
58
+
59
+ extend self: FetchResponse {
60
+
61
+ ok(): Bool
62
+ target js async "return self_.ok"
63
+
64
+ status(): Int
65
+ target js async "return self_.status"
66
+
67
+ statusText(): String
68
+ target js async "return self_.statusText"
69
+
70
+ header(name: String): Option[String]
71
+ target js async """
72
+ const header = self_.headers.get(name_)
73
+ return header != null
74
+ ? ff_core_Option.Some(header)
75
+ : ff_core_Option.None()
76
+ """
77
+
78
+ readText(): String
79
+ target js async "return await self_.text()"
80
+
81
+ readBuffer(): Buffer
82
+ target js async "return new DataView(await self_.arrayBuffer())"
83
+
84
+ }
@@ -0,0 +1,9 @@
1
+ newtype Instant(since1970: Duration)
2
+
3
+ extend self: Instant {
4
+
5
+ add(duration: Duration): Instant {
6
+ Instant(Duration(self.since1970.seconds + duration.seconds))
7
+ }
8
+
9
+ }
package/core/Int.ff ADDED
@@ -0,0 +1,61 @@
1
+ data Int {}
2
+
3
+ extend self: Int {
4
+
5
+ abs(): Int
6
+ target js sync "return Math.abs(self_)"
7
+
8
+ toFloat(): Float
9
+ target js sync "return self_"
10
+
11
+ bitNot(): Int
12
+ target js sync "return ~self_;"
13
+
14
+ bitOr(that: Int): Int
15
+ target js sync "return self_ | that_;"
16
+
17
+ bitAnd(that: Int): Int
18
+ target js sync "return self_ & that_;"
19
+
20
+ bitXor(that: Int): Int
21
+ target js sync "return self_ ^ that_;"
22
+
23
+ bitLeft(bits: Int = 1): Int
24
+ target js sync "return self_ << bits_;"
25
+
26
+ bitRight(bits: Int = 1, signed: Bool = True): Int
27
+ target js sync "return signed_ ? self_ >> bits_ : self_ >>> bits_;"
28
+
29
+ to(inclusiveEnd: Int): List[Int] {
30
+ mutable result = []
31
+ mutable n = inclusiveEnd
32
+ while {self <= n} {
33
+ result = [n, ...result]
34
+ n -= 1
35
+ }
36
+ result
37
+ }
38
+
39
+ until(exclusiveEnd: Int): List[Int] {
40
+ mutable result = []
41
+ mutable n = exclusiveEnd
42
+ while {self < n} {
43
+ result = [n - 1, ...result]
44
+ n -= 1
45
+ }
46
+ result
47
+ }
48
+
49
+ min(that: Int): Int {
50
+ if(self < that) {self} else {that}
51
+ }
52
+
53
+ max(that: Int): Int {
54
+ if(self > that) {self} else {that}
55
+ }
56
+
57
+ clamp(from: Int, to: Int): Int {
58
+ if(self <= from) {from} elseIf {self >= to} {to} else {self}
59
+ }
60
+
61
+ }