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,112 @@
1
+ // Based on https://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs
2
+
3
+ data Color {
4
+ R
5
+ B
6
+ }
7
+
8
+ data RB[A] {
9
+ E
10
+ T(color : Color, left: RB[A], element: A, right: RB[A])
11
+ }
12
+
13
+ insert[A: Order](x: A, s: RB[A]): RB[A] {
14
+ function ins(s: RB[A]): RB[A] {
15
+ | E => T(R, E, x, E)
16
+ | T(B, a, y, b) => Ordering.compare(x, y).{
17
+ | OrderingBefore => balance(ins(a), y, b)
18
+ | OrderingAfter => balance(a, y, ins(b))
19
+ | OrderingSame => a
20
+ }
21
+ | T(R, a, y, b) => Ordering.compare(x, y).{
22
+ | OrderingBefore => T(R, ins(a), y, b)
23
+ | OrderingAfter => T(R, a, y, ins(b))
24
+ | OrderingSame => s
25
+ }
26
+ }
27
+
28
+ ins(s).{
29
+ | E => panic("Unexpected empty tree")
30
+ | T(_, a, z, b) => T(B, a, z, b)
31
+ }
32
+ }
33
+
34
+ member[A: Order](x: A, s: RB[A]): Bool {
35
+ | x, E => False
36
+ | x, T(_, a, y, b,) => Ordering.compare(x, y).{
37
+ | OrderingBefore => member(x, a)
38
+ | OrderingAfter => member(x, b)
39
+ | OrderingSame => True
40
+ }
41
+ }
42
+
43
+ balance[A: Order](aa : RB[A], xx: A, bb: RB[A]): RB[A] {
44
+ | T(R, a, x, b), y, T(R, c, z, d) => T(R, T(B, a, x, b), y, T(B, c, z, d))
45
+ | T(R, T(R, a, x, b), y, c), z, d => T(R, T(B, a, x, b), y, T(B, c, z, d))
46
+ | T(R, a, x, T(R, b, y, c)), z, d => T(R, T(B, a, x, b), y, T(B, c, z, d))
47
+ | a, x, T(R, b, y, T(R, c, z, d)) => T(R, T(B, a, x, b), y, T(B, c, z, d))
48
+ | a, x, T(R, T(R, b, y, c), z, d) => T(R, T(B, a, x, b), y, T(B, c, z, d))
49
+ | a, x, b => T(B, a, x, b)
50
+ }
51
+
52
+ delete[A: Order](x: A, t: RB[A]): RB[A] {
53
+ function del(ss: RB[A]): RB[A] {
54
+ | E => E
55
+ | T(_, a, y, b) => Ordering.compare(x, y).{
56
+ | OrderingBefore => delformLeft(a, y, b)
57
+ | OrderingAfter => delformRight(a, y, b)
58
+ | OrderingSame => app(a, b)
59
+ }
60
+ }
61
+
62
+ function delformLeft(a: RB[A], y: A, b: RB[A]): RB[A] {
63
+ | T(B, _, _, _), y, b => balleft(del(a), y, b)
64
+ | a, y, b => T(R, del(a), y, b)
65
+ }
66
+
67
+ function delformRight(a: RB[A], y: A, b: RB[A]): RB[A] {
68
+ | a, y, T(B, _, _, _) => balright(a, y, del(b))
69
+ | a, y, b => T(R, a, y, del(b))
70
+ }
71
+
72
+ del(t).{
73
+ | T(_, a, y, b) => T(B, a, y, b)
74
+ | _ => E
75
+ }
76
+ }
77
+
78
+ balleft[A: Order](aa: RB[A], xx: A, bb: RB[A]): RB[A] {
79
+ | T(R, a, x, b), y, c => T(R, T(B, a, x, b), y, c)
80
+ | bl, x, T(B, a, y, b) => balance(bl, x, T(R, a, y, b))
81
+ | bl, x, T(R, T(B, a, y, b), z, c) => T(R, T(B, bl, x, a), y, (balance(b, z, sub1(c))))
82
+ | _, _, _ => panic("Unexhaustive match in balleft")
83
+ }
84
+
85
+ balright[A: Order](aa: RB[A], xx: A, bb: RB[A]): RB[A] {
86
+ | a, x, T(R, b, y, c) => T(R, a, x, T(B, b, y, c))
87
+ | T(B, a, x, b), y, bl => balance(T(R, a, x, b), y, bl)
88
+ | T(R, a, x, T(B, b, y, c)), z, bl => T(R, balance(sub1(a), x, b), y, T(B, c, z, bl))
89
+ | _, _, _ => panic("Unexhaustive match in balright")
90
+ }
91
+
92
+ sub1[A: Order](tt: RB[A]): RB[A] {
93
+ | T(B, a, x, b) => T(R, a, x, b)
94
+ | _ => panic("invariance violation")
95
+ }
96
+
97
+ app[A: Order](aa: RB[A], bb: RB[A]): RB[A] {
98
+ | E, x => x
99
+ | x, E => x
100
+ | T(R, a, x, b), T(R, c, y, d) =>
101
+ app(b, c).{
102
+ | T(R, b2, z, c2) => T(R, T(R, a, x, b2), z, T(R, c2, y, d))
103
+ | bc => T(R, a, x, T(R, bc, y, d))
104
+ }
105
+ | T(B, a, x, b), T(B, c, y, d) =>
106
+ app(b, c).{
107
+ | T(R, b2, z, c2) => T(R, T(B, a, x, b2), z, T(B, c2, y, d))
108
+ | bc => balleft(a, x, T(B, bc, y, d))
109
+ }
110
+ | a, T(R, b, x, c) => T(R, app(a, b), x, c)
111
+ | T(R, a, x, b), c => T(R, a, x, app(b, c))
112
+ }
@@ -0,0 +1,22 @@
1
+ import Kahrs
2
+
3
+ nodeMain(system: NodeSystem): Unit {
4
+
5
+ let values = [8, 3, 2, 1, 4, 7, 6, 5, 9]
6
+ let full = values.foldLeft(Kahrs.E, {t, a =>
7
+ let t2 = Kahrs.insert(a, t)
8
+ Log.debug("Insert " + a + ": " + Show.show(toList(t2)))
9
+ t2
10
+ })
11
+ values.foldLeft(full, {t, a =>
12
+ let t2 = Kahrs.delete(a, t)
13
+ Log.debug("Delete " + a + ": " + Show.show(toList(t2)))
14
+ t2
15
+ })
16
+
17
+ }
18
+
19
+ toList[A: Order](t: RB[A]): List[A] {
20
+ | E => []
21
+ | T(_, left, e, right) => [...toList(left), e, ...toList(right)]
22
+ }
@@ -0,0 +1,18 @@
1
+ import RbMap from ff:core
2
+
3
+ nodeMain(system: NodeSystem): Unit {
4
+ let values = [8, 3, 2, 1, 4, 7, 6, 5, 9]
5
+ let full = values.foldLeft(RbMap.E, {t, a =>
6
+ let t2 = RbMap.insert(a, "" + a, t)
7
+ Log.debug("Insert " + a + ": " + Show.show(t2.pairs()))
8
+ t2
9
+ })
10
+ values.foldLeft(full, {t, a =>
11
+ let t2 = RbMap.delete(a, t)
12
+ Log.debug("Delete " + a + ": " + Show.show(t2.pairs()))
13
+ t2
14
+ })
15
+
16
+ //RbMap.delete(1, RbMap.delete(1, RbMap.insert(1, "A", RbMap.E)))
17
+
18
+ }
@@ -0,0 +1,3 @@
1
+ compress(stream: Stream[Buffer]): Stream[Buffer] {panic("TODO")}
2
+
3
+ decompress(stream: Stream[Buffer]): Stream[Buffer] {panic("TODO")}
@@ -0,0 +1,34 @@
1
+ // Have: Gzipped .tar archives containing .xml files in S3.
2
+ // Want: Gzipped .tar archives containing .json files in S3.
3
+ // But: Too big to store it all in memory, and disk is too slow.
4
+ // Firefly iterators to the rescue!
5
+
6
+ convertFiles(bucket: S3Bucket): Unit {
7
+ bucket.listKeys(prefix = "xml/").each {key =>
8
+ let xmlTarGz = bucket.readStream(key)
9
+ let xmlTar = Gzip.decompress(xmlTarGz)
10
+ let entries = Tar.unarchive(xmlTar).map {entry =>
11
+ TarEntry(entry.name.replace(".xml", ".json"), process(entry))
12
+ }
13
+ let jsonTar = Tar.archive(entries)
14
+ let jsonTarGz = Gzip.compress(jsonTar)
15
+ bucket.writeStream(key.replace("xml/", "json/"), jsonTarGz)
16
+ )
17
+ }
18
+
19
+
20
+
21
+ Gzip.compress(stream: Stream[Buffer]): Stream[Buffer]
22
+ Gzip.decompress(stream: Stream[Buffer]): Stream[Buffer]
23
+ Tar.archive(stream: Stream[TarEntry]): Stream[Buffer]
24
+ Tar.unarchive(stream: Stream[Buffer]): Stream[TarEntry]
25
+ bucket.listKeys(prefix: String = ""): Stream[String]
26
+ bucket.readStream(key: String): Stream[Buffer]
27
+ bucket.writeStream(key: String, stream: Stream[Buffer]): Unit
28
+
29
+
30
+ class Stream[T](
31
+ next: () => Option[T]
32
+ close: () => Unit
33
+ )
34
+
@@ -0,0 +1,11 @@
1
+ data S3Bucket
2
+
3
+ extend self: S3Bucket {
4
+
5
+ listKeys(): Stream[String]
6
+
7
+ readStream(key: String): Stream[Buffer] {panic("TODO")}
8
+
9
+ writeStream(key: String, stream: Stream[Buffer]): Unit {panic("TODO")}
10
+
11
+ }
@@ -0,0 +1,5 @@
1
+ data TarEntry(name: String, stream: Stream[Buffer])
2
+
3
+ archive(stream: Stream[TarEntry]): Stream[Buffer] {panic("TODO")}
4
+
5
+ unarchive(stream: Stream[Buffer]): Stream[TarEntry] {panic("TODO")}
@@ -0,0 +1,81 @@
1
+ dependency ff:httpserver:0.0.0
2
+ import HttpServer from ff:httpserver
3
+
4
+ browserMain(system: BrowserSystem): Unit {
5
+
6
+ let document = system.js().global().get("document")
7
+ let input = document.call1("getElementById", "input")
8
+ let output = document.call1("getElementById", "output")
9
+ let button = document.call1("getElementById", "button")
10
+
11
+ let handler = system.js().function1 {event =>
12
+ let text = input.get("value").grabString()
13
+ let serialized = Serializable.serialize(text)
14
+ let result = system.httpClient().fetch(
15
+ url = "http://localhost:8080/rhyme"
16
+ method = "POST"
17
+ body = Some(HttpClient.bodyBuffer(serialized))
18
+ )
19
+ output.set("innerText", result.readText())
20
+ }
21
+
22
+ button.call2("addEventListener", "click", handler)
23
+
24
+ }
25
+
26
+ nodeMain(system: NodeSystem): Unit {
27
+ HttpServer.listen(system, "localhost", 8080) {request, response =>
28
+
29
+ if(request.path() == "/") {
30
+
31
+ response.setHeader("Content-Type", ["text/html; charset=UTF-8"])
32
+ response.writeText(system.assets().readText("/index.html"))
33
+
34
+ } elseIf {request.path().startsWith("/js/") && !request.path().contains("..")} {
35
+
36
+ let path = request.path().dropFirst("/js/".size())
37
+ response.setHeader("Content-Type", ["text/javascript"])
38
+ response.writeText(system.assets().readText("/browser/" + path))
39
+
40
+ } elseIf {request.path() == "/rhyme"} {
41
+
42
+ let buffer = request.readBuffer()
43
+ let deserialized = Serializable.deserialize(buffer)
44
+ let result = system.httpClient().fetch("https://api.datamuse.com/words?rel_rhy=" + deserialized)
45
+ let json = system.js().parseJson(result.readText())
46
+ let rhyme = json.get(0).get("word").grabString()
47
+ response.setHeader("Access-Control-Allow-Origin", ["*"])
48
+ response.writeText(rhyme)
49
+
50
+ } else {
51
+
52
+ response.writeStatus(404, Some("Not found"))
53
+
54
+ }
55
+
56
+ }
57
+ }
58
+
59
+ buildMain(system: BuildSystem) {
60
+ let browser: BrowserCode = system.compileForBrowser("Main.ff")
61
+ let browserAssets = if(system.buildMode()) {
62
+ browser.bundle().assets()
63
+ } else {
64
+ browser.assets()
65
+ }
66
+ let indexAssets = if(system.buildMode()) {
67
+ let html = system.packageAssets().readText("/index.html")
68
+ let newHtml = html.replace(
69
+ """<script type="module" src="/js/script/script/Main.mjs"></script>"""
70
+ """<script defer src="/js/Main.bundle.js"></script>"""
71
+ )
72
+ let stream = {[newHtml.toBuffer()].toStream()}
73
+ AssetSystem([Pair("/index.html", stream)].toMap())
74
+ } else {
75
+ system.packageAssets().asset("/index.html")
76
+ }
77
+ let assets = AssetSystem.create()
78
+ .addAssets("/", indexAssets)
79
+ .addAssets("/browser", browserAssets)
80
+ system.setAssets(assets)
81
+ }
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <script type="module" src="/js/script/script/Main.mjs"></script>
5
+ </head>
6
+ <body>
7
+ <h3>Find rhyme</h3>
8
+ <br>
9
+ <input id="input" value="fish">
10
+ <button id="button">Rhyme</button>
11
+ <br><br>
12
+ <div id="output"></div>
13
+ </body>
14
+ </html>
package/firefly.sh ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -e
3
+ firefly_link=`readlink -f "$0"`
4
+ firefly=`dirname "$firefly_link"`
5
+ node "$firefly/output/js/ff/compiler/Main.mjs" $@
@@ -0,0 +1,13 @@
1
+ dependency ff:httpserver:0.0.0
2
+ import HttpServer from ff:httpserver
3
+
4
+ nodeMain(system: NodeSystem): Unit {
5
+ let host = system.arguments().grab(0)
6
+ let port = system.arguments().grab(1).grabInt()
7
+ HttpServer.listen(system, host, port) {request, response =>
8
+ let parameters = request.parameters().map {"?" + _}.else {""}
9
+ response.setHeader("Location", ["https://www.firefly-lang.org" + request.path() + parameters])
10
+ response.writeStatus(302, Some("Found"))
11
+ }
12
+ }
13
+
@@ -0,0 +1 @@
1
+ package ff:httpserver:0.0.0
@@ -0,0 +1,184 @@
1
+ capability HttpRequest {}
2
+ capability HttpResponse {}
3
+
4
+ listen(system: NodeSystem, host: String, port: Int, handler: (HttpRequest, HttpResponse) => Unit): Unit
5
+ target node async """
6
+ import * as http from 'http'
7
+ ff_core_Task.Task_throwIfAborted($task)
8
+ const server = http.createServer(async (req, res) => {
9
+ try {
10
+ const channel = ff_core_Task.Task_channel($task)
11
+ ff_core_Task.Task_spawn($task, async task => {
12
+ try {
13
+ await handler_(req, res, task)
14
+ } finally {
15
+ await ff_core_Channel.Channel_write$(channel, null, task)
16
+ }
17
+ })
18
+ await ff_core_Channel.Channel_read$(channel, $task)
19
+ if(!res.headersSent) res.writeHead(200, 'OK')
20
+ } finally {
21
+ if(!res.headersSent) res.writeHead(500, 'Internal Server Error')
22
+ if(!res.writableEnded) res.end()
23
+ }
24
+ })
25
+ await new Promise((resolve, reject) => {
26
+ server.listen(port_, host_, error => {if(error != null) reject(error); else resolve()})
27
+ })
28
+ await new Promise((resolve, reject) => {
29
+ const abort = () => {
30
+ $task.controller.signal.removeEventListener('abort', abort)
31
+ const reason = $task.controller.signal.reason
32
+ $task.controller = new AbortController()
33
+ server.close(() => reject(reason))
34
+ }
35
+ $task.controller.signal.addEventListener('abort', abort)
36
+ })
37
+ """
38
+
39
+
40
+ extend self: HttpRequest {
41
+
42
+ method(): String
43
+ target node async "return self_.method"
44
+
45
+ path(): String
46
+ target node async """
47
+ let i = self_.url.indexOf('?')
48
+ return i === -1 ? self_.url : self_.url.slice(0, i)
49
+ """
50
+
51
+ parameters(): Option[String]
52
+ target node async """
53
+ let i = self_.url.indexOf('?')
54
+ return i === -1 ? ff_core_Option.None() : ff_core_Option.Some(self_.url.slice(i + 1))
55
+ """
56
+
57
+ parameter(name: String): Option[String]
58
+ target node async """
59
+ let i = self_.url.indexOf('?')
60
+ if(i === -1) return ff_core_Option.None()
61
+ let ps = self_.url.slice(i + 1).split('&')
62
+ let n = name_ + '='
63
+ let r = ps.find(p => p === name_ || p.startsWith(n))
64
+ if(r == null) return ff_core_Option.None()
65
+ return ff_core_Option.Some(r.slice(name_.length + 1))
66
+ """
67
+
68
+ header(name: String): Option[String]
69
+ target node async """
70
+ let header = self_.headers[name_.toLowerCase()]
71
+ if(header == null || header.length == 0) ff_core_Option.None()
72
+ return ff_core_Option.Some(Array.isArray(header) ? header[0] : header)
73
+ """
74
+
75
+ encoding(): Option[String]
76
+ target node async """
77
+ return self_.readableEncoding ? ff_core_Option.Some(self_.readableEncoding) : ff_core_Option.None()
78
+ """
79
+
80
+ readText(encoding: Option[String] = None): String {
81
+ self.readStream().toString(encoding.else {self.encoding().else {"utf8"}})
82
+ }
83
+
84
+ readBuffer(): Buffer {
85
+ self.readStream().toBuffer()
86
+ }
87
+
88
+ readStream(): Stream[Buffer]
89
+ target node async """
90
+ import * as http from 'http'
91
+ let task = null
92
+ let readable = self_
93
+ let doResolve = null
94
+ let doReject = null
95
+ let seenError = null
96
+ const abort = () => {
97
+ if(task != null) {
98
+ task.controller.signal.removeEventListener('abort', abort)
99
+ readable.destroy()
100
+ }
101
+ }
102
+ function open($task) {
103
+ ff_core_Task.Task_throwIfAborted($task)
104
+ if(self_.streamWorkaround != null) throw new Error("Can't open HttpRequest body stream twice")
105
+ task = $task
106
+ self_.streamWorkaround = true
107
+ readable.on('readable', () => {
108
+ if(doResolve != null) doResolve()
109
+ })
110
+ readable.on('error', error => {
111
+ task.controller.signal.removeEventListener('abort', abort)
112
+ seenError = error
113
+ if(doReject != null) doReject(error)
114
+ })
115
+ readable.on('close', () => {
116
+ task.controller.signal.removeEventListener('abort', abort)
117
+ if(doResolve != null) doResolve()
118
+ })
119
+ $task.controller.signal.addEventListener('abort', abort)
120
+ }
121
+ return ff_core_Stream.Stream(async function go($task) {
122
+ if(task == null) open($task)
123
+ let buffer = readable.read()
124
+ if(buffer != null) return ff_core_Option.Some(buffer)
125
+ if(seenError != null) throw seenError
126
+ if(readable.destroyed) return ff_core_Option.None()
127
+ let promise = new Promise((resolve, reject) => {
128
+ doResolve = () => {doResolve = null; doReject = null; resolve()}
129
+ doReject = error => {doResolve = null; doReject = null; reject(error)}
130
+ }).then(() => go($task))
131
+ return await promise
132
+ }, abort)
133
+ """
134
+
135
+ }
136
+
137
+ extend self: HttpResponse {
138
+
139
+ setHeader(name: String, values: List[String]): Unit
140
+ target node async "self_.setHeader(name_, ff_core_List.List_toArray(values_))"
141
+
142
+ writeStatus(code: Int, message: Option[String]): Unit
143
+ target node async "self_.writeHead(code_, message_.value_)"
144
+
145
+ write(buffer: Buffer): Unit
146
+ target node async """
147
+ import * as http from 'http'
148
+ ff_core_Task.Task_throwIfAborted($task)
149
+ if(!self_.write(buffer_)) {
150
+ let reject = null;
151
+ await new Promise((resolve, reject) => {
152
+ const abort = () => {
153
+ $task.controller.signal.removeEventListener('abort', abort)
154
+ reject($task.controller.signal.reason)
155
+ }
156
+ $task.controller.signal.addEventListener('abort', abort)
157
+ self_.once('drain', () => {
158
+ $task.controller.signal.removeEventListener('abort', abort)
159
+ resolve()
160
+ })
161
+ })
162
+ }
163
+ """
164
+
165
+ writeText(text: String, encoding: String = "utf8"): Unit
166
+ target node async """
167
+ import * as http from 'http'
168
+ ff_core_Task.Task_throwIfAborted($task)
169
+ if(!self_.write(text_, encoding_)) {
170
+ await new Promise((resolve, reject) => {
171
+ const abort = () => {
172
+ $task.controller.signal.removeEventListener('abort', abort)
173
+ reject($task.controller.signal.reason)
174
+ }
175
+ $task.controller.signal.addEventListener('abort', abort)
176
+ self_.once('drain', () => {
177
+ $task.controller.signal.removeEventListener('abort', abort)
178
+ resolve()
179
+ })
180
+ })
181
+ }
182
+ """
183
+
184
+ }
@@ -0,0 +1 @@
1
+ dependency ff:compiler:0.0.0