-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 한글 문장과 문자가 담긴 배열을 인자로 받아 규칙에 맞게 합성하는 assemble
함수 추가
#64
Changes from all commits
c138416
f07fd0c
d997e34
ae512d0
699cb9c
f79ae8c
2db83af
e8b8a43
4de0ab9
7626595
187130a
f26800c
daefd1e
dfdc14e
71f8a33
59f0379
d791949
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"es-hangul": minor | ||
--- | ||
|
||
feat: 한글 문장과 문자가 담긴 배열을 인자로 받아 규칙에 맞게 합성하는 `assemble` 함수 추가 |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { describe, it, expect, assert } from 'vitest'; | ||
import { binaryAssembleHangulCharacters, binaryAssembleHangul, isHangulAlphabet, isHangulCharacter } from './hangul'; | ||
|
||
describe('isHangul*', () => { | ||
it('isHangulCharacter는 완성된 한글 문자를 받으면 true를 반환한다', () => { | ||
expect(isHangulCharacter('가')).toBe(true); | ||
expect(isHangulCharacter('값')).toBe(true); | ||
expect(isHangulCharacter('ㄱ')).toBe(false); | ||
expect(isHangulCharacter('ㅏ')).toBe(false); | ||
expect(isHangulCharacter('a')).toBe(false); | ||
}); | ||
it('isHangulAlphabet은 조합되지않은 한글 문자를 받으면 true를 반환한다', () => { | ||
expect(isHangulAlphabet('가')).toBe(false); | ||
expect(isHangulAlphabet('값')).toBe(false); | ||
expect(isHangulAlphabet('ㄱ')).toBe(true); | ||
expect(isHangulAlphabet('ㅏ')).toBe(true); | ||
expect(isHangulAlphabet('a')).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('binaryAssembleHangulCharacters', () => { | ||
it('초성과 중성만 조합', () => { | ||
expect(binaryAssembleHangulCharacters('ㄱ', 'ㅏ')).toEqual('가'); | ||
}); | ||
|
||
it('초성과 중성이 합쳐진 문자와 종성을 조합', () => { | ||
expect(binaryAssembleHangulCharacters('가', 'ㅇ')).toEqual('강'); | ||
}); | ||
|
||
it('초성과 중성과 종성이 합쳐진 문자와 자음을 조합하여 겹받침 만들기', () => { | ||
expect(binaryAssembleHangulCharacters('갑', 'ㅅ')).toEqual('값'); | ||
}); | ||
|
||
it('초성과 중성이 합쳐진 문자와 모음을 조립하여 겹모음 만들기', () => { | ||
expect(binaryAssembleHangulCharacters('고', 'ㅏ')).toEqual('과'); | ||
}); | ||
|
||
it('모음만 있는 문자와 모음을 조합하여 겹모음 만들기', () => { | ||
expect(binaryAssembleHangulCharacters('ㅗ', 'ㅏ')).toEqual('ㅘ'); | ||
}); | ||
|
||
it('초성과 중성과 종성이 합쳐진 문자의 연음 법칙', () => { | ||
expect(binaryAssembleHangulCharacters('톳', 'ㅡ')).toEqual('토스'); | ||
}); | ||
|
||
it('초성과 중성과 종성(겹받침)이 합쳐진 문자의 연음 법칙', () => { | ||
expect(binaryAssembleHangulCharacters('닭', 'ㅏ')).toEqual('달가'); | ||
expect(binaryAssembleHangulCharacters('깎', 'ㅏ')).toEqual('까까'); | ||
}); | ||
|
||
it('문법에 맞지 않는 문자를 조합하면 단순 Join 한다 (문법 순서 틀림)', () => { | ||
expect(binaryAssembleHangulCharacters('ㅏ', 'ㄱ')).toEqual('ㅏㄱ'); | ||
expect(binaryAssembleHangulCharacters('까', 'ㅃ')).toEqual('까ㅃ'); | ||
expect(binaryAssembleHangulCharacters('ㅘ', 'ㅏ')).toEqual('ㅘㅏ'); | ||
}); | ||
|
||
it('순서대로 입력했을 때 조합이 불가능한 문자라면 단순 Join 한다', () => { | ||
expect(binaryAssembleHangulCharacters('뼈', 'ㅣ')).toEqual('뼈ㅣ'); | ||
}); | ||
|
||
it('소스가 두 글자 이상이라면 Invalid source 에러를 발생시킨다.', () => { | ||
assert.throws( | ||
() => binaryAssembleHangulCharacters('가나', 'ㄴ'), | ||
Error, | ||
'Invalid source character: 가나. Source must be one character.' | ||
); | ||
assert.throws( | ||
() => binaryAssembleHangulCharacters('ㄱㄴ', 'ㅏ'), | ||
Error, | ||
'Invalid source character: ㄱㄴ. Source must be one character.' | ||
); | ||
}); | ||
|
||
it('다음 문자가 한글 문자 한 글자가 아니라면 Invalid next character 에러를 발생시킨다.', () => { | ||
assert.throws( | ||
() => binaryAssembleHangulCharacters('ㄱ', 'a'), | ||
Error, | ||
'Invalid next character: a. Next character must be one of the chosung, jungsung, or jongsung.' | ||
); | ||
assert.throws( | ||
() => binaryAssembleHangulCharacters('ㄱ', 'ㅡㅏ'), | ||
Error, | ||
'Invalid next character: ㅡㅏ. Next character must be one of the chosung, jungsung, or jongsung.' | ||
); | ||
}); | ||
}); | ||
|
||
describe('binaryAssembleHangul', () => { | ||
it('문장과 모음을 조합하여 다음 글자를 생성한다', () => { | ||
expect(binaryAssembleHangul('저는 고양이를 좋아합닏', 'ㅏ')).toEqual('저는 고양이를 좋아합니다'); | ||
}); | ||
|
||
it('문장과 자음을 조합하여 홑받침을 생성한다', () => { | ||
expect(binaryAssembleHangul('저는 고양이를 좋아하', 'ㅂ')).toEqual('저는 고양이를 좋아합'); | ||
}); | ||
|
||
it('문장과 자음을 조합하여 겹받침을 생성한다', () => { | ||
expect(binaryAssembleHangul('저는 고양이를 좋아합', 'ㅅ')).toEqual('저는 고양이를 좋아핪'); | ||
}); | ||
|
||
it('조합이 불가능한 자음이 입력되면 단순 Join 한다', () => { | ||
expect(binaryAssembleHangul('저는 고양이를 좋아합', 'ㄲ')).toEqual('저는 고양이를 좋아합ㄲ'); | ||
expect(binaryAssembleHangul('저는 고양이를 좋아합', 'ㅂ')).toEqual('저는 고양이를 좋아합ㅂ'); | ||
}); | ||
|
||
it('조합이 불가능한 모음이 입력되면 단순 Join 한다', () => { | ||
expect(binaryAssembleHangul('저는 고양이를 좋아하', 'ㅏ')).toEqual('저는 고양이를 좋아하ㅏ'); | ||
expect(binaryAssembleHangul('저는 고양이를 좋아합니다', 'ㅜ')).toEqual('저는 고양이를 좋아합니다ㅜ'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import assert, { excludeLastElement, isBlank, joinString } from '.'; | ||
import { combineHangulCharacter, combineVowels, curriedCombineHangulCharacter } from '../combineHangulCharacter'; | ||
import { disassembleHangulToGroups } from '../disassemble'; | ||
import { removeLastHangulCharacter } from '../removeLastHangulCharacter'; | ||
import { canBeChosung, canBeJongsung, canBeJungsung, hasSingleBatchim } from '../utils'; | ||
|
||
export function isHangulCharacter(character: string) { | ||
return /^[가-힣]$/.test(character); | ||
} | ||
|
||
export function isHangulAlphabet(character: string) { | ||
return /^[ㄱ-ㅣ]$/.test(character); | ||
} | ||
|
||
/** | ||
* @name binaryAssembleHangulAlphabets | ||
* @description | ||
* 두 개의 한글 자모를 합칩니다. 완성된 한글 문자는 취급하지 않습니다. | ||
* @example | ||
* ``` | ||
* binaryAssembleHangulAlphabets('ㄱ', 'ㅏ') // 가 | ||
* binaryAssembleHangulAlphabets('ㅗ', 'ㅏ') // ㅘ | ||
* ``` | ||
*/ | ||
export function binaryAssembleHangulAlphabets(source: string, nextCharacter: string) { | ||
if (canBeJungsung(`${source}${nextCharacter}`)) { | ||
return combineVowels(source, nextCharacter); | ||
} | ||
|
||
const isConsonantSource = canBeJungsung(source) === false; | ||
if (isConsonantSource && canBeJungsung(nextCharacter)) { | ||
return combineHangulCharacter(source, nextCharacter); | ||
} | ||
|
||
return joinString(source, nextCharacter); | ||
} | ||
|
||
/** | ||
* @name linkHangulCharacters | ||
* @description | ||
* 연음 법칙을 적용하여 두 개의 한글 문자를 연결합니다. | ||
*/ | ||
export function linkHangulCharacters(source: string, nextCharacter: string) { | ||
const sourceJamo = disassembleHangulToGroups(source)[0]; | ||
const [, lastJamo] = excludeLastElement(sourceJamo); | ||
|
||
return joinString(removeLastHangulCharacter(source), combineHangulCharacter(lastJamo, nextCharacter)); | ||
} | ||
|
||
/** | ||
* @name binaryAssembleHangulCharacters | ||
* @description | ||
* 인자로 받은 한글 문자 2개를 합성합니다. | ||
* ```typescript | ||
* binaryAssembleHangulCharacters( | ||
* // 소스 문자 | ||
* source: string | ||
* // 다음 문자 | ||
* nextCharacter: string | ||
* ): string | ||
* ``` | ||
* @example | ||
* binaryAssembleHangulCharacters('ㄱ', 'ㅏ') // 가 | ||
* binaryAssembleHangulCharacters('가', 'ㅇ') // 강 | ||
* binaryAssembleHangulCharacters('갑', 'ㅅ') // 값 | ||
* binaryAssembleHangulCharacters('깎', 'ㅏ') // 까까 | ||
*/ | ||
export function binaryAssembleHangulCharacters(source: string, nextCharacter: string) { | ||
assert( | ||
isHangulCharacter(source) || isHangulAlphabet(source), | ||
`Invalid source character: ${source}. Source must be one character.` | ||
); | ||
assert( | ||
isHangulAlphabet(nextCharacter), | ||
`Invalid next character: ${nextCharacter}. Next character must be one of the chosung, jungsung, or jongsung.` | ||
); | ||
|
||
const sourceJamos = disassembleHangulToGroups(source)[0]; | ||
|
||
const isSingleCharacter = sourceJamos.length === 1; | ||
if (isSingleCharacter) { | ||
const sourceCharacter = sourceJamos[0]; | ||
return binaryAssembleHangulAlphabets(sourceCharacter, nextCharacter); | ||
} | ||
|
||
const [restJamos, lastJamo] = excludeLastElement(sourceJamos); | ||
|
||
const needLinking = canBeChosung(lastJamo) && canBeJungsung(nextCharacter); | ||
if (needLinking) { | ||
return linkHangulCharacters(source, nextCharacter); | ||
} | ||
|
||
const fixConsonant = curriedCombineHangulCharacter; | ||
const combineJungsung = fixConsonant(restJamos[0]); | ||
Comment on lines
+93
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이제 자음 1개 + 모음 1개로 이루어진, 받침이 없는 문자에 대한 대응을 시작합니다. |
||
|
||
if (canBeJungsung(`${lastJamo}${nextCharacter}`)) { | ||
return combineJungsung(`${lastJamo}${nextCharacter}`)(); | ||
} | ||
|
||
if (canBeJungsung(lastJamo) && canBeJongsung(nextCharacter)) { | ||
return combineJungsung(lastJamo)(nextCharacter); | ||
} | ||
|
||
const fixVowel = combineJungsung; | ||
const combineJongsung = fixVowel(restJamos[1]); | ||
|
||
const lastConsonant = lastJamo; | ||
|
||
if (hasSingleBatchim(source) && canBeJongsung(`${lastConsonant}${nextCharacter}`)) { | ||
return combineJongsung(`${lastConsonant}${nextCharacter}`); | ||
} | ||
Comment on lines
+107
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이미 처음부터 받침을 가지고 있던 문자가 입력되었다면
3개 케이스만 대응하면 됩니다. 연음법칙은 최상단에서 대응했으니, 겹받침을 생성하는 규칙만 적용해주면 됩니다. |
||
|
||
return joinString(source, nextCharacter); | ||
} | ||
|
||
/** | ||
* @name binaryAssembleHangul | ||
* @description | ||
* 인자로 받은 한글 문장과 한글 문자 하나를 합성합니다. | ||
* ```typescript | ||
* binaryAssembleHangul( | ||
* // 한글 문장 | ||
* source: string | ||
* // 한글 문자 | ||
* nextCharacter: string | ||
* ): string | ||
* ``` | ||
* @example | ||
* binaryAssembleHangul('저는 고양이를 좋아합닏', 'ㅏ') // 저는 고양이를 좋아합니다 | ||
* binaryAssembleHangul('저는 고양이를 좋아합', 'ㅅ') // 저는 고양이를 좋아핪 | ||
* binaryAssembleHangul('저는 고양이를 좋아하', 'ㅏ') // 저는 고양이를 좋아하ㅏ | ||
*/ | ||
export function binaryAssembleHangul(source: string, nextCharacter: string) { | ||
const [rest, lastCharacter] = excludeLastElement(source.split('')); | ||
const needJoinString = isBlank(lastCharacter) || isBlank(nextCharacter); | ||
|
||
return joinString( | ||
...rest, | ||
needJoinString | ||
? joinString(lastCharacter, nextCharacter) | ||
: binaryAssembleHangulCharacters(lastCharacter, nextCharacter) | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export function excludeLastElement(array: string[]): [string[], string] { | ||
const lastElement = array.at(-1); | ||
return [array.slice(0, -1), lastElement ?? '']; | ||
} | ||
|
||
export function joinString(...args: string[]) { | ||
return args.join(''); | ||
} | ||
|
||
export function isBlank(character: string) { | ||
return /^\s$/.test(character); | ||
} | ||
|
||
export default function assert(condition: boolean, errorMessage?: string): asserts condition { | ||
if (condition === false) { | ||
throw new Error(errorMessage ?? 'Invalid condition'); | ||
} | ||
} | ||
Comment on lines
+6
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { assembleHangul } from './assemble'; | ||
|
||
describe('assembleHangul', () => { | ||
it('온전한 한글과 한글 문자 조합', () => { | ||
expect(assembleHangul(['아버지가', ' ', '방ㅇ', 'ㅔ ', '들ㅇ', 'ㅓ갑니다'])).toEqual('아버지가 방에 들어갑니다'); | ||
}); | ||
it('온전한 한글만 조합', () => { | ||
expect(assembleHangul(['아버지가', ' ', '방에 ', '들어갑니다'])).toEqual('아버지가 방에 들어갑니다'); | ||
}); | ||
it('온전하지 않은 한글만 조합', () => { | ||
expect(assembleHangul(['ㅇ', 'ㅏ', 'ㅂ', 'ㅓ', 'ㅈ', 'ㅣ'])).toEqual('아버지'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { disassembleHangul } from './disassemble'; | ||
import { binaryAssembleHangul } from './_internal/hangul'; | ||
|
||
/** | ||
* @name assembleHangul | ||
* @description | ||
* 인자로 받은 배열에 담긴 한글 문장과 문자를 한글 규칙에 맞게 합성합니다. | ||
* ```typescript | ||
* assembleHangul( | ||
* // 한글 문자와 문장을 담고 있는 배열 | ||
* words: string[] | ||
* ): string | ||
* ``` | ||
* @example | ||
* assembleHangul(['아버지가', ' ', '방ㅇ', 'ㅔ ', '들ㅇ', 'ㅓ갑니다']) // 아버지가 방에 들어갑니다 | ||
* assembleHangul(['아버지가', ' ', '방에 ', '들어갑니다']) // 아버지가 방에 들어갑니다 | ||
* assembleHangul(['ㅇ', 'ㅏ', 'ㅂ', 'ㅓ', 'ㅈ', 'ㅣ']) // 아버지 | ||
*/ | ||
export function assembleHangul(words: string[]) { | ||
const disassembled = disassembleHangul(words.join('')).split(''); | ||
return disassembled.reduce(binaryAssembleHangul); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마지막 자모가 초성이 될 수 있는 문자이고 다음 입력 문자가 모음이면 연음법칙을 적용합니다.