firefly-compiler 0.4.4 → 0.4.6

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 (98) hide show
  1. package/.hintrc +5 -0
  2. package/README.md +42 -61
  3. package/compiler/Compiler.ff +6 -2
  4. package/compiler/Deriver.ff +10 -0
  5. package/compiler/Environment.ff +2 -1
  6. package/compiler/JsEmitter.ff +29 -17
  7. package/compiler/Resolver.ff +1 -1
  8. package/core/.firefly/package.ff +1 -2
  9. package/core/Box.ff +7 -0
  10. package/core/BuildSystem.ff +5 -4
  11. package/core/Core.ff +4 -0
  12. package/core/HttpClient.ff +1 -1
  13. package/core/JsValue.ff +22 -1
  14. package/core/Lock.ff +4 -4
  15. package/core/NodeSystem.ff +12 -3
  16. package/core/Path.ff +1 -1
  17. package/core/SourceLocation.ff +41 -0
  18. package/core/Stack.ff +6 -6
  19. package/core/StringMap.ff +3 -3
  20. package/lsp/LanguageServer.ff +32 -7
  21. package/lsp/stderr.txt +1 -0
  22. package/lsp/stdin.txt +11 -0
  23. package/lsp/stdout.txt +41 -0
  24. package/lux/.firefly/package.ff +2 -0
  25. package/lux/Lux.ff +473 -0
  26. package/lux/LuxEvent.ff +116 -0
  27. package/lux/Main.ff +126 -0
  28. package/lux/Main2.ff +144 -0
  29. package/meetup/AutoCompletion.ff +6 -0
  30. package/output/js/ff/compiler/Builder.mjs +444 -440
  31. package/output/js/ff/compiler/Compiler.mjs +416 -410
  32. package/output/js/ff/compiler/Dependencies.mjs +389 -385
  33. package/output/js/ff/compiler/Deriver.mjs +1170 -1166
  34. package/output/js/ff/compiler/Dictionaries.mjs +1309 -1305
  35. package/output/js/ff/compiler/Environment.mjs +1015 -1005
  36. package/output/js/ff/compiler/Inference.mjs +4268 -4264
  37. package/output/js/ff/compiler/JsEmitter.mjs +5391 -5353
  38. package/output/js/ff/compiler/JsImporter.mjs +266 -262
  39. package/output/js/ff/compiler/LspHook.mjs +793 -789
  40. package/output/js/ff/compiler/Main.mjs +1699 -1695
  41. package/output/js/ff/compiler/Parser.mjs +4008 -4004
  42. package/output/js/ff/compiler/Patterns.mjs +927 -923
  43. package/output/js/ff/compiler/Resolver.mjs +2307 -2303
  44. package/output/js/ff/compiler/Substitution.mjs +1150 -1146
  45. package/output/js/ff/compiler/Syntax.mjs +12434 -12430
  46. package/output/js/ff/compiler/Token.mjs +3096 -3092
  47. package/output/js/ff/compiler/Tokenizer.mjs +593 -589
  48. package/output/js/ff/compiler/Unification.mjs +1752 -1748
  49. package/output/js/ff/compiler/Wildcards.mjs +608 -604
  50. package/output/js/ff/compiler/Workspace.mjs +687 -683
  51. package/output/js/ff/core/Any.mjs +143 -139
  52. package/output/js/ff/core/Array.mjs +547 -543
  53. package/output/js/ff/core/AssetSystem.mjs +274 -270
  54. package/output/js/ff/core/Atomic.mjs +154 -150
  55. package/output/js/ff/core/Bool.mjs +152 -141
  56. package/output/js/ff/core/Box.mjs +112 -0
  57. package/output/js/ff/core/BrowserSystem.mjs +126 -122
  58. package/output/js/ff/core/Buffer.mjs +395 -391
  59. package/output/js/ff/core/BuildSystem.mjs +296 -290
  60. package/output/js/ff/core/Channel.mjs +189 -185
  61. package/output/js/ff/core/Char.mjs +149 -145
  62. package/output/js/ff/core/Core.mjs +300 -288
  63. package/output/js/ff/core/Duration.mjs +116 -112
  64. package/output/js/ff/core/Equal.mjs +179 -175
  65. package/output/js/ff/core/Error.mjs +142 -138
  66. package/output/js/ff/core/FileHandle.mjs +150 -146
  67. package/output/js/ff/core/Float.mjs +225 -214
  68. package/output/js/ff/core/HttpClient.mjs +190 -186
  69. package/output/js/ff/core/Instant.mjs +109 -105
  70. package/output/js/ff/core/Int.mjs +265 -254
  71. package/output/js/ff/core/IntMap.mjs +280 -276
  72. package/output/js/ff/core/JsSystem.mjs +238 -234
  73. package/output/js/ff/core/JsValue.mjs +714 -664
  74. package/output/js/ff/core/List.mjs +2334 -2321
  75. package/output/js/ff/core/Lock.mjs +230 -226
  76. package/output/js/ff/core/Log.mjs +163 -159
  77. package/output/js/ff/core/Map.mjs +362 -358
  78. package/output/js/ff/core/NodeSystem.mjs +302 -283
  79. package/output/js/ff/core/Nothing.mjs +104 -100
  80. package/output/js/ff/core/Option.mjs +1015 -1002
  81. package/output/js/ff/core/Ordering.mjs +730 -726
  82. package/output/js/ff/core/Pair.mjs +331 -318
  83. package/output/js/ff/core/Path.mjs +546 -542
  84. package/output/js/ff/core/RbMap.mjs +1940 -1936
  85. package/output/js/ff/core/Serializable.mjs +428 -424
  86. package/output/js/ff/core/Set.mjs +254 -250
  87. package/output/js/ff/core/Show.mjs +205 -201
  88. package/output/js/ff/core/SourceLocation.mjs +229 -0
  89. package/output/js/ff/core/Stack.mjs +541 -537
  90. package/output/js/ff/core/Stream.mjs +1304 -1300
  91. package/output/js/ff/core/String.mjs +365 -354
  92. package/output/js/ff/core/StringMap.mjs +280 -276
  93. package/output/js/ff/core/Task.mjs +320 -316
  94. package/output/js/ff/core/Try.mjs +507 -503
  95. package/output/js/ff/core/Unit.mjs +151 -103
  96. package/package.json +29 -29
  97. package/vscode/package-lock.json +5 -5
  98. package/vscode/package.json +1 -1
package/.hintrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "development"
4
+ ]
5
+ }
package/README.md CHANGED
@@ -1,96 +1,77 @@
1
1
  # Firefly programming language
2
2
 
3
- [Get the VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang)
3
+ A straightforward language for full-stack development.
4
4
 
5
- ## About the compiler
6
- This is a bootstrap transpiler for converting Firefly code to JavaScript code.
5
+ * Hybrid imperative+functional style with uncomplicated types.
6
+ * Code sharing between the browser and the server.
7
+ * Comprehensive IDE support (MacOS/Windows/Linux).
8
+ * Cross-compilation of executables (MacOS/Windows/Linux).
9
+ * Asynchronous functions without the async/await hassle.
10
+ * Super simple package management with repeatable builds.
7
11
 
8
- Status: Firefly has been bootstrapped and now transpiles itself into JavaScript.
12
+ *This is a preview. Please take it for a ride and tell us what you think!*
9
13
 
10
- You can create a whole webapp (frontend+backend) in a single file and it can be build as an executable for linux, windows and osx.
14
+ ## Writing your first webapp
11
15
 
12
- [See an example webapp](https://github.com/Ahnfelt/firefly-boot/blob/master/experimental/random/rhymeapp/Main.ff)
16
+ Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang).
13
17
 
14
- **Async/await inference:** https://www.ahnfelt.net/async-await-inference-in-firefly/
18
+ Create a `WebApp.ff` file.
15
19
 
20
+ Type `webapp` and autocomplete to get a minimalistic client and server (you may need to give the extension a few seconds to initialize first).
16
21
 
17
- ## Running a main file
22
+ Go to the *Run and debug* side bar and choose *create a launch.json file*.
18
23
 
19
- ```
20
- firefly.sh MyApp.ff
21
- ```
22
-
23
- *You must stand in the project directory when running this command.*
24
-
25
- This will run `main` or `nodeMain` in MyApp.ff.
26
-
27
- If there is a `buildMain(system: BuildSystem)` method, it will be run before `main`,
28
- and if it calls `system.setAssets(assets)`, those assets will be available to the main file via `system.assets()`.
24
+ Press F5 or click *Run*. View the running webapp at http://localhost:8080/
29
25
 
30
- You can also have a `browserMain(system: BrowserSystem)` in the file, which can be compiled to a browser JS-file and included as an asset.
31
-
32
- Missing dependencies are fetched from the central or configured package repository before building and running.
26
+ In the `WebApp.ff` file you'll see a `nodeMain`, which runs on the server, and a `browserMain`, which runs on the client. There's also a `buildMain` which is the build step that compiles the client side code to JavaScript so that it can be served to the browser.
33
27
 
34
28
  ## Building an executable
35
29
 
36
- ```
37
- firefly.sh build MyApp.ff
38
- ```
30
+ Install the `firefly` compiler:
39
31
 
40
- *You must stand in the project directory when running this command.*
32
+ ```npm install -g firefly-compiler```
41
33
 
42
- This will generate a stand-alone executable file `MyApp` with the necessary assets and dependencies, compatible with most linux distributions, windows and osx.
34
+ Build executables for Linux, MacOS and Windows:
43
35
 
44
- If there is a `buildMain(system: BuildSystem)` method, it will be run before producing the executables,
45
- and if it calls `system.setAssets(assets)`, those assets will be included in the executables and thus be available to the main file via `system.assets()`.
36
+ ```firefly build WebApp.ff```
46
37
 
47
- ## Building for the browser
38
+ You can find the executables in `.firefly/`
48
39
 
49
- ```
50
- firefly.sh browser MyApp.ff
51
- ```
40
+ The command line interface can also run `WebApp.ff` directly by typing:
52
41
 
53
- *You must stand in the project directory when running this command.*
42
+ ```firefly WebApp.ff```
54
43
 
55
- This generates a single, minified `MyApp.min.js`, compatible with all modern browsers (ES6+).
44
+ ## Connecting to PostgreSQL
56
45
 
57
- ## Bootstrapping the compiler
46
+ To connect to a PostgreSQL database, add this dependency to the top of the file:
58
47
 
59
- ```
60
- firefly.sh bootstrap
61
- ```
48
+ ```dependency ff:postgresql:0.0.0```
62
49
 
63
- *You must stand in the project directory when running this command.*
50
+ And import the `Pg` module from it:
64
51
 
65
- If you botch the compiler, just roll back the output directory and try again.
52
+ ```import Pg from ff:postgresql```
66
53
 
67
- ## Dependencies and imports
54
+ Create a connection pool with the appropriate connection parameters:
68
55
 
69
- You may list your dependencies either in the top of the main file or in `.firefly/package.ff`.
56
+ ```let pool = Pg.makePool(...)```
70
57
 
71
- In order to establish a package directory, and thus allow importing other files from the current package, you must place a `.firefly/package.ff` file.
58
+ And run your first transaction, e.g.:
72
59
 
73
- Example (all lines optional):
74
60
  ```
75
- package john:stuff:0.1.7
76
- dependency anne:goodies:2.1.0
77
- dependency chris:images:0.7.0
61
+ pool.transaction {connection =>
62
+ let emails = connection.statement("""
63
+ select email from users
64
+ """).map {row =>
65
+ row.getString("email").grab()
66
+ }
67
+ Log.debug(emails)
68
+ }
78
69
  ```
79
70
 
80
- You don't have to manually install dependencies - they will be automatically downloaded when necessary.
81
-
82
- ## Workspaces
71
+ ## What's next
83
72
 
84
- You may place a `.firefly-workspace` file somewhere in a parent directory.
73
+ For now, you're on your own! Lots of things you'll be missing. We're working on it.
85
74
 
86
- The workspace file configures where source code is fetched from.
75
+ To follow the development or get help, join `#firefly` on https://discord.gg/FHv8vXJNVf - we're a friendly bunch, and your feedback is appreciated!
87
76
 
88
- It can be a local directory or a remote repository.
89
-
90
- Example:
91
-
92
- ```
93
- foo:stuff projects/foo/stuff
94
- bar:* projects/foo
95
- * https://example.com/repository
96
- ```
77
+ This repository contains the source code of the compiler and language server. It's all written in Firefly! Apart from `extension.js`, the JavaScript files you see in this repository are generated from Firefly code.
@@ -66,6 +66,7 @@ coreImports: List[DImport] =
66
66
  "AssetSystem"
67
67
  "Atomic"
68
68
  "Bool"
69
+ "Box"
69
70
  "BrowserSystem"
70
71
  "Buffer"
71
72
  "BuildSystem"
@@ -96,6 +97,7 @@ coreImports: List[DImport] =
96
97
  "Serializable"
97
98
  "Set"
98
99
  "Show"
100
+ "SourceLocation"
99
101
  "Stack"
100
102
  "Stream"
101
103
  "String"
@@ -181,7 +183,7 @@ extend self: Compiler {
181
183
 
182
184
  let module = self.parse(packagePair, moduleName, None)
183
185
  let otherModules = self.imports(module)
184
- let resolver = Resolver.make(self.lspHook)
186
+ let resolver = Resolver.make(packagePair, moduleName, self.lspHook)
185
187
  let result = resolver.resolveModule(module, otherModules)
186
188
 
187
189
  self.resolvedModules = self.resolvedModules.add(packageName + ":" + moduleName, result)
@@ -230,7 +232,9 @@ extend self: Compiler {
230
232
  self.infer(i.packagePair, newModuleName)
231
233
  }
232
234
 
233
- let js = JsEmitter.make([module, ...otherModules], self.emitTarget, isMainModule, self.compilerModulePath)
235
+ let allModules = [module, ...otherModules]
236
+ let js =
237
+ JsEmitter.make(allModules, self.emitTarget, isMainModule, self.compilerModulePath, packagePair, moduleName)
234
238
  .emitModule(packagePair, module)
235
239
  let jsPath = self.jsOutputPath.slash(packagePair.group).slash(packagePair.name)
236
240
  let jsFile = jsPath.slash(moduleName + ".mjs")
@@ -33,6 +33,15 @@ extend self: Deriver {
33
33
  let coreWhitelist = [
34
34
  "ff:core/Serializable.DeserializationChecksumException"
35
35
  "ff:core/Core.GrabException"
36
+ "ff:core/Unit.Unit"
37
+ "ff:core/Pair.Pair"
38
+ "ff:core/Option.Option"
39
+ "ff:core/Int.Int"
40
+ "ff:core/Float.Float"
41
+ "ff:core/String.String"
42
+ "ff:core/Char.Char"
43
+ "ff:core/Bool.Bool"
44
+ "ff:core/List.List"
36
45
  ].toSet() // Maybe whitelist all?
37
46
  let missingInstance =
38
47
  self.findTypesThatNeedInstances("ff:core/Any.HasAnyTag", modulePrefix, coreWhitelist, True, module)
@@ -310,6 +319,7 @@ extend self: Deriver {
310
319
  "ff:core/Option.Option"
311
320
  "ff:core/List.List"
312
321
  "ff:core/Pair.Pair"
322
+ "ff:core/Unit.Unit"
313
323
  ].toSet()
314
324
  let missingInstance =
315
325
  self.findTypesThatNeedInstances("ff:core/Equal.Equal", modulePrefix, coreWhitelist, True, module)
@@ -91,11 +91,12 @@ processModule(module: Module, isCurrentModule: Bool, alreadyFlat: Bool): Environ
91
91
  let effect = method.signature.generics.filter {_ == "Q$"}
92
92
  let normalGenerics = method.signature.generics.filter {_ != "Q$"}
93
93
  let outerGenerics = if(alreadyFlat) {[]} else {d.generics}
94
+ let outerConstraints = if(alreadyFlat) {[]} else {d.constraints}
94
95
  Pair(
95
96
  prefix + method.signature.name
96
97
  Scheme(False, False, False, False, method.signature.Signature(
97
98
  generics = [...effect, ...outerGenerics, ...normalGenerics]
98
- constraints = [...d.constraints, ...method.signature.constraints]
99
+ constraints = [...outerConstraints, ...method.signature.constraints]
99
100
  parameters = [selfParameter, ...method.signature.parameters]
100
101
  ))
101
102
  )
@@ -8,6 +8,8 @@ capability JsEmitter(
8
8
  emitTarget: EmitTarget
9
9
  isMainModule: Bool
10
10
  compilerModulePath: Option[Path]
11
+ packagePair: PackagePair
12
+ moduleName: String
11
13
  mutable emittingAsync: Bool
12
14
  mutable tailCallUsed: Bool
13
15
  )
@@ -24,6 +26,8 @@ make(
24
26
  emitTarget: EmitTarget
25
27
  isMainModule: Bool
26
28
  compilerModulePath: Option[Path]
29
+ packagePair: PackagePair
30
+ moduleName: String
27
31
  ): JsEmitter {
28
32
  JsEmitter(
29
33
  otherModules = otherModules.map {m =>
@@ -34,6 +38,8 @@ make(
34
38
  emitTarget = emitTarget
35
39
  isMainModule = isMainModule
36
40
  compilerModulePath = compilerModulePath
41
+ packagePair = packagePair
42
+ moduleName = moduleName
37
43
  emittingAsync = False
38
44
  tailCallUsed = False
39
45
  )
@@ -298,7 +304,7 @@ extend self: JsEmitter {
298
304
  | EVariant(at, "ff:core/Unit.Unit", _, _) =>
299
305
  "(void 0)"
300
306
  | EVariant(at, name, _, arguments) =>
301
- let argumentsString = arguments.toList().flatten().map {self.emitArgument(_, async)}.join(", ")
307
+ let argumentsString = arguments.toList().flatten().map {self.emitArgument(at, _, async)}.join(", ")
302
308
  let newtype = self.processVariant(name)
303
309
  if(newtype) { argumentsString } else:
304
310
  escapeResolved(name) + "(" + argumentsString + ")"
@@ -347,9 +353,9 @@ extend self: JsEmitter {
347
353
  let call = "(" + self.emitTerm(function, async) + ")(" + self.emitTerm(value, async) + c + ")"
348
354
  if(await) {"(await " + call + ")"} else {call}
349
355
  | ECall(at, StaticCall(operator, _, _), _, [], [value], _) {!operator.grabFirst().isAsciiLetter()} =>
350
- "(" + operator + self.emitArgument(value, async) + ")"
356
+ "(" + operator + self.emitArgument(at, value, async) + ")"
351
357
  | ECall(at, StaticCall(operator, _, _), _, [], [left, right], _) {!operator.grabFirst().isAsciiLetter()} =>
352
- "(" + self.emitArgument(left, async) + " " + operator + " " + self.emitArgument(right, async) + ")"
358
+ "(" + self.emitArgument(at, left, async) + " " + operator + " " + self.emitArgument(at, right, async) + ")"
353
359
  | ECall(at, StaticCall("ff:unsafejs/UnsafeJs.import", _, _), _, _, [Argument(_, _, EString(_, url))], _) =>
354
360
  self.jsImporter.add(url.replace("\"", ""))
355
361
  | ECall(at, StaticCall("ff:unsafejs/UnsafeJs.await", _, _), _, _, [Argument(_, _, body)], _) =>
@@ -362,27 +368,27 @@ extend self: JsEmitter {
362
368
  | ECall(at, StaticCall("ff:core/Equal.equals", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
363
369
  primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
364
370
  } =>
365
- "(" + self.emitArgument(left, async) + " === " + self.emitArgument(right, async) + ")"
371
+ "(" + self.emitArgument(at, left, async) + " === " + self.emitArgument(at, right, async) + ")"
366
372
  | ECall(at, StaticCall("ff:core/Equal.notEquals", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
367
373
  primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
368
374
  } =>
369
- "(" + self.emitArgument(left, async) + " !== " + self.emitArgument(right, async) + ")"
375
+ "(" + self.emitArgument(at, left, async) + " !== " + self.emitArgument(at, right, async) + ")"
370
376
  | ECall(at, StaticCall("ff:core/Ordering.before", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
371
377
  primitiveTypes.contains(typeName)
372
378
  } =>
373
- "(" + self.emitArgument(left, async) + " < " + self.emitArgument(right, async) + ")"
379
+ "(" + self.emitArgument(at, left, async) + " < " + self.emitArgument(at, right, async) + ")"
374
380
  | ECall(at, StaticCall("ff:core/Ordering.notBefore", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
375
381
  primitiveTypes.contains(typeName)
376
382
  } =>
377
- "(" + self.emitArgument(left, async) + " >= " + self.emitArgument(right, async) + ")"
383
+ "(" + self.emitArgument(at, left, async) + " >= " + self.emitArgument(at, right, async) + ")"
378
384
  | ECall(at, StaticCall("ff:core/Ordering.after", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
379
385
  primitiveTypes.contains(typeName)
380
386
  } =>
381
- "(" + self.emitArgument(left, async) + " > " + self.emitArgument(right, async) + ")"
387
+ "(" + self.emitArgument(at, left, async) + " > " + self.emitArgument(at, right, async) + ")"
382
388
  | ECall(at, StaticCall("ff:core/Ordering.notAfter", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
383
389
  primitiveTypes.contains(typeName)
384
390
  } =>
385
- "(" + self.emitArgument(left, async) + " <= " + self.emitArgument(right, async) + ")"
391
+ "(" + self.emitArgument(at, left, async) + " <= " + self.emitArgument(at, right, async) + ")"
386
392
  | ECall(_, StaticCall("ff:core/Array.fillBy", _, _), effect, _, [size, Argument(_, _, ELambda(at,
387
393
  Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)@c])@l
388
394
  ))], _) {
@@ -390,7 +396,7 @@ extend self: JsEmitter {
390
396
  } =>
391
397
  let n = name.map {escapeResolved(_)}.else {"i"}
392
398
  "((() => {\n" +
393
- "const size = " + self.emitArgument(size, async) + ";\n" +
399
+ "const size = " + self.emitArgument(at, size, async) + ";\n" +
394
400
  "const result = [];\n" +
395
401
  "for(let " + n + " = 0; " + n + " < size; " + n + "++) {\n" +
396
402
  "result.push(" + self.emitTerm(body, async) + ");\n" +
@@ -404,7 +410,7 @@ extend self: JsEmitter {
404
410
  let d = dictionaryStrings.grabFirst()
405
411
  let asyncSuffix = if(await) {"$"} else {""}
406
412
  let n = escapeKeyword(name.reverse().takeWhile { _ != '.' }.reverse()) + asyncSuffix
407
- let emittedArguments = arguments.map{self.emitArgument(_, async)}
413
+ let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
408
414
  let controller = if(await) {["$task"]} else {[]}
409
415
  let call = d + "." + n + "(" + [...emittedArguments, ...ds, ...controller].join(", ") + ")"
410
416
  if(await) {"(await " + call + ")"} else {call}
@@ -414,7 +420,7 @@ extend self: JsEmitter {
414
420
  let await = async && effectTypeIsAsync(effect)
415
421
  let ds = dictionaries.map {self.emitDictionary(_)}
416
422
  let functionCode = escapeResolved(name) + if(await) {"$"} else {""}
417
- let emittedArguments = arguments.map {self.emitArgument(_, async)}
423
+ let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
418
424
  let controller = if(await) {["$task"]} else {[]}
419
425
  let call = functionCode + "(" + [...emittedArguments, ...ds, ...controller].join(", ") + ")"
420
426
  if(await) {"(await " + call + ")"} else {call}
@@ -433,7 +439,7 @@ extend self: JsEmitter {
433
439
  let await = async && effectTypeIsAsync(effect)
434
440
  if(!dictionaries.isEmpty()) {fail(at, "Internal error: Dictionaries in lambda call")}
435
441
  let functionCode = self.emitTerm(function, async)
436
- let emittedArguments = arguments.map {self.emitArgument(_, async)}
442
+ let emittedArguments = arguments.map {self.emitArgument(at, _, async)}
437
443
  let controller = if(await) {["$task"]} else {[]}
438
444
  let call = functionCode + "(" + [...emittedArguments, ...controller].join(", ") + ")"
439
445
  if(await) {"(await " + call + ")"} else {call}
@@ -500,7 +506,7 @@ extend self: JsEmitter {
500
506
  | ECall(at, StaticCall("ff:core/Core.doWhile", _, _), _, _, [Argument(_, _, doWhileBody)], _) {
501
507
  invokeImmediately(doWhileBody) | body
502
508
  } =>
503
- "while(!" + self.emitTerm(body, async) + ") {}"
509
+ "while(" + self.emitTerm(body, async) + ") {}"
504
510
  | ECall(at, StaticCall("ff:core/Core.if", _, _), _, _, [condition, body], _) =>
505
511
  "if(" + self.emitTerm(condition.value, async) + ") {\n" +
506
512
  if(last) {
@@ -511,7 +517,7 @@ extend self: JsEmitter {
511
517
  }
512
518
  | ECall(at, StaticCall("ff:core/Core.throw", _, _), _, _, [argument], [dictionary]) =>
513
519
  let d = self.emitDictionary(dictionary)
514
- let a = self.emitArgument(argument, async)
520
+ let a = self.emitArgument(at, argument, async)
515
521
  "throw Object.assign(new Error(), {ffException: ff_core_Any.toAny_(" + a + ", " + d + ")})"
516
522
  | ECall(at, StaticCall("ff:core/Core.try", _, _), _, _, _, _) {!last} =>
517
523
  throw(CompileError(at, "Statements can't be a try without a grab"))
@@ -769,8 +775,14 @@ extend self: JsEmitter {
769
775
  newtype
770
776
  }
771
777
 
772
- emitArgument(argument: Argument, async: Bool): String {
773
- self.emitTerm(argument.value, async)
778
+ emitArgument(callAt: Location, argument: Argument, async: Bool): String {
779
+ argument.value.{
780
+ | ECall(_, StaticCall("ff:core/SourceLocation.callSite", _, _), _, _, _, _) =>
781
+ "\"" + self.moduleName + ":" + callAt.line + ":" + callAt.column +
782
+ "," + self.packagePair.group + "," + self.packagePair.name + "\""
783
+ | value =>
784
+ self.emitTerm(value, async)
785
+ }
774
786
  }
775
787
 
776
788
  }
@@ -20,7 +20,7 @@ class ResolverState(
20
20
  mutable nextUnificationVariableIndex: Int
21
21
  )
22
22
 
23
- make(lspHook: LspHook): Resolver {
23
+ make(packagePair: PackagePair, moduleName: String, lspHook: LspHook): Resolver {
24
24
  Resolver(
25
25
  variables = [].toMap()
26
26
  variableLocations = [].toMap()
@@ -1,2 +1 @@
1
- package ff:core:0.0.0
2
- include "node_modules"
1
+ package ff:core:0.0.0
package/core/Box.ff ADDED
@@ -0,0 +1,7 @@
1
+ class Box[T](mutable value: T)
2
+
3
+ extend self[T]: Box[T] {
4
+ modify(body: T => T): Unit {
5
+ self.value = body(self.value)
6
+ }
7
+ }
@@ -48,14 +48,15 @@ extend self: BrowserCode {
48
48
 
49
49
  bundle(minify: Bool = True, sourceMap: Bool = False): BrowserBundle {
50
50
  let prefix = ".firefly/output/browser"
51
- let mainJsBaseFile = self.mainFile.absolute().removeLast(".ff").grab() + ".mjs"
51
+ let mainJsBaseFile = self.mainFile.base().removeLast(".ff").grab() + ".mjs"
52
52
  let mainJsFile = prefix + "/" + self.packageGroup + "/" + self.packageName + "/" + mainJsBaseFile
53
+ let mainDirectory = self.mainFile.parent().grab()
53
54
  let file = prefix + "/Main.bundle.js"
54
55
  internalCallEsBuild(self, mainJsFile = mainJsFile, outputPath = file, minify = minify, sourceMap = sourceMap)
55
56
  let assets = AssetSystem([
56
- Pair(file.dropFirst(prefix.size()), {self.mainFile.path(file).readStream()})
57
+ Pair(file.dropFirst(prefix.size()), {mainDirectory.path(file).readStream()})
57
58
  ...if(sourceMap) {[
58
- Pair(file.dropFirst(prefix.size()) + ".map", {self.mainFile.path(file + ".map").readStream()})
59
+ Pair(file.dropFirst(prefix.size()) + ".map", {mainDirectory.path(file + ".map").readStream()})
59
60
  ]} else {[]}
60
61
  ].toMap())
61
62
  BrowserBundle(assets)
@@ -124,7 +125,7 @@ internalListDirectory(path: Path): List[Pair[String, () => Stream[Buffer]]] {
124
125
  }
125
126
  }
126
127
  go(path).map {file =>
127
- Pair("/" + file.relativeTo(path), {file.readStream()})
128
+ Pair("/" + file.relativeTo(path).replace("\\", "/"), {file.readStream()})
128
129
  }.toList()
129
130
  }
130
131
 
package/core/Core.ff CHANGED
@@ -55,4 +55,8 @@ throwAny[T](exception: Any): T
55
55
  panic[T](message: String): T
56
56
  target js sync "throw new Error(message_)"
57
57
 
58
+
59
+ same[T](x: T, y: T): Bool
60
+ target js sync """return x_ === y_"""
61
+
58
62
  data GrabException()
@@ -27,7 +27,7 @@ extend self: HttpClient {
27
27
  try {
28
28
  const options = {headers: {}, signal: $task.controller.signal}
29
29
  options.method = method_
30
- ff_core_List.List_each(headers_, pair => {options.headers[pair.key_] = pair.value_})
30
+ ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
31
31
  if(body_.value_) options.body = body_.value_
32
32
  if(redirect_.RedirectError) options.redirect = "error"
33
33
  else if(redirect_.RedirectManual) options.redirect = "manual"
package/core/JsValue.ff CHANGED
@@ -26,6 +26,9 @@ extend self: JsValue {
26
26
  return self_
27
27
  """
28
28
 
29
+ equals[V: IsJsValue](value: V): Bool
30
+ target js sync "return self_ === value_"
31
+
29
32
  isString(): Bool
30
33
  target js sync "return typeof self_ === 'string'"
31
34
 
@@ -52,7 +55,7 @@ extend self: JsValue {
52
55
 
53
56
  isFunction(): Bool
54
57
  target js sync "return typeof self_ === 'function'"
55
-
58
+
56
59
  isNull(): Bool
57
60
  target js sync "return self_ === null"
58
61
 
@@ -80,6 +83,9 @@ extend self: JsValue {
80
83
  set[K: IsJsValue, V: IsJsValue](key: K, value: V): Unit
81
84
  target js sync "self_[key_] = value_"
82
85
 
86
+ delete[K: IsJsValue](key: K): Unit
87
+ target js sync "delete self_[key_]"
88
+
83
89
  with[K: IsJsValue, V: IsJsValue](key: K, value: V): JsValue
84
90
  target js sync "return {...self_, [key_]: value_}"
85
91
 
@@ -219,6 +225,19 @@ extend self: JsValue {
219
225
  grabMap(): Map[String, JsValue] {
220
226
  self.grabPairs().toMap()
221
227
  }
228
+
229
+ grabIntMap(): IntMap[JsValue]
230
+ target js sync """
231
+ if(!(self_ instanceof Map)) throw new Error('Expected map, got '+ typeof self_);;
232
+ return self_
233
+ """
234
+
235
+ grabStringMap(): StringMap[JsValue]
236
+ target js sync """
237
+ if(!(self_ instanceof Map)) throw new Error('Expected map, got '+ typeof self_);;
238
+ return self_
239
+ """
240
+
222
241
  //toArray(): Array[JsValue] // TODO
223
242
  //toList(): List[JsValue] // TODO
224
243
  // TODO: JS operators
@@ -233,6 +252,8 @@ instance Int: IsJsValue {}
233
252
  instance Float: IsJsValue {}
234
253
  instance Bool: IsJsValue {}
235
254
  instance Array[T: IsJsValue]: IsJsValue {}
255
+ instance IntMap[T: IsJsValue]: IsJsValue {}
256
+ instance StringMap[T: IsJsValue]: IsJsValue {}
236
257
  instance Error: IsJsValue {}
237
258
  instance Instant: IsJsValue {}
238
259
  instance Duration: IsJsValue {}
package/core/Lock.ff CHANGED
@@ -8,13 +8,13 @@ extend self: Lock {
8
8
  return {lock: self_, stack: [], queue: []}
9
9
  """
10
10
 
11
- acquire(): Unit
11
+ acquire(reentrant: Bool): Unit
12
12
  target js async """
13
13
  if(self_.level === 0) {
14
14
  self_.owner = $task
15
15
  self_.level += 1
16
16
  } else {
17
- if(self_.owner !== $task) {
17
+ if(self_.owner !== $task || !reentrant_) {
18
18
  try {
19
19
  await new Promise((resolve, reject) => {
20
20
  $task.controller.signal.addEventListener('abort', reject)
@@ -56,8 +56,8 @@ extend self: Lock {
56
56
  }
57
57
  """
58
58
 
59
- do[T](body: () => T): T {
60
- self.acquire()
59
+ do[T](reentrant: Bool, body: () => T): T {
60
+ self.acquire(reentrant)
61
61
  try {
62
62
  body()
63
63
  } finally {
@@ -8,12 +8,13 @@ extend self: NodeSystem {
8
8
  assets(): AssetSystem {
9
9
  let assetPkgSnapshotPath = self.path("/snapshot/output/assets")
10
10
  if(assetPkgSnapshotPath.isDirectory()) {
11
+ // Opendir gives ENOENT: no such file or directory, opendir '/snapshot/output/assets'
11
12
  function streams(path: Path): Stream[Pair[String, () => Stream[Buffer]]] {
12
- path.entries().flatMap {file =>
13
+ internalListDirectoryWithoutOpendir(self, path).toStream().flatMap {file =>
13
14
  if(file.isDirectory()) {
14
- streams(file.path())
15
+ streams(file)
15
16
  } else {
16
- [Pair(file.path().relativeTo(assetPkgSnapshotPath), {file.path().readStream()})].toStream()
17
+ [Pair("/" + file.relativeTo(assetPkgSnapshotPath), {file.readStream()})].toStream()
17
18
  }
18
19
  }
19
20
  }
@@ -86,3 +87,11 @@ extend self: NodeSystem {
86
87
 
87
88
  internalAssets(system: NodeSystem): AssetSystem
88
89
  target node async "return system_.assets_"
90
+
91
+ internalListDirectoryWithoutOpendir(system: NodeSystem, path: Path): Array[Path]
92
+ target node async """
93
+ import * as fsPromises from 'fs/promises'
94
+ import * as path from 'path'
95
+ let files = await fsPromises.readdir(path_)
96
+ return files.map(file => path.join(path_, file))
97
+ """
package/core/Path.ff CHANGED
@@ -93,7 +93,7 @@ extend self: Path {
93
93
  return ff_core_Option.Some(entry)
94
94
  },
95
95
  async () => {
96
- await dir.close()
96
+ if(dir !== null) await dir.close()
97
97
  }
98
98
  )
99
99
  """
@@ -0,0 +1,41 @@
1
+ newtype SourceLocation(location: String) // E.g. "directory/Module:line:column,group,package>Type>function"
2
+
3
+ here(location: SourceLocation = SourceLocation.callSite()): SourceLocation {
4
+ location
5
+ }
6
+
7
+ callSite(): SourceLocation {
8
+ panic("SourceLocation.callSite() can only occur as the default value of an argument")
9
+ }
10
+
11
+ extend self: SourceLocation {
12
+
13
+ group(): String {
14
+ self.location.dropWhile {_ != ','}.dropFirst().takeWhile {_ != ','}
15
+ }
16
+
17
+ package(): String {
18
+ self.location.dropWhile {_ != ','}.dropFirst().dropWhile {_ != ','}.dropFirst().takeWhile {_ != '>'}
19
+ }
20
+
21
+ directory(): String {
22
+ self.location.takeWhile {_ != ':'}.reverse().dropWhile {_ != '/'}.dropFirst().reverse()
23
+ }
24
+
25
+ module(): String {
26
+ self.location.takeWhile {_ != ':'}.reverse().takeWhile {_ != '/'}.reverse()
27
+ }
28
+
29
+ line(): Int {
30
+ self.location.dropWhile {_ != ':'}.dropFirst().takeWhile {_ != ':'}.grabInt()
31
+ }
32
+
33
+ column(): Int {
34
+ self.location.dropWhile {_ != ':'}.dropFirst().dropWhile {_ != ':'}.dropFirst().takeWhile {_ != ','}.grabInt()
35
+ }
36
+
37
+ breadcrumbs(): Array[String] {
38
+ self.location.dropWhile {_ != '>'}.dropFirst().split('>')
39
+ }
40
+
41
+ }