@@ -38,6 +38,9 @@ function assert(value: unknown, error: string): asserts value {
38
38
if ( ! value ) throw new Error ( error ) ;
39
39
}
40
40
41
+ // `Array.isArray`, but with more conservative type inference
42
+ const isArray : ( x : unknown ) => x is readonly unknown [ ] = Array . isArray ;
43
+
41
44
type Falsy = '' | 0 | false | null | undefined ;
42
45
export type 規則 < T = unknown > = [ unknown , T | 規則 < T > ] [ ] ;
43
46
@@ -182,7 +185,7 @@ export class 音韻地位 {
182
185
readonly 聲 : string ;
183
186
184
187
/**
185
- * 初始化音韻地位物件。(各項參數詳見 [`音韻地位`] 說明)
188
+ * 初始化音韻地位物件。(各項參數詳見 [[ `音韻地位`] ] 說明)
186
189
* @param 母 聲母:幫, 滂, 並, 明, …
187
190
* @param 呼 呼:`null`, 開, 合
188
191
* @param 等 等:一, 二, 三, 四
@@ -351,7 +354,7 @@ export class 音韻地位 {
351
354
}
352
355
353
356
/**
354
- * 表達式,可用於 [[`屬於`]] 函數
357
+ * 表達式,可用於 [[`. 屬於`]] 函數
355
358
* @example
356
359
* ```typescript
357
360
* > 音韻地位 = Qieyun.音韻地位.from描述('幫三凡入');
@@ -394,6 +397,8 @@ export class 音韻地位 {
394
397
/**
395
398
* 調整該音韻地位的屬性,會驗證調整後地位的合法性,返回新的對象。
396
399
*
400
+ * 本方法可使用一般形式(`.調整({ ... })`)、字串形式(`.調整('...')`)或標籤模板語法(`` .調整`...` ``)。
401
+ *
397
402
* **注意**:原對象不會被修改。
398
403
*
399
404
* @param 調整屬性 對象,其屬性可為六項基本屬性中的若干項,各屬性的值為欲修改成的值。
@@ -410,7 +415,97 @@ export class 音韻地位 {
410
415
* '見合三元上'
411
416
* ```
412
417
*/
413
- 調整 ( 調整屬性 : 部分音韻屬性 ) : 音韻地位 {
418
+ 調整 ( 調整屬性 : 部分音韻屬性 ) : 音韻地位 ;
419
+
420
+ /**
421
+ * @example
422
+ * ```typescript
423
+ * > 音韻地位 = Qieyun.音韻地位.from描述('幫三元上');
424
+ * > 音韻地位.調整`平聲`.描述 // 標籤模板語法(表達式為字面值時推薦)
425
+ * '幫三元平'
426
+ * > 音韻地位.調整('平聲').描述
427
+ * '幫三元平'
428
+ * > 音韻地位.調整`見母 合口`.描述
429
+ * '見合三元上'
430
+ * ```
431
+ */
432
+ 調整 ( 調整屬性 : string ) : 音韻地位 ;
433
+
434
+ /**
435
+ * @example
436
+ * ```typescript
437
+ * > 音韻地位 = Qieyun.音韻地位.from描述('幫三元上');
438
+ * > 音韻地位.調整`${'見'}母 ${'合口'}`.描述
439
+ * '見合三元上'
440
+ * ```
441
+ */
442
+ 調整 ( 調整屬性 : TemplateStringsArray , ...參數 : string [ ] ) : 音韻地位 ;
443
+
444
+ 調整 ( 調整屬性 : string | readonly string [ ] | 部分音韻屬性 , ...參數 : string [ ] ) : 音韻地位 {
445
+ if ( typeof 調整屬性 === 'string' ) 調整屬性 = [ 調整屬性 ] ;
446
+
447
+ if ( isArray ( 調整屬性 ) ) {
448
+ const tokenGroups : string [ ] [ ] = [ [ ] ] ;
449
+ for ( let i = 0 ; i < 調整屬性 . length ; i ++ ) {
450
+ let fragment = 調整屬性 [ i ] ;
451
+ if ( ! i ) {
452
+ fragment = fragment . trimStart ( ) ;
453
+ }
454
+ if ( i === 調整屬性 . length - 1 ) {
455
+ fragment = fragment . trimEnd ( ) ;
456
+ }
457
+
458
+ const tokens = fragment . split ( / \s + / ) ;
459
+ for ( let j = 0 ; j < tokens . length ; j ++ ) {
460
+ if ( tokens [ j ] ) {
461
+ tokenGroups [ tokenGroups . length - 1 ] . push ( tokens [ j ] ) ;
462
+ }
463
+ if ( j < tokens . length - 1 ) {
464
+ tokenGroups . push ( [ ] ) ;
465
+ }
466
+ }
467
+ if ( i < 參數 . length ) {
468
+ tokenGroups [ tokenGroups . length - 1 ] . push ( 參數 [ i ] ) ;
469
+ }
470
+ }
471
+
472
+ const 音韻屬性 : 部分音韻屬性 = { } ;
473
+ const tryAssign = < T extends keyof 部分音韻屬性 > ( 屬性 : T , 值 : 音韻地位 [ T ] ) => {
474
+ assert ( ! ( 屬性 in 音韻屬性 ) , `duplicated assignment of ${ 屬性 } ` ) ;
475
+ 音韻屬性 [ 屬性 ] = 值 ;
476
+ } ;
477
+
478
+ for ( let tokens of tokenGroups ) {
479
+ assert ( tokens . length , 'empty expression' ) ;
480
+ let original : string | undefined ;
481
+ if ( tokens . length === 1 ) {
482
+ switch ( tokens [ 0 ] ) {
483
+ case '開合中立' :
484
+ tryAssign ( '呼' , null ) ;
485
+ continue ;
486
+ case '不分重紐' :
487
+ tryAssign ( '重紐' , null ) ;
488
+ continue ;
489
+ }
490
+ original = tokens [ 0 ] ;
491
+ tokens = [ ...tokens [ 0 ] ] ;
492
+ }
493
+ let 屬性 = tokens [ tokens . length - 1 ] ;
494
+ const 值 = tokens [ tokens . length - 2 ] ;
495
+ assert (
496
+ 屬性 === '類' ? tokens . slice ( 0 , - 2 ) . join ( '' ) === '重紐' : tokens . length === 2 && [ '母' , '等' , '韻' , '聲' , '口' ] . includes ( 屬性 ) ,
497
+ `unrecognized expression: ${ original ?? tokens . join ( ', ' ) } `
498
+ ) ;
499
+ if ( 屬性 === '口' ) 屬性 = '呼' ;
500
+ if ( 屬性 === '類' ) 屬性 = '重紐' ;
501
+ const check = 檢查 [ 屬性 as keyof 部分音韻屬性 ] ;
502
+ assert ( check . includes ( 值 ) , `unexpected ${ 屬性 } : ${ 值 } ` ) ;
503
+ tryAssign ( 屬性 as keyof 部分音韻屬性 , 值 ) ;
504
+ }
505
+
506
+ 調整屬性 = 音韻屬性 ;
507
+ }
508
+
414
509
const { 母 = this . 母 , 呼 = this . 呼 , 等 = this . 等 , 重紐 = this . 重紐 , 韻 = this . 韻 , 聲 = this . 聲 } = 調整屬性 ;
415
510
return new 音韻地位 ( 母 , 呼 , 等 , 重紐 , 韻 , 聲 ) ;
416
511
}
@@ -449,17 +544,15 @@ export class 音韻地位 {
449
544
* * NOT 運算子:`非`, `not`, `~`, `!`
450
545
* * 括號:`(……)`, `(……)`
451
546
*
452
- * 各表達式及運算子之間以空格隔開。
453
- *
454
- * AND 運算子可省略。
547
+ * 各表達式及運算子之間必須以空格隔開。
455
548
*
456
- * 如 `(端精組 且 入聲) 或 (以母 且 四等 且 去聲)` 與 `端精組 入聲 或 以母 四等 去聲` 同義。
549
+ * AND 運算子可省略, 如 `(端精組 且 入聲) 或 (以母 且 四等 且 去聲)` 與 `端精組 入聲 或 以母 四等 去聲` 同義。
457
550
* @returns 若描述音韻地位的字串符合該音韻地位,回傳 `true`;否則回傳 `false`。
458
551
* @throws 若表達式為空、不合語法、或限定條件不合法,則拋出異常。
459
552
* @example
460
553
* ```typescript
461
554
* > 音韻地位 = Qieyun.音韻地位.from描述('幫三凡入');
462
- * > 音韻地位.屬於`章母`; // 標籤模板語法(表達式為字面值時推荐 )
555
+ * > 音韻地位.屬於`章母`; // 標籤模板語法(表達式為字面值時推薦 )
463
556
* false
464
557
* > 音韻地位.屬於('章母'); // 一般形式
465
558
* false
@@ -476,8 +569,8 @@ export class 音韻地位 {
476
569
*
477
570
* 嵌入的參數可以是:
478
571
*
479
- * * 函數:會被執行;若其傳回值為字串,會遞迴套用至 [[`音韻地位 .屬於`]] 函數,否則會檢測其真值
480
- * * 字串:會遞迴套用至 [[`音韻地位 .屬於`]] 函數
572
+ * * 函數:會被執行;若其傳回值為字串,會遞迴套用至 [[`.屬於`]] 函數,否則會檢測其真值
573
+ * * 字串:會遞迴套用至 [[`.屬於`]] 函數
481
574
* * 其他:會檢測其真值
482
575
*
483
576
* **注意**:
@@ -496,7 +589,7 @@ export class 音韻地位 {
496
589
* true
497
590
* ```
498
591
*/
499
- 屬於 ( 表達式 : readonly string [ ] , ...參數 : unknown [ ] ) : boolean ;
592
+ 屬於 ( 表達式 : TemplateStringsArray , ...參數 : unknown [ ] ) : boolean ;
500
593
501
594
屬於 ( 表達式 : string | readonly string [ ] , ...參數 : unknown [ ] ) : boolean {
502
595
if ( typeof 表達式 === 'string' ) 表達式 = [ 表達式 ] ;
@@ -526,7 +619,7 @@ export class 音韻地位 {
526
619
assert ( ! invalid , invalid + match [ 2 ] + '不存在' ) ;
527
620
return values . includes ( this [ match [ 2 ] as keyof typeof 檢查 ] ) ;
528
621
}
529
- throw new Error ( `unreconized test condition: ${ token } ` ) ;
622
+ throw new Error ( `unrecognized test condition: ${ token } ` ) ;
530
623
} ;
531
624
532
625
// 詞法分析,同時給普通運算元求值(惟函數型運算元留待後面惰性求值)
@@ -684,8 +777,8 @@ export class 音韻地位 {
684
777
*
685
778
* 判斷式可以是:
686
779
*
687
- * *   函數:會被執行;若其傳回值為非空字串,會套用至 [[`音韻地位 .屬於`]] 函數,若為布林值則直接決定是否跳過本規則,否則規則永遠不會被跳過
688
- * * 非空字串:描述音韻地位的表達式,會套用至 [[`音韻地位 .屬於`]] 函數
780
+ * *   函數:會被執行;若其傳回值為非空字串,會套用至 [[`.屬於`]] 函數,若為布林值則直接決定是否跳過本規則,否則規則永遠不會被跳過
781
+ * * 非空字串:描述音韻地位的表達式,會套用至 [[`.屬於`]] 函數
689
782
* *  布林值:直接決定是否跳過本規則
690
783
* *   其他:規則永遠不會被跳過(可用作指定後備結果)
691
784
*
0 commit comments