Skip to content

Lazy *full.kbb keys found from any pass; << writes to absolute paths (4.2.39) - #798

Merged
ddehilster merged 2 commits into
VisualText:masterfrom
ddehilster:fix/ostream-path-and-lazy-kb
Oct 20, 2026
Merged

ddehilster merged 2 commits into
VisualText:masterfrom
ddehilster:fix/ostream-path-and-lazy-kb

Conversation

@ddehilster

Copy link
Copy Markdown
Member

Two fixes found while building the ICD-10 analyzers. Both used to fail silently.

Lazy *full.kbb keys from any pass

A *full.kbb stays on disk and is searched one key at a time, but only the tokenizer and dictfindword() could reach that search. A later pass calling findconcept() on the file's top concept found only keys the tokenizer had happened to load. down() and next() listed only those keys, so a scan of ICD head words silently missed codes.

  • findconcept() / getconcept(): on a miss under a lazy file's top concept, they search that file and load the key with its whole block. Misses are remembered per file. Keys with a space, which SaveKB writes quoted ("heart attack":), are found too.
  • down(): the first call loads the whole file, so every key is listed once and keys already loaded aren't built twice. findconcept() by position does the same.
  • Tokenizer and dictfindword(): they now search a *full.kbb only if its top concept is dictionary or it has a same-stem .dict twin, so en-full.kbb with en-full.dict works as before. A domain KB such as an ICD index is no longer searched for every token and spelling candidate; that search took a spell pass from 0.1 s to 12 s. Its keys are also no longer registered as dictionary words.
    • Change for analyzer authors: dictfindword() no longer finds keys in domain files such as en-synonyms-full.kbb. Use findconcept() on the file's top concept instead.

"path" << x writes to an absolute path

The output operator put every file name under the input's _log folder, so G("$apppath") + "/out.txt" became <_log>/C:/.../out.txt. The open failed, nothing checked it, and every write was dropped with no message.

  • An absolute path (starting with /, \ or a drive letter) is now used as given, as openfile() already does. A relative name still goes in _log.
  • A file that can't be opened is reported once, in err.log and on stderr.
  • The path was also formatted into a fixed 512-byte buffer with no bound; it is now a std::string.

Tests

  • New fixtures kbb-lazy-lookup (nine checks) and output-path, run by one new step on Linux, Windows and macOS. On 4.3.15, kbb-lazy-lookup fails at its first check and stops at the kilo lookup.
  • With the shipped en-full.kbb, en-full.dict and en-synonyms-full.kbb, the dicttokz parse tree of 15 copies of doj.txt is byte-identical to 4.3.15's.
  • All local Windows fixture steps pass, except the compiled-analyzer step, which needs CI's packaged libraries. The parse-en-us tree is unchanged.

🤖 Generated with Claude Code

ddehilster and others added 2 commits October 20, 2026 11:12
The output operator appended every file name to the input's _log folder,
so an absolute path, like G("$apppath") + "/out.txt", became
<_log>/C:/.../out.txt. The open failed, nothing checked it, and every
write was dropped with no message. jsonwrite's help already says an
absolute path is used as given, and openfile() works that way.

Var::filevar now uses a path that starts with "/", "\" or a drive letter
as given. A relative name still goes in the _log folder. If the file
can't be opened, it says so once, in err.log and on stderr, and later
writes to that name do nothing. The path was also formatted into a
MAXSTR buffer with no bound; it is a std::string now.

Fixture output-path covers both kinds of path and a bad one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A *full.kbb stays on disk and is binary-searched one key at a time, but
only the tokenizer and dictfindword() reached that search. A later pass
calling findconcept() on the file's top concept found only keys the
tokenizer had happened to load, and down()/next() listed only those. An
ICD analyzer had to call dictfindword() before every lookup, and a scan
of its head words silently missed codes.

- findconcept() and getconcept() search the file on a miss under its
  top concept, build the key's whole block, and return it. Misses are
  remembered per file. A key with a space, which SaveKB writes quoted
  ("heart attack":), is found too.
- down(), and findconcept() by position, load the whole file the first
  time, so every key is listed once. Keys already loaded are not built
  twice.
- The tokenizer and dictfindword() search only dictionary files: a
  *full.kbb whose top concept is "dictionary", or that has a same-stem
  .dict twin (en-full.kbb with en-full.dict, linked as before). A domain
  KB such as an ICD index is no longer searched for every token and
  spelling candidate, which made a spell pass go from 0.1 s to 12 s,
  and its keys are no longer registered as dictionary words.

Fixture kbb-lazy-lookup checks each of these. With the shipped
en-full.kbb, en-full.dict and en-synonyms-full.kbb, the dicttokz parse
tree of 15x doj.txt is byte-identical to 4.3.15's.

The new CI step runs kbb-lazy-lookup and output-path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ddehilster
ddehilster merged commit 2d86020 into VisualText:master Oct 20, 2026
7 checks passed
ddehilster added a commit to VisualText/visualtext-files that referenced this pull request Oct 20, 2026
… 4.2.39)

lazyload.md gets a section on looking up keys from any pass. findconcept
and getconcept search a lazy file for a key not in memory yet, down
loads the whole file the first time, and the tokenizer and dictfindword
search only dictionary files: top concept "dictionary", or a same-named
.dict beside the .kbb. Its lemma example was run against the shipped
en-lemmas-full.kbb.

findconcept, getconcept, down and dictfindword each get a remark that
links to it. fileout says an absolute path now works with <<.
New_in_Current_Version gets a 4.2.39 entry.

Engine PR: VisualText/nlp-engine#798.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant