pip-services3-expressions-nodex
Version:
Tokenizers, parsers and expression calculators for Pip.Services in Node.js / ES2017
433 lines • 17.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressionCalculator = void 0;
const VariableCollection_1 = require("./variables/VariableCollection");
const Variable_1 = require("./variables/Variable");
const DefaultFunctionCollection_1 = require("./functions/DefaultFunctionCollection");
const ExpressionParser_1 = require("./parsers/ExpressionParser");
const ExpressionTokenType_1 = require("./parsers/ExpressionTokenType");
const TypeUnsafeVariantOperations_1 = require("../variants/TypeUnsafeVariantOperations");
const Variant_1 = require("../variants/Variant");
const CalculationStack_1 = require("./CalculationStack");
const ExpressionException_1 = require("./ExpressionException");
/**
* Implements an expression calculator class.
*/
class ExpressionCalculator {
/**
* Constructs this class and assigns expression string.
* @param expression The expression string.
*/
constructor(expression) {
this._defaultVariables = new VariableCollection_1.VariableCollection();
this._defaultFunctions = new DefaultFunctionCollection_1.DefaultFunctionCollection();
this._variantOperations = new TypeUnsafeVariantOperations_1.TypeUnsafeVariantOperations();
this._parser = new ExpressionParser_1.ExpressionParser();
this._autoVariables = true;
if (expression != null) {
this.expression = expression;
}
}
/**
* The expression string.
*/
get expression() {
return this._parser.expression;
}
/**
* The expression string.
*/
set expression(value) {
this._parser.expression = value;
if (this._autoVariables) {
this.createVariables(this._defaultVariables);
}
}
get originalTokens() {
return this._parser.originalTokens;
}
set originalTokens(value) {
this._parser.originalTokens = value;
if (this._autoVariables) {
this.createVariables(this._defaultVariables);
}
}
/**
* Gets the flag to turn on auto creation of variables for specified expression.
*/
get autoVariables() {
return this._autoVariables;
}
/**
* Sets the flag to turn on auto creation of variables for specified expression.
*/
set autoVariables(value) {
this._autoVariables = value;
}
/**
* Gets the manager for operations on variant values.
*/
get variantOperations() {
return this._variantOperations;
}
/**
* Sets the manager for operations on variant values.
*/
set variantOperations(value) {
this._variantOperations = value;
}
/**
* The list with default variables.
*/
get defaultVariables() {
return this._defaultVariables;
}
/**
* The list with default functions.
*/
get defaultFunctions() {
return this._defaultFunctions;
}
/**
* The list of original expression tokens.
*/
get initialTokens() {
return this._parser.initialTokens;
}
/**
* The list of processed expression tokens.
*/
get resultTokens() {
return this._parser.resultTokens;
}
/**
* Populates the specified variables list with variables from parsed expression.
* @param variables The list of variables to be populated.
*/
createVariables(variables) {
for (let variableName of this._parser.variableNames) {
if (variables.findByName(variableName) == null) {
variables.add(new Variable_1.Variable(variableName));
}
}
}
/**
* Cleans up this calculator from all data.
*/
clear() {
this._parser.clear();
this._defaultVariables.clear();
}
/**
* Evaluates this expression using default variables and functions.
* @returns the evaluation result.
*/
evaluate() {
return __awaiter(this, void 0, void 0, function* () {
return yield this.evaluateWithVariablesAndFunctions(null, null);
});
}
/**
* Evaluates this expression using specified variables.
* @param variables The list of variables
* @returns the evaluation result
*/
evaluateWithVariables(variables) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.evaluateWithVariablesAndFunctions(variables, null);
});
}
/**
* Evaluates this expression using specified variables and functions.
* @param variables The list of variables
* @param functions The list of functions
* @returns the evaluation result
*/
evaluateWithVariablesAndFunctions(variables, functions) {
return __awaiter(this, void 0, void 0, function* () {
let stack = new CalculationStack_1.CalculationStack();
variables = variables || this._defaultVariables;
functions = functions || this._defaultFunctions;
for (let token of this.resultTokens) {
if (yield this.evaluateConstant(token, stack)) {
continue;
}
if (yield this.evaluateVariable(token, stack, variables)) {
continue;
}
if (yield this.evaluateFunction(token, stack, functions)) {
continue;
}
if (yield this.evaluateLogical(token, stack)) {
continue;
}
if (yield this.evaluateArithmetical(token, stack)) {
continue;
}
if (yield this.evaluateBoolean(token, stack)) {
continue;
}
if (yield this.evaluateOther(token, stack)) {
continue;
}
throw new ExpressionException_1.ExpressionException(null, "INTERNAL", "Internal error", token.line, token.column);
}
if (stack.length != 1) {
throw new ExpressionException_1.ExpressionException(null, "INTERNAL", "Internal error", 0, 0);
}
return stack.pop();
});
}
evaluateConstant(token, stack) {
return __awaiter(this, void 0, void 0, function* () {
if (token.type != ExpressionTokenType_1.ExpressionTokenType.Constant) {
return false;
}
stack.push(token.value);
return true;
});
}
evaluateVariable(token, stack, variables) {
return __awaiter(this, void 0, void 0, function* () {
if (token.type != ExpressionTokenType_1.ExpressionTokenType.Variable) {
return false;
}
let variable = variables.findByName(token.value.asString);
if (variable == null) {
throw new ExpressionException_1.ExpressionException(null, "VAR_NOT_FOUND", "Variable " + token.value.asString + " was not found", token.line, token.column);
}
stack.push(variable.value);
return true;
});
}
evaluateFunction(token, stack, functions) {
return __awaiter(this, void 0, void 0, function* () {
if (token.type != ExpressionTokenType_1.ExpressionTokenType.Function) {
return false;
}
let func = functions.findByName(token.value.asString);
if (func == null) {
throw new ExpressionException_1.ExpressionException(null, "FUNC_NOT_FOUND", "Function " + token.value.asString + " was not found", token.line, token.column);
}
// Retrieve function parameters
let params = [];
let paramCount = stack.pop().asInteger;
while (paramCount > 0) {
params.splice(0, 0, stack.pop());
paramCount--;
}
let functionResult = yield func.calculate(params, this._variantOperations);
stack.push(functionResult);
return true;
});
}
evaluateLogical(token, stack) {
return __awaiter(this, void 0, void 0, function* () {
switch (token.type) {
case ExpressionTokenType_1.ExpressionTokenType.And:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.and(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Or:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.or(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Xor:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.xor(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Not:
{
stack.push(this._variantOperations.not(stack.pop()));
return true;
}
default:
return false;
}
});
}
evaluateArithmetical(token, stack) {
return __awaiter(this, void 0, void 0, function* () {
switch (token.type) {
case ExpressionTokenType_1.ExpressionTokenType.Plus:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.add(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Minus:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.sub(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Star:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.mul(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Slash:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.div(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Procent:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.mod(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Power:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.pow(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Unary:
{
stack.push(this._variantOperations.negative(stack.pop()));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.ShiftLeft:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.lsh(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.ShiftRight:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.rsh(value1, value2));
return true;
}
default:
return false;
}
});
}
evaluateBoolean(token, stack) {
return __awaiter(this, void 0, void 0, function* () {
switch (token.type) {
case ExpressionTokenType_1.ExpressionTokenType.Equal:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.equal(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.NotEqual:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.notEqual(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.More:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.more(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Less:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.less(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.EqualMore:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.moreEqual(value1, value2));
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.EqualLess:
{
let value2 = stack.pop();
let value1 = stack.pop();
stack.push(this._variantOperations.lessEqual(value1, value2));
return true;
}
default:
return false;
}
});
}
evaluateOther(token, stack) {
return __awaiter(this, void 0, void 0, function* () {
switch (token.type) {
case ExpressionTokenType_1.ExpressionTokenType.In:
{
let value2 = stack.pop();
let value1 = stack.pop();
let rvalue = this._variantOperations.in(value2, value1);
stack.push(rvalue);
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.NotIn:
{
let value2 = stack.pop();
let value1 = stack.pop();
let rvalue = this._variantOperations.in(value2, value1);
rvalue = Variant_1.Variant.fromBoolean(!rvalue.asBoolean);
stack.push(rvalue);
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.Element:
{
let value2 = stack.pop();
let value1 = stack.pop();
let rvalue = this._variantOperations.getElement(value1, value2);
stack.push(rvalue);
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.IsNull:
{
let rvalue = new Variant_1.Variant(stack.pop().isNull());
stack.push(rvalue);
return true;
}
case ExpressionTokenType_1.ExpressionTokenType.IsNotNull:
{
let rvalue = new Variant_1.Variant(!stack.pop().isNull());
stack.push(rvalue);
return true;
}
default:
return false;
}
});
}
}
exports.ExpressionCalculator = ExpressionCalculator;
//# sourceMappingURL=ExpressionCalculator.js.map