LSPS documentation logo
LSPS Documentation
Expressions

Properties, conditions, assignments, etc. are all defined as expressions of the LSPS Expression Language. Expressions use of operators, literals, keywords, and other constructs of the Expression Language, that end up returning a value of a type. Also literals are of a type, which does not change (unless cast but we will get to that later).

Evaluating Expressions

You can test expressions in the Expression Evaluator REPL view. By default, the view is not displayed: To display it, go to Window > Show View > Other and in the displayed dialog search for the view.

expressionEvaluatorREPL.png
To test an expression and display its return value, enter the expression into the view and click Evaluate or press CTRL+Enter (Enter will make a new line in your expression). Note that you can list the history of expression by pressing the arrow-up key and display auto-completion options by pressing CTRL+Space.

Important: The REPL view does not support calls to Standard Library resources, such as functions, data types, etc.

expressionEvaluatorREPLView.png

Note: When evaluating your expression in the Expression Evaluator REPL view, the expressions are evaluated in a "dummy context" outside of any model: under normal circumstance, on runtime, expressions are evaluated in a context of their model instance: so they can access data in their parent contexts. Contexts are represented by Instances of name-space elements: These elements include Modules, Processes, Plans, and sub-Processes. To fully understand when a context is created and what data it holds, refer to namespaces and contexts.

Let's try it out:

Enter the following into the Expression Evaluator REPL and try to predict the return value:

  • First, let's try some arithmetics:
    • 1 + 1
    • 1 + 1.12
    • 1/1.1
    • 1%1.1
    • 3**3
  • Let's try some concatenation:
    • "Hello, " + "world!"
    • "Hello, number " + 1
    • 1 + "hello" This results in an invalid expression since the + operator is interpreted as addition, not concatenation.
  • Let's try some comparing:
    • "Anne" < "James"

      Strings are compared lexicographically: A appears before J in alphabet.

    • "James" like "J*"
    • "James" like "J???s"
    • "James" like "Jo*"
    • 1 == 1,00
    • "Bob"<=>"Anne"
    • "Bob"<=>"John"
    • "Bob"<=>"Bob"
    • `d'2015-12-24 20:00:00.000' < d'2017-12-24 20:00:00.000'`
  • And now let's do some logic:
    • true and false
    • true and true
    • false and false
    • true or false
    • false or true
    • true or true
    • true xor false
    • false xor true
    • true xor true
    • true xor true
    • !true
    • !false
  • Now, chain multiple expressions:
    • "This will not return!";"Hello, " + #10 + "world!"

      Note that only the value of the last expression is returned.

  • Create Collections with items of some data type:
    • Sets: {1, 2, 3} and {1, 1, 2, 3}
    • Lists: [1, 1, 2, 3]

      Sets cannot contain items of the same value while Lists can.

      Mind that Collections are immutable: when you decide to change a List you need to create it anew.

  • [ 1 -> "Sunday", 2 -> "Monday"] is a Map. In this case its of the type Map<Integer, String>.
  • Evaluate the closure {x:Integer -> "Integer" + x}

    The closure has an Integer input parameter called x in the closure contexts: the closure a String, hence it is of the type { Integer : String }

We have been through some basic data types but there are other data types to explore and we will get gradually to all of them. Let's take a sneak peek and evaluate the following:

  • type(Integer) returns the Integer type.
  • Null is a special data type with the sole value null: all other types are super types of the Null type so any data type can all have the value null.
  • &var is a reference to a variable.

Note: We do not mention Records, which are complex data structures, and the related data types one of the reasons being that it is not possible to create a new Record type with the Expression Language since Records are modelled in diagrams similarly to processes.

Creating Local Variables

In expressions, you can create local variables using the syntax def <TYPE> <VARIABLE_NAME>.

Try this in REPL view:

def Boolean boolVar;

Though the Boolean variable exists now, it has no value: its value is null of the type Null as we mentioned above. To assign a value to a variable, use the assignment operator :=.

boolVar := true

Local variables cannot be accessed from outside of their scope, expression block. An expression block is the expression, a loop, an if block, and an explicit block between the keywords begin and end.

Evaluate the following:

  • Create a block with a local variable:
    begin
      def Boolean boolVar := true
    end
    
  • Call the variable:
    boolVar

If you need a variable that you can access from throughout the model or a particular namespace, use either global or local variables: however, these are part of modeling and we will deal with them in later courses on model resources.

Check Point

  1. Create a local variable called closureVar of the type Closure, which takes a list of integers as its input parameter and returns a String.
  2. Assign the variable a closure that concatenates the input list into a single String and returns the String.
  3. Create a list with integers 1-100 and feed it to the closure variable.

The entire expression should look something like this:

def {List<Integer>:String} closureVar;
closureVar := {myIntList:List<Integer> ->
     def String output;
     foreach Integer i in myIntList do
       output := output + i + ", "
     end;
     output
     };
def List<Integer> myIntList := 1..1000;
closureVar(myIntList)

Functions

You can consider functions special types of closures: they take arguments of certain types and return a value of a certain type, plus they can be used whenever you would use a closure: They are sort of closure constants.

To call functions from an expression, use the syntax <FUNCTION_CALL>(<PARAMETER_1>, <PARAMETER_2>).

However, it is not possible to create a new function: you can only call existing function.

Note: We will create functions as part of model resources in another course.

Luckily, there are plenty of functions available in the Standard Library which is automatically available in your Module. And unluckily, you cannot call them from the Expression Evaluator REPL: you need to use the Expression Evaluator of a Model instance. Open the Expression Evaluator over a model instance:

  1. Create a model and run it on the PDS Embedded Server. If you need detailed instructions, proceed as instructed in the Quickstart guide.
  2. Switch to the Management perspective.
  3. Refresh the Model Instances view and double-click the model instance to open its detail.
  4. In the lower part of the detail view, expand the Expression Evaluator: here you can evaluate expressions in the contexts of the model instance. You can select a running context in the Model Instance Explorer above the Expression Evaluator. For this course, you do not need to select any context: the expressions are automatically evaluated in the context of the Model instance.
  5. To make your life a bit easier when evaluating expression in the Expression Evaluator, make use of the following features:
    1. Press Ctrl and arrow up and down to go throw the previously entered expression.
    2. Press Ctrl and space to display auto-completion.
    3. Press Ctrl and enter to evaluate the expression.
  6. Evaluate the expression "Hello!". This is simply a String literal, which surprisingly enough returns itself: "Hello!"
  7. Let us now evaluate the expression Hello!. This is not a valid literal nor a variable name nor anything else that could be recognized as an expression: so what you have here is an invalid expression and the Evaluator tells you so.

Let's try it out:

Creating a date value like d'yyyy-MM-dd HH:mm:ss.SSS' is quite cumbersome: luckily there is a function that will make your life much easier: the date() function. In the Expression Evaluator, start typing date and press Ctrl + space to display the auto-completion dialog with the date functions. Click the one you like and define its parameters so it returns the correct date value.

functioncallAutoCompletion.png

Type Hierarchy and Casting

Data types are arranged in a hierarchy which restricts relationships between data types. A type can be a subtype or a supertype of another type: If a type is a subtype of another type, it can be always used instead of the supertype, but not vice versa. Since the typing is so strict it prevents you from using incorrect types; for example, you cannot accidentally pass an Integer value to a String variable unless you explicitly cast it to a String.

For the types of the language, the most generic data type is the Object type: all data types are subtypes of the Object type: when a variable is of the Object type, you can assign it a value of any type.

In a complex type, one is a subtype of the other if their inner members are each other's subtypes: Map<KA, VA>; is subtype of Map<KB, VB>, if KA is a subtype of KB and VA is a subtype of VB.

Let's try it out:

In the Expression Evaluator of your model instance, create an Object variable, for example, def Object myObject, and assign it a value of any type. To check the type of value the object holds, run typeOf(myObject). If you evaluate def Object myObject := [1,2,3]; typeOf(myObject), the expression will return the type List<Integer>

Otherwise, the data type structure of the built-in data types is pretty flat: there are only two other relationships:

  • The Decimal type and Integer type with the Decimal type being the super type: Wherever you can use a Decimal value, you can use an Integer value just as well.
  • The Collection type and, the Set and List types: Collection is however abstract; it serves to allow you to decide whether something will be a Set or List later during execution. Let's try it out: Evaluate the following:
  • def Collection<Object> myObject := [1,2,3]; typeOf(myObject)
  • def Collection<Object> myObject := {1,2,3}; typeOf(myObject)

If you want to change the value type, typically from a supertype to a subtype, you can use the build-in cast mechanism. Let's try it out: In the Expression Evaluator, evaluate the following expressions:

  • 1 as Decimal
  • 1.00 as Integer
  • 1.12 as Integer

Related Documentation

For details on features of the Expression Language, refer to the Expression Language Guide. As a reference, consider using the Expression Language Reference Card.