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.
- package/.hintrc +5 -0
- package/README.md +42 -61
- package/compiler/Compiler.ff +6 -2
- package/compiler/Deriver.ff +10 -0
- package/compiler/Environment.ff +2 -1
- package/compiler/JsEmitter.ff +29 -17
- package/compiler/Resolver.ff +1 -1
- package/core/.firefly/package.ff +1 -2
- package/core/Box.ff +7 -0
- package/core/BuildSystem.ff +5 -4
- package/core/Core.ff +4 -0
- package/core/HttpClient.ff +1 -1
- package/core/JsValue.ff +22 -1
- package/core/Lock.ff +4 -4
- package/core/NodeSystem.ff +12 -3
- package/core/Path.ff +1 -1
- package/core/SourceLocation.ff +41 -0
- package/core/Stack.ff +6 -6
- package/core/StringMap.ff +3 -3
- package/lsp/LanguageServer.ff +32 -7
- package/lsp/stderr.txt +1 -0
- package/lsp/stdin.txt +11 -0
- package/lsp/stdout.txt +41 -0
- package/lux/.firefly/package.ff +2 -0
- package/lux/Lux.ff +473 -0
- package/lux/LuxEvent.ff +116 -0
- package/lux/Main.ff +126 -0
- package/lux/Main2.ff +144 -0
- package/meetup/AutoCompletion.ff +6 -0
- package/output/js/ff/compiler/Builder.mjs +444 -440
- package/output/js/ff/compiler/Compiler.mjs +416 -410
- package/output/js/ff/compiler/Dependencies.mjs +389 -385
- package/output/js/ff/compiler/Deriver.mjs +1170 -1166
- package/output/js/ff/compiler/Dictionaries.mjs +1309 -1305
- package/output/js/ff/compiler/Environment.mjs +1015 -1005
- package/output/js/ff/compiler/Inference.mjs +4268 -4264
- package/output/js/ff/compiler/JsEmitter.mjs +5391 -5353
- package/output/js/ff/compiler/JsImporter.mjs +266 -262
- package/output/js/ff/compiler/LspHook.mjs +793 -789
- package/output/js/ff/compiler/Main.mjs +1699 -1695
- package/output/js/ff/compiler/Parser.mjs +4008 -4004
- package/output/js/ff/compiler/Patterns.mjs +927 -923
- package/output/js/ff/compiler/Resolver.mjs +2307 -2303
- package/output/js/ff/compiler/Substitution.mjs +1150 -1146
- package/output/js/ff/compiler/Syntax.mjs +12434 -12430
- package/output/js/ff/compiler/Token.mjs +3096 -3092
- package/output/js/ff/compiler/Tokenizer.mjs +593 -589
- package/output/js/ff/compiler/Unification.mjs +1752 -1748
- package/output/js/ff/compiler/Wildcards.mjs +608 -604
- package/output/js/ff/compiler/Workspace.mjs +687 -683
- package/output/js/ff/core/Any.mjs +143 -139
- package/output/js/ff/core/Array.mjs +547 -543
- package/output/js/ff/core/AssetSystem.mjs +274 -270
- package/output/js/ff/core/Atomic.mjs +154 -150
- package/output/js/ff/core/Bool.mjs +152 -141
- package/output/js/ff/core/Box.mjs +112 -0
- package/output/js/ff/core/BrowserSystem.mjs +126 -122
- package/output/js/ff/core/Buffer.mjs +395 -391
- package/output/js/ff/core/BuildSystem.mjs +296 -290
- package/output/js/ff/core/Channel.mjs +189 -185
- package/output/js/ff/core/Char.mjs +149 -145
- package/output/js/ff/core/Core.mjs +300 -288
- package/output/js/ff/core/Duration.mjs +116 -112
- package/output/js/ff/core/Equal.mjs +179 -175
- package/output/js/ff/core/Error.mjs +142 -138
- package/output/js/ff/core/FileHandle.mjs +150 -146
- package/output/js/ff/core/Float.mjs +225 -214
- package/output/js/ff/core/HttpClient.mjs +190 -186
- package/output/js/ff/core/Instant.mjs +109 -105
- package/output/js/ff/core/Int.mjs +265 -254
- package/output/js/ff/core/IntMap.mjs +280 -276
- package/output/js/ff/core/JsSystem.mjs +238 -234
- package/output/js/ff/core/JsValue.mjs +714 -664
- package/output/js/ff/core/List.mjs +2334 -2321
- package/output/js/ff/core/Lock.mjs +230 -226
- package/output/js/ff/core/Log.mjs +163 -159
- package/output/js/ff/core/Map.mjs +362 -358
- package/output/js/ff/core/NodeSystem.mjs +302 -283
- package/output/js/ff/core/Nothing.mjs +104 -100
- package/output/js/ff/core/Option.mjs +1015 -1002
- package/output/js/ff/core/Ordering.mjs +730 -726
- package/output/js/ff/core/Pair.mjs +331 -318
- package/output/js/ff/core/Path.mjs +546 -542
- package/output/js/ff/core/RbMap.mjs +1940 -1936
- package/output/js/ff/core/Serializable.mjs +428 -424
- package/output/js/ff/core/Set.mjs +254 -250
- package/output/js/ff/core/Show.mjs +205 -201
- package/output/js/ff/core/SourceLocation.mjs +229 -0
- package/output/js/ff/core/Stack.mjs +541 -537
- package/output/js/ff/core/Stream.mjs +1304 -1300
- package/output/js/ff/core/String.mjs +365 -354
- package/output/js/ff/core/StringMap.mjs +280 -276
- package/output/js/ff/core/Task.mjs +320 -316
- package/output/js/ff/core/Try.mjs +507 -503
- package/output/js/ff/core/Unit.mjs +151 -103
- package/package.json +29 -29
- package/vscode/package-lock.json +5 -5
- package/vscode/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,96 +1,77 @@
|
|
|
1
1
|
# Firefly programming language
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A straightforward language for full-stack development.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
12
|
+
*This is a preview. Please take it for a ride and tell us what you think!*
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
## Writing your first webapp
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang).
|
|
13
17
|
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
+
```npm install -g firefly-compiler```
|
|
41
33
|
|
|
42
|
-
|
|
34
|
+
Build executables for Linux, MacOS and Windows:
|
|
43
35
|
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
+
```firefly WebApp.ff```
|
|
54
43
|
|
|
55
|
-
|
|
44
|
+
## Connecting to PostgreSQL
|
|
56
45
|
|
|
57
|
-
|
|
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
|
-
|
|
50
|
+
And import the `Pg` module from it:
|
|
64
51
|
|
|
65
|
-
|
|
52
|
+
```import Pg from ff:postgresql```
|
|
66
53
|
|
|
67
|
-
|
|
54
|
+
Create a connection pool with the appropriate connection parameters:
|
|
68
55
|
|
|
69
|
-
|
|
56
|
+
```let pool = Pg.makePool(...)```
|
|
70
57
|
|
|
71
|
-
|
|
58
|
+
And run your first transaction, e.g.:
|
|
72
59
|
|
|
73
|
-
Example (all lines optional):
|
|
74
60
|
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
## Workspaces
|
|
71
|
+
## What's next
|
|
83
72
|
|
|
84
|
-
|
|
73
|
+
For now, you're on your own! Lots of things you'll be missing. We're working on it.
|
|
85
74
|
|
|
86
|
-
|
|
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
|
|
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.
|
package/compiler/Compiler.ff
CHANGED
|
@@ -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
|
|
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")
|
package/compiler/Deriver.ff
CHANGED
|
@@ -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)
|
package/compiler/Environment.ff
CHANGED
|
@@ -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 = [...
|
|
99
|
+
constraints = [...outerConstraints, ...method.signature.constraints]
|
|
99
100
|
parameters = [selfParameter, ...method.signature.parameters]
|
|
100
101
|
))
|
|
101
102
|
)
|
package/compiler/JsEmitter.ff
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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
|
}
|
package/compiler/Resolver.ff
CHANGED
|
@@ -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()
|
package/core/.firefly/package.ff
CHANGED
|
@@ -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
package/core/BuildSystem.ff
CHANGED
|
@@ -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.
|
|
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()), {
|
|
57
|
+
Pair(file.dropFirst(prefix.size()), {mainDirectory.path(file).readStream()})
|
|
57
58
|
...if(sourceMap) {[
|
|
58
|
-
Pair(file.dropFirst(prefix.size()) + ".map", {
|
|
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
package/core/HttpClient.ff
CHANGED
|
@@ -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.
|
|
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 {
|
package/core/NodeSystem.ff
CHANGED
|
@@ -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.
|
|
13
|
+
internalListDirectoryWithoutOpendir(self, path).toStream().flatMap {file =>
|
|
13
14
|
if(file.isDirectory()) {
|
|
14
|
-
streams(file
|
|
15
|
+
streams(file)
|
|
15
16
|
} else {
|
|
16
|
-
[Pair(file.
|
|
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
|
@@ -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
|
+
}
|