Skip to content

Latest commit

 

History

History
1470 lines (1080 loc) · 40.2 KB

language-syntax.adoc

File metadata and controls

1470 lines (1080 loc) · 40.2 KB

Built-in Types

The following sections describe the standard types that are built into the interpreter.

NXSL is loose typed programming language. The system will automatically determine each variable type, assign a certain type to a variable and convert a variable type from one to another, if necessary. For example, a result for 3 + "4" will be 7, because the system will automatically convert "4" string into an integer. In case if the system is not able to automatically convert a line into an appropriate integer, the operation will result in a runtime error.

NXSL supports the following variable types:

  • integer (32 bit),

  • unsigned integer (32 bit),

  • integer (64 bit), unsigned integer (64 bit),

  • floating-point number,

  • string,

  • boolean,

  • array,

  • hash map,

  • object.

In addition to that, NXSL also supports a special variable type – NULL. This value represents a variable with no value. NULL is the only possible value of type NULL. An attempt to perform any type of arithmetical or string operations with NULL variable will result in system runtime error.

It is possible to manually convert variable to a certain type, using a special function, named depending on the variable type. For example, string(4). That way it is also possible to convert NULL type variables. Therefore, to avoid runtime errors while processing NULL type variables, it is advised to use manual conversion.

NXSL does not require setting variable type beforehand. The only exception to this is arrays. In case if an array is required, operator array defines its subsequent variables as arrays. Accessing variable which was not previously assigned will return NULL value.

Although NXSL has object type variables, it is not an object-oriented language. It is not possible to define classes or create objects at script level – only in extensions written in C++. Object type variables are used to return information about complex NetXMS objects, like nodes or events, in a convenient way. Please note that assigning object type variables actually holds reference to an object, so assigning object value to another variable does not duplicate actual object, but just copy reference to it.

To get a human-readable representation of a variable or expression type for debugging, use the typeof() function, and to get a class name for object type variables, use classof() function.

String literals

A string literal is where you specify the contents of a string in a program. There are few different string literal types:

  • "string" - string literal where some characters preceded by a backslash have special meaning. If you need to insert double quote character you should prepend it with backslash.

  • 'string' - string literal with backslash being an ordinary character without special meaning. You cannot insert single quote character into such string literal.

  • """ Multi

    line

    string""" - string literal that can span multiple lines with backslash being an ordinary character.

The following character combinations are supported in "string" literals:

  • \b - produces Backspace character

  • \t - produces Horizontal tab character

  • \n - produces Newline character

  • \r - produces Carriage return character

  • '\xhh' - produces a character with code hh (where hh is two-digit hex number)

  • '\uhhhh' - produces a character with code hhhh (where hhhh is four-digit hex number). E.g. \u2026 produces Greek small letter Mu (μ)

  • \\ - produces Backslash character (\)

Truth Value Testing

Any variable can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:

  • false

  • NULL

  • zero of any numeric type, for example, 0, 0.0, 0j.

All other values are considered true — so objects of various types and arrays are always true.

Operations and built-in functions that have a Boolean result return boolean type. However, there’s an exception to this due to optimizations related to short-circuit evaluation of or and and operators. For or, if first operand not boolean type, but it’s value is considered true, that operand will be returned as result. For and, if first operand is considered false, that operand will be returned as result. This will not cause issues in subsequent logical operations, as returned value will be correctly considered as true or false; But when printing the result, it will not be converted to Boolean, so conversion using boolean() function might be needed.

For example:

a = [1,2,3];
b = true;
c = a or b;
println(c);  // will print "[1, 2, 3]" because first operand was returned by or operation
if (c) println("TRUE");  // will print "TRUE" as the array contained in c is considered as true
a = 0;
b = true;
c = a and b;
println(c); // will print "0" because first operand was returned by and operation
println(boolean(c));  // will print "false"

Variables

Variables in NXSL behave the same way as variables in most popular programming languages (C, C++, etc.) do, but in NXSL you don’t have to declare variables before you use them.

Scope of a variable can be either global (visible in any function in the script) or local (visible only in the function within which it was defined). Any variable is by default limited to the local function scope. Variable can be declared global using global operator.

For example:

x = 1;
myFunction();

function myFunction()
{
    println("x * 2 = " .. x * 2);
}

This script will cause run time error Error 5 in line 6: Invalid operation with NULL value, because variable x is local (in implicit main function) and is not visible in function myFunction. The following script will produce expected result (prints x=1):

global x = 1;
myFunction();

function myFunction()
{
    println("x * 2 = " .. x * 2);
}

Function Declaration

A function is a named code block that is generally intended to process specified input values into an output value, although this is not always the case. For example, the trace() function takes variables and static text and prints the values into server log. Like many languages, NXSL provides for user-defined functions. These may be located anywhere in the main program or loaded in from other scripts via the import keywords.

To define a function, you can use the following form:

function NAME ( ARGUMENTS ) BLOCK

where NAME is any valid identifier, ARGUMENTS is optional list of argument names, and BLOCK is code block.

To call a function you would use the following form:

NAME ( LIST )

where NAME is identifier used in function definition, and LIST is an optional list of expressions passed as function arguments.

To give a quick example of a simple subroutine:

function message()
{
    println("Hello!");
}

Function Arguments

The first argument you pass to the function is available within the function as $1, the second argument is $2, and so on. For example, this simple function adds two numbers and prints the result:

function add()
{
    result = $1 + $2;
    println("The result was: " .. result);
}

To call the subroutine and get a result:

add(1, 2);

If you want named arguments, list of aliases for $1, $2, etc. can be provided in function declaration inside the brackets:

function add(numberA, numberB)
{
    result = numberA + numberB;
    println("The result was: " .. result);
}

If parameter was not provided at function call, value of appropriate variable will be NULL.

Another option to name parameters is to provide named parameters inside of function. In this case leave braces of function empty and add names to function call. In this case parameters will be available in function as $parameterName.

Example:

func(param2: "text2", param1: "text1");
return 0;

function func()
{
    println($param1); //Will print "text1"
    println($param2); //Will print "text2"
}

The arguments received by script when it was launched are available in a global variable $ARGS, which is an array that contains all arguments. First argument available as $ARGS[1];

Return Values from a Function

You can return a value from a function using the return keyword:

function pct(value, total)
{
    return value / total * 100.0;
}

When called, return immediately terminates the current function and returns the value to the caller. If you don’t specify a value in return statement or function ends implicitly by reaching end of function’s block, then the return value is NULL.

Script entry point

NXSL handles script entry in 2 ways:

  • Explicit main() function

  • Implicit $main() function

If an explicitly defined main() exists, it will be called.

If an explicit main() doesn’t exist, an implicit $main() function will be created by the script interpreter and the script will enter at the $main() function.

The $main() function is constructed from code that is not a part of any other functions.

Calling library script functions

You can call functions from scripts that are stored in Script Library. One way is to use the import keyword accompanied by the name of the script:

import my_math_library;
println( add(1, 2) );

The other was is shown in this example:

println( my_math_library::add(1, 2) );

Strings

Strings are not objects, it’s a separate variable type. However, strings have methods and attributes described below

String attributes

isEmpty ⇒ Boolean

Returns "true" if string is empty or "false" otherwise.

Example
s = "";
println(s.isEmpty); // prints "true"
length ⇒ Integer

Returns number of characters in the string.

Example
s = "1234567890";
println(s.length); // prints '10'

String methods

compareTo(string) ⇒ Integer

Compares two strings lexicographically (alphabetical order). Returns -1 if the argument is a string lexicographically greater than this string, 1 if the argument is a string lexicographically less than this string or 0 if the argument is a string lexicographically equal to this string.

The is a difference to == comparison operator if strings contain numbers because values are converted to numberic type prior to comparison.

Example
println("a".compareTo("c")); // prints "-1"
println("c".compareTo("a")); // prints "1"
println("a".compareTo("aaa")); // prints "-1"
println("aaa".compareTo("a")); // prints "1"

println("100".compareTo("100.0")); // prints "-1"
println("100" == "100.0"); // prints true
compareToIgnoreCase(string) ⇒ Integer

Same as compareTo, but ignoring the case.

Example
println("aaa".compareToIgnoreCase("AAA")); // prints "0"
contains(string) ⇒ Boolean

Returns true if this string contains the argument string or false otherwise.

Example
println("aaa".contains("a")); // prints "true"
endsWith(string) ⇒ Boolean

Returns true if this string ends with the argument string or false otherwise.

Example
println("abc".endsWith("d")); // prints "false"
println("Just a sentence".endsWith("a sentence")); // prints "true"
equalsIgnoreCase(string) ⇒ Boolean

Returns true if argument string is equal to this string ignoring the case or false otherwise.

Example
println("abc".equalsIgnoreCase("ABC")); // prints "true"
indexOf(string) ⇒ Integer

Returns index of first occurence of argument string in this string or -1 if the argument is not a substring of the string.

Example
println("ABC-DEF-GHI".indexOf("-")); // prints "3"
println("ABC-DEF-GHI".indexOf("ABC")); // prints "0"
println("ABC-DEF-GHI".indexOf("JKL")); // prints "-1"
lastIndexOf(string) ⇒ Integer

Returns index of last occurence of argument string in this string or -1 if the argument is not a substring of the string.

Example
println("ABC-DEF-GHI".lastIndexOf("-")); // prints "7"
println("ABC-DEF-GHI".lastIndexOf("ABC")); // prints "0"
println("ABC-DEF-GHI".lastIndexOf("JKL")); // prints "-1"
left(numberOfCharacters, paddingCharacter) ⇒ String

Returns left numberOfCharacters of this string. If string length is less then numberOfCharacters, result will be padded on the right. paddingCharacter is optional, if not specified, space character will be used for padding.

Example
println("ABCDEFGHI".left(2)); // prints "AB"
println("123".left(5)); // prints "123  "
println("123".left(5, "_")); // prints "123__"
replace(whatToReplace, replaceBy) ⇒ String

Returns string where all occurencies of whatToReplace are replaced with replaceBy.

Example
println("A B C A D K L".replace("A", "<A>")); // prints "<A> B C <A> D K L"
right(numberOfCharacters, paddingCharacter) ⇒ String

Returns right numberOfCharacters of this string. If string length is less then numberOfCharacters, result will be padded on the left. paddingCharacter is optional, if not specified, space character will be used for padding.

Example
println("ABCDEFGHI".right(2)); // prints "HI"
println("123".right(5)); // prints "  123"
println("123".right(5, "_")); // prints "__123"
split(separator, trimWhitespace) ⇒ Array

Split string into array of strings at given separator.

trimWhitespace parameter is optional and is false by default. If set to true, space characters will be removed on both sides of strings produced by splitting. This parameter was added in version 5.1.4.

Example
println("ABC--DEF--GHI".split("--")); // prints "[ABC, DEF, GHI]"

s = "Alice, Bob,  Carol";
for (a : s.split(",")) println("|"..a.."|");
// Will print:
// |Alice |
// | Bob|
// |  Carol|

s = "Alice , Bob,  Carol";
for (a : s.split(",", true)) println("|"..a.."|");
// Will print:
// |Alice|
// |Bob|
// |Carol|
startsWith(string) ⇒ Boolean

Returns true if this string starts with the argument string or false otherwise.

Example
println("abc".startsWith("d")); // prints "false"
println("Just a sentence".startsWith("Just a")); // prints "true"
substring(position, numberOfCharacters) ⇒ String

Returns substring of this string starting from position and containing numberOfCharacters.

Example
println("ABCDEFGHIJK".substring(0,3)); // prints "ABC"
println("ABCDEFGHIJK".substring(6,3)); // prints "GHI"
println("ABCDEFGHIJK".substring(6,10)); // prints "GHIJK"
toLowerCase() ⇒ String

Converts this string to lowercase.

Example
println("ABC def".toLowerCase()); // prints "abc def"
toUpperCase() ⇒ String

Converts this string to uppercase.

Example
println("ABC def".toUpperCase()); // prints "ABC DEF"
trim() ⇒ String

Returns this string with whitespace from both sides removed.

Example
println("|" .. "  ABC  ".trim() .. "|") // prints "|ABC|"
trimLeft() ⇒ String

Returns this string with whitespace from left side removed.

Example
println("|" .. "  ABC  ".trimLeft() .. "|") // prints "|ABC   |"
trimRight() ⇒ String

Returns this string with whitespace from right side removed.

Example
println("|" .. "  ABC  ".trimRight() .. "|") // prints "|   ABC|"

Arrays

An array in NXSL is actually an ordered map. A map is a type that associates values to keys. This type is optimized for several different uses; it can be treated as an array, list (vector), hash table (an implementation of a map), dictionary, collection, stack, queue, and probably more. Nested arrays are supported, so elements of an array can be themselves arrays.

A key is 32-bit signed integer. When an array is created, its size is not specified and its map can have empty spots in it. For example, an array can have a element with a 0 key and an element with 4 key and no keys in-between. Attempting to access an array key which has not been defined is the same as accessing any other undefined variable: the result will be NULL.

Arrays are not objects, it’s a separate variable type. However, arrays have methods and attributes described below.

Array elements can be accessed using [index] operator. For example, to access element with index 3 of array a you should use

a[3];

To get subarray from the array use [a:b] operator. This operator returns subarray of an array from the element with index a inclusive till the element with index b exclusive. If a is omitted then subarray will be taken from the start of the array and if b is omitted then subarray will be taken till the end of the array.

Example:

a = [1, 2, 3, 4];
a2 = a[1:3]; // a2 will be [2, 3]
a3 = a[1:]; // a3 will be [2, 3, 4]

Array Initialization

New array can be created in two ways. First is to use array operator. This statement will create empty array and assign reference to it to variable a.

array a;

You can then assign values to the array. Please note arrays in NXSL are sparse, so indices can contain gaps.

array a;
a[1] = 1;
a[2] = 2;
a[260] = 260;
println(a[1]); // will print 1
println(a); // will print "[1, 2, 260]"

Second way is to use [] construct to create array already populated with values.

This statement will create array with four elements at positions 0, 1, 2, and 3, and assign reference to this array to variable a.

// no need to use "array a;" here, since we are creating it directly
a = [1, 2, 3, 4];

println(a[0]); // will actually print 1, since 1 is the 0th member
println(a); // will print "[1, 2, 3, 4]"

Array initialization can also be used directly in expressions, like this:

function f()
{
    return [2, "text", [1, 2, 3]];
}

In this example function f returns array of 3 elements - number, text, and another array of 3 numeric elements.

Array attributes

maxIndex ⇒ Integer

Returns highest index in the array.

Example
a = [1, 2, 3];
println(a.maxIndex); // prints '2'
println(a[a.maxIndex]); // prints '3'
minIndex ⇒ Integer

Returns lowest index in the array.

Example
a = [1, 2, 3];
println(a.minIndex); // prints '0'
size ⇒ Integer

Returns number of elements in the array.

Example
a = [1, 2, 3];
println(a.size); // prints '3'

Array methods

append(newElement) ⇒ Integer

Appends new element to the array. Returns highest index in the array - that’s index of the appended element.

Example
a = ["a","b","c"];
a.append("d");
println(a); // prints '[a, b, c, d]'
appendAll(anotherArray) ⇒ Integer

Appends elements of anotherArray to the array. Returns highest index in the array.

Example
a = [1,2];
b = [3,4];
a.appendAll(b);
println(a); // prints '[1, 2, 3, 4]'
indexOf(value) ⇒ Integer

Returns the index of first occurrence of the specified value or -1 if value is not found in the array.

Example
a = ["Normal", "Warning", "Minor", "Major", "Critical"];
println(a.indexOf("Major")); // Prints "3"
join(separator) ⇒ void

.Parameters

separator

String

Separator between array elements

Array operators

Table 1. Return
Example Name Result

…​

Spread operator

Used when all elements from an object or array need to be included in a new array or object or should be applied one-by-one in a function call’s arguments list.

Example
sub sum(i, j, k)
{
  return i + j + k;
}

data = %(10, 20, 30);
println(sum(...data));

a = %("one", "two");
b = %("new", ...a);

Concatenated string

Example
a = [1, 2, 3, 4];
println(a.join(";")); // will print "1;2;3;4"
println(a.join("; ")); // will print "1; 2; 3; 4"
insert(index, newElement) ⇒ void

Inserts new element to the array at index. Indexes of existing elements that had index greater or equal to index are incremented.

Example
array a;
a[0] = "aaa";
a[10] = "ccc";
a.insert(5, "bbb");
println(a[0]);  // prints "aaa"
println(a[5]);  // prints "bbb"
println(a[11]);  // prints "ccc" - because of the insert operation this element's index is now 11.
insertAll(index, anotherArray) ⇒ void

Inserts elements of anotherArray to the array at index. Indexes of existing elements that have index greater or equal to index are incremented.

Example
a = [1,2];
b = [3,4];
a.insertAll(1,b);
println(a); // prints '[1, 3, 4, 2]'
pop() ⇒ Element with highest index

Removes element with highest index from the array. Using push(value) and pop() methods it’s possible to use array as a stack. Or, using insert(0,value) and pop(), array will work as FIFO queue.

Example
a = []);
a.push("one");
a.push("two");
println(a.pop());
println(a.pop());
push(newElement) ⇒ Integer

Same as append().

remove(index) ⇒ void

Removes element at specified index. Indexes of elements that have index greater or equal to index are decremented.

Example
a = [1,2,3];
a.remove(0);
println(a);

Array conversion

String representation of array can be obtained by using string(array) function. The string representation consists of all array’s elements, enclosed in square brackets (“[]”). Adjacent elements are separated by the characters “, ” (a comma followed by a space).

Printed array is automatically converted to string.

a = [1, 2, 3, 4, 5, 6, 7];
println(a); // will print "[1, 2, 3, 4, 5, 6, 7]"
println(a .. " is an array"); // will print "[1, 2, 3, 4, 5, 6, 7] is an array"
println(["one", "two"]); // will print "[one, two]"
println([2, "text", [1, 2, 3]]); // will print "[2, text, [1, 2, 3]]"

Hash maps

Hash map allows to store data values in key:value pairs. A key is string. Numeric type can also be supplied as key, but it will be internally converted to string. Hash map cannot have two items with the same key. The values can be of any data type, including null, objects, arrays or hash maps.

Hash maps are not objects, it’s a separate variable type. However, hash maps have methods and attributes described below.

Array elements can be accessed using [key] operator. For example, to access element with key key of hash map h you should use

h["key"];

Hash Map Initialization

This statement will create an empty hash map and assign reference to in to variable h:

h = %{};

It’s also possible to create hash map already populated with values, e.g.:

h = %{"key":123, "another_key":456};

Hash Map Attributes

keys ⇒ Array

Returns array with keys of items in the hash map.

Example
h = %{100:"value1", 101:"value2"};
println(h.keys); // prints '[100,101]'
size ⇒ Integer

Returns number of items in the hash map.

Example
h = %{"a":null, "b":null, "c":null};
println(h.size); // prints '3'
values ⇒ Array

Returns array with values of items in the hash map.

Example
h = %{"key1":123, "key2":456};
println(h.keys); // prints '[123,456]'

Hash Map Methods

contains(key) ⇒ Boolean

Returns true, if hash map contains specified key or false otherwise.

Example
h = %{"key1":123, "key2":456};
println(h.contains("key2")); // prints 'true'
remove(key) ⇒ void

Removes item with specified key.

Example
h = %{"key1":123, "key2":456};
h.remove("key1");
println(h); // prints '{key2=456}'

Hash Map Conversion

Hash Map can be converted to string. string(hash-map) function is used to get string representation of hash map. The string representation lists all key-value pairs enclosed in curly brackets (“{}”). Value is separate from the key with equals sigh ("="). Items are separated by the characters “, ” (a comma followed by a space).

Printed array is automatically converted to string.

h = %{"key1":123, "key2":456};
println("This is a hash map: " .. string(h));
println("Or we can just print it this way: " .. h);

Operators

An operator is something that you feed with one or more values, which yields another value.

Arithmetic Operators

Example Name Result

-a

Negation

Opposite of a

a + b

Addition

Sum of a and b

a - b

Subtraction

Difference between a and b

a * b

Multiplication

Product of a and b

a / b

Division

Quotient of a and b

a % b

Modulus

Remainder of a divided by b

The division operator (/) returns a float value unless the two operands are integers (or strings that get converted to integers) and the numbers are evenly divisible, in which case an integer value will be returned.

Calling modulus on float operands will yield runtime error.

Assignment Operator

The assignment operator is =, which means that the left operand gets set to the value of the expression on the rights (that is, "gets set to").

Bitwise Operators

Example Name Result

~ a

Not

Bits that are set in a are unset, and vice versa.

a & b

And

Bits that are set in both operand are set.

a | b

Or

Bits that are set in either operand are set.

a ^ b

Xor

Bits that are set in only one operand are set.

a << b

Shift left

Shift the bits of a for b steps to the left (each step equals "multiply by two").

a >> b

Shift right

Shift the bits of a for b steps to the right (each step equals "divide by two").

Comparison Operators

Comparison operators allow you to compare two values.

Example Name Result

a == b

Equal

TRUE if a is equal to b.

a != b

Not equal

TRUE if a is not equal to b.

a < b

Less than

TRUE if a is strictly less than b.

a > b

Greater than

TRUE if a is strictly greater than b.

a <= b

Less than or equal to

TRUE if a is less than or equal to b.

a >= b

Greater than or equal to

TRUE if a is greater than or equal to b.

a ~= b

Match

Array containing full match of b and capture groups, if any. If nothing was matched, false (boolean) is returned. Capture groups are also assigned to special variables $1, $2, $3, etc. See see Regular expressions for additional information.

a match b

Match

Same as a ~= b

a imatch b

Match (case insensitive)

Same as a ~= b or a match b, but matching is case insensitive.

a like b

Like

Compare string value to a pattern using wildcard characters. Two wildcard characters are supported: * - represents zero, one or multiple characters. ? - represents any single character. Wildcard characters can be used in combinations.

a ilike b

Like (case insensitive)

Same as a like b, but comparison is case insensitive.

Example:

println("aaa bbb ccc" ~= "b+") // prints "[bbb]"
println("Username: John" ~= "Username: (\w+)"); // prints "[Username: John, John]"

println("abc" like "?bc*"); // prints "true"

Note that strings which actually contain number are converted to numeric type prior to comparison. So, for example:

s1 = "1";
s2 = "1.0";
i = 1;
println(s1 == s2); // prints "true"
println(s1 == i); // prints "true"

Incrementing/Decrementing Operators

NXSL supports C-style pre- and post-increment and decrement operators.

Example Name Result

++a

Pre-increment

Increments a by one, then returns a.

a++

Post-increment

Returns a, then increments a by one.

--a

Pre-decrement

Decrements a by one, then returns a.

a--

Post-decrement

Returns a, then decrements a by one.

Logical Operators

Example Name Result

! a

Not

TRUE if a is not TRUE.

not a

Not

Same as above. TRUE if a is not TRUE.

a && b

And

TRUE if both a and b is TRUE.

a and b

And

Same as above. TRUE if both a and b is TRUE.

a || b

Or

TRUE if either a or b is TRUE.

a or b

Or

Same as above. TRUE if either a or b is TRUE.

String Operators

Example Name Result

..

Concatenation operator

Returns the concatenation of its right and left arguments.

..=

Concatenating assignment operator

Appends the argument on the right side to the argument on the left side.

[a:b]

Substring operator

Returns substring of a string from the character with index a inclusive till the character with index b exclusive. Example: "1234"[1:3] will be "23". If a is omitted then substring will be taken from the start of the string and if b is omitted then substring will be taken till the end of the string.

Control structures

Any NXSL script is built out of a series of statements. A statement can be an assignment, a function call, a loop, a conditional statement or even a statement that does nothing (an empty statement). Statements usually end with a semicolon. In addition, statements can be grouped into a statement-group by encapsulating a group of statements with curly braces. A statement-group is a statement by itself as well. The various statement types are supported:

  • if

  • else

  • while

  • do-while

  • for

  • break

  • continue

  • switch

  • with

  • return

  • exit

if

The if construct is one of the most important features of many languages. It allows for conditional execution of code fragments. NXSL features an if structure that is similar to that of C:

if (expr)
    statement

else

Often you’d want to execute a statement if a certain condition is met, and a different statement if the condition is not met. This is what else is for. else extends an if statement to execute a statement in case the expression in the if statement evaluates to FALSE. The else statement is only executed if the if expression evaluated to FALSE.

while

while loops are the simplest type of loop in NXSL. They behave just like their C counterparts. The basic form of a while statement is:

while (expr)
    statement

The meaning of a while statement is simple. It tells NXSL to execute the nested statement(s) repeatedly, as long as the while expression evaluates to TRUE. The value of the expression is checked each time at the beginning of the loop, so even if this value changes during the execution of the nested statement(s), execution will not stop until the end of the iteration.

do-while

do-while loops are very similar to while loops, except the truth expression is checked at the end of each iteration instead of in the beginning. The main difference from regular while loops is that the first iteration of a do-while loop is guaranteed to run (the truth expression is only checked at the end of the iteration), whereas it may not necessarily run with a regular while loop (the truth expression is checked at the beginning of each iteration, if it evaluates to FALSE right from the beginning, the loop execution would end immediately).

for

for loops are the most complex loops in NXSL. They behave in two different ways: like their C counterparts or in Java way. The syntax of a for loop is:

for (expr1; expr2; expr3)
    statement

for (varName : array)
    statement

The first expression (expr1) is evaluated (executed) once unconditionally at the beginning of the loop.

In the beginning of each iteration, expr2 is evaluated. If it evaluates to TRUE, the loop continues and the nested statement(s) are executed. If it evaluates to FALSE, the execution of the loop ends.

At the end of each iteration, expr3 is evaluated (executed).

In the second example for cycle will call statement for each element in array. Element will be available as varName.

break

break ends execution of the current for, while, do-while or switch structure.

continue

continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the condition evaluation and then the beginning of the next iteration.

switch

The switch statement is similar to a series of if statements on the same expression. In many occasions, you may want to compare the same variable (or expression) with many different values, and execute a different piece of code depending on which value it equals to. This is exactly what the switch statement is for.

Example:

switch (input)
{
  case "1":
    trace(0,"Input is 1");
    break;
  case "2":
    trace(0,"Input is 2");
    break;
  default:
    trace(0, "Input is unknown");
}

The switch statement also allows to check ranges:

switch (input)
{
  case 1:
    trace(0,"Input is 1");
    break;
  case 2:
    trace(0,"Input is 2");
    break;
  case 3...7:
    trace(0,"Input is from 3 till 7");
    break;
  default:
    trace(0, "Input is unknown");
}

with

With statement is made to make the code cleaner and much more readable and to expose variable section to global scope for "Object query" Dashboard element. This statement consists of 2 parts: variable declaration (optional) and expression.

Structure:

with
  var = {code},
  ...
  var = {code}
expression

Good Night Simpsons Example for "Object query" Dashboard element. This example will filter out only nodes that are unreachable and will create 2 variables as data providers for columns: time node is down since and oldest alarm time.

with
  _down = { return SecondsToUptime(time() - downSince); },
 _oldestAlarm = {
  oldestAlarmTime = 99999999999;
  for (a : $node.alarms) {
   oldestAlarmTime = min(oldestAlarmTime, a.creationTime);
  }
  return strftime("%Y-%m-%d %H:%M", oldestAlarmTime);
 }
type == NODE and state & NodeState::Unreachable
//In Object query object attributes are available just using name.
//Like state ($node.state in other scripts)

return

If called from within a function, the return statement immediately ends execution of the current function, and returns its argument as the value of the function call. Calling return from main() function (either explicitly or implicitly defined) is equivalent of calling exit.

exit

The exit statement immediately ends execution of the entire script, and returns its argument as script execution result.

Expressions

The simplest yet most accurate way to define an expression is "anything that has a value".

The most basic forms of expressions are constants and variables. When you type a = 5, you’re assigning 5 into a. 5, obviously, has the value 5, or in other words 5 is an expression with the value of 5 (in this case, 5 is an integer constant).

Slightly more complex examples for expressions are functions. Functions are expressions with the value of their return value.

NXSL supports the following value types: integer values, floating point values (float), string values and arrays. Each of these value types can be assigned into variables or returned from functions.

Another good example of expression orientation is pre- and post-increment and decrement. You might be familiar with the notation of variable++ and variable--. These are increment and decrement operators. In NXSL, like in C, there are two types of increment - pre-increment and post-increment. Both pre-increment and post-increment essentially increment the variable, and the effect on the variable is identical. The difference is with the value of the increment expression. Pre-increment, which is written ++variable, evaluates to the incremented value. Post-increment, which is written variable++ evaluates to the original value of variable, before it was incremented.

A very common type of expressions are comparison expressions. These expressions evaluate to either FALSE or TRUE. NXSL supports > (bigger than), >= (bigger than or equal to), = (equal), != (not equal), < (less than) and <= (less than or equal to). These expressions are most commonly used inside conditional execution, such as if statements.

The last example of expressions is combined operator-assignment expressions. You already know that if you want to increment a by 1, you can simply write a++ or ++a. But what if you want to add more than one to it, for instance 3? In NXSL, adding 3 to the current value of a can be written a += 3. This means exactly "take the value of a, add 3 to it, and assign it back into a ". In addition to being shorter and clearer, this also results in faster execution. The value of a += 3, like the value of a regular assignment, is the assigned value. Notice that it is NOT 3, but the combined value of a plus 3 (this is the value that’s assigned into a). Any two-place operator can be used in this operator-assignment mode.

Short-circuit evaluation

Short-circuit evaluation denotes the semantics of some Boolean operators in which the second argument is only executed or evaluated if the first argument does not suffice to determine the value of the expression: when the first argument of the AND function evaluates to false, the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true. NXSL uses short-circuit evaluation for && and || boolean operators. This feature permits two useful programming constructs. Firstly, if the first sub-expression checks whether an expensive computation is needed and the check evaluates to false, one can eliminate expensive computation in the second argument. Secondly, it permits a construct where the first expression guarantees a condition without which the second expression may cause a run-time error. Both are illustrated in the following example:

if ((x != null) && ((trim(x) == "abc") || (long_running_test(x)))
   do_something();

Without short-circuit evaluation, trim(x) would cause run-time error if x is NULL. Also, long running function will only be called if condition (trim(x) == "abc") will be false.

Try…​Catch

If run-time error happens during NXSL code execution, the execution will normally stop and error message will be generated.

The try statement allows to define a block of code to be tested for errors while it is being executed. Should run-time error occur within that block, execution of code after the block will continue.

The catch statement allows to define a block of code to be executed, if an error occurs in the try block.

The try and catch keywords come in pairs:

try
{
  // Block to code to try
}
catch
{
  // Block of code to handle errors
}

The following variables with information about the error are avaiable within the catch block:

$errorcode

Error code

$errorline

Line number

$errormodule

If error happened in a function of script imported using import keyword - name of that script. Empty otherwise.

$errormsg

Full error message including error code, line number, error text and, if available, module name.

$errortext

Error text

Regular expressions

Since version 3.0, regular expression engine is changed to PCRE (Perl compatible). Syntax can be checked with pcregrep, perl itself or on regex101.com (select PCRE flavour).

Comments