From f70cea508cd30fa40770ea989fe2a19e715a357b Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 30 Dec 2022 15:13:28 +0100 Subject: Remove index syntax in favor of accessor methods --- tests/typ/compiler/array.typ | 168 +++++++++++++++++++++++++++--- tests/typ/compiler/call.typ | 8 +- tests/typ/compiler/color.typ | 22 ++++ tests/typ/compiler/dict.typ | 26 +++-- tests/typ/compiler/methods-collection.typ | 115 -------------------- tests/typ/compiler/methods-color.typ | 22 ---- tests/typ/compiler/methods-str.typ | 117 --------------------- tests/typ/compiler/methods.typ | 2 +- tests/typ/compiler/ops-invalid.typ | 13 ++- tests/typ/compiler/ops-prec.typ | 4 +- tests/typ/compiler/set.typ | 2 +- tests/typ/compiler/string.typ | 146 ++++++++++++++++++++++++++ 12 files changed, 359 insertions(+), 286 deletions(-) create mode 100644 tests/typ/compiler/color.typ delete mode 100644 tests/typ/compiler/methods-collection.typ delete mode 100644 tests/typ/compiler/methods-color.typ delete mode 100644 tests/typ/compiler/methods-str.typ create mode 100644 tests/typ/compiler/string.typ (limited to 'tests/typ/compiler') diff --git a/tests/typ/compiler/array.typ b/tests/typ/compiler/array.typ index cb8433cb..ccde8598 100644 --- a/tests/typ/compiler/array.typ +++ b/tests/typ/compiler/array.typ @@ -23,51 +23,187 @@ , rgb("002") ,)} +--- +// Test the `len` method. +#test(().len(), 0) +#test(("A", "B", "C").len(), 3) + --- // Test lvalue and rvalue access. { let array = (1, 2) - array(1) += 5 + array(0) + array.at(1) += 5 + array.at(0) test(array, (1, 8)) } +--- +// Test different lvalue method. +{ + let array = (1, 2, 3) + array.first() = 7 + array.at(1) *= 8 + test(array, (7, 16, 3)) +} + --- // Test rvalue out of bounds. -// Error: 2-14 array index out of bounds (index: 5, len: 3) -{(1, 2, 3)(5)} +// Error: 2-17 array index out of bounds (index: 5, len: 3) +{(1, 2, 3).at(5)} --- // Test lvalue out of bounds. { let array = (1, 2, 3) - // Error: 3-11 array index out of bounds (index: 3, len: 3) - array(3) = 5 + // Error: 3-14 array index out of bounds (index: 3, len: 3) + array.at(3) = 5 } +--- +// Test bad lvalue. +// Error: 2:3-2:14 cannot mutate a temporary value +#let array = (1, 2, 3) +{ array.len() = 4 } + +--- +// Test bad lvalue. +// Error: 2:3-2:15 type array has no method `yolo` +#let array = (1, 2, 3) +{ array.yolo() = 4 } + --- // Test negative indices. { let array = (1, 2, 3, 4) - test(array(0), 1) - test(array(-1), 4) - test(array(-2), 3) - test(array(-3), 2) - test(array(-4), 1) + test(array.at(0), 1) + test(array.at(-1), 4) + test(array.at(-2), 3) + test(array.at(-3), 2) + test(array.at(-4), 1) } --- -// Error: 2-15 array index out of bounds (index: -4, len: 3) -{(1, 2, 3)(-4)} +// The the `first` and `last` methods. +#test((1,).first(), 1) +#test((2,).last(), 2) +#test((1, 2, 3).first(), 1) +#test((1, 2, 3).last(), 3) + +--- +// Error: 3-13 array is empty +{ ().first() } --- -// Test non-collection indexing. +// Error: 3-12 array is empty +{ ().last() } +--- +// Test the `push` and `pop` methods. { - let x = 10pt - // Error: 3-4 expected collection, found length - x() = 1 + let tasks = (a: (1, 2, 3), b: (4, 5, 6)) + tasks.at("a").pop() + tasks.b.push(7) + test(tasks.a, (1, 2)) + test(tasks.at("b"), (4, 5, 6, 7)) } +--- +// Test the `insert` and `remove` methods. +{ + let array = (0, 1, 2, 4, 5) + array.insert(3, 3) + test(array, range(6)) + array.remove(1) + test(array, (0, 2, 3, 4, 5)) +} + +--- +// Error: 2:17-2:19 missing argument: index +#let numbers = () +{ numbers.insert() } +--- +// Test the `slice` method. +#test((1, 2, 3, 4).slice(2), (3, 4)) +#test(range(10).slice(2, 6), (2, 3, 4, 5)) +#test(range(10).slice(4, count: 3), (4, 5, 6)) +#test(range(10).slice(-5, count: 2), (5, 6)) +#test((1, 2, 3).slice(2, -2), ()) +#test((1, 2, 3).slice(-2, 2), (2,)) +#test((1, 2, 3).slice(-3, 2), (1, 2)) +#test("ABCD".split("").slice(1, -1).join("-"), "A-B-C-D") + +--- +// Error: 3-31 array index out of bounds (index: 12, len: 10) +{ range(10).slice(9, count: 3) } + +--- +// Error: 3-25 array index out of bounds (index: -4, len: 3) +{ (1, 2, 3).slice(0, -4) } + +--- +// Test the `position` method. +#test(("Hi", "❤️", "Love").position(s => s == "❤️"), 1) +#test(("Bye", "💘", "Apart").position(s => s == "❤️"), none) +#test(("A", "B", "CDEF", "G").position(v => v.len() > 2), 2) + +--- +// Test the `filter` method. +#test(().filter(even), ()) +#test((1, 2, 3, 4).filter(even), (2, 4)) +#test((7, 3, 2, 5, 1).filter(x => x < 5), (3, 2, 1)) + +--- +// Test the `map` method. +#test(().map(x => x * 2), ()) +#test((2, 3).map(x => x * 2), (4, 6)) + +--- +// Test the `fold` method. +#test(().fold("hi", grid), "hi") +#test((1, 2, 3, 4).fold(0, (s, x) => s + x), 10) + +--- +// Error: 21-31 function must have exactly two parameters +{ (1, 2, 3).fold(0, () => none) } + +--- +// Test the `rev` method. +#test(range(3).rev(), (2, 1, 0)) + +--- +// Test the `join` method. +#test(().join(), none) +#test((1,).join(), 1) +#test(("a", "b", "c").join(), "abc") +#test("(" + ("a", "b", "c").join(", ") + ")", "(a, b, c)") + +--- +// Error: 2-22 cannot join boolean with boolean +{(true, false).join()} + +--- +// Error: 2-20 cannot join string with integer +{("a", "b").join(1)} + +--- +// Test joining content. +// Ref: true +{([One], [Two], [Three]).join([, ], last: [ and ])}. + +--- +// Test the `sorted` method. +#test(().sorted(), ()) +#test(((true, false) * 10).sorted(), (false,) * 10 + (true,) * 10) +#test(("it", "the", "hi", "text").sorted(), ("hi", "it", "text", "the")) +#test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10)) + +--- +// Error: 2-26 cannot order content and content +{([Hi], [There]).sorted()} + +--- +// Error: 2-18 array index out of bounds (index: -4, len: 3) +{(1, 2, 3).at(-4)} + --- // Error: 3 expected closing paren {(} diff --git a/tests/typ/compiler/call.typ b/tests/typ/compiler/call.typ index dc582c9c..7ea0a998 100644 --- a/tests/typ/compiler/call.typ +++ b/tests/typ/compiler/call.typ @@ -48,25 +48,25 @@ #set text(family: "Arial", family: "Helvetica") --- -// Error: 2-6 expected callable or collection, found boolean +// Error: 2-6 expected function, found boolean {true()} --- #let x = "x" -// Error: 1-3 expected callable or collection, found string +// Error: 1-3 expected function, found string #x() --- #let f(x) = x -// Error: 1-6 expected callable or collection, found integer +// Error: 1-6 expected function, found integer #f(1)(2) --- #let f(x) = x -// Error: 1-6 expected callable or collection, found content +// Error: 1-6 expected function, found content #f[1](2) --- diff --git a/tests/typ/compiler/color.typ b/tests/typ/compiler/color.typ new file mode 100644 index 00000000..3b165d14 --- /dev/null +++ b/tests/typ/compiler/color.typ @@ -0,0 +1,22 @@ +// Test color modification methods. + +--- +// Test gray color modification. +#test(luma(20%).lighten(50%), luma(60%)) +#test(luma(80%).darken(20%), luma(63.9%)) +#test(luma(80%).negate(), luma(20%)) + +--- +// Test CMYK color conversion. +// Ref: true +#let c = cmyk(50%, 64%, 16%, 17%) +#rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%)) +#rect(width: 1cm, fill: c) +#rect(width: 1cm, fill: c.negate()) + +#for x in range(0, 11) { + square(size: 9pt, fill: c.lighten(x * 10%)) +} +#for x in range(0, 11) { + square(size: 9pt, fill: c.darken(x * 10%)) +} diff --git a/tests/typ/compiler/dict.typ b/tests/typ/compiler/dict.typ index d791f77b..0170cb8b 100644 --- a/tests/typ/compiler/dict.typ +++ b/tests/typ/compiler/dict.typ @@ -12,17 +12,17 @@ #dict #test(dict.normal, 1) -#test(dict("spacy key"), 2) +#test(dict.at("spacy key"), 2) --- // Test lvalue and rvalue access. { let dict = (a: 1, "b b": 1) - dict("b b") += 1 + dict.at("b b") += 1 dict.state = (ok: true, err: false) test(dict, (a: 1, "b b": 2, state: (ok: true, err: false))) test(dict.state.ok, true) - dict("state").ok = false + dict.at("state").ok = false test(dict.state.ok, false) test(dict.state.err, false) } @@ -31,18 +31,30 @@ // Test rvalue missing key. { let dict = (a: 1, b: 2) - // Error: 11-20 dictionary does not contain key "c" - let x = dict("c") + // Error: 11-23 dictionary does not contain key "c" + let x = dict.at("c") } --- // Missing lvalue is automatically none-initialized. { let dict = (:) - dict("b") += 1 + dict.at("b") += 1 test(dict, (b: 1)) } +--- +// Test dictionary methods. +#let dict = (a: 3, c: 2, b: 1) +#test("c" in dict, true) +#test(dict.len(), 3) +#test(dict.values(), (3, 1, 2)) +#test(dict.pairs((k, v) => k + str(v)).join(), "a3b1c2") + +{ dict.remove("c") } +#test("c" in dict, false) +#test(dict, (a: 3, b: 1)) + --- // Error: 24-32 pair has duplicate key {(first: 1, second: 2, first: 3)} @@ -65,7 +77,7 @@ --- // Error: 3-15 cannot mutate a temporary value -{ (key: value).other = "some" } +{ (key: "val").other = "some" } --- { diff --git a/tests/typ/compiler/methods-collection.typ b/tests/typ/compiler/methods-collection.typ deleted file mode 100644 index fcebf640..00000000 --- a/tests/typ/compiler/methods-collection.typ +++ /dev/null @@ -1,115 +0,0 @@ -// Test the collection methods. -// Ref: false - ---- -// Test the `len` method. -#test(().len(), 0) -#test(("A", "B", "C").len(), 3) -#test("Hello World!".len(), 12) -#test((a: 1, b: 2).len(), 2) - ---- -// The the `first` and `last` methods. -#test(().first(), none) -#test(().last(), none) -#test((1,).first(), 1) -#test((2,).last(), 2) -#test((1, 2, 3).first(), 1) -#test((1, 2, 3).last(), 3) - ---- -// Test the `push` and `pop` methods. -{ - let tasks = (a: (1, 2, 3), b: (4, 5, 6)) - tasks("a").pop() - tasks("b").push(7) - test(tasks("a"), (1, 2)) - test(tasks("b"), (4, 5, 6, 7)) -} - ---- -// Test the `insert` and `remove` methods. -{ - let array = (0, 1, 2, 4, 5) - array.insert(3, 3) - test(array, range(6)) - array.remove(1) - test(array, (0, 2, 3, 4, 5)) -} - ---- -// Error: 2:17-2:19 missing argument: index -#let numbers = () -{ numbers.insert() } - ---- -// Test the `slice` method. -#test((1, 2, 3, 4).slice(2), (3, 4)) -#test(range(10).slice(2, 6), (2, 3, 4, 5)) -#test(range(10).slice(4, count: 3), (4, 5, 6)) -#test(range(10).slice(-5, count: 2), (5, 6)) -#test((1, 2, 3).slice(2, -2), ()) -#test((1, 2, 3).slice(-2, 2), (2,)) -#test((1, 2, 3).slice(-3, 2), (1, 2)) -#test("ABCD".split("").slice(1, -1).join("-"), "A-B-C-D") - ---- -// Error: 3-31 array index out of bounds (index: 12, len: 10) -{ range(10).slice(9, count: 3) } - ---- -// Error: 3-25 array index out of bounds (index: -4, len: 3) -{ (1, 2, 3).slice(0, -4) } - ---- -// Test the `position` method. -#test(("Hi", "❤️", "Love").position(s => s == "❤️"), 1) -#test(("Bye", "💘", "Apart").position(s => s == "❤️"), none) -#test(("A", "B", "CDEF", "G").position(v => v.len() > 2), 2) - ---- -// Test the `rev` method. -#test(range(3).rev(), (2, 1, 0)) - ---- -// Test the `join` method. -#test(().join(), none) -#test((1,).join(), 1) -#test(("a", "b", "c").join(), "abc") -#test("(" + ("a", "b", "c").join(", ") + ")", "(a, b, c)") - ---- -// Error: 2-22 cannot join boolean with boolean -{(true, false).join()} - ---- -// Error: 2-20 cannot join string with integer -{("a", "b").join(1)} - ---- -// Test joining content. -// Ref: true -{([One], [Two], [Three]).join([, ], last: [ and ])}. - ---- -// Test the `sorted` method. -#test(().sorted(), ()) -#test(((true, false) * 10).sorted(), (false,) * 10 + (true,) * 10) -#test(("it", "the", "hi", "text").sorted(), ("hi", "it", "text", "the")) -#test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10)) - ---- -// Error: 2-26 cannot order content and content -{([Hi], [There]).sorted()} - ---- -// Test dictionary methods. -#let dict = (a: 3, c: 2, b: 1) -#test("c" in dict, true) -#test(dict.len(), 3) -#test(dict.values(), (3, 1, 2)) -#test(dict.pairs((k, v) => k + str(v)).join(), "a3b1c2") - -{ dict.remove("c") } -#test("c" in dict, false) -#test(dict, (a: 3, b: 1)) diff --git a/tests/typ/compiler/methods-color.typ b/tests/typ/compiler/methods-color.typ deleted file mode 100644 index 3b165d14..00000000 --- a/tests/typ/compiler/methods-color.typ +++ /dev/null @@ -1,22 +0,0 @@ -// Test color modification methods. - ---- -// Test gray color modification. -#test(luma(20%).lighten(50%), luma(60%)) -#test(luma(80%).darken(20%), luma(63.9%)) -#test(luma(80%).negate(), luma(20%)) - ---- -// Test CMYK color conversion. -// Ref: true -#let c = cmyk(50%, 64%, 16%, 17%) -#rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%)) -#rect(width: 1cm, fill: c) -#rect(width: 1cm, fill: c.negate()) - -#for x in range(0, 11) { - square(size: 9pt, fill: c.lighten(x * 10%)) -} -#for x in range(0, 11) { - square(size: 9pt, fill: c.darken(x * 10%)) -} diff --git a/tests/typ/compiler/methods-str.typ b/tests/typ/compiler/methods-str.typ deleted file mode 100644 index aead4aa4..00000000 --- a/tests/typ/compiler/methods-str.typ +++ /dev/null @@ -1,117 +0,0 @@ -// Test the string methods. -// Ref: false - ---- -// Test the `slice` method. -#test("abc".slice(1, 2), "b") -#test("abc🏡def".slice(2, 7), "c🏡") -#test("abc🏡def".slice(2, -2), "c🏡d") -#test("abc🏡def".slice(-3, -1), "de") - ---- -// Test the `contains` method. -#test("abc".contains("b"), true) -#test("b" in "abc", true) -#test("1234f".contains(regex("\d")), true) -#test(regex("\d") in "1234f", true) -#test("abc".contains("d"), false) -#test("1234g" in "1234f", false) -#test("abc".contains(regex("^[abc]$")), false) -#test("abc".contains(regex("^[abc]+$")), true) - ---- -// Test the `starts-with` and `ends-with` methods. -#test("Typst".starts-with("Ty"), true) -#test("Typst".starts-with(regex("[Tt]ys")), false) -#test("Typst".starts-with("st"), false) -#test("Typst".ends-with("st"), true) -#test("Typst".ends-with(regex("\d*")), true) -#test("Typst".ends-with(regex("\d+")), false) -#test("Typ12".ends-with(regex("\d+")), true) - ---- -// Test the `find` and `position` methods. -#let date = regex("\d{2}:\d{2}") -#test("Hello World".find("World"), "World") -#test("Hello World".position("World"), 6) -#test("It's 12:13 now".find(date), "12:13") -#test("It's 12:13 now".position(date), 5) - ---- -// Test the `match` method. -#test("Is there a".match("for this?"), none) -#test( - "The time of my life.".match(regex("[mit]+e")), - (start: 4, end: 8, text: "time", captures: ()), -) - -// Test the `matches` method. -#test("Hello there".matches("\d"), ()) -#test("Day by Day.".matches("Day"), ( - (start: 0, end: 3, text: "Day", captures: ()), - (start: 7, end: 10, text: "Day", captures: ()), -)) - -// Compute the sum of all timestamps in the text. -#let timesum(text) = { - let time = 0 - for match in text.matches(regex("(\d+):(\d+)")) { - let caps = match.captures - time += 60 * int(caps(0)) + int(caps(1)) - } - str(int(time / 60)) + ":" + str(mod(time, 60)) -} - -#test(timesum(""), "0:0") -#test(timesum("2:70"), "3:10") -#test(timesum("1:20, 2:10, 0:40"), "4:10") - ---- -// Test the `replace` method. -#test("ABC".replace("", "-"), "-A-B-C-") -#test("Ok".replace("Ok", "Nope", count: 0), "Ok") -#test("to add?".replace("", "How ", count: 1), "How to add?") -#test("AB C DEF GH J".replace(" ", ",", count: 2), "AB,C,DEF GH J") -#test("Walcemo" - .replace("o", "k") - .replace("e", "o") - .replace("k", "e") - .replace("a", "e"), - "Welcome" -) -#test("123".replace(regex("\d$"), "_"), "12_") -#test("123".replace(regex("\d{1,2}$"), "__"), "1__") - ---- -// Test the `trim` method. -#let str = "Typst, LaTeX, Word, InDesign" -#let array = ("Typst", "LaTeX", "Word", "InDesign") -#test(str.split(",").map(s => s.trim()), array) -#test("".trim(), "") -#test(" abc ".trim(at: start), "abc ") -#test(" abc ".trim(at: end, repeat: true), " abc") -#test(" abc".trim(at: start, repeat: false), "abc") -#test("aabcaa".trim("a", repeat: false), "abca") -#test("aabca".trim("a", at: start), "bca") -#test("aabcaa".trim("a", at: end, repeat: false), "aabca") -#test("".trim(regex(".")), "") -#test("123abc456".trim(regex("\d")), "abc") -#test("123abc456".trim(regex("\d"), repeat: false), "23abc45") -#test("123a4b5c678".trim(regex("\d"), repeat: true), "a4b5c") -#test("123a4b5c678".trim(regex("\d"), repeat: false), "23a4b5c67") -#test("123abc456".trim(regex("\d"), at: start), "abc456") -#test("123abc456".trim(regex("\d"), at: end), "123abc") -#test("123abc456".trim(regex("\d+"), at: end, repeat: false), "123abc") -#test("123abc456".trim(regex("\d{1,2}$"), repeat: false), "123abc4") -#test("hello world".trim(regex(".")), "") - ---- -// Error: 17-21 expected either `start` or `end` -{"abc".trim(at: left)} - ---- -// Test the `split` method. -#test("abc".split(""), ("", "a", "b", "c", "")) -#test("abc".split("b"), ("a", "c")) -#test("a123c".split(regex("\d")), ("a", "", "", "c")) -#test("a123c".split(regex("\d+")), ("a", "c")) diff --git a/tests/typ/compiler/methods.typ b/tests/typ/compiler/methods.typ index 07f6e410..f468320b 100644 --- a/tests/typ/compiler/methods.typ +++ b/tests/typ/compiler/methods.typ @@ -9,7 +9,7 @@ // Test mutating indexed value. { let matrix = (((1,), (2,)), ((3,), (4,))) - matrix(1)(0).push(5) + matrix.at(1).at(0).push(5) test(matrix, (((1,), (2,)), ((3, 5), (4,)))) } diff --git a/tests/typ/compiler/ops-invalid.typ b/tests/typ/compiler/ops-invalid.typ index 3d41a3d1..d51e42fb 100644 --- a/tests/typ/compiler/ops-invalid.typ +++ b/tests/typ/compiler/ops-invalid.typ @@ -90,20 +90,29 @@ { let x = 2 for _ in range(61) { - x *= 2 + (x) *= 2 } // Error: 4-18 cannot repeat this string 4611686018427387904 times {x * "abcdefgh"} } --- -// Error: 3-6 cannot mutate a temporary value +// Error: 4-5 unknown variable { (x) = "" } --- // Error: 3-8 cannot mutate a temporary value { 1 + 2 += 3 } +--- +// Error: 2:2-2:7 cannot apply 'not' to string +#let x = "Hey" +{not x = "a"} + +--- +// Error: 7-8 unknown variable +{ 1 + x += 3 } + --- // Error: 3-4 unknown variable { z = 1 } diff --git a/tests/typ/compiler/ops-prec.typ b/tests/typ/compiler/ops-prec.typ index 23afcc5f..eba0c8a9 100644 --- a/tests/typ/compiler/ops-prec.typ +++ b/tests/typ/compiler/ops-prec.typ @@ -12,8 +12,10 @@ #test("a" == "a" and 2 < 3, true) #test(not "b" == "b", false) +--- // Assignment binds stronger than boolean operations. -// Error: 2-7 cannot mutate a temporary value +// Error: 2:2-2:7 cannot mutate a temporary value +#let x = false {not x = "a"} --- diff --git a/tests/typ/compiler/set.typ b/tests/typ/compiler/set.typ index 7414ad5e..034bfab8 100644 --- a/tests/typ/compiler/set.typ +++ b/tests/typ/compiler/set.typ @@ -40,7 +40,7 @@ Hello *{x}* // Test relative path resolving in layout phase. #let choice = ("monkey.svg", "rhino.png", "tiger.jpg") #set enum(numbering: n => { - let path = "../../res/" + choice(n - 1) + let path = "../../res/" + choice.at(n - 1) move(dy: -0.15em, image(path, width: 1em, height: 1em)) }) diff --git a/tests/typ/compiler/string.typ b/tests/typ/compiler/string.typ new file mode 100644 index 00000000..8ac515a5 --- /dev/null +++ b/tests/typ/compiler/string.typ @@ -0,0 +1,146 @@ +// Test the string methods. +// Ref: false + +--- +// Test the `len` method. +#test("Hello World!".len(), 12) + +--- +// Test the `first` and `last` methods. +#test("Hello".first(), "H") +#test("Hello".last(), "o") +#test("🏳️‍🌈A🏳️‍⚧️".first(), "🏳️‍🌈") +#test("🏳️‍🌈A🏳️‍⚧️".last(), "🏳️‍⚧️") + +--- +// Error: 3-13 string is empty +{ "".first() } + +--- +// Error: 3-12 string is empty +{ "".last() } + +--- +// Test the `at` method. +#test("Hello".at(1), "e") +#test("Hello".at(4), "o") +#test("Hey: 🏳️‍🌈 there!".at(5), "🏳️‍🌈") + +--- +// Error: 3-16 string index out of bounds (index: 5, len: 5) +{ "Hello".at(5) } + +--- +// Test the `slice` method. +#test("abc".slice(1, 2), "b") +#test("abc🏡def".slice(2, 7), "c🏡") +#test("abc🏡def".slice(2, -2), "c🏡d") +#test("abc🏡def".slice(-3, -1), "de") + +--- +// Test the `contains` method. +#test("abc".contains("b"), true) +#test("b" in "abc", true) +#test("1234f".contains(regex("\d")), true) +#test(regex("\d") in "1234f", true) +#test("abc".contains("d"), false) +#test("1234g" in "1234f", false) +#test("abc".contains(regex("^[abc]$")), false) +#test("abc".contains(regex("^[abc]+$")), true) + +--- +// Test the `starts-with` and `ends-with` methods. +#test("Typst".starts-with("Ty"), true) +#test("Typst".starts-with(regex("[Tt]ys")), false) +#test("Typst".starts-with("st"), false) +#test("Typst".ends-with("st"), true) +#test("Typst".ends-with(regex("\d*")), true) +#test("Typst".ends-with(regex("\d+")), false) +#test("Typ12".ends-with(regex("\d+")), true) + +--- +// Test the `find` and `position` methods. +#let date = regex("\d{2}:\d{2}") +#test("Hello World".find("World"), "World") +#test("Hello World".position("World"), 6) +#test("It's 12:13 now".find(date), "12:13") +#test("It's 12:13 now".position(date), 5) + +--- +// Test the `match` method. +#test("Is there a".match("for this?"), none) +#test( + "The time of my life.".match(regex("[mit]+e")), + (start: 4, end: 8, text: "time", captures: ()), +) + +// Test the `matches` method. +#test("Hello there".matches("\d"), ()) +#test("Day by Day.".matches("Day"), ( + (start: 0, end: 3, text: "Day", captures: ()), + (start: 7, end: 10, text: "Day", captures: ()), +)) + +// Compute the sum of all timestamps in the text. +#let timesum(text) = { + let time = 0 + for match in text.matches(regex("(\d+):(\d+)")) { + let caps = match.captures + time += 60 * int(caps.at(0)) + int(caps.at(1)) + } + str(int(time / 60)) + ":" + str(mod(time, 60)) +} + +#test(timesum(""), "0:0") +#test(timesum("2:70"), "3:10") +#test(timesum("1:20, 2:10, 0:40"), "4:10") + +--- +// Test the `replace` method. +#test("ABC".replace("", "-"), "-A-B-C-") +#test("Ok".replace("Ok", "Nope", count: 0), "Ok") +#test("to add?".replace("", "How ", count: 1), "How to add?") +#test("AB C DEF GH J".replace(" ", ",", count: 2), "AB,C,DEF GH J") +#test("Walcemo" + .replace("o", "k") + .replace("e", "o") + .replace("k", "e") + .replace("a", "e"), + "Welcome" +) +#test("123".replace(regex("\d$"), "_"), "12_") +#test("123".replace(regex("\d{1,2}$"), "__"), "1__") + +--- +// Test the `trim` method. +#let str = "Typst, LaTeX, Word, InDesign" +#let array = ("Typst", "LaTeX", "Word", "InDesign") +#test(str.split(",").map(s => s.trim()), array) +#test("".trim(), "") +#test(" abc ".trim(at: start), "abc ") +#test(" abc ".trim(at: end, repeat: true), " abc") +#test(" abc".trim(at: start, repeat: false), "abc") +#test("aabcaa".trim("a", repeat: false), "abca") +#test("aabca".trim("a", at: start), "bca") +#test("aabcaa".trim("a", at: end, repeat: false), "aabca") +#test("".trim(regex(".")), "") +#test("123abc456".trim(regex("\d")), "abc") +#test("123abc456".trim(regex("\d"), repeat: false), "23abc45") +#test("123a4b5c678".trim(regex("\d"), repeat: true), "a4b5c") +#test("123a4b5c678".trim(regex("\d"), repeat: false), "23a4b5c67") +#test("123abc456".trim(regex("\d"), at: start), "abc456") +#test("123abc456".trim(regex("\d"), at: end), "123abc") +#test("123abc456".trim(regex("\d+"), at: end, repeat: false), "123abc") +#test("123abc456".trim(regex("\d{1,2}$"), repeat: false), "123abc4") +#test("hello world".trim(regex(".")), "") + +--- +// Error: 17-21 expected either `start` or `end` +{"abc".trim(at: left)} + +--- +// Test the `split` method. +#test("abc".split(""), ("", "a", "b", "c", "")) +#test("abc".split("b"), ("a", "c")) +#test("a123c".split(regex("\d")), ("a", "", "", "c")) +#test("a123c".split(regex("\d+")), ("a", "c")) -- cgit v1.2.3