UNPKG

bigfloat-esnext

Version:

A library for arbitrary precision floating point arithmetic.

187 lines (186 loc) 7.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.evaluate = void 0; const arithmetic_js_1 = require("./arithmetic.js"); const constants_js_1 = require("./constants.js"); const constructors_js_1 = require("./constructors.js"); const predicates_js_1 = require("./predicates.js"); const relational_js_1 = require("./relational.js"); function evaluate(source, 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, "") + "))))"; } const expression = parenthesize(source); const rx_tokens = /(-?\d+(?:\.\d+)?(?:e(-?|\+?)\d+)?)|(\(|\))|(\+|-|\/|\*\*|==|!=|<=?|>=?|\*|\^|%)/g; // Capture groups // [1] Number // [2] Paren // [3] Operator // Tokenize the expression const tokens = (expression.match(rx_tokens) || []).map(function (element) { const parens = ["(", ")"]; const 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}"`); }); let 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]]; } let last_left_paren; let i = 0; while (i <= arr.length) { if (typeof last_left_paren === "number") { const { value } = arr[i]; if (value === "(") { last_left_paren = i; } if (value === ")") { const start = arr.slice(0, last_left_paren); const term = arr.splice(last_left_paren, i - last_left_paren + 1); const end = arr.slice(last_left_paren, arr.length); return resolve([...start, ...resolve(term), ...end]); } if (arr[i].type === "operator" && arr[i + 1].type !== "paren") { const start = arr.slice(0, arr[i + 2].type === "operator" || arr[i + 2].type === "paren" ? last_left_paren + 1 : last_left_paren); const ops = arr.splice(i - 1, 3); const end = arr.slice(arr[i - 1].type === "operator" || (arr[i + 1] || {}).type === "paren" || i >= arr.length ? i - 1 : i, arr.length); const a = ops[0].value; const b = ops[2].value; const operator = ops[1].value; const bigfloat_return = { "+"() { return arithmetic_js_1.add(a, b); }, "-"() { return arithmetic_js_1.sub(a, b); }, "*"() { return arithmetic_js_1.mul(a, b); }, "/"() { return predicates_js_1.is_zero(b) ? constants_js_1.ZERO : arithmetic_js_1.div(a, b, precision); }, "**"() { return arithmetic_js_1.exponentiation(a, b); } }[operator]; const boolean_return = { "=="() { return relational_js_1.eq(a, b); }, "!="() { return !relational_js_1.eq(a, b); }, "<"() { return relational_js_1.lt(a, b); }, ">"() { return relational_js_1.gt(a, b); }, "<="() { return relational_js_1.lt(a, b) || relational_js_1.eq(a, b); }, ">="() { return relational_js_1.gt(a, b) || relational_js_1.eq(a, b); } }[operator]; const res = bigfloat_return ? { type: "number", value: bigfloat_return() } : { type: "boolean", value: boolean_return() }; return resolve([...start, res, ...end]); } } i += 1; } } const [result] = resolve(tokens); if (result.type === "number") { return constructors_js_1.string(result.value); } return result.value; } exports.evaluate = evaluate;