Expression Evaluation in Scripts
A script in the C4 Engine can contain methods of the type Evaluate Expression, sometimes called an expression method for short. An expression method holds a text string representing a mathematical expression that gets evaluated when script execution reaches the method.
For an expression method, a text box is displayed on the left side of the Method Info window where a textual expression may be entered. (The Method Info window is opened by selecting a method and then typing Ctrl-I.) A field for designating an output variable also appears in the lower-left corner of the Method Info window.
If there is a syntax error in an expression, then the method is colored yellow in the script viewport.
Operators
Expressions can use any of the operators from the following list. They are listed in order of evaluation precedence, which matches the precedence rules for C++. Parentheses can be used to group subexpressions.
Operator |
Description |
Variable types accepted |
Notes |
|
Member Access |
color, vector, point |
Returns a scalar component of type float. For colors, one of For vectors and points, one of |
|
Bitwise NOT |
boolean, integer |
This is a unary operator. |
|
Multiply |
boolean, integer, float, color, vector, point |
Colors can only be multiplied by other colors or by floating-point, integer, or boolean scalar values. Vectors and points can only be multiplied by other vectors and points or by floating-point, integer, or boolean scalar values. Multiplication is componentwise for colors, vectors, and points. |
|
Divide |
boolean, integer, float, color, vector, point |
A color, vector, or point type can only appear as the left operand, and they can be divided by floating-point, integer, or boolean scalar values. Integer division by zero is defined to be zero. |
|
Modulo |
boolean, integer |
If the right operand is zero, then the result is defined to be zero. |
|
Plus |
boolean, integer, float, string, color, vector, point |
Colors can only be added to other colors. Vectors and points can only be added to other vectors and points. A unary plus is accepted and performs no operation. |
|
Minus or Negate |
boolean, integer, float, color, vector, point |
Colors can only be subtracted from other colors. Vectors and points can only be subtracted from other vectors and points. |
|
Shift Left |
boolean, integer |
|
|
Shift Right |
boolean, integer |
The shift is always signed. |
|
Less Than |
boolean, integer, float |
Always returns a boolean result. |
|
Greater Than |
boolean, integer, float |
Always returns a boolean result. |
|
Less Than or Equal |
boolean, integer, float |
Always returns a boolean result. |
|
Greater Than or Equal |
boolean, integer, float |
Always returns a boolean result. |
|
Equal |
boolean, integer, float, string |
Always returns a boolean result. |
|
Not Equal |
boolean, integer, float, string |
Always returns a boolean result. |
|
Bitwise AND |
boolean, integer |
|
|
Bitwise XOR |
boolean, integer |
|
|
Bitwise OR |
boolean, integer |
|
Note that the logical operators &&
and ||
are not present in the table. These were intentionally left out of the scripting language because they are not necessary. The same results can be attained by using the &
and |
operators instead. If the evaluation semantics of &&
and ||
are needed, then you can use conditional execution to evaluate the expression in pieces.
Operands
Each operand in an expression can be any of the following:
- The boolean value
true
orfalse
. - An integer literal value using any features from the OpenDDL syntax. This includes underscores separating digits, support for binary, octal, and hexadecimal literals (using the prefixes
0b
,0o
, and0x
, respectively), and character literals enclosed in single quotes. Integers are always stored as signed 32-bit quantities. - A floating-point literal value matching the OpenDDL syntax, with the exception that it must be decimal and contain one of the characters '
.
', 'e
', or 'E
' (decimal point or exponent indicator). Floats are always stored as 32-bit quantities. - A literal string matching the OpenDDL syntax. Escape characters and Unicode are supported.
- The name of any variable defined for the script, possibly followed by the dot operator to access an individual component of a color, vector, or point.
In any binary operation appearing in an expression, a mismatch between the types of the two operands causes one of the operands to be promoted to the type of the other. The possible promotions are those in the following list:
- Boolean can be promoted to integer, float, or string.
- Integer can be promoted to float or string.
- Float can be promoted to string.
Result
An expression always produces an output value, and the boolean result for the expression method reflects the value of the expression. If the output value is not stored in a variable, then the boolean result is still valid. If the value of the expression is not boolean, then one of the following conversions is made to arrive at a boolean result:
- For an integer, the value 0 becomes false, and any other value becomes true.
- For a float, the value 0.0 becomes false, and any other value becomes true.
- For a string, the empty string becomes false, and any other string becomes true.
Expression Grammar
The following grammar provides a formal description of exactly how expressions are parsed.
identifier ::= [A-Za-z_] [0-9A-Za-z_]* hex-digit ::= [0-9A-Fa-f] escape-char ::= '\"' | "\'" | "\?" | "\\" | "\a" | "\b" | "\f" | "\n" | "\r" | "\t" | "\v" | "\x" hex-digit hex-digit bool-literal ::= "false" | "true" decimal-literal ::= [0-9] ("_"? [0-9])* hex-literal ::= ("0x" | "0X") hex-digit ("_"? hex-digit)* octal-literal ::= ("0o" | "0O") [0-7] ("_"? [0-7])* binary-literal ::= ("0b" | "0B") ("0" | "1") ("_"? ("0" | "1"))* char-literal ::= "'" ([#x20-#x26#x28-#x5B#x5D-#x7E] | escape-char)+ "'" integer-literal ::= decimal-literal | hex-literal | octal-literal | binary-literal | char-literal float-literal ::= (([0-9] ("_"? [0-9])* "." ([0-9] ("_"? [0-9])*)? | ([0-9] ("_"? [0-9])*)? "." [0-9] ("_"? [0-9])*) (("e" | "E") ("+" | "-")? [0-9] ("_"? [0-9])*)? | [0-9] ("_"? [0-9])* ("e" | "E") ("+" | "-")? [0-9] ("_"? [0-9])*) string-literal ::= ('"' ([#x20-#x21#x23-#x5B#x5D-#x7E#xA0-#xD7FF#xE000-#xFFFD#x010000-#x10FFFF] | escape-char | "\u" hex-digit hex-digit hex-digit hex-digit | "\U" hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit)* '"')+ primary-expression ::= ("+" | "-" | "~")? (identifier ("." identifier)? | integer-literal | float-literal | string-literal | "(" or-expression ")") multiplicative-expression ::= (multiplicative-expression "*" | multiplicative-expression "/" | multiplicative-expression "%")? primary-expression additive-expression ::= (additive-expression "+" | additive-expression "-")? multiplicative-expression shift-expression ::= (shift-expression "<<" | shift-expression ">>")? additive-expression relational-expression ::= (relational-expression "<" | relational-expression ">" | relational-expression "<<" | relational-expression ">>")? shift-expression equality-expression ::= (equality-expression "==" | equality-expression "!=")? relational-expression and-expression ::= (and-expression "&")? equality-expression xor-expression ::= (xor-expression "^")? and-expression or-expression ::= (or-expression "|")? xor-expression