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.
- package/compiler/Builder.ff +23 -13
- package/compiler/JsEmitter.ff +120 -76
- package/compiler/LspHook.ff +4 -2
- package/compiler/Main.ff +13 -7
- package/compiler/Resolver.ff +15 -15
- package/compiler/Syntax.ff +1 -0
- package/core/Array.ff +6 -4
- package/core/Int.ff +12 -12
- package/core/List.ff +6 -4
- package/experimental/benchmarks/ListGrab.ff +23 -0
- package/experimental/benchmarks/ListGrab.java +55 -0
- package/experimental/benchmarks/Pyrotek45.ff +30 -0
- package/experimental/benchmarks/Pyrotek45.java +64 -0
- package/experimental/tests/TestJson.ff +26 -0
- package/lsp/Handler.ff +55 -59
- package/lsp/SignatureHelpHandler.ff +5 -4
- package/lsp/TestReferences.ff +15 -0
- package/lsp/TestReferencesCase.ff +8 -0
- package/output/js/ff/compiler/Builder.mjs +50 -44
- package/output/js/ff/compiler/Dependencies.mjs +0 -2
- package/output/js/ff/compiler/Deriver.mjs +16 -140
- package/output/js/ff/compiler/Dictionaries.mjs +8 -222
- package/output/js/ff/compiler/Environment.mjs +12 -154
- package/output/js/ff/compiler/Inference.mjs +127 -1013
- package/output/js/ff/compiler/JsEmitter.mjs +434 -2344
- package/output/js/ff/compiler/JsImporter.mjs +0 -12
- package/output/js/ff/compiler/LspHook.mjs +20 -446
- package/output/js/ff/compiler/Main.mjs +96 -550
- package/output/js/ff/compiler/Parser.mjs +36 -356
- package/output/js/ff/compiler/Patterns.mjs +20 -200
- package/output/js/ff/compiler/Resolver.mjs +26 -340
- package/output/js/ff/compiler/Substitution.mjs +2 -160
- package/output/js/ff/compiler/Syntax.mjs +449 -3293
- package/output/js/ff/compiler/Token.mjs +9 -1095
- package/output/js/ff/compiler/Tokenizer.mjs +4 -2
- package/output/js/ff/compiler/Unification.mjs +26 -360
- package/output/js/ff/compiler/Wildcards.mjs +0 -86
- package/output/js/ff/compiler/Workspace.mjs +8 -96
- package/output/js/ff/core/Array.mjs +15 -8
- package/output/js/ff/core/AssetSystem.mjs +4 -14
- package/output/js/ff/core/Bool.mjs +0 -12
- package/output/js/ff/core/Core.mjs +0 -30
- package/output/js/ff/core/Int.mjs +24 -24
- package/output/js/ff/core/IntMap.mjs +0 -8
- package/output/js/ff/core/Json.mjs +0 -40
- package/output/js/ff/core/List.mjs +23 -32
- package/output/js/ff/core/Lock.mjs +0 -10
- package/output/js/ff/core/Map.mjs +0 -24
- package/output/js/ff/core/Option.mjs +10 -286
- package/output/js/ff/core/Ordering.mjs +16 -158
- package/output/js/ff/core/Pair.mjs +2 -34
- package/output/js/ff/core/Path.mjs +2 -28
- package/output/js/ff/core/Random.mjs +4 -4
- package/output/js/ff/core/RbMap.mjs +56 -644
- package/output/js/ff/core/Show.mjs +0 -16
- package/output/js/ff/core/Stream.mjs +14 -144
- package/output/js/ff/core/StringMap.mjs +0 -8
- package/output/js/ff/core/Try.mjs +4 -108
- package/output/js/ff/core/Unit.mjs +2 -16
- package/package.json +1 -1
- package/postgresql/Pg.ff +23 -23
- package/vscode/package.json +1 -1
package/compiler/Builder.ff
CHANGED
|
@@ -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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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(
|
package/compiler/JsEmitter.ff
CHANGED
|
@@ -223,9 +223,9 @@ extend self: JsEmitter {
|
|
|
223
223
|
}.join("\n")
|
|
224
224
|
let body = self.emitTailCall {
|
|
225
225
|
let casesString = cases.map {
|
|
226
|
-
|
|
226
|
+
self.emitCase(escapedArguments, _, [], [], True, True, async)
|
|
227
227
|
}.join("\n")
|
|
228
|
-
|
|
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 {
|
|
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(
|
|
499
|
+
| ESequential(_, EVariant(_, "ff:core/Unit.Unit", _, _), after) =>
|
|
494
500
|
self.emitStatements(after, last, async)
|
|
495
|
-
| ESequential(
|
|
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 {
|
|
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 {
|
|
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(
|
|
606
|
-
Argument(_, _, ECall(
|
|
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(
|
|
618
|
-
Argument(_, _, ECall(
|
|
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(
|
|
635
|
-
Argument(_, _, ECall(
|
|
636
|
-
Argument(_, _, ECall(
|
|
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(
|
|
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
|
-
|
|
676
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
|
710
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
|
737
|
-
let
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
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) {
|
|
745
|
-
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
|
|
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) {
|
|
752
|
-
|
|
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) {
|
|
762
|
-
|
|
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)
|
package/compiler/LspHook.ff
CHANGED
|
@@ -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),
|
|
114
|
+
} catch {| CommandLineError(message), _ =>
|
|
114
115
|
Log.debug(message)
|
|
115
|
-
} catch {| CompileError(at, message),
|
|
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]
|
|
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
|
-
|
|
|
173
|
-
"You must specify a Firefly file (.ff) as the argument to
|
|
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", _] =>
|
package/compiler/Resolver.ff
CHANGED
|
@@ -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 {
|
|
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 {
|
|
57
|
-
traits = module.traits.map {
|
|
58
|
-
instances = module.instances.map {
|
|
59
|
-
extends = module.extends.map {
|
|
60
|
-
lets = module.lets.map {
|
|
61
|
-
functions = module.functions.map {
|
|
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) {
|
package/compiler/Syntax.ff
CHANGED
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
|
-
|
|
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
|
-
|
|
31
|
-
mutable n =
|
|
32
|
-
while {
|
|
33
|
-
result
|
|
34
|
-
n
|
|
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
|
-
|
|
41
|
-
mutable n =
|
|
42
|
-
while {
|
|
43
|
-
result
|
|
44
|
-
n
|
|
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 {
|