firefly-compiler 0.4.20 → 0.4.22

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 (72) hide show
  1. package/compiler/Builder.ff +23 -13
  2. package/compiler/Dictionaries.ff +10 -10
  3. package/compiler/Inference.ff +167 -148
  4. package/compiler/JsEmitter.ff +122 -78
  5. package/compiler/LspHook.ff +6 -3
  6. package/compiler/Main.ff +24 -10
  7. package/compiler/Patterns.ff +1 -1
  8. package/compiler/Resolver.ff +167 -159
  9. package/compiler/Substitution.ff +2 -2
  10. package/compiler/Syntax.ff +1 -0
  11. package/compiler/Unification.ff +1 -1
  12. package/core/Array.ff +6 -4
  13. package/core/Int.ff +12 -12
  14. package/core/List.ff +6 -4
  15. package/core/Map.ff +8 -0
  16. package/core/Set.ff +7 -0
  17. package/experimental/benchmarks/ListGrab.ff +23 -0
  18. package/experimental/benchmarks/ListGrab.java +55 -0
  19. package/experimental/benchmarks/Pyrotek45.ff +30 -0
  20. package/experimental/benchmarks/Pyrotek45.java +64 -0
  21. package/experimental/tests/TestJson.ff +26 -0
  22. package/lsp/Handler.ff +65 -61
  23. package/lsp/SignatureHelpHandler.ff +5 -4
  24. package/lsp/TestReferences.ff +15 -0
  25. package/lsp/TestReferencesCase.ff +8 -0
  26. package/output/js/ff/compiler/Builder.mjs +50 -44
  27. package/output/js/ff/compiler/Dependencies.mjs +0 -2
  28. package/output/js/ff/compiler/Deriver.mjs +16 -140
  29. package/output/js/ff/compiler/Dictionaries.mjs +24 -238
  30. package/output/js/ff/compiler/Environment.mjs +12 -154
  31. package/output/js/ff/compiler/Inference.mjs +207 -1069
  32. package/output/js/ff/compiler/JsEmitter.mjs +434 -2342
  33. package/output/js/ff/compiler/JsImporter.mjs +0 -12
  34. package/output/js/ff/compiler/LspHook.mjs +20 -446
  35. package/output/js/ff/compiler/Main.mjs +110 -553
  36. package/output/js/ff/compiler/Parser.mjs +36 -356
  37. package/output/js/ff/compiler/Patterns.mjs +24 -204
  38. package/output/js/ff/compiler/Resolver.mjs +428 -554
  39. package/output/js/ff/compiler/Substitution.mjs +6 -164
  40. package/output/js/ff/compiler/Syntax.mjs +449 -3293
  41. package/output/js/ff/compiler/Token.mjs +9 -1095
  42. package/output/js/ff/compiler/Tokenizer.mjs +4 -2
  43. package/output/js/ff/compiler/Unification.mjs +30 -364
  44. package/output/js/ff/compiler/Wildcards.mjs +0 -86
  45. package/output/js/ff/compiler/Workspace.mjs +8 -96
  46. package/output/js/ff/core/Array.mjs +15 -8
  47. package/output/js/ff/core/AssetSystem.mjs +4 -14
  48. package/output/js/ff/core/Bool.mjs +0 -12
  49. package/output/js/ff/core/Core.mjs +0 -30
  50. package/output/js/ff/core/Int.mjs +24 -24
  51. package/output/js/ff/core/IntMap.mjs +0 -8
  52. package/output/js/ff/core/Json.mjs +0 -40
  53. package/output/js/ff/core/List.mjs +23 -32
  54. package/output/js/ff/core/Lock.mjs +0 -10
  55. package/output/js/ff/core/Map.mjs +26 -24
  56. package/output/js/ff/core/Option.mjs +10 -286
  57. package/output/js/ff/core/Ordering.mjs +16 -158
  58. package/output/js/ff/core/Pair.mjs +2 -34
  59. package/output/js/ff/core/Path.mjs +2 -28
  60. package/output/js/ff/core/Random.mjs +4 -4
  61. package/output/js/ff/core/RbMap.mjs +56 -644
  62. package/output/js/ff/core/Set.mjs +16 -0
  63. package/output/js/ff/core/Show.mjs +0 -16
  64. package/output/js/ff/core/Stream.mjs +14 -144
  65. package/output/js/ff/core/StringMap.mjs +0 -8
  66. package/output/js/ff/core/Try.mjs +4 -108
  67. package/output/js/ff/core/Unit.mjs +2 -16
  68. package/package.json +1 -1
  69. package/postgresql/Pg.ff +23 -23
  70. package/vscode/package.json +1 -1
  71. package/bin/firefly.mjs +0 -2
  72. package/guide/Main.ff +0 -22
@@ -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)
@@ -855,9 +899,9 @@ extractTypeName(type: Type): String {
855
899
 
856
900
  firstTypeName(types: List[Type]): String {
857
901
  types.grabFirst().{
858
- | TConstructor t => t
902
+ | TConstructor t => t.name
859
903
  | TVariable t => fail(t.at, " is still a unification variable")
860
- }.name
904
+ }
861
905
  }
862
906
 
863
907
  makeDictionaryName(traitName: String, typeName: String): String {
@@ -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
@@ -171,6 +173,7 @@ class ResultHook {
171
173
  instantiated: Box[Option[Instantiated]]
172
174
  )
173
175
  InferRecordFieldHook(
176
+ usageAt: Location
174
177
  unification: Unification
175
178
  environment: Environment
176
179
  expected: Type
@@ -186,15 +189,15 @@ showHook(hook: ResultHook): String {
186
189
  | InferLookupHook(unification, environment, expected, selfVariable, symbol, instantiated) => "InferLookupHook(...)"
187
190
  | InferParameterHook(unification, environment, parameter, missing) => "InferParameterHook(...)"
188
191
  | InferPatternHook(unification, environment, expected, pattern) => "InferPatternHook(...)"
189
- | InferRecordFieldHook(unification, environment, expected, recordType, fieldName) => "InferRecordFieldHook(...)"
192
+ | InferRecordFieldHook(usageAt, unification, environment, expected, recordType, fieldName) => "InferRecordFieldHook(...)"
190
193
  | InferSequentialStartHook(unification, term, missing) => "InferSequentialStartHook(...)"
191
194
  | InferTermHook(unification, environment, expected, term, recordType, missing) => "InferTermHook(...)"
192
195
  | ParseArgumentHook(callAt, argumentIndex, parameterName) => "ParseArgumentHook(...)"
193
196
  | ParseSymbolBegin => "ParseSymbolBegin(...)"
194
197
  | ParseSymbolEnd(name, kind, selectionStart, selectionEnd, start, end) => "ParseSymbolEnd(...)"
195
198
  | ResolveConstraintHook(symbol, constrant) => "ResolveConstraintHook(...)"
196
- | ResolveSignatureHook(signature, _) => "ResolveSignatureHook(...)"
197
- | ResolveSymbolHook(symbol, annotation) => "ResolveSymbolHook(...)"
199
+ | ResolveSignatureHook(signature, _, _) => "ResolveSignatureHook(...)"
200
+ | ResolveSymbolHook(symbol, annotation, _) => "ResolveSymbolHook(...)"
198
201
  | ResolveTypeHook(types, typeGenerics, symbol, explicitType) => "ResolveTypeHook(...)"
199
202
  | ResolveVariantFieldHook(symbol, type, commonField) => "ResolveVariantFieldHook(...)"
200
203
  }
package/compiler/Main.ff CHANGED
@@ -63,7 +63,10 @@ main(system: NodeSystem): Unit {
63
63
  prepareFireflyDirectory(system.path("."))
64
64
  let localMainFile = system.path(mainFile).base()
65
65
  buildScript(localMainFile, resolvedDependencies.mainPackagePair, EmitNode, resolvedDependencies)
66
- importAndRun(fireflyPath, "node", resolvedDependencies.mainPackagePair, localMainFile, arguments)
66
+ if(!importAndRun(fireflyPath, "node", resolvedDependencies.mainPackagePair, localMainFile, arguments)) {
67
+ let at = Location(system.path(mainFile + ".ff").absolute(), 1, 1)
68
+ throw(CompileError(at, "This module does not contain a 'nodeMain' function"))
69
+ }
67
70
 
68
71
  | BrowserCommand(mainFile) =>
69
72
  let resolvedDependencies = Dependencies.process(system.httpClient(), system.path(mainFile + ".ff"))
@@ -82,8 +85,9 @@ main(system: NodeSystem): Unit {
82
85
  importAndRun(fireflyPath, "build", resolvedDependencies.mainPackagePair, localMainFile, [])
83
86
 
84
87
  | CheckCommand(filePath) =>
85
- Builder.check(system, fireflyPath, system.path(filePath), Map.empty(), LspHook.disabled(), True)
86
-
88
+ let errors = Builder.check(system, fireflyPath, system.path(filePath), None, Map.empty(), LspHook.disabled(), True)
89
+ if(!errors.isEmpty()) {throw(CompileErrors(errors.distinct()))}
90
+
87
91
  | BootstrapCommand =>
88
92
  let workingDirectory = system.path(".")
89
93
  Builder.build(
@@ -110,11 +114,16 @@ main(system: NodeSystem): Unit {
110
114
  try {
111
115
  let command = parseCommandLine(system.arguments())
112
116
  runCommand(command)
113
- } catch {| CommandLineError(message), error =>
117
+ } catch {| CommandLineError(message), _ =>
114
118
  Log.debug(message)
115
- } catch {| CompileError(at, message), error =>
119
+ } catch {| CompileError(at, message), _ =>
116
120
  Log.debug(message)
117
121
  Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
122
+ } catch {| CompileErrors(errors), _ =>
123
+ errors.map {| CompileError(at, message) =>
124
+ Log.debug(message)
125
+ Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
126
+ }
118
127
  } grab()
119
128
  }
120
129
 
@@ -164,13 +173,13 @@ parseCommandLine(arguments: List[String]): MainCommand {
164
173
  }
165
174
  | ["check", ...checkArguments] =>
166
175
  checkArguments.{
167
- | [fileName] {fileName.removeLast(".ff") | Some(_)} =>
176
+ | [fileName] =>
168
177
  CheckCommand(fileName)
169
178
  | [_, _, ...] => throw(CommandLineError(
170
179
  "You must only specify a single argument to check." + usageString
171
180
  ))
172
- | _ => throw(CommandLineError(
173
- "You must specify a Firefly file (.ff) as the argument to build." + usageString
181
+ | [] => throw(CommandLineError(
182
+ "You must specify a Firefly file (.ff) or directory as the argument to check." + usageString
174
183
  ))
175
184
  }
176
185
  | ["bootstrap", _] =>
@@ -206,14 +215,19 @@ importAndRun(
206
215
  packagePair: PackagePair
207
216
  mainFile: String
208
217
  arguments: List[String]
209
- ): Unit
218
+ ): Bool
210
219
  target node async """
211
220
  const process = await import('process');
212
221
  const cwd = process.cwd();
213
222
  const workingDirectory = cwd.indexOf(':') == 1 ? 'file:///' + cwd : cwd;
214
223
  const packagePath = packagePair_.group_ + "/" + packagePair_.name_
215
224
  const main = await import(workingDirectory + "/.firefly/output/" + target_ + "/" + packagePath + "/" + mainFile_ + ".mjs");
216
- await main.$run$(fireflyPath_, arguments_)
225
+ if(typeof main.$run$ !== 'undefined') {
226
+ await main.$run$(fireflyPath_, arguments_);
227
+ return true;
228
+ } else {
229
+ return false;
230
+ }
217
231
  """
218
232
 
219
233
  prepareFireflyDirectory(path: Path) {
@@ -72,7 +72,7 @@ convert(modules: Map[String, Module], cases: List[MatchCase]): List[PatternCaseI
72
72
  None
73
73
  | PVariant p =>
74
74
  let fields = p.patterns.map(convertPattern).pairs().collect {
75
- | Pair(i, Some(p)) => Some(Pair("" + i, p))
75
+ | Pair(i, Some(info)) => Some(Pair("" + i, info))
76
76
  | _ => None
77
77
  }
78
78
  Some(PatternInfo(unqualifiedName(p.name), otherVariants(p.name), fields))