Skip to content

OPENNLP-1928: Regex removal (1/10): Replace trivial patterns with character scans - #1311

Merged
rzo1 merged 3 commits into
mainfrom
OPENNLP-1928-regex-removal-scans
Sep 18, 2026
Merged

rzo1 merged 3 commits into
mainfrom
OPENNLP-1928-regex-removal-scans

Conversation

@rzo1

@rzo1 rzo1 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Part 1 of the regex removal (OPENNLP-1926). Split out of #1275, which mixed output-preserving rewrites with behavior changes; this PR holds only the rewrites whose output is unchanged.

What changes

  • DefaultPOSContextGenerator, DefaultLemmatizerContextGenerator: the [A-Z] and [0-9] capital and digit features are ASCII range checks (StringUtil.containsAsciiUpperCase, containsAsciiDigit). They stay ASCII-only because trained models depend on them.
  • TokenPatternFeatureGenerator: the [^a-zA-Z] check is a character scan.
  • BioCodec: the typed outcome pattern (.+)-\w+ is lastIndexOf('-') plus a word character scan. extractNameType moves from NameFinderME to BioCodec; both sequence validators call it there. It was package-private.
  • BrownCluster: the tab pattern split is StringUtil.split(line, '\t') from OPENNLP-1955: Replace literal String.split calls with a regex-free StringUtil.split #1302, same semantics.

StringUtil

New helpers: containsAsciiUpperCase, containsAsciiDigit, isAsciiLetter, isAsciiLowerCase, isAsciiDigit, endOfAsciiDigits, isLineTerminator, indexOfLineTerminator. Some are only used by later parts (AD readers in #1276, CoNLL-U in #1314, the glob matcher in #1278).

Sequence

Part PR
prep #1312 CompatibilityMode (OPENNLP-1973)
1/10 this PR
2/10–7/10 #1276, #1277, #1278, #1279, #1281, #1280 (to be rebased onto this branch)
8/10 #1313 EmojiCharSequenceNormalizer (OPENNLP-1974), stacked on #1312
9/10 #1314 Output corrections (OPENNLP-1975), stacked on this PR
10/10 #1282

Commits by @krickert from #1275, regrouped.

Replaces the patterns in the POS tagger and lemmatizer context generators,
BioCodec, BrownCluster and TokenPatternFeatureGenerator with character
scans and adds the shared ASCII and line terminator helpers to StringUtil.
The output of every site is unchanged. The type extraction of
NameFinderME moves to BioCodec.

Split out of #1275; the changes that alter output follow in separate
parts.
@rzo1
rzo1 force-pushed the OPENNLP-1928-regex-removal-scans branch from 1d1b66a to 33581d0 Compare September 18, 2026 11:17

@krickert krickert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex changes look good. I tested the new scans against the old regexes using the 2 million strings and they're identical.

AI found this: drop BrownCluster.splitTabs. PR #1302 introduced StringUtil.split(line, '\t'), and the tests confirm they behave identically. We can just use StringUtil and remove the private helper and its imports.

A few minor observations (no action needed): isAsciiUpperCase is private while the other ASCII checks are public, and a few methods don't have callers yet—though I see they're needed for later. The Javadoc is also spot on.

Approving now assuming we do the split tab change before merge

@rzo1

rzo1 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for running the 2M-string comparison! BrownCluster now uses StringUtil.split(line, '\t'); the private helper and its imports are gone.

@rzo1
rzo1 marked this pull request as ready for review September 18, 2026 11:40
krickert added a commit to ai-pipestream/opennlp that referenced this pull request Sep 18, 2026
The AD corpus readers no longer use regular expressions. This includes
the reader changes split out of OPENNLP-1928 (apache#1275): ADNameSampleStream,
ADPOSSampleStream, ADSentenceSampleStream and ADSentenceStream replace
their tag, whitespace, underline, hyphen, alphanumeric, metadata and
punctuation patterns with character scans over the StringUtil helpers.

ADSentenceStream.SentenceParser reads node, leaf and bizarre-leaf lines and
the fallback check with cursor scans, ADSentenceStream recognizes the
sentence, title, box, paragraph and text tags by name, and the
package-private ADMetadata reads the text id, the paragraph id and the
source for ADNameSampleStream and ADSentenceSampleStream. The scans follow
the format, not the accidents of the old patterns: an AD tree line without
a tag keeps its full lexeme, the first lexeme character is no longer lost,
paren-in-group node tails and quoted-bracket tags are read, and whitespace
follows the Unicode definition.

Hyphenated words are preserved by default; the ADTokenSampleStreamFactory
option and the CLI chapter describe the switch. A leaf without a
functional tag in ADChunkSampleStream is tagged with its lexeme, as the POS
reader does. The corpora chapter states the reader contract.

On the real corpora the output is unchanged: all AD streams over
Bosque_CF_8.0 and FlorestaVirgem_CF_3.0 are byte-identical to the previous
readers, and the ArvoresDeitadasEval training data hashes are the same.

Squashed from the review rounds of apache#1276 for the rebase onto apache#1311.

@krickert krickert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread opennlp-api/src/main/java/opennlp/tools/util/StringUtil.java Outdated
krickert added a commit to ai-pipestream/opennlp that referenced this pull request Sep 18, 2026
The AD corpus readers no longer use regular expressions. This includes
the reader changes split out of OPENNLP-1928 (apache#1275): ADNameSampleStream,
ADPOSSampleStream, ADSentenceSampleStream and ADSentenceStream replace
their tag, whitespace, underline, hyphen, alphanumeric, metadata and
punctuation patterns with character scans over the StringUtil helpers.

ADSentenceStream.SentenceParser reads node, leaf and bizarre-leaf lines and
the fallback check with cursor scans, ADSentenceStream recognizes the
sentence, title, box, paragraph and text tags by name, and the
package-private ADMetadata reads the text id, the paragraph id and the
source for ADNameSampleStream and ADSentenceSampleStream. The scans follow
the format, not the accidents of the old patterns: an AD tree line without
a tag keeps its full lexeme, the first lexeme character is no longer lost,
paren-in-group node tails and quoted-bracket tags are read, and whitespace
follows the Unicode definition.

Hyphenated words are preserved by default; the ADTokenSampleStreamFactory
option and the CLI chapter describe the switch. A leaf without a
functional tag in ADChunkSampleStream is tagged with its lexeme, as the POS
reader does. The corpora chapter states the reader contract.

On the real corpora the output is unchanged: all AD streams over
Bosque_CF_8.0 and FlorestaVirgem_CF_3.0 are byte-identical to the previous
readers, and the ArvoresDeitadasEval training data hashes are the same.

Squashed from the review rounds of apache#1276 for the rebase onto apache#1311.
@jzonthemtn

Copy link
Copy Markdown
Contributor

+1 Thanks @rzo1

@rzo1
rzo1 merged commit 4cce2c5 into main Sep 18, 2026
10 checks passed
@rzo1
rzo1 deleted the OPENNLP-1928-regex-removal-scans branch September 18, 2026 17:04
krickert added a commit to ai-pipestream/opennlp that referenced this pull request Sep 18, 2026
The AD corpus readers no longer use regular expressions. This includes
the reader changes split out of OPENNLP-1928 (apache#1275): ADNameSampleStream,
ADPOSSampleStream, ADSentenceSampleStream and ADSentenceStream replace
their tag, whitespace, underline, hyphen, alphanumeric, metadata and
punctuation patterns with character scans over the StringUtil helpers.

ADSentenceStream.SentenceParser reads node, leaf and bizarre-leaf lines and
the fallback check with cursor scans, ADSentenceStream recognizes the
sentence, title, box, paragraph and text tags by name, and the
package-private ADMetadata reads the text id, the paragraph id and the
source for ADNameSampleStream and ADSentenceSampleStream. The scans follow
the format, not the accidents of the old patterns: an AD tree line without
a tag keeps its full lexeme, the first lexeme character is no longer lost,
paren-in-group node tails and quoted-bracket tags are read, and whitespace
follows the Unicode definition.

Hyphenated words are preserved by default; the ADTokenSampleStreamFactory
option and the CLI chapter describe the switch. A leaf without a
functional tag in ADChunkSampleStream is tagged with its lexeme, as the POS
reader does. The corpora chapter states the reader contract.

On the real corpora the output is unchanged: all AD streams over
Bosque_CF_8.0 and FlorestaVirgem_CF_3.0 are byte-identical to the previous
readers, and the ArvoresDeitadasEval training data hashes are the same.

Squashed from the review rounds of apache#1276 for the rebase onto apache#1311.
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.

3 participants