移动端
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

513 Zeilen
12 KiB

  1. export const Expression = function(_str, _flag = 0, _placeholderValueMap = {})
  2. {
  3. this.pos = 0; // 当前解析表达式字符串的位置
  4. this.str = _str; // 表达式
  5. this.errno = Expression.NO_ERROR; // 当前错误码
  6. this.placeholderValueMap = _placeholderValueMap; // 占位符值字典
  7. this.flag = _flag | Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION; // 行为标记
  8. this.placeholders = [];
  9. }
  10. Expression.NO_ERROR = 0;
  11. Expression.ERROR = 1;
  12. Expression.NULL_STRING = 2;
  13. Expression.BY_ZERO = 3;
  14. Expression.MISSING_DIGIT = 4;
  15. Expression.MISSING_RIGHT_PARENT = 5;
  16. Expression.MISSING_OPERATOR = 6;
  17. Expression.INVALID_CHARACTER = 7;
  18. Expression.MISSING_PLACEHOLDER = 8;
  19. Expression.INVALID_DIGIT = 9;
  20. Expression.INVALID_PLACEHOLDER = 10;
  21. Expression.MISSING_RIGHT_PLACEHOLDER_END = 11;
  22. Expression.INVALID_PLACEHOLDER_FUNCTION = 12;
  23. Expression.MISSING_COMPARE_OPERATOR = 13;
  24. Expression.INVALID_COMPARE_OPERATOR = 14;
  25. Expression.INCOMPLETE = 15;
  26. Expression.ErrorStr = [
  27. "",
  28. "错误",
  29. "空字符串",
  30. "除0",
  31. "缺失数字",
  32. "缺失右括号",
  33. "缺失运算符",
  34. "无效字符",
  35. "缺失占位符",
  36. "无效数字",
  37. "无效占位符",
  38. "缺失占位符结尾标识",
  39. "无效占位符内置函数",
  40. "缺失比较运算符",
  41. "无效比较运算符",
  42. "表达式不完整",
  43. ];
  44. Expression.PARSE_FLAG_NONE = 0;
  45. Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_RETURN_ZERO = 1; // 解析到错误则当前部分返回0
  46. Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION = 1 << 1; // 解析到错误则抛出异常
  47. Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER = 1 << 3; // 所有数字都作为占位符
  48. Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER = 1 << 4; // 忽略占位符缺失
  49. Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO = 1 << 5; // 忽略除0
  50. // 是否读取到字符串结尾
  51. Expression.prototype.__EOF = function()
  52. {
  53. return !this.str || this.pos >= this.str.length;
  54. };
  55. // 字符串转为数字
  56. Expression.prototype.__StrTo = function(numStr)
  57. {
  58. return Number(numStr)
  59. };
  60. // 开始解析, 重置状态
  61. Expression.prototype.__Ready = function()
  62. {
  63. this.pos = 0;
  64. this.errno = Expression.NO_ERROR;
  65. this.placeholders = [];
  66. let err = this.__CheckStr();
  67. return (err === Expression.NO_ERROR);
  68. };
  69. // 解析 值 括号 占位符 的优先级最高的表达式
  70. Expression.prototype.__EvalV = function()
  71. {
  72. if(this.__EOF())
  73. {
  74. return this.__FatalError(Expression.INCOMPLETE);
  75. }
  76. this.__SkipBlank();
  77. let nStr = this.str.substring(this.pos);
  78. let p = 0;
  79. let nag = false;
  80. let result = 0;
  81. if(nStr.charAt(p) === '-')
  82. {
  83. nag = true;
  84. this.pos++;
  85. p++;
  86. }
  87. p += this.__SkipBlank();
  88. if(nStr.charAt(p) === '(') // 括号表达式
  89. {
  90. p++;
  91. this.pos++;
  92. result = this.__EvalP();
  93. if(this.str.charAt(this.pos) !== ')')
  94. {
  95. return this.__FatalError(Expression.MISSING_RIGHT_PARENT);
  96. }
  97. this.pos++;
  98. }
  99. else if(nStr.charAt(p) === '{') // 占位符
  100. {
  101. p++;
  102. this.pos++;
  103. p += this.__SkipBlank();
  104. let d = '';
  105. let size = 0;
  106. let ch = nStr.charAt(p);
  107. while("`~@#%^&*()-+=,/?:;\"'|\\[{]}".indexOf(ch) === -1) // 中文支持
  108. {
  109. size++;
  110. d += ch;
  111. p++;
  112. this.pos++;
  113. if(p >= nStr.length)
  114. {
  115. return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END);
  116. }
  117. ch = nStr.charAt(p);
  118. }
  119. p += this.__SkipBlank();
  120. if(this.str.charAt(this.pos) !== '}')
  121. {
  122. return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END);
  123. }
  124. this.pos++;
  125. if(size > 0)
  126. {
  127. result = this.__ParsePlaceholder(d);
  128. }
  129. else
  130. {
  131. return this.__NormalError(Expression.MISSING_PLACEHOLDER);
  132. }
  133. }
  134. else // 纯数字
  135. {
  136. let d = '';
  137. let size = 0;
  138. let ch = nStr.charAt(p);
  139. while((ch >= '0' && ch <= '9') || ch === '.')
  140. {
  141. size++;
  142. d += ch;
  143. p++;
  144. this.pos++;
  145. if(p >= nStr.length)
  146. break;
  147. ch = nStr.charAt(p);
  148. }
  149. if(size > 0)
  150. {
  151. let m = d.indexOf(".");
  152. if(m !== -1)
  153. {
  154. let n = d.lastIndexOf(".");
  155. if(n !== -1 && m !== n)
  156. {
  157. return this.__FatalError(Expression.INVALID_DIGIT);
  158. }
  159. if(m === 0 || m === d.length - 1)
  160. d = d.substr(0, m) + d.substr(m + 1);
  161. }
  162. if(this.flag & Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER)
  163. result = this.__ParsePlaceholder(d);
  164. else
  165. result = this.__StrTo(d);
  166. }
  167. else
  168. {
  169. return this.__FatalError(Expression.MISSING_DIGIT);
  170. }
  171. }
  172. if(nag)
  173. result = -result;
  174. return result;
  175. };
  176. // 解析* /的优先级相对较高的表达式
  177. Expression.prototype.__EvalM = function()
  178. {
  179. if(this.__EOF())
  180. {
  181. return this.__FatalError(Expression.INCOMPLETE);
  182. }
  183. let first = this.__EvalV();
  184. let result = first;
  185. this.__SkipBlank();
  186. let nStr = this.str.substring(this.pos);
  187. while(nStr.length > 0)
  188. {
  189. let ch = nStr.charAt(0);
  190. if(ch !== '*' && ch !== '/')
  191. return result;
  192. else
  193. {
  194. this.pos++;
  195. let second = this.__EvalV();
  196. if(ch === '*')
  197. result = result *second;
  198. else
  199. {
  200. if(second === 0)
  201. {
  202. return this.__NormalError(Expression.BY_ZERO);
  203. }
  204. result = result / second;
  205. }
  206. }
  207. nStr = this.str.substring(this.pos);
  208. }
  209. return result;
  210. };
  211. // 解析+ -的优先级最低的表达式
  212. Expression.prototype.__EvalP = function()
  213. {
  214. if(this.__EOF())
  215. {
  216. return this.__FatalError(Expression.INCOMPLETE);
  217. }
  218. this.__SkipBlank();
  219. let first = this.__EvalM();
  220. let result = first;
  221. let nStr = this.str.substring(this.pos);
  222. while(nStr.length > 0)
  223. {
  224. let ch = nStr.charAt(0);
  225. if(ch !== '+' && ch !== '-')
  226. return result;
  227. else
  228. {
  229. this.pos++;
  230. let second = this.__EvalM();
  231. if(ch === '+')
  232. result = result + second;
  233. else
  234. result = result - second;
  235. }
  236. nStr = this.str.substring(this.pos);
  237. }
  238. return result;
  239. };
  240. // 解析比较运算符
  241. Expression.prototype.__EvalCmp = function()
  242. {
  243. if(this.__EOF())
  244. {
  245. return this.__FatalError(Expression.MISSING_COMPARE_OPERATOR);
  246. }
  247. this.__SkipBlank();
  248. let result = '';
  249. let nStr = this.str.substring(this.pos);
  250. while(nStr.length > 0)
  251. {
  252. let ch = nStr.charAt(0);
  253. if(ch !== '=' && ch !== '!' && ch !== '>' && ch !== '<')
  254. break;
  255. else
  256. {
  257. if(result.length === 0)
  258. {
  259. result += ch;
  260. this.pos++;
  261. }
  262. else // === 1
  263. {
  264. if(result !== "=") // 如果第一个字符不是=, 第二个字符必定是=
  265. {
  266. if(ch !== '=')
  267. return this.__FatalError(Expression.INVALID_COMPARE_OPERATOR);
  268. }
  269. else // 如果第一个字符是=, 第二个字符可以是=
  270. {
  271. if(ch === '=')
  272. result += ch;
  273. else
  274. {
  275. break;
  276. }
  277. }
  278. result += ch;
  279. this.pos++;
  280. }
  281. }
  282. if(result.length >= 2)
  283. break;
  284. nStr = this.str.substring(this.pos);
  285. }
  286. if(result === "=")
  287. result = "==";
  288. return result;
  289. };
  290. // 开始计算
  291. Expression.prototype.Calc = function()
  292. {
  293. if(!this.__Ready())
  294. return this.__OnError(0);
  295. return this.__EvalP();
  296. };
  297. // 判断表达式断言
  298. Expression.prototype.Predicate = function()
  299. {
  300. if(!this.__Ready())
  301. return this.__OnError(false);
  302. let a = this.__EvalP();
  303. if(this.HasError())
  304. return this.__OnError(false);
  305. let op = this.__EvalCmp();
  306. if(this.HasError())
  307. return this.__OnError(false);
  308. let b = this.__EvalP();
  309. if(this.HasError())
  310. return this.__OnError(false);
  311. //console.log(a + op + b);
  312. return eval(a + op + b);
  313. };
  314. Expression.prototype.FLAG = function(flags)
  315. {
  316. for(let i of arguments)
  317. {
  318. if((this.flag & i) === i)
  319. return true;
  320. }
  321. return false;
  322. }
  323. // 普通错误, 仅设置可允许忽略的错误时调用, 错误被忽略时不设置错误码
  324. Expression.prototype.__NormalError = function(err)
  325. {
  326. switch(err)
  327. {
  328. case Expression.BY_ZERO:
  329. if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO))
  330. return 0;
  331. break;
  332. case Expression.MISSING_PLACEHOLDER:
  333. if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER))
  334. return 0;
  335. break;
  336. case Expression.INVALID_DIGIT:
  337. if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_INVALID_DIGIT))
  338. return 0;
  339. break;
  340. }
  341. return this.__FatalError(err);
  342. };
  343. // 全局错误处理
  344. Expression.prototype.__OnError = function(ifError)
  345. {
  346. if(this.FLAG(Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION)) // 抛异常
  347. {
  348. throw new Error(this.Error());
  349. }
  350. else // 返回0
  351. {
  352. return ifError;
  353. }
  354. };
  355. // 计算是否有错误
  356. Expression.prototype.HasError = function()
  357. {
  358. return this.errno !== Expression.NO_ERROR;
  359. };
  360. // 返回计算的错误, 并清除错误
  361. Expression.prototype.GetError = function()
  362. {
  363. let e = this.errno;
  364. this.errno = Expression.NO_ERROR;
  365. return e;
  366. }
  367. // 获取当前错误的字符串
  368. Expression.prototype.Error = function()
  369. {
  370. return Expression.ErrorStr[this.errno];
  371. }
  372. // 致命错误
  373. Expression.prototype.__FatalError = function(err)
  374. {
  375. this.errno = err;
  376. throw new Error(this.Error());
  377. };
  378. Expression.prototype.ParsedPlaceholders = function()
  379. {
  380. return this.placeholders;
  381. }
  382. // 解析占位符
  383. Expression.prototype.__ParsePlaceholder = function(name)
  384. {
  385. this.placeholders.push(name);
  386. if(!this.placeholderValueMap)
  387. {
  388. return this.__NormalError(Expression.MISSING_PLACEHOLDER);
  389. }
  390. if(name.startsWith("$")) // 特殊环境变量
  391. return this.__ParseNumber(this.placeholderValueMap[name]);
  392. let ptr = this.placeholderValueMap;
  393. let d = null;
  394. let notFetch = false;
  395. let i = 0;
  396. do
  397. {
  398. let index = name.indexOf(".", i);
  399. let s = index === -1 ? name.substring(i) : name.substring(i, index);
  400. i += s.length + 1;
  401. s = s.trim();
  402. if(s.startsWith("$")) // 函数管线
  403. {
  404. if(!notFetch) // 此时当前值应该是数字, 不再继续向下取Object/Map
  405. {
  406. d = this.__ParseNumber(ptr); // 当前值转换为数字
  407. notFetch = true; // 此时当前值应该是数字, 不再继续向下取Object/Map
  408. }
  409. switch(s.substring(1).toLowerCase())
  410. {
  411. case "abs":
  412. d = Math.abs(d);
  413. break;
  414. case "neg":
  415. d = -d;
  416. break;
  417. case "lt0":
  418. if(d >= 0)
  419. return 0;
  420. break;
  421. case "gt0":
  422. if(d <= 0)
  423. return 0;
  424. break;
  425. default:
  426. return this.__FatalError(Expression.INVALID_PLACEHOLDER_FUNCTION);
  427. }
  428. }
  429. else
  430. {
  431. if(notFetch/*null != d*/) // TODO: 被管线函数处理过, 占位符表达式书写不应该出现该情况, 管线函数应都在表达式尾部, 并且不在表达式起始
  432. return d;
  433. ptr = ptr[s];
  434. if(null === ptr || undefined === ptr)
  435. {
  436. return this.__NormalError(Expression.MISSING_PLACEHOLDER);
  437. }
  438. }
  439. } while(i < name.length);
  440. if(null == d)
  441. return this.__ParseNumber(ptr);
  442. else
  443. return d;
  444. };
  445. // 检查表达式字符串
  446. Expression.prototype.__CheckStr = function()
  447. {
  448. if(!this.str)
  449. {
  450. return Expression.NULL_STRING;
  451. }
  452. return Expression.NO_ERROR;
  453. };
  454. // 跳过空白符
  455. Expression.prototype.__SkipBlank = function()
  456. {
  457. let i = 0;
  458. for(; (this.pos + i) < this.str.length; i++)
  459. {
  460. if(!/\s/.test(this.str.charAt(this.pos + i)))
  461. break;
  462. }
  463. this.pos += i;
  464. return i;
  465. };
  466. // 对象转数字
  467. Expression.prototype.__ParseNumber = function(val)
  468. {
  469. if(null === val)
  470. return this.__NormalError(Expression.INVALID_DIGIT);
  471. if(val instanceof Number)
  472. return val;
  473. else
  474. {
  475. try
  476. {
  477. return this.__StrTo(val.toString());
  478. }
  479. catch(e)
  480. {
  481. console.error(e);
  482. return this.__NormalError(Expression.INVALID_DIGIT);
  483. }
  484. }
  485. };