std/text/lib

std/text/src/lib.trb

UTF-8 text and the Unicode scalar value it is made of: Char, one Unicode scalar value, and String, a sequence of them stored as UTF-8. Bool belongs to std/core, not here - it is a primitive of the language, not a fact about text.

type Char

native type Char with Equals, Compare, Hash, Show

A Unicode scalar value: one character, decoded from UTF-8.

Examples

const letter = 'A'
print letter.toLowerCase()

Related

  • String - text made of many of them.

fn isDigit

native fn isDigit(): Bool

Whether the character is a decimal digit: 0 to 9, and nothing else.

fn isLetter

native fn isLetter(): Bool

Whether the character is a letter: its general category in the Unicode Character Database is Lu, Ll, Lt, Lm or Lo. Digits, marks, symbols and punctuation are not letters, and neither is a code point nothing is assigned to.

fn isWhitespace

native fn isWhitespace(): Bool

Whether the character has the Unicode property White_Space: the ASCII ones, the line and paragraph separators, the no-break space and the spaces of every width, the ideographic space included. The zero width space is not one.

fn byteLength

native fn byteLength(): Int

The number of bytes of the character in UTF-8 (1 to 4): what it adds to an offset into a String.

fn toUpperCase

native fn toUpperCase(): Self

The same character in upper case, or itself where there is no single upper-case code point for it.

This is the simple case mapping of UnicodeData.txt: one code point in, one code point out, for every script that has a case (Latin, Greek, Cyrillic, Armenian, Georgian, Deseret and the rest). So ß is answered unchanged - its upper case is SS, which is two code points and does not fit a Char - and so is every code point that has no upper case. A titlecase letter such as Dž goes to its capital DŽ.

Pitfalls

Upper-casing a text character by character is therefore not the same as the full conversion String.toUpperCase does: ß becomes SS there, and a capital sigma lower-cases to ς or σ by the word it stands in.

fn toLowerCase

native fn toLowerCase(): Self

The same character in lower case, or itself where there is none: the simple mapping, the mirror of Char.toUpperCase.

fn showNested

native fn showNested(): String

'A': inside of another value a character is written in single quotes, with escapes.

type TextIndex

type TextIndex with Equals, Compare, Hash, Show

A position in one text: before one of its characters, or at its end. Only a text hands one out - its searches, its String.start and String.end, String.indexAfter - so a position inside of a character cannot be written, and text[at..] with an at that text.indexOf answered cannot panic (docs/design/PANICS.md section 4). text[3..] is a compile error: a number counted by eye is exactly what lands inside of a character.

It holds a byte offset, which is what makes slicing at one O(1) and lets the slice share the storage. There is no arithmetic on it - at + 1 is the mistake the type exists to prevent, and String.indexAfter is what it means. A number from outside - a file format, an editor - becomes one through String.indexAt, which checks it, and one goes back to a number through String.byteOffset.

A position of one text used on another is a broken promise of the program, as an index of one list used on another is: slicing there panics where it is past the end or inside of a character of the text it is used on.

Examples

const text = "Grüße 👋"
match text.indexOf("ß") {
  Some(at) => print text[at..]
  None => print "no ß"
}

Related

fn compare

fn compare(other: Self): Ordering

The order of two positions in one text: the earlier one first.

fn show

fn show(): String

The byte offset, which is what a position is and what a message about one names.

type String

native type String with Equals, Compare, Hash, Show, Add, Slice<TextIndex>, From<Iterate<Char>>

UTF-8 text. There is no length() and no text[i] on purpose: say what you count (chars(), bytes()). Positions come from searching and are a TextIndex: only the text hands them out, so slicing at one is O(1), shares the storage and cannot land inside of a character. The byte-level members - byteLength, byteAt, sliceBytes, byteOffsetOf - are for a format that counts bytes.

A String is always valid UTF-8. The only ways in are literals, slices at character boundaries, String.from(Iterate<Char>) and runtime functions that validate - so reading a file whose bytes are not UTF-8 is an IoError, and there is no replacement character anywhere in the language.

Examples

const greeting = "Hello, World!"
print greeting.toUpperCase()
print greeting.split(", ")

Related

  • Char - one Unicode scalar value of the text.
  • Show - show() is the text unquoted, showNested() is quoted with escapes.

fn from

static fn from(value: Iterate<Char>): Self

String.from(characters), characters.to<String>().

Ordinary TorbScript, and it cannot be a function of the runtime for the same reason ArrayList.from cannot be one: value is a trait-typed Iterate of the program, so reading it means calling iterate and next through a witness table, which C code cannot do. So it collects the characters and hands them to concatenated: a String is a value, so appending to one in a loop copies everything that is already in it, which is O(n²) in the bytes.

fn chars

fn chars(): Iterate<Char>

The characters, decoded one at a time: for character in text.chars(). Ordinary TorbScript, see Characters.

fn bytes

fn bytes(): Bytes

The bytes of the UTF-8, in order, as a list of their own: Bytes, which is what a sink, a socket, a file and a digest take. One allocation and one copy of the storage, never a byte at a time; changing the list changes nothing of the text.

It is a list and not a lazy view like String.chars, because almost every caller wants the list. A loop that only reads and must not allocate walks String.byteAt up to String.byteLength instead.

Examples

const bytes = "hé".bytes()
print bytes

fn charAt

fn charAt(at: TextIndex): Char?

The character that begins at at, or None at the end. A position is never inside of a character.

fn charAtByte

native fn charAtByte(offset: Int): Char?

The character that begins at this byte offset, or None at and past the end: the byte-level form of String.charAt for a format that counts bytes. An offset inside a character panics like every other bad offset.

fn start

fn start(): TextIndex

The position before the first character.

fn end

fn end(): TextIndex

The position after the last character.

fn indexAfter

fn indexAfter(at: TextIndex): TextIndex?

The position after the character that begins at at, or None at the end. O(1).

fn indexBefore

fn indexBefore(at: TextIndex): TextIndex?

The position before the character that ends at at, or None at the start. O(1): at most four bytes back.

fn indexAt

fn indexAt(byteOffset: Int): TextIndex?

The position at a byte offset, or None where the offset is past the end or inside of a character: how a number from outside - a file format, an editor, a span of a lexer - becomes a position, checked.

fn byteOffset

fn byteOffset(of: TextIndex): Int

The byte offset of a position: for arithmetic on lines and columns, and for a format that counts bytes.

fn indexedChars

fn indexedChars(): Iterate<(index: TextIndex, character: Char)>

Every character with the position it begins at: what a scanner walks.

fn byteAt

native fn byteAt(offset: Int): UInt8?

The byte at this offset, or None at and past the end. A byte is never inside anything, so this never panics.

fn byteLength

native fn byteLength(): Int

How many bytes the text takes up in UTF-8. There is no shortcut for the number of characters: that means decoding.

fn isEmpty

native fn isEmpty(): Bool

Whether the text has no bytes at all. String.isBlank also treats pure whitespace as empty.

fn showNested

native fn showNested(): Self

"a\nb": inside of another value a string is quoted, with escapes. show() is the text itself.

fn slice

fn slice(range: Bounds<TextIndex>): Self

text[from..to] between two positions of this text. A position of another text that is past the end of this one or inside one of its characters panics, and so does a start after the end.

What an open end means is the receiver's decision (std/core's Bounds says so), and for a text it is its start and its end. An inclusive range (from..=to) takes the character that begins at to as well.

fn part

fn part(range: Bounds<TextIndex>): Self?

The text the range covers, or None where it would panic - a position past the end or inside of a character, which only a position of another text can be, or a start after the end: the total twin of text[from..to].

Examples

const text = "Grüße"
const at = text.indexOf("ß") ?? text.end()
print text.part(text.start()..at)

fn sliceBytes

native fn sliceBytes(from: Int, to: Int): Self

The bytes from one offset to another. The one form the runtime has, and what slice decides its offsets for.

fn contains

native fn contains(part: Self): Bool

Whether part occurs anywhere in the text.

fn startsWith

native fn startsWith(prefix: Self): Bool

Whether the text begins with prefix.

fn endsWith

native fn endsWith(suffix: Self): Bool

Whether the text ends with suffix.

fn indexOf

fn indexOf(part: Self): TextIndex?

Where the first occurrence of part begins, or None if it does not occur. An empty part is at the start.

Examples

const path = "a/b/c"
if const Some(at) = path.indexOf("/") {
  print path[..at]
}

fn lastIndexOf

fn lastIndexOf(part: Self): TextIndex?

Where the last occurrence of part begins, or None if it does not occur. An empty part is at the end of the text, which is where a search backwards finds it first.

Related

fn byteOffsetOf

native fn byteOffsetOf(part: Self): Int?

The byte offset of the first occurrence of part: String.indexOf for a format that counts bytes.

fn lastByteOffsetOf

native fn lastByteOffsetOf(part: Self): Int?

The byte offset of the last occurrence of part: String.lastIndexOf for a format that counts bytes.

fn substringBefore

fn substringBefore(part: Self): Self?

Everything before the first occurrence of part, or None if it does not occur.

fn substringAfter

fn substringAfter(part: Self): Self?

Everything after the first occurrence of part, or None if it does not occur.

fn withoutPrefix

fn withoutPrefix(part: Self): Self?

The text without part at its start, or None where it does not start with it. It cannot panic: what it cuts off is a whole text, so the cut is at a character boundary whatever the text holds.

Examples

print("--verbose".withoutPrefix("--") ?? "not an option")

Related

fn withoutSuffix

fn withoutSuffix(part: Self): Self?

The text without part at its end, or None where it does not end with it.

Examples

print("main.trb".withoutSuffix(".trb") ?? "main")

fn splitOnce

fn splitOnce(separator: Self): (before: Self, after: Self)?

The text before and after the first occurrence of separator, which is dropped, or None where it does not occur. One search, and no offset to get wrong - Go's strings.Cut.

Examples

match "name: Ada".splitOnce(": ") {
  Some(parts) => print "{parts.before} is {parts.after}"
  None => print "no field"
}

Related

fn dropping

fn dropping(characters: Int): Self

The text without its first characters characters. Total: a text with fewer characters answers the empty text, and a count of zero or less the whole one, as skip of an Iterate does. O(n) in the count, not in the text.

A character is a Unicode scalar value, a Char of chars().

Examples

print "Grüße".dropping(characters: 3)

Related

fn droppingLast

fn droppingLast(characters: Int): Self

The text without its last characters characters: the empty text where it has fewer, the whole one for a count of zero or less. O(n) in the count.

Examples

print "Grüße".droppingLast(characters: 2)

fn prefix

fn prefix(characters: Int): Self

The first characters characters of the text: the whole text where it has fewer, the empty one for a count of zero or less, as take of an Iterate does. O(n) in the count.

Examples

print "Grüße".prefix(characters: 3)

Related

fn suffix

fn suffix(characters: Int): Self

The last characters characters of the text: the whole text where it has fewer, the empty one for a count of zero or less. O(n) in the count.

Examples

print "Grüße".suffix(characters: 2)

fn trim

native fn trim(): Self

The text with leading and trailing whitespace removed.

fn toUpperCase

native fn toUpperCase(): Self

The text in upper case, by the full default case conversion of the Unicode standard (section 3.13): the simple mapping of Char.toUpperCase, except where SpecialCasing.txt gives a character more than one code point. So ß becomes SS, ʼn becomes ʼN and ffi becomes FFI, and the result can be longer than the text.

The conversion does not look at a language: there is no Turkish or Lithuanian tailoring, because a String has no locale. Bytes the text has that are no character are copied as they are.

fn toLowerCase

native fn toLowerCase(): Self

The text in lower case, by the same full default conversion. A capital sigma becomes ς at the end of a word - after a cased letter, and not before another one - and σ everywhere else; İ becomes an i and a combining dot above.

fn replace

native fn replace(part: Self, replacement: Self): Self

Every occurrence of part, replaced with replacement.

fn split

native fn split(separator: Self): List<Self>

The text cut apart at every occurrence of separator, which is dropped.

fn repeat

native fn repeat(times: Int): Self

The text, written after itself times times: "ab".repeat(3) is "ababab".

Panics

When times is negative, and with a text longer than 4 GiB is not supported when the result would be longer than a text can be.

fn isBlank

fn isBlank(): Bool

Whether the text is empty or holds nothing but whitespace.

fn lines

fn lines(): List<Self>

The text cut apart at every \n.

extend Char with TryFrom<Int64, NumberRangeError>

extend Char with TryFrom<Int64, NumberRangeError>

Not every number is a Unicode scalar value (surrogates, everything above 0x10FFFF).

fn tryFrom

native static fn tryFrom(value: Int64): Result<Self, NumberRangeError>