Appearance
Common mistakes
Almost every rule that fails to parse fails for one of the reasons below. Each entry shows the form that does not work and the form that does.
Syntax traps
Keywords must be uppercase
-- Wrong:
if prev_sum > 50 then current_value < 0
-- Right:
IF prev_sum > 50 THEN current_value < 0All keywords — IF, THEN, AND, OR, NOT, IN, EVERY, SOME — must be fully uppercase. The parser does no case-insensitive matching, so if and If are read as ordinary identifiers, not as the keyword.
Use = for equality, not ==
-- Wrong:
current_value == 5
-- Right:
current_value = 5There is one equals sign for equality and one only. == is not an operator in this language.
Comparisons do not chain
-- Wrong:
0 < current_value < 10
-- Right:
current_value > 0 AND current_value < 10Comparison operators are non-associative: each one takes exactly two arithmetic operands, and its result is a boolean that cannot be compared again.
NOT IN is two separate words
-- Wrong:
current_value NOTIN (0, 5)
-- Right:
current_value NOT IN (0, 5)NOTIN is a single identifier as far as the parser is concerned. The two keywords need whitespace between them.
ANY is not a keyword
-- Wrong:
ANY d IN digits(prev_sum): d = 9
-- Right:
SOME d IN digits(prev_sum): d = 9There are exactly two quantifiers, EVERY and SOME. ANY, ALL and EXISTS are not part of the language.
Keywords must be separated by whitespace or punctuation
-- Wrong (IFprev_sum is treated as a single identifier):
IFprev_sum > 0 THEN current_value < 10
-- Right:
IF prev_sum > 0 THEN current_value < 10A keyword is only recognised when the character next to it is a space, a parenthesis, a comma, a colon, or the end of the rule. Anything else and it merges into the neighbouring identifier.
Quantifiers need both IN and the colon
-- Wrong:
EVERY d digits(prev_sum): d <= 5
EVERY d IN digits(prev_sum) d <= 5
-- Right:
EVERY d IN digits(prev_sum): d <= 5The full shape is EVERY name IN iterable : condition, and none of the three separators — the loop variable, IN, the colon — is optional.
Functions need at least one argument
-- Wrong:
digits()
-- Right:
digits(current_value)Zero-argument calls are not valid syntax. Note also that digitAt takes exactly two arguments, while digits, highest_digit, lowest_digit and number_of_digits take exactly one — see digit functions.
Meaning traps
These parse cleanly. They just do not mean what they look like they mean.
NOT is looser than a comparison
-- These are the same rule:
NOT current_value > 5
NOT (current_value > 5)NOT applies to the whole comparison to its right, never to just the left operand. If you want "the negative of current_value is greater than 5", write the arithmetic explicitly.
A quantifier body runs to the end of the expression
-- Probably not what you meant — current_sum > 0 is inside the loop:
EVERY d IN digits(prev_sum): d < 9 AND current_sum > 0
-- Two independent conditions:
(EVERY d IN digits(prev_sum): d < 9) AND current_sum > 0Everything after the colon is a full condition, so it keeps consuming AND and OR clauses. Parenthesize the quantifier when you want it to stop.
AND binds tighter than OR
-- Parses as: a OR (b AND c)
prev_sum > 50 OR step_index = 0 AND current_value > 0
-- If you meant (a OR b) AND c:
(prev_sum > 50 OR step_index = 0) AND current_value > 0When in doubt, parenthesize
Parentheses are free and never change a correct expression's meaning. Adding them to any rule that mixes AND with OR, or that puts a quantifier next to another clause, costs nothing and removes the whole class of bug above.
Negative tests pass on null place variables
-- Intended as "never a 9", but permits every multi-place value:
formula != 9
-- Requires a single affected place first, then tests the formula:
place IN (1, 10, 100, 1000) AND formula != 9On a candidate like +47 that touches two decimal places, place, formula and digitBefore are all null — so formula != 9 is true and the step is allowed. See decimal places for the full explanation and for digitAt, the multi-place-safe alternative.