func main { def a: Array[Int] = getArray(); a.add(1); // Here the allocated memory block is expanded. def a2: Array[Int] = a; // No new memory block is allocated here. a.add(2); // Now a new copy of the buffer is created so a2 is not affected. printArray(a); // No new memory block is created or copied here. // Now the memory blocks of a and a2 are freed. } func getArray(): Array[Int] { def a: Array[Int]; a.add(0); return a; // No new memory block is allocated here and memory copy happens. } func printArray (a: Array[Int]) { def i: Int; for i = 0, i < a.getLength(), ++i Console.print("%d\n", a(i)); }`Array` class contains the following members:
handler this~init(ref[Array[ContentType]]); handler this~init(count: Int, args: ...ContentType);The first form initializes an array from another. The new array will use the same content of the given array, and no copy will happen until one of them changes the content. In that case, the content is copied before the change occurs to ensure that the other array is not affected.
def a1: Array[Int]({ 5, 2, 1 }); // Array will contain 3 elements: 5, 2, and 1. def a2: Array[Int](3, 5, 2, 1); // Array will contain 3 elements: 5, 2, and 1.
handler this.getLength (): ArchInt;Returns the number of items in the array.
handler this.getBufSize (): ArchInt;Returns the number of items that the current allocated memory could store. When exceeding that size the object will extend the memory capacity automatically.
handler this.assign (a: ref[Array[ContentType]]);Assigns new content to the array from another array. This function does not copy the contents of the other array, instead both arrays share the same buffer until one of the them needs to change its content, in which case that array will copy the buffer and end the sharing.
handler this.add (e: ContentType); handler this.add (argCount: Int, args: ...ContentType); handler this.add (e: Array[ContentType]);The first form adds an item to the array after extending the allocated memory if necessary. If the content is shared with another array, this function copies the content to a new buffer.
a.add({ 5, 2, 1 }); // Adds 3 elements: 5, 2, 1. a.add(3, 5, 2, 1); // Adds 3 elements: 5, 2, 1.
handler this.insert (index: ArchInt, element: ContentType);Adds new item to the array in the specified location after extending the allocated memory if necessary. If the content is shared with another array, this function copies the content to a new buffer.
handler this.remove (index: ArchInt);Removes the item at the specified index. If the content is shared with another array, this function copies the content to a new buffer.
handler this.slice (start: ArchInt, count: ArchInt);Copys part of the array and returns it as a new array. The copy starts from the item at index `start` and continues until `count` items are copied or the end of the array is reached.
handler this.clear ();Removes all items from the array. If the content is not shared with another array the buffer will be released.
func main { def str: String = getString(); str += " world"; // Memory block is expanded here. def str2: String = str; // No new memory is allocated here. str += "."; // Copy of the string buffer is created so str2 is not affected. printStr(str); // No new memory allocation or copy happens here. // Now memory blocks of str and str2 are freed. } func getString (): String { def s: String = "Hello"; return s; // No new memory allocation or copy happens here. } func printStr (s: String) { Console.print(s); // s is automatically casted into ptr[array[Char]]. }`String` class contains the following members:
def buf: ptr[array[Char]];A pointer to this string content.
handler this(i: ArchInt): CharThis operator could be used to retrieve the character at the specified location.
handler this == ptr[array[Char]]: Bool handler this > ptr[array[Char]]: Bool handler this < ptr[array[Char]]: Bool handler this >= ptr[array[Char]]: Bool handler this <= ptr[array[Char]]: BoolCompares this string to the given string.
1: handler this.getLength (): ArchInt; 2: func getLength (p: ptr[array[Char]]): ArchInt;1. Returns this string's length.
handler this.alloc (ArchInt);Allocate memory in advance. This function allows the user to allocate memory in advance for use with string operations that deals with the string buffer directly. This function is useful for dealing with libraries that deal with char pointers, while benefiting from the memory management functionality that this class provides.
handler this.realloc (ArchInt);Changes the size of allocated memory for this string. This function enables the user to change the buffer size while doing string operations directly on the buffer.
1: handler this.assign (str: ref[String]); 2: handler this.assign (buf: ptr[array[Char]]); 3: handler this.assign (buf: ptr[array[Char]], count: ArchInt); 4: func assign (target: ptr[array[Char]], fmt: ptr[array[Char]], ...any): Int;1. Assigns new content to ths string from another string. This function does not copy the content of the other string, instead they both share the same buffer until one of them needs to change it, at which point the buffer is copied and the sharing ends.
1. handler this.append (buf: ptr[array[Char]]); 2. handler this.append (buf: ptr[array[Char]], count: ArchInt); 3. handler this.append (c: Char); 4. handler this.append (i: Int[64]); 5. handler this.append (f: Float[64]);1. Appends the given buffer to the content's end of the string.
1. handler this.concat (buf: ptr[array[Char]]); 2. handler this.concat (buf: ptr[array[Char]], count: ArchInt); 3. handler this.concat (c: Char); 4. handler this.concat (i: Int[64]); 5. handler this.concat (f: Float[64]); 6. func concat (target: ptr[array[Char]], source: ptr[array[Char]]): ptr; 7. func concat (target: ptr[array[Char]], source: ptr[array[Char]], count: ArchInt): ptr;1-5. These functions are similar to `append` functions but they return the value in new string instead of editing the current string. It is possible to replace functions 1, 3, 4, and 5 with the `+` operator.
1. handler this.find (buf: ptr[array[Char]]): ArchInt; 2. handler this.find (c: Char): ArchInt; 3. func find (haystack: ptr[array[Char]], needle: ptr[array[Char]]): ptr[array[Char]]; 4. func find (haystack: ptr[array[Char]], c: Char): ptr[array[Char]];1. Searches for a string inside this string. It returns the start location of the found string, or -1 if nothing is found.
1: handler this.findLast (buf: ptr[array[Char]]): ArchInt; 2: handler this.findLast (c: Char): ArchInt; 3: func findLast (haystack: ptr[array[Char]], needle: ptr[array[Char]]): ptr[array[Char]]; 4: func findLast (haystack: ptr[array[Char]], c: Char): ptr[array[Char]];These functions are similar to `find` functions, but they start the search from the end of the string instead of the beginning.
1. handler this.compare (buf: ptr[array[Char]]): Int; 2. handler this.compare (buf: ptr[array[Char]], count: ArchInt): Int; 3. func compare (str1: ptr[array[Char]], str2: ptr[array[Char]]): Int; 4. func compare (str1: ptr[array[Char]], str2: ptr[array[Char]], count: ArchInt): Int;1. Compares the current string with the given string and returns 1 if the current one is bigger, -1 if it is smaller, or 0 if they are the same.
1. handler this.replace (match: ptr[array[Char]], replacement: ptr[array[Char]]): String; 2. func replace (chars: ptr[array[Char]], from: Char, to: Char): ptr[array[Char]];1. Replaces a part of the string with another string, and returns the result in a new string.
handler this.trim (): String;Removes the spaces (space, new line, or tab characters) from both sides of the string and returns the result in a new string.
handler this.trimStart (): String;This is similar to `trim` function but it trims from the beginning only.
handler this.trimEnd (): String;This is similar to `trim` function but it trims from the end only.
handler this.toUpperCase (): String;Replaces the latin small characters with their corresponding capital characters and returns the result in a new string.
handler this.toLowerCase (): String;Replaces the latin capital characters with their corresponding small characters and returns the result in a new string.
handler this.slice (start: ArchInt, count: ArchInt): String;Returns a part of the current string in a new string.
handler this.split (separator: ptr[array[Char]]): Array[String];Splits the current string into an array of strings using the given separator. The separator is not included in the result. If that separator is not found, it returns and array of one element which is the whole string.
func merge (parts: Array[String], separator: ptr[array[Char]]): String;Returns a new string which contains the given parts separated with the given separator.
1. func copy (target: ptr[array[Char]], source: ptr[array[Char]]): ptr; 2. func copy (target: ptr[array[Char]], source: ptr[array[Char]], count: ArchInt): ptr;1. Copies the array of chars from the source to the target. User should ensure that the memory allocated for target is enough to hold the source content.
func scan (source: ptr[array[Char]], fmt: ptr[array[Char]], ...any): Int;Scans the given string searching for the given items in the format and returns those items in the arguments after the format. This function is similar to `sscanf` system function.
func isSpace (c: Char): Bool;Tells whether the given character is a space, a tab, a new line, or a carriage return.
func isEqual (str1: ptr[array[Char]], str2: ptr[array[Char]]): Bool;Tells whether the two given strings are the same.
func remove (chars: ptr[array[Char]], c: Char): ptr[array[Char]];Removes every match of the given character from the given array of chars. Removing that character results in shifting following characters to replace it. This function modifies the provided string.
func format (fmt: ptr[array[Char]], values: ...any): String;Create a new string from given format after filling it with the given arguments. This function is similar to `sprintf` function, by replacing symbols starting with `%` in the format by a value from arguments that match the type specified. The symbol `%` is followed by a character specifying the type of the given argument. As follows:
func parseInt (str: ptr[array[Char]]): Int[64];Reads an integer from the given array of chars and returns it.
func parseFloat (str: ptr[array[Char]]): Float[64];Reads a float from the given array of chars and returns it.
func parseHexDigit (Char): Int;Parses the given HEX character.
class StringBuilder [formatMixin: ast_ref = _emptyMixin] { handler this~init(); handler this~init(initialSize: ArchInt, growSize: ArchInt); handler this~init(str: String, growSize: ArchInt); handler this.append (buf: CharsPtr); handler this.append (buf: CharsPtr, bufLen: ArchInt); handler this.append (c: Char); handler this.append (i: Int[64]); handler this.append (f: Float[64]); handler this.format(fmt: ptr[array[Char]], args: ...any); handler this.clear(); handler this.getLength(); handler this += CharsPtr this.append(value); handler this += Char this.append(value); handler this += Int[64] this.append(value); handler this += Float[64] this.append(value); handler this~cast[ref[String]]; }The following example shows how to use this class:
func main { def sb: StringBuilder( 1024, // preallocate 1024 bytes 512 // enlarge memory block by 512 whenever it's full ); sb.append("Result of "); sb.format("7 + 7 = %i\n", 7 + 7); def s: String = sb; Console.print(s); // Prints: Result of 7 + 7 = 14 }
handler this~init(); handler this~init(initialSize: ArchInt, growSize: ArchInt); handler this~init(str: String, growSize: ArchInt);The second form of the constructor preallocates the buffer with the size specified in the first arg, while the second argument specifies the size by which to enlarge the buffer everytime it's full.
def StringBuilderMixin { @format["gd"] handler this.formatTimestamp(ts: Int[64]) { this.append(Time.toString(ts)); } } func main { def sb: StringBuilder[StringBuilderMixin](1024, 512); def timestamp: Int[64] = ...; sb.format("Today's date is %gd", timestamp); ... }
func main { def m1: Map[Int, Int] = getMap(); m1.set(7, 50); // Memory buffer is expanded here. def m2: Map[Int, Int] = m1; // Buffer is not cloned here. m2.set(12, 7); // Buffer is cloned here so that m1 is not affected. printMap(m2); // No buffer cloning happens here. printInt(m2(7)); // Prints 50. printInt(m2(12)); // Prints 7. // Now buffers of m1 and m2 are freed. } func getMap (): Map[Int, Int] { def m: Map[Int, Int]; m.add(0, 10); return m; // No buffer cloning happens here. } func printMap (m: Map[Int, Int]) { def i: Int; for i = 0, i < m.getLength(), ++i { Console.print("%d = %d\n", m.keyAt(i), m.valAt(i)); } }Class `Map` contains the following members:
handler this~init (useIndex: Bool); handler this~init (ref[Map[KeyType, ValueType]]); handler this~init (ref[Map[KeyType, ValueType]], useIndex: Bool);The first form initializes empty Map and choosing between using an index or not. The use of index increases the speed of searching for items in the Map at the expense of consuming more memory and slowing down write operations.
def keys: Array[KeyType];The array of the keys in the map.
def values: Array[ValueType];The array of the values in the map.
handler this.getLength (): ArchInt;Returns the number of items in the map.
handler this.keyAt (ArchInt): ref[KeyType];Returns a reference to the key at the given location.
handler this.valAt (ArchInt): ref[ValueType];Returns a reference to the value at the given location.
handler this.set ( key: KeyType, value: ValueType ): ref[Map[KeyType, ValueType]];Sets a value for the given key. If that key doesn't exist it will be added. If it already exists its value will be replaced by the new one. This functions will return a reference to the Map itself which allows the user to chain multiple `set` operations in one statement.
handler this.setAt ( index: ArchInt, value: ValueType ): ref[Map[KeyType, ValueType]];Sets a new value on the given index, which means changing the value based on its index instead of its key. Returns a reference to the Map itself which allows the user to chain multiple `setAt` operations in a one statement.
handler this.insert (index: ArchInt, key: KeyType, value: ValueType);Inserts a new key and its value at the given index.
handler this.remove (key: KeyType): Bool;Removes the specified key and its value. Returns 1 if the key is removed, and 0 if the key does not exist.
handler this.removeAt (index: ArchInt);Removes the specified key and its value at the given index.
handler this.clear ();Clear all content and starts with an empty Map.
handler this.findPos (key: KeyType): ArchInt;Returns the index of the given key, or -1 if the key does not exist.
func main { def x: SrdRef[MyType] = SrdRef[MyType].construct(); def y: SrdRef[MyType] = x; // Both refs point to same object. Counter is now 2. x.release(); // Counter is now 1 and the object is not freed yet. y.release(); // Now the object is destructed and its memory is released. }`SrdRef` Class contains the following members:
handler this.alloc (): ref[ObjType]; func alloc (): SrdRef[ObjType];This function allocates memory for the object, but does not initialize the object and leaves that to the user who can use `~init`.
handler this.construct (); func construct (): SrdRef[ObjType];This function allocates memory for the object and initialize it also. Initializing the object is done without any arguments, so to use this function the object must enable initialization without arguments. If you need to initialize an object with arguments then you should use `allocate` then `~init`.
handler this.own (obj: ref[ObType]); func own (obj: ref[ObjType]): SrdRef[ObjType];This function assigns the object of this reference with a given object. After calling this function the reference takes the responsibility of releasing the object automatically. This function should be avoided if the given object is not allocated dynamically or if another code is responsible for releasing the object, since this will lead to segmentation fault.
handler this.release();Release this reference, and if this reference is the last to point to the object then it will release the object before that.
handler this.assign (r: ref[SrdRef[ObjType]]); handler this.assign (r: ref[WkRef[ObjType]]); handler this.assign (c: ref[RefCounter], o: ref[ObjType]);Set a new value to the reference, it changes the reference to point to the given object. The first two functions takes the value of the reference from types `SrdRef` and `WkRef` respectively with the same object type. On the other hand, the third function is used to make the reference points to an object with the same type of this reference, but belongs to a reference with another type. This function is used in casting while ensuring that releasing the object when terminating the reference will leads to releasing the original object with its original type that is created with. The next example explains the difference:
def x: SrdRef[MyType]; x.construct(); def y: SrdRef[SubType]; y.assign(x.refCounter, x.subObj); x.release(); // Nothing will be released as y still holds the object. y.release(); // Here x itself will be released, not x.subObj.
handler this.isNull(): Bool;Returns true if the reference is null.
handler this.release();Release this reference without changing references count.
handler this.assign (r: ref[SrdRef[ObjType]]); handler this.assign (r: ref[WkRef[ObjType]]); handler this.assign (c: ref[RefCounter], o: ref[ObjType]);Sets a new value to the reference, it changes the reference to point to the given object. The first two functions takes the value of the reference from types `SrdRef` and `WkRef` respectively with the same object type. On the other hand, the third function is used to make the reference points to an object with the same type of this reference, but belongs to a reference with another type. This function is used in casting while ensuring that releasing the object when terminating the reference will leads to releasing the original object with its original type that is created with. The next example explains the difference:
def x: SrdRef[MyType]; x.construct(); def y: WkRef[SubType]; y.assign(x.refCounter, x.subObj); def z: SrdRef[SubType]; z = y; x.release(); // Nothing is released here. z.release(); // x will be released here, not x.subObj.
handler this.isNull(): Bool;Returns true if the reference is null.
def x: UnqRef[MyType]; x.construct(); def y: UnqRef[MyType]; y = x; // Error. x.release(); // Object will be released here.This class contains the following items:
handler this.construct();This function allocate memory for the object and initialize it also. Initialization of the object is done without arguments, so to use this function the object must be able to initialize without arguments.
handler this.release();Release this reference and release the object.
handler this.isNull(): Bool;Returns true if the reference is null.
handler this.getCode(): String as_ptr;
handler this.getMessage(): String as_ptr;
@injection def value: DataType;
def error: SrdRef[Error];
handler this~cast[ref[DataType]] return this.value; handler this~cast[Bool] return this.error.isNull();Casting operation as `Bool` returns 1 if the operation succeed, which means the information is valid.
func success (v: DataType): Possible[DataType];
func failure (err: SrdRef[Error]): Possible[DataType];
@injection def value: DataType;
def isNull: Bool;
handler this~cast[ref[DataType]] return this.value;
@expname[malloc] func alloc (size: ArchInt): ptr;Allocates a memory block on the heap and returns its pointer. This is the same as `malloc` function from POSIX.
@expname[realloc] func realloc (p: ptr, newSize: ArchInt): ptr;Changes the size of an allocated memory block. This may result in copying the memory block to a new location. This is the same as `realloc` function from POSIX.
@expname[aligned_alloc] func allocAligned (alignment: ArchInt, size: ArchInt): ptr;This is the same as `aligned_alloc` function from POSIX.
@expname[free] func free (p: ptr);Frees an allocated memory blockl. This is the same as `free` function from POSIX.
@expname[memcpy] func copy (target: ptr, src: ptr, size: ArchInt): ptr;Copies a memory block to a new location. This is the same as `memcpy` function from POSIX.
@expname[memcmp] func compare (s1: ptr, s2: ptr, size: ArchInt): Int;Compares two memory blocks. Returns a value < 0 if the first non-matching byte in s1 is smaller than its counterpart in s2. Returns a value > 0 if it's greater. Returns 0 if the two blocks are identical. This is the same as `memcmp` function from POSIX.
@expname[memset] func set (s: ptr, c: Int, n: ArchInt): ptr;Sets all bytes in the specified memory buffer to the given value. This is the same as `memset` function from POSIX.
function overrideAllocator( customAlloc: ptr[function (size: ArchInt) => ptr[Void]], customRealloc: ptr[function (p: ptr[Void], newSize: ArchInt) => ptr[Void]], customAllocAligned: ptr[function (alignment: ArchInt, size: ArchInt) => ptr[Void]], customFree: ptr[function (pointer: ptr[Void])] ); function resetAllocator();Custom allocators will need to directly reach the system allocation functions, which are listed here:
@expname[abs] func abs (i: Int[32]): Int[32]; @expname[llabs] func abs (i: Int[64]): Int[64]; @expname[fabsf] func abs (f: Float[32]): Float[32]; @expname[fabs] func abs (f: Float[64]): Float[64];Get the absolute value of a number.
@expname[fmodf] func mod (x: Float[32], y: Float[32]): Float[32]; @expname[fmod] func mod (x: Float[64], y: Float[64]): Float[64];Get division remainder of two numbers.
@expname[expf] func exp (x: Float[32]): Float[32]; @expname[exp] func exp (x: Float[64]): Float[64];This is the same as `expf` and `exp` functions from POSIX.
@expname[exp2f] func exp2 (x: Float[32]): Float[32]; @expname[exp2] func exp2 (x: Float[64]): Float[64];This is the same as `exp2f` and `exp2` functions from POSIX.
@expname[logf] func log (x: Float[32]): Float[32]; @expname[log] func log (x: Float[64]): Float[64];This is the same as `logf` and `log` functions from POSIX.
@expname[log2f] func log2 (x: Float[32]): Float[32]; @expname[log2] func log2 (x: Float[64]): Float[64];This is the same as `log2f` and `log2` functions from POSIX.
@expname[log10f] func log10 (x: Float[32]): Float[32]; @expname[log10] func log10 (x: Float[64]): Float[64];This is the same as `log10f` and `log10` functions from POSIX.
@expname[sqrtf] func sqrt (x: Float[32]): Float[32]; @expname[sqrt] func sqrt (x: Float[64]): Float[64];This is the same as `sqrtf` and `sqrt` functions from POSIX.
@expname[cbrtf] func cbrt (x: Float[32]): Float[32]; @expname[cbrt] func cbrt (x: Float[64]): Float[64];This is the same as `cbrtf` and `cbrt` functions from POSIX.
@expname[powf] func pow (b: Float[32], e: Float[32]): Float[32]; @expname[pow] func pow (b: Float[64], e: Float[64]): Float[64];This is the same as `powf` and `pow` functions from POSIX.
@expname[sinf] func sin (x: Float[32]): Float[32]; @expname[sin] func sin (x: Float[64]): Float[64];This is the same as `sinf` and `sin` functions from POSIX.
@expname[asinf] func asin (x: Float[32]): Float[32]; @expname[asin] func asin (x: Float[64]): Float[64];This is the same as `asinf` and `asin` functions from POSIX.
@expname[sinhf] func sinh (x: Float[32]): Float[32]; @expname[sinh] func sinh (x: Float[64]): Float[64];This is the same as `sinhf` and `sinh` functions from POSIX.
@expname[asinhf] func asinh (x: Float[32]): Float[32]; @expname[asinh] func asinh (x: Float[64]): Float[64];This is the same as `asinhf` and `asinh` functions from POSIX.
@expname[cosf] func cos (x: Float[32]): Float[32]; @expname[cos] func cos (x: Float[64]): Float[64];This is the same as `cosf` and `cos` functions from POSIX.
@expname[acosf] func acos (x: Float[32]): Float[32]; @expname[acos] func acos (x: Float[64]): Float[64];This is the same as `acosf` and `acos` functions from POSIX.
@expname[coshf] func cosh (x: Float[32]): Float[32]; @expname[cosh] func cosh (x: Float[64]): Float[64];This is the same as `coshf` and `cosh` functions from POSIX.
@expname[acoshf] func acosh (x: Float[32]): Float[32]; @expname[acosh] func acosh (x: Float[64]): Float[64];This is the same as `acoshf` and `acosh` functions from POSIX.
@expname[tanf] func tan (x: Float[32]): Float[32]; @expname[tan] func tan (x: Float[64]): Float[64];This is the same as `tanf` and `tan` functions from POSIX.
@expname[atanf] func atan (x: Float[32]): Float[32]; @expname[atan] func atan (x: Float[64]): Float[64];This is the same as `atanf` and `atan` functions from POSIX.
@expname[atan2f] func atan2 (y: Float[32], x: Float[32]): Float[32]; @expname[atan2] func atan2 (y: Float[64], x: Float[64]): Float[64];This is the same as `atan2f` and `atan2` functions from POSIX.
@expname[tanhf] func tanh (x: Float[32]): Float[32]; @expname[tanh] func tanh (x: Float[64]): Float[64];This is the same as `tanhf` and `tanh` functions from POSIX.
@expname[atanhf] func atanh (x: Float[32]): Float[32]; @expname[atanh] func atanh (x: Float[64]): Float[64];This is the same as `atanhf` and `atanh` functions from POSIX.
@expname[ceilf] func ceil (x: Float[32]): Float[32]; @expname[ceil] func ceil (x: Float[64]): Float[64];This is the same as `ceilf` and `ceil` functions from POSIX.
@expname[floorf] func floor (x: Float[32]): Float[32]; @expname[floor] func floor (x: Float[64]): Float[64];This is the same as `floorf` and `floor` functions from POSIX.
@expname[roundf] func round (x: Float[32]): Float[32]; @expname[round] func round (x: Float[64]): Float[64];This is the same as `roundf` and `round` functions from POSIX.
@expname[rand] func random (): Int;This is the same as `rand` function from POSIX.
@expname[srand] func seedRandom (s: Word[32]);This is the same as `srand` function from POSIX.
func getBuildDependencies(): Array[String];Returns the required dependencies names needed in case of building executable version of projects that use this module. In the case of `Net` module the function returns one library which is Curl. `Net` module contains the full path for the file, not only its name because Alusus depends on a version of Curl bundled with Alusus.
1: func get ( url: ptr[array[Char]], result: ptr[ptr], resultCount: ptr[Int] ): Bool; 2: func get ( url: ptr[array[Char]], filename: ptr[array[Char]] ): Bool;1. Get the resource specified by the url and return it.
func uriEncode(CharsPtr): String;URL encodes the given string.
func uriDecode(CharsPtr): String;URL decodes the given string.
@expname[getchar] func getChar (): Int;This is similar to `getChar` function from POSIX.
@expname[putchar] func putChar (c: Int): Int;This is similar to `putchar` function from POSIX.
1. @expname[printf] func print (fmt: ptr[array[Char]], ...any): Int; 2. func print (i: Int[64]); 3. func print (f: Float[64]); 4. func print (f: Float[64], n: Int);1. This is the same as `printf` function from POSIX.
@expname[scanf] func scan (fmt: ptr[array[Char]], ...any): Int;This is the same as `scanf` function from POSIX.
func getInt (): Int;Asks the user to enter an integer.
print getFloat (): Float;Asks the user to enter a floating point number.
func getString (str: ptr[array[Char]], size: Word);Asks the user to enter a string.
@expname[usleep] func sleep (w: Word);This is the same as `usleep` function from POSIX.
@expname[setenv] func setEnv ( name: ptr[array[Char]], value: ptr[array[Char]], overwrite: Int ): Int;This is the same as `setenv` function from POSIX.
@expname[getenv] func getEnv (name: ptr[array[Char]]): ptr[array[Char]];This is the same as `getenv` function from POSIX.
@expname[system] func exec (cmd: ptr[array[Char]]): Int;This is the same as `system` function from POSIX.
@expname[exit] func exit (status: Int);This is the same as `exit` function from POSIX.
class DirEnt { def dType: Int[8]; def dName: array[Char, FILENAME_LENGTH]; };Information record about an item from folder items.
class FileNames { def count: Int; def names: array[array[Char, FILENAME_LENGTH]]; }A list of files' names.
def Seek: { def SET: 0; def CUR: 1; def END: 2; };Constants to deal with `seek` function.
func exists (filename: ptr[array[Char]]): Bool;Check if a specific file or folder exists.
@expname[rename] func rename (oldName: ptr[array[Char]], newName: ptr[array[Char]]): Int;Renames a file or a folder.
@expname[remove] func remove (filename: ptr[array[Char]]): Bool;Removes a file or a folder.
@expname[fopen] func openFile (filename: ptr[array[Char]], mode: ptr[array[Char]]): ptr[File];Opens a file. This is the same as `fopen` function from POSIX.
@expname[fclose] func closeFile(f: ptr[File]): Int;Closes an open file. This is the same as `fclose` function from POSIX.
@expname[fprintf] func print (f: ptr[File], fmt: ptr[array[Char]], ...any): Int;Print a string to the file. This is the same as `fprintf` function from POSIX.
@expname[scanf] func scan (f: ptr[File], fmt: ptr[array[Char]], ...any): Int;Reads inputs from the file. This is the same as `scanf` function from POSIX.
@expname[fwrite] func write ( content: ptr, size: ArchInt, count: ArchInt, f: ptr[File] ): ArchInt;Writes a memory block to a file. This is the same as `fwrite` function from POSIX.
@expname[fread] func read ( content: ptr, size: ArchInt, count: ArchInt, f: ptr[File] ): ArchInt;Reads a block of data from the file. This is the same as `fread` function from POSIX.
@expname[fflush] func flush (f: ptr[File]): Int;Flushes the write buffer. This is the same as `fflush` function from POSIX.
@expname[ftell] func tell (f: ptr[File]): ArchInt;Get the current seek position of the file. This is the same as `ftell` function from POSIX.
@expname[fseek] func seek (f: ptr[File], offset: ArchInt, seek: Int): Int;Moves the file read/write pointer. This is the same as `fseek` function from POSIX.
func createFile ( filename: ptr[array[Char]], content: ptr, contentSize: ArchInt ): Bool;Creates a file and store the given content in it.
func readFile ( filename: ptr[array[Char]], result: ptr[ptr], size: ptr[ArchInt] ): Bool;Reads the full contents of the file and returns it in a new block of memory. The caller is responsible for releasing the allocated memory buffer.
func makeDir (folderName: ptr[array[Char]], mode: Int): Bool;Create a new folder.
@expname[opendir] func (folderName: ptr[array[Char]]): ptr[Dir];Opens a folder for reading. This is the same as `opendir` function from POSIX.
@expname[closedir] func (folder: ptr[Dir]): Int;Closes an open folder. This is the same as `closedir` function from POSIX.
@expname[rewinddir] func rewindDir (dir: ptr[Dir]);Reset the position of a directory stream to the beginning of a directory. This is the same as `rewinddir` function from POSIX.
1. @expname[readdir] func readDir (dir: ptr[Dir]): ptr[DirEnt]; 2. func readDir (name: ptr[array[Char]]): ptr[FileNames];1. This is the same as `readdir` function from POSIX.
func match ( pattern: ptr[array[Char]], str: ptr[array[Char]], flags: Int ): Array[String];Apply the given pattern to the given string and return the result as an array if strings. In case of match the array contains the whole match for the pattern in the first item in the array, whereas the following items contain the partial match determined by the pattern inside parentheses. In case of no match, the result is an empty array.
class Matcher { handler this~init(pattern: ptr[array[Char]]); handler this~init(pattern: ptr[array[Char]], flags: Int); handler this.initialize(pattern: ptr[array[Char]]); handler this.initialize(pattern: ptr[array[Char]], flags: Int); handler this.release(); handler this.match (str: ptr[array[Char]]): Array[String]; }A class that allows the user to intialize regular expression then use it in multiple searching operations. The method `match` applies the pattern to the given string and returns an array of strings. In case there exists a match the array contains the whole match of the pattern in the first item in the array whereas the following items contain the partial match determined by the pattern inside parentheses. In case of no match, the result is an empty array.
class DetailedTime { def second: Int; def minute: Int; def hour: Int; def day: Int; def month: Int; def year: Int; def weekDay: Int; def yearDay: Int; def daylightSaving: Int; def timezoneOffset: Int[64]; def timezone: ptr[array[Char]]; };A record that holds the date and time information.
1. @expname[localtime] func getDetailedTime ( ts: ptr[Word[64]] ): ptr[DetailedTime]; 2. @expname[localtime_r] func getDetailedTime ( ts: ptr[Word[64]], ptr[DetailedTime] ): ptr[DetailedTime];1. This is the same as `localtime` function for POSIX.
@expname[time] func getTimestamp (result: ptr[Word[64]]): ptr[Word[64]];Returns the time as the number of seconds since the Epoch. This is the same as `time` function for POSIX.
@expname[clock] func getClock (): Int[64];This is the same as `clock` function for POSIX.
@expname[clock] func toString (ts: ptr[Word[64]]): ptr[array[Char]];This is the same as `clock` function for POSIX.
This library adds support for closures.
Closures are functions that carry needed environment data with them, i.e. carries a payload which includes values of outer
variables accessed by the function.
Inner functions can access global variables outside of it, but it can not access local variables inside an outer function that contains the inner function,
because the outer function could be terminated and its variables removed from memory before the inner function is called.
For example:
def pf: ptr[func]; func prepareFunc { def i: Int = 10; pf = func { Console.print("%d\n", i); // Error: by the time this line is executed // i would have been removed from memory }; } prepareFunc(); pf();To enable an inner function to access the outer function's variable, the function must carry a copy from the data that it uses from the external function, which is what closures provide. Closures are provided by `closure` library. The compiler automatically prepares a payload that encapsulates accessed outer variables and attaches them to the clsoure. The definitions of the closure are similar to inner function definition except that they use `closure` keyword instead of `function` keyword. Example of closure:
import "closure"; def pc: closure (); func prepareClosure { def i: Int = 10; pf = closure () { Console.print("%d\n", i); // Correct: access to i will be replaced // by the compiler to an access to a copy of i }; } prepareClosure(); pc();We can notice from the previous example that writing a closure without a body makes it a closure definition, and it is important to know that closure record contains a pointer to the function in addition to a shared reference to information record. Responsibility remains on the programmer to ensure that accessing shared references will not lead to circular references, and hence a memory leak. For example, if you access a shared reference from a closure and that reference is pointing to the record that contains the closure, then this will result in memory leak (the record holds the closure which in turn holds the record so both will stay in the memory). In that case, all we need to do is to make the closure hold a non-shared reference to avoid memory leaks. For example:
import "closure"; class Record { def c: closure (); def i: Int; } def r1: SrdRef[Record]; r1.construct(); r1.i = 10; r1.c = closure() { Console.print("%d\n", r1.i); }; r1.release(); // r1 will not be released and neither will the closure it owns. def r2: SrdRef[Record]; r2.construct(); r2.i = 10; def r22: WkRef[Record] = r2; r2.c = closure() { Console.print("%d\n", r22.i); }; r2.release(); // r2 will be released along with the closure it owns.Closure can receive values and return values as regular functions.
import "closure"; def pc: closure (Float): Int; func prepareClosure { def i: Int = 10; pf = closure (j: Float): Int { return i * j; }; } prepareClosure(); Console.print("%d\n", pc(3.5)); // Prints 35
closure (/*capture_mode_defs*/)&(/*args_defs*/): /*ret_type*/ { // closure body }In the next example we set the way to capture `n` as `by_ref` and that tells the closure to store a reference to that variable instead of a copy, so changing the variable's value inside the closure will lead to changing the original variable outside the closure.
func testCaptureByRef (n: Int) { def c: closure (i: Int): Int; c = closure (n: by_ref)&(i: Int): Int { return i * n++; }; print("n before calling c: %d\n", n); print("closure result: %d\n", c(3)); print("n after calling c: %d\n", n); } testCaptureByRef(5); /* output: n before calling c: 5 closure result: 15 n after calling c: 6 */
In that example if we change the way if capturing `n` from `by_ref` to `by_val` then the output after calling the closure is 5 instead of 6.
The following modes of data capturing are available:def c: closure (i: Int): Int; c.isNull() // returns true c = closure (i: Int): Int { return 0 }; c.isNull() // returns false
def MyMixin: { handler this.print() { Console.print(this.toString()); } } class MyType { Spp.astMgr.insertMixin[MyMixin]; // Now the class has the `print` method. ... }Mulitple mixins can also be merged into a single mixin usign the & operator, as in the following example:
def Mixin1: { handler this.print() { Console.print(this.toString()); } } def Mixin2: { handler this.save(filename: String) { def content: String = this.toString(); Fs.createFile(filename, content.buf, content.getLength()); } } def Mixins: Mixin1 & Mixin2; class MyType { Spp.astMgr.insertMixin[Mixins]; // Now the class has the `print` and the `save` methods. ... }
def exe: Build.Exe(WidgetGuide.start~ast, "hello_world"); exe.addDependency(Gtk~ast); // or exe.addDependency(String("libgtk....")); exe.addDependencies(Array[String]({ String("libcurl.so"), ... })); exe.addFlag("-Wl,-rpath,@executable_path"); if exe.generate() { Srl.Console.print("Build complete.\n"); } else { Srl.Console.print("Build failed.\n"); };The next code shows how libraries developers could add external dependencies information to their modules:
@deps["libmyextlib.so"] module MyLib { ... };Without adding `@deps` modifier, the `addDependency` function does nothing.
You can control the result of the build by changing the value of the `targetTriple` member of the `Exe` class, which allows targetting an OS different from the current OS. You can also specify a different linker from the default one by setting the `linkerFilename` of the `Exe` class. Through these two members you can target an OS different from the current OS. For example, you can build for Windows from a Linux machine by doing the following:
def exe: Build.Exe(WidgetGuide.start~ast, "hello_world"); exe.targetTriple = "x86_64-pc-win32"; exe.linkerFilename = "x86_64-w64-mingw32-g++"; exe.generate();
def wasm: Build.Wasm(HelloWorld.start~ast, "hello_world"); wasm.addDependency(String("stdlib.wasm")); wasm.addFlags({ String("--export=malloc"), String("--export=realloc") }); if wasm.generate() { Srl.Console.print("Build complete.\n"); } else { Srl.Console.print("Build failed.\n"); };
Build.genExecutable(start~ast, "hello_world");
Build.genWasm(start~ast, "hello_world");
func extractFromFile ( filename: ptr[array[Char]], folderName: ptr[array[Char]], callback: ptr[func (ptr[array[Char]], ptr): Int], arguments: ptr ): Int;Extracts a zip compressed file to the specified directory.
func compressToFile ( filename: ptr[array[Char]], files: ptr[array[ptr[array[Char]]]], fileCount: Int, extractType: ptr[array[Char]] ): Int;Creates a zip compressed file containing the given files.
$ apm helpImporting packages using Apm inside a program is done using `importFile` function:
import "Apm.alusus"; Apm.importFile("<author>/<pkg name>" [, "<filename>"]); Apm.importFile("<author>/<pkg name>", { "<filename1>", "<filename2>", ... });The second form of the function allows the user to include multiple files from the package at once. Using the second form to include multiple files is faster than calling the first form multiple times, because each call to this function will cause reading information from GitHub.
$ cd <example_project_folder> $ apm link <author>/<package_name>@<release> <path_to_local_package_copy>It is possible to remove the link using `unlink` command, which has the following form:
$ cd <example_project_folder> $ apm unlink <author>/<package_name>@<release>
func importFile (filename: ptr[array[Char]]);Include the file with the given name. This function do the work of `import` directive and does not differ from it except that it is able to include files dynamically, by using the name of a file generated dynamically at run time.
func addLocalization ( locale: ptr[array[Char]], key: ptr[array[Char]], value: ptr[array[Char]] );Adds a translation to translations list for a specific language. If there is no previous entry for the required key then the translation will be added even if the current language is different from given translation language. The reason behind this is that having a text with another language is better than no text at all. But if there is an entry for the given key then the translation will be added only if it matches the current system language.
def createPlain: ptr[@shared @no_bind function ():ref[TiObject]];Create an object and return a regular reference to it after initializing it.
def createShared؛: ptr[@shared @no_bind function ():SrdRef[TiObject]];Create an object and return a `SrdRef` to it after initializing it.
def initialize: ptr[@shared @no_bind function (ref[TiObject])];Intialize an object that already allocated in memory.
def terminate: ptr[@shared @no_bind function (ref[TiObject])];Terminate an object without releasing its memory.
handler this.getMyTypeInfo (): ref[TypeInfo];Gives a reference to this type info.
handler this.isDerivedFrom (ref[TypeInfo]): Bool;Tell you whether this object is from the class with the given info, or from a class derived from that class.
handler this.getInterface (ref[TypeInfo]): ref[TiInterface];Receives a reference for interface info and retruns a reference to that interface if exists, else it return 0.
handler this.getMyInterfaceInfo (): ref[TypeInfo];Gives a reference to this inference info.
handler this.getTiObject (): ref[TiObject];Returns the object that owns this inference.
interface~no_deref = getInterface[obj, InterfaceType];
if isDerivedFrom[obj, ObjType] { ... }
func test (parentObj: ref[ParentType]) { defDynCastedRef[childObj, parentObj, ChildType]; if childObj~ptr != 0 childObj.someFunc(...); }
func create (v: BasicType): SrdRef[ObjType]Creates an object from this class with the given value and return a `SrdRef` to that object.
func getTypeInfo (): ref[TypeInfo];This function returns a reference to the type's info, which means to the info of objects derived from this type.
def i: TiInt(45); printInt(i.value); i = 7; def o: ref[TiObject](i); if isDerivedFrom[o, TiInt] { def x: ref[TiInt](castRef[o, TiInt]); printInt(x.value); }What follows show a list of the basic classes with an info.
handler this.setMember (memberName: ptr[array[Char]], value: ref[TiObject]); handler this.setMember (memberIndex: Int, value: ref[TiObject]);Set the value of an object's member, the first to specify the member based on its name, wheras the second is based on the order of the member amongs other members.
handler this.getMemberCount(): Word;Querying number of members for this object.
handler this.getMember (memberName: ptr[array[Char]]): ref[TiObject]; handler this.getMember (memberIndex: Int): ref[TiObject];Get the value of the member, with the given name in the first form, or the index in the second form.
handler this.getMemberNeededType (memberName: ptr[array[Char]]): ref[[TypeInfo]; handler this.getMemberNeededType (memberIndex: Int): ref[TypeInfo];Get the type info for the specified member with given name in the first form, or the index in the second form.
handler this.getMemberKey (index: Int): String;Get the name of the memnber with the given index.
handler this.findMemberIndex (name: ptr[array[Char]]): Int;Returns the index of the memnber with the given name, or -1 if not found.
handler this.setElement (index: Int, value: ref[TiObject]);Set a new object to the element with the given index.
handler this.getElementCount (): Word;Get the number of elements that this object contains.
handler this.getElement (index: Int): ref[TiObject];Get the element with the given index.
handler this.getElementNeededType (index: Int): ref[TypeInfo];Get the info of the required type for the element with the given index.
handler this.addElement (value: ref[TiObject]): Int;Adds new element to the end of the container, and returns the index of the new element.
handler this.insertElement (index: Int, value: ref[TiObject]);Adds new element to the container at the given index, after shifting all elements that are at that index or after it.
handler this.removeElement (index: Int);تزيل العنصر الذي عند التسلسل المعين ثم تزحف كل العناصر التي تلي ذلك التسلسل. Removes the element at the given index, and shifts all elements that are after that index.
handler this.getElementsNeededType (): ref[TypeInfo];Get the info of the required type for the container's elements without the need to set any specific element.
handler this.setElement ( elementName: ptr[array[Char]], value: ref[TiObject] ): Int;Set a new value for the element with the given name and returns the index of that element.
handler this.getElement (elementName: ptr[array[Char]]): ref[TiObject];Get the value of the element with the given name.
handler this.getElementNeededType ( elementName: ptr[array[Char]] ): ref[TypeInfo];Get the info for the required type of the element with the given name.
handler this.getElementKey (index: Int): String;Get the name of the element with the given index.
handler this.findElementIndex (name: ptr[array[Char]]): Int;Get the index of the element with the given name, or -1 if not found.
handler this.addElement ( name: ptr[array[Char]], value: ref[TiObject] ): Int;Adds ew element with the given name to the container and returns the index of that new element.
handler this.insertElement ( index: Int, name: ptr[array[Char]], value: ref[TiObject] );Adds new element at the given index after shifting all elements that are at that index or after it.
handler this.removeElement (index: Int); handler this.removeElement (name: ptr[array[Char]]);Removes the element with the given index (the first form), or the given name (the second form).
handler this.getElementsNeededType (): ref[TypeInfo];Get the info of the required type for the container's elements without the need to specify a specific element.
func getTypeInfo (): ref[TypeInfo];This function returns a reference to this type info, which means the info of objects derived from this type.
func getTypeInfo (): ref[TypeInfo];This function returns a reference to this type, which means the type of objects derived from this type.
func create (value: ptr[array[Char]]): SrdRef[TheType];Creates an object from this class with the given value, and returns `SrdRef` to this object.
The module `Spp` contains functions to deal directly with `spp` library. Which is the library that is responsible for supporting standard programming paradigm. The elements of this module allow users to deal directly with the compiler from their programs.
handler this.dumpLlvmIrForElement (element: ref[TiObject]);`dumpLlvmIrForElement` function prints the intermediate code for a specific element from the source code. The printed intermediate code is a `LLVM IR` code. The function accepts one argument which is a pointer to the AST for that element. It is possible to get this pointer using `~ast` command as shown in the next example:
Spp.buildMgr.dumpLlvmIrForElement(myFunc~ast);
handler this.buildObjectFileForElement ( element: ref[TiObject], filename: ptr[array[Char]], targetTriple: ptr[array[Char]] ): Bool;This function creates object file for the fiven element. Later it is possible to pass that file to the linker to create an executable file. The third argument is a value that determines the architecture used in the building. In case of passing 0 to this argument, the system's current architecture will be used. For example, to build an executable code with web assembly architecture we should pass "wasm32-unknown-unknown". For more information about this value, it is possible to refer to `LLVM` documentation.
Spp.buildMgr.buildObjectFileForElement(MyModule~ast, "output_filename", 0);
func raiseBuildNotice ( code: ptr[array[Char]], severity: Int, astNode: ref[TiObject] );This function allows the programmer to raise a build notification programitically during preprocessing. This function takes 3 arguments:
def errorCode: "SPPH1006"; Spp.buildMgr.raiseBuildNotice(errorCode, 1, funcArgNode); // The above line will show an build error: Invalid function argument name.
def TiObject: alias Core.Basic.TiObject; handler this.addCustomCommand ( identifier: ptr[array[Char]], grammarAst: ref[TiObject], handler: ptr[func (SrdRef[TiObject]): SrdRef[TiObject]] );This function is used to dynamically add a grammar rule for a new command to the language. This function accepts three arguments:
ast { keywords: <keywords of the command>; args: <description of the args following the keyword>; }The command could have one keyword, or multiple ones (separated by | in the definition). The command could accepts no arguments, or accepts one argument or multiple arguments (separated by + in the definition). Every argument from these arguments consits of the path that leads to the parsing rule for the argument followed by * sign followed by parentheses that contain the lower and upper limits (respectively) for this argument appearence inside the command. Refer to the source code for Alsus rules for more information about the available commands that could be used as arguments.
Spp.grammarMgr.addCustomCommand( "TestCommand", ast { keywords: "test_cmd"; args: "module".Expression(0, 2) + "module".Set*(1,1); }, func (args: SrdRef[TiObject]): SrdRef[TiObject] { ... } );The above example defines a new command that starts with the keyword `test_cmd` then followed by an argument with struct type that could appear once or twice consecutively or not at all, these expressions are followed by an expression with type set that must appear exactly once.
def TiObject: alias Core.Basic.TiObject; handler this.addCustomGrammar ( identifier: ptr[array[Char]], baseIdentifier: ptr[array[Char]], grammarAst: ref[TiObject] );This function is used to dynamically add a new grammar rule to the language. It is different from `addCustomCommand` function in that the new rule is not restricted to be a command (it could be an expresssion or anything) and the new rule is derived from another rule. This function accepts three arguments:
ast { <path_to_value_to_update>: { <new_value> }; ... }Rule's tree contains a set of inputs, each input is consist from the path to the value we want to change then the new value. The path is relative to the root of the rule.
Spp.grammarMgr.addCustomGrammar( "ClosureSigExp", "FuncSigExpression", ast { BitwiseExp.vars: { enable: 1 } } );
handler this.findElements ( comparison: ref[Core.Basic.TiObject], target: ref[Core.Basic.TiObject], flags: Word ): Array[ref[Core.Basic.TiObject]]; handler this.findElements( comparison: ref[TiObject], target: ref[TiObject], flags: Word, modifierKwd: CharsPtr, kwdTranslations: ref[Map[String, String]] ): Array[ref[TiObject]];Searches through the soruce code for elements that match the given search criteria. The first two arguments are references on two ASTs. The first one to an expression that represents the search criteria while the second is the ast that we want to search in it.
elements = Spp.astMgr.findElements( ast { elementType == "function" }, MyClass~ast, Spp.SeekerFlags.SKIP_OWNERS | Spp.SeekerFlags.SKIP_USES | Spp.SeekerFlags.SKIP_CHILDREN );It is possible to use search criteria by type or by the modifiers applied on the element. It is also possible to use a composite condition that contains `and` and `or` operators. Some examples of search criteria:
elementType == "function" // search for functions elementType == "type" // search for types elementType == "module" // search for modules elementType == "var" // serach for variables modifier == "public" // search for elements with @public modifier elementType == "func" && modifier == "public" // search for functions with @public modifier
handler this.getDefinitionName ( element: ref[Core.Basic.TiObject] ): String;Returns the name of the given element, which is the name of the definition owning it.
handler this.getModifiers ( element: ref[Core.Basic.TiObject] ): ref[Core.Basic.Containing];Get the list of modifiers applied on the given element.
handler this.findModifier( modifiers: ref[Core.Basic.Containing], kwd: ptr[array[Char]] ): ref[Core.Basic.TiObject];Find a modifier inside a list of modifiers. Seach is done using the keyword of the modifier. For example, to seach for `@expname` you need to pass the keyword `expname`.
handler this.findModifierForElement( element: ref[Core.Basic.TiObject], kwd: ptr[array[Char]] ): ref[Core.Basic.TiObject]; handler this.findModifierForElement( element: ref[Core.Basic.TiObject], kwd: ptr[array[Char]], kwdTranslations: ref[Map[String, String]] ): ref[Core.Basic.TiObject];Find the modifier by the given keyword on the given element. The second form of this method translates modifiers against the given translations map before comparing them against the requested keyword.
handler this.getModifierKeyword( modifier: ref[Core.Basic.TiObject] ): Srl.String;Returns the keyword for the given modifier.
handler this.getModifierParams( modifier: ref[Core.Basic.TiObject], result: ref[Array[Core.Basic.TiObject]] ) => Bool;Returns a list of all arguments passed to the modifier. This functions returns a boolean with value 1 on success, and 0 on failure.
handler this.getModifierStringParams( modifier: ref[Core.Basic.TiObject], result: ref[Array[String]] ) => Bool;Returns a list of all string arguments passed to the modifier. For example, if we have `deps["lib1", "lib2"]` modifier and we use this function, then we get from it a list for two elements, the first is "lib1", and the second is "lib2". This functions returns a boolean with value 1 on success, and 0 on failure.
handler this.getClassVars (parent: ref[TiObject]): Array[ref[TiObject]]; handler this.getClassVars ( parent: ref[TiObject], kwd: ptr[array[Char]], kwdTranslations: ref[Map[String, String]] ): Array[ref[TiObject]];Gets the list of variables of a given type. The second version brings only the variables carrying a specific modifier.
handler this.getClassVarNames (parent: ref[TiObject]): Array[String]; handler this.getClassVarNames ( parent: ref[TiObject], kwd: ptr[array[Char]], kwdTranslations: ref[Map[String, String]] ): Array[String];Gets the list of names of variables of a given type. The scond version brings only the names of variables carrying a given modifier.
handler this.getClassFuncs (parent: ref[TiObject]): Array[ref[TiObject]]; handler this.getClassFuncs ( parent: ref[TiObject], kwd: ptr[array[Char]], kwdTranslations: ref[Map[String, String]] ): Array[ref[TiObject]];Get the list of functions of a given type. The second version brings only the functions carrying a given modifier.
handler this.getClassFuncNames (parent: ref[TiObject]): Array[String]; handler this.getClassFuncNames ( parent: ref[TiObject], kwd: ptr[array[Char]], kwdTranslations: ref[Map[String, String]] ): Array[String];Gets the list of names of functions of a given type. The second version gets only the names of functions having a specific modifier.
handler this.getFuncArgTypes (element: ref[TiObject]): ref[TiObject]Gets the list of argument definitions for the given function.
handler this.getFuncArgType (element: ref[TiObject], index: Int): ref[TiObject]Gets the definition of the given function's argument at the given index.
handler this.getSourceFullPathForElement( element: ref[Core.Basic.TiObject] ) => String;Returns the full file name with the path for the source code file that contains the given element.
handler this.getSourceDirectoryForElement( element: ref[Core.Basic.TiObject] ) => String;Returns the full folder path which contains the source code file that contains the given element.
handler this.insertAst( element: ref[Core.Basic.TiObject], interpolations: ref[Map[String, ref[Core.Basic.TiObject]]] ) => Bool; handler this.insertAst( element: ref[Core.Basic.TiObject], interpolations: ref[Map[String, SrdRef[Core.Basic.TiObject]]] ) => Bool;Inserts the given ast in the current position after applying the given interpolations on the given ast in a way similar to how macros work. The first argument to this function is the tree to be inserted, while the second argument holds the interpolations list.
def i: Int; for i = 0, i < 10, ++i { def counter: TiStr = String.format("%i", i); Spp.astMgr.insertAst( ast { def n__counter__: Int }, Map[String, ref[TiObject]]().set(String("counter"), counter) ); }
handler this.buildAst( element: ref[Core.Basic.TiObject], interpolations: ref[Map[String, ref[Core.Basic.TiObject]]], result: ref[SrdRef[Core.Basic.TiObject]] ) => Bool; handler this.buildAst( element: ref[Core.Basic.TiObject], interpolations: ref[Map[String, SrdRef[Core.Basic.TiObject]]], result: ref[SrdRef[Core.Basic.TiObject]] ) => Bool; handler this.buildAst( element: ref[Core.Basic.TiObject], interpolations: ref[Map[String, ref[Core.Basic.TiObject]]] ): SrdRef[Core.Basic.TiObject];This function is similar to `insertAst` function, except that it creates ast and returns it to the caller instead of inserting it directly in the current position of preprocessing. The user could later insert the result in the current position using `insertAst`. The next example, is a modification of the previous one to use this function and also clearing the variables that it defines. The example creates a definition then use that definition as an interpolation in later call to `insertAst` function:
def i: Int; for i = 0, i < 10, ++i { def counter: TiStr = String.format("%i", i); def result: SrdRef[TiObject]; Spp.astMgr.buildAst( ast { def n__counter__: Int }, Map[String, ref[TiObject]]().set(String("counter"), counter), result ); Spp.astMgr.insertAst( ast { definition; n__counter = 0; }, Map[String, ref[TiObject]]() .set(String("counter"), counter) .set(String("definition"), result) ); }
handler this.insertCopyHandlers(obj: ref[TiObject]); @member macro insertCopyHandlers [this];Generates the copy operation and the copy constructor for the current type. It takes care of copying all member variables of that type. This macro must be used inside the body of a user type to generate the operations for that typel.
handler this.insertMixin(obj: ref[TiObject]); @member macro insertMixin [this, target];Inserts the given mixen into a class. The function inserts the mixin into the current location, so it must be used within a `preprocess` statement. The macro is just a helper that calls the function from a preprocess statement and gives it the AST ptr of the mixen. Visit mixins for more info.
handler this.getCurrentPreprocessOwner(): ref[Core.Basic.TiObject];Returns a reference to the AST element that owns the currenly running preprocessing expression.
handler this.getCurrentPreprocessInsertionPosition(): Int;Returns the position (inside the owner of the current preprocessing experssion) in which AST elements will be inserted when calling `insertAst` function.
handler this.getVariableDomain(element: ref[Core.Basic.TiObject]) => Int;Returns a value that show the domain in which the given variable is defined. The result is one of the following values:
def DefinitionDomain: { def FUNCTION: 0; // Var is a function local variable. def OBJECT: 1; // Var is a class member. def GLOBAL: 2; // Var is a global or shared variable. }
handler this.traceType(element: ref[Core.Basic.TiObject]) => ref[Spp.Ast.Type];Traces the type that the given SPP element points to, and returns that type.
handler this.matchTemplateInstance( template: ref[Spp.Ast.Template], templateInput: ref[Core.Basic.TiObject], result: ref[SrdRef[Core.Basic.TiObject]] ) => Bool;Returns the template instance that matches the given input. If no existing instance matches the input, then creates a new instance.
handler this.isCastableTo( srcTypeRef: ref[Core.Basic.TiObject], targetTypeRef: ref[Core.Basic.TiObject], implicit: Bool ) => Bool;Checks wether the given type (srcTypeRef) can be casted as a certian other type (targetTypeRef). The type arguments can be the type itself, or a reference to it. The third argument specifies wether to check for implicit casting or explicit casting.
handler this.computeResultType( element: ref[Core.Basic.TiObject], result: ref[ref[Core.Basic.TiObject]], resultIsValue: ref[Bool] ) => Bool;This function computes the result from the given expression and returns it. The result could be a class or any other defintion like module or function. The last argument tells us if the result is a value (a variable from the given class for example) or the class itself (which means it tells you whether the expression is a definition for a class, or a variable from that class).
handler this.cloneAst(element: ref[Core.Basic.TiObject]): Srl.SrdRef[Core.Basic.TiObject] { return this.cloneAst(element, nullRef[Core.Basic.TiObject]); } handler this.cloneAst( element: ref[Core.Basic.TiObject], sourceLocationNode: ref[Core.Basic.TiObject] ): Srl.SrdRef[Core.Basic.TiObject];This function clonse the given AST. The second form of this function allows the addition of a position in the source code to the source code positions stack related to the generated tree. The next argument in the next form is not the position in source code that we want to add to the stack, instead it is a the AST element we want to take the position from.
handler this.dumpData(obj: ref[Core.Basic.TiObject]);Prints the given ast to the console in a string format.
handler this.getReferenceTypeFor( astType: ref[Core.Basic.TiObject] ): ref[Spp.Ast.ReferenceType];Returns the reference type of the given type.
handler this.tryGetDeepReferenceContentType( astType: ref[Spp.Ast.Type] ): ref[Spp.Ast.Type];Returns the content type of the given type. If the given type is a reference then the function returns the content that this reference points to, otherwise it returns the type itself. If the given type is a reference to a reference then the function search recursively until it reaches a non-reference type and returns it.
import "Srl/Console"; import "Srl/refs"; import "Srl/System"; import "Core/Data"; import "Spp"; use Srl; use Core.Basic; use Core.Data; use Core.Data.Ast; def TioSrdRef: alias SrdRef[Core.Basic.TiObject]; func generateWhere (obj: ref[TiObject]): TioSrdRef { def result: TioSrdRef; if obj~ptr == 0 { System.fail(1, "generateWhere: obj is null.\n"); } if isDerivedFrom[obj, ComparisonOperator] { def binding: ref[Binding](getInterface[obj, Binding]); def mapContaining: ref[MapContaining](getInterface[obj, MapContaining]); if !Spp.astMgr.buildAst( ast { String("{{name}}") + String(" {{op}} '") + val + String("'") }, Map[String, ref[TiObject]]() .set(String("name"), mapContaining.getElement("first")) .set(String("op"), binding.getMember("type")) .set(String("val"), mapContaining.getElement("second")), result ) { System.fail(1, "generateWhere/ComparisonOperator: error\n"); } } else if isDerivedFrom[obj, LogOperator] { def binding: ref[Binding](getInterface[obj, Binding]); def mapContaining: ref[MapContaining](getInterface[obj, MapContaining]); if !Spp.astMgr.buildAst( ast { (cond1) + String(" {{op}} ") + (cond2) }, Map[String, ref[TiObject]]() .set(String("cond1"), generateWhere(mapContaining.getElement("first")).obj) .set(String("op"), binding.getMember("type")) .set(String("cond2"), generateWhere(mapContaining.getElement("second")).obj), result ) { System.fail(1, "generateWhere/LogOperator: error\n"); } } else if isDerivedFrom[obj, Bracket] { def mapContaining: ref[MapContaining](getInterface[obj, MapContaining]); if !Spp.astMgr.buildAst( ast { String("(") + (cond) + String(")") }, Map[String, ref[TiObject]]() .set(String("cond"), generateWhere(mapContaining.getElement("operand")).obj), result ) { System.fail(1, "generateWhere/Bracket: error\n"); } } else { if !Spp.astMgr.buildAst( obj, Map[String, ref[TiObject]](), result ) { System.fail(1, "Failed to build condition.\n"); } } return result; } macro where [condition] { preprocess { if !Spp.astMgr.insertAst( generateWhere(ast condition).obj ) { System.fail(1, "Failed to insert condition.\n"); } } } func test { def firstName: String("Mohammed"); def position: String("Engineer"); def query: String = where[name == firstName && (pos == position || pos == "Lawyer")]; Console.print("%s\n", query.buf); // Execution output: name == 'Mohammed' && (pos == 'Engineer' || pos == 'Lawyer') } test();