Cloud Software Group, Inc. EBX®
Documentation > Developer Guide > EBX® Script
Navigation mode Documentation > Developer Guide > EBX® Script

# Reference

## Introduction

EBX Script is a simple and quite powerful language intended to be used anywhere where Java is currently needed like EBX customization (computed values, table trigger, script task…​).

It is based on the DSL (Domain Specific Language). DSL is a procedural and strongly statically typed language.

To write a script, the EBX IDE can be used. It is an integrated development environment that provides tools and features to assist in defining scripts for function fields, table triggers and script tasks. It includes an editor with various features like syntax highlighting, contextual code completion, source compiling and publishing.

When a new function field or a table trigger is created using the DMA , a default script is created in the EBX IDE.

When a script task is created using the IDE, it can be specified in the Workflow Model .

## Lexical structure

### Introduction

A script has following structure:

    <unit usage statement 1>
    <unit usage statement 2>
    ...
    <unit usage statement N>

    <model usage statement 1>
    <model usage statement 2>
    ...
    <model usage statement M>

    <function or procedure definition 1>
    <function or procedure definition 2>
    ...
    <function or procedure definition M>

For more information, see Types declared in a data model , Unit and function and procedure .

The following example is a script of a function field.

    // This field returns the full address for current record.
    export function getValue(): string
    begin
      var address := _ebx.record.FirstName | ' ' | _ebx.record.LastName;

      for street in record.OfficeAddress.Street do
      begin
        address |= '\n' | street;
      end;

      address |= '\n' | _ebx.record.OfficeAddress.ZipCode | ' ' | _ebx.record.OfficeAddress.City;
      address |= '\n' | _ebx.record.OfficeAddress.Country;

      return address;
    end

The following example is a script of a table trigger.

     //This trigger adds the full name of current record.
    export procedure onBeforeCreate()
    begin
    _ebx.record.fullName := _ebx.record.firstName | ' ' | _ebx.record.lastName;
    end

### Character set

The Unicode character set is supported.

### Character case sensitivity

The DSL is case-sensitive.

### Comments

A single line comment extends from // to the end of the current line:

    // This is a comment
    if _ebx.record.LastName = 'Doe' then // This is another comment.
      return true;

A multi-line comment extends from /* and ends with */ :

    /* This is an example of a multi-line
       comment */
    if _ebx.record.isActive then
      return false;

### Keywords

The reserved keywords are: and , or , not , uses , as , from model export , typeof , tableof , primarykeyof , mutable , immutable , modifiable unmodifiable , persisted function , procedure , const , var , if , then , else , for , while , in , do , begin , end , return , true , false , raise , null .

Reserved keywords cannot be used as plain (unquoted) identifiers.

## Identifiers

### Unquoted identifier

An unquoted identifier is an unlimited-length sequence of letters, digits, or underscore ( _ ). The first character must be a letter or an underscore.

Valid letters are a to z and A to Z . Valid digits are 0 to 9 .

An unquoted identifier may not be equal to a reserved keyword.

### Quoted identifiers

A quoted identifier is an unlimited length of any Unicode character except double quote ( " ).

Quoted identifiers must be used surrounded by double quotes.

An unquoted identifier can be used surrounded by double quotes.This means that identifier "a_name" is equal to a_name.

Quoted identifiers can be reserved keywords.

## References

### References to data models

A script may declare references to data models.

For data models deployed as an XSD file, supported syntaxes are:

    references model short_name from module module_name as alias;
    references model short_name from module module_name;
    references model file_path from module module_name as alias;

The short name is the name of the file without the path and extension. For exemple the short name of /WEB-INF/ebx/schemas/Directory.xsd is Directory .

If no alias is specified, the alias is assumed to be equal to the short name or the file path.

It is possible to declare a data model from current module by using one of the following syntaxes:

    references model short_name from module . as alias;
    references model short_name from module .;
    references model file_path from module . as alias;

For embedded models, use one of the following syntaxes:

    references model short_name as alias;
    references model short_name;

Data model references samples:

    references model Directory from module "ebx-test" as dic;
    references model Directory from module "ebx-test";
    references model "/WEB-INF/ebx/schemas/userservice/Directory.xsd" from module "ebx-test" as dic;

    references model Directory from module . as dic;
    references model Directory from module .;
    references model "/WEB-INF/ebx/schemas/userservice/Directory.xsd" from module . as dic;

    references model catalog as cat;
    references model catalog;

## Types

### Predefined types

The following predefined types are supported:

Type | Keyword | Properties | EBX® corresponding types
--- | --- | --- | ---
Boolean | boolean |  | xs:boolean
Decimal (unlimited precision) | decimal |  | xs:decimal
Integer (32 bits) | int |  | xs:int xs:integer
String | string | length (int greater or equal to 0) | xs:string xs:Name osd:text osd:html osd:email osd:password 1 osd:color osd:dataspaceKey osd:datasetName
Timestamp (millisecond precision and without time-zone) | timestamp | year month (1 to 12) day (1 to 31) hour (0 to 23) minute (0 to 59) second (0.000 to 59.999) | xs:dateTime
Date (without time-zone) | date | year month (1 to 12) day (1 to 31) | xs:date
Time (millisecond precision) | time | hour (0 to 23) minute (0 to 59) second (0.000 to 59.999) | xs:time
Locale | locale |  | osd:locale
URI (Uniform Resource Identifier ) | uri |  | xs:anyURI
Resource | resource |  | osd:resource
Dataspace | dataspace | name identifier (the name prefixed by 'B' or 'V') isSnapshot (Is true if the dataspace is a snapshot) |
Dataset | dataset | name dataspace |
Transaction | transaction | dataspace isAllPrivileges (read/write boolean property) executionInformation (read/write string property) executionTimestamp executionUUIDString |

1 It is not possible to define a function field for type osd:password .

### Complex types

A complex type is the type of a group (complex) node defined in an EBX® schema.

For more information, see the chapter complex variables

### List types

The DSL supports lists. Declare a list by using the following syntax:

    list<item_type>

A list is unmodifiable by default. You can declare a modifiable list by using following syntax:

    modifiable list<item_type>

An item cannot be added, removed, or replaced if the list is unmodifiable.

Use indexed notation to set or get a value. (The first index is 0):

    // Set first value of cities.
    cities[0] := 'Paris';

    // Get the second item of cities.
    return cities[1];

An index can be any expression of int or decimal type. If the index is a decimal, the fractional part is ignored.

A get expression returns null if the index is null, negative, or out of range.

A set expression reports an error at runtime if the index is null, negative, or out of range.

The property size returns the size of the list:

    var size := cities.size;

You can iterate a list.For more information, see for loops .

Multi-value schema fields

Schema fields that are multi-valued are considered of list type.

A multi-valued schema field is a field that has its maximum number of values (maxOccurs) greater than one or set to "unbounded".

Indexed notation is used to access or set a value. The first index is 0. In the following example, record field 'OfficeAddress/street' is multi-valued:

    // Return the second line of the street part of the address.
    return OfficeAddress.street[1].

The typeof keyword can be used with multi-valued fields (see chapter keyword typeof ).

### Keyword typeof

Use the keyword typeof to specify a type depending on a field or variable.

For example, to reference the type of record field OfficeAddress , use the following syntax:

    typeof record.OfficeAddress

If a field is multi-valued, it is considered a list. You can reference the type of an item of the list using following syntax:

    typeof record.Addresses[*]

### Types declared in a data model

To reference the type of a field in current data model that has path /root/User/Address , use the following syntax:

    var address: .User.Address;

Note that '/' are replaced by '.' and that first step is omitted.

To get the type of a field in from a data model that has path /root/Sales/Amount in a data model declared with alias 'hist', use the following syntax:

    var amount: hist.Sales.Amount

If a field is multi-valued, it is considered a list. You can reference the type of an item of the list using following syntax:

    var products: hist.Sales.products[*]

### Record, table, primary key

If path "/root/test/table" of a data model sample is for a model, use one of the following syntaxes for specifying a record:

    var immutableRecord1: sample.test.table;
    var immutableRecord2: immutable sample.test.table
    var mutableRecord: mutable sample.test.table

When no modifier is specified, a type is immutable.

Use the following syntax for specifying a table:

    var aTable: tableof sample.test.table;
    aTable := db.findTable<.directory.Customer>(_ebx.dataset);

Use one of the following syntaxes for specifying a primary key:

    var immutablePrimaryKey1: primarykeyof sample.test.table;
    var immutablePrimaryKey: immutable primarykeyof sample.test.table
    var mutablePrimaryKey: mutable primarykeyof sample.test.table

When no modifier is specified, the primary key is immutable.

### Type modifiers

A type may be:

- immutable : it is not possible to modify an item of this type.

- mutable : it may be possible to modify an item of this type.

A simple type is always immutable.

You can assign a mutable value to a variable or a return value of a compatible immutable type, but assigning an immutable value to a mutable variable or return value generates an error at compile time.

You can specify an immutable type using one of following syntax:

    immutable typeof record.OfficeAddress
    immutable .User.Address
    immutable hist.Sales.Amount

You can specify an mutable type using one of following syntax:

    mutable typeof record.OfficeAddress
    mutable .User.Address
    mutable hist.Sales.Amount

Some types may not support immutable or mutable keywords and generate an error at compile time.

A list may be:

- unmodifiable : it is not possible to remove on insert an item. Items may be immutable or not.

- modifiable : it is possible to remove on insert an item. Items may be immutable or not.

You can assign a modifiable list to a variable or a return value of a compatible unmodifiable list type, but assigning an unmodifiable list to a modifiable variable or return list generates an error at compile time.

You can modify a mutable complex or list object, but other constraints may apply that can prevent modifying some attributes of an object.

You can specify an unmodifiable list type using following syntax:

    unmodifiable typeof  _ebx.record.Addresses[*]
    unmodifiable .User.children[*]
    unmodifiable hist.Sales.products[*]

You can specify a modifiable list type using following syntax:

    modifiable typeof  _ebx.record.Addresses[*]
    modifiable .User.children[*]
    modifiable hist.Sales.products[*]

## Keyword raise

Use the keyword raise to reports an error during the execution of a trigger or a function field.

The following syntax is used to raise an error without a message:

    raise code;

The following syntax is used to raise an error with a message:

    raise code, message;

- code is the error identifier. It must be an integer 32 value.

- message specifies the error message to be displayed.It is an optional string value.

Example of using the keyword raise in a function field:

    // this function raises an error when the name is empty.
    uses core.string as string;
    export function getValue(): string
    begin
     var name := _ebx.record.name;
     if not string.isEmpty(name) then
      return name;

    //value cannot be empty!
     raise 341i;
    end

Example of using the keyword raise in a table trigger:

    // this trigger checks sales amount and reports an error if the allowed amount is exceeded.
    export procedure onBeforeCreate()
    begin
    if (_ebx.record.amount > 1000) then
    raise 450 , 'Sales Amount '| _ebx.record.amount |' has exceeded the allowed amount of 1000';
    end

## Literals

### String literal

String literals can be any sequence of Unicode characters surrounded by single quotes. The following table displays characters that need to be replaced by an escape sequence:

Character | Escape sequence
--- | ---
Tab | \t
Backspace | \b
New line | \n
Carriage return | \r
Form feed | \f
Single quote | \'
Backlash | \\

Specify a character by using a Unicode escape sequence that has format \uXXXX , where XXXX is the hexadecimal code of the Unicode character.

Examples

Value | Syntax
--- | ---
O’Harra | 'O\'Harra'
Noël | 'No\u00EBl'
été | '\u00e9t\u00E9'

Note

An invalid escape or Unicode sequence generates an error at compile time.

### Decimal literal

The following decimal formats are supported:

Format | Examples
--- | ---
Without fraction | 546 -67
Floating point | 54.987 -433.876 0.00054 -0.0032
Exponent notation | 34.654e-5 -45E+65 1.543e23

### Integer (32 bits) literal

Integer 32 bits literals have a format similar to 90324i or -543i .

The value must be between -2147483648i and 2147483647i inclusive.

Attention: If the i letter is missing the value will be considered a decimal.

### Timestamp literal

Timestamp literals have the format dt(yyyy-MM-dd hh:mm:ss.sss) .

Seconds are optional. When they are not specified, 0 is assumed. Seconds can have fractions up to millisecond precision.

Any dates that are not valid in the Gregorian calendar generate errors at compile time.

Examples:

    dt(2010-01-02 00:00:00.000)
    dt(2019-2-3 12:56:7)
    dt(2019-2-3 12:56:7.5)
    dt(2019-5-7 1:6)

### Date literal

Date literals have the format d(yyyy-MM-dd) .

Any dates that are not valid in the Gregorian calendar generate errors at compile time.

Examples:

    d(2010-01-02)
    d(2019-2-3)
    dt(2019-5-7)

### Time literal

Time literals have the format t(hh:mm:ss.sss) .

Seconds are optional. When they are not specified, 0 is assumed. Seconds can have fractions up to millisecond precision.

Invalid times generate an error at compile time.

Examples:

    t(00:00:00)
    t(12:56:7)
    t(12:56:7.5)
    t(1:6)

### Boolean literal

A Boolean literal is either the keyword true or the keyword false .

### Null literal

A null literal is the keyword null .

Use this literal only in the following situations:

- To set a variable or field,

- As a return value.

Note

It is not possible to use this keyword to test if a variable or a field is null . Instead, use the function isNull() .

## Operators

### Precedence

By default, operation evaluation order is based on precedence and associativity. Use parentheses to explicitly indicate the order of evaluation.

The following table shows all operators, from highest to lowest precedence, and their associativity:

Precedence Level | Operator | Operand type | Result type | Associativity
--- | --- | --- | --- | ---
9 | (access to an element of a list) . (access to fields) () (parenthesis) | List index must be an int or a decimal. | Can be any type. | Left to right.
8 | not | boolean | boolean |
7 | * / | decimal or int | decimal | Left to right.
6 | + - | decimal or int | decimal | Left to right.
5 | | (string concatenation) | string | string | Left to right.
4 | < <= > >= | string, decimal, int, timestamp, date, time (3). | boolean | Not associative.
3 | = <> | string, decimal, int, timestamp, date, time, boolean (3). | boolean |
2 | and | boolean | boolean | Left to right.
1 | or | boolean | boolean | Left to right.

### Arithmetic operators

All arithmetic operators ( * , / , + and - ) are evaluated in decimal with 34 digits precision (IEEE 754R Decimal128 format). Values of ìnt type are always converted to decimal before comparison.

Result is null if any operand is null .

A decimal value is automatically converted when assigned to an int type variable. An error occurs if the value cannot be converted because it is not an integer, is greater than 2147483647, or is less than -2147483648.

### String concatenation operator

The string concatenation operator ( | ) operands can be of any type and are automatically converted to a string before concatenation. The null value is replaced by the empty string.

The string concatenation operator ( | ) replaces all null operands by the empty string before executing the concatenation.

### Boolean operators

Boolean operators use thread-value logic.

The truth table for the and operator is as follows:

And | true | false | null
true | true | false | null
false | false | false | false
null | null | false | null

The truth table for the or operator is as follows:

Or | true | false | null
true | true | true | true
false | true | false | null
null | true | null | null

### Comparison operators

A comparison operator ( < , <= , > , => , = and <> ) compares two operands of same type. Values of int type are always converted to decimal before comparison.

The returned value of a comparator is of boolean type. This value is true or false only if all operands are not null . It is null if any of its operand is null .

### Built-in and unit functions

Built-in and unit functions usually return null if a parameter is null .

There can be exceptions, so be sure to read the API documentation .

## Assignments

The following table shows all assignment operators:

Operator | Operand type | Description
--- | --- | ---
:= | Can be used with any type. | Standard assignment.
|= | string | a |= b is equivalent to a := a | b
+= | decimal, int | a += b is equivalent to a := a + b
-= | decimal, int | a -= b is equivalent to a := a - b
*= | decimal, int | a *= b is equivalent to a := a * b
/= | decimal, int | a /= b is equivalent to a := a / b

## Statement blocks

A statement block is used to group multiple statements. It starts by the keyword begin and ends with the keyword end . Most statements need to be terminated by a ; :

    begin
      statement_1;
      statement_2;
    end

A statement block can contain one or more statement blocks that can also contain statement blocks:

    begin
      statement_1;
      begin
        statement_2_1;
        statement_2_3;
        begin
          statement_3_1;
          statement_3_2;
        end
      end
      statement_3;
    end

## Variables and constants

### Introduction

Any statement in a block can declare a variable or a constant.

A variable or a constant always has a type that is fixed when the variable is declared.

A value can be assigned to a variable using the assignment operator := .

Its is an error to assign a value of the wrong type to a variable, or to change the value of a constant after its declaration.

### Assignment statements

### Declaration with type detection

A variable or constant type can be detected automatically if initialized when declared. This feature is called type inference.

Syntax for a variable is:

    var variable_name := a_value;

The syntax for a constant is:

    const const_name := a_value;

Examples:

    begin
      var firstName := 'John';        // Variable of type string.
      var age := 30i;                 // Variable of type int.
      var cost := 34.54;              // Variable of type decimal.
      var value := 43;                // Variable of type decimal not int
                                      // because the letter `i` is missing.
      var address := record.address;  // Variable of an immutable complex.

      const MAX_AGE := 100i;              // Constant of type int.
      const DEFAULT_COUNTRY := 'France'; //  Constant of type string.
    end

Note

A constant is not necessarily immutable.

### Explicit type definition

It is possible to explicitly specify the variable’s type.

The syntax for a variable is:

    // Following variable initial value is null.
    var variable_1_name : variable_1_type;

    // Value a_value must be compatible with type variable_1_type.
    var variable_2_name : variable_1_type := a_value;

The syntax for a constant is:

    const const_name : variable_1_type := a_value;

Examples:

    begin
      var firstName : string;
      firstName := 'John';

      var age : int;
      age := 30;
      var address : typeof record.address;
      address := record.address;

      const MAX_AGE : int := 100;
      const DEFAULT_COUNTRY : string := 'France';
    end

Variables that are not initialized have a null value:

    begin
      var firstName : string;
      if isNull(fistName) then
         do_something(); // Statement will be executed.
    end

The following statements have errors:

    begin
      // Following will not compile because variable is not initialized and no type
      // is defined.
      var firstName;
      // Following will not compile because variable type is decimal and value is
      // a string.
      var amount : decimal := 'test';
    end

### Scope

A variable or constant scope spans from its declaration to the end of the statement block where it was declared:

    begin
      var firstName := 'John';
      var lastName := 'Doe';

      begin
        // Following is OK because firstName and lastName are visible.
        var fullName := firstName | ' ' | lastName;
        ....
      end

      // Following will NOT compile because fullName is out of the scope.
      var message := 'Please contact ' | fullName;
    end

A variable with same name cannot exist in same block or sub blocks:

    begin
      var firstName := 'John';

      begin
        var firstName := 'Bob'; // Error! Variable already declared.
        ....
      end
    end

The following is correct:

    begin
      begin
        var firstName := 'Bob';
        ....
      end
      // The following is not an error, because block where previous variable was
      // declared is ended.
      var firstName := 'John';
    end

### Complex variables

Variable or constants of complex type can be declared using type detection or typeof keyword .

Dot notation is used to access fields of a variable of complex type.

Examples:

    // Declaration using type detection.
    var address1 := _ebx.record.OfficeAddress;

    // Declaration using typeof notation.
    var address2 : typeof _ebx.record.OfficeAddress;
    address2 := address1;

Each step (parts separated by a dot) is an identifier . This means that following quoted notation can be used for any step:

    var city := _ebx.record."OfficeAddress".City;

This is useful for steps equal to a reserved keyword or using characters, such as the minus character ( - ) or dot ( . ), that are not compatible with unquoted identifiers.

At runtime, any step can evaluate to null . In this case the full field expression evaluates to null .

### Multi-valued fields

Multi-valued fields are treated as ordered lists. Index notation [index] is used to access an item of the list. Indexes are 0 based (first index is 0).

In the following example, the field "Address" is multivalued:

    var city := _ebx.record.Address[1].City;

If an index is out of bound, the indexed expression will return null . In this case the full field expression evaluates to null .

An index may be a decimal or int expression . If decimal` the fractional part will be ignored.

## Predefined variables

### Introduction

Predefined variables allow access to information of the context. The following predefined variables described in this section are common to contexts (function field, table trigger, dataset trigger). They are constant and of an immutable complex type.

Note that there are some specific predefined variables for each contexts: function field, table trigger and script task.

For details on these specific variables, see the links Function field , Table trigger and Script Task .

### Dataspace

The predefined variable _ebx.dataspace provides access to information on the current dataspace.

This variable has following fields:

Name | Type | Description
--- | --- | ---
name | string | The name of the dataspace. Since the dataspace namespace and the snapshot namespace are independent, the returned string is only an identifier in the context of one of the namespaces. For a global dataspace or snapshot identifier, use field id .
identifier | string | The persistent identifier of a dataspace or snapshot. Compared to name , this identifier additionally specifies whether this id is for a dataspace or a snapshot.
isSnapshot | boolean | Is true if dataspace is a snapshot and false if dataspace is a branch.

Example:

    // Returns details on the current dataspace.
    export function getValue(): string
    begin
      if _ebx.dataspace.isSnapshot then
        return 'Snapshot: ' |  dataspace.name;
      else
        return 'Branch: ' |  dataspace.name;
    end

### Dataset

The predefined variable _ebx.dataset provides access to information on the current dataset.

This variable has the following fields:

Name | Type | Description
--- | --- | ---
name | string | The name of the dataset
dataspace | dataspace | The dataspace of the dataset

Example:

    // Returns the current dataset name.
    export function getValue(): string
    begin
      return _ebx.dataset.name;
    end

Node: The predefined variable _ebx.dataset is not available in the context of a dataset trigger when the trigger is of type afterDelete .

### Root

The predefined variable _ebx.root is available when the current script is for a table trigger, dataset trigger or for a function field. It provides read-only access to the fields of current datasets.

Its fields are defined by the current EBX® schema.

Examples:

    // this function field returns information on current dataset data.
    export function getValue(): string
    begin
    	return _ebx.root.City | ' ' | _ebx.root.Region;
    end

    // this table trigger logs information of current dataset data.
    uses core.log as log;
    export procedure onNewContext()
    begin
    	log.info(_ebx.root.City | ' ' | _ebx.root.Region);
    end

    // this dataset trigger logs information of current dataset data.
    uses core.log as log;
    export procedure onAfterDuplicate()
    begin
    	log.info(_ebx.root.Country);
    end

## Functions and procedures

### Functions

Functions are methods that return a value.

A function with no parameters has the following syntax:

    function function_name(): return_value_type
    begin
      ....
      return a_value;
    end

A function with parameters has the following syntax:

    function function_name(
      parameter_1 : parameter_1_type,
      parameter_2 : parameter_2_type...,): return_value_type
    begin
      ....
      return a_value;
    end

It’s possible to declare a parameter constant using the const keyword:

    function function_name(const parameter_1 : parameter_1_type): return_value_type
    begin
      ....
      return a_value;
    end

The last statement of a function must always be a return statement. The function can include as many return statement as necessary.

Example:

    function getFullName(firstName: string, lastName: string): string
    begin
      if isNull(firstName) then
        return lastName;

      return firstName | ' ' | lastName;
    end

It is illegal to return a value different from the one declared:

    function getValue(): string
    begin
        return 0; // Error! Return type is decimal, not string.
    end

### Procedures

Procedures are methods that do not return a value.

A procedure with no parameters has the following syntax:

    procedure procedure_name()
    begin
      ....
    end

A procedure with parameters has the following syntax:

    procedure procedure_name(
      parameter_1 : parameter_1_type,
      parameter_2 : parameter_2_type...)
    begin
      ....
    end

It’s possible to declare a parameter constant using the const keyword:

    function procedure_name(const parameter_1 : parameter_1_type): return_value_type
    begin
      ....
      return a_value;
    end

A procedure cannot have a return statement, but the function can include as many return statements as necessary. A procedure statement cannot return a value.

## Exported function or procedures

An exported function can be called directly by EBX®. Only one exported function or procedure is allowed per script. Its signature (name, return type) depends on the context.

For details on the exported function or procedure of each context, see the Function field and the Table trigger .

### Parameters

Simple type parameters are passed by value. This means that a function or a procedure receives a copy of the original value.

Complex and list types are passed by reference. This means that a function or a procedure receives the original object. If the function or procedure modifies the object, the original one is modified.

## If statement

### "If then" statements

An "if then" statement has the following syntax:

    if condition-expression then
      then-statement

The condition expression must evaluate to a boolean type.

The 'then' statement can be a statement block .

### "If then else" statements

An "if then else" statement has following syntax:

    if condition-expression then
      then-statement
    else
      else-statement

The condition expression must be of boolean type.

A 'then' or 'else' statement can be a statement block .

Note

Expression:

    if condition-expression then
      statements-a;
    else
      statements-b;

Cannot be equivalent to:

    if not condition-expression then
      statements-b;
    else
      statements-a;

Indeed, if the expression is null, the else statement is executed in both cases.

## Loops

### "For in do" loops

A "for in do" loop statement is used to select each item of a list and execute a block statement. It has following syntax:

    for item_variable_name in list do
    begin
      statement_a;
      statement_b;
      ...
    end

The block statement is executed once for each value of the list. At each iteration, the read-only item variable takes a value of the list in the order of the list.

The name of the item variable must be unique in the current scope or an error will be generated at compile time.

If a single statement must be executed for each item, the following simpler syntax can be used:

    for item_variable_name in list do statement;

The following example iterates a list of complex:

    // Concatenate all city addresses in a single string.
    var cities := '';
    for address in _ebx.record.Addresses do
    begin
      if cities <> '' then cities |= ', ';
      cities |= address.city;
    end

### "While do" loops

A "while do" loop statement is used to execute a statement block until a condition is true . It has following syntax:

    while condition do
    begin
      statement_a;
      statement_b;
      ...
    end

If a single statement must be executed, the following simpler syntax can be used:

    while condition do statement_a;

The following example calculates factorial of a value:

    function factorial(value : decimal): decimal
    begin
     var factorial := value;
     while(value > 1) do
     begin
       value -= 1;
       factorial *= value;
     end
     return factorial;
    end

A decimal type is used instead of an int type to avoid out of range errors.

## Units

EBX® provides an API that is packaged in "units".

A unit can define multiple function or procedures.

Except for the default one, a unit must be declared before usage. The declaration must be at the top of the script and must follow these types of syntaxes:

    uses package_name.unit_name_a;
    uses package_name.unit_name_b as alias_b;

Currently, package_name is always core . A unit alias must be unique in a script.

Methods can be referenced using following syntaxes:

    value1 := package_name.unit_name_a.function_a();
    value2 := package_name.unit_name_a.function_b(parameter1, parameter2);

    value3 := alias_a.function_c();
    value4 := alias_a.function_d(parameter1, parameter2);

    package_name.unit_name_a.procedure_a();
    package_name.unit_name_a.procedure_b(parameter1, parameter2);

    alias_a.procedure_c();
    alias_a.procedure_d(parameter1, parameter2);

The following example uses the core.list unit to create a list:

    uses core.list as list;

    export function getValue(): typeof _ebx.record.Cities
    begin
      return list.of('Paris', 'Bruxelles', 'Berlin');
    end

For details on the provided units, see the API Documentation .

## Embedded SQL queries

It is possible to embed SQL query expressions. These are validated at compile time.

Embedded queries must be between back-quote and prefixed by sql .

Sample:

    uses core.data as d;
    references model myModel from module "ebx-test" as m;

    var query := sql `select u.f_name as firstName, u.l_name as lastName from m.users u where location = ?`;
    // adds the dataset corresponding to the model 'm' to the query.
    db.addDataset(query, ‘m', d.findDataset(d.findDataspace('myDataspace'), 'myDataset'));
    d.setParameter(query, 0, 'France');;

    for tuple in d.execute(query) do
    begin
       var firstName := tuple.firstName;
       var lastName := tuple.lastName;
    end;
    d.close(query); // Optional. Query is closed automatically if the last tuple is reached or script ended.

## Logging and debugging

The unit unit.log provides a function that can be used to log a message.

A message logged by scripts can be viewed using Administration>Repository management>Scripting EBX® menu.

Another useful feature is that if a runtime encounters an error while executing a script, it is usually logged with the line in the script where the error occurred.

## Initial script

When a new function field or table trigger is created using the DMA , an initial script is created.

The following example is a script created for a table trigger:

    uses core.datetime as datetime;
    export procedure onBeforeCreate()
    begin
    record.inscriptionDate := datetime.now();
    end

The following example is a script created for a function field of type string:

    // Returns the full name.
    export function getValue(): string
    begin
    	return _ebx.record.FirstName | ' ' | _ebx.record.LastName;
    end

For details on initial script, see the Function field and the Table trigger .

Documentation > Developer Guide > EBX® Script