index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. const ESCAPE = {
  2. 'n': '\n',
  3. 'f': '\f',
  4. 'r': '\r',
  5. 't': '\t',
  6. 'v': '\v',
  7. };
  8. const CONSTANTS = {
  9. 'null': data => null,
  10. 'true': data => true,
  11. 'false': data => false,
  12. 'undefined': data => undefined,
  13. }
  14. const OPERATORS = {
  15. '+': (data, a, b) => a(data) + b(data),
  16. '-': (data, a, b) => a(data) - b(data),
  17. '*': (data, a, b) => a(data) * b(data),
  18. '/': (data, a, b) => a(data) / b(data),
  19. '%': (data, a, b) => a(data) % b(data),
  20. '===': (data, a, b) => a(data) === b(data),
  21. '!==': (data, a, b) => a(data) !== b(data),
  22. '==': (data, a, b) => a(data) == b(data),
  23. '!=': (data, a, b) => a(data) != b(data),
  24. '<': (data, a, b) => a(data) < b(data),
  25. '>': (data, a, b) => a(data) > b(data),
  26. '<=': (data, a, b) => a(data) <= b(data),
  27. '>=': (data, a, b) => a(data) >= b(data),
  28. '&&': (data, a, b) => a(data) && b(data),
  29. '||': (data, a, b) => a(data) || b(data),
  30. '!': (data, a) => !a(data),
  31. };
  32. function isNumber(char) {
  33. return char >= '0' && char <= '9' && typeof char === 'string';
  34. }
  35. function isExpOperator(char) {
  36. return (char === '-' || char === '+' || isNumber(char));
  37. }
  38. function isIdent(char) {
  39. return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || char === '_' || char === '$';
  40. }
  41. class Expression {
  42. constructor(content) {
  43. if (!content) throw new Error('invalid expression');
  44. this.content = content;
  45. }
  46. lex() {
  47. let content = this.content;
  48. let length = content.length;
  49. let index = 0;
  50. let tokens = [];
  51. while (index < length) {
  52. let char = content.charAt(index);
  53. if (char === '"' || char === '\'') {
  54. // 字符串
  55. let start = ++index;
  56. let escape = false;
  57. let value = '';
  58. let token;
  59. while (index < length) {
  60. let c = content.charAt(index);
  61. if (escape) {
  62. if (c === 'u') {
  63. let hex = content.substring(index + 1, index + 5);
  64. if (!hex.match(/[\da-f]{4}/i)) {
  65. throw new Error(`invalid expression: ${content}, invalid unicode escape [\\u${hex}]`);
  66. }
  67. index += 4;
  68. value += String.fromCharCode(parseInt(hex, 16));
  69. } else {
  70. let rep = ESCAPE[c];
  71. value = value + (rep || c);
  72. }
  73. escape = false;
  74. } else if (c === '\\') {
  75. escape = true;
  76. } else if (c === char) {
  77. index++;
  78. token = {
  79. index: start,
  80. constant: true,
  81. text: char + value + char,
  82. value,
  83. };
  84. break;
  85. } else {
  86. value += c;
  87. }
  88. index++;
  89. }
  90. if (!token) {
  91. throw new Error(`invalid expression: ${content}`);
  92. } else {
  93. tokens.push(token);
  94. }
  95. } else if (isNumber(char) || (char === '.' && isNumber(content.charAt(index + 1)))) {
  96. // 数字
  97. let start = index;
  98. let value = '';
  99. while (index < length) {
  100. let c = content.charAt(index).toLowerCase();
  101. if (c === '.' || isNumber(c)) {
  102. value += c;
  103. } else {
  104. let c2 = content.charAt(index + 1);
  105. if (c === 'e' && isExpOperator(c2)) {
  106. value += c;
  107. } else if (isExpOperator(c) && c2 && isNumber(c2) && value.charAt(value.length - 1) === 'e') {
  108. value += c;
  109. } else if (isExpOperator(c) && (!c2 || !isNumber(c2)) && value.charAt(value.length - 1) == 'e') {
  110. throw new Error(`invalid expression: ${content}`);
  111. } else {
  112. break;
  113. }
  114. }
  115. index++;
  116. }
  117. tokens.push({
  118. index: start,
  119. constant: true,
  120. text: value,
  121. value: Number(value),
  122. })
  123. } else if (isIdent(char)) {
  124. // 标识符
  125. let start = index;
  126. while (index < length) {
  127. let c = content.charAt(index);
  128. if (!(isIdent(c) || isNumber(c))) {
  129. break;
  130. }
  131. index++;
  132. }
  133. tokens.push({
  134. index: start,
  135. text: content.slice(start, index),
  136. identifier: true
  137. });
  138. } else if ('(){}[].,;:?'.indexOf(char) >= 0) {
  139. // 边界
  140. tokens.push({
  141. index,
  142. text: char
  143. });
  144. index++;
  145. } else if (char === ' ' || char === '\r' || char === '\t' || char === '\n' || char === '\v' || char === '\u00A0') {
  146. // 空格
  147. index++;
  148. } else {
  149. // 操作符
  150. let char2 = char + content.charAt(index + 1);
  151. let char3 = char2 + content.charAt(index + 2);
  152. let op1 = OPERATORS[char];
  153. let op2 = OPERATORS[char2];
  154. let op3 = OPERATORS[char3];
  155. if (op1 || op2 || op3) {
  156. let text = op3 ? char3 : op2 ? char2 : char;
  157. tokens.push({
  158. index: index,
  159. text,
  160. operator: true
  161. });
  162. index += text.length;
  163. } else {
  164. throw new Error(`invalid expression: ${content}`);
  165. }
  166. }
  167. }
  168. this.tokens = tokens;
  169. return tokens;
  170. }
  171. parse() {
  172. let tokens = this.lex();
  173. let func;
  174. let token = tokens[0];
  175. let text = token.text;
  176. if (tokens.length > 0 && text !== '}' && text !== ')' && text !== ']') {
  177. func = this.expression();
  178. }
  179. return data => func && func(data);
  180. }
  181. expect(text) {
  182. let tokens = this.tokens;
  183. let token = tokens[0];
  184. if (!text || text === (token && token.text)) {
  185. return tokens.shift();
  186. }
  187. }
  188. consume(text) {
  189. if (!this.tokens.length) throw new Error(`parse expression error: ${this.content}`);
  190. let token = this.expect(text);
  191. if (!token) throw new Error(`parse expression error: ${this.content}`);
  192. return token;
  193. }
  194. expression() {
  195. return this.ternary();
  196. }
  197. ternary() {
  198. let left = this.logicalOR();
  199. let token;
  200. if (token = this.expect('?')) {
  201. let middle = this.expression();
  202. this.consume(':')
  203. let right = this.expression();
  204. return data => left(data) ? middle(data) : right(data);
  205. }
  206. return left;
  207. }
  208. binary(left, op, right) {
  209. let fn = OPERATORS[op];
  210. return data => fn(data, left, right);
  211. }
  212. unary() {
  213. let token;
  214. if (this.expect('+')) {
  215. return this.primary();
  216. } else if (token = this.expect('-')) {
  217. return this.binary(data => 0, token.text, this.unary());
  218. } else if (token = this.expect('!')) {
  219. let fn = OPERATORS[token.text];
  220. let right = this.unary();
  221. return data => fn(data, right);
  222. } else {
  223. return this.primary();
  224. }
  225. }
  226. logicalOR() {
  227. let left = this.logicalAND();
  228. let token;
  229. while (token = this.expect('||')) {
  230. left = this.binary(left, token.text, this.logicalAND());
  231. }
  232. return left;
  233. }
  234. logicalAND() {
  235. let left = this.equality();
  236. let token;
  237. while (token = this.expect('&&')) {
  238. left = this.binary(left, token.text, this.equality());
  239. }
  240. return left;
  241. }
  242. equality() {
  243. let left = this.relational();
  244. let token;
  245. while (token = this.expect('==') || this.expect('!=') || this.expect('===') || this.expect('!==')) {
  246. left = this.binary(left, token.text, this.relational());
  247. }
  248. return left;
  249. }
  250. relational() {
  251. let left = this.additive();
  252. let token;
  253. while (token = this.expect('<') || this.expect('>') || this.expect('<=') || this.expect('>=')) {
  254. left = this.binary(left, token.text, this.additive());
  255. }
  256. return left;
  257. }
  258. additive() {
  259. let left = this.multiplicative();
  260. let token;
  261. while (token = this.expect('+') || this.expect('-')) {
  262. left = this.binary(left, token.text, this.multiplicative());
  263. }
  264. return left;
  265. }
  266. multiplicative() {
  267. let left = this.unary();
  268. let token;
  269. while (token = this.expect('*') || this.expect('/') || this.expect('%')) {
  270. left = this.binary(left, token.text, this.unary());
  271. }
  272. return left;
  273. }
  274. primary() {
  275. let token = this.tokens[0];
  276. let primary;
  277. if (this.expect('(')) {
  278. primary = this.expression();
  279. this.consume(')');
  280. } else if (this.expect('[')) {
  281. primary = this.array();
  282. } else if (this.expect('{')) {
  283. primary = this.object();
  284. } else if (token.identifier && token.text in CONSTANTS) {
  285. primary = CONSTANTS[this.consume().text];
  286. } else if (token.identifier) {
  287. primary = this.identifier();
  288. } else if (token.constant) {
  289. primary = this.constant();
  290. } else {
  291. throw new Error(`parse expression error: ${this.content}`);
  292. }
  293. let next;
  294. let context;
  295. while (next = this.expect('(') || this.expect('[') || this.expect('.')) {
  296. if (next.text === '(') {
  297. primary = this.functionCall(primary, context);
  298. context = null;
  299. } else if (next.text === '[') {
  300. context = primary;
  301. primary = this.objectIndex(primary);
  302. } else {
  303. context = primary;
  304. primary = this.fieldAccess(primary);
  305. }
  306. }
  307. return primary;
  308. }
  309. fieldAccess(object) {
  310. let getter = this.identifier();
  311. return data => {
  312. let o = object(data);
  313. return o && getter(o);
  314. };
  315. }
  316. objectIndex(object) {
  317. let indexFn = this.expression();
  318. this.consume(']');
  319. return data => {
  320. let o = object(data);
  321. let key = indexFn(data) + '';
  322. return o && o[key];
  323. };
  324. }
  325. functionCall(func, context) {
  326. let args = [];
  327. if (this.tokens[0].text !== ')') {
  328. do {
  329. args.push(this.expression());
  330. } while (this.expect(','));
  331. }
  332. this.consume(')');
  333. return data => {
  334. let callContext = context && context(data);
  335. let fn = func(data, callContext);
  336. return fn && fn.apply(callContext, args.length ? args.map(arg => arg(data)) : null);
  337. };
  338. }
  339. array() {
  340. let elements = [];
  341. let token = this.tokens[0];
  342. if (token.text !== ']') {
  343. do {
  344. if (this.tokens[0].text === ']') break;
  345. elements.push(this.expression());
  346. } while (this.expect(','));
  347. }
  348. this.consume(']');
  349. return data => elements.map(element => element(data));
  350. }
  351. object() {
  352. let keys = [];
  353. let values = [];
  354. let token = this.tokens[0];
  355. if (token.text !== '}') {
  356. do {
  357. token = this.tokens[0];
  358. if (token.text === '}') break;
  359. token = this.consume();
  360. if (token.constant) {
  361. keys.push(token.value);
  362. } else if (token.identifier) {
  363. keys.push(token.text);
  364. } else {
  365. throw new Error(`parse expression error: ${this.content}`);
  366. }
  367. this.consume(':');
  368. values.push(this.expression());
  369. } while (this.expect(','));
  370. }
  371. this.consume('}');
  372. return data => {
  373. let object = {};
  374. for (let i = 0, length = values.length; i < length; i++) {
  375. object[keys[i]] = values[i](data);
  376. }
  377. return object;
  378. };
  379. }
  380. identifier() {
  381. let id = this.consume().text;
  382. let token = this.tokens[0];
  383. let token2 = this.tokens[1];
  384. let token3 = this.tokens[2];
  385. // 连续读取 . 操作符后的非函数调用标识符
  386. while (token && token.text === '.' && token2 && token2.identifier && token3 && token3.text !== '(') {
  387. id += this.consume().text + this.consume().text;
  388. token = this.tokens[0];
  389. token2 = this.tokens[1];
  390. token3 = this.tokens[2];
  391. }
  392. return data => {
  393. let elements = id.split('.');
  394. let key;
  395. for (let i = 0; elements.length > 1; i++) {
  396. key = elements.shift();
  397. data = data[key];
  398. if (!data) break;
  399. }
  400. key = elements.shift();
  401. return data && data[key];
  402. };
  403. }
  404. constant() {
  405. let value = this.consume().value;
  406. return data => value;
  407. }
  408. }
  409. module.exports = Expression;