Expressions are language entities just like statements. The main difference is that expressions should be evaluated in order to calculate a value. Statements on the other hand are just instructions that have no value. Expressions are typically used within statements to provide specific value at a specific place in the code. The interpretation of this value depends on the statement type.
In programming languages usually we deal with two types of expressions:
1. Binary expressions - having two operands. it is important that the operands are of the same type or one of the types can be converted to the other (usually the smaller one is converted to the bigger one)
2. Unary expressions - having one operand. the type of the operand is preserved.
Example: not #a, -#a etc.
In addition these two major groups are split into more sub groups based on the operator used.
1. Logical expressions - evaluate to a Boolean value.
2. Arithmetic expressions - evaluate to a typed value. the type depends on the type of the operands.
In the case of SCL an interesting fact is that logical expressions can be treated like bitwise expressions if they involve operands of integral types. So if #a and #b are of type INT (or any other integer type) the value of #a xor #b will be the bitwise XOR of the two operands. However if #a and #b are of type BOOL then the value of #a xor #b will be logical XOR of the two operands. The same is true for operators: OR, AND & NOT.
The next separation that exists is by priority. Each operator has a well known priority (relative to the other ones) defined by the language. This brings the requirement to define each possible expression type individually and then make a top-down hierarchy out of all possible expression types. As a general rule: the expression type at the top of the hierarchy is assigned the lowest priority. At the bottom the expression type is assigned the highest priority. This is how language grammars are defined as well. Expression types are usually recursively defined in the grammar in order to define their priority.
Having all this in mind we have a number of expression types defined in SCL:
In this definition primary expressions have the biggest priority when evaluating expressions. This is why parentheses force the expression inside to be evaluated first for example. The same priority is applied for functions, variables and literals (language constants).
Operators that fall on the same line share the same priority.
In the case of complex expressions the evaluation order will look like this:
The expression nesting level is up to the user.
Of course there is nothing to prevent you from writing invalid expressions like: #a + true
In this case the expression is considered malformed and the value behind it unknown. The SCL interpreter in Process Simulate will inform you about such expressions with an error.
But how does this happen? How do you know that the expression is malformed? Well here we rely on the SCL type system to infer the expression's type. If the expression's type cannot be inferred the expression is considered malformed.
It may also happen that you use an invalid expression type in a given context. For example: arithmetic expression instead of a logical expression. In this case you'll also get an error.
These checks are applied automatically to all expressions that you write based on the context in which you evaluate the expression.
One thing that is still missing (v2402) is the ability to validate expressions as you write.
Type inference is useful in other places too. To mention a few:
When assigning value to a variable
When passing input arguments of a FC/FB
When evaluating expressions in the debugger
So much about expressions. There is a lot to talk about here but these are the basics. This topic also falls into the category of getting to know SCL in Process Simulate better.
I hope you'll find it useful.
See you in the next post!
Comentários