amis-formula
Version:
负责 amis 里面的表达式实现,内置公式,编辑器等
726 lines (722 loc) • 24.2 kB
JavaScript
/**
* 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 };