opengrammar-server 2.0.615350
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.npm.md +95 -0
- package/bin/opengrammar-server.js +111 -0
- package/dist/server.js +48639 -0
- package/package.json +80 -0
- package/server-node.ts +159 -0
- package/server.ts +15 -0
- package/src/analyzer.ts +542 -0
- package/src/dictionary.ts +1973 -0
- package/src/index.ts +978 -0
- package/src/nlp/nlp-engine.ts +17 -0
- package/src/nlp/tone-analyzer.ts +269 -0
- package/src/rephraser.ts +146 -0
- package/src/rules/categories/academic-writing.ts +182 -0
- package/src/rules/categories/adjectives-adverbs.ts +152 -0
- package/src/rules/categories/articles.ts +160 -0
- package/src/rules/categories/business-writing.ts +250 -0
- package/src/rules/categories/capitalization.ts +79 -0
- package/src/rules/categories/clarity.ts +117 -0
- package/src/rules/categories/common-errors.ts +601 -0
- package/src/rules/categories/confused-words.ts +219 -0
- package/src/rules/categories/conjunctions.ts +176 -0
- package/src/rules/categories/dangling-modifiers.ts +123 -0
- package/src/rules/categories/formality.ts +274 -0
- package/src/rules/categories/formatting-idioms.ts +323 -0
- package/src/rules/categories/gerund-infinitive.ts +274 -0
- package/src/rules/categories/grammar-advanced.ts +294 -0
- package/src/rules/categories/grammar.ts +286 -0
- package/src/rules/categories/inclusive-language.ts +280 -0
- package/src/rules/categories/nouns-pronouns.ts +233 -0
- package/src/rules/categories/prepositions-extended.ts +217 -0
- package/src/rules/categories/prepositions.ts +159 -0
- package/src/rules/categories/punctuation.ts +347 -0
- package/src/rules/categories/quantity-agreement.ts +200 -0
- package/src/rules/categories/readability.ts +293 -0
- package/src/rules/categories/sentence-structure.ts +100 -0
- package/src/rules/categories/spelling-advanced.ts +164 -0
- package/src/rules/categories/spelling.ts +119 -0
- package/src/rules/categories/style-tone.ts +511 -0
- package/src/rules/categories/style.ts +78 -0
- package/src/rules/categories/subject-verb-agreement.ts +201 -0
- package/src/rules/categories/tone-rules.ts +206 -0
- package/src/rules/categories/verb-tense.ts +582 -0
- package/src/rules/context-filter.ts +446 -0
- package/src/rules/index.ts +96 -0
- package/src/rules/ruleset-part1-cj-pu-sp.json +657 -0
- package/src/rules/ruleset-part1-np-ad-aa-pr.json +831 -0
- package/src/rules/ruleset-part1-ss-vt.json +907 -0
- package/src/rules/ruleset-part2-cw-st-nf.json +318 -0
- package/src/rules/ruleset-part3-aw-bw-il-rd.json +161 -0
- package/src/rules/types.ts +79 -0
- package/src/shared-types.ts +152 -0
- package/src/spellchecker.ts +418 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import { createRegexRule, type Rule } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* Common Everyday Errors — CE Module
|
|
6
|
+
* Catches the errors that trip people up most frequently:
|
|
7
|
+
* • Missing comma in direct address greetings (Hi John → Hi, John)
|
|
8
|
+
* • Incorrectly split compound words (my self → myself)
|
|
9
|
+
* • Missing articles before age descriptors (a 9-year-old)
|
|
10
|
+
* • Incorrect spacing in pronouns (him self → himself)
|
|
11
|
+
* • Wrongly split common words (every one → everyone)
|
|
12
|
+
* • Common phrase errors (in case of → in case)
|
|
13
|
+
* ═══════════════════════════════════════════════════════════════
|
|
14
|
+
*/
|
|
15
|
+
export const commonErrorRules: Rule[] = [
|
|
16
|
+
|
|
17
|
+
// ════════════════════════════════════════════════════════
|
|
18
|
+
// 1. GREETING COMMA (Direct Address)
|
|
19
|
+
// "Hi John" → "Hi, John" "Hello Sarah" → "Hello, Sarah"
|
|
20
|
+
// ════════════════════════════════════════════════════════
|
|
21
|
+
createRegexRule({
|
|
22
|
+
id: 'CE_GRT_hi_no_comma',
|
|
23
|
+
category: 'grammar',
|
|
24
|
+
pattern: /\bHi\s+([A-Z][a-zA-Z]+)\b(?!,)/,
|
|
25
|
+
suggestion: (m) => `Hi, ${m[1]}`,
|
|
26
|
+
reason: 'Add a comma after a greeting before the person\'s name: "Hi, [Name]".',
|
|
27
|
+
}),
|
|
28
|
+
createRegexRule({
|
|
29
|
+
id: 'CE_GRT_hello_no_comma',
|
|
30
|
+
category: 'grammar',
|
|
31
|
+
pattern: /\bHello\s+([A-Z][a-zA-Z]+)\b(?!,)/,
|
|
32
|
+
suggestion: (m) => `Hello, ${m[1]}`,
|
|
33
|
+
reason: 'Add a comma after a greeting before the person\'s name: "Hello, [Name]".',
|
|
34
|
+
}),
|
|
35
|
+
createRegexRule({
|
|
36
|
+
id: 'CE_GRT_hey_no_comma',
|
|
37
|
+
category: 'grammar',
|
|
38
|
+
pattern: /\bHey\s+([A-Z][a-zA-Z]+)\b(?!,)/,
|
|
39
|
+
suggestion: (m) => `Hey, ${m[1]}`,
|
|
40
|
+
reason: 'Add a comma after a greeting before the person\'s name: "Hey, [Name]".',
|
|
41
|
+
}),
|
|
42
|
+
createRegexRule({
|
|
43
|
+
id: 'CE_GRT_dear_no_comma',
|
|
44
|
+
category: 'grammar',
|
|
45
|
+
pattern: /\bDear\s+([A-Z][a-zA-Z]+)\b(?!,)/,
|
|
46
|
+
suggestion: (m) => `Dear ${m[1]},`,
|
|
47
|
+
reason: 'Add a comma after the name in a letter salutation: "Dear [Name],".',
|
|
48
|
+
}),
|
|
49
|
+
createRegexRule({
|
|
50
|
+
id: 'CE_GRT_greetings_no_comma',
|
|
51
|
+
category: 'grammar',
|
|
52
|
+
pattern: /\bGreetings\s+([A-Z][a-zA-Z]+)\b(?!,)/,
|
|
53
|
+
suggestion: (m) => `Greetings, ${m[1]}`,
|
|
54
|
+
reason: 'Add a comma after a greeting before the person\'s name.',
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
// ════════════════════════════════════════════════════════
|
|
58
|
+
// 2. SPLIT REFLEXIVE PRONOUNS
|
|
59
|
+
// "my self" → "myself", "him self" → "himself", etc.
|
|
60
|
+
// ════════════════════════════════════════════════════════
|
|
61
|
+
createRegexRule({
|
|
62
|
+
id: 'CE_REF_myself_split',
|
|
63
|
+
category: 'spelling',
|
|
64
|
+
pattern: /\bmy\s+self\b/i,
|
|
65
|
+
suggestion: 'myself',
|
|
66
|
+
reason: '"Myself" is one word, not two.',
|
|
67
|
+
}),
|
|
68
|
+
createRegexRule({
|
|
69
|
+
id: 'CE_REF_himself_split',
|
|
70
|
+
category: 'spelling',
|
|
71
|
+
pattern: /\bhim\s+self\b/i,
|
|
72
|
+
suggestion: 'himself',
|
|
73
|
+
reason: '"Himself" is one word, not two.',
|
|
74
|
+
}),
|
|
75
|
+
createRegexRule({
|
|
76
|
+
id: 'CE_REF_herself_split',
|
|
77
|
+
category: 'spelling',
|
|
78
|
+
pattern: /\bher\s+self\b/i,
|
|
79
|
+
suggestion: 'herself',
|
|
80
|
+
reason: '"Herself" is one word, not two.',
|
|
81
|
+
}),
|
|
82
|
+
createRegexRule({
|
|
83
|
+
id: 'CE_REF_yourself_split',
|
|
84
|
+
category: 'spelling',
|
|
85
|
+
pattern: /\byour\s+self\b/i,
|
|
86
|
+
suggestion: 'yourself',
|
|
87
|
+
reason: '"Yourself" is one word, not two.',
|
|
88
|
+
}),
|
|
89
|
+
createRegexRule({
|
|
90
|
+
id: 'CE_REF_themselves_split',
|
|
91
|
+
category: 'spelling',
|
|
92
|
+
pattern: /\bthem\s+selves\b/i,
|
|
93
|
+
suggestion: 'themselves',
|
|
94
|
+
reason: '"Themselves" is one word, not two.',
|
|
95
|
+
}),
|
|
96
|
+
createRegexRule({
|
|
97
|
+
id: 'CE_REF_ourselves_split',
|
|
98
|
+
category: 'spelling',
|
|
99
|
+
pattern: /\bour\s+selves\b/i,
|
|
100
|
+
suggestion: 'ourselves',
|
|
101
|
+
reason: '"Ourselves" is one word, not two.',
|
|
102
|
+
}),
|
|
103
|
+
createRegexRule({
|
|
104
|
+
id: 'CE_REF_itself_split',
|
|
105
|
+
category: 'spelling',
|
|
106
|
+
pattern: /\bits\s+self\b/i,
|
|
107
|
+
suggestion: 'itself',
|
|
108
|
+
reason: '"Itself" is one word, not two.',
|
|
109
|
+
}),
|
|
110
|
+
createRegexRule({
|
|
111
|
+
id: 'CE_REF_yourselves_split',
|
|
112
|
+
category: 'spelling',
|
|
113
|
+
pattern: /\byour\s+selves\b/i,
|
|
114
|
+
suggestion: 'yourselves',
|
|
115
|
+
reason: '"Yourselves" is one word, not two.',
|
|
116
|
+
}),
|
|
117
|
+
|
|
118
|
+
// ════════════════════════════════════════════════════════
|
|
119
|
+
// 3. INCORRECTLY SPLIT COMPOUND WORDS
|
|
120
|
+
// ════════════════════════════════════════════════════════
|
|
121
|
+
createRegexRule({
|
|
122
|
+
id: 'CE_CPD_everyone',
|
|
123
|
+
category: 'spelling',
|
|
124
|
+
pattern: /\bevery\s+one\b(?!\s+(of|who|that|which|else))/i,
|
|
125
|
+
suggestion: 'everyone',
|
|
126
|
+
reason: '"Everyone" (meaning all people) is one word.',
|
|
127
|
+
}),
|
|
128
|
+
createRegexRule({
|
|
129
|
+
id: 'CE_CPD_someone',
|
|
130
|
+
category: 'spelling',
|
|
131
|
+
pattern: /\bsome\s+one\b(?!\s+(of|who|that|which|else))/i,
|
|
132
|
+
suggestion: 'someone',
|
|
133
|
+
reason: '"Someone" is one word.',
|
|
134
|
+
}),
|
|
135
|
+
createRegexRule({
|
|
136
|
+
id: 'CE_CPD_anyone',
|
|
137
|
+
category: 'spelling',
|
|
138
|
+
pattern: /\bany\s+one\b(?!\s+(of|who|that|which|else))/i,
|
|
139
|
+
suggestion: 'anyone',
|
|
140
|
+
reason: '"Anyone" is one word.',
|
|
141
|
+
}),
|
|
142
|
+
createRegexRule({
|
|
143
|
+
id: 'CE_CPD_noone',
|
|
144
|
+
category: 'spelling',
|
|
145
|
+
pattern: /\bno\s+one\s+else\b/i,
|
|
146
|
+
suggestion: 'no one else',
|
|
147
|
+
reason: '"No one" is kept as two words (unlike everyone/someone).',
|
|
148
|
+
}),
|
|
149
|
+
createRegexRule({
|
|
150
|
+
id: 'CE_CPD_everything',
|
|
151
|
+
category: 'spelling',
|
|
152
|
+
pattern: /\bevery\s+thing\b/i,
|
|
153
|
+
suggestion: 'everything',
|
|
154
|
+
reason: '"Everything" is one word.',
|
|
155
|
+
}),
|
|
156
|
+
createRegexRule({
|
|
157
|
+
id: 'CE_CPD_something',
|
|
158
|
+
category: 'spelling',
|
|
159
|
+
pattern: /\bsome\s+thing\b/i,
|
|
160
|
+
suggestion: 'something',
|
|
161
|
+
reason: '"Something" is one word.',
|
|
162
|
+
}),
|
|
163
|
+
createRegexRule({
|
|
164
|
+
id: 'CE_CPD_anything',
|
|
165
|
+
category: 'spelling',
|
|
166
|
+
pattern: /\bany\s+thing\b/i,
|
|
167
|
+
suggestion: 'anything',
|
|
168
|
+
reason: '"Anything" is one word.',
|
|
169
|
+
}),
|
|
170
|
+
createRegexRule({
|
|
171
|
+
id: 'CE_CPD_everybody',
|
|
172
|
+
category: 'spelling',
|
|
173
|
+
pattern: /\bevery\s+body\b/i,
|
|
174
|
+
suggestion: 'everybody',
|
|
175
|
+
reason: '"Everybody" is one word.',
|
|
176
|
+
}),
|
|
177
|
+
createRegexRule({
|
|
178
|
+
id: 'CE_CPD_somebody',
|
|
179
|
+
category: 'spelling',
|
|
180
|
+
pattern: /\bsome\s+body\b/i,
|
|
181
|
+
suggestion: 'somebody',
|
|
182
|
+
reason: '"Somebody" is one word.',
|
|
183
|
+
}),
|
|
184
|
+
createRegexRule({
|
|
185
|
+
id: 'CE_CPD_anybody',
|
|
186
|
+
category: 'spelling',
|
|
187
|
+
pattern: /\bany\s+body\b/i,
|
|
188
|
+
suggestion: 'anybody',
|
|
189
|
+
reason: '"Anybody" is one word.',
|
|
190
|
+
}),
|
|
191
|
+
createRegexRule({
|
|
192
|
+
id: 'CE_CPD_nowhere',
|
|
193
|
+
category: 'spelling',
|
|
194
|
+
pattern: /\bno\s+where\b/i,
|
|
195
|
+
suggestion: 'nowhere',
|
|
196
|
+
reason: '"Nowhere" is one word.',
|
|
197
|
+
}),
|
|
198
|
+
createRegexRule({
|
|
199
|
+
id: 'CE_CPD_somewhere',
|
|
200
|
+
category: 'spelling',
|
|
201
|
+
pattern: /\bsome\s+where\b/i,
|
|
202
|
+
suggestion: 'somewhere',
|
|
203
|
+
reason: '"Somewhere" is one word.',
|
|
204
|
+
}),
|
|
205
|
+
createRegexRule({
|
|
206
|
+
id: 'CE_CPD_anywhere',
|
|
207
|
+
category: 'spelling',
|
|
208
|
+
pattern: /\bany\s+where\b/i,
|
|
209
|
+
suggestion: 'anywhere',
|
|
210
|
+
reason: '"Anywhere" is one word.',
|
|
211
|
+
}),
|
|
212
|
+
createRegexRule({
|
|
213
|
+
id: 'CE_CPD_everywhere',
|
|
214
|
+
category: 'spelling',
|
|
215
|
+
pattern: /\bevery\s+where\b/i,
|
|
216
|
+
suggestion: 'everywhere',
|
|
217
|
+
reason: '"Everywhere" is one word.',
|
|
218
|
+
}),
|
|
219
|
+
createRegexRule({
|
|
220
|
+
id: 'CE_CPD_maybe',
|
|
221
|
+
category: 'spelling',
|
|
222
|
+
pattern: /\bmay\s+be\b(?!\s+(true|false|right|wrong|possible|necessary))/i,
|
|
223
|
+
suggestion: 'maybe',
|
|
224
|
+
reason: '"Maybe" (meaning perhaps) is one word. "May be" is the verb phrase.',
|
|
225
|
+
}),
|
|
226
|
+
createRegexRule({
|
|
227
|
+
id: 'CE_CPD_cannot',
|
|
228
|
+
category: 'spelling',
|
|
229
|
+
pattern: /\bcan\s+not\b/i,
|
|
230
|
+
suggestion: 'cannot',
|
|
231
|
+
reason: '"Cannot" is typically written as one word in formal usage.',
|
|
232
|
+
}),
|
|
233
|
+
createRegexRule({
|
|
234
|
+
id: 'CE_CPD_without',
|
|
235
|
+
category: 'spelling',
|
|
236
|
+
pattern: /\bwith\s+out\b/i,
|
|
237
|
+
suggestion: 'without',
|
|
238
|
+
reason: '"Without" is one word.',
|
|
239
|
+
}),
|
|
240
|
+
createRegexRule({
|
|
241
|
+
id: 'CE_CPD_within',
|
|
242
|
+
category: 'spelling',
|
|
243
|
+
pattern: /\bwith\s+in\b/i,
|
|
244
|
+
suggestion: 'within',
|
|
245
|
+
reason: '"Within" is one word.',
|
|
246
|
+
}),
|
|
247
|
+
createRegexRule({
|
|
248
|
+
id: 'CE_CPD_outside',
|
|
249
|
+
category: 'spelling',
|
|
250
|
+
pattern: /\bout\s+side\b/i,
|
|
251
|
+
suggestion: 'outside',
|
|
252
|
+
reason: '"Outside" is one word.',
|
|
253
|
+
}),
|
|
254
|
+
createRegexRule({
|
|
255
|
+
id: 'CE_CPD_inside',
|
|
256
|
+
category: 'spelling',
|
|
257
|
+
pattern: /\bin\s+side\b/i,
|
|
258
|
+
suggestion: 'inside',
|
|
259
|
+
reason: '"Inside" is one word.',
|
|
260
|
+
}),
|
|
261
|
+
createRegexRule({
|
|
262
|
+
id: 'CE_CPD_overall',
|
|
263
|
+
category: 'spelling',
|
|
264
|
+
pattern: /\bover\s+all\b/i,
|
|
265
|
+
suggestion: 'overall',
|
|
266
|
+
reason: '"Overall" is one word.',
|
|
267
|
+
}),
|
|
268
|
+
createRegexRule({
|
|
269
|
+
id: 'CE_CPD_sometimes',
|
|
270
|
+
category: 'spelling',
|
|
271
|
+
pattern: /\bsome\s+times\b/i,
|
|
272
|
+
suggestion: 'sometimes',
|
|
273
|
+
reason: '"Sometimes" is one word.',
|
|
274
|
+
}),
|
|
275
|
+
createRegexRule({
|
|
276
|
+
id: 'CE_CPD_meanwhile',
|
|
277
|
+
category: 'spelling',
|
|
278
|
+
pattern: /\bmean\s+while\b/i,
|
|
279
|
+
suggestion: 'meanwhile',
|
|
280
|
+
reason: '"Meanwhile" is one word.',
|
|
281
|
+
}),
|
|
282
|
+
createRegexRule({
|
|
283
|
+
id: 'CE_CPD_however',
|
|
284
|
+
category: 'spelling',
|
|
285
|
+
pattern: /\bhow\s+ever\b/i,
|
|
286
|
+
suggestion: 'however',
|
|
287
|
+
reason: '"However" is one word.',
|
|
288
|
+
}),
|
|
289
|
+
createRegexRule({
|
|
290
|
+
id: 'CE_CPD_whatever',
|
|
291
|
+
category: 'spelling',
|
|
292
|
+
pattern: /\bwhat\s+ever\b/i,
|
|
293
|
+
suggestion: 'whatever',
|
|
294
|
+
reason: '"Whatever" is one word.',
|
|
295
|
+
}),
|
|
296
|
+
createRegexRule({
|
|
297
|
+
id: 'CE_CPD_whenever',
|
|
298
|
+
category: 'spelling',
|
|
299
|
+
pattern: /\bwhen\s+ever\b/i,
|
|
300
|
+
suggestion: 'whenever',
|
|
301
|
+
reason: '"Whenever" is one word.',
|
|
302
|
+
}),
|
|
303
|
+
createRegexRule({
|
|
304
|
+
id: 'CE_CPD_wherever',
|
|
305
|
+
category: 'spelling',
|
|
306
|
+
pattern: /\bwhere\s+ever\b/i,
|
|
307
|
+
suggestion: 'wherever',
|
|
308
|
+
reason: '"Wherever" is one word.',
|
|
309
|
+
}),
|
|
310
|
+
createRegexRule({
|
|
311
|
+
id: 'CE_CPD_whoever',
|
|
312
|
+
category: 'spelling',
|
|
313
|
+
pattern: /\bwho\s+ever\b/i,
|
|
314
|
+
suggestion: 'whoever',
|
|
315
|
+
reason: '"Whoever" is one word.',
|
|
316
|
+
}),
|
|
317
|
+
createRegexRule({
|
|
318
|
+
id: 'CE_CPD_whatever2',
|
|
319
|
+
category: 'spelling',
|
|
320
|
+
pattern: /\bwhat\s+so\s+ever\b/i,
|
|
321
|
+
suggestion: 'whatsoever',
|
|
322
|
+
reason: '"Whatsoever" is one word.',
|
|
323
|
+
}),
|
|
324
|
+
createRegexRule({
|
|
325
|
+
id: 'CE_CPD_into',
|
|
326
|
+
category: 'grammar',
|
|
327
|
+
pattern: /\b(walked|ran|jumped|fell|came|go|goes|put|get|gets|turn|turns|turned|log|logs|logged|sign|signs|signed|plug|plugs|plugged|bump|bumps|bumped|break|broke|broken)\s+in\s+to\b/i,
|
|
328
|
+
suggestion: (m) => `${m[1]} into`,
|
|
329
|
+
reason: 'Use "into" (one word) for direction or transformation after movement verbs.',
|
|
330
|
+
}),
|
|
331
|
+
createRegexRule({
|
|
332
|
+
id: 'CE_CPD_onto',
|
|
333
|
+
category: 'grammar',
|
|
334
|
+
pattern: /\b(step|steps|stepped|climb|climbs|climbed|move|moves|moved|fall|falls|fell|jump|jumps|jumped|get|gets|got|hold|holds|held)\s+on\s+to\b/i,
|
|
335
|
+
suggestion: (m) => `${m[1]} onto`,
|
|
336
|
+
reason: 'Use "onto" (one word) when expressing movement to a surface.',
|
|
337
|
+
}),
|
|
338
|
+
createRegexRule({
|
|
339
|
+
id: 'CE_CPD_everyday',
|
|
340
|
+
category: 'grammar',
|
|
341
|
+
pattern: /\bevery\s+day\s+(life|situation|task|problem|issue|use|basis|routine|object|item|thing|word|language|experience|activity|event|occurrence)\b/i,
|
|
342
|
+
suggestion: (m) => `everyday ${m[1]}`,
|
|
343
|
+
reason: 'Use "everyday" (one word) as an adjective meaning "ordinary" or "routine".',
|
|
344
|
+
}),
|
|
345
|
+
createRegexRule({
|
|
346
|
+
id: 'CE_CPD_already',
|
|
347
|
+
category: 'spelling',
|
|
348
|
+
pattern: /\ball\s+ready\b(?!\s+to)/i,
|
|
349
|
+
suggestion: 'already',
|
|
350
|
+
reason: '"Already" (meaning by now) is one word. "All ready" means completely prepared.',
|
|
351
|
+
}),
|
|
352
|
+
createRegexRule({
|
|
353
|
+
id: 'CE_CPD_altogether',
|
|
354
|
+
category: 'spelling',
|
|
355
|
+
pattern: /\ball\s+together\b/i,
|
|
356
|
+
suggestion: 'altogether',
|
|
357
|
+
reason: '"Altogether" (meaning entirely) is one word. "All together" means in a group.',
|
|
358
|
+
}),
|
|
359
|
+
|
|
360
|
+
// ════════════════════════════════════════════════════════
|
|
361
|
+
// 4. MISSING ARTICLE BEFORE AGE/COUNT DESCRIPTORS
|
|
362
|
+
// ════════════════════════════════════════════════════════
|
|
363
|
+
createRegexRule({
|
|
364
|
+
id: 'CE_ART_am_age',
|
|
365
|
+
category: 'grammar',
|
|
366
|
+
pattern: /\b(am|are|is|was|were)\s+(\d+)-year-old\b/i,
|
|
367
|
+
suggestion: (m) => `${m[1]} a ${m[2]}-year-old`,
|
|
368
|
+
reason: 'Add article "a" before an age descriptor used as a noun: "I am a 9-year-old".',
|
|
369
|
+
}),
|
|
370
|
+
|
|
371
|
+
// ════════════════════════════════════════════════════════
|
|
372
|
+
// 5. COMMON PHRASE ERRORS
|
|
373
|
+
// ════════════════════════════════════════════════════════
|
|
374
|
+
createRegexRule({
|
|
375
|
+
id: 'CE_PHR_at_the_end_of_a_day',
|
|
376
|
+
category: 'grammar',
|
|
377
|
+
pattern: /\bat\s+the\s+end\s+of\s+a\s+day\b/i,
|
|
378
|
+
suggestion: 'at the end of the day',
|
|
379
|
+
reason: 'The idiom uses "the day", not "a day".',
|
|
380
|
+
}),
|
|
381
|
+
createRegexRule({
|
|
382
|
+
id: 'CE_PHR_for_the_mean_time',
|
|
383
|
+
category: 'grammar',
|
|
384
|
+
pattern: /\bfor\s+the\s+mean\s+time\b/i,
|
|
385
|
+
suggestion: 'for the meantime',
|
|
386
|
+
reason: '"Meantime" is one word in this expression.',
|
|
387
|
+
}),
|
|
388
|
+
createRegexRule({
|
|
389
|
+
id: 'CE_PHR_in_a_nutshell',
|
|
390
|
+
category: 'style',
|
|
391
|
+
pattern: /\bI\s+believe\s+that\b/i,
|
|
392
|
+
suggestion: 'I believe',
|
|
393
|
+
reason: '"I believe that" can usually drop "that" for conciseness.',
|
|
394
|
+
}),
|
|
395
|
+
createRegexRule({
|
|
396
|
+
id: 'CE_PHR_going_to',
|
|
397
|
+
category: 'style',
|
|
398
|
+
pattern: /\bgonna\b/i,
|
|
399
|
+
suggestion: 'going to',
|
|
400
|
+
reason: '"Gonna" is informal. Use "going to" in writing.',
|
|
401
|
+
}),
|
|
402
|
+
createRegexRule({
|
|
403
|
+
id: 'CE_PHR_want_to',
|
|
404
|
+
category: 'style',
|
|
405
|
+
pattern: /\bwanna\b/i,
|
|
406
|
+
suggestion: 'want to',
|
|
407
|
+
reason: '"Wanna" is informal. Use "want to" in writing.',
|
|
408
|
+
}),
|
|
409
|
+
createRegexRule({
|
|
410
|
+
id: 'CE_PHR_got_to',
|
|
411
|
+
category: 'style',
|
|
412
|
+
pattern: /\bgotta\b/i,
|
|
413
|
+
suggestion: 'have to / got to',
|
|
414
|
+
reason: '"Gotta" is informal. Use "have to" or "got to" in writing.',
|
|
415
|
+
}),
|
|
416
|
+
createRegexRule({
|
|
417
|
+
id: 'CE_PHR_kind_of',
|
|
418
|
+
category: 'style',
|
|
419
|
+
pattern: /\bkinda\b/i,
|
|
420
|
+
suggestion: 'kind of',
|
|
421
|
+
reason: '"Kinda" is informal. Use "kind of" in writing.',
|
|
422
|
+
}),
|
|
423
|
+
createRegexRule({
|
|
424
|
+
id: 'CE_PHR_sort_of',
|
|
425
|
+
category: 'style',
|
|
426
|
+
pattern: /\bsorta\b/i,
|
|
427
|
+
suggestion: 'sort of',
|
|
428
|
+
reason: '"Sorta" is informal. Use "sort of" in writing.',
|
|
429
|
+
}),
|
|
430
|
+
createRegexRule({
|
|
431
|
+
id: 'CE_PHR_out_of',
|
|
432
|
+
category: 'style',
|
|
433
|
+
pattern: /\boutta\b/i,
|
|
434
|
+
suggestion: 'out of',
|
|
435
|
+
reason: '"Outta" is informal slang. Use "out of" in writing.',
|
|
436
|
+
}),
|
|
437
|
+
|
|
438
|
+
// ════════════════════════════════════════════════════════
|
|
439
|
+
// 6. DOUBLE NEGATIVE
|
|
440
|
+
// ════════════════════════════════════════════════════════
|
|
441
|
+
createRegexRule({
|
|
442
|
+
id: 'CE_DBL_dont_know_nothing',
|
|
443
|
+
category: 'grammar',
|
|
444
|
+
pattern: /\bdon'?t\s+know\s+nothing\b/i,
|
|
445
|
+
suggestion: "don't know anything",
|
|
446
|
+
reason: 'Avoid double negatives. Use "don\'t know anything" instead.',
|
|
447
|
+
}),
|
|
448
|
+
createRegexRule({
|
|
449
|
+
id: 'CE_DBL_never_seen_nothing',
|
|
450
|
+
category: 'grammar',
|
|
451
|
+
pattern: /\bnever\s+(seen|heard|done|read|said|told|found|met)\s+nothing\b/i,
|
|
452
|
+
suggestion: (m) => `never ${m[1]} anything`,
|
|
453
|
+
reason: 'Avoid double negatives. Use "never ... anything" instead of "never ... nothing".',
|
|
454
|
+
}),
|
|
455
|
+
createRegexRule({
|
|
456
|
+
id: 'CE_DBL_cant_do_nothing',
|
|
457
|
+
category: 'grammar',
|
|
458
|
+
pattern: /\bcan'?t\s+do\s+nothing\b/i,
|
|
459
|
+
suggestion: "can't do anything",
|
|
460
|
+
reason: 'Avoid double negatives. Use "can\'t do anything" instead.',
|
|
461
|
+
}),
|
|
462
|
+
|
|
463
|
+
// ════════════════════════════════════════════════════════
|
|
464
|
+
// 7. CAPITALIZATION AFTER GREETING PUNCTUATION
|
|
465
|
+
// "Hi, john" → "Hi, John" (name should be capitalized)
|
|
466
|
+
// ════════════════════════════════════════════════════════
|
|
467
|
+
createRegexRule({
|
|
468
|
+
id: 'CE_CAP_name_after_greeting_hi',
|
|
469
|
+
category: 'grammar',
|
|
470
|
+
pattern: /\b(Hi|Hello|Hey|Dear|Greetings),\s+([a-z][a-zA-Z]+)\b/,
|
|
471
|
+
suggestion: (m) => `${m[1]}, ${m[2].charAt(0).toUpperCase() + m[2].slice(1)}`,
|
|
472
|
+
reason: 'Capitalize the name after a greeting.',
|
|
473
|
+
}),
|
|
474
|
+
|
|
475
|
+
// ════════════════════════════════════════════════════════
|
|
476
|
+
// 8. COMMON CONFUSED CONSTRUCTIONS
|
|
477
|
+
// ════════════════════════════════════════════════════════
|
|
478
|
+
createRegexRule({
|
|
479
|
+
id: 'CE_CON_i_is',
|
|
480
|
+
category: 'grammar',
|
|
481
|
+
pattern: /\bI\s+is\b/,
|
|
482
|
+
suggestion: 'I am',
|
|
483
|
+
reason: 'The correct form is "I am", not "I is".',
|
|
484
|
+
}),
|
|
485
|
+
createRegexRule({
|
|
486
|
+
id: 'CE_CON_you_am',
|
|
487
|
+
category: 'grammar',
|
|
488
|
+
pattern: /\byou\s+am\b/i,
|
|
489
|
+
suggestion: 'you are',
|
|
490
|
+
reason: 'The correct form is "you are", not "you am".',
|
|
491
|
+
}),
|
|
492
|
+
createRegexRule({
|
|
493
|
+
id: 'CE_CON_we_is',
|
|
494
|
+
category: 'grammar',
|
|
495
|
+
pattern: /\bwe\s+is\b/i,
|
|
496
|
+
suggestion: 'we are',
|
|
497
|
+
reason: 'The correct form is "we are", not "we is".',
|
|
498
|
+
}),
|
|
499
|
+
createRegexRule({
|
|
500
|
+
id: 'CE_CON_they_is',
|
|
501
|
+
category: 'grammar',
|
|
502
|
+
pattern: /\bthey\s+is\b/i,
|
|
503
|
+
suggestion: 'they are',
|
|
504
|
+
reason: 'The correct form is "they are", not "they is".',
|
|
505
|
+
}),
|
|
506
|
+
createRegexRule({
|
|
507
|
+
id: 'CE_CON_he_are',
|
|
508
|
+
category: 'grammar',
|
|
509
|
+
pattern: /\b(he|she|it)\s+are\b/i,
|
|
510
|
+
suggestion: (m) => `${m[1]} is`,
|
|
511
|
+
reason: `The correct form is "${'"he/she/it is"'}".`,
|
|
512
|
+
}),
|
|
513
|
+
createRegexRule({
|
|
514
|
+
id: 'CE_CON_this_are',
|
|
515
|
+
category: 'grammar',
|
|
516
|
+
pattern: /\bthis\s+are\b/i,
|
|
517
|
+
suggestion: 'these are',
|
|
518
|
+
reason: 'Use "these are" with plural, or "this is" with singular.',
|
|
519
|
+
}),
|
|
520
|
+
createRegexRule({
|
|
521
|
+
id: 'CE_CON_that_are',
|
|
522
|
+
category: 'grammar',
|
|
523
|
+
pattern: /\bthat\s+are\b(?!\s+(going|coming|looking|trying|working))/i,
|
|
524
|
+
suggestion: 'those are',
|
|
525
|
+
reason: 'Use "those are" with plural distant objects.',
|
|
526
|
+
}),
|
|
527
|
+
|
|
528
|
+
// ════════════════════════════════════════════════════════
|
|
529
|
+
// 9. MISSING COMMA AFTER INTRODUCTORY PHRASES
|
|
530
|
+
// ════════════════════════════════════════════════════════
|
|
531
|
+
createRegexRule({
|
|
532
|
+
id: 'CE_COM_however_no_comma',
|
|
533
|
+
category: 'grammar',
|
|
534
|
+
pattern: /^However\s+(?!,)/,
|
|
535
|
+
suggestion: 'However, ',
|
|
536
|
+
reason: 'Use a comma after "However" when it begins a sentence.',
|
|
537
|
+
}),
|
|
538
|
+
createRegexRule({
|
|
539
|
+
id: 'CE_COM_therefore_no_comma',
|
|
540
|
+
category: 'grammar',
|
|
541
|
+
pattern: /^Therefore\s+(?!,)/,
|
|
542
|
+
suggestion: 'Therefore, ',
|
|
543
|
+
reason: 'Use a comma after "Therefore" when it begins a sentence.',
|
|
544
|
+
}),
|
|
545
|
+
createRegexRule({
|
|
546
|
+
id: 'CE_COM_furthermore_no_comma',
|
|
547
|
+
category: 'grammar',
|
|
548
|
+
pattern: /^Furthermore\s+(?!,)/,
|
|
549
|
+
suggestion: 'Furthermore, ',
|
|
550
|
+
reason: 'Use a comma after "Furthermore" when it begins a sentence.',
|
|
551
|
+
}),
|
|
552
|
+
createRegexRule({
|
|
553
|
+
id: 'CE_COM_moreover_no_comma',
|
|
554
|
+
category: 'grammar',
|
|
555
|
+
pattern: /^Moreover\s+(?!,)/,
|
|
556
|
+
suggestion: 'Moreover, ',
|
|
557
|
+
reason: 'Use a comma after "Moreover" when it begins a sentence.',
|
|
558
|
+
}),
|
|
559
|
+
createRegexRule({
|
|
560
|
+
id: 'CE_COM_meanwhile_no_comma',
|
|
561
|
+
category: 'grammar',
|
|
562
|
+
pattern: /^Meanwhile\s+(?!,)/,
|
|
563
|
+
suggestion: 'Meanwhile, ',
|
|
564
|
+
reason: 'Use a comma after "Meanwhile" when it begins a sentence.',
|
|
565
|
+
}),
|
|
566
|
+
createRegexRule({
|
|
567
|
+
id: 'CE_COM_finally_no_comma',
|
|
568
|
+
category: 'grammar',
|
|
569
|
+
pattern: /^Finally\s+(?!,)/,
|
|
570
|
+
suggestion: 'Finally, ',
|
|
571
|
+
reason: 'Use a comma after "Finally" when it begins a sentence.',
|
|
572
|
+
}),
|
|
573
|
+
createRegexRule({
|
|
574
|
+
id: 'CE_COM_additionally_no_comma',
|
|
575
|
+
category: 'grammar',
|
|
576
|
+
pattern: /^Additionally\s+(?!,)/,
|
|
577
|
+
suggestion: 'Additionally, ',
|
|
578
|
+
reason: 'Use a comma after "Additionally" when it begins a sentence.',
|
|
579
|
+
}),
|
|
580
|
+
createRegexRule({
|
|
581
|
+
id: 'CE_COM_unfortunately_no_comma',
|
|
582
|
+
category: 'grammar',
|
|
583
|
+
pattern: /^Unfortunately\s+(?!,)/,
|
|
584
|
+
suggestion: 'Unfortunately, ',
|
|
585
|
+
reason: 'Use a comma after "Unfortunately" when it begins a sentence.',
|
|
586
|
+
}),
|
|
587
|
+
createRegexRule({
|
|
588
|
+
id: 'CE_COM_fortunately_no_comma',
|
|
589
|
+
category: 'grammar',
|
|
590
|
+
pattern: /^Fortunately\s+(?!,)/,
|
|
591
|
+
suggestion: 'Fortunately, ',
|
|
592
|
+
reason: 'Use a comma after "Fortunately" when it begins a sentence.',
|
|
593
|
+
}),
|
|
594
|
+
createRegexRule({
|
|
595
|
+
id: 'CE_COM_yes_no_comma',
|
|
596
|
+
category: 'grammar',
|
|
597
|
+
pattern: /^(Yes|No|Well|Sure|Indeed|Absolutely|Certainly|Exactly|Honestly|Clearly|Obviously|Frankly)\s+(?!,)/,
|
|
598
|
+
suggestion: (m) => `${m[1]}, `,
|
|
599
|
+
reason: `Use a comma after "${'"Yes/No/Well"'}" when it begins a sentence.`,
|
|
600
|
+
}),
|
|
601
|
+
];
|