@fable-org/fable-library-ts 2.4.0 → 2.4.1
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/BigInt.ts +34 -32
- package/CHANGELOG.md +16 -1
- package/Double.ts +4 -2
- package/Event.ts +9 -1
- package/List.ts +17 -4
- package/MailboxProcessor.ts +12 -6
- package/Observable.ts +5 -3
- package/RegExp.ts +47 -23
- package/Seq.ts +88 -0
- package/Set.ts +16 -3
- package/String.ts +3 -1
- package/Types.ts +13 -13
- package/Util.ts +7 -2
- package/package.json +1 -1
package/BigInt.ts
CHANGED
|
@@ -135,12 +135,12 @@ export function toIntN_unchecked(bits: number, x: bigint, signed: boolean): bigi
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export function toIntN(bits: number, x: bigint, signed: boolean): bigint {
|
|
138
|
-
|
|
139
|
-
if (
|
|
138
|
+
const truncated = signed ? BigInt.asIntN(bits, x) : BigInt.asUintN(bits, x);
|
|
139
|
+
if (truncated !== x) {
|
|
140
140
|
const s = signed ? "a signed" : "an unsigned";
|
|
141
141
|
throw new Exception(`Value was either too large or too small for ${s} ${bits}-bit integer.`);
|
|
142
142
|
}
|
|
143
|
-
return
|
|
143
|
+
return truncated;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export function toInt8(x: bigint): int8 { return Number(toIntN(8, x, true)); }
|
|
@@ -176,6 +176,9 @@ export function toFloat64(x: bigint): float64 { return Number(x); }
|
|
|
176
176
|
export function toDecimal(x: bigint): decimal {
|
|
177
177
|
const isNegative = x < zero;
|
|
178
178
|
const bits = abs(x);
|
|
179
|
+
if ((bits >> 96n) !== zero) {
|
|
180
|
+
throw new Exception("Value was either too large or too small for a Decimal.");
|
|
181
|
+
}
|
|
179
182
|
const low = Number(BigInt.asUintN(32, bits));
|
|
180
183
|
const mid = Number(BigInt.asUintN(32, bits >> 32n));
|
|
181
184
|
const high = Number(BigInt.asUintN(32, bits >> 64n));
|
|
@@ -228,6 +231,8 @@ export function divRem(x: bigint, y: bigint, out?: FSharpRef<bigint>): bigint |
|
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
export function greatestCommonDivisor(x: bigint, y: bigint): bigint {
|
|
234
|
+
x = abs(x);
|
|
235
|
+
y = abs(y);
|
|
231
236
|
while (y > zero) {
|
|
232
237
|
const q = x / y;
|
|
233
238
|
const r = x - q * y;
|
|
@@ -237,8 +242,17 @@ export function greatestCommonDivisor(x: bigint, y: bigint): bigint {
|
|
|
237
242
|
return x;
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
// Number of bits in the binary representation of x (x must be positive)
|
|
246
|
+
function bitLength(x: bigint): number {
|
|
247
|
+
const hex = x.toString(16);
|
|
248
|
+
return (hex.length - 1) * 4 + (32 - Math.clz32(fromHexCode(hex.charCodeAt(0))));
|
|
249
|
+
}
|
|
250
|
+
|
|
240
251
|
export function getBitLength(x: bigint): int64 {
|
|
241
|
-
|
|
252
|
+
if (x < zero) {
|
|
253
|
+
x = -x - one; // two's complement bit length excluding the sign bit
|
|
254
|
+
}
|
|
255
|
+
return x === zero ? zero : BigInt(bitLength(x));
|
|
242
256
|
}
|
|
243
257
|
|
|
244
258
|
export function log2(x: bigint): float64 {
|
|
@@ -274,7 +288,10 @@ export function log(x: bigint, base: float64): float64 {
|
|
|
274
288
|
}
|
|
275
289
|
|
|
276
290
|
export function ilog2(x: bigint): bigint {
|
|
277
|
-
|
|
291
|
+
if (x < zero) {
|
|
292
|
+
throw new Exception("Value must be non-negative.");
|
|
293
|
+
}
|
|
294
|
+
return x === zero ? zero : BigInt(bitLength(x) - 1);
|
|
278
295
|
}
|
|
279
296
|
|
|
280
297
|
// export function copySign
|
|
@@ -305,35 +322,20 @@ function fromHexCode(code: number): number {
|
|
|
305
322
|
|
|
306
323
|
function toSignedBytes(x: bigint, isBigEndian: boolean): Uint8Array {
|
|
307
324
|
const isNeg = x < 0n;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
x = x
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const first = hex.charCodeAt(0);
|
|
318
|
-
const isLow = 48 <= first && first <= 55; // 0..7
|
|
319
|
-
const start = (isNeg && isLow) || (!isNeg && !isLow) ? 1 : 0;
|
|
320
|
-
const bytes = new Uint8Array(start + (len + odd) / 2);
|
|
321
|
-
const inc = isBigEndian ? 1 : -1;
|
|
322
|
-
let pos = isBigEndian ? 0 : bytes.length - 1;
|
|
323
|
-
if (start > 0) {
|
|
324
|
-
bytes[pos] = isNeg ? 255 : 0;
|
|
325
|
-
pos += inc;
|
|
326
|
-
}
|
|
327
|
-
if (odd > 0) {
|
|
328
|
-
bytes[pos] = fromHexCode(first);
|
|
329
|
-
pos += inc;
|
|
325
|
+
const sentinel = isNeg ? -1n : 0n;
|
|
326
|
+
const bytes: number[] = []; // little-endian
|
|
327
|
+
do {
|
|
328
|
+
bytes.push(Number(BigInt.asUintN(8, x)));
|
|
329
|
+
x = x >> 8n;
|
|
330
|
+
} while (x !== sentinel);
|
|
331
|
+
// extra byte if the top byte's sign bit would misrepresent the sign
|
|
332
|
+
if (isNeg !== (bytes[bytes.length - 1] > 127)) {
|
|
333
|
+
bytes.push(isNeg ? 255 : 0);
|
|
330
334
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const b = fromHexCode(hex.charCodeAt(i + 1));
|
|
334
|
-
bytes[pos] = (a << 4) | b;
|
|
335
|
+
if (isBigEndian) {
|
|
336
|
+
bytes.reverse();
|
|
335
337
|
}
|
|
336
|
-
return bytes;
|
|
338
|
+
return new Uint8Array(bytes);
|
|
337
339
|
}
|
|
338
340
|
|
|
339
341
|
function fromSignedBytes(bytes: ArrayLike<uint8>, isBigEndian: boolean) {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
last_commit_released:
|
|
2
|
+
last_commit_released: d6ae6bd3790b57b31941a118cdffaeb6a59155c3
|
|
3
3
|
updaters:
|
|
4
4
|
- package.json:
|
|
5
5
|
file: package.json
|
|
@@ -15,6 +15,21 @@ All notable changes to this project will be documented in this file.
|
|
|
15
15
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
16
16
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
17
17
|
|
|
18
|
+
## 2.4.1 - 2026-07-09
|
|
19
|
+
|
|
20
|
+
### 🐞 Bug Fixes
|
|
21
|
+
|
|
22
|
+
* MailboxProcessor dropping falsy messages and unit replies ([8299b2e7](https://github.com/fable-compiler/Fable/commit/8299b2e7f690e9136906653155883a08607aa4a4))
|
|
23
|
+
* *(all)* Add `Seq.enumerateTryWith` for try/with in seq, list, array comprehensions (#4750) ([448c90d7](https://github.com/fable-compiler/Fable/commit/448c90d7ccba80ee3d3c38432a2a1d1407f6dc69))
|
|
24
|
+
* *(js/ts)* Match .NET NaN semantics in JS comparison, min and max ([7e5106f9](https://github.com/fable-compiler/Fable/commit/7e5106f9d93cb55ae4aca4656868fa0c53493991))
|
|
25
|
+
* *(js/ts)* BigInteger byte-array corruption, checked conversions, gcd, log2 (#4743) ([f3cb7f2b](https://github.com/fable-compiler/Fable/commit/f3cb7f2bfc9e46e558763867d6c1ba323bcf281c))
|
|
26
|
+
* *(js/ts)* Observable/Event dropping null/undefined/unit values (#4742) ([75bff73b](https://github.com/fable-compiler/Fable/commit/75bff73be3743fe0af37f99c541b10f6978150f3))
|
|
27
|
+
* *(js/ts)* EndsWith regression for empty pattern with non-ordinal comparison ([7793ed7b](https://github.com/fable-compiler/Fable/commit/7793ed7b0fc1f33bf3da6c0ea31607e172726267))
|
|
28
|
+
* *(js/ts/python)* Quote strings with the %A format specifier (#4749) ([75321f3b](https://github.com/fable-compiler/Fable/commit/75321f3b2cb70ea345a0a45d6343cbb6e0de466f))
|
|
29
|
+
* *(ts/js)* `Regex.Replace` count/substitutions and `Regex.Split` semantics (#4741) ([d6ae6bd3](https://github.com/fable-compiler/Fable/commit/d6ae6bd3790b57b31941a118cdffaeb6a59155c3))
|
|
30
|
+
|
|
31
|
+
<strong><small>[View changes on Github](https://github.com/fable-compiler/Fable/compare/7f915f1dd66b9a5fbbd56f858b07d39b98519b65..d6ae6bd3790b57b31941a118cdffaeb6a59155c3)</small></strong>
|
|
32
|
+
|
|
18
33
|
## 2.4.0 - 2026-07-05
|
|
19
34
|
|
|
20
35
|
### 🚀 Features
|
package/Double.ts
CHANGED
|
@@ -36,18 +36,20 @@ export function isInfinity(x: number) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function max(x: number, y: number): number {
|
|
39
|
-
return x
|
|
39
|
+
return Math.max(x, y);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function min(x: number, y: number): number {
|
|
43
|
-
return x
|
|
43
|
+
return Math.min(x, y);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export function maxMagnitude(x: number, y: number): number {
|
|
47
|
+
if (Number.isNaN(x) || Number.isNaN(y)) { return NaN; }
|
|
47
48
|
return Math.abs(x) > Math.abs(y) ? x : y;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export function minMagnitude(x: number, y: number): number {
|
|
52
|
+
if (Number.isNaN(x) || Number.isNaN(y)) { return NaN; }
|
|
51
53
|
return Math.abs(x) < Math.abs(y) ? x : y;
|
|
52
54
|
}
|
|
53
55
|
|
package/Event.ts
CHANGED
|
@@ -35,8 +35,16 @@ export class Event$2<Delegate extends Function, Args> {
|
|
|
35
35
|
public Trigger(value: Args): void;
|
|
36
36
|
public Trigger(sender: any, value: Args): void
|
|
37
37
|
public Trigger(senderOrValue: any, valueOrUndefined?: Args): void {
|
|
38
|
+
// Disambiguate the overloads by arity, not by value: a two-argument call may
|
|
39
|
+
// legitimately pass `undefined` as the event value (e.g. unit events).
|
|
38
40
|
let sender: any = null;
|
|
39
|
-
|
|
41
|
+
let value: Args;
|
|
42
|
+
if (arguments.length <= 1) {
|
|
43
|
+
value = senderOrValue as Args;
|
|
44
|
+
} else {
|
|
45
|
+
sender = senderOrValue;
|
|
46
|
+
value = valueOrUndefined as Args;
|
|
47
|
+
}
|
|
40
48
|
this.delegates.forEach(f => { f(sender, value) });
|
|
41
49
|
}
|
|
42
50
|
}
|
package/List.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { IComparer, IEqualityComparer, isArrayLike, MutableArray, Exception, defaultOf, toIterator, compare, structuralHash, equals, IDisposable, disposeSafe, IEnumerator, getEnumerator } from "./Util.ts";
|
|
3
|
+
import { Record, toString } from "./Types.ts";
|
|
3
4
|
import { defaultArg, some, value as value_1, Option } from "./Option.ts";
|
|
4
|
-
import { IComparer, IEqualityComparer, disposeSafe, isArrayLike, MutableArray, Exception, IDisposable, defaultOf, toIterator, getEnumerator, IEnumerator, compare, structuralHash, equals } from "./Util.ts";
|
|
5
5
|
import { float64, int32 } from "./Int32.ts";
|
|
6
|
-
import { Record } from "./Types.ts";
|
|
7
6
|
import { class_type, record_type, option_type, TypeInfo } from "./Reflection.ts";
|
|
8
7
|
import { SR_inputSequenceTooLong, SR_inputSequenceEmpty, SR_inputMustBeNonNegative, SR_notEnoughElements, SR_differentLengths, SR_keyNotFoundAlt, SR_indexOutOfBounds, SR_inputWasEmpty } from "./Global.ts";
|
|
9
8
|
import { KeyNotFoundException_$ctor_Z721C83C5 } from "./System.Collections.Generic.ts";
|
|
@@ -20,7 +19,21 @@ export class FSharpList<T> extends Record implements Iterable<T> {
|
|
|
20
19
|
}
|
|
21
20
|
toString(): string {
|
|
22
21
|
const xs: FSharpList<T> = this;
|
|
23
|
-
|
|
22
|
+
let result = "[";
|
|
23
|
+
let first = true;
|
|
24
|
+
const enumerator: IEnumerator<T> = getEnumerator(xs);
|
|
25
|
+
try {
|
|
26
|
+
while (enumerator["System.Collections.IEnumerator.MoveNext"]()) {
|
|
27
|
+
let x: T = (undefined as any), matchValue: any = (undefined as any), s: string = (undefined as any);
|
|
28
|
+
const x_1: T = enumerator["System.Collections.Generic.IEnumerator`1.get_Current"]();
|
|
29
|
+
result = ((first ? result : (result + "; ")) + ((x = x_1, (matchValue = x, (typeof matchValue === "string") ? ((s = (matchValue as string), ("\"" + s) + "\"")) : toString(x)))));
|
|
30
|
+
first = false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
disposeSafe(enumerator as IDisposable);
|
|
35
|
+
}
|
|
36
|
+
return result + "]";
|
|
24
37
|
}
|
|
25
38
|
Equals(other: any): boolean {
|
|
26
39
|
const xs: FSharpList<T> = this;
|
package/MailboxProcessor.ts
CHANGED
|
@@ -28,7 +28,7 @@ class MailboxQueue<Msg> {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
public tryGet() {
|
|
31
|
+
public tryGet(): { value: Msg } | undefined {
|
|
32
32
|
if (this.firstAndLast) {
|
|
33
33
|
const value = this.firstAndLast[0].value;
|
|
34
34
|
if (this.firstAndLast[0].next) {
|
|
@@ -36,7 +36,9 @@ class MailboxQueue<Msg> {
|
|
|
36
36
|
} else {
|
|
37
37
|
delete this.firstAndLast;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
// Wrap the value so falsy messages (0, false, "", null, undefined/unit)
|
|
40
|
+
// can be distinguished from an empty queue.
|
|
41
|
+
return { value };
|
|
40
42
|
}
|
|
41
43
|
return void 0;
|
|
42
44
|
}
|
|
@@ -64,11 +66,11 @@ export class MailboxProcessor<Msg> {
|
|
|
64
66
|
|
|
65
67
|
function __processEvents<Msg>($this: MailboxProcessor<Msg>) {
|
|
66
68
|
if ($this.continuation) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
+
const dequeued = $this.messages.tryGet();
|
|
70
|
+
if (dequeued !== void 0) {
|
|
69
71
|
const cont = $this.continuation;
|
|
70
72
|
delete $this.continuation;
|
|
71
|
-
cont(value);
|
|
73
|
+
cont(dequeued.value);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -97,15 +99,19 @@ export function postAndAsyncReply<Reply, Msg>(
|
|
|
97
99
|
buildMessage: (c: AsyncReplyChannel<Reply>) => Msg,
|
|
98
100
|
) {
|
|
99
101
|
let result: Reply;
|
|
102
|
+
// Use an explicit flag because the reply value itself may be undefined
|
|
103
|
+
// (e.g. AsyncReplyChannel<unit>), which must still complete the async.
|
|
104
|
+
let replied = false;
|
|
100
105
|
let continuation: Continuation<Reply>;
|
|
101
106
|
function checkCompletion() {
|
|
102
|
-
if (
|
|
107
|
+
if (replied && continuation !== void 0) {
|
|
103
108
|
continuation(result);
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
const reply = {
|
|
107
112
|
reply: (res: Reply) => {
|
|
108
113
|
result = res;
|
|
114
|
+
replied = true;
|
|
109
115
|
checkCompletion();
|
|
110
116
|
},
|
|
111
117
|
};
|
package/Observable.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { FSharpChoice$2_$union, Choice_tryValueIfChoice1Of2, Choice_tryValueIfChoice2Of2 } from "./Choice.ts";
|
|
3
|
-
import { Option, value } from "./Option.ts";
|
|
3
|
+
import { Option, some, value } from "./Option.ts";
|
|
4
4
|
import { IDisposable } from "./Util.ts";
|
|
5
5
|
|
|
6
6
|
export interface IObserver<T> {
|
|
@@ -56,7 +56,7 @@ export function choose<T, U>(chooser: (x: T) => Option<U>, source: IObservable<T
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function filter<T>(predicate: (x: T) => boolean, source: IObservable<T>): IObservable<T> {
|
|
59
|
-
return choose((x) => predicate(x) ? x :
|
|
59
|
+
return choose((x) => predicate(x) ? some(x) : undefined, source);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function map<T, U>(mapping: (x: T) => U, source: IObservable<T>): IObservable<U> {
|
|
@@ -120,11 +120,13 @@ export function merge<T>(source1: IObservable<T>, source2: IObservable<T>): IObs
|
|
|
120
120
|
export function pairwise<T>(source: IObservable<T>): IObservable<[T, T]> {
|
|
121
121
|
return new Observable<[T, T]>((observer) => {
|
|
122
122
|
let last: T;
|
|
123
|
+
let haveLast = false;
|
|
123
124
|
return source.Subscribe(new Observer<T>((next) => {
|
|
124
|
-
if (
|
|
125
|
+
if (haveLast) {
|
|
125
126
|
observer.OnNext([last, next]);
|
|
126
127
|
}
|
|
127
128
|
last = next;
|
|
129
|
+
haveLast = true;
|
|
128
130
|
}, observer.OnError, observer.OnCompleted));
|
|
129
131
|
});
|
|
130
132
|
}
|
package/RegExp.ts
CHANGED
|
@@ -117,28 +117,32 @@ export function replace(
|
|
|
117
117
|
input = tmp;
|
|
118
118
|
limit = undefined;
|
|
119
119
|
}
|
|
120
|
-
if (typeof replacement === "
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.replace(/\$
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
if (typeof replacement === "string") {
|
|
121
|
+
const rep = replacement as string;
|
|
122
|
+
// .NET replacement patterns cannot be rewritten as JS replacement strings unambiguously
|
|
123
|
+
// (e.g. `$$0` must stay literal "$0", `$0` doesn't work with JS regex (see #1155), and
|
|
124
|
+
// `${1}` followed by a digit must not become `$1` + digit), so interpret them with an
|
|
125
|
+
// evaluator instead, honoring the `$$` escape left-to-right
|
|
126
|
+
replacement = (match: any): string =>
|
|
127
|
+
rep.replace(/\$(?:(\$)|(&)|(`)|(')|(\d+)|\{(\d+)\}|\{([^}]+)\})/g,
|
|
128
|
+
(s, dollar?: string, and?: string, before?: string, after?: string, num?: string, numBraced?: string, name?: string) => {
|
|
129
|
+
if (dollar != null) { return "$"; }
|
|
130
|
+
if (and != null) { return match[0]; }
|
|
131
|
+
if (before != null) { return match.input.substring(0, match.index); }
|
|
132
|
+
if (after != null) { return match.input.substring(match.index + match[0].length); }
|
|
133
|
+
const n = num ?? numBraced;
|
|
134
|
+
if (n != null) {
|
|
135
|
+
const i = parseInt(n, 10);
|
|
136
|
+
// Same as .NET: an unmatched group is replaced by an empty string,
|
|
137
|
+
// a nonexistent group number keeps the substitution pattern literally
|
|
138
|
+
return i < match.length ? (match[i] ?? "") : s;
|
|
139
|
+
}
|
|
140
|
+
// Named groups are `${name}` in .NET (in regex: groups are `(?<name>...)` in both)
|
|
141
|
+
return match.groups != null && (name as string) in match.groups ? (match.groups[name as string] ?? "") : s;
|
|
142
|
+
});
|
|
141
143
|
}
|
|
144
|
+
limit = limit == null ? -1 : limit;
|
|
145
|
+
return input.substring(0, offset) + input.substring(offset).replace(reg as RegExp, replacer);
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
export function split(reg: string | RegExp, input: string, limit?: number, offset: number = 0) {
|
|
@@ -148,6 +152,26 @@ export function split(reg: string | RegExp, input: string, limit?: number, offse
|
|
|
148
152
|
input = tmp;
|
|
149
153
|
limit = undefined;
|
|
150
154
|
}
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
// JS String.split(regex, limit) truncates the result and discards the remainder,
|
|
156
|
+
// whereas .NET keeps it in the last element (`limit` is the max number of elements),
|
|
157
|
+
// keeps the text before `offset` in the first element and includes the values of
|
|
158
|
+
// matched capture groups, so iterate the matches manually like .NET Regex.Split does
|
|
159
|
+
const result: string[] = [];
|
|
160
|
+
let prev = 0;
|
|
161
|
+
let count = limit == null ? -1 : limit - 1;
|
|
162
|
+
for (const m of matches(reg as RegExp, input, offset)) {
|
|
163
|
+
if (count === 0) {
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
result.push(input.substring(prev, m.index));
|
|
167
|
+
prev = m.index + m[0].length;
|
|
168
|
+
for (let i = 1; i < m.length; i++) {
|
|
169
|
+
if (m[i] !== undefined) {
|
|
170
|
+
result.push(m[i]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
count--;
|
|
174
|
+
}
|
|
175
|
+
result.push(input.substring(prev));
|
|
176
|
+
return result;
|
|
153
177
|
}
|
package/Seq.ts
CHANGED
|
@@ -508,6 +508,94 @@ export function enumerateWhile<T>(guard: (() => boolean), xs: Iterable<T>): Iter
|
|
|
508
508
|
return concat<Iterable<T>, T>(unfold<int32, Iterable<T>>((i: int32): Option<[Iterable<T>, int32]> => (guard() ? ([xs, i + 1] as [Iterable<T>, int32]) : undefined), 0));
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
export function enumerateTryWith<T>(source: Iterable<T>, catchFilter: ((arg0: Exception) => int32), catchHandler: ((arg0: Exception) => Iterable<T>)): Iterable<T> {
|
|
512
|
+
return mkSeq<T>((): IEnumerator<T> => {
|
|
513
|
+
let e: Option<IEnumerator<T>> = undefined;
|
|
514
|
+
let caught = false;
|
|
515
|
+
let started = false;
|
|
516
|
+
let finished = false;
|
|
517
|
+
let curr: Option<T> = undefined;
|
|
518
|
+
const tryNext = (): boolean => {
|
|
519
|
+
try {
|
|
520
|
+
let en_2: IEnumerator<T>;
|
|
521
|
+
if (e == null) {
|
|
522
|
+
const en_1: IEnumerator<T> = getEnumerator(source);
|
|
523
|
+
e = en_1;
|
|
524
|
+
en_2 = en_1;
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
en_2 = value_1(e);
|
|
528
|
+
}
|
|
529
|
+
if (en_2["System.Collections.IEnumerator.MoveNext"]()) {
|
|
530
|
+
curr = some(en_2["System.Collections.Generic.IEnumerator`1.get_Current"]());
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
finished = true;
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch (matchValue: any) {
|
|
539
|
+
if (!caught) {
|
|
540
|
+
const ex_1: Exception = matchValue;
|
|
541
|
+
if (catchFilter(ex_1) !== 0) {
|
|
542
|
+
caught = true;
|
|
543
|
+
if (e == null) {
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
const en_3: IEnumerator<T> = value_1(e);
|
|
547
|
+
try {
|
|
548
|
+
disposeSafe(en_3);
|
|
549
|
+
}
|
|
550
|
+
catch (matchValue_1: any) {
|
|
551
|
+
}
|
|
552
|
+
e = undefined;
|
|
553
|
+
}
|
|
554
|
+
e = getEnumerator(catchHandler(ex_1));
|
|
555
|
+
return tryNext();
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
throw matchValue;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
throw matchValue;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
return Enumerator_FromFunctions$1_$ctor_58C54629<T>((): T => {
|
|
567
|
+
if (!started) {
|
|
568
|
+
Enumerator_notStarted<void>();
|
|
569
|
+
}
|
|
570
|
+
else if (finished) {
|
|
571
|
+
Enumerator_alreadyFinished<void>();
|
|
572
|
+
}
|
|
573
|
+
if (curr != null) {
|
|
574
|
+
return value_1(curr);
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
return Enumerator_alreadyFinished<T>();
|
|
578
|
+
}
|
|
579
|
+
}, (): boolean => {
|
|
580
|
+
if (!started) {
|
|
581
|
+
started = true;
|
|
582
|
+
}
|
|
583
|
+
if (finished) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
return tryNext();
|
|
588
|
+
}
|
|
589
|
+
}, (): void => {
|
|
590
|
+
if (e == null) {
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
disposeSafe(value_1(e));
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
511
599
|
export function filter<T>(f: ((arg0: T) => boolean), xs: Iterable<T>): Iterable<T> {
|
|
512
600
|
return choose<T, T>((x: T): Option<T> => {
|
|
513
601
|
if (f(x)) {
|
package/Set.ts
CHANGED
|
@@ -3,10 +3,9 @@ import { record_type, bool_type, list_type, option_type, class_type, TypeInfo }
|
|
|
3
3
|
import { some, value as value_2, Option } from "./Option.ts";
|
|
4
4
|
import { int32 } from "./Int32.ts";
|
|
5
5
|
import { structuralHash, ISet, toIterator, IDisposable, disposeSafe, getEnumerator, isArrayLike, MutableArray, IEnumerator, IComparer, Exception } from "./Util.ts";
|
|
6
|
-
import { Record } from "./Types.ts";
|
|
6
|
+
import { toString, Record } from "./Types.ts";
|
|
7
7
|
import { fold as fold_2, cons, singleton as singleton_1, empty as empty_1, ofArrayWithTail, tail, head, isEmpty as isEmpty_1, FSharpList } from "./List.ts";
|
|
8
8
|
import { fold as fold_1, fill, setItem } from "./Array.ts";
|
|
9
|
-
import { join } from "./String.ts";
|
|
10
9
|
import { NotSupportedException_$ctor_Z721C83C5 } from "./System.ts";
|
|
11
10
|
import { skip, truncate, iterateIndexed, exists as exists_1, forAll as forAll_1, fold as fold_3, reduce, iterate as iterate_1, map as map_1 } from "./Seq.ts";
|
|
12
11
|
import { HashSet__get_Comparer, HashSet_$ctor_Z6150332D, HashSet } from "./MutableSet.ts";
|
|
@@ -1495,7 +1494,21 @@ export class FSharpSet<T> implements ISet<T>, Iterable<T>, Iterable<T> {
|
|
|
1495
1494
|
}
|
|
1496
1495
|
toString(): string {
|
|
1497
1496
|
const this$: FSharpSet<T> = this;
|
|
1498
|
-
|
|
1497
|
+
let result = "set [";
|
|
1498
|
+
let first = true;
|
|
1499
|
+
const enumerator: IEnumerator<T> = getEnumerator(this$);
|
|
1500
|
+
try {
|
|
1501
|
+
while (enumerator["System.Collections.IEnumerator.MoveNext"]()) {
|
|
1502
|
+
let x: T = (undefined as any), matchValue: any = (undefined as any), s: string = (undefined as any);
|
|
1503
|
+
const x_1: T = enumerator["System.Collections.Generic.IEnumerator`1.get_Current"]();
|
|
1504
|
+
result = ((first ? result : (result + "; ")) + ((x = x_1, (matchValue = x, (typeof matchValue === "string") ? ((s = (matchValue as string), ("\"" + s) + "\"")) : toString(x)))));
|
|
1505
|
+
first = false;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
finally {
|
|
1509
|
+
disposeSafe(enumerator as IDisposable);
|
|
1510
|
+
}
|
|
1511
|
+
return result + "]";
|
|
1499
1512
|
}
|
|
1500
1513
|
get [Symbol.toStringTag](): string {
|
|
1501
1514
|
return "FSharpSet";
|
package/String.ts
CHANGED
|
@@ -81,7 +81,7 @@ export function endsWith(str: string, pattern: string, ic: boolean | StringCompa
|
|
|
81
81
|
return str.endsWith(pattern);
|
|
82
82
|
}
|
|
83
83
|
if (str.length >= pattern.length) {
|
|
84
|
-
return cmp(str.slice(-pattern.length), pattern, ic) === 0;
|
|
84
|
+
return cmp(str.slice(str.length - pattern.length), pattern, ic) === 0;
|
|
85
85
|
}
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
@@ -253,6 +253,8 @@ function formatReplacement(rep: any, flags: any, padLength: any, precision: any,
|
|
|
253
253
|
}
|
|
254
254
|
} else if (rep instanceof Date) {
|
|
255
255
|
rep = dateToString(rep);
|
|
256
|
+
} else if (format === "A" && typeof rep === "string") {
|
|
257
|
+
rep = "\"" + rep + "\"";
|
|
256
258
|
} else {
|
|
257
259
|
rep = toString(rep);
|
|
258
260
|
}
|
package/Types.ts
CHANGED
|
@@ -9,18 +9,25 @@ export function seqToString<T>(self: Iterable<T>): string {
|
|
|
9
9
|
let str = "[";
|
|
10
10
|
for (const x of self) {
|
|
11
11
|
if (count === 0) {
|
|
12
|
-
str +=
|
|
12
|
+
str += toStringQuoted(x);
|
|
13
13
|
} else if (count === 100) {
|
|
14
14
|
str += "; ...";
|
|
15
15
|
break;
|
|
16
16
|
} else {
|
|
17
|
-
str += "; " +
|
|
17
|
+
str += "; " + toStringQuoted(x);
|
|
18
18
|
}
|
|
19
19
|
count++;
|
|
20
20
|
}
|
|
21
21
|
return str + "]";
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Structured (%A-style) rendering of a value used as an element/field of a
|
|
25
|
+
// container. F#'s structured formatting (which records, unions, lists, etc.
|
|
26
|
+
// use in their ToString) quotes strings, e.g. `["a"; "b"]` and `{ Name = "John" }`.
|
|
27
|
+
function toStringQuoted(x: any, callStack = 0): string {
|
|
28
|
+
return typeof x === "string" ? "\"" + x + "\"" : toString(x, callStack);
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
export function toString(x: any, callStack = 0): string {
|
|
25
32
|
if (x != null && typeof x === "object") {
|
|
26
33
|
if (typeof x.toString === "function" && x.toString !== Object.prototype.toString) {
|
|
@@ -31,7 +38,7 @@ export function toString(x: any, callStack = 0): string {
|
|
|
31
38
|
const cons = Object.getPrototypeOf(x)?.constructor;
|
|
32
39
|
return cons === Object && callStack < 10
|
|
33
40
|
// Same format as recordToString
|
|
34
|
-
? "{ " + Object.entries(x).map(([k, v]) => k + " = " +
|
|
41
|
+
? "{ " + Object.entries(x).map(([k, v]) => k + " = " + toStringQuoted(v, callStack + 1)).join("\n ") + " }"
|
|
35
42
|
: cons?.name ?? "";
|
|
36
43
|
}
|
|
37
44
|
}
|
|
@@ -39,23 +46,16 @@ export function toString(x: any, callStack = 0): string {
|
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
export function unionToString(name: string, fields: any[]) {
|
|
42
|
-
function unionFieldToString(x: any): string {
|
|
43
|
-
if (typeof x === "string") {
|
|
44
|
-
return '"' + x + '"';
|
|
45
|
-
}
|
|
46
|
-
return toString(x);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
49
|
if (fields.length === 0) {
|
|
50
50
|
return name;
|
|
51
51
|
} else {
|
|
52
52
|
let fieldStr;
|
|
53
53
|
let withParens = true;
|
|
54
54
|
if (fields.length === 1) {
|
|
55
|
-
fieldStr =
|
|
55
|
+
fieldStr = toStringQuoted(fields[0]);
|
|
56
56
|
withParens = fieldStr.indexOf(" ") >= 0;
|
|
57
57
|
} else {
|
|
58
|
-
fieldStr = fields.map((x: any) =>
|
|
58
|
+
fieldStr = fields.map((x: any) => toStringQuoted(x)).join(", ");
|
|
59
59
|
}
|
|
60
60
|
return name + (withParens ? " (" : " ") + fieldStr + (withParens ? ")" : "");
|
|
61
61
|
}
|
|
@@ -119,7 +119,7 @@ function recordToJSON<T>(self: T) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
function recordToString<T>(self: T) {
|
|
122
|
-
return "{ " + Object.entries(self as any).map(([k, v]) => k + " = " +
|
|
122
|
+
return "{ " + Object.entries(self as any).map(([k, v]) => k + " = " + toStringQuoted(v)).join("\n ") + " }";
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function recordGetHashCode<T>(self: T) {
|
package/Util.ts
CHANGED
|
@@ -564,7 +564,12 @@ export function compareDates(x: Date | IDateTime | IDateTimeOffset, y: Date | ID
|
|
|
564
564
|
}
|
|
565
565
|
|
|
566
566
|
export function comparePrimitives<T>(x: T, y: T): number {
|
|
567
|
-
|
|
567
|
+
if (x === y) { return 0; }
|
|
568
|
+
if (x < y) { return -1; }
|
|
569
|
+
if (x > y) { return 1; }
|
|
570
|
+
// Neither equal nor ordered: at least one operand is NaN.
|
|
571
|
+
// Match .NET Double.CompareTo: NaN equals NaN and is less than any other value.
|
|
572
|
+
return Number.isNaN(x as unknown) ? (Number.isNaN(y as unknown) ? 0 : -1) : 1;
|
|
568
573
|
}
|
|
569
574
|
|
|
570
575
|
export function compareArraysWith<T>(x: ArrayLike<T>, y: ArrayLike<T>, comp: (x: T, y: T) => number): number {
|
|
@@ -616,7 +621,7 @@ export function compare<T>(x: T, y: T): number {
|
|
|
616
621
|
} else if (isArrayLike(x)) {
|
|
617
622
|
return isArrayLike(y) ? compareArrays(x, y) : -1;
|
|
618
623
|
} else if (typeof x !== "object") {
|
|
619
|
-
return x
|
|
624
|
+
return comparePrimitives(x, y);
|
|
620
625
|
} else if (x instanceof Date) {
|
|
621
626
|
return y instanceof Date ? compareDates(x, y) : -1;
|
|
622
627
|
} else {
|
package/package.json
CHANGED