For any construct that opens a block with a colon — obj.method(args):,
constructorDefine():,
functionDefine.name(...):,
name ... <- code:,
?(cond) <- run: (sugar for test.ifTrue), etc. — the parser checks whether the
first statement of the body starts on the
same source line as that colon.
:
If the next statement begins on the same line, the body is exactly one
statement. You do not need a closing end;
for that block.
test.ifTrue(1): print.string("one-line if ok");
?(test.isLess(1, 2)) <- run: print.string("concise if");
constructorDefine(): this.x.setVar(0);
functionDefine.bump(n): return.number(math.add(n, 1));
:
If the first token of the body is on a later line than the colon, the
block uses the usual multi-statement form and must end with end; (with
nested inner blocks balanced the same way as always).
test.ifTrue(1):
print.string("multi-line");
end;
The rule is shared by every block that uses this colon form in the parser — for example:
test.if*, test.while*, and related control blocksfunctionDefine.name(...): and name ... <- code:constructorDefine(): and method bodies inside classeserror.try / error.except and other namespace.method(...): blocks
A one-line body is still a single statement: you cannot put two statements after the colon on the same line
without splitting into multiple lines and an end; block.