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,278 @@
1
+ import Token
2
+ import Syntax
3
+ import LspHook
4
+
5
+ tokenize(file: String, code: String, completionAt: Option[Location], attemptFixes: Bool): Array[Token] {
6
+
7
+ let completionLine = completionAt.filter {_.file == file}.map {_.line}.else {-1}
8
+ let completionColumn = completionAt.filter {_.file == file}.map {_.column}.else {-1}
9
+
10
+ let tokens = Stack.make[Token]()
11
+ mutable line = 1
12
+ mutable lineOffset = 0
13
+
14
+ mutable startLine = line
15
+ mutable startLineOffset = lineOffset
16
+
17
+ let operatorCharactersString = "!@#$%&/=?+|^~*<>.:-;"
18
+ mutable operatorCharacters = Set.empty[Char]()
19
+ List.range(operatorCharactersString.size()).map {j =>
20
+ operatorCharacters = operatorCharacters.add(operatorCharactersString.grab(j))
21
+ }
22
+
23
+ function emitToken(kind: TokenKind, startOffset: Int, stopOffset: Int): Unit {
24
+ if(!tokens.isEmpty()) {
25
+ let last = tokens.grabLast()
26
+ if(last.stopLine == startLine && last.kind == LLower && kind.afterKeyword()) {
27
+ if(
28
+ completionLine == last.startLine &&
29
+ completionColumn >= 1 + last.startOffset - last.startLineOffset &&
30
+ completionColumn <= 1 + last.stopOffset - last.stopLineOffset
31
+ ) {
32
+ tokens.push(Token(
33
+ file, code, LSeparator
34
+ startLine, startLineOffset, startLineOffset
35
+ startLine, startLineOffset, startLineOffset
36
+ ))
37
+ } else {
38
+ tokens.modify(tokens.size() - 1) {_.Token(kind = LKeyword)}
39
+ }
40
+ }
41
+ if(last.stopLine != startLine && last.kind.beforeSeparator() && kind.afterSeparator()) {
42
+ tokens.push(Token(
43
+ file, code, LSeparator
44
+ startLine, startLineOffset, startLineOffset
45
+ startLine, startLineOffset, startLineOffset
46
+ ))
47
+ }
48
+ }
49
+ tokens.push(Token(
50
+ file, code, kind
51
+ startLine, startLineOffset, startOffset
52
+ line, lineOffset, stopOffset
53
+ ))
54
+ }
55
+
56
+ mutable i = 0
57
+
58
+ function throwError(message: String) {
59
+ let column = (i - startLineOffset) + 1
60
+ throw(CompileError(
61
+ Location(file, line, column)
62
+ message
63
+ ))
64
+ }
65
+
66
+ try {
67
+ while {i < code.size()} {
68
+
69
+ startLine = line
70
+ startLineOffset = lineOffset
71
+
72
+ if(completionLine == line) {
73
+ while {i < code.size() && (code.grab(i) == ' ' || code.grab(i) == '\t' || code.grab(i) == '\r')} {
74
+ if(completionColumn == 1 + i - lineOffset) {
75
+ emitToken(LLower, i, i)
76
+ }
77
+ i += 1
78
+ }
79
+ if(i < code.size() && completionColumn == 1 + i - lineOffset) {
80
+ let c = code.grab(i)
81
+ if(!c.isAsciiLetterOrDigit() && c != '_' && c != '\'' && c != '"') {
82
+ emitToken(LLower, i, i)
83
+ if(i + 1 < code.size() && code.grab(i) == '_') {i += 1}
84
+ }
85
+ }
86
+ Unit // Think about how to avoid requiring this
87
+ } else {
88
+ while {i < code.size() && (code.grab(i) == ' ' || code.grab(i) == '\t' || code.grab(i) == '\r')} {i += 1}
89
+ }
90
+
91
+ if(i < code.size()):
92
+
93
+ let start = i
94
+
95
+ if(code.grab(i) == '\n') {
96
+
97
+ i += 1
98
+ line += 1
99
+ lineOffset = i
100
+
101
+ } elseIf {code.grab(i) == '/' && code.grab(i + 1) == '/'} {
102
+
103
+ i += 2
104
+ while {i < code.size() && code.grab(i) != '\n'} {i += 1}
105
+
106
+ } elseIf {code.grab(i) == '/' && code.grab(i + 1) == '*'} {
107
+
108
+ i += 2
109
+ while {i < code.size() && (code.grab(i) != '*' || code.grab(i + 1) != '/')} {
110
+ if(i >= code.size()) {
111
+ throwError(
112
+ "Expected end of comment started on line " + startLine + ", got end of file."
113
+ )
114
+ }
115
+ if(code.grab(i) == '\n') {
116
+ line += 1
117
+ lineOffset = i + 1
118
+ }
119
+ i += 1
120
+ }
121
+ i += 2
122
+
123
+ } elseIf {code.grab(i) == '"' || code.grab(i) == '\''} {
124
+
125
+ let endSign = code.grab(i)
126
+
127
+ mutable multiLine = i + 2 < code.size() &&
128
+ code.grab(i) == '"' && code.grab(i + 1) == '"' && code.grab(i + 2) == '"'
129
+
130
+ i += if(multiLine) {3} else {1}
131
+ while {i < code.size() && (multiLine || code.grab(i) != endSign)} {
132
+ if(code.grab(i) == '\n') {
133
+ if(multiLine) {
134
+ line += 1
135
+ lineOffset = i + 1
136
+ } else {
137
+ throwError(
138
+ "Unexpected end of line in string."
139
+ )
140
+ }
141
+ }
142
+ if(i >= code.size()) {
143
+ throwError(
144
+ "Expected end of string started on line " + startLine + ", got end of file."
145
+ )
146
+ }
147
+ if(code.grab(i) == '\\' && code.grab(i + 1) != '\n') {i += 1}
148
+ if(multiLine &&
149
+ i + 2 < code.size() && (i + 3 >= code.size() || code.grab(i + 3) != '"') &&
150
+ code.grab(i) == '"' && code.grab(i + 1) == '"' && code.grab(i + 2) == '"'
151
+ ) {
152
+ multiLine = False
153
+ i += 2
154
+ } else {
155
+ i += 1
156
+ }
157
+ }
158
+ i += 1
159
+ emitToken(if(endSign == '"') {LString} else {LChar}, start, i)
160
+
161
+ } elseIf {code.grab(i).isAsciiLetter()} {
162
+
163
+ let kind = if(code.grab(i) >= 'a') {LLower} else {LUpper}
164
+ i += 1
165
+ while {i < code.size() && code.grab(i).isAsciiLetterOrDigit()} {i += 1}
166
+ if(i < code.size() && kind == LUpper && code.grab(i) == '.') {
167
+ i += 1
168
+ emitToken(LNamespace, start, i)
169
+ } else {
170
+ emitToken(kind, start, i)
171
+ }
172
+
173
+ } elseIf {code.grab(i).isAsciiDigit()} {
174
+
175
+ mutable dot = False
176
+ mutable exponent = False
177
+ while {i < code.size() && code.grab(i).isAsciiDigit()} {
178
+ i += 1
179
+ if((code.grab(i) == 'e' || code.grab(i) == 'E') && !exponent) {
180
+ i += 1
181
+ dot = True
182
+ exponent = True
183
+ if(code.grab(i) == '+' || code.grab(i) == '-') {i += 1}
184
+ }
185
+ if(
186
+ i + 1 < code.size() && code.grab(i) == '.' &&
187
+ code.grab(i + 1).isAsciiDigit() &&
188
+ !dot && !exponent
189
+ ) {
190
+ i += 1
191
+ dot = True
192
+ }
193
+ }
194
+ emitToken(if(dot || exponent) {LFloat} else {LInt}, start, i)
195
+
196
+ } elseIf {code.grab(i) == '_'} {
197
+
198
+ i += 1
199
+ emitToken(LWildcard, start, i)
200
+
201
+ } elseIf {code.grab(i) == ','} {
202
+
203
+ i += 1
204
+ emitToken(LComma, start, i)
205
+
206
+ } elseIf {operatorCharacters.contains(code.grab(i))} {
207
+
208
+ i += 1
209
+
210
+ if(code.grab(i - 1) == '.' && i + 1 < code.size() && code.grab(i) == '.' && code.grab(i + 1) != '.') {
211
+ emitToken(LDot, start, i)
212
+ let newStart = i
213
+ if(!completionAt.isEmpty()) {emitToken(LLower, newStart, newStart)}
214
+ i += 1
215
+ emitToken(LDot, newStart, i)
216
+ } else:
217
+
218
+ while {i < code.size() && operatorCharacters.contains(code.grab(i))} {i += 1}
219
+ let o =
220
+ if(i - start == 1 && code.grab(i - 1) == '.') {
221
+ LDot
222
+ } elseIf {i - start == 1 && code.grab(i - 1) == ';'} {
223
+ LSemicolon
224
+ } elseIf {i - start == 1 && code.grab(i - 1) == '|'} {
225
+ LPipe
226
+ } elseIf {i - start == 1 && code.grab(i - 1) == ':'} {
227
+ LColon
228
+ } elseIf {i - start == 3 && code.grab(i - 3) == '.' && code.grab(i - 2) == '.' && code.grab(i - 1) == '.'} {
229
+ LDotDotDot
230
+ } elseIf {i - start == 2 && code.grab(i - 2) == '=' && code.grab(i - 1) == '>'} {
231
+ LArrowThick
232
+ } elseIf {i - start == 1 && code.grab(i - 1) == '='} {
233
+ LAssign
234
+ } elseIf {i - start == 2 && code.grab(i - 2) == '+' && code.grab(i - 1) == '='} {
235
+ LAssignPlus
236
+ } elseIf {i - start == 2 && code.grab(i - 2) == '-' && code.grab(i - 1) == '='} {
237
+ LAssignMinus
238
+ } elseIf {i - start == 3 && code.grab(i - 3) == ':' && code.grab(i - 2) == ':' && code.grab(i - 1) == '='} {
239
+ LAssignLink
240
+ } else {
241
+ LOperator
242
+ }
243
+
244
+ emitToken(o, start, i)
245
+
246
+ } elseIf {
247
+ code.grab(i) == '(' || code.grab(i) == '[' || code.grab(i) == '{'
248
+ } {
249
+
250
+ i += 1
251
+ emitToken(LBracketLeft, start, i)
252
+
253
+ } elseIf {
254
+ code.grab(i) == ')' || code.grab(i) == ']' || code.grab(i) == '}'
255
+ } {
256
+
257
+ i += 1
258
+ emitToken(LBracketRight, start, i)
259
+
260
+ } elseIf {
261
+ i < code.size()
262
+ } {
263
+
264
+ if(attemptFixes) {i += 1} else:
265
+ throwError("Unexpected character: " + Show.show(code.grab(i)))
266
+
267
+ }
268
+
269
+ }
270
+ } catch {| GrabException e, error =>
271
+ throw(CompileError(Location(file, line, i - lineOffset), "Unexpected end of file"))
272
+ } grab()
273
+
274
+ List.range(5).each {_ => emitToken(LEnd, i, i) }
275
+
276
+ tokens.drain()
277
+
278
+ }
@@ -0,0 +1,220 @@
1
+ import Syntax
2
+ import Inference
3
+
4
+ class Unification(
5
+ mutable substitution: Map[Int, Type]
6
+ mutable constraints: Map[Int, Map[String, ConstraintGenerics]]
7
+ mutable nextUnificationVariableIndex: Int
8
+ mutable instances: Map[InstanceKey, InstanceValue]
9
+ mutable affects: Map[Int, Set[Int]]
10
+ attemptFixes: Bool
11
+ )
12
+
13
+ data ConstraintGenerics(at: Location, generics: List[Type])
14
+
15
+ data InstanceKey(traitName: String, typeName: String)
16
+
17
+ data InstanceValue(
18
+ generics: List[String]
19
+ constraints: List[Constraint]
20
+ packagePair: PackagePair
21
+ moduleName: String
22
+ traitName: String
23
+ typeArguments: List[Type]
24
+ )
25
+
26
+ fail[T](at: Location, message: String): T {
27
+ throw(CompileError(at, message))
28
+ }
29
+
30
+ make(modules: List[Module], attemptFixes: Bool): Unification {
31
+
32
+ Unification(
33
+ Map.empty()
34
+ Map.empty()
35
+ 3 // To avoid collision with the parser and resolver
36
+ modules.flatMap {module =>
37
+ let moduleName = module.file.dropLast(".ff".size())
38
+ module.instances.map {definition =>
39
+ let typeName = definition.typeArguments.grabFirst().{
40
+ | TConstructor(_, name, _) => name
41
+ | TVariable(_, i) => fail(definition.at, "Unexpected unification variable: $" + i)
42
+ }
43
+ Pair(
44
+ InstanceKey(definition.traitName, typeName)
45
+ InstanceValue(
46
+ generics = definition.generics
47
+ constraints = definition.constraints
48
+ packagePair = module.packagePair
49
+ moduleName = moduleName
50
+ traitName = definition.traitName
51
+ typeArguments = definition.typeArguments
52
+ )
53
+ )
54
+ }
55
+ }.toMap()
56
+ [].toMap()
57
+ attemptFixes
58
+ )
59
+ }
60
+
61
+ extend self: Unification {
62
+
63
+ withLocalInstances[T](instances: Map[InstanceKey, InstanceValue], body: () => T): T {
64
+ let oldInstances = self.instances
65
+ self.instances = self.instances.addAll(instances)
66
+ try {
67
+ body()
68
+ } finally {
69
+ self.instances = oldInstances
70
+ } grab()
71
+ }
72
+
73
+ freshUnificationVariable(at: Location): Type {
74
+ let result = TVariable(at, self.nextUnificationVariableIndex)
75
+ self.nextUnificationVariableIndex += 3
76
+ result
77
+ }
78
+
79
+ instantiate(instantiation: Map[String, Type], type: Type): Type {
80
+ | _, TConstructor(at, name, []) =>
81
+ instantiation.get(name).{
82
+ | Some(t) => t
83
+ | None => type
84
+ }
85
+ | _, TConstructor(at, name, generics) =>
86
+ TConstructor(at, name, generics.map {self.instantiate(instantiation, _)})
87
+ | _, TVariable(_, i) {self.get(i) | Some(t)} =>
88
+ self.instantiate(instantiation, t)
89
+ | _, TVariable(_, i) =>
90
+ type
91
+ }
92
+
93
+ instantiateConstraint(instantiation: Map[String, Type], constraint: Constraint): Constraint {
94
+ | _, Constraint(at, name, generics) =>
95
+ Constraint(at, name, generics.map {self.instantiate(instantiation, _)})
96
+ }
97
+
98
+ constrain(at: Location, type: Type, constraintName: String, generics: List[Type]): Unit {
99
+ type.{
100
+ | TVariable(_, i) {self.get(i) | Some(t)} =>
101
+ self.constrain(at, t, constraintName, generics)
102
+ | TVariable(_, i) =>
103
+ self.constraints.get(i).{
104
+ | None =>
105
+ self.constraints = self.constraints.add(
106
+ i, [Pair(constraintName, ConstraintGenerics(at, generics))].toMap()
107
+ )
108
+ | Some(map) =>
109
+ map.get(constraintName).{
110
+ | None =>
111
+ let newMap = map.add(constraintName, ConstraintGenerics(at, generics))
112
+ self.constraints = self.constraints.add(i, newMap)
113
+ | Some(ConstraintGenerics(_, generics2)) =>
114
+ generics.zip(generics2).each {| Pair(t1, t2) => self.unify(at, t1, t2) }
115
+ }
116
+ }
117
+ | TConstructor(_, name, generics2) =>
118
+ self.instances.get(InstanceKey(constraintName, name)).{
119
+ | None =>
120
+ let g1 = if(generics.isEmpty()) {""} else {"[...]"}
121
+ let g2 = if(generics2.isEmpty()) {""} else {"[...]"}
122
+ if(!self.attemptFixes) {
123
+ throw(CompileError(at, "No such instance: " + name + g2 + ": " + constraintName + g1))
124
+ }
125
+ | Some(definition) =>
126
+ let unificationVariables = definition.generics.map {_ => self.freshUnificationVariable(at) }
127
+ let instantiation = definition.generics.zip(unificationVariables).toMap()
128
+ let traitType1 = self.instantiate(instantiation,
129
+ TConstructor(at, definition.traitName, definition.typeArguments)
130
+ )
131
+ let traitType2 = TConstructor(at, constraintName, [type, ...generics])
132
+ self.unify(at, traitType1, traitType2)
133
+ definition.constraints.each {constraint =>
134
+ self.instantiateConstraint(instantiation, constraint).{
135
+ | Constraint(_, constraintName, newGenerics) =>
136
+ self.constrain(at, newGenerics.grabFirst(), constraintName, newGenerics.dropFirst())
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ get(index: Int): Option[Type] {
144
+ self.substitution.get(index).map {
145
+ | TVariable(_, i) {self.substitution.get(i) | Some(t)} =>
146
+ self.substitution = self.substitution.add(index, t)
147
+ t
148
+ | t => t
149
+ }
150
+ }
151
+
152
+ substitute(type: Type): Type {
153
+ | TVariable(_, i) {self.get(i) | Some(t)} => self.substitute(t)
154
+ | TVariable(_, _) => type
155
+ | TConstructor t => t.TConstructor(generics = t.generics.map {t => self.substitute(t)})
156
+ }
157
+
158
+ unify(at: Location, t1: Type, t2: Type): Unit {
159
+ | _, TVariable(_, i1), TVariable(_, i2) {i1 == i2} =>
160
+ | _, TVariable(_, i), _ {self.get(i) | Some(t)} => self.unify(at, t, t2)
161
+ | _, _, TVariable(_, i) {self.get(i) | Some(t)} => self.unify(at, t1, t)
162
+ | _, TVariable(_, i), _ => self.bind(at, i, t2)
163
+ | _, _, TVariable(_, i) => self.bind(at, i, t1)
164
+ | _, TConstructor(_, name1, generics1), TConstructor(_, name2, generics2) =>
165
+ if(name1 != name2 || generics1.size() != generics2.size()) {
166
+ if(!self.attemptFixes) {
167
+ let t3 = self.substitute(t1)
168
+ let t4 = self.substitute(t2)
169
+ throw(CompileError(at
170
+ "Type mismatch: " + t3.show([t3, t4]) + " vs. " + t4.show([t3, t4])
171
+ ))
172
+ } elseIf {name1.startsWith("Function$") && name2.startsWith("Function$")} {
173
+ generics1.dropLast().zip(generics2.dropLast()).each {| Pair(t3, t4) => self.unify(at, t3, t4)}
174
+ generics1.takeLast().zip(generics2.takeLast()).each {| Pair(t3, t4) => self.unify(at, t3, t4)}
175
+ }
176
+ Unit // TODO: Think about how to avoid writing this
177
+ } else {
178
+ generics1.zip(generics2).each {| Pair(t1, t2) => self.unify(at, t1, t2) }
179
+ }
180
+ }
181
+
182
+ bind(at: Location, index: Int, type: Type): Unit {
183
+ if(self.occursIn(index, type)) {
184
+ let t = self.substitute(type)
185
+ throw(CompileError(at, "Infinite type: " + TVariable(at, index).show([t]) + " = " + t.show([])))
186
+ }
187
+ self.substitution = self.substitution.add(index, type)
188
+ self.constraints.get(index).each {map =>
189
+ self.constraints = self.constraints.remove(index)
190
+ map.pairs().each {| Pair(name, ConstraintGenerics(at2, generics)) =>
191
+ self.constrain(at2, type, name, generics)
192
+ }
193
+ }
194
+ self.affects.get(index).each {affected =>
195
+ self.affects.remove(index)
196
+ affected.each {i =>
197
+ self.affect(at, type, TVariable(at, i))
198
+ }
199
+ }
200
+ }
201
+
202
+ affect(at: Location, source: Type, target: Type): Unit {
203
+ Pair(self.substitute(source), self.substitute(target)).{
204
+ | Pair(TVariable(_, i1), TVariable(_, i2)) =>
205
+ let is = self.affects.get(i1).else([].toSet)
206
+ self.affects = self.affects.add(i1, is.add(i2))
207
+ | Pair(_, TConstructor(_, "Q$", _)) =>
208
+ | Pair(TConstructor(_, "ff:core/Nothing.Nothing", _), _) =>
209
+ | Pair(t1, t2) =>
210
+ self.unify(at, t1, t2)
211
+ }
212
+ }
213
+
214
+ occursIn(index: Int, t: Type): Bool {
215
+ | _, TVariable(_, i) {self.get(i) | Some(type)} => self.occursIn(index, type)
216
+ | _, TVariable(_, i) => i == index
217
+ | _, TConstructor(_, _, generics) => generics.any {t => self.occursIn(index, t)}
218
+ }
219
+
220
+ }
@@ -0,0 +1,50 @@
1
+ import Syntax
2
+
3
+ class Wildcards(mutable seenWildcards : Int)
4
+
5
+ make(): Wildcards {
6
+ Wildcards(0)
7
+ }
8
+
9
+ fail[T](at: Location, message: String): T {
10
+ panic(message + " " + at.show())
11
+ }
12
+
13
+ extend self: Wildcards {
14
+
15
+ fixWildcards(term: Term): Term {
16
+ | ELet e => e.ELet(value = self.fixWildcards(e.value), body = self.fixWildcards(e.body))
17
+ | ESequential e => e.ESequential(before = self.fixWildcards(e.before), after = self.fixWildcards(e.after))
18
+ | EAssign e => e.EAssign(value = self.fixWildcards(e.value))
19
+ | EAssignField e => e.EAssignField(record = self.fixWildcards(e.record), value = self.fixWildcards(e.value))
20
+ | EPipe e => e.EPipe(value = self.fixWildcards(e.value), function = self.fixWildcards(e.function))
21
+ | ECall e =>
22
+ e.ECall(
23
+ target = e.target.{
24
+ | DynamicCall call => call.DynamicCall(function = self.fixWildcards(call.function))
25
+ | _ => fail(e.at, "Internal error: Static calls not expected in the Wildcards phase")
26
+ },
27
+ arguments = e.arguments.map { a => a.Argument(value = self.fixWildcards(a.value)) }
28
+ )
29
+ | EList e =>
30
+ e.EList(items = e.items.map { | Pair(item, spread) =>
31
+ Pair(self.fixWildcards(item), spread)
32
+ })
33
+ | ECopy e =>
34
+ e.ECopy(
35
+ record = self.fixWildcards(e.record),
36
+ arguments = e.arguments.map { a => a.Field(value = self.fixWildcards(a.value)) }
37
+ )
38
+ | EVariant e =>
39
+ e.EVariant(arguments = e.arguments.map { _.map { a =>
40
+ a.Argument(value = self.fixWildcards(a.value))
41
+ }})
42
+ | ERecord e => e.ERecord(fields = e.fields.map { a => a.Field(value = self.fixWildcards(a.value)) })
43
+ | EField e => e.EField(record = self.fixWildcards(e.record))
44
+ | EWildcard e =>
45
+ self.seenWildcards += 1
46
+ e.EWildcard(index = self.seenWildcards)
47
+ | _ => term
48
+ }
49
+
50
+ }
@@ -0,0 +1,88 @@
1
+ import Syntax
2
+
3
+ data Workspace(
4
+ rules: List[WorkspaceRule]
5
+ defaultLocation: String
6
+ packageDirectory: String
7
+ )
8
+
9
+ data WorkspaceRule(
10
+ packageGroup: String
11
+ packageName: Option[String]
12
+ location: String
13
+ )
14
+
15
+ centralLocation = "https://www.firefly-lang.org/packages/"
16
+
17
+ loadWorkspace(path: Path): Workspace {
18
+ let packageDirectory = if(path.extension() == ".ff") {path.parent().grab()} else {path}
19
+ let workspaceFile = packageDirectory.slash(".firefly-workspace")
20
+ if(workspaceFile.exists()) {
21
+ parseWorkspaceFile(workspaceFile, packageDirectory)
22
+ } elseIf {!packageDirectory.parent().isEmpty()} {
23
+ loadWorkspace(packageDirectory.parent().grab())
24
+ } else {
25
+ Workspace([], centralLocation, ".")
26
+ }
27
+ }
28
+
29
+ parseWorkspaceFile(path: Path, packageDirectory: Path): Workspace {
30
+ let text = path.readText()
31
+ mutable defaultLocation = None
32
+ let lines = text.split('\n').toList().map {_.replace("\r", "").takeWhile {_ != '#'}}.filter {_.size() != 0}
33
+ let rules = lines.collect {line =>
34
+ let columns = line.replace("\t", " ").split(' ').toList().filter {_.size() != 0}
35
+ defaultLocation.each {_ =>
36
+ panic("Unexpected rule after the * rule: " + line)
37
+ }
38
+ if(columns.size() != 2) {
39
+ panic("Could not parse workspace rule: " + line)
40
+ }
41
+ let package = columns.grab(0)
42
+ let location = columns.grab(1)
43
+ let fixedLocation = if(location.endsWith("/")) {location} else {location + "/"}
44
+ if(package == "*") {
45
+ defaultLocation = Some(fixedLocation)
46
+ None
47
+ } else {
48
+ let packageParts = package.split(':')
49
+ if(packageParts.size() != 2) {
50
+ panic("Could not parse workspace package: " + package)
51
+ }
52
+ if(packageParts.grab(0) == "*") {
53
+ panic("Unexpected wildcard: " + package)
54
+ }
55
+ Some(WorkspaceRule(
56
+ packageGroup = packageParts.grab(0)
57
+ packageName = if(packageParts.grab(1) != "*") {packageParts.grab(1)}
58
+ location = fixedLocation
59
+ ))
60
+ }
61
+ }
62
+ Workspace(rules, defaultLocation.else {centralLocation}, packageDirectory.absolute())
63
+ }
64
+
65
+ extend self: Workspace {
66
+
67
+ findPackageLocation(packagePair: PackagePair, version: Version): String {
68
+ self.rules.find {rule =>
69
+ rule.packageGroup == packagePair.group && rule.packageName.all {_ == packagePair.name}
70
+ }.map {rule =>
71
+ let prefix = if(rule.packageName == None) {packagePair.name + "/"} else {""}
72
+ if(rule.location.contains(":")) {
73
+ rule.location + prefix + tarGzName(packagePair, version)
74
+ } else {
75
+ self.packageDirectory + "/" + rule.location + prefix
76
+ }
77
+ }.else {
78
+ self.defaultLocation + packagePair.group + "/" + packagePair.name + "/" +
79
+ tarGzName(packagePair, version)
80
+ }
81
+ }
82
+
83
+ }
84
+
85
+ tarGzName(packagePair: PackagePair, version: Version): String {
86
+ packagePair.group + "_" + packagePair.name + "_" +
87
+ version.major + "_" + version.minor + "_" + version.patch + ".tar.gz"
88
+ }
@@ -0,0 +1,2 @@
1
+ package ff:core:0.0.0
2
+ include "node_modules"
package/core/Any.ff ADDED
@@ -0,0 +1,30 @@
1
+ data Any {}
2
+ data AnyTag[T] {}
3
+
4
+ trait T: HasAnyTag {
5
+ anyTag(): AnyTag[T]
6
+ }
7
+
8
+ toAny[T: HasAnyTag](value: T): Any
9
+ target js sync """
10
+ const anyTag = ff_core_Any_HasAnyTag$T.anyTag_()
11
+ return {anyTag: anyTag, value: value_}
12
+ """
13
+
14
+ fromAny[T: HasAnyTag](any: Any): Option[T]
15
+ target js sync """
16
+ const anyTag = ff_core_Any_HasAnyTag$T.anyTag_()
17
+ return any_.anyTag === anyTag ? ff_core_Option.Some(any_.value) : ff_core_Option.None()
18
+ """
19
+
20
+ extend self[T]: AnyTag[T] {
21
+ show(): String
22
+ target js sync """
23
+ return self_
24
+ """
25
+ }
26
+
27
+ internalAnyTag[T](tag: String): AnyTag[T]
28
+ target js sync """
29
+ return tag_
30
+ """