From 28458cdb575074ec7491f5f28bd0cb9fc9e406de Mon Sep 17 00:00:00 2001 From: David de Hilster Date: Sun, 18 Oct 2026 22:26:27 -0400 Subject: [PATCH] KBFuncs: no wrapped strpiece positions, valid JSON arrays Brings this repo's KBFuncs.nlp copies in step with the canonical visualText/spec/KBFuncs.nlp fix (VisualText/visualtext-files, same branch name). The functions were replaced by one script, and only where a copy held the known old text, so every copy got the same code: - StripEndDigits: a one-letter name only came back right because position -1 wraps since engine 4.2.15, and a name with no trailing digits lost its last letter ("item" -> "ite"). It now counts positions up from 0. - SeparateConCount (where present): only the trailing digits count ("a1b2" is "a1b" + "2", not "a1" + "12"); no position arithmetic that can go negative. - JsonKBRecurse (where present): an array followed by a plain sibling is now valid JSON, adjacent arrays with different names stay separate, and the stray hier.txt debug file is gone. Shapes that were valid JSON are written byte for byte as before; matches the jsonwrite builtin fix in VisualText/nlp-engine#796. Each analyzer was run on its inputs before and after with engine 4.2.36: output unchanged. Co-Authored-By: Claude Opus 5.5 --- tutorial-02/spec/KBFuncs.nlp | 11 ++++++----- tutorial-03/spec/KBFuncs.nlp | 11 ++++++----- tutorial-06/spec/KBFuncs.nlp | 11 ++++++----- tutorial-08/spec/KBFuncs.nlp | 11 ++++++----- tutorial-11/spec/KBFuncs.nlp | 11 ++++++----- tutorial-13/tutorial-13-b/spec/KBFuncs.nlp | 11 ++++++----- tutorial-15/spec/KBFuncs.nlp | 11 ++++++----- 7 files changed, 42 insertions(+), 35 deletions(-) diff --git a/tutorial-02/spec/KBFuncs.nlp b/tutorial-02/spec/KBFuncs.nlp index 75e2d18..8bf63ba 100644 --- a/tutorial-02/spec/KBFuncs.nlp +++ b/tutorial-02/spec/KBFuncs.nlp @@ -117,13 +117,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-03/spec/KBFuncs.nlp b/tutorial-03/spec/KBFuncs.nlp index 75e2d18..8bf63ba 100644 --- a/tutorial-03/spec/KBFuncs.nlp +++ b/tutorial-03/spec/KBFuncs.nlp @@ -117,13 +117,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-06/spec/KBFuncs.nlp b/tutorial-06/spec/KBFuncs.nlp index 75e2d18..8bf63ba 100644 --- a/tutorial-06/spec/KBFuncs.nlp +++ b/tutorial-06/spec/KBFuncs.nlp @@ -117,13 +117,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-08/spec/KBFuncs.nlp b/tutorial-08/spec/KBFuncs.nlp index 75e2d18..8bf63ba 100644 --- a/tutorial-08/spec/KBFuncs.nlp +++ b/tutorial-08/spec/KBFuncs.nlp @@ -117,13 +117,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-11/spec/KBFuncs.nlp b/tutorial-11/spec/KBFuncs.nlp index 28a9656..39192d4 100644 --- a/tutorial-11/spec/KBFuncs.nlp +++ b/tutorial-11/spec/KBFuncs.nlp @@ -117,13 +117,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-13/tutorial-13-b/spec/KBFuncs.nlp b/tutorial-13/tutorial-13-b/spec/KBFuncs.nlp index fd639c3..e273d50 100644 --- a/tutorial-13/tutorial-13-b/spec/KBFuncs.nlp +++ b/tutorial-13/tutorial-13-b/spec/KBFuncs.nlp @@ -150,13 +150,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } diff --git a/tutorial-15/spec/KBFuncs.nlp b/tutorial-15/spec/KBFuncs.nlp index c4762f5..b38a36f 100644 --- a/tutorial-15/spec/KBFuncs.nlp +++ b/tutorial-15/spec/KBFuncs.nlp @@ -173,13 +173,14 @@ CountName(L("con"),L("root")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); }