In this post I would like to talk about type systems. You may have never heard this term before but it is an essential part of every programming language. It is so important that it either makes the language grab you or loose you from first try. We may say that behind every good language there is a good type system. But what it really is?
Well, shortly said the type system is the entity that assigns the property that we call "Type" to all variables, literals, functions, expressions etc. used by the language. Then the types are used to ensure correctness of the code. The implementation of the type system is often referred as a "type checker".
In the case of SCL the type system dictates that:
Boolean literals (TRUE/FALSE) are assigned a BOOL type.
Integer literals (1,2,3,...) are assigned a DINT type. If the value doesn't fit in a DINT type then a LINT type is assigned.
Hex/Oct/Bin literals (16#FF, 8#77,2#101,...) are assigned a UDINT type. If the value does not fit in a UDINT type then a ULINT type is assigned.
Floating point literals (1.3, 3.14, 2.75,...) are assigned a LREAL type.
String literals ('hello', 'part1',...) are assigned a STRING type.
Time literals (TIME#2ms, T#10s10ms,...) are assigned a TIME type.
Variables are assigned the type used when declaring them.
Functions are assigned the type used when defining them.
etc.
To summarize: all leaf nodes in the program tree is assigned a well-known type.
How about expressions?
Expressions are more complex and often include a number of operators/operands.
Operators have priority so the calculations are done in the proper order. The expression is represented as a sub tree of the program tree. The sub tree is visited using a post order traversal algorithm (left node, right node, token) in order to evaluate its type.
During the traversal process we determine the type of the left/right nodes and decide for the type of the expression based on the operator (token) used.
Imagine we have an expression like this: TRUE AND FALSE
To calculate the type of the expression we know in advance that the AND operator in SCL can be used in logical and bitwise expressions (this comes from the language grammar). Therefore the type of the operands determines the expression type. Boolean operands denote a Boolean expression. Integral operands denote a bitwise expression. Easy stuff right?. But how do we know if a type is integral or not?
You see … the type should provide some metadata describing it. We usually refer to this metadata collectively as type traits.
So once we make sure that both operands are of the same type we can easily say if the expression is logical or bitwise.
This approach is then applied recursively to all operators in the language. Eventually you get a way to determine the type of any expression regardless of its complexity.
To summarize: expressions are assigned a well-known type based on the operators/operands involved.
How about invalid expressions? Expressions like: 5 AND TRUE
In this case we won't be able to evaluate the expression's type since the intention is unclear. This is neither a logical or bitwise expression. In this case the type checker throws an exception. And this is how we get feedback from the system that the expression is malformed.
Note: The expression node in SCL contains a pointer to its type. If the pointer is null the type is evaluated by the type checker. If already evaluated the type is simply used. This is a nice optimization that helps to save time when evaluating the type of complex expressions.
Now that we have all this its time do mention some of its usages:
Statement interpretation - Assignment operator, loop control variables, case statement etc. rely on the type of the variable to implement their logic.
Expression evaluation - This is a two step process. First we check the expression type. Then we evaluate the expression using its type (operators are defined per type).
Type casting - Once we have the type of an expression it is easy to cast the value of it to another type. Type casting is frequently used when evaluating expressions since operators only work on values of the same type.
Ensure valid expressions are used in conditional statements - if, elsif, while etc. all work on logical expressions only.
Debugging - Show the expression type/value in the variables/watch windows.
Code validation - Highlight malformed expressions.
Others
I think you are already getting a sense of the importance of the type system. Essentially these are the rules ensuring the correct behavior of the language.
During my work on SCL it was important for me to understand that a programming language is a fine tuned state machine defined by two essential components:
The output of the parser - the program tree
The language rules - the type system
I hope this was an interesting finding for you as well.
See you in the next post!
Comments