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
31 changes: 31 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,37 @@ jobs:
done
echo "[zone-mode] one run of the zone passes per zone, zones kept as stubs"

- name: -ZONES runs only the zones it names (Non-20.04)
if: matrix.id != 'docker-ubuntu-20.04'
shell: bash
run: |
set -eo pipefail
# -ZONES 2-3: zone 1 gets only the passes before the zone line, so
# it keeps its tokens and is no stub. $isfirstzone and $islastzone
# mark the first and last zone that runs, and -DEV writes trees for
# every zone that runs.
FIX=.github/workflows/tests/zone-mode
rm -rf "$FIX/logs" "$FIX"/input/*_log "$FIX/output"
out=$(./bin/nlp -ANA "$FIX" -WORK ./ -DEV -ZONES 2-3 "$FIX/input/text.txt" | tr -d '\r')
want=$(printf '%s\n' \
"zone 1: first=1 last=0 lines under root=1 words=3 text=three four five" \
"zone 2: first=0 last=1 lines under root=1 words=1 text=six" \
"summary: zones=2 stubs=2 first=0 last=0")
if [ "$out" != "$want" ]; then
echo "FAIL(-ZONES): stdout was:"; echo "$out"; echo "wanted:"; echo "$want"; exit 1
fi
L="$FIX/input/text.txt_log"
if [ -f "$L/z001-ana003.tree" ] || [ ! -f "$L/z002-ana003.tree" ] || [ ! -f "$L/z003-ana003.tree" ]; then
echo "FAIL(-ZONES): wrong zone trees:"; ls "$L"; exit 1
fi
# A list, and a spec that is not zone numbers.
out=$(./bin/nlp -ANA "$FIX" -WORK ./ -ZONES 1,3 "$FIX/input/text.txt" | tr -d '\r' | grep -c '^zone ' || true)
if [ "$out" != "2" ]; then echo "FAIL(-ZONES 1,3): ran $out zones"; exit 1; fi
if ./bin/nlp -ANA "$FIX" -WORK ./ -ZONES 3-1 "$FIX/input/text.txt" > /dev/null 2>&1; then
echo "FAIL(-ZONES 3-1): a bad range was accepted"; exit 1
fi
echo "[-ZONES] only the named zones run"

- name: A rec pass that never settles is stopped (Non-20.04)
if: matrix.id != 'docker-ubuntu-20.04'
shell: bash
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,36 @@ jobs:
done
echo "[zone-mode] one run of the zone passes per zone, zones kept as stubs"

- name: -ZONES runs only the zones it names
shell: bash
run: |
set -eo pipefail
# -ZONES 2-3: zone 1 gets only the passes before the zone line, so
# it keeps its tokens and is no stub. $isfirstzone and $islastzone
# mark the first and last zone that runs, and -DEV writes trees for
# every zone that runs.
FIX=.github/workflows/tests/zone-mode
rm -rf "$FIX/logs" "$FIX"/input/*_log "$FIX/output"
out=$(./bin/nlp -ANA "$FIX" -WORK ./ -DEV -ZONES 2-3 "$FIX/input/text.txt" | tr -d '\r')
want=$(printf '%s\n' \
"zone 1: first=1 last=0 lines under root=1 words=3 text=three four five" \
"zone 2: first=0 last=1 lines under root=1 words=1 text=six" \
"summary: zones=2 stubs=2 first=0 last=0")
if [ "$out" != "$want" ]; then
echo "FAIL(-ZONES): stdout was:"; echo "$out"; echo "wanted:"; echo "$want"; exit 1
fi
L="$FIX/input/text.txt_log"
if [ -f "$L/z001-ana003.tree" ] || [ ! -f "$L/z002-ana003.tree" ] || [ ! -f "$L/z003-ana003.tree" ]; then
echo "FAIL(-ZONES): wrong zone trees:"; ls "$L"; exit 1
fi
# A list, and a spec that is not zone numbers.
out=$(./bin/nlp -ANA "$FIX" -WORK ./ -ZONES 1,3 "$FIX/input/text.txt" | tr -d '\r' | grep -c '^zone ' || true)
if [ "$out" != "2" ]; then echo "FAIL(-ZONES 1,3): ran $out zones"; exit 1; fi
if ./bin/nlp -ANA "$FIX" -WORK ./ -ZONES 3-1 "$FIX/input/text.txt" > /dev/null 2>&1; then
echo "FAIL(-ZONES 3-1): a bad range was accepted"; exit 1
fi
echo "[-ZONES] only the named zones run"

- name: A rec pass that never settles is stopped
shell: bash
run: |
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,36 @@ jobs:
done
echo "[zone-mode] one run of the zone passes per zone, zones kept as stubs"

- name: -ZONES runs only the zones it names
shell: bash
run: |
set -eo pipefail
# -ZONES 2-3: zone 1 gets only the passes before the zone line, so
# it keeps its tokens and is no stub. $isfirstzone and $islastzone
# mark the first and last zone that runs, and -DEV writes trees for
# every zone that runs.
FIX=.github/workflows/tests/zone-mode
rm -rf "$FIX/logs" "$FIX"/input/*_log "$FIX/output"
out=$(./bin/Release/nlp.exe -ANA "$FIX" -WORK ./ -DEV -ZONES 2-3 "$FIX/input/text.txt" | tr -d '\r')
want=$(printf '%s\n' \
"zone 1: first=1 last=0 lines under root=1 words=3 text=three four five" \
"zone 2: first=0 last=1 lines under root=1 words=1 text=six" \
"summary: zones=2 stubs=2 first=0 last=0")
if [ "$out" != "$want" ]; then
echo "FAIL(-ZONES): stdout was:"; echo "$out"; echo "wanted:"; echo "$want"; exit 1
fi
L="$FIX/input/text.txt_log"
if [ -f "$L/z001-ana003.tree" ] || [ ! -f "$L/z002-ana003.tree" ] || [ ! -f "$L/z003-ana003.tree" ]; then
echo "FAIL(-ZONES): wrong zone trees:"; ls "$L"; exit 1
fi
# A list, and a spec that is not zone numbers.
out=$(./bin/Release/nlp.exe -ANA "$FIX" -WORK ./ -ZONES 1,3 "$FIX/input/text.txt" | tr -d '\r' | grep -c '^zone ' || true)
if [ "$out" != "2" ]; then echo "FAIL(-ZONES 1,3): ran $out zones"; exit 1; fi
if ./bin/Release/nlp.exe -ANA "$FIX" -WORK ./ -ZONES 3-1 "$FIX/input/text.txt" > /dev/null 2>&1; then
echo "FAIL(-ZONES 3-1): a bad range was accepted"; exit 1
fi
echo "[-ZONES] only the named zones run"

- name: A rec pass that never settles is stopped
shell: bash
run: |
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,23 @@ runs once per node of that type:
- The KB and `G()` carry from zone to zone. Node offsets count from the
start of the document, and there is one set of output files.
- After its passes a zone keeps its node, attributes and span, and its
subtree is freed. Memory goes by the largest zone, not the document.
subtree is freed. Memory goes by the largest zone and by the tree the
passes before the `zone` line build, which is of the whole document.
- The passes after an `endzone` line run once, after the last zone, on the
zone stubs and the whole KB: the place for work that belongs to the whole
document, such as dumping the KB.
- A zone pass may change its zone but not the tree above it (eg, group
`_TEXTZONE` nodes into sections); that stops the run with an error.
- With `-DEV`, the first three zones write their pass trees as
`z001-ana040.tree` and so on.
- `-ZONES 40-45` runs only those zones (also `-ZONES 7` or `-ZONES 3,40-45`;
numbers from 1, as in the tree names). The other zones get only the passes
before the `zone` line, so a problem in zone 4,000 of a book can be rerun in
seconds. `$isfirstzone` and `$islastzone` mark the first and last zone that
runs, and with `-DEV` every zone that runs writes its pass trees. The KB and
`G()` start without what the skipped zones would have added, so a zone that
depends on earlier ones can come out differently than in a full run.
(Since 4.2.50.)
- Compiled analyzers run zones the same way (since 4.2.49; before that they
ignored the `zone` and `endzone` lines).

Expand Down
8 changes: 8 additions & 0 deletions include/Api/lite/nlp.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,14 @@ class LITE_API NLP
long long runLimitMs_ = 0;
bool runStopped_ = false;

// ZONE RANGE (-ZONES): each new Parse runs only these zones; see
// Parse::setZoneRange. Empty is every zone.
public:
void setZoneRange(const _TCHAR *spec) { zoneRange_ = spec ? spec : _T(""); }
const _TCHAR *getZoneRange() { return zoneRange_.c_str(); }
private:
std::basic_string<_TCHAR> zoneRange_;

// When code is well debugged and stable, can remove the stuff below.
#ifndef STABLE_
public:
Expand Down
8 changes: 8 additions & 0 deletions include/Api/lite/nlp_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ class LITE_API NLP_ENGINE
long long getRunLimit();
bool lastRunStopped();

// Zone mode: run only these zones of each input, eg "40-45" or
// "3,40-45" (zone numbers from 1, as in z040-ana###.tree). The others
// get only the passes before the zone line. Empty or 0 is every zone.
// False, and no change, if the spec is not that form.
bool setZoneRange(const _TCHAR *spec);
const _TCHAR *getZoneRange();

private:
void analyzeFile(const std::string &file, _TCHAR *outdir, std::_t_ofstream *fp);
int createDir(_TCHAR *dirPath);
Expand All @@ -194,4 +201,5 @@ class LITE_API NLP_ENGINE
// At the end of the class, so its layout is unchanged before it.
std::basic_string<_TCHAR> m_datum; // G("$datum") for analyses.
long long m_runLimitMs = 0; // setRunLimit; 0 = none.
std::basic_string<_TCHAR> m_zoneRange; // setZoneRange; empty = every zone.
};
9 changes: 9 additions & 0 deletions include/Api/lite/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ All rights reserved.
#include <set>
#include <chrono>
#include <functional>
#include <vector>

typedef void TREE; // 07/07/03 AM.
typedef void LTREE; // 07/07/03 AM.
Expand Down Expand Up @@ -696,6 +697,14 @@ class LITE_API Parse

_TCHAR zprefix_[32] = {0}; // zonePrefix()'s "z001-ana".

// ZONE RANGE (-ZONES 40-45): run only these zones; the others get only
// the passes before the zone line. Empty, the default, is every zone.
public:
void setZoneRange(const _TCHAR *spec);
private:
bool zoneSelected(long num);
std::vector<std::pair<long,long>> zoneRange_; // [first, last], from 1.

#ifndef STABLE_
public:
static int getCount();
Expand Down
58 changes: 58 additions & 0 deletions include/Api/lite/zonerange.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
Copyright (c) 2026 by Text Analysis International, Inc.
All rights reserved.
********************************************************************************
*
* NAME: ZONERANGE.H
* FILE: include/Api/lite/zonerange.h
* SUBJ: Read a -ZONES spec: which zones of a zone-mode run to run.
* NOTE: Used by Parse, which applies it, and NLP_ENGINE, which checks it.
*
*******************************************************************************/

#ifndef ZONERANGE_H_
#define ZONERANGE_H_

#include <vector>
#include <utility>

// "7", "40-45" or a list such as "3,40-45" into [first, last] pairs. Zone
// numbers count from 1, as in the z040-ana###.tree names. False, with
// ranges cleared, if the spec is not that form.
template <typename CH>
inline bool parseZoneRange(const CH *spec, std::vector<std::pair<long,long>> &ranges)
{
ranges.clear();
if (!spec || !*spec)
return false;
const CH *p = spec;
auto digit = [](CH c) { return c >= '0' && c <= '9'; };
while (digit(*p))
{
long lo = 0;
while (digit(*p))
lo = lo * 10 + (*p++ - '0');
long hi = lo;
if (*p == '-')
{
++p;
if (!digit(*p))
break;
hi = 0;
while (digit(*p))
hi = hi * 10 + (*p++ - '0');
}
if (lo < 1 || hi < lo)
break;
ranges.push_back(std::make_pair(lo, hi));
if (!*p)
return true;
if (*p != ',')
break;
++p;
}
ranges.clear();
return false;
}

#endif
2 changes: 2 additions & 0 deletions lite/nlp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,7 @@ Parse *parse; // The current text analysis instance.
parse = new Parse(); // 05/13/99 AM.
parse->nlp_ = this; // 06/25/03 AM.
parse->setRunLimit(runLimitMs_); // 0 = none; see Parse::overBudget. // 10/25/26 DD.
parse->setZoneRange(zoneRange_.c_str()); // -ZONES; empty = every zone.
parse->htab_ = this->htab_; // 06/25/03 AM.

parse->setEana(eana); // 10/13/99 AM.
Expand Down Expand Up @@ -1610,6 +1611,7 @@ Parse *parse; // The current text analysis instance.
parse = new Parse(); // 05/13/99 AM.
parse->nlp_ = this; // 06/25/03 AM.
parse->setRunLimit(runLimitMs_); // 0 = none; see Parse::overBudget. // 10/25/26 DD.
parse->setZoneRange(zoneRange_.c_str()); // -ZONES; empty = every zone.
parse->htab_ = this->htab_; // 06/25/03 AM.

parse->setEana(eana); // 10/13/99 AM.
Expand Down
21 changes: 20 additions & 1 deletion lite/nlp_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ MIT License
*******************************************************************************/

#include "nlp_engine.h"
#include "lite/zonerange.h"

#include <filesystem>
#include <ctime>
Expand Down Expand Up @@ -853,10 +854,28 @@ bool NLP_ENGINE::lastRunStopped() {
return m_nlp && m_nlp->getRunStopped();
}

// init() may have made a new NLP; give it the limit, and clear the flag.
// init() may have made a new NLP; give it the limit and the zone range, and
// clear the flag.
void NLP_ENGINE::armRunLimit() {
m_nlp->setRunLimit(m_runLimitMs);
m_nlp->setRunStopped(false);
m_nlp->setZoneRange(m_zoneRange.c_str());
}

bool NLP_ENGINE::setZoneRange(const _TCHAR *spec) {
if (!spec || !*spec || !_tcscmp(spec, _T("0"))) {
m_zoneRange.clear();
return true;
}
std::vector<std::pair<long,long>> ranges;
if (!parseZoneRange(spec, ranges))
return false;
m_zoneRange = spec;
return true;
}

const _TCHAR *NLP_ENGINE::getZoneRange() {
return m_zoneRange.c_str();
}

// The datum to pass to an analysis: 0 when unset, as before.
Expand Down
Loading
Loading