Expressions are ColdC tokens and operators which perform their defined behavior when evaluated and take on a value after completion (also known as the return value). If they evaluated without error, the return value is a standard ColdC data type. If an error occurred, the return value is an error data type and execution of the method is halted.
There are many different types of expressions:
- Data
- Operators
- Variable Expressions
- Function Call Expressions
- Method Call Expressions
- Error Handling Expressions
- Looping Expressions
Data
All ColdC data has a type and a logical truth value it will return when evaluated as an expression. The following is a list of ColdC data types along with their names and literal representation in ColdC:
| Type | Name | Representation |
|---|---|---|
| Integer | 'integer | 42 |
| Float | 'float | 1.0 |
| String | 'string | "This String" |
| Symbol | 'symbol | 'identifier |
| List | 'list | [1, 2, 3, 4] |
| Object Number | 'objnum | #1234 |
| Object Name | 'objname | $identifier |
| Dictionary | 'dictionary | #[["key", $value]] |
| Error Code | 'error | ~identifier |
| Frob | 'frob | <object, value> |
| Buffer | 'buffer | `[98, 117, 102, 102, 101, 114] |
The type of data being manipulated can be determined using the type() function.
Operators
Operators are tokens used to perform simple operations on expression values, such as adding two values together. See Operators for more information on ColdC operators.
Variable Expression
ColdC provides two kinds of variables, local variables and object variables. Local variables are used within methods to temporarily store data. In order for a local variable to be used it must first be declared at the top of the method in a var declaration (see Methods). Object variables are defined on an object, and can be used to store data for an indefinite period of time (at least until the object is destroyed). More information on variables can be found in Objects.
When a variable is evaluated as an expression, its value is that of its contents (the data stored in the variable name). Initially variables will contain the integer data value of 0 (zero). To set the contents of a variable, use the assignment expression (=.
The value of a local variable is specific to the currently executing method frame. When the method is invoked at another time, all of the local variables begin again with the value of 0 (zero).
If the variable is a local variable, it must be declared as a local variable at the top of the method (see Methods). If it is not declared at the top of the method, the interpreter will assume it is an object variable. In the case that both a local variable and an object variable have the same name, the local variable will take precedence.
It is possible to indirectly access and set data on an object variable using the functions get_var() and set_var(). Normally, on the object on which it is defined, an object variable is accessed and set just as if it were a local variable. These functions are useful for indirectly setting or accessing the variable, or for when there is a possible conflict with a local variable. Object methods should be used to access variables on objects on which the variables are inherited but not defined.
Function Call Expression
Functions are driver-defined procedures which perform certain actions which ColdC is incapable of (such as opening a network connection), or which the driver can handle more efficiently. A function is called using the function call expression, which has the following syntax:
function(arg1, arg2, ...)
In this example function is an identifier naming the function, and arg1 and arg2 are expressions. There are no limits to the number of arguments a function can accept. However, even if there are no arguments the parentheses must be present. Arguments are evaluated from left to right.
As an example, the pad() function pads a string argument with spaces to a certain length. When evaluated with the string "foo" and a length of six characters:
pad("foo", 6)
=> "foo "
There are driver functions available for a variety of tasks. A comprehensive explanation of the available functions is available in Function Reference.
Method Call Expression
Methods defined on an object can be executed with the method call expression, which has the following syntax:
receiver.method(arg1, arg2, ...)
In this example receiver is the object where method may be called, and arg1 and arg2 are expressions. There is no limit to the number of arguments sent, although even if there are no arguments the parentheses must be present. Arguments are evaluated from left to right.
The receiver may be omitted, in which case it is assumed to be the current object. If receiver does not exist in the database, the error ~objnf is thrown.
Any ColdC data (with the exception of frobs) may be used in place of the object as the receiver. If a method is called in this manner, the interpreter will lookup an object with an object name that is the same as the type of data being used (such as the object $string if the data is a string). If an object is found in the database with this name, the method is called on that object and the data is sent as the first argument (subsequent arguments will still be sent). If not, the error ~objnf is thrown.
If a frob is used as the receiver, the class object for the frob becomes the receiver and the representation of the frob is sent as the first argument. Because of this difference it is possible to have a special method that only a frob may call. For more information on frobbed methods see Methods.
method must be either the name of the method, or an expression which results in a symbol for the name of the method. If method cannot be found on receiver or on any of receiver's ancestors, then the error ~methodnf is thrown.
The result from a method call expression is the value returned by the method. If the method does not return a value, the result is receiver. If receiver is a frob, then the method is called on the frob's class object, with the frob's representation inserted as the first argument (other arguments are placed after the representation).
The following are examples of method call expressions:
.message("I don't see that here.");
=> $admin_atyreus
$sys.user_types()
=> [$player, $creator, $admin, $ghost]
[1, 2, 3].reverse()
=> [3, 2, 1]
$sys.is_system($admin_atyreus)
=> 3
(<$thing_frob, #[['myname, "coins"], ['amount, 300]]>).name()
=> "300 coins"
In order to prevent incidents of infinite recursion, there is a maximum calling depth for methods. If calling a method would exceed the maximum calling depth, the error ~maxdepth is thrown.
Overridden Methods
If a method overrides another method defined on an ancestor, the overriding method can call the overridden method using the function pass(). Arguments to pass() are sent to the overridden method as if the method were called normally. The return value of pass() is the normal return value of the overridden method.
For instance, passing to an overridden method with
pass("this arg", 2);
would be rougly equivalent to calling the same method, if it was not overridden, with
obj.method("this arg", 2);
When executed in this manner, the overridden method sees the same object, sender, and caller as the current method.
In the situation where a descendant overrides a method defined on two of its ancestors, you can determine which method is passed to using the function find_next_method(). The following method can also be of use:
arg obj, method;
var trace, current;
current = (> obj.find_method(method) <);
trace = [];
while (current) {
trace += [current];
current = (| obj.find_next_method(method, current) |);
}
return trace;
Error Handling Expression
Two mechanisms exist for handling errors in expressions. These are the critical expression and the propagation expression. The critical expression is used to ignore any errors thrown from inside the expression. It has the following syntax:
(| expression |)
If an error occurs in expression the interpreter will stop evaluating expression and continue to execute the current method as if it had succeeded evaluating expression. The value of expression will be the error code for the error which occurred.
The propagation expression causes any error thrown by a method call within the expression to be thrown as if the current method threw it. It has the following syntax:
(> expression <)
If an error occurs in expression it will propagate to the calling method as if the error had originated in the calling method and not in the current method. Otherwise, the calling method will receive the error as ~methoderr.
Critical expressions override the behavior of catch statements so that errors which occur within critical expressions do not trigger catch error handlers. Propagation expressions do not override critical expressions or catch statements. They do not prevent errors from being caught; they only determine how errors propagate if they are not caught.
For more information on handling errors, see Errors.
Looping Expressions
ColdC offers several expressions that enable looping through lists, dictionaries or integer ranges. Their main advantages over looping statements are brevity and preallocating of the lists or dictionaries they return (for greater speed).
map
The map expression loops a variable through a list, dictionary, or integer range, evaluating an expression for each iteration. Results from each iteration are collected into a list and returned when the loop is completed. The syntax for map can be either of the following:
map var in (what_expr) to (iteration_expr)
map var in [lower_expr .. upper_expr] to (iteration_expr)
Examples:
map x in ([1, 2, 3]) to (tostr(x))
=> ["1", "2", "3"]
map x in [5 .. 15] to (x)
=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
hash
The hash expression loops a variable through a list, dictionary, or integer range, evaluating an expression for each iteration. Results from each iteration must be contained within a two element list. The list is inserted as the key/value pair into a dictionary. After the loop completes, the final dictionary is returned. The syntax for hash can be either of the following:
hash var in (what_expr) to (interation_expr)
hash var in [lower_expr .. upper_expr] to (iteration_expr)
Examples:
hash x in ([1, 2, 3]) to ([toobjnum(x), x])
=> #[[$root, 1], [$sys, 2], [$files, 3]]
hash x in [1 .. 3] to (.random_pair())
=> #[["Dancer", 1], ["Miro", 7], ["$root", 1]]
In the above example the method .random_pair() returns a random two element key/value pair.
find
The find expression is used to find the position of something in a list or dictionary. It does this by looping through the given list or dictionary and testing the iteration expression. When the iteration expression evaluates true it stops and returns the given position. The syntax of find is:
find var in (what_expr) where (iteration_expr)
find var in [lower_expr .. upper_expr] where (iteration_expr)
If the iteration expression never evaluates true, find returns a zero. The following examples assume the variable list is set as:
["First line","second line","3rd line"]
list[find x in (list) where (x.match_regexp("co"))]
=> "second line"
find x in [1 .. listlen(list)] where (list[x].match_regexp("co"))
=> 2
filter
The filter expression is used to selectively pull elements from a list or dictionary. It loops through the given list or dictionary and adds each element to a new list if the iteration expression tests true. The final list is returned after filter finishes looping. The syntax of filter is:
filter var in (what_expr) where (iteration_expr)
filter var in [lower_expr .. upper_expr] where (iteration_expr)
The following examples assume the variable list is set as:
["First line", "second line", "3rd line"]
filter x in (list) where (x.match_regexp("co"))
=> ["second line"]
filter x in [1 .. list.length()] where (list[x].match_regexp("co"));
=> [2]