Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,8 @@ jobs:
'tree:_sent _color apples* .' \
'tree:_sent _color sky* and* _color grass* .' \
'tree:Blue' \
'concept:sent root:_sent sawcallerG:0 backcall:0 colors:red;' \
'concept:sent root:_sent sawcallerG:0 backcall:0 colors:crimson;green;' \
'concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:red;' \
'concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:crimson;green;' \
'self:0' \
'missing:0')
if [ "$got" != "$want" ]; then
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,8 @@ jobs:
'tree:_sent _color apples* .' \
'tree:_sent _color sky* and* _color grass* .' \
'tree:Blue' \
'concept:sent root:_sent sawcallerG:0 backcall:0 colors:red;' \
'concept:sent root:_sent sawcallerG:0 backcall:0 colors:crimson;green;' \
'concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:red;' \
'concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:crimson;green;' \
'self:0' \
'missing:0')
if [ "$got" != "$want" ]; then
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/tests/call-analyzer-callee/spec/colors.nlp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ G("mine") = "callee";
addnumval(L("out"),"backcall",callanalyzer(pnroot(),L("out"),"call-analyzer"));
@@CODE

# As in any analyzer written to run on its own. When called, _ROOT is the
# root the called analyzer is given, not the node's own name.
@NODES _ROOT

@CHECK
if (!N("color",1))
fail();
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tests/call-analyzer-callee/spec/mark.nlp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# analyzer reached.
###############################################

@PATH _ROOT

@POST
N("callee",1) = 1;

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/tests/call-analyzer/spec/call.nlp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
@CODE
G("results") = makeconcept(findroot(),"results");
G("mine") = "caller";

# Every attribute name becomes a dictionary word with no attributes, so this
# puts "crimson" in the KB bare -- the way an earlier call or the caller's own
# KB work leaves common words. The called analyzer must still find crimson's
# entry in its lazy shades-full.dict.
addstrval(G("results"),"crimson","bare");
"out.txt" << "loaded-before:" << str(findana("call-analyzer-callee")) << "\n";
@@CODE

Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/tests/call-analyzer/spec/report.nlp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
# NOTE: What each line proves:
# loaded-* the called analyzer was loaded by the first call
# mine its G() variables are its own
# tree it changed only the sentence nodes it was given:
# tree it changed only the sentence nodes it was given,
# though its passes select @NODES _ROOT and @PATH _ROOT:
# colors became _color nodes and * marks each word
# it visited; the top-level "Blue" is untouched
# _color red/green come from its regular dictionary, loaded
# into the shared KB; crimson only from its lazy
# shades-full.dict, looked up on the passed text
# concept it wrote into this KB's concept directly, saw the
# given node as its root, and could not call back
# root it was given (_ROOT), and could not call back
# self an analyzer cannot call itself
# missing an unknown analyzer is an error, not a crash
#
Expand All @@ -24,8 +25,8 @@
# tree:_sent _color apples* .
# tree:_sent _color sky* and* _color grass* .
# tree:Blue
# concept:sent root:_sent sawcallerG:0 backcall:0 colors:red;
# concept:sent root:_sent sawcallerG:0 backcall:0 colors:crimson;green;
# concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:red;
# concept:sent root:_ROOT sawcallerG:0 backcall:0 colors:crimson;green;
# self:0
# missing:0
###############################################
Expand Down
52 changes: 33 additions & 19 deletions cs/libconsh/cg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3512,27 +3512,41 @@ return true;

CONCEPT *CG::findWordConcept(_TCHAR *str)
{
CONCEPT *word;
if (!(word = kbm_->dict_find_word(str)) )
CONCEPT *word = kbm_->dict_find_word(str);
if (fullKBBs_.empty() && fullDicts_.empty())
return word;
if (word && findAttrs(word))
return word; // A dictionary entry already in memory.

// Not in memory, or in memory with no attributes of its own: try a
// lazily-loaded "*full" dictionary. // 06/10/26.
if (fullMissCache_.count(str))
{
if (!word)
logMissingWord(str); // Known absent: skip the search but
return word; // still record it for this file's log.
}

CONCEPT *full = findFullWord(str);
if (!word)
{
if (!full)
{
// Not in memory: try a lazily-loaded "*full" dictionary. // 06/10/26.
if (!fullKBBs_.empty() || !fullDicts_.empty())
{
if (fullMissCache_.count(str))
{
logMissingWord(str); // Known absent: skip the search but
return 0; // still record it for this file's log.
}
word = findFullWord(str);
if (!word)
{
fullMissCache_.insert(str); // Negative cache (run-wide).
logMissingWord(str); // Record for later KB curation.
}
return word;
}
return 0;
fullMissCache_.insert(str); // Negative cache (run-wide).
logMissingWord(str); // Record for later KB curation.
}
return full;
}

// A bare word says nothing about what the dictionaries hold for it: every
// attribute name is a dictionary word, so any KB work that names one leaves
// it bare -- and under callanalyzer() the KB is shared with other analyzers.
// A dict hit has been added to word itself; a kbb-only hit is linked to it.
// Searched once either way. // 09/13/26 DD.
fullMissCache_.insert(str);
CONCEPT *meaning = 0;
if (full && full != word && !findVal(word, _T("meaning"), meaning))
addVal(word, _T("meaning"), full);
return word;
}

Expand Down
45 changes: 43 additions & 2 deletions lite/callana.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ All rights reserved.
//
// WHAT RUNS ON EACH CALL. The passes, in order, on the subtree, with these
// differences from running on a text of its own:
// - the root they get is a stand-in named _ROOT that holds pnode's
// children for the length of the call (see borrowChildren), so they
// select and traverse exactly as on a text of their own;
// - tokenizer passes are skipped, because the tree already exists;
// - a dicttok or dicttokz pass looks up the subtree's words instead,
// including in the lazy *full files, which only load a word once it is
Expand Down Expand Up @@ -286,6 +289,42 @@ for (const auto &file : dicts)
}
}

// Give the called analyzer a root of its own, as it has running on a text:
// named _ROOT, unsealed, with no parent or siblings, spanning node's text and
// holding node's children. @NODES, @PATH and @MULTI select by name and only
// descend into unsealed nodes, and they walk a root's siblings; node itself is
// named for what the caller built, usually sealed, and has siblings in the
// caller's tree. So without this, @NODES _ROOT selects nothing, deeper selects
// never reach inside, and passes can run on the caller's nodes next to node.
static Node<Pn> *borrowChildren(Parse *parse, Node<Pn> *node)
{
Pn *pn = node->getData();
Sym *sym = ((Htab *) parse->getNLP()->getHtab())->hsym(_T("_ROOT"));
Node<Pn> *root = Pn::makeTnode(pn->getStart(), pn->getEnd(),
pn->getUstart(), pn->getUend(), PNNODE, pn->getText(),
sym->getStr(), sym, pn->getLine());
root->getData()->setUnsealed(true);

Node<Pn> *children = node->Down();
root->setDown(children);
if (children)
children->setUp(root); // Only a first child points up.
node->setDown(0);
return root;
}

// Put the children back under node, as the called analyzer left them, and
// free the borrowed root. Variables set on the root go with it.
static void returnChildren(Node<Pn> *root, Node<Pn> *node)
{
Node<Pn> *children = root->Down();
node->setDown(children);
if (children)
children->setUp(node);
root->setDown(0);
Node<Pn>::DeleteNodeAndData(root);
}

// Run a loaded analyzer's passes on the subtree under node.
static bool runCallee(Parse *parse, NLP *callee, Node<Pn> *node, CONCEPT *con)
{
Expand All @@ -309,7 +348,8 @@ long rulepass = parse->getRulepass();
Dlist<Ipair> *vars = parse->getVars();
std::string appdir = parse->getAppdir();

Tree<Pn> subtree(node); // Does not own the nodes.
Node<Pn> *root = borrowChildren(parse, node);
Tree<Pn> subtree(root); // Does not own the nodes.
parse->setTree(&subtree);
parse->setAna(ana);
parse->setVars(0);
Expand All @@ -334,12 +374,13 @@ for (Delt<Seqn> *step = ana->getSeq(); step && ok; step = step->Right())
parse->setCurrpass(num);
parse->setRulepass(num);
if (isDictTok(algoname))
((DICTTok *) algo)->applyToSubtree(parse, node);
((DICTTok *) algo)->applyToSubtree(parse, root);
else
ok = algo->Execute(parse, pass);
}

calls.pop_back();
returnChildren(root, node);

if (parse->getVars())
Dlist<Ipair>::DeleteDlistAndData(parse->getVars());
Expand Down
2 changes: 1 addition & 1 deletion nlp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ All rights reserved.
#include "lite/nlp_engine.h"
#include "version.h"

#define NLP_ENGINE_VERSION "4.1.0"
#define NLP_ENGINE_VERSION "4.1.1"

bool cmdReadArgs(int, _TCHAR *argv[], _TCHAR *&, _TCHAR *&, _TCHAR *&, _TCHAR *&, bool &, bool &, bool &, bool &, bool &, bool &, int &);
void cmdHelpargs(_TCHAR *);
Expand Down
Loading