diff --git a/src/2-two/postTagger/model/connectors.js b/src/2-two/postTagger/model/connectors.js new file mode 100644 index 0000000..63b1572 --- /dev/null +++ b/src/2-two/postTagger/model/connectors.js @@ -0,0 +1,86 @@ +// Before the meal ended, we left. +// Before the meal, we left. +// After the news that she resigned, we called. +const noun = '(#Noun && !#Possessive && !@hasComma)' +const modifiers = '(#Determiner|#Possessive)? #Adverb+? #Adjective+?' +const subject = `${modifiers} ${noun}+` +const predicate = '#Adverb+? not? (#Verb && !#Gerund && !#Particle)' + +const clauses = ['before', 'after', 'since', 'until', 'till', 'as', 'than', 'when', 'whereas'] +const rules = clauses.flatMap(word => [ + // [before] she left + // [after] she left + // [since] she left... + { match: `[${word}] ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: `${word}-clause` }, + // [Before] the guests from the village arrived, we ate. + // [After] the guests from the village arrived, we ate. + // [Since] the guests from the village arrived, we ate. ... + { match: `[${word}] ${subject} (from|of|with|in|on|at|beside|near) ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: `${word}-mod-subj` }, +]) + +export default [ + ...rules, + // [Before] the dog and the cat woke, she left. + // [before] the dog and the cat woke + // [after] the dog and the cat woke... + ...['before', 'after', 'until', 'when', 'while'].map(word => ({ + match: `^[${word}] ${subject} and ${subject} ${predicate}`, + group: 0, tag: 'Conjunction', reason: `${word}-joint-subj`, + })), + // She bought flowers, [for] I was ill. + { match: `@hasComma [for] ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: 'causal-for' }, + // Everyone [but] me agreed. + ...['everyone', 'everybody', 'everything', 'anyone', 'anybody', 'anything', 'nobody', 'nothing', 'all'].map(word => ({ + match: `${word} [but] (me|him|her|us|them|#Determiner|#Possessive|#ProperNoun)`, + group: 0, tag: 'Preposition', reason: 'exceptive-but', + })), + + // The cat slept [under] the table. He sat [beside] me. + // the plane flew well [above] the clouds + // she stood directly [below] the window... + ...['above', 'below', 'under', 'over', 'beside', 'behind', 'against', 'outside', 'inside', 'near'].map(word => ({ + match: `[(${word} && !#Verb)] (#Determiner|#Possessive|#Pronoun|#ProperNoun)`, + group: 0, tag: 'Preposition', reason: `${word}-space-obj`, + })), + // We looked [under] the bed. + { match: '#Verb [under] (#Determiner|#Possessive|#Pronoun)', group: 0, tag: 'Preposition', reason: 'under-obj' }, + // She sings [like] her mother + { match: '(#Verb && !#Auxiliary && !#Modal && !do && !does && !did && !have && !has && !had) [like] (#Noun|#Determiner|#Possessive)', group: 0, tag: 'Preposition', reason: 'like-like' }, + // images on a screen [like] humans do + { match: '#Noun [like] #Noun+ (do|does|did)$', group: 0, tag: 'Preposition', reason: 'noun-like-cmp' }, + // cities [like] New York, Boston + ...['', '#Place ', '#Place #Place '].map(prefix => ({ + match: `#Plural [like] ${prefix}(#Place && @hasComma) #Place`, + group: 0, tag: 'Preposition', reason: 'like-place', + })), + // [Like] his brother, he enjoys chess + { match: '^[like] (#Determiner|#Possessive)? #Adjective+? (#Noun && @hasComma)', group: 0, tag: 'Preposition', reason: 'init-like' }, + // She sings [like] her mother does + { match: `(#Verb && !#Auxiliary && !#Modal && !do && !does && !did && !have && !has && !had) [like] ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: 'manner-like' }, + // I like tea, [like] my sister does. + { match: `@hasComma [like] ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: 'comma-like-cl' }, + // We talked about the fact [that] she resigned. + { match: `#Noun [that] ${subject} ${predicate}`, group: 0, tag: 'Conjunction', reason: 'noun-that' }, + // I have heard that story [before] + { match: '#Verb (#Determiner|#Possessive)? #Noun+? [(before|since)]$', group: 0, tag: 'Adverb', notIf: '@hasQuestionMark', reason: 'time-adv' }, + // We met shortly [after]. + { match: '(shortly|soon|long) [after]$', group: 0, tag: 'Adverb', reason: 'after-adv' }, + // She has [since] moved. + { match: '(has|have|had) [since] #PastTense', group: 0, tag: 'Adverb', reason: 'perf-since-adv' }, + // She has not arrived [yet]. + { match: '#PastTense [yet]$', group: 0, tag: 'Adverb', reason: 'yet-adv' }, + // Who did she arrive [before]? + { + match: '^(who|whom) #Verb #Pronoun #Verb [before]$', + group: 0, + tag: 'Preposition', + reason: 'before-end', + }, + // We will leave [when] the rain stops. + { + match: '#Modal #Infinitive [when] #Determiner', + group: 0, + tag: 'Conjunction', + reason: 'leave-when', + }, +] diff --git a/tests/two/tagger/grammar/connectors.test.js b/tests/two/tagger/grammar/connectors.test.js new file mode 100644 index 0000000..479c7df --- /dev/null +++ b/tests/two/tagger/grammar/connectors.test.js @@ -0,0 +1,73 @@ +import test from 'tape' +import nlp from '../../_lib.js' + +test('connectors distinguish nominal objects from finite clauses', t => { + const cases = [ + ['She left before me.', 'before', 'Preposition'], + ['She left before I finished.', 'before', 'Conjunction'], + ['Before dinner, we rested.', 'before', 'Preposition'], + ['Before the dinner ended, we rested.', 'before', 'Conjunction'], + ['He rested after swimming.', 'after', 'Preposition'], + ['He rested after she finished.', 'after', 'Conjunction'], + ['Since the wedding, she has called.', 'since', 'Preposition'], + ['Since the new bridge opened, traffic flows smoothly.', 'since', 'Conjunction'], + ['We waited until noon.', 'until', 'Preposition'], + ['We waited until the very tired driver returned.', 'until', 'Conjunction'], + ['After the guests from the village arrived, we ate.', 'after', 'Conjunction'], + ['She works as a nurse.', 'as', 'Preposition'], + ['As the child slept, we whispered.', 'as', 'Conjunction'], + ['She is taller than me.', 'than', 'Preposition'], + ['She is taller than I am.', 'than', 'Conjunction'], + ['She bought flowers for me.', 'for', 'Preposition'], + ['She bought flowers, for I was ill.', 'for', 'Conjunction'], + ['Everyone but me agreed.', 'but', 'Preposition'], + ['She sings like her mother.', 'like', 'Preposition'], + ['She sings like her mother does.', 'like', 'Conjunction'], + ['Like his brother, he enjoys chess.', 'like', 'Preposition'], + ['We talked about the fact that she resigned.', 'that', 'Conjunction'], + ['The cat slept under the table.', 'under', 'Preposition'], + ['He sat beside me.', 'beside', 'Preposition'], + ['The bird flew over the fence.', 'over', 'Preposition'], + ['They stood behind us.', 'behind', 'Preposition'], + ['He leaned against the wall.', 'against', 'Preposition'], + ['He sat near the door.', 'near', 'Preposition'], + ] + for (const [str, word, tag] of cases) { + const term = nlp(str).match(word) + t.equal(term.has('#' + tag), true, str) + const other = tag === 'Preposition' ? 'Conjunction' : 'Preposition' + t.equal(term.has('#' + other), false, str + ' excludes ' + other) + } + t.end() +}) + +test('connector rules preserve boundaries and other word senses', t => { + for (const [str, word, tag] of [ + ['Before you, I was the youngest.', 'before', 'Preposition'], + ['After dinner I left.', 'after', 'Preposition'], + ['Before lunch she called.', 'before', 'Preposition'], + ['Since the wedding she has called.', 'since', 'Preposition'], + ['Before dinner, we rested before we cooked.', 'before', 'Preposition'], + ['We left before dinner, and the guests arrived.', 'before', 'Preposition'], + ['After the news that she resigned, we called.', 'after', 'Preposition'], + ['She spoke before the announcement that the shop was closing.', 'before', 'Preposition'], + ['The nurses are sweet as pie and the doctor is wonderful.', 'as', 'Preposition'], + ['I thanked everyone but she left.', 'but', 'Conjunction'], + ['I like her mother.', 'like', 'Verb'], + ['I would like tea.', 'like', 'Verb'], + ['We do like tea.', 'like', 'Verb'], + ['Like my page.', 'like', 'Verb'], + ['The game is over.', 'over', 'Adjective'], + ['The ship will near the coast.', 'near', 'Verb'], + ['She has since moved.', 'since', 'Adverb'], + ['I have heard that story before.', 'before', 'Adverb'], + ['We met shortly after.', 'after', 'Adverb'], + ['She has not arrived yet.', 'yet', 'Adverb'], + ['Who did she arrive before?', 'before', 'Preposition'], + ]) { + t.equal(nlp(str).match(word).first().has('#' + tag), true, str) + } + const doc = nlp('Before dinner, we rested before we cooked.') + t.equal(doc.match('before').last().has('#Conjunction'), true, 'same word, different complements') + t.end() +})