UNPKG

amis-formula

Version:

负责 amis 里面的表达式实现,内置公式,编辑器等

726 lines (722 loc) 24.2 kB
/** * amis-formula v6.13.0 * Copyright 2021-2025 fex */ import { __assign, __spreadArray, __read } from 'tslib'; import { getFilters } from './filter.js'; var TokenName = {}; TokenName[1 /* TokenEnum.BooleanLiteral */] = 'Boolean'; TokenName[2 /* TokenEnum.RAW */] = 'Raw'; TokenName[3 /* TokenEnum.Variable */] = 'Variable'; TokenName[4 /* TokenEnum.OpenScript */] = 'OpenScript'; TokenName[5 /* TokenEnum.CloseScript */] = 'CloseScript'; TokenName[6 /* TokenEnum.EOF */] = 'EOF'; TokenName[7 /* TokenEnum.Identifier */] = 'Identifier'; TokenName[8 /* TokenEnum.Literal */] = 'Literal'; TokenName[9 /* TokenEnum.NumericLiteral */] = 'Numeric'; TokenName[10 /* TokenEnum.Punctuator */] = 'Punctuator'; TokenName[11 /* TokenEnum.StringLiteral */] = 'String'; TokenName[12 /* TokenEnum.RegularExpression */] = 'RegularExpression'; TokenName[13 /* TokenEnum.TemplateRaw */] = 'TemplateRaw'; TokenName[14 /* TokenEnum.TemplateLeftBrace */] = 'TemplateLeftBrace'; TokenName[15 /* TokenEnum.TemplateRightBrace */] = 'TemplateRightBrace'; TokenName[16 /* TokenEnum.OpenFilter */] = 'OpenFilter'; TokenName[17 /* TokenEnum.Char */] = 'Char'; var mainStates = { START: 0, SCRIPT: 1, EXPRESSION: 2, BLOCK: 3, Template: 4, Filter: 5 }; var rawStates = { START: 0, ESCAPE: 1 }; var numberStates = { START: 0, ZERO: 1, DIGIT: 2, POINT: 3, DIGIT_FRACTION: 4, EXP: 5 }; var stringStates = { START: 0, START_QUOTE_OR_CHAR: 1, ESCAPE: 2 }; var punctuatorList = [ '===', '!==', '>>>', '==', '!=', '<>', '<=', '>=', '||', '&&', '++', '--', '<<', '>>', '**', '+=', '*=', '/=', '<', '>', '=', '*', '/', '-', '+', '^', '!', '~', '%', '&', '|', '(', ')', '[', ']', '{', '}', '?', ':', ';', ',', '.', '$' ]; var escapes = { '"': 0, '\\': 1, '/': 2, 'b': 3, 'f': 4, 'n': 5, 'r': 6, 't': 7, 'u': 8 // 4 hexadecimal digits }; function isDigit1to9(char) { return char >= '1' && char <= '9'; } function isDigit(char) { return char >= '0' && char <= '9'; } function isExp(char) { return char === 'e' || char === 'E'; } function escapeString(text, allowedLetter) { if (allowedLetter === void 0) { allowedLetter = []; } return text.replace(/\\(.)/g, function (_, text) { return text === 'b' ? '\b' : text === 'f' ? '\f' : text === 'n' ? '\n' : text === 'r' ? '\r' : text === 't' ? '\t' : text === 'v' ? '\v' : text === '\\' ? '\\' : text === '/' ? '/' : ~allowedLetter.indexOf(text) ? text : _; }); } function formatNumber(value) { return Number(value); } function lexer(input, options) { if (options === void 0) { options = {}; } var line = 1; var column = 1; var index = 0; var mainState = mainStates.START; var states = [mainState]; var tokenCache = []; options = __assign({}, options); var allowFilter = options.allowFilter !== false; if (!options.isFilter) { var filterKeys_1 = Object.keys(getFilters()); if (options.filters) { filterKeys_1.push.apply(filterKeys_1, __spreadArray([], __read(Object.keys(options.filters)), false)); } options.isFilter = function (name) { return filterKeys_1.includes(name); }; } if (options.evalMode || options.variableMode) { pushState(mainStates.EXPRESSION); } function pushState(state) { states.push((mainState = state)); } function popState() { states.pop(); mainState = states[states.length - 1]; } function position(value) { if (value && typeof value === 'string') { var lines = value.split(/[\r\n]+/); return { index: index + value.length, line: line + lines.length - 1, column: column + lines[lines.length - 1].length }; } return { index: index, line: line, column: column }; } function eof() { if (index >= input.length) { return { type: TokenName[6 /* TokenEnum.EOF */], value: undefined, start: position(), end: position() }; } } function raw() { if (mainState !== mainStates.START) { return null; } var buffer = ''; var state = rawStates.START; var i = index; while (i < input.length) { var ch = input[i]; if (state === rawStates.ESCAPE) { if (escapes.hasOwnProperty(ch) || ch === '$') { buffer += ch; i++; state = rawStates.START; } else { var pos = position(buffer + ch); throw new SyntaxError("Unexpected token ".concat(ch, " in ").concat(pos.line, ":").concat(pos.column)); } } else { if (ch === '\\') { buffer += ch; i++; state = rawStates.ESCAPE; continue; } else if (ch === '$') { var nextCh = input[i + 1]; if (nextCh === '{') { break; } else if (nextCh === '$') { // $$ 用法兼容 tokenCache.push({ type: TokenName[3 /* TokenEnum.Variable */], value: '&', raw: '$$', start: position(input.substring(index, i)), end: position(input.substring(index, i + 2)) }); break; } else { // 支持旧的 $varName 的取值方法 var match = /^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*/.exec(input.substring(i + 1)); if (match) { tokenCache.push({ type: TokenName[3 /* TokenEnum.Variable */], value: match[0], raw: match[0], start: position(input.substring(index, i)), end: position(input.substring(index, i + 1 + match[0].length)) }); break; } } } i++; buffer += ch; } } if (i > index) { return { type: TokenName[2 /* TokenEnum.RAW */], value: escapeString(buffer, ['`', '$']), raw: buffer, start: position(), end: position(buffer) }; } return tokenCache.length ? tokenCache.shift() : null; } function openScript() { if (mainState === mainStates.Template || mainState === mainStates.EXPRESSION) { return null; } var ch = input[index]; if (ch === '$') { var nextCh = input[index + 1]; if (nextCh === '{') { pushState(mainStates.SCRIPT); var value = input.substring(index, index + 2); return { type: TokenName[4 /* TokenEnum.OpenScript */], value: value, start: position(), end: position(value) }; } } return null; } function expression() { if (mainState !== mainStates.SCRIPT && mainState !== mainStates.EXPRESSION && mainState !== mainStates.BLOCK && mainState !== mainStates.Filter) { return null; } var token = literal() || identifier() || numberLiteral() || stringLiteral() || punctuator() || char(); if ((token === null || token === void 0 ? void 0 : token.value) === '{' && token.type == 'Punctuator') { pushState(mainStates.BLOCK); } else if ((token === null || token === void 0 ? void 0 : token.value) === '}' && token.type == 'Punctuator') { if (mainState === mainStates.Filter) { popState(); } var prevState = mainState; popState(); if (prevState === mainStates.SCRIPT || prevState === mainStates.EXPRESSION) { return { type: TokenName[prevState === mainStates.EXPRESSION ? 15 /* TokenEnum.TemplateRightBrace */ : 5 /* TokenEnum.CloseScript */], value: token.value, start: position(), end: position(token.value) }; } } // filter 过滤器部分需要特殊处理 if (mainState === mainStates.SCRIPT && (token === null || token === void 0 ? void 0 : token.type) == 'Punctuator' && token.value === '|' && allowFilter) { // 怎么区分是过滤还是位运算呢? // 靠外面反馈吧 if (options === null || options === void 0 ? void 0 : options.isFilter) { var restInput = input.substring(token.start.index + 1).trim(); var m = /^[A-Za-z0-9_$@][A-Za-z0-9_\-$@]*/.exec(restInput); if (!m || !options.isFilter(m[0])) { return token; } } pushState(mainStates.Filter); return { type: TokenName[16 /* TokenEnum.OpenFilter */], value: '|', start: position(), end: position('|') }; } else if (mainState === mainStates.Filter && (token === null || token === void 0 ? void 0 : token.value) === '|' && token.type == 'Punctuator') { return { type: TokenName[16 /* TokenEnum.OpenFilter */], value: '|', start: position(), end: position('|') }; } if (!token && input[index] === '`') { pushState(mainStates.Template); return { type: TokenName[10 /* TokenEnum.Punctuator */], value: '`', start: position(), end: position('`') }; } return token; } function char() { if (mainState !== mainStates.Filter) { return null; } var i = index; var ch = input[i]; if (ch === '\\') { var nextCh = input[i + 1]; if (nextCh === '$' || ~punctuatorList.indexOf(nextCh) || escapes.hasOwnProperty(nextCh)) { i++; ch = nextCh === 'b' ? '\b' : nextCh === 'f' ? '\f' : nextCh === 'n' ? '\n' : nextCh === 'r' ? '\r' : nextCh === 't' ? '\t' : nextCh === 'v' ? '\v' : nextCh; } else { var pos = position(input.substring(index, index + 2)); throw new SyntaxError("Unexpected token ".concat(nextCh, " in ").concat(pos.line, ":").concat(pos.column)); } } var token = { type: TokenName[17 /* TokenEnum.Char */], value: ch, start: position(), end: position(input.substring(index, i + 1)) }; return token; } function template() { if (mainState !== mainStates.Template) { return null; } var state = stringStates.START; var i = index; while (i < input.length) { var ch = input[i]; if (state === stringStates.ESCAPE) { if (escapes.hasOwnProperty(ch) || ch === '`' || ch === '$') { i++; state = stringStates.START_QUOTE_OR_CHAR; } else { var pos = position(input.substring(index, i + 1)); throw new SyntaxError("Unexpected token ".concat(ch, " in ").concat(pos.line, ":").concat(pos.column)); } } else if (ch === '\\') { i++; state = stringStates.ESCAPE; } else if (ch === '`') { popState(); tokenCache.push({ type: TokenName[10 /* TokenEnum.Punctuator */], value: '`', start: position(input.substring(index, i)), end: position(input.substring(index, i + 1)) }); break; } else if (ch === '$') { var nextCh = input[i + 1]; if (nextCh === '{') { pushState(mainStates.EXPRESSION); tokenCache.push({ type: TokenName[14 /* TokenEnum.TemplateLeftBrace */], value: '${', start: position(input.substring(index, i)), end: position(input.substring(index, i + 2)) }); break; } i++; } else { i++; } } if (i > index) { var value = input.substring(index, i); return { type: TokenName[13 /* TokenEnum.TemplateRaw */], value: escapeString(value, ['`', '$']), raw: value, start: position(), end: position(value) }; } return tokenCache.length ? tokenCache.shift() : null; } function skipWhiteSpace() { while (index < input.length) { var ch = input[index]; if (ch === '\r') { // CR (Unix) index++; line++; column = 1; if (input.charAt(index) === '\n') { // CRLF (Windows) index++; } } else if (ch === '\n') { // LF (MacOS) index++; line++; column = 1; } else if (ch === '\t' || ch === ' ') { index++; column++; } else { break; } } } function punctuator() { var find = punctuatorList.find(function (punctuator) { return input.substring(index, index + punctuator.length) === punctuator; }); if (find) { return { type: TokenName[10 /* TokenEnum.Punctuator */], value: find, start: position(), end: position(find) }; } return null; } // substring(index, index + 4) 在某些情况会匹配错误 // 比如变量名称为 trueValue // ${value2|isTrue:trueValue:falseValue} function literal() { // {4,10} 匹配长度就足够判断 ("true").length <= targetLength <= ("undefined").length + 1 var match = input.substring(index).match(/^\w{4,10}/); if (!match) { return null; } var keyword = match[0].toLowerCase(); var value = keyword; var isLiteral = false; if (keyword === 'true' || keyword === 'null') { isLiteral = true; value = keyword === 'true' ? true : null; } else if (keyword === 'false') { isLiteral = true; value = false; } else if (keyword === 'undefined') { isLiteral = true; value = undefined; } if (isLiteral) { return { type: value === true || value === false ? TokenName[1 /* TokenEnum.BooleanLiteral */] : TokenName[8 /* TokenEnum.Literal */], value: value, raw: keyword, start: position(), end: position(keyword) }; } return null; } function numberLiteral() { var i = index; var passedValueIndex = i; var state = numberStates.START; iterator: while (i < input.length) { var char_1 = input.charAt(i); switch (state) { case numberStates.START: { if (char_1 === '0') { passedValueIndex = i + 1; state = numberStates.ZERO; } else if (isDigit1to9(char_1)) { passedValueIndex = i + 1; state = numberStates.DIGIT; } else { return null; } break; } case numberStates.ZERO: { if (char_1 === '.') { state = numberStates.POINT; } else if (isExp(char_1)) { state = numberStates.EXP; } else { break iterator; } break; } case numberStates.DIGIT: { if (isDigit(char_1)) { passedValueIndex = i + 1; } else if (char_1 === '.') { state = numberStates.POINT; } else if (isExp(char_1)) { state = numberStates.EXP; } else { break iterator; } break; } case numberStates.POINT: { if (isDigit(char_1)) { passedValueIndex = i + 1; state = numberStates.DIGIT_FRACTION; } else { break iterator; } break; } case numberStates.DIGIT_FRACTION: { if (isDigit(char_1)) { passedValueIndex = i + 1; } else if (isExp(char_1)) { state = numberStates.EXP; } else { break iterator; } break; } } i++; } if (passedValueIndex > 0) { var value = input.slice(index, passedValueIndex); return { type: TokenName[9 /* TokenEnum.NumericLiteral */], value: formatNumber(value), raw: value, start: position(), end: position(value) }; } return null; } function stringLiteral() { var startQuote = '"'; var state = stringStates.START; var i = index; while (i < input.length) { var ch = input[i]; if (state === stringStates.START) { if (ch === '"' || ch === "'") { startQuote = ch; i++; state = stringStates.START_QUOTE_OR_CHAR; } else { break; } } else if (state === stringStates.ESCAPE) { if (escapes.hasOwnProperty(ch) || ch === startQuote) { i++; state = stringStates.START_QUOTE_OR_CHAR; } else { var pos = position(input.substring(index, i + 1)); throw new SyntaxError("Unexpected token ".concat(ch, " in ").concat(pos.line, ":").concat(pos.column)); } } else if (ch === '\\') { i++; state = stringStates.ESCAPE; } else if (ch === startQuote) { i++; break; } else { i++; } } if (i > index) { var value = input.substring(index, i); return { type: TokenName[11 /* TokenEnum.StringLiteral */], value: escapeString(value.substring(1, value.length - 1), [startQuote]), raw: value, start: position(), end: position(value) }; } return null; } function identifier() { // 变量模式是 resolveVariable 的时候使用的 // 这个纯变量获取模式,不支持其他什么表达式 // 仅仅支持 xxx.xxx 或者 xxx[ exression ] 这类语法 // 所以纯变量模式支持纯数字作为变量名 var reg = (options === null || options === void 0 ? void 0 : options.variableMode) ? /^[\u4e00-\u9fa5A-Za-z0-9_$@][\u4e00-\u9fa5A-Za-z0-9_\-$@]*/ : /^(?:[\u4e00-\u9fa5A-Za-z_$@]([\u4e00-\u9fa5A-Za-z0-9_$@]|\\(?:\.|\[|\]|\(|\)|\{|\}|\s|=|!|>|<|\||&|\+|-|\*|\/|\^|~|%|&|\?|:|;|,))*|\d+[\u4e00-\u9fa5A-Za-z_$@](?:[\u4e00-\u9fa5A-Za-z0-9_$@]|\\(?:\.|\[|\]|\(|\)|\{|\}|\s|=|!|>|<|\||&|\+|-|\*|\/|\^|~|%|&|\?|:|;|,))*)/; var match = reg.exec(input.substring(index, index + 256) // 变量长度不能超过 256 ); if (match) { return { type: TokenName[7 /* TokenEnum.Identifier */], value: match[0].replace(/\\(\.|\[|\]|\(|\)|\{|\}|\s|=|!|>|<|\||&|\+|-|\*|\/|\^|~|%|&|\?|:|;|,)/g, function (_, v) { return v; }), start: position(), end: position(match[0]) }; } return null; } function getNextToken() { if (tokenCache.length) { return tokenCache.shift(); } if (mainState === mainStates.SCRIPT || mainState === mainStates.EXPRESSION || mainState === mainStates.BLOCK) { skipWhiteSpace(); } return eof() || raw() || openScript() || expression() || template(); } return { next: function () { var token = getNextToken(); if (token) { index = token.end.index; line = token.end.line; column = token.end.column; return token; } var pos = position(); throw new SyntaxError("unexpected character \"".concat(input[index], "\" at ").concat(pos.line, ":").concat(pos.column)); } }; } export { TokenName, lexer };