firefly-compiler 0.5.63 → 0.5.64

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 (59) hide show
  1. package/compiler/Builder.ff +47 -31
  2. package/compiler/Compiler.ff +42 -47
  3. package/compiler/Dependencies.ff +4 -2
  4. package/compiler/Deriver.ff +2 -2
  5. package/compiler/Dictionaries.ff +11 -12
  6. package/compiler/Environment.ff +5 -5
  7. package/compiler/Inference.ff +1 -2
  8. package/compiler/JsEmitter.ff +34 -33
  9. package/compiler/Main.ff +32 -31
  10. package/compiler/ModuleCache.ff +17 -25
  11. package/compiler/Parser.ff +7 -11
  12. package/compiler/Resolver.ff +8 -11
  13. package/compiler/Syntax.ff +53 -17
  14. package/compiler/Unification.ff +2 -5
  15. package/core/BuildSystem.ff +1 -1
  16. package/core/Path.ff +11 -1
  17. package/core/SourceLocation.ff +4 -0
  18. package/experimental/foldertest/.firefly/package.ff +1 -0
  19. package/experimental/foldertest/Root.ff +10 -0
  20. package/experimental/foldertest/a/A.ff +3 -0
  21. package/experimental/foldertest/a/MainA.ff +3 -0
  22. package/experimental/tests/TestCancel.ff +19 -0
  23. package/fireflysite/CommunityOverview.ff +0 -2
  24. package/fireflysite/FrontPage.ff +0 -2
  25. package/fireflysite/Main.ff +1 -1
  26. package/fireflysite/PackagesOverview.ff +0 -2
  27. package/fireflysite/Router.ff +3 -3
  28. package/fireflysite/{ExamplesOverview.ff → demos/ExamplesOverview.ff} +3 -3
  29. package/fireflysite/{RouteFront.ff → routes/RouteFront.ff} +2 -3
  30. package/fireflysite/{RouteNonMarkdown.ff → routes/RouteNonMarkdown.ff} +5 -5
  31. package/fireflysite/{RouteReference.ff → routes/RouteReference.ff} +2 -3
  32. package/lsp/CompletionHandler.ff +2 -2
  33. package/lsp/Handler.ff +14 -13
  34. package/lsp/LanguageServer.ff +5 -4
  35. package/output/js/ff/compiler/Builder.mjs +118 -80
  36. package/output/js/ff/compiler/Compiler.mjs +85 -89
  37. package/output/js/ff/compiler/Dependencies.mjs +6 -8
  38. package/output/js/ff/compiler/Deriver.mjs +6 -6
  39. package/output/js/ff/compiler/Dictionaries.mjs +6 -6
  40. package/output/js/ff/compiler/Environment.mjs +10 -6
  41. package/output/js/ff/compiler/Inference.mjs +4 -4
  42. package/output/js/ff/compiler/JsEmitter.mjs +60 -38
  43. package/output/js/ff/compiler/Main.mjs +66 -56
  44. package/output/js/ff/compiler/ModuleCache.mjs +34 -38
  45. package/output/js/ff/compiler/Parser.mjs +14 -14
  46. package/output/js/ff/compiler/Resolver.mjs +12 -22
  47. package/output/js/ff/compiler/Substitution.mjs +2 -2
  48. package/output/js/ff/compiler/Syntax.mjs +395 -183
  49. package/output/js/ff/compiler/Unification.mjs +18 -32
  50. package/output/js/ff/core/BuildSystem.mjs +2 -6
  51. package/output/js/ff/core/Path.mjs +32 -0
  52. package/output/js/ff/core/SourceLocation.mjs +12 -0
  53. package/package.json +1 -1
  54. package/vscode/package.json +1 -1
  55. package/vscode/syntaxes/firefly.tmLanguage.json +299 -299
  56. package/fireflysite/ReferenceIntroduction.ff +0 -11
  57. /package/fireflysite/{CountingButtonDemo.ff → demos/CountingButtonDemo.ff} +0 -0
  58. /package/fireflysite/{MatchingPasswordsDemo.ff → demos/MatchingPasswordsDemo.ff} +0 -0
  59. /package/fireflysite/{PostgresqlDemo.ff → demos/PostgresqlDemo.ff} +0 -0
package/compiler/Main.ff CHANGED
@@ -28,11 +28,11 @@ main(system: NodeSystem): Unit {
28
28
  let fireflyPath = detectFireflyPath(system)
29
29
 
30
30
  function buildScript(
31
- mainFile: String
31
+ mainPath: Path
32
32
  mainPackagePair: PackagePair
33
33
  emitTarget: EmitTarget
34
34
  resolvedDependencies: ResolvedDependencies
35
- ) {
35
+ ): ModuleKey {
36
36
  let fixedPackagePaths = if(resolvedDependencies.packagePaths.contains(PackagePair("ff", "core"))) {
37
37
  resolvedDependencies.packagePaths
38
38
  } else {
@@ -47,11 +47,13 @@ main(system: NodeSystem): Unit {
47
47
  | EmitBrowser => "browser"
48
48
  | EmitExecutable => "executable"
49
49
  }
50
+ let folders = mainPath.parent().grab().relativeListTo(fixedPackagePaths.grab(mainPackagePair))
51
+ let name = mainPath.base().removeLast(".ff").grab()
52
+ let moduleKey = ModuleKey(mainPackagePair, folders, name)
50
53
  Builder.build(
51
54
  system = system
52
55
  emitTarget = emitTarget
53
- mainPackage = mainPackagePair
54
- mainModules = [mainFile]
56
+ mainModules = [moduleKey]
55
57
  resolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
56
58
  compilerModulePath = compilerModulePath
57
59
  tempPath = system.path(".firefly").slash("temporary")
@@ -59,6 +61,7 @@ main(system: NodeSystem): Unit {
59
61
  printMeasurements = False
60
62
  moduleCache = ModuleCache.new(0)
61
63
  )
64
+ moduleKey
62
65
  }
63
66
 
64
67
  function runCommand(command: MainCommand) {
@@ -66,13 +69,13 @@ main(system: NodeSystem): Unit {
66
69
  let resolvedDependencies = Dependencies.process(
67
70
  system.httpClient()
68
71
  DependencyLock.new(system.mainTask())
69
- system.path(mainFile + ".ff")
72
+ system.path(mainFile)
70
73
  )
71
74
  prepareFireflyDirectory(system.path("."))
72
- let localMainFile = system.path(mainFile).base()
73
- buildScript(localMainFile, resolvedDependencies.mainPackagePair, EmitNode, resolvedDependencies)
74
- if(!importAndRun(system, fireflyPath, "node", resolvedDependencies.mainPackagePair, localMainFile, arguments)) {
75
- let at = Location(system.path(mainFile + ".ff").absolute(), 1, 1)
75
+ let mainPath = system.path(mainFile)
76
+ let moduleKey = buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitNode, resolvedDependencies)
77
+ if(!importAndRun(system, fireflyPath, "node", moduleKey, arguments)) {
78
+ let at = Location(system.path(mainFile).absolute(), 1, 1)
76
79
  throw(CompileError(at, "This module does not contain a 'nodeMain' function"))
77
80
  }
78
81
 
@@ -80,12 +83,12 @@ main(system: NodeSystem): Unit {
80
83
  let resolvedDependencies = Dependencies.process(
81
84
  system.httpClient()
82
85
  DependencyLock.new(system.mainTask())
83
- system.path(mainFile + ".ff")
86
+ system.path(mainFile)
84
87
  )
85
88
  prepareFireflyDirectory(system.path("."))
86
- let localMainFile = system.path(mainFile).base()
87
- buildScript(mainFile, resolvedDependencies.mainPackagePair, EmitBrowser, resolvedDependencies)
88
- bundleForBrowser(system, resolvedDependencies.mainPackagePair, localMainFile)
89
+ let mainPath = system.path(mainFile)
90
+ buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitBrowser, resolvedDependencies)
91
+ bundleForBrowser(system, resolvedDependencies.mainPackagePair, mainPath.base())
89
92
 
90
93
  | BuildCommand(mainFile) =>
91
94
  let resolvedDependencies = Dependencies.process(
@@ -94,11 +97,11 @@ main(system: NodeSystem): Unit {
94
97
  system.path(mainFile + ".ff")
95
98
  )
96
99
  prepareFireflyDirectory(system.path("."))
97
- let localMainFile = system.path(mainFile).base()
98
- buildScript(localMainFile, resolvedDependencies.mainPackagePair, EmitBuild, resolvedDependencies)
99
- buildScript(localMainFile, resolvedDependencies.mainPackagePair, EmitExecutable, resolvedDependencies)
100
- bundleForPkg(system, resolvedDependencies.mainPackagePair, localMainFile)
101
- importAndRun(system, fireflyPath, "build", resolvedDependencies.mainPackagePair, localMainFile, [])
100
+ let mainPath = system.path(mainFile)
101
+ let moduleKey = buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitBuild, resolvedDependencies)
102
+ buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitExecutable, resolvedDependencies)
103
+ bundleForPkg(system, resolvedDependencies.mainPackagePair, mainPath.base())
104
+ importAndRun(system, fireflyPath, "build", moduleKey, [])
102
105
 
103
106
  | CheckCommand(filePath) =>
104
107
  let errors = Builder.check(
@@ -113,7 +116,6 @@ main(system: NodeSystem): Unit {
113
116
  newVersion = 0
114
117
  lspHook = LspHook.disabled()
115
118
  infer = True
116
- checkDependencies = False
117
119
  )
118
120
  if(!errors.isEmpty()) {throw(CompileErrors(errors.distinct()))}
119
121
 
@@ -123,8 +125,7 @@ main(system: NodeSystem): Unit {
123
125
  Builder.build(
124
126
  system = system
125
127
  emitTarget = EmitNode
126
- mainPackage = PackagePair("ff", "compiler")
127
- mainModules = ["Main"]
128
+ mainModules = [ModuleKey(PackagePair("ff", "compiler"), [], "Main")]
128
129
  resolvedDependencies = ResolvedDependencies(
129
130
  mainPackagePair = PackagePair("ff", "compiler")
130
131
  packages = [Pair(
@@ -150,8 +151,9 @@ main(system: NodeSystem): Unit {
150
151
  let path = system.path(filePath)
151
152
  let code = path.readText()
152
153
  let packagePair = PackagePair("script", "script")
154
+ let moduleKey = ModuleKey(packagePair, [], path.base().removeLast(".ff").grab())
153
155
  let tokens = Tokenizer.tokenize(path.absolute(), code, None, False)
154
- let parser = Parser.new(packagePair, path.base(), tokens, True, LspHook.disabled())
156
+ let parser = Parser.new(moduleKey, tokens, True, LspHook.disabled())
155
157
  let module = parser.parseModuleWithPackageInfo().module
156
158
  makeSymbolColumns(module)
157
159
  }
@@ -191,11 +193,11 @@ These are the commands:
191
193
 
192
194
  parseCommandLine(arguments: List[String]): MainCommand {
193
195
  | [mainFile, ...mainArguments] {mainFile.removeLast(".ff") | Some(mainName)} =>
194
- RunCommand(mainName, mainArguments)
196
+ RunCommand(mainFile, mainArguments)
195
197
  | ["run", ...runArguments] =>
196
198
  runArguments.{
197
199
  | [mainFile, ...mainArguments] {mainFile.removeLast(".ff") | Some(mainName)} =>
198
- RunCommand(mainName, mainArguments)
200
+ RunCommand(mainFile, mainArguments)
199
201
  | _ => throw(CommandLineError(
200
202
  "You must specify a Firefly file (.ff) as first argument to run." + usageString
201
203
  ))
@@ -203,7 +205,7 @@ parseCommandLine(arguments: List[String]): MainCommand {
203
205
  | ["browser", ...browserArguments] =>
204
206
  browserArguments.{
205
207
  | [mainFile] {mainFile.removeLast(".ff") | Some(mainName)} =>
206
- BrowserCommand(mainName)
208
+ BrowserCommand(mainFile)
207
209
  | [_, _, ...] => throw(CommandLineError(
208
210
  "You must only specify a single argument to browser." + usageString
209
211
  ))
@@ -214,7 +216,7 @@ parseCommandLine(arguments: List[String]): MainCommand {
214
216
  | ["build", ...buildArguments] =>
215
217
  buildArguments.{
216
218
  | [mainFile] {mainFile.removeLast(".ff") | Some(mainName)} =>
217
- BuildCommand(mainName)
219
+ BuildCommand(mainFile)
218
220
  | [_, _, ...] => throw(CommandLineError(
219
221
  "You must only specify a single argument to build." + usageString
220
222
  ))
@@ -266,15 +268,14 @@ importAndRun(
266
268
  system: NodeSystem
267
269
  fireflyPath: Path
268
270
  target: String
269
- packagePair: PackagePair
270
- mainFile: String
271
+ moduleKey: ModuleKey
271
272
  arguments: List[String]
272
273
  ): Bool {
273
274
  let process = Js.await(Js.dynamicImport("process"))
274
275
  let cwd = process->cwd()
275
276
  let workingDirectory = if(cwd->indexOf(":") === 1) {"file:///" + cwd?} else {cwd?}
276
- let packagePath = packagePair.group + "/" + packagePair.name
277
- let runFile = workingDirectory + "/.firefly/output/" + target + "/" + packagePath + "/" + mainFile + ".run.mjs"
277
+ let packagePath = moduleKey.packagePair.groupName("/")
278
+ let runFile = workingDirectory + "/.firefly/output/" + target + "/" + packagePath + "/" + moduleKey.importName() + ".run.mjs"
278
279
  let runFilePath = if(runFile.contains("://")) {system.pathFromUrl(runFile)} else {system.path(runFile)}
279
280
  if(runFilePath.exists()) {
280
281
  let main = Js.await(Js.dynamicImport(runFile))
@@ -336,7 +337,7 @@ makeSymbolColumns(module: Module): List[List[String]] {
336
337
  let all = [...toplevel, ...types, ...traits]
337
338
  all.map {r =>
338
339
  let generics = if(r.generics.isEmpty()) {""} else {"[" + r.generics.join(", ") + "]"}
339
- let header = (module.file + " " + r.name + generics).trim()
340
+ let header = (module.moduleKey.importName() + " " + r.name + generics).trim()
340
341
  [header, ...r.symbols.sort()]
341
342
  }
342
343
  }
@@ -34,19 +34,18 @@ extend self: ModuleCache {
34
34
  invalidate(key: String) {
35
35
  //Log.trace("Invalidate: " + uri)
36
36
  self.parsedModules.get(key).each: | Pair(module, _) =>
37
- let moduleName = module.file.dropLast(3)
38
37
  self.remove([key])
39
38
  self.parsedModules.each {| k, Pair(m, _) =>
40
- if(m.imports.any {i => i.package == module.packagePair && i.file == moduleName}) {
39
+ if(m.imports.any {i => i.moduleKey == module.moduleKey}) {
41
40
  //Log.trace("Invalidating due to import of invalidated module: " + m.packagePair.groupName() + "/" + m.file)
42
41
  self.remove([k])
43
42
  }
44
43
  }
45
44
  }
46
45
 
47
- filesNotImporting(packagePair: PackagePair, moduleName: String): List[String] {
46
+ filesNotImporting(moduleKey: ModuleKey): List[String] {
48
47
  self.parsedModules.toList().collect {| Pair(k, Pair(m, _)) =>
49
- if(!m.imports.any {i => i.package == packagePair && i.file == moduleName}): k
48
+ if(!m.imports.any {i => i.moduleKey == moduleKey}): k
50
49
  }
51
50
  }
52
51
 
@@ -86,11 +85,10 @@ extend self: ModuleCache {
86
85
 
87
86
  cacheParsedModule(
88
87
  packagePaths: Map[PackagePair, Path]
89
- packagePair: PackagePair
90
- moduleName: String
88
+ moduleKey: ModuleKey
91
89
  body: Path => Module
92
90
  ): Module {
93
- let path = modulePath(packagePaths, packagePair, moduleName)
91
+ let path = modulePath(packagePaths, moduleKey)
94
92
  self.parsedModules.get(path.absolute()).map {_.first}.else:
95
93
  let result = body(path)
96
94
  self.parsedModules = self.parsedModules.add(path.absolute(), Pair(result, self.version))
@@ -99,11 +97,10 @@ extend self: ModuleCache {
99
97
 
100
98
  cacheResolvedModule(
101
99
  packagePaths: Map[PackagePair, Path]
102
- packagePair: PackagePair
103
- moduleName: String
100
+ moduleKey: ModuleKey
104
101
  body: Path => Module
105
102
  ): Module {
106
- let path = modulePath(packagePaths, packagePair, moduleName)
103
+ let path = modulePath(packagePaths, moduleKey)
107
104
  self.resolvedModules.get(path.absolute()).map {_.first}.else:
108
105
  let result = body(path)
109
106
  self.resolvedModules = self.resolvedModules.add(path.absolute(), Pair(result, self.version))
@@ -112,11 +109,10 @@ extend self: ModuleCache {
112
109
 
113
110
  cacheDerivedModule(
114
111
  packagePaths: Map[PackagePair, Path]
115
- packagePair: PackagePair
116
- moduleName: String
112
+ moduleKey: ModuleKey
117
113
  body: Path => Module
118
114
  ): Module {
119
- let path = modulePath(packagePaths, packagePair, moduleName)
115
+ let path = modulePath(packagePaths, moduleKey)
120
116
  self.derivedModules.get(path.absolute()).map {_.first}.else:
121
117
  let result = body(path)
122
118
  self.derivedModules = self.derivedModules.add(path.absolute(), Pair(result, self.version))
@@ -125,11 +121,10 @@ extend self: ModuleCache {
125
121
 
126
122
  cacheInferredModule(
127
123
  packagePaths: Map[PackagePair, Path]
128
- packagePair: PackagePair
129
- moduleName: String
124
+ moduleKey: ModuleKey
130
125
  body: Path => Module
131
126
  ): Module {
132
- let path = modulePath(packagePaths, packagePair, moduleName)
127
+ let path = modulePath(packagePaths, moduleKey)
133
128
  self.inferredModules.get(path.absolute()).map {_.first}.else:
134
129
  let result = body(path)
135
130
  self.inferredModules = self.inferredModules.add(path.absolute(), Pair(result, self.version))
@@ -138,12 +133,11 @@ extend self: ModuleCache {
138
133
 
139
134
  cacheEmittedModule(
140
135
  packagePaths: Map[PackagePair, Path]
141
- packagePair: PackagePair
142
- moduleName: String
136
+ moduleKey: ModuleKey
143
137
  isMainModule: Bool
144
138
  body: Path => Unit
145
139
  ): Unit {
146
- let path = modulePath(packagePaths, packagePair, moduleName)
140
+ let path = modulePath(packagePaths, moduleKey)
147
141
  if(isMainModule || !self.emittedModules.contains(path.absolute())):
148
142
  self.emittedModules = self.emittedModules.add(path.absolute(), self.version)
149
143
  try {
@@ -168,12 +162,10 @@ mergeVersionedMap[T](oldMap: Map[String, T], newMap: Map[String, T], getVersion:
168
162
 
169
163
  modulePath(
170
164
  packagePaths: Map[PackagePair, Path]
171
- packagePair: PackagePair
172
- moduleName: String
165
+ moduleKey: ModuleKey
173
166
  ): Path {
174
- let packagePath = packagePaths.get(packagePair).else {
175
- panic("Internal error - package path missing: " + packagePair.groupName())
167
+ let packagePath = packagePaths.get(moduleKey.packagePair).else {
168
+ panic("Internal error - package path missing: " + moduleKey.packagePair.groupName())
176
169
  }
177
- let file = moduleName + ".ff"
178
- packagePath.slash(file)
170
+ moduleKey.path(packagePath)
179
171
  }
@@ -4,8 +4,7 @@ import Syntax
4
4
  import LspHook
5
5
 
6
6
  class Parser(
7
- packagePair: PackagePair
8
- file: String
7
+ moduleKey: ModuleKey
9
8
  tokens: List[Token]
10
9
  end: Token
11
10
  targetIsNode: Bool
@@ -18,15 +17,13 @@ class Parser(
18
17
  data Poly(generics: List[String], constraints: List[Constraint])
19
18
 
20
19
  new(
21
- packagePair: PackagePair
22
- file: String
20
+ moduleKey: ModuleKey
23
21
  tokens: List[Token]
24
22
  targetIsNode: Bool
25
23
  lspHook: LspHook
26
24
  ): Parser {
27
25
  Parser(
28
- packagePair = packagePair
29
- file = file
26
+ moduleKey = moduleKey
30
27
  tokens = tokens
31
28
  end = tokens.grabLast()
32
29
  targetIsNode = targetIsNode
@@ -128,7 +125,7 @@ extend self: Parser {
128
125
  } else {
129
126
  DPackage(
130
127
  location
131
- self.packagePair
128
+ self.moduleKey.packagePair
132
129
  Version(location, 0, 0, 0)
133
130
  TargetNames(node = self.targetIsNode, browser = !self.targetIsNode)
134
131
  )
@@ -175,7 +172,7 @@ extend self: Parser {
175
172
  } elseIf {self.current().is(LKeyword) && self.current().rawIs4("data", "class", "capability", "newtype")} {
176
173
  types.push(self.parseTypeDefinition())
177
174
  } elseIf {self.current().is(LKeyword) && self.current().rawIs("import")} {
178
- imports.push(self.parseImportDefinition(self.packagePair))
175
+ imports.push(self.parseImportDefinition(self.moduleKey.packagePair))
179
176
  } elseIf {self.current().is(LKeyword) && self.current().rawIs("include")} {
180
177
  throw(CompileError(self.current().at()
181
178
  "Includes must be at the top of the file or below 'package'"
@@ -195,8 +192,7 @@ extend self: Parser {
195
192
  }
196
193
 
197
194
  Module(
198
- file = self.file
199
- packagePair = self.packagePair
195
+ moduleKey = self.moduleKey
200
196
  imports = imports.toList()
201
197
  lets = lets.toList()
202
198
  functions = functions.toList()
@@ -527,7 +523,7 @@ extend self: Parser {
527
523
  } else {
528
524
  currentPackagePair
529
525
  }
530
- DImport(fileToken.at(), alias, packagePair, path.toList(), fileToken.raw())
526
+ DImport(fileToken.at(), alias, ModuleKey(packagePair, path.toList(), fileToken.raw()))
531
527
  }
532
528
 
533
529
  parsePackageDefinition(): DPackage {
@@ -26,7 +26,7 @@ data CaseVariable(
26
26
  asBound: Option[String]
27
27
  )
28
28
 
29
- new(packagePair: PackagePair, moduleName: String, lspHook: LspHook): Resolver {
29
+ new(lspHook: LspHook): Resolver {
30
30
  Resolver(
31
31
  variables = [].toMap()
32
32
  variableLocations = [].toMap()
@@ -54,8 +54,6 @@ extend self: Resolver {
54
54
  }
55
55
 
56
56
  resolveModule(module: Module, otherModules: List[Module]): Module {
57
- let moduleNamespace =
58
- module.file.replace("\\", "/").reverse().takeWhile {_ != '/'}.reverse().takeWhile {_ != '.'}
59
57
  let self2 = self.processImports(module.imports, otherModules)
60
58
  let self3 = self2.processDefinitions(module, None)
61
59
 
@@ -103,11 +101,11 @@ extend self: Resolver {
103
101
  processImports(imports: List[DImport], modules: List[Module]): Resolver {
104
102
  mutable resolver = self
105
103
  imports.each {import =>
106
- modules.find {_.file.dropLast(3) == import.file}.{
104
+ modules.find {_.moduleKey == import.moduleKey}.{
107
105
  | Some(module) =>
108
106
  resolver = resolver.processDefinitions(module, Some(import.alias))
109
107
  | None =>
110
- throw(CompileError(import.at, "No such module: " + import.file))
108
+ throw(CompileError(import.at, "No such module: " + import.moduleKey.importName()))
111
109
  }
112
110
  }
113
111
  resolver
@@ -115,9 +113,7 @@ extend self: Resolver {
115
113
 
116
114
  processDefinitions(module: Module, importAlias: Option[String]): Resolver {
117
115
  function entry(name: String, unqualified: Bool): List[Pair[String, String]] {
118
- let full =
119
- module.packagePair.groupName() + "/" +
120
- module.file.dropLast(3) + "." + name
116
+ let full = module.moduleKey.qualifiedSymbol(name)
121
117
  importAlias.{
122
118
  | None => [Pair(name, full), Pair(full, full)]
123
119
  | Some(alias) {unqualified} => [Pair(alias + "." + name, full), Pair(name, full), Pair(full, full)]
@@ -125,9 +121,10 @@ extend self: Resolver {
125
121
  }
126
122
  }
127
123
  let isCore = // TODO: Extend imports to list unqualified symbols instead of this
128
- module.packagePair.group == "ff" &&
129
- module.packagePair.name == "core" &&
130
- module.file == "Core.ff"
124
+ module.moduleKey.packagePair.group == "ff" &&
125
+ module.moduleKey.packagePair.name == "core" &&
126
+ module.moduleKey.folders == [] &&
127
+ module.moduleKey.name == "Core"
131
128
  let lets = module.lets.flatMap {entry(_.name, isCore)}.toMap()
132
129
  let letLocations = module.lets.flatMap {d => entry(d.name, True).map {_.mapSecond {_ => d.at}}}.toMap()
133
130
  let functions = module.functions.flatMap {entry(_.signature.name, isCore)}.toMap()
@@ -22,18 +22,7 @@ data PackageInfo(
22
22
  includes: List[DInclude]
23
23
  )
24
24
 
25
- data Module(
26
- file: String
27
- packagePair: PackagePair
28
- imports: List[DImport]
29
- types: List[DType]
30
- traits: List[DTrait]
31
- instances: List[DInstance]
32
- extends: List[DExtend]
33
- lets: List[DLet]
34
- functions: List[DFunction]
35
- )
36
-
25
+ // TODO rename to PackageKey
37
26
  data PackagePair(
38
27
  group: String
39
28
  name: String
@@ -43,8 +32,58 @@ extend self: PackagePair {
43
32
  groupName(delimiter: String = ":"): String {
44
33
  self.group + delimiter + self.name
45
34
  }
35
+ isCore(): Bool {
36
+ self.group == "ff" &&
37
+ self.name == "core"
38
+ }
39
+ moduleKey(packageRoot: Path, modulePath: Path): Option[ModuleKey] {
40
+ let parts = modulePath.relativeListTo(packageRoot)
41
+ let folders = parts.dropLast()
42
+ parts.last().flatMap {_.removeLast(".ff")}.filter {_ =>
43
+ folders.all {_.first().any {_.isAsciiLower()}} &&
44
+ folders.all {_.all {c => c.isAsciiLower() || c.isAsciiDigit()}}
45
+ }.map: name =>
46
+ ModuleKey(self, folders, name)
47
+ }
48
+ }
49
+
50
+ data ModuleKey(
51
+ packagePair: PackagePair
52
+ folders: List[String]
53
+ name: String
54
+ )
55
+
56
+ extend self: ModuleKey {
57
+ // "folder/Module" as in: import folder/Module from group:package
58
+ importName(): String {
59
+ self.folders.map {_ + "/"}.join() + self.name
60
+ }
61
+ // "C:\myProject\folder\Module.ff"
62
+ path(packageRoot: Path): Path {
63
+ let parent = self.folders.foldLeft(packageRoot) {p, f => p.slash(f)}
64
+ parent.slash(self.name + ".ff")
65
+ }
66
+ // "group:package/folder/Module"
67
+ qualifiedName(): String {
68
+ self.packagePair.groupName() + "/" + self.importName()
69
+ }
70
+ // "ff:core/List.List_map"
71
+ qualifiedSymbol(symbol: String): String {
72
+ self.qualifiedName() + "." + symbol
73
+ }
46
74
  }
47
75
 
76
+ data Module(
77
+ moduleKey: ModuleKey
78
+ imports: List[DImport]
79
+ types: List[DType]
80
+ traits: List[DTrait]
81
+ instances: List[DInstance]
82
+ extends: List[DExtend]
83
+ lets: List[DLet]
84
+ functions: List[DFunction]
85
+ )
86
+
48
87
  data DPackage(
49
88
  at: Location
50
89
  packagePair: PackagePair
@@ -69,9 +108,7 @@ data TargetNames(
69
108
  data DImport(
70
109
  at: Location
71
110
  alias: String
72
- package: PackagePair
73
- directory: List[String]
74
- file: String
111
+ moduleKey: ModuleKey
75
112
  )
76
113
  data DFunction(
77
114
  at: Location
@@ -181,8 +218,7 @@ data MatchGuard(
181
218
  )
182
219
 
183
220
  data Dictionary(
184
- packagePair: PackagePair
185
- moduleName: String
221
+ moduleKey: ModuleKey
186
222
  traitName: String
187
223
  typeName: String
188
224
  dictionaries: List[Dictionary]
@@ -17,8 +17,7 @@ data InstanceKey(traitName: String, typeName: String)
17
17
  data InstanceValue(
18
18
  generics: List[String]
19
19
  constraints: List[Constraint]
20
- packagePair: PackagePair
21
- moduleName: String
20
+ moduleKey: ModuleKey
22
21
  traitName: String
23
22
  typeArguments: List[Type]
24
23
  )
@@ -34,7 +33,6 @@ new(modules: List[Module], attemptFixes: Bool): Unification {
34
33
  Map.new()
35
34
  3 // To avoid collision with the parser and resolver
36
35
  modules.flatMap {module =>
37
- let moduleName = module.file.dropLast(".ff".size())
38
36
  module.instances.map {definition =>
39
37
  let typeName = definition.typeArguments.grabFirst().{
40
38
  | TConstructor(_, name, _) => name
@@ -45,8 +43,7 @@ new(modules: List[Module], attemptFixes: Bool): Unification {
45
43
  InstanceValue(
46
44
  generics = definition.generics
47
45
  constraints = definition.constraints
48
- packagePair = module.packagePair
49
- moduleName = moduleName
46
+ moduleKey = module.moduleKey
50
47
  traitName = definition.traitName
51
48
  typeArguments = definition.typeArguments
52
49
  )
@@ -148,7 +148,7 @@ internalCompile(buildSystem: BuildSystem, mainFiles: List[Path], target: String)
148
148
  Js.await(Js.rawIdentifier("$firefly_compiler")->"buildViaBuildSystem_$"(
149
149
  buildSystem!
150
150
  internalPath(buildSystem, buildSystem!->"fireflyPath_"?)!
151
- mainFiles.map {_.base()}
151
+ mainFiles!
152
152
  target
153
153
  Js.currentTask()!
154
154
  ))
package/core/Path.ff CHANGED
@@ -101,10 +101,20 @@ extend self: Path {
101
101
  }
102
102
 
103
103
  relativeTo(path: Path): String {
104
- let nodePath = Js.import("path")
104
+ let nodePath = Js.import("path")
105
105
  nodePath->relative(path.absolutePath, self.absolutePath)?
106
106
  }
107
107
 
108
+ relativeListTo(path: Path): List[String] {
109
+ let nodePath = Js.import("path")
110
+ let relative = self.relativeTo(path)
111
+ if(nodePath->sep === "\\") {
112
+ relative.split('/').flatMap {_.split('\\')}.filter {_ != ""}
113
+ } else {
114
+ relative.split('/').filter {_ != ""}
115
+ }
116
+ }
117
+
108
118
  endsWith(parts: List[String]): Bool {
109
119
  function go(pathOption: Option[Path], reversed: List[String]): Bool {
110
120
  | _, [] => True
@@ -26,6 +26,10 @@ extend self: SourceLocation {
26
26
  self.location.takeWhile {_ != ':'}.reverse().takeWhile {_ != '/'}.reverse()
27
27
  }
28
28
 
29
+ directoryAndModule(): String {
30
+ self.location.takeWhile {_ != ':'}.reverse().reverse()
31
+ }
32
+
29
33
  line(): Int {
30
34
  self.location.dropWhile {_ != ':'}.dropFirst().takeWhile {_ != ':'}.grabInt()
31
35
  }
@@ -0,0 +1 @@
1
+ package foldergroup:test:0.0.0
@@ -0,0 +1,10 @@
1
+ import a.A
2
+
3
+ nodeMain(system: NodeSystem) {
4
+ Log.trace("Hello Root")
5
+ A.functionA()
6
+ }
7
+
8
+ functionRoot() {
9
+ Log.trace("function Root")
10
+ }
@@ -0,0 +1,3 @@
1
+ functionA() {
2
+ Log.trace("function A")
3
+ }
@@ -0,0 +1,3 @@
1
+ nodeMain(system: NodeSystem) {
2
+ Log.trace("Hello from a/MainA")
3
+ }
@@ -0,0 +1,19 @@
1
+ nodeMain(system: NodeSystem) {
2
+
3
+ let task1 = system.mainTask().spawn {task =>
4
+ Log.debug("Starting task 1")
5
+ try {
6
+ while {True} {
7
+ task.sleep(Duration(0.1))
8
+ }
9
+ } catchAny {error =>
10
+ Log.debug(error.name())
11
+ }
12
+ }
13
+
14
+ Log.debug("Aborting task 1")
15
+ task1.abort()
16
+ Log.debug("Aborted task 1")
17
+
18
+ }
19
+
@@ -1,6 +1,4 @@
1
1
  import Guide
2
- import CountingButtonDemo
3
- import MatchingPasswordsDemo
4
2
 
5
3
  sections(): List[Section] {
6
4
  [
@@ -1,6 +1,4 @@
1
1
  import Guide
2
- import CountingButtonDemo
3
- import MatchingPasswordsDemo
4
2
 
5
3
  sections(): List[Section] {
6
4
  [
@@ -4,7 +4,7 @@ import Lux from ff:lux
4
4
  import Css from ff:lux
5
5
  import Guide
6
6
  import GettingStarted
7
- import ExamplesOverview
7
+ import demos.ExamplesOverview
8
8
  import PackagesOverview
9
9
  import CommunityOverview
10
10
  import FrontPage
@@ -1,6 +1,4 @@
1
1
  import Guide
2
- import CountingButtonDemo
3
- import MatchingPasswordsDemo
4
2
 
5
3
  sections(): List[Section] {
6
4
  [