Script Language An Introduction

Introduction

This section gives a short introduction to the Expression language. Examples for Expressions and Scripts (which are Expressions spanning more than one line) will be given. The intention is to give a first impression of the language's syntax, of where expressions can be used, and to list some of the language's properties.

All concepts will be explained in detail in the next two sections. Section Language Core will explain the base concepts. The chapter Advanced Expressions will explain more advanced features.

Note: For more information, see:

What is the Expression Language?

The Expression language is a lightweight, interpreted, server-side scripting language which allows you to define and manipulate runtime data.

An Expression can consist of a single expression such as referencing the street property of an address or calculating the age of a person, or of longer, more sophisticated calculations and manipulations. With the correct syntax, Expressions may span more than one line. Expressions spanning several lines are usually called Scripts. Expressions that only point to a Data Class property (such as $person.name) are called bindings.

For more complicated actions, the Expression language provides flow control statements that can be used to take different paths through a Script and make decisions while a Script runs. Functions can be defined to simplify the readability of complex pieces of code, and structured Data Classes can be defined to encapsulate and structure runtime data values or Functions.

Expressions are interpreted on the server. Thus, no cumbersome compiling and linking must be done. You can execute Expressions immediately to see their effects and results on the system, and you can rapidly refine your code. Some of language features are:

  • Simple expression syntax
  • Simple data definition and usage
  • Support for object oriented Data Classes
  • Support for Script Function definition
  • Simple typed variable definition

Where can Expressions be used?

Expressions are used in many places. Some of the most prominent places are:

In binding Data Class Properties to fields in Screens

Copy

$person.name

In the expression above we find the variable $person. The variable points to a Data Class which defines a person's attributes. One of the attributes is called name which contains the person's name. The binding expression is a simple way of telling the system that the input in a text field must be stored and fetched from the given position in the Data Class.

In expressions for field definitions in Output Forms

Copy
$person.address.fullAddress()

In the Output Form, the field with the above expression will contain the full address of the referenced person. The attribute address references a Data Class holding attributes related to addresses. The Class Function full Address defined for Data Classes of the type Address is called to assemble the full address text.

In condition definitions used in Rules

Copy
AGE($person.birthday) > 21

This Expression calculates the age of a person and compares the resulting number of years to the number 21. If the number of years is higher than 21 the condition has the result true, other wise the condition returns false.

In Processes to decide about the actions to be taken:

Copy
$action == 'calculate interest'

If the variable action has the value calculate interest the Screen flow path associated with the given Expression will be followed.

The Ubiquitous Hello World

The following Script shows how to implement a hello world application in Expression language syntax.

Copy
Script
Local String $text;
$text := 'Hello World';
STDOUT($text);
End

A Script is a listing which defines a task that needs to do more than we can accomplish with a single line Expression. A Script always start with the keyword Script and ends with the keyword End. The statements (or lines, since each statement occupies one line) in a script must be terminated with a semicolon. Exceptions to this rule are the Script and End keywords, which do not need to be followed by a semicolon.

Shortcuts

The expression language editor supports the following shortcuts:

  • CTRL-A, select all
  • CTRL-S, save the expression. (This is available when editing script functions)
  • CTRL-W, close the expression editor
  • ALT-F, format expression
  • CTRL-ALT-P, print the expression

Language Core

Character Set

The Expression language supports the UTF-8 character set for text literals. If no UTF-8 support is available, characters can be entered using the escaped notation, i.e. \u0041 is the UTF-8 escape sequence for the character 'A'. UTF-8 escape sequences must be given using hex characters ('0'..'9'|'a'..'f'|'A'..'F').

All script content which is not part of a text literal must be given in ASCII encoding.

Upper- and Lowercase

The script language is case sensitive. All reserved words start with an upper-case letter. Internal functions are usually written with all letters in upper-case.

Whitespace and Line endings

White spaces (space " ", tab "\t", newline "\n" and carriage return "\r") are default token separators. These characters are ignored when an expression or a script is parsed.

Expressions do not have a semicolon at the end of a line:

Copy
$person.name

is a syntactically correct Expression, while

Copy
$person.name;

results in a syntax error.

Scripts need a semicolon at the end of a statement. Usually we write one statement on each line:

Copy
Script
Local String $s;
$s := 'hello';
End

The example above is a syntactically correct script, while the following example results in a syntax error:

Copy
  Script
      Local $s String
      $s:='hello'
  End

Comments

The Expression language supports three types of comments:

Multi line comment

/* some text */

Text starting with /* and ending with */ can be used as a comment in expressions and scripts. In scripts this type of comment can take more than one line:

Copy
 Script
   /* 
        this is a 
        multiline 
        comment 
      */
   String $s := NewObject(String);
   $s := 'hello';
End

Multi line comment for Script Function Business Object or Data Class method

@param

@return

@example

When a multi line comment precedes a script function in the Script Function Business Object or a Data Class method, some lines of this comment are given special handling by the Interactive Script Editor (ISE). Lines starting with @param, @return, @example, in fact, are displayed in the left-side panel as illustrated in the following picture:

MultiLineComment.png

Single line comment

// some text

Text starting with // defines the rest of the line as a comment. This type of comment can only be used in Scripts. The following example contains syntactically correct single line comments:

Copy
Script
 // this is the comment
  STDOUT('hello');
 // some other comment
End

Literals

A literal is a data value which can be directly put into an expression or a script.

This is an "Integer" (number) literal:

Copy
12

This is a "Float" (floating point number) literal:

Copy
1.2

The following two examples are "String" literals (texts):

Copy
hello
Copy
goodbye

The "null" literal is a literal whose meaning is undefined:

Copy
null

And the "Nothing" literal is an undefined class:

Copy
nothing

"true" and "false" are literals whose value is true or false, respectively:

Copy
true
Copy
false

All reserved words are literals.

Identifier

An identifier is a simple name, i.e. a class name, a function name or a variable name. Identifiers can not contain any white space or any special characters.

The pattern for an identifier is:

Copy
('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')* 

Variables

Expression language variables are identifiers which start with a $ character. Variables have a variable type and a collection type. The variable type is always the name of a Data Class. The collection type is either "None" for single elements, "Indexed" for a list of numbered elements, or "Named" for a list of named elements.

The following example shows a few examples of what can be stored in variables, from single, primitive types to indexed and named elements, to full-featured Data Classes. The first three will be explained in this chapter, while Data Classes will be explained in the Advanced Expressions section.

Examples of structures that can be stored in variables:

variables.bmp

Collection Type None

Variables with collection type "None" are single element variables.

Copy
// Define the single element variable
    Local String $s;
// Create a single element and assign it to the variable
$s := NEW('String');

Collection Type Indexed

Variables with collection type "Indexed" specify a list of variables of the given variable type. Elements in the collection can be accessed using an index starting with 1 and ending with $array.size().

Copy
// Define an indexed collection variable (also called an "Array")
Local Indexed Person $list;
// Create the collection and assign it to the variable
$list := NewIndexed('Person');
// Create a new element and add it to the collection at index 1
$list[1] := NEW('Person');

Collection Type Named

Variables with collection type "Named" specify a map of variables of the given variable type. Elements in the map can be accessed using a name (sometimes also called a "key") which must be unique in the specific "map."

Copy
// define a named collection (also called a "Map")
Local Named Address $map;
    // Create the collection and assign it to the variable
    $map := NewNamed('Address');
    // Create a new element and add it to the collection with the name 'home'
$map['home'] := NEW('Address');

Operations

This chapter explains some basic operations and provides examples of these operations.

A variable of a given type can always be substituted with code evaluating to a value of the same type. Given the following example:

$a && $b

where $a and $b are boolean variables, the following example is valid, too:

$a && ($c || $d)

where ($c || $d) evaluates to a boolean value and can thus take the place of $b.

Boolean Operations

The result of a boolean operation is always either true or false. In the examples given below, the variables $a and $b are boolean variables, meaning that they contain either true or false.

And

&&, and, AND

Calculates the boolean AND function. Returns true if both arguments are true, false otherwise. The following examples are all semantically equivalent, i.e. they do the same thing.

Copy
$a && $b 

$a and $b 

AND($a,$b)

Or

||, or, OR

Calculates the boolean OR function. Returns true if at least one of the arguments are true, false otherwise. The following examples are semantically equivalent.

Copy
$a || $b 

$a or $b 

OR($a,$b)

Not

!, not, NOT

Calculates the boolean negation. Returns true if the argument evaluates to false and false if it evaluates to true. The following examples are semantically equivalent.

Copy
!$a 

not $a 

NOT($a)

Equality and Relational Operations

Equality operations work with arbitrary types (numbers, texts or data classes). Relation operations work exclusively with numbers. The result of equality and relation operations is always either true or false. In the examples for this section, the variables $a and $b are variables of identical but arbitrary types.

Equal

==, EQUAL

Returns true if the two arguments are equal, and false if they are different.

Copy
$a == $b 

EQUAL($a,$b)

Not Equal

!=, NOTEQUAL

Returns true if the two arguments are different, and false if they are equal.

Copy
$a!=$b 

NOTEQUAL($a,$b)

Less

<, LESS

Returns true if the first argument is smaller than the second, false if they are equal or if the second argument is smaller.

Copy
$a<$b 

LESS($a,$b)

Less or Equal

<=, LESSEQUAL

Returns true if the first argument is smaller than or equal to the second, false if the second argument is smaller.

Copy
$a<=$b 

LESSEQUAL($a,$b)

Greater

\>, GREATER

Returns true if the first argument is greater than the second, false if they are equal or if the second argument is greater.

Copy
$a>$b 

GREATER($a,$b)

Greater or Equal

\>=, GREATEREQUAL

Returns true if the first argument is greater than or equal to the second, false if the second argument is greater.

Copy
$a>=$b 

GREATEREQUAL($a,$b)

Arithmetic Operations

Arithmetic operations calculate numeric values based on numeric operands. In this section the variables $a and $b contain numerical values.

Addition

+, ADD

Returns the sum of the two arguments.

Copy
$a + $b 

ADD($a,$b)

Subtraction

-, SUB

Subtracts the second argument from the first and returns the result.

Copy
$a - $b 

SUB($a,$b)

Multiplication

*, MUL

Returns the product of the two arguments.

Copy
$a * $b 

MUL($a,$b)

Division

/, DIV

Returns the quotient of the two arguments where $a is the dividend and $b the divisor.

Copy
$a / $b

DIV($a,$b)

Modulo (Division Rest)

%, mod, MOD

Returns the division rest of the integer division (e.g. returns 1 for MOD(5,2) because the integer division evaluates to 2).

Copy
$a % $b 

$a mod $b 

MOD($a,$b)

Integer Division

div, DIVV

Returns the integer division result.

Copy
$a div $b 

DIVV($a,$b)

Unary Minus and Unary Plus

-, +

The unary minus function multiplies the value with -1, while unary plus multiplies the value with 1 (which usually doesn't do anything at all).

Copy
-12, +3.4

Reserved Words

Reserved words are keywords of the script language. These words may not be used as variable-, data-class- or function names.

Reserved Words: Begin, Break, Class, ClassFunction, Condition, DataClass, DataType, Do, Else, ElseIf, End, extends, Facet, For, From, Function, Global, If, Indexed, JavaType, Local, MetaData, Method, Named, None, PrimitiveType, Property, Return, Script, Step, Structure, Then, To, Type, While

Advanced Expressions

Functions

A function (or subroutine) is a portion of code within a larger program, which performs a specific task and is relatively independent from the remaining code. Breaking up a program into subroutines brings many advantages, including:

  • Reducing the duplication of code in a program by replicating useful functionality, such as mathematical functions
  • Enabling reuse of code across multiple programs
  • Decomposing complex problems into simpler pieces (this improves maintainability and ease of extension)
  • Improving readability (use descriptive function names)
  • Hiding or regulating part of the program

The Expression Language has a collection of built-in functions which are listed in the Internal Function Reference appendix.

From the function user point of view a function consists of a name, a list of arguments and a return value. The user calls the function and receives a result, which is a value that can be further used in the Expression.

The Expression Language allows a user to define new functions. The definition of a function uses the following pattern:

Copy
'Function' functionName '(' functionArgumentList ')' ':' functionReturnType 'Begin' expressions
            'End'

The following Script defines a function that calculates the mean value of the number list given as the single argument of the function.

Copy
Script

 // define the function
 Function Mean(Indexed Double $data) : Double Begin 
    If $data.size() == 0 Then 
       Return 0.0;
    Else
       Double $sum  := 0;
       For Integer $i := 1 Condition $i <= $data.size() Step $i := $i + 1 Do 
          $sum := $sum + $data[$i];
       End
       Return $sum / $data.size();
    End
 End

 // prepare 100 random values each between 0 and 100
 Indexed Double $values;
 $values := NewIndexed('Double');
 For Integer $i := 1 Condition $i <= 100 Step $i := $i + 1 Do 
    $values[$i] := RANDOM() * 100;
 End
 // use the function
 Return Mean($values);

End

A script function can be called using the URL pattern /script/{FUNCTION_NAME}(?{PARAMETER}={VALUE}(&{PARAMETER}={VALUE})*)?

For example: the function Echo which takes one parameter can be called using the URL /script/Echo?text=hello.

Primitive Types

Primitive types are Java-backed types provided by Appway which are designed to be read-only. Primitive types cannot be edited in Appway Studio and all of them are passed by values. As an example, the primitive type that holds text of any kind is called "String." Another primitive type is "Integer", which is used for storing full numbers. When defining variables, we can use primitive types directly.

Copy
Script
// define a variable which will hold a text
Local String $myText;
 // assign a value to the variable
$myText := 'This is indeed a surprise. We did not look for you till Monday afternoon.';
End

The following is a table of primitive types known to the Expression Language:

Type Description
Any Any kind of value
Base64Binary Base 64 encoded binary data
Boolean Truth values true and false
Date A date and a time (time stamp)
Double A 64-bit floating point number
Float A 32-bit floating point number
Integer A 32-bit integer number
Long A 64-bit integer number
Real A 64-bit floating point number
Short A 16-bit integer number
String A 64 KB text

 

There are also further example of Primitive Types covering fit for purpose needs like Money, Records or RestClient. You will find more info in the respective sections.

Primitive types can be defined using a Script. A primitive type definition associates a Java native type (e.g. java.lang.String ) with a name known in the script language (e.g. String ). If a Script containing a primitive type definition is executed, the new definition will be visible to all scripts executed afterwards. Behind the scene, the definition is used for the creation of a Data Type business object which is part of the configuration. If we need to redefine a primitive type or a data class which already exists we must first drop the existing definition in order for the new definition to be effective. Dropping a definition is done with the Function DROP TYPE:

Copy
Script
DROPTYPE('String');
 ...
End    

The next script shows the definition of the String primitive type.

Copy
Script
Type String Begin
 JavaType java.lang.String;
End
End

If we want to make sure that our definition is used even if the same type has already been defined we first drop the primitive type or data class and redefine it afterwards.

Copy
Script
 DROPTYPE('String');
 Type String Begin
 JavaType java.lang.String;
End
End

Data Classes

A data class defines a set of data which belongs together. For example, we could define a data class which handles Addresses. Let's call this class Address. An address consists of the properties street, number, zip, city and country. More precisely, the property street of the class Address is a text which holds the name of the street the address should refer to. The Expression Language gives the user the possibility to define such classes using the Expression Language. In the listing below we find the Script which defines the Address data class:

Copy
Script
DataClass Address Begin
Property[street, String, None];
Property[number, Integer, None];
Property[zip, String, None];
Property[city, String, None];
Property[country, String, None];
End
End

Next to the definition of properties we can also define Class Functions, Facets and meta data. The order in which these definitions can be given is as follows:

Copy
DataClass
Properties ...
ClassFunction...
Facets ...
MetaData ..
End

Meta Data

Scripts allows the user to define meta data for data classes. Meta data can be used by SDK programmers to store additional data for Business Objects. It is defined using the following pattern:

Copy
'MetaData[' nameText ',' valueText ']'

The Meta data is stored as key-value pairs in the definition of the data class. The studio tools do not currently expose Meta data to the user.

Class Functions

Class Functions (sometimes also called "Methods") are functions which are tied closely to data classes. We can think of Class Functions as special data class properties which calculate some value related to the data class. To use a Class Function, we need a data class instance, and we must know the number and kinds of arguments needed by the Class Function.

Let's say we have a Person class with a first name and a last name property. The definition looks as follows:

Copy
DataClass Person Begin
Property[firstname, String, None];
Property[lastname, String, None];
End

If we want to output the full name we may use the following expression:

Copy
CONCAT($myPerson.firstname, ' ', $myPerson.lastname)

One drawback of the Expression above is that it needs a lot of writing.

Another drawback is that it is not resistant against changes. Say we would like to change the order in which the full name is assembled. Instead of "first name last name," we would like "last name, first name." This would means we'd have to change the Expression in every location we ever used it, which would involve a lot of manual work.

A better solution is to define a Class Function that will output the full name. We can then use this Class Function whenever we need the full name. This way we can easily change the order of properties in only one place if needed and we don't have to worry about every location we used a person's full name.

Copy
DataClass Person Begin
Property[firstname, String, None];
Property[lastname, String, None];
Function fullname() : String Begin 
Return CONCAT($this.firstname, ' ', $this.lastname);
End
End

We can now get the full name using the following Expression:

Copy
$myPerson.fullname()

When we define a Class Function, we use the implicitly defined variable $this to refer to the current data class instance the Class Function is bound to.

Script Flow Control

Scripts have five flow control statements:

  • If
  • While
  • For
  • Return
  • Break

Each of these control statements are discussed in more detail in the sections below.

If

Copy
If $x>0 Then
...
Else
 ...
End

When the interpreter finds an If, it expects a Boolean condition - for example "$x > 0" which means "the variable x contains a number that is greater than zero" - and evaluates that condition. If the condition is true, the statement block following the "Then" is executed, and the statements in the Else block are skipped. Otherwise, the execution continues in the following block - either in the Else block (which is optional), or if there is no Else block, then after End

The If statement will execute either the Then block or the Else block, but never both.

By using Else If it is possible to combine several conditions. Only the statements following the first true condition will be executed. All other statements will be ignored. The Else statements will be executed if none of the Else If conditions are true.

Copy
If $x>100 Then
...
ElseIf $x>50 Then
...
ElseIf $x>0 Then
...
Else
...
End

While

Copy
While $x < 10 Do
...
$x:=$x+1;
End

When the interpreter finds a While it expects a Boolean condition - for example "$x < 10" - and evaluates that condition. If the condition is true the statement block following the Do is executed. After executing the statement block the condition is evaluated again and if it is true the statement block is executed again. This keeps on going until the condition is false and the control returns to the point after the End.

In other words, the statements between Do and End are evaluated again and again until the While condition evaluates to false.

You must make sure that the While condition will eventually evaluate to false If it always remains true, the Script will never finish (an "infinite loop").

For

Copy
For $x:=1 Condition $x < 10 Step $x:=$x+1 Do
...
End
            

When the interpreter finds a For it expects an initial statement, a condition and a step statement. The initial statement is executed when the interpreter finds the For If the condition is true, the statement block following the Do is executed. After the statement block has been executed, the step statement is executed, followed again by the condition statement. The loop of execution (statement block, step statement, condition) is being processed until the condition is false, at which time the control returns to the point after the End.

You must make sure that the For condition will eventually evaluate to false If it always remains true, the Script will never finish (an "infinite loop").

Return

Copy
Script
...
Return Mean($value);
End

When the interpreter finds a Return statement it expects an expression. The interpreter evaluates the expression and leaves the current scope, returning the evaluated value to the parent scope. Return can be used in functions that must return a value or in Scripts to leave the Script and return a value.

Break

When the interpreter finds a Break, it terminates the surrounding loop (ignoring its condition) and resumes control after the End statement of the respective loop instruction: {code:[language:java,numbering:false]} Script While ... Do Break; End For ... Do Break; End End

When a Break is not surrounded by a loop, control is returned to the caller: {code:[language:java,numbering:false]} Script ... Break; ... End

Example: Function A calls function B which then calls a Break. In this case, the execution of function A continues on the next line after the invocation of B.

Obsolete Expressions Keywords

During Expression Language's development, some keywords were changed. To retain backwards compatibility, the old keywords still work, but you should not use them - they are considered deprecated.

Don't be confused if you see old "legacy" Expressions using different keywords. Here are some of the keywords that were deprecated, and what you should use instead:

Obsolete, Deprecated Keywords Replacement Keywords
Type PrimitiveType
Structure DataClass
Method ClassFunction

 

Some Function names have also been deprecated:

Obsolete, Deprecated Function Names Replacement Function Names
NEWARRAY NewIndexed
NEWMAP NewNamed
TODATAARRAY TOINDEXED ToIndexed
DROPSTRUCTURE DROPTYPE

Expression Language EBNF

What's EBNF? The Extended Backus-Naur Form - or, in short, EBNF - is a way to describe the rules used when writing in a computer programming language. The rules written down in this chapter explain how to write Expressions. Basically, EBNF breaks code down into its pieces and sub-pieces.

For example, this is the first rule in the Expression Language EBNF:

Copy
script: 
'Script'  
    ( typeDef | structureDef | functionDef | statement)*   
'End'

In plain English, this means that part of the code which we name "script" consists of the word Script, followed one or several of the parts named typeDef, structureDef, functionDef or statement. Finally, "script" ends with the word End.

As you may already have figured out, brackets are used to group things. The horizontal line | is used as "or", and the star * means "the previous thing can be repeated or ignored". A similar mark to the star is the question mark? Which means -the previous thing can be repeated, but must be used at least once?"

Here are a few examples:

EBNF Examples

Pattern Description
'a' | 'b' Either 'a' or 'b'
('a' | 'b')* Repeating 'a' or 'b' as many times as you want. Even an empty string would be okay, as would be 'a', 'b', 'aba', 'bbb', or other combinations.
('a' | 'b')? Similar to the rule above, but the question mark means that at least one repetition is required. So the empty string would not be okay, but 'a', 'b', 'abbbaba', or 'bbbb' would be.

 

The rules can be combined, so you could write something like:

Copy
('a' | 'b') 'z'*

This would mean either 'a' or 'b' followed by as many 'z' as you like.

With those rules in mind, the following section should help you understand how Expressions are written.

Expression Language EBNF

Copy
script: 
'Script'  
    ( typeDef | structureDef | functionDef | statement)*   
'End'

typeDef: 
'PrimitiveType' className ( 'extends' className )? 
    'JavaType' fullClassName ';'  
    (facetDef ';' | metadataDef ';')* 
'End'

structureDef := 
'DataClass' className  ( 'extends' IDENTIFIER )? 
    ( propertyDef ';' | methodDef | facetDef ';' | metadataDef ';')* 
'End'

functionDef:
'Function' functionName '(' argumentList? ')' ':' ('Indexed' | 'Named' | 'None')? className
'Begin'
    statement*
'End'

methodDef:
'ClassFunction' functionName '(' argumentList? ')' ':' ('Indexed' | 'Named' | 'None')? className
'Begin'
    statement*
'End'

propertyDef:
'Property' '[' propertyName ',' className ',' ('Indexed | 'Named' | 'None') (',' commentText)? ']'

facetDef:
'Facet' '[' className ':' propertyName ',' ('comment'|'default'|'constraint') ',' expressionText ',' commentText ']'

metadataDef:
'MetaData' '[' nameText ',' valueText ']'

argumentList:
('Indexed | 'Named' | 'None')? className variableName (',' ('Indexed | 'Named' | 'None')? className variableName)

statement:
variableDef ';' | ifStatement | forStatement | whileStatement | returnStatement ';' | breakStatement ';' | expression ';'

variableDef:
('Local' | 'Global') variableName className ('Indexed | 'Named' | 'None')?

ifStatement:
'If' expression 'Then'
    statement*
('ElseIf' expression 'Then'
    statement*
)*
('Else'
    statement*
)
'End'

forStatement:
'For' expression 'Condition' expression 'Step' expression 'Do'
    statement*
'End'

whileStatement:
'While' expression 'Do'
    statement*
'End'

returnStatement:
'Return' expression

breakStatement: 'Break'

///////////////////////////////////////////////////////////////////////

constantValue:
NUM_INT | NUM_FLOAT | STRING | 'true' | 'false' | 'null' | 'void'

primitiveElement:
constantValue | valuePath | fullClassName | '(' expression ')'

booleanNegation:=
('!' | 'not')* primitiveElement

signExpression:=
'-' booleanNegation | '+' booleanNegation | booleanNegation

multiplyingExpression:
signExpression ( ('*' | '/' | '%' | 'mod' | 'div') signExpression )*

addingExpression: 
multiplyingExpression ( ('+' | '-') multiplyingExpression )*

relationalExpression:
addingExpression ( ('==' | '!=' | '<>' | '>' | '>=' | '<' | '<=' | ':=') addingExpression )*

expression:
relationalExpression ( ('and' | 'or' | 'isa' | 'hasa') relationalExpression )*

expressionList:
expression (',' expression)*

/////////////////////////////////////////////////////////////////////////

valuePath:=
(variableName | functionCall) ( '.'  IDENTIFIER | '[' expression ']' | '(' expressionList? ')' | '{' expression '}' )* 

/////////////////////////////////////////////////////////////////////////

functionCall:
functionName '(' expressionList? ')'

/////////////////////////////////////////////////////////////////////////

variableName: '$' IDENTIFIER

functionName: IDENTIFIER

className: IDENTIFIER

propertyName: IDENTIFIER

fullClassName: IDENTIFIER ('.' IDENTIFIER)*

expressionText: STRING

commentText: STRING

nameText: STRING

valueText: STRING

/////////////////////////////////////////////////////////////////////////

NUM_INT: '0'..'9' ('0'..'9')*

NUM_FLOAT: ('0'..'9')+ '.' ( ('0'..'9')+ )?

IDENTIFIER:
('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*

STRING:
 '\"'
 ( ~('\"'|'\\')
 | ESCAPE_SEQUENCE
 )*
 '\"'
 |
 '\''
 ( ~('\''|'\\')
 | ESCAPE_SEQUENCE
 )*
 '\''
  ;

COMMENT:
 '//' (~('\n'|'\r'))*  | '/*' (.)* '*/' 

ESCAPE_SEQUENCE
'\\' 'b'
| '\\' 't'
| '\\' 'n'
| '\\' 'f'
| '\\' 'r'
| '\\' '\"'
| '\\' '\''
| '\\' '\\'
| UNICODE_CHAR

UNICODE_CHAR: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT

HEX_DIGIT: '0'..'9'|'a'..'f'|'A'..'F'

WS: (' '|'\n'|'\r'|'\t')+