Skip to content

Latest commit

 

History

History
1722 lines (1311 loc) · 47.3 KB

File metadata and controls

1722 lines (1311 loc) · 47.3 KB

Built-in Types

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

NXSL is loosely 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.

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.

Global variables

By default, variables are local to the function where they are first assigned. This means variables defined in the main script are not visible to other functions:

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 to the implicit main function and is not visible in myFunction.

To make a variable accessible across all functions, declare it with the global keyword:

global x = 1;
myFunction();

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

This script produces the expected result and prints x * 2 = 2.

Local variables

By default, variables are local to the function where they are first assigned. Starting with version 6.0, you can explicitly declare a variable as local using the local keyword. This is useful for clarity and to ensure a variable is local even if a global variable with the same name exists:

global x = 100;

function myFunction()
{
    local x = 5;  // Creates a new local variable, does not affect global x
    println("local x = " .. x);  // Prints: local x = 5
}

myFunction();
println("global x = " .. x);  // Prints: global x = 100

The local keyword can also be used without initialization:

function process()
{
    local result;  // Explicitly declare local variable
    if (condition)
        result = "yes";
    else
        result = "no";
    return result;
}

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

NXSL provides four ways to access function arguments. In most cases, you should use standard named parameters (method 1).

Declare parameter names in the function signature. This is the clearest and most commonly used approach:

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

add(5, 3);  // Prints: The result was: 8

If a parameter is not provided at function call, its value will be NULL:

function greet(name, title)
{
    if (title == null)
        title = "User";
    println("Hello, " .. title .. " " .. name);
}

greet("Alice");           // Prints: Hello, User Alice
greet("Bob", "Dr.");      // Prints: Hello, Dr. Bob
2. Call-site named parameters

Parameters can be named at the call site using param: value syntax. The function declaration has empty parentheses, and parameters are accessed with the $ prefix:

function process()
{
    println("Name: " .. $name);
    println("Age: " .. $age);
}

process(age: 30, name: "Alice");
// Prints:
// Name: Alice
// Age: 30

This approach allows calling with parameters in any order but is less commonly used.

3. Positional parameter variables

Arguments are automatically available as $1, $2, $3, etc., regardless of how the function is declared:

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

add(5, 3);  // Prints: The result was: 8

This is useful for quick scripts but makes code harder to read. Standard named parameters (method 1) are preferred.

4. The $ARGS array

All arguments passed to a script are available in the global $ARGS array. The first argument is $ARGS[1]:

function main()
{
    println("Script received " .. $ARGS.size .. " arguments");
    for (i = 1; i <= $ARGS.size; i++)
        println("Argument " .. i .. ": " .. $ARGS[i]);
}

This is primarily used when the number of arguments is variable or unknown.

Mixing argument styles

You can combine positional and named arguments in the same function call. Named arguments count toward positional parameter indices:

function myFunc(arg1)
{
    println("arg1: " .. arg1);
    println("$2: " .. $2);
    println("$arg3: " .. $arg3);
}

myFunc("first", arg3: "third", "second");
// Prints:
// arg1: first
// $2: third       (named parameter arg3 occupies position 2)
// $arg3: third

myFunc("first", "second", arg3: "third");
// Prints:
// arg1: first
// $2: second      (positional parameter in position 2)
// $arg3: third

While this flexibility exists, mixing argument styles can make code harder to understand. For clarity, stick to standard named parameters (method 1) in most cases.

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 two ways:

  • Explicit main() function

  • Implicit $main() function

Explicit main() function:

If an explicitly defined main() function exists, it will be called. This approach is recommended for scripts that accept parameters, as it allows you to declare named parameters:

function main(nodeName, threshold)
{
    println("Processing:", nodeName);
    if (threshold == null)
        threshold = 90;
    // Script logic here
    return 0;
}

Script arguments are passed to main() parameters in order. Arguments are also available via $1, $2, etc. and the $ARGS array within the function.

Implicit $main() function:

If an explicit main() doesn’t exist, the interpreter creates an implicit $main() function from all code that is not part of any other function:

// This code becomes the implicit $main()
println("Script started");
result = calculate($1);
return result;

function calculate(value)
{
    return value * 2;
}
Warning
If both an explicit main() function and top-level code exist, the top-level code is silently discarded. Only the explicit main() function will execute:
println("This will NOT execute");  // Discarded when main() exists

function main()
{
    println("Only this executes");
}

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 way is shown in this example:

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

Optional function calls

Added in version 6.0

To prevent script errors when calling a function that may not exist, use the optional keyword. If the function is present, it is called, and the expression evaluates to its return value. If the function does not exist, the call is safely ignored, and the expression evaluates to NULL without halting the script.

// If f() exists, v gets its return value.
// If f() does not exist, v becomes NULL.
v = optional f();

The same logic applies when calling a function from a specific library.

// If my_library and function f() in it exists, v gets its return value.
// Otherwise, v becomes NULL.
v = optional my_library::f();

Strings

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

String attributes

isEmpty: bool

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

s = "";
println(s.isEmpty); // prints "true"
length: int

Returns number of characters in the string.

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

String methods

compareTo(string): int

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.

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): int

Same as compareTo, but ignoring the case.

println("aaa".compareToIgnoreCase("AAA")); // prints "0"
contains(string): bool

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

println("aaa".contains("a")); // prints "true"
endsWith(string): bool

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

println("abc".endsWith("d")); // prints "false"
println("Just a sentence".endsWith("a sentence")); // prints "true"
equalsIgnoreCase(string): bool

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

println("abc".equalsIgnoreCase("ABC")); // prints "true"
fuzzyEquals(string, threshold): bool

Returns true if argument string is approximately equal to this string using fuzzy comparison based on similarity score. Optional threshold parameter (float, 0.0 to 1.0) sets the minimum similarity score required for a match — higher values require closer match. Default threshold is 0.2.

Added in version 6.0

println("kitten".fuzzyEquals("kittens"));      // prints "true"
println("abc".fuzzyEquals("xyz"));             // prints "false"
println("hello".fuzzyEquals("hallo", 0.9));    // prints "false" (higher threshold)
fuzzyEqualsIgnoreCase(string, threshold): bool

Same as fuzzyEquals, but ignoring the case.

Added in version 6.0

println("Kitten".fuzzyEqualsIgnoreCase("kittens"));  // prints "true"
println("Hello".fuzzyEqualsIgnoreCase("HALLO"));     // prints "true"
indexOf(string): int

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

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): int

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

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.

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.

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.

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.

Added in version 5.1.4: trimWhitespace parameter

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.

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): bool

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

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.

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.

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

Converts this string to uppercase.

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

Returns this string with whitespace from both sides removed.

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

Returns this string with whitespace from left side removed.

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

Returns this string with whitespace from right side removed.

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

Arrays

An array in NXSL is an ordered, sparse, integer-indexed collection. It can be treated as an array, list (vector), 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

Arrays can be created using the [] construct. This is the recommended method for array initialization.

An empty array can be created by assigning [] to a variable:

a = [];

Arrays can also be initialized with values:

a = [1, 2, 3, 4];

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

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

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

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.

Deprecated in version 5.0. The array keyword for declaring arrays is deprecated. New scripts should use [] syntax instead.

// Deprecated - do not use in new code
array a;

// Recommended - use this instead
a = [];

Array attributes

maxIndex: int

Returns highest index in the array.

a = [1, 2, 3];
println(a.maxIndex); // prints '2'
println(a[a.maxIndex]); // prints '3'
minIndex: int

Returns lowest index in the array.

a = [1, 2, 3];
println(a.minIndex); // prints '0'
size: int

Returns number of elements in the array.

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

Array methods

append(newElement): int

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

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

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

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

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

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

Returns concatenated string of all array elements separated by the specified separator.

Table 1. Parameters

separator

String

Separator between array elements

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.

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.

a = [1,2];
b = [3,4];
a.insertAll(1,b);
println(a); // prints '[1, 3, 4, 2]'
pop(): any

Removes and returns 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.

a = [];
a.push("one");
a.push("two");
println(a.pop());
println(a.pop());
push(newElement): int

Same as append().

remove(index): void

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

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

Spread operator

The spread operator (…​) expands array elements in place. Use it when all elements from an array need to be included in a new array or applied one-by-one as function arguments.

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

data = [10, 20, 30];
println(sum(...data)); // expands to sum(10, 20, 30), prints 60

a = ["one", "two"];
b = ["new", ...a];
println(b); // prints "[new, one, two]"

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.

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

Returns number of items in the hash map.

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

Returns array with values of items in the hash map.

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

Hash Map Methods

contains(key): bool

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

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

Removes item with specified key.

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.

Member Access Operators

Example Name Result

a.b

Member access

Access attribute or method b of object a. Runtime error if a is null.

a?.b

Safe navigation

Access attribute or method b of object a. Returns null if a is null (no error).

The safe navigation operator ?. is useful for chaining method calls when intermediate results might be null:

node = FindObject("my-server");
println(node.name);   // Runtime error if node is null
println(node?.name);  // Safe: prints nothing if node is null

// Useful in conditions to avoid nested null checks
if ($node.createSNMPTransport()?.getValue("1.3.6.1.2.1.1.1.0") == 10)
{
    // Process result
}

// Without safe navigation, you would need:
t = $node.createSNMPTransport();
if (t != null)
{
    if (t.getValue("1.3.6.1.2.1.1.1.0") == 10)
    {
        // Process result
    }
}

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

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

NXSL supports two kinds of comments: single-line comments and block (multi-line) comments.

  • Single-line comments start with // and continue to the end of the current line.

  • Block comments start with /* and end with */. Block comments may span multiple lines.

  • Block comments in NXSL can be nested; an inner /* …​ */ pair is treated as part of the enclosing comment.

Comments are treated as whitespace by the parser and may appear wherever whitespace is allowed (between tokens, at line start, at end of line, inside code blocks, etc.). Comment markers that appear inside string literals are not recognized as comment delimiters.

Examples:

// Single-line comment
x = 1; // comment after code

/* Multi-line comment
   spanning several lines */
y = 2;

/* Nested block comments are allowed:
   outer open
   /* inner comment */
   outer continues
*/
z = 3;

Metadata

NXSL scripts may start with one or more metadata declarations. Metadata declarations must appear only at the very beginning of the script (before any other code or non-whitespace characters). Each declaration is a single line with the following syntax:

@meta(key1=value1, key2=value2, ...)

Multiple @meta declarations are allowed; they are processed independently.

Metadata keys and their meanings are domain-specific.