yomitan-core 1.0.2 → 1.2.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/README.md +8 -1
- package/dist/anki-connect-B2UXtiUS.js +6 -0
- package/dist/anki-connect-BXvjYYm0.cjs +6 -0
- package/dist/{anki-connect-B_YpChhu.cjs → anki-connect-DGx7Gr0H.cjs} +3 -3
- package/dist/{anki-connect-B_YpChhu.cjs.map → anki-connect-DGx7Gr0H.cjs.map} +1 -1
- package/dist/{anki-connect-697E04LF.js → anki-connect-Dau8nAXZ.js} +3 -3
- package/dist/{anki-connect-697E04LF.js.map → anki-connect-Dau8nAXZ.js.map} +1 -1
- package/dist/anki.cjs +6 -6
- package/dist/anki.d.cts.map +1 -1
- package/dist/anki.d.ts +2 -2
- package/dist/anki.d.ts.map +1 -1
- package/dist/anki.js +6 -6
- package/dist/{audio-ZuaDkaUZ.d.ts → audio-DN-FhZ5k.d.ts} +1 -1
- package/dist/{audio-ZuaDkaUZ.d.ts.map → audio-DN-FhZ5k.d.ts.map} +1 -1
- package/dist/{audio-url-generator-CgTMaTM2.js → audio-url-generator-C7mkxYeq.js} +2 -2
- package/dist/{audio-url-generator-CfXaxZ47.cjs → audio-url-generator-CZECihX-.cjs} +2 -2
- package/dist/{audio-url-generator-C23AXVj1.cjs → audio-url-generator-CorFRyBU.cjs} +2 -2
- package/dist/{audio-url-generator-C23AXVj1.cjs.map → audio-url-generator-CorFRyBU.cjs.map} +1 -1
- package/dist/{audio-url-generator-DykdzZJu.js → audio-url-generator-_6Qk0i3T.js} +2 -2
- package/dist/{audio-url-generator-DykdzZJu.js.map → audio-url-generator-_6Qk0i3T.js.map} +1 -1
- package/dist/audio.cjs +2 -2
- package/dist/audio.d.ts +2 -2
- package/dist/audio.js +2 -2
- package/dist/{batch-processor-CXZZhx5t.js → batch-processor-BR-gB3H3.js} +1 -1
- package/dist/{batch-processor-CXZZhx5t.js.map → batch-processor-BR-gB3H3.js.map} +1 -1
- package/dist/batch-processor-CSF1acTw.cjs +3 -0
- package/dist/{batch-processor-C8pSzqRE.cjs → batch-processor-DFqM_L-_.cjs} +1 -1
- package/dist/{batch-processor-C8pSzqRE.cjs.map → batch-processor-DFqM_L-_.cjs.map} +1 -1
- package/dist/batch-processor-Quo9jUyf.js +3 -0
- package/dist/{core-C_0QjRBI.d.ts → core-CRjMTF7c.d.ts} +1 -1
- package/dist/{core-C_0QjRBI.d.ts.map → core-CRjMTF7c.d.ts.map} +1 -1
- package/dist/database.d.ts +3 -3
- package/dist/debug-I2dyAd2e.cjs +51 -0
- package/dist/debug-I2dyAd2e.cjs.map +1 -0
- package/dist/debug-hF63AFkq.js +38 -0
- package/dist/debug-hF63AFkq.js.map +1 -0
- package/dist/{dictionary-CfzEFbXZ.d.ts → dictionary-DdxghSJP.d.ts} +1 -1
- package/dist/{dictionary-CfzEFbXZ.d.ts.map → dictionary-DdxghSJP.d.ts.map} +1 -1
- package/dist/{dictionary-data-util-BY_72f3u.cjs → dictionary-data-util-DmLJMtGc.cjs} +1 -1
- package/dist/{dictionary-data-util-BY_72f3u.cjs.map → dictionary-data-util-DmLJMtGc.cjs.map} +1 -1
- package/dist/{dictionary-data-util-CKDT7n2t.js → dictionary-data-util-gUUD9ElK.js} +1 -1
- package/dist/{dictionary-data-util-CKDT7n2t.js.map → dictionary-data-util-gUUD9ElK.js.map} +1 -1
- package/dist/{dictionary-database-BRAqOF_M.d.ts → dictionary-database-D0lvK2n3.d.ts} +2 -2
- package/dist/{dictionary-database-BRAqOF_M.d.ts.map → dictionary-database-D0lvK2n3.d.ts.map} +1 -1
- package/dist/{dictionary-importer-C7mNL5CJ.js → dictionary-importer-BPlLeF5R.js} +5 -5
- package/dist/{dictionary-importer-C7mNL5CJ.js.map → dictionary-importer-BPlLeF5R.js.map} +1 -1
- package/dist/{dictionary-importer-CbMwsqEX.cjs → dictionary-importer-BvgP1Fs9.cjs} +5 -5
- package/dist/{dictionary-importer-CbMwsqEX.cjs.map → dictionary-importer-BvgP1Fs9.cjs.map} +1 -1
- package/dist/{dictionary-importer-C-AlWsIN.d.ts → dictionary-importer-CDbCAbwG.d.ts} +2 -2
- package/dist/{dictionary-importer-C-AlWsIN.d.ts.map → dictionary-importer-CDbCAbwG.d.ts.map} +1 -1
- package/dist/dictionary-importer-DEMVqTjR.js +8 -0
- package/dist/dictionary-importer-Luz6usUQ.cjs +8 -0
- package/dist/{dictionary-update-checker-DE2ymYcs.js → dictionary-update-checker-CLbtQ48X.js} +2 -2
- package/dist/{dictionary-update-checker-DE2ymYcs.js.map → dictionary-update-checker-CLbtQ48X.js.map} +1 -1
- package/dist/dictionary-update-checker-CsS7MxK_.js +4 -0
- package/dist/{dictionary-update-checker-CU3q0aoP.cjs → dictionary-update-checker-DgjPsURI.cjs} +2 -2
- package/dist/{dictionary-update-checker-xIwF77-R.cjs → dictionary-update-checker-DmiIUoKw.cjs} +2 -2
- package/dist/{dictionary-update-checker-xIwF77-R.cjs.map → dictionary-update-checker-DmiIUoKw.cjs.map} +1 -1
- package/dist/{display-generator-vjbG05fn.js → display-generator-BAEJDrHT.js} +9 -7
- package/dist/display-generator-BAEJDrHT.js.map +1 -0
- package/dist/display-generator-D5fkD0JI.cjs +10 -0
- package/dist/display-generator-DZLMaVuP.js +10 -0
- package/dist/{display-generator-BhV_MOD1.cjs → display-generator-D_0chBzj.cjs} +9 -7
- package/dist/display-generator-D_0chBzj.cjs.map +1 -0
- package/dist/{errors-ZI2FcGUl.cjs → errors-0pIJI6N2.cjs} +1 -1
- package/dist/{errors-ZI2FcGUl.cjs.map → errors-0pIJI6N2.cjs.map} +1 -1
- package/dist/{errors-D2JfYV-C.js → errors-DvKlMRXC.js} +1 -1
- package/dist/{errors-D2JfYV-C.js.map → errors-DvKlMRXC.js.map} +1 -1
- package/dist/frequency-ranking-BXjfhhUQ.js +3 -0
- package/dist/{frequency-ranking-DmNIoxut.cjs → frequency-ranking-Cx1kkIrw.cjs} +1 -1
- package/dist/{frequency-ranking-DzbdZghE.js → frequency-ranking-DEJMTMdg.js} +1 -1
- package/dist/{frequency-ranking-DzbdZghE.js.map → frequency-ranking-DEJMTMdg.js.map} +1 -1
- package/dist/{frequency-ranking-Dm6xCMkY.cjs → frequency-ranking-DVYxTXN-.cjs} +1 -1
- package/dist/{frequency-ranking-Dm6xCMkY.cjs.map → frequency-ranking-DVYxTXN-.cjs.map} +1 -1
- package/dist/furigana-MU_j_m5i.cjs +5 -0
- package/dist/{furigana-BnY7-dJ-.cjs → furigana-SZuBaUyl.cjs} +2 -2
- package/dist/{furigana-BnY7-dJ-.cjs.map → furigana-SZuBaUyl.cjs.map} +1 -1
- package/dist/furigana-ZCs0Xs0b.js +5 -0
- package/dist/{furigana-CI6LUA-6.js → furigana-h18vaQ7a.js} +2 -2
- package/dist/{furigana-CI6LUA-6.js.map → furigana-h18vaQ7a.js.map} +1 -1
- package/dist/import.cjs +6 -6
- package/dist/import.d.ts +3 -3
- package/dist/import.js +6 -6
- package/dist/index.cjs +44 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -18
- package/dist/index.js.map +1 -1
- package/dist/{japanese-BVV1Ctk-.cjs → japanese-C83fmEfb.cjs} +1 -1
- package/dist/{japanese-BVV1Ctk-.cjs.map → japanese-C83fmEfb.cjs.map} +1 -1
- package/dist/{japanese-DhmmW35J.js → japanese-OKEGf_i4.js} +2 -2
- package/dist/{japanese-DhmmW35J.js.map → japanese-OKEGf_i4.js.map} +1 -1
- package/dist/{json-CyQBn52D.cjs → json-Cdj6aIOQ.cjs} +1 -1
- package/dist/{json-CyQBn52D.cjs.map → json-Cdj6aIOQ.cjs.map} +1 -1
- package/dist/{json-DTq_IXXF.js → json-rIgOFqVY.js} +1 -1
- package/dist/{json-DTq_IXXF.js.map → json-rIgOFqVY.js.map} +1 -1
- package/dist/{language-DFFp_7nc.d.ts → language-DMwuc2-C.d.ts} +1 -1
- package/dist/{language-DFFp_7nc.d.ts.map → language-DMwuc2-C.d.ts.map} +1 -1
- package/dist/language.cjs +12 -12
- package/dist/language.d.cts +41 -3
- package/dist/language.d.cts.map +1 -1
- package/dist/language.d.ts +44 -6
- package/dist/language.d.ts.map +1 -1
- package/dist/language.js +5 -5
- package/dist/lookup.cjs +7 -6
- package/dist/lookup.d.cts +5 -78
- package/dist/lookup.d.cts.map +1 -1
- package/dist/lookup.d.ts +8 -81
- package/dist/lookup.d.ts.map +1 -1
- package/dist/lookup.js +7 -6
- package/dist/{media-loader-CMEuD0fn.js → media-loader-C1WZVVaA.js} +1 -1
- package/dist/{media-loader-CMEuD0fn.js.map → media-loader-C1WZVVaA.js.map} +1 -1
- package/dist/media-loader-CWNb22gc.cjs +3 -0
- package/dist/media-loader-CrQnDnSp.js +3 -0
- package/dist/{media-loader-DS2ZXpSI.cjs → media-loader-RE32nGs3.cjs} +1 -1
- package/dist/{media-loader-DS2ZXpSI.cjs.map → media-loader-RE32nGs3.cjs.map} +1 -1
- package/dist/{multi-language-transformer-CjLO0S8V.d.ts → multi-language-transformer-CFu0CVWY.d.ts} +3 -3
- package/dist/{multi-language-transformer-CjLO0S8V.d.ts.map → multi-language-transformer-CFu0CVWY.d.ts.map} +1 -1
- package/dist/multi-language-transformer-Dk8S4W6j.js +12503 -0
- package/dist/multi-language-transformer-Dk8S4W6j.js.map +1 -0
- package/dist/{multi-language-transformer-B2133Zg_.cjs → multi-language-transformer-N66Mmb2E.cjs} +9614 -5
- package/dist/multi-language-transformer-N66Mmb2E.cjs.map +1 -0
- package/dist/{pronunciation-generator-cNtAQi5s.js → pronunciation-generator-BXcwS3By.js} +2 -2
- package/dist/{pronunciation-generator-cNtAQi5s.js.map → pronunciation-generator-BXcwS3By.js.map} +1 -1
- package/dist/{pronunciation-generator-CPwnr43z.cjs → pronunciation-generator-CP1kjYje.cjs} +1 -1
- package/dist/{pronunciation-generator-B8y__W7I.cjs → pronunciation-generator-CeC3LFPU.cjs} +1 -1
- package/dist/{pronunciation-generator-B8y__W7I.cjs.map → pronunciation-generator-CeC3LFPU.cjs.map} +1 -1
- package/dist/{pronunciation-generator-BvTbUlOy.js → pronunciation-generator-I1xpdJu0.js} +1 -1
- package/dist/render.cjs +96 -8
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +33 -3
- package/dist/render.d.cts.map +1 -1
- package/dist/render.d.ts +35 -5
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +95 -9
- package/dist/render.js.map +1 -1
- package/dist/{sentence-parser-DUQJ3cTT.cjs → sentence-parser-8i1Y_OGS.cjs} +74 -46
- package/dist/sentence-parser-8i1Y_OGS.cjs.map +1 -0
- package/dist/{translator-TiTDVbmS.d.cts → sentence-parser-BQpu2oSA.d.cts} +80 -4
- package/dist/sentence-parser-BQpu2oSA.d.cts.map +1 -0
- package/dist/{sentence-parser-sx7y6Oyh.js → sentence-parser-CXDs4R_c.js} +74 -46
- package/dist/sentence-parser-CXDs4R_c.js.map +1 -0
- package/dist/{translator-CYvGmODX.d.ts → sentence-parser-CmsHUc_p.d.ts} +81 -5
- package/dist/sentence-parser-CmsHUc_p.d.ts.map +1 -0
- package/dist/sentence-parser-DQccqSpq.cjs +6 -0
- package/dist/sentence-parser-DsleseSI.js +6 -0
- package/dist/structured-content-generator-BhEkwa0T.js +4 -0
- package/dist/{structured-content-generator-C5Hhaurz.cjs → structured-content-generator-CokLKgrb.cjs} +2 -2
- package/dist/{structured-content-generator-C5Hhaurz.cjs.map → structured-content-generator-CokLKgrb.cjs.map} +1 -1
- package/dist/{structured-content-generator-D48bLhPA.cjs → structured-content-generator-_z5dJECG.cjs} +2 -2
- package/dist/{structured-content-generator-aJieXElq.js → structured-content-generator-gnjtdBcC.js} +2 -2
- package/dist/{structured-content-generator-aJieXElq.js.map → structured-content-generator-gnjtdBcC.js.map} +1 -1
- package/dist/{text-utilities-CFDC9Bkr.cjs → text-utilities-CTsN1Bq8.cjs} +1 -1
- package/dist/{text-utilities-CFDC9Bkr.cjs.map → text-utilities-CTsN1Bq8.cjs.map} +1 -1
- package/dist/{text-utilities-D5eno9GK.js → text-utilities-DjLdStjv.js} +1 -1
- package/dist/{text-utilities-D5eno9GK.js.map → text-utilities-DjLdStjv.js.map} +1 -1
- package/dist/translator-BGI37WRC.js +7 -0
- package/dist/translator-Cd1ECBM1.cjs +7 -0
- package/dist/{translator-B1uTusjx.cjs → translator-ClzybVmi.cjs} +2 -2
- package/dist/{translator-B1uTusjx.cjs.map → translator-ClzybVmi.cjs.map} +1 -1
- package/dist/{translator-CrDE1UJW.js → translator-jFVzLjEs.js} +2 -2
- package/dist/{translator-CrDE1UJW.js.map → translator-jFVzLjEs.js.map} +1 -1
- package/package.json +1 -1
- package/dist/anki-connect-D-02BbHi.cjs +0 -6
- package/dist/anki-connect-Vn-cnxs8.js +0 -6
- package/dist/batch-processor-DItpL6Pp.js +0 -3
- package/dist/batch-processor-fiLvFS2_.cjs +0 -3
- package/dist/dictionary-importer-B9M3SD2y.js +0 -8
- package/dist/dictionary-importer-DnR8Bsai.cjs +0 -8
- package/dist/dictionary-update-checker-Cnjz4tQD.js +0 -4
- package/dist/display-generator-BhV_MOD1.cjs.map +0 -1
- package/dist/display-generator-Cn4vCiSI.js +0 -10
- package/dist/display-generator-G2e03o0R.cjs +0 -10
- package/dist/display-generator-vjbG05fn.js.map +0 -1
- package/dist/frequency-ranking-B-kvnxro.js +0 -3
- package/dist/furigana-cpPhFnBv.cjs +0 -5
- package/dist/furigana-kZjqnnFC.js +0 -5
- package/dist/japanese-DdsS8rT1.d.ts +0 -41
- package/dist/japanese-DdsS8rT1.d.ts.map +0 -1
- package/dist/japanese-DgmP3i76.d.cts +0 -41
- package/dist/japanese-DgmP3i76.d.cts.map +0 -1
- package/dist/media-loader-BBTESZXn.cjs +0 -3
- package/dist/media-loader-CNKoAjQs.js +0 -3
- package/dist/multi-language-transformer-B2133Zg_.cjs.map +0 -1
- package/dist/multi-language-transformer-BkwMTV2g.js +0 -2893
- package/dist/multi-language-transformer-BkwMTV2g.js.map +0 -1
- package/dist/sentence-parser-B-gM8mKk.js +0 -5
- package/dist/sentence-parser-Cp3cm_QU.cjs +0 -5
- package/dist/sentence-parser-DUQJ3cTT.cjs.map +0 -1
- package/dist/sentence-parser-sx7y6Oyh.js.map +0 -1
- package/dist/structured-content-generator-Dww8IT7Y.js +0 -4
- package/dist/translator-BiTHo9Nm.js +0 -7
- package/dist/translator-CYvGmODX.d.ts.map +0 -1
- package/dist/translator-TiTDVbmS.d.cts.map +0 -1
- package/dist/translator-Y7Haj_qJ.cjs +0 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const require_japanese = require('./japanese-
|
|
2
|
+
const require_japanese = require('./japanese-C83fmEfb.cjs');
|
|
3
|
+
const require_debug = require('./debug-I2dyAd2e.cjs');
|
|
3
4
|
|
|
4
5
|
//#region src/lookup/sentence-parser.ts
|
|
5
6
|
/**
|
|
@@ -17,22 +18,48 @@ var SentenceParser = class {
|
|
|
17
18
|
async parseText(text, language, options) {
|
|
18
19
|
const scanLength = options.scanLength ?? options.maxLength ?? 20;
|
|
19
20
|
const enabledDictionaryMap = this._buildFindTermDictionaryMap(options.enabledDictionaryMap);
|
|
21
|
+
require_debug.debugYomitanCore("sentence-parser", "parseText:start", {
|
|
22
|
+
textLength: text.length,
|
|
23
|
+
textPreview: text.slice(0, 120),
|
|
24
|
+
textCodePoints: require_debug.codePointPreview(text),
|
|
25
|
+
lineCount: text.split("\n").length,
|
|
26
|
+
scanLength,
|
|
27
|
+
language,
|
|
28
|
+
enabledDictionaryCount: enabledDictionaryMap.size,
|
|
29
|
+
enabledDictionaryNames: [...enabledDictionaryMap.keys()],
|
|
30
|
+
searchResolution: options.searchResolution ?? "letter",
|
|
31
|
+
removeNonJapaneseCharacters: options.removeNonJapaneseCharacters,
|
|
32
|
+
deinflect: options.deinflect
|
|
33
|
+
});
|
|
20
34
|
const lines = text.split("\n");
|
|
21
35
|
const results = [];
|
|
22
|
-
for (const line of lines) {
|
|
23
|
-
const segments = await this._parseLine(line, language, enabledDictionaryMap, scanLength, options);
|
|
24
|
-
results.push(
|
|
36
|
+
for (const [lineIndex, line] of lines.entries()) {
|
|
37
|
+
const segments = await this._parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options);
|
|
38
|
+
results.push(segments);
|
|
25
39
|
}
|
|
40
|
+
require_debug.debugYomitanCore("sentence-parser", "parseText:complete", {
|
|
41
|
+
lineCount: results.length,
|
|
42
|
+
lineSegmentCounts: results.map((line) => line.length),
|
|
43
|
+
totalSegments: results.reduce((count, line) => count + line.length, 0),
|
|
44
|
+
matchedSegments: results.reduce((count, line) => count + line.filter((segment) => Array.isArray(segment.headwords)).length, 0)
|
|
45
|
+
});
|
|
26
46
|
return results;
|
|
27
47
|
}
|
|
28
48
|
/**
|
|
29
49
|
* Parses a single line using extension-equivalent scanning parser semantics.
|
|
30
50
|
*/
|
|
31
|
-
async _parseLine(line, language, enabledDictionaryMap, scanLength, options) {
|
|
51
|
+
async _parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options) {
|
|
32
52
|
const findTermsOptions = this._createFindTermsOptions(language, enabledDictionaryMap, options);
|
|
33
53
|
const cache = new Map();
|
|
34
54
|
const result = [];
|
|
35
55
|
let previousUngroupedSegment = null;
|
|
56
|
+
let lookupCount = 0;
|
|
57
|
+
let cacheHits = 0;
|
|
58
|
+
let cacheMisses = 0;
|
|
59
|
+
let matchedSegmentCount = 0;
|
|
60
|
+
let unmatchedSegmentCount = 0;
|
|
61
|
+
let emptyEntryLookups = 0;
|
|
62
|
+
const lookupMissSamples = [];
|
|
36
63
|
let i = 0;
|
|
37
64
|
const ii = line.length;
|
|
38
65
|
while (i < ii) {
|
|
@@ -41,8 +68,18 @@ var SentenceParser = class {
|
|
|
41
68
|
const substring = line.substring(i, i + scanLength);
|
|
42
69
|
let cached = cache.get(substring);
|
|
43
70
|
if (!cached) {
|
|
71
|
+
cacheMisses += 1;
|
|
44
72
|
const { dictionaryEntries, originalTextLength: originalTextLength$1 } = await this._translator.findTerms("simple", substring, findTermsOptions);
|
|
73
|
+
lookupCount += 1;
|
|
45
74
|
let segments$1 = [];
|
|
75
|
+
if (dictionaryEntries.length === 0) {
|
|
76
|
+
emptyEntryLookups += 1;
|
|
77
|
+
if (lookupMissSamples.length < 5) lookupMissSamples.push({
|
|
78
|
+
substring: substring.slice(0, 40),
|
|
79
|
+
codePoints: require_debug.codePointPreview(substring, 20),
|
|
80
|
+
originalTextLength: originalTextLength$1
|
|
81
|
+
});
|
|
82
|
+
}
|
|
46
83
|
if (dictionaryEntries.length > 0 && originalTextLength$1 > 0 && (originalTextLength$1 !== character.length || require_japanese.isCodePointJapanese(codePoint))) {
|
|
47
84
|
const source = substring.substring(0, originalTextLength$1);
|
|
48
85
|
segments$1 = this._createMatchedSegments(source, dictionaryEntries);
|
|
@@ -52,36 +89,38 @@ var SentenceParser = class {
|
|
|
52
89
|
segments: segments$1
|
|
53
90
|
};
|
|
54
91
|
cache.set(substring, cached);
|
|
55
|
-
}
|
|
92
|
+
} else cacheHits += 1;
|
|
56
93
|
const { originalTextLength, segments } = cached;
|
|
57
94
|
if (segments.length > 0) {
|
|
58
95
|
previousUngroupedSegment = null;
|
|
59
96
|
result.push(...segments);
|
|
97
|
+
matchedSegmentCount += segments.length;
|
|
60
98
|
i += Math.max(1, originalTextLength);
|
|
61
99
|
} else {
|
|
62
100
|
if (previousUngroupedSegment === null) {
|
|
63
101
|
previousUngroupedSegment = {
|
|
64
102
|
text: character,
|
|
65
|
-
reading: ""
|
|
66
|
-
term: character,
|
|
67
|
-
entries: [],
|
|
68
|
-
furigana: [{
|
|
69
|
-
text: character,
|
|
70
|
-
reading: ""
|
|
71
|
-
}]
|
|
103
|
+
reading: ""
|
|
72
104
|
};
|
|
73
105
|
result.push(previousUngroupedSegment);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
previousUngroupedSegment.term = previousUngroupedSegment.text;
|
|
77
|
-
previousUngroupedSegment.furigana = [{
|
|
78
|
-
text: previousUngroupedSegment.text,
|
|
79
|
-
reading: ""
|
|
80
|
-
}];
|
|
81
|
-
}
|
|
106
|
+
unmatchedSegmentCount += 1;
|
|
107
|
+
} else previousUngroupedSegment.text += character;
|
|
82
108
|
i += character.length;
|
|
83
109
|
}
|
|
84
110
|
}
|
|
111
|
+
require_debug.debugYomitanCore("sentence-parser", "parseLine:complete", {
|
|
112
|
+
lineIndex,
|
|
113
|
+
lineLength: line.length,
|
|
114
|
+
linePreview: line.slice(0, 120),
|
|
115
|
+
lookupCount,
|
|
116
|
+
cacheHits,
|
|
117
|
+
cacheMisses,
|
|
118
|
+
emptyEntryLookups,
|
|
119
|
+
matchedSegmentCount,
|
|
120
|
+
unmatchedSegmentCount,
|
|
121
|
+
finalSegmentCount: result.length,
|
|
122
|
+
missSamples: lookupMissSamples
|
|
123
|
+
});
|
|
85
124
|
return result;
|
|
86
125
|
}
|
|
87
126
|
_createMatchedSegments(source, dictionaryEntries) {
|
|
@@ -89,35 +128,24 @@ var SentenceParser = class {
|
|
|
89
128
|
const firstEntry = dictionaryEntries[0];
|
|
90
129
|
const firstHeadword = firstEntry.headwords[0];
|
|
91
130
|
if (!firstHeadword) return [];
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return [{
|
|
97
|
-
text: source,
|
|
98
|
-
reading,
|
|
99
|
-
term,
|
|
100
|
-
entries: filteredEntries,
|
|
101
|
-
furigana
|
|
102
|
-
}];
|
|
103
|
-
}
|
|
104
|
-
_trimEntriesForSource(dictionaryEntries, source) {
|
|
105
|
-
const trimmed = [];
|
|
106
|
-
for (const entry of dictionaryEntries) {
|
|
107
|
-
const headwords = [];
|
|
108
|
-
for (const headword of entry.headwords) {
|
|
131
|
+
const trimmedHeadwords = [];
|
|
132
|
+
for (const dictionaryEntry of dictionaryEntries) {
|
|
133
|
+
const validHeadwords = [];
|
|
134
|
+
for (const headword of dictionaryEntry.headwords) {
|
|
109
135
|
const validSources = headword.sources.filter((src) => src.originalText === source && src.isPrimary && src.matchType === "exact");
|
|
110
|
-
if (validSources.length > 0)
|
|
111
|
-
|
|
136
|
+
if (validSources.length > 0) validHeadwords.push({
|
|
137
|
+
term: headword.term,
|
|
138
|
+
reading: headword.reading,
|
|
112
139
|
sources: validSources
|
|
113
140
|
});
|
|
114
141
|
}
|
|
115
|
-
if (
|
|
116
|
-
...entry,
|
|
117
|
-
headwords
|
|
118
|
-
});
|
|
142
|
+
if (validHeadwords.length > 0) trimmedHeadwords.push(validHeadwords);
|
|
119
143
|
}
|
|
120
|
-
return
|
|
144
|
+
return [{
|
|
145
|
+
text: source,
|
|
146
|
+
reading: firstHeadword.reading ?? "",
|
|
147
|
+
...trimmedHeadwords.length > 0 ? { headwords: trimmedHeadwords } : {}
|
|
148
|
+
}];
|
|
121
149
|
}
|
|
122
150
|
/**
|
|
123
151
|
* Builds a FindTermDictionary map from the input format.
|
|
@@ -161,4 +189,4 @@ Object.defineProperty(exports, 'SentenceParser', {
|
|
|
161
189
|
return SentenceParser;
|
|
162
190
|
}
|
|
163
191
|
});
|
|
164
|
-
//# sourceMappingURL=sentence-parser-
|
|
192
|
+
//# sourceMappingURL=sentence-parser-8i1Y_OGS.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-parser-8i1Y_OGS.cjs","names":["translator: Translator","text: string","language: string","options: SentenceParserOptions","results: ParseTextLine[]","line: string","lineIndex: number","enabledDictionaryMap: Map<string, FindTermDictionary>","scanLength: number","result: ParseTextSegment[]","previousUngroupedSegment: ParseTextSegment | null","lookupMissSamples: Array<{ substring: string; codePoints: string[]; originalTextLength: number }>","segments: ParseTextSegment[]","originalTextLength","source: string","dictionaryEntries: TermDictionaryEntry[]","trimmedHeadwords: ParseTextHeadword[][]","validHeadwords: ParseTextHeadword[]","inputMap: Map<string, SentenceParserDictionaryInput>"],"sources":["../src/lookup/sentence-parser.ts"],"sourcesContent":["/*\n * Copyright (C) 2023-2025 Yomitan Authors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { isCodePointJapanese } from '../language/ja/japanese';\nimport type { TermDictionaryEntry } from '../types/dictionary';\nimport type { ParseTextHeadword, ParseTextLine, ParseTextSegment } from '../types/parse';\nimport type { FindTermDictionary, FindTermsTextReplacements, SearchResolution } from '../types/translation';\nimport { codePointPreview, debugYomitanCore } from '../util/debug';\nimport type { Translator } from './translator';\nexport type { ParseTextHeadword, ParseTextLine, ParseTextResultItem, ParseTextSegment } from '../types/parse';\n\nexport interface SentenceParserDictionaryInput {\n index: number;\n priority?: number;\n alias?: string;\n allowSecondarySearches?: boolean;\n partsOfSpeechFilter?: boolean;\n useDeinflections?: boolean;\n}\n\nexport interface SentenceParserOptions {\n /** The enabled dictionary map for lookups. */\n enabledDictionaryMap: Map<string, SentenceParserDictionaryInput>;\n /** Window size used by scanning parser. Defaults to 20. */\n scanLength?: number;\n /** Backward compatibility alias for scanLength. */\n maxLength?: number;\n /** Search resolution for translator. */\n searchResolution?: SearchResolution;\n /** Whether to remove non-Japanese characters in lookups. */\n removeNonJapaneseCharacters?: boolean;\n /** Whether to apply deinflection. */\n deinflect?: boolean;\n /** Text replacements forwarded to translator. */\n textReplacements?: FindTermsTextReplacements;\n}\n\ninterface ParseScanCacheEntry {\n originalTextLength: number;\n segments: ParseTextSegment[];\n}\n\n/**\n * Parses text into segments with dictionary lookups using the same\n * scanning-parser model as Yomitan extension (non-MeCab path).\n */\nexport class SentenceParser {\n private _translator: Translator;\n\n constructor(translator: Translator) {\n this._translator = translator;\n }\n\n /**\n * Parses the given text into lines of parsed segments.\n */\n async parseText(text: string, language: string, options: SentenceParserOptions): Promise<ParseTextLine[]> {\n const scanLength = options.scanLength ?? options.maxLength ?? 20;\n const enabledDictionaryMap = this._buildFindTermDictionaryMap(options.enabledDictionaryMap);\n debugYomitanCore('sentence-parser', 'parseText:start', {\n textLength: text.length,\n textPreview: text.slice(0, 120),\n textCodePoints: codePointPreview(text),\n lineCount: text.split('\\n').length,\n scanLength,\n language,\n enabledDictionaryCount: enabledDictionaryMap.size,\n enabledDictionaryNames: [...enabledDictionaryMap.keys()],\n searchResolution: options.searchResolution ?? 'letter',\n removeNonJapaneseCharacters: options.removeNonJapaneseCharacters,\n deinflect: options.deinflect,\n });\n\n const lines = text.split('\\n');\n const results: ParseTextLine[] = [];\n\n for (const [lineIndex, line] of lines.entries()) {\n const segments = await this._parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options);\n results.push(segments);\n }\n\n debugYomitanCore('sentence-parser', 'parseText:complete', {\n lineCount: results.length,\n lineSegmentCounts: results.map((line) => line.length),\n totalSegments: results.reduce((count, line) => count + line.length, 0),\n matchedSegments: results.reduce(\n (count, line) => count + line.filter((segment) => Array.isArray(segment.headwords)).length,\n 0,\n ),\n });\n\n return results;\n }\n\n /**\n * Parses a single line using extension-equivalent scanning parser semantics.\n */\n private async _parseLine(\n line: string,\n lineIndex: number,\n language: string,\n enabledDictionaryMap: Map<string, FindTermDictionary>,\n scanLength: number,\n options: SentenceParserOptions,\n ): Promise<ParseTextLine> {\n const findTermsOptions = this._createFindTermsOptions(language, enabledDictionaryMap, options);\n const cache = new Map<string, ParseScanCacheEntry>();\n\n const result: ParseTextSegment[] = [];\n let previousUngroupedSegment: ParseTextSegment | null = null;\n let lookupCount = 0;\n let cacheHits = 0;\n let cacheMisses = 0;\n let matchedSegmentCount = 0;\n let unmatchedSegmentCount = 0;\n let emptyEntryLookups = 0;\n const lookupMissSamples: Array<{ substring: string; codePoints: string[]; originalTextLength: number }> = [];\n\n let i = 0;\n const ii = line.length;\n while (i < ii) {\n const codePoint = line.codePointAt(i) as number;\n const character = String.fromCodePoint(codePoint);\n const substring = line.substring(i, i + scanLength);\n\n let cached = cache.get(substring);\n if (!cached) {\n cacheMisses += 1;\n const { dictionaryEntries, originalTextLength } = await this._translator.findTerms(\n 'simple',\n substring,\n findTermsOptions,\n );\n lookupCount += 1;\n\n let segments: ParseTextSegment[] = [];\n if (dictionaryEntries.length === 0) {\n emptyEntryLookups += 1;\n if (lookupMissSamples.length < 5) {\n lookupMissSamples.push({\n substring: substring.slice(0, 40),\n codePoints: codePointPreview(substring, 20),\n originalTextLength,\n });\n }\n }\n\n if (\n dictionaryEntries.length > 0 &&\n originalTextLength > 0 &&\n (originalTextLength !== character.length || isCodePointJapanese(codePoint))\n ) {\n const source = substring.substring(0, originalTextLength);\n segments = this._createMatchedSegments(source, dictionaryEntries);\n }\n\n cached = {\n originalTextLength,\n segments,\n };\n cache.set(substring, cached);\n } else {\n cacheHits += 1;\n }\n\n const { originalTextLength, segments } = cached;\n if (segments.length > 0) {\n previousUngroupedSegment = null;\n result.push(...segments);\n matchedSegmentCount += segments.length;\n i += Math.max(1, originalTextLength);\n } else {\n if (previousUngroupedSegment === null) {\n previousUngroupedSegment = {\n text: character,\n reading: ''\n };\n result.push(previousUngroupedSegment);\n unmatchedSegmentCount += 1;\n } else {\n previousUngroupedSegment.text += character;\n }\n i += character.length;\n }\n }\n\n debugYomitanCore('sentence-parser', 'parseLine:complete', {\n lineIndex,\n lineLength: line.length,\n linePreview: line.slice(0, 120),\n lookupCount,\n cacheHits,\n cacheMisses,\n emptyEntryLookups,\n matchedSegmentCount,\n unmatchedSegmentCount,\n finalSegmentCount: result.length,\n missSamples: lookupMissSamples,\n });\n\n return result;\n }\n\n private _createMatchedSegments(source: string, dictionaryEntries: TermDictionaryEntry[]): ParseTextSegment[] {\n if (dictionaryEntries.length === 0) {\n return [];\n }\n\n const firstEntry = dictionaryEntries[0];\n const firstHeadword = firstEntry.headwords[0];\n if (!firstHeadword) {\n return [];\n }\n\n const trimmedHeadwords: ParseTextHeadword[][] = [];\n for (const dictionaryEntry of dictionaryEntries) {\n const validHeadwords: ParseTextHeadword[] = [];\n for (const headword of dictionaryEntry.headwords) {\n const validSources = headword.sources.filter(\n (src) => src.originalText === source && src.isPrimary && src.matchType === 'exact',\n );\n\n if (validSources.length > 0) {\n validHeadwords.push({\n term: headword.term,\n reading: headword.reading,\n sources: validSources,\n });\n }\n }\n if (validHeadwords.length > 0) {\n trimmedHeadwords.push(validHeadwords);\n }\n }\n\n return [\n {\n text: source,\n reading: firstHeadword.reading ?? '',\n ...(trimmedHeadwords.length > 0 ? { headwords: trimmedHeadwords } : {}),\n },\n ];\n }\n\n /**\n * Builds a FindTermDictionary map from the input format.\n */\n private _buildFindTermDictionaryMap(\n inputMap: Map<string, SentenceParserDictionaryInput>,\n ): Map<string, FindTermDictionary> {\n const result = new Map<string, FindTermDictionary>();\n\n for (const [name, config] of inputMap.entries()) {\n result.set(name, {\n index: config.index,\n alias: config.alias ?? name,\n allowSecondarySearches: config.allowSecondarySearches ?? false,\n partsOfSpeechFilter: config.partsOfSpeechFilter ?? true,\n useDeinflections: config.useDeinflections ?? true,\n });\n }\n\n return result;\n }\n\n /**\n * Creates a FindTermsOptions object for scanning parser lookups.\n */\n private _createFindTermsOptions(\n language: string,\n enabledDictionaryMap: Map<string, FindTermDictionary>,\n options: SentenceParserOptions,\n ) {\n return {\n matchType: 'exact' as const,\n deinflect: options.deinflect ?? true,\n primaryReading: '',\n mainDictionary: '',\n sortFrequencyDictionary: null,\n sortFrequencyDictionaryOrder: 'descending' as const,\n removeNonJapaneseCharacters:\n options.removeNonJapaneseCharacters ??\n (language === 'ja' || language === 'zh' || language === 'yue' || language === 'ko'),\n textReplacements: options.textReplacements ?? [null],\n enabledDictionaryMap,\n excludeDictionaryDefinitions: null,\n searchResolution: options.searchResolution ?? ('letter' as const),\n language,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KanjiDictionaryEntry, TermDictionaryEntry, TermSourceMatchType } from "./dictionary-6Gj8AfGC.cjs";
|
|
1
|
+
import { KanjiDictionaryEntry, TermDictionaryEntry, TermSource, TermSourceMatchType } from "./dictionary-6Gj8AfGC.cjs";
|
|
2
2
|
import { DictionaryDB } from "./dictionary-database-3OesNIMt.cjs";
|
|
3
3
|
|
|
4
4
|
//#region src/types/translation.d.ts
|
|
@@ -41,9 +41,31 @@ type FindTermDictionary = {
|
|
|
41
41
|
};
|
|
42
42
|
type TermEnabledDictionaryMap = Map<string, FindTermDictionary>;
|
|
43
43
|
type KanjiEnabledDictionaryMap = Map<string, FindKanjiDictionary>; //#endregion
|
|
44
|
-
//#region src/
|
|
44
|
+
//#region src/types/parse.d.ts
|
|
45
45
|
|
|
46
46
|
//# sourceMappingURL=translation.d.ts.map
|
|
47
|
+
type ParseTextHeadword = {
|
|
48
|
+
term: string;
|
|
49
|
+
reading: string;
|
|
50
|
+
sources: TermSource[];
|
|
51
|
+
};
|
|
52
|
+
type ParseTextSegment = {
|
|
53
|
+
text: string;
|
|
54
|
+
reading: string;
|
|
55
|
+
headwords?: ParseTextHeadword[][];
|
|
56
|
+
};
|
|
57
|
+
type ParseTextLine = ParseTextSegment[];
|
|
58
|
+
type ParseTextResultItem = {
|
|
59
|
+
id: string;
|
|
60
|
+
source: 'scanning-parser' | 'mecab';
|
|
61
|
+
dictionary: null | string;
|
|
62
|
+
index: number;
|
|
63
|
+
content: ParseTextLine[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/lookup/translator.d.ts
|
|
68
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
47
69
|
type TermDictionaryEntry$1 = TermDictionaryEntry;
|
|
48
70
|
type FindTermsMode = 'group' | 'term' | 'merge' | 'simple';
|
|
49
71
|
/**
|
|
@@ -176,5 +198,59 @@ declare class Translator {
|
|
|
176
198
|
private _addUserFacingInflections;
|
|
177
199
|
private _hasAny;
|
|
178
200
|
} //#endregion
|
|
179
|
-
|
|
180
|
-
|
|
201
|
+
//#region src/lookup/sentence-parser.d.ts
|
|
202
|
+
interface SentenceParserDictionaryInput {
|
|
203
|
+
index: number;
|
|
204
|
+
priority?: number;
|
|
205
|
+
alias?: string;
|
|
206
|
+
allowSecondarySearches?: boolean;
|
|
207
|
+
partsOfSpeechFilter?: boolean;
|
|
208
|
+
useDeinflections?: boolean;
|
|
209
|
+
}
|
|
210
|
+
interface SentenceParserOptions {
|
|
211
|
+
/** The enabled dictionary map for lookups. */
|
|
212
|
+
enabledDictionaryMap: Map<string, SentenceParserDictionaryInput>;
|
|
213
|
+
/** Window size used by scanning parser. Defaults to 20. */
|
|
214
|
+
scanLength?: number;
|
|
215
|
+
/** Backward compatibility alias for scanLength. */
|
|
216
|
+
maxLength?: number;
|
|
217
|
+
/** Search resolution for translator. */
|
|
218
|
+
searchResolution?: SearchResolution;
|
|
219
|
+
/** Whether to remove non-Japanese characters in lookups. */
|
|
220
|
+
removeNonJapaneseCharacters?: boolean;
|
|
221
|
+
/** Whether to apply deinflection. */
|
|
222
|
+
deinflect?: boolean;
|
|
223
|
+
/** Text replacements forwarded to translator. */
|
|
224
|
+
textReplacements?: FindTermsTextReplacements;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Parses text into segments with dictionary lookups using the same
|
|
228
|
+
* scanning-parser model as Yomitan extension (non-MeCab path).
|
|
229
|
+
*/
|
|
230
|
+
declare class SentenceParser {
|
|
231
|
+
private _translator;
|
|
232
|
+
constructor(translator: Translator);
|
|
233
|
+
/**
|
|
234
|
+
* Parses the given text into lines of parsed segments.
|
|
235
|
+
*/
|
|
236
|
+
parseText(text: string, language: string, options: SentenceParserOptions): Promise<ParseTextLine[]>;
|
|
237
|
+
/**
|
|
238
|
+
* Parses a single line using extension-equivalent scanning parser semantics.
|
|
239
|
+
*/
|
|
240
|
+
private _parseLine;
|
|
241
|
+
private _createMatchedSegments;
|
|
242
|
+
/**
|
|
243
|
+
* Builds a FindTermDictionary map from the input format.
|
|
244
|
+
*/
|
|
245
|
+
private _buildFindTermDictionaryMap;
|
|
246
|
+
/**
|
|
247
|
+
* Creates a FindTermsOptions object for scanning parser lookups.
|
|
248
|
+
*/
|
|
249
|
+
private _createFindTermsOptions;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
//# sourceMappingURL=sentence-parser.d.ts.map
|
|
254
|
+
|
|
255
|
+
export { FindKanjiDictionary, FindKanjiOptions, FindTermDictionary, FindTermsMatchType, FindTermsMode, FindTermsOptions, FindTermsSortOrder, FindTermsTextReplacement, FindTermsTextReplacements, KanjiEnabledDictionaryMap, ParseTextHeadword, ParseTextLine, ParseTextResultItem, ParseTextSegment, SearchResolution, SentenceParser, SentenceParserOptions, TermEnabledDictionaryMap, Translator };
|
|
256
|
+
//# sourceMappingURL=sentence-parser-BQpu2oSA.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-parser-BQpu2oSA.d.cts","names":[],"sources":["../src/types/translation.ts","../src/types/parse.ts","../src/lookup/translator.ts","../src/lookup/sentence-parser.ts"],"sourcesContent":null,"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCodePointJapanese } from "./japanese-OKEGf_i4.js";
|
|
2
|
+
import { codePointPreview, debugYomitanCore } from "./debug-hF63AFkq.js";
|
|
2
3
|
|
|
3
4
|
//#region src/lookup/sentence-parser.ts
|
|
4
5
|
/**
|
|
@@ -16,22 +17,48 @@ var SentenceParser = class {
|
|
|
16
17
|
async parseText(text, language, options) {
|
|
17
18
|
const scanLength = options.scanLength ?? options.maxLength ?? 20;
|
|
18
19
|
const enabledDictionaryMap = this._buildFindTermDictionaryMap(options.enabledDictionaryMap);
|
|
20
|
+
debugYomitanCore("sentence-parser", "parseText:start", {
|
|
21
|
+
textLength: text.length,
|
|
22
|
+
textPreview: text.slice(0, 120),
|
|
23
|
+
textCodePoints: codePointPreview(text),
|
|
24
|
+
lineCount: text.split("\n").length,
|
|
25
|
+
scanLength,
|
|
26
|
+
language,
|
|
27
|
+
enabledDictionaryCount: enabledDictionaryMap.size,
|
|
28
|
+
enabledDictionaryNames: [...enabledDictionaryMap.keys()],
|
|
29
|
+
searchResolution: options.searchResolution ?? "letter",
|
|
30
|
+
removeNonJapaneseCharacters: options.removeNonJapaneseCharacters,
|
|
31
|
+
deinflect: options.deinflect
|
|
32
|
+
});
|
|
19
33
|
const lines = text.split("\n");
|
|
20
34
|
const results = [];
|
|
21
|
-
for (const line of lines) {
|
|
22
|
-
const segments = await this._parseLine(line, language, enabledDictionaryMap, scanLength, options);
|
|
23
|
-
results.push(
|
|
35
|
+
for (const [lineIndex, line] of lines.entries()) {
|
|
36
|
+
const segments = await this._parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options);
|
|
37
|
+
results.push(segments);
|
|
24
38
|
}
|
|
39
|
+
debugYomitanCore("sentence-parser", "parseText:complete", {
|
|
40
|
+
lineCount: results.length,
|
|
41
|
+
lineSegmentCounts: results.map((line) => line.length),
|
|
42
|
+
totalSegments: results.reduce((count, line) => count + line.length, 0),
|
|
43
|
+
matchedSegments: results.reduce((count, line) => count + line.filter((segment) => Array.isArray(segment.headwords)).length, 0)
|
|
44
|
+
});
|
|
25
45
|
return results;
|
|
26
46
|
}
|
|
27
47
|
/**
|
|
28
48
|
* Parses a single line using extension-equivalent scanning parser semantics.
|
|
29
49
|
*/
|
|
30
|
-
async _parseLine(line, language, enabledDictionaryMap, scanLength, options) {
|
|
50
|
+
async _parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options) {
|
|
31
51
|
const findTermsOptions = this._createFindTermsOptions(language, enabledDictionaryMap, options);
|
|
32
52
|
const cache = new Map();
|
|
33
53
|
const result = [];
|
|
34
54
|
let previousUngroupedSegment = null;
|
|
55
|
+
let lookupCount = 0;
|
|
56
|
+
let cacheHits = 0;
|
|
57
|
+
let cacheMisses = 0;
|
|
58
|
+
let matchedSegmentCount = 0;
|
|
59
|
+
let unmatchedSegmentCount = 0;
|
|
60
|
+
let emptyEntryLookups = 0;
|
|
61
|
+
const lookupMissSamples = [];
|
|
35
62
|
let i = 0;
|
|
36
63
|
const ii = line.length;
|
|
37
64
|
while (i < ii) {
|
|
@@ -40,8 +67,18 @@ var SentenceParser = class {
|
|
|
40
67
|
const substring = line.substring(i, i + scanLength);
|
|
41
68
|
let cached = cache.get(substring);
|
|
42
69
|
if (!cached) {
|
|
70
|
+
cacheMisses += 1;
|
|
43
71
|
const { dictionaryEntries, originalTextLength: originalTextLength$1 } = await this._translator.findTerms("simple", substring, findTermsOptions);
|
|
72
|
+
lookupCount += 1;
|
|
44
73
|
let segments$1 = [];
|
|
74
|
+
if (dictionaryEntries.length === 0) {
|
|
75
|
+
emptyEntryLookups += 1;
|
|
76
|
+
if (lookupMissSamples.length < 5) lookupMissSamples.push({
|
|
77
|
+
substring: substring.slice(0, 40),
|
|
78
|
+
codePoints: codePointPreview(substring, 20),
|
|
79
|
+
originalTextLength: originalTextLength$1
|
|
80
|
+
});
|
|
81
|
+
}
|
|
45
82
|
if (dictionaryEntries.length > 0 && originalTextLength$1 > 0 && (originalTextLength$1 !== character.length || isCodePointJapanese(codePoint))) {
|
|
46
83
|
const source = substring.substring(0, originalTextLength$1);
|
|
47
84
|
segments$1 = this._createMatchedSegments(source, dictionaryEntries);
|
|
@@ -51,36 +88,38 @@ var SentenceParser = class {
|
|
|
51
88
|
segments: segments$1
|
|
52
89
|
};
|
|
53
90
|
cache.set(substring, cached);
|
|
54
|
-
}
|
|
91
|
+
} else cacheHits += 1;
|
|
55
92
|
const { originalTextLength, segments } = cached;
|
|
56
93
|
if (segments.length > 0) {
|
|
57
94
|
previousUngroupedSegment = null;
|
|
58
95
|
result.push(...segments);
|
|
96
|
+
matchedSegmentCount += segments.length;
|
|
59
97
|
i += Math.max(1, originalTextLength);
|
|
60
98
|
} else {
|
|
61
99
|
if (previousUngroupedSegment === null) {
|
|
62
100
|
previousUngroupedSegment = {
|
|
63
101
|
text: character,
|
|
64
|
-
reading: ""
|
|
65
|
-
term: character,
|
|
66
|
-
entries: [],
|
|
67
|
-
furigana: [{
|
|
68
|
-
text: character,
|
|
69
|
-
reading: ""
|
|
70
|
-
}]
|
|
102
|
+
reading: ""
|
|
71
103
|
};
|
|
72
104
|
result.push(previousUngroupedSegment);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
previousUngroupedSegment.term = previousUngroupedSegment.text;
|
|
76
|
-
previousUngroupedSegment.furigana = [{
|
|
77
|
-
text: previousUngroupedSegment.text,
|
|
78
|
-
reading: ""
|
|
79
|
-
}];
|
|
80
|
-
}
|
|
105
|
+
unmatchedSegmentCount += 1;
|
|
106
|
+
} else previousUngroupedSegment.text += character;
|
|
81
107
|
i += character.length;
|
|
82
108
|
}
|
|
83
109
|
}
|
|
110
|
+
debugYomitanCore("sentence-parser", "parseLine:complete", {
|
|
111
|
+
lineIndex,
|
|
112
|
+
lineLength: line.length,
|
|
113
|
+
linePreview: line.slice(0, 120),
|
|
114
|
+
lookupCount,
|
|
115
|
+
cacheHits,
|
|
116
|
+
cacheMisses,
|
|
117
|
+
emptyEntryLookups,
|
|
118
|
+
matchedSegmentCount,
|
|
119
|
+
unmatchedSegmentCount,
|
|
120
|
+
finalSegmentCount: result.length,
|
|
121
|
+
missSamples: lookupMissSamples
|
|
122
|
+
});
|
|
84
123
|
return result;
|
|
85
124
|
}
|
|
86
125
|
_createMatchedSegments(source, dictionaryEntries) {
|
|
@@ -88,35 +127,24 @@ var SentenceParser = class {
|
|
|
88
127
|
const firstEntry = dictionaryEntries[0];
|
|
89
128
|
const firstHeadword = firstEntry.headwords[0];
|
|
90
129
|
if (!firstHeadword) return [];
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return [{
|
|
96
|
-
text: source,
|
|
97
|
-
reading,
|
|
98
|
-
term,
|
|
99
|
-
entries: filteredEntries,
|
|
100
|
-
furigana
|
|
101
|
-
}];
|
|
102
|
-
}
|
|
103
|
-
_trimEntriesForSource(dictionaryEntries, source) {
|
|
104
|
-
const trimmed = [];
|
|
105
|
-
for (const entry of dictionaryEntries) {
|
|
106
|
-
const headwords = [];
|
|
107
|
-
for (const headword of entry.headwords) {
|
|
130
|
+
const trimmedHeadwords = [];
|
|
131
|
+
for (const dictionaryEntry of dictionaryEntries) {
|
|
132
|
+
const validHeadwords = [];
|
|
133
|
+
for (const headword of dictionaryEntry.headwords) {
|
|
108
134
|
const validSources = headword.sources.filter((src) => src.originalText === source && src.isPrimary && src.matchType === "exact");
|
|
109
|
-
if (validSources.length > 0)
|
|
110
|
-
|
|
135
|
+
if (validSources.length > 0) validHeadwords.push({
|
|
136
|
+
term: headword.term,
|
|
137
|
+
reading: headword.reading,
|
|
111
138
|
sources: validSources
|
|
112
139
|
});
|
|
113
140
|
}
|
|
114
|
-
if (
|
|
115
|
-
...entry,
|
|
116
|
-
headwords
|
|
117
|
-
});
|
|
141
|
+
if (validHeadwords.length > 0) trimmedHeadwords.push(validHeadwords);
|
|
118
142
|
}
|
|
119
|
-
return
|
|
143
|
+
return [{
|
|
144
|
+
text: source,
|
|
145
|
+
reading: firstHeadword.reading ?? "",
|
|
146
|
+
...trimmedHeadwords.length > 0 ? { headwords: trimmedHeadwords } : {}
|
|
147
|
+
}];
|
|
120
148
|
}
|
|
121
149
|
/**
|
|
122
150
|
* Builds a FindTermDictionary map from the input format.
|
|
@@ -155,4 +183,4 @@ var SentenceParser = class {
|
|
|
155
183
|
|
|
156
184
|
//#endregion
|
|
157
185
|
export { SentenceParser };
|
|
158
|
-
//# sourceMappingURL=sentence-parser-
|
|
186
|
+
//# sourceMappingURL=sentence-parser-CXDs4R_c.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-parser-CXDs4R_c.js","names":["translator: Translator","text: string","language: string","options: SentenceParserOptions","results: ParseTextLine[]","line: string","lineIndex: number","enabledDictionaryMap: Map<string, FindTermDictionary>","scanLength: number","result: ParseTextSegment[]","previousUngroupedSegment: ParseTextSegment | null","lookupMissSamples: Array<{ substring: string; codePoints: string[]; originalTextLength: number }>","segments: ParseTextSegment[]","originalTextLength","source: string","dictionaryEntries: TermDictionaryEntry[]","trimmedHeadwords: ParseTextHeadword[][]","validHeadwords: ParseTextHeadword[]","inputMap: Map<string, SentenceParserDictionaryInput>"],"sources":["../src/lookup/sentence-parser.ts"],"sourcesContent":["/*\n * Copyright (C) 2023-2025 Yomitan Authors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { isCodePointJapanese } from '../language/ja/japanese';\nimport type { TermDictionaryEntry } from '../types/dictionary';\nimport type { ParseTextHeadword, ParseTextLine, ParseTextSegment } from '../types/parse';\nimport type { FindTermDictionary, FindTermsTextReplacements, SearchResolution } from '../types/translation';\nimport { codePointPreview, debugYomitanCore } from '../util/debug';\nimport type { Translator } from './translator';\nexport type { ParseTextHeadword, ParseTextLine, ParseTextResultItem, ParseTextSegment } from '../types/parse';\n\nexport interface SentenceParserDictionaryInput {\n index: number;\n priority?: number;\n alias?: string;\n allowSecondarySearches?: boolean;\n partsOfSpeechFilter?: boolean;\n useDeinflections?: boolean;\n}\n\nexport interface SentenceParserOptions {\n /** The enabled dictionary map for lookups. */\n enabledDictionaryMap: Map<string, SentenceParserDictionaryInput>;\n /** Window size used by scanning parser. Defaults to 20. */\n scanLength?: number;\n /** Backward compatibility alias for scanLength. */\n maxLength?: number;\n /** Search resolution for translator. */\n searchResolution?: SearchResolution;\n /** Whether to remove non-Japanese characters in lookups. */\n removeNonJapaneseCharacters?: boolean;\n /** Whether to apply deinflection. */\n deinflect?: boolean;\n /** Text replacements forwarded to translator. */\n textReplacements?: FindTermsTextReplacements;\n}\n\ninterface ParseScanCacheEntry {\n originalTextLength: number;\n segments: ParseTextSegment[];\n}\n\n/**\n * Parses text into segments with dictionary lookups using the same\n * scanning-parser model as Yomitan extension (non-MeCab path).\n */\nexport class SentenceParser {\n private _translator: Translator;\n\n constructor(translator: Translator) {\n this._translator = translator;\n }\n\n /**\n * Parses the given text into lines of parsed segments.\n */\n async parseText(text: string, language: string, options: SentenceParserOptions): Promise<ParseTextLine[]> {\n const scanLength = options.scanLength ?? options.maxLength ?? 20;\n const enabledDictionaryMap = this._buildFindTermDictionaryMap(options.enabledDictionaryMap);\n debugYomitanCore('sentence-parser', 'parseText:start', {\n textLength: text.length,\n textPreview: text.slice(0, 120),\n textCodePoints: codePointPreview(text),\n lineCount: text.split('\\n').length,\n scanLength,\n language,\n enabledDictionaryCount: enabledDictionaryMap.size,\n enabledDictionaryNames: [...enabledDictionaryMap.keys()],\n searchResolution: options.searchResolution ?? 'letter',\n removeNonJapaneseCharacters: options.removeNonJapaneseCharacters,\n deinflect: options.deinflect,\n });\n\n const lines = text.split('\\n');\n const results: ParseTextLine[] = [];\n\n for (const [lineIndex, line] of lines.entries()) {\n const segments = await this._parseLine(line, lineIndex, language, enabledDictionaryMap, scanLength, options);\n results.push(segments);\n }\n\n debugYomitanCore('sentence-parser', 'parseText:complete', {\n lineCount: results.length,\n lineSegmentCounts: results.map((line) => line.length),\n totalSegments: results.reduce((count, line) => count + line.length, 0),\n matchedSegments: results.reduce(\n (count, line) => count + line.filter((segment) => Array.isArray(segment.headwords)).length,\n 0,\n ),\n });\n\n return results;\n }\n\n /**\n * Parses a single line using extension-equivalent scanning parser semantics.\n */\n private async _parseLine(\n line: string,\n lineIndex: number,\n language: string,\n enabledDictionaryMap: Map<string, FindTermDictionary>,\n scanLength: number,\n options: SentenceParserOptions,\n ): Promise<ParseTextLine> {\n const findTermsOptions = this._createFindTermsOptions(language, enabledDictionaryMap, options);\n const cache = new Map<string, ParseScanCacheEntry>();\n\n const result: ParseTextSegment[] = [];\n let previousUngroupedSegment: ParseTextSegment | null = null;\n let lookupCount = 0;\n let cacheHits = 0;\n let cacheMisses = 0;\n let matchedSegmentCount = 0;\n let unmatchedSegmentCount = 0;\n let emptyEntryLookups = 0;\n const lookupMissSamples: Array<{ substring: string; codePoints: string[]; originalTextLength: number }> = [];\n\n let i = 0;\n const ii = line.length;\n while (i < ii) {\n const codePoint = line.codePointAt(i) as number;\n const character = String.fromCodePoint(codePoint);\n const substring = line.substring(i, i + scanLength);\n\n let cached = cache.get(substring);\n if (!cached) {\n cacheMisses += 1;\n const { dictionaryEntries, originalTextLength } = await this._translator.findTerms(\n 'simple',\n substring,\n findTermsOptions,\n );\n lookupCount += 1;\n\n let segments: ParseTextSegment[] = [];\n if (dictionaryEntries.length === 0) {\n emptyEntryLookups += 1;\n if (lookupMissSamples.length < 5) {\n lookupMissSamples.push({\n substring: substring.slice(0, 40),\n codePoints: codePointPreview(substring, 20),\n originalTextLength,\n });\n }\n }\n\n if (\n dictionaryEntries.length > 0 &&\n originalTextLength > 0 &&\n (originalTextLength !== character.length || isCodePointJapanese(codePoint))\n ) {\n const source = substring.substring(0, originalTextLength);\n segments = this._createMatchedSegments(source, dictionaryEntries);\n }\n\n cached = {\n originalTextLength,\n segments,\n };\n cache.set(substring, cached);\n } else {\n cacheHits += 1;\n }\n\n const { originalTextLength, segments } = cached;\n if (segments.length > 0) {\n previousUngroupedSegment = null;\n result.push(...segments);\n matchedSegmentCount += segments.length;\n i += Math.max(1, originalTextLength);\n } else {\n if (previousUngroupedSegment === null) {\n previousUngroupedSegment = {\n text: character,\n reading: ''\n };\n result.push(previousUngroupedSegment);\n unmatchedSegmentCount += 1;\n } else {\n previousUngroupedSegment.text += character;\n }\n i += character.length;\n }\n }\n\n debugYomitanCore('sentence-parser', 'parseLine:complete', {\n lineIndex,\n lineLength: line.length,\n linePreview: line.slice(0, 120),\n lookupCount,\n cacheHits,\n cacheMisses,\n emptyEntryLookups,\n matchedSegmentCount,\n unmatchedSegmentCount,\n finalSegmentCount: result.length,\n missSamples: lookupMissSamples,\n });\n\n return result;\n }\n\n private _createMatchedSegments(source: string, dictionaryEntries: TermDictionaryEntry[]): ParseTextSegment[] {\n if (dictionaryEntries.length === 0) {\n return [];\n }\n\n const firstEntry = dictionaryEntries[0];\n const firstHeadword = firstEntry.headwords[0];\n if (!firstHeadword) {\n return [];\n }\n\n const trimmedHeadwords: ParseTextHeadword[][] = [];\n for (const dictionaryEntry of dictionaryEntries) {\n const validHeadwords: ParseTextHeadword[] = [];\n for (const headword of dictionaryEntry.headwords) {\n const validSources = headword.sources.filter(\n (src) => src.originalText === source && src.isPrimary && src.matchType === 'exact',\n );\n\n if (validSources.length > 0) {\n validHeadwords.push({\n term: headword.term,\n reading: headword.reading,\n sources: validSources,\n });\n }\n }\n if (validHeadwords.length > 0) {\n trimmedHeadwords.push(validHeadwords);\n }\n }\n\n return [\n {\n text: source,\n reading: firstHeadword.reading ?? '',\n ...(trimmedHeadwords.length > 0 ? { headwords: trimmedHeadwords } : {}),\n },\n ];\n }\n\n /**\n * Builds a FindTermDictionary map from the input format.\n */\n private _buildFindTermDictionaryMap(\n inputMap: Map<string, SentenceParserDictionaryInput>,\n ): Map<string, FindTermDictionary> {\n const result = new Map<string, FindTermDictionary>();\n\n for (const [name, config] of inputMap.entries()) {\n result.set(name, {\n index: config.index,\n alias: config.alias ?? name,\n allowSecondarySearches: config.allowSecondarySearches ?? false,\n partsOfSpeechFilter: config.partsOfSpeechFilter ?? true,\n useDeinflections: config.useDeinflections ?? true,\n });\n }\n\n return result;\n }\n\n /**\n * Creates a FindTermsOptions object for scanning parser lookups.\n */\n private _createFindTermsOptions(\n language: string,\n enabledDictionaryMap: Map<string, FindTermDictionary>,\n options: SentenceParserOptions,\n ) {\n return {\n matchType: 'exact' as const,\n deinflect: options.deinflect ?? true,\n primaryReading: '',\n mainDictionary: '',\n sortFrequencyDictionary: null,\n sortFrequencyDictionaryOrder: 'descending' as const,\n removeNonJapaneseCharacters:\n options.removeNonJapaneseCharacters ??\n (language === 'ja' || language === 'zh' || language === 'yue' || language === 'ko'),\n textReplacements: options.textReplacements ?? [null],\n enabledDictionaryMap,\n excludeDictionaryDefinitions: null,\n searchResolution: options.searchResolution ?? ('letter' as const),\n language,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { KanjiDictionaryEntry, TermDictionaryEntry, TermSourceMatchType } from "./dictionary-
|
|
2
|
-
import { DictionaryDB$1 as DictionaryDB } from "./dictionary-database-
|
|
1
|
+
import { KanjiDictionaryEntry, TermDictionaryEntry, TermSource, TermSourceMatchType } from "./dictionary-DdxghSJP.js";
|
|
2
|
+
import { DictionaryDB$1 as DictionaryDB } from "./dictionary-database-D0lvK2n3.js";
|
|
3
3
|
|
|
4
4
|
//#region src/types/translation.d.ts
|
|
5
5
|
type SearchResolution = 'letter' | 'word';
|
|
@@ -41,9 +41,31 @@ type FindTermDictionary = {
|
|
|
41
41
|
};
|
|
42
42
|
type TermEnabledDictionaryMap = Map<string, FindTermDictionary>;
|
|
43
43
|
type KanjiEnabledDictionaryMap = Map<string, FindKanjiDictionary>; //#endregion
|
|
44
|
-
//#region src/
|
|
44
|
+
//#region src/types/parse.d.ts
|
|
45
45
|
|
|
46
46
|
//# sourceMappingURL=translation.d.ts.map
|
|
47
|
+
type ParseTextHeadword = {
|
|
48
|
+
term: string;
|
|
49
|
+
reading: string;
|
|
50
|
+
sources: TermSource[];
|
|
51
|
+
};
|
|
52
|
+
type ParseTextSegment = {
|
|
53
|
+
text: string;
|
|
54
|
+
reading: string;
|
|
55
|
+
headwords?: ParseTextHeadword[][];
|
|
56
|
+
};
|
|
57
|
+
type ParseTextLine = ParseTextSegment[];
|
|
58
|
+
type ParseTextResultItem = {
|
|
59
|
+
id: string;
|
|
60
|
+
source: 'scanning-parser' | 'mecab';
|
|
61
|
+
dictionary: null | string;
|
|
62
|
+
index: number;
|
|
63
|
+
content: ParseTextLine[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/lookup/translator.d.ts
|
|
68
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
47
69
|
type TermDictionaryEntry$1 = TermDictionaryEntry;
|
|
48
70
|
type FindTermsMode = 'group' | 'term' | 'merge' | 'simple';
|
|
49
71
|
/**
|
|
@@ -176,5 +198,59 @@ declare class Translator {
|
|
|
176
198
|
private _addUserFacingInflections;
|
|
177
199
|
private _hasAny;
|
|
178
200
|
} //#endregion
|
|
179
|
-
|
|
180
|
-
|
|
201
|
+
//#region src/lookup/sentence-parser.d.ts
|
|
202
|
+
interface SentenceParserDictionaryInput {
|
|
203
|
+
index: number;
|
|
204
|
+
priority?: number;
|
|
205
|
+
alias?: string;
|
|
206
|
+
allowSecondarySearches?: boolean;
|
|
207
|
+
partsOfSpeechFilter?: boolean;
|
|
208
|
+
useDeinflections?: boolean;
|
|
209
|
+
}
|
|
210
|
+
interface SentenceParserOptions {
|
|
211
|
+
/** The enabled dictionary map for lookups. */
|
|
212
|
+
enabledDictionaryMap: Map<string, SentenceParserDictionaryInput>;
|
|
213
|
+
/** Window size used by scanning parser. Defaults to 20. */
|
|
214
|
+
scanLength?: number;
|
|
215
|
+
/** Backward compatibility alias for scanLength. */
|
|
216
|
+
maxLength?: number;
|
|
217
|
+
/** Search resolution for translator. */
|
|
218
|
+
searchResolution?: SearchResolution;
|
|
219
|
+
/** Whether to remove non-Japanese characters in lookups. */
|
|
220
|
+
removeNonJapaneseCharacters?: boolean;
|
|
221
|
+
/** Whether to apply deinflection. */
|
|
222
|
+
deinflect?: boolean;
|
|
223
|
+
/** Text replacements forwarded to translator. */
|
|
224
|
+
textReplacements?: FindTermsTextReplacements;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Parses text into segments with dictionary lookups using the same
|
|
228
|
+
* scanning-parser model as Yomitan extension (non-MeCab path).
|
|
229
|
+
*/
|
|
230
|
+
declare class SentenceParser {
|
|
231
|
+
private _translator;
|
|
232
|
+
constructor(translator: Translator);
|
|
233
|
+
/**
|
|
234
|
+
* Parses the given text into lines of parsed segments.
|
|
235
|
+
*/
|
|
236
|
+
parseText(text: string, language: string, options: SentenceParserOptions): Promise<ParseTextLine[]>;
|
|
237
|
+
/**
|
|
238
|
+
* Parses a single line using extension-equivalent scanning parser semantics.
|
|
239
|
+
*/
|
|
240
|
+
private _parseLine;
|
|
241
|
+
private _createMatchedSegments;
|
|
242
|
+
/**
|
|
243
|
+
* Builds a FindTermDictionary map from the input format.
|
|
244
|
+
*/
|
|
245
|
+
private _buildFindTermDictionaryMap;
|
|
246
|
+
/**
|
|
247
|
+
* Creates a FindTermsOptions object for scanning parser lookups.
|
|
248
|
+
*/
|
|
249
|
+
private _createFindTermsOptions;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
//# sourceMappingURL=sentence-parser.d.ts.map
|
|
254
|
+
|
|
255
|
+
export { FindKanjiDictionary, FindKanjiOptions, FindTermDictionary, FindTermsMatchType, FindTermsMode, FindTermsOptions, FindTermsSortOrder, FindTermsTextReplacement, FindTermsTextReplacements, KanjiEnabledDictionaryMap, ParseTextHeadword, ParseTextLine, ParseTextResultItem, ParseTextSegment, SearchResolution, SentenceParser as SentenceParser$1, SentenceParserOptions, TermEnabledDictionaryMap, Translator as Translator$1 };
|
|
256
|
+
//# sourceMappingURL=sentence-parser-CmsHUc_p.d.ts.map
|