@yar.ua/numerals
Version:
Number to text - Inflector for Ukrainian numerals
103 lines (102 loc) • 3.82 kB
JavaScript
import { DummySyntaxNode } from "./syntax/node.js";
import { NumeralLexeme } from "./lexeme/numeral/numeral.js";
import { Relation } from "./syntax/relation.js";
import { ensureInteger, RangeError } from "./errors.js";
import { pop_triplet, get_relation_type, triplet_into_lexemes } from "./utils.js";
export const MAX_DIGIT_COUNT = 27;
export function inflect(whole, form, insert_ones = false, strict_range = false) {
const root = new DummySyntaxNode(form);
build_tree(root, whole, insert_ones, strict_range);
root.agree();
return root.text();
}
export function build_tree(root, whole, insert_ones = false, strict_range = false) {
whole = ensureInteger(whole);
whole = whole.replace(/^0*/, "");
if (whole.length === 0) {
root.add_child(NumeralLexeme.ordinal("0"), Relation.nummod);
return;
}
const digits = whole.split("");
build_tree_from_digits(root, digits, insert_ones, strict_range);
}
export function build_tree_from_digits(root, digits, insert_ones = false, strict_range = false) {
if (digits.length > MAX_DIGIT_COUNT) {
if (strict_range) {
throw new RangeError(digits, MAX_DIGIT_COUNT);
}
else {
return build_tree_digit_wise(root, digits);
}
}
let power = 0;
while (digits.length > 0) {
const [ones, tens, hundreds] = pop_triplet(digits);
const numbers = triplet_into_monolexemic_numbers(hundreds, tens, ones);
if (numbers.length > 0) {
if (power === 0) {
const [ord, ...card] = numbers;
root.add_child(NumeralLexeme.ordinal(ord), Relation.amod);
card.forEach((n) => root.add_child(NumeralLexeme.cardinal(n), Relation.num));
break;
}
else {
const large_number = "1" + "0".repeat(power);
if (numbers.length === 1 && numbers[0] === "1") {
root.add_child(NumeralLexeme.ordinal(large_number), Relation.amod);
}
else {
const compound_lexeme = NumeralLexeme.ordinal_compound(numbers, large_number);
root.add_child(compound_lexeme, Relation.nummod);
}
break;
}
}
power += 3;
}
power += 3;
let is_first = true;
let current_root = root;
while (digits.length > 0) {
const [ones, tens, hundreds] = pop_triplet(digits);
const lexemes = triplet_into_lexemes(hundreds, tens, ones);
if (lexemes.length > 0) {
const countable_lexeme = NumeralLexeme.cardinal("1" + "0".repeat(power));
current_root = root.add_child(countable_lexeme, Relation.num);
is_first = true;
if (!insert_ones) {
if (lexemes.length === 1 && lexemes[0].value == "1") {
continue;
}
}
lexemes.forEach((lexeme) => {
current_root.add_child(lexeme, get_relation_type(lexeme, is_first));
is_first = false;
});
}
power += 3;
}
}
function build_tree_digit_wise(root, digits) {
const ordinal_part = digits.pop();
root.add_child(NumeralLexeme.ordinal(ordinal_part), Relation.nummod);
digits.reverse().forEach((digit) => root.add_child(NumeralLexeme.cardinal(digit), Relation.num));
}
function triplet_into_monolexemic_numbers(hundreds, tens, ones) {
const result = [];
if (tens === "1") {
result.push(tens + ones);
}
else {
if (ones !== "0") {
result.push(ones);
}
if (tens !== "0") {
result.push(tens + "0");
}
}
if (hundreds !== "0") {
result.push(hundreds + "00");
}
return result;
}