Introduction

This document is a work-in-progress and is not normative. It only applies to version 4 of the Rant language.

Found an error? Want something improved? Submit an issue or a pull request!

This documentation is a reference for the language features, standard library, and major runtime features of version 4.x of Rant. While every effort is made to make the documentation as approachable as possible, it is not primarily intended to be a beginner's guide to the language.

Scope

This documentation is divided into the following chapters:

Other helpful resources

For API documentation, see docs.rs.

For a step-by-step beginner's guide to Rant, see the Getting Started page on the Rant website.

Language features

Text

In Rant, text is considered as a combination of two types of tokens: fragments and whitespace.

Any string of text is a valid Rant program.

Fragments

A fragment is defined in Rant as any sequence of characters that:

  1. contains no whitespace,
  2. contains no reserved characters (e.g. braces, brackets, etc.)

Rant will output any fragment verbatim.

# Prints "Hello"
Hello

Whitespace

All sequences of whitespace in a Rant program are normalized to a single space by default.

Rant is very selective about the whitespace it prints. By default, whitespace included in a Rant program is only printed if it is between any two of the following:

  • Fragments
  • Numbers
  • Getters
  • Blocks containing any of the above
  • Blocks that satisfy the aforementioned requirement

All other whitespace (including line breaks) is ignored. Escaped whitespace (\t, \s, etc.) are exempt from this rule.

TODO: Add examples

Hinting

Because Rant's whitespace rules are quite strict, it is sometimes necessary to give the compiler additional information about the structure of your output.

This can be achieved by adding hints (symbolized by a ') to your code. A hint informs the compiler that the next element of code will print to the output, and that the whitespace between it and another printing element should be included.

The following elements are implicitly hinted:

  • Numbers
  • Variable getters
  • Blocks containing at least one fragment, number, getter, hinted element, or nested block that meets this requirement

The act of a block inheriting a hint from its contents is known as "taking the hint."

# Implicitly hinted (block contains fragments)
The coin landed on {Heads|Tails}.

# Explicitly hint the function call so the compiler knows it prints something
Your lucky number is '[num:1;100].

Sinking

If you want to suppress the output of a code element, you can sink it with the _ operator.

Sinking an element has the opposite effect of hinting: it will be marked as "non-printing" and any whitespace between it and another non-printing element will also be ignored.

Only fragments and numbers cannot be sinked; the _ character will be interpreted as text in such contexts.

Blocks that are sinked cannot take a hint.

{Now you see me...}     # Prints like normal
_{Now you don't.}       # Prints nothing
Goodbye _{cruel} world! # Prints "Goodbye world!"
_Fragment               # Prints "_Fragment", as fragments cannot be sinked

String literals

If you want to treat any text as a single fragment, you can wrap it in a string literal using double quotes:

"    This text is indented"

They even work over several lines:

"This
text
has
several
lines"

Blocks

A block is a unit of Rant code subdivided into zero or more parts.

Syntax

A block is defined by a set of braces containing zero or more sections of Rant code separated by vertical pipes (|). Here are a few examples:

{}          # Empty block
{A}         # Block with one element
{A|B}       # Block with multiple elements

Use cases

Blocks serve several purposes in Rant, ranging from simple element selection to collections and even loops. They can contain any kind of Rant code—even other blocks.

Item selection

In their simplest form, blocks resolve a single element from their body and add it to the output.

# Randomly prints "Heads" or "Tails" with uniform probability
{Heads|Tails}

By default, blocks select a random element with uniform probability, but the selection strategy can be customized if needed using a selector.

Entanglement

A group of blocks can be "entangled" so that they all coordinate their selections.

# Create a selector and store it in a variable
<$sync = [mksel:one]>
# Both blocks use the `sync` selector, so they're entangled
[sel:<sync>]{Dogs|Cats} say \"[sel:<sync>]{woof|meow}!\"

##
Possible outputs:
- Dogs say "woof!"
- Cats say "meow!"
##

Variable scope

Blocks also act as scopes for local variables. Any variables created inside of a block are destroyed immediately after the block resolves.

Stored blocks

Blocks can also be stored in variables for later use. To treat a block like a value instead of resolving, you need to prefix it with the * symbol. If the block requires a hint or sink, the * must appear after it.

<$cool-block = *{Ohhh yeah!}>

Then you can resolve it later with the [resolve] function:

[resolve:<cool-block>] # "Ohhh yeah!"

Blocks are similar to closures when used this way, but only in that they contain code you can store and run later. Stored blocks make several guarantees that closures and functions cannot:

  1. They never require arguments
  2. They never capture variables
  3. They will always consume attributes

Restrictions on function bodies and dynamic keys

Blocks used as function bodies and dynamic accessor keys are "linear" blocks: they can only contain one element and never consume attributes. Attempting to use a multi-element block in these contexts will cause a compiler error.

This design consideration prevents function calls and accessors from unintentionally consuming attributes unless the user explicitly requests it by adding an inner block.

Functions

Functions contain code that can be executed arbitrarily. The Rant standard library includes several native functions, but you can also define your own.

Calling functions

Function calls use the following syntax:

# Function with no arguments
[func-name]

# Function with one argument
[func-name: arg1]

# Function with multiple arguments, delimited by ';'
[func-name: arg1; arg2]

Shadowed functions are still callable

The situation may occasionally arise where you accidentally (or intentionally) define a non-function variable with the same name as a function from a parent scope (e.g. a stdlib function) and then try to call it:

<$rep = "not a function">
[rep:10] # Wait a sec...
[sep:\n]
{
    # ...
}

Some people might (understandably) assume that this would crash the program, but this code actually still works!

When this happens, Rant will perform what is known as function percolation: the runtime will search each parent scope up to the global scope until it finds a function with the same name, and then call it as normal.

Function percolation only applies to function calls, so getters will still correctly retrieve the new variable instead of the function.

Function composition

When function calls are nested inside each other, they can become difficult to read and understand at a glance:

[e: [d: [c: [b: [a: 1]; 2]; 3]; 4]; 5] # Welcome to bracket hell

An alternative way to write nested function calls is by using iterative (rather than recursive) syntax-- a method known as function composition.

By using function composition, the same expression above can be rewritten with only a single pair of brackets:

[a: 1 & b: 2 & c: 3 & d: 4 & e: 5] # Much more readable!

In a composed function, the output of one function is fed into the next. By default, the output is inserted as the first argument; however, this can be changed by passing the composition value [] as an argument:

[$get-zipper] {
    {<add>|<sub>}
}

# Pass the output of [get-zipper] as the third argument for [zip]
[get-zipper & zip: (1; 2; 3); (4; 5; 6); []]

If the composition value is a function, you can call it directly using an anonymous call:

[$get-math-func] {
    {<add>|<mul>|<sub>|<div>}
}

# Get a random math function and call it, passing in two numbers
[get-math-func & ![]: 3.0; 2.0]

Defining functions

To define a function and assign it to a variable, the syntax is as follows:

# Define a parameterless function named `say-hello` that prints "Hello"
[$say-hello] {
    Hello
}

# Define a function `greet` with parameter `name` that prints a custom greeting
[$greet: name] {
    Hello, <name>!
}

# Define a function `flip-coin` that returns the value of either `heads` and `tails`
[$flip-coin: heads; tails] {
    {<heads>|<tails>}
}

Optional parameters

A function parameter can be marked as optional using the ? symbol. When the argument is omitted for an optional parameter, it will default to ~. Please note that any optional parameters must appear after all required parameters, and before any variadic parameter.

# Generates a map for a pet with a name and species (defaults to "dog")
[$gen-pet: name; species?] {
    @(
        name = <name>|
        species = [alt: <species>; dog]
    )
}

Variadic parameters

Functions support variadic parameters with the special symbols * and +.

A * parameter is optional and defaults to an empty list, while a + parameter is required and must contain at least one element.

Functions may only have up to one variadic parameter, and it must appear last in the signature.

[$how-many:items*] {
    [len:<items>]
}

[how-many: foo; bar; baz] # Outputs "3"

Closures

It is sometimes necessary to pass a function as a value rather than define it with a name, so that it can be called elsewhere later on. This can be done using closures, which are an implementation of anonymous functions with the ability to "capture" external variables.

Basic closure syntax with no parameters consists of a ? symbol inside brackets preceding the function body.

# Creates a closure and places it on the output
[?]{Hello from closure!}

Parameterized closures

If you want to add parameters to a closure, specify the parameter names as you would with a normal function inside the closure's signature:

[?: param1; param2]{ ... }

Calling anonymous functions

Calling an anonymous function uses the same syntax as regular function calls, but the function name is replaced by the ! symbol followed by an expression that provides the function (or closure) you want to call.

# Define a function that returns a parameterless box
[$get-anon-func] {
    [?]{Hello from anonymous function}
}

[![get-anon-func]]          # "Hello from anonymous function"

# Define a function that returns a box with a single parameter
[$get-greet-func] {
    [?:name]{Hello, <name>!}
}

[![get-greet-func]:Rant]    # "Hello, Rant!"

Behavior of captured variables

As mentioned previously, a closure can access variables defined outside of its body. When this happens, it "captures" a reference to that variable for use inside the function. As a result, changes made to a captured variable persist between calls.

Variable capturing can also be used to maintain a persistent state within a closure instance: even if the original variable falls out of scope, the closure still keeps it alive.

# Create a function with a persistent state
{
    <$foo-num = 1>
    # Define a function [next-foo] in the parent scope
    [$^next-foo] {
        # Modify `foo-num` from inside the function
        foo <$n = <foo-num>; foo-num = [add: <foo-num>; 1]; n>
    }
} # `foo-num` falls out of scope here, but [next-foo] can still access it

# Call [next-foo] multiple times
[rep:4][sep:\n]
{
    [next-foo]
}
##
  Output:

  foo 1
  foo 2
  foo 3
  foo 4
##

Limitations on variable capture

Capturing is currently only supported on variables accessed locally from the function body. Descoped and explicit global accessors do not capture variables.

Comments

Comments are used to annotate code without affecting its behavior. This is useful for adding descriptions to your code that offer further explanation of the functionality.

There are two types of comments in Rant: single-line and multi-line.

Single-line comments

A single-line comment begins with the # character and terminates at the end of the current line. They do not have to start at the beginning of a line.

# This is a comment
{A|B|C} # This is an inline comment

Multi-line comments

A multi-line comment can span multiple lines and is enclosed by two # characters on each end:

##
This is
a multi-line
comment
##

Escape sequences

It will, of course, be necessary to sometimes print reserved characters, or characters that cannot be typed on a standard keyboard. To this end, Rant offers escape sequences.

Character escape sequences

Any reserved (and most non-reserved) characters can be printed by prefixing each one with a backslash character:

\{This text will print with braces around it\}

Special escape sequences

Certain characters are not interpreted verbatim when escaped, and instead have special meaning. Here's a list of them:

Escape sequenceOutput character
\rCarriage return (U+000D)
\nLine feed (U+000A)
\sSpace (U+0020)
\tTab (U+0009)
\0Null character (U+0000)
\xXXUTF-8 character code [See below]

Unicode escape sequences

Escape sequences can also be used to print characters by their Unicode code point in hexadecimal format:

\xE4    # 'ä'

Data types

Rant has 10 built-in data types:

Type nameDescriptionPass type
stringSequence of UTF-8 charactersby-value
int64-bit signed integerby-value
float64-bit double-precision floatby-value
boolBoolean valueby-value
functionFunction/closureby-ref
listList of valuesby-ref
mapString-keyed collection of valuesby-ref
blockStored blockby-ref
specialHandle to internal runtime data, such as a selectorby-ref
emptyUnit type representing "null" valueby-value

The empty type

To represent the lack of a value, Rant has the empty type, which has only one possible value, represented by the token ~.

<$nothing = ~>    # i.e. <$nothing>
[type:<nothing>]  # empty

Boolean values

The two boolean values are represented by the keywords true and false. These keywords can still be used as fragments when not used alone, as their string representations are simply "true" and "false"; however, if they are passed on their own and need to be strings, you will need to put them in a string literal.

Type inference for expressions

In order to resolve type ambiguities, Rant makes a few basic assumptions:

Integers

Any number token without a decimal place becomes an int.

[type:123]  # int

Floats

Any number token with a decimal place becomes a float.

[type:123.0]  # float

Top-level fragments or whitespace

Any expression containing top-level text (i.e. not in a collection) evaluates to a string.

Whitespace at the start or end of an expression is ignored.

# Since there are fragments and whitespace, it's a string
[type:99 bottles of beer]  # string

# Since there is whitespace between the numbers, the value is still a string
[type:99 99]  # string

Multiple values in a sequence

If there are multiple values in an expression and at least one is non-empty, it evaluates to a string.

# Even though they are all integer tokens, they are treated as text
[type:10 20 30 40 50]  # string

Empties

Expressions containing only empties evaluate to the empty value.

Expressions containing nothing also evaluate to the empty value.

[type:~~]       # empty
[type:~]        # empty
[type:]         # empty

Variables and accessors

Variables enable you to store and retrieve values.

Rant is a dynamically-typed language, meaning that variables have no set type; for example, you can initialize a variable with an integer, and change it later on to a string.

Accessors

Accessors are the central means of reading and writing data in Rant, and are denoted by a pair of angle brackets.

They have several uses:

  • defining new variables
  • getting/setting variable values
  • accessing elements in collections

Accessors have three subtypes: definitions, setters, and getters.

Definitions

A definition creates a new variable in the current scope. They are denoted by placing a $ symbol before the variable name.

It is optional to assign a value in a definition. You can leave the assignment part out and it will be initialized to the empty value (~).

# Define a variable `name` but leave it empty (value is `~`)
<$name>

# Define a variable `name` and assign it the string "Nick"
<$name = Nick>

Setters

A setter modifies an existing variable or value.

# Define a variable
<$name = Bob>
# Overwrite value on existing variable `name`
<name = Susan>

Along with setting variables, setters can also write to specific elements of collections.

<$numbers = (1; 2; 3)>
<numbers/0 = 4> # list is now (4; 2; 3)

Getters

A getter retrieves some value and prints it to the output.

Attempting to retrieve a variable that does not exist causes a runtime error.

# Get value of `name` (note the lack of '$')
<$name = Robin>
My name is <name>.\n # Prints "My name is Robin."

Providing a default value when a getter fails

If a getter fails (due to a missing variable, index, or key), the user may sometimes want to provide a default value to fall back on instead of raising an error. To do this, a fallback expression can be added to the end of the getter.

A fallback expression only runs if the data requested by the associated getter is not found; otherwise, it is ignored completely.

Example

# Store a value to use as a fallback
<$fallback = "I don't exist!">

{
    # Define a variable `foo`
    <$foo = "I exist!">

    # Get `foo` with fallback
    <foo ? <fallback>> # -> "I exist!"
}\n

# Getting `foo` again out of scope will trigger the fallback
<foo ? <fallback>> # -> "I don't exist!"

# Getting `foo` without a fallback here would crash the program
<foo> # error

Multi-part accessors

To aid readability, Rant also allows you to place several access operations in a single accessor block. Simply end each operation with a semicolon; the final semicolon is optional and may be omitted.

<$first-name = John; $last-name = Smith; $full-name = <first-name>\s<last-name>;>

Anonymous accessors

In order to access a key or index on a value, it normally must be stored in a variable; however, anonymous accessors remove this requirement by accepting a raw value instead of a variable name.

To create an anonymous accessor, replace the variable name in the accessor with ! followed by a single expression (block, collection, accessor, function call, etc.) and write the rest of your access path as normal.

Both getters and setters can be made anonymous; however, anonymous setters must include at least one index or key.

Example 1

# Create two lists
<$list1 = (1;2;3;4;5;6;7;8)>
<$list2 = (a;b;c;d;e;f;g;h)>

# Create a function that returns one of two lists
[$get-either-list]{{<list1>|<list2>}}

# Call [get-random-list] and get the 4th item (index 3) of the returned list
<![get-either-list]/3> # Returns either '4' or 'd'

Example 2

# Get the first letter of a string literal
<!"hello"/0> # Returns 'h'

Variable scope

Variables only live as long as the expression or block in which they are defined.

Blocks, function arguments, setter expressions, dynamic keys, and function bodies are all examples of variable scopes. As soon as a scope is finished running, all variables defined within it are discarded.

Child scopes

Child scopes inherit variables from parent scopes. In addition, they may define their own variables.

{
    <$a = 1>
    {
        <$b = 2>
        [add: <a>; <b>] # Outputs "3"
    }
}

Shadowing

Variables in a parent scope can be temporarily hidden ("shadowed") by defining a variable of the same name in a child scope.

When the child variable goes out of scope, the shadowed parent variable will once again become accessible.

# Define variable `a` in parent scope
<$a = foo>
a is <a>\n
{
    # Define another variable `a` in child scope
    # Parent variable is not affected
    <$a = bar>
    a is <a>\n
}
# Parent variable takes over after child scope exits
a is <a>

##
    Output:

    a is foo
    a is bar
    a is foo
##

The 'descope' operator

If a child scope shadows a parent variable and you want to access the parent variable explicitly, you can use the descope operator.

To do this, prefix the variable name with a ^ character.

<$a = foo>
{
    <$a = bar>
    Shadowed: <a>\n
    Descoped: <^a>\n
}

##
    Output:

    Shadowed: bar
    Descoped: foo
##

Adding more than one ^ character in a row will skip up to that many scopes. These operations are called "n-descopes", where n is the number of scopes skipped: For example, <^^foo> is a 2-descope, <^^^foo> is a 3-descope, and so on.

# Define `test`
<$test = foo>
{
    # Shadow `test`
    <$test = bar>
    {
        # Shadow `test` again
        <$test = baz>
        <^^test> <^test> <test>
    }
}
## Output: "foo bar baz"

While the compiler imposes no explicit limit on descope depth, use cases for large descope depths are rare and it is recommended to avoid them when possible.

# An example of a 360-descope.
<^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
foo>

Explicitly accessing global variables

The top level of a Rant program implicitly defines a local variable scope; because of this, top-level variables will only persist for the duration of the program.

To explicitly access a variable in the global scope, prefix the path with the / character.

Similarly to the descope operator, this method also allows you to override shadowing on globals.

<$foo = Hello from program scope!>
<$/foo = Hello from global scope!>

Local: <foo>\n
Global: </foo>\n

##
    Output:

    Local: Hello from program scope!
    Global: Hello from global scope!
##

Collections

Rant's variable system has two collection types: lists and maps.

Lists

Lists are resizable arrays of values. They may contain multiple types of values.

Lists are initialized using a pair of parentheses containing the list elements, separated by semicolons:

# A pair of parentheses is treated like an empty list
<$empty-list = ()>

# Initialize a list with one empty element
<$one-empty = (~)>

# Create a list with two empty elements
<$two-empty = (;)> # Short for (~;~)

# Create a list of numbers
<$lucky-numbers = (1; 2; 3; 5; 7; 11; 13)>

# Create a list of lists
<$important-lists = ((A; B; C); (D; E; F))>

List indexing

List indices start at 0 and can be accessed by adding the index to the accessor path.

You can also use negative indices to access items relative to the end of a list, starting from -1 and going down.

# Create a list
<$list = (1;2;3;4;5;6;7;8;9)>

# Get first item
<list/0>\n      # 1

# Get second item
<list/1>\n      # 2

# Get last item
<list/-1>\n     # 9

# Get second to last item
<list/-2>\n     # 8

# Change third item from 3 to -3
<list/2 = -3>
<list/2>        # -3

Maps

Maps are un-ordered collections of key-value pairs, where each key is unique. Map keys are always strings: even if you try to use a non-string as a map key, it will be automatically converted to a string first.

Maps use similar syntax to lists, but with some extra requirements:

  • The @ symbol must appear before the opening ( to identify the collection as a map.
  • Each element must be a key-value pair, with the key and value separated by an = symbol.

Map keys come in two flavors:

  • Static keys: Evaluated at compile-time. They must be identifiers or string literals.
  • Dynamic keys: Evaluated at run-time. They must be blocks.
# Create an empty map
<$empty-map = @()>

# Create a map with various value types
<$secret-key = occupation>
<$person = @(
    name = John;
    age = 25;
    "favorite color" = {red|green|blue};
    {<secret-key>} = programmer
)>

Retrieving values from collections

Variable accessors can access individual elements in lists and maps by using the access operator /.

<$person = @(
    name = Bob;
    age = 30;
    hobbies = (programming;hiking)
)>

name = <person/name>\n
age = <person/age>\n
hobbies = [join:,\s;<person/hobbies>]

##
    Output:

    name = Bob
    age = 30
    hobbies = programming, hiking
##

Additionally, a variable access path does not need to be made out of entirely constants. You can use a block to resolve to a key or index.

Map example

<$my-map = @(
    a = foo; b = bar; c = baz
)>

<my-map/{{a|b|c}}>
# Outputs "foo", "bar", or "baz"

List example

<$my-list = (foo;bar;baz)>

<my-list/0>             # "foo"
<my-list/1>             # "bar"
<my-list/2>             # "baz"
<my-list/{[num:0;2]}>   # "foo", "bar", or "baz"

Runtime features

This section deals with features specific to the Rant runtime.

Attributes

Attributes are special runtime settings that modify block behavior, such as how many times a block repeats or how it selects which element to run.

Attributes are set by calling the standard library's attribute functions and stored in the program's attribute stack. Each frame of the attribute stack stores a full set of attributes.

When a block resolves, it consumes attributes from the topmost attribute frame and replaces them with their default values.

Frames can be added to and removed from the attribute stack to preserve sets of attributes for later use or limit their scope.

Repetitions

By setting the repetitions attribute, you can control how many times the next encountered block will run. This attribute is set with the [rep] function.

Example

[rep:10]{[step]\n}
# Output:
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10

Separators

The separator attribute controls what is printed between each block repetition. It is set using the [sep] function.

Example

[rep:4][sep:" and "]
It just keeps {going}...

# Output:
# It just keeps going and going and going and going...

Selectors

The selector attribute controls how Rant chooses which branch of a block to take. It does this using a special state machine object, which must be created separately but can be shared between blocks to coordinate their behavior.

Example

# Print every element of the block in a random order with no duplicates

<$s=[mksel:deck]>  # Create a "deck" selector
[sel:<s>]          # Apply the selector
[rep:all]          # Set repetitions
[sep:,\s]          # Set separator
{A|B|C|D|E|F|G|H}

# Output
# F, C, E, G, B, H, D, A

Attribute frames

The Rant runtime maintains a stack of "attribute frames", which store set of attributes. The resolver always reads attributes from the topmost frame in the attribute frame stack. This stack can be used to limit the scope of block attributes.

The attribute frame stack can be manipulated with the [push-attrs] and [pop-attrs] functions.

Formatters

Rant can passively apply various filters to program output.

The following formatters are currently supported:

  • Capitalization
  • Numbers
  • Whitespace normalization

Case formatter

The case formatter can automatically adjust capitalization of output text according to a variety of filter presets.

Number formatter

The number formatter controls how Rant converts numeric values (integers and floats) to strings in the program output.

Whitespace formatter

The whitespace formatter controls how Rant normalizes printable whitespace found in programs.

Modules

Rant supports basic dependency management via a module system. Modules are libraries of Rant functions that you can write and load into other Rant programs for use. Each module resides in its own source file.

Writing modules

A module is simply a Rant program that returns a map containing the module's contents. Similar to a regular Rant program, modules are expected have the .rant file extension.

Below is a basic example of a very simple module:

# lol.rant: Laughter Generation Module

<$module = @()>

[$module/cackle: len?] {
  [rep:[alt:<len>;2]]{h{a|e|ee|ue|aa|o}}!!!
}

<module>

Importing and using modules

A module can be imported using the [require] function. For this example, we'll import the lol module previously shown.

# Looks for `lol.rant` and imports it as map variable `lol`.
# You don't need to specify the file extension.
[require: lol]

# Call the `[cackle]` function from the module
[lol/cackle: 16]
# --> haahoheehehehuehohoheehahohaaheehoheehaa!!!

Imported modules are cached by the Rant context running the program. If you import the same module twice, it will be fetched from cache.

Compiler errors in modules

Rant needs to compile modules before they can be imported. If a module fails to compile, a runtime error will be triggered with the compiler error list.

Where Rant looks for modules

When you run [require], Rant looks for the module in the following locations in-order:

  1. The requesting program's file location
    • Skipped if the program was not loaded from a file
  2. The local modules path
    • Defaults to the current working directory, but the host app can reconfigure it
  3. The global modules path
    • Set by the RANT_MODULES_PATH environment variable
    • Can be disabled by the host app

If Rant cannot locate a module with a matching path and name, it will trigger a runtime error.

Relative paths in [require]

The [require] function can also accept a relative path to a module. This makes it possible to access modules in subfolders of any of the module search locations.

For example, if your application has a rant_modules subfolder in its main directory, you can import modules from it like this:

# Imports `my-module`
[require:rant_modules/my-module]

Rant Standard Library

The Rant Standard Library contains many useful and some essential functions that are natively implemented in the Rant runtime itself.

Categories

Below is a list of broad categories that standard library features fall under:

General functions

Functions which fall under no specific category or are widely useful go here.

Attribute/Control flow functions

Functions for performing conditional branching, setting attributes, and other forms of control flow.

Collection functions

Functions that read from, manipulate, or create new collections.

Generator functions

Functions for generating various types of values, often using randomization.

Formatting functions

Functions for changing the output formatting.

String functions

Functions for working with strings.

Boolean functions

Functions for performing boolean algebra.

Comparison functions

Functions for comparing values to each other.

Math functions

Various arithmetic functions.

Conversion functions

Functions for converting values to other types.

Verification functions

Functions for testing values against various criteria.

Assertion functions

Functions for verifying program state and function output.

Constants

Special constant values, such as the language version in use.

Standard Library: General functions

[alt: a; b+]

any or empty

Prints the first argument that isn't the empty value ~. If all arguments are ~, prints ~.

[call: func; args]

any or empty

Calls the function func with the argument list args.

Errors

Causes a runtime error if either of the following are true:

  • func isn't a function
  • args isn't a list

[copy: value]

any

Returns a shallow clone of value.

[either: condition; true-val; false-val]

any

Returns true-val if condition is true, or false-val if condition is false.

condition must be of type bool.

[fork: seed?]

Forks (overrides) the current RNG with a new RNG seeded by both the old RNG's seed and the specified seed. If no seed is provided, a random one is generated.

The seed value must be an int or string.

Errors

Causes a runtime error if seed is neither an int nor string.

Example

# Entangles {yee|woo} with {haw|hoo}, i.e. forces them both to pick the same index

[fork:a]{yee|woo}[unfork]-
[fork:a]{haw|hoo}[unfork]!

# Output is either "yee-haw!" or "woo-hoo!"

[nop: args*]

Does absolutely nothing. Intended as a convenience function for use as a default/placeholder callback.

[require: module-path]

Imports the module at the specified relative path and assigns it to a local variable with a name matching the file name.

Rant requires module files to have the .rant extension in order to load them; as such, it is not necessary to supply the file extension in the path.

Example

# Import module `my-module`
[require: my-module]

# Call `hello-world` function in `my-module`
[my-module/hello-world]

[resolve: block]

any or empty

Resolves the specified block.

Errors

Causes a runtime error if the provided value is not a block.

[seed]

int

Returns the seed value of the currently active runtime RNG.

[type: value]

string

Returns the name of value's type. The type name can be any of the following:

  • string
  • float
  • int
  • bool
  • function
  • list
  • map
  • special
  • empty

[unfork]

Removes the last RNG created by [fork] and resumes use of the previous RNG.

Standard Library: Attributes & Control flow

[break: value?]

Exits the current repeater, optionally overwriting the iteration output with value.

Errors

Causes a runtime error if called outside of a repeater.

[continue: value?]

Interrupts execution of the current repeater iteration and proceeds with the next one, optionally overwriting the iteration output with value.

Errors

Causes a runtime error if called outside of a repeater.

[count-attrs]

int

Prints the current size of the attribute frame stack.

[else]

Marks the next block as conditional and causes it to resolve iff the last block was conditional and its condition evaluated to false.

[else-if: condition]

Marks the next block as conditional and causes it to resolve iff the following are true:

  • condition is true
  • The last block was conditional and its condition evaluated to false

[if: condition]

Marks the next block as conditional and causes it to resolve iff condition is true.

[mksel: selector-mode]

special

Creates and returns a selector with the specified mode.

Selector modes

Mode nameDescription
randomSelects a random element each time.
oneSelects the same, random element each time.
forwardSelects in a wrapping sequence from left to right.
reverseSelects in a wrapping reverse sequence from right to left.
deckSelects each element once in a random sequence, then reshuffles.
deck-loopSelects each element once in a wrapping random sequence, without reshuffling.
deck-clampSelects each element once in a random sequence, repeating the final element.
forward-clampSelects from left to right, then repeats the right-most element.
reverse-clampSelects from right to left, then repeats the left-most element.
pingSelects from left to right, switching directions when a boundary is reached.
pongSelects from left to right, switching directions when a boundary is reached.
no-doubleEnsures that no one element index is selected twice in a row.

[push-attrs]

Pushes a new attribute frame onto the attribute frame stack, overriding the previous one.

[pop-attrs]

Removes the topmost attribute frame from the attribute frame stack, handing control back to the previous one.

Errors

Popping the last attribute frame results in a runtime error.

[rep: reps]

Sets the repetition count for the next block to reps. The value of reps must either be a non-negative int or one of the special modes listed below.

Special rep modes

Mode nameDescription
onceRun the block only once. (default behavior)
foreverRepeat the next block until explicitly stopped.
allRepeat as many times as there are elements in the next block.

Examples

# Print the fragment 3 times
[rep:3]{ha}
# hahaha
# Print the fragment between 3 and 8 times
[rep:[num:3;8]]{ha}
# hahahaha
# hahahahahahaha
# hahaha
# hahahahaha
# ...

[return: value?]

Returns from the current function, optionally overriding the function's output with a value.

[sel: selector]

Sets the selector for the next block.

[sep: separator]

Sets the separator value for the next block to separator. The value of separator may be a string, number, or function. If it is a function, it will be called separately for each usage and its return value will be printed.

Examples

# Print comma-separated list of numbers 1-10
[rep:10][sep:,\s]{[step]}

##
  Output:
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10
##
# Print list of numbers 1-10 separated by random sequence of spaces and newlines
[rep:10][sep:*{\n|\s}]{[step]}

##
  Output:
  1 2 3
  4 5 6 7 8
  9
  10
##

Standard Library: Collection functions

[assoc: keys; values]

map

Creates a map from a list of keys and a list of values. Each key in keys will be matched with the value at the same index in values.

Raises a runtime error if the lists are not the same length.

Example

# Generate a map of the "Big Five" personality traits
# with random rating values that add up to 50
<$personality = 
  [assoc:
    (ope; con; ext; agr; neu);
    [shred: 50; 5; [rand: 1; 10]] # Use random variance to reduce trait deviation
  ]
>

# Print the values
[whitespace-fmt:verbatim]
"Openness to experience:"   <personality/ope>\n
"Conscientiousness:"        <personality/con>\n
"Extraversion:"             <personality/ext>\n
"Agreeableness:"            <personality/agr>\n
"Neuroticism:"              <personality/neu>\n


##
  EXAMPLE OUTPUT:

  Openness to experience:   7
  Conscientiousness:        11
  Extraversion:             5
  Agreeableness:            12
  Neuroticism:              15
##

[clear: collection]

Removes all elements from a list or map.

Errors

Causes a runtime error if collection is not a list or map.

[filter: list; predicate]

list

Runs a predicate function against all items in a list and returns another list containing only the values that the predicate returned true on.

The predicate function must accept a single parameter and return a bool value.

Examples

Filter a list of numbers by only those divisible by 3
<$numbers = (1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12)>
<$multiples-of-three = [filter: <numbers>; [?:x] { [is-factor: <x>; 3] }]>
[join: ,\s; <multiples-of-three>]
# -> 3, 6, 9, 12
Filter a list of numbers by only odd numbers
<$numbers = (1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12)>
# `is-odd` is a function, so we can simply pass it in as the predicate!
<$odd-numbers = [filter: <numbers>; <is-odd>]>
[join: ,\s; <odd-numbers>]
# -> 1, 3, 5, 7, 9, 11
Filter a list of words to only those that are 3 letters or less
<$words = [split: "the quick brown fox jumps over the lazy dog";\s]>
<$short-words = [filter: <words>; [?:word] { [le: [len: <word>]; 3] }]>
[join: \s; <short-words>]
# -> the fox the dog

[index-of: list; element]

int or empty

Returns the index of the first occurrence of element in list. If no match is found, returns ~.

Examples

<$letters = (A; A; B; C; C; D; E)>
[index-of: <letters>; A & assert-eq: 0]
[index-of: <letters>; C & assert-eq: 3]
[index-of: <letters>; E & assert-eq: 6]
[index-of: <letters>; F & assert-eq: ~]

[join: separator; list]

any*

Prints the elements of a list in order separated by the separator value.

[last-index-of: list; element]

any

Returns the index of the last occurrence of element in list. If no match is found, returns ~.

<$letters = (A; A; B; C; C; D; E)>
[last-index-of: <letters>; A & assert-eq: 1]
[last-index-of: <letters>; C & assert-eq: 4]
[last-index-of: <letters>; E & assert-eq: 6]
[last-index-of: <letters>; F & assert-eq: ~]

[map: list; map-func]

list

Applies a function to each item in a list and returns another list with the results in the same order.

The predicate function must accept a single parameter, but can return anything.

Example

# Multiple each element of a list by 10
<$numbers = (1; 2; 3; 4; 5; 6; 7; 8; 9; 10)>
<$tens = [map: <numbers>; [?:x] { [mul: <x>; 10] }]>
[join: ,\s; <tens>]
# -> 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

[oxford-join: comma; conj; comma-conj; list]

any*

A variant of the join function for conveniently formatting comma-separated lists.

Three distinct separator values are required:

  • comma: the comma value, which separates items except for the last two
  • conj: the conjunction value, which is only used to separate items in pairs (lists of 2)
  • comma-conj: the comma-conjunction value, which separates the final two values

These arguments can be used to configure several aspects of list formatting-- namely, the inclusion of the Oxford comma or the choice of conjunction separating the final two items.

The separator values are applied as follows:

  • Lists of 1 item use no separators.
  • Lists of 2 items use conj to separate the two items.
  • Lists of 3 or more items separate the final two items with comma-conj; all others use comma.

Examples

Print lists with Oxford comma
<$numbers = ()>
[rep: 5][sep: \n]
{
  [push: <numbers>; [step]]
  [oxford-join: ,\s; \sand\s; ,\sand\s; <numbers>]
}

##
  OUTPUT:

  1
  1 and 2
  1, 2, and 3
  1, 2, 3, and 4
  1, 2, 3, 4, and 5
##
Print lists without Oxford comma
<$numbers = ()>
[rep: 5][sep: \n]
{
  [push: <numbers>; [step]]
  [oxford-join: ,\s; \sand\s; \sand\s; <numbers>]
}

##
  OUTPUT:

  1
  1 and 2
  1, 2 and 3
  1, 2, 3 and 4
  1, 2, 3, 4 and 5
##

[push: list; value]

Appends a value to the end of a list.

[pop: list]

any

Removes the last value from a list and prints it.

[insert: collection; value; pos]

Inserts value into a list or map at the position pos.

If collection is a list, pos must be an int.
If collection is a map, pos may be any non-empty value.

Errors

Causes a runtime error if any of the following are true:

  • collection is not a list or map
  • pos is an unsupported type for the provided collection
  • collection is a list and pos is out of range

[len: obj]

int

Prints the length of obj.

For strings, this is the number of bytes; for lists and maps, this is the number of elements. All other value types give a length of 1.

[remove: collection; pos]

Removes the value at the pos from a list or map.

If collection is a list, pos must be an int.
If collection is a map, pos may be any non-empty value.

Errors

Causes a runtime error if any of the following are true:

  • collection is not a list or map
  • pos is an unsupported type for the provided collection
  • collection is a list and pos is out of range

[shuffle: list]

Shuffles the elements of a list in-place.

Example

# Shuffles a list of letters and concatenates them into a single string
<$letters = (A;B;C;D;E;F;G;H;I;J;K;L)>
[shuffle: <letters>]
[join: ; <letters>]

# ~> GKIBCHLEJADF

[shuffled: list]

list

Creates a shuffled copy of a list.

Example

# Shuffle the words in a string
<$message = "the quick brown fox jumps over he lazy dog">
[join: \s; [shuffled: [split: <message>; \s]]]

# ~> jumps fox quick dog lazy the brown the over

[sift: list; target-size]

Removes random elements from a list in-place until the number of elements in the list reaches target-size. If the number of elements in the list is less than or equal to target-size, this function does nothing.

Example

# Remove a random element from a list and print the contents at each iteration
<$list = [split: "Sphinx of black quartz, judge my vow."; \s]>
[rep: [len: <list>]]
[sep: \n]
{
  # Print the current list contents
  [join: \s; <list>]
  # Sift the list to n - 1
  [sift: <list>; [sub: [len: <list>]; 1]]
}

##
  EXAMPLE OUTPUT:

  Sphinx of black quartz, judge my vow.
  Sphinx of black quartz, my vow.
  Sphinx of black quartz, vow.
  Sphinx of black vow.
  Sphinx black vow.
  Sphinx vow.
  vow.
##

[sifted: list; target-size]

list

Returns a copy of a list with random elements removed until the number of elements in the list copy reaches target-size. If the number of elements in the list is less than or equal to target-size, this function simply returns an exact copy of the original list.

Example

# Create a random subset of abilities for a character

<$char-traits = (
  berserk;    xray-vision;  speaks-to-bees; 
  vampirism;  flying;       telekinesis; 
  many-legs;  high-jump;    bee-allergy
)>

<$npc = @(
  name = "Foo Bar";
  traits = [sifted: <char-traits>; 2];
)>

# Print character info
<npc/name>: '[join: ,\s; <npc/traits>]

# ~> Foo Bar: speaks-to-bees, many-legs

[squish: list; target-size]

Merges random adjacent elements in a list using addition until the number of elements in the list reaches target-size. If the number of elements in the list is less than or equal to target-size, this function does nothing.

Example

# Merge random items in a number list
<$numbers = (100; 100; 100; 100; 100; 100; 100; 100; 100; 100)>

# Print the original list
Before: '[join: ,\s; <numbers>]\n

# Squish the list down to 5 elements
[squish: <numbers>; 5]

# Print the modified list
After: '[join: ,\s; <numbers>]\n

##
  EXAMPLE OUTPUT:

  Before: 100, 100, 100, 100, 100, 100, 100, 100, 100, 100
  After: 100, 200, 100, 400, 200
##

[squished: list; target-size]

list

Returns a copy of a list with random adjacent elements merged using addition until the number of elements in the list copy reaches target-size. If the number of elements in the list is less than or equal to target-size, this function simply returns an exact copy of the original list.

[sort: list]

Sorts the elements of a list in-place in ascending order.

[sorted: list]

list

Creates a copy of a list with its elements sorted in ascending order.

[sum: list]

any

Adds the elements of a list together from left to right and prints the result.

[take: collection; pos]

any

Removes the value at pos from a list or map and prints it.

If collection is a list, pos must be an int.
If collection is a map, pos may be any non-empty value.

Errors

Causes a runtime error if any of the following are true:

  • collection is not a list or map
  • pos is an unsupported type for the provided collection
  • collection is a list and pos is out of range

Errors

Causes a runtime error if collection is a list and the pos is out of range.

[translate: list; map]

list

Matches each item in a list to a map and returns a list with the corresponding map values. Values that have no corresponding key in the map are passed through as-is.

Example

# Constructs a substitution cipher function
[$make-cipher: alphabet] {
  <
    $letters = [split: <alphabet>];
    $sub-letters = [shuffled: <letters>];
    $cipher = [assoc: <letters>; <sub-letters>];
    $cipher-rev = [assoc: <sub-letters>; <letters>];
  >
  # Return cipher functions
  @(
    encode = [?: message] {
      [sum: [translate: [split: <message>]; <cipher>]]
    };
    decode = [?: message] {
      [sum: [translate: [split: <message>]; <cipher-rev>]]
    };
  )
}

# Create a cipher and encode/decode a message
<$cipher = [make-cipher: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"]>
<$secret-message = "The quick brown fox jumps over the lazy dog.">
<$encoded-message = [cipher/encode: <secret-message>]>
<$decoded-message = [cipher/decode: <encoded-message>]>

# Finally, print the result
Original: \"<secret-message>\"\n
Encoded: \"<encoded-message>\"\n
Decoded: \"<decoded-message>\"\n

##
  EXAMPLE OUTPUT:

  Original: "The quick brown fox jumps over the lazy dog."
  Encoded: "kWj KGvaF QcDiq HDx CGpMJ DOjc AWj Tsyt fDN."
  Decoded: "The quick brown fox jumps over the lazy dog."
##

[zip: list-a; list-b; zip-func]

list

Returns a new list that combines each pair of items from the two input lists using the specified function. The lists do not need to be the same length; if there is a difference, it will be made up with empty values.

The zip-func must accept two parameters.

Examples

# Dot product
[$dot: a; b] {
  [zip: <a>; <b>; <mul> & sum]
}

[dot: (1; 2; 3); (4; 5; 6)] # 32

Standard Library: Generators

[dig: count?]

string

Prints a uniformly random decimal digit. If count is specified, repeats count times.

# Generate a random 32-character decimal string
[dig:32] # ~> 01952208554533821061510695429126

[digh: count?]

string

Prints a uniformly random lowercase hexadecimal digit. If count is specified, repeats count times.

Example

# Generate a random 32-character hex string
[digh:32] # ~> f4e5bef31ea02eac22f220e68e837587

[dignz: count?]

string

Prints a uniformly random non-zero decimal digit. If count is specified, repeats count times.

# Generate a random 32-character decimal string without zeros
[dignz:32] # ~> 92558761934966287236132511739511

[maybe: p?]

bool

Returns a bool value with p probability of being true.

p must be either a float or empty. If omitted, it will default to 0.5.

[rand: a; b]

int

Prints a random integer with uniform distribution between a and b (both inclusive).

Example

You roll the dice and get '[rand:1;6] and '[rand:1;6].
# You roll the dice and get 2 and 5.

[randf: a; b]

float

Prints a random float with uniform distribution between a (inclusive) and b (exclusive).

[rand-list: a; b; n]

list

Prints a list of n random integers with uniform distribution between a and b (both inclusive).

Example

# 2-dice roll

<$roll = [rand-list: 1; 6; 2]>
You rolled '[join: \sand\s; <roll>] for a total of '[sum: <roll>].

# ~> You rolled 5 and 3 for a total of 8.

[randf-list: a; b; n]

list

Prints a list of n random floats with uniform distribution between a (inclusive) and b (exclusive).

[shred: input; count; variance]

list

Generates a list of count random numbers that vary from each other by variance, and whose sum equals input.

All three arguments must be numbers, and count must be an int.

Errors

Causes a runtime error if:

  • input, count, or variance is not a number
  • count is zero or negative

Example

# Generate and print a list of 5 random numbers that add up to 1000
<$parts = [shred:1000;5;200]>
[join:" + ";<parts>] = [sum:<parts>]

##
Output:
170 + 378 + 83 + 189 + 180 = 1000
##

Standard Library: Formatting

[whitespace-fmt: mode?; custom-value?]

string or empty

Gets or sets the whitespace normalization mode. This does not affect whitespace in escape sequences and string literals.

Passing no arguments to this function will simply get the current mode.

Formatting modes

ModeDescription
default(Default) Normalizes all whitespace to a single ASCII space character (0x20).
verbatimPrints all printable whitespace as it appears in the code.
ignore-allIgnores all printable whitespace.
customReplaces all whitespace sequences with the custom-value argument.

Example

[whitespace-fmt: default] The quick brown fox    jumps over the lazy dog.
# Output: The quick brown fox jumps over the lazy dog.

[whitespace-fmt: verbatim] The quick brown fox    jumps over the lazy dog.
# Output: The quick brown fox    jumps over the lazy dog.

[whitespace-fmt: ignore-all] The quick brown fox    jumps over the lazy dog.
# Output: Thequickbrownfoxjumpsoverthelazydog.

[whitespace-fmt: custom; ...\s] The quick brown fox    jumps over the lazy dog.
# Output: The... quick... brown... fox... jumps... over... the... lazy... dog.

Standard Library: String functions

[lines: str]

list

Splits string str by line feed characters (0x0A, \n) and returns a list of the results.

[lower: str]

string

Converts string str to lowercase and returns the result.

[split: text; sep?]

list

Splits the input text by sep into a list of strings. If sep is omitted, splits into characters.

[seg: text; size]

list

Segments the input text into a list of strings of size length.

[upper: str]

string

Converts string str to uppercase and returns the result.

Standard Library: Boolean functions

[and: a; b; c*]

bool

Performs a short-circuiting boolean AND on the operands and returns the result.

[not: a]

bool

Returns the inverse of the input boolean value.

[or: a; b; c*]

bool

Performs a short-circuiting boolean OR on the operands and returns the result.

[xor: a; b]

bool

Performs a boolean XOR on the operands and returns the result.

Comparison

[eq: lhs; rhs]

bool

Returns true if lhs and rhs are equal.

[ge: lhs; rhs]

bool

Returns true if lhs is greater than or equal to rhs.

[gt: lhs; rhs]

bool

Returns true if lhs is greater than rhs.

[le: lhs; rhs]

bool

Returns true if lhs is less than or equal to rhs.

[lt: lhs; rhs]

bool

Returns true if lhs is less than rhs.

[neq: lhs; rhs]

bool

Returns true if lhs and rhs are not equal.

Standard Library: Math functions

[abs: num]

int or float

Calculates the absolute value of num.

Errors

Raises an error if num is an integer and the absolute value overflows.

[add: lhs; rhs]

any

Adds two values.

[ceil: val]

int

Gets the smallest integer that is greater than or equal to val.

val must be a float.

[div: lhs; rhs]

any

Divides two values.

[floor: val]

int

Gets the largest integer that is less than or equal to val.

val must be a float.

[frac: val]

float

Gets the fractional part of val.

val must be a float.

[max: values+]

any

Returns the largest value in values.

Any elements of type list in values will be expanded to their individual elements before the maximum value is calculated.

Examples

# Arguments can be single values
[max: 3; 2 & assert-eq: 3]

# Lists are treated as their individual elements
[max: (3; 2) & assert-eq: 3]

# Even alongside single values, lists are still expanded!
[max: 3; (4; -2; 0; 10); 6 & assert-eq: 10]

[min: values+]

any

Returns the smallest value in values.

Any elements of type list in values will be expanded to their individual elements before the minimum value is calculated.

Examples

# Arguments can be single values
[min: 3; 2 & assert-eq: 2]

# Lists are treated as their individual elements
[min: (3; 2) & assert-eq: 2]

# Even alongside single values, lists are still expanded!
[min: 3; (4; -2; 0; 10); 6 & assert-eq: -2]

[mod: lhs; rhs]

any

Gets the modulus of two values.

[mul: lhs; rhs]

any

Multiplies two values.

[mul-add: lhs; rhs; add]

any

Multiplies two values, then adds another value to the result.

[neg: n]

any

Negates a value.

[recip: n]

any

Gets the reciprocal of a value.

[sub: lhs; rhs]

any

Subtracts two values.

[sin: x]

float

Calculates the sine of x (in radians).

[cos: x]

float

Calculates the cosine of x (in radians).

[tan: x]

float

Calculates the tangent of x (in radians).

[asin: x]

float

Calculates the arcsine (in radians) of x.

[acos: x]

float

Calculates the arccosine (in radians) of x.

[atan: x]

float

Calculates the arctangent (in radians) of x.

[atan2: y; x]

float

Calculates the four-quadrant arctangent (in radians) of y / x.

Notes

Returns 0 if x or y is 0.

[sqrt: x]

float

Calculates the square root of x.

[pow: x; y]

Raises x to the power of y. Both x and y can be int or float.

Errors

Raises an overflow error if a x is an int and x ^ y causes an overflow.

Standard Library: Conversion functions

[float: value]

float or empty

Attempts to convert value to a float value and returns the result. If the conversion fails, returns empty value.

[int: value]

int or empty

Attempts to convert value to an int value and returns the reuslt. If the conversion fails, returns empty value.

[string: value]

string or empty

Attempts to convert value to a string value and returns the result. If the conversion fails, returns empty value.

Standard Library: Verification functions

[is-any: value]

bool

Returns true if value is any non-empty type.

[is-between: value; a; b]

bool

Returns true if value is bewteen a and b (both inclusive).

[is-bool: value]

bool

Returns true if value is of type bool.

[is-empty: value]

bool

Returns true if value is ~.

[is-even: number]

bool

Returns true if number is an even number.

[is-float: value]

bool

Returns true if value is of type float.

[is-int: value]

bool

Returns true if value is of type int.

[is-nan: value]

bool

Returns true if value is of type float and equal to NaN (Not a Number).

[is-number: value]

bool

Returns true if value is of type int or float.

[is-odd: number]

bool

Returns true if number is an odd number.

[is-string: value]

bool

Returns true if value is of type string.

Assertion

[assert: condition; message?]

Asserts that condition is true.

If condition is false, raises a runtime error; if true, does nothing.

The message displayed in the runtime error can be customized by passing a string to message.

Examples

# does absolutely nothing
[assert: true]

# [assertion error] assertion failed: condition was false
[assert: false]

# [assertion error] ooooops!
[assert: false; "ooooops!"] 

[assert-eq: actual; expected; message?]

Asserts that expected and actual are equal.

Like [assert], a custom error message can be provided.

[assert-neq: actual; unexpected; message?]

Asserts that unexpected and actual are not equal.

Like [assert], a custom error message can be provided.

Standard Library: Constants

RANT_VERSION

string

The version of the Rant language currently being used.

Glossary

Accessor

Accessors are program elements that read or write values. They are the primary means of interacting with variables.

There are three types of accessors: getters, setters, and definitions.

Access path

An access path is used to access variables or items in collections. They consist of /-delimited series of identifiers, keys, and indices.

Associative block

Associative blocks are a special block type dividing each element into left- and right-hand sides.

The left-hand side (LHS) associaties some value (usually a weight or case value) with the element, while the right-hand side (RHS) contains the code associated with the element.

This block type is typically used for conditional or weighted branching.

Attribute

Attributes modify block behavior, such as how many times a block repeats or how it selects elements.

Attributes can be set through attribute functions.

Attribute frame

An attribute frame is a full set of all available attributes. A program's attribute stack always contains at least one frame.

Attribute functions

A function that reads or writes an attribute.

Attribute stack

Each program maintains an "attribute stack", which stores and provides attributes to blocks. When a block runs, it consumes the attributes in the topmost frame of the attribute stack, replacing them with their default values.

The user can add and remove frames from the attribute stack to preserve attributes for later use.

Block

A block is a section of a Rant program subdivided into zero or more parts. Blocks act as variable scopes and can be combined with various constructs to produce a wide variety of behaviors.

Closure

A closure is a function that captures one or more variables from its environment.

Construct

A construct is a group of one or more runtime variables can be used to interact with the Rant runtime and customize various aspects of program behavior.

Definition

A definition is an accessor type that creates a new variable.

Descoping

Descoping is a type of variable access that explicitly retrieves a variable from a parent scope, overriding shadowing.

Dynamic key

A dynamic key is an access path key, consisting of a single-element block, that must be resolved before the containing path can be read. The block resolves to the final key or index that will be inserted into the path.

Formatter

A formatter is a runtime component that passively changes the output in some way.

Fragment

A fragment is a sequence of any non-whitespace, non-reserved characters found in a Rant program.

Function percolation

Function percolation is the ability of function calls to access functions from parent scopes despite shadowing by non-function variables. It is an implicit form of descoping.

Getter

A getter is an accessor type that retrieves a value from a variable or collection.

Hint

A hint is a compile-time operation that informs the Rant compiler that the next program element is expected to print to the output.

Identifier

A name assigned to a variable or module.

Identifiers enforce specific formatting requirements to ensure consistency:

  • Must contain at least one alphanumeric character.
  • Must only contain alphanumeric characters, underscores, and hyphens.

List

A list is an ordered collection of values, accessible by index.

Map

A map is a collection of key-value pairs, also known as an "associative array".

Module

A module is a library of Rant functions that can be loaded into another program using the [require] function.

Repeater

A block that can iterate multiple times. Repeaters are created by calling [rep].

Resolution

Resolution refers to the process of producing an output from a block, after which the block is referred to as 'resolved.'

Resolver

The component of the Rant runtime that handles block resolution.

Shadowing

A variable is shadowed (hidden) when it is overridden by a variable of the same name in a child scope.

Setter

A setter is an accessor type that writes to a variable or collection.

Sink

A sink is an operation that suppresses the output of a program element while still allowing its execution.