bigfloat.js
Version:
A library for arbitrary precision floating point arithmetic.
200 lines (199 loc) • 7.73 kB
JavaScript
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.evaluate = void 0;
var arithmetic_js_1 = require("./arithmetic.js");
var constants_js_1 = require("./constants.js");
var constructors_js_1 = require("./constructors.js");
var predicates_js_1 = require("./predicates.js");
var relational_js_1 = require("./relational.js");
function evaluate(source, precision) {
if (precision === void 0) { precision = constants_js_1.PRECISION; }
if (typeof source !== "string") {
throw Error("The first parameter was expected to be a string.");
}
// This function relies on an algorithm that fully parenthesizes the expression
function parenthesize(expr) {
return ("((((" +
expr
.replace(/\(/g, "((((")
.replace(/\)/g, "))))")
.replace(/(^|[^!])===?/g, ")))==(((")
.replace(/<=/g, ")))<=(((")
.replace(/>=/g, ")))>=(((")
.replace(/<(?!=)/g, ")))<(((")
.replace(/>(?!=)/g, ")))>(((")
.replace(/!==?/g, ")))!=(((")
.replace(/(^|[^e])\+/g, "))+((")
.replace(/(^|[^e])-(?!\d)/g, "))-((")
.replace(/\^|\*\*/g, "**")
.replace(/(^|[^*])\*(?!\*)/g, ")*(")
.replace(/\//g, ")/(")
.replace(/%/g, ")%(")
.replace(/ /g, "") +
"))))");
}
var expression = parenthesize(source);
var rx_tokens = /(-?\d+(?:\.\d+)?(?:e(-?|\+?)\d+)?)|(\(|\))|(\+|-|\/|\*\*|==|!=|<=?|>=?|\*|\^|%)/g;
// Capture groups
// [1] Number
// [2] Paren
// [3] Operator
// Tokenize the expression
var tokens = (expression.match(rx_tokens) || []).map(function (element) {
var parens = ["(", ")"];
var operators = [
"+",
"-",
"*",
"**",
"/",
"%",
"==",
"!=",
"<",
">",
"<=",
">="
];
if (element === "%") {
throw Error("The modulo operator is not supported yet.");
}
if (parens.includes(element)) {
return {
type: "paren",
value: element
};
}
else if (operators.includes(element)) {
return {
type: "operator",
value: element
};
}
else if (predicates_js_1.is_number(element)) {
return {
type: "number",
value: constructors_js_1.normalize(constructors_js_1.make(element.replace("+", "")))
};
}
throw Error("Unexpected token \"" + element + "\"");
});
var n = 0;
tokens.forEach(function (element, index) {
if (element.value === "**") {
if (tokens[index + 2].value === "**") {
tokens.splice(index + 1, 0, { value: "(", type: "paren" });
n += 1;
}
else {
while (n) {
n -= 1;
tokens.splice(index + 3, 0, { value: ")", type: "paren" });
}
}
}
});
// Recursively resolve the parentheses
function resolve(arr) {
// Remove parens when there's only one value
if (arr.length <= 3) {
return [arr[1]];
}
var last_left_paren;
var i = 0;
var _loop_1 = function () {
if (typeof last_left_paren === "number") {
var value = arr[i].value;
if (value === "(") {
last_left_paren = i;
}
if (value === ")") {
var start = arr.slice(0, last_left_paren);
var term = arr.splice(last_left_paren, i - last_left_paren + 1);
var end = arr.slice(last_left_paren, arr.length);
return { value: resolve(__spreadArrays(start, resolve(term), end)) };
}
if (arr[i].type === "operator" && arr[i + 1].type !== "paren") {
var start = arr.slice(0, arr[i + 2].type === "operator" || arr[i + 2].type === "paren"
? last_left_paren + 1
: last_left_paren);
var ops = arr.splice(i - 1, 3);
var end = arr.slice(arr[i - 1].type === "operator" ||
(arr[i + 1] || {}).type === "paren" ||
i >= arr.length
? i - 1
: i, arr.length);
var a_1 = ops[0].value;
var b_1 = ops[2].value;
var operator = ops[1].value;
var bigfloat_return = {
"+": function () {
return arithmetic_js_1.add(a_1, b_1);
},
"-": function () {
return arithmetic_js_1.sub(a_1, b_1);
},
"*": function () {
return arithmetic_js_1.mul(a_1, b_1);
},
"/": function () {
return predicates_js_1.is_zero(b_1) ? constants_js_1.ZERO : arithmetic_js_1.div(a_1, b_1, precision);
},
"**": function () {
return arithmetic_js_1.exponentiation(a_1, b_1);
}
}[operator];
var boolean_return = {
"==": function () {
return relational_js_1.eq(a_1, b_1);
},
"!=": function () {
return !relational_js_1.eq(a_1, b_1);
},
"<": function () {
return relational_js_1.lt(a_1, b_1);
},
">": function () {
return relational_js_1.gt(a_1, b_1);
},
"<=": function () {
return relational_js_1.lt(a_1, b_1) || relational_js_1.eq(a_1, b_1);
},
">=": function () {
return relational_js_1.gt(a_1, b_1) || relational_js_1.eq(a_1, b_1);
}
}[operator];
var res = bigfloat_return
? {
type: "number",
value: bigfloat_return()
}
: {
type: "boolean",
value: boolean_return()
};
return { value: resolve(__spreadArrays(start, [res], end)) };
}
}
i += 1;
};
while (i <= arr.length) {
var state_1 = _loop_1();
if (typeof state_1 === "object")
return state_1.value;
}
}
var result = resolve(tokens)[0];
if (result.type === "number") {
return constructors_js_1.string(result.value);
}
return result.value;
}
exports.evaluate = evaluate;
;