@chaffjs/lang-en 0.3.0 → 0.6.0

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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The document named right after a reference, or undefined when the reference is into this document.
3
+ * "of this Agreement" and "of the Agreement" are this document; "of the Master Agreement" is another.
4
+ */
5
+ export declare const citedDocumentAfter: (text: string, end: number) => string | undefined;
6
+ //# sourceMappingURL=citation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation.d.ts","sourceRoot":"","sources":["../src/citation.ts"],"names":[],"mappings":"AA4BA;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,KAAG,MAAM,GAAG,SAQvE,CAAC"}
@@ -0,0 +1,43 @@
1
+ // A reference into another document: "Section 9 of the Master Agreement" is not this document's Section 9.
2
+ const OF = /^,? of (?:the )?/u;
3
+ const CAPITALISED = /^[A-Z][\w'’-]*/u;
4
+ /**
5
+ * How a document names itself. "Section 3 of the Agreement" in an agreement means this one.
6
+ * "the Act" and "the Code" are left out: a statute calls itself "this Act", and a contract's "the Act" is a law it cites.
7
+ */
8
+ const SELF = new Set(["Agreement", "Contract", "Terms", "Policy"]);
9
+ /** Words that may sit inside a title: "Code of Federal Regulations". */
10
+ const JOINERS = new Set(["of", "and", "for", "on"]);
11
+ const MAX_WORDS = 8;
12
+ /** Capitalised words from the start of `rest`, allowing a joiner between two of them. Punctuation ends the title. */
13
+ const titleWords = (rest) => {
14
+ const tokens = rest.split(" ");
15
+ const words = [];
16
+ for (let index = 0; index < tokens.length && words.length < MAX_WORDS; index += 1) {
17
+ const token = tokens[index] ?? "";
18
+ const word = CAPITALISED.exec(token)?.[0];
19
+ const joins = word === undefined && words.length > 0 && JOINERS.has(token) && CAPITALISED.test(tokens[index + 1] ?? "");
20
+ if (word === undefined && !joins)
21
+ break;
22
+ words.push(word ?? token);
23
+ if (word !== undefined && word.length < token.length)
24
+ break;
25
+ }
26
+ return words;
27
+ };
28
+ /**
29
+ * The document named right after a reference, or undefined when the reference is into this document.
30
+ * "of this Agreement" and "of the Agreement" are this document; "of the Master Agreement" is another.
31
+ */
32
+ export const citedDocumentAfter = (text, end) => {
33
+ const rest = text.slice(end, end + 120);
34
+ const of = OF.exec(rest);
35
+ if (of === null)
36
+ return undefined;
37
+ const words = titleWords(rest.slice(of[0].length));
38
+ if (words.length === 0)
39
+ return undefined;
40
+ const name = words.join(" ");
41
+ return words.length === 1 && SELF.has(name) ? undefined : name;
42
+ };
43
+ //# sourceMappingURL=citation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation.js","sourceRoot":"","sources":["../src/citation.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAE3G,MAAM,EAAE,GAAG,mBAAmB,CAAC;AAC/B,MAAM,WAAW,GAAG,iBAAiB,CAAC;AACtC;;;GAGG;AACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACnE,wEAAwE;AACxE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AACpD,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,qHAAqH;AACrH,MAAM,UAAU,GAAG,CAAC,IAAY,EAAY,EAAE;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxH,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK;YAAE,MAAM;QACxC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC;QAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;YAAE,MAAM;IAC9D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAE,GAAW,EAAsB,EAAE;IAClF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,eAAe,EAA0B,MAAM,gBAAgB,CAAC;AAoB5F;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,eA6BrB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAgB,eAAe,EAA0B,MAAM,gBAAgB,CAAC;AAoB5F;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,eA8BrB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { split, SentenceSplitterSyntax } from "sentence-splitter";
2
2
  import { loadLexicons } from "./lexicons.js";
3
+ import { structure } from "./structure.js";
3
4
  import { isReady, prepare, tokenize } from "./pos.js";
4
5
  // chaff からは型だけを取る。実行時の値依存を作らない。アダプタは単体で動く。
5
6
  const LATIN_LETTER = /[a-z]/giu;
@@ -46,6 +47,7 @@ export const adapter = {
46
47
  return [...source.matchAll(LATIN_LETTER)].length / total;
47
48
  },
48
49
  lexicons: loadLexicons(),
50
+ structure,
49
51
  segment: (text) => {
50
52
  const sentences = split(text)
51
53
  .filter((node) => node.type === SentenceSplitterSyntax.Sentence)
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGtD,2CAA2C;AAE3C,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,QAAkB,EAAY,EAAE;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC1C,OAAO;QACL,GAAG,QAAQ;QACX,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;KAClJ,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAoB;IACtC,IAAI,EAAE,UAAU;IAChB,EAAE,EAAE,IAAI;IACR,UAAU,EAAE,CAAC;IACb,YAAY,EAAE;QACZ,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,IAAI;QACf,6CAA6C;QAC7C,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;KACnB;IACD,OAAO,EAAE,CAAC,IAAkB,EAAiB,EAAE;QAC7C,0CAA0C;QAC1C,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,EAAE,CAAC,MAAc,EAAU,EAAE;QACjC,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;IAC3D,CAAC;IACD,QAAQ,EAAE,YAAY,EAAE;IACxB,OAAO,EAAE,CAAC,IAAY,EAAgB,EAAE;QACtC,MAAM,SAAS,GAAe,KAAK,CAAC,IAAI,CAAC;aACtC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,sBAAsB,CAAC,QAAQ,CAAC;aAC/D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3H,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1E,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGtD,2CAA2C;AAE3C,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,QAAkB,EAAY,EAAE;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC1C,OAAO;QACL,GAAG,QAAQ;QACX,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;KAClJ,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAoB;IACtC,IAAI,EAAE,UAAU;IAChB,EAAE,EAAE,IAAI;IACR,UAAU,EAAE,CAAC;IACb,YAAY,EAAE;QACZ,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,IAAI;QACf,6CAA6C;QAC7C,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;KACnB;IACD,OAAO,EAAE,CAAC,IAAkB,EAAiB,EAAE;QAC7C,0CAA0C;QAC1C,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,EAAE,CAAC,MAAc,EAAU,EAAE;QACjC,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;IAC3D,CAAC;IACD,QAAQ,EAAE,YAAY,EAAE;IACxB,SAAS;IACT,OAAO,EAAE,CAAC,IAAY,EAAgB,EAAE;QACtC,MAAM,SAAS,GAAe,KAAK,CAAC,IAAI,CAAC;aACtC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,sBAAsB,CAAC,QAAQ,CAAC;aAC/D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3H,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1E,CAAC;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"lexicons.d.ts","sourceRoot":"","sources":["../src/lexicons.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAY5D;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,MAAK,MAAY,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAQ7D,CAAC"}
1
+ {"version":3,"file":"lexicons.d.ts","sourceRoot":"","sources":["../src/lexicons.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAiB5D;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,MAAK,MAAY,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAQ7D,CAAC"}
package/dist/lexicons.js CHANGED
@@ -8,7 +8,12 @@ const toEntry = (raw) => {
8
8
  if (!isRecord(raw) || typeof raw["pattern"] !== "string")
9
9
  return undefined;
10
10
  const weight = raw["weight"];
11
- return { pattern: raw["pattern"], weight: typeof weight === "number" ? weight : undefined };
11
+ const instead = raw["instead_of"];
12
+ return {
13
+ pattern: raw["pattern"],
14
+ weight: typeof weight === "number" ? weight : undefined,
15
+ instead_of: typeof instead === "string" ? instead : undefined,
16
+ };
12
17
  };
13
18
  /**
14
19
  * 語彙表はアダプタが持つ。detector は共通で、これだけが言語別。spec §11。
@@ -1 +1 @@
1
- {"version":3,"file":"lexicons.js","sourceRoot":"","sources":["../src/lexicons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAG7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AAE5E,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAE5I,MAAM,OAAO,GAAG,CAAC,GAAY,EAA4B,EAAE;IACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC9F,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAc,GAAG,EAA2B,EAAE,CACzE,WAAW,CAAC,GAAG,CAAC;KACb,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;KACxC,MAAM,CAA0B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IAC7C,MAAM,GAAG,GAAY,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAClG,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IACnF,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC,EAAE,EAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"lexicons.js","sourceRoot":"","sources":["../src/lexicons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAG7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AAE5E,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAE5I,MAAM,OAAO,GAAG,CAAC,GAAY,EAA4B,EAAE;IACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IAClC,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC;QACvB,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACvD,UAAU,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KAC9D,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAc,GAAG,EAA2B,EAAE,CACzE,WAAW,CAAC,GAAG,CAAC;KACb,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;KACxC,MAAM,CAA0B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IAC7C,MAAM,GAAG,GAAY,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAClG,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IACnF,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC,EAAE,EAAE,CAAC,CAAC"}
package/dist/pos.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pos.d.ts","sourceRoot":"","sources":["../src/pos.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AA4E5C,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,KAAG,MAAiE,CAAC;AAqBrG,eAAO,MAAM,OAAO,QAAO,IAE1B,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,OAAoC,CAAC;AAwBhE,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,KAAK,EAAE,GAAG,SAIjD,CAAC"}
1
+ {"version":3,"file":"pos.d.ts","sourceRoot":"","sources":["../src/pos.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AA4E5C,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,KAAG,MAAiE,CAAC;AA4BrG,eAAO,MAAM,OAAO,QAAO,IAE1B,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,OAAoC,CAAC;AAwBhE,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,KAAK,EAAE,GAAG,SAIjD,CAAC"}
package/dist/pos.js CHANGED
@@ -67,14 +67,21 @@ export const upos = (tag) => (PUNCTUATION.has(tag) ? "PUNCT" : (BY_TAG[tag] ?? "
67
67
  * 受動は be + 過去分詞。過去分詞だけでは完了形(has reviewed)と見分けられないので、
68
68
  * 直前の be を見る。間に副詞が挟まる(was quickly approved)ぶんだけ遡る。
69
69
  */
70
- const ADVERB_TAGS = new Set(["RB", "RBR", "RBS"]);
70
+ /**
71
+ * be と過去分詞の間に立てるもの。副詞と、副詞をつなぐ接続詞(was fully and carefully reviewed)。
72
+ *
73
+ * これでも拾えない形がある。`was fully and finally approved` では wink が `approved` を
74
+ * VBN ではなく **VBD** と付けるため、そもそも過去分詞として見えない。解析器の限界で、
75
+ * VBD も受動と見なすと `The team was here and approved it` まで受動になる。直さない。
76
+ */
77
+ const SKIPPABLE = new Set(["RB", "RBR", "RBS", "CC", ","]);
71
78
  const BE = new Set(["be", "am", "is", "are", "was", "were", "been", "being"]);
72
79
  const isBe = (entry) => BE.has(entry.lemma ?? entry.value.toLowerCase()) || BE.has(entry.value.toLowerCase());
73
80
  const isPassive = (tagged, at) => {
74
81
  if (tagged[at]?.pos !== "VBN")
75
82
  return false;
76
83
  const before = tagged.slice(0, at).reverse();
77
- const head = before.find((entry) => !ADVERB_TAGS.has(entry.pos));
84
+ const head = before.find((entry) => !SKIPPABLE.has(entry.pos));
78
85
  return head !== undefined && isBe(head);
79
86
  };
80
87
  const state = { ready: undefined };
package/dist/pos.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pos.js","sourceRoot":"","sources":["../src/pos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pos.js","sourceRoot":"","sources":["../src/pos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { StructurePatterns } from "chaffjs/plugin";
2
+ /** "IV" → 4, "xii" → 12. Undefined for anything that is not a roman numeral. */
3
+ export declare const parseRoman: (text: string) => number | undefined;
4
+ export declare const structure: StructurePatterns;
5
+ //# sourceMappingURL=structure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../src/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2C,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAQjG,gFAAgF;AAChF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,SAKlD,CAAC;AA+VF,eAAO,MAAM,SAAS,EAAE,iBAAuG,CAAC"}
@@ -0,0 +1,329 @@
1
+ import { citedDocumentAfter } from "./citation.js";
2
+ // Contracts, specifications and statutes in English. core nests what this reads; it does not know
3
+ // how English numbers its articles.
4
+ const ROMAN = { i: 1, v: 5, x: 10, l: 50, c: 100 };
5
+ /** "IV" → 4, "xii" → 12. Undefined for anything that is not a roman numeral. */
6
+ export const parseRoman = (text) => {
7
+ const values = [...text.toLowerCase()].map((char) => ROMAN[char]);
8
+ if (values.length === 0 || values.some((value) => value === undefined))
9
+ return undefined;
10
+ const known = values.filter((value) => value !== undefined);
11
+ return known.reduce((total, value, index) => ((known[index + 1] ?? 0) > value ? total - value : total + value), 0);
12
+ };
13
+ const numberOf = (text) => {
14
+ if (text === undefined)
15
+ return undefined;
16
+ if (/^\d{1,3}(?:\.\d{1,3}){0,5}$/u.test(text))
17
+ return text;
18
+ const roman = parseRoman(text);
19
+ return roman === undefined ? undefined : String(roman);
20
+ };
21
+ /**
22
+ * A heading is the number followed by nothing, punctuation, or a capitalised title.
23
+ * "Article 3 shall apply to…" at the start of a line is a sentence, not the heading of Article 3.
24
+ */
25
+ const titleOf = (rest) => {
26
+ const trimmed = rest.replace(/^[.:\-–—\s]+/u, "").trim();
27
+ if (trimmed !== "" && /^\p{Ll}/u.test(trimmed))
28
+ return undefined;
29
+ return trimmed;
30
+ };
31
+ const ARTICLE = /^\s{0,3}(?:ARTICLE|Article)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
32
+ const SECTION = /^\s{0,3}(?:SECTION|Section|§)\s*(?<n>\d{1,3}(?:\.\d{1,3}){0,5})\b(?<rest>.*)$/u;
33
+ const LETTERED = /^\s{0,6}\((?<n>[a-z]{1,4}|\d{1,3})\)\s+(?<rest>\S.*)$/u;
34
+ const MULTI_ROMAN = /^(?:ii|iii|iv|vi|vii|viii|ix)$/u;
35
+ const headed = (pattern, line, label) => {
36
+ const groups = pattern.exec(line)?.groups;
37
+ const number = numberOf(groups?.["n"]);
38
+ const heading = titleOf(groups?.["rest"] ?? "");
39
+ if (groups === undefined || number === undefined || heading === undefined)
40
+ return undefined;
41
+ const parts = number.split(".");
42
+ return {
43
+ kind: "article",
44
+ depth: parts.length,
45
+ number,
46
+ absolute: true,
47
+ label: label(groups["n"] ?? ""),
48
+ heading,
49
+ rest: heading,
50
+ ordinal: Number(parts.at(-1)),
51
+ };
52
+ };
53
+ /**
54
+ * 開いている項目の書き方。"(ii)" はローマ数字、"(b)" は英字。一文字の "(i)" はどちらにも読めるので、
55
+ * 開いたときの読みを覚えておく代わりに、一つ上に英字が開いていたかで決め直す。
56
+ */
57
+ const styleOfLabel = (open, index, all) => {
58
+ const inner = /^\((?<n>[a-z0-9]{1,4})\)$/u.exec(open.label)?.groups?.["n"];
59
+ if (inner === undefined)
60
+ return undefined;
61
+ if (/^\d+$/u.test(inner))
62
+ return "digit";
63
+ if (MULTI_ROMAN.test(inner))
64
+ return "roman";
65
+ const above = all[index - 1];
66
+ return /^[ivx]$/u.test(inner) && above !== undefined && styleOfLabel(above, index - 1, all) === "letter" && above.depth < open.depth ? "roman" : "letter";
67
+ };
68
+ const styles = (context) => context.open.map((open, index, all) => styleOfLabel(open, index, all));
69
+ /** "(h)" の次の "(i)" は英字。開いている英字の次の文字なら、ローマ数字とは読まない。 */
70
+ const followsLetter = (raw, context) => context.open.some((open, index) => styles(context)[index] === "letter" && open.number.charCodeAt(0) + 1 === raw.charCodeAt(0));
71
+ /** "(i)" is a roman numeral right under "(a)", or when a roman list is already open; the letter i otherwise. */
72
+ const styleOf = (raw, context) => {
73
+ if (/^\d+$/u.test(raw))
74
+ return "digit";
75
+ if (MULTI_ROMAN.test(raw))
76
+ return "roman";
77
+ if (!/^[ivx]$/u.test(raw) || followsLetter(raw, context))
78
+ return "letter";
79
+ const open = styles(context);
80
+ return open.includes("roman") || open.at(-1) === "letter" ? "roman" : "letter";
81
+ };
82
+ /**
83
+ * A sibling has the depth of the open item written the same way: "(b)" closes "(i)" and sits beside "(a)".
84
+ * A new way of numbering goes one deeper than whatever is open.
85
+ */
86
+ const depthFor = (style, context) => {
87
+ const open = styles(context);
88
+ const sibling = [...context.open].reverse().find((_item, reversed) => open[context.open.length - 1 - reversed] === style);
89
+ return sibling?.depth ?? (context.open.at(-1)?.depth ?? 0) + 1;
90
+ };
91
+ const LETTER_BEFORE_A = "a".charCodeAt(0) - 1;
92
+ /** "(b)" は 2 番目、"(ii)" も 2 番目。二文字以上の英字("(aa)")は並びが決まらないので付けない。 */
93
+ const ordinalOf = (raw, style) => {
94
+ if (style === "digit")
95
+ return Number(raw);
96
+ if (style === "roman")
97
+ return parseRoman(raw);
98
+ return raw.length === 1 ? raw.charCodeAt(0) - LETTER_BEFORE_A : undefined;
99
+ };
100
+ const lettered = (line, context) => {
101
+ const groups = LETTERED.exec(line)?.groups;
102
+ const raw = groups?.["n"];
103
+ if (groups === undefined || raw === undefined)
104
+ return undefined;
105
+ const style = styleOf(raw, context);
106
+ // 番地は書かれたままの "ii" を使う。参照「Section 4.2(a)(ii)」も同じ形で書かれるので、そのまま引ける。
107
+ return {
108
+ kind: "item",
109
+ depth: depthFor(style, context),
110
+ ordinal: ordinalOf(raw, style),
111
+ number: raw,
112
+ absolute: false,
113
+ label: `(${raw})`,
114
+ heading: "",
115
+ rest: groups["rest"]?.trim() ?? "",
116
+ };
117
+ };
118
+ /** Chapters restart in each part, so a chapter continues its part's address: PART II, CHAPTER 1 → pt2.ch1. */
119
+ const PART = /^\s{0,3}(?:PART|Part)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
120
+ const CHAPTER = /^\s{0,3}(?:CHAPTER|Chapter)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
121
+ const chapter = (pattern, line, depth, prefix, word) => {
122
+ const groups = pattern.exec(line)?.groups;
123
+ const number = numberOf(groups?.["n"]);
124
+ const heading = titleOf(groups?.["rest"] ?? "");
125
+ if (groups === undefined || number === undefined || heading === undefined)
126
+ return undefined;
127
+ const label = `${word} ${groups["n"] ?? ""}`;
128
+ return { kind: "chapter", depth, number: prefix + number, absolute: false, label, heading, rest: heading, ordinal: Number(number) };
129
+ };
130
+ const numbered = (line, context) => chapter(PART, line, -2, "pt", "Part") ??
131
+ chapter(CHAPTER, line, -1, "ch", "Chapter") ??
132
+ headed(ARTICLE, line, (n) => `Article ${n}`) ??
133
+ headed(SECTION, line, (n) => `Section ${n}`) ??
134
+ lettered(line, context);
135
+ const mentions = (pattern, text, attrs) => [...text.matchAll(pattern)].flatMap((match) => {
136
+ const found = attrs(match.groups ?? {}, match[0]);
137
+ return found === undefined ? [] : [{ start: match.index, end: match.index + match[0].length, attrs: found }];
138
+ });
139
+ const DEFINITIONS = [
140
+ /["“](?<term>[^"”\n]{1,60})["”] (?:means|shall mean|refers to|has the meaning)\b/gu,
141
+ /\((?:the |hereinafter )?["“](?<term>[^"”\n]{1,60})["”]\)/gu,
142
+ /\(hereinafter referred to as ["“](?<term>[^"”\n]{1,60})["”]\)/gu,
143
+ ];
144
+ const definitions = (text) => DEFINITIONS.flatMap((pattern) => mentions(pattern, text, (groups) => (groups["term"] === undefined ? undefined : { term: groups["term"] })));
145
+ const REFERENCE = /\b(?:Sections?|Articles?|§) ?(?<n>\d{1,3}(?:\.\d{1,3}){0,5}|[IVXLC]{1,7})\b/gu;
146
+ const SUBDIVISION = /^\((?<p>[a-z0-9]{1,4})\)/u;
147
+ /** "(a)(ii)(3)" is as deep as a reference goes; more parentheses are text, not a deeper address. */
148
+ const MAX_SUBDIVISIONS = 4;
149
+ /**
150
+ * "(a)(ii)" のような続きの括弧を、正規表現を複雑にせずに一つずつ読む。
151
+ * 再帰にしないのは、括弧が延々と続く行でスタックを使い切らないため。
152
+ */
153
+ const subdivisions = (text, from) => {
154
+ const parts = [];
155
+ let end = from;
156
+ while (parts.length < MAX_SUBDIVISIONS) {
157
+ const part = SUBDIVISION.exec(text.slice(end, end + 6))?.groups?.["p"];
158
+ if (part === undefined)
159
+ break;
160
+ parts.push(part);
161
+ end += part.length + 2;
162
+ }
163
+ return { parts, end };
164
+ };
165
+ /**
166
+ * "Section 4.2(a)" → 4.2.a, "Article III" → 3. The same addresses the tree gives.
167
+ * "Section 9 of the Master Agreement" carries the other document's name, and is not looked up in this tree.
168
+ */
169
+ const references = (text) => [...text.matchAll(REFERENCE)].flatMap((match) => {
170
+ const main = numberOf(match.groups?.["n"]);
171
+ if (main === undefined)
172
+ return [];
173
+ const { parts, end } = subdivisions(text, match.index + match[0].length);
174
+ const document = citedDocumentAfter(text, end);
175
+ const attrs = { target: [main, ...parts].join("."), label: text.slice(match.index, end), ...(document === undefined ? {} : { document }) };
176
+ return [{ start: match.index, end, attrs }];
177
+ });
178
+ /** Longest first, and never inside a word: "shall not" is not also "shall", "mayor" is not "may". */
179
+ const MARKERS = [
180
+ ["is required to", "must"],
181
+ ["shall not", "must-not"],
182
+ ["must not", "must-not"],
183
+ ["may not", "must-not"],
184
+ ["agrees to", "must"],
185
+ ["shall", "must"],
186
+ ["must", "must"],
187
+ ["may", "may"],
188
+ ];
189
+ const isWordChar = (char) => char !== undefined && /[\p{L}\p{N}_]/u.test(char);
190
+ /** Every whole-word occurrence. A loop, not recursion: a line with thousands of "may" must not exhaust the stack. */
191
+ const wordAt = (lower, word) => {
192
+ const found = [];
193
+ for (let at = lower.indexOf(word); at !== -1; at = lower.indexOf(word, at + 1)) {
194
+ if (!isWordChar(lower[at - 1]) && !isWordChar(lower[at + word.length]))
195
+ found.push(at);
196
+ }
197
+ return found;
198
+ };
199
+ const SENTENCE_OPENERS = new Set([".", "!", "?", ":", ";", '"', "“", "("]);
200
+ /** "May" in the middle of a sentence is the month: "published in May 2023". At the start it can be the modal. */
201
+ const isMonthName = (text, at) => {
202
+ if (!text.startsWith("May", at))
203
+ return false;
204
+ const before = text.slice(Math.max(0, at - 4), at).trimEnd();
205
+ return before !== "" && !SENTENCE_OPENERS.has(before.at(-1) ?? "");
206
+ };
207
+ /**
208
+ * Longest marker first; a hit that overlaps one already kept is dropped. Overlap is checked with a mark
209
+ * per character, not by comparing every pair, which would go quadratic on a line with thousands of markers.
210
+ */
211
+ const obligations = (text) => {
212
+ const lower = text.toLowerCase();
213
+ const taken = new Uint8Array(text.length);
214
+ const kept = [];
215
+ MARKERS.forEach(([marker, type]) => {
216
+ wordAt(lower, marker).forEach((start) => {
217
+ const end = start + marker.length;
218
+ if (isMonthName(text, start))
219
+ return;
220
+ if (taken.subarray(start, end).some((mark) => mark === 1))
221
+ return;
222
+ taken.fill(1, start, end);
223
+ kept.push({ start, end, attrs: { marker, type } });
224
+ });
225
+ });
226
+ return kept.sort((left, right) => left.start - right.start);
227
+ };
228
+ /** Find the number first, then look at what is right before (a currency) and right after (a unit). */
229
+ const NUMBER_RUN = /\d[\d,.]{0,15}/gu;
230
+ const UNITS = [
231
+ "business days",
232
+ "business day",
233
+ "calendar days",
234
+ "calendar day",
235
+ "days",
236
+ "day",
237
+ "weeks",
238
+ "week",
239
+ "months",
240
+ "month",
241
+ "years",
242
+ "year",
243
+ "hours",
244
+ "hour",
245
+ "minutes",
246
+ "minute",
247
+ "percent",
248
+ "times",
249
+ "%",
250
+ ];
251
+ const CURRENCIES = ["USD", "EUR", "$", "€", "£"];
252
+ /** "30." の終わりの点は文の終わり。数の一部にしない。 */
253
+ const withoutTrailingPunctuation = (run) => {
254
+ let end = run.length;
255
+ while (end > 0 && (run[end - 1] === "." || run[end - 1] === ","))
256
+ end -= 1;
257
+ return run.slice(0, end);
258
+ };
259
+ /** One space or tab may sit between a number and its unit or currency. */
260
+ const isGap = (char) => char === " " || char === "\t";
261
+ const unitAfter = (text, from) => {
262
+ const gap = isGap(text[from]) ? 1 : 0;
263
+ const unit = UNITS.find((candidate) => text.startsWith(candidate, from + gap) && !isWordChar(text[from + gap + candidate.length]));
264
+ return unit;
265
+ };
266
+ /** The currency just before the number, allowing one space. Looks back a few characters, never at the whole line. */
267
+ const currencyBefore = (text, at) => {
268
+ const end = isGap(text[at - 1]) ? at - 1 : at;
269
+ return CURRENCIES.find((currency) => text.startsWith(currency, end - currency.length));
270
+ };
271
+ const quantities = (text) => [...text.matchAll(NUMBER_RUN)].flatMap((match) => {
272
+ const digits = withoutTrailingPunctuation(match[0]);
273
+ const value = Number(digits.replace(/,/gu, ""));
274
+ const end = match.index + digits.length;
275
+ const unit = unitAfter(text, end) ?? currencyBefore(text, match.index);
276
+ return unit === undefined || Number.isNaN(value) || isWordChar(text[match.index - 1]) ? [] : [{ start: match.index, end, attrs: { value, unit } }];
277
+ });
278
+ const MONTHS = ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
279
+ const MONTH_WORD = /\b(?<month>[A-Z][a-z]{2,8})\b/gu;
280
+ const ISO_DATE = /\b(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})\b/gu;
281
+ const DAY_BEFORE = /(?<d>\d{1,2})(?:st|nd|rd|th)? $/u;
282
+ const DAY_YEAR_AFTER = /^ (?<d>\d{1,2})(?:st|nd|rd|th)?,? (?<y>\d{4})\b/u;
283
+ const YEAR_AFTER = /^,? (?<y>\d{4})\b/u;
284
+ const pad = (value) => value.padStart(2, "0");
285
+ /** "1 April 2024": the day written before the month. */
286
+ const dayBefore = (text, at) => {
287
+ const found = DAY_BEFORE.exec(text.slice(Math.max(0, at - 6), at));
288
+ const day = found?.groups?.["d"];
289
+ return found === null || day === undefined ? undefined : { day, start: at - found[0].length };
290
+ };
291
+ /** "April 1, 2024" → 2024-04-01. */
292
+ const monthDayYear = (text, at, end, month) => {
293
+ const found = DAY_YEAR_AFTER.exec(text.slice(end, end + 16));
294
+ if (found?.groups === undefined)
295
+ return undefined;
296
+ const value = `${found.groups["y"] ?? ""}-${pad(String(month))}-${pad(found.groups["d"] ?? "")}`;
297
+ return { start: at, end: end + found[0].length, attrs: { value } };
298
+ };
299
+ /** "1 April 2024" → 2024-04-01, "April 2024" → 2024-04. */
300
+ const monthYear = (text, at, end, month) => {
301
+ const found = YEAR_AFTER.exec(text.slice(end, end + 8));
302
+ const year = found?.groups?.["y"];
303
+ if (found === null || year === undefined)
304
+ return undefined;
305
+ const before = dayBefore(text, at);
306
+ const value = [year, pad(String(month)), ...(before === undefined ? [] : [pad(before.day)])].join("-");
307
+ return { start: before?.start ?? at, end: end + found[0].length, attrs: { value } };
308
+ };
309
+ /**
310
+ * A month name alone is not a date: "May" is also the modal verb, so it counts only with a year beside it.
311
+ * The month is found first and its neighbours read with anchored patterns, never one long alternation.
312
+ */
313
+ const namedDate = (text, match) => {
314
+ const month = MONTHS.indexOf((match.groups?.["month"] ?? "").toLowerCase()) + 1;
315
+ if (month === 0)
316
+ return undefined;
317
+ const end = match.index + match[0].length;
318
+ return monthDayYear(text, match.index, end, month) ?? monthYear(text, match.index, end, month);
319
+ };
320
+ const isoDate = (match) => ({
321
+ start: match.index,
322
+ end: match.index + match[0].length,
323
+ attrs: { value: `${match.groups?.["y"] ?? ""}-${match.groups?.["m"] ?? ""}-${match.groups?.["d"] ?? ""}` },
324
+ });
325
+ const dates = (text) => [...[...text.matchAll(MONTH_WORD)].flatMap((match) => namedDate(text, match) ?? []), ...[...text.matchAll(ISO_DATE)].map(isoDate)].sort((left, right) => left.start - right.start);
326
+ /** "2.5 days" and "1.5 times" are amounts, not section 2.5 titled "days". */
327
+ const countedAfter = (_number, rest) => unitAfter(` ${rest}`, 0) !== undefined;
328
+ export const structure = { numbered, definitions, references, obligations, quantities, dates, countedAfter };
329
+ //# sourceMappingURL=structure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure.js","sourceRoot":"","sources":["../src/structure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ # Contractions and the long forms that say the same thing.
2
+ #
3
+ # Neither is wrong. The rule only asks whether one document picks one and keeps it.
4
+ id: contraction
5
+ language: en
6
+ entries:
7
+ - pattern: "don't"
8
+ instead_of: do not
9
+ - pattern: "doesn't"
10
+ instead_of: does not
11
+ - pattern: "didn't"
12
+ instead_of: did not
13
+ - pattern: "can't"
14
+ instead_of: cannot
15
+ - pattern: "won't"
16
+ instead_of: will not
17
+ - pattern: "isn't"
18
+ instead_of: is not
19
+ - pattern: "aren't"
20
+ instead_of: are not
21
+ - pattern: "wasn't"
22
+ instead_of: was not
23
+ - pattern: "it's"
24
+ instead_of: it is
25
+ - pattern: "we're"
26
+ instead_of: we are
27
+ - pattern: "they're"
28
+ instead_of: they are
29
+ - pattern: "you're"
30
+ instead_of: you are
31
+ - pattern: "we'll"
32
+ instead_of: we will
33
+ - pattern: "we've"
34
+ instead_of: we have
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaffjs/lang-en",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "description": "English language adapter for chaff",
5
5
  "license": "MIT",
6
6
  "author": "isamu",
@@ -0,0 +1,41 @@
1
+ // A reference into another document: "Section 9 of the Master Agreement" is not this document's Section 9.
2
+
3
+ const OF = /^,? of (?:the )?/u;
4
+ const CAPITALISED = /^[A-Z][\w'’-]*/u;
5
+ /**
6
+ * How a document names itself. "Section 3 of the Agreement" in an agreement means this one.
7
+ * "the Act" and "the Code" are left out: a statute calls itself "this Act", and a contract's "the Act" is a law it cites.
8
+ */
9
+ const SELF = new Set(["Agreement", "Contract", "Terms", "Policy"]);
10
+ /** Words that may sit inside a title: "Code of Federal Regulations". */
11
+ const JOINERS = new Set(["of", "and", "for", "on"]);
12
+ const MAX_WORDS = 8;
13
+
14
+ /** Capitalised words from the start of `rest`, allowing a joiner between two of them. Punctuation ends the title. */
15
+ const titleWords = (rest: string): string[] => {
16
+ const tokens = rest.split(" ");
17
+ const words: string[] = [];
18
+ for (let index = 0; index < tokens.length && words.length < MAX_WORDS; index += 1) {
19
+ const token = tokens[index] ?? "";
20
+ const word = CAPITALISED.exec(token)?.[0];
21
+ const joins = word === undefined && words.length > 0 && JOINERS.has(token) && CAPITALISED.test(tokens[index + 1] ?? "");
22
+ if (word === undefined && !joins) break;
23
+ words.push(word ?? token);
24
+ if (word !== undefined && word.length < token.length) break;
25
+ }
26
+ return words;
27
+ };
28
+
29
+ /**
30
+ * The document named right after a reference, or undefined when the reference is into this document.
31
+ * "of this Agreement" and "of the Agreement" are this document; "of the Master Agreement" is another.
32
+ */
33
+ export const citedDocumentAfter = (text: string, end: number): string | undefined => {
34
+ const rest = text.slice(end, end + 120);
35
+ const of = OF.exec(rest);
36
+ if (of === null) return undefined;
37
+ const words = titleWords(rest.slice(of[0].length));
38
+ if (words.length === 0) return undefined;
39
+ const name = words.join(" ");
40
+ return words.length === 1 && SELF.has(name) ? undefined : name;
41
+ };
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { split, SentenceSplitterSyntax } from "sentence-splitter";
2
2
  import { loadLexicons } from "./lexicons.ts";
3
+ import { structure } from "./structure.ts";
3
4
  import { isReady, prepare, tokenize } from "./pos.ts";
4
5
  import type { AdapterNeeds, LanguageAdapter, Segmentation, Sentence } from "chaffjs/plugin";
5
6
 
@@ -48,6 +49,7 @@ export const adapter: LanguageAdapter = {
48
49
  return [...source.matchAll(LATIN_LETTER)].length / total;
49
50
  },
50
51
  lexicons: loadLexicons(),
52
+ structure,
51
53
  segment: (text: string): Segmentation => {
52
54
  const sentences: Sentence[] = split(text)
53
55
  .filter((node) => node.type === SentenceSplitterSyntax.Sentence)
package/src/lexicons.ts CHANGED
@@ -11,7 +11,12 @@ const isRecord = (value: unknown): value is Record<string, unknown> => typeof va
11
11
  const toEntry = (raw: unknown): LexiconEntry | undefined => {
12
12
  if (!isRecord(raw) || typeof raw["pattern"] !== "string") return undefined;
13
13
  const weight = raw["weight"];
14
- return { pattern: raw["pattern"], weight: typeof weight === "number" ? weight : undefined };
14
+ const instead = raw["instead_of"];
15
+ return {
16
+ pattern: raw["pattern"],
17
+ weight: typeof weight === "number" ? weight : undefined,
18
+ instead_of: typeof instead === "string" ? instead : undefined,
19
+ };
15
20
  };
16
21
 
17
22
  /**
package/src/pos.ts CHANGED
@@ -81,7 +81,14 @@ export const upos = (tag: string): string => (PUNCTUATION.has(tag) ? "PUNCT" : (
81
81
  * 受動は be + 過去分詞。過去分詞だけでは完了形(has reviewed)と見分けられないので、
82
82
  * 直前の be を見る。間に副詞が挟まる(was quickly approved)ぶんだけ遡る。
83
83
  */
84
- const ADVERB_TAGS = new Set(["RB", "RBR", "RBS"]);
84
+ /**
85
+ * be と過去分詞の間に立てるもの。副詞と、副詞をつなぐ接続詞(was fully and carefully reviewed)。
86
+ *
87
+ * これでも拾えない形がある。`was fully and finally approved` では wink が `approved` を
88
+ * VBN ではなく **VBD** と付けるため、そもそも過去分詞として見えない。解析器の限界で、
89
+ * VBD も受動と見なすと `The team was here and approved it` まで受動になる。直さない。
90
+ */
91
+ const SKIPPABLE = new Set(["RB", "RBR", "RBS", "CC", ","]);
85
92
 
86
93
  const BE = new Set(["be", "am", "is", "are", "was", "were", "been", "being"]);
87
94
 
@@ -90,7 +97,7 @@ const isBe = (entry: Tagged): boolean => BE.has(entry.lemma ?? entry.value.toLow
90
97
  const isPassive = (tagged: readonly Tagged[], at: number): boolean => {
91
98
  if (tagged[at]?.pos !== "VBN") return false;
92
99
  const before = tagged.slice(0, at).reverse();
93
- const head = before.find((entry) => !ADVERB_TAGS.has(entry.pos));
100
+ const head = before.find((entry) => !SKIPPABLE.has(entry.pos));
94
101
  return head !== undefined && isBe(head);
95
102
  };
96
103
 
@@ -0,0 +1,366 @@
1
+ import type { Mention, NumberedLine, NumberingContext, StructurePatterns } from "chaffjs/plugin";
2
+ import { citedDocumentAfter } from "./citation.ts";
3
+
4
+ // Contracts, specifications and statutes in English. core nests what this reads; it does not know
5
+ // how English numbers its articles.
6
+
7
+ const ROMAN: Readonly<Record<string, number>> = { i: 1, v: 5, x: 10, l: 50, c: 100 };
8
+
9
+ /** "IV" → 4, "xii" → 12. Undefined for anything that is not a roman numeral. */
10
+ export const parseRoman = (text: string): number | undefined => {
11
+ const values = [...text.toLowerCase()].map((char) => ROMAN[char]);
12
+ if (values.length === 0 || values.some((value) => value === undefined)) return undefined;
13
+ const known = values.filter((value) => value !== undefined);
14
+ return known.reduce((total, value, index) => ((known[index + 1] ?? 0) > value ? total - value : total + value), 0);
15
+ };
16
+
17
+ const numberOf = (text: string | undefined): string | undefined => {
18
+ if (text === undefined) return undefined;
19
+ if (/^\d{1,3}(?:\.\d{1,3}){0,5}$/u.test(text)) return text;
20
+ const roman = parseRoman(text);
21
+ return roman === undefined ? undefined : String(roman);
22
+ };
23
+
24
+ /**
25
+ * A heading is the number followed by nothing, punctuation, or a capitalised title.
26
+ * "Article 3 shall apply to…" at the start of a line is a sentence, not the heading of Article 3.
27
+ */
28
+ const titleOf = (rest: string): string | undefined => {
29
+ const trimmed = rest.replace(/^[.:\-–—\s]+/u, "").trim();
30
+ if (trimmed !== "" && /^\p{Ll}/u.test(trimmed)) return undefined;
31
+ return trimmed;
32
+ };
33
+
34
+ const ARTICLE = /^\s{0,3}(?:ARTICLE|Article)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
35
+ const SECTION = /^\s{0,3}(?:SECTION|Section|§)\s*(?<n>\d{1,3}(?:\.\d{1,3}){0,5})\b(?<rest>.*)$/u;
36
+ const LETTERED = /^\s{0,6}\((?<n>[a-z]{1,4}|\d{1,3})\)\s+(?<rest>\S.*)$/u;
37
+ const MULTI_ROMAN = /^(?:ii|iii|iv|vi|vii|viii|ix)$/u;
38
+
39
+ const headed = (pattern: RegExp, line: string, label: (n: string) => string): NumberedLine | undefined => {
40
+ const groups = pattern.exec(line)?.groups;
41
+ const number = numberOf(groups?.["n"]);
42
+ const heading = titleOf(groups?.["rest"] ?? "");
43
+ if (groups === undefined || number === undefined || heading === undefined) return undefined;
44
+ const parts = number.split(".");
45
+ return {
46
+ kind: "article",
47
+ depth: parts.length,
48
+ number,
49
+ absolute: true,
50
+ label: label(groups["n"] ?? ""),
51
+ heading,
52
+ rest: heading,
53
+ ordinal: Number(parts.at(-1)),
54
+ };
55
+ };
56
+
57
+ type Style = "letter" | "roman" | "digit";
58
+
59
+ /**
60
+ * 開いている項目の書き方。"(ii)" はローマ数字、"(b)" は英字。一文字の "(i)" はどちらにも読めるので、
61
+ * 開いたときの読みを覚えておく代わりに、一つ上に英字が開いていたかで決め直す。
62
+ */
63
+ const styleOfLabel = (open: NumberedLine, index: number, all: readonly NumberedLine[]): Style | undefined => {
64
+ const inner = /^\((?<n>[a-z0-9]{1,4})\)$/u.exec(open.label)?.groups?.["n"];
65
+ if (inner === undefined) return undefined;
66
+ if (/^\d+$/u.test(inner)) return "digit";
67
+ if (MULTI_ROMAN.test(inner)) return "roman";
68
+ const above = all[index - 1];
69
+ return /^[ivx]$/u.test(inner) && above !== undefined && styleOfLabel(above, index - 1, all) === "letter" && above.depth < open.depth ? "roman" : "letter";
70
+ };
71
+
72
+ const styles = (context: NumberingContext): (Style | undefined)[] => context.open.map((open, index, all) => styleOfLabel(open, index, all));
73
+
74
+ /** "(h)" の次の "(i)" は英字。開いている英字の次の文字なら、ローマ数字とは読まない。 */
75
+ const followsLetter = (raw: string, context: NumberingContext): boolean =>
76
+ context.open.some((open, index) => styles(context)[index] === "letter" && open.number.charCodeAt(0) + 1 === raw.charCodeAt(0));
77
+
78
+ /** "(i)" is a roman numeral right under "(a)", or when a roman list is already open; the letter i otherwise. */
79
+ const styleOf = (raw: string, context: NumberingContext): Style => {
80
+ if (/^\d+$/u.test(raw)) return "digit";
81
+ if (MULTI_ROMAN.test(raw)) return "roman";
82
+ if (!/^[ivx]$/u.test(raw) || followsLetter(raw, context)) return "letter";
83
+ const open = styles(context);
84
+ return open.includes("roman") || open.at(-1) === "letter" ? "roman" : "letter";
85
+ };
86
+
87
+ /**
88
+ * A sibling has the depth of the open item written the same way: "(b)" closes "(i)" and sits beside "(a)".
89
+ * A new way of numbering goes one deeper than whatever is open.
90
+ */
91
+ const depthFor = (style: Style, context: NumberingContext): number => {
92
+ const open = styles(context);
93
+ const sibling = [...context.open].reverse().find((_item, reversed) => open[context.open.length - 1 - reversed] === style);
94
+ return sibling?.depth ?? (context.open.at(-1)?.depth ?? 0) + 1;
95
+ };
96
+
97
+ const LETTER_BEFORE_A = "a".charCodeAt(0) - 1;
98
+
99
+ /** "(b)" は 2 番目、"(ii)" も 2 番目。二文字以上の英字("(aa)")は並びが決まらないので付けない。 */
100
+ const ordinalOf = (raw: string, style: Style): number | undefined => {
101
+ if (style === "digit") return Number(raw);
102
+ if (style === "roman") return parseRoman(raw);
103
+ return raw.length === 1 ? raw.charCodeAt(0) - LETTER_BEFORE_A : undefined;
104
+ };
105
+
106
+ const lettered = (line: string, context: NumberingContext): NumberedLine | undefined => {
107
+ const groups = LETTERED.exec(line)?.groups;
108
+ const raw = groups?.["n"];
109
+ if (groups === undefined || raw === undefined) return undefined;
110
+ const style = styleOf(raw, context);
111
+ // 番地は書かれたままの "ii" を使う。参照「Section 4.2(a)(ii)」も同じ形で書かれるので、そのまま引ける。
112
+ return {
113
+ kind: "item",
114
+ depth: depthFor(style, context),
115
+ ordinal: ordinalOf(raw, style),
116
+ number: raw,
117
+ absolute: false,
118
+ label: `(${raw})`,
119
+ heading: "",
120
+ rest: groups["rest"]?.trim() ?? "",
121
+ };
122
+ };
123
+
124
+ /** Chapters restart in each part, so a chapter continues its part's address: PART II, CHAPTER 1 → pt2.ch1. */
125
+ const PART = /^\s{0,3}(?:PART|Part)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
126
+ const CHAPTER = /^\s{0,3}(?:CHAPTER|Chapter)\s+(?<n>\d{1,3}|[IVXLC]{1,7})\b(?<rest>.*)$/u;
127
+
128
+ const chapter = (pattern: RegExp, line: string, depth: number, prefix: string, word: string): NumberedLine | undefined => {
129
+ const groups = pattern.exec(line)?.groups;
130
+ const number = numberOf(groups?.["n"]);
131
+ const heading = titleOf(groups?.["rest"] ?? "");
132
+ if (groups === undefined || number === undefined || heading === undefined) return undefined;
133
+ const label = `${word} ${groups["n"] ?? ""}`;
134
+ return { kind: "chapter", depth, number: prefix + number, absolute: false, label, heading, rest: heading, ordinal: Number(number) };
135
+ };
136
+
137
+ const numbered = (line: string, context: NumberingContext): NumberedLine | undefined =>
138
+ chapter(PART, line, -2, "pt", "Part") ??
139
+ chapter(CHAPTER, line, -1, "ch", "Chapter") ??
140
+ headed(ARTICLE, line, (n) => `Article ${n}`) ??
141
+ headed(SECTION, line, (n) => `Section ${n}`) ??
142
+ lettered(line, context);
143
+
144
+ const mentions = (
145
+ pattern: RegExp,
146
+ text: string,
147
+ attrs: (groups: Readonly<Record<string, string | undefined>>, whole: string) => Mention["attrs"] | undefined,
148
+ ): Mention[] =>
149
+ [...text.matchAll(pattern)].flatMap((match) => {
150
+ const found = attrs(match.groups ?? {}, match[0]);
151
+ return found === undefined ? [] : [{ start: match.index, end: match.index + match[0].length, attrs: found }];
152
+ });
153
+
154
+ const DEFINITIONS = [
155
+ /["“](?<term>[^"”\n]{1,60})["”] (?:means|shall mean|refers to|has the meaning)\b/gu,
156
+ /\((?:the |hereinafter )?["“](?<term>[^"”\n]{1,60})["”]\)/gu,
157
+ /\(hereinafter referred to as ["“](?<term>[^"”\n]{1,60})["”]\)/gu,
158
+ ];
159
+
160
+ const definitions = (text: string): Mention[] =>
161
+ DEFINITIONS.flatMap((pattern) => mentions(pattern, text, (groups) => (groups["term"] === undefined ? undefined : { term: groups["term"] })));
162
+
163
+ const REFERENCE = /\b(?:Sections?|Articles?|§) ?(?<n>\d{1,3}(?:\.\d{1,3}){0,5}|[IVXLC]{1,7})\b/gu;
164
+ const SUBDIVISION = /^\((?<p>[a-z0-9]{1,4})\)/u;
165
+
166
+ /** "(a)(ii)(3)" is as deep as a reference goes; more parentheses are text, not a deeper address. */
167
+ const MAX_SUBDIVISIONS = 4;
168
+
169
+ /**
170
+ * "(a)(ii)" のような続きの括弧を、正規表現を複雑にせずに一つずつ読む。
171
+ * 再帰にしないのは、括弧が延々と続く行でスタックを使い切らないため。
172
+ */
173
+ const subdivisions = (text: string, from: number): { readonly parts: readonly string[]; readonly end: number } => {
174
+ const parts: string[] = [];
175
+ let end = from;
176
+ while (parts.length < MAX_SUBDIVISIONS) {
177
+ const part = SUBDIVISION.exec(text.slice(end, end + 6))?.groups?.["p"];
178
+ if (part === undefined) break;
179
+ parts.push(part);
180
+ end += part.length + 2;
181
+ }
182
+ return { parts, end };
183
+ };
184
+
185
+ /**
186
+ * "Section 4.2(a)" → 4.2.a, "Article III" → 3. The same addresses the tree gives.
187
+ * "Section 9 of the Master Agreement" carries the other document's name, and is not looked up in this tree.
188
+ */
189
+ const references = (text: string): Mention[] =>
190
+ [...text.matchAll(REFERENCE)].flatMap((match) => {
191
+ const main = numberOf(match.groups?.["n"]);
192
+ if (main === undefined) return [];
193
+ const { parts, end } = subdivisions(text, match.index + match[0].length);
194
+ const document = citedDocumentAfter(text, end);
195
+ const attrs = { target: [main, ...parts].join("."), label: text.slice(match.index, end), ...(document === undefined ? {} : { document }) };
196
+ return [{ start: match.index, end, attrs }];
197
+ });
198
+
199
+ /** Longest first, and never inside a word: "shall not" is not also "shall", "mayor" is not "may". */
200
+ const MARKERS: readonly (readonly [string, "must" | "must-not" | "may"])[] = [
201
+ ["is required to", "must"],
202
+ ["shall not", "must-not"],
203
+ ["must not", "must-not"],
204
+ ["may not", "must-not"],
205
+ ["agrees to", "must"],
206
+ ["shall", "must"],
207
+ ["must", "must"],
208
+ ["may", "may"],
209
+ ];
210
+
211
+ const isWordChar = (char: string | undefined): boolean => char !== undefined && /[\p{L}\p{N}_]/u.test(char);
212
+
213
+ /** Every whole-word occurrence. A loop, not recursion: a line with thousands of "may" must not exhaust the stack. */
214
+ const wordAt = (lower: string, word: string): number[] => {
215
+ const found: number[] = [];
216
+ for (let at = lower.indexOf(word); at !== -1; at = lower.indexOf(word, at + 1)) {
217
+ if (!isWordChar(lower[at - 1]) && !isWordChar(lower[at + word.length])) found.push(at);
218
+ }
219
+ return found;
220
+ };
221
+
222
+ const SENTENCE_OPENERS = new Set([".", "!", "?", ":", ";", '"', "“", "("]);
223
+
224
+ /** "May" in the middle of a sentence is the month: "published in May 2023". At the start it can be the modal. */
225
+ const isMonthName = (text: string, at: number): boolean => {
226
+ if (!text.startsWith("May", at)) return false;
227
+ const before = text.slice(Math.max(0, at - 4), at).trimEnd();
228
+ return before !== "" && !SENTENCE_OPENERS.has(before.at(-1) ?? "");
229
+ };
230
+
231
+ /**
232
+ * Longest marker first; a hit that overlaps one already kept is dropped. Overlap is checked with a mark
233
+ * per character, not by comparing every pair, which would go quadratic on a line with thousands of markers.
234
+ */
235
+ const obligations = (text: string): Mention[] => {
236
+ const lower = text.toLowerCase();
237
+ const taken = new Uint8Array(text.length);
238
+ const kept: Mention[] = [];
239
+ MARKERS.forEach(([marker, type]) => {
240
+ wordAt(lower, marker).forEach((start) => {
241
+ const end = start + marker.length;
242
+ if (isMonthName(text, start)) return;
243
+ if (taken.subarray(start, end).some((mark) => mark === 1)) return;
244
+ taken.fill(1, start, end);
245
+ kept.push({ start, end, attrs: { marker, type } });
246
+ });
247
+ });
248
+ return kept.sort((left, right) => left.start - right.start);
249
+ };
250
+
251
+ /** Find the number first, then look at what is right before (a currency) and right after (a unit). */
252
+ const NUMBER_RUN = /\d[\d,.]{0,15}/gu;
253
+ const UNITS = [
254
+ "business days",
255
+ "business day",
256
+ "calendar days",
257
+ "calendar day",
258
+ "days",
259
+ "day",
260
+ "weeks",
261
+ "week",
262
+ "months",
263
+ "month",
264
+ "years",
265
+ "year",
266
+ "hours",
267
+ "hour",
268
+ "minutes",
269
+ "minute",
270
+ "percent",
271
+ "times",
272
+ "%",
273
+ ];
274
+ const CURRENCIES = ["USD", "EUR", "$", "€", "£"];
275
+
276
+ /** "30." の終わりの点は文の終わり。数の一部にしない。 */
277
+ const withoutTrailingPunctuation = (run: string): string => {
278
+ let end = run.length;
279
+ while (end > 0 && (run[end - 1] === "." || run[end - 1] === ",")) end -= 1;
280
+ return run.slice(0, end);
281
+ };
282
+
283
+ /** One space or tab may sit between a number and its unit or currency. */
284
+ const isGap = (char: string | undefined): boolean => char === " " || char === "\t";
285
+
286
+ const unitAfter = (text: string, from: number): string | undefined => {
287
+ const gap = isGap(text[from]) ? 1 : 0;
288
+ const unit = UNITS.find((candidate) => text.startsWith(candidate, from + gap) && !isWordChar(text[from + gap + candidate.length]));
289
+ return unit;
290
+ };
291
+
292
+ /** The currency just before the number, allowing one space. Looks back a few characters, never at the whole line. */
293
+ const currencyBefore = (text: string, at: number): string | undefined => {
294
+ const end = isGap(text[at - 1]) ? at - 1 : at;
295
+ return CURRENCIES.find((currency) => text.startsWith(currency, end - currency.length));
296
+ };
297
+
298
+ const quantities = (text: string): Mention[] =>
299
+ [...text.matchAll(NUMBER_RUN)].flatMap((match) => {
300
+ const digits = withoutTrailingPunctuation(match[0]);
301
+ const value = Number(digits.replace(/,/gu, ""));
302
+ const end = match.index + digits.length;
303
+ const unit = unitAfter(text, end) ?? currencyBefore(text, match.index);
304
+ return unit === undefined || Number.isNaN(value) || isWordChar(text[match.index - 1]) ? [] : [{ start: match.index, end, attrs: { value, unit } }];
305
+ });
306
+
307
+ const MONTHS = ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
308
+ const MONTH_WORD = /\b(?<month>[A-Z][a-z]{2,8})\b/gu;
309
+ const ISO_DATE = /\b(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})\b/gu;
310
+ const DAY_BEFORE = /(?<d>\d{1,2})(?:st|nd|rd|th)? $/u;
311
+ const DAY_YEAR_AFTER = /^ (?<d>\d{1,2})(?:st|nd|rd|th)?,? (?<y>\d{4})\b/u;
312
+ const YEAR_AFTER = /^,? (?<y>\d{4})\b/u;
313
+
314
+ const pad = (value: string): string => value.padStart(2, "0");
315
+
316
+ /** "1 April 2024": the day written before the month. */
317
+ const dayBefore = (text: string, at: number): { readonly day: string; readonly start: number } | undefined => {
318
+ const found = DAY_BEFORE.exec(text.slice(Math.max(0, at - 6), at));
319
+ const day = found?.groups?.["d"];
320
+ return found === null || day === undefined ? undefined : { day, start: at - found[0].length };
321
+ };
322
+
323
+ /** "April 1, 2024" → 2024-04-01. */
324
+ const monthDayYear = (text: string, at: number, end: number, month: number): Mention | undefined => {
325
+ const found = DAY_YEAR_AFTER.exec(text.slice(end, end + 16));
326
+ if (found?.groups === undefined) return undefined;
327
+ const value = `${found.groups["y"] ?? ""}-${pad(String(month))}-${pad(found.groups["d"] ?? "")}`;
328
+ return { start: at, end: end + found[0].length, attrs: { value } };
329
+ };
330
+
331
+ /** "1 April 2024" → 2024-04-01, "April 2024" → 2024-04. */
332
+ const monthYear = (text: string, at: number, end: number, month: number): Mention | undefined => {
333
+ const found = YEAR_AFTER.exec(text.slice(end, end + 8));
334
+ const year = found?.groups?.["y"];
335
+ if (found === null || year === undefined) return undefined;
336
+ const before = dayBefore(text, at);
337
+ const value = [year, pad(String(month)), ...(before === undefined ? [] : [pad(before.day)])].join("-");
338
+ return { start: before?.start ?? at, end: end + found[0].length, attrs: { value } };
339
+ };
340
+
341
+ /**
342
+ * A month name alone is not a date: "May" is also the modal verb, so it counts only with a year beside it.
343
+ * The month is found first and its neighbours read with anchored patterns, never one long alternation.
344
+ */
345
+ const namedDate = (text: string, match: RegExpExecArray): Mention | undefined => {
346
+ const month = MONTHS.indexOf((match.groups?.["month"] ?? "").toLowerCase()) + 1;
347
+ if (month === 0) return undefined;
348
+ const end = match.index + match[0].length;
349
+ return monthDayYear(text, match.index, end, month) ?? monthYear(text, match.index, end, month);
350
+ };
351
+
352
+ const isoDate = (match: RegExpExecArray): Mention => ({
353
+ start: match.index,
354
+ end: match.index + match[0].length,
355
+ attrs: { value: `${match.groups?.["y"] ?? ""}-${match.groups?.["m"] ?? ""}-${match.groups?.["d"] ?? ""}` },
356
+ });
357
+
358
+ const dates = (text: string): Mention[] =>
359
+ [...[...text.matchAll(MONTH_WORD)].flatMap((match) => namedDate(text, match) ?? []), ...[...text.matchAll(ISO_DATE)].map(isoDate)].sort(
360
+ (left, right) => left.start - right.start,
361
+ );
362
+
363
+ /** "2.5 days" and "1.5 times" are amounts, not section 2.5 titled "days". */
364
+ const countedAfter = (_number: string, rest: string): boolean => unitAfter(` ${rest}`, 0) !== undefined;
365
+
366
+ export const structure: StructurePatterns = { numbered, definitions, references, obligations, quantities, dates, countedAfter };