hyperformula
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
257 lines (255 loc) • 6.9 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.BooleanPlugin = void 0;
var _Cell = require("../../Cell");
var _errorMessage = require("../../error-message");
var _FunctionPlugin = require("./FunctionPlugin");
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
/**
* Interpreter plugin containing boolean functions
*/
class BooleanPlugin extends _FunctionPlugin.FunctionPlugin {
/**
* Corresponds to TRUE()
*
* Returns the logical true
*
* @param ast
* @param state
*/
literalTrue(ast, state) {
return this.runFunction(ast.args, state, this.metadata('TRUE'), () => true);
}
/**
* Corresponds to FALSE()
*
* Returns the logical false
*
* @param ast
* @param state
*/
literalFalse(ast, state) {
return this.runFunction(ast.args, state, this.metadata('FALSE'), () => false);
}
/**
* Corresponds to IF(expression, value_if_true, value_if_false)
*
* Returns value specified as second argument if expression is true and third argument if expression is false
*
* @param ast
* @param state
*/
conditionalIf(ast, state) {
return this.runFunction(ast.args, state, this.metadata('IF'), (condition, arg2, arg3) => {
return condition ? arg2 : arg3;
});
}
/**
* Implementation for the IFS function. Returns the value that corresponds to the first true condition.
*
* @param ast
* @param state
*/
ifs(ast, state) {
return this.runFunction(ast.args, state, this.metadata('IFS'), (...args) => {
for (let idx = 0; idx < args.length; idx += 2) {
if (args[idx]) {
return args[idx + 1];
}
}
return new _Cell.CellError(_Cell.ErrorType.NA, _errorMessage.ErrorMessage.NoConditionMet);
});
}
/**
* Corresponds to AND(expression1, [expression2, ...])
*
* Returns true if all of the provided arguments are logically true, and false if any of it is logically false
*
* @param ast
* @param state
*/
and(ast, state) {
return this.runFunction(ast.args, state, this.metadata('AND'), (...args) => args.filter(arg => arg !== undefined).every(arg => !!arg));
}
/**
* Corresponds to OR(expression1, [expression2, ...])
*
* Returns true if any of the provided arguments are logically true, and false otherwise
*
* @param ast
* @param state
*/
or(ast, state) {
return this.runFunction(ast.args, state, this.metadata('OR'), (...args) => args.filter(arg => arg !== undefined).some(arg => arg));
}
not(ast, state) {
return this.runFunction(ast.args, state, this.metadata('NOT'), arg => !arg);
}
xor(ast, state) {
return this.runFunction(ast.args, state, this.metadata('XOR'), (...args) => {
let cnt = 0;
args.filter(arg => arg !== undefined).forEach(arg => {
if (arg) {
cnt++;
}
});
return cnt % 2 === 1;
});
}
switch(ast, state) {
return this.runFunction(ast.args, state, this.metadata('SWITCH'), (selector, ...args) => {
const n = args.length;
let i = 0;
for (; i + 1 < n; i += 2) {
if (args[i] instanceof _Cell.CellError) {
continue;
}
if (this.arithmeticHelper.eq(selector, args[i])) {
return args[i + 1];
}
}
if (i < n) {
return args[i];
} else {
return new _Cell.CellError(_Cell.ErrorType.NA, _errorMessage.ErrorMessage.NoDefault);
}
});
}
iferror(ast, state) {
return this.runFunction(ast.args, state, this.metadata('IFERROR'), (arg1, arg2) => {
if (arg1 instanceof _Cell.CellError) {
return arg2;
} else {
return arg1;
}
});
}
ifna(ast, state) {
return this.runFunction(ast.args, state, this.metadata('IFNA'), (arg1, arg2) => {
if (arg1 instanceof _Cell.CellError && arg1.type === _Cell.ErrorType.NA) {
return arg2;
} else {
return arg1;
}
});
}
choose(ast, state) {
return this.runFunction(ast.args, state, this.metadata('CHOOSE'), (selector, ...args) => {
if (selector > args.length) {
return new _Cell.CellError(_Cell.ErrorType.NUM, _errorMessage.ErrorMessage.Selector);
}
return args[selector - 1];
});
}
}
exports.BooleanPlugin = BooleanPlugin;
BooleanPlugin.implementedFunctions = {
'TRUE': {
method: 'literalTrue',
parameters: []
},
'FALSE': {
method: 'literalFalse',
parameters: []
},
'IF': {
method: 'conditionalIf',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
defaultValue: false,
passSubtype: true
}]
},
'IFS': {
method: 'ifs',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}],
repeatLastArgs: 2
},
'AND': {
method: 'and',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}],
repeatLastArgs: 1,
expandRanges: true
},
'OR': {
method: 'or',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}],
repeatLastArgs: 1,
expandRanges: true
},
'XOR': {
method: 'xor',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}],
repeatLastArgs: 1,
expandRanges: true
},
'NOT': {
method: 'not',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.BOOLEAN
}]
},
'SWITCH': {
method: 'switch',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.NOERROR
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}],
repeatLastArgs: 1
},
'IFERROR': {
method: 'iferror',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}]
},
'IFNA': {
method: 'ifna',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}]
},
'CHOOSE': {
method: 'choose',
parameters: [{
argumentType: _FunctionPlugin.FunctionArgumentType.INTEGER,
minValue: 1
}, {
argumentType: _FunctionPlugin.FunctionArgumentType.SCALAR,
passSubtype: true
}],
repeatLastArgs: 1
}
};