Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

752 righe
20 KiB

  1. const fs = require('fs-extra');
  2. const path = require('path');
  3. const { randomUUID } = require('crypto');
  4. const { v4: uuidv4 } = require('uuid');
  5. const NOOP = function() {};
  6. const OPERATORS = {
  7. '{' : 'L_CURLY',
  8. '}' : 'R_CURLY',
  9. '(' : 'L_PAREN',
  10. ')' : 'R_PAREN',
  11. '[' : 'L_BRACKET',
  12. ']' : 'R_BRACKET',
  13. '.' : 'PERIOD',
  14. ',' : 'COMMA',
  15. ':' : 'COLON',
  16. '?' : 'QUESTION',
  17. // Arithmetic operators
  18. '+' : 'ADDICTIVE',
  19. '-' : 'ADDICTIVE',
  20. '*' : 'MULTIPLICATIVE',
  21. '/' : 'MULTIPLICATIVE',
  22. '%' : 'MULTIPLICATIVE',
  23. // Comparison operators
  24. '===': 'EQUALITY',
  25. '!==': 'EQUALITY',
  26. '==' : 'EQUALITY',
  27. '!=' : 'EQUALITY',
  28. '<' : 'RELATIONAL',
  29. '>' : 'RELATIONAL',
  30. '<=' : 'RELATIONAL',
  31. '>=' : 'RELATIONAL',
  32. 'in' : 'RELATIONAL',
  33. // Logical operators
  34. '&&' : 'LOGICAL_AND',
  35. '||' : 'LOGICAL_OR',
  36. '!' : 'LOGICAL_NOT',
  37. // Bitwise operators
  38. '&' : 'BITWISE_AND',
  39. '|' : 'BITWISE_OR',
  40. '^' : 'BITWISE_XOR',
  41. '~' : 'BITWISE_NOT',
  42. '<<' : 'BITWISE_SHIFT',
  43. '>>' : 'BITWISE_SHIFT',
  44. '>>>': 'BITWISE_SHIFT'
  45. };
  46. const EXPRESSIONS = {
  47. 'in' : function(a, b) { return a() in b(); },
  48. '?' : function(a, b, c) { return a() ? b() : c(); },
  49. '+' : function(a, b) { a = a(); b = b(); return a == null ? b : b == null ? a : a + b; },
  50. '-' : function(a, b) { return a() - b(); },
  51. '*' : function(a, b) { return a() * b(); },
  52. '/' : function(a, b) { return a() / b(); },
  53. '%' : function(a, b) { return a() % b(); },
  54. '===': function(a, b) { return a() === b(); },
  55. '!==': function(a, b) { return a() !== b(); },
  56. '==' : function(a, b) { return a() == b(); },
  57. '!=' : function(a, b) { return a() != b(); },
  58. '<' : function(a, b) { return a() < b(); },
  59. '>' : function(a, b) { return a() > b(); },
  60. '<=' : function(a, b) { return a() <= b(); },
  61. '>=' : function(a, b) { return a() >= b(); },
  62. '&&' : function(a, b) { return a() && b(); },
  63. '||' : function(a, b) { return a() || b(); },
  64. '&' : function(a, b) { return a() & b(); },
  65. '|' : function(a, b) { return a() | b(); },
  66. '^' : function(a, b) { return a() ^ b(); },
  67. '<<' : function(a, b) { return a() << b(); },
  68. '>>' : function(a, b) { return a() >> b(); },
  69. '>>>': function(a, b) { return a() >>> b(); },
  70. '~' : function(a) { return ~a(); },
  71. '!' : function(a) { return !a(); }
  72. };
  73. const ESCAPE = {
  74. 'n': '\n',
  75. 'f': '\f',
  76. 'r': '\r',
  77. 't': '\t',
  78. 'v': '\v',
  79. "'": "'",
  80. '"': '"',
  81. '`': '`'
  82. };
  83. const formatters = require('../formatters');
  84. // User formatters
  85. if (fs.existsSync('extensions/server_connect/formatters')) {
  86. const files = fs.readdirSync('extensions/server_connect/formatters');
  87. for (let file of files) {
  88. if (path.extname(file) == '.js') {
  89. Object.assign(formatters, require(`../../extensions/server_connect/formatters/${file}`));
  90. }
  91. }
  92. }
  93. function lexer(expr) {
  94. let tokens = [],
  95. token,
  96. name,
  97. start,
  98. index = 0,
  99. op = true,
  100. ch,
  101. ch2,
  102. ch3;
  103. while (index < expr.length) {
  104. start = index;
  105. ch = read();
  106. if (isQuote(ch) && op) {
  107. name = 'STRING';
  108. token = readString(ch);
  109. op = false;
  110. } else if ((isDigid(ch) || (is('.') && peek() && isDigid(peek()))) && op) {
  111. name = 'NUMBER';
  112. token = readNumber();
  113. op = false;
  114. } else if (isAlpha(ch) && op) {
  115. name = 'IDENT';
  116. token = readIdent();
  117. if (is('(')) {
  118. name = 'METHOD';
  119. }
  120. op = false;
  121. } else if (is('/') && op && (token == '(' || token == ',' || token == '?' || token == ':') && testRegexp()) {
  122. name = 'REGEXP';
  123. token = readRegexp();
  124. op = false;
  125. } else if (isWhitespace(ch)) {
  126. index++;
  127. continue;
  128. } else if ((ch3 = read(3)) && OPERATORS[ch3]) {
  129. name = OPERATORS[ch3];
  130. token = ch3;
  131. op = true;
  132. index += 3;
  133. } else if ((ch2 = read(2)) && OPERATORS[ch2]) {
  134. name = OPERATORS[ch2];
  135. token = ch2;
  136. op = true;
  137. index += 2;
  138. } else if (OPERATORS[ch]) {
  139. name = OPERATORS[ch];
  140. token = ch;
  141. op = true;
  142. index++;
  143. } else {
  144. throw new Error('Lexer Error: Unexpected token "' + ch + '" at column ' + index + ' in expression {{' + expr + '}}');
  145. }
  146. tokens.push({
  147. name: name,
  148. index: start,
  149. value: token
  150. });
  151. }
  152. return tokens;
  153. function read(n) {
  154. if (!n) n = 1;
  155. return expr.substr(index, n);
  156. }
  157. function peek(n) {
  158. n = n || 1;
  159. return index + n < expr.length ? expr[index + n] : false;
  160. }
  161. function is(chars) {
  162. return chars.indexOf(ch) != -1;
  163. }
  164. function isQuote(ch) {
  165. return ch == '"' || ch == "'";
  166. }
  167. function isDigid(ch) {
  168. return ch >= '0' && ch <= '9';
  169. }
  170. function isAlpha(ch) {
  171. return (ch >= 'a' && ch <= 'z') ||
  172. (ch >= 'A' && ch <= 'Z') ||
  173. ch == '_' || ch == '$';
  174. }
  175. function isAlphaNum(ch) {
  176. return isAlpha(ch) || isDigid(ch);
  177. }
  178. function isWhitespace(ch) {
  179. return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\u00A0';
  180. }
  181. function isExpOperator(ch) {
  182. return ch == '-' || ch == '+' || isDigid(ch);
  183. }
  184. function readString(quote) {
  185. let str = '', esc = false;
  186. index++;
  187. while (index < expr.length) {
  188. ch = read();
  189. if (esc) {
  190. if (ch == 'u') {
  191. // unicode escape
  192. index++;
  193. let hex = read(4);
  194. if (!hex.match(/[\da-f]{4}/i)) {
  195. throw new Error('Lexer Error: Invalid unicode escape at column ' + index + ' in expression {{' + expr + '}}');
  196. }
  197. str += String.fromCharCode(parseInt(hex, 16));
  198. index += 3;
  199. } else {
  200. str += ESCAPE[ch] ? ESCAPE[ch] : ch;
  201. }
  202. esc = false;
  203. } else if (ch == '\\') {
  204. // escape character
  205. esc = true;
  206. } else if (ch == quote) {
  207. // end of string
  208. index ++;
  209. return str;
  210. } else {
  211. str += ch;
  212. }
  213. index++;
  214. }
  215. throw new Error('Lexer Error: Unterminated string at column ' + index + ' in expression {{' + expr + '}}');
  216. }
  217. function readNumber() {
  218. let num = '', exp = false;
  219. while (index < expr.length) {
  220. ch = read();
  221. if (isDigid(ch) || (is('.') && peek() && isDigid(peek()))) {
  222. num += ch;
  223. } else {
  224. let next = peek();
  225. if (is('eE') && isExpOperator(next)) {
  226. num += 'e';
  227. exp = true;
  228. } else if (isExpOperator(ch) && next && isDigid(next) && exp) {
  229. num += ch;
  230. exp = false;
  231. } else if (isExpOperator(ch) && (!next || !isDigid(next)) && exp) {
  232. throw new Error('Lexer Error: Invalid exponent at column ' + index + ' in expression {{' + expr + '}}');
  233. } else {
  234. break;
  235. }
  236. }
  237. index++;
  238. }
  239. return +num;
  240. }
  241. function readIdent() {
  242. let ident = '';
  243. while (index < expr.length) {
  244. ch = read();
  245. if (isAlphaNum(ch)) {
  246. ident += ch;
  247. } else {
  248. break;
  249. }
  250. index++;
  251. }
  252. return ident;
  253. }
  254. function readRegexp() {
  255. let re = '', mod = '', esc = false;
  256. index ++;
  257. while (index < expr.length) {
  258. ch = read();
  259. if (esc) {
  260. esc = false;
  261. } else if (ch == '\\') {
  262. esc = true;
  263. } else if (ch == '/') {
  264. index++;
  265. while ('ign'.indexOf(ch = read()) != -1) {
  266. mod += ch;
  267. index++;
  268. }
  269. return re + '%%%' + mod;
  270. }
  271. re += ch;
  272. index++;
  273. }
  274. throw new Error('Lexer Error: Unterminated regexp at column ' + index + ' in expression {{' + expr + '}}');
  275. }
  276. function testRegexp() {
  277. var idx = index, ok = true;
  278. try {
  279. readRegexp();
  280. } catch (e) {
  281. ok = false;
  282. }
  283. // reset our index and ch
  284. index = idx;
  285. ch = '/';
  286. return ok;
  287. }
  288. }
  289. function parser(expr, scope) {
  290. let tokens = lexer(expr),
  291. context = undefined,
  292. RESERVED = {
  293. 'PI' : function() { return Math.PI; },
  294. 'UUID' : function() { return randomUUID ? randomUUID() : uuidv4(); },
  295. 'NOW' : function() { return date(); },
  296. 'NOW_UTC' : function() { return utc_date(); },
  297. 'TIMESTAMP': function() { return timestamp(); },
  298. '$this' : function() { return scope.data; },
  299. '$global' : function() { return globalScope.data; },
  300. '$parent' : function() { return scope.parent && scope.parent.data; },
  301. 'null' : function() { return null; },
  302. 'true' : function() { return true; },
  303. 'false' : function() { return false; },
  304. '_' : function() { return { __dmxScope__: true } }
  305. };
  306. return start()();
  307. function pad(s, n) {
  308. return ('000' + s).substr(-n);
  309. }
  310. function date(dt) {
  311. dt = dt || new Date();
  312. return pad(dt.getFullYear(), 4) + '-' + pad(dt.getMonth() + 1, 2) + '-' + pad(dt.getDate(), 2) + ' ' +
  313. pad(dt.getHours(), 2) + ':' + pad(dt.getMinutes(), 2) + ':' + pad(dt.getSeconds(), 2);
  314. }
  315. function utc_date(dt) {
  316. dt = dt || new Date();
  317. return pad(dt.getUTCFullYear(), 4) + '-' + pad(dt.getUTCMonth() + 1, 2) + '-' + pad(dt.getUTCDate(), 2) + 'T' +
  318. pad(dt.getUTCHours(), 2) + ':' + pad(dt.getUTCMinutes(), 2) + ':' + pad(dt.getUTCSeconds(), 2) + 'Z';
  319. }
  320. function timestamp(dt) {
  321. dt = dt || new Date();
  322. return ~~(dt / 1000);
  323. }
  324. function read() {
  325. if (tokens.length === 0) {
  326. throw new Error('Parser Error: Unexpected end of expression {{' + expr + '}}');
  327. }
  328. return tokens[0];
  329. }
  330. function peek(e) {
  331. if (tokens.length > 0) {
  332. let token = tokens[0];
  333. if (!e || token.name == e) {
  334. return token;
  335. }
  336. }
  337. return false;
  338. }
  339. function expect(e) {
  340. let token = peek(e);
  341. if (token) {
  342. tokens.shift();
  343. return token;
  344. }
  345. return false;
  346. }
  347. function consume(e) {
  348. if (!expect(e)) {
  349. throw new Error('Parser Error: Unexpected token, expecting ' + e + ' in expression {{' + expr + '}}');
  350. }
  351. }
  352. function fn(expr) {
  353. let args = [].slice.call(arguments, 1);
  354. return function() {
  355. if (EXPRESSIONS[expr]) {
  356. return EXPRESSIONS[expr].apply(context, args);
  357. } else {
  358. return expr;
  359. }
  360. }
  361. }
  362. function start() {
  363. return conditional();
  364. }
  365. function conditional() {
  366. let left = logicalOr(), middle, token;
  367. if ((token = expect('QUESTION'))) {
  368. middle = conditional();
  369. if ((token = expect('COLON'))) {
  370. return fn('?', left, middle, conditional());
  371. } else {
  372. throw new Error('Parse Error: Expecting : in expression {{' + expr + '}}');
  373. }
  374. } else {
  375. return left;
  376. }
  377. }
  378. function logicalOr() {
  379. let left = logicalAnd(), token;
  380. while (true) {
  381. if ((token = expect('LOGICAL_OR'))) {
  382. left = fn(token.value, left, logicalAnd());
  383. } else {
  384. return left;
  385. }
  386. }
  387. }
  388. function logicalAnd() {
  389. let left = bitwiseOr(), token;
  390. if ((token = expect('LOGICAL_AND'))) {
  391. left = fn(token.value, left, logicalAnd());
  392. }
  393. return left;
  394. }
  395. function bitwiseOr() {
  396. let left = bitwiseXor(), token;
  397. if ((token = expect('BITWISE_OR'))) {
  398. left = fn(token.value, left, bitwiseXor());
  399. }
  400. return left;
  401. }
  402. function bitwiseXor() {
  403. let left = bitwiseAnd(), token;
  404. if ((token = expect('BITWISE_XOR'))) {
  405. left = fn(token.value, left, bitwiseAnd());
  406. }
  407. return left;
  408. }
  409. function bitwiseAnd() {
  410. let left = equality(), token;
  411. if ((token = expect('BITWISE_AND'))) {
  412. left = fn(token.value, left, bitwiseAnd());
  413. }
  414. return left;
  415. }
  416. function equality() {
  417. let left = relational(), token;
  418. if ((token = expect('EQUALITY'))) {
  419. left = fn(token.value, left, equality());
  420. }
  421. return left;
  422. }
  423. function relational() {
  424. let left = bitwiseShift(), token;
  425. if ((token = expect('RELATIONAL'))) {
  426. left = fn(token.value, left, relational());
  427. }
  428. return left;
  429. }
  430. function bitwiseShift() {
  431. let left = addictive(), token;
  432. if ((token = expect('BITWISE_SHIFT'))) {
  433. left = fn(token.value, left, addictive());
  434. }
  435. return left;
  436. }
  437. function addictive() {
  438. let left = multiplicative(), token;
  439. while ((token = expect('ADDICTIVE'))) {
  440. left = fn(token.value, left, multiplicative());
  441. }
  442. return left;
  443. }
  444. function multiplicative() {
  445. let left = unary(), token;
  446. while ((token = expect('MULTIPLICATIVE'))) {
  447. left = fn(token.value, left, unary());
  448. }
  449. return left;
  450. }
  451. function unary() {
  452. let token;
  453. if ((token = expect('ADDICTIVE'))) {
  454. if (token.value == '+') {
  455. return primary();
  456. } else {
  457. return fn(token.value, function() { return 0; }, unary());
  458. }
  459. } else if ((token = expect('LOGICAL_NOT'))) {
  460. return fn(token.value, unary());
  461. }
  462. return primary();
  463. }
  464. function primary() {
  465. let value, next;
  466. if (expect('L_PAREN')) {
  467. value = start();
  468. consume('R_PAREN');
  469. } else if (expect('L_CURLY')) {
  470. let obj = {};
  471. if (read().name != 'R_CURLY') {
  472. do {
  473. let key = expect().value;
  474. consume('COLON');
  475. obj[key] = start()();
  476. } while (expect('COMMA'));
  477. }
  478. value = fn(obj);
  479. consume('R_CURLY');
  480. } else if (expect('L_BRACKET')) {
  481. let arr = [];
  482. if (read().name != 'R_BRACKET') {
  483. do {
  484. arr.push(start()());
  485. } while (expect('COMMA'));
  486. }
  487. value = fn(arr);
  488. consume('R_BRACKET');
  489. } else if (expect('PERIOD')) {
  490. value = peek() ? objectMember(fn(scope.data)) : fn(scope.data);
  491. } else {
  492. let token = expect();
  493. if (token === false) {
  494. throw new Error('Parser Error: Not a primary expression {{' + expr + '}}');
  495. }
  496. if (token.name == 'IDENT') {
  497. value = RESERVED.hasOwnProperty(token.value)
  498. ? RESERVED[token.value]
  499. : function() { return scope.get(token.value) };
  500. } else if (token.name == 'METHOD') {
  501. if (!formatters[token.value]) {
  502. throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expression + '}}');
  503. }
  504. value = fn(formatters[token.value]);
  505. } else if (token.name == 'REGEXP') {
  506. value = function() {
  507. let re = token.value.split('%%%');
  508. return new RegExp(re[0], re[1]);
  509. };
  510. } else {
  511. value = function() { return token.value };
  512. }
  513. }
  514. while ((next = expect('L_PAREN') || expect('L_BRACKET') || expect('PERIOD'))) {
  515. if (next.value == '(') {
  516. value = functionCall(value, context);
  517. } else if (next.value == '[') {
  518. value = objectIndex(value);
  519. } else if (next.value == '.') {
  520. context = value;
  521. value = objectMember(value);
  522. } else {
  523. throw new Error('Parser Error: Parse error in expression {{' + expr + '}}');
  524. }
  525. }
  526. context = undefined;
  527. return value;
  528. }
  529. function functionCall(func, ctx) {
  530. let argsFn = [];
  531. if (read().name != 'R_PAREN') {
  532. do {
  533. argsFn.push(start());
  534. } while (expect('COMMA'));
  535. }
  536. consume('R_PAREN');
  537. return function() {
  538. let args = [];
  539. if (ctx) args.push(ctx());
  540. for (let argFn of argsFn) {
  541. args.push(argFn());
  542. }
  543. let fnPtr = func() || NOOP;
  544. return fnPtr.apply(null, args);
  545. }
  546. }
  547. function objectIndex(obj) {
  548. let indexFn = start();
  549. consume('R_BRACKET');
  550. return function() {
  551. let o = obj(),
  552. i = indexFn();
  553. if (typeof o != 'object') return undefined;
  554. if (o.__dmxScope__) {
  555. return scope.get(i);
  556. }
  557. return o[i];
  558. }
  559. }
  560. function objectMember(obj) {
  561. let token = expect();
  562. return function() {
  563. let o = obj();
  564. if (token.name == 'METHOD') {
  565. if (!formatters[token.value]) {
  566. throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expr + '}}');
  567. }
  568. return formatters[token.value];
  569. }
  570. if (o && o.__dmxScope) {
  571. return scope.get(token.value);
  572. }
  573. return o && o[token.value];
  574. }
  575. }
  576. }
  577. function parseValue(value, scope) {
  578. if (value == null) return value;
  579. value = value.valueOf();
  580. if (typeof value == 'object') {
  581. for (let key in value) {
  582. value[key] = parseValue(value[key], scope);
  583. }
  584. }
  585. if (typeof value == 'string') {
  586. if (value.substr(0, 2) == '{{' && value.substr(-2) == '}}') {
  587. let expr = value.replace(/^\{\{|\}\}$/g, '');
  588. if (expr.indexOf('{{') == -1) {
  589. return parser(expr, scope);
  590. }
  591. }
  592. return parseTemplate(value, scope);
  593. }
  594. return value;
  595. }
  596. function parseTemplate(template, scope) {
  597. return template.replace(/\{\{(.*?)\}\}/g, function(a, m) {
  598. var value = parser(m, scope);
  599. return value != null ? String(value) : '';
  600. });
  601. }
  602. exports.lexer = lexer;
  603. exports.parse = parser;
  604. exports.parseValue = parseValue;
  605. exports.parseTemplate = parseTemplate;