firefly-compiler 0.4.20 → 0.4.21

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 (62) hide show
  1. package/compiler/Builder.ff +23 -13
  2. package/compiler/JsEmitter.ff +120 -76
  3. package/compiler/LspHook.ff +4 -2
  4. package/compiler/Main.ff +13 -7
  5. package/compiler/Resolver.ff +15 -15
  6. package/compiler/Syntax.ff +1 -0
  7. package/core/Array.ff +6 -4
  8. package/core/Int.ff +12 -12
  9. package/core/List.ff +6 -4
  10. package/experimental/benchmarks/ListGrab.ff +23 -0
  11. package/experimental/benchmarks/ListGrab.java +55 -0
  12. package/experimental/benchmarks/Pyrotek45.ff +30 -0
  13. package/experimental/benchmarks/Pyrotek45.java +64 -0
  14. package/experimental/tests/TestJson.ff +26 -0
  15. package/lsp/Handler.ff +55 -59
  16. package/lsp/SignatureHelpHandler.ff +5 -4
  17. package/lsp/TestReferences.ff +15 -0
  18. package/lsp/TestReferencesCase.ff +8 -0
  19. package/output/js/ff/compiler/Builder.mjs +50 -44
  20. package/output/js/ff/compiler/Dependencies.mjs +0 -2
  21. package/output/js/ff/compiler/Deriver.mjs +16 -140
  22. package/output/js/ff/compiler/Dictionaries.mjs +8 -222
  23. package/output/js/ff/compiler/Environment.mjs +12 -154
  24. package/output/js/ff/compiler/Inference.mjs +127 -1013
  25. package/output/js/ff/compiler/JsEmitter.mjs +434 -2344
  26. package/output/js/ff/compiler/JsImporter.mjs +0 -12
  27. package/output/js/ff/compiler/LspHook.mjs +20 -446
  28. package/output/js/ff/compiler/Main.mjs +96 -550
  29. package/output/js/ff/compiler/Parser.mjs +36 -356
  30. package/output/js/ff/compiler/Patterns.mjs +20 -200
  31. package/output/js/ff/compiler/Resolver.mjs +26 -340
  32. package/output/js/ff/compiler/Substitution.mjs +2 -160
  33. package/output/js/ff/compiler/Syntax.mjs +449 -3293
  34. package/output/js/ff/compiler/Token.mjs +9 -1095
  35. package/output/js/ff/compiler/Tokenizer.mjs +4 -2
  36. package/output/js/ff/compiler/Unification.mjs +26 -360
  37. package/output/js/ff/compiler/Wildcards.mjs +0 -86
  38. package/output/js/ff/compiler/Workspace.mjs +8 -96
  39. package/output/js/ff/core/Array.mjs +15 -8
  40. package/output/js/ff/core/AssetSystem.mjs +4 -14
  41. package/output/js/ff/core/Bool.mjs +0 -12
  42. package/output/js/ff/core/Core.mjs +0 -30
  43. package/output/js/ff/core/Int.mjs +24 -24
  44. package/output/js/ff/core/IntMap.mjs +0 -8
  45. package/output/js/ff/core/Json.mjs +0 -40
  46. package/output/js/ff/core/List.mjs +23 -32
  47. package/output/js/ff/core/Lock.mjs +0 -10
  48. package/output/js/ff/core/Map.mjs +0 -24
  49. package/output/js/ff/core/Option.mjs +10 -286
  50. package/output/js/ff/core/Ordering.mjs +16 -158
  51. package/output/js/ff/core/Pair.mjs +2 -34
  52. package/output/js/ff/core/Path.mjs +2 -28
  53. package/output/js/ff/core/Random.mjs +4 -4
  54. package/output/js/ff/core/RbMap.mjs +56 -644
  55. package/output/js/ff/core/Show.mjs +0 -16
  56. package/output/js/ff/core/Stream.mjs +14 -144
  57. package/output/js/ff/core/StringMap.mjs +0 -8
  58. package/output/js/ff/core/Try.mjs +4 -108
  59. package/output/js/ff/core/Unit.mjs +2 -16
  60. package/package.json +1 -1
  61. package/postgresql/Pg.ff +23 -23
  62. package/vscode/package.json +1 -1
@@ -88,15 +88,17 @@ check(
88
88
  system: NodeSystem
89
89
  fireflyPath: Path
90
90
  path: Path
91
+ mustContain: Option[String]
91
92
  virtualFiles: Map[String, String]
92
93
  lspHook: LspHook
93
94
  infer: Bool
94
- ) {
95
+ ): List[CompileError] {
95
96
  let packages = path.isDirectory().{
96
- | True => findPackageFiles(path)
97
+ | True => findPackageFiles(path, mustContain)
97
98
  | False {path.endsWith([".firefly", "package.ff"])} => [PackageFiles(path.parent().grab(), Some(path), [])]
98
99
  | False => [PackageFiles(path.parent().grab(), None, [path])]
99
100
  }
101
+ let errors = Array.make()
100
102
 
101
103
  packages.filter {!_.files.isEmpty()}.each {package =>
102
104
  let firstFile = package.files.grabFirst()
@@ -118,13 +120,19 @@ check(
118
120
  )
119
121
  package.files.each {file =>
120
122
  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
- }
123
+ try {
124
+ if(infer) {
125
+ compiler.infer(resolvedDependencies.mainPackagePair, localFile.dropLast(".ff".size()))
126
+ } else {
127
+ compiler.resolve(resolvedDependencies.mainPackagePair, localFile.dropLast(".ff".size()))
128
+ }
129
+ Unit
130
+ } catch {| CompileError(_, _) @ c, error =>
131
+ errors.push(c)
132
+ } grab()
126
133
  }
127
134
  }
135
+ errors.drain()
128
136
 
129
137
  }
130
138
 
@@ -135,8 +143,8 @@ capability PackageFiles(
135
143
  )
136
144
 
137
145
 
138
- findPackageFiles(path: Path): List[PackageFiles] {
139
- let files = findFireflyFiles(path)
146
+ findPackageFiles(path: Path, mustContain: Option[String]): List[PackageFiles] {
147
+ let files = findFireflyFiles(path, mustContain)
140
148
  let split = files.partition {_.endsWith([".firefly", "package.ff"])}
141
149
  let packageFiles = split.first
142
150
  mutable singleFiles = split.second
@@ -153,15 +161,17 @@ findPackageFiles(path: Path): List[PackageFiles] {
153
161
  [...multiFileProjects, ...singleFileProjects]
154
162
  }
155
163
 
156
- findFireflyFiles(path: Path): List[Path] {
164
+ findFireflyFiles(path: Path, mustContain: Option[String]): List[Path] {
157
165
  let split = path.entries().toList().partition {_.isDirectory()}
158
166
  let directories = split.first.map {_.path()}.filter {_.base().all {c =>
159
167
  c == '.' || c.isAsciiLower() || c.isAsciiDigit()
160
168
  }}
161
- let fireflyFiles = split.second.map {_.path()}.filter {
162
- _.extension() == ".ff"
169
+ let fireflyFiles = split.second.map {_.path()}.filter {file =>
170
+ file.extension() == ".ff" && mustContain.all {s =>
171
+ file.readText().contains(s)
172
+ }
163
173
  }
164
- [...fireflyFiles, ...directories.flatMap {findFireflyFiles(_)}]
174
+ [...fireflyFiles, ...directories.flatMap {findFireflyFiles(_, mustContain)}]
165
175
  }
166
176
 
167
177
  internalCreateExecutable(
@@ -223,9 +223,9 @@ extend self: JsEmitter {
223
223
  }.join("\n")
224
224
  let body = self.emitTailCall {
225
225
  let casesString = cases.map {
226
- "{\n" + self.emitCase(escapedArguments, _, True, True, async) + "\n}"
226
+ self.emitCase(escapedArguments, _, [], [], True, True, async)
227
227
  }.join("\n")
228
- "{\n" + shadowingWorkaround + "\n" + casesString + "\n}"
228
+ shadowingWorkaround + "\n" + casesString
229
229
  }
230
230
  signature + " {\n" + body + "\n}"
231
231
  }
@@ -343,7 +343,7 @@ extend self: JsEmitter {
343
343
  Patterns.convertAndCheck(self.otherModules, cases)
344
344
  let arguments = cases.grab(0).patterns.pairs().map {"_" + (_.first + 1)}
345
345
  let escapedArguments = arguments.map(escapeKeyword) // emitCase arguments must be preescaped
346
- let caseStrings = cases.map {"{\n" + self.emitCase(escapedArguments, _, True, True, newAsync) + "\n}"}
346
+ let caseStrings = cases.map {self.emitCase(escapedArguments, _, [], [], True, True, newAsync)}
347
347
  let prefix = if(newAsync) {"async "} else {""}
348
348
  "(" + prefix + "(" + [...escapedArguments, ...controller].join(", ") + ") => " +
349
349
  "{\n" + caseStrings.join("\n") + "\n})"
@@ -356,6 +356,12 @@ extend self: JsEmitter {
356
356
  "(" + operator + self.emitArgument(at, value, async) + ")"
357
357
  | ECall(at, StaticCall(operator, _, _), _, [], [left, right], _) {!operator.grabFirst().isAsciiLetter()} =>
358
358
  "(" + self.emitArgument(at, left, async) + " " + operator + " " + self.emitArgument(at, right, async) + ")"
359
+ | ECall(at, StaticCall("ff:core/List.List_grab", _, _), _, _, [Argument(_, _, EVariable(_, x1)), Argument(_, _, EVariable(_, x2))], _) =>
360
+ "(" + escapeResolved(x1) + "[" + escapeResolved(x2) + "] ?? " +
361
+ "ff_core_List.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
362
+ | ECall(at, StaticCall("ff:core/Array.Array_grab", _, _), _, _, [Argument(_, _, EVariable(_, x1)), Argument(_, _, EVariable(_, x2))], _) =>
363
+ "(" + escapeResolved(x1) + ".array[" + escapeResolved(x2) + "] ?? " +
364
+ "ff_core_Array.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
359
365
  | ECall(at, StaticCall("ff:unsafejs/UnsafeJs.import", _, _), _, _, [Argument(_, _, EString(_, url))], _) =>
360
366
  self.jsImporter.add(url.replace("\"", ""))
361
367
  | ECall(at, StaticCall("ff:unsafejs/UnsafeJs.await", _, _), _, _, [Argument(_, _, body)], _) =>
@@ -490,9 +496,9 @@ extend self: JsEmitter {
490
496
  self.emitStatements(body, last, async)
491
497
  | EVariant(at, "ff:core/Unit.Unit", _, _) =>
492
498
  ""
493
- | ESequential(at, EVariant(at, "ff:core/Unit.Unit", _, _), after) =>
499
+ | ESequential(_, EVariant(_, "ff:core/Unit.Unit", _, _), after) =>
494
500
  self.emitStatements(after, last, async)
495
- | ESequential(at, before, EVariant(at, "ff:core/Unit.Unit", _, _)) =>
501
+ | ESequential(_, before, EVariant(_, "ff:core/Unit.Unit", _, _)) =>
496
502
  self.emitStatements(before, False, async)
497
503
  | ESequential(at, before, after) =>
498
504
  self.emitStatements(before, False, async) + ";\n" + self.emitStatements(after, last, async)
@@ -561,7 +567,7 @@ extend self: JsEmitter {
561
567
  Patterns.convertAndCheck(self.otherModules, cases)
562
568
  if(!last) {"do "}.else {""} +
563
569
  "{\nconst _1 = " + self.emitTerm(value, async) + ";\n" +
564
- cases.map {"{\n" + self.emitCase(["_1"], _, True, last, async) + "\n}"}.join("\n") +
570
+ cases.map {self.emitCase(["_1"], _, [], [], True, last, async)}.join("\n") +
565
571
  "\n}" + if(!last) {" while(false)"}.else {""}
566
572
  | _ =>
567
573
  detectIfElse(term).{
@@ -594,16 +600,16 @@ extend self: JsEmitter {
594
600
  let arguments = ["_exception.value_", "_error"]
595
601
  cases.{
596
602
  | [case] =>
597
- self.emitCase(arguments, case, False, last, catchAsync)
603
+ self.emitCase(arguments, case, [], [], False, last, catchAsync)
598
604
  | cs =>
599
605
  let caseStrings =
600
- cases.map {"{\n" + self.emitCase(arguments, _, True, last, catchAsync) + "\n}"}
606
+ cases.map {self.emitCase(arguments, _, [], [], True, last, catchAsync)}
601
607
  if(last) {caseStrings.join("\n")} else {"do {\n" + caseStrings.join("\n") + "\n} while(false)"}
602
608
  }
603
609
  }
604
610
  term.{
605
- | ECall(at, StaticCall("ff:core/Try.Try_finally", _, _), _, _, [
606
- Argument(_, _, ECall(at, StaticCall("ff:core/Core.try", _, _), _, _, [
611
+ | ECall(_, StaticCall("ff:core/Try.Try_finally", _, _), _, _, [
612
+ Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
607
613
  Argument(_, _, ELambda(_, Lambda(_, tryEffect, [MatchCase(_, [], [], tryBody)])))
608
614
  ], _))
609
615
  Argument(_, _, ELambda(_, Lambda(_, finallyEffect, [MatchCase(_, [], [], finallyBody)])))
@@ -614,8 +620,8 @@ extend self: JsEmitter {
614
620
  "try {\n" + self.emitStatements(tryBody, last, tryAsync) +
615
621
  "\n} finally {\n" + self.emitStatements(finallyBody, last, finallyAsync) + "\n}"
616
622
  )
617
- | ECall(at, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
618
- Argument(_, _, ECall(at, StaticCall("ff:core/Core.try", _, _), _, _, [
623
+ | ECall(_, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
624
+ Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
619
625
  Argument(_, _, ELambda(_, Lambda(_, tryEffect, [MatchCase(_, [], [], tryBody)])))
620
626
  ], _))
621
627
  Argument(_, _, ELambda(_, Lambda(_, catchEffect, cases)))
@@ -631,9 +637,9 @@ extend self: JsEmitter {
631
637
  emitCatch(catchEffect, cases) +
632
638
  "\n}"
633
639
  )
634
- | ECall(at, StaticCall("ff:core/Try.Try_finally", _, _), _, _, [
635
- Argument(_, _, ECall(at, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
636
- Argument(_, _, ECall(at, StaticCall("ff:core/Core.try", _, _), _, _, [
640
+ | ECall(_, StaticCall("ff:core/Try.Try_finally", _, _), _, _, [
641
+ Argument(_, _, ECall(_, StaticCall("ff:core/Try.Try_catch", _, _), _, _, [
642
+ Argument(_, _, ECall(_, StaticCall("ff:core/Core.try", _, _), _, _, [
637
643
  Argument(_, _, ELambda(_, Lambda(_, tryEffect, [MatchCase(_, [], [], tryBody)])))
638
644
  ], _))
639
645
  Argument(_, _, ELambda(_, Lambda(_, catchEffect, cases)))
@@ -657,7 +663,23 @@ extend self: JsEmitter {
657
663
  }
658
664
  }
659
665
 
660
- emitCase(arguments: List[String], matchCase: MatchCase, jump: Bool, last: Bool, async: Bool): String {
666
+ emitCase(
667
+ arguments: List[String]
668
+ matchCase: MatchCase
669
+ conditions: List[String]
670
+ variables: List[String]
671
+ jump: Bool
672
+ last: Bool
673
+ async: Bool
674
+ ): String {
675
+ function emitWrapper(code: String): String {
676
+ if(conditions.isEmpty()) {"{\n"} else {
677
+ "if(" + conditions.join(" && ") + ") {\n"
678
+ } +
679
+ variables.join() +
680
+ code +
681
+ "\n}"
682
+ }
661
683
  Pair(matchCase.patterns, matchCase.guards).{
662
684
  | Pair([p, ...ps], _) =>
663
685
  self.emitPattern(
@@ -665,18 +687,45 @@ extend self: JsEmitter {
665
687
  p
666
688
  arguments.dropFirst()
667
689
  matchCase.MatchCase(patterns = ps)
690
+ conditions
691
+ variables
668
692
  jump
669
693
  last
670
694
  async
671
695
  )
696
+ | Pair([], [MatchGuard(_, e, PVariant(_, "ff:core/Bool.True", _))]) {variables.isEmpty()} =>
697
+ let newCase = matchCase.MatchCase(patterns = [], guards = [])
698
+ self.emitCase([], newCase, [...conditions, self.emitTerm(e, async)], [], jump, last, async)
699
+ | Pair([], [MatchGuard(_, e, PVariant(_, "ff:core/Bool.True", _))]) =>
700
+ let newCase = matchCase.MatchCase(patterns = [], guards = [])
701
+ let code = self.emitCase([], newCase, [self.emitTerm(e, async)], [], jump, last, async)
702
+ emitWrapper(code)
672
703
  | Pair([], [guard, ...guards]) =>
673
704
  let guardName = "_guard" + (guards.size() + 1)
674
705
  let newCase = matchCase.MatchCase(patterns = [guard.pattern], guards = guards)
675
- "const " + guardName + " = " + self.emitTerm(guard.term, async) + ";\n" +
676
- self.emitCase([guardName], newCase, jump, last, async)
706
+ let code =
707
+ "const " + guardName + " = " + self.emitTerm(guard.term, async) + ";\n" +
708
+ self.emitCase([guardName], newCase, [], [], jump, last, async)
709
+ emitWrapper(code)
677
710
  | Pair([], []) =>
678
- self.emitStatements(matchCase.body, last, async) +
679
- if(jump && last) {"\nreturn"} elseIf {jump} {"\nbreak"} else {""}
711
+ let statementsCode = self.emitStatements(matchCase.body, last, async)
712
+ let lastLine = statementsCode.reverse().takeWhile {_ != '\n'}.reverse()
713
+ let returns =
714
+ lastLine.startsWith("return ") ||
715
+ lastLine.startsWith("break ") ||
716
+ lastLine.startsWith("continue ") ||
717
+ lastLine.startsWith("return;") ||
718
+ lastLine.startsWith("break;") ||
719
+ lastLine.startsWith("continue;") ||
720
+ lastLine.startsWith("throw ")
721
+ let code = statementsCode + if(jump && last && !returns) {
722
+ "\nreturn"
723
+ } elseIf {jump && !returns} {
724
+ "\nbreak"
725
+ } else {
726
+ ""
727
+ }
728
+ emitWrapper(code)
680
729
  }
681
730
  }
682
731
 
@@ -685,81 +734,78 @@ extend self: JsEmitter {
685
734
  pattern: MatchPattern
686
735
  arguments: List[String]
687
736
  matchCase: MatchCase
737
+ conditions: List[String]
738
+ variables: List[String]
688
739
  jump: Bool
689
740
  last: Bool
690
741
  async: Bool
691
742
  ): String {
692
743
  pattern.{
693
744
  | PString(_, value) =>
694
- "if(" + argument + " === " + value + ") {\n" +
695
- self.emitCase(arguments, matchCase, jump, last, async) +
696
- "\n}"
745
+ let newConditions = [...conditions, argument + " === " + value]
746
+ self.emitCase(arguments, matchCase, newConditions, variables, jump, last, async)
697
747
  | PInt(_, value) =>
698
- "if(" + argument + " === " + value + ") {\n" +
699
- self.emitCase(arguments, matchCase, jump, last, async) +
700
- "\n}"
748
+ let newConditions = [...conditions, argument + " === " + value]
749
+ self.emitCase(arguments, matchCase, newConditions, variables, jump, last, async)
701
750
  | PChar(_, value) =>
702
- "if(" + argument + " === " + charLiteralToNumber(value) + ") {\n" +
703
- self.emitCase(arguments, matchCase, jump, last, async) +
704
- "\n}"
751
+ let newConditions = [...conditions, argument + " === " + charLiteralToNumber(value)]
752
+ self.emitCase(arguments, matchCase, newConditions, variables, jump, last, async)
705
753
  | PVariable(_, None) =>
706
- self.emitCase(arguments, matchCase, jump, last, async)
754
+ self.emitCase(arguments, matchCase, conditions, variables, jump, last, async)
707
755
  | PVariable(_, Some(name)) =>
708
756
  let escaped = escapeKeyword(name)
709
- if(escaped != argument) {"const " + escaped + " = " + argument + ";\n"} else {""} +
710
- self.emitCase(arguments, matchCase, jump, last, async)
757
+ let newVariables = if(escaped != argument) {
758
+ [...variables, "const " + escaped + " = " + argument + ";\n"]
759
+ } else {variables}
760
+ self.emitCase(arguments, matchCase, conditions, newVariables, jump, last, async)
711
761
  | PVariant(_, "ff:core/Bool.False", []) =>
712
- "if(!" + argument + ") {\n" +
713
- self.emitCase(arguments, matchCase, jump, last, async) +
714
- "\n}"
762
+ self.emitCase(arguments, matchCase, [...conditions, "!" + argument], variables, jump, last, async)
715
763
  | PVariant(_, "ff:core/Bool.True", []) =>
716
- "if(" + argument + ") {\n" +
717
- self.emitCase(arguments, matchCase, jump, last, async) +
718
- "\n}"
719
- | PVariant(_, "List$Empty", []) =>
720
- mutable shortArgument = argument
721
- mutable shortCount = 0
722
- while {shortArgument.endsWith(".slice(1)")} {
723
- shortArgument = shortArgument.dropLast(".slice(1)".size())
724
- shortCount += 1
725
- }
726
- "if(" + shortArgument + ".length === " + shortCount + ") {\n" +
727
- self.emitCase(arguments, matchCase, jump, last, async) +
728
- "\n}"
729
- | PVariant(_, "List$Link", [head, tail]) =>
730
- mutable shortArgument = argument
731
- mutable shortCount = 0
732
- while {shortArgument.endsWith(".slice(1)")} {
733
- shortArgument = shortArgument.dropLast(".slice(1)".size())
734
- shortCount += 1
764
+ self.emitCase(arguments, matchCase, [...conditions, argument], variables, jump, last, async)
765
+ | PVariant(_, emptyOrLink, _) {emptyOrLink == "List$Empty" || emptyOrLink == "List$Link"} =>
766
+ mutable restPattern = None
767
+ function listPatterns(matchPattern: MatchPattern): List[MatchPattern] {
768
+ | PVariant(_, "List$Empty", []) =>
769
+ []
770
+ | PVariant(_, "List$Link", [head, tail]) =>
771
+ [head, ...listPatterns(tail)]
772
+ | p =>
773
+ restPattern = Some(p)
774
+ []
735
775
  }
736
- let newArguments = [shortArgument + "[" + shortCount + "]", argument + ".slice(1)", ...arguments]
737
- let newMatchCase = matchCase.MatchCase(patterns = [head, tail, ...matchCase.patterns])
738
- "if(" + shortArgument + ".length > " + shortCount + ") {\n" +
739
- self.emitCase(newArguments, newMatchCase, jump, last, async) +
740
- "\n}"
776
+ let patterns = listPatterns(pattern)
777
+ let itemArguments = patterns.pairs().map {| Pair(i, _) => argument + "[" + i + "]"}
778
+ let restArgument = restPattern.map {_ => argument + ".slice(" + patterns.size() + ")"}
779
+ let newArguments = [...itemArguments, ...restArgument.toList(), ...arguments]
780
+ let newMatchCase = matchCase.MatchCase(
781
+ patterns = [...patterns, ...restPattern.toList(), ...matchCase.patterns]
782
+ )
783
+ let operator = restPattern.map {_ => ">="}.else {"==="}
784
+ let newConditions = [...conditions, argument + ".length " + operator + " " + patterns.size()]
785
+ self.emitCase(newArguments, newMatchCase, newConditions, variables, jump, last, async)
741
786
  | PVariant(_, name, patterns) =>
742
787
  let processed = self.processVariantCase(name, argument)
743
788
  let newMatchCase = matchCase.MatchCase(patterns = [...patterns, ...matchCase.patterns])
744
- if(processed.loneVariant) {""} else {
745
- "if(" + argument + "." + processed.variantName + ") {\n"
746
- } +
747
- self.emitCase([...processed.arguments, ...arguments], newMatchCase, jump, last, async) +
748
- if(processed.loneVariant) {""} else {"\n}"}
789
+ let newConditions = if(processed.loneVariant) {conditions} else {
790
+ [...conditions, argument + "." + processed.variantName]
791
+ }
792
+ let newArguments = [...processed.arguments, ...arguments]
793
+ self.emitCase(newArguments, newMatchCase, newConditions, variables, jump, last, async)
749
794
  | PVariantAs(at, name, variableAt, variable) =>
750
795
  let processed = self.processVariantCase(name, argument)
751
- if(processed.loneVariant) {""} else {
752
- "if(" + argument + "." + processed.variantName + ") {\n"
753
- } +
754
- variable.map(escapeKeyword).filter {_ != argument}.map {
755
- "const " + _ + " = " + argument + ";\n"
756
- }.else {""} +
757
- self.emitCase(arguments, matchCase, jump, last, async) +
758
- if(processed.loneVariant) {""} else {"\n}"}
796
+ let newConditions = if(processed.loneVariant) {conditions} else {
797
+ [...conditions, argument + "." + processed.variantName]
798
+ }
799
+ let newVariables = variable.map(escapeKeyword).filter {_ != argument}.map {
800
+ [...variables, "const " + _ + " = " + argument + ";\n"]
801
+ }.else {[]}
802
+ self.emitCase(arguments, matchCase, newConditions, newVariables, jump, last, async)
759
803
  | PAlias(_, pattern, variable) =>
760
804
  let escaped = escapeKeyword(variable)
761
- if(escaped != argument) {"const " + escaped + " = " + argument + ";\n"} else {""} +
762
- self.emitPattern(argument, pattern, arguments, matchCase, jump, last, async)
805
+ let newVariables = if(escaped != argument) {
806
+ [...variables, "const " + escaped + " = " + argument + ";\n"]
807
+ } else {variables}
808
+ self.emitPattern(argument, pattern, arguments, matchCase, conditions, newVariables, jump, last, async)
763
809
  }
764
810
  }
765
811
 
@@ -771,8 +817,6 @@ extend self: JsEmitter {
771
817
  }
772
818
 
773
819
  processVariantCase(name: String, argument: String): ProcessedVariantCase {
774
- if(name == "List$Empty") {ProcessedVariantCase(name, False, False, [])} else:
775
- if(name == "List$Link") {ProcessedVariantCase(name, False, False, [argument + "[0]", argument + ".slice(1)"])} else:
776
820
  let variantNameUnqualified = name.reverse().takeWhile {_ != '.'}.reverse()
777
821
  let variantName = escapeKeyword(variantNameUnqualified)
778
822
  let moduleName = name.dropLast(variantNameUnqualified.size() + 1)
@@ -96,6 +96,7 @@ class ResultHook {
96
96
  ResolveSymbolHook(
97
97
  symbol: SymbolHook
98
98
  annotation: Option[Type]
99
+ topLevel: Bool
99
100
  )
100
101
  ResolveTypeHook(
101
102
  types: Map[String, String]
@@ -110,6 +111,7 @@ class ResultHook {
110
111
  ResolveSignatureHook(
111
112
  signature: Signature
112
113
  isInstanceMethod: Bool
114
+ topLevel: Bool
113
115
  )
114
116
  ResolveVariantFieldHook(
115
117
  symbol: SymbolHook
@@ -193,8 +195,8 @@ showHook(hook: ResultHook): String {
193
195
  | ParseSymbolBegin => "ParseSymbolBegin(...)"
194
196
  | ParseSymbolEnd(name, kind, selectionStart, selectionEnd, start, end) => "ParseSymbolEnd(...)"
195
197
  | ResolveConstraintHook(symbol, constrant) => "ResolveConstraintHook(...)"
196
- | ResolveSignatureHook(signature, _) => "ResolveSignatureHook(...)"
197
- | ResolveSymbolHook(symbol, annotation) => "ResolveSymbolHook(...)"
198
+ | ResolveSignatureHook(signature, _, _) => "ResolveSignatureHook(...)"
199
+ | ResolveSymbolHook(symbol, annotation, _) => "ResolveSymbolHook(...)"
198
200
  | ResolveTypeHook(types, typeGenerics, symbol, explicitType) => "ResolveTypeHook(...)"
199
201
  | ResolveVariantFieldHook(symbol, type, commonField) => "ResolveVariantFieldHook(...)"
200
202
  }
package/compiler/Main.ff CHANGED
@@ -82,8 +82,9 @@ main(system: NodeSystem): Unit {
82
82
  importAndRun(fireflyPath, "build", resolvedDependencies.mainPackagePair, localMainFile, [])
83
83
 
84
84
  | CheckCommand(filePath) =>
85
- Builder.check(system, fireflyPath, system.path(filePath), Map.empty(), LspHook.disabled(), True)
86
-
85
+ let errors = Builder.check(system, fireflyPath, system.path(filePath), None, Map.empty(), LspHook.disabled(), True)
86
+ if(!errors.isEmpty()) {throw(CompileErrors(errors))}
87
+
87
88
  | BootstrapCommand =>
88
89
  let workingDirectory = system.path(".")
89
90
  Builder.build(
@@ -110,11 +111,16 @@ main(system: NodeSystem): Unit {
110
111
  try {
111
112
  let command = parseCommandLine(system.arguments())
112
113
  runCommand(command)
113
- } catch {| CommandLineError(message), error =>
114
+ } catch {| CommandLineError(message), _ =>
114
115
  Log.debug(message)
115
- } catch {| CompileError(at, message), error =>
116
+ } catch {| CompileError(at, message), _ =>
116
117
  Log.debug(message)
117
118
  Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
119
+ } catch {| CompileErrors(errors), _ =>
120
+ errors.map {| CompileError(at, message) =>
121
+ Log.debug(message)
122
+ Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
123
+ }
118
124
  } grab()
119
125
  }
120
126
 
@@ -164,13 +170,13 @@ parseCommandLine(arguments: List[String]): MainCommand {
164
170
  }
165
171
  | ["check", ...checkArguments] =>
166
172
  checkArguments.{
167
- | [fileName] {fileName.removeLast(".ff") | Some(_)} =>
173
+ | [fileName] =>
168
174
  CheckCommand(fileName)
169
175
  | [_, _, ...] => throw(CommandLineError(
170
176
  "You must only specify a single argument to check." + usageString
171
177
  ))
172
- | _ => throw(CommandLineError(
173
- "You must specify a Firefly file (.ff) as the argument to build." + usageString
178
+ | [] => throw(CommandLineError(
179
+ "You must specify a Firefly file (.ff) or directory as the argument to check." + usageString
174
180
  ))
175
181
  }
176
182
  | ["bootstrap", _] =>
@@ -49,16 +49,16 @@ extend self: Resolver {
49
49
 
50
50
  resolveModule(module: Module, otherModules: List[Module]): Module {
51
51
  let moduleNamespace =
52
- module.file.replace("\\", "/").reverse().takeWhile { _ != '/' }.reverse().takeWhile { _ != '.' }
52
+ module.file.replace("\\", "/").reverse().takeWhile {_ != '/'}.reverse().takeWhile {_ != '.'}
53
53
  let self2 = self.processImports(module.imports, otherModules)
54
54
  let self3 = self2.processDefinitions(module, None)
55
55
  let module2 = module.Module(
56
- types = module.types.map { self3.resolveTypeDefinition(_) }
57
- traits = module.traits.map { self3.resolveTraitDefinition(_) }
58
- instances = module.instances.map { self3.resolveInstanceDefinition(_) }
59
- extends = module.extends.map { self3.resolveExtendDefinition(_) }
60
- lets = module.lets.map { self3.resolveLetDefinition(_, True) }
61
- functions = module.functions.map { self3.resolveFunctionDefinition(_, True, False) }
56
+ types = module.types.map {self3.resolveTypeDefinition(_)}
57
+ traits = module.traits.map {self3.resolveTraitDefinition(_)}
58
+ instances = module.instances.map {self3.resolveInstanceDefinition(_)}
59
+ extends = module.extends.map {self3.resolveExtendDefinition(_)}
60
+ lets = module.lets.map {self3.resolveLetDefinition(_, True)}
61
+ functions = module.functions.map {self3.resolveFunctionDefinition(_, True, False)}
62
62
  )
63
63
  module2.instances.each {_.typeArguments.each {self3.checkInstanceType(_)}}
64
64
  module2
@@ -135,7 +135,7 @@ extend self: Resolver {
135
135
  resolveTypeDefinition(definition: DType): DType {
136
136
  if(self.lspHook.isAt(definition.at)) {
137
137
  self.lspHook.emit(
138
- ResolveSymbolHook(SymbolHook(definition.name, definition.at, definition.at), None)
138
+ ResolveSymbolHook(SymbolHook(definition.name, definition.at, definition.at), None, topLevel = True)
139
139
  )
140
140
  }
141
141
  let generics = definition.generics.map {g => Pair(g, g)}.toMap()
@@ -168,7 +168,7 @@ extend self: Resolver {
168
168
  variants = definition.variants.map {v =>
169
169
  if(self.lspHook.isAt(v.at)) {
170
170
  self.lspHook.emit(
171
- ResolveSymbolHook(SymbolHook(v.name, v.at, v.at), None)
171
+ ResolveSymbolHook(SymbolHook(v.name, v.at, v.at), None, topLevel = True)
172
172
  )
173
173
  }
174
174
  v.Variant(fields = v.fields.map {f =>
@@ -191,7 +191,7 @@ extend self: Resolver {
191
191
  if(self.lspHook.isAt(definition.at) || self.lspHook.isDefinedAt(definition.at)) {
192
192
  self.lspHook.emit(
193
193
  ResolveSymbolHook(
194
- SymbolHook(definition.name, definition.at, definition.at), None
194
+ SymbolHook(definition.name, definition.at, definition.at), None, topLevel = True
195
195
  )
196
196
  )
197
197
  }
@@ -221,7 +221,7 @@ extend self: Resolver {
221
221
  if(self.lspHook.isAt(definition.at) || self.lspHook.isDefinedAt(traitDefinedAt)) {
222
222
  self.lspHook.emit(
223
223
  ResolveSymbolHook(
224
- SymbolHook(definition.traitName, definition.at, traitDefinedAt), None
224
+ SymbolHook(definition.traitName, definition.at, traitDefinedAt), None, topLevel = True
225
225
  )
226
226
  )
227
227
  }
@@ -266,7 +266,7 @@ extend self: Resolver {
266
266
  if(self.lspHook.isAt(definition.at) || self.lspHook.isDefinedAt(definition.at)) {
267
267
  self.lspHook.emit(
268
268
  ResolveSymbolHook(
269
- SymbolHook(definition.name, definition.at, definition.at), None
269
+ SymbolHook(definition.name, definition.at, definition.at), None, topLevel = topLevel
270
270
  )
271
271
  )
272
272
  }
@@ -287,7 +287,7 @@ extend self: Resolver {
287
287
  if(self.lspHook.isAt(e.at) || self.lspHook.isDefinedAt(at)) {
288
288
  self.lspHook.emit(
289
289
  ResolveSymbolHook(
290
- SymbolHook(e.name, e.at, at), None
290
+ SymbolHook(e.name, e.at, at), None, topLevel = True
291
291
  )
292
292
  )
293
293
  }
@@ -369,7 +369,7 @@ extend self: Resolver {
369
369
  if(self.lspHook.isAt(e.at) || self.lspHook.isDefinedAt(e.at)) {
370
370
  self.lspHook.emit(
371
371
  ResolveSymbolHook(
372
- SymbolHook(e.name, e.at, e.at), None
372
+ SymbolHook(e.name, e.at, e.at), None, topLevel = False
373
373
  )
374
374
  )
375
375
  }
@@ -492,7 +492,7 @@ extend self: Resolver {
492
492
  resolveSignature(signature: Signature, topLevel: Bool, isInstanceMethod: Bool): Signature {
493
493
  if(self.lspHook.isAt(signature.at) || self.lspHook.isDefinedAt(signature.at)) {
494
494
  self.lspHook.emit(
495
- ResolveSignatureHook(signature, isInstanceMethod)
495
+ ResolveSignatureHook(signature, isInstanceMethod, topLevel = topLevel)
496
496
  )
497
497
  }
498
498
  let newSignature = if(topLevel) {
@@ -9,6 +9,7 @@ extend self: Location {
9
9
  }
10
10
 
11
11
  data CompileError(at: Location, message: String)
12
+ data CompileErrors(errors: List[CompileError])
12
13
 
13
14
  data ModuleWithPackageInfo(
14
15
  packageInfo: Option[PackageInfo]
package/core/Array.ff CHANGED
@@ -35,10 +35,7 @@ extend self[T]: Array[T] {
35
35
 
36
36
  grab(index: Int): T
37
37
  target js sync """
38
- if(index_ < 0 || index_ >= self_.array.length) {
39
- ff_core_Try.internalThrowGrabException_()
40
- }
41
- return self_.array[index_]
38
+ return self_.array[index_] ?? internalGrab_(self_, index_);
42
39
  """
43
40
 
44
41
  grabFirst(): T {self.grab(0)}
@@ -261,3 +258,8 @@ sortRange[T](array: Array[T], compare: (T, T) => Ordering, start: Int, end: Int)
261
258
  }
262
259
  }
263
260
  }
261
+
262
+ internalGrab[T](self: Array[T], index: Int): T
263
+ target js sync """
264
+ return index_ < 0 || index_ >= self_.array.length ? ff_core_Try.internalThrowGrabException_() : self_.array[index_];
265
+ """
package/core/Int.ff CHANGED
@@ -27,23 +27,23 @@ extend self: Int {
27
27
  target js sync "return signed_ ? self_ >> bits_ : self_ >>> bits_;"
28
28
 
29
29
  to(inclusiveEnd: Int): List[Int] {
30
- mutable result = []
31
- mutable n = inclusiveEnd
32
- while {self <= n} {
33
- result = [n, ...result]
34
- n -= 1
30
+ let result = Array.make()
31
+ mutable n = self
32
+ while {n <= inclusiveEnd} {
33
+ result.push(n)
34
+ n += 1
35
35
  }
36
- result
36
+ result.drain()
37
37
  }
38
38
 
39
39
  until(exclusiveEnd: Int): List[Int] {
40
- mutable result = []
41
- mutable n = exclusiveEnd
42
- while {self < n} {
43
- result = [n - 1, ...result]
44
- n -= 1
40
+ let result = Array.make()
41
+ mutable n = self
42
+ while {n < exclusiveEnd} {
43
+ result.push(n)
44
+ n += 1
45
45
  }
46
- result
46
+ result.drain()
47
47
  }
48
48
 
49
49
  min(that: Int): Int {