UNPKG

enterprise-loyalty-components

Version:

企业版忠诚度组件库

1,408 lines (1,368 loc) 48.7 kB
'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;