The Lash Book

3. Expressions and Operators

Lash expressions are checked before they are lowered to Bash. Some operators can be used anywhere a value is allowed; others are only supported in specific contexts.

Arithmetic

var n = 10
n += 2
n -= 1
n *= 3
n /= 2
n %= 4
n++
n--

Arithmetic operators are numeric:

  • *, /, %
  • +, -
  • unary -, unary +

Compound assignments other than += require a variable target and numeric operands.

String concatenation with +

+ is numeric unless either side is string-like.

let name = "lash"
let archive = name + ".tar.gz"

Mixed number + string is rejected when the checker can prove the mismatch.

Comparisons and truthiness

if count > 0
    echo "has items"
end

if name == "lash"
    echo "matched"
end

== and != lower to Bash [[ left == right ]] in condition code paths. Numeric comparisons such as <, >, <=, and >= lower to arithmetic tests.

For a bare condition like if count, Lash treats numeric non-zero as true. argv is special: bare argv in a condition checks whether any positional arguments exist.

Regex match

if branch =~ "^main$"
    echo "protected branch"
end

=~ lowers to Bash regex matching in condition positions. It is intentionally not supported as a general value expression.

Logical operators

if ready && !dry_run
    echo "apply"
end

Supported logical operators:

  • !
  • &&
  • ||

Length with #

let items = ["a", "b", "c"]
let item_count = #items
let first_length = #items[0]
let arg_count = #argv
echo $"items={item_count}"
echo $"first-length={first_length}"
echo $"args={arg_count}"

# works for identifiers, index access, string literals, array literals, map literals, and argv.

Range expressions

for i in 1..5
    echo $"{i}"
end

for i in 1..10 step 2
    echo $"{i}"
end

Ranges are only supported as the top-level iterable in for ... in. They lower through seq.

Value pipes

Value pipes pass the left value as the first argument to a function call on the right.

fn wrap(value, prefix = "[")
    return prefix + value + "]"
end

let label = "ok" | wrap("status=")

As an expression, the right-hand side of | must be a function call. As a standalone statement, pipe handling is more limited and intended for assignment-sink forms supported by the compiler.