TIBCO Software Inc. EBX®
Documentation > Developer Guide > EBX® Scripting
Navigation modeDocumentation > Developer Guide > EBX® Scripting

Function field

Introduction

Use the function DSL (Domain Specific Language) to define a function fieldfunction field.

This DSL is a procedural and strongly statically typed language.

To create and modify a function field, use the Data Model Assistant (DMA).

The Data Model Assistant (DMA) includes a script editor that provides contextual code completion.

Lexical structure

Introduction

A script has following structure:

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

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

For more information, see Unit and function and procedure.

Example:

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

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

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

  return address;
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 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 record.isActive then
  return false;

Keywords

The reserved keywords are: and, or, not, uses, as, export, typeof, mutable, immutable, unmodifiable, function, procedure, const, var, if, then, else, for, while, in, do, begin, end, return, true, false, 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.

Types

Simple types

The following simple types are supported:

TypeKeywordPropertiesEBX® corresponding types

Boolean

boolean

xs:boolean

Decimal (unlimited precision)

decimal

xs:decimal

Integer (32 bits)

int

xs:int

xs:integer

String

string

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

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

Reference to a schema node’s type (for example, when you declare a function parameter or a variable) implies using keyword typeof.

List types

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

list<item_type>

Declare an unmodifiable list by using following syntax:

unmodifiable list<item_type>

An item cannot be added, removed, or replaced if the list is unmodifiable. An item of the list might or might not be modifiable, depending on its type.

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 decimal type. Convert the index value to an integer by dropping the fractional part.

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).

Mutable and immutable types

You cannot modify an immutable complex or list object.

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

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 a return value generates an error at compile time.

Keyword typeof

Use the keyword typeof to specify a type depending on a field or variable. You can use this keyword preceded by the keyword mutable or immutable.

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

typeof record.OfficeAddress

By default, the type of a record field is always immutable. You can specify a mutable type using following syntax:

mutable 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[*]

You can use the keyword 'mutable' on types that have a mutable form:

mutable typeof record.Addresses[*]

If the type does not have a mutable form, then the keyword is simply ignored. This is the case for simple types.

===

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:

CharacterEscape 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

ValueSyntax

O’Harra

'O\'Harra'

Noël

'No\u00EBl'

été

'\u00e9\u00E9'

Note

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

Decimal or integer literal

The following decimal formats are supported:

FormatExamples

Integer

546

-67

Floating point

54.987

-433.876

0.00054

-0.0032

Exponent notation

34.654e-5

-45E+65

1.543e23

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:

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 LevelOperatorOperand typeResult typeAssociativity

9

[](access to an element of a list)

. (access to fields)

() (parenthesis)

List index must be a decimal.

Can be any type.

Left to right.

8

not

Boolean

Boolean

7

*

/

Decimal

Decimal

Left to right.

6

+

-

Decimal

Decimal

Left to right.

5

| (string concatenation)

String

String

Left to right.

4

<

<=

>

>=

String, Decimal, timestamp, date, time (3).

Boolean

Not associative.

3

=

<>

String, decimal, 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). 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. Variables of ìnt' 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:

OperatorOperand typeDescription

:=

Can be used with any type.

Standard assignment.

|=

string

a |= b is equivalent to a := a | b

+=

decimal

a += b is equivalent to a := a + b

-=

decimal

a -= b is equivalent to a := a - b

*=

decimal

a *= b is equivalent to a := a * b

/=

decimal

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 := 30;                  // Variable of type decimal.
  var address := record.address;  // Variable of an immutable complex.

  const MAX_AGE := 100;              // Decimal constant.
  const DEFAULT_COUNTRY := 'France'; // String constant.
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 : decimal;
  age := 30;
  var address : typeof record.address;
  address := record.address;

  const MAX_AGE : decimal := 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 age : 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 := record.OfficeAddress;

// Declaration using typeof notation.
var address2 : typeof 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 := 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 := 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 expression. Only the integer part of the expression will be used.

Predefined variables

Introduction

Predefined variables allow access to the context. These contextual variables are constant and of an immutable complex type.

Record

The predefined variable record is available only if the current script is for a function field of a table. It allows read-only access to the current record.

Its fields are defined by the current EBX® schema.

Example:

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

Root

The predefined variable root is available only if the current script is for a function instance field of a dataset. It provides read-only access to the instance fields of current datasets.

Its fields are defined by the current EBX® schema.

Example:

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

Dataspace

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

This variable has following fields:

NameTypeDescription

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.

id

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 dataspace.isSnapshot then
    return 'Snapshot: ' |  dataspace.name;
  else
    return 'Branch: ' |  dataspace.name;
end

Dataset

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

This variable has the following fields:

NameTypeDescription

name

string

The name of the dataset

Example:

// Returns the current dataset name.
export function getValue(): string
begin
  return dataset.name;
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

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

Exported function

An exported function can be called directly by EBX®.

Only one exported function is allowed per script. Its signature (name, and return type) depends on the type of the function field.

For the field of a record, definition must be similar to:

export function getValue(): typeof record.<path>
begin
	...
end

For the field of a dataset, definition must be similar to:

export function getValue(): typeof root.<path>
begin
	...
end

When a function field is first edited, EBX® provides an initial script with the correct definition of the exported function.

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

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.

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 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

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 record.Cities
begin
  return list.of('Paris', 'Bruxelles', 'Berlin');
end

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

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 is created using the DMA, an initial script is created.

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

export function getValue(): string
begin
	return 'A string value';
end

The exported function signature (name, and return type) depends on the field’s type and should not be changed.

Documentation > Developer Guide > EBX® Scripting