saQut provides a set of built-in functions on three types: strings, arrays,
and structs. You do not import them and you do not define them; they are always
available. This page documents each one with a runnable example.
If you have not used a “method” before, start with the next section. For the
signatures on their own, jump to the quick reference tables.
You call a built-in with the dot call syntax: write the value, a dot, the
method name, and any extra arguments in parentheses:
string name ="saqut";
print(name.upper()); // "SAQUT"
Read this as: “take name, and apply upper to it.” The value on the left of
the dot is what the function works on; anything inside the parentheses are extra
arguments:
string s ="hello world";
print(s.replace("world", "saQut")); // s is the value; "world" and
// "saQut" are the two arguments
Not object-orientation. The dot here is syntax only. saQut has no classes,
objects, or methods-on-types in the OOP sense; name.upper() is sugar for a
plain function call that reads left to right.
Because most string built-ins return a new value, you can chain calls, each
result flows into the next dot:
Strings in saQut are immutable: none of these functions change the original
string. They return a new string (or a number/bool). If you want to keep a
result, assign it to a variable.
string s ="hello";
s.upper(); // computes "HELLO" but THROWS IT AWAY, s is unchanged
Counts the bytes in the string. For plain English/ASCII text one byte = one
character, so the byte count and the character count are the same.
UTF-8 gotcha. saQut stores text as UTF-8, where non-ASCII characters take
more than one byte. length() returns the byte count, not the number of
visible characters:
print("café".length()); // 5, not 4, 'é' is 2 bytes
For pure ASCII text you can ignore this. For international text, be aware that
length(), substring(), and charAt() all work in bytes.
This function has more behavior worth spelling out, so here it is in full detail.
replace finds every occurrence of old in the string and returns a new
string with each one swapped for new. The original is untouched.
string s ="the cat sat on the mat";
string r = s.replace("at", "og");
print(r); // "the cog sog on the mog"
print(s); // "the cat sat on the mat", original unchanged
Walk through what happened: replace("at", "og") scanned the text and found
"at" inside c-at, s-at, and m-at, replacing each with "og" to give
cog, sog, mog. The word the has no at, so it stayed.
It replaces all matches, not just the first. If you only wanted the first
one, replace is not the tool, it always does every match.
print("a".replace("a", "bb")); // "bb", grew from 1 to 2 chars
Matches don’t overlap. After a match is replaced, scanning continues after
the inserted text, so the new text is never re-scanned:
print("aaa".replace("aa", "b")); // "ba", first "aa"→"b", one "a" left over
A practical example, turning a sentence into a URL slug:
string title ="Hello World Post";
string slug = title.lower().replace("", "-");
print(slug); // "hello-world-post"
Notice the chaining: title.lower() returns a string, and we immediately
call .replace(...) on that result. Because each call returns a new string, you
can line them up left to right.
Returns a piece of the string starting at byte index start, taking
length bytes. Indices start at 0.
Read the second argument carefully. It is a length (a count), not an
end position. substring(2, 3) means “start at index 2 and take 3
characters”, it does not mean “from index 2 up to index 3”.
Returns a one-character string at byte index index (starting at 0). saQut
has no separate “single character” type here, so you get back a length-1 string.
string s ="saqut";
print(s.charAt(0)); // "s"
print(s.charAt(4)); // "t"
To loop over a string character by character (ASCII):
Searches for sub and returns the byte index of its first occurrence. If
sub is not found, it returns null, so the result type is int?
(nullable). You must check for null before using the number.
string s ="hello world";
int? at =s.indexOf("world");
if (at != null) {
print(at); // 6
}
int? missing =s.indexOf("xyz");
if (missing == null) {
print("not found");
}
Returning null (instead of the C convention of -1) means the “not found”
case can’t be silently mistaken for a real index, the compiler forces you to
handle it.
Arrays are reference types, several of these functions change the array
in place (marked “mutates” below). Because arrays are shared, an in-place
change is visible through every variable that points at the same array.
Every struct, no matter what fields it has, comes with two functions for
turning it into text. They are handy for debugging and for producing output
other programs can read.
print() stands apart from the methods above: it is not a method on a type but
a host function, a function implemented in C++ inside the compiler and
exposed to your program through saQut’s FFI seam. It takes one value of any type
and writes it out:
print(42); // 42
print(3.14); // 3.14
print("text"); // text
print(true); // 1 (bool prints as 1 / 0)
print is the first host function exposed through the FFI seam. Other
standard-library functions (files, math, and so on) are exposed through the same
host-function mechanism.