firefly-compiler 0.4.17 → 0.4.19
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.
- package/compiler/Builder.ff +1 -1
- package/compiler/Compiler.ff +6 -5
- package/compiler/Inference.ff +31 -19
- package/compiler/JsEmitter.ff +98 -71
- package/compiler/JsImporter.ff +1 -1
- package/compiler/LspHook.ff +4 -4
- package/compiler/Main.ff +6 -6
- package/compiler/Parser.ff +39 -39
- package/compiler/Patterns.ff +2 -0
- package/compiler/Syntax.ff +1 -1
- package/compiler/Tokenizer.ff +2 -2
- package/compiler/Workspace.ff +2 -2
- package/core/Array.ff +135 -294
- package/core/Buffer.ff +3 -3
- package/core/BuildSystem.ff +1 -1
- package/core/Equal.ff +36 -52
- package/core/HttpClient.ff +1 -1
- package/core/IntMap.ff +14 -18
- package/core/JsSystem.ff +1 -1
- package/core/JsValue.ff +6 -12
- package/core/Json.ff +19 -28
- package/core/List.ff +281 -312
- package/core/Map.ff +4 -8
- package/core/NodeSystem.ff +2 -2
- package/core/Option.ff +0 -4
- package/core/Ordering.ff +10 -6
- package/core/Pair.ff +0 -4
- package/core/Random.ff +12 -26
- package/core/RbMap.ff +216 -216
- package/core/Serializable.ff +9 -18
- package/core/Set.ff +0 -1
- package/core/SourceLocation.ff +1 -1
- package/core/Stack.ff +32 -45
- package/core/Stream.ff +10 -14
- package/core/String.ff +24 -6
- package/core/StringMap.ff +15 -19
- package/guide/Main.ff +20 -2
- package/lsp/CompletionHandler.ff +4 -4
- package/lsp/Handler.ff +45 -34
- package/lsp/HoverHandler.ff +2 -2
- package/lsp/LanguageServer.ff +2 -2
- package/lsp/SignatureHelpHandler.ff +1 -1
- package/lsp/SymbolHandler.ff +1 -1
- package/lux/Lux.ff +3 -3
- package/output/js/ff/compiler/Builder.mjs +19 -21
- package/output/js/ff/compiler/Compiler.mjs +18 -20
- package/output/js/ff/compiler/Dependencies.mjs +8 -10
- package/output/js/ff/compiler/Deriver.mjs +234 -236
- package/output/js/ff/compiler/Dictionaries.mjs +6 -8
- package/output/js/ff/compiler/Environment.mjs +42 -44
- package/output/js/ff/compiler/Inference.mjs +346 -304
- package/output/js/ff/compiler/JsEmitter.mjs +907 -833
- package/output/js/ff/compiler/JsImporter.mjs +0 -2
- package/output/js/ff/compiler/LspHook.mjs +10 -12
- package/output/js/ff/compiler/Main.mjs +109 -111
- package/output/js/ff/compiler/Parser.mjs +405 -407
- package/output/js/ff/compiler/Patterns.mjs +64 -50
- package/output/js/ff/compiler/Resolver.mjs +36 -38
- package/output/js/ff/compiler/Substitution.mjs +4 -6
- package/output/js/ff/compiler/Syntax.mjs +160 -162
- package/output/js/ff/compiler/Token.mjs +52 -54
- package/output/js/ff/compiler/Tokenizer.mjs +16 -18
- package/output/js/ff/compiler/Unification.mjs +24 -26
- package/output/js/ff/compiler/Wildcards.mjs +0 -2
- package/output/js/ff/compiler/Workspace.mjs +18 -20
- package/output/js/ff/core/Any.mjs +0 -2
- package/output/js/ff/core/Array.mjs +216 -613
- package/output/js/ff/core/AssetSystem.mjs +2 -4
- package/output/js/ff/core/Atomic.mjs +0 -2
- package/output/js/ff/core/Bool.mjs +0 -2
- package/output/js/ff/core/Box.mjs +0 -2
- package/output/js/ff/core/BrowserSystem.mjs +0 -2
- package/output/js/ff/core/Buffer.mjs +0 -2
- package/output/js/ff/core/BuildSystem.mjs +12 -14
- package/output/js/ff/core/Channel.mjs +0 -2
- package/output/js/ff/core/Char.mjs +0 -2
- package/output/js/ff/core/Core.mjs +0 -2
- package/output/js/ff/core/Duration.mjs +0 -2
- package/output/js/ff/core/Equal.mjs +0 -22
- package/output/js/ff/core/Error.mjs +0 -2
- package/output/js/ff/core/FileHandle.mjs +0 -2
- package/output/js/ff/core/Float.mjs +0 -2
- package/output/js/ff/core/HttpClient.mjs +2 -4
- package/output/js/ff/core/Instant.mjs +0 -2
- package/output/js/ff/core/Int.mjs +8 -10
- package/output/js/ff/core/IntMap.mjs +32 -42
- package/output/js/ff/core/JsSystem.mjs +1 -3
- package/output/js/ff/core/JsValue.mjs +5 -12
- package/output/js/ff/core/Json.mjs +23 -56
- package/output/js/ff/core/List.mjs +648 -1989
- package/output/js/ff/core/Lock.mjs +0 -2
- package/output/js/ff/core/Log.mjs +0 -2
- package/output/js/ff/core/Map.mjs +10 -20
- package/output/js/ff/core/NodeSystem.mjs +6 -8
- package/output/js/ff/core/Nothing.mjs +0 -2
- package/output/js/ff/core/Option.mjs +8 -18
- package/output/js/ff/core/Ordering.mjs +20 -98
- package/output/js/ff/core/Pair.mjs +6 -16
- package/output/js/ff/core/Path.mjs +12 -14
- package/output/js/ff/core/Random.mjs +24 -54
- package/output/js/ff/core/RbMap.mjs +54 -56
- package/output/js/ff/core/Serializable.mjs +19 -36
- package/output/js/ff/core/Set.mjs +0 -14
- package/output/js/ff/core/Show.mjs +0 -2
- package/output/js/ff/core/SourceLocation.mjs +0 -2
- package/output/js/ff/core/Stream.mjs +34 -44
- package/output/js/ff/core/String.mjs +31 -5
- package/output/js/ff/core/StringMap.mjs +32 -42
- package/output/js/ff/core/Task.mjs +0 -2
- package/output/js/ff/core/Try.mjs +0 -2
- package/output/js/ff/core/Unit.mjs +0 -2
- package/package.json +1 -1
- package/vscode/package.json +1 -1
- package/webserver/WebServer.ff +8 -8
- package/output/js/ff/core/Stack.mjs +0 -603
package/compiler/Builder.ff
CHANGED
package/compiler/Compiler.ff
CHANGED
|
@@ -23,8 +23,8 @@ capability Compiler(
|
|
|
23
23
|
mutable derivedModules: Map[String, Module]
|
|
24
24
|
mutable inferredModules: Map[String, Module]
|
|
25
25
|
mutable emittedModules: Set[String]
|
|
26
|
-
mutable phaseDurations: List[Pair[String, Duration]]
|
|
27
26
|
mutable phaseDurationDelta: Duration
|
|
27
|
+
phaseDurations: Array[Pair[String, Duration]]
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
make(
|
|
@@ -50,8 +50,8 @@ make(
|
|
|
50
50
|
derivedModules = Map.empty()
|
|
51
51
|
inferredModules = Map.empty()
|
|
52
52
|
emittedModules = Set.empty()
|
|
53
|
-
phaseDurations = []
|
|
54
53
|
phaseDurationDelta = Duration(0.0)
|
|
54
|
+
phaseDurations = [].toArray()
|
|
55
55
|
)
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -100,7 +100,6 @@ coreImports: List[DImport] =
|
|
|
100
100
|
"Set"
|
|
101
101
|
"Show"
|
|
102
102
|
"SourceLocation"
|
|
103
|
-
"Stack"
|
|
104
103
|
"Stream"
|
|
105
104
|
"String"
|
|
106
105
|
"StringMap"
|
|
@@ -126,12 +125,14 @@ extend self: Compiler {
|
|
|
126
125
|
let duration = Duration(stop.seconds - start.seconds)
|
|
127
126
|
self.phaseDurationDelta = Duration(self.phaseDurationDelta.seconds + duration.seconds)
|
|
128
127
|
let text = phase + " " + packagePair.groupName() + "/" + moduleName
|
|
129
|
-
self.phaseDurations
|
|
128
|
+
self.phaseDurations.push(Pair(text, duration))
|
|
130
129
|
result
|
|
131
130
|
}
|
|
132
131
|
|
|
133
132
|
printMeasurements(): Unit {
|
|
134
|
-
let worst = self.phaseDurations.sortBy {
|
|
133
|
+
let worst = self.phaseDurations.toList().sortBy {
|
|
134
|
+
(_.second.seconds + 1000000.0) + ""
|
|
135
|
+
}.takeLast(5).reverse()
|
|
135
136
|
worst.each {| Pair(text, duration) =>
|
|
136
137
|
Log.debug(text + ":\t" + duration.show())
|
|
137
138
|
}
|
package/compiler/Inference.ff
CHANGED
|
@@ -240,7 +240,7 @@ extend self: Inference {
|
|
|
240
240
|
}
|
|
241
241
|
environment.Environment(symbols = environment1.symbols.addAll(symbols))
|
|
242
242
|
}
|
|
243
|
-
|
|
243
|
+
let guards = [].toArray()
|
|
244
244
|
let environment3 = case.guards.foldLeft(environment1) {environment2, g =>
|
|
245
245
|
let guardType = self.unification.freshUnificationVariable(g.at)
|
|
246
246
|
let guardTerm = self.inferTerm(environment2, guardType, g.term)
|
|
@@ -248,12 +248,12 @@ extend self: Inference {
|
|
|
248
248
|
let noEffect = TConstructor(at, "ff:core/Nothing.Nothing", [])
|
|
249
249
|
Scheme(True, False, False, False, Signature(at, name, False, [], [], [], type, noEffect))
|
|
250
250
|
}
|
|
251
|
-
guards
|
|
251
|
+
guards.push(g.MatchGuard(term = guardTerm))
|
|
252
252
|
environment2.Environment(symbols = environment2.symbols.addAll(symbols))
|
|
253
253
|
}
|
|
254
254
|
case.MatchCase(
|
|
255
255
|
body = self.inferTerm(environment3, returnType, case.body)
|
|
256
|
-
guards = guards.
|
|
256
|
+
guards = guards.drain()
|
|
257
257
|
)
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -301,6 +301,18 @@ extend self: Inference {
|
|
|
301
301
|
let recordType =
|
|
302
302
|
TConstructor(at, "Record$" + parameters.map {_.name}.join("$"), parameters.map {_.valueType})
|
|
303
303
|
variableOption.toList().map {Pair(_, Pair(variableAt, recordType))}.toMap()
|
|
304
|
+
| PVariant(at, "List$Empty", []) =>
|
|
305
|
+
let itemType = self.unification.freshUnificationVariable(at)
|
|
306
|
+
let listType = TConstructor(at, core("List"), [itemType])
|
|
307
|
+
self.unification.unify(at, expected, listType)
|
|
308
|
+
[].toMap()
|
|
309
|
+
| PVariant(at, "List$Link", [head, tail]) =>
|
|
310
|
+
let itemType = self.unification.freshUnificationVariable(at)
|
|
311
|
+
let listType = TConstructor(at, core("List"), [itemType])
|
|
312
|
+
self.unification.unify(at, expected, listType)
|
|
313
|
+
let headVariables = self.inferPattern(environment, itemType, head)
|
|
314
|
+
let tailVariables = self.inferPattern(environment, listType, tail)
|
|
315
|
+
headVariables.addAll(tailVariables)
|
|
304
316
|
| PVariant(at, name, patterns) =>
|
|
305
317
|
let instantiated = self.lookup(environment, expected, at, name, [], None).else {
|
|
306
318
|
throw(CompileError(at, "No such variant: " + name))
|
|
@@ -366,7 +378,7 @@ extend self: Inference {
|
|
|
366
378
|
InferRecordFieldHook(self.unification, environment, expected, t, e.field)
|
|
367
379
|
)
|
|
368
380
|
}
|
|
369
|
-
let fieldNames = name.split('$').
|
|
381
|
+
let fieldNames = name.split('$').dropFirst(1)
|
|
370
382
|
fieldNames.pairs().find {_.second == e.field}.map {_.first}.map {index =>
|
|
371
383
|
let t1 = typeArguments.grab(index)
|
|
372
384
|
self.unification.unify(e.at, expected, t1)
|
|
@@ -943,7 +955,8 @@ extend self: Inference {
|
|
|
943
955
|
}
|
|
944
956
|
}
|
|
945
957
|
}
|
|
946
|
-
mutable remainingArguments = arguments
|
|
958
|
+
mutable remainingArguments = arguments.toArray()
|
|
959
|
+
remainingArguments.reverse()
|
|
947
960
|
let newArguments = parameters.map {p =>
|
|
948
961
|
let t = p.valueType
|
|
949
962
|
function defaultArgument(): Argument {
|
|
@@ -958,21 +971,20 @@ extend self: Inference {
|
|
|
958
971
|
}
|
|
959
972
|
}
|
|
960
973
|
}
|
|
961
|
-
remainingArguments.{
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
974
|
+
if(remainingArguments.isEmpty()) {
|
|
975
|
+
defaultArgument()
|
|
976
|
+
} elseIf {remainingArguments.grabLast().name.isEmpty()} {
|
|
977
|
+
let a = remainingArguments.pop().grab()
|
|
978
|
+
let e2 = self.inferTerm(environment, t, a.value)
|
|
979
|
+
Argument(a.at, Some(p.name), e2)
|
|
980
|
+
} else {
|
|
981
|
+
remainingArguments.find {_.name.contains(p.name)}.map {| Argument(at, _, e) =>
|
|
982
|
+
remainingArguments.indexWhere {_.name.contains(p.name)}.each {remainingArguments.delete(_, 1)}
|
|
966
983
|
let e2 = self.inferTerm(environment, t, e)
|
|
967
984
|
Argument(at, Some(p.name), e2)
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
let e2 = self.inferTerm(environment, t, e)
|
|
972
|
-
Argument(at, Some(p.name), e2)
|
|
973
|
-
}.else {
|
|
974
|
-
defaultArgument()
|
|
975
|
-
}
|
|
985
|
+
}.else {
|
|
986
|
+
defaultArgument()
|
|
987
|
+
}
|
|
976
988
|
}
|
|
977
989
|
}
|
|
978
990
|
if(!self.lspHook.isEnabled()) {
|
|
@@ -982,7 +994,7 @@ extend self: Inference {
|
|
|
982
994
|
}
|
|
983
995
|
newArguments
|
|
984
996
|
} else {
|
|
985
|
-
[...newArguments, ...remainingArguments.map {a => a.Argument(
|
|
997
|
+
[...newArguments, ...remainingArguments.drain().map {a => a.Argument(
|
|
986
998
|
value = self.inferTerm(environment, self.unification.freshUnificationVariable(a.at), a.value)
|
|
987
999
|
)}]
|
|
988
1000
|
}
|
package/compiler/JsEmitter.ff
CHANGED
|
@@ -77,7 +77,7 @@ extend self: JsEmitter {
|
|
|
77
77
|
packagePair.name == "core"
|
|
78
78
|
) {["esbuild"]} else {[]}
|
|
79
79
|
let jsImports = self.jsImporter.generateImports(ignoreJsImports.toSet())
|
|
80
|
-
[jsImports, ...parts].map {
|
|
80
|
+
[jsImports, ...parts].map {_.join("\n\n")}.join("\n\n") + "\n"
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
withEmittingAsync[T](body: () => T): T {
|
|
@@ -90,14 +90,14 @@ extend self: JsEmitter {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
emitRun(functions: List[DFunction], mainPackagePair: PackagePair, bootstrapping: Bool): List[String] {
|
|
93
|
-
let buildMainFunction = functions.find {
|
|
93
|
+
let buildMainFunction = functions.find {_.signature.name == "buildMain"}.filter {_ =>
|
|
94
94
|
self.emitTarget != EmitBrowser && self.emitTarget != EmitExecutable
|
|
95
95
|
}
|
|
96
96
|
let willRunOnNode = self.emitTarget != EmitBrowser
|
|
97
|
-
let targetMain = if(willRunOnNode) {
|
|
97
|
+
let targetMain = if(willRunOnNode) {"nodeMain"} else {"browserMain"}
|
|
98
98
|
let mainFunction =
|
|
99
|
-
functions.find {
|
|
100
|
-
mainFunction.map {_.signature.name}.map {
|
|
99
|
+
functions.find {_.signature.name == targetMain}.orElse {functions.find {_.signature.name == "main"}}
|
|
100
|
+
mainFunction.map {_.signature.name}.map {mainName => [[
|
|
101
101
|
"export async function $run$(fireflyPath_, arguments_) {"
|
|
102
102
|
"Error.stackTraceLimit = 50"
|
|
103
103
|
"const $task = {controller: new AbortController(), subtasks: new Set(), promise: new Promise(() => {}), started: performance.now() * 0.001}"
|
|
@@ -164,31 +164,31 @@ extend self: JsEmitter {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
emitExtendsDefinition(definition: DExtend): String {
|
|
167
|
-
let typeName = extractTypeName(definition.type).reverse().takeWhile {
|
|
168
|
-
let methods = definition.methods.map {
|
|
167
|
+
let typeName = extractTypeName(definition.type).reverse().takeWhile {_ != '.'}.reverse()
|
|
168
|
+
let methods = definition.methods.map {method =>
|
|
169
169
|
method.DFunction(
|
|
170
170
|
signature = method.signature.Signature(
|
|
171
171
|
name = typeName + "_" + method.signature.name
|
|
172
172
|
)
|
|
173
173
|
)
|
|
174
174
|
}
|
|
175
|
-
let syncMethods = methods.map {
|
|
176
|
-
let asyncMethods = self.withEmittingAsync {methods.map {
|
|
175
|
+
let syncMethods = methods.map {"export " + self.emitFunctionDefinition(_, False)}
|
|
176
|
+
let asyncMethods = self.withEmittingAsync {methods.map {"export " + self.emitFunctionDefinition(_, True)}}
|
|
177
177
|
[...syncMethods, ...asyncMethods].join("\n\n")
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
emitInstanceDefinition(definition: DInstance): String {
|
|
181
181
|
let name = makeDictionaryName(definition.traitName, firstTypeName(definition.typeArguments))
|
|
182
|
-
let methods = definition.methods.map {self.emitFunctionDefinition(_, False)}.map {
|
|
182
|
+
let methods = definition.methods.map {self.emitFunctionDefinition(_, False)}.map {_.dropFirst("function ".size())} // TODO
|
|
183
183
|
let asyncMethods = self.withEmittingAsync {
|
|
184
|
-
definition.methods.map {self.emitFunctionDefinition(_, True)}.map {
|
|
184
|
+
definition.methods.map {self.emitFunctionDefinition(_, True)}.map {"async " + _.dropFirst("async function ".size())} // TODO
|
|
185
185
|
}
|
|
186
186
|
let body = "{\n" + [...methods, ...asyncMethods].join(",\n") + "\n}"
|
|
187
187
|
definition.constraints.{
|
|
188
188
|
| [] =>
|
|
189
189
|
"export const " + name + " = " + body + ";"
|
|
190
190
|
| constraints =>
|
|
191
|
-
let dictionaries = constraints.map {
|
|
191
|
+
let dictionaries = constraints.map {c =>
|
|
192
192
|
makeDictionaryName(c.name, firstTypeName(c.generics))
|
|
193
193
|
}
|
|
194
194
|
"export function " + name + "(" + dictionaries.join(", ") + ") { return " + body + "}"
|
|
@@ -213,12 +213,12 @@ extend self: JsEmitter {
|
|
|
213
213
|
| _ => False
|
|
214
214
|
}
|
|
215
215
|
} =>
|
|
216
|
-
let body = self.emitTailCall {
|
|
216
|
+
let body = self.emitTailCall {self.emitStatements(matchCase.body, True, async)}
|
|
217
217
|
signature + " {\n" + body + "\n}"
|
|
218
218
|
| Lambda(_, effect, cases) =>
|
|
219
219
|
Patterns.convertAndCheck(self.otherModules, cases)
|
|
220
|
-
let escapedArguments = definition.signature.parameters.map {
|
|
221
|
-
let shadowingWorkaround = definition.signature.parameters.map {
|
|
220
|
+
let escapedArguments = definition.signature.parameters.map {_.name + "_a"}
|
|
221
|
+
let shadowingWorkaround = definition.signature.parameters.map {p =>
|
|
222
222
|
"const " + p.name + "_a = " + escapeKeyword(p.name) + ";"
|
|
223
223
|
}.join("\n")
|
|
224
224
|
let body = self.emitTailCall {
|
|
@@ -247,7 +247,7 @@ extend self: JsEmitter {
|
|
|
247
247
|
|
|
248
248
|
emitSignature(signature: Signature, async: Bool, suffix: String = ""): String {
|
|
249
249
|
let parameterStrings = signature.parameters.map {self.emitParameter(_, async)}
|
|
250
|
-
let dictionaryStrings = signature.constraints.map {
|
|
250
|
+
let dictionaryStrings = signature.constraints.map {c =>
|
|
251
251
|
makeDictionaryName(c.name, firstTypeName(c.generics))
|
|
252
252
|
}
|
|
253
253
|
let controller = if(async) {["$task"]} else {[]}
|
|
@@ -258,19 +258,19 @@ extend self: JsEmitter {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
emitParameter(parameter: Parameter, async: Bool): String {
|
|
261
|
-
let defaultValue = parameter.default.map {
|
|
261
|
+
let defaultValue = parameter.default.map {" = " + self.emitTerm(_, async) }.else {""}
|
|
262
262
|
escapeKeyword(parameter.name) + defaultValue
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
emitTypeDefinition(definition: DType): String {
|
|
266
|
-
if(definition.newtype) {
|
|
266
|
+
if(definition.newtype) {"// newtype " + definition.name} else:
|
|
267
267
|
"// type " + definition.name + "\n" +
|
|
268
|
-
definition.variants.map {
|
|
268
|
+
definition.variants.map {self.emitVariantDefinition(definition, _)}.join("\n")
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
emitVariantDefinition(typeDefinition: DType, definition: Variant): String {
|
|
272
272
|
let allFields = [...typeDefinition.commonFields, ...definition.fields]
|
|
273
|
-
let fields = allFields.map {
|
|
273
|
+
let fields = allFields.map {escapeKeyword(_.name)}.join(", ")
|
|
274
274
|
if(allFields.isEmpty()) {
|
|
275
275
|
"const " + definition.name + "$ = {" + definition.name + ": true};\n" +
|
|
276
276
|
"export function " + definition.name + "(" + fields + ") {\n" +
|
|
@@ -287,7 +287,7 @@ extend self: JsEmitter {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
emitTerm(term: Term, async: Bool): String {
|
|
290
|
+
emitTerm(term: Term, async: Bool): String {term.{
|
|
291
291
|
| EString(at, value) {value.startsWith("\"\"\"")} =>
|
|
292
292
|
"`" + value.dropFirst(3).dropLast(3).replace("`", "\\`") + "`" // TODO: Fix escaping
|
|
293
293
|
| EString(at, value) => value
|
|
@@ -306,7 +306,7 @@ extend self: JsEmitter {
|
|
|
306
306
|
| EVariant(at, name, _, arguments) =>
|
|
307
307
|
let argumentsString = arguments.toList().flatten().map {self.emitArgument(at, _, async)}.join(", ")
|
|
308
308
|
let newtype = self.processVariant(name)
|
|
309
|
-
if(newtype) {
|
|
309
|
+
if(newtype) {argumentsString} else:
|
|
310
310
|
escapeResolved(name) + "(" + argumentsString + ")"
|
|
311
311
|
| EVariantIs(at, "ff:core/Bool.False", _) =>
|
|
312
312
|
"function(_v) { return !_v ? ff_core_Option.Some(_v) : ff_core_Option.None(); }"
|
|
@@ -320,17 +320,17 @@ extend self: JsEmitter {
|
|
|
320
320
|
"return _v." + escapeResolved(n) + " ? ff_core_Option.Some(_v) : ff_core_Option.None();" +
|
|
321
321
|
"})"
|
|
322
322
|
| ECopy(at, name, record, fields) =>
|
|
323
|
-
let fieldCode = fields.map {
|
|
323
|
+
let fieldCode = fields.map {f => escapeKeyword(f.name) + " = " + self.emitTerm(f.value, async)}.join(", ")
|
|
324
324
|
"{..." + self.emitTerm(record, async) + ", " + fieldCode + "}"
|
|
325
325
|
| EField(at, newtype, record, field) =>
|
|
326
|
-
if(newtype) {
|
|
326
|
+
if(newtype) {self.emitTerm(record, async)} else:
|
|
327
327
|
self.emitTerm(record, async) + "." + escapeKeyword(field)
|
|
328
328
|
| ELambda(at, Lambda(_, effect, [MatchCase(_, patterns, [], body)])) {
|
|
329
329
|
patterns.all {| PVariable _ => True | _ => False }
|
|
330
330
|
} =>
|
|
331
331
|
let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
|
|
332
332
|
let patternParameters = patterns.map {
|
|
333
|
-
| PVariable p => p.name.map(escapeKeyword).else{"_"}
|
|
333
|
+
| PVariable p => p.name.map(escapeKeyword).else {"_"}
|
|
334
334
|
| _ => panic("!")
|
|
335
335
|
}
|
|
336
336
|
let controller = if(newAsync) {["$task"]} else {[]}
|
|
@@ -341,9 +341,9 @@ extend self: JsEmitter {
|
|
|
341
341
|
let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
|
|
342
342
|
let controller = if(newAsync) {["$task"]} else {[]}
|
|
343
343
|
Patterns.convertAndCheck(self.otherModules, cases)
|
|
344
|
-
let arguments = cases.grab(0).patterns.pairs().map {
|
|
344
|
+
let arguments = cases.grab(0).patterns.pairs().map {"_" + (_.first + 1)}
|
|
345
345
|
let escapedArguments = arguments.map(escapeKeyword) // emitCase arguments must be preescaped
|
|
346
|
-
let caseStrings = cases.map {
|
|
346
|
+
let caseStrings = cases.map {"{\n" + self.emitCase(escapedArguments, _, True, True, newAsync) + "\n}"}
|
|
347
347
|
let prefix = if(newAsync) {"async "} else {""}
|
|
348
348
|
"(" + prefix + "(" + [...escapedArguments, ...controller].join(", ") + ") => " +
|
|
349
349
|
"{\n" + caseStrings.join("\n") + "\n})"
|
|
@@ -389,7 +389,7 @@ extend self: JsEmitter {
|
|
|
389
389
|
primitiveTypes.contains(typeName)
|
|
390
390
|
} =>
|
|
391
391
|
"(" + self.emitArgument(at, left, async) + " <= " + self.emitArgument(at, right, async) + ")"
|
|
392
|
-
| ECall(_, StaticCall("ff:core/
|
|
392
|
+
| ECall(_, StaticCall("ff:core/List.fillBy", _, _), effect, _, [size, Argument(_, _, ELambda(at,
|
|
393
393
|
Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)@c])@l
|
|
394
394
|
))], _) {
|
|
395
395
|
!effectTypeIsAsync(effect)
|
|
@@ -411,12 +411,18 @@ extend self: JsEmitter {
|
|
|
411
411
|
let ds = dictionaryStrings.dropFirst()
|
|
412
412
|
let d = dictionaryStrings.grabFirst()
|
|
413
413
|
let asyncSuffix = if(await) {"$"} else {""}
|
|
414
|
-
let n = escapeKeyword(name.reverse().takeWhile {
|
|
414
|
+
let n = escapeKeyword(name.reverse().takeWhile {_ != '.'}.reverse()) + asyncSuffix
|
|
415
415
|
let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
|
|
416
416
|
let controller = if(await) {["$task"]} else {[]}
|
|
417
417
|
let call = d + "." + n + "(" + [...emittedArguments, ...ds, ...controller].join(", ") + ")"
|
|
418
418
|
if(await) {"(await " + call + ")"} else {call}
|
|
419
419
|
| ECall(at, StaticCall(name, _, _), effect, typeArguments, arguments, dictionaries) =>
|
|
420
|
+
if(name.contains("bundleForBrowser")) { // TODO: Delete this test (for branch arraysonly)
|
|
421
|
+
if(!arguments.grab(0).name.contains("system")) {
|
|
422
|
+
Log.debug("Wrong arguments for bundleForBrowser: " + Show.show(arguments.map {_.name}))
|
|
423
|
+
throw(GrabException())
|
|
424
|
+
}
|
|
425
|
+
}
|
|
420
426
|
detectIfElse(term).{
|
|
421
427
|
| [] =>
|
|
422
428
|
let await = async && effectTypeIsAsync(effect)
|
|
@@ -447,7 +453,7 @@ extend self: JsEmitter {
|
|
|
447
453
|
if(await) {"(await " + call + ")"} else {call}
|
|
448
454
|
| ERecord(at, fields) =>
|
|
449
455
|
if(fields.isEmpty()) {"{}"} else {
|
|
450
|
-
let list = fields.map {
|
|
456
|
+
let list = fields.map {f => escapeKeyword(f.name) + ": " + self.emitTerm(f.value, async)}
|
|
451
457
|
"{\n" + list.join(",\n") + "\n}"
|
|
452
458
|
}
|
|
453
459
|
| EWildcard(at, index) =>
|
|
@@ -459,10 +465,10 @@ extend self: JsEmitter {
|
|
|
459
465
|
"(function() {\n" + self.emitStatements(term, True, async) + "\n})()"
|
|
460
466
|
}}
|
|
461
467
|
|
|
462
|
-
emitDictionary(d
|
|
468
|
+
emitDictionary(d: Dictionary): String {
|
|
463
469
|
let m = if(d.moduleName != "") {
|
|
464
470
|
d.packagePair.groupName("_") + "_" + d.moduleName.replace("/", "_") + "."
|
|
465
|
-
} else {
|
|
471
|
+
} else {""}
|
|
466
472
|
let c = m + makeDictionaryName(d.traitName, d.typeName)
|
|
467
473
|
if(d.dictionaries.isEmpty()) {
|
|
468
474
|
c
|
|
@@ -474,7 +480,7 @@ extend self: JsEmitter {
|
|
|
474
480
|
emitStatements(term: Term, last: Bool, async: Bool): String {
|
|
475
481
|
term.{
|
|
476
482
|
| EFunctions(at, functions, body) =>
|
|
477
|
-
let functionStrings = functions.map {
|
|
483
|
+
let functionStrings = functions.map {f =>
|
|
478
484
|
let newAsync = self.emittingAsync && effectTypeIsAsync(f.signature.effect)
|
|
479
485
|
self.emitFunctionDefinition(f, newAsync)
|
|
480
486
|
}
|
|
@@ -670,37 +676,37 @@ extend self: JsEmitter {
|
|
|
670
676
|
self.emitCase([guardName], newCase, jump, last, async)
|
|
671
677
|
| Pair([], []) =>
|
|
672
678
|
self.emitStatements(matchCase.body, last, async) +
|
|
673
|
-
if(jump && last) {
|
|
679
|
+
if(jump && last) {"\nreturn"} elseIf {jump} {"\nbreak"} else {""}
|
|
674
680
|
}
|
|
675
681
|
}
|
|
676
682
|
|
|
677
683
|
emitPattern(
|
|
678
|
-
argument: String
|
|
679
|
-
pattern: MatchPattern
|
|
680
|
-
arguments: List[String]
|
|
681
|
-
matchCase: MatchCase
|
|
682
|
-
jump: Bool
|
|
683
|
-
last: Bool
|
|
684
|
+
argument: String
|
|
685
|
+
pattern: MatchPattern
|
|
686
|
+
arguments: List[String]
|
|
687
|
+
matchCase: MatchCase
|
|
688
|
+
jump: Bool
|
|
689
|
+
last: Bool
|
|
684
690
|
async: Bool
|
|
685
691
|
): String {
|
|
686
692
|
pattern.{
|
|
687
693
|
| PString(_, value) =>
|
|
688
|
-
"if(" + argument + "
|
|
694
|
+
"if(" + argument + " === " + value + ") {\n" +
|
|
689
695
|
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
690
696
|
"\n}"
|
|
691
697
|
| PInt(_, value) =>
|
|
692
|
-
"if(" + argument + "
|
|
698
|
+
"if(" + argument + " === " + value + ") {\n" +
|
|
693
699
|
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
694
700
|
"\n}"
|
|
695
701
|
| PChar(_, value) =>
|
|
696
|
-
"if(" + argument + "
|
|
702
|
+
"if(" + argument + " === " + charLiteralToNumber(value) + ") {\n" +
|
|
697
703
|
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
698
704
|
"\n}"
|
|
699
705
|
| PVariable(_, None) =>
|
|
700
706
|
self.emitCase(arguments, matchCase, jump, last, async)
|
|
701
707
|
| PVariable(_, Some(name)) =>
|
|
702
708
|
let escaped = escapeKeyword(name)
|
|
703
|
-
if(escaped != argument) {
|
|
709
|
+
if(escaped != argument) {"const " + escaped + " = " + argument + ";\n"} else {""} +
|
|
704
710
|
self.emitCase(arguments, matchCase, jump, last, async)
|
|
705
711
|
| PVariant(_, "ff:core/Bool.False", []) =>
|
|
706
712
|
"if(!" + argument + ") {\n" +
|
|
@@ -710,67 +716,88 @@ extend self: JsEmitter {
|
|
|
710
716
|
"if(" + argument + ") {\n" +
|
|
711
717
|
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
712
718
|
"\n}"
|
|
719
|
+
| PVariant(_, "List$Empty", []) =>
|
|
720
|
+
mutable shortArgument = argument
|
|
721
|
+
mutable shortCount = 0
|
|
722
|
+
while {shortArgument.endsWith(".slice(1)")} {
|
|
723
|
+
shortArgument = shortArgument.dropLast(".slice(1)".size())
|
|
724
|
+
shortCount += 1
|
|
725
|
+
}
|
|
726
|
+
"if(" + shortArgument + ".length === " + shortCount + ") {\n" +
|
|
727
|
+
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
728
|
+
"\n}"
|
|
729
|
+
| PVariant(_, "List$Link", [head, tail]) =>
|
|
730
|
+
mutable shortArgument = argument
|
|
731
|
+
mutable shortCount = 0
|
|
732
|
+
while {shortArgument.endsWith(".slice(1)")} {
|
|
733
|
+
shortArgument = shortArgument.dropLast(".slice(1)".size())
|
|
734
|
+
shortCount += 1
|
|
735
|
+
}
|
|
736
|
+
let newArguments = [shortArgument + "[" + shortCount + "]", argument + ".slice(1)", ...arguments]
|
|
737
|
+
let newMatchCase = matchCase.MatchCase(patterns = [head, tail, ...matchCase.patterns])
|
|
738
|
+
"if(" + shortArgument + ".length > " + shortCount + ") {\n" +
|
|
739
|
+
self.emitCase(newArguments, newMatchCase, jump, last, async) +
|
|
740
|
+
"\n}"
|
|
713
741
|
| PVariant(_, name, patterns) =>
|
|
714
742
|
let processed = self.processVariantCase(name, argument)
|
|
715
743
|
let newMatchCase = matchCase.MatchCase(patterns = [...patterns, ...matchCase.patterns])
|
|
716
|
-
if(processed.loneVariant) {
|
|
744
|
+
if(processed.loneVariant) {""} else {
|
|
717
745
|
"if(" + argument + "." + processed.variantName + ") {\n"
|
|
718
746
|
} +
|
|
719
747
|
self.emitCase([...processed.arguments, ...arguments], newMatchCase, jump, last, async) +
|
|
720
|
-
if(processed.loneVariant) {
|
|
748
|
+
if(processed.loneVariant) {""} else {"\n}"}
|
|
721
749
|
| PVariantAs(at, name, variableAt, variable) =>
|
|
722
750
|
let processed = self.processVariantCase(name, argument)
|
|
723
|
-
if(processed.loneVariant) {
|
|
751
|
+
if(processed.loneVariant) {""} else {
|
|
724
752
|
"if(" + argument + "." + processed.variantName + ") {\n"
|
|
725
753
|
} +
|
|
726
|
-
variable.map(escapeKeyword).filter {
|
|
754
|
+
variable.map(escapeKeyword).filter {_ != argument}.map {
|
|
727
755
|
"const " + _ + " = " + argument + ";\n"
|
|
728
756
|
}.else {""} +
|
|
729
757
|
self.emitCase(arguments, matchCase, jump, last, async) +
|
|
730
|
-
if(processed.loneVariant) {
|
|
758
|
+
if(processed.loneVariant) {""} else {"\n}"}
|
|
731
759
|
| PAlias(_, pattern, variable) =>
|
|
732
760
|
let escaped = escapeKeyword(variable)
|
|
733
|
-
if(escaped != argument) {
|
|
761
|
+
if(escaped != argument) {"const " + escaped + " = " + argument + ";\n"} else {""} +
|
|
734
762
|
self.emitPattern(argument, pattern, arguments, matchCase, jump, last, async)
|
|
735
763
|
}
|
|
736
764
|
}
|
|
737
765
|
|
|
738
|
-
emitList(items: List[Pair[Term, Bool]], async: Bool): String {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
"ff_core_List.Link(" + self.emitTerm(e, async) + ", " + self.emitList(list, async) + ")"
|
|
745
|
-
| [Pair(e, True), ...list] =>
|
|
746
|
-
"ff_core_List.List_addAll(" + self.emitTerm(e, async) + ", " + self.emitList(list, async) + ")"
|
|
747
|
-
}}
|
|
766
|
+
emitList(items: List[Pair[Term, Bool]], async: Bool): String {
|
|
767
|
+
"[" + items.map {
|
|
768
|
+
| Pair(item, False) => self.emitTerm(item, async)
|
|
769
|
+
| Pair(item, True) => "..." + self.emitTerm(item, async)
|
|
770
|
+
}.join(", ") + "]"
|
|
771
|
+
}
|
|
748
772
|
|
|
749
773
|
processVariantCase(name: String, argument: String): ProcessedVariantCase {
|
|
750
|
-
|
|
774
|
+
if(name == "List$Empty") {ProcessedVariantCase(name, False, False, [])} else:
|
|
775
|
+
if(name == "List$Link") {ProcessedVariantCase(name, False, False, [argument + "[0]", argument + ".slice(1)"])} else:
|
|
776
|
+
let variantNameUnqualified = name.reverse().takeWhile {_ != '.'}.reverse()
|
|
751
777
|
let variantName = escapeKeyword(variantNameUnqualified)
|
|
752
778
|
let moduleName = name.dropLast(variantNameUnqualified.size() + 1)
|
|
753
779
|
let variantModule = self.otherModules.grab(moduleName)
|
|
754
780
|
mutable newtype = False
|
|
755
781
|
mutable loneVariant = False
|
|
756
|
-
let newArguments = variantModule.types.collectFirst {
|
|
757
|
-
definition.variants.find {
|
|
782
|
+
let newArguments = variantModule.types.collectFirst {definition =>
|
|
783
|
+
definition.variants.find {_.name == variantName }.map {variant =>
|
|
758
784
|
newtype = definition.newtype
|
|
759
785
|
loneVariant = definition.variants.size() == 1
|
|
760
|
-
[...definition.commonFields.map {
|
|
786
|
+
[...definition.commonFields.map {_.name}, ...variant.fields.map {_.name}]
|
|
761
787
|
}
|
|
762
|
-
}.grab().map {
|
|
788
|
+
}.grab().map {field => if(newtype) {argument} else {argument + "." + escapeKeyword(field)}}
|
|
763
789
|
ProcessedVariantCase(variantName, newtype, loneVariant, newArguments)
|
|
764
790
|
}
|
|
765
791
|
|
|
766
792
|
processVariant(name: String): Bool {
|
|
767
|
-
|
|
793
|
+
if(name.startsWith("List$")) {False} else:
|
|
794
|
+
let variantNameUnqualified = name.reverse().takeWhile {_ != '.'}.reverse()
|
|
768
795
|
let variantName = escapeKeyword(variantNameUnqualified)
|
|
769
796
|
let moduleName = name.dropLast(variantNameUnqualified.size() + 1)
|
|
770
797
|
let variantModule = self.otherModules.grab(moduleName)
|
|
771
798
|
mutable newtype = False
|
|
772
|
-
let newArguments = variantModule.types.collectFirst {
|
|
773
|
-
definition.variants.find {
|
|
799
|
+
let newArguments = variantModule.types.collectFirst {definition =>
|
|
800
|
+
definition.variants.find {_.name == variantName}.map {variant =>
|
|
774
801
|
newtype = definition.newtype
|
|
775
802
|
}
|
|
776
803
|
}.grab()
|
|
@@ -801,11 +828,11 @@ detectIfElse(term: Term): List[Pair[Term, Term]] {
|
|
|
801
828
|
[Pair(condition.value, invokeImmediately(body.value))]
|
|
802
829
|
| ECall(at, StaticCall("ff:core/Option.Option_elseIf", _, _), _, _, [option, condition, body], _) =>
|
|
803
830
|
let list = detectIfElse(option.value)
|
|
804
|
-
if(list.isEmpty()) {
|
|
831
|
+
if(list.isEmpty()) {[]} else:
|
|
805
832
|
[Pair(invokeImmediately(condition.value), invokeImmediately(body.value)), ...list]
|
|
806
833
|
| ECall(at, StaticCall("ff:core/Option.Option_else", _, _), _, _, [option, body], _) =>
|
|
807
834
|
let list = detectIfElse(option.value)
|
|
808
|
-
if(list.isEmpty()) {
|
|
835
|
+
if(list.isEmpty()) {[]} else:
|
|
809
836
|
[Pair(EVariant(at, "ff:core/Bool.True", [], None), invokeImmediately(body.value)), ...list]
|
|
810
837
|
| _ =>
|
|
811
838
|
[]
|
|
@@ -848,7 +875,7 @@ charLiteralToNumber(charLiteral: String): String {
|
|
|
848
875
|
}
|
|
849
876
|
|
|
850
877
|
escapeResolved(word: String): String {
|
|
851
|
-
let parts = word.replace(":", ".").replace("/", ".").split('.')
|
|
878
|
+
let parts = word.replace(":", ".").replace("/", ".").split('.')
|
|
852
879
|
let initialParts = parts.dropLast()
|
|
853
880
|
if(initialParts.isEmpty()) {
|
|
854
881
|
escapeKeyword(parts.grabLast())
|
package/compiler/JsImporter.ff
CHANGED
|
@@ -37,7 +37,7 @@ extend self: JsImporter {
|
|
|
37
37
|
let rest4 = rest3.dropFirst(" from '".size())
|
|
38
38
|
let url = rest4.takeWhile {_ != '\''}
|
|
39
39
|
if(url.size() == 0) {throw(CompileError(at, "Expected module name after \" from '\""))}
|
|
40
|
-
if(url.any{_ == '\n'}) {throw(CompileError(at, "Unclosed module name string"))}
|
|
40
|
+
if(url.any {_ == '\n'}) {throw(CompileError(at, "Unclosed module name string"))}
|
|
41
41
|
let rest5 = rest4.dropFirst(url.size() + 1)
|
|
42
42
|
let importName = self.add(url)
|
|
43
43
|
space + "const " + name + " = " + importName + self.process(at, rest5)
|
package/compiler/LspHook.ff
CHANGED
|
@@ -7,7 +7,7 @@ class LspHook(
|
|
|
7
7
|
definedAt: Location
|
|
8
8
|
insertIdentifier: Bool
|
|
9
9
|
trackSymbols: Bool
|
|
10
|
-
|
|
10
|
+
arrayOfResults: Array[ResultHook]
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
disabled(): LspHook {
|
|
@@ -20,7 +20,7 @@ make(at: Option[Location], definedAt: Option[Location], insertIdentifier: Bool,
|
|
|
20
20
|
definedAt = definedAt.else {Location("^lsp", -7, -7)}
|
|
21
21
|
insertIdentifier = insertIdentifier
|
|
22
22
|
trackSymbols = trackSymbols
|
|
23
|
-
|
|
23
|
+
arrayOfResults = [].toArray()
|
|
24
24
|
)
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -41,10 +41,10 @@ extend self: LspHook {
|
|
|
41
41
|
self.definedAt.file == at.file
|
|
42
42
|
}
|
|
43
43
|
emit(result: ResultHook) {
|
|
44
|
-
self.
|
|
44
|
+
self.arrayOfResults.push(result)
|
|
45
45
|
}
|
|
46
46
|
results(): List[ResultHook] {
|
|
47
|
-
self.
|
|
47
|
+
self.arrayOfResults.toList()
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|