diff --git a/src/lib.d.ts b/src/lib.d.ts index 721330b..150da74 100644 --- a/src/lib.d.ts +++ b/src/lib.d.ts @@ -317,7 +317,7 @@ declare namespace __String { function localeCompare(this: string, compareString: string, locale?: string): int; - function match(this: string, expr: RegExp): string[]; + function match(this: string, expr: RegExp): string[] | null; @borrows("this", "expr") function matchAll(this: string, expr: RegExp): Iterator; @@ -399,7 +399,7 @@ declare class String { localeCompare(this: string, compareString: string, locale?: string): int; - match(this: string, expr: RegExp): string[]; + match(this: string, expr: RegExp): string[] | null; @borrows("this", "expr") matchAll(this: string, expr: RegExp): Iterator; diff --git a/src/lib.ts b/src/lib.ts index 1fc348a..61ad577 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -4,8 +4,6 @@ /// /// -// @strict-null false - export function parseInt(val: string, radix = 10) { return strtol(val, null, radix); } @@ -948,13 +946,15 @@ namespace __String { return result; } - export function match(this: string, expr: RegExp): string[] { + export function match(this: string, expr: RegExp): string[] | null { if (!expr.global) { const result = expr.exec(this); if (result != null) { - return [...result]; + // -mm=own: into a local first, which the return then moves (as matchAll's yield does) + const groups: string[] = [...result]; + return groups; } return null; @@ -976,7 +976,9 @@ namespace __String { break; } - return results; + // an empty array when nothing matched, not JavaScript's null: a global match can always be + // walked or counted without a check + return results; } @borrows("this", "expr") @@ -1500,7 +1502,7 @@ export class String { return this.value.localeCompare(compareString, locale); } - public match(expr: RegExp): string[] { + public match(expr: RegExp): string[] | null { return this.value.match(expr); } diff --git a/src/native/lib.native.d.ts b/src/native/lib.native.d.ts index 6d3f0dd..06d1405 100644 --- a/src/native/lib.native.d.ts +++ b/src/native/lib.native.d.ts @@ -18,8 +18,8 @@ @nodrops declare function time(destTime: Reference); // String -@nodrops declare function strtol(str: string, end: Reference, base: int): int; -@nodrops declare function strtod(str: string, end: Reference): double; +@nodrops declare function strtol(str: string, end: Reference | null, base: int): int; +@nodrops declare function strtod(str: string, end: Reference | null): double; @nodrops declare function strncmp(str: string, other: string, count: index): int; // a pointer into `str`, not a string of its own: a `string` result from C is copied @nodrops declare function strstr(str: string, other: string): Opaque; @@ -111,7 +111,7 @@ declare function GC_gcollect(): void; // regex @nodrops declare function regexp_test(expr: string, flags: string, s: string): int; -@nodrops declare function regexp_exec(expr: string, flags: string, s: string, cm: Opaque): Opaque; +@nodrops declare function regexp_exec(expr: string, flags: string, s: string, cm: Opaque | null): Opaque | null; @nodrops declare function regexp_match_results_size(cm: Opaque): index; @nodrops declare function regexp_match_results_prefix_length(cm: Opaque): index; @nodrops declare function regexp_match_results_suffix_position(cm: Opaque): index; diff --git a/tests/string_match.ts b/tests/string_match.ts index 74fa7ba..fbbcbb8 100644 --- a/tests/string_match.ts +++ b/tests/string_match.ts @@ -1,11 +1,24 @@ const paragraph = 'The quick brown fox jumps over the lazy dog. It barked.'; const regex = /[A-Z]/g; +// match gives string[] | null: null when a pattern without the g flag does not match const found = paragraph.match(regex); -assert(found.length == 2); -assert(found == "T,I"); +assert(found != null); +if (found != null) { + assert(found.length == 2); + assert(found == "T,I"); -console.log(found); -// Expected output: Array ["T", "I"] + console.log(found); + // Expected output: Array ["T", "I"] +} + +assert(paragraph.match(/[0-9]/) == null, "no match is null"); + +// with the g flag no match is an empty array (JavaScript gives null there too) +const none = paragraph.match(/[0-9]/g); +assert(none != null, "a global match is never null"); +if (none != null) { + assert(none.length == 0, "and is empty when nothing matched"); +} console.log("ALL DONE"); diff --git a/tests/string_match_lastIndex.ts b/tests/string_match_lastIndex.ts index 7e93ae4..6960e36 100644 --- a/tests/string_match_lastIndex.ts +++ b/tests/string_match_lastIndex.ts @@ -6,7 +6,11 @@ function main() { assert(re.lastIndex == 1, "test moved lastIndex"); const digits = "1a2b3".match(re); - assert(digits == "1,2,3", "match starts at the beginning"); + assert(digits != null, "match finds the digits"); + if (digits != null) { + assert(digits == "1,2,3", "match starts at the beginning"); + } + assert(re.lastIndex == 0, "match leaves lastIndex at 0"); assert(re.exec("1a2b3") != null, "exec finds the first digit"); @@ -19,10 +23,16 @@ function main() { // empty matches const empties = "abc".match(/x*/g); - assert(empties.length == 4, "an empty match at every position"); + assert(empties != null, "empty matches are matches"); + if (empties != null) { + assert(empties.length == 4, "an empty match at every position"); + } const words = "ab cd".match(/\w*/g); - assert(words == "ab,,cd,", "empty matches between the words"); + assert(words != null, "words and the empty matches between them"); + if (words != null) { + assert(words == "ab,,cd,", "empty matches between the words"); + } const emptyAll = [..."ab".matchAll(/x*/g)]; assert(emptyAll.length == 3, "matchAll: an empty match at every position");