---
title: Language Reference
---
# Language Reference

Ripsaw is a simple and expressive scripting language which is used for [Workflow Steps](../workflow-steps.md) like the [uploaded crash report condition](../workflow-steps.md#issue-crash) to provide complete customization to data introspection and conditional actions.

## Syntax

### Comments

A _comment_ serves as program documentation and is identified with `#`. Each line must be preceeded with a `#` character.

```ripsaw
# a comment
```

### Expressions

Ripsaw programs are made up of literal and dynamic expressions, described more in detail below.  Expressions can be separated by newline or semicolon in any combination.

```ripsaw
# newline delimited expressions
del(item.user_info)
timestamp = .array[0].date
message = "hello world"
```

### Whitespace

Whitespace is any non-empty string as defined by the [Unicode `White_Space` property](https://en.wikipedia.org/wiki/Unicode_character_property#Whitespace)).

Ripsaw is a "free-form" language, meaning that all forms of whitespace serve only to separate tokens in the
grammar, and have no semantic significance.

### Keywords

Keywords are reserved words that are used for primitive language features, such as `if`, and cannot be used as
variable assignments or other custom directives. The following words are reserved:

* `abort`
* `as`
* `break`
* `continue`
* `else`
* `false`
* `for`
* `if`
* `impl`
* `in`
* `let`
* `loop`
* `null`
* `return`
* `self`
* `std`
* `then`
* `this`
* `true`
* `type`
* `until`
* `use`
* `while`

### Types

Ripsaw supports data in the following types:

* [Array](#array)
* [Boolean](#boolean)
* [Float](#float)
* [Integer](#integer)
* [Null](#null)
* [Object](#object)
* [Regular Expression](#regular-expression)
* [String](#string)
* [Timestamp](#timestamp)

## Expressions

### Type Literals

As in most other languages, **literals** in Ripsaw are values written exactly as they are meant to be
interpreted. Literals include things like strings, Booleans, and integers.

#### Array

An _array_ literal is a comma-delimited set of expressions that represents a contiguous growable array type.

```ripsaw
[]

["first", "second", "third"]

["mixed", 1, 1.0, true, false, {"foo": "bar"}]

["first-level", ["second-level", ["third-level"]]

[.field1, .field2, to_int!("2"), variable_1]

[
  "expressions",
  1 + 2,
  2 == 5,
  true || false
]
```

#### Boolean

A _Boolean_ literal represents a binary value which can only be either `true` or `false`.

#### Float

A _float_ literal is a decimal representation of a 64-bit floating-point type (specifically, the
"binary64" type defined in IEEE 754-2008).

A decimal floating-point literal consists of an integer part (decimal digits), a decimal point, a
fractional part (decimal digits).

!!! note "Range"
    Floats in Ripsaw can range from `-1.7976931348623157E+308f64` to `1.7976931348623157E+308f64`. Floats outside that range are wrapped.

!!! note "Underscores"
    Floats can use underscore (`_`) characters instead of `,` to make them human readable. For example, `1_000_000`.

```ripsaw
1_000_000.01
1000000.01
1.001
```

#### Integer

An _integer_ literal is a sequence of digits representing a 64-bit signed integer type.

!!! note "Range"
    Integers in Ripsaw can range from `-9223372036854775807` to `9223372036854775807`. Integers outside that range are wrapped.

!!! note "Underscores"
    Integers can use underscore (`_`) characters instead of `,` to make them human readable. For example, `1_000_000`.

```ripsaw
1_000_000
479
```

#### Null

A _null_ literal is the absence of a defined value.

```ripsaw
null
```

#### Object

An _object_ literal is a growable key/value structure that is syntactically equivalent to a JSON object.

##### Ordering
Object fields are ordered alphabetically by the key in ascending order. Therefore, operations like
encoding into JSON produce a string with keys that are in ascending alphabetical order.

#### Regular Expression

A _regular expression_ literal represents a [Regular Expression](https://en.wikipedia.org/wiki/Regular_expression) used for string matching and
parsing.

Regular expressions are defined by the `r` sigil and wrapped with single quotes (`r'...'`). The value between
the quotes uses the [Rust regex syntax](https://docs.rs/regex/latest/regex/#syntax)).

##### Flags
Regular expressions allow for flags. Flags can be combined, as in `r'(?ixm)pattern'`,
`r'(?im)pattern'`, etc.

To learn more about regular expressions in Rust—and by extension in Ripsaw—we recommend the in-browser [Rustexp expression editor and tester](https://rustexp.lpil.uk/).

All flag values:

* `i` - Case insensitive
* `m` - Multi-line mode
* `x` - Ignore whitespace
* `s` - Allow `.` to match `\n`
* `U` - Swap the meaning of `x*` and `x*?`
* `u` - Unicode support (enabled by default)

##### Named Captures
Regular expressions support named capture groups, allowing extractions to be associated with keys.
Named captures should be preceded with a `?P<name>` declaration. This regex, for example:

```ripsaw
r'(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})'
```

extracts captures with the `y`, `m`, and `d` keys.

#### String

A _string_ literal is a [UTF-8–encoded](https://en.wikipedia.org/wiki/UTF-8) string. String literals can be raw or interpreted.

**Raw string** literals are composed of the	uninterpreted (implicitly UTF-8-encoded) characters
between single quotes identified with the `s` sigil and wrapped with single quotes (`s'...'`); in
particular, backslashes have no special meaning and the string may contain newlines.

**Interpreted string** literals are character sequences between double quotes (`"..."`). Within the
quotes, any character may appear except unescaped newline and unescaped double quote. The text
between the quotes forms the result of the literal, with backslash escapes interpreted as defined
below. Strings can be templated by enclosing variables in doubled curly braces. The value of the
variables are inserted into the string at that position.

```
"Hello, world! 🌎"

"Hello, world! \u1F30E"

"Hello, \
 world!"

s'Hello, world!'

s'{ "foo": "bar" }'
```

##### Backslash Escapes
Special characters, such as newlines, can be expressed with a backslash escape.

* `\u{7FFF}` - 24-bit Unicode character code (up to 6 digits)"
* `\n` - Newline
* `\r` - Carriage return
* `\t` - Tab
* `\\` - Backslash
* `\0` - Null
* `\\"` - Double quote
* `\'` - Single quote
* `\{` - Brace

##### Templates
Strings can be templated by enclosing a variable name with `{{ '{{..}}' }}`. The
value of the variable is inserted into the string at this position at runtime.
Currently, the variable has to be a string. Only variables are supported, if
you want to insert a path from the event you must assign it to a variable
first. To insert a `{{ '{{' }}` into the string it can be escaped with a `\\`
escape: `{{ '\\{{..\\}}' }}`.

##### Multi-line Strings
Long strings can be split over multiple lines by adding a backslash just before the
newline. The newline and any whitespace at the start of the ensuing line is not
included in the string.

##### Concatenation
Strings can be concatenated with the `+` operator.

##### Invalid Characters
Invalid UTF-8 sequences are replaced with the `�` character.

#### Timestamp
A _timestamp_ literal defines a native timestamp expressed in the [RFC 3339 format](https://www.rfc-editor.org/info/rfc3339/)) with a nanosecond precision.

Timestamp literals are defined by the `t` sigil and wrapped with single quotes (`t'2021-02-11T10:32:50.553955473Z'`).

##### Timezones
As defined in RFC 3339 format, timestamp literals support UTC and local offsets.

### Dynamic Expressions

Ripsaw is an expression-oriented language. A Ripsaw program consists entirely of expressions and every expression returns a value.

#### Abort

An `abort` expression causes the Ripsaw program to terminate, aborting any modifications made. `abort` optionally accepts a message.

```ripsaw
if contains(string!(.message), "hello") {
    abort
}

if contains(string!(.message), "goodbye") {
    abort "said goodbye without hello"
}
```

See [`assert()`](./functions.md#assert) to conditionally abort a program.

#### Arithmetic

An _arithmetic_ expression performs an operation on two expressions (operands) as defined by the
operator, returning the result of the expression. Supported operators include `+`, `-`, `*`, `/`
(float division), `//` (integer division), and `%` (remainder).

Although arithmetic is commonly applied to numbers, you can use it with other types as well, such as strings.

```ripsaw
1 + 1
# returns: 2

1.0 + 2
# returns: 3.0

"hello" + ", " + "world"
# returns "hello, world"
```

#### Assignment

An _assignment_ expression assigns the result of the right-hand-side expression to the left-hand
side target (path or variable), returning the value of the right-hand side expression only if the
expression succeeds. If the expression errors, the error must be
[handled](./errors.md#error-handling).

```ripsaw
my_variable = "Hello, World!"

# assignment from path
my_variable = .nested.item[0]
```

#### Block
A _block_ expression is a sequence of one or more expressions within matching brace brackets. Blocks
return the result of the last evaluated expression within the block.

Blocks can't be empty. Instead, empty blocks (`{}`) are treated as blank objects.

```ripsaw
{
    contents = "123"
    to_int!(contents)
}

# returns: 123

# block with assignment:
parsed_var = {
    contents = "123"
    to_int!(contents)
}
```

#### Coalesce
A _coalesce_ expression is composed of multiple expressions (operands) delimited by a coalesce operator,
short-circuiting on the first expression that doesn't violate the operator condition.

A coalesce expression returns the value of the first expression (operand) that doesn't violate the operator condition.

```ripsaw
to_int("not int") ?? to_int("{") ?? "malformed"

# returns: "malformed"
```

#### Comparison
A _comparison_ expression compares two expressions (operands) and produces a Boolean as defined by the
operator. Please refer to the [match function](./functions.md#match) for matching a string against a
regular expression.

Comparison expressions return a Boolean as defined by the operator.

##### Operators

* `==` - Equal. Operates on all types.
* `!=` - Not equal. Operates on all types.
* `>=` - Greater than or equal. Operates on `int`, `float`, and `timestamp` types.
* `>` -  Greater than. Operates on `int`, `float`, and `timestamp` types.
* `<=` - Less than or equal. Operates on `int`, `float`, and `timestamp` types.
* `<` - Less than. Operates on `int`, `float`, and `timestamp` types.

```ripsaw
1 == 1.0
# returns: true

"foo" != "bar"
# returns: true

2 >= 2
# returns: true

t'2024-04-04T22:22:22.234142+01:00' < t'2024-04-04T22:22:22.234142+04:00'
# returns: false
```

#### Function Call

A _function call_ expression invokes built-in [Ripsaw functions](./functions.md).

Function calls return the value of the function invocation if the invocation succeeds. If the
invocation fails, the error must be [handled](./errors.md#error-handling) and null is returned.

Functions can _only_ return a single value. If multiple values are relevant, you should wrap them in an array or object.

```ripsaw
split("hello, world!", ", ")

# returns: ["hello", "world!"]
```

Some functions accept closures, which are an optional piece of code resolved by the function call. It is primarily used in functions that iterate over collections:

```ripsaw
for_each([1, 2, 3]) -> |index, value| { ... }
```

!!! note "Fallibility"
    Ripsaw functions can be marked as "fallible" or "infallible". When a function
    is defined as fallible, it can fail at runtime, requiring the error to be
    handled before the program can be compiled.

    If a function is defined as infallible, it means that **given the correct
    function arguments**, the function can never fail at runtime, and thus no
    error handling is needed.

    Note that even if a function is defined as infallible, if any of its
    arguments can fail at runtime, the function is considered to be fallible, and
    thus the error case needs to be handled in this case.

    The Ripsaw compiler ensures all potential errors in a program are handled, so
    there's no need to worry about missing any potential runtime failures.

!!! note "Type Safety"
    Function arguments enforce type safety when the type of the value supplied is known:

    ```ripsaw
    round("not a number") # fails at compile time
    ```

    If the type of the value is not known, you need to handle the potential argument error:

    ```ripsaw
    number = int(.message) ?? 0
    round(number)
    ```

See the [errors reference](./errors.md) for a guide to error handling.

#### If
An _if_ expression specifies the conditional execution of two branches according to the value of a Boolean
expression. If the Boolean expression evaluates to `true`, the "if" branch is executed, otherwise the "else"
branch is executed (if present).

```ripsaw
if true {
    "Hello, World!"
}

# returns: "Hello, World!"

if false {
    # not evaluated
    2
}

# returns: null

if false {
    # not evaluated
    null
} else if false {
    "no"
} else {
    "yes"
}

# returns: "yes"
```

#### Index

An _index_ expression denotes an element of an array. Array indices in Ripsaw start at zero.

```ripsaw
.array[0]

# returns the first element
```

#### Logical

A _logical_ expression compares two expressions (operands), short-circuiting on the last expression
evaluated as defined by the operator.

##### Operators

* `&&` - Conditional AND. Supports boolean expressions only.
* `||` - Conditional OR. Supports any expression.
* `!` - NOT. Supports boolean expressions only.

```ripsaw
true && true

# returns: true

false || true
# returns: true

null || false
# returns: false

!false
# returns: true

null || "foo"
# returns: "foo"
```

#### Path
A _path_ expression is a sequence of period-delimited segments that represent the location of a value
within an object.
A leading "." means the path points to the event.

`path_segments` denote a segment of a nested path. Each segment must be delimited by a `.` character
and only contain alpha-numeric characters and `_` (`a-zA-Z0-9_`). Segments that contain
characters outside of this range must be quoted.

```ripsaw
.parent.child

# returns: the value of child within parent

.array[0].thing

# returns: the value of thing within the first item of an array

.array[0]."special characters".child

# returns: the value of child within "special characters" within
#          the first item of an array
```

#### Variable

A _variable_ expression names variables. A variable is a sequence of one or more letters and digits.
The first character in a variable must be a letter.

```ripsaw
my_variable = 1
my_variable == 1

# returns: true

my_object = { "one": 1 }
my_object.one

# returns: 1
```
