hyperformula
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
174 lines (172 loc) • 5.86 kB
JavaScript
;
exports.__esModule = true;
exports.CellContentParser = exports.CellContent = void 0;
exports.isBoolean = isBoolean;
exports.isError = isError;
exports.isFormula = isFormula;
var _Cell = require("./Cell");
var _DateTimeHelper = require("./DateTimeHelper");
var _errorMessage = require("./error-message");
var _errors = require("./errors");
var _ArithmeticHelper = require("./interpreter/ArithmeticHelper");
var _InterpreterValue = require("./interpreter/InterpreterValue");
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
var CellContent;
(function (CellContent) {
class Number {
constructor(value) {
this.value = value;
this.value = (0, _InterpreterValue.cloneNumber)(this.value, (0, _ArithmeticHelper.fixNegativeZero)((0, _InterpreterValue.getRawValue)(this.value)));
}
}
CellContent.Number = Number;
class String {
constructor(value) {
this.value = value;
}
}
CellContent.String = String;
class Boolean {
constructor(value) {
this.value = value;
}
}
CellContent.Boolean = Boolean;
class Empty {
static getSingletonInstance() {
if (!Empty.instance) {
Empty.instance = new Empty();
}
return Empty.instance;
}
}
CellContent.Empty = Empty;
class Formula {
constructor(formula) {
this.formula = formula;
}
}
CellContent.Formula = Formula;
class Error {
constructor(errorType, message) {
this.value = new _Cell.CellError(errorType, message);
}
}
CellContent.Error = Error;
})(CellContent || (exports.CellContent = CellContent = {}));
/**
* Checks whether string looks like formula or not.
*
* @param text - formula
*/
function isFormula(text) {
return text.startsWith('=');
}
function isBoolean(text) {
const tl = text.toLowerCase();
return tl === 'true' || tl === 'false';
}
function isError(text, errorMapping) {
const upperCased = text.toUpperCase();
const errorRegex = /#[A-Za-z0-9\/]+[?!]?/;
return errorRegex.test(upperCased) && Object.prototype.hasOwnProperty.call(errorMapping, upperCased);
}
class CellContentParser {
constructor(config, dateHelper, numberLiteralsHelper) {
this.config = config;
this.dateHelper = dateHelper;
this.numberLiteralsHelper = numberLiteralsHelper;
}
parse(content) {
if (content === undefined || content === null) {
return CellContent.Empty.getSingletonInstance();
} else if (typeof content === 'number') {
if ((0, _ArithmeticHelper.isNumberOverflow)(content)) {
return new CellContent.Error(_Cell.ErrorType.NUM, _errorMessage.ErrorMessage.ValueLarge);
} else {
return new CellContent.Number(content);
}
} else if (typeof content === 'boolean') {
return new CellContent.Boolean(content);
} else if (content instanceof Date) {
const dateVal = this.dateHelper.dateToNumber({
day: content.getDate(),
month: content.getMonth() + 1,
year: content.getFullYear()
});
const timeVal = (0, _DateTimeHelper.timeToNumber)({
hours: content.getHours(),
minutes: content.getMinutes(),
seconds: content.getSeconds() + content.getMilliseconds() / 1000
});
const val = dateVal + timeVal;
if (val < 0) {
return new CellContent.Error(_Cell.ErrorType.NUM, _errorMessage.ErrorMessage.DateBounds);
}
if (val % 1 === 0) {
return new CellContent.Number(new _InterpreterValue.DateNumber(val, 'Date()'));
} else if (val < 1) {
return new CellContent.Number(new _InterpreterValue.TimeNumber(val, 'Date()'));
} else {
return new CellContent.Number(new _InterpreterValue.DateTimeNumber(val, 'Date()'));
}
} else if (typeof content === 'string') {
if (isBoolean(content)) {
return new CellContent.Boolean(content.toLowerCase() === 'true');
} else if (isFormula(content)) {
return new CellContent.Formula(content);
} else if (isError(content, this.config.errorMapping)) {
return new CellContent.Error(this.config.errorMapping[content.toUpperCase()]);
} else {
let trimmedContent = content.trim();
let mode = 0;
let currency;
if (trimmedContent.endsWith('%')) {
mode = 1;
trimmedContent = trimmedContent.slice(0, trimmedContent.length - 1);
} else {
const res = this.currencyMatcher(trimmedContent);
if (res !== undefined) {
mode = 2;
[currency, trimmedContent] = res;
}
}
const val = this.numberLiteralsHelper.numericStringToMaybeNumber(trimmedContent);
if (val !== undefined) {
let parseAsNum;
if (mode === 1) {
parseAsNum = new _InterpreterValue.PercentNumber(val / 100);
} else if (mode === 2) {
parseAsNum = new _InterpreterValue.CurrencyNumber(val, currency);
} else {
parseAsNum = val;
}
return new CellContent.Number(parseAsNum);
}
const parsedDateNumber = this.dateHelper.dateStringToDateNumber(trimmedContent);
if (parsedDateNumber !== undefined) {
return new CellContent.Number(parsedDateNumber);
} else {
return new CellContent.String(content.startsWith('\'') ? content.slice(1) : content);
}
}
} else {
throw new _errors.UnableToParseError(content);
}
}
currencyMatcher(token) {
for (const currency of this.config.currencySymbol) {
if (token.startsWith(currency)) {
return [currency, token.slice(currency.length)];
}
if (token.endsWith(currency)) {
return [currency, token.slice(0, token.length - currency.length)];
}
}
return undefined;
}
}
exports.CellContentParser = CellContentParser;