volangstdlibreferencefunctions

# Standard Library

The Volang Standard Library provides a comprehensive set of built-in functions essential for modifying text, performing mathematical operations, and other common operations. These functions are available globally in every script.

# Input Operations

This section describes functions for reading input data within a logic block. Every logic block can define one or more input channels. When an input value changes, the block's script is executed. These functions allow the script to determine which input triggered the execution and to read the current values of all inputs.

# input::channel

Returns the identifier of the input channel that triggered the current script execution. When a block has multiple inputs, this function is essential for determining which input caused the block to run, allowing you to handle each input differently.

Parameters:

  • None.

Returns:

  • A string containing the id of the input that triggered the execution.

Example:

channel = input::channel()

if (channel == "input") {
    // The main toggle input was triggered
    ...
} else if (channel == "on") {
    // The dedicated "on" input was triggered
    ...
} else if (channel == "off") {
    // The dedicated "off" input was triggered
    ...
}

# input::value

Returns the value of the input that triggered the current script execution. The return type depends on the input's defined type (boolean, number, or string). Use this function together with input::channel() to identify both which input changed and what its new value is.

Parameters:

  • None.

Returns:

  • The current value of the triggering input. The type depends on the input definition (boolean, number, or string).

Example:

channel = input::channel()
value = input::value()

// React to a rising edge on a boolean input
if (channel == "input" and value) {
    output::toggle("output")
}

# input::get

Retrieves the last known value of a specific input by its identifier. Unlike input::value(), which only returns the value of the input that triggered the current execution, this function can read the stored value of any input at any time.

Parameters:

  • id (string): The identifier of the input to read.

Returns:

  • The last known value of the specified input. The type depends on the input definition (boolean, number, or string).

Example:

// Read the current temperature from an input
current_temp = input::get("value")

// Use the value in control logic
if (current_temp > 25.0) {
    output::set("cooling", true)
}

# Output Operations

This section describes functions for controlling the outputs of a logic block. Each block can define one or more outputs that propagate values to other connected blocks.

# output::get

Retrieves the current value of a specific output by its identifier. This is useful for reading the current state of an output before deciding whether to change it.

Parameters:

  • id (string): The identifier of the output to read.

Returns:

  • The current value of the specified output. The type depends on the output definition (boolean, number, or string).

Example:

// Check the current output state before toggling
prev = output::get("output")
if (prev) {
    std::print("Output is currently ON, turning OFF")
}
output::set("output", !prev)

# output::set

Sets the value of a specific output by its identifier. The value type must match the type defined for that output (boolean, number, or string). Setting an output propagates the new value to any connected blocks.

Parameters:

  • id (string): The identifier of the output to set.
  • value: The new value to assign. Must match the output's defined type.

Returns:

  • None.

Example:

// Set a boolean output
output::set("output", true)

// Set a numeric output based on calculation
midpoint = config::get("midpoint")
hysteresis = config::get("hysteresis")
value = input::get("value")

if (value >= midpoint + hysteresis) {
    output::set("output", true)
} else if (value <= midpoint - hysteresis) {
    output::set("output", false)
}

# output::toggle

Toggles a boolean output, flipping its current value. If the output is currently true, it becomes false, and vice versa. This function only works with boolean-type outputs.

Parameters:

  • id (string): The identifier of the boolean output to toggle.

Returns:

  • None.

Example:

channel = input::channel()
value = input::value()

// Toggle the output on a rising edge of the input
if (channel == "input") {
    last_state = state::get("last_input_state")
    if (value and !last_state) {
        output::toggle("output")
    }
    state::set("last_input_state", value)
}

# State Operations

This section describes functions for managing persistent state within a logic block. State variables retain their values between script executions, making them essential for tracking conditions over time such as edge detection, counters, or timing information. The available state variables and their types are defined in the block descriptor.

# state::get

Retrieves the current value of a persistent state variable by its identifier.

Parameters:

  • id (string): The identifier of the state variable to read.

Returns:

  • The current value of the state variable. The type depends on the state variable definition (boolean, number, or string).

Example:

// Use state to detect a rising edge (transition from false to true)
value = input::value()
last_state = state::get("last_input_state")

if (value and !last_state) {
    // Rising edge detected - take action
    output::toggle("output")
}

state::set("last_input_state", value)

# state::set

Stores a value in a persistent state variable by its identifier. The value is preserved between script executions, allowing the block to remember information across multiple input events. The value type must match the type defined for that state variable.

Parameters:

  • id (string): The identifier of the state variable to update.
  • value: The new value to store. Must match the state variable's defined type.

Returns:

  • None.

Example:

// Track the timestamp of the last input change
channel = input::channel()
value = input::value()

if (channel == "input") {
    state::set("last_change_time", time::now())

    // Calculate how long the input was in its previous state
    elapsed = time::now() - state::get("last_change_time")
    if (elapsed > 60) {
        std::print("Input was stable for over a minute")
    }
}

# Configuration Operations

This section describes functions for reading block configuration parameters. Configuration values are set by the user in Voldeno Studio and are read-only from within the script. They allow blocks to be customized without modifying the code — for example, setting thresholds, delays, or operating modes.

# config::get

Retrieves the value of a configuration parameter by its identifier. Configuration parameters are defined in the block descriptor and their values are set by the user in Voldeno Studio. The script can only read these values, not modify them.

Parameters:

  • id (string): The identifier of the configuration parameter to read.

Returns:

  • The value of the configuration parameter. The type depends on the parameter definition (number, string, or enum). Enum-type parameters return their zero-based numeric index.

Example:

// Read delay configuration in milliseconds
delay_on_ms = config::get("delay_on_ms")
delay_off_ms = config::get("delay_off_ms")

// Use an enum config to select behavior mode
mode = config::get("mode") // Returns 0, 1, 2... based on selected option

if (mode == 0) {
    // First mode logic
    ...
} else if (mode == 1) {
    // Second mode logic
    ...
}

// Read a numeric threshold from config
scale = config::get("scale")
offset = config::get("offset")
result = input::value() * scale + offset
output::set("value", result)

# Callback Operations

This section describes functions for scheduling deferred function calls. Volang is a non-blocking language and does not provide a sleep function. Instead, callbacks provide a mechanism for time-based logic by scheduling a user-defined function to be called after a specified delay.

This is essential for implementing behaviors such as delays, timed pulses, motor control sequences, or any logic that needs to act after a period of time has elapsed. A callback can pass arguments to the called function, and the function can schedule further callbacks to create multi-step sequences.

Only one callback can be active at a time per block. Scheduling a new callback while one is already pending requires clearing the existing one first with callback::clear().

# callback::set

Schedules a deferred call to a user-defined extern function after a specified delay in milliseconds. The function is identified by its name as a string. Additional arguments can be passed and will be delivered to the callback function as its parameters.

The target function must be declared with the extern fn syntax in the script. When the delay elapses, the system calls that function with the provided arguments.

Parameters:

  • delay_ms (integer): The delay in milliseconds before the function is called.
  • function_name (string): The name of the extern fn to call.
  • ...args (optional): Additional arguments to pass to the callback function. You can pass zero or more values of any type.

Returns:

  • None.

Example:

// Define a simple callback handler
extern fn onTimeout() {
    output::set("output", true)
}

// Schedule the callback to fire after a configurable delay
delay_ms = config::get("delay_ms")
callback::set(delay_ms, "onTimeout")

Example — passing arguments:

// Callback that chains into the next step
extern fn onSequence(step) {
    if (step == 1) {
        output::set("output", true)
        // Schedule step 2 after a pulse duration
        pulse_ms = config::get("pulse_ms")
        callback::set(pulse_ms, "onSequence", 2)
        return
    }
    if (step == 2) {
        output::set("output", false)
        return
    }
}

// Start the sequence after an initial delay
callback::set(1000, "onSequence", 1)

# callback::clear

Cancels any currently scheduled callback for the block. This should be called before scheduling a new callback, or when the pending action is no longer needed (e.g., the input changed before the delay expired).

Parameters:

  • None.

Returns:

  • None.

Example:

extern fn onCallback(value) {
    if (value == 1) {
        output::set("output", true)
    } else if (value == 2) {
        output::set("output", false)
    }
}

channel = input::channel()
value = input::value()

if (channel == "input") {
    last = state::get("last_input_state")
    if (value != last) {
        state::set("last_input_state", value)

        // Always clear the previous callback before scheduling a new one
        callback::clear()

        if (value) {
            delay_ms = config::get("delay_on_ms")
            if (delay_ms > 0) {
                callback::set(delay_ms, "onCallback", 1)
            } else {
                output::set("output", true)
            }
        } else {
            delay_ms = config::get("delay_off_ms")
            if (delay_ms > 0) {
                callback::set(delay_ms, "onCallback", 2)
            } else {
                output::set("output", false)
            }
        }
    }
}

# String Operations

This section describes functions that manipulate strings.

# str::len

Calculates the number of characters in a given string.

Parameters:

  • text (string): The input string to measure.

Returns:

  • An integer value representing the count of characters in the string.

Example:

// Check if the user PIN code has the required length
user_pin = "1234"
if (str::len(user_pin) == 4) {
    ...
}

# str::trim

Removes all leading and trailing whitespace characters (spaces, tabs, newlines) from the string. This is essential for cleaning up user input or parsing commands received from external systems where extra spacing might occur.

Parameters:

  • text (string): The input string to trim.

Returns:

  • A new text containing the text without surrounding whitespace.

Example:

// Clean up a command received from an external module
input = "  START  "
command = str::trim(input)

// Now the string is "START" and can be compared safely
if (command == "START") {
    ....
}

# str::concat

Concatenates (joins) two or more strings into a single string. This function is variadic, meaning it accepts a variable number of arguments, allowing you to build complex messages or commands dynamically.

Parameters:

  • ...strings: A sequence of string arguments to be joined together. You can pass as many arguments as needed, separated by commas. A single argument can be also specified, in which case this function returns the argument itself.

Returns:

  • A new string consisting of all input strings appended in order.

Example:

room = "Living Room"
temp = "22.5"

// Construct a full status message using multiple arguments
message = str::concat("Status: ", room, " is currently ", temp, "°C")

// Result: "Status: Living Room is currently 22.5°C"

# str::count

Counts the number of non-overlapping occurrences of a substring within a string. Optionally, the search can be restricted to a specific range within the string using the start and end position arguments.

Parameters:

  • text (string): The string to search within.
  • substring (string): The substring to count occurrences of.
  • start (integer, optional): The position to start the search from. Defaults to 0.
  • end (integer, optional): The position to end the search at (exclusive). Defaults to the end of the string.

Returns:

  • An integer value representing the number of non-overlapping occurrences of the substring.

Example:

// Count all occurrences of a word
text = "I love apples, apple are my favorite fruit"
count = str::count(text, "apple") // Returns 2

// Count occurrences within a range
data = "abcabcabc"
count = str::count(data, "abc", 1)    // Returns 2 (skips first "abc")
count = str::count(data, "abc", 1, 7) // Returns 1

// Empty substring returns length + 1
count = str::count("hello", "") // Returns 6

// Non-overlapping: "aaaa" contains 2 non-overlapping "aa"
count = str::count("aaaa", "aa") // Returns 2

# str::replace

Replaces occurrences of a substring within a string with a new substring. By default, all occurrences are replaced. An optional count parameter limits the number of replacements performed from left to right.

Parameters:

  • text (string): The original string.
  • old (string): The substring to be replaced.
  • new (string): The substring to replace with.
  • count (integer, optional): Maximum number of replacements. If omitted, all occurrences are replaced.

Returns:

  • A new string with the replacements applied.

Example:

// Replace all occurrences
result = str::replace("one one was a horse", "one", "three")
// Returns "three three was a horse"

// Replace only the first occurrence
result = str::replace("one one was a horse", "one", "three", 1)
// Returns "three one was a horse"

// Delete by replacing with empty string
result = str::replace("hello", "l", "") // Returns "heo"

// Empty old string inserts between every character
result = str::replace("abc", "", "-") // Returns "-a-b-c-"

# str::number

Parses a string argument and converts it into a numeric value. The function automatically detects the format: if the string contains a decimal point, it returns a float number, otherwise, it returns an integer number.

Parameters:

  • text: The string containing the number representation.

Returns:

  • A numeric value, either integer or float, depending on the input format.

Example:

// Case 1: Parsing an integer (e.g., brightness level)
brightness = str::number("80") // Returns Integer 80

// Case 2: Parsing a float (e.g., temperature)
temp_str = "21.5"
threshold = str::number(temp_str) // Returns Float 21.5
current_temp = 22

// Using the converted value in logic
if (current_temp > threshold) {
    ...
}

# str::fmt

Constructs a formatted string by replacing placeholders {} within a template string with the string representation of the provided arguments. It accepts a variable number of arguments of different types (string, numeric and boolean values) and automatically converts them to text before insertion. The replacement is positional: the first {} is replaced by the first argument, the second {} by the second, and so on.

Parameters:

  • template: The format string containing curly brace placeholders {}.
  • ...args: A sequence of values to substitute into the placeholders. Supported types include numbers, strings, and boolean literals (true/false).

Returns:

  • A new string with all placeholders replaced by the corresponding values.

Example:

sensorName = "Kitchen_Main"
temp = 22.5
isActive = true

// Format a complex log message without manual concatenation
log = str::fmt("Sensor {} status: Active={}, Value={}°C", sensorName, isActive, temp)

// Result: "Sensor Kitchen_Main status: Active=true, Value=22.5°C"

# str::split

Initializes a string splitting operation and creates an iterator state. This function does not perform the actual splitting immediately. Instead, it returns an opaque handle object that encapsulates the current position and logic required to traverse the string. This object must be passed to str::split_next or str::split_has_next to retrieve the actual substrings one by one.

Parameters:

  • text: The source string to be split.
  • separator: The delimiter string that marks the boundaries between chunks.

Returns:

  • An opaque handle representing the active splitting session. You should not modify this object directly.

Example:

rawData = "sensor1;25.5;active"

// Initialize the splitter. 'iterator' now holds the state.
iterator = str::split(rawData, ";")

// The iterator is now ready to be consumed by split_next...

# str::split_has_next

Checks if there are more substrings waiting to be retrieved from the current splitting session. This function examines the opaque state object created by str::split. It does not modify the state or advance the iterator position; it only verifies availability. It is commonly used as the condition in a while loop to iterate through all parts of a string.

Parameters:

  • state: The opaque handle returned by str::split.

Returns:

  • true if at least one more substring is available; otherwise, returns false.

Example:

data = "RED,GREEN,BLUE"
iterator = str::split(data, ",")

// Loop as long as there are tokens left
while (str::split_has_next(iterator)) {
    ...
    // Safe to call split_next() here
    // color = str::split_next(iterator) 
}

# str::split_next

Extracts and returns the next substring from the split sequence and advances the iterator to the next position. This function consumes the current segment of the string identified by the opaque state. It is designed to be called sequentially until all parts of the string have been processed.

Parameters:

  • state: The opaque handle returned by str::split.

Returns:

  • The next segment of the original text.
Note

It is recommended to verify availability using str::split_has_next(state) before calling this function to ensure safe iteration.

Example:

// Scenario: Parsing a command string "SET_COLOR;255;0;0"
commandRaw = "SET_COLOR;255;0;0"
iter = str::split(commandRaw, ";")

// We know the format, so we can extract parts manually
if (str::split_has_next(iter)) {
    cmd = str::split_next(iter)   // "SET_COLOR"
    r = str::split_next(iter)     // "255"
    g = str::split_next(iter)     // "0"
    b = str::split_next(iter)     // "0"
    ...
}

# Array Operations

This section describes functions for creating and manipulating arrays.

# array::new

Creates a new array initialized with the provided values. This function is variadic, meaning it accepts a variable number of arguments of any type. If called without arguments, it creates an empty array. Arrays can hold mixed types and their size can be dynamically changed using other array functions.

Parameters:

  • ...values (optional): A sequence of values to initialize the array with. You can pass zero or more arguments of any type (numbers, strings, booleans, or even other arrays). If no arguments are provided, an empty array is created.

Returns:

  • An opaque handle representing the created array. This handle should be passed to other array functions (such as array::put_at, array::get_at, array::len) to manipulate the array contents.

Example:

// Create an empty array
empty = array::new()

// Create an array with initial numeric values
temperatures = array::new(21.5, 22.0, 19.8, 23.1)

// Create an array with mixed types
config = array::new("sensor1", 100, true)

// Create an array with string values for room names
rooms = array::new("Living Room", "Kitchen", "Bedroom", "Bathroom")

// Arrays can be used to collect sensor readings over time
readings = array::new()
// Later: array::push(readings, current_value)

# array::len

Returns the number of elements currently stored in the array. This function is useful for checking if an array is empty, iterating over all elements, or validating array size before accessing specific indices.

Parameters:

  • arr: The opaque array handle returned by array::new.

Returns:

  • An integer value representing the number of elements in the array.

Example:

// Check the size of an array
temps = array::new(21.5, 22.0, 19.8)
count = array::len(temps) // Returns 3

// Check if an array is empty
data = array::new()
if (array::len(data) == 0) {
    std::print("No data collected yet")
}

// Use length for iteration bounds
sensors = array::new("temp1", "temp2", "humidity")
i = 0
while (i < array::len(sensors)) {
    // Process each sensor...
    i = i + 1
}

# array::get_at

Retrieves the value stored at the specified index in the array. Array indices are zero-based, meaning the first element is at index 0, the second at index 1, and so on.

Parameters:

  • arr: The opaque array handle returned by array::new.
  • index: The zero-based position of the element to retrieve.

Returns:

  • The value stored at the specified index. The return type depends on what was stored at that position.
Warning

You must ensure the index is within valid bounds (i.e., 0 <= index < array::len(arr)) before calling this function. Attempting to access an index that does not exist will result in a program execution error, which may lead to undefined behavior. Always validate the index using array::len() first.

Example:

temps = array::new(21.5, 22.0, 19.8, 23.1)

// Safe access: check bounds first
index = 2
if (index < array::len(temps)) {
    value = array::get_at(temps, index) // Returns 19.8
    std::print(str::fmt("Temperature at index {}: {}°C", index, value))
}

// Iterate safely over all elements
i = 0
while (i < array::len(temps)) {
    temp = array::get_at(temps, i)
    if (temp > 22.0) {
        std::print(str::fmt("High temperature detected: {}°C", temp))
    }
    i = i + 1
}

// DANGEROUS: Never do this without bounds checking!
// value = array::get_at(temps, 10) // Undefined behavior - may crash!

# array::put_at

Stores a value at the specified index in the array, replacing any existing value at that position. Array indices are zero-based, meaning the first element is at index 0, the second at index 1, and so on. This function modifies the array in place and does not return a value.

Parameters:

  • arr: The opaque array handle returned by array::new.
  • index: The zero-based position where the value should be stored.
  • value: The value to store at the specified index. The type of the value must match the type of the element already stored at that position in the array.

Returns:

  • None.
Warning

You must ensure the index is within valid bounds (i.e., 0 <= index < array::len(arr)) before calling this function. Attempting to write to an index that does not exist will result in a program execution error, which may lead to undefined behavior. Always validate the index using array::len() first.

Example:

// Create an array with initial values
temps = array::new(21.5, 22.0, 19.8, 23.1)

// Safe update: check bounds first
index = 1
if (index < array::len(temps)) {
    array::put_at(temps, index, 25.0) // Replace 22.0 with 25.0
}

// Update all values in a loop
i = 0
while (i < array::len(temps)) {
    current = array::get_at(temps, i)
    // Apply a correction factor
    array::put_at(temps, i, current + 0.5)
    i = i + 1
}

// Update specific elements in an array of strings
sensors = array::new("sensor1", "sensor2", "sensor3")
array::put_at(sensors, 0, "temp1")
array::put_at(sensors, 1, "temp2")
array::put_at(sensors, 2, "humidity")

// DANGEROUS: Never do this without bounds checking!
// array::put_at(temps, 10, 99.9) // Undefined behavior - may crash!

# array::append

Adds a value to the end of the array, increasing its size by one. This function is useful for dynamically building arrays when the final size is not known in advance.

Parameters:

  • arr: The opaque array handle returned by array::new.
  • value: The value to add at the end of the array.

Returns:

  • None.

Example:

// Build an array dynamically by appending values
temps = array::new(21.5)
array::append(temps, 22.0)
array::append(temps, 19.8)
array::append(temps, 23.1)
// temps now contains: [21.5, 22.0, 19.8, 23.1]

// Collect sensor readings over time
readings = array::new(0.0) // Initialize with first reading
array::append(readings, 0.5)
array::append(readings, 1.2)

// Check the new size
count = array::len(readings) // Returns 3

// Append in a loop (e.g., collecting 5 readings)
data = array::new(100)
i = 0
while (i < 4) {
    array::append(data, 100 + i)
    i = i + 1
}
// data now contains: [100, 100, 101, 102, 103]

# array::clear

Removes all elements from the array, leaving it empty. After calling this function, array::len(arr) will return 0. This function is useful for reusing an array without creating a new one.

Parameters:

  • arr: The opaque array handle returned by array::new.

Returns:

  • None.

Example:

// Create and populate an array
temps = array::new(21.5, 22.0, 19.8, 23.1)
std::print(str::fmt("Length before clear: {}", array::len(temps))) // 4

// Clear all elements
array::clear(temps)
std::print(str::fmt("Length after clear: {}", array::len(temps))) // 0

// The array can be reused
array::append(temps, 25.0)
array::append(temps, 26.5)
// temps now contains: [25.0, 26.5]

// Useful for periodic data collection - clear old readings
readings = array::new(0.0)
// ... collect readings ...
array::append(readings, 1.5)
array::append(readings, 2.3)
// ... process readings ...
// Reset for next collection cycle
array::clear(readings)

# Map Operations

This section describes functions for creating and manipulating maps (key-value dictionaries). Maps in Volang use strings as keys and can store values of any type.

# map::new

Creates a new empty map. A map is a collection of key-value pairs where keys are always strings. Values can be of any type (numbers, strings, booleans, arrays, or other maps). Maps are useful for storing structured data, configuration settings, or representing JSON-like objects.

Parameters:

  • None.

Returns:

  • An opaque handle representing the created map. This handle should be passed to other map functions (such as map::set, map::get, map::contains) to manipulate the map contents.

Example:

// Create an empty map
config = map::new()

// The map is now ready to store key-value pairs
// map::set(config, "temperature", 22.5)
// map::set(config, "location", "Living Room")

// Maps are also returned by some functions, e.g., HTTP response headers
// In http::on_response callback, 'headers' parameter is a map

# map::len

Returns the number of key-value pairs currently stored in the map. This function is useful for checking if a map is empty or determining how many entries it contains.

Parameters:

  • map: The opaque map handle returned by map::new.

Returns:

  • An integer value representing the number of key-value pairs in the map.

Example:

config = map::new()

// Check the size of an empty map
count = map::len(config) // Returns 0

// After adding elements, the count increases
// map::set(config, "temperature", 22.5)
// map::set(config, "humidity", 45)
// count = map::len(config) // Returns 2

// Check if a map is empty
if (map::len(config) == 0) {
    std::print("Map is empty")
}

# map::set

Adds or updates a value for the specified key in the map. If the key does not exist, a new key-value pair is created. If the key already exists, the value is replaced with the new one, but the new value must be of the same type as the existing value.

Parameters:

  • map: The opaque map handle returned by map::new.
  • key: A string specifying the key name.
  • value: The value to store. Can be of any type (number, string, boolean, array, or another map).

Returns:

  • None.

Example:

config = map::new()

// Add new key-value pairs
map::set(config, "temperature", 22.5)
map::set(config, "location", "Living Room")
map::set(config, "active", true)

// Update existing value (same type required)
map::set(config, "temperature", 23.0) // OK - float to float

// Store different types under different keys
map::set(config, "sensor_count", 5)      // integer
map::set(config, "sensor_name", "temp1") // string
map::set(config, "enabled", true)        // boolean

# map::get

Retrieves the value associated with the specified key from the map. The return type depends on what was stored under that key.

Parameters:

  • map: The opaque map handle returned by map::new.
  • key: A string specifying the key name to retrieve.

Returns:

  • The value stored under the specified key. The return type matches the type of the stored value.
Warning

The specified key must exist in the map. Attempting to retrieve a value for a non-existent key will result in a runtime error. Always use map::contains to verify the key exists before calling this function.

Example:

config = map::new()
map::set(config, "temperature", 22.5)
map::set(config, "location", "Living Room")

// Safe access: check if key exists first
if (map::contains(config, "temperature")) {
    temp = map::get(config, "temperature")
    std::print(str::fmt("Temperature: {}°C", temp))
}

// Access value when you know the key exists
location = map::get(config, "location") // Returns "Living Room"

// DANGEROUS: Never do this without checking first!
// value = map::get(config, "nonexistent") // Runtime error!

# map::clear

Removes all key-value pairs from the map, leaving it empty. After calling this function, map::len(map) will return 0. This function is useful for reusing a map without creating a new one.

Parameters:

  • map: The opaque map handle returned by map::new.

Returns:

  • None.

Example:

config = map::new()
map::set(config, "temperature", 22.5)
map::set(config, "humidity", 45)
std::print(str::fmt("Size before clear: {}", map::len(config))) // 2

// Clear all entries
map::clear(config)
std::print(str::fmt("Size after clear: {}", map::len(config))) // 0

// The map can be reused
map::set(config, "new_key", "new_value")

# map::contains

Checks whether the specified key exists in the map. This function should be used before calling map::get to avoid runtime errors when accessing non-existent keys.

Parameters:

  • map: The opaque map handle returned by map::new.
  • key: A string specifying the key name to check.

Returns:

  • true if the key exists in the map; otherwise, returns false.

Example:

config = map::new()
map::set(config, "temperature", 22.5)
map::set(config, "location", "Living Room")

// Check if a key exists
if (map::contains(config, "temperature")) {
    std::print("Temperature key exists")
    temp = map::get(config, "temperature")
}

// Check for non-existent key
if (map::contains(config, "humidity")) {
    humidity = map::get(config, "humidity")
} else {
    std::print("Humidity not set")
}

// Use in conditional logic
key = "location"
if (map::contains(config, key)) {
    value = map::get(config, key)
    std::print(str::fmt("Found {}: {}", key, value))
}

# map::keys

Returns an array containing all keys currently stored in the map. The returned array can be iterated using array::len and array::get_at to process each key. The order of keys is not guaranteed.

Parameters:

  • map: The opaque map handle returned by map::new.

Returns:

  • An array of strings, where each element is a key from the map.

Example:

settings = map::new()
map::set(settings, "brightness", 80)
map::set(settings, "color", "warm")
map::set(settings, "enabled", true)

keys = map::keys(settings)
i = 0
while (i < array::len(keys)) {
    key = array::get_at(keys, i)
    value = map::get(settings, key)
    std::print(str::fmt("{} = {}", key, value))
    i = i + 1
}

# map::values

Returns an array containing all values currently stored in the map. The returned array can be iterated using array::len and array::get_at. The order of values corresponds to the order of keys returned by map::keys.

Parameters:

  • map: The opaque map handle returned by map::new.

Returns:

  • An array of values, where each element is a value from the map. Elements can be of any type.

Example:

sensors = map::new()
map::set(sensors, "temp", 22.5)
map::set(sensors, "humidity", 65)

values = map::values(sensors)
i = 0
while (i < array::len(values)) {
    std::print(array::get_at(values, i))
    i = i + 1
}

# JSON Operations

This section describes functions for working with JSON data. JSON (JavaScript Object Notation) is a common format for data exchange, especially when communicating with web APIs.

# json::get

Extracts a value from a JSON string using a dot-notation path. This function parses the JSON and navigates through nested objects to retrieve the value at the specified location.

Parameters:

  • json: A string containing valid JSON data.
  • path: A string specifying the path to the desired field using dot notation (e.g., "a.b.c.d").

Returns:

  • The value at the specified path. The return type depends on the JSON value type: string, number, bool, or array (the same array type as described in Array Operations, usable with array::* functions).

Example:

// Simple JSON object
json_data = """{"temperature": 22.5, "location": "Living Room", "active": true}"""
temp = json::get(json_data, "temperature") // Returns 22.5
location = json::get(json_data, "location") // Returns "Living Room"
active = json::get(json_data, "active") // Returns true

// Nested JSON object
nested = """{"sensor": {"id": "temp1", "value": 23.5, "enabled": false}}"""
sensor_id = json::get(nested, "sensor.id") // Returns "temp1"
sensor_value = json::get(nested, "sensor.value") // Returns 23.5
sensor_enabled = json::get(nested, "sensor.enabled") // Returns false

// Deeply nested structure
deep = """{"building": {"floor": {"room": {"temperature": 22.0}}}}"""
room_temp = json::get(deep, "building.floor.room.temperature") // Returns 22.0

// Getting an array value
json_with_array = """{"readings": [21.5, 22.0, 22.5]}"""
readings = json::get(json_with_array, "readings") // Returns array [21.5, 22.0, 22.5]

# HTTP Operations

This section describes functions for making HTTP requests to external services. These functions enable your Volang scripts to communicate with web APIs, send notifications, or integrate with third-party services.

# http::client

Creates a new HTTP client instance required for making HTTP requests. The client manages connection settings, handles request execution, and must be created before calling any other HTTP functions.

Parameters:

  • None.

Returns:

  • An opaque handle representing the HTTP client. This handle should be passed to other HTTP function (such as http::call) to execute requests.

Example:

// Create an HTTP client
client = http::client()

// The client is now ready to make requests

# http::set_method

Sets the HTTP method for the request. This function must be called before executing the request with http::call. If not set, the default method is GET.

Parameters:

  • client: The opaque HTTP client handle returned by http::client.
  • method: A string specifying the HTTP method. Supported values: "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS".

Returns:

  • None.

Example:

client = http::client()

// Set method to POST for sending data
http::set_method(client, "POST")

// Set method to GET for retrieving data (this is the default)
http::set_method(client, "GET")

// Set method to PUT for updating resources
http::set_method(client, "PUT")

// Set method to DELETE for removing resources
http::set_method(client, "DELETE")

# http::set_header

Adds a custom HTTP header to the request. This function can be called multiple times to add multiple headers. Headers are used to pass additional information with the request, such as authentication tokens, content type specifications, or custom metadata.

Parameters:

  • client: The opaque HTTP client handle returned by http::client.
  • key: A string specifying the header name (e.g., "Content-Type", "Authorization").
  • value: A string specifying the header value.

Returns:

  • None.

Example:

client = http::client()
http::set_method(client, "POST")

// Set content type for JSON data
http::set_header(client, "Content-Type", "application/json")

// Add authorization header with API key
http::set_header(client, "Authorization", "Bearer my-api-token-123")

// Add custom headers
http::set_header(client, "X-Custom-Header", "custom-value")
http::set_header(client, "Accept", "application/json")

# http::set_body

Sets the request body content. This function is typically used with POST, PUT, or PATCH requests to send data to the server. The body can contain JSON, form data, or any other text-based content. Remember to set the appropriate Content-Type header using http::set_header to match the body format.

Parameters:

  • client: The opaque HTTP client handle returned by http::client.
  • body: A string containing the request body content.

Returns:

  • None.

Example:

client = http::client()
http::set_method(client, "POST")
http::set_header(client, "Content-Type", "application/json")

// Set JSON body
http::set_body(client, """{"temperature": 22.5, "humidity": 45}""")

// Or build the body dynamically using str::fmt
temp = 22.5
humidity = 45
body = str::fmt("""{"temperature": {}, "humidity": {}}""", temp, humidity)
http::set_body(client, body)

# http::call

Executes the HTTP request to the specified URL using the configured client settings (method, headers, body). This function is asynchronous - it sends the request and returns immediately. The response is delivered via the http::on_response callback function, which must be defined in your script. Supported protocols are http:// and https://.

Parameters:

  • client: The opaque HTTP client handle returned by http::client.
  • url: A string containing the full URL to send the request to.

Returns:

  • None. The response is delivered asynchronously via the http::on_response callback.

Callback: http::on_response(status, body, headers)

To receive the HTTP response, you must define an external callback function with the following signature:

extern fn http::on_response(status, body, headers) {
    // Handle the response here
}

Callback Parameters:

  • status: An integer representing the HTTP status code (e.g., 200 for success, 404 for not found, 500 for server error).
  • body: A string containing the response body content.
  • headers: A map object (see Map Operations) containing response headers as key-value pairs.

Example:

// Define the response callback - this will be called when the response arrives
extern fn http::on_response(status, body, headers) {
    if (status == 200) {
        std::print(str::fmt("Success! Response: {}", body))
    } else {
        std::print(str::fmt("Error: HTTP {}", status))
    }
}

// Prepare and send the request
client = http::client()
http::set_method(client, "GET")
http::set_header(client, "Accept", "application/json")
http::call(client, "http://192.168.1.100/api/status")

// For POST request with data
client2 = http::client()
http::set_method(client2, "POST")
http::set_header(client2, "Content-Type", "application/json")
http::set_body(client2, """{"command": "turn_on"}""")
http::call(client2, "http://192.168.1.100/api/device/relay1")

# Time Operations

# time::now

Retrieves the current system time as a Unix timestamp. The value represents the number of seconds elapsed since 00:00:00 UTC on January 1, 1970 (the Unix Epoch).

This function is essential for tracking when events occurred, calculating time elapsed between actions, or synchronizing with external servers.

Parameters:

  • None.

Returns:

  • An integer value representing the current Unix timestamp (in seconds).

Example:

// Store the time when the motion was last detected
last_motion_time = time::now()

// ... later in the code ...

// Check if more than 60 seconds have passed since the last motion
if (time::now() > last_motion_time + 60) {
   ...
}

# time::uptime

Returns the number of seconds elapsed since the module was powered on or last restarted. This value is useful for monitoring system health, detecting unexpected restarts, or implementing time-based logic that should reset after a power cycle.

Unlike time::now(), which provides an absolute timestamp, time::uptime() provides a relative measurement that starts from zero each time the module boots.

Parameters:

  • None.

Returns:

  • An integer value representing the number of seconds since the module started.

Example:

// Log a warning if the module has been running for more than 24 hours
// (might indicate it hasn't been restarted for maintenance)
if (time::uptime() > 86400) {
    std::print("Module running for over 24 hours")
}

// Delay initialization logic until the module has been stable for 10 seconds
if (time::uptime() > 10) {
    // Safe to assume all sensors have initialized
    ...
}

// Detect if the module was recently restarted
if (time::uptime() < 60) {
    std::print("Module just started, initializing...")
}

# Math Operations

# math::abs

Calculates the absolute value of a number. If the input is negative, it returns the positive equivalent; if the input is positive, it returns the value unchanged. This function supports both integers and float types.

It is particularly useful for calculating the difference (delta) between two sensor readings without worrying about which value is larger (e.g., checking if the temperature has changed by more than 1 degree in any direction).

Parameters:

  • value: The number to process.

Returns:

  • A numeric value representing the non-negative value of the input. The return type matches the input type.

Example:

target_temp = 21.0
current_temp = 19.5

// Calculate the difference ignoring direction
delta = math::abs(target_temp - current_temp)

// If the difference is significant (more than 0.5 degrees), take action
if (delta > 0.5) {
    ....
}

# math::round

Rounds the given floating-point number to the nearest integer value. Halfway cases (where the fractional part is exactly 0.5) are rounded away from zero.

Parameters:

  • value: The number to be rounded.

Returns:

  • An integer representing the nearest whole number.

Example:

// Example 1: Rounding sensor data for display
rawTemp = 21.7
displayTemp = math::round(rawTemp) // Returns 22

// Example 2: Halfway cases (rounding away from zero)
val1 = math::round(2.5)  // Returns 3
val2 = math::round(-2.5) // Returns -3

// Setting a dimmer level (must be an integer 0-100)
calculatedLevel = 55.4
dimmerLevel = math::round(calculatedLevel) // Returns 55

# math::random

Returns a pseudo-random floating-point number in the range [0.0, 1.0). Each call produces a different value. The result can be scaled and combined with math::round to produce random integers within a desired range.

Parameters:

None.

Returns:

  • A float value greater than or equal to 0.0 and less than 1.0.

Example:

// Get a random value between 0.0 and 1.0
r = math::random()

// Generate a random integer between 1 and 100
n = math::round(math::random() * 100) + 1

// Random boolean decision (50/50)
if (math::random() < 0.5) {
    output::set("led", true)
} else {
    output::set("led", false)
}

# Base64 Operations

This section describes functions for encoding data using Base64 encoding schemes.

# base64::encode

Encodes a string into its Base64 representation using the standard alphabet (RFC 4648 Table 1). The output uses the characters A-Z, a-z, 0-9, +, / and is padded with = characters to ensure the output length is a multiple of 4.

Parameters:

  • data (string): The input string to encode.

Returns:

  • A string containing the Base64-encoded representation of the input.

Example:

// Encode a simple string
encoded = base64::encode("Hello, World!") // Returns "SGVsbG8sIFdvcmxkIQ=="

// Encode credentials for HTTP Basic Auth
credentials = str::concat("user", ":", "password")
auth = str::concat("Basic ", base64::encode(credentials))
http::set_header(client, "Authorization", auth)

// Empty string encodes to empty string
encoded = base64::encode("") // Returns ""

# base64::url_encode

Encodes a string into its Base64url representation using the URL-safe alphabet (RFC 4648 Table 2). The output uses the characters A-Z, a-z, 0-9, -, _ and does not include padding characters. This encoding is safe for use in URLs, query parameters, and JWT segments.

Parameters:

  • data (string): The input string to encode.

Returns:

  • A string containing the Base64url-encoded representation of the input, without padding.

Example:

// Encode for URL-safe usage
encoded = base64::url_encode("Hello, World!") // Returns "SGVsbG8sIFdvcmxkIQ"

// Build a JWT header and payload
header = base64::url_encode("""{"alg":"RS256","typ":"JWT"}""")
payload = base64::url_encode("""{"sub":"1234567890","name":"John"}""")
signing_input = str::concat(header, ".", payload)

// Note the difference from standard base64:
// base64::encode("subjects?_d")     returns "c3ViamVjdHM/X2Q="
// base64::url_encode("subjects?_d") returns "c3ViamVjdHM_X2Q"

# Cryptographic Operations

This section describes functions for performing cryptographic operations.

# crypto::rs256_sign

Signs data using an RSA private key with the RS256 algorithm (RSASSA-PKCS1-v1_5 using SHA-256). This function is typically used for creating JWT signatures or other authenticated messages.

Parameters:

  • data (string): The data to sign.
  • private_key_pem (string): The RSA private key in PEM format.

Returns:

  • A string containing the Base64url-encoded signature.

Example:

// Create a JWT signature
header = base64::url_encode("""{"alg":"RS256","typ":"JWT"}""")
payload = base64::url_encode("""{"sub":"device-001","iat":1700000000}""")
signing_input = str::concat(header, ".", payload)

// Sign with the private key
signature = crypto::rs256_sign(signing_input, private_key)

// Assemble the complete JWT
jwt = str::concat(signing_input, ".", signature)

# Asynchronous Operations

This section describes callback functions that can be defined to handle asynchronous events and responses.

Reference documentation for Volang standard library functions and modules.