Fix substring(str, start) - the 2-argument form was completely broken

Found while writing a new Expressions Reference chapter for the manual,
where every function got run for real before being documented rather
than trusted from reading the source alone - substring('helloworld', 7)
threw "operand is not valid." immediately after producing the correct
answer, confirmed via debug tracing showing an extra Operand constructed
on empty input right after the 2-arg case finished.

Root cause: a missing pair of braces around the 3-argument-only branch
meant Operand parm3(in, tag) and its own closing-paren check ran
unconditionally, even after the 2-arg form's own "if(in.ifNext(')'))"
check had already matched and produced string = parm1.string.substring(
parm2.string.asInteger()) - so it immediately tried to parse a third
argument from whatever was left (nothing, since the real closing paren
was already consumed), and construction on empty input always throws.
The 3-argument form (substring(str, start, length)) was unaffected,
since its own path never hit the first if-branch's early exit.

Fixed by properly scoping the 3-arg branch inside an else, so it only
runs when the 2-arg form's own closing paren wasn't found. Verified both
forms with real jet-2.0 runs: substring('helloworld', 7) -> "rld",
substring('helloworld', 0, 5) -> "hello".
This commit is contained in:
Brad Arant committed 2026-08-26 10:39:38 -07:00
1 parent 9f3349b671
commit 97142ce9b4
1 file changed
+19 -6
+19 -6
View File
@@ -38,13 +38,26 @@ namespace jet {
Operand parm2(in, tag); Operand parm2(in, tag);
if(in.ifNext(")")) { if(in.ifNext(")")) {
string = parm1.string.substring(parm2.string.asInteger()); string = parm1.string.substring(parm2.string.asInteger());
} else if(!in.ifNext(",")) } else {
// Real, pre-existing bug fixed here: the 3-arg-only branch below
// used to run unconditionally (missing braces around this
// else-if), so even after the 2-arg form above already
// succeeded, it fell through into trying to parse a third
// argument from whatever was left (nothing, since the closing
// ")" was already consumed) - always throwing "operand is not
// valid." right after computing the correct 2-arg result.
// Confirmed via real debug tracing showing an extra Operand
// constructed on empty input immediately after a successful
// 2-arg substring(str, start) call - the 2-arg form was
// completely unusable before this fix.
if(!in.ifNext(","))
throw coreutils::Exception("Expecting , in SUBSTRING expression."); throw coreutils::Exception("Expecting , in SUBSTRING expression.");
Operand parm3(in, tag); Operand parm3(in, tag);
if(in.ifNext(")")) { if(in.ifNext(")")) {
string = parm1.string.substring(parm2.string.asInteger(), parm3.string.asInteger()); string = parm1.string.substring(parm2.string.asInteger(), parm3.string.asInteger());
} else } else
throw coreutils::Exception("Expecting ) at end of substring expression."); throw coreutils::Exception("Expecting ) at end of substring expression.");
}
} else if(in.ifNextIgnoreCase("LEFT")) { } else if(in.ifNextIgnoreCase("LEFT")) {
if(!in.ifNext("(")) if(!in.ifNext("("))
throw coreutils::Exception("Expecting ( for LEFT parameters."); throw coreutils::Exception("Expecting ( for LEFT parameters.");