diff --git "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.spec.ts" "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.spec.ts" index 8274aff..133f73b 100644 --- "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.spec.ts" +++ "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.spec.ts" @@ -202,8 +202,8 @@ test('簡略描述', t => { test簡略描述('幫三C凡入', '幫凡入', '省略C類'); test簡略描述('見開三B庚平', '見開三庚平', '省略B類'); test簡略描述('明三A清平', '明清平', '省略等、A類'); - test簡略描述('云合三B蒸入', '云蒸入', '省略呼、等、類'); - test簡略描述('影開三B蒸入', '影開B蒸入', '不省略類'); + test簡略描述('云合三B支去', '云支去', '省略呼、等、類'); + test簡略描述('云合三B蒸入', '云B蒸入', '不省略類'); test簡略描述('見開一歌平', '見開一歌平', '不省略'); test簡略描述('端開四麻平', '端開四麻平', '不省略'); }); @@ -257,15 +257,42 @@ test('不合法音韻地位', t => { testCase('見合三A幽上', /unexpected 幽韻合口/, '呼搭配(僅開/僅合口韻)'); testCase('幫四A先平', /unexpected 類/, '類搭配(等)'); testCase('章開三A支平', /unexpected 類/, '類搭配(母)'); + testCase('云合三A支平', /unexpected 云母A類/, '類搭配(云母)'); testCase('明三清上', /missing 類 \(should be A\)/, '類搭配(韻)'); + testCase('幫三B清入', /unexpected 清韻B類/, '類搭配(韻)'); testCase('明三陽去', /missing 類 \(should be C typically\)/, '類搭配(韻,可能有邊緣地位)'); - testCase('幫三B清入', /unexpected 清韻幫母B類/, '類搭配(綜合)'); testCase('幫三C嚴入', /unexpected 嚴韻脣音/, '母搭配(凡韻)'); testCase('幫三C之平', /unexpected 之韻脣音/, '母搭配(脣音之韻)'); testCase('初開三真去', /unexpected 真韻開口莊組/, '母搭配(臻韻)'); testCase('知開三庚平', /unexpected 庚韻三等知母/, '母搭配(非莊組銳聲母)'); }); +test('蒸幽麻韻擴展地位', t => { + for (const 描述 of [ + // 蒸 + '影開三B蒸平', + '影合三B蒸平', + '影合三C蒸平', + '知合三蒸平', + '心合三蒸平', + '生合三蒸平', + // 幽 + '影開三B幽平', + '知開三幽平', + '心開三幽平', + '章開三幽平', + // 麻 + '並三B麻平', + ]) { + t.is(音韻地位.from描述(描述).描述, 描述); + } + // 蒸韻云母開口 + for (const 描述 of ['云開三B蒸平', '云開三C蒸平']) { + t.throws(() => 音韻地位.from描述(描述), { message: /unexpected 云母開口 \(note: marginal 音韻地位/ }); + t.is(音韻地位.from描述(描述, false, ['云母開口']).描述, 描述); + } +}); + test('邊緣地位', t => { function passes(描述: string, 邊緣地位指定: 邊緣地位種類指定, testMessage?: string) { t.is( @@ -297,11 +324,7 @@ test('邊緣地位', t => { // 非嚴格邊緣地位 throws('云開三C之平', [], /unexpected 云母開口 \(note: marginal 音韻地位/, '云母開口'); passes('云開三C之平', ['云母開口'], '云母開口'); - passes('云開三C之平', ['云母開口', '蒸幽韻特殊類'], '非嚴格音韻地位類型指定'); - - // 蒸幽韻特殊類 - throws('明三A幽去', [], /unexpected 幽韻明母A類 \(note: marginal 音韻地位/, '幽韻脣音A類'); - passes('明三A幽去', ['蒸幽韻特殊類'], '幽韻脣音A類'); + passes('云合三B支平', ['云母開口'], '非嚴格音韻地位類型指定'); // 端組類隔 throws('泥開四陽上', [], /unexpected 陽韻四等泥母/, '端組類隔(陽韻)'); diff --git "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" index 97f0ad7..5982e7b 100644 --- "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" +++ "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" @@ -34,15 +34,14 @@ export type 部分音韻屬性 = Partial kind); @@ -1152,7 +1141,7 @@ export class 音韻地位 { } if (!類 && 等 === '三' && 鈍音母.includes(母)) { - const [典型搭配類] = 類搭配(母, 呼, 韻); + const [典型搭配類] = 類搭配(母, 韻); if (典型搭配類.length === 1) { 類 = 典型搭配類; } @@ -1168,39 +1157,26 @@ export class 音韻地位 { * 取得給定條件下可搭配的類,分為「不含邊緣地位」與「含邊緣地位」兩種。 * 用於 `音韻地位` 的 `.驗證`、`.from描述`、`.簡略描述`。 */ -function 類搭配(母: string, 呼: string | null, 韻: string): [string, string] { - const 搭配 = (function 類搭配不計云母(): [string, string] { - if (韻 === '陽') { - return ['C', 'CA']; - } else if (['蒸', '幽'].includes(韻)) { - if (呼 === '合') { - return ['B', 'B']; - } - const 含邊緣地位類 = 韻 === '蒸' ? 'CB' : 'AB'; - if ([...'幫滂並明'].includes(母)) { - return ['B', 含邊緣地位類]; - } else { - return [含邊緣地位類[0], 含邊緣地位類]; - } - } - for (const [搭配類, 搭配韻] of [ - ['C', [...'東鍾之微魚虞廢殷元文歌尤嚴凡']], - ['AB', [...'支脂祭真仙宵侵鹽']], // 幽 already handled above (same for 蒸 & 陽) - ['A', [...'麻清']], - ['B', [...'庚']], - //['CB', [...'蒸']], - //['CA', [...'陽']], - ] as const) { - if (搭配韻.includes(韻)) { - return [搭配類, 搭配類]; - } +function 類搭配(母: string, 韻: string): [string, string] { + let 搭配: [string, string] | null = null; + for (const [搭配類, 搭配韻] of [ + ['C', [...'東鍾之微魚虞廢殷元文歌尤嚴凡']], + ['AB', [...'支脂祭真仙宵麻幽侵鹽']], + ['A', [...'清']], + ['B', [...'庚']], + ['BC', [...'蒸']], + ['CA', [...'陽']], + ] as const) { + if (搭配韻.includes(韻)) { + 搭配 = [搭配類 === 'CA' ? 'C' : 搭配類, 搭配類]; + break; } + } + if (搭配 === null) { throw new Error(`unknown 韻: ${韻}`); - })(); + } if (母 === '云') { - const 搭配類 = 搭配[1].includes('B') ? 'B' : 搭配[1].includes('C') ? 'C' : ''; - const 典型搭配類 = [...搭配[0]].includes(搭配類) ? 搭配類 : ''; - return [典型搭配類, 搭配類]; + return 搭配.map(x => x.replace(/A/g, '')) as typeof 搭配; } return 搭配; }