User Reference:Expression Syntax
BCI2000 contains an expression parser that evaluates user-defined arithmetic expressions at run-time. Expression syntax tries to be intuitive and is based on C and Matlab notation.
The empty expression is a valid expression, and evaluates to 0.
Numbers may be given as literals in decimal or scientific notation: 100, 1e2, 100.0, (1000/10) all refer to the same number. Complex numbers are not supported.
- Arithmetic operators
^ unary- * / + -
- Unlike C but consistent with Matlab,
a^bevaluates to the b-th power of a.
- Comparison operators
< > <= >= == != ~=
- Note that a single equals sign is not a valid operator. For the "not equal" operator, the C version (!=) is supported along with the Matlab version (~=).
- Logical operators
! ~ && ||
- Again, ! and ~ are synonymous to match both C and Matlab conventions. When mixing boolean and numerical expressions, values need to be converted back and forth between numbers and logical values. As usual, zero is treated as false, and nonzero values are treated as true. Reversely, false is converted into the number 0, and true is converted into 1. There are no literals for true and false; use 0 and 1 instead.
- Condition operator
- As in C, the ternary condition operator ?: allows for if-then-else-like constructs. If condition is true, the condition operator evaluates to the value of if-expr, and to the value of else-expr otherwise.
- Operator precedence follows the order of appearance in the above list. As usual, parentheses ( ) may be used to override operator precedence.
By default, expressions know a number of mathematical functions by name. These are:
- fabs, abs (alias to fabs)
- fmod, mod (alias to fmod)
- floor, ceil
- exp, log, log10
- sin, cos, tan
- asin, acos, atan, atan2
- sinh, cosh, tanh
Arguments and behavior of those functions agree with their implementations in the standard C library. In addition, the following functions are provided which are not present the standard C library:
- bits(value, position, count) converts value to a 64 bit unsigned integer, and extracts count bits starting at bit position. Bits are counted from least to most significant, starting at zero.
- bit(value, position) computes the result of bits(value, position, 1) in a slightly more efficient way.
By deriving from the Expression class, a programmer may support additional functions from his own code.
Expressions support the creation of variables, and may contain statements that perform assignment, as in
On the right hand side of the assignment, only existing variables may occur. On the left side, when a non-existing variable is referenced, it is created.
The scope of variables depends on the expression's evaluation context. When calling Expression::Evaluate(), a pointer to an Expression::VariableContainer may be specified by the programmer. When no such container is specified, variables cannot be used. When the container is cleared each time before an expression is evaluated, variables are local to the expression. When the same container is specified across expression evaluation, variables keep their values across multiple evaluations. When the same container is specified when evaluating multiple expressions, variables and values are shared amongst these expressions.
Expressions may involve state variables. These are referred to by name as in
When a variable exists with the same name as a state, the state takes precedence. To address states unambiguously, use
When assigning to state variables, always use
Depending on context, expressions may involve signal values:
Here, "Signal" refers to a filter's input signal. Consistently with configuration parameters (e.g., indices specified inside the LinearClassifier matrix), indices are one-based. Similarly, labels and physical units may be used when defined for the signal in question, e.g.
Signal( "CPz", 10ms )
will address a channel labeled "CPz", and a physical sample position 10ms from the epoch begin when defined for the signal in question. (Note: In previous versions of BCI2000 (previous to the Apr 2008 build, up to source revision 1914), expressions used zero-based indices; you may have to update your configuration accordingly when using expressions that involve signals.)
The semicolon may be used to concatenate multiple statements and expressions into a single expression. The expression will then evaluate to the value of the last statement or expression. Examples: The expression "4;5" evaluates to 5; the expression "x:=4; y:=3;" evaluates to 3; the expression ";" evaluates to 0; the expression "1;" evaluates to 1.
Expressions support C++-style comments. Beginning with two slashes, a comment extends to the end of a line:
Result:=PreviousTargetCode-State(TargetCode); // Compute the return value PreviousTargetCode:=State(TargetCode); // Store the target code Result; // Return the result
- An expression that evaluates to the ResultCode state only if that equals the TargetCode state (i.e., in case of a successful trial):
- A mapping of TargetCode values to arbitrary numbers:
- There is no integer arithmetic. All evaluation is done in floating point, even if only integers are involved. As a result, comparisons may fail unexpectedly, or boolean conversion may always yield true:
- should be true independently of x but will always evaluate to false due to roundoff errors.
- In most cases, a numerically robust reformulation of the expression should resolve the problem.
- Sometimes, operator precedence or behavior may not match your intuition. When in doubt, use brackets:
- rather than
- which is equivalent to the previous line but may be misunderstood as