Last active
May 7, 2022 13:55
-
-
Save RanolP/ccd8edb2a06aa03afa242f710013a76f to your computer and use it in GitHub Desktop.
rke(I like to call it '갇'), Replace Korean Entities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type TailId = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27; | |
function tailIdOf(word: string): TailId { | |
if (word.length === 0) { | |
return -1; | |
} | |
const last = word[word.length - 1]; | |
if (last < '가' || '힣' < last) { | |
return -1; | |
} | |
// 위의 if 문에서 '가' <= last이므로 last.charCodeAt(0)은 양의 정수고 | |
// 그 값을 28로 나눈 나머지이므로 0 ~ 27 범위입니다. | |
// 따라서 TailId라고 단언할 수 있습니다. | |
return (last.charCodeAt(0) - 44032) % 28 as TailId; | |
} | |
export function rke(template: TemplateStringsArray, ...args: unknown[]): string { | |
let result = ''; | |
for (let i = 0; i < template.raw.length; i++) { | |
const argument = i == 0 ? null : String(args[i - 1]); | |
if (argument !== null) { | |
result += argument; | |
} | |
const current = template.raw[i]; | |
if (current.length === 0) { | |
continue; | |
} | |
const [head, ...splitted] = current.split('\\'); | |
result += head; | |
for (const token of splitted) { | |
const tail = tailIdOf(result); | |
switch (token[0]) { | |
case '은': | |
case '는': | |
result += tail === -1 ? '은(는)' : tail !== 0 ? '은' : '는'; | |
result += token.slice(1); | |
break; | |
case '이': | |
if (token.length > 1 && token[1] != ' ') { | |
result += tail === -1 ? '(이)' : tail !== 0 ? '이' : ''; | |
result += token.slice(1); | |
break; | |
} | |
case '가': | |
result += tail === -1 ? '이(가)' : tail !== 0 ? '이' : '가'; | |
result += token.slice(1); | |
break; | |
case '을': | |
case '를': | |
result += tail === -1 ? '을(를)' : tail !== 0 ? '을' : '를'; | |
result += token.slice(1); | |
break; | |
case '와': | |
case '과': | |
result += tail === -1 ? '와(과)' : tail !== 0 ? '과' : '와'; | |
result += token.slice(1); | |
break; | |
case '으': | |
result += tail === -1 ? '(으)' : tail !== 0 && tail !== 8 ? '으' : ''; | |
result += token.slice(1); | |
break; | |
default: | |
result += token; | |
continue; | |
} | |
} | |
} | |
return result; | |
} |
아래 코드를 복사해서 콘솔에서 테스트해보세요! (압축한 버전과 타입을 제거한 버전입니다, 둘 중 하나를 쓰세요)
function tailIdOf(e){if(0===e.length)return-1;const c=e[e.length-1];return c<"가"||"힣"<c?-1:(c.charCodeAt(0)-44032)%28}function rke(e,...c){let t="";for(let n=0;n<e.raw.length;n++){const s=0==n?null:String(c[n-1]);null!==s&&(t+=s);const a=e.raw[n];if(0===a.length)continue;const[l,...r]=a.split("\\");t+=l;for(const e of r){const c=tailIdOf(t);switch(e[0]){case"은":case"는":t+=-1===c?"은(는)":0!==c?"은":"는",t+=e.slice(1);break;case"이":if(e.length>1&&" "!=e[1]){t+=-1===c?"(이)":0!==c?"이":"",t+=e.slice(1);break}case"가":t+=-1===c?"이(가)":0!==c?"이":"가",t+=e.slice(1);break;case"을":case"를":t+=-1===c?"을(를)":0!==c?"을":"를",t+=e.slice(1);break;case"와":case"과":t+=-1===c?"와(과)":0!==c?"과":"와",t+=e.slice(1);break;case"으":t+=-1===c?"(으)":0!==c&&8!==c?"으":"",t+=e.slice(1);break;default:t+=e;continue}}}return t}
function tailIdOf(word) {
if (word.length === 0) {
return -1;
}
const last = word[word.length - 1];
if (last < '가' || '힣' < last) {
return -1;
}
// 위의 if 문에서 '가' <= last이므로 last.charCodeAt(0)은 양의 정수고
// 그 값을 28로 나눈 나머지이므로 0 ~ 27 범위입니다.
// 따라서 TailId라고 단언할 수 있습니다.
return (last.charCodeAt(0) - 44032) % 28;
}
function rke(template, ...args) {
let result = '';
for (let i = 0; i < template.raw.length; i++) {
const argument = i == 0 ? null : String(args[i - 1]);
if (argument !== null) {
result += argument;
}
const current = template.raw[i];
if (current.length === 0) {
continue;
}
const [head, ...splitted] = current.split('\\');
result += head;
for (const token of splitted) {
const tail = tailIdOf(result);
switch (token[0]) {
case '은':
case '는':
result += tail === -1 ? '은(는)' : tail !== 0 ? '은' : '는';
result += token.slice(1);
break;
case '이':
if (token.length > 1 && token[1] != ' ') {
result += tail === -1 ? '(이)' : tail !== 0 ? '이' : '';
result += token.slice(1);
break;
}
case '가':
result += tail === -1 ? '이(가)' : tail !== 0 ? '이' : '가';
result += token.slice(1);
break;
case '을':
case '를':
result += tail === -1 ? '을(를)' : tail !== 0 ? '을' : '를';
result += token.slice(1);
break;
case '와':
case '과':
result += tail === -1 ? '와(과)' : tail !== 0 ? '과' : '와';
result += token.slice(1);
break;
case '으':
result += tail === -1 ? '(으)' : tail !== 0 && tail !== 8 ? '으' : '';
result += token.slice(1);
break;
default:
result += token;
continue;
}
}
}
return result;
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
rke
사용법
자세한 명세
은/는, 이/가, 을/를, 와/과, (으)*, (이)*를 처리합니다.
이/가와 (이)*는
\이
로 겹치기 때문에\이
뒤에 공백이 아닌 문자가 있을 때만 (이)*로 간주합니다.모든 값은
String(v)
를 통해 문자열화 합니다.영문이나 숫자 등 받침 유무를 알 수 없는 문자가 앞말이라면 (으)로, (이)지만, 이(가) 등으로 표시합니다.
한계
이스케이프 문자를 사용할 경우 올바르게 처리하지 않습니다. 다음과 같이 우회해야 합니다.