func main(): str := "Hello Amélie!" !! Testing strings like $str >> str:upper() = "HELLO AMÉLIE!" >> str:lower() = "hello amélie!" >> str:lower():title() = "Hello Amélie!" >> str[1] = "H" >> "I":lower() = "i" >> "I":lower(language="tr_TR") = "ı" >> "i":upper() = "I" >> "i":upper(language="tr_TR") = "İ" >> "ian":title() = "Ian" >> "ian":title(language="tr_TR") = "İan" >> "I":caseless_equals("ı") = no >> "I":caseless_equals("ı", language="tr_TR") = yes >> str[9] = "é" >> \UE9 = "é" >> \U65\U301 = "é" >> \{Penguin}:codepoint_names() = ["PENGUIN"] >> \[31;1] = "$\e[31;1m" >> \UE9 == \U65\U301 = yes amelie := "Am$(\UE9)lie" >> amelie:split() = ["A", "m", "é", "l", "i", "e"] >> amelie:utf32_codepoints() = [:Int32, 65, 109, 233, 108, 105, 101] >> amelie:bytes() = [:Byte, 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65] >> Text.from_bytes([:Byte 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])! = "Amélie" >> Text.from_bytes([Byte(0xFF)]) = none:Text amelie2 := "Am$(\U65\U301)lie" >> amelie2:split() = ["A", "m", "é", "l", "i", "e"] >> amelie2:utf32_codepoints() = [:Int32, 65, 109, 233, 108, 105, 101] >> amelie2:bytes() = [:Byte, 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65] >> amelie:codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] >> amelie2:codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] >> "Hello":replace("e", "X") = "HXllo" >> "Hello":has("l") = yes >> "Hello":has("x") = no >> "Hello":replace("l", "") = "Heo" >> "xxxx":replace("x", "") = "" >> "xxxx":replace("y", "") = "xxxx" >> "One two three four five six":replace("e ", "") = "Ontwo threfour fivsix" >> amelie:has(amelie2) = yes >> multiline := " line one line two " = "line one$\nline two" !! Interpolation tests: >> "A $(1+2)" = "A 3" >> 'A $(1+2)' = 'A $(1+2)' >> `A $(1+2)` = "A 3" >> $"A $(1+2)" = "A 3" >> $$"A $(1+2)" = 'A $(1+2)' >> $="A =(1+2)" = "A 3" >> ${one {nested} two $(1+2)} = "one {nested} two 3" c := "É̩" >> c:codepoint_names() = ["LATIN CAPITAL LETTER E WITH ACUTE", "COMBINING VERTICAL LINE BELOW"] >> c == Text.from_codepoint_names(c:codepoint_names())! = yes >> c == Text.from_codepoints(c:utf32_codepoints()) = yes >> c == Text.from_bytes(c:bytes())! = yes >> "one$(\n)two$(\n)three":lines() = ["one", "two", "three"] >> "one$(\n)two$(\n)three$(\n)":lines() = ["one", "two", "three"] >> "one$(\n)two$(\n)three$(\n\n)":lines() = ["one", "two", "three", ""] >> "one$(\r\n)two$(\r\n)three$(\r\n)":lines() = ["one", "two", "three"] >> "":lines() = [:Text] !! Test splitting and joining text: >> "one,, two,three":split(",") = ["one", "", " two", "three"] >> [t for t in "one,, two,three":by_split(",")] = ["one", "", " two", "three"] >> "one,, two,three":split_any(", ") = ["one", "two", "three"] >> [t for t in "one,, two,three":by_split_any(", ")] = ["one", "two", "three"] >> ",one,, two,three,":split(",") = ["", "one", "", " two", "three", ""] >> [t for t in ",one,, two,three,":by_split(",")] = ["", "one", "", " two", "three", ""] >> ",one,, two,three,":split_any(", ") = ["", "one", "two", "three", ""] >> [t for t in ",one,, two,three,":by_split_any(", ")] = ["", "one", "two", "three", ""] >> "abc":split() = ["a", "b", "c"] >> "one two three":split_any() = ["one", "two", "three"] >> ", ":join(["one", "two", "three"]) = "one, two, three" >> "":join(["one", "two", "three"]) = "onetwothree" >> "+":join(["one"]) = "one" >> "+":join([:Text]) = "" >> "":split() = [:Text] !! Test text slicing: >> "abcdef":slice() = "abcdef" >> "abcdef":slice(from=3) = "cdef" >> "abcdef":slice(to=-2) = "abcde" >> "abcdef":slice(from=2, to=4) = "bcd" >> "abcdef":slice(from=5, to=1) = "" >> house := "家" = "家" >> house.length = 1 >> house:codepoint_names() = ["CJK Unified Ideographs-5BB6"] >> house:utf32_codepoints() = [:Int32, 23478] >> "🐧":codepoint_names() = ["PENGUIN"] >> Text.from_codepoint_names(["not a valid name here buddy"]) = none : Text >> "Hello":replace("ello", "i") = "Hi" >> "":translate({"<"="<", ">"=">"}) = "<tag>" >> "Abc":repeat(3) = "AbcAbcAbc" >> "abcde":starts_with("ab") = yes >> "abcde":starts_with("bc") = no >> "abcde":ends_with("de") = yes >> "abcde":starts_with("cd") = no >> "abcde":without_prefix("ab") = "cde" >> "abcde":without_suffix("ab") = "abcde" >> "abcde":without_prefix("de") = "abcde" >> "abcde":without_suffix("de") = "abc" >> ("hello" ++ " " ++ "Amélie"):reversed() = "eilémA olleh" do: !! Testing concatenation-stability: ab := Text.from_codepoint_names(["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"])! >> ab:codepoint_names() = ["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"] >> ab.length = 1 a := Text.from_codepoint_names(["LATIN SMALL LETTER E"])! b := Text.from_codepoint_names(["COMBINING VERTICAL LINE BELOW"])! >> (a++b):codepoint_names() = ["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"] >> (a++b) == ab = yes >> (a++b).length = 1 do: concat := "e" ++ Text.from_codepoints([Int32(0x300)]) >> concat.length = 1 concat2 := concat ++ Text.from_codepoints([Int32(0x302)]) >> concat2.length = 1 concat3 := concat2 ++ Text.from_codepoints([Int32(0x303)]) >> concat3.length = 1 final := Text.from_codepoints([Int32(0x65), Int32(0x300), Int32(0x302), Int32(0x303)]) >> final.length = 1 >> concat3 == final = yes concat4 := Text.from_codepoints([Int32(0x65), Int32(0x300)]) ++ Text.from_codepoints([Int32(0x302), Int32(0x303)]) >> concat4.length = 1 >> concat4 == final = yes >> "x":left_pad(5) = " x" >> "x":right_pad(5) = "x " >> "x":middle_pad(5) = " x " >> "1234":left_pad(8, "XYZ") = "XYZX1234" >> "1234":right_pad(8, "XYZ") = "1234XYZX" >> "1234":middle_pad(9, "XYZ") = "XY1234XYZ" >> amelie:width() = 6 cowboy := "🤠" >> cowboy:width() = 2 >> cowboy:left_pad(4) = " 🤠" >> cowboy:right_pad(4) = "🤠 " >> cowboy:middle_pad(4) = " 🤠 " >> " one, ":trim(" ,") = "one" >> " one, ":trim(" ,", left=no) = " one" >> " one, ":trim(" ,", right=no) = "one, " >> " ":trim(" ,") = "" >> " ":trim(" ,", left=no) = ""