firefly-compiler 0.5.76 → 0.5.78

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 (157) hide show
  1. package/bin/firefly.mjs +1 -1
  2. package/compiler/Builder.ff +3 -1
  3. package/compiler/Compiler.ff +7 -3
  4. package/compiler/JsEmitter.ff +1564 -623
  5. package/compiler/Main.ff +1 -1
  6. package/compiler/SourceMap.ff +149 -0
  7. package/core/Buffer.ff +2 -1
  8. package/core/BuildSystem.ff +3 -1
  9. package/core/Js.ff +6 -0
  10. package/firefly.sh +1 -1
  11. package/fireflysite/Main.ff +2 -1
  12. package/output/js/ff/compiler/Builder.mjs +8 -44
  13. package/output/js/ff/compiler/Builder.mjs.map +181 -0
  14. package/output/js/ff/compiler/Compiler.mjs +23 -16
  15. package/output/js/ff/compiler/Compiler.mjs.map +155 -0
  16. package/output/js/ff/compiler/Dependencies.mjs +12 -14
  17. package/output/js/ff/compiler/Dependencies.mjs.map +128 -0
  18. package/output/js/ff/compiler/DependencyLock.mjs +1 -4
  19. package/output/js/ff/compiler/DependencyLock.mjs.map +22 -0
  20. package/output/js/ff/compiler/Deriver.mjs +10 -11
  21. package/output/js/ff/compiler/Deriver.mjs.map +204 -0
  22. package/output/js/ff/compiler/Dictionaries.mjs +12 -13
  23. package/output/js/ff/compiler/Dictionaries.mjs.map +183 -0
  24. package/output/js/ff/compiler/Environment.mjs +24 -27
  25. package/output/js/ff/compiler/Environment.mjs.map +141 -0
  26. package/output/js/ff/compiler/Inference.mjs +83 -92
  27. package/output/js/ff/compiler/Inference.mjs.map +355 -0
  28. package/output/js/ff/compiler/JsEmitter.mjs +3689 -1687
  29. package/output/js/ff/compiler/JsEmitter.mjs.map +423 -0
  30. package/output/js/ff/compiler/JsImporter.mjs +9 -12
  31. package/output/js/ff/compiler/JsImporter.mjs.map +52 -0
  32. package/output/js/ff/compiler/LspHook.mjs +17 -18
  33. package/output/js/ff/compiler/LspHook.mjs.map +140 -0
  34. package/output/js/ff/compiler/Main.mjs +57 -67
  35. package/output/js/ff/compiler/Main.mjs.map +226 -0
  36. package/output/js/ff/compiler/ModuleCache.mjs +1 -4
  37. package/output/js/ff/compiler/ModuleCache.mjs.map +82 -0
  38. package/output/js/ff/compiler/Parser.mjs +54 -57
  39. package/output/js/ff/compiler/Parser.mjs.map +406 -0
  40. package/output/js/ff/compiler/Patterns.mjs +19 -22
  41. package/output/js/ff/compiler/Patterns.mjs.map +119 -0
  42. package/output/js/ff/compiler/Resolver.mjs +40 -39
  43. package/output/js/ff/compiler/Resolver.mjs.map +294 -0
  44. package/output/js/ff/compiler/SourceMap.mjs +402 -0
  45. package/output/js/ff/compiler/SourceMap.mjs.map +83 -0
  46. package/output/js/ff/compiler/Substitution.mjs +1 -4
  47. package/output/js/ff/compiler/Substitution.mjs.map +138 -0
  48. package/output/js/ff/compiler/Syntax.mjs +250 -249
  49. package/output/js/ff/compiler/Syntax.mjs.map +351 -0
  50. package/output/js/ff/compiler/Token.mjs +17 -22
  51. package/output/js/ff/compiler/Token.mjs.map +113 -0
  52. package/output/js/ff/compiler/Tokenizer.mjs +5 -12
  53. package/output/js/ff/compiler/Tokenizer.mjs.map +101 -0
  54. package/output/js/ff/compiler/Unification.mjs +32 -33
  55. package/output/js/ff/compiler/Unification.mjs.map +135 -0
  56. package/output/js/ff/compiler/Wildcards.mjs +1 -4
  57. package/output/js/ff/compiler/Wildcards.mjs.map +69 -0
  58. package/output/js/ff/compiler/Workspace.mjs +17 -16
  59. package/output/js/ff/compiler/Workspace.mjs.map +96 -0
  60. package/output/js/ff/core/Any.mjs +1 -4
  61. package/output/js/ff/core/Any.mjs.map +21 -0
  62. package/output/js/ff/core/Array.mjs +14 -15
  63. package/output/js/ff/core/Array.mjs.map +90 -0
  64. package/output/js/ff/core/AssetSystem.mjs +1 -4
  65. package/output/js/ff/core/AssetSystem.mjs.map +56 -0
  66. package/output/js/ff/core/Atomic.mjs +1 -4
  67. package/output/js/ff/core/Atomic.mjs.map +24 -0
  68. package/output/js/ff/core/Bool.mjs +4 -9
  69. package/output/js/ff/core/Bool.mjs.map +19 -0
  70. package/output/js/ff/core/BrowserSystem.mjs +1 -8
  71. package/output/js/ff/core/BrowserSystem.mjs.map +47 -0
  72. package/output/js/ff/core/Buffer.mjs +18 -17
  73. package/output/js/ff/core/Buffer.mjs.map +123 -0
  74. package/output/js/ff/core/BuildSystem.mjs +7 -10
  75. package/output/js/ff/core/BuildSystem.mjs.map +87 -0
  76. package/output/js/ff/core/Channel.mjs +1 -4
  77. package/output/js/ff/core/Channel.mjs.map +67 -0
  78. package/output/js/ff/core/Char.mjs +1 -8
  79. package/output/js/ff/core/Char.mjs.map +21 -0
  80. package/output/js/ff/core/Core.mjs +5 -8
  81. package/output/js/ff/core/Core.mjs.map +40 -0
  82. package/output/js/ff/core/Crypto.mjs +1 -4
  83. package/output/js/ff/core/Crypto.mjs.map +65 -0
  84. package/output/js/ff/core/Date.mjs +8 -7
  85. package/output/js/ff/core/Date.mjs.map +168 -0
  86. package/output/js/ff/core/Duration.mjs +4 -9
  87. package/output/js/ff/core/Duration.mjs.map +18 -0
  88. package/output/js/ff/core/Equal.mjs +9 -14
  89. package/output/js/ff/core/Equal.mjs.map +22 -0
  90. package/output/js/ff/core/Error.mjs +1 -8
  91. package/output/js/ff/core/Error.mjs.map +21 -0
  92. package/output/js/ff/core/FileHandle.mjs +1 -8
  93. package/output/js/ff/core/FileHandle.mjs.map +32 -0
  94. package/output/js/ff/core/Float.mjs +4 -5
  95. package/output/js/ff/core/Float.mjs.map +93 -0
  96. package/output/js/ff/core/HttpClient.mjs +1 -4
  97. package/output/js/ff/core/HttpClient.mjs.map +62 -0
  98. package/output/js/ff/core/Int.mjs +4 -9
  99. package/output/js/ff/core/Int.mjs.map +49 -0
  100. package/output/js/ff/core/IntMap.mjs +3 -6
  101. package/output/js/ff/core/IntMap.mjs.map +52 -0
  102. package/output/js/ff/core/Js.mjs +13 -8
  103. package/output/js/ff/core/Js.mjs.map +121 -0
  104. package/output/js/ff/core/JsSystem.mjs +1 -8
  105. package/output/js/ff/core/JsSystem.mjs.map +59 -0
  106. package/output/js/ff/core/JsValue.mjs +23 -42
  107. package/output/js/ff/core/JsValue.mjs.map +155 -0
  108. package/output/js/ff/core/Json.mjs +25 -26
  109. package/output/js/ff/core/Json.mjs.map +134 -0
  110. package/output/js/ff/core/List.mjs +11 -12
  111. package/output/js/ff/core/List.mjs.map +123 -0
  112. package/output/js/ff/core/Lock.mjs +7 -14
  113. package/output/js/ff/core/Lock.mjs.map +52 -0
  114. package/output/js/ff/core/Log.mjs +1 -8
  115. package/output/js/ff/core/Log.mjs.map +26 -0
  116. package/output/js/ff/core/Map.mjs +6 -7
  117. package/output/js/ff/core/Map.mjs.map +67 -0
  118. package/output/js/ff/core/NodeSystem.mjs +11 -14
  119. package/output/js/ff/core/NodeSystem.mjs.map +137 -0
  120. package/output/js/ff/core/Nothing.mjs +1 -10
  121. package/output/js/ff/core/Nothing.mjs.map +11 -0
  122. package/output/js/ff/core/Option.mjs +10 -15
  123. package/output/js/ff/core/Option.mjs.map +67 -0
  124. package/output/js/ff/core/Ordering.mjs +12 -13
  125. package/output/js/ff/core/Ordering.mjs.map +48 -0
  126. package/output/js/ff/core/Pair.mjs +8 -13
  127. package/output/js/ff/core/Pair.mjs.map +48 -0
  128. package/output/js/ff/core/Path.mjs +5 -14
  129. package/output/js/ff/core/Path.mjs.map +178 -0
  130. package/output/js/ff/core/Queue.mjs +1 -4
  131. package/output/js/ff/core/Queue.mjs.map +45 -0
  132. package/output/js/ff/core/Random.mjs +1 -4
  133. package/output/js/ff/core/Random.mjs.map +79 -0
  134. package/output/js/ff/core/RbMap.mjs +1 -16
  135. package/output/js/ff/core/RbMap.mjs.map +92 -0
  136. package/output/js/ff/core/Serializable.mjs +17 -22
  137. package/output/js/ff/core/Serializable.mjs.map +73 -0
  138. package/output/js/ff/core/Set.mjs +6 -7
  139. package/output/js/ff/core/Set.mjs.map +45 -0
  140. package/output/js/ff/core/Show.mjs +11 -20
  141. package/output/js/ff/core/Show.mjs.map +29 -0
  142. package/output/js/ff/core/SourceLocation.mjs +7 -8
  143. package/output/js/ff/core/SourceLocation.mjs.map +41 -0
  144. package/output/js/ff/core/Stream.mjs +1 -4
  145. package/output/js/ff/core/Stream.mjs.map +92 -0
  146. package/output/js/ff/core/String.mjs +14 -19
  147. package/output/js/ff/core/String.mjs.map +106 -0
  148. package/output/js/ff/core/StringMap.mjs +3 -6
  149. package/output/js/ff/core/StringMap.mjs.map +52 -0
  150. package/output/js/ff/core/Task.mjs +1 -8
  151. package/output/js/ff/core/Task.mjs.map +79 -0
  152. package/output/js/ff/core/Try.mjs +3 -6
  153. package/output/js/ff/core/Try.mjs.map +41 -0
  154. package/output/js/ff/core/Unit.mjs +5 -12
  155. package/output/js/ff/core/Unit.mjs.map +18 -0
  156. package/package.json +1 -1
  157. package/vscode/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import Syntax
2
2
  import Patterns
3
3
  import JsImporter
4
+ import SourceMap from ff:compiler
4
5
 
5
6
  class JsEmitter(
6
7
  otherModules: Map[String, Module]
@@ -11,6 +12,11 @@ class JsEmitter(
11
12
  moduleKey: ModuleKey
12
13
  mutable emittingAsync: Bool
13
14
  mutable tailCallUsed: Bool
15
+ mutable writtenColumn: Int
16
+ writtenStrings: Array[Array[String]]
17
+ writtenSegments: Array[Array[List[Int]]]
18
+ writtenAnchors: IntMap[List[String]]
19
+ writtenNames: StringMap[Int]
14
20
  )
15
21
 
16
22
  data EmitTarget {
@@ -38,37 +44,135 @@ new(
38
44
  moduleKey = moduleKey
39
45
  emittingAsync = False
40
46
  tailCallUsed = False
47
+ writtenColumn = 0
48
+ writtenStrings = [[].toArray()].toArray()
49
+ writtenSegments = [[].toArray()].toArray()
50
+ writtenAnchors = IntMap.new()
51
+ writtenNames = StringMap.new()
41
52
  )
42
53
  }
43
54
 
44
55
  fail[T](at: Location, message: String): T {
45
- panic(message + " " + at.show())
56
+ throw(CompileError(at, message))
46
57
  }
47
58
 
48
59
  extend self: JsEmitter {
60
+
61
+ writeUnmapped(text: String) {
62
+ self.writtenStrings.grabLast().push(text)
63
+ self.writtenSegments.grabLast().push(
64
+ [self.writtenColumn]
65
+ )
66
+ self.writtenColumn += text.size()
67
+ }
68
+
69
+ writeMapped(at: Location, text: String) {
70
+ self.writtenStrings.grabLast().push(text)
71
+ self.writtenSegments.grabLast().push(
72
+ [self.writtenColumn, 0, at.line - 1, at.column - 1]
73
+ )
74
+ self.writtenColumn += text.size()
75
+ }
49
76
 
50
- emitModule(module: Module): String {
77
+ writeNamed(name: String, at: Location, text: String) {
78
+ let unqualified = name.reverse().takeWhile {c => c != '.' && c != '_'}.reverse()
79
+ let nameIndex = self.writtenNames.getOrSet(unqualified) {self.writtenNames.size()}
80
+ self.writtenStrings.grabLast().push(text)
81
+ self.writtenSegments.grabLast().push(
82
+ [self.writtenColumn, 0, at.line - 1, at.column - 1, nameIndex]
83
+ )
84
+ self.writtenColumn += text.size()
85
+ }
86
+
87
+ writeLine() {
88
+ self.writtenStrings.push(Array.new())
89
+ self.writtenSegments.push(Array.new())
90
+ self.writtenColumn = 0
91
+ }
92
+
93
+ writeAnchor(): Int {
94
+ self.writtenStrings.size() - 1
95
+ }
96
+
97
+ writeAnchorLines(anchor: Int, lines: List[String]) {
98
+ if(self.writtenAnchors.has(anchor)) {
99
+ self.writtenAnchors.set(anchor, [...self.writtenAnchors.grab(anchor), ...lines])
100
+ } else {
101
+ self.writtenAnchors.set(anchor, lines)
102
+ }
103
+ }
104
+
105
+ makeOutput(): String {
106
+ SourceMap.makeOutput(
107
+ self.writtenStrings, self.writtenAnchors
108
+ )
109
+ }
110
+
111
+ makeOutputAndSourceMap(fireflyFile: String, fireflySource: Option[String]): Pair[String, Json] {
112
+ SourceMap.makeOutputAndSourceMap(
113
+ fireflyFile
114
+ fireflySource
115
+ self.writtenStrings
116
+ self.writtenSegments
117
+ self.writtenAnchors
118
+ self.writtenNames
119
+ )
120
+ }
121
+
122
+ emitModule(module: Module) {
51
123
  let selfImport = self.emitImport(self.moduleKey)
52
124
  let imports = [
53
125
  self.compilerModuleFileUrl.map {"import * as $firefly_compiler from '" + _ + "'"}.toList()
54
126
  module.imports.sortBy {_.moduleKey}.map {self.emitImport(_.moduleKey)}
55
127
  ].flatten()
56
- let parts = [
57
- if(imports.any {_ == selfImport}) {imports} else {[selfImport, ...imports]}
58
- module.types.map {self.emitTypeDefinition(_)}
59
- module.lets.map {"export " + self.emitLetDefinition(_, False, False)}
60
- module.functions.map {"export " + self.emitFunctionDefinition(_, False)}
61
- self.withEmittingAsync {module.functions.map {"export " + self.emitFunctionDefinition(_, True)}}
62
- module.extends.map {self.emitExtendsDefinition(_)}
63
- module.instances.map {self.emitInstanceDefinition(_)}
64
- ]
128
+ let liner = Liner(self, True)
129
+ if(!imports.any {_ == selfImport}) {
130
+ liner.writeLines()
131
+ self.writeUnmapped(selfImport)
132
+ }
133
+ imports.each {import =>
134
+ liner.writeLines()
135
+ self.writeUnmapped(import)
136
+ }
137
+ let anchor = self.writeAnchor()
138
+ module.types.each {
139
+ liner.writeLines()
140
+ self.emitTypeDefinition(_)
141
+ }
142
+ module.lets.each {
143
+ liner.writeLines()
144
+ self.writeUnmapped("export ")
145
+ self.emitLetDefinition(_, False, False)
146
+ }
147
+ module.functions.each {
148
+ liner.writeLines()
149
+ self.writeUnmapped("export ")
150
+ self.emitFunctionDefinition(_, False)
151
+ }
152
+ self.withEmittingAsync {
153
+ module.functions.each {
154
+ liner.writeLines()
155
+ self.writeUnmapped("export ")
156
+ self.emitFunctionDefinition(_, True)
157
+ }
158
+ }
159
+ module.extends.each {
160
+ liner.writeLines()
161
+ self.emitExtendsDefinition(_)
162
+ }
163
+ module.instances.each {
164
+ liner.writeLines()
165
+ self.emitInstanceDefinition(_)
166
+ }
65
167
  let ignoreJsImports = if(self.emitTarget == EmitExecutable && self.moduleKey.packagePair.isCore()) {
66
168
  ["esbuild"]
67
169
  } else {
68
170
  []
69
171
  }
70
172
  let jsImports = self.jsImporter.generateImports(ignoreJsImports.toSet())
71
- [jsImports, ...parts].map {_.join("\n\n")}.join("\n\n") + "\n"
173
+ if(!jsImports.isEmpty()) {
174
+ self.writeAnchorLines(anchor, jsImports)
175
+ }
72
176
  }
73
177
 
74
178
  emitImport(moduleKey: ModuleKey): String {
@@ -89,7 +193,7 @@ extend self: JsEmitter {
89
193
  }
90
194
  }
91
195
 
92
- emitRun(moduleName: String, functions: List[DFunction], mainPackagePair: PackagePair, bootstrapping: Bool): List[String] {
196
+ makeRun(moduleName: String, functions: List[DFunction], mainPackagePair: PackagePair, bootstrapping: Bool): List[String] {
93
197
  let buildMainFunction = functions.find {_.signature.name == "buildMain"}.filter {_ =>
94
198
  self.emitTarget != EmitBrowser && self.emitTarget != EmitExecutable
95
199
  }
@@ -156,14 +260,15 @@ extend self: JsEmitter {
156
260
  ].join("\n")]}.else {[]}
157
261
  }
158
262
 
159
- emitLetDefinition(definition: DLet, mutable: Bool, async: Bool): String {
160
- let mutability = if(mutable) {"let"} else {"const"}
161
- let valueCode = self.emitTerm(definition.value, async)
162
- let assignmentCode = if(!mutable || valueCode != "(void 0)") {" = " + valueCode} else {""}
163
- mutability + " " + escapeKeyword(definition.name) + assignmentCode + ";"
263
+ emitLetDefinition(definition: DLet, mutable: Bool, async: Bool) {
264
+ self.writeMapped(definition.at, if(mutable) {"let "} else {"const "})
265
+ self.writeNamed(definition.name, definition.at, escapeKeyword(definition.name))
266
+ self.writeMapped(definition.at, " = ") // TODO: No = value when the right hand side would be (void 0)
267
+ self.emitTerm(definition.value, async)
268
+ self.writeMapped(definition.at, ";")
164
269
  }
165
270
 
166
- emitExtendsDefinition(definition: DExtend): String {
271
+ emitExtendsDefinition(definition: DExtend) {
167
272
  let typeName = extractTypeName(definition.type).reverse().takeWhile {_ != '.'}.reverse()
168
273
  let methods = definition.methods.map {method =>
169
274
  method.DFunction(
@@ -172,31 +277,59 @@ extend self: JsEmitter {
172
277
  )
173
278
  )
174
279
  }
175
- let syncMethods = methods.map {"export " + self.emitFunctionDefinition(_, False)}
176
- let asyncMethods = self.withEmittingAsync {methods.map {"export " + self.emitFunctionDefinition(_, True)}}
177
- [...syncMethods, ...asyncMethods].join("\n\n")
280
+ let liner = Liner(self, True)
281
+ methods.each {
282
+ liner.writeLines()
283
+ self.writeMapped(definition.at, "export ")
284
+ self.emitFunctionDefinition(_, False)
285
+ }
286
+ self.withEmittingAsync {methods.each {
287
+ liner.writeLines()
288
+ self.writeMapped(definition.at, "export ")
289
+ self.emitFunctionDefinition(_, True)
290
+ }}
178
291
  }
179
292
 
180
- emitInstanceDefinition(definition: DInstance): String {
293
+ emitInstanceDefinition(definition: DInstance) {
181
294
  let name = makeDictionaryName(definition.traitName, firstTypeName(definition.typeArguments))
182
- let methods = definition.methods.map {self.emitFunctionDefinition(_, False)}.map {_.dropFirst("function ".size())} // TODO
183
- let asyncMethods = self.withEmittingAsync {
184
- definition.methods.map {self.emitFunctionDefinition(_, True)}.map {"async " + _.dropFirst("async function ".size())} // TODO
185
- }
186
- let body = "{\n" + [...methods, ...asyncMethods].join(",\n") + "\n}"
187
295
  definition.constraints.{
188
296
  | [] =>
189
- "export const " + name + " = " + body + ";"
297
+ self.writeMapped(definition.at, "export const ")
298
+ self.writeNamed(name, definition.at, name)
299
+ self.writeMapped(definition.at, " = ")
190
300
  | constraints =>
191
301
  let dictionaries = constraints.map {c =>
192
302
  makeDictionaryName(c.name, firstTypeName(c.generics))
193
303
  }
194
- "export function " + name + "(" + dictionaries.join(", ") + ") { return " + body + "}"
304
+ self.writeMapped(definition.at, "export function ")
305
+ self.writeNamed(name, definition.at, name)
306
+ self.writeMapped(definition.at, "(" + dictionaries.join(", ") + ") { return ")
307
+ }
308
+ self.writeMapped(definition.at, "{")
309
+ self.writeLine()
310
+ definition.methods.each {
311
+ self.emitFunctionDefinition(_, False, asMethod = True)
312
+ self.writeMapped(definition.at, ",")
313
+ self.writeLine()
314
+ }
315
+ self.withEmittingAsync {
316
+ definition.methods.map {
317
+ self.emitFunctionDefinition(_, True, asMethod = True)
318
+ self.writeMapped(definition.at, ",")
319
+ self.writeLine()
320
+ }
321
+ }
322
+ self.writeMapped(definition.at, "}")
323
+ definition.constraints.{
324
+ | [] => self.writeMapped(definition.at, ";")
325
+ | _ => self.writeMapped(definition.at, "}")
195
326
  }
196
327
  }
197
328
 
198
- emitFunctionDefinition(definition: DFunction, async: Bool, suffix: String = ""): String {
199
- let signature = self.emitSignature(definition.signature, async, suffix)
329
+ emitFunctionDefinition(definition: DFunction, async: Bool, suffix: String = "", asMethod: Bool = False): Unit {
330
+ self.emitSignature(definition.signature, async, suffix, asMethod)
331
+ self.writeMapped(definition.at, " {")
332
+ self.writeLine()
200
333
  definition.body.{
201
334
  | Lambda(_, effect, [matchCase]) {
202
335
  matchCase.patterns.all {
@@ -204,234 +337,451 @@ extend self: JsEmitter {
204
337
  | _ => False
205
338
  }
206
339
  } =>
207
- let body = self.emitTailCall {self.emitStatements(matchCase.body, True, False, async)}
208
- signature + " {\n" + body + "\n}"
340
+ self.emitTailCall {self.emitStatements(matchCase.body, True, False, async)}
209
341
  | Lambda(_, effect, cases) =>
210
- Patterns.convertAndCheck(self.otherModules, cases)
211
- let escapedArguments = definition.signature.parameters.map {_.name + "_a"}
212
- let shadowingWorkaround = definition.signature.parameters.map {p =>
213
- "const " + p.name + "_a = " + escapeKeyword(p.name) + ";"
214
- }.join("\n")
215
- let body = self.emitTailCall {
216
- let casesString = cases.pairs().map {| Pair(i, c) =>
342
+ Patterns.convertAndCheck(self.otherModules, cases) // TODO no type errors in emitter
343
+ self.emitTailCall {
344
+ let liner = Liner(self, False)
345
+ definition.signature.parameters.each {p =>
346
+ liner.writeLines()
347
+ self.writeMapped(p.at, "const ")
348
+ self.writeNamed(p.name, p.at, p.name + "_a")
349
+ self.writeMapped(p.at, " = ")
350
+ self.writeNamed(p.name, p.at, escapeKeyword(p.name))
351
+ self.writeMapped(p.at, ";")
352
+ }
353
+ let argumentTerms = definition.signature.parameters.map {p =>
354
+ {self.writeNamed(p.name, p.at, p.name + "_a")}
355
+ }
356
+ cases.pairs().each {| Pair(i, c) =>
357
+ liner.writeLines()
217
358
  let lastCase = i == cases.size() - 1
218
- self.emitCase(escapedArguments, c, [], [], True, True, False, lastCase, async)
219
- }.join("\n")
220
- shadowingWorkaround + "\n" + casesString
359
+ self.emitCase(argumentTerms, c, [], [], True, True, False, lastCase, async)
360
+ }
221
361
  }
222
- signature + " {\n" + body + "\n}"
223
362
  }
363
+ self.writeLine()
364
+ self.writeMapped(definition.at, "}")
224
365
  }
225
366
 
226
- emitTailCall(body: () => String): String {
367
+ emitTailCall(body: () => Unit) {
227
368
  let outerTailCallUsed = self.tailCallUsed
228
369
  self.tailCallUsed = False
229
- let result = body()
370
+ let anchor = self.writeAnchor()
371
+ body()
230
372
  let tailCallUsed = self.tailCallUsed
231
373
  self.tailCallUsed = outerTailCallUsed
232
374
  if(tailCallUsed) {
233
- "_tailcall: for(;;) {\n" + result + "\nreturn\n}"
234
- } else {
235
- result
375
+ self.writeAnchorLines(anchor, ["_tailcall: for(;;) {"])
376
+ self.writeLine()
377
+ self.writeUnmapped("return")
378
+ self.writeLine()
379
+ self.writeUnmapped("}")
236
380
  }
237
381
  }
238
382
 
239
- emitSignature(signature: Signature, async: Bool, suffix: String = ""): String {
240
- let parameterStrings = signature.parameters.map {self.emitParameter(_, async)}
241
- let dictionaryStrings = signature.constraints.map {c =>
242
- makeDictionaryName(c.name, firstTypeName(c.generics))
243
- }
244
- let controller = if(async) {["$task"]} else {[]}
245
- let parameters = "(" + [...parameterStrings, ...dictionaryStrings, ...controller].join(", ") + ")"
383
+ emitSignature(signature: Signature, async: Bool, suffix: String = "", asMethod: Bool = False): Unit {
246
384
  let prefix = if(async) {"async "} else {""}
247
385
  let asyncSuffix = if(async) {"$"} else {""}
248
- prefix + "function " + escapeKeyword(signature.name) + suffix + asyncSuffix + parameters
386
+ let fullPrefix = prefix + if(asMethod) {""} else {"function "}
387
+ self.writeMapped(signature.at, fullPrefix)
388
+ self.writeNamed(signature.name, signature.at, escapeKeyword(signature.name) + suffix + asyncSuffix)
389
+
390
+ self.writeMapped(signature.at, "(")
391
+ let comma = Comma(self)
392
+
393
+ signature.parameters.each {
394
+ comma.writeComma()
395
+ self.emitParameter(_, async)
396
+ }
397
+ signature.constraints.each {c =>
398
+ comma.writeComma()
399
+ self.writeMapped(c.at, makeDictionaryName(c.name, firstTypeName(c.generics)))
400
+ }
401
+ if(async) {
402
+ comma.writeComma()
403
+ self.writeMapped(signature.at, "$task")
404
+ }
405
+
406
+ self.writeMapped(signature.at, ")")
249
407
  }
250
408
 
251
- emitParameter(parameter: Parameter, async: Bool): String {
252
- let defaultValue = parameter.default.map {" = " + self.emitTerm(_, async) }.else {""}
253
- escapeKeyword(parameter.name) + defaultValue
409
+ emitParameter(parameter: Parameter, async: Bool) {
410
+ self.writeNamed(parameter.name, parameter.at, escapeKeyword(parameter.name))
411
+ parameter.default.each {e =>
412
+ self.writeMapped(e.at, " = ")
413
+ self.emitTerm(e, async)
414
+ }
254
415
  }
255
416
 
256
- emitTypeDefinition(definition: DType): String {
257
- if(definition.newtype) {"// newtype " + definition.name} else:
258
- "// type " + definition.name + "\n" +
259
- definition.variants.map {self.emitVariantDefinition(definition, _)}.join("\n")
417
+ emitTypeDefinition(definition: DType) {
418
+ if(definition.newtype) {
419
+ self.writeMapped(definition.at, "// newtype " + definition.name)
420
+ } else {
421
+ self.writeMapped(definition.at, "// type " + definition.name)
422
+ self.writeLine()
423
+ let liner = Liner(self, double = False)
424
+ definition.variants.each {
425
+ liner.writeLines()
426
+ self.emitVariantDefinition(definition, _)
427
+ }
428
+ }
260
429
  }
261
430
 
262
- emitVariantDefinition(typeDefinition: DType, definition: Variant): String {
431
+ emitVariantDefinition(typeDefinition: DType, definition: Variant) {
263
432
  let allFields = [...typeDefinition.commonFields, ...definition.fields]
264
- let fields = allFields.map {escapeKeyword(_.name)}.join(", ")
433
+ function emitFields() {
434
+ let comma = Comma(self)
435
+ allFields.each {f =>
436
+ comma.writeComma()
437
+ self.writeNamed(f.name, f.at, escapeKeyword(f.name))
438
+ }
439
+ }
440
+ function emitConstructor() {
441
+ self.writeMapped(definition.at, "export function ")
442
+ self.writeNamed(definition.name, definition.at, definition.name)
443
+ self.writeMapped(definition.at, "(")
444
+ emitFields()
445
+ self.writeMapped(definition.at, ") {")
446
+ self.writeLine()
447
+ }
265
448
  if(allFields.isEmpty()) {
266
- "const " + definition.name + "$ = {" + definition.name + ": true};\n" +
267
- "export function " + definition.name + "(" + fields + ") {\n" +
268
- "return " + definition.name + "$;\n" +
269
- "}"
449
+ self.writeMapped(definition.at, "const ")
450
+ self.writeNamed(definition.name, definition.at, definition.name + "$")
451
+ self.writeMapped(definition.at, " = {")
452
+ self.writeNamed(definition.name, definition.at, definition.name)
453
+ self.writeMapped(definition.at, ": true};")
454
+ self.writeLine()
455
+ emitConstructor()
456
+ self.writeMapped(definition.at, "return ")
457
+ self.writeNamed(definition.name, definition.at, definition.name + "$")
458
+ self.writeMapped(definition.at, ";")
459
+ self.writeLine()
460
+ self.writeMapped(definition.at, "}")
270
461
  } elseIf {typeDefinition.variants.size() == 1} {
271
- "export function " + definition.name + "(" + fields + ") {\n" +
272
- "return {" + fields + "};\n" +
273
- "}"
462
+ emitConstructor()
463
+ self.writeMapped(definition.at, "return {")
464
+ emitFields()
465
+ self.writeMapped(definition.at, "};")
466
+ self.writeLine()
467
+ self.writeMapped(definition.at, "}")
274
468
  } else {
275
- "export function " + definition.name + "(" + fields + ") {\n" +
276
- "return {" + definition.name + ": true, " + fields + "};\n" +
277
- "}"
469
+ emitConstructor()
470
+ self.writeMapped(definition.at, "return {")
471
+ self.writeNamed(definition.name, definition.at, definition.name)
472
+ self.writeMapped(definition.at, ": true, ")
473
+ emitFields()
474
+ self.writeMapped(definition.at, "};")
475
+ self.writeLine()
476
+ self.writeMapped(definition.at, "}")
278
477
  }
279
478
  }
280
479
 
281
- emitTerm(term: Term, async: Bool, ignored: Bool = False): String {term.{
480
+ emitTerm(term: Term, async: Bool, ignored: Bool = False) {term.{
282
481
  | EString(at, value) {value.startsWith("\"\"\"")} =>
283
- "`" + value.dropFirst(3).dropLast(3).replace("`", "\\`") + "`" // TODO: Fix escaping
284
- | EString(at, value) => value
285
- | EChar(at, value) => charLiteralToNumber(value)
286
- | EInt(at, value) => value
287
- | EFloat(at, value) => value
288
- | EVariable(at, name) => escapeResolved(name)
482
+ self.writeMapped(at, "\"" +
483
+ value.dropFirst(3).dropLast(3).replace("\r", "\\r").replace("\n", "\\n").replace("\"", "\\\"") +
484
+ "\"")
485
+ | EString(at, value) =>
486
+ self.writeMapped(at, value)
487
+ | EChar(at, value) =>
488
+ self.writeMapped(at, charLiteralToNumber(value))
489
+ | EInt(at, value) =>
490
+ self.writeMapped(at, value)
491
+ | EFloat(at, value) =>
492
+ self.writeMapped(at, value)
493
+ | EVariable(at, name) =>
494
+ self.writeNamed(name, at, escapeResolved(name))
289
495
  | EList(at, _, items) =>
290
- self.emitList(items, async)
496
+ self.emitList(at, items, async)
291
497
  | EVariant(at, "ff:core/Bool.False", _, _) =>
292
- "false"
498
+ self.writeMapped(at, "false")
293
499
  | EVariant(at, "ff:core/Bool.True", _, _) =>
294
- "true"
500
+ self.writeMapped(at, "true")
295
501
  | EVariant(at, "ff:core/Unit.Unit", _, _) =>
296
- "(void 0)"
502
+ self.writeMapped(at, "(void 0)")
297
503
  | EVariant(at, name, _, arguments) =>
298
- let argumentsString = arguments.toList().flatten().map {self.emitArgument(at, _, async)}.join(", ")
299
504
  let newtype = self.processVariant(name)
300
- if(newtype) {argumentsString} else:
301
- escapeResolved(name) + "(" + argumentsString + ")"
505
+ if(newtype) {
506
+ self.emitArgument(at, arguments.grab().grabFirst(), async)
507
+ } else {
508
+ self.writeNamed(name, at, escapeResolved(name))
509
+ self.writeMapped(term.at, "(")
510
+ let comma = Comma(self)
511
+ arguments.toList().flatten().each {
512
+ comma.writeComma()
513
+ self.emitArgument(at, _, async)
514
+ }
515
+ self.writeMapped(term.at, ")")
516
+ }
302
517
  | EVariantIs(at, "ff:core/Bool.False", _) =>
303
- "function(_v) { return !_v ? ff_core_Option.Some(_v) : ff_core_Option.None(); }"
518
+ self.writeMapped(at, "function(_v) { return !_v ? ff_core_Option.Some(_v) : ff_core_Option.None(); }")
304
519
  | EVariantIs(at, "ff:core/Bool.True", _) =>
305
- "function(_v) { return _v ? ff_core_Option.Some(_v) : ff_core_Option.None(); }"
520
+ self.writeMapped(at, "function(_v) { return _v ? ff_core_Option.Some(_v) : ff_core_Option.None(); }")
306
521
  | EVariantIs(at, "ff:core/Unit.Unit", _) =>
307
- "function(_v) { return ff_core_Option.Some(_v); }"
522
+ self.writeMapped(at, "function(_v) { return ff_core_Option.Some(_v); }")
308
523
  | EVariantIs(at, name, _) =>
309
524
  let n = name.reverse().takeWhile {_ != '.'}.reverse()
310
- "(function(_v) { " +
311
- "return _v." + escapeResolved(n) + " ? ff_core_Option.Some(_v) : ff_core_Option.None();" +
312
- "})"
525
+ self.writeMapped(at, "(function(_v) { return _v.")
526
+ self.writeNamed(n, at, escapeResolved(n))
527
+ self.writeMapped(at, " ? ff_core_Option.Some(_v) : ff_core_Option.None();})")
313
528
  | ECopy(at, name, record, fields) =>
314
- let fieldCode = fields.map {f => escapeKeyword(f.name) + " = " + self.emitTerm(f.value, async)}.join(", ")
315
- "{..." + self.emitTerm(record, async) + ", " + fieldCode + "}"
529
+ self.writeMapped(at, "{...")
530
+ let comma = Comma(self)
531
+ comma.writeComma()
532
+ self.emitTerm(record, async)
533
+ fields.each {f =>
534
+ comma.writeComma()
535
+ self.writeNamed(f.name, at, escapeKeyword(f.name))
536
+ self.writeMapped(at, " = ")
537
+ self.emitTerm(f.value, async)
538
+ }
539
+ self.writeMapped(at, "}")
316
540
  | EField(at, newtype, record, field) =>
317
541
  if(newtype) {self.emitTerm(record, async)} else:
318
- self.emitTerm(record, async) + "." + escapeKeyword(field)
542
+ self.emitTerm(record, async)
543
+ self.writeMapped(at, ".")
544
+ self.writeNamed(field, at, escapeKeyword(field))
319
545
  | ELambda(at, Lambda(_, effect, [MatchCase(_, patterns, [], body)])) {
320
546
  patterns.all {| PVariable _ => True | _ => False }
321
547
  } =>
322
548
  let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
323
- let patternParameters = patterns.map {
324
- | PVariable p => p.name.map(escapeKeyword).else {"_"}
325
- | _ => panic("!")
549
+ self.writeMapped(term.at, "(")
550
+ if(newAsync) {self.writeMapped(term.at, "async ")}
551
+ self.writeMapped(term.at, "(")
552
+ let comma = Comma(self)
553
+ patterns.each {
554
+ | PVariable(patternAt, Some(name)) =>
555
+ comma.writeComma()
556
+ self.writeNamed(name, patternAt, escapeKeyword(name))
557
+ | PVariable(patternAt, None) =>
558
+ comma.writeComma()
559
+ self.writeMapped(patternAt, "_")
560
+ | _ =>
561
+ throw(CompileError(at, "Internal compiler error"))
326
562
  }
327
- let controller = if(newAsync) {["$task"]} else {[]}
328
- let parameters = [...patternParameters, ...controller].join(", ")
329
- let prefix = if(newAsync) {"async "} else {""}
330
- "(" + prefix + "(" + parameters + ") => {\n" + self.emitStatements(body, True, False, newAsync) + "\n})"
563
+ if(newAsync) {
564
+ comma.writeComma()
565
+ self.writeMapped(term.at, "$task")
566
+ }
567
+ self.writeMapped(term.at, ") => {")
568
+ self.writeLine()
569
+ self.emitStatements(body, True, False, newAsync)
570
+ self.writeLine()
571
+ self.writeMapped(term.at, "})")
331
572
  | ELambda(at, Lambda(_, effect, cases)) =>
332
- let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
333
- let controller = if(newAsync) {["$task"]} else {[]}
334
573
  Patterns.convertAndCheck(self.otherModules, cases)
335
- let arguments = cases.grab(0).patterns.pairs().map {"_" + (_.first + 1)}
336
- let escapedArguments = arguments.map(escapeKeyword) // emitCase arguments must be preescaped
337
- let caseStrings = cases.pairs().map {| Pair(i, c) =>
574
+ let arguments = cases.grab(0).patterns.pairs().map {| Pair(i, p) => Pair(p.at, "_" + (i + 1))}
575
+ let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
576
+
577
+ self.writeMapped(term.at, "(")
578
+ if(newAsync) {self.writeMapped(term.at, "async ")}
579
+ self.writeMapped(term.at, "(")
580
+ let comma = Comma(self)
581
+ arguments.each {a =>
582
+ comma.writeComma()
583
+ self.writeMapped(a.first, a.second)
584
+ }
585
+ let argumentTerms = arguments.map {| Pair(at, n) => {self.writeMapped(at, n)}}
586
+ if(newAsync) {
587
+ comma.writeComma()
588
+ self.writeMapped(term.at, "$task")
589
+ }
590
+ self.writeMapped(term.at, ") => {")
591
+ self.writeLine()
592
+ let liner = Liner(self, double = False)
593
+ cases.pairs().each {| Pair(i, c) =>
594
+ liner.writeLines()
338
595
  let lastCase = i == cases.size() - 1
339
- self.emitCase(escapedArguments, c, [], [], True, True, False, lastCase, newAsync)
596
+ self.emitCase(argumentTerms, c, [], [], True, True, False, lastCase, newAsync)
340
597
  }
341
- let prefix = if(newAsync) {"async "} else {""}
342
- "(" + prefix + "(" + [...escapedArguments, ...controller].join(", ") + ") => " +
343
- "{\n" + caseStrings.join("\n") + "\n})"
598
+ self.writeLine()
599
+ self.writeMapped(term.at, "})")
344
600
  | EPipe(at, value, effect, function) =>
345
601
  let await = async && effectTypeIsAsync(effect)
346
- let c = if(await) {", $task"} else {""}
347
- let call = "(" + self.emitTerm(function, async) + ")(" + self.emitTerm(value, async) + c + ")"
348
- if(await) {"(await " + call + ")"} else {call}
349
- | _ {self.emitAssignment(term, async) | Some(code)} =>
350
- if(ignored) {code} else {"(" + code + ", void 0)"}
602
+ if(await) {self.writeMapped(term.at, "(await ")}
603
+ self.writeMapped(term.at, "(")
604
+ self.emitTerm(function, async)
605
+ self.writeMapped(term.at, ")")
606
+ self.writeMapped(term.at, "(")
607
+ self.emitTerm(value, async)
608
+ if(await) {self.writeMapped(term.at, ", $task")}
609
+ self.writeMapped(term.at, ")")
610
+ if(await) {self.writeMapped(term.at, ")")}
611
+ | _ {self.emitAssignment(term, async, ignored)} =>
351
612
  | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries) {
352
- self.emitSpecialCall(term, async, name, arguments.map {_.value}, dictionaries) | Some(code)
613
+ self.emitSpecialCall(term, async, name, arguments.map {_.value}, dictionaries)
353
614
  } =>
354
- code
355
615
  | ECall(at, StaticCall(name, _, True), effect, typeArguments, arguments, dictionaries) =>
356
616
  let await = async && effectTypeIsAsync(effect)
357
- let dictionaryStrings = dictionaries.map {self.emitDictionary(_)}
617
+ let dictionaryStrings = dictionaries.map {self.makeDictionary(_)}
358
618
  let ds = dictionaryStrings.dropFirst()
359
619
  let d = dictionaryStrings.grabFirst()
360
620
  let asyncSuffix = if(await) {"$"} else {""}
361
- let n = escapeKeyword(name.reverse().takeWhile {_ != '.'}.reverse()) + asyncSuffix
362
- let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
363
- let controller = if(await) {["$task"]} else {[]}
364
- let call = d + "." + n + "(" + [...emittedArguments, ...ds, ...controller].join(", ") + ")"
365
- if(await) {"(await " + call + ")"} else {call}
621
+ if(await) {self.writeMapped(term.at, "(await ")}
622
+ self.writeMapped(term.at, d)
623
+ self.writeMapped(term.at, ".")
624
+ self.writeNamed(name, at, escapeKeyword(name.reverse().takeWhile {_ != '.'}.reverse()) + asyncSuffix)
625
+ self.writeMapped(term.at, "(")
626
+ let comma = Comma(self)
627
+ arguments.each {
628
+ comma.writeComma()
629
+ self.emitArgument(at, _, async)
630
+ }
631
+ ds.each {
632
+ comma.writeComma()
633
+ self.writeMapped(term.at, _)
634
+ }
635
+ if(await) {
636
+ comma.writeComma()
637
+ self.writeMapped(term.at, "$task")
638
+ }
639
+ self.writeMapped(term.at, ")")
640
+ if(await) {self.writeMapped(term.at, ")")}
366
641
  | ECall(at, StaticCall(name, _, _), effect, typeArguments, arguments, dictionaries) =>
367
642
  detectIfElse(term).{
368
643
  | [] =>
369
644
  let await = async && effectTypeIsAsync(effect)
370
- let ds = dictionaries.map {self.emitDictionary(_)}
371
- let functionCode = escapeResolved(name) + if(await) {"$"} else {""}
372
- let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
373
- let controller = if(await) {["$task"]} else {[]}
374
- let call = functionCode + "(" + [...emittedArguments, ...ds, ...controller].join(", ") + ")"
375
- if(await) {"(await " + call + ")"} else {call}
645
+ let dictionaryStrings = dictionaries.map {self.makeDictionary(_)}
646
+ let asyncSuffix = if(await) {"$"} else {""}
647
+ if(await) {self.writeMapped(term.at, "(await ")}
648
+ self.writeNamed(name, at, escapeResolved(name) + asyncSuffix)
649
+ self.writeMapped(term.at, "(")
650
+ let comma = Comma(self)
651
+ arguments.each {
652
+ comma.writeComma()
653
+ self.emitArgument(at, _, async)
654
+ }
655
+ dictionaryStrings.each {
656
+ comma.writeComma()
657
+ self.writeMapped(term.at, _)
658
+ }
659
+ if(await) {
660
+ comma.writeComma()
661
+ self.writeMapped(term.at, "$task")
662
+ }
663
+ self.writeMapped(term.at, ")")
664
+ if(await) {self.writeMapped(term.at, ")")}
376
665
  | [Pair(EVariant(_, "ff:core/Bool.True", _, _), elseBody), ...list] =>
377
- "(" + list.foldLeft(self.emitTerm(elseBody, async)) {| otherwise, Pair(condition, body) =>
378
- self.emitTerm(condition, async) +
379
- "\n? " + self.emitTerm(body, async) + "\n: " + otherwise
380
- } + ")"
666
+ self.writeMapped(term.at, "(")
667
+ list.reverse().each {| Pair(condition, body) =>
668
+ self.emitTerm(condition, async)
669
+ self.writeLine()
670
+ self.writeMapped(condition.at, "? ")
671
+ self.emitTerm(body, async)
672
+ self.writeLine()
673
+ self.writeMapped(condition.at, ": ")
674
+ }
675
+ self.emitTerm(elseBody, async)
676
+ self.writeMapped(term.at, ")")
381
677
  | list =>
382
- "(" + list.foldLeft("ff_core_Option.None()") {| otherwise, Pair(condition, body) =>
383
- self.emitTerm(condition, async) +
384
- "\n? ff_core_Option.Some(" + self.emitTerm(body, async) + ")\n: " + otherwise
385
- } + ")"
678
+ self.writeMapped(term.at, "(")
679
+ list.reverse().each {| Pair(condition, body) =>
680
+ self.emitTerm(condition, async)
681
+ self.writeLine()
682
+ self.writeMapped(term.at, "? ff_core_Option.Some(")
683
+ self.emitTerm(body, async)
684
+ self.writeMapped(term.at, ")")
685
+ self.writeLine()
686
+ self.writeMapped(term.at, ": ")
687
+ }
688
+ self.writeMapped(term.at, "ff_core_Option.None())")
386
689
  }
387
690
  | ECall(at, DynamicCall(function, _), effect, typeArguments, arguments, dictionaries) =>
388
691
  let await = async && effectTypeIsAsync(effect)
389
692
  if(!dictionaries.isEmpty()) {fail(at, "Internal error: Dictionaries in lambda call")}
390
- let functionCode = self.emitTerm(function, async)
391
- let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
392
- let controller = if(await) {["$task"]} else {[]}
393
- let call = functionCode + "(" + [...emittedArguments, ...controller].join(", ") + ")"
394
- if(await) {"(await " + call + ")"} else {call}
693
+ if(await) {self.writeMapped(term.at, "(await ")}
694
+ self.emitTerm(function, async)
695
+ self.writeMapped(term.at, "(")
696
+ let comma = Comma(self)
697
+ arguments.each {
698
+ comma.writeComma()
699
+ self.emitArgument(at, _, async)
700
+ }
701
+ if(await) {
702
+ comma.writeComma()
703
+ self.writeMapped(term.at, "$task")
704
+ }
705
+ self.writeMapped(term.at, ")")
706
+ if(await) {self.writeMapped(term.at, ")")}
395
707
  | ERecord(at, fields) =>
396
- if(fields.isEmpty()) {"{}"} else {
397
- let list = fields.map {f => escapeKeyword(f.name) + ": " + self.emitTerm(f.value, async)}
398
- "{\n" + list.join(",\n") + "\n}"
708
+ if(fields.isEmpty()) {
709
+ self.writeMapped(term.at, "{}")
710
+ } else {
711
+ self.writeMapped(term.at, "{")
712
+ self.writeLine()
713
+ let comma = Comma(self)
714
+ let liner = Liner(self, double = False)
715
+ fields.each {f =>
716
+ comma.writeComma()
717
+ liner.writeLines()
718
+ self.writeNamed(f.name, f.at, escapeKeyword(f.name))
719
+ self.writeMapped(f.at, ": ")
720
+ self.emitTerm(f.value, async)
721
+ }
722
+ self.writeLine()
723
+ self.writeMapped(term.at, "}")
399
724
  }
400
725
  | EWildcard(at, index) =>
401
726
  if(index == 0) {fail(at, "Unbound wildcard")}
402
- "_w" + index
727
+ self.writeMapped(at, "_w" + index)
403
728
  | ESequential(_, ESequential(_, ESequential(_, before1, before2), before3), after) {
404
729
  safeCommable(before1) && safeCommable(before2) && safeCommable(before3) && safeCommable(after)
405
730
  } =>
406
- "(" + self.emitTerm(before1, async, ignored = True) + ", " +
407
- self.emitTerm(before2, async, ignored = True) + ", " +
408
- self.emitTerm(before3, async, ignored = True) + ", " +
409
- self.emitTerm(after, async, ignored) + ")"
731
+ self.writeMapped(term.at, "(")
732
+ self.emitTerm(before1, async, ignored = True)
733
+ self.writeMapped(term.at, ", ")
734
+ self.emitTerm(before2, async, ignored = True)
735
+ self.writeMapped(term.at, ", ")
736
+ self.emitTerm(before3, async, ignored = True)
737
+ self.writeMapped(term.at, ", ")
738
+ self.emitTerm(after, async, ignored)
739
+ self.writeMapped(term.at, ")")
410
740
  | ESequential(_, ESequential(_, before1, before2), after) {
411
741
  safeCommable(before1) && safeCommable(before2) && safeCommable(after)
412
742
  } =>
413
- "(" + self.emitTerm(before1, async, ignored = True) + ", " +
414
- self.emitTerm(before2, async, ignored = True) + ", " +
415
- self.emitTerm(after, async, ignored) + ")"
743
+ self.writeMapped(term.at, "(")
744
+ self.emitTerm(before1, async, ignored = True)
745
+ self.writeMapped(term.at, ", ")
746
+ self.emitTerm(before2, async, ignored = True)
747
+ self.writeMapped(term.at, ", ")
748
+ self.emitTerm(after, async, ignored)
749
+ self.writeMapped(term.at, ")")
416
750
  | ESequential(_, before, after) {
417
751
  safeCommable(before) && safeCommable(after)
418
752
  } =>
419
- "(" + self.emitTerm(before, async, ignored = True) + ", " +
420
- self.emitTerm(after, async, ignored) + ")"
753
+ self.writeMapped(term.at, "(")
754
+ self.emitTerm(before, async, ignored = True)
755
+ self.writeMapped(term.at, ", ")
756
+ self.emitTerm(after, async, ignored)
757
+ self.writeMapped(term.at, ")")
421
758
  | _ {async} =>
422
- "(await (async function() {\n" + self.emitStatements(term, True, False, async) + "\n})())"
759
+ self.writeMapped(term.at, "(await (async function() {")
760
+ self.writeLine()
761
+ self.emitStatements(term, True, False, async)
762
+ self.writeLine()
763
+ self.writeMapped(term.at, "})())")
423
764
  | _ =>
424
- "(function() {\n" + self.emitStatements(term, True, False, async) + "\n})()"
765
+ self.writeMapped(term.at, "(function() {")
766
+ self.writeLine()
767
+ self.emitStatements(term, True, False, async)
768
+ self.writeLine()
769
+ self.writeMapped(term.at, "})()")
425
770
  }}
426
771
 
427
- emitField(term: Term, async: Bool, dot: String = "."): String {
772
+ emitField(term: Term, async: Bool, dot: String = ".") {
428
773
  term.{
429
- | EString(_, q) {safeBare(q) | Some(s)} => dot + s
430
- | _ => "[" + self.emitTerm(term, async) + "]"
774
+ | EString(at, q) {safeBare(q) | Some(s)} =>
775
+ self.writeMapped(term.at, dot)
776
+ self.writeNamed(s, at, s)
777
+ | _ =>
778
+ self.writeMapped(term.at, "[")
779
+ self.emitTerm(term, async)
780
+ self.writeMapped(term.at, "]")
431
781
  }
432
782
  }
433
783
 
434
- emitDictionary(d: Dictionary): String {
784
+ makeDictionary(d: Dictionary): String {
435
785
  let m = if(d.moduleKey.name != "") {
436
786
  d.moduleKey.packagePair.groupName("_") + "_" +
437
787
  d.moduleKey.folders.map {_ + "_"}.join() +
@@ -441,81 +791,130 @@ extend self: JsEmitter {
441
791
  if(d.dictionaries.isEmpty()) {
442
792
  c
443
793
  } else {
444
- c + "(" + d.dictionaries.map {self.emitDictionary(_)}.join(", ") + ")"
794
+ c + "(" + d.dictionaries.map {self.makeDictionary(_)}.join(", ") + ")"
445
795
  }
446
796
  }
447
797
 
448
- emitStatements(term: Term, last: Bool, break: Bool, async: Bool): String {
798
+ emitStatements(term: Term, last: Bool, break: Bool, async: Bool) {
449
799
  term.{
450
800
  | EFunctions(at, functions, body) =>
451
- let functionStrings = functions.map {f =>
801
+ let liner = Liner(self, double = False)
802
+ functions.each {f =>
803
+ liner.writeLines()
452
804
  let newAsync = self.emittingAsync && effectTypeIsAsync(f.signature.effect)
453
805
  self.emitFunctionDefinition(f, newAsync)
454
806
  }
455
- functionStrings.join("\n") + "\n" + self.emitStatements(body, last, break, async)
807
+ liner.writeLines()
808
+ self.emitStatements(body, last, break, async)
456
809
  | ELet(at, mutable, name, valueType, value, body) =>
457
- self.emitLetDefinition(DLet(at, name, valueType, value), mutable, async) + "\n" +
810
+ self.emitLetDefinition(DLet(at, name, valueType, value), mutable, async)
811
+ self.writeLine()
458
812
  self.emitStatements(body, last, break, async)
459
813
  | EVariant(at, "ff:core/Unit.Unit", _, _) =>
460
- ""
461
814
  | ESequential(_, EVariant(_, "ff:core/Unit.Unit", _, _), after) =>
462
815
  self.emitStatements(after, last, break, async)
463
816
  | ESequential(_, before, EVariant(_, "ff:core/Unit.Unit", _, _)) =>
464
817
  self.emitStatements(before, False, break, async)
465
818
  | ESequential(at, before, after) =>
466
- self.emitStatements(before, False, False, async) + ";\n" +
819
+ self.emitStatements(before, False, False, async)
820
+ self.writeMapped(term.at, ";")
821
+ self.writeLine()
467
822
  self.emitStatements(after, last, break, async)
468
823
  | ECall(at, StaticCall(name, True, instanceCall), effect, _, arguments, _) =>
469
824
  if(instanceCall) {throw(CompileError(at, "Not yet implemented: Tail calls on trait methods."))}
470
825
  self.tailCallUsed = True
471
- let pair = arguments.map {a =>
472
- Some(Pair(
473
- "const " + escapeKeyword(a.name.grab() + "_r") + " = " + self.emitTerm(a.value, async) + ";"
474
- escapeKeyword(a.name.grab()) + " = " + escapeKeyword(a.name.grab() + "_r")
475
- ))
476
- }.collect {_}.unzip()
477
- "{\n" + pair.first.join("\n") + "\n" + pair.second.join("\n") + "\ncontinue _tailcall\n}"
826
+ self.writeMapped(term.at, "{")
827
+ arguments.each {a =>
828
+ self.writeLine()
829
+ self.writeMapped(a.at, "const ")
830
+ self.writeNamed(a.name.grab(), a.at, escapeKeyword(a.name.grab() + "_r"))
831
+ self.writeMapped(a.at, " = ")
832
+ self.emitTerm(a.value, async)
833
+ self.writeMapped(a.at, ";")
834
+ }
835
+ arguments.each {a =>
836
+ self.writeLine()
837
+ self.writeNamed(a.name.grab(), a.at, escapeKeyword(a.name.grab()))
838
+ self.writeMapped(term.at, " = ")
839
+ self.writeNamed(a.name.grab(), a.at, escapeKeyword(a.name.grab() + "_r"))
840
+ }
841
+ self.writeLine()
842
+ self.writeMapped(term.at, "continue _tailcall")
843
+ self.writeLine()
844
+ self.writeMapped(term.at, "}")
478
845
  | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries) {
479
- self.emitSpecialStatement(term, last, async, name, arguments.map {_.value}, dictionaries) | Some(code)
846
+ self.emitSpecialStatement(term, last, async, name, arguments.map {_.value}, dictionaries)
480
847
  } =>
481
- code
482
848
  | EPipe(at, value, _, ELambda(_, Lambda(_, _, cases))) =>
483
849
  Patterns.convertAndCheck(self.otherModules, cases)
484
- if(!last && !break) {"do "}.else {""} +
485
- "{\nconst _1 = " + self.emitTerm(value, async) + ";\n" +
486
- cases.pairs().map {| Pair(i, c) =>
850
+ if(!last && !break) {self.writeMapped(term.at, "do ")}
851
+ self.writeMapped(term.at, "{")
852
+ self.writeLine()
853
+ self.writeMapped(term.at, "const _1 = ")
854
+ self.emitTerm(value, async)
855
+ self.writeMapped(term.at, ";")
856
+ cases.pairs().each {| Pair(i, c) =>
857
+ self.writeLine()
487
858
  let lastCase = i == cases.size() - 1
488
- self.emitCase(["_1"], c, [], [], True, last, break, lastCase, async)
489
- }.join("\n") +
490
- "\n}" + if(!last && !break) {" while(false)"}.else {""}
491
- | _ {self.emitAssignment(term, async) | Some(code)} =>
492
- code
859
+ self.emitCase([{self.writeMapped(c.at, "_1")}], c, [], [], True, last, break, lastCase, async)
860
+ }
861
+ self.writeLine()
862
+ self.writeMapped(term.at, "}")
863
+ if(!last && !break) {self.writeMapped(term.at, " while(false)")}
864
+ | _ {self.emitAssignment(term, async, True)} =>
493
865
  | _ =>
494
866
  detectIfElse(term).{
495
867
  | [] =>
496
868
  if(break) {
497
- "if(!" + self.emitTerm(term, async) + ") break"
869
+ self.writeMapped(term.at, "if(!")
870
+ self.emitTerm(term, async)
871
+ self.writeMapped(term.at, ") break")
498
872
  } elseIf {last} {
499
- "return " + self.emitTerm(term, async)
873
+ self.writeMapped(term.at, "return ")
874
+ self.emitTerm(term, async)
500
875
  } else {
501
876
  self.emitTerm(term, async, ignored = True)
502
877
  }
503
878
  | [Pair(EVariant(_, "ff:core/Bool.True", _, _), elseBody), ...list] =>
504
- let initial = "{\n" + self.emitStatements(elseBody, last, break, async) + "\n}"
505
- list.foldLeft(initial) {| otherwise, Pair(condition, body) =>
506
- "if(" + self.emitTerm(condition, async) + ") {\n" +
507
- self.emitStatements(body, last, break, async) + "\n} else " + otherwise
879
+ list.reverse().each {| Pair(condition, body) =>
880
+ self.writeMapped(condition.at, "if(")
881
+ self.emitTerm(condition, async)
882
+ self.writeMapped(condition.at, ") {")
883
+ self.writeLine()
884
+ self.emitStatements(body, last, break, async)
885
+ self.writeLine()
886
+ self.writeMapped(condition.at, "} else ")
508
887
  }
888
+ self.writeMapped(term.at, "{")
889
+ self.writeLine()
890
+ self.emitStatements(elseBody, last, break, async)
891
+ self.writeLine()
892
+ self.writeMapped(term.at, "}")
509
893
  | list {!last} =>
510
- list.foldLeft("{}") {| otherwise, Pair(condition, body) =>
511
- "if(" + self.emitTerm(condition, async) + ") {\n" +
512
- self.emitStatements(body, last, break, async) + "\n} else " + otherwise
894
+ list.reverse().each {| Pair(condition, body) =>
895
+ self.writeMapped(condition.at, "if(")
896
+ self.emitTerm(condition, async)
897
+ self.writeMapped(condition.at, ") {")
898
+ self.writeLine()
899
+ self.emitStatements(body, last, break, async)
900
+ self.writeLine()
901
+ self.writeMapped(condition.at, "} else ")
513
902
  }
903
+ self.writeMapped(term.at, "{}")
514
904
  | list =>
515
- list.foldLeft("return ff_core_Option.None()") {| otherwise, Pair(condition, body) =>
516
- "if(" + self.emitTerm(condition, async) + ") {\n" +
517
- "return ff_core_Option.Some(" + self.emitTerm(body, async) + ")\n} else " + otherwise
905
+ list.reverse().each {| Pair(condition, body) =>
906
+ self.writeMapped(condition.at, "if(")
907
+ self.emitTerm(condition, async)
908
+ self.writeMapped(condition.at, ") {")
909
+ self.writeLine()
910
+ self.writeMapped(condition.at, "return ff_core_Option.Some(")
911
+ self.emitTerm(body, async)
912
+ self.writeMapped(condition.at, ")")
913
+ self.writeLine()
914
+ self.writeMapped(condition.at, "} else ")
518
915
  }
916
+ self.writeMapped(term.at, "return ff_core_Option.None()")
917
+
519
918
  }
520
919
  }
521
920
  }
@@ -523,44 +922,89 @@ extend self: JsEmitter {
523
922
  emitAssignment(
524
923
  term: Term
525
924
  async: Bool
526
- ): Option[String] {
527
- | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries), _ =>
528
- name.{
529
- | "ff:core/JsValue.JsValue_set" {arguments.map {_.value} | [e1, e2, e3]} =>
530
- Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " = " + self.emitTerm(e3, async))
531
- | "ff:core/JsValue.JsValue_increment" {arguments.map {_.value} | [e1, e2, e3]} =>
532
- Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " += " + self.emitTerm(e3, async))
533
- | "ff:core/JsValue.JsValue_decrement" {arguments.map {_.value} | [e1, e2, e3]} =>
534
- Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " -= " + self.emitTerm(e3, async))
535
- | "ff:core/JsSystem.JsSystem_set" {arguments.map {_.value} | [e1, EString(_, q), e3]} {
536
- noSideEffects(e1)} {safeBare(q) | Some(s)
537
- } =>
538
- Some(s + " = " + self.emitTerm(e3, async))
539
- | "ff:core/JsSystem.JsSystem_increment" {arguments.map {_.value} | [e1, EString(_, q), e3]} {
540
- noSideEffects(e1)} {safeBare(q) | Some(s)
541
- } =>
542
- Some(s + " += " + self.emitTerm(e3, async))
543
- | "ff:core/JsSystem.JsSystem_decrement" {arguments.map {_.value} | [e1, EString(_, q), e3]} {
544
- noSideEffects(e1)} {safeBare(q) | Some(s)
545
- } =>
546
- Some(s + " -= " + self.emitTerm(e3, async))
547
- | "ff:core/Js.set" {arguments.map {_.value} | [EString(_, q), e2]} {safeBare(q) | Some(s)} =>
548
- Some(s + " = " + self.emitTerm(e2, async))
549
- | "ff:core/Js.increment" {arguments.map {_.value} | [EString(_, q), e2]} {safeBare(q) | Some(s)} =>
550
- Some(s + " += " + self.emitTerm(e2, async))
551
- | "ff:core/Js.decrement" {arguments.map {_.value} | [EString(_, q), e2]} {safeBare(q) | Some(s)} =>
552
- Some(s + " -= " + self.emitTerm(e2, async))
553
- | _ => None
554
- }
555
- | EAssign(at, operator, name, value), _ =>
556
- Some(escapeKeyword(name) + " " + operator + "= " + self.emitTerm(value, async))
557
- | EAssignField(at, operator, record, field, value), _ =>
558
- Some(
559
- self.emitTerm(record, async) + "." + escapeKeyword(field) + " " + operator + "= " +
925
+ ignored: Bool
926
+ ): Bool {
927
+ let anchor = self.writeAnchor()
928
+ let emitted = term.{
929
+ | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries) =>
930
+ name.{
931
+ | "ff:core/JsValue.JsValue_set" {arguments.map {_.value} | [e1, e2, e3]} =>
932
+ self.emitTerm(e1, async)
933
+ self.emitField(e2, async)
934
+ self.writeMapped(term.at, " = ")
935
+ self.emitTerm(e3, async)
936
+ True
937
+ | "ff:core/JsValue.JsValue_increment" {arguments.map {_.value} | [e1, e2, e3]} =>
938
+ self.emitTerm(e1, async)
939
+ self.emitField(e2, async)
940
+ self.writeMapped(term.at, " += ")
941
+ self.emitTerm(e3, async)
942
+ True
943
+ | "ff:core/JsValue.JsValue_decrement" {arguments.map {_.value} | [e1, e2, e3]} =>
944
+ self.emitTerm(e1, async)
945
+ self.emitField(e2, async)
946
+ self.writeMapped(term.at, " -= ")
947
+ self.emitTerm(e3, async)
948
+ True
949
+ | "ff:core/JsSystem.JsSystem_set" {arguments.map {_.value} | [e1, EString(at, q), e3]} {
950
+ noSideEffects(e1)} {safeBare(q) | Some(s)
951
+ } =>
952
+ self.writeNamed(s, at, s)
953
+ self.writeMapped(term.at, " = ")
954
+ self.emitTerm(e3, async)
955
+ True
956
+ | "ff:core/JsSystem.JsSystem_increment" {arguments.map {_.value} | [e1, EString(at, q), e3]} {
957
+ noSideEffects(e1)} {safeBare(q) | Some(s)
958
+ } =>
959
+ self.writeNamed(s, at, s)
960
+ self.writeMapped(term.at, " += ")
961
+ self.emitTerm(e3, async)
962
+ True
963
+ | "ff:core/JsSystem.JsSystem_decrement" {arguments.map {_.value} | [e1, EString(at, q), e3]} {
964
+ noSideEffects(e1)} {safeBare(q) | Some(s)
965
+ } =>
966
+ self.writeNamed(s, at, s)
967
+ self.writeMapped(term.at, " -= ")
968
+ self.emitTerm(e3, async)
969
+ True
970
+ | "ff:core/Js.set" {arguments.map {_.value} | [EString(at, q), e2]} {safeBare(q) | Some(s)} =>
971
+ self.writeNamed(s, at, s)
972
+ self.writeMapped(term.at, " = ")
973
+ self.emitTerm(e2, async)
974
+ True
975
+ | "ff:core/Js.increment" {arguments.map {_.value} | [EString(at, q), e2]} {safeBare(q) | Some(s)} =>
976
+ self.writeNamed(s, at, s)
977
+ self.writeMapped(term.at, " += ")
978
+ self.emitTerm(e2, async)
979
+ True
980
+ | "ff:core/Js.decrement" {arguments.map {_.value} | [EString(at, q), e2]} {safeBare(q) | Some(s)} =>
981
+ self.writeNamed(s, at, s)
982
+ self.writeMapped(term.at, " -= ")
983
+ self.emitTerm(e2, async)
984
+ True
985
+ | _ =>
986
+ False
987
+ }
988
+ | EAssign(at, operator, name, value) =>
989
+ self.writeNamed(name, at, escapeKeyword(name))
990
+ self.writeMapped(term.at, " " + operator + "= ")
560
991
  self.emitTerm(value, async)
561
- )
562
- | _, _ =>
563
- None
992
+ True
993
+ | EAssignField(at, operator, record, field, value) =>
994
+ self.emitTerm(record, async)
995
+ self.writeMapped(term.at, ".")
996
+ self.writeNamed(field, at, escapeKeyword(field))
997
+ self.writeMapped(term.at, " " + operator + "= ")
998
+ self.emitTerm(value, async)
999
+ True
1000
+ | _ =>
1001
+ False
1002
+ }
1003
+ if(!ignored && emitted) {
1004
+ self.writeAnchorLines(anchor, ["("])
1005
+ self.writeMapped(term.at, ", void 0)")
1006
+ }
1007
+ emitted
564
1008
  }
565
1009
 
566
1010
  emitSpecialCall(
@@ -569,98 +1013,174 @@ extend self: JsEmitter {
569
1013
  name: String
570
1014
  arguments: List[Term]
571
1015
  dictionaries: List[Dictionary]
572
- ): Option[String] {
1016
+ ): Bool {
573
1017
  name.{
574
1018
  | operator {!operator.grabFirst().isAsciiLetter()} {arguments | [value]} =>
575
- Some("(" + operator + self.emitTerm(value, async) + ")")
1019
+ self.writeMapped(term.at, "(")
1020
+ self.writeMapped(term.at, operator)
1021
+ self.emitTerm(value, async)
1022
+ self.writeMapped(term.at, ")")
1023
+ True
576
1024
  | operator {!operator.grabFirst().isAsciiLetter()} {arguments | [left, right]} =>
577
- Some("(" + self.emitTerm(left, async) + " " + operator + " " + self.emitTerm(right, async) + ")")
1025
+ self.writeMapped(term.at, "(")
1026
+ self.emitTerm(left, async)
1027
+ self.writeMapped(term.at, " ")
1028
+ self.writeMapped(term.at, operator)
1029
+ self.writeMapped(term.at, " ")
1030
+ self.emitTerm(right, async)
1031
+ self.writeMapped(term.at, ")")
1032
+ True
578
1033
  | "ff:core/List.List_grab" {arguments | [e1, e2]} {noSideEffects(e1) && noSideEffects(e2)} =>
579
- let code1 = self.emitTerm(e1, async)
580
- let code2 = self.emitTerm(e2, async)
581
- Some(
582
- "(" + code1 + "[" + code2 + "] ?? " +
583
- "ff_core_List.List_grab(" + code1 + ", " + code2 + "))"
584
- )
1034
+ self.writeMapped(term.at, "(")
1035
+ self.emitTerm(e1, async)
1036
+ self.writeMapped(term.at, "[")
1037
+ self.emitTerm(e2, async)
1038
+ self.writeMapped(term.at, "] ?? ff_core_List.List_grab(")
1039
+ self.emitTerm(e1, async)
1040
+ self.writeMapped(term.at, ", ")
1041
+ self.emitTerm(e2, async)
1042
+ self.writeMapped(term.at, "))")
1043
+ True
585
1044
  | "ff:core/Array.Array_grab" {arguments | [e1, e2]} {noSideEffects(e1) && noSideEffects(e2)} =>
586
- let code1 = self.emitTerm(e1, async)
587
- let code2 = self.emitTerm(e2, async)
588
- Some(
589
- "(" + code1 + ".array[" + code2 + "] ?? " +
590
- "ff_core_Array.Array_grab(" + code1 + ", " + code2 + "))"
591
- )
1045
+ self.writeMapped(term.at, "(")
1046
+ self.emitTerm(e1, async)
1047
+ self.writeMapped(term.at, ".array[")
1048
+ self.emitTerm(e2, async)
1049
+ self.writeMapped(term.at, "] ?? ff_core_Array.Array_grab(")
1050
+ self.emitTerm(e1, async)
1051
+ self.writeMapped(term.at, ", ")
1052
+ self.emitTerm(e2, async)
1053
+ self.writeMapped(term.at, "))")
1054
+ True
592
1055
  | "ff:core/List.List_size" {arguments | [e]} =>
593
- Some(
594
- self.emitTerm(e, async) + ".length"
595
- )
1056
+ self.emitTerm(e, async)
1057
+ self.writeMapped(term.at, ".length")
1058
+ True
596
1059
  | "ff:core/Array.Array_size" {arguments | [e]} =>
597
- Some(
598
- self.emitTerm(e, async) + ".array.length"
599
- )
1060
+ self.emitTerm(e, async)
1061
+ self.writeMapped(term.at, ".array.length")
1062
+ True
600
1063
  | "ff:core/String.String_size" {arguments | [e]} =>
601
- Some(
602
- self.emitTerm(e, async) + ".length"
603
- )
1064
+ self.emitTerm(e, async)
1065
+ self.writeMapped(term.at, ".length")
1066
+ True
604
1067
  | "ff:core/Equal.equals" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
605
1068
  primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
606
1069
  } =>
607
- Some("(" + self.emitTerm(left, async) + " === " + self.emitTerm(right, async) + ")")
1070
+ self.writeMapped(term.at, "(")
1071
+ self.emitTerm(left, async)
1072
+ self.writeMapped(term.at, " === ")
1073
+ self.emitTerm(right, async)
1074
+ self.writeMapped(term.at, ")")
1075
+ True
608
1076
  | "ff:core/Equal.notEquals" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
609
1077
  primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
610
1078
  } =>
611
- Some("(" + self.emitTerm(left, async) + " !== " + self.emitTerm(right, async) + ")")
1079
+ self.writeMapped(term.at, "(")
1080
+ self.emitTerm(left, async)
1081
+ self.writeMapped(term.at, " !== ")
1082
+ self.emitTerm(right, async)
1083
+ self.writeMapped(term.at, ")")
1084
+ True
612
1085
  | "ff:core/Ordering.before" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
613
1086
  primitiveTypes.contains(typeName)
614
1087
  } =>
615
- Some("(" + self.emitTerm(left, async) + " < " + self.emitTerm(right, async) + ")")
1088
+ self.writeMapped(term.at, "(")
1089
+ self.emitTerm(left, async)
1090
+ self.writeMapped(term.at, " < ")
1091
+ self.emitTerm(right, async)
1092
+ self.writeMapped(term.at, ")")
1093
+ True
616
1094
  | "ff:core/Ordering.notBefore" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
617
1095
  primitiveTypes.contains(typeName)
618
1096
  } =>
619
- Some("(" + self.emitTerm(left, async) + " >= " + self.emitTerm(right, async) + ")")
1097
+ self.writeMapped(term.at, "(")
1098
+ self.emitTerm(left, async)
1099
+ self.writeMapped(term.at, " >= ")
1100
+ self.emitTerm(right, async)
1101
+ self.writeMapped(term.at, ")")
1102
+ True
620
1103
  | "ff:core/Ordering.after" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
621
1104
  primitiveTypes.contains(typeName)
622
1105
  } =>
623
- Some("(" + self.emitTerm(left, async) + " > " + self.emitTerm(right, async) + ")")
1106
+ self.writeMapped(term.at, "(")
1107
+ self.emitTerm(left, async)
1108
+ self.writeMapped(term.at, " > ")
1109
+ self.emitTerm(right, async)
1110
+ self.writeMapped(term.at, ")")
1111
+ True
624
1112
  | "ff:core/Ordering.notAfter" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, typeName, [])]} {
625
1113
  primitiveTypes.contains(typeName)
626
1114
  } =>
627
- Some("(" + self.emitTerm(left, async) + " <= " + self.emitTerm(right, async) + ")")
1115
+ self.writeMapped(term.at, "(")
1116
+ self.emitTerm(left, async)
1117
+ self.writeMapped(term.at, " <= ")
1118
+ self.emitTerm(right, async)
1119
+ self.writeMapped(term.at, ")")
1120
+ True
628
1121
  | "ff:core/List.fillBy" {term | ECall call} {arguments | [size, ELambda(at,
629
- Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)@c])@l
1122
+ Lambda(_, _, [MatchCase(_, [PVariable(variableAt, name)], [], body)@c])@l
630
1123
  )]} {
631
1124
  !effectTypeIsAsync(call.effect)
632
1125
  } =>
633
1126
  let n = name.map {escapeResolved(_)}.else {"i"}
634
1127
  let newAsync = self.emittingAsync && effectTypeIsAsync(call.effect)
635
- let await = if(newAsync) {"await "} else {""}
636
- Some(
637
- await + "((() => {\n" +
638
- "const size = " + self.emitTerm(size, async) + ";\n" + // Not correct if async and body isn't
639
- "const result = [];\n" +
640
- "for(let " + n + " = 0; " + n + " < size; " + n + "++) {\n" +
641
- "result.push(" + self.emitTerm(body, newAsync) + ");\n" +
642
- "}\n" +
643
- "return result;\n" +
644
- "})())"
645
- )
646
- | "ff:core/Js.import" {arguments | [EString(_, url)]} =>
1128
+ if(newAsync) {self.writeMapped(term.at, "await ")}
1129
+ self.writeMapped(term.at, "((() => {")
1130
+ self.writeLine()
1131
+ self.writeMapped(term.at, "const size = ")
1132
+ self.emitTerm(size, async) // Not correct if async and body isn't
1133
+ self.writeMapped(term.at, ";")
1134
+ self.writeLine()
1135
+ self.writeMapped(term.at, "const result = [];")
1136
+ self.writeMapped(term.at, "for(let ")
1137
+ self.writeNamed(name.else {"_"}, variableAt, n)
1138
+ self.writeMapped(term.at, " = 0; ")
1139
+ self.writeNamed(name.else {"_"}, variableAt, n)
1140
+ self.writeMapped(term.at, " < size; ")
1141
+ self.writeNamed(name.else {"_"}, variableAt, n)
1142
+ self.writeMapped(term.at, "++) {")
1143
+ self.writeLine()
1144
+ self.writeMapped(term.at, "result.push(")
1145
+ self.emitTerm(body, newAsync)
1146
+ self.writeMapped(term.at, ");")
1147
+ self.writeLine()
1148
+ self.writeMapped(term.at, "}")
1149
+ self.writeLine()
1150
+ self.writeMapped(term.at, "return result;")
1151
+ self.writeLine()
1152
+ self.writeMapped(term.at, "})())")
1153
+ True
1154
+ | "ff:core/Js.import" {arguments | [EString(at, url)]} =>
647
1155
  self.emitTarget.{
648
- | EmitBrowser => Some("(() => {throw new Error('Node.js imports are not supported in the browser')})()")
649
- | _ => Some(self.jsImporter.add(url.replace("\"", "")))
1156
+ | EmitBrowser =>
1157
+ self.writeMapped(at, "(() => {throw new Error('Node.js imports are not supported in the browser')})()")
1158
+ | _ =>
1159
+ self.writeMapped(at, self.jsImporter.add(url.replace("\"", "")))
650
1160
  }
651
- | "ff:core/Js.browserImport" {arguments | [EString(_, url)]} =>
1161
+ True
1162
+ | "ff:core/Js.browserImport" {arguments | [EString(at, url)]} =>
652
1163
  self.emitTarget.{
653
- | EmitBrowser => Some(self.jsImporter.add(url.replace("\"", "")))
654
- | _ => Some("(() => {throw new Error('Browser imports are not supported in Node.js')})()")
1164
+ | EmitBrowser =>
1165
+ self.writeMapped(at, self.jsImporter.add(url.replace("\"", "")))
1166
+ | _ =>
1167
+ self.writeMapped(at, "(() => {throw new Error('Browser imports are not supported in Node.js')})()")
655
1168
  }
1169
+ True
656
1170
  | "ff:core/Js.dynamicImport" {arguments | [url]} =>
657
- Some("import(" + self.emitTerm(url, async) + ")")
1171
+ self.writeMapped(term.at, "import(")
1172
+ self.emitTerm(url, async)
1173
+ self.writeMapped(term.at, ")")
1174
+ True
658
1175
  | "ff:core/Js.await" {arguments | [body]} =>
659
1176
  if(async) {
660
- Some("(await " + self.emitTerm(body, async) + ")")
1177
+ self.writeMapped(term.at, "(await ")
1178
+ self.emitTerm(body, async)
1179
+ self.writeMapped(term.at, ")")
661
1180
  } else {
662
- Some(self.emitTerm(body, async))
1181
+ self.emitTerm(body, async)
663
1182
  }
1183
+ True
664
1184
  | name {name.removeFirst("ff:core/Js.async") | Some(n)} {n.all {_.isAsciiDigit()}} {
665
1185
  arguments | [ELambda(at, Lambda(_, effect, [MatchCase(_, patterns, [], body)]))]
666
1186
  } {
@@ -670,78 +1190,172 @@ extend self: JsEmitter {
670
1190
  | PVariable p => p.name.map(escapeKeyword).else {"_"}
671
1191
  | _ => panic("!")
672
1192
  }
673
- Some(
674
- "async (" + patternParameters.join(", ") + ") => {\n" +
675
- self.emitStatements(body, True, False, False) +
676
- "\n}"
677
- )
1193
+ self.writeMapped(term.at, "async (" + patternParameters.join(", ") + ") => {")
1194
+ self.writeLine()
1195
+ self.emitStatements(body, True, False, False)
1196
+ self.writeLine()
1197
+ self.writeMapped(term.at, "}")
1198
+ True
678
1199
  | name {name.startsWith("ff:core/Js.async")} =>
679
1200
  throw(CompileError(term.at, "JS async functions must take a simple parameter list"))
680
1201
  | "ff:core/Js.cancelled" =>
681
- Some(if(async) {"$task.controller_.signal.aborted"} else {"false"})
1202
+ self.writeMapped(term.at, if(async) {"$task.controller_.signal.aborted"} else {"false"})
1203
+ True
682
1204
  | "ff:core/Js.throwIfCancelled" =>
683
- Some(if(async) {"((() => ff_core_Task.Task_throwIfAborted($task))())"} else {""})
1205
+ if(async) {self.writeMapped(term.at, "((() => ff_core_Task.Task_throwIfAborted($task))())")}
1206
+ True
684
1207
  | "ff:core/Js.currentTask" =>
685
- Some("$task")
1208
+ self.writeMapped(term.at, "$task")
1209
+ True
686
1210
  | "ff:core/Js.controller" =>
687
- Some("$task.controller_")
1211
+ self.writeMapped(term.at, "$task.controller_")
1212
+ True
688
1213
  | "ff:core/Js.setController" {arguments | [a]} =>
689
- Some("($task.controller_ = " + self.emitTerm(a, async) + ")")
1214
+ self.writeMapped(term.at, "($task.controller_ = ")
1215
+ self.emitTerm(a, async)
1216
+ self.writeMapped(term.at, ")")
1217
+ True
690
1218
  | "ff:core/Js.inAsync" =>
691
- Some(if(self.emittingAsync) {"true"} else {"false"})
1219
+ self.writeMapped(term.at, if(self.emittingAsync) {"true"} else {"false"})
1220
+ True
692
1221
  | "ff:core/Js.inBrowser" =>
693
- Some(if(self.emitTarget == EmitBrowser) {"true"} else {"false"})
1222
+ self.writeMapped(term.at, if(self.emitTarget == EmitBrowser) {"true"} else {"false"})
1223
+ True
694
1224
  | "ff:core/Js.inNode" =>
695
- Some(if(self.emitTarget == EmitNode) {"true"} else {"false"})
1225
+ self.writeMapped(term.at, if(self.emitTarget == EmitNode) {"true"} else {"false"})
1226
+ True
696
1227
  | "ff:core/Js.inBuild" =>
697
- Some(if(self.emitTarget == EmitBuild) {"true"} else {"false"})
1228
+ self.writeMapped(term.at, if(self.emitTarget == EmitBuild) {"true"} else {"false"})
1229
+ True
698
1230
  | "ff:core/Js.value" {arguments | [e]} =>
699
- Some(self.emitTerm(e, async))
1231
+ self.emitTerm(e, async)
1232
+ True
700
1233
  | "ff:core/Js.fromValue" {arguments | [e]} =>
701
- Some(self.emitTerm(e, async))
702
- | "ff:core/Js.rawIdentifier" {arguments | [EString(_, op)]} =>
703
- Some(op.replace("\"", ""))
704
- | "ff:core/Js.unaryOperator" {arguments | [EString(_, op), a1]} =>
705
- Some("(" + op.replace("\"", "") + self.emitTerm(a1, async) + ")")
706
- | "ff:core/Js.binaryOperator" {arguments | [EString(_, op), a1, a2]} =>
707
- Some("(" + self.emitTerm(a1, async) + " " + op.replace("\"", "") + " " + self.emitTerm(a2, async) + ")")
708
- | "ff:core/Js.shortCircuitingOperator" {arguments | [EString(_, op), a1, a2]} =>
709
- Some("(" + self.emitTerm(a1, async) + " " + op.replace("\"", "") + " " + self.emitTerm(invokeImmediately(a2), async) + ")")
1234
+ self.emitTerm(e, async)
1235
+ True
1236
+ | "ff:core/Js.rawIdentifier" {arguments | [EString(at, op)]} =>
1237
+ self.writeMapped(at, op.replace("\"", ""))
1238
+ True
1239
+ | "ff:core/Js.unaryOperator" {arguments | [EString(at, op), a1]} =>
1240
+ self.writeMapped(term.at, "(")
1241
+ self.writeMapped(at, op.replace("\"", ""))
1242
+ self.emitTerm(a1, async)
1243
+ self.writeMapped(term.at, ")")
1244
+ True
1245
+ | "ff:core/Js.binaryOperator" {arguments | [EString(at, op), a1, a2]} =>
1246
+ self.writeMapped(term.at, "(")
1247
+ self.emitTerm(a1, async)
1248
+ self.writeMapped(term.at, " ")
1249
+ self.writeMapped(at, op.replace("\"", ""))
1250
+ self.writeMapped(term.at, " ")
1251
+ self.emitTerm(a2, async)
1252
+ self.writeMapped(term.at, ")")
1253
+ True
1254
+ | "ff:core/Js.shortCircuitingOperator" {arguments | [EString(at, op), a1, a2]} =>
1255
+ self.writeMapped(term.at, "(")
1256
+ self.emitTerm(a1, async)
1257
+ self.writeMapped(term.at, " ")
1258
+ self.writeMapped(at, op.replace("\"", ""))
1259
+ self.writeMapped(term.at, " ")
1260
+ self.emitTerm(invokeImmediately(a2), async)
1261
+ self.writeMapped(term.at, ")")
1262
+ True
710
1263
  | "ff:core/JsValue.JsValue_spreadToArray" {arguments | [e1]} =>
711
- Some("[..." + self.emitTerm(e1, async) + "]")
1264
+ self.writeMapped(term.at, "[...")
1265
+ self.emitTerm(e1, async)
1266
+ self.writeMapped(term.at, "]")
1267
+ True
712
1268
  | "ff:core/JsValue.JsValue_typeof" {arguments | [e]} =>
713
- Some("(typeof " + self.emitTerm(e, async) + ")")
1269
+ self.writeMapped(term.at, "(typeof ")
1270
+ self.emitTerm(e, async)
1271
+ self.writeMapped(term.at, ")")
1272
+ True
714
1273
  | "ff:core/JsValue.JsValue_instanceof" {arguments | [e1, e2]} =>
715
- Some("(" + self.emitTerm(e1, async) + " instanceof " + self.emitTerm(e2, async) + ")")
1274
+ self.writeMapped(term.at, "(")
1275
+ self.emitTerm(e1, async)
1276
+ self.writeMapped(term.at, " instanceof ")
1277
+ self.emitTerm(e2, async)
1278
+ self.writeMapped(term.at, ")")
1279
+ True
716
1280
  | "ff:core/JsValue.JsValue_get" {arguments | [e1, e2]} =>
717
- Some(self.emitTerm(e1, async) + self.emitField(e2, async))
1281
+ self.emitTerm(e1, async)
1282
+ self.emitField(e2, async)
1283
+ True
718
1284
  | "ff:core/JsValue.JsValue_equals" {arguments | [e1, e2]} =>
719
- Some("(" + self.emitTerm(e1, async) + " === " + self.emitTerm(e2, async) + ")")
1285
+ self.writeMapped(term.at, "(")
1286
+ self.emitTerm(e1, async)
1287
+ self.writeMapped(term.at, " === ")
1288
+ self.emitTerm(e2, async)
1289
+ self.writeMapped(term.at, ")")
1290
+ True
720
1291
  | "ff:core/JsValue.JsValue_notEquals" {arguments | [e1, e2]} =>
721
- Some("(" + self.emitTerm(e1, async) + " !== " + self.emitTerm(e2, async) + ")")
1292
+ self.writeMapped(term.at, "(")
1293
+ self.emitTerm(e1, async)
1294
+ self.writeMapped(term.at, " !== ")
1295
+ self.emitTerm(e2, async)
1296
+ self.writeMapped(term.at, ")")
1297
+ True
722
1298
  | "ff:core/Int.Int_bitAnd" {arguments | [e1, e2]} =>
723
- Some("(" + self.emitTerm(e1, async) + " & " + self.emitTerm(e2, async) + ")")
1299
+ self.writeMapped(term.at, "(")
1300
+ self.emitTerm(e1, async)
1301
+ self.writeMapped(term.at, " & ")
1302
+ self.emitTerm(e2, async)
1303
+ self.writeMapped(term.at, ")")
1304
+ True
724
1305
  | "ff:core/Int.Int_bitRightUnsigned" {arguments | [e1, e2]} =>
725
- Some("(" + self.emitTerm(e1, async) + " >>> " + self.emitTerm(e2, async) + ")")
1306
+ self.writeMapped(term.at, "(")
1307
+ self.emitTerm(e1, async)
1308
+ self.writeMapped(term.at, " >>> ")
1309
+ self.emitTerm(e2, async)
1310
+ self.writeMapped(term.at, ")")
1311
+ True
726
1312
  | "ff:core/Int.Int_bitRight" {arguments | [e1, e2]} =>
727
- Some("(" + self.emitTerm(e1, async) + " >> " + self.emitTerm(e2, async) + ")")
1313
+ self.writeMapped(term.at, "(")
1314
+ self.emitTerm(e1, async)
1315
+ self.writeMapped(term.at, " >> ")
1316
+ self.emitTerm(e2, async)
1317
+ self.writeMapped(term.at, ")")
1318
+ True
728
1319
  | name {name.removeFirst("ff:core/JsValue.JsValue_call") | Some(n)} {n.all {_.isAsciiDigit()}} {
729
1320
  arguments | [e1, e2, ...es]
730
1321
  } =>
731
- let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
732
- Some(self.emitTerm(e1, async) + self.emitField(e2, async) + "(" + argumentCode + ")")
1322
+ self.emitTerm(e1, async)
1323
+ self.emitField(e2, async)
1324
+ self.writeMapped(term.at, "(")
1325
+ let comma = Comma(self)
1326
+ es.each {
1327
+ comma.writeComma()
1328
+ self.emitTerm(_, async)
1329
+ }
1330
+ self.writeMapped(term.at, ")")
1331
+ True
733
1332
  | name {name.removeFirst("ff:core/JsValue.JsValue_callValue") | Some(n)} {n.all {_.isAsciiDigit()}} {
734
1333
  arguments | [e1, ...es]
735
1334
  } =>
736
- let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
737
- Some(self.emitTerm(e1, async) + "(" + argumentCode + ")")
1335
+ self.emitTerm(e1, async)
1336
+ self.writeMapped(term.at, "(")
1337
+ let comma = Comma(self)
1338
+ es.each {
1339
+ comma.writeComma()
1340
+ self.emitTerm(_, async)
1341
+ }
1342
+ self.writeMapped(term.at, ")")
1343
+ True
738
1344
  | name {name.removeFirst("ff:core/JsValue.JsValue_new") | Some(n)} {n.all {_.isAsciiDigit()}} {
739
1345
  arguments | [e1, ...es]
740
1346
  } =>
741
- let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
742
- Some("(new " + self.emitTerm(e1, async) + "(" + argumentCode + ")" + ")")
1347
+ self.writeMapped(term.at, "(new ")
1348
+ self.emitTerm(e1, async)
1349
+ self.writeMapped(term.at, "(")
1350
+ let comma = Comma(self)
1351
+ es.each {
1352
+ comma.writeComma()
1353
+ self.emitTerm(_, async)
1354
+ }
1355
+ self.writeMapped(term.at, "))")
1356
+ True
743
1357
  | name {name == "ff:core/JsValue.JsValue_with" || name == "ff:core/Json.Json_with"} =>
744
- function go(e: Term, fields: List[Pair[Term, Term]]): String {
1358
+ function go(e: Term, fields: List[Pair[Term, Term]]) {
745
1359
  e.{
746
1360
  | ECall(_, StaticCall(n, _, _), _, _, [a1, a2, a3], _) {n == name} =>
747
1361
  go(a1.value, [Pair(a2.value, a3.value), ...fields])
@@ -755,86 +1369,163 @@ extend self: JsEmitter {
755
1369
  } {
756
1370
  as.all {noSideEffects(_.value)}
757
1371
  } =>
758
- "{" + fields.map {p =>
759
- self.emitField(p.first, async, dot = "") + ": " + self.emitTerm(p.second, async)
760
- }.join(", ") + "}"
1372
+ self.writeMapped(e.at, "{")
1373
+ let comma = Comma(self)
1374
+ fields.each {p =>
1375
+ comma.writeComma()
1376
+ self.emitField(p.first, async, dot = "")
1377
+ self.writeMapped(e.at, ": ")
1378
+ self.emitTerm(p.second, async)
1379
+ }
1380
+ self.writeMapped(e.at, "}")
761
1381
  | _ =>
762
- "{..." + self.emitTerm(e, async) + ", " + fields.map {p =>
763
- self.emitField(p.first, async, dot = "") + ": " + self.emitTerm(p.second, async)
764
- }.join(", ") + "}"
1382
+ self.writeMapped(e.at, "{...")
1383
+ self.emitTerm(e, async)
1384
+ fields.each {p =>
1385
+ self.writeMapped(e.at, ", ")
1386
+ self.emitField(p.first, async, dot = "")
1387
+ self.writeMapped(e.at, ": ")
1388
+ self.emitTerm(p.second, async)
1389
+ }
1390
+ self.writeMapped(e.at, "}")
765
1391
  }
766
1392
  }
767
- Some(go(term, []))
1393
+ go(term, [])
1394
+ True
768
1395
  | name {name.removeFirst("ff:core/JsSystem.JsSystem_call") | Some(n)} {n.all {_.isAsciiDigit()}} {
769
- arguments | [e1, EString(_, q)@e2, ...es]
1396
+ arguments | [e1, EString(at, q)@e2, ...es]
770
1397
  } {noSideEffects(e1)} =>
771
- let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
772
- Some(safeBare(q).else {"globalThis[" + self.emitTerm(e2, async) + "]"} + "(" + argumentCode + ")")
1398
+ safeBare(q).{
1399
+ | Some(bare) => self.writeNamed(q, at, bare)
1400
+ | None =>
1401
+ self.writeMapped(term.at, "globalThis[")
1402
+ self.emitTerm(e2, async)
1403
+ self.writeMapped(term.at, "]")
1404
+ }
1405
+ self.writeMapped(term.at, "(")
1406
+ let comma = Comma(self)
1407
+ es.each {
1408
+ comma.writeComma()
1409
+ self.emitTerm(_, async)
1410
+ }
1411
+ self.writeMapped(term.at, ")")
1412
+ True
773
1413
  | name {name.removeFirst("ff:core/JsSystem.JsSystem_function") | Some(n)} {n.all {_.isAsciiDigit()}} {
774
1414
  arguments | [e1, e2]
775
1415
  } {noSideEffects(e1)} {term | ECall call} {!effectTypeIsAsync(call.effect)} =>
776
- Some(self.emitTerm(e2, async))
777
- | "ff:core/JsSystem.JsSystem_get" {arguments | [e1, EString(_, q)@e2]} {noSideEffects(e1)} =>
778
- Some(safeBare(q).else {"globalThis[" + self.emitTerm(e2, async) + "]"})
1416
+ self.emitTerm(e2, async)
1417
+ True
1418
+ | "ff:core/JsSystem.JsSystem_get" {arguments | [e1, EString(at, q)@e2]} {noSideEffects(e1)} =>
1419
+ safeBare(q).{
1420
+ | Some(bare) => self.writeNamed(q, at, bare)
1421
+ | None =>
1422
+ self.writeMapped(term.at, "globalThis[")
1423
+ self.emitTerm(e2, async)
1424
+ self.writeMapped(term.at, "]")
1425
+ }
1426
+ True
779
1427
  | "ff:core/JsSystem.JsSystem_object" {arguments | [e]} {noSideEffects(e)} =>
780
- Some("{}")
1428
+ self.writeMapped(term.at, "{}")
1429
+ True
781
1430
  | "ff:core/JsSystem.JsSystem_new0" {arguments | [e]} {noSideEffects(e)} =>
782
- Some("{}")
1431
+ self.writeMapped(term.at, "{}")
1432
+ True
783
1433
  | "ff:core/JsSystem.JsSystem_null" {arguments | [e]} {noSideEffects(e)} =>
784
- Some("null")
1434
+ self.writeMapped(term.at, "null")
1435
+ True
785
1436
  | "ff:core/JsSystem.JsSystem_undefined" {arguments | [e]} {noSideEffects(e)} =>
786
- Some("(void 0)")
1437
+ self.writeMapped(term.at, "(void 0)")
1438
+ True
787
1439
  | name {name.removeFirst("ff:core/Js.call") | Some(n)} {n.all {_.isAsciiDigit()}} {
788
- arguments | [EString(_, q)@e1, ...es]
1440
+ arguments | [EString(at, q)@e1, ...es]
789
1441
  } =>
790
- let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
791
- Some(safeBare(q).else {"globalThis[" + self.emitTerm(e1, async) + "]"} + "(" + argumentCode + ")")
1442
+ safeBare(q).{
1443
+ | Some(bare) => self.writeNamed(q, at, bare)
1444
+ | None =>
1445
+ self.writeMapped(term.at, "globalThis[")
1446
+ self.emitTerm(e1, async)
1447
+ self.writeMapped(term.at, "]")
1448
+ }
1449
+ self.writeMapped(term.at, "(")
1450
+ let comma = Comma(self)
1451
+ es.each {
1452
+ comma.writeComma()
1453
+ self.emitTerm(_, async)
1454
+ }
1455
+ self.writeMapped(term.at, ")")
1456
+ True
792
1457
  | name {name.removeFirst("ff:core/Js.function") | Some(n)} {n.all {_.isAsciiDigit()}} {
793
1458
  arguments | [e1]
794
1459
  } {term | ECall call} =>
795
1460
  if(self.emittingAsync && effectTypeIsAsync(call.effect)) {
796
1461
  let argumentCode = 1.to(n.grabInt()).map {"a_" + _}.join(", ")
797
1462
  let taskCode = if(argumentCode == "") {"$task"} else {", $task"}
798
- Some("(async (" + argumentCode + ") => await " + self.emitTerm(e1, async) + "(" + argumentCode + taskCode + "))")
1463
+ self.writeMapped(term.at, "(async (" + argumentCode + ") => await ")
1464
+ self.emitTerm(e1, async)
1465
+ self.writeMapped(term.at, "(" + argumentCode + taskCode + "))")
799
1466
  } else {
800
- Some(self.emitTerm(e1, async))
1467
+ self.emitTerm(e1, async)
1468
+ }
1469
+ True
1470
+ | "ff:core/Js.get" {arguments | [EString(at, q)@e1]} =>
1471
+ safeBare(q).{
1472
+ | Some(bare) => self.writeNamed(q, at, bare)
1473
+ | None =>
1474
+ self.writeMapped(term.at, "globalThis[")
1475
+ self.emitTerm(e1, async)
1476
+ self.writeMapped(term.at, "]")
801
1477
  }
802
- | "ff:core/Js.get" {arguments | [EString(_, q)@e1]} =>
803
- Some(safeBare(q).else {"globalThis[" + self.emitTerm(e1, async) + "]"})
1478
+ True
804
1479
  | "ff:core/Js.object" =>
805
- Some("{}")
1480
+ self.writeMapped(term.at, "{}")
1481
+ True
806
1482
  | "ff:core/Js.new0" =>
807
- Some("{}")
1483
+ self.writeMapped(term.at, "{}")
1484
+ True
808
1485
  | "ff:core/Js.null" =>
809
- Some("null")
1486
+ self.writeMapped(term.at, "null")
1487
+ True
810
1488
  | "ff:core/Js.undefined" =>
811
- Some("(void 0)")
1489
+ self.writeMapped(term.at, "(void 0)")
1490
+ True
812
1491
  | "ff:core/Js.globalThis" =>
813
- Some("globalThis")
1492
+ self.writeMapped(term.at, "globalThis")
1493
+ True
814
1494
  | "ff:core/BrowserSystem.BrowserSystem_js" {arguments | [e]} {noSideEffects(e)} =>
815
- Some("globalThis")
1495
+ self.writeMapped(term.at, "globalThis")
1496
+ True
816
1497
  | "ff:core/BuildSystem.BuildSystem_js" {arguments | [e]} {noSideEffects(e)} =>
817
- Some("globalThis")
1498
+ self.writeMapped(term.at, "globalThis")
1499
+ True
818
1500
  | "ff:core/NodeSystem.NodeSystem_js" {arguments | [e]} {noSideEffects(e)} =>
819
- Some("globalThis")
1501
+ self.writeMapped(term.at, "globalThis")
1502
+ True
820
1503
  | "ff:core/Js.jsSystem" =>
821
- Some("globalThis")
1504
+ self.writeMapped(term.at, "globalThis")
1505
+ True
822
1506
  | "ff:core/Json.string" {arguments | [e]} =>
823
- Some(self.emitTerm(e, async))
1507
+ self.emitTerm(e, async)
1508
+ True
824
1509
  | "ff:core/Json.int" {arguments | [e]} =>
825
- Some(self.emitTerm(e, async))
1510
+ self.emitTerm(e, async)
1511
+ True
826
1512
  | "ff:core/Json.float" {arguments | [e]} =>
827
- Some(self.emitTerm(e, async))
1513
+ self.emitTerm(e, async)
1514
+ True
828
1515
  | "ff:core/Json.bool" {arguments | [e]} =>
829
- Some(self.emitTerm(e, async))
1516
+ self.emitTerm(e, async)
1517
+ True
830
1518
  | "ff:core/Json.array" {arguments | [e]} =>
831
- Some(self.emitTerm(e, async))
1519
+ self.emitTerm(e, async)
1520
+ True
832
1521
  | "ff:core/Json.null" {arguments | [e]} =>
833
- Some("null")
1522
+ self.writeMapped(term.at, "null")
1523
+ True
834
1524
  | "ff:core/Json.object" {arguments | [e]} =>
835
- Some("{}")
1525
+ self.writeMapped(term.at, "{}")
1526
+ True
836
1527
  | _ =>
837
- None
1528
+ False
838
1529
  }
839
1530
  }
840
1531
 
@@ -845,202 +1536,294 @@ extend self: JsEmitter {
845
1536
  name: String
846
1537
  arguments: List[Term]
847
1538
  dictionaries: List[Dictionary]
848
- ): Option[String] {
1539
+ ): Bool {
849
1540
  name.{
850
1541
  | "ff:core/Core.while" {arguments | [condition, body]} =>
851
- Some(
852
- "while(" + self.emitTerm(invokeImmediately(condition), async) + ") {\n" +
853
- self.emitStatements(invokeImmediately(body), False, False, async) + "\n}"
854
- )
1542
+ self.writeMapped(term.at, "while")
1543
+ self.writeMapped(term.at, "(")
1544
+ self.emitTerm(invokeImmediately(condition), async)
1545
+ self.writeMapped(term.at, ") {")
1546
+ self.writeLine()
1547
+ self.emitStatements(invokeImmediately(body), False, False, async)
1548
+ self.writeLine()
1549
+ self.writeMapped(term.at, "}")
1550
+ True
855
1551
  | "ff:core/Core.doWhile" {arguments | [doWhileBody]} {
856
1552
  invokeImmediately(doWhileBody) | body
857
1553
  } =>
858
- Some(
859
- "while(true) {\n" +
860
- self.emitStatements(body, False, True, async) +
861
- "\n}"
862
- )
1554
+ self.writeMapped(term.at, "while")
1555
+ self.writeMapped(term.at, "(true) {")
1556
+ self.writeLine()
1557
+ self.emitStatements(body, False, True, async)
1558
+ self.writeLine()
1559
+ self.writeMapped(term.at, "}")
1560
+ True
863
1561
  | "ff:core/Option.Option_each" {arguments | [list, ELambda(_, Lambda(_, _, [
864
- MatchCase(_, [PVariable(_, name)], [], body)
1562
+ MatchCase(_, [PVariable(nameAt, name)], [], body)
865
1563
  ]))]} =>
866
- Some(
867
- "{\nconst if_o = " + self.emitTerm(list, async) + "\nif(if_o.Some) {\n" +
868
- name.map {"const " + escapeKeyword(_) + " = if_o.value_;\n"}.else {""} +
869
- self.emitStatements(body, last, False, async) +
870
- "\n}\n}"
871
- )
1564
+ self.writeMapped(term.at, "{")
1565
+ self.writeLine()
1566
+ self.writeMapped(term.at, "const if_o = ")
1567
+ self.emitTerm(list, async)
1568
+ self.writeLine()
1569
+ self.writeMapped(term.at, "if(if_o.Some) {")
1570
+ self.writeLine()
1571
+ name.each {n =>
1572
+ self.writeMapped(term.at, "const ")
1573
+ self.writeNamed(n, nameAt, escapeKeyword(n))
1574
+ self.writeMapped(term.at, " = if_o.value_;")
1575
+ self.writeLine()
1576
+ }
1577
+ self.emitStatements(body, last, False, async)
1578
+ self.writeLine()
1579
+ self.writeMapped(term.at, "}")
1580
+ self.writeLine()
1581
+ self.writeMapped(term.at, "}")
1582
+ True
872
1583
  | n {n == "ff:core/List.List_each" || n == "ff:core/List.List_eachWhile"} {arguments | [
873
1584
  ECall(_, StaticCall(r, _, _), _, _, [start, end], _)
874
- ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)]))
1585
+ ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(nameAt, name)], [], body)]))
875
1586
  ]} {r == "ff:core/Int.Int_until" || r == "ff:core/Int.Int_to"} =>
876
- let startCode = self.emitTerm(start.value, async)
877
- let endCode = self.emitTerm(end.value, async)
1587
+ self.writeMapped(term.at, "for")
1588
+ self.writeMapped(term.at, "(let for_i = ")
1589
+ self.emitTerm(start.value, async)
1590
+ self.writeMapped(term.at, ", for_e = ")
1591
+ self.emitTerm(end.value, async)
878
1592
  let op = if(r == "ff:core/Int.Int_until") {"<"} else {"<="}
879
- Some(
880
- "for(let " +
881
- "for_i = " + startCode + ", for_e = " + endCode + "; for_i " + op + " for_e; for_i++) {\n" +
882
- name.map {"const " + escapeKeyword(_) + " = for_i;\n"}.else {""} +
883
- self.emitStatements(body, last, n.endsWith("eachWhile"), async) +
884
- "\n}"
885
- )
1593
+ self.writeMapped(term.at, "; for_i " + op + " for_e; for_i++) {")
1594
+ self.writeLine()
1595
+ name.each {n =>
1596
+ self.writeMapped(term.at, "const ")
1597
+ self.writeNamed(n, nameAt, escapeKeyword(n))
1598
+ self.writeMapped(term.at, " = for_i;")
1599
+ self.writeLine()
1600
+ }
1601
+ self.emitStatements(body, last, n.endsWith("eachWhile"), async)
1602
+ self.writeLine()
1603
+ self.writeMapped(term.at, "}")
1604
+ True
886
1605
  | n {n == "ff:core/List.List_each" || n == "ff:core/List.List_eachWhile"} {arguments | [
887
1606
  ECall(_, StaticCall("ff:core/List.List_reverse", _, _), _, _, [
888
1607
  Argument(_, _, ECall(_, StaticCall(r, _, _), _, _, [start, end], _))
889
1608
  ], _)
890
- ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)]))
1609
+ ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(nameAt, name)], [], body)]))
891
1610
  ]} {r == "ff:core/Int.Int_until" || r == "ff:core/Int.Int_to"} =>
892
- let startCode = self.emitTerm(start.value, async)
893
- let endCode = self.emitTerm(end.value, async)
894
- let delta = if(r == "ff:core/Int.Int_until") {" - 1"} else {""}
895
- Some(
896
- "for(let " +
897
- "for_e = " + startCode + ", for_i = " + endCode + delta + "; for_i >= for_e; for_i--) {\n" +
898
- name.map {"const " + escapeKeyword(_) + " = for_i;\n"}.else {""} +
899
- self.emitStatements(body, last, n.endsWith("eachWhile"), async) +
900
- "\n}"
901
- )
1611
+ self.writeMapped(term.at, "for")
1612
+ self.writeMapped(term.at, "(let for_e = ")
1613
+ self.emitTerm(start.value, async)
1614
+ self.writeMapped(term.at, ", for_i = ")
1615
+ self.emitTerm(end.value, async)
1616
+ if(r == "ff:core/Int.Int_until") {self.writeMapped(term.at, " - 1")}
1617
+ self.writeMapped(term.at, "; for_i >= for_e; for_i--) {")
1618
+ self.writeLine()
1619
+ name.each {n =>
1620
+ self.writeMapped(term.at, "const ")
1621
+ self.writeNamed(n, nameAt, escapeKeyword(n))
1622
+ self.writeMapped(term.at, " = for_i;")
1623
+ self.writeLine()
1624
+ }
1625
+ self.emitStatements(body, last, n.endsWith("eachWhile"), async)
1626
+ self.writeLine()
1627
+ self.writeMapped(term.at, "}")
1628
+ True
902
1629
  | n {n == "ff:core/List.List_each" || n == "ff:core/List.List_eachWhile"} {arguments | [
903
1630
  ECall(_, StaticCall("ff:core/List.List_zip", _, _), _, _, [list1, list2], _)
904
1631
  ELambda(_, Lambda(_, _, [MatchCase(_, [
905
- PVariant(_, "ff:core/Pair.Pair", [PVariable(_, name1), PVariable(_, name2)])
1632
+ PVariant(_, "ff:core/Pair.Pair", [PVariable(at1, name1), PVariable(at2, name2)])
906
1633
  ], [], body)]))
907
1634
  ]} =>
908
- let fusion1 = self.emitLightFusion("for_a", list1.value, async)
909
- let fusion2 = self.emitLightFusion("for_a2", list2.value, async)
910
- let start1 = fusion1.second.first
911
- let end1 = fusion1.second.second
912
- let listCode1 = fusion1.first
913
- let start2 = fusion2.second.first
914
- let end2 = fusion2.second.second
915
- let listCode2 = fusion2.first
916
- Some(
917
- "for(let for_a = " + listCode1 + ", for_i = " + start1 + ", for_l = " + end1 + ", " +
918
- "for_a2 = " + listCode2 + ", for_i2 = " + start2 + ", for_l2 = " + end2 +
919
- "; for_i < for_l && for_i2 < for_l2; for_i++, for_i2++) {\n" +
920
- name1.map {"const " + escapeKeyword(_) + " = for_a[for_i];\n"}.else {""} +
921
- name2.map {"const " + escapeKeyword(_) + " = for_a2[for_i2];\n"}.else {""} +
922
- self.emitStatements(body, last, n.endsWith("eachWhile"), async) +
923
- "\n}"
924
- )
1635
+ self.writeMapped(term.at, "for(let ")
1636
+ self.emitLightFusion("", list1.value, n.startsWith("ff:core/Array."), async)
1637
+ self.writeMapped(term.at, ", ")
1638
+ self.emitLightFusion("2", list2.value, n.startsWith("ff:core/Array."), async)
1639
+ self.writeMapped(term.at, "; for_i < for_l && for_i2 < for_l2; for_i++, for_i2++) {")
1640
+ self.writeLine()
1641
+ name1.each {
1642
+ self.writeMapped(at1, "const " + escapeKeyword(_) + " = for_a[for_i];")
1643
+ self.writeLine()
1644
+ }
1645
+ name2.each {
1646
+ self.writeMapped(at2, "const " + escapeKeyword(_) + " = for_a2[for_i2];")
1647
+ self.writeLine()
1648
+ }
1649
+ self.emitStatements(body, last, n.endsWith("eachWhile"), async)
1650
+ self.writeLine()
1651
+ self.writeMapped(term.at, "}")
1652
+ True
925
1653
  | n {n == "ff:core/List.List_each" || n == "ff:core/List.List_eachWhile"} {arguments | [
926
- ECall(_, StaticCall("ff:core/List.List_pairs", _, _), _, _, [list], _)
1654
+ ECall(_, StaticCall("ff:core/List.List_pairs", _, _), _, _, [list1], _)
927
1655
  ELambda(_, Lambda(_, _, [MatchCase(_, [
928
- PVariant(_, "ff:core/Pair.Pair", [PVariable(_, name1), PVariable(_, name2)])
1656
+ PVariant(_, "ff:core/Pair.Pair", [PVariable(at1, name1), PVariable(at2, name2)])
929
1657
  ], [], body)]))
930
1658
  ]} =>
931
- let fusion = self.emitLightFusion("for_a", list.value, async)
932
- let start = fusion.second.first
933
- let end = fusion.second.second
934
- let listCode = fusion.first
935
- Some(
936
- "for(let for_a = " + listCode + ", for_i = " + start + ", for_l = " + end +
937
- "; for_i < for_l; for_i++) {\n" +
938
- name1.map {"const " + escapeKeyword(_) + " = for_i;\n"}.else {""} +
939
- name2.map {"const " + escapeKeyword(_) + " = for_a[for_i];\n"}.else {""} +
940
- self.emitStatements(body, last, n.endsWith("eachWhile"), async) +
941
- "\n}"
942
- )
1659
+ self.writeMapped(term.at, "for(let ")
1660
+ self.emitLightFusion("", list1.value, n.startsWith("ff:core/Array."), async)
1661
+ self.writeMapped(term.at, "; for_i < for_l; for_i++) {")
1662
+ self.writeLine()
1663
+ name1.each {
1664
+ self.writeMapped(at1, "const " + escapeKeyword(_) + " = for_i;")
1665
+ self.writeLine()
1666
+ }
1667
+ name2.each {
1668
+ self.writeMapped(at2, "const " + escapeKeyword(_) + " = for_a[for_i];")
1669
+ self.writeLine()
1670
+ }
1671
+ self.emitStatements(body, last, n.endsWith("eachWhile"), async)
1672
+ self.writeLine()
1673
+ self.writeMapped(term.at, "}")
1674
+ True
943
1675
  | n {
944
1676
  n == "ff:core/List.List_each" || n == "ff:core/List.List_eachWhile" ||
945
1677
  n == "ff:core/Array.Array_each" || n == "ff:core/Array.Array_eachWhile"
946
1678
  } {
947
- arguments | [list, ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)]))]
1679
+ arguments | [list1, ELambda(_, Lambda(_, _, [MatchCase(_, [PVariable(at1, name1)], [], body)]))]
948
1680
  } =>
949
- let fusion = self.emitLightFusion("for_a", list, async)
950
- let start = fusion.second.first
951
- let end = fusion.second.second
952
- let listCode = fusion.first + if(n.startsWith("ff:core/Array.")) {".array"} else {""}
953
- Some(
954
- "for(let for_a = " + listCode + ", for_i = " + start + ", for_l = " + end +
955
- "; for_i < for_l; for_i++) {\n" +
956
- name.map {"const " + escapeKeyword(_) + " = for_a[for_i];\n"}.else {""} +
957
- self.emitStatements(body, last, n.endsWith("eachWhile"), async) +
958
- "\n}"
959
- )
1681
+ self.writeMapped(term.at, "for(let ")
1682
+ self.emitLightFusion("", list1, n.startsWith("ff:core/Array."), async)
1683
+ self.writeMapped(term.at, "; for_i < for_l; for_i++) {")
1684
+ self.writeLine()
1685
+ name1.each {
1686
+ self.writeMapped(at1, "const " + escapeKeyword(_) + " = for_a[for_i];")
1687
+ self.writeLine()
1688
+ }
1689
+ self.emitStatements(body, last, n.endsWith("eachWhile"), async)
1690
+ self.writeLine()
1691
+ self.writeMapped(term.at, "}")
1692
+ True
960
1693
  | "ff:core/Array.Array_push" {arguments | [array, value]} =>
961
- Some(self.emitTerm(array, async) + ".array.push(" + self.emitTerm(value, async) + ")")
1694
+ self.emitTerm(array, async)
1695
+ self.writeMapped(array.at, ".array.push(")
1696
+ self.emitTerm(value, async)
1697
+ self.writeMapped(array.at, ")")
1698
+ True
962
1699
  | "ff:core/Core.if" {arguments | [condition, body]} =>
963
- Some(
964
- "if(" + self.emitTerm(condition, async) + ") {\n" +
965
- if(last) {
966
- "return ff_core_Option.Some(" + self.emitTerm(invokeImmediately(body), async) +
967
- ")\n} else return ff_core_Option.None()"
968
- } else {
969
- self.emitStatements(invokeImmediately(body), False, False, async) + "\n}"
970
- }
971
- )
972
- | "ff:core/Core.throw" {term | ECall c} {c.arguments | [argument]} {dictionaries | [dictionary]} =>
973
- let d = self.emitDictionary(dictionary)
974
- let a = self.emitArgument(term.at, argument, async)
975
- Some("throw Object.assign(new Error(), {ffException: ff_core_Any.toAny_(" + a + ", " + d + ")})")
976
- | "ff:core/Try.Try_catch" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
977
- Some(code)
978
- | "ff:core/Try.Try_catchAny" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
979
- Some(code)
980
- | "ff:core/Try.Try_finally" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
981
- Some(code)
1700
+ self.writeMapped(condition.at, "if(")
1701
+ self.emitTerm(condition, async)
1702
+ self.writeMapped(condition.at, ") {")
1703
+ self.writeLine()
1704
+ if(last) {
1705
+ self.writeMapped(term.at, "return ff_core_Option.Some(")
1706
+ self.emitTerm(invokeImmediately(body), async)
1707
+ self.writeMapped(term.at, ")")
1708
+ } else {
1709
+ self.emitStatements(invokeImmediately(body), False, False, async)
1710
+ }
1711
+ self.writeLine()
1712
+ self.writeMapped(term.at, "}")
1713
+ if(last) {self.writeMapped(term.at, " else return ff_core_Option.None()")}
1714
+ True
1715
+ | "ff:core/Core.throw" {term | ECall c} {c.arguments | [argument]} {dictionaries | [hasAnyTag, show]} =>
1716
+ self.writeMapped(term.at, "throw ff_core_Js.initializeError_(new Error(), ")
1717
+ self.emitArgument(term.at, argument, async)
1718
+ self.writeMapped(term.at, ", ")
1719
+ self.writeMapped(term.at, self.makeDictionary(hasAnyTag))
1720
+ self.writeMapped(term.at, ", ")
1721
+ self.writeMapped(term.at, self.makeDictionary(show))
1722
+ self.writeMapped(term.at, ")")
1723
+ True
1724
+ | "ff:core/Try.Try_catch" {self.emitTryCatchFinally(term, last, async)} =>
1725
+ True
1726
+ | "ff:core/Try.Try_catchAny" {self.emitTryCatchFinally(term, last, async)} =>
1727
+ True
1728
+ | "ff:core/Try.Try_finally" {self.emitTryCatchFinally(term, last, async)} =>
1729
+ True
982
1730
  | "ff:core/Js.throwIfCancelled" =>
983
- Some(if(async) {"ff_core_Task.Task_throwIfAborted($task)"} else {""})
1731
+ if(async) {self.writeMapped(term.at, "ff_core_Task.Task_throwIfAborted($task)")}
1732
+ True
984
1733
  | "ff:core/Js.throw" {term | ECall c} {c.arguments | [argument]} =>
985
- Some("throw " + self.emitTerm(argument.value, async))
1734
+ self.writeMapped(term.at, "throw ")
1735
+ self.emitTerm(argument.value, async)
1736
+ True
986
1737
  | _ =>
987
- None
1738
+ False
988
1739
  }
989
1740
  }
990
1741
 
991
- emitLightFusion(listName: String, list: Term, async: Bool): Pair[String, Pair[String, String]] {
992
- mutable start = "0"
993
- mutable end = listName + ".length"
994
- let listCode = list.{
1742
+ emitLightFusion(
1743
+ suffix: String
1744
+ list: Term
1745
+ isArray: Bool
1746
+ async: Bool
1747
+ ) {
1748
+ function wrapUnlessInt(term: Term, before: String, after: String) {
1749
+ | EInt _, _, _ =>
1750
+ self.emitTerm(term, async)
1751
+ | _, _, _ =>
1752
+ if(before != "") {self.writeMapped(term.at, before)}
1753
+ self.emitTerm(term, async)
1754
+ if(after != "") {self.writeMapped(term.at, after)}
1755
+ }
1756
+ list.{
995
1757
  | ECall(_, StaticCall("ff:core/List.List_dropFirst", _, _), _, _, [a1, a2], _) =>
996
- start = self.emitTerm(a2.value, async)
997
- if(!start.all {_.isAsciiDigit()}) {
998
- start = "Math.max(" + start + ", 0)"
999
- }
1758
+ self.writeMapped(list.at, "for_a" + suffix + " = ")
1000
1759
  self.emitTerm(a1.value, async)
1760
+ if(isArray) {self.writeMapped(list.at, ".array")}
1761
+ self.writeMapped(list.at, ", for_i" + suffix + " = ")
1762
+ wrapUnlessInt(a2.value, "Math.max(", ", 0)")
1763
+ self.writeMapped(list.at, ", for_l" + suffix + " = ")
1764
+ self.writeMapped(list.at, "for_a" + suffix + ".length")
1001
1765
  | ECall(_, StaticCall("ff:core/List.List_dropLast", _, _), _, _, [a1, a2], _) =>
1002
- let count = self.emitTerm(a2.value, async)
1003
- if(!count.all {_.isAsciiDigit()}) {
1004
- end = end + " - Math.max(" + count + ", 0)"
1005
- } else {
1006
- end = end + " - " + count
1007
- }
1766
+ self.writeMapped(list.at, "for_a" + suffix + " = ")
1008
1767
  self.emitTerm(a1.value, async)
1768
+ if(isArray) {self.writeMapped(list.at, ".array")}
1769
+ self.writeMapped(list.at, ", for_i" + suffix + " = 0")
1770
+ self.writeMapped(list.at, ", for_l" + suffix + " = ")
1771
+ self.writeMapped(list.at, "for_a" + suffix + ".length - ")
1772
+ wrapUnlessInt(a2.value, "Math.max(", ", 0)")
1009
1773
  | ECall(_, StaticCall("ff:core/List.List_takeFirst", _, _), _, _, [a1, a2], _) =>
1010
- end = self.emitTerm(a2.value, async)
1011
- if(!end.all {_.isAsciiDigit()}) {
1012
- end = "Math.max(" + end + ", 0)"
1013
- }
1014
- end = "Math.min(" + end + ", " + listName + ".length)"
1774
+ self.writeMapped(list.at, "for_a" + suffix + " = ")
1015
1775
  self.emitTerm(a1.value, async)
1776
+ if(isArray) {self.writeMapped(list.at, ".array")}
1777
+ self.writeMapped(list.at, ", for_i" + suffix + " = 0")
1778
+ self.writeMapped(list.at, ", for_l" + suffix + " = ")
1779
+ self.writeMapped(list.at, "Math.min(")
1780
+ wrapUnlessInt(a2.value, "Math.max(", ", 0)")
1781
+ self.writeMapped(list.at, ", for_a" + suffix + ".length)")
1016
1782
  | ECall(_, StaticCall("ff:core/List.List_takeLast", _, _), _, _, [a1, a2], _) =>
1017
- let count = self.emitTerm(a2.value, async)
1018
- if(!count.all {_.isAsciiDigit()}) {
1019
- start = "Math.max(" + listName + ".length - Math.max(" + count + ", 0), 0)"
1020
- } else {
1021
- start = "Math.max(" + listName + ".length - " + count + ", 0)"
1022
- }
1783
+ self.writeMapped(list.at, "for_a" + suffix + " = ")
1023
1784
  self.emitTerm(a1.value, async)
1785
+ if(isArray) {self.writeMapped(list.at, ".array")}
1786
+ self.writeMapped(list.at, ", for_i" + suffix + " = ")
1787
+ self.writeMapped(list.at, "Math.max(for_a" + suffix + ".length - ")
1788
+ wrapUnlessInt(a2.value, "Math.max(", ", 0)")
1789
+ self.writeMapped(list.at, ", 0), for_l" + suffix + " = ")
1790
+ self.writeMapped(list.at, "for_a" + suffix + ".length")
1024
1791
  | _ =>
1792
+ self.writeMapped(list.at, "for_a" + suffix + " = ")
1025
1793
  self.emitTerm(list, async)
1794
+ if(isArray) {self.writeMapped(list.at, ".array")}
1795
+ self.writeMapped(list.at, ", for_i" + suffix + " = 0")
1796
+ self.writeMapped(list.at, ", for_l" + suffix + " = ")
1797
+ self.writeMapped(list.at, "for_a" + suffix + ".length")
1026
1798
  }
1027
- Pair(listCode, Pair(start, end))
1028
1799
  }
1029
1800
 
1030
- emitTryCatchFinally(term: Term, last: Bool, async: Bool): Option[String] {
1031
- function emitCatch(catchEffect: Type, cases: List[MatchCase]): String {
1801
+ emitTryCatchFinally(term: Term, last: Bool, async: Bool): Bool {
1802
+ function emitCatch(catchEffect: Type, cases: List[MatchCase]) {
1032
1803
  let catchAsync = self.emittingAsync && effectTypeIsAsync(catchEffect)
1033
1804
  Patterns.convertAndCheck(self.otherModules, cases)
1034
- let arguments = ["_exception.value_", "_error"]
1805
+ let arguments = [
1806
+ {self.writeMapped(term.at, "_exception.value_")}
1807
+ {self.writeMapped(term.at, "_error")}
1808
+ ]
1035
1809
  cases.{
1036
1810
  | [case] =>
1037
1811
  self.emitCase(arguments, case, [], [], False, last, False, True, catchAsync)
1038
1812
  | cs =>
1039
- let caseStrings = cases.pairs().map {| Pair(i, c) =>
1813
+ if(last) {
1814
+ self.writeMapped(term.at, "do {")
1815
+ self.writeLine()
1816
+ }
1817
+ let liner = Liner(self, double = False)
1818
+ cases.pairs().each {| Pair(i, c) =>
1819
+ liner.writeLines()
1040
1820
  let lastCase = i == cases.size() - 1
1041
1821
  self.emitCase(arguments, c, [], [], True, last, False, lastCase, catchAsync)
1042
1822
  }
1043
- if(last) {caseStrings.join("\n")} else {"do {\n" + caseStrings.join("\n") + "\n} while(false)"}
1823
+ if(last) {
1824
+ self.writeLine()
1825
+ self.writeMapped(term.at, "} while(false)")
1826
+ } else {}
1044
1827
  }
1045
1828
  }
1046
1829
  term.{
@@ -1052,24 +1835,41 @@ extend self: JsEmitter {
1052
1835
  ], _) =>
1053
1836
  let tryAsync = self.emittingAsync && effectTypeIsAsync(tryEffect)
1054
1837
  let finallyAsync = self.emittingAsync && effectTypeIsAsync(finallyEffect)
1055
- Some(
1056
- "try {\n" + self.emitStatements(tryBody, last, False, tryAsync) +
1057
- "\n} finally {\n" + self.emitStatements(finallyBody, last, False, finallyAsync) + "\n}"
1058
- )
1838
+ self.writeMapped(term.at, "try {")
1839
+ self.writeLine()
1840
+ self.emitStatements(tryBody, last, False, tryAsync)
1841
+ self.writeLine()
1842
+ self.writeMapped(term.at, "} finally {")
1843
+ self.writeLine()
1844
+ self.emitStatements(finallyBody, last, False, finallyAsync)
1845
+ self.writeLine()
1846
+ self.writeMapped(term.at, "}")
1847
+ True
1059
1848
  | ECall(_, StaticCall("ff:core/Try.Try_catchAny", _, _), _, _, [
1060
1849
  Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
1061
1850
  Argument(_, _, ELambda(_, Lambda(_, tryEffect, [MatchCase(_, [], [], tryBody)])))
1062
1851
  ], _))
1063
- Argument(_, _, ELambda(_, Lambda(_, catchEffect, [MatchCase(_, [PVariable(_, name)], [], catchBody)])))
1852
+ Argument(_, _, ELambda(_, Lambda(_, catchEffect, [
1853
+ MatchCase(_, [PVariable(nameAt, name)], [], catchBody)
1854
+ ])))
1064
1855
  ], _) =>
1065
1856
  let tryAsync = self.emittingAsync && effectTypeIsAsync(tryEffect)
1066
- Some(
1067
- "try {\n" +
1068
- self.emitStatements(tryBody, last, False, tryAsync) +
1069
- "\n} catch" + name.map {"(" + escapeKeyword(_) + ")"}.else {""} + " {\n" +
1070
- self.emitStatements(catchBody, last, False, tryAsync) +
1071
- "\n}"
1072
- )
1857
+ self.writeMapped(term.at, "try {")
1858
+ self.writeLine()
1859
+ self.emitStatements(tryBody, last, False, tryAsync)
1860
+ self.writeLine()
1861
+ self.writeMapped(term.at, "} catch")
1862
+ name.each {n =>
1863
+ self.writeMapped(nameAt, "(")
1864
+ self.writeNamed(n, nameAt, escapeKeyword(n))
1865
+ self.writeMapped(nameAt, ")")
1866
+ }
1867
+ self.writeMapped(term.at, " {")
1868
+ self.writeLine()
1869
+ self.emitStatements(catchBody, last, False, tryAsync)
1870
+ self.writeLine()
1871
+ self.writeMapped(term.at, "}")
1872
+ True
1073
1873
  | ECall(_, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
1074
1874
  Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
1075
1875
  Argument(_, _, ELambda(_, Lambda(_, tryEffect, [MatchCase(_, [], [], tryBody)])))
@@ -1077,16 +1877,25 @@ extend self: JsEmitter {
1077
1877
  Argument(_, _, ELambda(_, Lambda(_, catchEffect, cases)))
1078
1878
  ], [dictionary]) =>
1079
1879
  let tryAsync = self.emittingAsync && effectTypeIsAsync(tryEffect)
1080
- let d = self.emitDictionary(dictionary)
1081
- Some(
1082
- "try {\n" + self.emitStatements(tryBody, last, False, tryAsync) +
1083
- "\n} catch(_error) {\n" +
1084
- "if(!_error.ffException) throw _error\n" +
1085
- "const _exception = ff_core_Any.fromAny_(_error.ffException, " + d + ")\n" +
1086
- "if(!_exception.Some) throw _error\n" +
1087
- emitCatch(catchEffect, cases) +
1088
- "\n}"
1089
- )
1880
+ let d = self.makeDictionary(dictionary)
1881
+ self.writeMapped(term.at, "try {")
1882
+ self.writeLine()
1883
+ self.emitStatements(tryBody, last, False, tryAsync)
1884
+ self.writeLine()
1885
+ self.writeMapped(term.at, "} catch(_error) {")
1886
+ self.writeLine()
1887
+ self.writeMapped(term.at, "if(!_error.ffException) throw _error")
1888
+ self.writeLine()
1889
+ self.writeMapped(term.at, "const _exception = ff_core_Any.fromAny_(_error.ffException, ")
1890
+ self.writeMapped(term.at, d)
1891
+ self.writeMapped(term.at, ")")
1892
+ self.writeLine()
1893
+ self.writeMapped(term.at, "if(!_exception.Some) throw _error")
1894
+ self.writeLine()
1895
+ emitCatch(catchEffect, cases)
1896
+ self.writeLine()
1897
+ self.writeMapped(term.at, "}")
1898
+ True
1090
1899
  | ECall(_, StaticCall("ff:core/Try.Try_finally", _, _), _, _, [
1091
1900
  Argument(_, _, ECall(_, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
1092
1901
  Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
@@ -1098,40 +1907,67 @@ extend self: JsEmitter {
1098
1907
  ], _) =>
1099
1908
  let tryAsync = self.emittingAsync && effectTypeIsAsync(tryEffect)
1100
1909
  let finallyAsync = self.emittingAsync && effectTypeIsAsync(finallyEffect)
1101
- let d = self.emitDictionary(dictionary)
1102
- Some(
1103
- "try {\n" + self.emitStatements(tryBody, last, False, tryAsync) +
1104
- "\n} catch(_error) {\n" +
1105
- "if(!_error.ffException) throw _error\n" +
1106
- "const _exception = ff_core_Any.fromAny_(_error.ffException, " + d + ")\n" +
1107
- "if(!_exception.Some) throw _error\n" +
1108
- emitCatch(catchEffect, cases) +
1109
- "\n} finally {\n" + self.emitStatements(finallyBody, last, False, finallyAsync) + "\n}"
1110
- )
1910
+ let d = self.makeDictionary(dictionary)
1911
+ self.writeMapped(term.at, "try {")
1912
+ self.writeLine()
1913
+ self.emitStatements(tryBody, last, False, tryAsync)
1914
+ self.writeLine()
1915
+ self.writeMapped(term.at, "} catch(_error) {")
1916
+ self.writeLine()
1917
+ self.writeMapped(term.at, "if(!_error.ffException) throw _error")
1918
+ self.writeLine()
1919
+ self.writeMapped(term.at, "const _exception = ff_core_Any.fromAny_(_error.ffException, ")
1920
+ self.writeMapped(term.at, d)
1921
+ self.writeMapped(term.at, ")")
1922
+ self.writeLine()
1923
+ self.writeMapped(term.at, "if(!_exception.Some) throw _error")
1924
+ self.writeLine()
1925
+ emitCatch(catchEffect, cases)
1926
+ self.writeLine()
1927
+ self.writeMapped(term.at, "} finally {")
1928
+ self.writeLine()
1929
+ self.emitStatements(finallyBody, last, False, finallyAsync)
1930
+ self.writeLine()
1931
+ self.writeMapped(term.at, "}")
1932
+ True
1111
1933
  | _ =>
1112
- None
1934
+ False
1113
1935
  }
1114
1936
  }
1115
1937
 
1116
1938
  emitCase(
1117
- arguments: List[String]
1939
+ arguments: List[() => Unit]
1118
1940
  matchCase: MatchCase
1119
- conditions: List[String]
1120
- variables: List[String]
1941
+ conditions: List[() => Unit]
1942
+ variables: List[() => Unit]
1121
1943
  jump: Bool
1122
1944
  last: Bool
1123
1945
  break: Bool
1124
1946
  lastCase: Bool
1125
1947
  async: Bool
1126
- ): String {
1127
- function emitWrapper(code: String): String {
1128
- if(conditions.isEmpty()) {"{\n"} else {
1129
- "if(" + conditions.join(" && ") + ") {\n"
1130
- } +
1131
- variables.join() +
1132
- code +
1133
- "\n}"
1948
+ ) {
1949
+ function emitWrapperStart() {
1950
+ if(conditions.isEmpty()) {
1951
+ self.writeMapped(matchCase.at, "{")
1952
+ } else {
1953
+ self.writeMapped(matchCase.at, "if(")
1954
+ conditions.pairs().each {| Pair(i, c) =>
1955
+ c()
1956
+ if(i < conditions.size() - 1) {
1957
+ self.writeMapped(matchCase.at, " && ")
1958
+ }
1959
+ }
1960
+ self.writeMapped(matchCase.at, ") {")
1961
+ }
1962
+ self.writeLine()
1963
+ variables.each {_()}
1134
1964
  }
1965
+
1966
+ function emitWrapperEnd() {
1967
+ self.writeLine()
1968
+ self.writeMapped(matchCase.at, "}")
1969
+ }
1970
+
1135
1971
  Pair(matchCase.patterns, matchCase.guards).{
1136
1972
  | Pair([p, ...ps], _) =>
1137
1973
  self.emitPattern(
@@ -1149,79 +1985,103 @@ extend self: JsEmitter {
1149
1985
  )
1150
1986
  | Pair([], [MatchGuard(_, e, PVariant(_, "ff:core/Bool.True", _))]) {variables.isEmpty()} =>
1151
1987
  let newCase = matchCase.MatchCase(patterns = [], guards = [])
1152
- self.emitCase([], newCase, [...conditions, self.emitTerm(e, async)], [], jump, last, break, lastCase, async)
1988
+ self.emitCase([], newCase, [...conditions, {self.emitTerm(e, async)}], [], jump, last, break, lastCase, async)
1153
1989
  | Pair([], [MatchGuard(_, e, PVariant(_, "ff:core/Bool.True", _))]) =>
1154
1990
  let newCase = matchCase.MatchCase(patterns = [], guards = [])
1155
- let code = self.emitCase([], newCase, [self.emitTerm(e, async)], [], jump, last, break, lastCase, async)
1156
- emitWrapper(code)
1991
+ emitWrapperStart()
1992
+ self.emitCase([], newCase, [{self.emitTerm(e, async)}], [], jump, last, break, lastCase, async)
1993
+ emitWrapperEnd()
1157
1994
  | Pair([], [guard, ...guards]) =>
1158
1995
  let guardName = "_guard" + (guards.size() + 1)
1159
1996
  let newCase = matchCase.MatchCase(patterns = [guard.pattern], guards = guards)
1160
- let code =
1161
- "const " + guardName + " = " + self.emitTerm(guard.term, async) + ";\n" +
1162
- self.emitCase([guardName], newCase, [], [], jump, last, break, lastCase, async)
1163
- emitWrapper(code)
1997
+ emitWrapperStart()
1998
+ self.writeMapped(guard.at, "const " + guardName + " = ")
1999
+ self.emitTerm(guard.term, async)
2000
+ self.writeMapped(guard.at, ";")
2001
+ self.writeLine()
2002
+ self.emitCase([{self.writeMapped(guard.at, guardName)}], newCase, [], [], jump, last, break, lastCase, async)
2003
+ emitWrapperEnd()
1164
2004
  | Pair([], []) =>
1165
- let statementsCode = self.emitStatements(matchCase.body, last, break, async)
1166
- let lastLine = statementsCode.reverse().takeWhile {_ != '\n'}.reverse()
1167
- let returns =
1168
- lastLine.startsWith("return ") ||
1169
- lastLine.startsWith("break ") ||
1170
- lastLine.startsWith("continue ") ||
1171
- lastLine.startsWith("return;") ||
1172
- lastLine.startsWith("break;") ||
1173
- lastLine.startsWith("continue;") ||
1174
- lastLine.startsWith("throw ")
1175
- let code = statementsCode + if(jump && last && !returns) {
1176
- "\nreturn"
2005
+ emitWrapperStart()
2006
+ self.emitStatements(matchCase.body, last, break, async)
2007
+ let returns = self.writtenStrings.last().any {line =>
2008
+ line.first().any {part =>
2009
+ part.startsWith("return ") ||
2010
+ part.startsWith("break ") ||
2011
+ part.startsWith("continue ") ||
2012
+ part.startsWith("return;") ||
2013
+ part.startsWith("break;") ||
2014
+ part.startsWith("continue;") ||
2015
+ part.startsWith("throw ")
2016
+ }
2017
+ }
2018
+ if(jump && last && !returns) {
2019
+ self.writeLine()
2020
+ self.writeMapped(matchCase.at, "return")
1177
2021
  } elseIf {jump && !returns && !lastCase} {
1178
- if(break) {"\ncontinue"} else {"\nbreak"}
1179
- } else {
1180
- ""
2022
+ self.writeLine()
2023
+ self.writeMapped(matchCase.at, if(break) {"continue"} else {"break"})
1181
2024
  }
1182
- emitWrapper(code)
2025
+ emitWrapperEnd()
1183
2026
  }
1184
2027
  }
1185
2028
 
1186
2029
  emitPattern(
1187
- argument: String
2030
+ argument: () => Unit
1188
2031
  pattern: MatchPattern
1189
- arguments: List[String]
2032
+ arguments: List[() => Unit]
1190
2033
  matchCase: MatchCase
1191
- conditions: List[String]
1192
- variables: List[String]
2034
+ conditions: List[() => Unit]
2035
+ variables: List[() => Unit]
1193
2036
  jump: Bool
1194
2037
  last: Bool
1195
2038
  break: Bool
1196
2039
  lastCase: Bool
1197
2040
  async: Bool
1198
- ): String {
1199
- function addCondition(condition: String): List[String] {
2041
+ ) {
2042
+ function addUnaryCondition(at: Location, operator: String, right: () => Unit): List[() => Unit] {
2043
+ addCondition {
2044
+ self.writeMapped(at, operator)
2045
+ right()
2046
+ }
2047
+ }
2048
+ function addBinaryCondition(at: Location, operator: String, left: () => Unit, right: () => Unit): List[() => Unit] {
2049
+ addCondition {
2050
+ left()
2051
+ self.writeMapped(at, " " + operator + " ")
2052
+ right()
2053
+ }
2054
+ }
2055
+ function addCondition(condition: () => Unit): List[() => Unit] {
1200
2056
  if(lastCase) {conditions} else {[...conditions, condition]}
1201
2057
  }
1202
2058
  pattern.{
1203
2059
  | PString(_, value) =>
1204
- let newConditions = addCondition(argument + " === " + value)
2060
+ let newConditions = addBinaryCondition(pattern.at, "===", argument, {self.writeMapped(pattern.at, value)})
1205
2061
  self.emitCase(arguments, matchCase, newConditions, variables, jump, last, break, lastCase, async)
1206
2062
  | PInt(_, value) =>
1207
- let newConditions = addCondition(argument + " === " + value)
2063
+ let newConditions = addBinaryCondition(pattern.at, "===", argument, {self.writeMapped(pattern.at, value)})
1208
2064
  self.emitCase(arguments, matchCase, newConditions, variables, jump, last, break, lastCase, async)
1209
2065
  | PChar(_, value) =>
1210
- let newConditions = addCondition(argument + " === " + charLiteralToNumber(value))
2066
+ let newConditions = addBinaryCondition(pattern.at, "===", argument, {self.writeMapped(pattern.at, charLiteralToNumber(value))})
1211
2067
  self.emitCase(arguments, matchCase, newConditions, variables, jump, last, break, lastCase, async)
1212
2068
  | PVariable(_, None) =>
1213
2069
  self.emitCase(arguments, matchCase, conditions, variables, jump, last, break, lastCase, async)
1214
- | PVariable(_, Some(name)) =>
1215
- let escaped = escapeKeyword(name)
1216
- let newVariables = if(escaped != argument) {
1217
- [...variables, "const " + escaped + " = " + argument + ";\n"]
1218
- } else {variables}
2070
+ | PVariable(at, Some(name)) =>
2071
+ let newVariables = [...variables, {
2072
+ self.writeMapped(at, "const ")
2073
+ self.writeNamed(name, at, escapeKeyword(name))
2074
+ self.writeMapped(at, " = ")
2075
+ argument()
2076
+ self.writeMapped(at, ";")
2077
+ self.writeLine()
2078
+ }]
1219
2079
  self.emitCase(arguments, matchCase, conditions, newVariables, jump, last, break, lastCase, async)
1220
- | PVariant(_, "ff:core/Bool.False", []) =>
1221
- self.emitCase(arguments, matchCase, addCondition("!" + argument), variables, jump, last, break, lastCase, async)
2080
+ | PVariant(at, "ff:core/Bool.False", []) =>
2081
+ self.emitCase(arguments, matchCase, addUnaryCondition(at, "!", argument), variables, jump, last, break, lastCase, async)
1222
2082
  | PVariant(_, "ff:core/Bool.True", []) =>
1223
2083
  self.emitCase(arguments, matchCase, addCondition(argument), variables, jump, last, break, lastCase, async)
1224
- | PVariant(_, emptyOrLink, _) {emptyOrLink == "List$Empty" || emptyOrLink == "List$Link"} =>
2084
+ | PVariant(at, emptyOrLink, _) {emptyOrLink == "List$Empty" || emptyOrLink == "List$Link"} =>
1225
2085
  mutable restPattern = None
1226
2086
  function listPatterns(matchPattern: MatchPattern): List[MatchPattern] {
1227
2087
  | PVariant(_, "List$Empty", []) =>
@@ -1233,49 +2093,90 @@ extend self: JsEmitter {
1233
2093
  []
1234
2094
  }
1235
2095
  let patterns = listPatterns(pattern)
1236
- let itemArguments = patterns.pairs().map {| Pair(i, _) => argument + "[" + i + "]"}
1237
- let restArgument = restPattern.map {_ => argument + ".slice(" + patterns.size() + ")"}
2096
+ let itemArguments = patterns.pairs().map {| Pair(i, _) => {
2097
+ argument()
2098
+ self.writeMapped(at, "[" + i + "]")
2099
+ }}
2100
+ let restArgument = restPattern.map {_ => {
2101
+ argument()
2102
+ self.writeMapped(at, ".slice(" + patterns.size() + ")")
2103
+ }}
1238
2104
  let newArguments = [...itemArguments, ...restArgument.toList(), ...arguments]
1239
2105
  let newMatchCase = matchCase.MatchCase(
1240
2106
  patterns = [...patterns, ...restPattern.toList(), ...matchCase.patterns]
1241
2107
  )
1242
2108
  let operator = restPattern.map {_ => ">="}.else {"==="}
1243
- let newConditions = addCondition(argument + ".length " + operator + " " + patterns.size())
2109
+ let newConditions = addBinaryCondition(at, operator
2110
+ {
2111
+ argument()
2112
+ self.writeMapped(at, ".length")
2113
+ }
2114
+ {
2115
+ self.writeMapped(at, "" + patterns.size())
2116
+ }
2117
+ )
1244
2118
  self.emitCase(newArguments, newMatchCase, newConditions, variables, jump, last, break, lastCase, async)
1245
- | PVariant(_, name, patterns) =>
1246
- let processed = self.processVariantCase(name, argument)
2119
+ | PVariant(at, name, patterns) =>
2120
+ let processed = self.processVariantCase(at, name, argument)
1247
2121
  let newMatchCase = matchCase.MatchCase(patterns = [...patterns, ...matchCase.patterns])
1248
2122
  let newConditions = if(processed.loneVariant || lastCase) {conditions} else {
1249
- [...conditions, argument + "." + processed.variantName]
2123
+ [...conditions, {
2124
+ argument()
2125
+ self.writeMapped(at, ".")
2126
+ self.writeNamed(processed.variantName, at, processed.variantName)
2127
+ }]
1250
2128
  }
1251
2129
  let newArguments = [...processed.arguments, ...arguments]
1252
2130
  self.emitCase(newArguments, newMatchCase, newConditions, variables, jump, last, break, lastCase, async)
1253
2131
  | PVariantAs(at, name, variableAt, variable) =>
1254
- let processed = self.processVariantCase(name, argument)
2132
+ let processed = self.processVariantCase(at, name, argument)
1255
2133
  let newConditions = if(processed.loneVariant || lastCase) {conditions} else {
1256
- [...conditions, argument + "." + processed.variantName]
2134
+ [...conditions, {
2135
+ argument()
2136
+ self.writeMapped(at, ".")
2137
+ self.writeNamed(processed.variantName, at, processed.variantName)
2138
+ }]
1257
2139
  }
1258
- let newVariables = variable.map(escapeKeyword).filter {_ != argument}.map {
1259
- [...variables, "const " + _ + " = " + argument + ";\n"]
1260
- }.else {[]}
2140
+ let newVariables = variable.map {x =>
2141
+ [...variables, {
2142
+ self.writeMapped(at, "const ")
2143
+ self.writeNamed(x, at, escapeKeyword(x))
2144
+ self.writeMapped(at, " = ")
2145
+ argument()
2146
+ self.writeMapped(at, ";")
2147
+ self.writeLine()
2148
+ }]
2149
+ }.else {variables}
1261
2150
  self.emitCase(arguments, matchCase, newConditions, newVariables, jump, last, break, lastCase, async)
1262
- | PAlias(_, pattern, variable) =>
1263
- let escaped = escapeKeyword(variable)
1264
- let newVariables = if(escaped != argument) {
1265
- [...variables, "const " + escaped + " = " + argument + ";\n"]
1266
- } else {variables}
2151
+ | PAlias(at, pattern, variable) =>
2152
+ let newVariables = [...variables, {
2153
+ self.writeMapped(at, "const ")
2154
+ self.writeNamed(variable, at, escapeKeyword(variable))
2155
+ self.writeMapped(at, " = ")
2156
+ argument()
2157
+ self.writeMapped(at, ";")
2158
+ self.writeLine()
2159
+ }]
1267
2160
  self.emitPattern(argument, pattern, arguments, matchCase, conditions, newVariables, jump, last, break, lastCase, async)
1268
2161
  }
1269
2162
  }
1270
2163
 
1271
- emitList(items: List[Pair[Term, Bool]], async: Bool): String {
1272
- "[" + items.map {
1273
- | Pair(item, False) => self.emitTerm(item, async)
1274
- | Pair(item, True) => "..." + self.emitTerm(item, async)
1275
- }.join(", ") + "]"
2164
+ emitList(at: Location, items: List[Pair[Term, Bool]], async: Bool) {
2165
+ self.writeMapped(at, "[")
2166
+ let comma = Comma(self)
2167
+ items.each {
2168
+ | Pair(item, False) =>
2169
+ comma.writeComma()
2170
+ self.emitTerm(item, async)
2171
+ | Pair(item, True) =>
2172
+ comma.writeComma()
2173
+ self.writeMapped(item.at, "...")
2174
+ self.emitTerm(item, async)
2175
+ }
2176
+ self.writeMapped(at, "]")
1276
2177
  }
1277
2178
 
1278
- processVariantCase(name: String, argument: String): ProcessedVariantCase {
2179
+ processVariantCase(at: Location, name: String, argument: () => Unit): ProcessedVariantCase {
1279
2180
  let variantNameUnqualified = name.reverse().takeWhile {_ != '.'}.reverse()
1280
2181
  let variantName = escapeKeyword(variantNameUnqualified)
1281
2182
  let moduleName = name.dropLast(variantNameUnqualified.size() + 1)
@@ -1288,7 +2189,10 @@ extend self: JsEmitter {
1288
2189
  loneVariant = definition.variants.size() == 1
1289
2190
  [...definition.commonFields.map {_.name}, ...variant.fields.map {_.name}]
1290
2191
  }
1291
- }.grab().map {field => if(newtype) {argument} else {argument + "." + escapeKeyword(field)}}
2192
+ }.grab().map {field => if(newtype) {argument} else {{
2193
+ argument()
2194
+ self.writeMapped(at, "." + escapeKeyword(field))
2195
+ }}}
1292
2196
  ProcessedVariantCase(variantName, newtype, loneVariant, newArguments)
1293
2197
  }
1294
2198
 
@@ -1299,7 +2203,7 @@ extend self: JsEmitter {
1299
2203
  let moduleName = name.dropLast(variantNameUnqualified.size() + 1)
1300
2204
  let variantModule = self.otherModules.grab(moduleName)
1301
2205
  mutable newtype = False
1302
- let newArguments = variantModule.types.collectFirst {definition =>
2206
+ variantModule.types.collectFirst {definition =>
1303
2207
  definition.variants.find {_.name == variantName}.map {variant =>
1304
2208
  newtype = definition.newtype
1305
2209
  }
@@ -1307,12 +2211,17 @@ extend self: JsEmitter {
1307
2211
  newtype
1308
2212
  }
1309
2213
 
1310
- emitArgument(callAt: Location, argument: Argument, async: Bool): String {
2214
+ emitArgument(callAt: Location, argument: Argument, async: Bool) {
1311
2215
  argument.value.{
1312
- | ECall(_, StaticCall("ff:core/SourceLocation.callSite", _, _), _, _, _, _) =>
1313
- "\"" + self.moduleKey.folders.map {_ + "/"}.join() + self.moduleKey.name +
1314
- ":" + callAt.line + ":" + callAt.column +
1315
- "," + self.moduleKey.packagePair.group + "," + self.moduleKey.packagePair.name + "\""
2216
+ | ECall(at, StaticCall("ff:core/SourceLocation.callSite", _, _), _, _, _, _) =>
2217
+ self.writeMapped(at, "\"")
2218
+ self.moduleKey.folders.each {
2219
+ self.writeMapped(at, _ + "/")
2220
+ }
2221
+ self.writeMapped(at, self.moduleKey.name)
2222
+ self.writeMapped(at, ":" + callAt.line + ":" + callAt.column)
2223
+ self.writeMapped(at, "," + self.moduleKey.packagePair.group + "," + self.moduleKey.packagePair.name)
2224
+ self.writeMapped(at, "\"")
1316
2225
  | value =>
1317
2226
  self.emitTerm(value, async)
1318
2227
  }
@@ -1320,13 +2229,20 @@ extend self: JsEmitter {
1320
2229
 
1321
2230
  }
1322
2231
 
1323
- data ProcessedVariantCase(
2232
+ capability ProcessedVariantCase(
1324
2233
  variantName: String
1325
2234
  newtype: Bool
1326
2235
  loneVariant: Bool
1327
- arguments: List[String]
2236
+ arguments: List[() => Unit]
1328
2237
  )
1329
2238
 
2239
+ rawJs(at: Location, rawIdentifier: String): Term {
2240
+ let noEffect = TConstructor(at, "ff:core/Nothing.Nothing", [])
2241
+ ECall(at, StaticCall("ff:core/Js.rawIdentifier", False, False), noEffect, [], [
2242
+ Argument(at, None, EString(at, rawIdentifier))
2243
+ ], [])
2244
+ }
2245
+
1330
2246
  detectIfElse(term: Term): List[Pair[Term, Term]] {
1331
2247
  | ECall(at, StaticCall("ff:core/Core.if", _, _), _, _, [condition, body], _) =>
1332
2248
  [Pair(condition.value, invokeImmediately(body.value))]
@@ -1448,3 +2364,28 @@ primitiveTypes = [
1448
2364
  "ff:core/Float.Float"
1449
2365
  "ff:core/String.String"
1450
2366
  ].toSet()
2367
+
2368
+ class Liner(emitter: JsEmitter, double: Bool, mutable first: Bool = True)
2369
+
2370
+ extend self: Liner {
2371
+ writeLines() {
2372
+ if(self.first) {
2373
+ self.first = False
2374
+ } else {
2375
+ self.emitter.writeLine()
2376
+ if(self.double) {self.emitter.writeLine()}
2377
+ }
2378
+ }
2379
+ }
2380
+
2381
+ class Comma(emitter: JsEmitter, mutable first: Bool = True, delimiter: String = ", ")
2382
+
2383
+ extend self: Comma {
2384
+ writeComma() {
2385
+ if(self.first) {
2386
+ self.first = False
2387
+ } else {
2388
+ self.emitter.writeUnmapped(self.delimiter)
2389
+ }
2390
+ }
2391
+ }