firefly-compiler 0.4.24 → 0.4.26

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 (83) hide show
  1. package/compiler/Builder.ff +14 -1
  2. package/compiler/Compiler.ff +13 -40
  3. package/compiler/Main.ff +15 -1
  4. package/compiler/ModuleCache.ff +160 -0
  5. package/core/Map.ff +8 -0
  6. package/core/Path.ff +4 -0
  7. package/experimental/bidirectional/Bidi.ff +88 -0
  8. package/lsp/Handler.ff +109 -37
  9. package/lsp/LanguageServer.ff +12 -8
  10. package/lsp/TestReferences.ff +3 -1
  11. package/lux/.firefly/package.ff +0 -1
  12. package/output/js/ff/compiler/Builder.mjs +30 -16
  13. package/output/js/ff/compiler/Compiler.mjs +29 -67
  14. package/output/js/ff/compiler/Dependencies.mjs +0 -2
  15. package/output/js/ff/compiler/Deriver.mjs +0 -2
  16. package/output/js/ff/compiler/Dictionaries.mjs +0 -2
  17. package/output/js/ff/compiler/Environment.mjs +0 -2
  18. package/output/js/ff/compiler/Inference.mjs +0 -2
  19. package/output/js/ff/compiler/JsEmitter.mjs +0 -2
  20. package/output/js/ff/compiler/JsImporter.mjs +0 -2
  21. package/output/js/ff/compiler/LspHook.mjs +0 -2
  22. package/output/js/ff/compiler/Main.mjs +8 -8
  23. package/output/js/ff/compiler/ModuleCache.mjs +428 -0
  24. package/output/js/ff/compiler/Parser.mjs +0 -2
  25. package/output/js/ff/compiler/Patterns.mjs +0 -2
  26. package/output/js/ff/compiler/Resolver.mjs +0 -2
  27. package/output/js/ff/compiler/Substitution.mjs +0 -2
  28. package/output/js/ff/compiler/Syntax.mjs +0 -2
  29. package/output/js/ff/compiler/Token.mjs +0 -2
  30. package/output/js/ff/compiler/Tokenizer.mjs +0 -2
  31. package/output/js/ff/compiler/Unification.mjs +0 -2
  32. package/output/js/ff/compiler/Wildcards.mjs +0 -2
  33. package/output/js/ff/compiler/Workspace.mjs +0 -2
  34. package/output/js/ff/core/Any.mjs +0 -2
  35. package/output/js/ff/core/Array.mjs +0 -2
  36. package/output/js/ff/core/AssetSystem.mjs +0 -2
  37. package/output/js/ff/core/Atomic.mjs +0 -2
  38. package/output/js/ff/core/Bool.mjs +0 -2
  39. package/output/js/ff/core/BrowserSystem.mjs +0 -2
  40. package/output/js/ff/core/Buffer.mjs +0 -2
  41. package/output/js/ff/core/BuildSystem.mjs +0 -2
  42. package/output/js/ff/core/Channel.mjs +0 -2
  43. package/output/js/ff/core/Char.mjs +0 -2
  44. package/output/js/ff/core/Core.mjs +0 -2
  45. package/output/js/ff/core/Duration.mjs +0 -2
  46. package/output/js/ff/core/Equal.mjs +0 -2
  47. package/output/js/ff/core/Error.mjs +0 -2
  48. package/output/js/ff/core/FileHandle.mjs +0 -2
  49. package/output/js/ff/core/Float.mjs +0 -2
  50. package/output/js/ff/core/HttpClient.mjs +0 -2
  51. package/output/js/ff/core/Instant.mjs +0 -2
  52. package/output/js/ff/core/Int.mjs +0 -2
  53. package/output/js/ff/core/IntMap.mjs +0 -2
  54. package/output/js/ff/core/JsSystem.mjs +0 -2
  55. package/output/js/ff/core/JsValue.mjs +0 -2
  56. package/output/js/ff/core/Json.mjs +0 -2
  57. package/output/js/ff/core/List.mjs +0 -2
  58. package/output/js/ff/core/Lock.mjs +0 -2
  59. package/output/js/ff/core/Log.mjs +0 -2
  60. package/output/js/ff/core/Map.mjs +16 -2
  61. package/output/js/ff/core/NodeSystem.mjs +0 -2
  62. package/output/js/ff/core/Nothing.mjs +0 -2
  63. package/output/js/ff/core/Option.mjs +0 -2
  64. package/output/js/ff/core/Ordering.mjs +0 -2
  65. package/output/js/ff/core/Pair.mjs +0 -2
  66. package/output/js/ff/core/Path.mjs +12 -2
  67. package/output/js/ff/core/Random.mjs +0 -2
  68. package/output/js/ff/core/RbMap.mjs +0 -2
  69. package/output/js/ff/core/Serializable.mjs +0 -2
  70. package/output/js/ff/core/Set.mjs +0 -2
  71. package/output/js/ff/core/Show.mjs +0 -2
  72. package/output/js/ff/core/SourceLocation.mjs +0 -2
  73. package/output/js/ff/core/Stream.mjs +0 -2
  74. package/output/js/ff/core/String.mjs +0 -2
  75. package/output/js/ff/core/StringMap.mjs +0 -2
  76. package/output/js/ff/core/Task.mjs +0 -2
  77. package/output/js/ff/core/Try.mjs +0 -2
  78. package/output/js/ff/core/Unit.mjs +0 -2
  79. package/package.json +1 -1
  80. package/vscode/client/src/extension.ts +4 -3
  81. package/vscode/language-configuration.json +10 -1
  82. package/vscode/package.json +1 -1
  83. package/output/js/ff/core/Box.mjs +0 -114
@@ -6,6 +6,7 @@ import Compiler
6
6
  import Unification
7
7
  import Dependencies
8
8
  import JsEmitter
9
+ import ModuleCache
9
10
  import LspHook
10
11
 
11
12
  build(
@@ -18,6 +19,7 @@ build(
18
19
  tempPath: Path
19
20
  jsOutputPath: Path
20
21
  printMeasurements: Bool
22
+ moduleCache: ModuleCache
21
23
  ): Unit {
22
24
 
23
25
  if(tempPath.exists()) {tempPath.delete()}
@@ -34,6 +36,7 @@ build(
34
36
  jsPathFile
35
37
  resolvedDependencies
36
38
  Map.empty()
39
+ moduleCache
37
40
  lspHook = LspHook.disabled()
38
41
  )
39
42
  compiler.emit(mainPackage, mainModule, isMainModule = True)
@@ -81,6 +84,7 @@ buildViaBuildSystem(system: NodeSystem, fireflyPath: Path, mainFile: String, tar
81
84
  tempPath = system.path(".firefly/temporary")
82
85
  jsOutputPath = system.path(".firefly/output").slash(target)
83
86
  printMeasurements = False
87
+ moduleCache = ModuleCache.empty(0)
84
88
  )
85
89
  }
86
90
 
@@ -90,8 +94,11 @@ check(
90
94
  path: Path
91
95
  mustContain: Option[String]
92
96
  virtualFiles: Map[String, String]
97
+ cache: ModuleCache
98
+ newVersion: Int
93
99
  lspHook: LspHook
94
100
  infer: Bool
101
+ checkDependencies: Bool
95
102
  ): List[CompileError] {
96
103
  let packages = path.isDirectory().{
97
104
  | True => findPackageFiles(path, mustContain)
@@ -109,6 +116,7 @@ check(
109
116
  resolvedDependencies.packagePaths.add(PackagePair("ff", "core"), fireflyPath.slash("core"))
110
117
  }
111
118
  let fixedResolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
119
+ let newCache = cache.without(newVersion, path)
112
120
  let compiler = Compiler.make(
113
121
  EmitBuild
114
122
  system.mainTask()
@@ -116,9 +124,13 @@ check(
116
124
  package.root.slash(".firefly").slash("temporary")
117
125
  fixedResolvedDependencies
118
126
  virtualFiles
127
+ newCache
119
128
  lspHook
120
129
  )
121
- package.files.each {file =>
130
+ let files = if(checkDependencies) {package.files} else {
131
+ package.files.filter {!_.contains([".firefly", "dependencies"])}
132
+ }
133
+ files.each {file =>
122
134
  let localFile = file.base()
123
135
  try {
124
136
  if(infer) {
@@ -131,6 +143,7 @@ check(
131
143
  errors.push(c)
132
144
  } grab()
133
145
  }
146
+ cache.mergeVersions(compiler.cache)
134
147
  }
135
148
  errors.drain()
136
149
 
@@ -1,5 +1,6 @@
1
1
  import Syntax
2
2
  import Tokenizer
3
+ import ModuleCache
3
4
  import Parser
4
5
  import Resolver
5
6
  import Deriver
@@ -17,12 +18,8 @@ capability Compiler(
17
18
  packagePaths: Map[PackagePair, Path]
18
19
  singleFilePackages: Set[PackagePair]
19
20
  virtualFiles: Map[String, String]
21
+ cache: ModuleCache
20
22
  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
23
  mutable phaseDurationDelta: Duration
27
24
  phaseDurations: Array[Pair[String, Duration]]
28
25
  )
@@ -34,6 +31,7 @@ make(
34
31
  jsOutputPath: Path
35
32
  resolvedDependencies: ResolvedDependencies
36
33
  virtualFiles: Map[String, String]
34
+ cache: ModuleCache
37
35
  lspHook: LspHook
38
36
  ): Compiler {
39
37
  Compiler(
@@ -44,12 +42,8 @@ make(
44
42
  packagePaths = resolvedDependencies.packagePaths
45
43
  singleFilePackages = resolvedDependencies.singleFilePackages
46
44
  virtualFiles = virtualFiles
45
+ cache = cache
47
46
  lspHook = lspHook
48
- parsedModules = Map.empty()
49
- resolvedModules = Map.empty()
50
- derivedModules = Map.empty()
51
- inferredModules = Map.empty()
52
- emittedModules = Set.empty()
53
47
  phaseDurationDelta = Duration(0.0)
54
48
  phaseDurations = [].toArray()
55
49
  )
@@ -66,7 +60,6 @@ coreImports: List[DImport] =
66
60
  "AssetSystem"
67
61
  "Atomic"
68
62
  "Bool"
69
- "Box"
70
63
  "BrowserSystem"
71
64
  "Buffer"
72
65
  "BuildSystem"
@@ -139,14 +132,9 @@ extend self: Compiler {
139
132
  }
140
133
 
141
134
  parse(packagePair: PackagePair, moduleName: String, importedAt: Option[Location]): Module {
135
+ self.cache.cacheParsedModule(self.packagePaths, packagePair, moduleName): path =>
142
136
  let packageName = packagePair.groupName()
143
- self.parsedModules.get(packageName + ":" + moduleName).else:
144
137
  self.measure("Parse", packagePair, moduleName):
145
- let packagePath = self.packagePaths.get(packagePair).else {
146
- panic("Internal error - package path missing: " + packagePair.groupName())
147
- }
148
- let file = moduleName + ".ff"
149
- let path = packagePath.slash(file)
150
138
  let code = self.virtualFiles.get(path.absolute()).else {
151
139
  importedAt.each {at => if(!path.exists()) {
152
140
  throw(CompileError(at, "Imported module not found: " + packageName + "/" + moduleName))
@@ -155,7 +143,7 @@ extend self: Compiler {
155
143
  }
156
144
  let completionAt = if(self.lspHook.isEnabled() && self.lspHook.insertIdentifier) {self.lspHook.at}
157
145
  let tokens = Tokenizer.tokenize(path.absolute(), code, completionAt, self.lspHook.isEnabled())
158
- let parser = Parser.make(packagePair, file, tokens, self.emitTarget != EmitBrowser, self.lspHook)
146
+ let parser = Parser.make(packagePair, path.base(), tokens, self.emitTarget != EmitBrowser, self.lspHook)
159
147
  let module = if(self.singleFilePackages.contains(packagePair)) {
160
148
  parser.parseModuleWithPackageInfo().module
161
149
  } else {
@@ -164,7 +152,6 @@ extend self: Compiler {
164
152
  let result = module.Module(
165
153
  imports = [...coreImports, ...module.imports]
166
154
  )
167
- self.parsedModules = self.parsedModules.add(packageName + ":" + moduleName, result)
168
155
  result
169
156
  }
170
157
 
@@ -180,34 +167,25 @@ extend self: Compiler {
180
167
  }
181
168
 
182
169
  resolve(packagePair: PackagePair, moduleName: String): Module {
183
- let packageName = packagePair.groupName()
184
- self.resolvedModules.get(packageName + ":" + moduleName).else:
170
+ self.cache.cacheResolvedModule(self.packagePaths, packagePair, moduleName): path =>
185
171
  self.measure("Resolve", packagePair, moduleName):
186
172
 
187
173
  let module = self.parse(packagePair, moduleName, None)
188
174
  let otherModules = self.imports(module)
189
175
  let resolver = Resolver.make(packagePair, moduleName, self.lspHook)
190
- let result = resolver.resolveModule(module, otherModules)
191
-
192
- self.resolvedModules = self.resolvedModules.add(packageName + ":" + moduleName, result)
193
- result
176
+ resolver.resolveModule(module, otherModules)
194
177
  }
195
178
 
196
179
  derive(packagePair: PackagePair, moduleName: String): Module {
197
- let packageName = packagePair.groupName()
198
- self.derivedModules.get(packageName + ":" + moduleName).else:
180
+ self.cache.cacheDerivedModule(self.packagePaths, packagePair, moduleName): path =>
199
181
  self.measure("Derive", packagePair, moduleName):
200
182
 
201
183
  let module = self.resolve(packagePair, moduleName)
202
- let result = Deriver.make().deriveModule(module)
203
-
204
- self.derivedModules = self.derivedModules.add(packageName + ":" + moduleName, result)
205
- result
184
+ Deriver.make().deriveModule(module)
206
185
  }
207
186
 
208
187
  infer(packagePair: PackagePair, moduleName: String): Module {
209
- let packageName = packagePair.groupName()
210
- self.inferredModules.get(packageName + ":" + moduleName).else:
188
+ self.cache.cacheInferredModule(self.packagePaths, packagePair, moduleName): path =>
211
189
  self.measure("Infer", packagePair, moduleName):
212
190
 
213
191
  let module = self.derive(packagePair, moduleName)
@@ -216,17 +194,12 @@ extend self: Compiler {
216
194
  }
217
195
  let inference = Inference.make([module, ...otherModules], self.lspHook)
218
196
  let inferredModule = inference.inferModule(module, otherModules)
219
- let result = Dictionaries.make([module, ...otherModules]).processModule(inferredModule, otherModules)
220
-
221
- self.inferredModules = self.inferredModules.add(packageName + ":" + moduleName, result)
222
- result
197
+ Dictionaries.make([module, ...otherModules]).processModule(inferredModule, otherModules)
223
198
  }
224
199
 
225
200
  emit(packagePair: PackagePair, moduleName: String, isMainModule: Bool): Unit {
226
- let packageName = packagePair.groupName()
227
- if(self.emittedModules.contains(packageName + ":" + moduleName)) {} else:
201
+ self.cache.cacheEmittedModule(self.packagePaths, packagePair, moduleName): path =>
228
202
  self.measure("Emit", packagePair, moduleName):
229
- self.emittedModules = self.emittedModules.add(packageName + ":" + moduleName)
230
203
 
231
204
  let module = self.infer(packagePair, moduleName)
232
205
  let otherModules = self.imports(module).map {i =>
package/compiler/Main.ff CHANGED
@@ -8,6 +8,7 @@ import Builder
8
8
  import Dependencies
9
9
  import JsEmitter
10
10
  import Inference
11
+ import ModuleCache
11
12
  import LspHook
12
13
 
13
14
  data MainCommand {
@@ -54,6 +55,7 @@ main(system: NodeSystem): Unit {
54
55
  tempPath = system.path(".firefly").slash("temporary")
55
56
  jsOutputPath = system.path(".firefly").path("output").path(targetName)
56
57
  printMeasurements = False
58
+ moduleCache = ModuleCache.empty(0)
57
59
  )
58
60
  }
59
61
 
@@ -85,7 +87,18 @@ main(system: NodeSystem): Unit {
85
87
  importAndRun(fireflyPath, "build", resolvedDependencies.mainPackagePair, localMainFile, [])
86
88
 
87
89
  | CheckCommand(filePath) =>
88
- let errors = Builder.check(system, fireflyPath, system.path(filePath), None, Map.empty(), LspHook.disabled(), True)
90
+ let errors = Builder.check(
91
+ system = system
92
+ fireflyPath = fireflyPath
93
+ path = system.path(filePath)
94
+ mustContain = None
95
+ virtualFiles = Map.empty()
96
+ cache = ModuleCache.empty(1)
97
+ newVersion = 0
98
+ lspHook = LspHook.disabled()
99
+ infer = True
100
+ checkDependencies = False
101
+ )
89
102
  if(!errors.isEmpty()) {throw(CompileErrors(errors.distinct()))}
90
103
 
91
104
  | BootstrapCommand =>
@@ -108,6 +121,7 @@ main(system: NodeSystem): Unit {
108
121
  tempPath = workingDirectory.slash("output").slash("temporary")
109
122
  jsOutputPath = workingDirectory.slash("output").slash("js")
110
123
  printMeasurements = True
124
+ moduleCache = ModuleCache.empty(0)
111
125
  )
112
126
  }
113
127
 
@@ -0,0 +1,160 @@
1
+ import Syntax
2
+
3
+ class ModuleCache(
4
+ version: Int
5
+ mutable parsedModules: Map[String, Pair[Module, Int]]
6
+ mutable resolvedModules: Map[String, Pair[Module, Int]]
7
+ mutable derivedModules: Map[String, Pair[Module, Int]]
8
+ mutable inferredModules: Map[String, Pair[Module, Int]]
9
+ mutable emittedModules: Map[String, Int]
10
+ )
11
+
12
+ empty(version: Int): ModuleCache {
13
+ ModuleCache(
14
+ version = version
15
+ parsedModules = Map.empty()
16
+ resolvedModules = Map.empty()
17
+ derivedModules = Map.empty()
18
+ inferredModules = Map.empty()
19
+ emittedModules = Map.empty()
20
+ )
21
+ }
22
+
23
+ extend self: ModuleCache {
24
+
25
+ remove(paths: List[Path]) {
26
+ if(!paths.isEmpty()):
27
+ let keys = paths.map {_.absolute()}
28
+ self.parsedModules = self.parsedModules.removeList(keys)
29
+ self.resolvedModules = self.resolvedModules.removeList(keys)
30
+ self.derivedModules = self.derivedModules.removeList(keys)
31
+ self.inferredModules = self.inferredModules.removeList(keys)
32
+ self.emittedModules = self.emittedModules.removeList(keys)
33
+ }
34
+
35
+ without(newVersion: Int, path: Path): ModuleCache {
36
+ let key = path.absolute()
37
+ if(path.isFile()) {
38
+ self.ModuleCache(
39
+ version = newVersion
40
+ parsedModules = self.parsedModules.remove(key)
41
+ resolvedModules = self.resolvedModules.remove(key)
42
+ derivedModules = self.derivedModules.remove(key)
43
+ inferredModules = self.inferredModules.remove(key)
44
+ emittedModules = self.emittedModules.remove(key)
45
+ )
46
+ } else {
47
+ function invalidated(p: String): Bool {
48
+ p.startsWith(key) && !p.contains(".firefly/dependencies") && !p.contains(".firefly\\dependencies")
49
+ }
50
+ self.ModuleCache(
51
+ version = newVersion
52
+ parsedModules = self.parsedModules.toList().filter {| Pair(p, _) => !invalidated(p)}.toMap()
53
+ resolvedModules = self.resolvedModules.toList().filter {| Pair(p, _) => !invalidated(p)}.toMap()
54
+ derivedModules = self.derivedModules.toList().filter {| Pair(p, _) => !invalidated(p)}.toMap()
55
+ inferredModules = self.inferredModules.toList().filter {| Pair(p, _) => !invalidated(p)}.toMap()
56
+ emittedModules = self.emittedModules.toList().filter {| Pair(p, _) => !invalidated(p)}.toMap()
57
+ )
58
+ }
59
+ }
60
+
61
+ mergeVersions(cache: ModuleCache): Unit {
62
+ self.parsedModules = mergeVersionedMap(self.parsedModules, cache.parsedModules) {_.second}
63
+ self.resolvedModules = mergeVersionedMap(self.resolvedModules, cache.resolvedModules) {_.second}
64
+ self.derivedModules = mergeVersionedMap(self.derivedModules, cache.derivedModules) {_.second}
65
+ self.inferredModules = mergeVersionedMap(self.inferredModules, cache.inferredModules) {_.second}
66
+ self.emittedModules = mergeVersionedMap(self.emittedModules, cache.emittedModules) {_}
67
+ }
68
+
69
+ cacheParsedModule(
70
+ packagePaths: Map[PackagePair, Path]
71
+ packagePair: PackagePair
72
+ moduleName: String
73
+ body: Path => Module
74
+ ): Module {
75
+ let path = modulePath(packagePaths, packagePair, moduleName)
76
+ self.parsedModules.get(path.absolute()).map {_.first}.else:
77
+ let result = body(path)
78
+ self.parsedModules = self.parsedModules.add(path.absolute(), Pair(result, self.version))
79
+ result
80
+ }
81
+
82
+ cacheResolvedModule(
83
+ packagePaths: Map[PackagePair, Path]
84
+ packagePair: PackagePair
85
+ moduleName: String
86
+ body: Path => Module
87
+ ): Module {
88
+ let path = modulePath(packagePaths, packagePair, moduleName)
89
+ self.resolvedModules.get(path.absolute()).map {_.first}.else:
90
+ let result = body(path)
91
+ self.resolvedModules = self.resolvedModules.add(path.absolute(), Pair(result, self.version))
92
+ result
93
+ }
94
+
95
+ cacheDerivedModule(
96
+ packagePaths: Map[PackagePair, Path]
97
+ packagePair: PackagePair
98
+ moduleName: String
99
+ body: Path => Module
100
+ ): Module {
101
+ let path = modulePath(packagePaths, packagePair, moduleName)
102
+ self.derivedModules.get(path.absolute()).map {_.first}.else:
103
+ let result = body(path)
104
+ self.derivedModules = self.derivedModules.add(path.absolute(), Pair(result, self.version))
105
+ result
106
+ }
107
+
108
+ cacheInferredModule(
109
+ packagePaths: Map[PackagePair, Path]
110
+ packagePair: PackagePair
111
+ moduleName: String
112
+ body: Path => Module
113
+ ): Module {
114
+ let path = modulePath(packagePaths, packagePair, moduleName)
115
+ self.inferredModules.get(path.absolute()).map {_.first}.else:
116
+ let result = body(path)
117
+ self.inferredModules = self.inferredModules.add(path.absolute(), Pair(result, self.version))
118
+ result
119
+ }
120
+
121
+ cacheEmittedModule(
122
+ packagePaths: Map[PackagePair, Path]
123
+ packagePair: PackagePair
124
+ moduleName: String
125
+ body: Path => Unit
126
+ ): Unit {
127
+ let path = modulePath(packagePaths, packagePair, moduleName)
128
+ if(!self.emittedModules.contains(path.absolute())):
129
+ self.emittedModules = self.emittedModules.add(path.absolute(), self.version)
130
+ try {
131
+ body(path)
132
+ } catchAny {error =>
133
+ self.emittedModules = self.emittedModules.remove(path.absolute())
134
+ error.rethrow()
135
+ } grab()
136
+ }
137
+
138
+ }
139
+
140
+ mergeVersionedMap[T](oldMap: Map[String, T], newMap: Map[String, T], getVersion: T => Int): Map[String, T] {
141
+ mutable result = newMap
142
+ oldMap.each {k, v =>
143
+ if(!newMap.get(k).any {getVersion(_) >= getVersion(v)}) {
144
+ result = result.add(k, v)
145
+ }
146
+ }
147
+ result
148
+ }
149
+
150
+ modulePath(
151
+ packagePaths: Map[PackagePair, Path]
152
+ packagePair: PackagePair
153
+ moduleName: String
154
+ ): Path {
155
+ let packagePath = packagePaths.get(packagePair).else {
156
+ panic("Internal error - package path missing: " + packagePair.groupName())
157
+ }
158
+ let file = moduleName + ".ff"
159
+ packagePath.slash(file)
160
+ }
package/core/Map.ff CHANGED
@@ -41,6 +41,14 @@ extend self[K: Order, V]: Map[K, V] {
41
41
  }
42
42
  Map(result)
43
43
  }
44
+
45
+ removeList(keys: List[K]): Map[K, V] {
46
+ mutable result = self.redBlack
47
+ keys.each {k =>
48
+ result = RbMap.delete(k, result)
49
+ }
50
+ Map(result)
51
+ }
44
52
 
45
53
  pairs(): List[Pair[K, V]] { // TODO: Remove this method (use toList)
46
54
  self.toList()
package/core/Path.ff CHANGED
@@ -119,6 +119,10 @@ extend self: Path {
119
119
  go(Some(self), parts.reverse())
120
120
  }
121
121
 
122
+ contains(parts: List[String]): Bool {
123
+ self.endsWith(parts) || self.parent().any {_.contains(parts)}
124
+ }
125
+
122
126
  base(): String
123
127
  target node async """
124
128
  import * as path from 'path'
@@ -0,0 +1,88 @@
1
+ data Expression {
2
+ EVariable(name: String)
3
+ EApply(function: Expression, argument: Expression)
4
+ ELambda(variable: String, body: Expression)
5
+ EAnnotate(type: Type, expression: Expression)
6
+ }
7
+
8
+ data Type(name: String, typeArguments: List[Type])
9
+
10
+ data Context(variables: Map[String, Type])
11
+
12
+ data TypeError(message: String)
13
+
14
+
15
+ check(c0: Context, t0: Type, e0: Expression): Unit {
16
+ e0.{
17
+ | ELambda(x1, e1) =>
18
+ t0.{
19
+ | Type("Function", [t2, t3]) =>
20
+ let c2 = c0.Context(variables = c0.variables.add(x1, t2))
21
+ check(c2, t3, e1)
22
+ t3
23
+ | _ =>
24
+ throw(TypeError("Function type not expected here"))
25
+ }
26
+ | _ =>
27
+ let t1 = infer(c0, e0)
28
+ checkSame(t0, t1)
29
+ }
30
+ }
31
+
32
+
33
+ infer(c0: Context, e0: Expression): Type {
34
+ e0.{
35
+ | EVariable(x1) =>
36
+ c0.variables.get(x1).{
37
+ | None =>
38
+ throw(TypeError("Variable not in scope: " + x1))
39
+ | Some(t1) =>
40
+ t1
41
+ }
42
+ | EApply(e1, e2) =>
43
+ let t1 = infer(c0, e1)
44
+ t1.{
45
+ | Type("Function", [t2, t3]) =>
46
+ check(c0, t2, e2)
47
+ t3
48
+ | _ =>
49
+ throw(TypeError("Can't call non-functions"))
50
+ }
51
+ | ELambda _ =>
52
+ throw(TypeError("Lambda functions must be explicitly typed"))
53
+ | EAnnotate(t1, e1) =>
54
+ checkType(t1)
55
+ check(c0, t1, e1)
56
+ t1
57
+ }
58
+ }
59
+
60
+
61
+ checkSame(t1: Type, t2: Type): Unit {
62
+ if(t1.name != t2.name) {throw(TypeError("Type mismatch: " + t1.name + " vs. " + t2.name + ""))}
63
+ if(t1.typeArguments.size() != t2.typeArguments.size()) {
64
+ throw(TypeError(
65
+ "Type mismatch: " + t1.name + " with " +
66
+ t1.typeArguments.size() + " vs. " + t2.typeArguments.size() +
67
+ " type arguments"
68
+ ))
69
+ }
70
+ t1.typeArguments.zip(t2.typeArguments).each {| Pair(t3, t4) =>
71
+ checkSame(t3, t4)
72
+ }
73
+ }
74
+
75
+
76
+ checkType(t0: Type): Unit {
77
+ t0.{
78
+ | Type("Function", [t1, t2]) =>
79
+ checkType(t1)
80
+ checkType(t2)
81
+ | Type("Bool", []) =>
82
+ | Type("Int", []) =>
83
+ | Type("List", [t1]) =>
84
+ checkType(t1)
85
+ | _ =>
86
+ throw(TypeError("Type is not well formed"))
87
+ }
88
+ }