Skip to content

Commit

Permalink
feat: accept more 音韻地位s
Browse files Browse the repository at this point in the history
- '蒸幽韻特殊類' is no longer needed, and is thus removed
  - 蒸韻 (with any 呼) now allows B/C類 or any 銳音
    (Note: This includes both 云母B類 and 云母C類)
  - 幽韻 now allows A/B類 or any 銳音
- 麻韻 now allows A/B類 or any 銳音
  • Loading branch information
syimyuzya committed Oct 6, 2024
1 parent e5e1066 commit d06e9a4
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 70 deletions.
39 changes: 31 additions & 8 deletions src/lib/音韻地位.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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簡略描述('端開四麻平', '端開四麻平', '不省略');
});
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 陽韻四等泥母/, '端組類隔(陽韻)');
Expand Down
100 changes: 38 additions & 62 deletions src/lib/音韻地位.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,14 @@ export type 部分音韻屬性 = Partial<Pick<音韻地位, '母' | '呼' | '等
* **注意**:內建的邊緣地位白名單**已涵蓋內建資料中全部邊緣地位**,故使用內建資料之音韻地位時完全不需使用此參數。
*
* 目前支持的種類如下:
* - `'陽韻A類'` (!)
* - `'端組類隔'` (!)
* - `'咍韻脣音'` (!)
* - `'匣母三等'` (!)
* - `'羣邪俟母非三等'` (!)
* - `'云母開口'`
* - `'蒸幽韻特殊類'`
* - `'陽韻A類'`
* - `'端組類隔'`
* - `'咍韻脣音'`
* - `'匣母三等'`
* - `'羣邪俟母非三等'`
* - `'云母開口'` (+)
*
* 標注「(!)」者,僅可當待建立地位確實為該類型邊緣地位時,才可以列入,否則無法建立音韻地位。
* 未標注「(+)」者,僅可當待建立地位確實為該類型邊緣地位時,才可以列入,否則無法建立音韻地位。而標注「(+)」者則在建立任意音韻地位時均可列入
*/
export type 邊緣地位種類指定 = readonly string[];

Expand All @@ -66,11 +65,6 @@ const 已知邊緣地位 = new Set([
// 云母開口
'云開三C之上', // 矣
'云開三B仙平', // 焉
// 蒸幽韻特殊類
'影開三B蒸入', // 抑
'溪開三B蒸平', // 硱
'溪開三B幽平', // 𠁫
'曉開三B幽平', // 烋
]);

export const _UNCHECKED: 邊緣地位種類指定 = ['@UNCHECKED@'];
Expand Down Expand Up @@ -371,7 +365,7 @@ export class 音韻地位 {
get 簡略描述(): string {
const {,,} = this;
let {,,} = this;
if ( && 類搭配(, , )[0] === ) {
if ( && 類搭配(, )[0] === ) {
= null;
}
if ( === '合' && === '云') {
Expand Down Expand Up @@ -916,17 +910,18 @@ export class 音韻地位 {
*
* 類:
* - 限幫見影組三等,其餘情形均須取 `null`(不分類)
* - 前元音韻(支脂祭真仙宵麻庚清幽侵):須取 A 或 B,其中部分韻限其中一種,或額外限制搭配
* - 前元音韻(支脂祭真仙宵麻庚清幽侵):須取 A 或 B,其中清韻限 A 類,庚韻限 B 類
* - 其餘韻一般須取 C
* - 蒸韻:須取 C 或 B
* - 陽韻:限 C 類,但有取 A 類之罕見例外
* - 云母:限非 A 類
*
* 韻:
* - 凡韻:限脣音
* - 嚴韻、之魚殷痕韻:限非脣音
* - 臻韻:限莊組
* - 真殷韻開口、麻韻三等(端組為四等)、清幽韻:限非莊組
* - 庚韻三等、蒸韻合口:銳音限莊組
* - 真殷韻開口、清韻:限非莊組
* - 庚韻非二等:銳音限莊組
*
* @param 母 聲母:幫, 滂, 並, 明, …
* @param 呼 呼:`null`, 開, 合
Expand Down Expand Up @@ -971,7 +966,7 @@ export class 音韻地位 {
}

// 驗證搭配
// 順序:從搭配規則從基本到精細
// 順序:搭配規則從基本到精細

// 聲(僅韻-聲搭配)
=== '入' && 陰聲韻.includes() && reject(`unexpected ${}韻入聲`);
Expand Down Expand Up @@ -1020,12 +1015,15 @@ export class 音韻地位 {
} else if (!鈍音母.includes()) {
&& reject('unexpected 類 for 銳音聲母');
} else {
const [典型搭配類, 搭配類] = 類搭配(, , );
const [典型搭配類, 搭配類] = 類搭配(, );
if (!) {
const suggestion = 典型搭配類.length === 1 ? ` (should be ${典型搭配類}${典型搭配類 !== 搭配類 ? ' typically' : ''})` : '';
reject(`missing 類${suggestion}`);
} else if (!搭配類.includes()) {
reject(`unexpected ${}${}${}類`);
if ( === '云' && === 'A') {
reject(`unexpected 云母A類`);
}
reject(`unexpected ${}${}類`);
}
}

Expand All @@ -1036,14 +1034,11 @@ export class 音韻地位 {
=== '凡' && reject(`unexpected 凡韻非脣音`);
}
if ([...'莊初崇生俟'].includes()) {
=== '三' && ['麻', '清', '幽'].includes() && reject(`unexpected ${}${ === '麻' ? '三等' : ''}莊組`);
=== '三' && === '清' && reject(`unexpected ${}韻莊組`);
=== '開' && ['真', '殷'].includes() && reject(`unexpected ${}韻開口莊組`);
} else {
=== '臻' && reject(`unexpected 臻韻非莊組`);
if (!鈍音母.includes()) {
=== '庚' && !== '二' && reject(`unexpected 庚韻${}${}母`);
=== '蒸' && === '合' && reject(`unexpected 蒸韻合口${}母`);
}
=== '庚' && !== '二' && !鈍音母.includes() && reject(`unexpected 庚韻${}${}母`);
}

// 邊緣搭配
Expand All @@ -1068,12 +1063,6 @@ export class 音韻地位 {
['匣母三等', true, === '匣' && === '三', `匣母三等`],
['羣邪俟母非三等', true, !== '三' && [...'羣邪俟'].includes(), `${}${}等`],
['云母開口', false, === '云' && === '開' && ![...'宵幽侵鹽嚴'].includes(), '云母開口'],
[
'蒸幽韻特殊類',
false,
&& ['蒸', '幽'].includes() && ([...'幫滂並明'].includes() ? !== 'B' : === '開' && === 'B'),
`${}${}${}類`,
],
] as const;

const knownKinds: string[] = marginalTests.map(([kind]) => kind);
Expand Down Expand Up @@ -1152,7 +1141,7 @@ export class 音韻地位 {
}

if (! && === '三' && 鈍音母.includes()) {
const [典型搭配類] = 類搭配(, , );
const [典型搭配類] = 類搭配(, );
if (典型搭配類.length === 1) {
= 典型搭配類;
}
Expand All @@ -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 搭配;
}
Expand Down

0 comments on commit d06e9a4

Please sign in to comment.