Skip to main content

Getting started with NSScript

1. How To write & run an NSScript​

In this basic hello world example, the Kotlin println() function is called to write to the console.

hello_world.nss
#!/usr/bin/env nss

println("Hello World!")
Output
$ nss hello_world.nss
Hello World!

2. How do I perform imports in NSScript?​

With NSScript, it is possible to:

  • import Kotlin/Java APIs
  • import code from another script

API imports​

It is possible to import Kotlin/Java APIs with regular import statements. The standard libraries are available on the classpath, so these are always accessible.

Importing the Java UUID class from the standard Java API is as simple as adding an import to access its full functionality.

Java import example:​

uuid.nss
#!/usr/bin/env nss

import java.util.UUID

println(UUID.randomUUID())
Output
$ nss uuid.nss
2f95518a-578b-43b0-9a94-9543a41de58e

Script imports​

It is possible to import code and functionality from other scripts. These imports are not transitive, but do allow for scripts to centralize some functionality. The contents of the global scope of a script import will be available in the global scope of the importing script.

All script imports should be defined only once and at the top of the script file. The path of the import is relative to the importing script, but could also be defined as an absolute path, though not recommended.

Script import example:​

common.nss
#!/usr/bin/env nss

fun sayHello(target: String) {
println("Hello $target!")
}
hello_again.nss
#!/usr/bin/env nss
@file:Import("common.nss")

sayHello("Mars")
Output
$ nss hello_again.nss
Hello Mars!

3. Writing scope and execution functions​

See NS DSL design for what scope functions and execution functions are and how they differ. The examples below show how to write the functions in practice.

Scope functions​

Both the script itself (the global scope) as several functions in the API are "scoping functions". Every scoping function can provide some functionality and/or modify the scope context. All functions will adapt their behavior depending on the context within which they execute.

Attention

Since the scripting language itself allows access from withing the scope of a function body to any parent scope, any functionality introduced by a parent scope function can be accessed if it was not redeclared in the scope itself or another parent in the chain. This applies to things explicitly defined in the bodies of functions, as well as things provided implicitly by the API.

Example Scope function:​

scope_example.nss
#!/usr/bin/env nss

class MyScope

fun example(body: MyScope.() -> Unit) {
MyScope().body()
}

example {
val hiStr = "Hello"
val targetStr = "World"
example {
val targetStr = "Mars"
println("$hiStr $targetStr!")
}
}
Output
$ nss scope_example.nss
Hello Mars!

Scope context Example:​

The scope context is a fieldFieldA property of a DataElement expanded to a database column and user interface input element. that is provided by every scope function. The name of this fieldFieldA property of a DataElement expanded to a database column and user interface input element. is ctx and this contains data that is relevant within the current scope. Additionally, the scope context also contains a fieldFieldA property of a DataElement expanded to a database column and user interface input element. script, which contains the script context with data that is fixed for the entire script.

script_scope.nss
#!/usr/bin/env nss

println("Current directory: ${ctx.path}")
println("Script directory: ${ctx.script.path}")

dir("../new_dir") {
println("Scoped directory: ${ctx.path}")
println("Still original script directory: ${ctx.script.path}")
}

dir(ctx.script.path) {
println("Script directory as scope: ${ctx.path}")
}
Output
$ nss Desktop/script_scope.nss
Current directory: /home/username
Script directory: /home/username/Desktop
Scoped directory: /home/new_dir
Still original script directory: /home/username/Desktop
Script directory as scope: /home/username/Desktop

Execution functions​

Though execution functions looks similar to scope functions, they serve a vastly different purpose. They are builders which allow you to construct the parameters to execute some functionality. Functions are used to supply arguments to the builder and once the function has executed logic defined in the function body, it will use the constructed data to execute the command.

Because execution functions are builders, but defined as a function body that executed on an execution template, it is version transparent and possible to use logic in the function body to determine how the arguments should be populated.

Unlike scope functions, execution functions do not redefine the ctx scope context fieldFieldA property of a DataElement expanded to a database column and user interface input element., but they do inherit it from the parent scope and take it into account when executing. To avoid confusion, it is not allowed to use scope functions inside of execution functions, as this would also serve no purpose.

Example Execution function:​

execute_echo.nss
#!/usr/bin/env nss

val currentPlanet = "Earth"
exec {
arg("echo", "Hello")
if (currentPlanet == "Earth") {
arg("World!")
} else {
arg("Mars!")
}
}
Output
$ nss execute_echo.nss
Hello World!

4. How do I serialize data as JSON in a script?​

JSON serialization is supported through [kotlinx serialization](https://kotlinlang.org/api/kotlinx .serialization/kotlinx-serialization-json/). The example shows how this is done:

serialize_json.nss
#!/usr/bin/env nss

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString

@Serializable
data class DataClass(val hello: String, val number: Int)

val data = DataClass("world", 42)

val json = Json.encodeToString(data)
println(json)
Output
$ nss serialize_json.nss
{"hello":"world","number":42}