Concept


In programming we call any text a `String` regardless of whether it consists of a single character, a word, or a big block of text. A `String` is basically a sequence of characters of unspecified size that usually ends with the \0 character (character whose ASCII code is 0). In Alusus there are two ways to define a String:

  1. The first way is the class `String` which is included in the standard libraries.
  2. The second way is using the `ptr[array[Char]]` type, which is a pointer to an array of characters. This is the low level way of dealing with strings, which is similar to, and compatible with, the C language.

String Class


This class simplifies dealing with strings since it takes the responsibility of allocating and releasing the memory for the string while taking into account the performance and avoiding unnecessary operations like copying and allocating memory. Also, it contains many helper methods, and it exists in Standard Runtime Library `Srl`, which means we must import it before using this class.

Importing and defining a String variable can be done like this:

import "Srl/String.alusus";  // first we import this class
def string_name: String = "Initial Value"; // define a string

Where the initial value is an optional initial value for the variable `string_name`, and we should remember to enclose the string in double quotes.

Example:
Define and print a string in different ways.

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String="Alusus";
Console.print(str); // Alusus
// In case you want to print it with some explanatory sentences, you can do that as follows:
Console.print(String("language: ") + str); // language: Alusus
// You can also execute the print operation in the following way using the placeholder `s`
Console.print("language: %s", str.buf); // language: Alusus

Note that in this case we use the property `buf` to get a pointer to the string buffer because the print function (which uses libc's printf function) expects C lang strings (i.e. pointer to characters), not an object of type `String`.

You can receive a string from the user by creating an array of chars and using `getString` function in the `Console` module.

Example:
An Alusus program to show a string entered by the user.

import "Srl/Console.alusus"
import "Srl/String.alusus";
use Srl;
def str: String; // define a string
def s: array[Char, 100]; // an array of characters with specific size for input purposes
Console.print("Enter a string: ");
Console.getString(s~ptr, 100); // we pass a pointer to the array with its size
str=s~ptr; // now the characters are copied from the array to the string object
Console.print(String("You entered: ") + str + String("\n"));
/*
Enter a string: Hello World!
You entered: Hello World!
*/

Strings Concatenation


It means adding one string to another, which is done by putting the strings beside each other. For example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def firstName: String = "Ali";
def lastName: String = "Ahmad";
def fullName: String;
fullName = firstName + " " + lastName;
Console.print(String("fullName: ") + fullName); // fullName: Ali Ahmad

Storing and Accessing Strings


Strings are stored in memory character by character and in order. When defining a variable of type `String` an initial memory is allocated for the variable. This memory is increased automatically when necessary. Note the following example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String = "Language "; // here a memory is allocated for the string
str += "Alusus" // here the memory is increased

The number of characters in the string represents the length of the string and we can know that by using `getLength` function. It is also possible to access any character in the string by putting its index enclosed in parentheses after the string name.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def firstName: String = "Ali";
def lastName: String = "Ahmad";
def fullName: String;
fullName = firstName+ " " + lastName
Console.print(String("fullName: ") + fullName+String("\n")) // fullName: Ali Ahmad
Console.print(String("Length: ") + fullName.getLength()+String("\n")) // Length: 9
Console.print(String("Character: ") + fullName(5)) // Character: h

English characters are stored using 1 byte for each character, whereas other languages, like Arabic, needs 2 or more bytes for each character. For example, the string "اب" needs 4 bytes, whereas the string "ab" needs 2 bytes only.

Note: The encoding used in the strings is "UTF-8" and the difference in number bytes between English and other languages is determined by "UTF-8", not Alusus.

String Comparison


We can use operators like ==, !=, <, >, <=, >= to compares strings.
Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str1: String="Alusus";
def str2: String="Language";
// the two strings are the identical
if (str1 == str2) Console.print("str1 == str2\n");
// the two strings are different
if (str1 != str2) Console.print("str1 != str2\n");
// the first string is after the second one in the dictionary order.
if (str1 > str2) Console.print("str1 > str2\n");
if (str1 >= str2) Console.print("str1 >= str2\n");
if (str1 < str2) Console.print("str1 < str2\n");
if (str1 <= str2) Console.print("str1 <= str2\n");
/*
str1 != str2
str1 < str2
str1 <= str2
*/

Notes:

  • The object of type `String` contains a pointer to an array of chars `ptr[array[Char]]` and this array change its size when adding new content.
  • Alusus compiler convert `String` to an array of chars when necessary except for variadic functions.
    For example, in case of print function all the arguments after the first one does not specify a type (because it is a variadic function), and this type of functions accept any variable regardless of its type.
    In this case, the compiler doesn't know that it needs an array of chars because the decision is made inside the function which reads the first argument and determines the types of the others based on it. Because of that, the compiler passes the `String` as it is without converting it into an array of chars and this won't work because the `print` function knows nothing about `String` objects which is an inner class in Alusus, so if you want to pass a string you should use its buffer "string_obj.buf" to pass the array of chars inside the string.

String Methods


We will give some examples of the most important methods of this class. You can view all its methods here.

- We use `find` function to execute the following search operations in the strings:

  1. Find a string inside this string. Returns the location of the first character in the string it found, or -1 if not found.
  2. Find a character in this string. Returns the location of the character it found, or -1 if not found.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String = "Alusus language properly deals with memory";
// here we print the first index that contains the letter 'u' in this string `str'.
Console.print("%d\n", str.find('u')); // 2
// here we print the index at which the string "language" is found (the index of its first letter).
Console.print("%d\n", str.find("language")); // 7

- We use `findLast` function in a way similar to the previous function but the search operation starts from the end of the string.
Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String="Alusus language is based on the C language";
def keyword: char = 's';
// Find first occurrence of 's'
if (str.find(keyword) != -1) {
  Console.print("First occurrence is %d\n",str.find(keyword));
  // First occurrence is 3
}
// Find the last occurrence of 's'
if (str.findLast(keyword) != -1) {
  Console.print("Last occurrence is %d\n",str.findLast(keyword));
  // Last occurrence is 21
}

- You can use `append` function to execute the following operations:

  1. Add a new string to this string.
  2. Add a specific number if characters to this string.
  3. Add a character to the end of this string.
  4. Add an integer or a float to this string.

In all previous cases, the modification is done directly on the string that call these functions.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str1: String = "Alusus language is compatible with ";
def str2: String = "the C language.";
str1.append(str2); // add the second string to the first
Console.print(String("Adding str2 to str1: ") + str1);

/*
Adding str2 to str1: Alusus language is compatible with the C language.
*/

Note: It is also possible to use `+=` operator instead of this function.

- You can use `concat` function in a similar way to `append` function with a small difference which is that this function returns the result in a new string instead of modifying the current string.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str1: String = "Alusus language is based on ";
def str2: String = "the C language.";
def str: String;
str = str1.concat(str2); // add the second string to the first one
Console.print(String("Adding str2 to str1 and storing the result in str: ") + str);

Note: It is also possible to use `+` operator instead of this function.

- You can use `compare` function to do comparison between strings (dictionary order).

  • This function compares the string that called it with the given string.
  • Returns 0 if they are equal.
  • Returns a positive value if the callee string is bigger.
  • Returns a negative value if the callee string is smaller.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str1: String = "Alusus language is compatible with ";
def str2: String = "the C language.";
Console.print("%d\n", str1.compare(str2));
Console.print("%d\n", str1.compare(str1));
Console.print("%d\n", str2.compare(str1));
/*
-51
0
51
*/

- You can use `replace` function to modify a specific from the string "Substring" which called it (modification is not on the same string, instead it is done on a copy). We pass to it the substring we want to remove and the string we want to replace it with.

Example:
We will replace the word "language" with "*" in the following:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str1: String = "Alusus language is compatible with the C language.";
def str2 : String;
str2=str1.replace("language","*");
Console.print(str2)
/*
Alusus * is compatible with the C *.
*/

- You can change the case of letters from upper to lower or the opposite, using `toLowerCase` and `toUpperCase` functions (the modification is done on a copy).

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String = "Alusus language is compatible with the C language.\n";
str = str.toUpperCase();
Console.print(str);
str = str.toLowerCase();
Console.print(str);
/*
ALUSUS LANGUAGE IS COMPATIBLE WITH THE C LANGUAGE.
alusus language is compatible with the c language.
*/

- You can use `split` function to split a string into multiple parts based on a specific character (or a string), and this function returns an array of the strings.
Example:

import "Srl/Console";
import "Srl/String";
import "Srl/Array";
use Srl;
def str: String = "Alusus language is compatible with the C language.";
def arr: Array[String];
arr = str.split(" "); // splitting the string into words and store it in array
def index: Int;
for index = 0, index < arr.getLength(), index++ {
  Console.print("The value of element %d is: %s\n", index, arr(index).buf);
}
/*
The value of element 0 is: Alusus
The value of element 1 is: language
The value of element 2 is: is
The value of element 3 is: compatible
The value of element 4 is: with
The value of element 5 is: the
The value of element 6 is: C
The value of element 7 is: language.
*/

- You can use `slice` function to copy (or cut) a part from the string and store it in a new string without modifying the original string since the function returns the part ("Substring") as new string. We pass to this function the index of the character we want to start copying from it and the number of characters we want to copy.

Example:

import "Srl/Console.alusus";
import "Srl/String.alusus";
use Srl;
def str: String="Alusus language is compatible with the C language.";
def substring: String;
substring = str.slice(7, 8);
Console.print(substring);
/*
language
*/

ptr[array[Char]] Type


We can use this type to define a string. To access the characters of this string we will user the operator `~cnt` because the variable is a pointer and we want to access the content it points to, which is what this operator does.

Example 1:

import "Srl/Console.alusus"
use Srl.Console;
def s: ptr[array[Char]] = "Alusus"; // define a string
print("Lannguage: %s\n", s); // print it
print("First character: %c\n", s~cnt(0)); // accessing the first item and print it
print("Second one: %c\n", s~cnt(1)); // accessing the second item and print it
print("ASCII of A: %d",s~cnt(0)); // print the value of the first item in ASCII
/*
Lannguage: Alusus
First character: A
Second one: l
ASCII of A: 65
*/

Example 2:
An Alusus program to show a string entered by the user.

import "Srl/Console.alusus"
use Srl.Console;
def s: ptr[array[Char]];
def input: array[Char, 100]; // define an array of chars for input
print("Enter a string: ");
getString(input~ptr, 100);
s = input~ptr;
print("You entered : %s\n",s) // print it
/*
Enter a string: Alusus Language
You entered : Alusus Language
*/

- Generally, using `String` class is easier, more flexible, and safer. Also, it contains many helper methods, and it can be used instead of `ptr[array[Char]]` so it is better to use it. But sometimes, we may need to deal with C libraries and functions, in that case we need to deal with this type (it is compatible with char* in C).

- The type `ptr[array[Char]]` is compatible with `char *` in C as we mentioned, so it is useful to deal with the system libraries and functions.

- When dealing with this type it is hard to use the strings dynamically because we need to allocate the memory and release it manually, whereas in `String` class these operations are done easily and automatically.

- The size of the string is fixed since the size of the characters buffer is predetermined and you can not change it unless you allocate another memory dynamically and manually, so changing the string by removing or adding an item becomes complicated.

Note that in the previous example we defined a variable `s` with type `ptr[array[Char]]` and then we defined an array of chars `input` with size 100 for input operation, so the entered string must be no longer than 100 characters.