enterprise-loyalty-components
Version:
企业版忠诚度组件库
1,408 lines (1,368 loc) • 48.7 kB
JavaScript
'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var TypeEnum;
(function (TypeEnum) {
TypeEnum["Number"] = "Number";
TypeEnum["Integer"] = "Integer";
TypeEnum["String"] = "String";
TypeEnum["Date"] = "Date";
TypeEnum["Time"] = "Time";
TypeEnum["DateTime"] = "DateTime";
TypeEnum["Arithmetic"] = "Arithmetic";
TypeEnum["Comparison"] = "Comparison";
TypeEnum["LogicOperator"] = "LogicOperator";
TypeEnum["IdentiferComparison"] = "IdentiferComparison";
TypeEnum["BracketStart"] = "BracketStart";
TypeEnum["BracketEnd"] = "BracketEnd";
TypeEnum["Comma"] = "Comma";
TypeEnum["Semicolon"] = "Semicolon";
TypeEnum["Function"] = "Function";
TypeEnum["FunctionCall"] = "FunctionCall";
TypeEnum["FqnProperty"] = "FqnProperty";
TypeEnum["CustomProperty"] = "CustomProperty";
TypeEnum["Identifier"] = "Identifier";
TypeEnum["Boolean"] = "Boolean";
TypeEnum["Null"] = "Null";
TypeEnum["LogicNot"] = "LogicNot";
})(TypeEnum || (TypeEnum = {}));
function isScalaType(t) {
return [
TypeEnum.CustomProperty,
TypeEnum.FqnProperty,
TypeEnum.Boolean,
TypeEnum.Integer,
TypeEnum.Number,
TypeEnum.String,
TypeEnum.Null
].includes(t);
}
function isOperatorType(t) {
return [
TypeEnum.Comparison,
TypeEnum.Arithmetic,
TypeEnum.LogicOperator,
TypeEnum.IdentiferComparison
].includes(t);
}
/**
* 判断路径path是否在target中存在
* @param path 路径,必须以this开头
* @param fqn 目标对象
*/
function matchFqnPath(path, fqn, pos) {
var paths = path.split('.');
paths.shift();
var obj = fqn;
while (paths.length > 0) {
var subPath = paths.shift();
if (obj.hasOwnProperty && obj.hasOwnProperty(subPath)) {
obj = obj[subPath];
}
else {
throw { code: 'InvalidFqnPath', pos: pos };
}
}
return obj;
}
/**
* 获取括号结束位置,即左括号对应的右括号的位置
* start 为左括号的位置
*/
function getBracketEndTokenPosition(start, tokens) {
var i = start;
var bracketCount = 0;
while ((i < tokens.length && bracketCount > 0) || i === start) {
var itoken = tokens[i++];
if (itoken.type === TypeEnum.BracketStart) {
bracketCount++;
}
else if (itoken.type === TypeEnum.BracketEnd) {
bracketCount--;
}
}
if (bracketCount > 0) {
throw FunctionError(tokens[start], 'Function Arguments must end with `)`.');
}
return i - 1;
}
function FunctionError(token, message) {
return { code: 'InvalidFunction', pos: token.pos, message: message };
}
function getFuncArgs(tokens) {
var args = [];
var i = 0;
var brackCount = 0;
while (i < tokens.length) {
var token = tokens[i++];
if (token.type === TypeEnum.Comma) {
if (brackCount === 0) {
args.push(tokens.splice(0, i - 1));
tokens.shift();
i = 0;
}
// 防止出现`SUM(1,2,)`参数最后为逗号的情况
if (!tokens.length) {
throw { message: 'Function arguments error' };
}
continue;
}
if (token.type === TypeEnum.BracketStart) {
brackCount++;
}
else if (token.type === TypeEnum.BracketEnd) {
brackCount--;
}
if (i === tokens.length) {
if (brackCount !== 0) {
throw { message: 'Function arguments error' };
}
args.push(tokens);
}
}
return args;
}
function macthOperatorType(valve) {
if ([','].includes(valve)) {
return TypeEnum.Comma;
}
if (['==', '!=', '>', '>=', '<', '<='].includes(valve)) {
return TypeEnum.Comparison;
}
if (['&&', '||', '!'].includes(valve)) {
return TypeEnum.LogicOperator;
}
if (['+', '-', '*', '/', '%'].includes(valve)) {
return TypeEnum.Arithmetic;
}
throw { code: 'invalid-operator', message: '无效的比较符' };
}
// 判断是否为正确的日期格式
function isAcceptableDate(v) {
return /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$/.test(v);
}
// 判断是否为正确的时间格式
function isAcceptableTime(v) {
// return /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/.test(v);
return /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])$/.test(v);
}
// 判断是否为正确的ISO时间格式
function isAcceptableDateTime(v) {
// tslint:disable-next-line
return /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/.test(v);
}
var ArithmeticOperators = ['+', '-', '*', '/', '%'];
var ComparisonOperators = ['>', '<', '='];
var SingleCharOperators = ['(', ')', ';'];
var LogicOperators = ['&', '|', '!'];
function Position(start, end, line) {
return { start: start, end: end, line: line };
}
function generateTokens(input, options) {
var fqn = options.fqn;
var functions = options.functions;
var declarations = options.declarations;
var customProperties = options.customProperties;
var tokens = [];
var len = input.length;
var i = 0;
var line = 0;
while (i < len) {
var ch = input[i++];
if (ch === ';') {
break;
}
if (ch === ' ' || ch === '\t') {
continue;
}
if (ch === '\n') {
line++;
continue;
}
// 特殊字符串
if (SingleCharOperators.includes(ch)) {
tokens.push({
type: {
'(': TypeEnum.BracketStart,
')': TypeEnum.BracketEnd
}[ch],
value: ch,
pos: Position(i - 1, i - 1, line)
});
continue;
}
var doubleOperators = eatSpecialDoubleOperators(input.slice(i - 1));
if (doubleOperators) {
tokens.push({
type: TypeEnum.IdentiferComparison,
value: doubleOperators.replace(/\s+/g, ' ').toUpperCase(),
pos: Position(i - 1, i - 1 + doubleOperators.length, line)
});
i = i + doubleOperators.length;
continue;
}
var singleOperators = eatSpecialSingleOperators(input.slice(i - 1));
if (singleOperators) {
tokens.push({
type: TypeEnum.IdentiferComparison,
value: singleOperators.toUpperCase(),
pos: Position(i - 1, i - 1 + singleOperators.length, line)
});
i = i + singleOperators.length;
continue;
}
var commaToken = eatContinuousOperators([','], i - 1);
if (commaToken) {
tokens.push(__assign({}, commaToken, { type: macthOperatorType(commaToken.value) }));
i = commaToken.pos.end + 1;
continue;
}
var continueToken = eatContinuousOperators(LogicOperators.concat(ArithmeticOperators, ComparisonOperators), i - 1);
if (continueToken) {
tokens.push(__assign({}, continueToken, { type: macthOperatorType(continueToken.value) }));
i = continueToken.pos.end + 1;
continue;
}
var customProperty = eatCustomProperties(i - 1);
if (customProperty) {
tokens.push(customProperty);
i = customProperty.pos.end + 1;
continue;
}
// fqn
if (ch === 't') {
var start = i - 1;
var firstWord = input.slice(start, start + 4);
var nextOfThis = input[start + 4];
if (firstWord === 'this' && ['.', ' ', '\t', undefined].includes(nextOfThis)) {
i = start + 4;
while (i < len && /[A-Za-z_0-9\.\[\]]/.test(input[i])) {
i++;
}
var word = input.slice(start, i);
var pos = Position(start, i - 1, line);
var fqnPathType = matchFqnPath(word, fqn, pos);
if (word) {
tokens.push({
vType: fqnPathType,
type: TypeEnum.FqnProperty,
value: word,
pos: pos
});
}
continue;
}
}
// 内置函数
if (/[A-Za-z_]/.test(ch)) {
var start = i - 1;
while (i < len && /[A-Za-z_0-9]/.test(input[i])) {
i++;
}
var word = input.slice(start, i);
if ((functions.includes(word.toLowerCase()) || functions.includes(word.toUpperCase()))
&& input[i] === '(') {
tokens.push({
type: TypeEnum.Function,
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
else if (declarations.includes(word) && input[i] === '(') {
tokens.push({
type: TypeEnum.Function,
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
else if (['TRUE', 'true', 'FALSE', 'false'].includes(word)) {
tokens.push({
type: TypeEnum.Boolean,
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
else if (['NULL', 'null'].includes(word)) {
tokens.push({
type: TypeEnum.Null,
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
else {
throw { code: 'InvalidIdentifier', pos: Position(start, i - 1, line) };
}
}
// 数字(整数&浮点数)
if (/[0-9]/.test(ch)) {
var start = i - 1;
var isDot = false;
while (i < len) {
var char = input[i];
if (char === '.') {
if (isDot) {
throw {
code: 'InvalidNumberFormatter',
message: 'Too many dot in a number.',
pos: Position(i, i, line)
};
}
isDot = true;
i++;
}
else if (/[0-9]/.test(char)) {
i++;
}
else {
break;
}
}
var word = input.slice(start, i);
var type = word.includes('.') ? TypeEnum.Number : TypeEnum.Integer;
tokens.push({
type: type,
vType: { $type: type },
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
// 字符串字面量
if (ch === '\'' || ch === '"') {
var start = i - 1;
var escaped = false;
while (i < len) {
var char = input[i];
if (char === ch && !escaped) {
break;
}
escaped = !escaped && char === '\\';
i++;
}
if (input[i] !== ch) {
throw {
code: 'StringEndLost',
message: '字符串缺少结束符',
pos: Position(start, i - 1, line)
};
}
var word = input.slice(start + 1, i);
i++;
tokens.push({
type: TypeEnum.String,
vType: { $type: TypeEnum.String },
value: word,
pos: Position(start, i - 1, line)
});
continue;
}
throw { code: 'InvalidIdentifier', pos: Position(i - 1, i - 1, line) };
}
return tokens;
// 连续的操作符
function eatContinuousOperators(opers, start) {
var index = start;
var word = input[index];
var leftSpace = input[index - 1] ? [' ', '\t'].includes(input[index - 1]) : false;
var rightSpace = input[index + 1] ? [' ', '\t'].includes(input[index + 1]) : false;
if (opers.includes(word)) {
while (opers.includes(input[index + 1])) {
word += input[index + 1];
index++;
}
return {
value: word,
pos: Position(start, index, line),
leftSpace: leftSpace,
rightSpace: rightSpace
};
}
return false;
}
// 自定义属性ID=> {12345}
function eatCustomProperties(start) {
var matched = input.slice(start).match(/^-?\{\d+\}/);
if (matched) {
var value = matched[0];
var pos = Position(start, start + value.length - 1, line);
var propertyId_1 = Number(value.slice(1, value.length - 1));
var customProperty = customProperties.find(function (item) { return item.id === propertyId_1; });
if (!customProperty) {
throw { code: 'invalid-custom-property', pos: pos };
}
return {
value: value,
pos: pos,
type: TypeEnum.CustomProperty,
vType: { $type: customProperty.dataType }
};
}
return false;
}
}
var specialSingleOperators = ['in', 'contain', 'between', 'and'];
function eatSpecialSingleOperators(input) {
var i = 0;
var word = input[i];
while (i <= input.length && ![' ', '\t', undefined].includes(input[i + 1])) {
word += input[++i];
}
if (specialSingleOperators.find(function (item) {
return item.toLowerCase() === word || item.toUpperCase() === word;
})) {
return word;
}
return false;
}
function eatSpecialDoubleOperators(input) {
var matched = matchRegExpList(input, [
/^(begin\s+with)\s+/, /^(BEGIN\s+WITH)\s+/,
/^(end\s+with)\s+/, /^(END\s+WITH)\s+/,
/^(include\s+any)\s+/, /^(INCLUDE\s+ANY)\s+/,
/^(include\s+all)\s+/, /^(INCLUDE\s+ALL)\s+/,
/^(exclude\s+any)\s+/, /^(EXCLUDE\s+ANY)\s+/,
/^(exclude\s+all)\s+/, /^(EXCLUDE\s+ALL)\s+/,
/^(not\s+in)\s+/, /^(NOT\s+IN)\s+/,
/^(not\s+contain)\s+/, /^(NOT\s+CONTAIN)\s+/
]);
if (matched) {
return matched;
}
return false;
}
function matchRegExpList(input, regexpList) {
var i = 0;
while (i < regexpList.length) {
var matched = input.match(regexpList[i]);
if (matched) {
return matched[1];
}
i++;
}
return false;
}
function matchBrackets(tokens) {
var start = tokens.findIndex(function (item) { return item.type === TypeEnum.BracketStart; });
if (start === -1) {
return;
}
var end = getBracketEndTokenPosition(start, tokens);
var eatTokens = tokens.slice(start, end + 1);
if (eatTokens.length <= 2) {
throw {
code: 'BracketsContentEmpty',
message: '括号内内容不能为空',
pos: {
start: tokens[start].pos.start,
end: tokens[end].pos.end,
line: tokens[start].pos.line
}
};
}
tokens.splice(start, eatTokens.length, match(eatTokens.slice(1, eatTokens.length - 1)));
matchBrackets(tokens);
}
function matchFunctions(tokens) {
var index = tokens.findIndex(function (item) { return item.type === TypeEnum.Function; });
if (index === -1) {
return;
}
var start = index + 1;
var end = getBracketEndTokenPosition(start, tokens);
var eatTokens = tokens.slice(index, end + 1);
var argsTokens = tokens.slice(index + 2, end);
var args = getFuncArgs(argsTokens);
args.forEach(function (arg) {
if (!arg.length) {
throw { code: 'invalid-function-arg', message: '函数参数值不能为空' };
}
});
tokens.splice(index, eatTokens.length, {
type: TypeEnum.FunctionCall,
value: tokens[index].value,
tokens: eatTokens,
args: args.map(function (arg) { return match(arg); })
});
matchFunctions(tokens);
}
function matchCommonOperator(tokens, type, operators) {
var i = tokens.findIndex(function (item) { return item.type === type && operators.includes(item.value); });
if (i === -1) {
return;
}
var token = tokens[i];
var left = tokens[i - 1];
var right = tokens[i + 1];
if (!left || !right) {
throw { code: 'E1' };
}
tokens.splice(i - 1, 3, {
tokens: [left, token, right],
operator: token,
left: match([left]),
right: match([right])
});
matchCommonOperator(tokens, type, operators);
}
function matchArithmetic(tokens, operators) {
matchCommonOperator(tokens, TypeEnum.Arithmetic, operators);
}
function matchLogicOperator(tokens) {
matchCommonOperator(tokens, TypeEnum.LogicOperator, ['&&', '||']);
}
function matchComparison(tokens) {
matchCommonOperator(tokens, TypeEnum.Comparison, ['>', '>=', '<', '<=', '==', '!=']);
matchCommonOperator(tokens, TypeEnum.IdentiferComparison, [
'CONTAIN', 'NOT CONTAIN', 'IN', 'NOT IN', 'BEGIN WITH', 'END WITH',
'INCLUDE ANY', 'INCLUDE ALL', 'EXCLUDE ANY', 'EXCLUDE ALL', 'BETWEEN'
]);
}
function matchLogicNot(tokens) {
var i = tokens.findIndex(function (vtoken) {
return vtoken.type === TypeEnum.LogicOperator
&& vtoken.value.includes('!');
});
if (i === -1) {
return;
}
var token = tokens[i];
var right = tokens[i + 1];
if (!right) {
throw { code: 'E1' };
}
tokens.splice(i, 2, {
tokens: [token, right],
operator: token,
right: match([right])
});
matchLogicNot(tokens);
}
function matchIdentiferComparison(tokens) {
var i = tokens.findIndex(function (vtoken) { return (vtoken.type === TypeEnum.IdentiferComparison); });
if (i === -1) {
return '';
}
var token = tokens[i];
if (['BEGIN WITH', 'END WITH', 'INCLUDE ANY', 'INCLUDE ALL', 'EXCLUDE ANY', 'EXCLUDE ALL'].includes(token.value)) {
var left = tokens[i - 1];
var right = tokens[i + 1];
if (!left || !right) {
throw { code: 'E1' };
}
tokens.splice(i - 1, 3, {
tokens: [left, token, right],
operator: token,
left: match([left]),
right: match([right])
});
}
else if (token.value === 'BETWEEN') {
var left = tokens[i - 1];
if (!left) {
throw { code: 'BetweenLeftComparsionTokenError' };
}
var endIndex = matchBetweenAnd(tokens, i);
var firstComparsionTokens = tokens.slice(i + 1, endIndex);
if (firstComparsionTokens.length !== 1) {
throw { code: 'AndLeftComparsionTokenError' };
}
var secondComparsionToken = tokens[endIndex + 1];
if (!secondComparsionToken) {
throw { code: 'AndRightComparsionTokenError' };
}
tokens.splice(i - 1, 5, {
tokens: [left, token, firstComparsionTokens[0], tokens[endIndex], secondComparsionToken],
operator: token,
left: match([left]),
andLeft: match([firstComparsionTokens[0]]),
andRight: match([secondComparsionToken])
});
}
}
// function matchNegativeDigit(tokens: IToken[]) {
// const i = tokens.findIndex(token => {
// return token.type === TypeEnum.Arithmetic && token.value === '-';
// });
// const leftToken = tokens[i-1];
// const rightToken = tokens[i+1];
// }
function match(tokens) {
if (tokens.length === 0) {
return;
}
if (tokens.length === 1) {
var token = tokens[0];
if (!token.type) {
return token;
}
if (!isScalaType(token.type) && token.type !== TypeEnum.FunctionCall) {
throw { code: 'invalid-token-start' };
}
return tokens[0];
}
if (tokens.length === 2) {
var token = tokens[0];
if (token.type === TypeEnum.LogicOperator && token.value.includes('!')) {
var right = tokens[1];
return {
tokens: [token, right],
operator: token,
right: match([right])
};
}
else if (token.type === TypeEnum.Arithmetic) {
if (token.value === '-') {
if (!token.rightSpace) {
var right = tokens[1];
return {
tokens: [token, right],
operator: token,
right: match([right])
};
}
}
}
throw { code: 'invalid-token-size' };
}
if (tokens.length === 3) {
if (tokens[0].type === 'Function') {
matchFunctions(tokens);
return match(tokens);
}
if (!isOperatorType(tokens[1].type)) {
throw { code: 'invalid-operator' };
}
if (!isScalaType(tokens[0].type)) {
if (!tokens[0].left) {
throw { code: 'invalid-expression' };
}
}
if (!isScalaType(tokens[2].type)) {
if (!tokens[2].left) {
throw { code: 'invalid-expression' };
}
}
return {
left: tokens[0],
operator: tokens[1],
right: tokens[2]
};
}
// 函数
matchFunctions(tokens);
// 括号
matchBrackets(tokens);
// 逻辑非
matchLogicNot(tokens);
// 四则运算乘除取余
matchArithmetic(tokens, ['*', '/', '%']);
// 四则运算加减
matchArithmetic(tokens, ['+', '-']);
// 比较 BEGIN WITH/END WITH...
matchIdentiferComparison(tokens);
// 比较符运算
matchComparison(tokens);
// 逻辑与、逻辑或
matchLogicOperator(tokens);
return match(tokens);
}
function matchBetweenAnd(tokens, betweenIndex) {
var i = betweenIndex + 1;
var matched = false;
while (i < tokens.length) {
if (tokens[i].type === TypeEnum.IdentiferComparison && tokens[i].value === 'AND') {
matched = true;
return i;
}
i++;
}
if (!matched) {
throw { code: 'BetweenNotMatchedAnd' };
}
return i;
}
function generateType($type) {
return { $type: $type };
}
// 是否为数字类型
function isDigit(v) {
return ['Number', 'Integer'].includes(v);
}
// 是否存在非数字类型
function hasNotDigitArgs(args) {
return args.find(function (arg) { return !isDigit(arg.vType.$type); });
}
// 是否存在Number类型
function hasNumberArgs(args) {
return args.find(function (arg) { return 'Number' === arg.vType.$type; });
}
// 是否存在非布尔类型
function hasNotBooleanArgs(args) {
return args.find(function (arg) { return 'Boolean' !== arg.vType.$type; });
}
// 是否为字符串类型
function isString(v) {
return ['String'].includes(v);
}
// 是否存在非类字符串类型
function hasNotStringArgs(args) {
return args.find(function (arg) { return !isString(arg.vType.$type); });
}
function AND(later) {
var args = later.args;
if (hasNotBooleanArgs(args)) {
throw { message: 'AND 函数的参数只能为布尔类型' };
}
later.vType = generateType('Boolean');
return later.vType;
}
function AVG(later) {
var args = later.args;
var firstArgType = args[0].vType.$type;
if (['IntegerArray', 'NumberArray'].includes(firstArgType)) {
if (args.length > 1) {
throw { message: 'AVG 函数的第一个参数如果为数组,则只接受一个参数' };
}
later.vType = generateType('Number');
return later.vType;
}
if (hasNotDigitArgs(args)) {
throw { message: 'AVG 函数的参数不能为非数字类型' };
}
later.vType = generateType('Number');
return later.vType;
}
function COUNT(later) {
var args = later.args;
var firstArgType = args[0].vType.$type;
if (['IntegerArray', 'NumberArray'].includes(firstArgType)) {
if (args.length > 1) {
throw { message: 'COUNT 函数的第一个参数如果为数组,则只接受一个参数' };
}
later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number');
return later.vType;
}
if (args.length === 1) {
later.vType = generateType('Integer');
return later.vType;
}
if (isObject$1(args[0].vType.$type) && args.length > 1) {
throw { message: '当 COUNT 函数第一个参数为数组时,只接受一个参数' };
}
later.vType = generateType(TypeEnum.Integer);
return later.vType;
}
function isObject$1(v) {
return Object.prototype.toString.call(v) === '[object Object]';
}
// 在日期上添加指定 秒/分/小时/日/月/年
function DATE_ADD(later, name) {
var args = later.args;
if (args.length !== 2) {
throw { message: name + " \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E24\u4E2A\u53C2\u6570" };
}
if ('DateTime' !== args[0].vType.$type) {
throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E00\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" };
}
if ('Integer' !== args[1].vType.$type) {
throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A\u6574\u6570\u7C7B\u578B" };
}
later.vType = generateType('DateTime');
return later.vType;
}
// 日期上指定的值
function DATE_FETCH(later, name) {
var args = later.args;
if (args.length !== 1) {
throw { message: name + " \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" };
}
if ('DateTime' !== args[0].vType.$type) {
throw { message: name + " \u51FD\u6570\u7684\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" };
}
later.vType = generateType('Integer');
return later.vType;
}
// 生成日期对象
function DATE(later) {
var args = later.args;
if (args.length !== 1) {
throw { message: "Date \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" };
}
var arg = args[0];
if (arg.vType.$type !== 'String' || !arg.value) {
throw { message: 'Date 函数的参数必须为字符串类型' };
}
if (!isAcceptableDate(arg.value)) {
throw { message: 'Date 函数参数必须为 yyyy-dd-mm' };
}
later.vType = generateType('Date');
return later.vType;
}
// 生成日期时间对象
function DATETIME(later) {
var args = later.args;
if (args.length !== 1) {
throw { message: "DateTime \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" };
}
var arg = args[0];
if (arg.vType.$type !== 'String' || !arg.value) {
throw { message: 'DateTime 函数的参数必须为字符串类型' };
}
// tslint:disable-next-line
if (!isAcceptableDateTime(arg.value)) {
throw { message: 'DateTime 函数参数必须为ISO时间格式 2019-01-01T00:00:00.000Z' };
}
later.vType = generateType('DateTime');
return later.vType;
}
// 比较两个日期在指定单位上的偏移量
function DURATION_DIFF(later) {
var name = 'DURATION_DIFF';
var args = later.args;
if (args.length !== 3) {
throw { message: name + " \u51FD\u6570\u7684\u63A5\u53D7\u4E09\u4E2A\u53C2\u6570" };
}
if ('DateTime' !== args[0].vType.$type) {
throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E00\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" };
}
if ('DateTime' !== args[1].vType.$type) {
throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" };
}
if (!['DAY', 'HOUR', 'MINUTE', 'SECOND'].includes(args[2].value)) {
throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E09\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DAY\u3001HOUR\u3001MINUTE\u3001SECOND \u4E2D\u7684\u4E00\u4E2A" };
}
later.vType = generateType('Integer');
return later.vType;
}
// import { generateType } from '@/ast/functions/utils';
// 条件格式固定为:目标对象,比较符和参考值,目标对象/变量可以为变量、常量或函数返回值,不支持混合加减乘除运算,返回值为满足条件的参考值集合。
function FILTER(later) {
var args = later.args;
if (args.length < 2) {
throw { message: 'FILTER 函数至少需要两个参数' };
}
var conditionArgs = args.slice(1);
for (var _i = 0, conditionArgs_1 = conditionArgs; _i < conditionArgs_1.length; _i++) {
var arg = conditionArgs_1[_i];
if (arg.vType.$type !== TypeEnum.Boolean) {
throw { message: 'FILTER 函数第一个之后的参数返回类型都必须为布尔类型' };
}
if (!arg.operator) {
throw { message: 'FILTER 函数条件参数必须为条件表达式' };
}
}
later.vType = args[0].vType;
return later.vType;
}
// 生成ID对象
function ID(later) {
var args = later.args;
if (args.length !== 1) {
throw { message: "ID \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" };
}
var arg = args[0];
if ((arg.vType.$type !== 'String' && arg.vType.$type !== 'Integer') || !arg.value) {
throw { message: 'ID 函数的参数必须为字符串或整数类型' };
}
later.vType = generateType('Id');
return later.vType;
}
// 合并字符串
function JOINER(later) {
var args = later.args;
if (hasNotStringArgs(args)) {
throw { message: 'JOINER 函数的参数不能为非 字符串、邮件地址、URL、手机号 等类型' };
}
later.vType = generateType('String');
return later.vType;
}
// 获取字符串函数的长度
// 函数需要为一个参数
// 参数1:目标字符串
function LENGTH(later) {
var args = later.args;
if (args.length !== 1) {
throw { message: 'LENGTH 函数的只接受一个参数' };
}
if (!isString(args[0].vType.$type)) {
throw { message: 'LENGTH 函数的参数必须为 字符串、邮件地址、URL、手机号 等类型' };
}
later.vType = generateType('Integer');
return later.vType;
}
function MAX(later) {
var args = later.args;
var firstArgType = args[0].vType.$type;
if (['IntegerArray', 'NumberArray'].includes(firstArgType)) {
if (args.length > 1) {
throw { message: 'MAX 函数的第一个参数如果为数组,则只接受一个参数' };
}
later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number');
return later.vType;
}
if (hasNotDigitArgs(args)) {
throw { message: 'MAX 函数的参数不能为非数字类型' };
}
later.vType = generateType(hasNumberArgs(args) ? 'Number' : 'Integer');
return later.vType;
}
function MIN(later) {
var args = later.args;
var firstArgType = args[0].vType.$type;
if (['IntegerArray', 'NumberArray'].includes(firstArgType)) {
if (args.length > 1) {
throw { message: 'MIN 函数的第一个参数如果为数组,则只接受一个参数' };
}
later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number');
return later.vType;
}
if (hasNotDigitArgs(args)) {
throw { message: 'MIN 函数的参数不能为非数字类型' };
}
later.vType = generateType(hasNumberArgs(args) ? 'Number' : 'Integer');
return later.vType;
}
// 取余,与 / 相同
function MOD(later) {
var args = later.args;
if (args.length !== 2) {
throw { message: 'MOD 函数只接受两个参数' };
}
if (hasNotDigitArgs(args)) {
throw { message: 'MOD 函数的参数不能为非数字类型' };
}
later.vType = generateType('Number');
return later.vType;
}
function NEGATIVE(later) {
var args = later.args;
if (args.length > 1) {
throw { message: 'NEGATIVE 函数只接受一个参数,且参数类型为布尔类型' };
}
if (args[0].vType.$type !== 'Boolean') {
throw { message: 'NEGATIVE 函数只接受一个参数,且参数类型为布尔类型' };
}
later.vType = generateType('Boolean');
return later.vType;
}
// 生成当前日期时间对象
function NOW(later) {
var args = later.args;
if (args.length !== 0) {
throw { message: "NOW \u51FD\u6570\u7684\u4E0D\u63A5\u53D7\u4EFB\u4F55\u53C2\u6570" };
}
later.vType = generateType('DateTime');
return later.vType;
}
// 判断字符串在目标字符串中出现的次数
// 函数需要为两个参数
// 参数1: 指定字符串
// 参数2: 目标字符串
function OCCURRENCE_TIMES(later) {
var args = later.args;
if (args.length !== 2) {
throw { message: 'OCCURRENCE_TIMES 函数的参数必须为两个' };
}
if (!isString(args[0].vType.$type)) {
throw { message: 'OCCURRENCE_TIMES 函数的第一个参数必须为字符串类型' };
}
if (!isString(args[1].vType.$type)) {
throw { message: 'OCCURRENCE_TIMES 函数的第二个参数必须为字符串类型' };
}
later.vType = generateType(TypeEnum.Integer);
return later.vType;
}
function OR(later) {
var args = later.args;
if (hasNotBooleanArgs(args)) {
throw { message: 'OR 函数的参数只能为布尔类型' };
}
later.vType = generateType('Boolean');
return later.vType;
}
// 向后截取字符串函数
// 函数需要为两个参数
// 参数1: 需要截取的目标字符串
// 参数2: 需要截取的开始索引值
function SUBSTR_AFTER(later) {
var args = later.args;
if (args.length !== 2) {
throw { message: 'SUBSTR_AFTER 函数的参数必须为两个' };
}
if (!isString(args[0].vType.$type)) {
throw { message: 'SUBSTR_AFTER 函数的第一个参数必须为 字符串、邮件地址、URL、手机号 等类型' };
}
if (args[1].vType.$type !== 'Integer') {
throw { message: 'SUBSTR_AFTER 函数的第二个参数必须为整数类型' };
}
later.vType = generateType('String');
return later.vType;
}
// 向前截取字符串函数
// 函数需要为两个参数
// 参数1: 需要截取的目标字符串
// 参数2: 需要截取的开始索引值
function SUBSTR_LAST(later) {
var args = later.args;
if (args.length !== 2) {
throw { message: 'SUBSTR_LAST 函数的参数必须为两个' };
}
if (!isString(args[0].vType.$type)) {
throw { message: 'SUBSTR_LAST 函数的第一个参数必须为 字符串、邮件地址、URL、手机号 等类型' };
}
if (args[1].vType.$type !== 'Integer') {
throw { message: 'SUBSTR_LAST 函数的第二个参数必须为整数类型' };
}
later.vType = generateType('String');
return later.vType;
}
// 字符串截取函数
// 函数需要为三个参数
// 参数1: 需要截取的目标字符串
// 参数2: 需要截取的开始索引值
// 参数3: 需要截取的字符串长度
function SUBSTR(later) {
var args = later.args;
if (args.length !== 3) {
throw { message: 'SUBSTR 函数的参数必须为三个' };
}
if (!isString(args[0].vType.$type)) {
throw { message: 'SUBSTR 函数的第一个参数必须为 字符串、邮件地址、URL、手机号 等类型' };
}
if (args[1].vType.$type !== 'Integer') {
throw { message: 'SUBSTR 函数的第二个参数必须为整数类型' };
}
if (args[2].vType.$type !== 'Integer') {
throw { message: 'SUBSTR 函数的第一个参数必须为整数类型' };
}
later.vType = generateType('String');
return later.vType;
}
function SUM(later) {
var args = later.args;
var firstArgType = args[0].vType.$type;
if (['IntegerArray', 'NumberArray'].includes(firstArgType)) {
if (args.length > 1) {
throw { message: 'SUM 函数的第一个参数如果为数组,则只接受一个参数' };
}
later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number');
return later.vType;
}
if (hasNotDigitArgs(args)) {
throw { message: 'SUM 函数的参数不能为非数字类型' };
}
later.vType = generateType(hasNumberArgs(args) ? 'Number' : 'Integer');
return later.vType;
}
// 生成时间对象
function TIME(later) {
var args = later.args;
if (args.length !== 1) {
throw { message: "TIME \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" };
}
var arg = args[0];
if (arg.vType.$type !== 'String' || !arg.value) {
throw { message: 'TIME 函数的参数必须为字符串类型' };
}
if (!isAcceptableTime(arg.value)) {
throw { message: 'TIME 函数参数必须为 HH-mm-ss' };
}
later.vType = generateType('Time');
return later.vType;
}
var Functions = /*#__PURE__*/Object.freeze({
AND: AND,
AVG: AVG,
COUNT: COUNT,
DATE_ADD: DATE_ADD,
DATE_FETCH: DATE_FETCH,
DATE: DATE,
DATETIME: DATETIME,
DURATION_DIFF: DURATION_DIFF,
FILTER: FILTER,
ID: ID,
JOINER: JOINER,
LENGTH: LENGTH,
MAX: MAX,
MIN: MIN,
MOD: MOD,
NEGATIVE: NEGATIVE,
NOW: NOW,
OCCURRENCE_TIMES: OCCURRENCE_TIMES,
OR: OR,
SUBSTR_AFTER: SUBSTR_AFTER,
SUBSTR_LAST: SUBSTR_LAST,
SUBSTR: SUBSTR,
SUM: SUM,
TIME: TIME
});
// 是否为数字
function isDigit$1(v) {
return ['Number', 'Integer'].includes(v);
}
function isDateTime(v) {
return ['DateTime', 'Date', 'Time'].includes(v);
}
// 是否为字符串
function isStringLike$1(v) {
return ['String', 'Email', 'Url', 'PhoneNr'].includes(v);
}
// function isString(v: string) {
// return v === 'String';
// }
var EqualDataTypes = [
'String', 'Email', 'Url', 'PhoneNr',
'DateTime', 'Date', 'Time',
'Number', 'Integer',
'Boolean',
'Id', 'IdArray'
];
function inference(later) {
if (!later) {
return { vType: undefined };
}
return __assign({}, later, { vType: calcute(later) });
}
function calcute(lat) {
if (!lat) {
return { $type: undefined };
}
if (lat.vType) {
return lat.vType;
}
if (isScalaType(lat.type)) {
lat.vType = { $type: lat.type };
return lat.vType;
}
if (lat.type === TypeEnum.FunctionCall) {
return calcuteFunction(lat);
}
if (!lat.operator) {
lat.vType = calcute(lat);
return lat.vType;
}
// 逻辑非
if (lat.operator && lat.operator.type === TypeEnum.LogicOperator && !lat.left) {
if (!lat.right) {
throw { message: '逻辑非表达式缺少比较值' };
}
var rightType = calcute(lat.right).$type;
if (rightType !== 'Boolean') {
throw { message: '非布尔类型不能参与逻辑运算' };
}
lat.vType = { $type: 'Boolean' };
return lat.vType;
}
// 负数
if (lat.operator && lat.operator.type === 'Arithmetic' && !lat.left) {
if (!lat.right) {
throw { message: '负数表达式缺少数字' };
}
var rightType = calcute(lat.right);
if (!isDigit$1(rightType.$type)) {
throw { message: '非数字类型不能取负数' };
}
lat.vType = rightType;
return lat.vType;
}
lat.vType = calcuteExpression(lat);
return lat.vType;
}
// 运算符的优先级如下 [!] -> [*,/,%] -> [&&,||] -> [+,-] -> [==,<,>,<=,>=]
function calcuteExpression(lat) {
var leftType = calcute(lat.left).$type;
var operator = lat.operator;
var operatorValue = operator.value;
if (operator.type === 'IdentiferComparison') {
if (operator.value === 'BETWEEN') {
if (!['Integer', 'Number', 'DateTime', 'Date', 'Time'].includes(leftType)) {
throw { pos: lat.left.pos, message: '非数字和时间类型不能参计 between and 表达式 的运算' };
}
var andLeftType = calcute(lat.andLeft).$type;
if (!['Integer', 'Number', 'DateTime', 'Date', 'Time'].includes(andLeftType)) {
throw { pos: lat.left.pos, message: '非数字和时间类型不能参计 between and 表达式 的运算' };
}
var andRightType = calcute(lat.andRight).$type;
if (!['Integer', 'Number', 'DateTime', 'Date', 'Time'].includes(andRightType)) {
throw { pos: lat.left.pos, message: '非数字和时间类型不能参计 between and 表达式 的运算' };
}
if (leftType !== andLeftType || leftType !== andRightType || andLeftType !== andRightType) {
throw { pos: lat.left.pos, message: 'between and 表达式参与的数据类型必须相等' };
}
}
else if (['CONTAIN', 'NOT CONTAIN', 'BEGIN WITH', 'END WITH'].includes(operator.value)) {
var oRightType = calcute(lat.right).$type;
if (!isStringLike$1(leftType)) {
throw { pos: lat.left.pos, message: '非 String,Email,Url,PhoneNr 类型不能参与比较' };
}
if (!isStringLike$1(oRightType)) {
throw { pos: lat.right.pos, message: '非 String,Email,Url,PhoneNr 类型不能参与比较' };
}
}
else if (['IN', 'NOT IN'].includes(operator.value)) {
var oRightType = calcute(lat.right).$type;
if (leftType !== 'Id') {
throw { pos: lat.left.pos, message: leftType + " \u64CD\u4F5C\u7B26\u5DE6\u4FA7\u5FC5\u987B\u4E3AId\u7C7B\u578B" };
}
if (oRightType !== 'IdArray') {
throw { pos: lat.left.pos, message: oRightType + " \u64CD\u4F5C\u7B26\u53F3\u4FA7\u5FC5\u987B\u4E3AId\u6570\u7EC4\u7C7B\u578B" };
}
}
else if (['INCLUDE ALL', 'INCLUDE ANY', 'EXCLUDE ALL', 'EXCLUDE ANY'].includes(operator.value)) {
var oRightType = calcute(lat.right).$type;
if (leftType !== 'IdArray') {
throw { pos: lat.left.pos, message: leftType + " \u64CD\u4F5C\u7B26\u5DE6\u4FA7\u5FC5\u987B\u4E3AId\u6570\u7EC4\u7C7B\u578B" };
}
if (oRightType !== 'IdArray') {
throw { pos: lat.left.pos, message: oRightType + " \u64CD\u4F5C\u7B26\u53F3\u4FA7\u5FC5\u987B\u4E3AId\u6570\u7EC4\u7C7B\u578B" };
}
}
return { $type: 'Boolean' };
}
var rightType = calcute(lat.right).$type;
if (operator.type === 'Comparison') {
if (operator.value === '==' || operator.value === '!=') {
if (leftType === 'Null' || rightType === 'Null') {
return { $type: 'Boolean' };
}
if (isStringLike$1(leftType) && isStringLike$1(rightType)) {
return { $type: 'Boolean' };
}
if (leftType === 'Boolean' && rightType === 'Boolean') {
return { $type: 'Boolean' };
}
if (!EqualDataTypes.includes(leftType)) {
throw { pos: lat.left.pos, message: '相等比较符左侧值类型错误' };
}
if (!EqualDataTypes.includes(rightType)) {
throw { pos: lat.right.pos, message: '相等比较符右侧值类型错误' };
}
}
if (!isDigit$1(leftType) && !isDateTime(leftType)) {
throw { pos: lat.left.pos, message: '非数字或日期时间类型不能参与比较' };
}
if (!isDigit$1(rightType) && !isDateTime(rightType)) {
throw { pos: lat.right.pos, message: '非数字或日期时间类型不能参与比较' };
}
if (isDigit$1(leftType) && isDigit$1(rightType)) {
return { $type: 'Boolean' };
}
if (isDateTime(leftType) && isDateTime(rightType) && leftType === rightType) {
return { $type: 'Boolean' };
}
throw { pos: lat.left.pos, message: '比较符两侧的数据类型必须一致' };
}
if (operator.type === 'Arithmetic') {
if (!isDigit$1(leftType)) {
throw { pos: lat.left.pos, message: '非数字类型不能参与计算' };
}
if (!isDigit$1(rightType)) {
throw { pos: lat.right.pos, message: '非数字类型不能参与计算' };
}
if ('+-*%'.includes(operatorValue)) {
var $type = (leftType === 'Number' || rightType === 'Number') ? 'Number' : 'Integer';
return { $type: $type };
}
if (operatorValue === '/') {
return { $type: 'Number' };
}
}
if (lat.operator.type === TypeEnum.LogicOperator) {
if (operatorValue === '&&' || operatorValue === '||') {
if (leftType !== TypeEnum.Boolean) {
throw { message: '非布尔类型不能参与逻辑运算' };
}
if (rightType !== TypeEnum.Boolean) {
throw { message: '非布尔类型不能参与逻辑运算' };
}
return { $type: 'Boolean' };
}
}
throw { code: 'Unknown' };
}
// 计算函数的返回值类型
function calcuteFunction(later) {
var args = later.args;
// if (!args.length) {
// throw { message: 'Function arguments is required.' };
// }
args.forEach(function (item) {
item.vType = calcute(item);
});
var functionName = later.value.toUpperCase();
if ([
'AND', 'AVG', 'COUNT', 'DURATION_DIFF',
'FILTER', 'JOINER', 'LENGTH', 'MAX', 'MIN', 'MOD',
'OCCURRENCE_TIMES', 'OR', 'SUBSTR_AFTER', 'SUBSTR_LAST', 'SUBSTR', 'SUM',
'NEGATIVE', 'NOW', 'DATE', 'TIME', 'DATETIME', 'ID'
].includes(functionName)) {
return Functions[functionName](later);
}
if ([
'DATE_ADD_SECOND',
'DATE_ADD_MINUTE',
'DATE_ADD_HOUR',
'DATE_ADD_DAY',
'DATE_ADD_MONTH',
'DATE_ADD_YEAR'
].includes(functionName)) {
return DATE_ADD(later, later.value);
}
if ([
'WEEK_OF_YEAR',
'YEAR',
'QUARTER',
'MONTH',
'DAY_OF_WEEK',
'DAY',
'HOUR',
'MINUTE',
'ALIGNED_WEEK_OF_MONTH',
'ALIGNED_WEEK_OF_YEAR'
].includes(functionName)) {
return DATE_FETCH(later, later.value);
}
throw { code: 'InvalidFunction', message: '无效的函数' };
}
function ExpressionInference(input, options) {
try {
// tslint:disable-next-line
// console.log(input);
var tokens = generateTokens(input, options);
// tslint:disable-next-line
// console.log(tokens);
var composeTokens = match(tokens);
// tslint:disable-next-line
// console.log(composeTokens);
var inferencedResult = inference(composeTokens);
return inferencedResult.vType;
}
catch (err) {
throw err;
}
}
module.exports = ExpressionInference;