Operators

Parsley's operators mostly work as you'd expect from other languages, with a few notable additions: set operations on arrays and dictionaries, regex matching with ~, null coalescing with ??, inclusive ranges with .., and a family of I/O operators for files and databases.

Arithmetic

5 + 3               // 8
10 - 4              // 6
6 * 7               // 42
7 / 2               // 3       (integer division truncates)
7 / 2.0             // 3.5     (mixed → float)
7 % 3               // 1
-5                  // -5      (unary negation)

Mixed integer/float operations promote to float. Division by zero is a runtime error.

String & Array Arithmetic

Several operators are overloaded for strings and arrays:

"hello" + " world"  // "hello world"   (string concatenation)
"ha" * 3            // "hahaha"         (string repetition)
10 + "px"           // "10px"           (auto-coercion to string)

[1, 2] ++ [3, 4]    // [1, 2, 3, 4]    (array concatenation)
[1, 2] * 3          // [1, 2, 1, 2, 1, 2]
[1,2,3,4,5] / 2     // [[1, 2], [3, 4], [5]]  (chunking)

⚠️ ++ is the array/dictionary merge operator, not string concatenation. On non-arrays it wraps each side: "a" ++ "b"["a", "b"].

⚠️ + with a string on either side converts the other operand to a string and concatenates. 10 + "px" is "10px", not an error.

Dictionary Merge

++ merges dictionaries. Right-side values win on key conflicts:

{a: 1, b: 2} ++ {b: 3, c: 4}  // {a: 1, b: 3, c: 4}

Path & URL Arithmetic

+ joins path and URL segments:

@/usr/local + "bin"    // @/usr/local/bin
@./config + "app.json" // @./config/app.json

Money Arithmetic

Money supports +, - (same currency only), and *, / with scalars. Uses banker's rounding. See Money.

DateTime Arithmetic

DateTime values support + and - with integers (days) and durations. See DateTime.

Comparison

5 == 5               // true
5 != 3               // true
3 < 5                // true
5 > 3                // true
3 <= 3               // true
5 >= 5               // true

Works on numbers, strings, money, and datetimes. Equality (==, !=) works on all types.

⚠️ String comparison uses natural sort order: "file2" < "file10" is true. This means numeric substrings within strings are compared by value, not lexicographically.

Logical

!true                // false
not false            // true
true & false         // false
true | false         // true
true and false       // false
true or false        // true
true && false        // false   (alias for &)
true || false        // true    (alias for |)

All six forms (&/&&/and, |/||/or) are equivalent. and has higher precedence than or:

true | false & false   // true   — parsed as: true | (false & false)

Logical operators use truthiness (see Booleans for the full list of falsy values).

Null Coalescing

?? returns the left side unless it is null, in which case it evaluates and returns the right side. Short-circuits — the right side is not evaluated if the left is non-null.

null ?? "default"    // "default"
false ?? "default"   // false      (not null, so left wins)
0 ?? "default"       // 0          (not null, so left wins)

⚠️ ?? checks for null only, not general falsiness. Use || if you want to fall through on any falsy value.

Membership

in and not in test membership across arrays, dictionaries, and strings:

2 in [1, 2, 3]           // true    (element in array)
"x" in {x: 1, y: 2}     // true    (key in dictionary)
"ell" in "hello"          // true    (substring in string)
"z" not in [1, 2, 3]     // true
5 in null                 // false   (null-safe, never errors)

Set Operations on Arrays

&, |, and - perform set operations when both operands are arrays:

[1,2,3] & [2,3,4]   // [2, 3]         (intersection)
[1,2,3] | [2,3,4]   // [1, 2, 3, 4]   (union, deduplicated)
[1,2,3] - [2,3]     // [1]             (difference)

These also work on dictionaries (matching by key):

let d = {a: 1, b: 2, c: 3}
d & {b: 99, c: 99}  // {b: 2, c: 3}   (intersection, values from left)
d - {b: 0}          // {a: 1, c: 3}    (difference, values in right ignored)

⚠️ & and | on arrays perform set operations. On non-array values they are boolean and/or. This overloading is resolved by operand type at runtime.

Pattern Matching

~ matches a string against a regex and returns an array of matches (or null on no match). !~ returns a boolean.

"hello-123" ~ /([a-z]+)-(\d+)/
// ["hello-123", "hello", "123"]  — [0] is full match, [1..] are capture groups

"abc" ~ /z/          // null     (no match)
"abc" !~ /z/         // true     (no match → true)
"abc" !~ /b/         // false    (match → false)

Schema Checking

is and is not test whether a record or table was created from a given schema:

record is User       // true if record's schema is User
record is not User   // negated

Non-record/table values always return false (no error). See Data Model.

Range

.. creates an inclusive integer range. Supports ascending and descending:

1..5                 // [1, 2, 3, 4, 5]
5..1                 // [5, 4, 3, 2, 1]

Indexing & Slicing

let a = [10, 20, 30]
a[0]                 // 10
a[-1]                // 30       (negative index from end)
a[0:2]               // [10, 20] (slice, end exclusive)

let s = "hello"
s[1]                 // "e"
s[-2:]               // "lo"

Optional Access

[?n] returns null instead of erroring on out-of-bounds:

let a = [1, 2, 3]
a[?10]               // null     (instead of index error)
a[?0]                // 1        (works normally when in bounds)

Dictionary access always returns null for missing keys — no optional form needed.

Spread

... is used in two contexts:

Rest in destructuring — collects remaining elements:

let [first, ...rest] = [1, 2, 3, 4]
// first = 1, rest = [2, 3, 4]

let {a, ...rest} = {a: 1, b: 2, c: 3}
// a = 1, rest = {b: 2, c: 3}

Spread in tags — expands a dictionary into tag attributes:

let attrs = {class: "card", id: "main"}
<div ...attrs>"content"</div>

I/O Operators

These operators are syntactic sugar for file and database operations. They are covered in detail in their respective manual pages.

File I/O

Operator Meaning Example
<== Read from file data <== @./file.txt
<=/= Fetch from URL data <=/= @https://api.example.com
==> Write to file data ==> @./output.txt
==>> Append to file line ==>> @./log.txt
=/=> Write to network target payload =/=> JSON(@https://api.example.com)
=/=>> Append to network target line =/=>> text(sftp, "/var/log/app.log")

<=/=, =/=>, and =/=>> are also usable as expressions — they return a response value that can be captured with let or used inline:

let response = <=/= JSON(@https://api.example.com/users)
let result = payload =/=> JSON(@https://api.example.com/items)

See File I/O and HTTP & Networking.

Database

Operator Meaning Example
<=?=> Query one row db <=?=> "SELECT ..."
<=??=> Query many rows db <=??=> "SELECT ..."
<=!=> Execute (INSERT, etc.) db <=!=> "INSERT ..."

See Database.

Command Execution

Operator Meaning Example
<=#=> Execute shell command result <=#=> "ls -la"

See Shell Commands.

Precedence Table

From lowest to highest:

Precedence Operators
1 (lowest) ,
2 | || or ??
3 & && and
4 == != ~ !~ in not in is <=?=> <=??=> <=!=>
5 < > <= >=
6 + - ..
7 ++
8 * / %
9 -x !x not x (prefix)
10 [] . (index/access)
11 (highest) () (call)

Key Differences from Other Languages

See Also