UNPKG

hyperformula

Version:

HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas

249 lines (247 loc) 5.75 kB
"use strict"; exports.__esModule = true; exports.RomanPlugin = void 0; var _Cell = require("../../Cell"); var _errorMessage = require("../../error-message"); var _InterpreterValue = require("../InterpreterValue"); var _FunctionPlugin = require("./FunctionPlugin"); /** * @license * Copyright (c) 2025 Handsoncode. All rights reserved. */ class RomanPlugin extends _FunctionPlugin.FunctionPlugin { roman(ast, state) { return this.runFunction(ast.args, state, this.metadata('ROMAN'), (val, mode) => { val = Math.trunc(val); if (mode === false) { mode = 4; } else if (mode === true) { mode = 0; } mode = (0, _InterpreterValue.getRawValue)(this.coerceScalarToNumberOrError(mode)); if (mode instanceof _Cell.CellError) { return mode; } mode = Math.trunc(mode); if (mode < 0) { return new _Cell.CellError(_Cell.ErrorType.VALUE, _errorMessage.ErrorMessage.ValueSmall); } if (mode > 4) { return new _Cell.CellError(_Cell.ErrorType.VALUE, _errorMessage.ErrorMessage.ValueLarge); } return romanMode(val, mode); }); } arabic(ast, state) { return this.runFunction(ast.args, state, this.metadata('ARABIC'), inputString => { inputString = inputString.trim().toUpperCase(); let minusSign = false; if (inputString.startsWith('-')) { inputString = inputString.slice(1); minusSign = true; if (inputString === '') { return new _Cell.CellError(_Cell.ErrorType.VALUE, _errorMessage.ErrorMessage.InvalidRoman); } } const work = { input: inputString, acc: 0 }; eatToken(work, { token: 'MMM', val: 3000 }, { token: 'MM', val: 2000 }, { token: 'M', val: 1000 }); eatToken(work, { token: 'IM', val: 999 }, { token: 'VM', val: 995 }, { token: 'XM', val: 990 }, { token: 'LM', val: 950 }, { token: 'CM', val: 900 }); eatToken(work, { token: 'D', val: 500 }, { token: 'ID', val: 499 }, { token: 'VD', val: 495 }, { token: 'XD', val: 490 }, { token: 'LD', val: 450 }, { token: 'CD', val: 400 }); eatToken(work, { token: 'CCC', val: 300 }, { token: 'CC', val: 200 }, { token: 'C', val: 100 }); eatToken(work, { token: 'IC', val: 99 }, { token: 'VC', val: 95 }, { token: 'XC', val: 90 }); eatToken(work, { token: 'L', val: 50 }, { token: 'IL', val: 49 }, { token: 'VL', val: 45 }, { token: 'XL', val: 40 }); eatToken(work, { token: 'XXX', val: 30 }, { token: 'XX', val: 20 }, { token: 'X', val: 10 }); eatToken(work, { token: 'IX', val: 9 }); eatToken(work, { token: 'V', val: 5 }, { token: 'IV', val: 4 }); eatToken(work, { token: 'III', val: 3 }, { token: 'II', val: 2 }, { token: 'I', val: 1 }); if (work.input !== '') { return new _Cell.CellError(_Cell.ErrorType.VALUE, _errorMessage.ErrorMessage.InvalidRoman); } else { return minusSign ? -work.acc : work.acc; } }); } } exports.RomanPlugin = RomanPlugin; RomanPlugin.implementedFunctions = { 'ROMAN': { method: 'roman', parameters: [{ argumentType: _FunctionPlugin.FunctionArgumentType.NUMBER, minValue: 1, lessThan: 4000 }, { argumentType: _FunctionPlugin.FunctionArgumentType.NOERROR, optionalArg: true, defaultValue: 0 }] }, 'ARABIC': { method: 'arabic', parameters: [{ argumentType: _FunctionPlugin.FunctionArgumentType.STRING }] } }; function eatToken(inputAcc, ...tokens) { for (const token of tokens) { if (inputAcc.input.startsWith(token.token)) { inputAcc.input = inputAcc.input.slice(token.token.length); inputAcc.acc += token.val; break; } } } function romanMode(input, mode) { const work = { val: input % 1000, acc: 'M'.repeat(Math.floor(input / 1000)) }; if (mode === 4) { absorb(work, 'IM', 999, 1000); absorb(work, 'ID', 499, 500); } if (mode >= 3) { absorb(work, 'VM', 995, 1000); absorb(work, 'VD', 495, 500); } if (mode >= 2) { absorb(work, 'XM', 990, 1000); absorb(work, 'XD', 490, 500); } if (mode >= 1) { absorb(work, 'LM', 950, 1000); absorb(work, 'LD', 450, 500); } absorb(work, 'CM', 900, 1000); absorb(work, 'CD', 400, 500); absorb(work, 'D', 500, 900); work.acc += 'C'.repeat(Math.floor(work.val / 100)); work.val %= 100; if (mode >= 2) { absorb(work, 'IC', 99, 100); absorb(work, 'IL', 49, 50); } if (mode >= 1) { absorb(work, 'VC', 95, 100); absorb(work, 'VL', 45, 50); } absorb(work, 'XC', 90, 100); absorb(work, 'XL', 40, 50); absorb(work, 'L', 50, 90); work.acc += 'X'.repeat(Math.floor(work.val / 10)); work.val %= 10; absorb(work, 'IX', 9, 10); absorb(work, 'IV', 4, 5); absorb(work, 'V', 5, 9); work.acc += 'I'.repeat(work.val); return work.acc; } function absorb(valAcc, token, lower, upper) { if (valAcc.val >= lower && valAcc.val < upper) { valAcc.val -= lower; valAcc.acc += token; } }