Operators are tokens used to perform simple operations on expression values, such as adding two values together. ColdC provides a variety of operators to perform arithmetic and logical operations on data. Most of these operators fall into two categories: unary operators and binary operators.
Unary operators act on a single expression value. In ColdC, all unary operators are single characters which precede expressions. For example, !str is the logical negation of the variable str.
Binary operators act on two expression values. For example, a + b is the sum of the variables a and b.
Some operators are neither unary nor binary. More information on these operators can be found in each sub section:
- Operator Precedence
- Index Operators
- Arithmetic Operators
- Increment/Decrement Operators
- Non-Numeric Use
- Assignment Operators
- Simple Assignments
- Scatter Assignments
- Dual Role Assignments
- Default Assignments
- Logical Operators
- Conditional Operators
- List Splice Operator
Operator Precedence
When the interpreter executes an expression it evaluates each operator it in a certain order. It is easy to write an expression which has an unclear order of evaluation. For instance, the expression 7 - 2 + 3 could be parsed as (7 - 2) + 3 (8) or as 7 - (2 + 3) (2). To resolve these ambiguities, each operator has two properties when interpreted: precedence and associativity.
Precedence determines whether an operator is more important than another operator. For instance, A + B * C is equivalent to A + (B * C) because multiplication has higher precedence (is more important) than addition.
Associativity determines whether operators at the same precedence level associate left to right or right to left; A - B - C is equivalent to (A - B) - Cbecause subtraction associates left to right.
Here is a list of operators grouped by precedence, in order from highest to lowest (L indicates left-to-right associativity, R indicates right-to-left):
L []
L .
R -- ++
R ! - (unary)
L * / %
L + - (binary)
L == != > >= < <=
L in
L &&
L ||
R ?:
R = += -= /= *= ?=
Index Operators
Indexing involves lookup in a string, list, buffer, or dictionary. Depending upon the specific operator used, indexing can return either the position of a desired element or it can return the element at the specified position. There are two indexing operators, each performing one of these roles:
[]
in
The binary operator index lookup (in) is used to find the position of an element. It may only be used with strings, lists, and buffers. When used this operator will return an integer representing the position within the right side data where the left side element exists. If the specified element does not exist, a 0 (zero) is returned. Indexing is case-insensitive when using strings. Examples:
"C" in "abcdefg"
=> 3
"C" in ["a", "b", "c", "d", "e", "f"]
=> 3
The expression find and the functions stridx(), listidx(), and bufidx() may also be used in finding the positional index of an element.
The index retrieval operator ([]) is used to retrieve an element found within the data. It may be used with strings, lists, buffers, and dictionaries. It is a binary operator, although its syntax is not as standard as others:
data_elements[index]
The right data expression (index is used as the key to lookup within the left data expression (data_elements). In all but the dictionary, the right data expression must result in an integer which represents the position in the right data expression to retrieve from. With a dictionary, the right data expression may be any data type (see Data Types: Dictionary).
Arithmetic Operators
ColdC provides several operators for performing arithmetic operations. These operators apply primarily to integers and floats, but some of them may be used with non-integer data for easier formatting and manipulation. If any arithmetic operator is used with inappropriate data the error ~type is thrown.
If both sides of a binary arithmetic expression are integers, the result will always be an integer, even if the result would have floating precision. If one side of the expression is a float, the result will always be a float. For instance, 3 / 2 (both integers) would result in 1, whereas 3 / 2.0 would result in 1.5.
The arithmetic operators are:
| + | ignored | (unary) |
| - | negate | (unary) |
| + | addition | (binary) |
| - | subtraction | (binary) |
| * | multiply | (binary) |
| / | divide | (binary) |
| % | modulo | (binary) |
| ++ | increment | (unary) |
| -- | decrement | (unary) |
The unary - operator negates the numeric value (reverses its positive/negative value). The unary + operator has no effect on its argument, and is provided simply for completeness. The binary operators + and - add and subtract their arguments.
The binary multiplication operator * multiplies the left argument by the right argument. The binary division operator / divides the left argument by the right argument and returns the whole result. The binary modulus operator % divides the left argument by the right argument and returns the remainder result.
Increment/Decrement Operators
The unary increment (++) and decrement (--) operators perform a dual purpose. They add or subtract 1 (one) from a numeric variable, assigning the result back to the variable. They must be used directly on a variable. Each of the following pairs is equivalent:
i = i + 1
i++
i = i - 1
i--
The result returned from an increment or decrement expression will depend on which side of the variable it is placed. If the operator is on the left side of the variable, the value of the variable is modified before it is returned. If the operator is on the right side of the variable, the value of the variable is returned first and then the variable is incremented or decremented.
In the examples below assume i is 10. Assuming this, in the first example x is assigned the value 10 where in the second example it is assigned the value 11:
x = i++
x = ++i
Non-Numeric Use
The binary + operator can also be applied to strings, lists, and buffers. Using it in this way will cause it to concatenate the two values. For instance, the expression ("foo" + "bar") would evaluate to "foobar", and the expression (["foo", "bar"] + ["baz"]) would result in the list ["foo", "bar", "baz"].
As long as both sides of the operator are the same data type, no error will be raised. If one side of the operator is a string (either side), and the other side is not a string, the non-string element will be converted to its literal representation and joined with the string. For example, the expression ("list: " + [1, 2, 3]) would evaluate to "list: [1, 2, 3]".
The binary * may be applied with a string on the left side and an integer on the right side. In this situation the left side string is duplicated by the number of times specified by the right side integer. Example: ("-" * 5) would result in "-----".
Dual Role Assignment operators also behave in the same way, as is applicable. For instance, the dual role arithmetic operator += would behave the same as the standard arithmetic operator + when evaluating the addition expression.
Assignment Operators
Data is stored in a variable through an assignment. Assignments can either be simple, scattered, dual role, or default. Simple assignments store the result of an expression to a single variable. A scatter assignment stores each element in the result of a list expression across multiple variables. Dual role assignments join another operator and the simple assignment operator into one for the purpose of simplicity and optimizing, such as += which is both the addition operator and the assignment operator. Default assignments only assign the value if the current value of the variable is false.
Simple
Both simple and scatter assignments use the the same equals sign = operator. A simple assignment stores the result of the expression on the right into the variable on the left, such as:
variable = expression
The function set_var() may also be used for the same purpose, but only on an object variable (see Variable Expressions).
Scatter
In a scatter assignment, multiple variables are specified within square brackets on the left, and each element from the list expression on the right is assigned to the respective variable on the left, with any remaining elements in the list being discarded. For example:
[var1, var2, var3] = ["this", ["for"], 1, 'that]
In this example, var1 is assigned the string "this", var2 is assigned the list ["for"], var3 is assigned the integer 1, and the remaining symbol 'that is discarded.
The remaining elements may all be grouped in a new list which is stored in the last variable by using the list splice operator (@) on the last variable in the scatter list, such as:
[var1, var2, @var3] = ["this", ["for"], 1, 'that]
In this example, var3 would have the value [1, 'that]. The list splice operator may only be used this way on the last variable in the scatter list.
Scatter assignments may also be nested, assuming the list expression is a nested list in the same order as the scatter list. For example:
[var1, [var2, @var3], var4] = ["this", ["for", 1, 2, 3], 'that]
In this example, var1 is assigned "this", var2 is assigned "for", var3 is assigned the new list [1, 2, 3], and var4 is assigned the remaining symbol 'that.
Dual Role
Dual role assignment operators join an arithmetic operator and the simple assignment operator into one for the purpose of simplicity and optimization. The dual role assignment operators are:
+=
-=
*=
/=
These operators must have a variable as the left argument. When used, the arithmetic expression is evaluated as normal, and the result is assigned back into the variable on the left. The following examples are equivalent:
x = x + 5
x += 5
When evaluating the arithmetic expression the operator behaves the same as the equivalent arithmetic operator, including any non-numeric behavior.
Default
The default assignment is used to assign a value to a variable if the current value of the variable is logically false. The default assignment operator is:
?=
And the syntax is:
variable ?= value_expression
When executed, the driver tests the logical value of variable. If it is false, it assigns the result of value_expression, otherwise it is untouched. Example:
len ?= 79
This expression will assign 79 to len if it is false (for instance, if it is 0 (zero)). This operator is equivalent to a combination of the simple assignment and the if-else conditional expression:
len = len ? len : 79
Logical Operators
ColdC provides a number of relational and logical operators. These operators are primarily useful for expressing conditions, since they return either true or false, represented by the integers 1 and 0.
The binary operators == and != test the left and right arguments for equality and inequality, respectively. The arguments are equal if each side has the same data type and contains the same value. Equality of strings is not case-sensitive, but equality of symbols is, so ("foo" == "fOo") is true, but ('car == 'CAr) is false. Lists are equal if all of the elements in each list are equal and are in the same order.
The <, <=, >=, and > operators test whether their left-hand arguments are less than, less than or equal to, greater than or equal to, or greater than their right-hand arguments, respectively. Arguments to these operators must both be of the same type, and must be either numeric or strings. String comparison is not case-sensitive, so ("fooa" < "fooB") is true, even though the ASCII value of 'a' is greater than that of 'B' (the function strcmp() may be used for a true lexical case-sensitive comparison of strings).
The unary ! operator tests whether its argument is false, thus acting as a logical NOT operation.
Conditional Operators
The || and && operators act as logical OR and AND operators, respectively. The expression (this || that) is equivalent to this OR that, where the return value of the expression is this if this is logically true. If this is not logically true, then the return value of the expression is that.
The expression (this && that) is equivalent to this AND that, where the return value of the expression is logically true when both this and that are also logically true. If either this or that are not logically true then the return value is false.
ColdC's conditional operators are short-circuit operators, meaning that the right-hand argument is never evaluated if the left-hand argument is sufficient to determine the value of the expression. This is important if the right-hand argument has side-effects or could cause an error. For instance, because of this it is possible to have the following expression:
(| dict[key] |) || throw(~nope, "Nope, doesn't exist")
With this expression if key is in the dictionary dict then the return value is the related value to key. If it is not, the error ~keynf is thrown by the index operator ([]). However, the expression is a critical expression (see Error Handling Expressions). Because of this the left value becomes ~keynf, which is logically false, and the interpreter moves onto the throw function.
The ?: operator is a trinary operator, with the following syntax:
condition ? true_expr : false_expr
The result of this expression is the result of true_expr if condition is true, or the result of false_expr if condition is false. This is similar to the conditional if-else statement.
List Splice Operator
The splice operator (@) is used to expand lists and to group remaining arguments in scatter assignments and method calls into lists.
Splice can be used to expand a list in three situations: within a method call, within a function call or within another list. When expanding, elements within the list to be spliced are placed starting at the appropriate position within the spliced list. For example:
[1, @['a, 'b, 'c], 2, 3]
=> [1, 'a, 'b, 'c, 2, 3]
Note: the function listgraft() behaves in the same manner.
When using splice to expand a list as arguments to a function it looks similar:
others = [1, "this"];
$string.do_something(this, that, @others);
When the method do_something is called it would be called with four arguments, and the last two arguments would have values of 1 and "this".
The splicing operator is always evaluated last and is not listed in the operator precedence list. Because of its restricted nature the splicing operator never causes ambiguity.