firefly-compiler 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.firefly-workspace +1 -0
  2. package/.vscode/settings.json +5 -0
  3. package/LICENSE.txt +21 -0
  4. package/README.md +96 -0
  5. package/bin/firefly.mjs +2 -0
  6. package/compiler/.firefly/package.ff +1 -0
  7. package/compiler/Builder.ff +218 -0
  8. package/compiler/Compiler.ff +241 -0
  9. package/compiler/Dependencies.ff +179 -0
  10. package/compiler/Deriver.ff +647 -0
  11. package/compiler/Dictionaries.ff +205 -0
  12. package/compiler/Environment.ff +166 -0
  13. package/compiler/Inference.ff +1117 -0
  14. package/compiler/JsEmitter.ff +861 -0
  15. package/compiler/JsImporter.ff +56 -0
  16. package/compiler/LspHook.ff +188 -0
  17. package/compiler/Main.ff +237 -0
  18. package/compiler/Parser.ff +1383 -0
  19. package/compiler/Patterns.ff +111 -0
  20. package/compiler/Resolver.ff +620 -0
  21. package/compiler/Substitution.ff +178 -0
  22. package/compiler/Syntax.ff +299 -0
  23. package/compiler/Token.ff +180 -0
  24. package/compiler/Tokenizer.ff +278 -0
  25. package/compiler/Unification.ff +220 -0
  26. package/compiler/Wildcards.ff +50 -0
  27. package/compiler/Workspace.ff +88 -0
  28. package/core/.firefly/package.ff +2 -0
  29. package/core/Any.ff +30 -0
  30. package/core/Array.ff +249 -0
  31. package/core/AssetSystem.ff +61 -0
  32. package/core/Atomic.ff +64 -0
  33. package/core/Bool.ff +13 -0
  34. package/core/BrowserSystem.ff +14 -0
  35. package/core/Buffer.ff +211 -0
  36. package/core/BuildSystem.ff +144 -0
  37. package/core/Channel.ff +131 -0
  38. package/core/Char.ff +18 -0
  39. package/core/Core.ff +58 -0
  40. package/core/Duration.ff +15 -0
  41. package/core/Equal.ff +52 -0
  42. package/core/Error.ff +20 -0
  43. package/core/FileHandle.ff +41 -0
  44. package/core/Float.ff +41 -0
  45. package/core/HttpClient.ff +84 -0
  46. package/core/Instant.ff +9 -0
  47. package/core/Int.ff +61 -0
  48. package/core/IntMap.ff +85 -0
  49. package/core/JsSystem.ff +66 -0
  50. package/core/JsValue.ff +240 -0
  51. package/core/List.ff +440 -0
  52. package/core/Lock.ff +144 -0
  53. package/core/Log.ff +24 -0
  54. package/core/Map.ff +126 -0
  55. package/core/NodeSystem.ff +88 -0
  56. package/core/Nothing.ff +1 -0
  57. package/core/Option.ff +133 -0
  58. package/core/Ordering.ff +157 -0
  59. package/core/Pair.ff +55 -0
  60. package/core/Path.ff +393 -0
  61. package/core/RbMap.ff +216 -0
  62. package/core/Serializable.ff +173 -0
  63. package/core/Set.ff +38 -0
  64. package/core/Show.ff +43 -0
  65. package/core/Stack.ff +263 -0
  66. package/core/Stream.ff +406 -0
  67. package/core/String.ff +175 -0
  68. package/core/StringMap.ff +85 -0
  69. package/core/Task.ff +138 -0
  70. package/core/Try.ff +81 -0
  71. package/core/Unit.ff +3 -0
  72. package/experimental/random/AltGeneric.ff +44 -0
  73. package/experimental/random/Async.ff +68 -0
  74. package/experimental/random/Buffer2.ff +77 -0
  75. package/experimental/random/Cat.ff +12 -0
  76. package/experimental/random/Dictionary.ff +52 -0
  77. package/experimental/random/Example.ff +46 -0
  78. package/experimental/random/Generic.ff +102 -0
  79. package/experimental/random/HappyEyeballs.ff +40 -0
  80. package/experimental/random/HashMap.ff +72 -0
  81. package/experimental/random/IfElseUnit.ff +9 -0
  82. package/experimental/random/InputOutput.ff +23 -0
  83. package/experimental/random/ListVsArray.ff +45 -0
  84. package/experimental/random/Main.ff +44 -0
  85. package/experimental/random/MapTest.ff +67 -0
  86. package/experimental/random/OldTaskSystem.ff +210 -0
  87. package/experimental/random/PatternTest.ff +39 -0
  88. package/experimental/random/Patterns.ff +226 -0
  89. package/experimental/random/ReadBytesTest.ff +10 -0
  90. package/experimental/random/RunLength.ff +65 -0
  91. package/experimental/random/Scrape.ff +51 -0
  92. package/experimental/random/Serialization.ff +217 -0
  93. package/experimental/random/SerializationTest.ff +46 -0
  94. package/experimental/random/Serializer.ff +36 -0
  95. package/experimental/random/StdInOutErr.ff +4 -0
  96. package/experimental/random/Symbols.ff +74 -0
  97. package/experimental/random/Tag.ff +49 -0
  98. package/experimental/random/Tensor.ff +52 -0
  99. package/experimental/random/Try.ff +56 -0
  100. package/experimental/random/Tsv.ff +9 -0
  101. package/experimental/random/TypesAreModules.ff +87 -0
  102. package/experimental/random/blueprints/Blueprint.ff +52 -0
  103. package/experimental/random/blueprints/Main.ff +11 -0
  104. package/experimental/random/blueprints/Pretty.ff +58 -0
  105. package/experimental/random/blueprints/User.ff +64 -0
  106. package/experimental/random/blueprintsystem/BlueprintSystem.ff +48 -0
  107. package/experimental/random/blueprintsystem/Deserialize.ff +53 -0
  108. package/experimental/random/blueprintsystem/ReadJs.ff +13 -0
  109. package/experimental/random/blueprintsystem/User.ff +2 -0
  110. package/experimental/random/kahrs/Kahrs.ff +112 -0
  111. package/experimental/random/kahrs/TestKahrs.ff +22 -0
  112. package/experimental/random/kahrs/TestMap.ff +18 -0
  113. package/experimental/random/streaming/Gzip.ff +3 -0
  114. package/experimental/random/streaming/Main.ff +34 -0
  115. package/experimental/random/streaming/S3Bucket.ff +11 -0
  116. package/experimental/random/streaming/Tar.ff +5 -0
  117. package/experimental/rhymeapp/Main.ff +81 -0
  118. package/experimental/rhymeapp/index.html +14 -0
  119. package/firefly.sh +5 -0
  120. package/fireflysite/Main.ff +13 -0
  121. package/httpserver/.firefly/package.ff +1 -0
  122. package/httpserver/HttpServer.ff +184 -0
  123. package/lsp/.firefly/package.ff +1 -0
  124. package/lsp/CompletionHandler.ff +814 -0
  125. package/lsp/Handler.ff +551 -0
  126. package/lsp/HoverHandler.ff +82 -0
  127. package/lsp/LanguageServer.ff +229 -0
  128. package/lsp/SignatureHelpHandler.ff +55 -0
  129. package/lsp/SymbolHandler.ff +167 -0
  130. package/output/js/ff/compiler/Builder.mjs +483 -0
  131. package/output/js/ff/compiler/Compiler.mjs +410 -0
  132. package/output/js/ff/compiler/Dependencies.mjs +388 -0
  133. package/output/js/ff/compiler/Deriver.mjs +1166 -0
  134. package/output/js/ff/compiler/Dictionaries.mjs +1305 -0
  135. package/output/js/ff/compiler/Environment.mjs +1005 -0
  136. package/output/js/ff/compiler/Inference.mjs +4264 -0
  137. package/output/js/ff/compiler/JsEmitter.mjs +5353 -0
  138. package/output/js/ff/compiler/JsImporter.mjs +262 -0
  139. package/output/js/ff/compiler/LspHook.mjs +789 -0
  140. package/output/js/ff/compiler/Main.mjs +1695 -0
  141. package/output/js/ff/compiler/Parser.mjs +4004 -0
  142. package/output/js/ff/compiler/Patterns.mjs +923 -0
  143. package/output/js/ff/compiler/Resolver.mjs +2303 -0
  144. package/output/js/ff/compiler/Substitution.mjs +1146 -0
  145. package/output/js/ff/compiler/Syntax.mjs +12430 -0
  146. package/output/js/ff/compiler/Token.mjs +3092 -0
  147. package/output/js/ff/compiler/Tokenizer.mjs +589 -0
  148. package/output/js/ff/compiler/Unification.mjs +1748 -0
  149. package/output/js/ff/compiler/Wildcards.mjs +604 -0
  150. package/output/js/ff/compiler/Workspace.mjs +683 -0
  151. package/output/js/ff/core/Any.mjs +139 -0
  152. package/output/js/ff/core/Array.mjs +594 -0
  153. package/output/js/ff/core/AssetSystem.mjs +270 -0
  154. package/output/js/ff/core/Atomic.mjs +186 -0
  155. package/output/js/ff/core/Bool.mjs +141 -0
  156. package/output/js/ff/core/BrowserSystem.mjs +122 -0
  157. package/output/js/ff/core/Buffer.mjs +467 -0
  158. package/output/js/ff/core/BuildSystem.mjs +320 -0
  159. package/output/js/ff/core/Channel.mjs +268 -0
  160. package/output/js/ff/core/Char.mjs +145 -0
  161. package/output/js/ff/core/Core.mjs +300 -0
  162. package/output/js/ff/core/Duration.mjs +112 -0
  163. package/output/js/ff/core/Equal.mjs +175 -0
  164. package/output/js/ff/core/Error.mjs +138 -0
  165. package/output/js/ff/core/FileHandle.mjs +164 -0
  166. package/output/js/ff/core/Float.mjs +214 -0
  167. package/output/js/ff/core/HttpClient.mjs +210 -0
  168. package/output/js/ff/core/Instant.mjs +105 -0
  169. package/output/js/ff/core/Int.mjs +254 -0
  170. package/output/js/ff/core/IntMap.mjs +282 -0
  171. package/output/js/ff/core/JsSystem.mjs +234 -0
  172. package/output/js/ff/core/JsValue.mjs +678 -0
  173. package/output/js/ff/core/List.mjs +2335 -0
  174. package/output/js/ff/core/Lock.mjs +322 -0
  175. package/output/js/ff/core/Log.mjs +159 -0
  176. package/output/js/ff/core/Map.mjs +358 -0
  177. package/output/js/ff/core/NodeSystem.mjs +288 -0
  178. package/output/js/ff/core/Nothing.mjs +100 -0
  179. package/output/js/ff/core/Option.mjs +1002 -0
  180. package/output/js/ff/core/Ordering.mjs +734 -0
  181. package/output/js/ff/core/Pair.mjs +318 -0
  182. package/output/js/ff/core/Path.mjs +768 -0
  183. package/output/js/ff/core/RbMap.mjs +1936 -0
  184. package/output/js/ff/core/Serializable.mjs +434 -0
  185. package/output/js/ff/core/Set.mjs +250 -0
  186. package/output/js/ff/core/Show.mjs +201 -0
  187. package/output/js/ff/core/Stack.mjs +595 -0
  188. package/output/js/ff/core/Stream.mjs +1300 -0
  189. package/output/js/ff/core/String.mjs +433 -0
  190. package/output/js/ff/core/StringMap.mjs +282 -0
  191. package/output/js/ff/core/Task.mjs +345 -0
  192. package/output/js/ff/core/Try.mjs +503 -0
  193. package/output/js/ff/core/Unit.mjs +103 -0
  194. package/package.json +29 -0
  195. package/postgresql/.firefly/include/package-lock.json +250 -0
  196. package/postgresql/.firefly/include/package.json +5 -0
  197. package/postgresql/.firefly/include/prepare.sh +2 -0
  198. package/postgresql/.firefly/package.ff +3 -0
  199. package/postgresql/Pg.ff +530 -0
  200. package/unsafejs/.firefly/package.ff +1 -0
  201. package/unsafejs/UnsafeJs.ff +19 -0
  202. package/vscode/.vscode/launch.json +18 -0
  203. package/vscode/.vscode/tasks.json +33 -0
  204. package/vscode/LICENSE.txt +21 -0
  205. package/vscode/Prepublish.ff +15 -0
  206. package/vscode/README.md +17 -0
  207. package/vscode/client/package-lock.json +544 -0
  208. package/vscode/client/package.json +22 -0
  209. package/vscode/client/src/extension.ts +64 -0
  210. package/vscode/client/tsconfig.json +12 -0
  211. package/vscode/icons/firefly-icon.png +0 -0
  212. package/vscode/icons/firefly-icon.svg +10 -0
  213. package/vscode/icons/firefly-logo-notext.png +0 -0
  214. package/vscode/icons/firefly-logo.png +0 -0
  215. package/vscode/language-configuration.json +39 -0
  216. package/vscode/package-lock.json +3623 -0
  217. package/vscode/package.json +144 -0
  218. package/vscode/snippets-none.json +1 -0
  219. package/vscode/snippets.json +241 -0
  220. package/vscode/syntaxes/firefly.tmLanguage.json +294 -0
  221. package/vscode/tsconfig.json +20 -0
@@ -0,0 +1 @@
1
+ ff:* .
@@ -0,0 +1,5 @@
1
+ {
2
+ "editor.suggest.showWords": false,
3
+ "firefly.trace.server": "messages",
4
+ "editor.trimAutoWhitespace": false
5
+ }
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2023 Joakim Ahnfelt-Rønne, Michael Werk Ravnsmed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Firefly programming language
2
+
3
+ [Get the VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang)
4
+
5
+ ## About the compiler
6
+ This is a bootstrap transpiler for converting Firefly code to JavaScript code.
7
+
8
+ Status: Firefly has been bootstrapped and now transpiles itself into JavaScript.
9
+
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.
11
+
12
+ [See an example webapp](https://github.com/Ahnfelt/firefly-boot/blob/master/experimental/random/rhymeapp/Main.ff)
13
+
14
+ **Async/await inference:** https://www.ahnfelt.net/async-await-inference-in-firefly/
15
+
16
+
17
+ ## Running a main file
18
+
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()`.
29
+
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.
33
+
34
+ ## Building an executable
35
+
36
+ ```
37
+ firefly.sh build MyApp.ff
38
+ ```
39
+
40
+ *You must stand in the project directory when running this command.*
41
+
42
+ This will generate a stand-alone executable file `MyApp` with the necessary assets and dependencies, compatible with most linux distributions, windows and osx.
43
+
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()`.
46
+
47
+ ## Building for the browser
48
+
49
+ ```
50
+ firefly.sh browser MyApp.ff
51
+ ```
52
+
53
+ *You must stand in the project directory when running this command.*
54
+
55
+ This generates a single, minified `MyApp.min.js`, compatible with all modern browsers (ES6+).
56
+
57
+ ## Bootstrapping the compiler
58
+
59
+ ```
60
+ firefly.sh bootstrap
61
+ ```
62
+
63
+ *You must stand in the project directory when running this command.*
64
+
65
+ If you botch the compiler, just roll back the output directory and try again.
66
+
67
+ ## Dependencies and imports
68
+
69
+ You may list your dependencies either in the top of the main file or in `.firefly/package.ff`.
70
+
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.
72
+
73
+ Example (all lines optional):
74
+ ```
75
+ package john:stuff:0.1.7
76
+ dependency anne:goodies:2.1.0
77
+ dependency chris:images:0.7.0
78
+ ```
79
+
80
+ You don't have to manually install dependencies - they will be automatically downloaded when necessary.
81
+
82
+ ## Workspaces
83
+
84
+ You may place a `.firefly-workspace` file somewhere in a parent directory.
85
+
86
+ The workspace file configures where source code is fetched from.
87
+
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
+ ```
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import * as firefly from '../output/js/ff/compiler/Main.mjs';
@@ -0,0 +1 @@
1
+ package ff:compiler:0.0.0
@@ -0,0 +1,218 @@
1
+ import Tokenizer
2
+ import Parser
3
+ import Syntax
4
+ import Resolver
5
+ import Compiler
6
+ import Unification
7
+ import Dependencies
8
+ import JsEmitter
9
+ import LspHook
10
+
11
+ build(
12
+ system: NodeSystem
13
+ emitTarget: EmitTarget
14
+ mainPackage: PackagePair
15
+ mainModule: String
16
+ resolvedDependencies: ResolvedDependencies
17
+ compilerModulePath: Option[Path]
18
+ tempPath: Path
19
+ jsOutputPath: Path
20
+ printMeasurements: Bool
21
+ ): Unit {
22
+
23
+ if(tempPath.exists()) {tempPath.delete()}
24
+ tempPath.createDirectory()
25
+
26
+ let jsPathFile = tempPath.slash("js")
27
+ jsPathFile.createDirectory(createParentDirectories = True)
28
+
29
+ let success = do {
30
+ let compiler = Compiler.make(
31
+ emitTarget
32
+ system.mainTask()
33
+ compilerModulePath
34
+ jsPathFile
35
+ resolvedDependencies
36
+ Map.empty()
37
+ lspHook = LspHook.disabled()
38
+ )
39
+ compiler.emit(mainPackage, mainModule, isMainModule = True)
40
+ if(printMeasurements) {compiler.printMeasurements()}
41
+ resolvedDependencies.packagePaths.each {packagePair, packagePath =>
42
+ resolvedDependencies.packages.get(packagePair).each {packageInfo =>
43
+ processIncludes(jsPathFile, packagePath, packageInfo)
44
+ }
45
+ }
46
+ True
47
+ }
48
+
49
+ if(success) {
50
+ if(jsOutputPath.exists()) {jsOutputPath.delete()}
51
+ jsPathFile.renameTo(jsOutputPath)
52
+ }
53
+
54
+ }
55
+
56
+ processIncludes(jsPathFile: Path, packagePath: Path, info: PackageInfo): Unit {
57
+ info.includes.each {include =>
58
+ let fromPath = packagePath.slash(".firefly").slash("include").slash(include.path)
59
+ let toPath = jsPathFile.slash(info.package.packagePair.groupName("/")).slash(include.path)
60
+ fromPath.copyTo(toPath)
61
+ }
62
+ }
63
+
64
+ buildViaBuildSystem(system: NodeSystem, fireflyPath: Path, mainFile: String, target: String) {
65
+ let resolvedDependencies = Dependencies.process(system.httpClient(), system.path(mainFile))
66
+ let fixedPackagePaths = if(resolvedDependencies.packagePaths.contains(PackagePair("ff", "core"))) {
67
+ resolvedDependencies.packagePaths
68
+ } else {
69
+ resolvedDependencies.packagePaths.add(PackagePair("ff", "core"), fireflyPath.slash("core"))
70
+ }
71
+ if(target != "browser") {
72
+ panic("buildViaBuildSystem is currently limited to browser target only - the restriction can be lifted")
73
+ }
74
+ build(
75
+ system = system
76
+ emitTarget = EmitBrowser
77
+ mainPackage = resolvedDependencies.mainPackagePair
78
+ mainModule = mainFile.dropLast(".ff".size())
79
+ resolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
80
+ compilerModulePath = None
81
+ tempPath = system.path(".firefly/temporary")
82
+ jsOutputPath = system.path(".firefly/output").slash(target)
83
+ printMeasurements = False
84
+ )
85
+ }
86
+
87
+ check(
88
+ system: NodeSystem
89
+ fireflyPath: Path
90
+ path: Path
91
+ virtualFiles: Map[String, String]
92
+ lspHook: LspHook
93
+ infer: Bool
94
+ ) {
95
+ let packages = path.isDirectory().{
96
+ | True => findPackageFiles(path)
97
+ | False {path.endsWith([".firefly", "package.ff"])} => [PackageFiles(path.parent().grab(), Some(path), [])]
98
+ | False => [PackageFiles(path.parent().grab(), None, [path])]
99
+ }
100
+
101
+ packages.filter {!_.files.isEmpty()}.each {package =>
102
+ let firstFile = package.files.grabFirst()
103
+ let resolvedDependencies = Dependencies.process(system.httpClient(), firstFile)
104
+ let fixedPackagePaths = if(resolvedDependencies.packagePaths.contains(PackagePair("ff", "core"))) {
105
+ resolvedDependencies.packagePaths
106
+ } else {
107
+ resolvedDependencies.packagePaths.add(PackagePair("ff", "core"), fireflyPath.slash("core"))
108
+ }
109
+ let fixedResolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
110
+ let compiler = Compiler.make(
111
+ EmitBuild
112
+ system.mainTask()
113
+ None
114
+ package.root.slash(".firefly").slash("temporary")
115
+ fixedResolvedDependencies
116
+ virtualFiles
117
+ lspHook
118
+ )
119
+ package.files.each {file =>
120
+ let localFile = file.base()
121
+ if(infer) {
122
+ compiler.infer(resolvedDependencies.mainPackagePair, localFile.dropLast(".ff".size()))
123
+ } else {
124
+ compiler.resolve(resolvedDependencies.mainPackagePair, localFile.dropLast(".ff".size()))
125
+ }
126
+ }
127
+ }
128
+
129
+ }
130
+
131
+ capability PackageFiles(
132
+ root: Path
133
+ packageFile: Option[Path]
134
+ files: List[Path]
135
+ )
136
+
137
+
138
+ findPackageFiles(path: Path): List[PackageFiles] {
139
+ let files = findFireflyFiles(path)
140
+ let split = files.partition {_.endsWith([".firefly", "package.ff"])}
141
+ let packageFiles = split.first
142
+ mutable singleFiles = split.second
143
+ let multiFileProjects = packageFiles.map {packageFile =>
144
+ let projectRoot = packageFile.parent().grab().parent().grab()
145
+ let files = singleFiles.partition {_.isInsideOf(projectRoot)}
146
+ singleFiles = files.second
147
+ PackageFiles(projectRoot, Some(packageFile), files.first)
148
+ }
149
+ let singleFileProjects = singleFiles.map {file =>
150
+ let projectRoot = file.parent().grab()
151
+ PackageFiles(projectRoot, None, [file])
152
+ }
153
+ [...multiFileProjects, ...singleFileProjects]
154
+ }
155
+
156
+ findFireflyFiles(path: Path): List[Path] {
157
+ let split = path.entries().toList().partition {_.isDirectory()}
158
+ let directories = split.first.map {_.path()}.filter {_.base().all {c =>
159
+ c == '.' || c.isAsciiLower() || c.isAsciiDigit()
160
+ }}
161
+ let fireflyFiles = split.second.map {_.path()}.filter {
162
+ _.extension() == ".ff"
163
+ }
164
+ [...fireflyFiles, ...directories.flatMap {findFireflyFiles(_)}]
165
+ }
166
+
167
+ internalCreateExecutable(
168
+ self: BuildSystem
169
+ mainJsFile: Path = ".firefly/output/executable/Main.bundle.js"
170
+ outputPath: Path = ".firefly/output"
171
+ targets: List[String] = ["host"]
172
+ assets: AssetSystem = AssetSystem.create()
173
+ ): Unit
174
+ target node {
175
+ let assetOutputPath = outputPath.slash("assets")
176
+ assets.files.pairs().each {| Pair(path, makeStream) =>
177
+ let p = assetOutputPath.slash(path)
178
+ p.parent().grab().createDirectory(createParentDirectories = True)
179
+ p.writeStream(makeStream())
180
+ }
181
+ let json = """{
182
+ "name": "main",
183
+ "bin": {
184
+ "firefly-main": "Main.bundle.js"
185
+ },
186
+ "devDependencies": {
187
+ "pkg": "^5.8.0"
188
+ },
189
+ "pkg": {
190
+ "scripts": "Main.bundle.js",
191
+ "outputPath": "bin",
192
+ "assets": ["../assets/**/*"],
193
+ "targets": [
194
+ "node18-linux-x64",
195
+ "node18-macos-x64",
196
+ "node18-win-x64"
197
+ ]
198
+ }
199
+ }"""
200
+ let packageFile = outputPath.slash("executable/package.json")
201
+ packageFile.writeText(json)
202
+ internalCallPkg(self, packageFile, outputPath, targets)
203
+ }
204
+
205
+ internalCallPkg(
206
+ self: BuildSystem
207
+ packageFile: Path
208
+ outputPath: Path
209
+ targets: List[String]
210
+ ): Unit
211
+ target node async """
212
+ import * as pkg from 'pkg'
213
+ return await pkg.exec([
214
+ packageFile_,
215
+ '--out-path', outputPath_,
216
+ '--target', ff_core_List.List_toArray(targets_).join(',')
217
+ ])
218
+ """
@@ -0,0 +1,241 @@
1
+ import Syntax
2
+ import Tokenizer
3
+ import Parser
4
+ import Resolver
5
+ import Deriver
6
+ import Inference
7
+ import JsEmitter
8
+ import Dictionaries
9
+ import Dependencies
10
+ import LspHook
11
+
12
+ capability Compiler(
13
+ emitTarget: EmitTarget
14
+ task: Task
15
+ compilerModulePath: Option[Path]
16
+ jsOutputPath: Path
17
+ packagePaths: Map[PackagePair, Path]
18
+ singleFilePackages: Set[PackagePair]
19
+ virtualFiles: Map[String, String]
20
+ lspHook: LspHook
21
+ mutable parsedModules: Map[String, Module]
22
+ mutable resolvedModules: Map[String, Module]
23
+ mutable derivedModules: Map[String, Module]
24
+ mutable inferredModules: Map[String, Module]
25
+ mutable emittedModules: Set[String]
26
+ mutable phaseDurations: List[Pair[String, Duration]]
27
+ mutable phaseDurationDelta: Duration
28
+ )
29
+
30
+ make(
31
+ emitTarget: EmitTarget
32
+ task: Task
33
+ compilerModulePath: Option[Path]
34
+ jsOutputPath: Path
35
+ resolvedDependencies: ResolvedDependencies
36
+ virtualFiles: Map[String, String]
37
+ lspHook: LspHook
38
+ ): Compiler {
39
+ Compiler(
40
+ emitTarget = emitTarget
41
+ task = task
42
+ compilerModulePath = compilerModulePath
43
+ jsOutputPath = jsOutputPath
44
+ packagePaths = resolvedDependencies.packagePaths
45
+ singleFilePackages = resolvedDependencies.singleFilePackages
46
+ virtualFiles = virtualFiles
47
+ lspHook = lspHook
48
+ parsedModules = Map.empty()
49
+ resolvedModules = Map.empty()
50
+ derivedModules = Map.empty()
51
+ inferredModules = Map.empty()
52
+ emittedModules = Set.empty()
53
+ phaseDurations = []
54
+ phaseDurationDelta = Duration(0.0)
55
+ )
56
+ }
57
+
58
+ fail[T](at: Location, message: String): T {
59
+ panic(message + " " + at.show())
60
+ }
61
+
62
+ coreImports: List[DImport] =
63
+ [
64
+ "Any"
65
+ "Array"
66
+ "AssetSystem"
67
+ "Atomic"
68
+ "Bool"
69
+ "BrowserSystem"
70
+ "Buffer"
71
+ "BuildSystem"
72
+ "Channel"
73
+ "Char"
74
+ "Core"
75
+ "Duration"
76
+ "Equal"
77
+ "Error"
78
+ "FileHandle"
79
+ "Float"
80
+ "HttpClient"
81
+ "Instant"
82
+ "Int"
83
+ "IntMap"
84
+ "JsValue"
85
+ "JsSystem"
86
+ "List"
87
+ "Lock"
88
+ "Log"
89
+ "Map"
90
+ "NodeSystem"
91
+ "Nothing"
92
+ "Option"
93
+ "Ordering"
94
+ "Pair"
95
+ "Path"
96
+ "Serializable"
97
+ "Set"
98
+ "Show"
99
+ "Stack"
100
+ "Stream"
101
+ "String"
102
+ "StringMap"
103
+ "Task"
104
+ "Try"
105
+ "Unit"
106
+ ].map {moduleName =>
107
+ DImport(
108
+ at = Location("<prelude>", 1, 1)
109
+ alias = moduleName
110
+ package = PackagePair("ff", "core")
111
+ directory = []
112
+ file = moduleName
113
+ )
114
+ }
115
+
116
+ extend self: Compiler {
117
+
118
+ measure[T](phase: String, packagePair: PackagePair, moduleName: String, body: () => T): T {
119
+ let start = Duration(self.task.elapsed().seconds - self.phaseDurationDelta.seconds)
120
+ let result = body()
121
+ let stop = Duration(self.task.elapsed().seconds - self.phaseDurationDelta.seconds)
122
+ let duration = Duration(stop.seconds - start.seconds)
123
+ self.phaseDurationDelta = Duration(self.phaseDurationDelta.seconds + duration.seconds)
124
+ let text = phase + " " + packagePair.groupName() + "/" + moduleName
125
+ self.phaseDurations = [Pair(text, duration), ...self.phaseDurations]
126
+ result
127
+ }
128
+
129
+ printMeasurements(): Unit {
130
+ let worst = self.phaseDurations.sortBy {(_.second.seconds + 1000000.0) + ""}.takeLast(5).reverse()
131
+ worst.each {| Pair(text, duration) =>
132
+ Log.debug(text + ":\t" + duration.show())
133
+ }
134
+ }
135
+
136
+ parse(packagePair: PackagePair, moduleName: String, importedAt: Option[Location]): Module {
137
+ let packageName = packagePair.groupName()
138
+ self.parsedModules.get(packageName + ":" + moduleName).else:
139
+ self.measure("Parse", packagePair, moduleName):
140
+ let packagePath = self.packagePaths.get(packagePair).else {
141
+ panic("Internal error - package path missing: " + packagePair.groupName())
142
+ }
143
+ let file = moduleName + ".ff"
144
+ let path = packagePath.slash(file)
145
+ let code = self.virtualFiles.get(path.absolute()).else {
146
+ importedAt.each {at => if(!path.exists()) {
147
+ throw(CompileError(at, "Imported module not found: " + packageName + "/" + moduleName))
148
+ }}
149
+ path.readText()
150
+ }
151
+ let completionAt = if(self.lspHook.isEnabled() && self.lspHook.insertIdentifier) {self.lspHook.at}
152
+ let tokens = Tokenizer.tokenize(path.absolute(), code, completionAt, self.lspHook.isEnabled())
153
+ let parser = Parser.make(packagePair, file, tokens, self.emitTarget != EmitBrowser, self.lspHook)
154
+ let module = if(self.singleFilePackages.contains(packagePair)) {
155
+ parser.parseModuleWithPackageInfo().module
156
+ } else {
157
+ parser.parseModuleWithoutPackageInfo()
158
+ }
159
+ let result = module.Module(
160
+ imports = [...coreImports, ...module.imports]
161
+ )
162
+ self.parsedModules = self.parsedModules.add(packageName + ":" + moduleName, result)
163
+ result
164
+ }
165
+
166
+ imports(module: Module): List[Module] {
167
+ module.imports.map {import =>
168
+ let newPackagePair = import.package
169
+ let newModuleName = import.directory.map {_ + "/"}.join("") + import.file
170
+ if(!self.packagePaths.contains(newPackagePair)) {
171
+ throw(CompileError(import.at, "Missing dependency declaration for: " + newPackagePair.groupName()))
172
+ }
173
+ self.parse(newPackagePair, newModuleName, Some(import.at))
174
+ }
175
+ }
176
+
177
+ resolve(packagePair: PackagePair, moduleName: String): Module {
178
+ let packageName = packagePair.groupName()
179
+ self.resolvedModules.get(packageName + ":" + moduleName).else:
180
+ self.measure("Resolve", packagePair, moduleName):
181
+
182
+ let module = self.parse(packagePair, moduleName, None)
183
+ let otherModules = self.imports(module)
184
+ let resolver = Resolver.make(self.lspHook)
185
+ let result = resolver.resolveModule(module, otherModules)
186
+
187
+ self.resolvedModules = self.resolvedModules.add(packageName + ":" + moduleName, result)
188
+ result
189
+ }
190
+
191
+ derive(packagePair: PackagePair, moduleName: String): Module {
192
+ let packageName = packagePair.groupName()
193
+ self.derivedModules.get(packageName + ":" + moduleName).else:
194
+ self.measure("Derive", packagePair, moduleName):
195
+
196
+ let module = self.resolve(packagePair, moduleName)
197
+ let result = Deriver.make().deriveModule(module)
198
+
199
+ self.derivedModules = self.derivedModules.add(packageName + ":" + moduleName, result)
200
+ result
201
+ }
202
+
203
+ infer(packagePair: PackagePair, moduleName: String): Module {
204
+ let packageName = packagePair.groupName()
205
+ self.inferredModules.get(packageName + ":" + moduleName).else:
206
+ self.measure("Infer", packagePair, moduleName):
207
+
208
+ let module = self.derive(packagePair, moduleName)
209
+ let otherModules = self.imports(module).map {i =>
210
+ self.derive(i.packagePair, i.file.dropLast(".ff".size()))
211
+ }
212
+ let inference = Inference.make([module, ...otherModules], self.lspHook)
213
+ let inferredModule = inference.inferModule(module, otherModules)
214
+ let result = Dictionaries.make([module, ...otherModules]).processModule(inferredModule, otherModules)
215
+
216
+ self.inferredModules = self.inferredModules.add(packageName + ":" + moduleName, result)
217
+ result
218
+ }
219
+
220
+ emit(packagePair: PackagePair, moduleName: String, isMainModule: Bool): Unit {
221
+ let packageName = packagePair.groupName()
222
+ if(self.emittedModules.contains(packageName + ":" + moduleName)) {} else:
223
+ self.measure("Emit", packagePair, moduleName):
224
+ self.emittedModules = self.emittedModules.add(packageName + ":" + moduleName)
225
+
226
+ let module = self.infer(packagePair, moduleName)
227
+ let otherModules = self.imports(module).map {i =>
228
+ let newModuleName = i.file.dropLast(".ff".size())
229
+ self.emit(i.packagePair, newModuleName, isMainModule = False)
230
+ self.infer(i.packagePair, newModuleName)
231
+ }
232
+
233
+ let js = JsEmitter.make([module, ...otherModules], self.emitTarget, isMainModule, self.compilerModulePath)
234
+ .emitModule(packagePair, module)
235
+ let jsPath = self.jsOutputPath.slash(packagePair.group).slash(packagePair.name)
236
+ let jsFile = jsPath.slash(moduleName + ".mjs")
237
+ jsPath.createDirectory(createParentDirectories = True)
238
+ jsFile.writeText(js)
239
+ }
240
+
241
+ }