Gotchas

This page documents important Jyro behaviours and the correct way to handle them.

1. Empty arrays and objects are truthy

Jyro treats all arrays and objects as truthy - even empty ones. This means an if check on a collection always passes, regardless of whether it contains any elements.

Check the length explicitly instead:

# INCORRECT - always enters the block, even when Data.items is []
if Data.items then ... end

# CORRECT - tests whether the array actually has elements
if Length(Data.items) > 0 then ... end

2. ToNumber returns null for invalid input

When ToNumber cannot parse a string as a number it returns null, not 0. Code that assumes a numeric result without checking will propagate null through subsequent calculations.

Always test the return value before using it:

var num = ToNumber(Data.input)
if num is null then
    fail "Invalid number: " + Data.input
end

3. Division by zero terminates the script

Division or modulo by zero throws an immediate runtime error that terminates the script. There is no special infinity or NaN value.

Guard every division with a zero check:

if count != 0 then
    Data.average = total / count
else
    Data.average = 0
end

4. All array functions return new arrays

Every standard library function that operates on an array returns a new array. The original is never mutated. Assign the return value to capture the result.

Assign the result back to the variable:

var items = [3, 1, 2]
Sort(items)                # items is STILL [3, 1, 2] - result discarded
var sorted = Sort(items)   # sorted is [1, 2, 3]

var items = [1, 2]
Append(items, 3)           # items is STILL [1, 2] - result discarded
items = Append(items, 3)   # NOW items is [1, 2, 3]

5. Cannot modify a collection during iteration

Reassigning the array that a foreach loop is iterating over causes a runtime error. The iterator holds a reference to the original collection and detects the mutation.

Collect changes into a separate array, then apply them after the loop:

# INCORRECT - runtime error: modifying the iterated collection
foreach item in Data.items do
    Data.items = Append(Data.items, item)
end

# CORRECT - accumulate into a separate array, merge afterwards
var additions = []
foreach item in Data.items do
    if item.shouldDuplicate then
        additions = Append(additions, item)
    end
end
foreach addition in additions do
    Data.items = Append(Data.items, addition)
end

6. Nested properties need intermediate objects

Assigning to a deeply nested path like Data.a.b.c fails if the intermediate objects (Data.a, Data.a.b) do not already exist. Jyro does not auto-create parent objects on assignment.

Create each level explicitly, or assign a nested literal:

# INCORRECT - runtime error if Data.deep doesn't exist
Data.deep.nested.value = "x"

# CORRECT - build the path one level at a time
Data.deep = {}
Data.deep.nested = {}
Data.deep.nested.value = "x"

# ALSO CORRECT - assign the whole structure as a literal
Data.deep = {"nested": {"value": "x"}}

7. Missing then after if/elseif

The then keyword is required after every if and elseif condition. Omitting it causes a parse error.

# INCORRECT - parse error
if x > 0
    ...
end

# CORRECT
if x > 0 then
    ...
end

8. Missing do after while/for/foreach

Loop statements require the do keyword after the condition or iteration clause. Omitting it causes a parse error.

# INCORRECT - parse error
while x > 0
    ...
end

# CORRECT
while x > 0 do
    ...
end

9. Missing end for blocks

Every if, while, for, foreach, and switch block must be closed with end. A missing end causes a parse error at the end of the script or at the start of the next block.

10. No string interpolation

Jyro does not support template syntax like ${name} or {name} inside strings. These are treated as literal characters, not variable references. Use the + operator to concatenate values into strings:

# INCORRECT - produces the literal text "${name}", not the variable's value
"Hello ${name}"
"Hello {name}"

# CORRECT - concatenation converts the variable to a string
"Hello " + name

11. Min/Max/Sum/Average take arrays, not multiple arguments

The aggregate math functions each accept a single array argument, not variadic arguments.

Wrap the values in an array literal:

# INCORRECT - too many arguments
Sum(1, 2, 3)
Min(a, b)

# CORRECT - pass a single array
Sum([1, 2, 3])
Min([a, b])

12. Merge takes an array of objects

Like the aggregate math functions, Merge accepts a single array of objects to merge together. It does not accept multiple object arguments.

# INCORRECT - too many arguments
Merge(obj1, obj2)

# CORRECT - pass an array of objects (later entries override earlier ones)
Merge([obj1, obj2])

13. GroupBy takes a field name string, not a lambda

GroupBy groups objects by a named field and expects a plain string, not a lambda expression. This is consistent with other field-based query functions like WhereByField and SortByField.

# INCORRECT - GroupBy does not accept lambdas
GroupBy(arr, x => x.category)

# CORRECT - pass the field name as a string
GroupBy(arr, "category")

14. Regex patterns - no \d, \w, \s

Jyro processes string escape sequences before the string reaches the regex engine. Sequences like \d, \w, and \s are resolved during string parsing, so they do not reach the regex engine as metacharacters. Use equivalent character classes instead:

# INCORRECT - \d is resolved by the string parser before reaching regex
RegexMatch(text, "\d+")

# CORRECT - character class reaches the regex engine intact
RegexMatch(text, "[0-9]+")

See Strings - Regex Pattern Escaping for the full substitution table.

15. return/fail message must be on the same line

The return and fail keywords accept an optional string message, but it must appear on the same line. A string on the next line is parsed as a separate expression statement, not as the message.

# INCORRECT - "message" is a standalone expression, not the return message
return
"message"

# CORRECT - message on the same line
return "message"

16. For loop bounds are exclusive

The upper bound of to and the lower bound of downto are exclusive, similar to < and > respectively. The loop variable never reaches the boundary value. To include the boundary, adjust it by one:

for i in 0 to 5 do     # iterates: 0, 1, 2, 3, 4 (NOT 5)
    ...
end

for i in 5 downto 0 do # iterates: 5, 4, 3, 2, 1 (NOT 0)
    ...
end

# To include 5:
for i in 0 to 5 + 1 do # iterates: 0, 1, 2, 3, 4, 5
    ...
end

17. Function names are PascalCase and case-sensitive

All standard library functions use PascalCase naming (e.g. ToUpper, WhereByField). Function lookup is case-sensitive, so toupper or toUpper will fail with an “unknown function” error.

# INCORRECT - case mismatch
toupper("hello")
toUpper("hello")

# CORRECT
ToUpper("hello")

18. No semicolons

Statements are separated by newlines. Semicolons are not part of the syntax and will cause a parse error if used.

19. fail is for business logic, not debugging

The fail keyword terminates the script with an error status and is intended for signalling validation failures and business rule violations to the host application. It is not a debugging tool - use Data properties to write diagnostic output instead.


Back to top

Copyright © Mesch Systems 2025-2026. All rights reserved.