--
← Back to blog
Learn NSL: Variables, Functions, and Control Flow

Learn NSL: Variables, Functions, and Control Flow

Variables

NSL has three ways to declare variables:

x = 42                   # implicit declaration
let y = "hello"          # explicit with let
const PI = 3.14159       # immutable constant

Compound assignment works on variables, dict members, and list indices:

x += 10
config.count *= 2
items[0] -= 1

Data Types

| Type | Example | Notes |

|------|---------|-------|

| int | 42, 0xFF, 0b1010 | 64-bit, supports hex/binary/octal |

| float | 3.14, 1.5e10 | 64-bit IEEE 754 |

| string | "hello", 'world' | UTF-8, single or double quotes |

| bool | true, false | |

| null | null | |

| list | [1, 2, 3] | ordered, mutable |

| dict | {name: "a", x: 1} | ordered map, string keys |

| set | {|1, 2, 3|} | unordered unique values |

| regex | /\d+/gi | full regex support |

| time | 14:30 | HH:MM literal |

| char | c'A' | single character |

String interpolation with f-strings:

let name = "NSL"
let version = 2
print(f"{name} version {version}")  # "NSL version 2"

Multiline strings with triple quotes:

let text = """
This is a
multiline string
"""

Raw strings skip escape processing: r"no\escapes\here"

Procedures

Procedures are defined with :: and use indentation for the body:

:: add(a, b) ->
    return a + b

:: greet(name) ->
    print(f"Hello, {name}!")

# Call them
let sum = add(3, 4)
greet("world")

Procedures are hoisted -- you can call them before their definition in the file.

Lambdas

Lambdas are anonymous functions created with fn:

# Single expression
let double = fn(x) => x * 2

# Multi-statement block
let process = fn(x) =>
    let result = x * 2
    return result + 1

# With type annotation
let square = fn(x) -> int => x * x

Lambdas are first-class values -- pass them to functions, store in variables, return from other functions.

Control Flow

If / Else

if temperature > 30
    print("Hot!")
else if temperature > 20
    print("Nice")
else
    print("Cold")

Ternary expressions

let label = "hot" if temp > 30 else "cold"

Note: the syntax is value_if_true if condition else value_if_false.

While and Do-While

while count > 0
    count -= 1

do
    let inp = input("Continue? ")
while inp != "no"

For Each

NSL uses for each (not for):

# Iterate a list
for each item in [1, 2, 3, 4, 5]
    print(item)

# With filter
for each item in items where item > 3
    print(item)

# Iterate dict keys
for each key in config
    print(f"{key} = {config[key]}")

# Destructure key-value pairs
for each [key, val] in entries(config)
    print(f"{key}: {val}")

# Destructure nested lists
for each [x, y] in [[1,2], [3,4], [5,6]]
    print(f"({x}, {y})")

Break and Continue

for each n in range(100)
    if n == 50
        break
    if n % 2 == 0
        continue
    print(n)

Error Handling

try
    let result = risky_operation()
    print(result)
catch error
    print(f"Error: {error}")
finally
    cleanup()

Throw your own errors:

throw "something went wrong"

Modules

Import code from other files with use:

use "utils.nsl"
use "core/math_helpers"    # .nsl extension auto-appended

Module search order: absolute path, relative to current file, relative to CWD, NSL_PATH env var.

Defer

Run cleanup code when leaving a scope:

let handle = open_file("data.txt")
defer close(handle)
# ... use handle, it will be closed automatically

Assertions

assert(x > 0, "x must be positive")
require x > 0 else "x must be positive"   # throws on failure

All Posts