ya-kansuji 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/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/index.cjs +401 -0
- package/dist/index.d.cts +148 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.iife.min.js +3 -0
- package/dist/index.js +389 -0
- package/package.json +69 -0
- package/src/constants.ts +16 -0
- package/src/formatter/gov.ts +21 -0
- package/src/formatter/judic.ts +42 -0
- package/src/formatter/lawyer.ts +22 -0
- package/src/formatter/simple.ts +30 -0
- package/src/formatters.ts +88 -0
- package/src/groups.ts +31 -0
- package/src/index.ts +13 -0
- package/src/parse.ts +136 -0
package/src/groups.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { UNIT_EXP4 } from './constants.js'
|
|
2
|
+
|
|
3
|
+
const GROUP_BASE = 10_000n
|
|
4
|
+
const GROUP_UNITS = ['', ...UNIT_EXP4] as const
|
|
5
|
+
const GROUP_COUNT = GROUP_UNITS.length
|
|
6
|
+
const MAX_EXCLUSIVE = GROUP_BASE ** BigInt(GROUP_COUNT)
|
|
7
|
+
const MAX_DIGITS = GROUP_COUNT * 4
|
|
8
|
+
|
|
9
|
+
// 有効な最上位から4桁ごとのグループを返す。途中のゼログループも含む。
|
|
10
|
+
export function groups4(num: bigint): Array<[number, string]> {
|
|
11
|
+
if (num < 0n || num >= MAX_EXCLUSIVE) {
|
|
12
|
+
throw new RangeError(`Kansuji formatters require a bigint between 0 and 10^${MAX_DIGITS} - 1`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ret: Array<[number, string]> = []
|
|
16
|
+
let value = num
|
|
17
|
+
let unitIndex = 0
|
|
18
|
+
do {
|
|
19
|
+
const unit = GROUP_UNITS[unitIndex]
|
|
20
|
+
if (unit === undefined) {
|
|
21
|
+
throw new RangeError(
|
|
22
|
+
`Kansuji formatters require a bigint between 0 and 10^${MAX_DIGITS} - 1`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
ret.push([Number(value % GROUP_BASE), unit])
|
|
26
|
+
value /= GROUP_BASE
|
|
27
|
+
unitIndex++
|
|
28
|
+
} while (value > 0n)
|
|
29
|
+
|
|
30
|
+
return ret.reverse()
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { toBigInt, toNumber } from './parse.js'
|
|
2
|
+
export { UNIT_EXP3, UNIT_EXP4 } from './constants.js'
|
|
3
|
+
export {
|
|
4
|
+
getFormatter,
|
|
5
|
+
registerFormatter,
|
|
6
|
+
toKan,
|
|
7
|
+
type KansujiFormatter,
|
|
8
|
+
type KansujiFormatterOptions,
|
|
9
|
+
} from './formatters.js'
|
|
10
|
+
export { simple } from './formatter/simple.js'
|
|
11
|
+
export { gov } from './formatter/gov.js'
|
|
12
|
+
export { lawyer } from './formatter/lawyer.js'
|
|
13
|
+
export { judicH, judicV } from './formatter/judic.js'
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { NUM_ALT_CHARS, NUM_NORMALIZED_CHARS, UNIT_EXP3, UNIT_EXP4 } from './constants.js'
|
|
2
|
+
|
|
3
|
+
const altChars = Array.from(NUM_ALT_CHARS)
|
|
4
|
+
const normChars = Array.from(NUM_NORMALIZED_CHARS)
|
|
5
|
+
const NORMALIZE_MAP = new Map(altChars.map((c, i) => [c, normChars[i] as string]))
|
|
6
|
+
const SEPARATORS = new Set([
|
|
7
|
+
',', ',', '、',
|
|
8
|
+
'\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u0020', '\u00A0', '\u1680',
|
|
9
|
+
'\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007',
|
|
10
|
+
'\u2008', '\u2009', '\u200A', '\u2028', '\u2029', '\u202F', '\u205F', '\u3000',
|
|
11
|
+
'\uFEFF',
|
|
12
|
+
])
|
|
13
|
+
const DIGIT_VALUES = [0n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n] as const
|
|
14
|
+
const SPECIAL_NUMBER_VALUES = new Map<string, bigint>([
|
|
15
|
+
['卄', 20n],
|
|
16
|
+
['廿', 20n],
|
|
17
|
+
['卅', 30n],
|
|
18
|
+
['丗', 30n],
|
|
19
|
+
['卌', 40n],
|
|
20
|
+
['皕', 200n],
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
const SINGLE_UNITS = [...UNIT_EXP3, ...UNIT_EXP4.filter((u) => Array.from(u).length === 1)]
|
|
24
|
+
const MULTI_UNITS = UNIT_EXP4.filter((u) => Array.from(u).length > 1)
|
|
25
|
+
const CHAR_CLASS = [
|
|
26
|
+
...new Set([...altChars, ...normChars, ...SINGLE_UNITS, ...SPECIAL_NUMBER_VALUES.keys()]),
|
|
27
|
+
].join('')
|
|
28
|
+
|
|
29
|
+
const PART_SOURCE = `${MULTI_UNITS.join('|')}|[${CHAR_CLASS}]`
|
|
30
|
+
const KANSUJI_REGEXP = new RegExp(`(?:マイナス)?(?:${PART_SOURCE})+`, 'u')
|
|
31
|
+
const PART_REGEXP = new RegExp(PART_SOURCE, 'gu')
|
|
32
|
+
|
|
33
|
+
function createUnitValues(units: readonly string[], base: bigint): Map<string, bigint> {
|
|
34
|
+
let value = base
|
|
35
|
+
return new Map(units.map((unit) => {
|
|
36
|
+
const entry: [string, bigint] = [unit, value]
|
|
37
|
+
value *= base
|
|
38
|
+
return entry
|
|
39
|
+
}))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const UNIT_EXP3_VALUES = createUnitValues(UNIT_EXP3, 10n)
|
|
43
|
+
const UNIT_EXP4_VALUES = createUnitValues(UNIT_EXP4, 10_000n)
|
|
44
|
+
const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER)
|
|
45
|
+
|
|
46
|
+
function clean(str: string): string {
|
|
47
|
+
let result = ''
|
|
48
|
+
for (const c of str) {
|
|
49
|
+
const normalized = NORMALIZE_MAP.get(c) ?? c
|
|
50
|
+
if (!SEPARATORS.has(normalized)) result += normalized
|
|
51
|
+
}
|
|
52
|
+
return result
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 漢数字・大字・全角数字が混じったテキストを bigint に変換する。
|
|
57
|
+
*
|
|
58
|
+
* 照合前にカンマ・読点・各種空白などの区切り文字を除去し、先頭から連続してマッチする
|
|
59
|
+
* 数値表現だけを読み取る(例: `toBigInt('五x六')` は `5n`)。マッチしなければ `0n` を返す。
|
|
60
|
+
* 先頭の「マイナス」は負号として扱うが、ASCII の `-` や U+2212(−)は負号として扱わない。
|
|
61
|
+
* 値の上限はなく、無量大数(10^68)を超える大きさもそのまま扱える。
|
|
62
|
+
*
|
|
63
|
+
* @param str 変換対象の文字列
|
|
64
|
+
* @returns テキストが表す整数値
|
|
65
|
+
* @example
|
|
66
|
+
* toBigInt('一〇二四') // => 1024n
|
|
67
|
+
* toBigInt('マイナス千二百') // => -1200n
|
|
68
|
+
* toBigInt('一無量大数') // => 10n ** 68n
|
|
69
|
+
*/
|
|
70
|
+
export function toBigInt(str: string): bigint {
|
|
71
|
+
const cleaned = clean(String(str))
|
|
72
|
+
const matched = KANSUJI_REGEXP.exec(cleaned)
|
|
73
|
+
if (!matched) return 0n
|
|
74
|
+
const matchedText = matched[0]
|
|
75
|
+
if (matchedText === undefined) return 0n
|
|
76
|
+
|
|
77
|
+
let ret3 = 0n
|
|
78
|
+
let ret4 = 0n
|
|
79
|
+
let curnum: bigint | null = null
|
|
80
|
+
for (const c of matchedText.match(PART_REGEXP) ?? []) {
|
|
81
|
+
if (c >= '1' && c <= '9') {
|
|
82
|
+
const digit = DIGIT_VALUES[c.charCodeAt(0) - 48]!
|
|
83
|
+
curnum = (curnum ?? 0n) * 10n + digit
|
|
84
|
+
} else if (c === '0') {
|
|
85
|
+
if (curnum !== null) curnum *= 10n
|
|
86
|
+
} else {
|
|
87
|
+
const specialNumber = SPECIAL_NUMBER_VALUES.get(c)
|
|
88
|
+
if (specialNumber !== undefined) {
|
|
89
|
+
ret3 += specialNumber
|
|
90
|
+
curnum = null
|
|
91
|
+
} else {
|
|
92
|
+
const unit4 = UNIT_EXP4_VALUES.get(c)
|
|
93
|
+
if (unit4 !== undefined) {
|
|
94
|
+
if (curnum !== null) {
|
|
95
|
+
ret3 += curnum
|
|
96
|
+
curnum = null
|
|
97
|
+
}
|
|
98
|
+
if (ret3 === 0n) ret3 = 1n
|
|
99
|
+
ret4 += ret3 * unit4
|
|
100
|
+
ret3 = 0n
|
|
101
|
+
} else {
|
|
102
|
+
const unit3 = UNIT_EXP3_VALUES.get(c)
|
|
103
|
+
if (unit3 !== undefined) {
|
|
104
|
+
curnum ??= 1n
|
|
105
|
+
ret3 += curnum * unit3
|
|
106
|
+
curnum = null
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (curnum !== null) ret3 += curnum
|
|
113
|
+
const ret = ret4 + ret3
|
|
114
|
+
return matchedText.startsWith('マイナス') ? -ret : ret
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* {@link toBigInt} と同じ規則でテキストを解釈し、`number` として返す。
|
|
119
|
+
*
|
|
120
|
+
* 結果の絶対値が `Number.MAX_SAFE_INTEGER`(2^53 − 1)を超える場合は `RangeError` を
|
|
121
|
+
* 投げる。安全整数を超えうる値を扱うときは {@link toBigInt} を使うこと。
|
|
122
|
+
*
|
|
123
|
+
* @param str 変換対象の文字列
|
|
124
|
+
* @returns テキストが表す数値
|
|
125
|
+
* @throws {RangeError} 値が安全整数の範囲を超える場合
|
|
126
|
+
* @example
|
|
127
|
+
* toNumber('一〇二四') // => 1024
|
|
128
|
+
* toNumber('一無量大数') // throws RangeError
|
|
129
|
+
*/
|
|
130
|
+
export function toNumber(str: string): number {
|
|
131
|
+
const value = toBigInt(str)
|
|
132
|
+
if (value > MAX_SAFE_BIGINT || value < -MAX_SAFE_BIGINT) {
|
|
133
|
+
throw new RangeError(`kansuji value exceeds Number.MAX_SAFE_INTEGER: ${str}`)
|
|
134
|
+
}
|
|
135
|
+
return Number(value)
|
|
136
|
+
}
|