expressionparser
Version:
Parse simple expressions, in a language of your own description
525 lines • 17.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
require("mocha");
const chai_1 = require("chai");
const index_1 = require("../index");
const termVals = {
a: 12,
b: 9,
c: -3,
_TEST: 42,
xadd: (a, b) => a + b,
xneg: (x) => -x,
isEven: (x) => x % 2 == 0,
};
const termTypes = {
xadd: "function",
xneg: "function",
isEven: "function",
};
const parser = index_1.init(index_1.formula, (term) => {
if (term in termVals) {
return termVals[term];
}
else {
throw new Error(`Invalid term: ${term}`);
}
}, (term) => {
if (term in termTypes) {
return termTypes[term];
}
else {
return "number";
}
});
const calc = (expression, terms) => {
return parser.expressionToValue(expression, terms);
};
describe("Infix Simple Arithmetic", () => {
it("should result in 0", () => {
const result = calc("1^1 - ((1 + 1) * 2) / 4");
chai_1.expect(result).to.equal(0);
});
});
describe("Infix Modular Arithmetic", () => {
it("should result in 3", () => {
const result = calc("15 % 12");
chai_1.expect(result).to.equal(3);
});
it("should result in 4", () => {
const result = calc("MOD(16, 12)");
chai_1.expect(result).to.equal(4);
});
});
describe("Quadratic Formula", () => {
it("should result in -1", () => {
const result = calc("(-b - sqrt(b^2 - 4 * a * c))/(2 * a)");
chai_1.expect(result).to.equal(-1);
});
});
describe("External Function", () => {
it("should result in 2", () => {
const result = calc("xadd(1,1)");
chai_1.expect(result).to.equal(2);
});
it("should result in 2", () => {
const result = calc("xneg(-2)");
chai_1.expect(result).to.equal(2);
});
it("should result in [1, 2]", () => {
const result = calc("map(xneg, [-1, -2])");
chai_1.expect(result).to.eql([1, 2]);
});
});
describe("Additional Terms", () => {
it("should result in 3", () => {
const result = calc("x + y", { x: 1, y: 2 });
chai_1.expect(result).to.equal(3);
});
it("should result in true", () => {
const result = calc("x = UNDEFINED", { x: undefined });
chai_1.expect(result).to.equal(true);
});
});
describe("Simple Boolean Expression", () => {
it("should result in true", () => {
const result = calc("1 + 1 = 2");
chai_1.expect(result).to.equal(true);
});
it("should result in false", () => {
const result = calc("!(1 + 1 = 2)");
chai_1.expect(result).to.equal(false);
});
});
describe("Boolean Expression", () => {
it("should result in true", () => {
const result = calc("(1 = 1) AND (1 != 2) AND (1 <> 2) AND (1 < 2) AND (2 > 1) AND (1 <= 1) AND ((1 >= 1) OR FALSE) AND (PI = PI)");
chai_1.expect(result).to.equal(true);
});
it("should result in true", () => {
const result = calc("(1 = 1) AND ISPRIME(5)");
chai_1.expect(result).to.equal(true);
});
});
describe("Case Insensitive Expression", () => {
it("should result in true", () => {
const result = calc("(E = E) and (LN2 != LN10) and (LOG2E <> LOG10E) and (ROUND(SQRTHALF) = ROUND(1/SQRT2)) and (TRUE != FALSE) and (LENGTH(EMPTY) = 0)");
chai_1.expect(result).to.equal(true);
});
});
describe("Terminal", () => {
it("should result in 42", () => {
const result = calc("_TEST + 1 - 1");
chai_1.expect(result).to.equal(42);
});
it("should raise error", () => {
chai_1.expect(() => {
calc("_TEST + _INVALID");
}).to.throw("Invalid term");
});
});
describe("Grouping", () => {
it("should raise error", () => {
chai_1.expect(() => {
calc("(TRUE AND FALSE");
}).to.throw('Mismatched Grouping (unexpected "(")');
});
it("should raise error", () => {
chai_1.expect(() => {
calc(")TRUE AND FALSE");
}).to.throw('Mismatched Grouping (unexpected closing ")")');
});
it("should raise error", () => {
chai_1.expect(() => {
calc("((TRUE) AND (FALSE)");
}).to.throw('Mismatched Grouping (unexpected "(")');
});
it("should result in false", () => {
chai_1.expect(calc("((TRUE) AND (FALSE))")).to.equal(false);
});
});
describe("Calls and Arrays", () => {
it("should result in 4", () => {
const result = calc("GCD(8, 12)");
chai_1.expect(result).to.equal(4);
});
it("should result in 5", () => {
const result = calc("AVERAGE([4,5,6])");
chai_1.expect(result).to.equal(5);
});
it("should result in 5", () => {
const result = calc("SUM(SORT(REVERSE([1,2,2])))");
chai_1.expect(result).to.equal(5);
});
it('should result in ABCDEFG"', () => {
const result = calc('STRING(MAP("UPPER", CHARARRAY("abcdefg\\"")))');
chai_1.expect(result).to.equal('ABCDEFG"');
});
it("should result in [true, false, true]", () => {
const result = calc('MAP("NOT", [FALSE, TRUE, FALSE])');
chai_1.expect(result).to.eql([true, false, true]);
});
it("should result in 6", () => {
const result = calc('REDUCE("ADD", 0, [1, 2, 3])');
chai_1.expect(result).to.equal(6);
});
it("should result in -6", () => {
const result = calc('REDUCE("SUB", 0, [3, 2, 1])');
chai_1.expect(result).to.equal(-6);
});
it("should result in 25", () => {
const result = calc('REDUCE("DIV", 100, [2, 2, 1])');
chai_1.expect(result).to.equal(25);
});
it("should result in 4", () => {
const result = calc('REDUCE("MUL", 1, [2, 2, 1])');
chai_1.expect(result).to.equal(4);
});
it("should result in [ 97, 98, 99, 100, 101, 102, 103 ]", () => {
const result = calc('MAP("CODE", CHARARRAY("abcdefg"))');
chai_1.expect(result).to.eql([97, 98, 99, 100, 101, 102, 103]);
});
it("should result in 700", () => {
const result = calc('REDUCE("+", 0, MAP("CODE", CHARARRAY("abcdefg")))');
chai_1.expect(result).to.equal(700);
});
it("should throw error", () => {
chai_1.expect(() => {
calc('REDUCE("_TEST_", 0, [1, 2, 3])');
}).to.throw("Unknown function: _TEST_");
});
});
describe("More Functions", () => {
it("should result in 10", () => {
const result = calc("IF(7 < 5, 8, 10)");
chai_1.expect(result).to.equal(10);
});
it("should result in 8", () => {
const result = calc("IF(7 > 5, 8, 10)");
chai_1.expect(result).to.equal(8);
});
it("should result in 8", () => {
chai_1.expect(() => {
calc("IF(7 > 5, 8)");
}).to.throw("Incorrect number of arguments. Expected 3");
});
it("should result in 10", () => {
const result = calc("LENGTH(RANGE(0, 10))");
chai_1.expect(result).to.equal(10);
});
// optional arguments
it("should result in 10", () => {
const result = calc("LENGTH(RANGE(10))");
chai_1.expect(result).to.equal(10);
});
// different typed arguments
it("should result in 3", () => {
const result = calc("RANGE([1, 2, 3, 4])");
chai_1.expect(result).to.equal(3);
});
it("should result in 'A'", () => {
const result = calc("CHAR(65)");
chai_1.expect(result).to.equal("A");
});
it("should result in 5", () => {
const result = calc("MIN([5, 6, 7, 8])");
chai_1.expect(result).to.equal(5);
});
it("should result in 5", () => {
const result = calc("MAX([5, 4, 3, 2])");
chai_1.expect(result).to.equal(5);
});
it("should result in 5", () => {
const result = calc("INDEX([5, 4, 3, 2], 0)");
chai_1.expect(result).to.equal(5);
});
it('should result in "a,b"', () => {
const result = calc('JOIN(",", ["a", "b"])');
chai_1.expect(result).to.equal("a,b");
});
it("should result in ['a', 'b']", () => {
const result = calc('SPLIT(",", "a,b")');
chai_1.expect(result).to.eql(["a", "b"]);
});
});
describe("Array Functions", () => {
it("should result in [[1, 2], [3, 4]]", () => {
const result = calc("ZIP([1, 3], [2, 4])");
chai_1.expect(result).to.eql([
[1, 2],
[3, 4],
]);
});
it("should result in [1, 3], [2, 4]", () => {
const result = calc("UNZIP([[1, 2], [3, 4]])");
chai_1.expect(result).to.eql([
[1, 3],
[2, 4],
]);
});
it("should result in [42, 69]", () => {
const result = calc("TAKE(2, [42, 69, 54])");
chai_1.expect(result).to.eql([42, 69]);
});
it("should result in [69, 54]", () => {
const result = calc("DROP(2, [1, 42, 69, 54])");
chai_1.expect(result).to.eql([69, 54]);
});
it("should result in [42, 69]", () => {
const result = calc("SLICE(1, 3, [1, 42, 69, 54])");
chai_1.expect(result).to.eql([42, 69]);
});
it("should result in [42, 69, 54]", () => {
const result = calc("CONCAT([42, 69], [54])");
chai_1.expect(result).to.eql([42, 69, 54]);
});
it("should result in 42", () => {
const result = calc("HEAD([42, 69, 54])");
chai_1.expect(result).to.equal(42);
});
it("should result in [69, 54]", () => {
const result = calc("TAIL([42, 69, 54])");
chai_1.expect(result).to.eql([69, 54]);
});
it("should result in 54", () => {
const result = calc("LAST([42, 69, 54])");
chai_1.expect(result).to.equal(54);
});
it("should result in [2,3,4]", () => {
const result = calc("CONS(2, [3, 4])");
chai_1.expect(result).to.eql([2, 3, 4]);
});
it("should result in [2,4,6]", () => {
const result = calc("FILTER(isEven, [1,2,3,4,5,6])");
chai_1.expect(result).to.eql([2, 4, 6]);
});
it("should result in [0,2,4]", () => {
const result = calc("TAKEWHILE(isEven, [0,2,4,5,6,7,8])");
chai_1.expect(result).to.eql([0, 2, 4]);
});
it("should result in [5,6,7,8]", () => {
const result = calc("DROPWHILE(isEven, [0,2,4,5,6,7,8])");
chai_1.expect(result).to.eql([5, 6, 7, 8]);
});
});
describe("Dictionaries", () => {
it("should result in 5", () => {
const result = calc('GET("b", DICT(["a", "b"], [1, 5]))');
chai_1.expect(result).to.equal(5);
});
it("should result in 5", () => {
const result = calc('GET("b", PUT("b", 5, DICT(["a", "b"], [1, 4])))');
chai_1.expect(result).to.equal(5);
});
it("should result in 5", () => {
const result = calc('GET("b", UNZIPDICT([["a", 1], ["b", 5]]))');
chai_1.expect(result).to.equal(5);
});
it('should result in ["a", "b"]', () => {
const result = calc('KEYS(UNZIPDICT([["b", 1], ["a", 5]]))');
chai_1.expect(result).to.eql(["a", "b"]);
});
it("should result in [5, 1]", () => {
const result = calc('VALUES(UNZIPDICT([["b", 1], ["a", 5]]))');
chai_1.expect(result).to.eql([5, 1]);
});
});
describe("Maths", () => {
it("should be false", () => {
const result = calc("ISNAN(1/0)");
chai_1.expect(result).to.equal(false);
});
it("should be true", () => {
const result = calc("(1/0) = INFINITY");
chai_1.expect(result).to.equal(true);
});
it("should be false", () => {
const result = calc("ISNAN(0)");
chai_1.expect(result).to.equal(false);
});
it("should be -1", () => {
const result = calc("(-1)");
chai_1.expect(result).to.equal(-1);
});
it("should be true", () => {
const result = calc("isNaN(sqrt(-1))");
chai_1.expect(result).to.equal(true);
});
it("should be false", () => {
const result = calc("isNaN(sqrt(abs(-1)))");
chai_1.expect(result).to.equal(false);
});
it("should be true", () => {
const result = calc("TAN(ATAN(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("COS(ACOS(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("SIN(ASIN(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("TANH(ATANH(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("COSH(ACOSH(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("SINH(ASINH(1)) ~= 1");
chai_1.expect(result).to.equal(true);
});
it("should result in PI/4", () => {
const result = calc("ATAN2(2,2)");
chai_1.expect(result).to.equal(Math.PI / 4);
});
it("should result in 1", () => {
const result = calc("LN(E)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("LOG(10)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("LOG2(2)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("FLOOR(1.9)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("CEIL(0.1)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("ROUND(0.6)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("ROUND(1.1)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("TRUNC(1.9)");
chai_1.expect(result).to.equal(1);
});
it("should result in 1", () => {
const result = calc("SIGN(5)");
chai_1.expect(result).to.equal(1);
});
it("should result in -1", () => {
const result = calc("FLOOR(-0.1)");
chai_1.expect(result).to.equal(-1);
});
it("should result in -1", () => {
const result = calc("CEIL(-1.1)");
chai_1.expect(result).to.equal(-1);
});
it("should result in -1", () => {
const result = calc("ROUND(-0.6)");
chai_1.expect(result).to.equal(-1);
});
it("should result in -1", () => {
const result = calc("ROUND(-1.1)");
chai_1.expect(result).to.equal(-1);
});
it("should result in -1", () => {
const result = calc("TRUNC(-1.9)");
chai_1.expect(result).to.equal(-1);
});
it("should result in -1", () => {
const result = calc("SIGN(-5)");
chai_1.expect(result).to.equal(-1);
});
it("should be true", () => {
const result = calc("EXP(1) = E");
chai_1.expect(result).to.equal(true);
});
it("should be true", () => {
const result = calc("CUBEROOT(27) = 3");
chai_1.expect(result).to.equal(true);
});
it("should result in 90", () => {
const result = calc("DEGREES(RADIANS(90))");
chai_1.expect(result).to.equal(90);
});
it("should result in '100'", () => {
const result = calc("DEC2BIN(4)");
chai_1.expect(result).to.equal("100");
});
it("should result in 4", () => {
const result = calc('BIN2DEC("100")');
chai_1.expect(result).to.equal(4);
});
it("should result in 'f'", () => {
const result = calc("LOWER(UPPER(DEC2HEX(15)))");
chai_1.expect(result).to.equal("f");
});
it("should result in 16", () => {
const result = calc('HEX2DEC("F")');
chai_1.expect(result).to.equal(15);
});
it("should be true", () => {
const result = calc("0.99999999999999999 + EPSILON > 1");
chai_1.expect(result).to.equal(true);
});
});
describe("Exceptions", () => {
it("should throw 'Too few arguments'", () => {
chai_1.expect(() => {
calc("GCD(2)");
}).to.throw("Too few arguments. Expected 2, found 1 (2)");
});
it("should throw 'Expected number'", () => {
chai_1.expect(() => {
calc('add("A", "B")');
}).to.throw("Expected number, found: string");
});
it("should throw 'Expected array'", () => {
chai_1.expect(() => {
calc('sort("ABC")');
}).to.throw("Expected array, found: string");
});
it("should throw 'Expected array or string'", () => {
chai_1.expect(() => {
calc("index(1, 1)");
}).to.throw("Expected array or string, found: number");
});
it("should throw 'Expected string'", () => {
chai_1.expect(() => {
calc("BIN2DEC(10)");
}).to.throw("Expected string, found: number");
});
it("should throw 'Expected char'", () => {
chai_1.expect(() => {
calc('CODE("FOO")');
}).to.throw("Expected char, found: string");
});
});
describe("Tokenization of adjacent symbols", () => {
it("should be the same", () => {
const a = parser.tokenize("RANGE(-5,-1)");
const b = parser.tokenize("RANGE(-5, -1)");
chai_1.expect(a).to.eql(b);
});
});
describe("Evaluation of objects with prototypes", () => {
const ProtoObj = function () {
this.x = 42;
};
ProtoObj.prototype.xSq = function () {
return this.x * this.x;
};
termVals["foo"] = new ProtoObj();
it("should be the same object", () => {
const result = calc("foo");
chai_1.expect(result).to.equal(termVals["foo"]);
});
});
//# sourceMappingURL=formula.test.js.map
;