@concordance-wiki/nlp 0.1.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.
- package/LICENSE +674 -0
- package/README.md +7 -0
- package/dist/dictionary/build.d.ts +19 -0
- package/dist/dictionary/build.d.ts.map +1 -0
- package/dist/dictionary/build.js +72 -0
- package/dist/dictionary/build.js.map +1 -0
- package/dist/dictionary/stopwords.d.ts +16 -0
- package/dist/dictionary/stopwords.d.ts.map +1 -0
- package/dist/dictionary/stopwords.js +27 -0
- package/dist/dictionary/stopwords.js.map +1 -0
- package/dist/dictionary/types.d.ts +34 -0
- package/dist/dictionary/types.d.ts.map +1 -0
- package/dist/dictionary/types.js +2 -0
- package/dist/dictionary/types.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/keywords/confidence.d.ts +56 -0
- package/dist/keywords/confidence.d.ts.map +1 -0
- package/dist/keywords/confidence.js +59 -0
- package/dist/keywords/confidence.js.map +1 -0
- package/dist/keywords/defined.d.ts +26 -0
- package/dist/keywords/defined.d.ts.map +1 -0
- package/dist/keywords/defined.js +33 -0
- package/dist/keywords/defined.js.map +1 -0
- package/dist/keywords/findings.d.ts +13 -0
- package/dist/keywords/findings.d.ts.map +1 -0
- package/dist/keywords/findings.js +35 -0
- package/dist/keywords/findings.js.map +1 -0
- package/dist/keywords/ngrams.d.ts +47 -0
- package/dist/keywords/ngrams.d.ts.map +1 -0
- package/dist/keywords/ngrams.js +64 -0
- package/dist/keywords/ngrams.js.map +1 -0
- package/dist/keywords/options.d.ts +22 -0
- package/dist/keywords/options.d.ts.map +1 -0
- package/dist/keywords/options.js +22 -0
- package/dist/keywords/options.js.map +1 -0
- package/dist/keywords/publish.d.ts +50 -0
- package/dist/keywords/publish.d.ts.map +1 -0
- package/dist/keywords/publish.js +96 -0
- package/dist/keywords/publish.js.map +1 -0
- package/dist/keywords/score.d.ts +60 -0
- package/dist/keywords/score.d.ts.map +1 -0
- package/dist/keywords/score.js +0 -0
- package/dist/keywords/score.js.map +1 -0
- package/dist/keywords/similar.d.ts +19 -0
- package/dist/keywords/similar.d.ts.map +1 -0
- package/dist/keywords/similar.js +52 -0
- package/dist/keywords/similar.js.map +1 -0
- package/dist/locale/load-pack.d.ts +11 -0
- package/dist/locale/load-pack.d.ts.map +1 -0
- package/dist/locale/load-pack.js +82 -0
- package/dist/locale/load-pack.js.map +1 -0
- package/dist/locale/normalize.d.ts +3 -0
- package/dist/locale/normalize.d.ts.map +1 -0
- package/dist/locale/normalize.js +14 -0
- package/dist/locale/normalize.js.map +1 -0
- package/dist/locale/pack.d.ts +34 -0
- package/dist/locale/pack.d.ts.map +1 -0
- package/dist/locale/pack.js +2 -0
- package/dist/locale/pack.js.map +1 -0
- package/dist/locale/registry.d.ts +16 -0
- package/dist/locale/registry.d.ts.map +1 -0
- package/dist/locale/registry.js +35 -0
- package/dist/locale/registry.js.map +1 -0
- package/dist/locale/stopwords.d.ts +3 -0
- package/dist/locale/stopwords.d.ts.map +1 -0
- package/dist/locale/stopwords.js +17 -0
- package/dist/locale/stopwords.js.map +1 -0
- package/dist/locale/suffixes.d.ts +7 -0
- package/dist/locale/suffixes.d.ts.map +1 -0
- package/dist/locale/suffixes.js +21 -0
- package/dist/locale/suffixes.js.map +1 -0
- package/dist/locale/tag.d.ts +4 -0
- package/dist/locale/tag.d.ts.map +1 -0
- package/dist/locale/tag.js +5 -0
- package/dist/locale/tag.js.map +1 -0
- package/dist/scan/automaton.d.ts +46 -0
- package/dist/scan/automaton.d.ts.map +1 -0
- package/dist/scan/automaton.js +92 -0
- package/dist/scan/automaton.js.map +1 -0
- package/dist/scan/context.d.ts +20 -0
- package/dist/scan/context.d.ts.map +1 -0
- package/dist/scan/context.js +26 -0
- package/dist/scan/context.js.map +1 -0
- package/dist/scan/occurrences.d.ts +64 -0
- package/dist/scan/occurrences.d.ts.map +1 -0
- package/dist/scan/occurrences.js +109 -0
- package/dist/scan/occurrences.js.map +1 -0
- package/dist/scan/tokens.d.ts +12 -0
- package/dist/scan/tokens.d.ts.map +1 -0
- package/dist/scan/tokens.js +34 -0
- package/dist/scan/tokens.js.map +1 -0
- package/dist/text/boundaries.d.ts +12 -0
- package/dist/text/boundaries.d.ts.map +1 -0
- package/dist/text/boundaries.js +13 -0
- package/dist/text/boundaries.js.map +1 -0
- package/dist/text/comparison-form.d.ts +9 -0
- package/dist/text/comparison-form.d.ts.map +1 -0
- package/dist/text/comparison-form.js +23 -0
- package/dist/text/comparison-form.js.map +1 -0
- package/dist/text/context.d.ts +6 -0
- package/dist/text/context.d.ts.map +1 -0
- package/dist/text/context.js +12 -0
- package/dist/text/context.js.map +1 -0
- package/dist/text/search-tokens.d.ts +10 -0
- package/dist/text/search-tokens.d.ts.map +1 -0
- package/dist/text/search-tokens.js +29 -0
- package/dist/text/search-tokens.js.map +1 -0
- package/dist/text/singular.d.ts +7 -0
- package/dist/text/singular.d.ts.map +1 -0
- package/dist/text/singular.js +12 -0
- package/dist/text/singular.js.map +1 -0
- package/locales/en/pack.yaml +18 -0
- package/locales/en/stopwords.txt +536 -0
- package/locales/en/suffixes.txt +7 -0
- package/locales/fr/pack.yaml +15 -0
- package/locales/fr/stopwords.txt +1021 -0
- package/locales/fr/suffixes.txt +12 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.js","sourceRoot":"","sources":["../../src/locale/pack.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Locale } from "@concordance-wiki/core";
|
|
2
|
+
import type { LanguagePack } from "./pack.js";
|
|
3
|
+
/**
|
|
4
|
+
* The pack of a locale: the exact tag first, then its language alone, so that `fr-CA`
|
|
5
|
+
* reads the `fr` pack until a plugin registers a more specific one.
|
|
6
|
+
*/
|
|
7
|
+
export declare function languagePack(locale: Locale): LanguagePack;
|
|
8
|
+
export declare function registerLanguagePack(pack: LanguagePack): void;
|
|
9
|
+
export declare function availableLocales(): Locale[];
|
|
10
|
+
/** The locale of a source: its own, then the project's, then `en`; always canonical. */
|
|
11
|
+
export declare function resolveLocale(source: {
|
|
12
|
+
locale?: Locale;
|
|
13
|
+
}, project: {
|
|
14
|
+
locale?: Locale;
|
|
15
|
+
}): Locale;
|
|
16
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/locale/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAc9C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CASzD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAK7D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE/F"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { loadLanguagePack } from "./load-pack.js";
|
|
2
|
+
import { canonicalLocale } from "./tag.js";
|
|
3
|
+
// Resolves from both src/locale/ and dist/locale/, which sit at the same depth.
|
|
4
|
+
const shipped = ["en", "fr"].map((locale) => loadLanguagePack(new URL(`../../locales/${locale}/`, import.meta.url)));
|
|
5
|
+
const packs = new Map(shipped.map((pack) => [pack.locale, pack]));
|
|
6
|
+
// Code-unit order, not locale order: the output must not depend on the collation data of the runtime.
|
|
7
|
+
function byCodeUnit(a, b) {
|
|
8
|
+
return Number(a > b) - Number(a < b);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The pack of a locale: the exact tag first, then its language alone, so that `fr-CA`
|
|
12
|
+
* reads the `fr` pack until a plugin registers a more specific one.
|
|
13
|
+
*/
|
|
14
|
+
export function languagePack(locale) {
|
|
15
|
+
const tag = canonicalLocale(locale);
|
|
16
|
+
const pack = packs.get(tag) ?? packs.get(new Intl.Locale(tag).language);
|
|
17
|
+
if (pack === undefined) {
|
|
18
|
+
throw new Error(`no language pack for locale "${tag}": the engine ships ${availableLocales().join(", ")}; other locales come from plugins`);
|
|
19
|
+
}
|
|
20
|
+
return pack;
|
|
21
|
+
}
|
|
22
|
+
export function registerLanguagePack(pack) {
|
|
23
|
+
if (packs.has(pack.locale)) {
|
|
24
|
+
throw new Error(`a language pack for locale "${pack.locale}" is already registered`);
|
|
25
|
+
}
|
|
26
|
+
packs.set(pack.locale, pack);
|
|
27
|
+
}
|
|
28
|
+
export function availableLocales() {
|
|
29
|
+
return [...packs.keys()].sort(byCodeUnit);
|
|
30
|
+
}
|
|
31
|
+
/** The locale of a source: its own, then the project's, then `en`; always canonical. */
|
|
32
|
+
export function resolveLocale(source, project) {
|
|
33
|
+
return canonicalLocale(source.locale ?? project.locale ?? "en");
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/locale/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,gFAAgF;AAChF,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC1C,gBAAgB,CAAC,IAAI,GAAG,CAAC,iBAAiB,MAAM,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CACvE,CAAC;AACF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAuB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAExF,sGAAsG;AACtG,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IACxE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,gCAAgC,GAAG,uBAAuB,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,mCAAmC,CAC3H,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,MAAM,yBAAyB,CAAC,CAAC;IACvF,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,MAA2B,EAAE,OAA4B;IACrF,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stopwords.d.ts","sourceRoot":"","sources":["../../src/locale/stopwords.ts"],"names":[],"mappings":"AAKA,sGAAsG;AACtG,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUpD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Code-unit order, not locale order: the output must not depend on the collation data of the runtime.
|
|
2
|
+
function byCodeUnit(a, b) {
|
|
3
|
+
return Number(a > b) - Number(a < b);
|
|
4
|
+
}
|
|
5
|
+
/** One word per line, `#` starts a comment; returns the unique lowercase words in code unit order. */
|
|
6
|
+
export function loadStopwords(text) {
|
|
7
|
+
const words = new Set();
|
|
8
|
+
for (const line of text.split("\n")) {
|
|
9
|
+
const hash = line.indexOf("#");
|
|
10
|
+
const word = (hash === -1 ? line : line.slice(0, hash)).trim().toLowerCase();
|
|
11
|
+
if (word !== "") {
|
|
12
|
+
words.add(word);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return [...words].sort(byCodeUnit);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=stopwords.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stopwords.js","sourceRoot":"","sources":["../../src/locale/stopwords.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC7E,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inflected-form suffixes of a pack's `suffixes.txt`: one suffix per line, an optional
|
|
3
|
+
* leading hyphen, `#` starts a comment; returns the unique suffixes in comparison form
|
|
4
|
+
* (through the pack's normaliser), in code unit order.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadSuffixes(text: string, normalize: (text: string) => string): string[];
|
|
7
|
+
//# sourceMappingURL=suffixes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suffixes.d.ts","sourceRoot":"","sources":["../../src/locale/suffixes.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,EAAE,CAUxF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Code-unit order, not locale order: the output must not depend on the collation data of the runtime.
|
|
2
|
+
function byCodeUnit(a, b) {
|
|
3
|
+
return Number(a > b) - Number(a < b);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The inflected-form suffixes of a pack's `suffixes.txt`: one suffix per line, an optional
|
|
7
|
+
* leading hyphen, `#` starts a comment; returns the unique suffixes in comparison form
|
|
8
|
+
* (through the pack's normaliser), in code unit order.
|
|
9
|
+
*/
|
|
10
|
+
export function loadSuffixes(text, normalize) {
|
|
11
|
+
const suffixes = new Set();
|
|
12
|
+
for (const line of text.split("\n")) {
|
|
13
|
+
const hash = line.indexOf("#");
|
|
14
|
+
const suffix = normalize((hash === -1 ? line : line.slice(0, hash)).trim().replace(/^-/, ""));
|
|
15
|
+
if (suffix !== "") {
|
|
16
|
+
suffixes.add(suffix);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return [...suffixes].sort(byCodeUnit);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=suffixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suffixes.js","sourceRoot":"","sources":["../../src/locale/suffixes.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAmC;IAC5E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9F,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YAClB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/locale/tag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag.js","sourceRoot":"","sources":["../../src/locale/tag.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** A sequence of words to recognise, identified by `key`. */
|
|
2
|
+
export interface Pattern<K = string> {
|
|
3
|
+
key: K;
|
|
4
|
+
words: readonly string[];
|
|
5
|
+
}
|
|
6
|
+
/** A pattern found in a token sequence: `start` inclusive, `end` exclusive, as token indexes. */
|
|
7
|
+
export interface RawMatch<K = string> {
|
|
8
|
+
key: K;
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
}
|
|
12
|
+
interface Output<K> {
|
|
13
|
+
key: K;
|
|
14
|
+
length: number;
|
|
15
|
+
}
|
|
16
|
+
/** One state of the automaton: the words read so far, as a node of the pattern trie. */
|
|
17
|
+
export interface AutomatonState<K = string> {
|
|
18
|
+
readonly next: ReadonlyMap<string, AutomatonState<K>>;
|
|
19
|
+
/** The longest proper suffix of this state that is also a state; the root fails to itself. */
|
|
20
|
+
readonly fail: AutomatonState<K>;
|
|
21
|
+
/** Every pattern ending here, own or through failure links, longest first. */
|
|
22
|
+
readonly output: readonly Output<K>[];
|
|
23
|
+
}
|
|
24
|
+
export interface Automaton<K = string> {
|
|
25
|
+
readonly root: AutomatonState<K>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Aho-Corasick over words: the trie of the patterns with failure links, so that a text is read
|
|
29
|
+
* once whatever the number of patterns. A pattern without words is ignored, and a key given
|
|
30
|
+
* twice for the same words is kept once.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildAutomaton<K = string>(patterns: readonly Pattern<K>[]): Automaton<K>;
|
|
33
|
+
/** Every pattern occurrence in the tokens, in one pass, in order of end then longest first. */
|
|
34
|
+
export declare function scan<K>(automaton: Automaton<K>, tokens: readonly {
|
|
35
|
+
word: string;
|
|
36
|
+
}[]): RawMatch<K>[];
|
|
37
|
+
/**
|
|
38
|
+
* Resolves overlaps: a match contained in another one is dropped, so that among the matches
|
|
39
|
+
* sharing a start the longest wins and "keyword page" is not counted again as "page"; matches
|
|
40
|
+
* that only partly overlap, from different starts, are all kept since each may name another
|
|
41
|
+
* entity. Two keys on exactly the same span both stay: they are two spellings of the same
|
|
42
|
+
* words. The result is in text order; equal spans keep their input order.
|
|
43
|
+
*/
|
|
44
|
+
export declare function longestMatches<K>(matches: readonly RawMatch<K>[]): RawMatch<K>[];
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=automaton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automaton.d.ts","sourceRoot":"","sources":["../../src/scan/automaton.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,MAAM;IACjC,GAAG,EAAE,CAAC,CAAC;IACP,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,iGAAiG;AACjG,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,MAAM;IAClC,GAAG,EAAE,CAAC,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,MAAM,CAAC,CAAC;IAChB,GAAG,EAAE,CAAC,CAAC;IACP,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,MAAM;IACxC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,MAAM;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;CAClC;AA0BD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAuBxF;AAED,+FAA+F;AAC/F,wBAAgB,IAAI,CAAC,CAAC,EACpB,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAClC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAYf;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAgBhF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
class State {
|
|
2
|
+
next = new Map();
|
|
3
|
+
fail;
|
|
4
|
+
output = [];
|
|
5
|
+
constructor(fail) {
|
|
6
|
+
this.fail = fail ?? this;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** The state the words lead to from the root, created along the way when missing. */
|
|
10
|
+
function stateOf(root, words) {
|
|
11
|
+
let state = root;
|
|
12
|
+
for (const word of words) {
|
|
13
|
+
let child = state.next.get(word);
|
|
14
|
+
if (child === undefined) {
|
|
15
|
+
child = new State(root);
|
|
16
|
+
state.next.set(word, child);
|
|
17
|
+
}
|
|
18
|
+
state = child;
|
|
19
|
+
}
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Aho-Corasick over words: the trie of the patterns with failure links, so that a text is read
|
|
24
|
+
* once whatever the number of patterns. A pattern without words is ignored, and a key given
|
|
25
|
+
* twice for the same words is kept once.
|
|
26
|
+
*/
|
|
27
|
+
export function buildAutomaton(patterns) {
|
|
28
|
+
const root = new State();
|
|
29
|
+
for (const { key, words } of patterns) {
|
|
30
|
+
if (words.length === 0)
|
|
31
|
+
continue;
|
|
32
|
+
const state = stateOf(root, words);
|
|
33
|
+
if (!state.output.some((output) => output.key === key)) {
|
|
34
|
+
state.output.push({ key, length: words.length });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Breadth-first, so that the failure link of a state is final before its children use it.
|
|
38
|
+
// The array iterator reads the elements pushed during the walk.
|
|
39
|
+
const queue = [...root.next.values()];
|
|
40
|
+
for (const state of queue) {
|
|
41
|
+
for (const [word, child] of state.next) {
|
|
42
|
+
let fail = state.fail;
|
|
43
|
+
while (fail !== root && !fail.next.has(word))
|
|
44
|
+
fail = fail.fail;
|
|
45
|
+
child.fail = fail.next.get(word) ?? root;
|
|
46
|
+
child.output = [...child.output, ...child.fail.output];
|
|
47
|
+
queue.push(child);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { root };
|
|
51
|
+
}
|
|
52
|
+
/** Every pattern occurrence in the tokens, in one pass, in order of end then longest first. */
|
|
53
|
+
export function scan(automaton, tokens) {
|
|
54
|
+
const { root } = automaton;
|
|
55
|
+
const matches = [];
|
|
56
|
+
let state = root;
|
|
57
|
+
for (const [index, token] of tokens.entries()) {
|
|
58
|
+
while (state !== root && !state.next.has(token.word))
|
|
59
|
+
state = state.fail;
|
|
60
|
+
state = state.next.get(token.word) ?? root;
|
|
61
|
+
for (const { key, length } of state.output) {
|
|
62
|
+
matches.push({ key, start: index + 1 - length, end: index + 1 });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return matches;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolves overlaps: a match contained in another one is dropped, so that among the matches
|
|
69
|
+
* sharing a start the longest wins and "keyword page" is not counted again as "page"; matches
|
|
70
|
+
* that only partly overlap, from different starts, are all kept since each may name another
|
|
71
|
+
* entity. Two keys on exactly the same span both stay: they are two spellings of the same
|
|
72
|
+
* words. The result is in text order; equal spans keep their input order.
|
|
73
|
+
*/
|
|
74
|
+
export function longestMatches(matches) {
|
|
75
|
+
const byStart = [...matches].sort((a, b) => a.start - b.start || b.end - a.end);
|
|
76
|
+
const kept = [];
|
|
77
|
+
// Farthest end of the matches starting before the current group, and of the group itself.
|
|
78
|
+
let outerEnd = -1;
|
|
79
|
+
let groupStart = -1;
|
|
80
|
+
let groupEnd = -1;
|
|
81
|
+
for (const match of byStart) {
|
|
82
|
+
if (match.start !== groupStart) {
|
|
83
|
+
outerEnd = Math.max(outerEnd, groupEnd);
|
|
84
|
+
groupStart = match.start;
|
|
85
|
+
groupEnd = match.end;
|
|
86
|
+
}
|
|
87
|
+
if (match.end === groupEnd && match.end > outerEnd)
|
|
88
|
+
kept.push(match);
|
|
89
|
+
}
|
|
90
|
+
return kept;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=automaton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automaton.js","sourceRoot":"","sources":["../../src/scan/automaton.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** An inline code span the scanned text leaves out: what was written, at the offset it stood at. */
|
|
2
|
+
export interface ElidedCode {
|
|
3
|
+
/** Offset in the scanned text where the code stood; the code precedes the character there. */
|
|
4
|
+
at: number;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
/** What the context of an occurrence is cut from: the scanned text and the code it leaves out. */
|
|
8
|
+
export interface QuotedText {
|
|
9
|
+
text: string;
|
|
10
|
+
code?: readonly ElidedCode[];
|
|
11
|
+
}
|
|
12
|
+
/** How many characters of the text an occurrence quotes. */
|
|
13
|
+
export declare const CONTEXT_WIDTH = 80;
|
|
14
|
+
/**
|
|
15
|
+
* The context of an occurrence: a window of `width` characters of the text as written, the
|
|
16
|
+
* inline code the scan skipped put back in place, centred on the match at `start`–`end` of the
|
|
17
|
+
* scanned text. A code span at the start of the match precedes it, one at its end follows it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function occurrenceContext(quoted: QuotedText, start: number, end: number, width?: number): string;
|
|
20
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/scan/context.ts"],"names":[],"mappings":"AAEA,oGAAoG;AACpG,MAAM,WAAW,UAAU;IACzB,8FAA8F;IAC9F,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC9B;AAED,4DAA4D;AAC5D,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,SAAgB,GACpB,MAAM,CAcR"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { contextAround } from "../text/context.js";
|
|
2
|
+
/** How many characters of the text an occurrence quotes. */
|
|
3
|
+
export const CONTEXT_WIDTH = 80;
|
|
4
|
+
/**
|
|
5
|
+
* The context of an occurrence: a window of `width` characters of the text as written, the
|
|
6
|
+
* inline code the scan skipped put back in place, centred on the match at `start`–`end` of the
|
|
7
|
+
* scanned text. A code span at the start of the match precedes it, one at its end follows it.
|
|
8
|
+
*/
|
|
9
|
+
export function occurrenceContext(quoted, start, end, width = CONTEXT_WIDTH) {
|
|
10
|
+
const code = quoted.code ?? [];
|
|
11
|
+
let written = "";
|
|
12
|
+
let cursor = 0;
|
|
13
|
+
let from = start;
|
|
14
|
+
let to = end;
|
|
15
|
+
for (const span of code) {
|
|
16
|
+
written += quoted.text.slice(cursor, span.at) + span.text;
|
|
17
|
+
cursor = span.at;
|
|
18
|
+
if (span.at <= start)
|
|
19
|
+
from += span.text.length;
|
|
20
|
+
if (span.at < end)
|
|
21
|
+
to += span.text.length;
|
|
22
|
+
}
|
|
23
|
+
written += quoted.text.slice(cursor);
|
|
24
|
+
return contextAround(written, from, to, width);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/scan/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAenD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAkB,EAClB,KAAa,EACb,GAAW,EACX,KAAK,GAAG,aAAa;IAErB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,EAAE,GAAG,GAAG,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1D,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACjB,IAAI,IAAI,CAAC,EAAE,IAAI,KAAK;YAAE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAC/C,IAAI,IAAI,CAAC,EAAE,GAAG,GAAG;YAAE,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC5C,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Dictionary, DictionaryTarget } from "../dictionary/types.js";
|
|
2
|
+
import type { LanguagePack } from "../locale/pack.js";
|
|
3
|
+
import { type ElidedCode } from "./context.js";
|
|
4
|
+
/** The `confidence.glossary_occurrence` block of the profile, every key resolved. */
|
|
5
|
+
export interface OccurrenceScale {
|
|
6
|
+
base: number;
|
|
7
|
+
per_occurrence: number;
|
|
8
|
+
cap: number;
|
|
9
|
+
homonym_factor: number;
|
|
10
|
+
type_prefix_bonus: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ScannedParagraph {
|
|
13
|
+
line: number;
|
|
14
|
+
/** The text the scan reads, inline code left out. */
|
|
15
|
+
text: string;
|
|
16
|
+
/** Heading of the enclosing section, when any. */
|
|
17
|
+
section?: string;
|
|
18
|
+
/** The inline code the text leaves out, in text order, quoted back in the context of an occurrence. */
|
|
19
|
+
code?: readonly ElidedCode[];
|
|
20
|
+
}
|
|
21
|
+
/** A document reduced to the text units the scan reads, excluded zones already removed. */
|
|
22
|
+
export interface ScannedDocument {
|
|
23
|
+
path: string;
|
|
24
|
+
paragraphs: readonly ScannedParagraph[];
|
|
25
|
+
}
|
|
26
|
+
export interface Occurrence {
|
|
27
|
+
/** The dictionary key that matched. */
|
|
28
|
+
key: string;
|
|
29
|
+
target: Pick<DictionaryTarget, "id" | "kind">;
|
|
30
|
+
source: string;
|
|
31
|
+
path: string;
|
|
32
|
+
line: number;
|
|
33
|
+
/** Code unit offset of the match in the paragraph text. */
|
|
34
|
+
position: number;
|
|
35
|
+
/** The match as written, from its first to its last token. */
|
|
36
|
+
text: string;
|
|
37
|
+
section?: string;
|
|
38
|
+
/** 80 characters of the text as written, inline code included, centred on the match, an ellipsis marking each cut. */
|
|
39
|
+
context: string;
|
|
40
|
+
/** The type slug announced by the word right before the match, when it is a type prefix. */
|
|
41
|
+
expectedType?: string;
|
|
42
|
+
confidence: number;
|
|
43
|
+
}
|
|
44
|
+
export interface ScanDocumentInput {
|
|
45
|
+
document: ScannedDocument;
|
|
46
|
+
source: string;
|
|
47
|
+
dictionary: Dictionary;
|
|
48
|
+
pack: LanguagePack;
|
|
49
|
+
/** Type slug to the words announcing it, for the locale of the dictionary. */
|
|
50
|
+
typePrefixes: Readonly<Record<string, readonly string[]>>;
|
|
51
|
+
scale: OccurrenceScale;
|
|
52
|
+
}
|
|
53
|
+
/** Canonical order of occurrences: path, line, position, target, then key. */
|
|
54
|
+
export declare function compareOccurrences(a: Occurrence, b: Occurrence): number;
|
|
55
|
+
/**
|
|
56
|
+
* Every mention of a dictionary entry in the paragraphs of a document, one per target of the
|
|
57
|
+
* entry, in canonical order. Confidence is the base of the scale, plus
|
|
58
|
+
* the type prefix bonus when a prefix announces the mention, halved (or whatever the scale
|
|
59
|
+
* says) for a homonym; the increment per further occurrence belongs to the combination step.
|
|
60
|
+
*/
|
|
61
|
+
export declare function scanDocument(input: ScanDocumentInput): Occurrence[];
|
|
62
|
+
/** Confidence of an entry mentioned `count` times in a document: the increments up to the cap. */
|
|
63
|
+
export declare function occurrenceConfidence(count: number, scale: OccurrenceScale): number;
|
|
64
|
+
//# sourceMappingURL=occurrences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"occurrences.d.ts","sourceRoot":"","sources":["../../src/scan/occurrences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAQtD,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAGlE,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uGAAuG;IACvG,IAAI,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC9B;AAED,2FAA2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sHAAsH;IACtH,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1D,KAAK,EAAE,eAAe,CAAC;CACxB;AA4DD,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAQvE;AAqCD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,UAAU,EAAE,CAanE;AAED,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAElF"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { buildAutomaton, longestMatches, scan, } from "./automaton.js";
|
|
2
|
+
import { occurrenceContext } from "./context.js";
|
|
3
|
+
import { tokenize } from "./tokens.js";
|
|
4
|
+
function byCodeUnit(a, b) {
|
|
5
|
+
return Number(a > b) - Number(a < b);
|
|
6
|
+
}
|
|
7
|
+
// The same dictionary is scanned against every document: its automaton is built once.
|
|
8
|
+
const automatons = new WeakMap();
|
|
9
|
+
function automatonOf(dictionary, pack) {
|
|
10
|
+
let byPack = automatons.get(dictionary);
|
|
11
|
+
if (byPack === undefined) {
|
|
12
|
+
byPack = new WeakMap();
|
|
13
|
+
automatons.set(dictionary, byPack);
|
|
14
|
+
}
|
|
15
|
+
let automaton = byPack.get(pack);
|
|
16
|
+
if (automaton === undefined) {
|
|
17
|
+
// Keys are tokenised like the texts, so that "mot-clé" matches its two tokens.
|
|
18
|
+
const patterns = [...dictionary.entries.values()].map((entry) => ({
|
|
19
|
+
key: entry,
|
|
20
|
+
words: tokenize(entry.key, pack).map((token) => token.word),
|
|
21
|
+
}));
|
|
22
|
+
automaton = buildAutomaton(patterns);
|
|
23
|
+
byPack.set(pack, automaton);
|
|
24
|
+
}
|
|
25
|
+
return automaton;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Comparison form of every prefix word to the type it announces; a word listed under several
|
|
29
|
+
* types announces none. A prefix is a single word: the token right before the match.
|
|
30
|
+
*/
|
|
31
|
+
function prefixTypes(typePrefixes, pack) {
|
|
32
|
+
const types = new Map();
|
|
33
|
+
for (const [type, words] of Object.entries(typePrefixes)) {
|
|
34
|
+
for (const prefix of words) {
|
|
35
|
+
const word = tokenize(prefix, pack)
|
|
36
|
+
.map((token) => token.word)
|
|
37
|
+
.join(" ");
|
|
38
|
+
types.set(word, types.has(word) ? undefined : type);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return types;
|
|
42
|
+
}
|
|
43
|
+
// The automaton only reports spans inside the tokens it read, so the slice is never empty.
|
|
44
|
+
function spanOf(tokens, match) {
|
|
45
|
+
const span = tokens.slice(match.start, match.end);
|
|
46
|
+
return {
|
|
47
|
+
start: Math.min(...span.map((token) => token.start)),
|
|
48
|
+
end: Math.max(...span.map((token) => token.end)),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Canonical order of occurrences: path, line, position, target, then key. */
|
|
52
|
+
export function compareOccurrences(a, b) {
|
|
53
|
+
return (byCodeUnit(a.path, b.path) ||
|
|
54
|
+
a.line - b.line ||
|
|
55
|
+
a.position - b.position ||
|
|
56
|
+
byCodeUnit(a.target.id, b.target.id) ||
|
|
57
|
+
byCodeUnit(a.key, b.key));
|
|
58
|
+
}
|
|
59
|
+
/** The occurrences of one match: one per target of the entry, at the same confidence. */
|
|
60
|
+
function occurrencesOf(scan, match) {
|
|
61
|
+
const { input, paragraph, tokens, prefixes } = scan;
|
|
62
|
+
const { scale } = input;
|
|
63
|
+
const span = spanOf(tokens, match);
|
|
64
|
+
const before = tokens[match.start - 1];
|
|
65
|
+
const expectedType = before === undefined ? undefined : prefixes.get(before.word);
|
|
66
|
+
const announced = expectedType === undefined ? 0 : scale.type_prefix_bonus;
|
|
67
|
+
const factor = match.key.homonym ? scale.homonym_factor : 1;
|
|
68
|
+
const base = {
|
|
69
|
+
key: match.key.key,
|
|
70
|
+
source: input.source,
|
|
71
|
+
path: input.document.path,
|
|
72
|
+
line: paragraph.line,
|
|
73
|
+
position: span.start,
|
|
74
|
+
text: paragraph.text.slice(span.start, span.end),
|
|
75
|
+
...(paragraph.section === undefined ? {} : { section: paragraph.section }),
|
|
76
|
+
context: occurrenceContext(paragraph, span.start, span.end),
|
|
77
|
+
...(expectedType === undefined ? {} : { expectedType }),
|
|
78
|
+
confidence: (scale.base + announced) * factor,
|
|
79
|
+
};
|
|
80
|
+
return match.key.targets.map((target) => ({
|
|
81
|
+
...base,
|
|
82
|
+
target: { id: target.id, kind: target.kind },
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Every mention of a dictionary entry in the paragraphs of a document, one per target of the
|
|
87
|
+
* entry, in canonical order. Confidence is the base of the scale, plus
|
|
88
|
+
* the type prefix bonus when a prefix announces the mention, halved (or whatever the scale
|
|
89
|
+
* says) for a homonym; the increment per further occurrence belongs to the combination step.
|
|
90
|
+
*/
|
|
91
|
+
export function scanDocument(input) {
|
|
92
|
+
const { document, dictionary, pack } = input;
|
|
93
|
+
const automaton = automatonOf(dictionary, pack);
|
|
94
|
+
const prefixes = prefixTypes(input.typePrefixes, pack);
|
|
95
|
+
const occurrences = [];
|
|
96
|
+
for (const paragraph of document.paragraphs) {
|
|
97
|
+
const tokens = tokenize(paragraph.text, pack);
|
|
98
|
+
const scanned = { input, paragraph, tokens, prefixes };
|
|
99
|
+
for (const match of longestMatches(scan(automaton, tokens))) {
|
|
100
|
+
occurrences.push(...occurrencesOf(scanned, match));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return occurrences.sort(compareOccurrences);
|
|
104
|
+
}
|
|
105
|
+
/** Confidence of an entry mentioned `count` times in a document: the increments up to the cap. */
|
|
106
|
+
export function occurrenceConfidence(count, scale) {
|
|
107
|
+
return Math.min(scale.cap, scale.base + scale.per_occurrence * (count - 1));
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=occurrences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"occurrences.js","sourceRoot":"","sources":["../../src/scan/occurrences.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LanguagePack } from "../locale/pack.js";
|
|
2
|
+
/** A word of a text as the scan compares it, with its character span in the text as given. */
|
|
3
|
+
export interface Token {
|
|
4
|
+
/** Comparison form of the word: normalised by the pack, then singularised. */
|
|
5
|
+
word: string;
|
|
6
|
+
start: number;
|
|
7
|
+
/** Exclusive, in code units of the original text. */
|
|
8
|
+
end: number;
|
|
9
|
+
}
|
|
10
|
+
/** The words of a text in text order, each in comparison form with its span in the text. */
|
|
11
|
+
export declare function tokenize(text: string, pack: LanguagePack): Token[];
|
|
12
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/scan/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAQ,MAAM,mBAAmB,CAAC;AAG5D,8FAA8F;AAC9F,MAAM,WAAW,KAAK;IACpB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;CACb;AAyBD,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,KAAK,EAAE,CAWlE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { singularize } from "../text/singular.js";
|
|
2
|
+
const notLetterOrDigit = /[^\p{L}\p{M}\p{N}]/gu;
|
|
3
|
+
// The segmenter cuts a hyphenated compound into its parts ("mot-clé" gives the tokens
|
|
4
|
+
// "mot" and "clé", so that both "mot" and "mot clé" match there) but keeps
|
|
5
|
+
// an apostrophe inside its word ("l'écran"): the parts an apostrophe binds are cut here the same
|
|
6
|
+
// way, with the apostrophe characters the pack unifies.
|
|
7
|
+
function pieces(segment, pack) {
|
|
8
|
+
const result = [];
|
|
9
|
+
let start = 0;
|
|
10
|
+
for (const match of segment.text.matchAll(notLetterOrDigit)) {
|
|
11
|
+
if (pack.normalize(match[0]) !== "'")
|
|
12
|
+
continue;
|
|
13
|
+
result.push({ text: segment.text.slice(start, match.index), start: segment.index + start });
|
|
14
|
+
start = match.index + 1;
|
|
15
|
+
}
|
|
16
|
+
result.push({ text: segment.text.slice(start), start: segment.index + start });
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
/** The words of a text in text order, each in comparison form with its span in the text. */
|
|
20
|
+
export function tokenize(text, pack) {
|
|
21
|
+
const tokens = [];
|
|
22
|
+
for (const segment of pack.segment(text)) {
|
|
23
|
+
if (!segment.isWordLike)
|
|
24
|
+
continue;
|
|
25
|
+
for (const piece of pieces(segment, pack)) {
|
|
26
|
+
const word = singularize(pack.normalize(piece.text), pack.plural);
|
|
27
|
+
if (word === "")
|
|
28
|
+
continue;
|
|
29
|
+
tokens.push({ word, start: piece.start, end: piece.start + piece.text.length });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return tokens;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/scan/tokens.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAgBlD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEhD,sFAAsF;AACtF,2EAA2E;AAC3E,iGAAiG;AACjG,wDAAwD;AACxD,SAAS,MAAM,CAAC,OAAa,EAAE,IAAkB;IAC/C,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5D,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,SAAS;QAC/C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC;QAC5F,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAkB;IACvD,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,OAAO,CAAC,UAAU;YAAE,SAAS;QAClC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAClE,IAAI,IAAI,KAAK,EAAE;gBAAE,SAAS;YAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|