slopless 0.2.24 → 0.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/rules/semantic-thinness/patterns/abstract-metaphor-claim.json +83 -1
- package/dist/rules/semantic-thinness/patterns/deictic-summary.json +31 -2
- package/dist/rules/semantic-thinness/patterns/empty-scene-transition.json +2 -1
- package/dist/rules/semantic-thinness/private/concrete-guards.js +2 -0
- package/dist/rules/semantic-thinness/private/pattern-matcher.js +22 -6
- package/dist/rules/syntactic-patterns/contrast/contrastive-aphorism.js +2 -0
- package/dist/rules/syntactic-patterns/contrast/negation-reframe.js +1 -1
- package/dist/rules/syntactic-patterns/contrast/private/action-reframe-vocabulary.js +47 -0
- package/dist/rules/syntactic-patterns/contrast/private/audience-replacement.js +267 -0
- package/dist/rules/syntactic-patterns/contrast/private/copular-reframe.js +39 -1
- package/dist/rules/syntactic-patterns/contrast/private/evidence-limitation-pair.js +307 -0
- package/dist/rules/syntactic-patterns/contrast/private/inline-semicolon-reframe.js +50 -0
- package/dist/rules/syntactic-patterns/contrast/private/inline-short-negation.js +1 -0
- package/dist/rules/syntactic-patterns/contrast/private/negation-reframe-matcher.js +64 -7
- package/dist/rules/syntactic-patterns/contrast/private/negation-reframe-parts.js +37 -25
- package/dist/rules/syntactic-patterns/contrast/private/negation-vocabulary.js +27 -0
- package/dist/rules/syntactic-patterns/contrast/private/negative-consequence.js +301 -0
- package/dist/rules/syntactic-patterns/contrast/private/negative-slop-frames.js +51 -38
- package/dist/rules/syntactic-patterns/contrast/private/reframe-classification.js +21 -0
- package/dist/rules/syntactic-patterns/contrast/private/sequence-reframes.js +9 -0
- package/dist/rules/syntactic-patterns/contrast/private/temporal-reframe.js +318 -0
- package/dist/rules/syntactic-patterns/lead-ins/generic-signposting.js +85 -22
- package/dist/rules/syntactic-patterns/lead-ins/private/component-assignment-frame.js +239 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/discourse-evaluation.js +1 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/evaluative-colon-frame.js +166 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/reaction-frame.js +29 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/relative-discourse-frame.js +119 -0
- package/package.json +5 -5
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { tokens } from "../../../../shared/matchers/prose-patterns.js";
|
|
2
|
+
import { normalizeForMatch } from "../../../../shared/text/normalize.js";
|
|
3
|
+
const COPULAS = new Set(["are", "is", "was", "were"]);
|
|
4
|
+
const DETERMINERS = new Set(["a", "an", "the"]);
|
|
5
|
+
const ACTOR_ROLES = new Set([
|
|
6
|
+
"agent",
|
|
7
|
+
"analyst",
|
|
8
|
+
"author",
|
|
9
|
+
"creator",
|
|
10
|
+
"editor",
|
|
11
|
+
"model",
|
|
12
|
+
"producer",
|
|
13
|
+
"researcher",
|
|
14
|
+
"writer"
|
|
15
|
+
]);
|
|
16
|
+
const WRITING_ROLES = new Set([
|
|
17
|
+
"author",
|
|
18
|
+
"creator",
|
|
19
|
+
"editor",
|
|
20
|
+
"producer",
|
|
21
|
+
"writer"
|
|
22
|
+
]);
|
|
23
|
+
const COMPONENT_HEADS = new Set([
|
|
24
|
+
"basis",
|
|
25
|
+
"context",
|
|
26
|
+
"goal",
|
|
27
|
+
"input",
|
|
28
|
+
"material",
|
|
29
|
+
"output",
|
|
30
|
+
"source"
|
|
31
|
+
]);
|
|
32
|
+
const WRITING_MATERIALS = new Set([
|
|
33
|
+
"article",
|
|
34
|
+
"brief",
|
|
35
|
+
"content",
|
|
36
|
+
"copy",
|
|
37
|
+
"draft",
|
|
38
|
+
"evidence",
|
|
39
|
+
"findings",
|
|
40
|
+
"interviews",
|
|
41
|
+
"notes",
|
|
42
|
+
"recommendation",
|
|
43
|
+
"report",
|
|
44
|
+
"research",
|
|
45
|
+
"transcript"
|
|
46
|
+
]);
|
|
47
|
+
const EXPLANATION_WORDS = new Set([
|
|
48
|
+
"although",
|
|
49
|
+
"because",
|
|
50
|
+
"if",
|
|
51
|
+
"since",
|
|
52
|
+
"so",
|
|
53
|
+
"therefore",
|
|
54
|
+
"unless",
|
|
55
|
+
"when",
|
|
56
|
+
"where",
|
|
57
|
+
"which",
|
|
58
|
+
"while",
|
|
59
|
+
"who"
|
|
60
|
+
]);
|
|
61
|
+
const DETAIL_PREPOSITIONS = new Set([
|
|
62
|
+
"after",
|
|
63
|
+
"at",
|
|
64
|
+
"before",
|
|
65
|
+
"by",
|
|
66
|
+
"from",
|
|
67
|
+
"in",
|
|
68
|
+
"into",
|
|
69
|
+
"of",
|
|
70
|
+
"on",
|
|
71
|
+
"through",
|
|
72
|
+
"to",
|
|
73
|
+
"under",
|
|
74
|
+
"with"
|
|
75
|
+
]);
|
|
76
|
+
const TECHNICAL_LABELS = new Set([
|
|
77
|
+
"array",
|
|
78
|
+
"bytes",
|
|
79
|
+
"classifier",
|
|
80
|
+
"csv",
|
|
81
|
+
"event",
|
|
82
|
+
"file",
|
|
83
|
+
"image",
|
|
84
|
+
"interface",
|
|
85
|
+
"json",
|
|
86
|
+
"object",
|
|
87
|
+
"request",
|
|
88
|
+
"response",
|
|
89
|
+
"schema",
|
|
90
|
+
"service",
|
|
91
|
+
"string",
|
|
92
|
+
"tensor",
|
|
93
|
+
"vector"
|
|
94
|
+
]);
|
|
95
|
+
function withoutDeterminer(words) {
|
|
96
|
+
return DETERMINERS.has(words[0] ?? "") ? words.slice(1) : words;
|
|
97
|
+
}
|
|
98
|
+
function trimTerminalPunctuation(text) {
|
|
99
|
+
let end = text.length;
|
|
100
|
+
while (end > 0 && [".", "!", "?"].includes(text[end - 1] ?? "")) {
|
|
101
|
+
end -= 1;
|
|
102
|
+
}
|
|
103
|
+
return text.slice(0, end);
|
|
104
|
+
}
|
|
105
|
+
function hasTechnicalSyntax(text) {
|
|
106
|
+
const blockedCharacters = new Set([
|
|
107
|
+
"`",
|
|
108
|
+
"(",
|
|
109
|
+
")",
|
|
110
|
+
"[",
|
|
111
|
+
"]",
|
|
112
|
+
"{",
|
|
113
|
+
"}",
|
|
114
|
+
"=",
|
|
115
|
+
"<",
|
|
116
|
+
">",
|
|
117
|
+
"/",
|
|
118
|
+
"\\"
|
|
119
|
+
]);
|
|
120
|
+
for (const character of text) {
|
|
121
|
+
if (blockedCharacters.has(character) ||
|
|
122
|
+
(character >= "0" && character <= "9")) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
function parseNormalizedAssignment(normalized) {
|
|
129
|
+
if (normalized.length === 0 || hasTechnicalSyntax(normalized)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const words = tokens(normalized);
|
|
133
|
+
const copulaIndex = words.findIndex((word) => COPULAS.has(word));
|
|
134
|
+
if (copulaIndex < 1 ||
|
|
135
|
+
copulaIndex > 3 ||
|
|
136
|
+
words.length - copulaIndex - 1 < 1 ||
|
|
137
|
+
words.length - copulaIndex - 1 > 4) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const subject = withoutDeterminer(words.slice(0, copulaIndex));
|
|
141
|
+
const complement = withoutDeterminer(words.slice(copulaIndex + 1));
|
|
142
|
+
const blocked = [...subject, ...complement].some((word) => EXPLANATION_WORDS.has(word) || DETAIL_PREPOSITIONS.has(word));
|
|
143
|
+
return subject.length === 0 || complement.length === 0 || blocked
|
|
144
|
+
? undefined
|
|
145
|
+
: { complement, subject };
|
|
146
|
+
}
|
|
147
|
+
function assignmentClauseCandidates(clause) {
|
|
148
|
+
const normalized = trimTerminalPunctuation(normalizeForMatch(clause));
|
|
149
|
+
const candidates = [normalized];
|
|
150
|
+
const delimiterIndexes = [
|
|
151
|
+
normalized.indexOf(","),
|
|
152
|
+
normalized.indexOf(";"),
|
|
153
|
+
normalized.indexOf(" - "),
|
|
154
|
+
normalized.indexOf(" \u2013 "),
|
|
155
|
+
normalized.indexOf(" \u2014 ")
|
|
156
|
+
].filter((index) => index > 0);
|
|
157
|
+
const firstDelimiter = Math.min(...delimiterIndexes);
|
|
158
|
+
if (Number.isFinite(firstDelimiter)) {
|
|
159
|
+
candidates.push(normalized.slice(0, firstDelimiter).trim());
|
|
160
|
+
}
|
|
161
|
+
return candidates;
|
|
162
|
+
}
|
|
163
|
+
function parseAssignment(clause) {
|
|
164
|
+
for (const candidate of assignmentClauseCandidates(clause)) {
|
|
165
|
+
const assignment = parseNormalizedAssignment(candidate);
|
|
166
|
+
if (assignment !== undefined) {
|
|
167
|
+
return assignment;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
function isActorAssignment(assignment) {
|
|
173
|
+
const subjectHead = assignment.subject.at(-1) ?? "";
|
|
174
|
+
const complementHead = assignment.complement.at(-1) ?? "";
|
|
175
|
+
return ACTOR_ROLES.has(subjectHead) && ACTOR_ROLES.has(complementHead);
|
|
176
|
+
}
|
|
177
|
+
function isComponentAssignment(assignment) {
|
|
178
|
+
const subjectHead = assignment.subject.at(-1) ?? "";
|
|
179
|
+
return COMPONENT_HEADS.has(subjectHead);
|
|
180
|
+
}
|
|
181
|
+
function hasWritingContext(actor, component) {
|
|
182
|
+
const actorWords = [...actor.subject, ...actor.complement];
|
|
183
|
+
const componentHead = component.complement.at(-1) ?? "";
|
|
184
|
+
return (actorWords.some((word) => WRITING_ROLES.has(word)) ||
|
|
185
|
+
WRITING_MATERIALS.has(componentHead));
|
|
186
|
+
}
|
|
187
|
+
function isTechnicalInterface(actor, component) {
|
|
188
|
+
return [...actor.complement, ...component.complement].some((word) => TECHNICAL_LABELS.has(word));
|
|
189
|
+
}
|
|
190
|
+
function splitClauses(text) {
|
|
191
|
+
const normalized = normalizeForMatch(text);
|
|
192
|
+
const separators = [];
|
|
193
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
194
|
+
const character = normalized[index] ?? "";
|
|
195
|
+
const isPunctuationSeparator = character === "," || character === ";";
|
|
196
|
+
const isSpacedDash = ["-", "\u2013", "\u2014"].includes(character) &&
|
|
197
|
+
(normalized[index - 1] ?? "").trim() === "" &&
|
|
198
|
+
(normalized[index + 1] ?? "").trim() === "";
|
|
199
|
+
if (isPunctuationSeparator || isSpacedDash) {
|
|
200
|
+
separators.push(index);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (separators.length !== 1) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const separator = separators[0] ?? -1;
|
|
207
|
+
const first = normalized.slice(0, separator).trim();
|
|
208
|
+
const second = normalized.slice(separator + 1).trim();
|
|
209
|
+
return first.length > 0 && second.length > 0 ? [first, second] : undefined;
|
|
210
|
+
}
|
|
211
|
+
export function matchComponentAssignmentFrame(first, second) {
|
|
212
|
+
const pair = second === undefined ? splitClauses(first) : [first, second];
|
|
213
|
+
if (pair === undefined) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const firstAssignment = parseAssignment(pair[0]);
|
|
217
|
+
const secondAssignment = parseAssignment(pair[1]);
|
|
218
|
+
if (firstAssignment === undefined || secondAssignment === undefined) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const actor = isActorAssignment(firstAssignment)
|
|
222
|
+
? firstAssignment
|
|
223
|
+
: isActorAssignment(secondAssignment)
|
|
224
|
+
? secondAssignment
|
|
225
|
+
: undefined;
|
|
226
|
+
const component = isComponentAssignment(firstAssignment)
|
|
227
|
+
? firstAssignment
|
|
228
|
+
: isComponentAssignment(secondAssignment)
|
|
229
|
+
? secondAssignment
|
|
230
|
+
: undefined;
|
|
231
|
+
return actor !== undefined &&
|
|
232
|
+
component !== undefined &&
|
|
233
|
+
actor !== component &&
|
|
234
|
+
hasWritingContext(actor, component) &&
|
|
235
|
+
!isTechnicalInterface(actor, component)
|
|
236
|
+
? "component-assignment-frame"
|
|
237
|
+
: undefined;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=component-assignment-frame.js.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { hasConcreteTechnicalToken } from "../../../../shared/matchers/concrete-evidence.js";
|
|
2
|
+
import { tokens } from "../../../../shared/matchers/prose-patterns.js";
|
|
3
|
+
import { normalizeForMatch } from "../../../../shared/text/normalize.js";
|
|
4
|
+
const SUBJECT_HEADS = new Set([
|
|
5
|
+
"answer",
|
|
6
|
+
"answers",
|
|
7
|
+
"approach",
|
|
8
|
+
"approaches",
|
|
9
|
+
"architecture",
|
|
10
|
+
"architectures",
|
|
11
|
+
"audit",
|
|
12
|
+
"audits",
|
|
13
|
+
"choice",
|
|
14
|
+
"choices",
|
|
15
|
+
"decision",
|
|
16
|
+
"decisions",
|
|
17
|
+
"design",
|
|
18
|
+
"designs",
|
|
19
|
+
"machine",
|
|
20
|
+
"machines",
|
|
21
|
+
"method",
|
|
22
|
+
"methods",
|
|
23
|
+
"model",
|
|
24
|
+
"models",
|
|
25
|
+
"pipeline",
|
|
26
|
+
"pipelines",
|
|
27
|
+
"plan",
|
|
28
|
+
"plans",
|
|
29
|
+
"platform",
|
|
30
|
+
"platforms",
|
|
31
|
+
"problem",
|
|
32
|
+
"problems",
|
|
33
|
+
"process",
|
|
34
|
+
"processes",
|
|
35
|
+
"solution",
|
|
36
|
+
"solutions",
|
|
37
|
+
"stack",
|
|
38
|
+
"stacks",
|
|
39
|
+
"strategy",
|
|
40
|
+
"strategies",
|
|
41
|
+
"setup",
|
|
42
|
+
"setups",
|
|
43
|
+
"system",
|
|
44
|
+
"systems",
|
|
45
|
+
"workflow",
|
|
46
|
+
"workflows"
|
|
47
|
+
]);
|
|
48
|
+
const EVALUATIVE_ADJECTIVES = new Set([
|
|
49
|
+
"boring",
|
|
50
|
+
"clear",
|
|
51
|
+
"complex",
|
|
52
|
+
"easy",
|
|
53
|
+
"hard",
|
|
54
|
+
"heavy",
|
|
55
|
+
"obvious",
|
|
56
|
+
"simple",
|
|
57
|
+
"straightforward"
|
|
58
|
+
]);
|
|
59
|
+
const EVALUATIVE_LINKS = new Set([
|
|
60
|
+
"are",
|
|
61
|
+
"feel",
|
|
62
|
+
"feels",
|
|
63
|
+
"is",
|
|
64
|
+
"look",
|
|
65
|
+
"looks",
|
|
66
|
+
"was",
|
|
67
|
+
"were"
|
|
68
|
+
]);
|
|
69
|
+
const QUANTITY_WORDS = new Set([
|
|
70
|
+
"one",
|
|
71
|
+
"two",
|
|
72
|
+
"three",
|
|
73
|
+
"four",
|
|
74
|
+
"five",
|
|
75
|
+
"six",
|
|
76
|
+
"seven",
|
|
77
|
+
"eight",
|
|
78
|
+
"nine",
|
|
79
|
+
"ten",
|
|
80
|
+
"dozen",
|
|
81
|
+
"hundred",
|
|
82
|
+
"thousand",
|
|
83
|
+
"million",
|
|
84
|
+
"several"
|
|
85
|
+
]);
|
|
86
|
+
const MONTHS_AND_TIMES = new Set([
|
|
87
|
+
"january",
|
|
88
|
+
"february",
|
|
89
|
+
"march",
|
|
90
|
+
"april",
|
|
91
|
+
"may",
|
|
92
|
+
"june",
|
|
93
|
+
"july",
|
|
94
|
+
"august",
|
|
95
|
+
"september",
|
|
96
|
+
"october",
|
|
97
|
+
"november",
|
|
98
|
+
"december",
|
|
99
|
+
"midnight",
|
|
100
|
+
"noon"
|
|
101
|
+
]);
|
|
102
|
+
function hasDigitOrEquationSymbol(text) {
|
|
103
|
+
for (const character of text) {
|
|
104
|
+
if ((character >= "0" && character <= "9") ||
|
|
105
|
+
["=", "<", ">", "+", "\u00d7", "\u00f7"].includes(character)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function firstAsciiWord(text) {
|
|
112
|
+
let word = "";
|
|
113
|
+
for (const character of text.trimStart()) {
|
|
114
|
+
const isLetter = (character >= "A" && character <= "Z") ||
|
|
115
|
+
(character >= "a" && character <= "z");
|
|
116
|
+
const isWordContinuation = isLetter || (character >= "0" && character <= "9") || character === "-";
|
|
117
|
+
if (!isWordContinuation) {
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
word += character;
|
|
121
|
+
}
|
|
122
|
+
return word.length > 0 ? word : undefined;
|
|
123
|
+
}
|
|
124
|
+
function isIdentifierLike(word) {
|
|
125
|
+
for (const character of word.slice(1)) {
|
|
126
|
+
if (character >= "A" && character <= "Z") {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function hasExcludedEvidence(text, words) {
|
|
133
|
+
const trimmed = text.trimStart();
|
|
134
|
+
const firstWord = firstAsciiWord(trimmed);
|
|
135
|
+
const startsWithNamedTechnicalSubject = firstWord !== undefined &&
|
|
136
|
+
isIdentifierLike(firstWord) &&
|
|
137
|
+
hasConcreteTechnicalToken(text);
|
|
138
|
+
return (hasDigitOrEquationSymbol(text) ||
|
|
139
|
+
trimmed.startsWith('"') ||
|
|
140
|
+
trimmed.startsWith("'") ||
|
|
141
|
+
text.includes("`") ||
|
|
142
|
+
startsWithNamedTechnicalSubject ||
|
|
143
|
+
words.some((word) => QUANTITY_WORDS.has(word) || MONTHS_AND_TIMES.has(word)));
|
|
144
|
+
}
|
|
145
|
+
export function matchEvaluativeColonFrame(text) {
|
|
146
|
+
const colonIndex = text.indexOf(":");
|
|
147
|
+
if (colonIndex < 0 || text.indexOf(":", colonIndex + 1) >= 0) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const prefix = normalizeForMatch(text.slice(0, colonIndex));
|
|
151
|
+
const detail = text.slice(colonIndex + 1);
|
|
152
|
+
const prefixWords = tokens(prefix);
|
|
153
|
+
const detailWords = tokens(detail);
|
|
154
|
+
const copulaIndex = prefixWords.findIndex((word) => EVALUATIVE_LINKS.has(word));
|
|
155
|
+
if (copulaIndex < 1 ||
|
|
156
|
+
copulaIndex > 3 ||
|
|
157
|
+
prefixWords.length !== copulaIndex + 2 ||
|
|
158
|
+
!SUBJECT_HEADS.has(prefixWords[copulaIndex - 1] ?? "") ||
|
|
159
|
+
!EVALUATIVE_ADJECTIVES.has(prefixWords[copulaIndex + 1] ?? "") ||
|
|
160
|
+
detailWords.length < 2 ||
|
|
161
|
+
hasExcludedEvidence(detail, detailWords)) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
return prefixWords.join("-");
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=evaluative-colon-frame.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const REACTION_NOUNS = new Set(["detail", "part", "piece", "point", "thing"]);
|
|
2
|
+
const REACTION_VERBS = new Set(["got", "hit", "stuck", "surprised"]);
|
|
3
|
+
export function matchReactionFrame(words) {
|
|
4
|
+
const contracted = words[0] === "here's";
|
|
5
|
+
const start = contracted || (words[0] === "here" && words[1] === "is")
|
|
6
|
+
? contracted
|
|
7
|
+
? 1
|
|
8
|
+
: 2
|
|
9
|
+
: -1;
|
|
10
|
+
if (start < 0 ||
|
|
11
|
+
words[start] !== "the" ||
|
|
12
|
+
!REACTION_NOUNS.has(words[start + 1] ?? "") ||
|
|
13
|
+
words[start + 2] !== "that") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const verb = words[start + 3];
|
|
17
|
+
const directReaction = verb !== "stuck" &&
|
|
18
|
+
REACTION_VERBS.has(verb ?? "") &&
|
|
19
|
+
["me", "us"].includes(words[start + 4] ?? "") &&
|
|
20
|
+
words.length === start + 5;
|
|
21
|
+
const stuckReaction = verb === "stuck" &&
|
|
22
|
+
words[start + 4] === "with" &&
|
|
23
|
+
["me", "us"].includes(words[start + 5] ?? "") &&
|
|
24
|
+
words.length === start + 6;
|
|
25
|
+
return directReaction || stuckReaction
|
|
26
|
+
? "here-is-the-reaction-frame"
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=reaction-frame.js.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { tokens } from "../../../../shared/matchers/prose-patterns.js";
|
|
2
|
+
const DETERMINERS = new Set(["a", "an", "the", "this", "that"]);
|
|
3
|
+
const RELATIVE_FRAME_MODIFIERS = new Set([
|
|
4
|
+
"central",
|
|
5
|
+
"clearest",
|
|
6
|
+
"core",
|
|
7
|
+
"important",
|
|
8
|
+
"main",
|
|
9
|
+
"practical",
|
|
10
|
+
"real",
|
|
11
|
+
"simple",
|
|
12
|
+
"useful"
|
|
13
|
+
]);
|
|
14
|
+
const RELATIVE_HELPER_VERBS = new Set([
|
|
15
|
+
"helped",
|
|
16
|
+
"helps",
|
|
17
|
+
"mattered",
|
|
18
|
+
"matters",
|
|
19
|
+
"worked",
|
|
20
|
+
"works"
|
|
21
|
+
]);
|
|
22
|
+
const RELATIVE_USEFULNESS_ADJECTIVES = new Set([
|
|
23
|
+
"actionable",
|
|
24
|
+
"better",
|
|
25
|
+
"clear",
|
|
26
|
+
"easier",
|
|
27
|
+
"effective",
|
|
28
|
+
"helpful",
|
|
29
|
+
"practical",
|
|
30
|
+
"reliable",
|
|
31
|
+
"useful",
|
|
32
|
+
"valuable"
|
|
33
|
+
]);
|
|
34
|
+
const RELATIVE_EVALUATIONS = new Set([
|
|
35
|
+
"clear",
|
|
36
|
+
"consistent",
|
|
37
|
+
"predictable",
|
|
38
|
+
"repeatable",
|
|
39
|
+
"simple",
|
|
40
|
+
"straightforward",
|
|
41
|
+
"useful"
|
|
42
|
+
]);
|
|
43
|
+
const RELATIVE_FRAME_HEADS = new Set([
|
|
44
|
+
"answer",
|
|
45
|
+
"approach",
|
|
46
|
+
"claim",
|
|
47
|
+
"detail",
|
|
48
|
+
"evidence",
|
|
49
|
+
"factor",
|
|
50
|
+
"feature",
|
|
51
|
+
"fix",
|
|
52
|
+
"focus",
|
|
53
|
+
"idea",
|
|
54
|
+
"lesson",
|
|
55
|
+
"method",
|
|
56
|
+
"move",
|
|
57
|
+
"plan",
|
|
58
|
+
"point",
|
|
59
|
+
"process",
|
|
60
|
+
"question",
|
|
61
|
+
"result",
|
|
62
|
+
"review",
|
|
63
|
+
"rule",
|
|
64
|
+
"signal",
|
|
65
|
+
"solution",
|
|
66
|
+
"step",
|
|
67
|
+
"strategy",
|
|
68
|
+
"thing",
|
|
69
|
+
"way"
|
|
70
|
+
]);
|
|
71
|
+
function relativeFrameHeadIndex(words) {
|
|
72
|
+
if (!DETERMINERS.has(words[0] ?? "")) {
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
75
|
+
if (RELATIVE_FRAME_HEADS.has(words[1] ?? "")) {
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
return RELATIVE_FRAME_MODIFIERS.has(words[1] ?? "") &&
|
|
79
|
+
RELATIVE_FRAME_HEADS.has(words[2] ?? "")
|
|
80
|
+
? 2
|
|
81
|
+
: -1;
|
|
82
|
+
}
|
|
83
|
+
function matchRelativeHelperFrame(text, words, headIndex) {
|
|
84
|
+
const helperIndex = headIndex + 2;
|
|
85
|
+
const colonIndex = text.indexOf(":");
|
|
86
|
+
if (words[headIndex + 1] !== "that" ||
|
|
87
|
+
!RELATIVE_HELPER_VERBS.has(words[helperIndex] ?? "") ||
|
|
88
|
+
colonIndex < 0 ||
|
|
89
|
+
tokens(text.slice(0, colonIndex)).length !== helperIndex + 1) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
return `relative-${words[helperIndex] ?? "helps"}-frame`;
|
|
93
|
+
}
|
|
94
|
+
function matchRelativeUsefulnessFrame(words, headIndex) {
|
|
95
|
+
const makesIndex = headIndex + 2;
|
|
96
|
+
if (words[headIndex + 1] !== "that" ||
|
|
97
|
+
!["made", "makes"].includes(words[makesIndex] ?? "")) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const usefulnessIndex = words.findIndex((word, index) => index > makesIndex + 1 &&
|
|
101
|
+
index <= makesIndex + 6 &&
|
|
102
|
+
RELATIVE_USEFULNESS_ADJECTIVES.has(word));
|
|
103
|
+
const evaluationIndex = usefulnessIndex + 2;
|
|
104
|
+
return usefulnessIndex > 0 &&
|
|
105
|
+
["are", "is", "was", "were"].includes(words[usefulnessIndex + 1] ?? "") &&
|
|
106
|
+
RELATIVE_EVALUATIONS.has(words[evaluationIndex] ?? "") &&
|
|
107
|
+
words.length === evaluationIndex + 1
|
|
108
|
+
? "relative-makes-useful-evaluation"
|
|
109
|
+
: undefined;
|
|
110
|
+
}
|
|
111
|
+
export function matchRelativeDiscourseFrame(text, words) {
|
|
112
|
+
const headIndex = relativeFrameHeadIndex(words);
|
|
113
|
+
if (headIndex < 0) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return (matchRelativeHelperFrame(text, words, headIndex) ??
|
|
117
|
+
matchRelativeUsefulnessFrame(words, headIndex));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=relative-discourse-frame.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slopless",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"description": "Deterministic textlint rules and CLI for catching prose slop in English Markdown.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"textlint",
|
|
@@ -19,18 +19,18 @@
|
|
|
19
19
|
"cli"
|
|
20
20
|
],
|
|
21
21
|
"license": "MIT",
|
|
22
|
-
"author": "
|
|
22
|
+
"author": "berelevant.ai (https://berelevant.ai)",
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public",
|
|
25
25
|
"provenance": true
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/
|
|
29
|
+
"url": "git+https://github.com/berelevant-ai/slopless.git"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://github.com/
|
|
31
|
+
"homepage": "https://github.com/berelevant-ai/slopless#readme",
|
|
32
32
|
"bugs": {
|
|
33
|
-
"url": "https://github.com/
|
|
33
|
+
"url": "https://github.com/berelevant-ai/slopless/issues"
|
|
34
34
|
},
|
|
35
35
|
"type": "module",
|
|
36
36
|
"main": "./dist/index.js",
|