From 75a5b5e4dec8695bbaac1c2ae8e1c2298b583c74 Mon Sep 17 00:00:00 2001 From: upstream Date: Wed, 14 Oct 2026 18:48:44 +0200 Subject: [PATCH] Upstream spencermountain/compromise state of the touched files before ff559ff7cd --- src/1-one/sweep/methods/tagger/index.js | 58 +++++++++++++++++++++++++ tests/03-three/misc/sweep-tag.test.js | 40 +++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/1-one/sweep/methods/tagger/index.js create mode 100644 tests/03-three/misc/sweep-tag.test.js diff --git a/src/1-one/sweep/methods/tagger/index.js b/src/1-one/sweep/methods/tagger/index.js new file mode 100644 index 0000000..7f79c93 --- /dev/null +++ b/src/1-one/sweep/methods/tagger/index.js @@ -0,0 +1,58 @@ +import canBe from './canBe.js' + +const tagger = function (list, document, world) { + const { model, methods } = world + const { getDoc, setTag, unTag } = methods.one + const looksPlural = methods.two.looksPlural + if (list.length === 0) { + return list + } + return list.map(todo => { + if (!todo.tag && !todo.chunk && !todo.unTag) { + return + } + const reason = todo.reason || todo.match + const terms = getDoc([todo.pointer], document)[0] + let validated + // handle 'safe' tag + if (todo.safe === true) { + // check for conflicting tags + if (canBe(terms, todo.tag, model) === false) { + return + } + // dont tag half of a hyphenated word + if (terms[terms.length - 1].post === '-') { + return + } + // Only an exact single tag was checked by canBe; arrays and shorthand + // still need their individual checks in setTag. + if (typeof todo.tag === 'string' && Object.hasOwn(model.one.tagSet, todo.tag)) { + validated = model.one.tagSet[todo.tag] + } + } + if (todo.tag !== undefined) { + setTag(terms, todo.tag, world, todo.safe, `[post] '${reason}'`, validated) + // quick and dirty plural tagger 😕 + if (todo.tag === 'Noun' && looksPlural) { + const term = terms[terms.length - 1] + if (looksPlural(term.text)) { + setTag([term], 'Plural', world, todo.safe, 'quick-plural') + } else { + setTag([term], 'Singular', world, todo.safe, 'quick-singular') + } + } + // allow freezing this match, too + if (todo.freeze === true) { + terms.forEach(term => (term.frozen = true)) + } + } + if (todo.unTag !== undefined) { + unTag(terms, todo.unTag, model.one.tagSet, reason) + } + // allow setting chunks, too + if (todo.chunk) { + terms.forEach(t => (t.chunk = todo.chunk)) + } + }) +} +export default tagger diff --git a/tests/03-three/misc/sweep-tag.test.js b/tests/03-three/misc/sweep-tag.test.js new file mode 100644 index 0000000..b8235d8 --- /dev/null +++ b/tests/03-three/misc/sweep-tag.test.js @@ -0,0 +1,40 @@ +import test from 'tape' +import nlp from '../../lib/three.js' +const here = '[three/misc/sweep-tag] ' + +test('cache-one:', function (t) { + const matches = [{ match: '(he|she|they|#Noun) (has|have) (a|an)' }] + const net = nlp.buildNet(matches) + const doc = nlp(`you have a appointment`) + t.equal(doc.has(net), true, here + 'Noun cache') + t.end() +}) + +test('sweep-root:', function (t) { + const matches = [{ match: '{appointment}' }] + const net = nlp.buildNet(matches) + const doc = nlp(`you have some appointments`) + doc.compute('root') + t.equal(doc.has(net), true, here + 'root cache') + t.end() +}) + +test('sweep-freeze:', function (t) { + const matches = [ + { match: 'juicy fruit', tag: 'Singular', freeze: true }, + { match: 'front steps', tag: 'Plural', freeze: true }, + { match: 'juicy', tag: 'Adjective' }, + { match: 'front', tag: 'Adjective' }, + ] + const doc = nlp(`i ate juicy fruit on the front steps`) + const net = nlp.buildNet(matches) + doc.sweep(net) + let m = doc.match('juicy fruit') + t.equal(m.has('#Singular #Singular'), true, here + 'juicy fruit') + t.equal(m.has('#Adjective'), false, here + 'juicy fruit frozen') + + m = doc.match('front steps') + t.equal(m.has('#Plural #Plural'), true, here + 'front steps') + t.equal(m.has('#Adjective'), false, here + 'front steps frozen') + t.end() +})