@qrvey/formula-lang 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/__tests__/integration/__mocks__/elasticsearchScripts.ts +21 -0
  3. package/__tests__/integration/__mocks__/sqlScripts.ts +20 -0
  4. package/__tests__/integration/elasticsearch.test.ts +158 -0
  5. package/__tests__/integration/expression.test.ts +22 -0
  6. package/__tests__/integration/redshift.test.ts +127 -0
  7. package/__tests__/integration/sonwflake.test.ts +123 -0
  8. package/__tests__/unit/parser.test.ts +106 -0
  9. package/__tests__/{src → unit}/transpiler.test.ts +0 -17
  10. package/dist/constants/index.d.ts +12 -2
  11. package/dist/constants/index.js +21 -1
  12. package/dist/constants/index.js.map +1 -1
  13. package/dist/constants/interfaces.d.ts +8 -2
  14. package/dist/errors/definitions.d.ts +20 -5
  15. package/dist/errors/definitions.js +28 -5
  16. package/dist/errors/definitions.js.map +1 -1
  17. package/dist/errors/engineErrors.d.ts +2 -0
  18. package/dist/errors/engineErrors.js +21 -0
  19. package/dist/errors/engineErrors.js.map +1 -0
  20. package/dist/errors/index.d.ts +2 -5
  21. package/dist/errors/index.js +4 -38
  22. package/dist/errors/index.js.map +1 -1
  23. package/dist/functions/abs.d.ts +5 -0
  24. package/dist/functions/abs.js +35 -0
  25. package/dist/functions/abs.js.map +1 -0
  26. package/dist/functions/datedif.d.ts +5 -0
  27. package/dist/functions/datedif.js +61 -0
  28. package/dist/functions/datedif.js.map +1 -0
  29. package/dist/functions/day.d.ts +5 -0
  30. package/dist/functions/day.js +36 -0
  31. package/dist/functions/day.js.map +1 -0
  32. package/dist/functions/hour.d.ts +5 -0
  33. package/dist/functions/hour.js +36 -0
  34. package/dist/functions/hour.js.map +1 -0
  35. package/dist/functions/if.d.ts +5 -0
  36. package/dist/functions/if.js +49 -0
  37. package/dist/functions/if.js.map +1 -0
  38. package/dist/functions/ifs.d.ts +5 -0
  39. package/dist/functions/ifs.js +71 -0
  40. package/dist/functions/ifs.js.map +1 -0
  41. package/dist/functions/index.js +20 -0
  42. package/dist/functions/index.js.map +1 -1
  43. package/dist/functions/mid.d.ts +3 -0
  44. package/dist/functions/mid.js +15 -12
  45. package/dist/functions/mid.js.map +1 -1
  46. package/dist/functions/minute.d.ts +5 -0
  47. package/dist/functions/minute.js +36 -0
  48. package/dist/functions/minute.js.map +1 -0
  49. package/dist/functions/month.d.ts +5 -0
  50. package/dist/functions/month.js +36 -0
  51. package/dist/functions/month.js.map +1 -0
  52. package/dist/functions/second.d.ts +5 -0
  53. package/dist/functions/second.js +36 -0
  54. package/dist/functions/second.js.map +1 -0
  55. package/dist/functions/year.d.ts +5 -0
  56. package/dist/functions/year.js +36 -0
  57. package/dist/functions/year.js.map +1 -0
  58. package/dist/grammar/generated/qformula.lang.js +10 -10
  59. package/dist/grammar/generated/qformula.lang.js.map +1 -1
  60. package/dist/parser/json-parser.js +42 -6
  61. package/dist/parser/json-parser.js.map +1 -1
  62. package/dist/transpiler/index.d.ts +9 -7
  63. package/dist/transpiler/index.js +38 -27
  64. package/dist/transpiler/index.js.map +1 -1
  65. package/dist/transpiler/validateFuncStructure.d.ts +2 -0
  66. package/dist/transpiler/validateFuncStructure.js +37 -0
  67. package/dist/transpiler/validateFuncStructure.js.map +1 -0
  68. package/dist/utils/customFunctions.d.ts +3 -0
  69. package/dist/utils/customFunctions.js +21 -0
  70. package/dist/utils/customFunctions.js.map +1 -0
  71. package/dist/utils/elasticsearch/index.d.ts +2 -0
  72. package/dist/utils/elasticsearch/index.js +16 -0
  73. package/dist/utils/elasticsearch/index.js.map +1 -0
  74. package/dist/utils/elasticsearch/scripts.d.ts +10 -0
  75. package/dist/utils/elasticsearch/scripts.js +36 -0
  76. package/dist/utils/elasticsearch/scripts.js.map +1 -0
  77. package/dist/utils/getMonthMaxDayAllowed.d.ts +7 -0
  78. package/dist/utils/getMonthMaxDayAllowed.js +22 -0
  79. package/dist/utils/getMonthMaxDayAllowed.js.map +1 -0
  80. package/dist/utils/getNodeValue.d.ts +2 -0
  81. package/dist/utils/getNodeValue.js +9 -0
  82. package/dist/utils/getNodeValue.js.map +1 -0
  83. package/dist/utils/index.d.ts +9 -2
  84. package/dist/utils/index.js +9 -8
  85. package/dist/utils/index.js.map +1 -1
  86. package/dist/utils/isDateParam.d.ts +2 -0
  87. package/dist/utils/isDateParam.js +8 -0
  88. package/dist/utils/isDateParam.js.map +1 -0
  89. package/dist/utils/isNumberParam.d.ts +2 -0
  90. package/dist/utils/isNumberParam.js +8 -0
  91. package/dist/utils/isNumberParam.js.map +1 -0
  92. package/dist/utils/isPositiveNumberParam.d.ts +2 -0
  93. package/dist/utils/isPositiveNumberParam.js +8 -0
  94. package/dist/utils/isPositiveNumberParam.js.map +1 -0
  95. package/dist/utils/isStringParam.d.ts +2 -0
  96. package/dist/utils/isStringParam.js +8 -0
  97. package/dist/utils/isStringParam.js.map +1 -0
  98. package/dist/utils/isValidDate.d.ts +1 -0
  99. package/dist/utils/isValidDate.js +4 -0
  100. package/dist/utils/isValidDate.js.map +1 -0
  101. package/dist/utils/sql/index.d.ts +1 -0
  102. package/dist/utils/sql/index.js +2 -0
  103. package/dist/utils/sql/index.js.map +1 -0
  104. package/dist/utils/sql/scripts.d.ts +3 -0
  105. package/dist/utils/sql/scripts.js +39 -0
  106. package/dist/utils/sql/scripts.js.map +1 -0
  107. package/package.json +1 -1
  108. package/pkg/dist/constants/index.d.ts +42 -0
  109. package/pkg/dist/constants/index.js +86 -0
  110. package/pkg/dist/constants/index.js.map +1 -0
  111. package/pkg/dist/constants/interfaces.d.ts +62 -0
  112. package/pkg/dist/constants/interfaces.js +2 -0
  113. package/pkg/dist/constants/interfaces.js.map +1 -0
  114. package/pkg/dist/errors/definitions.d.ts +26 -0
  115. package/pkg/dist/errors/definitions.js +42 -0
  116. package/pkg/dist/errors/definitions.js.map +1 -0
  117. package/pkg/dist/errors/elasticsearch.d.ts +24 -0
  118. package/pkg/dist/errors/elasticsearch.js +27 -0
  119. package/pkg/dist/errors/elasticsearch.js.map +1 -0
  120. package/pkg/dist/errors/engineErrors.d.ts +2 -0
  121. package/pkg/dist/errors/engineErrors.js +21 -0
  122. package/pkg/dist/errors/engineErrors.js.map +1 -0
  123. package/pkg/dist/errors/index.d.ts +2 -0
  124. package/pkg/dist/errors/index.js +5 -0
  125. package/pkg/dist/errors/index.js.map +1 -0
  126. package/pkg/dist/errors/snowflake.d.ts +15 -0
  127. package/pkg/dist/errors/snowflake.js +17 -0
  128. package/pkg/dist/errors/snowflake.js.map +1 -0
  129. package/pkg/dist/functions/abs.d.ts +5 -0
  130. package/pkg/dist/functions/abs.js +35 -0
  131. package/pkg/dist/functions/abs.js.map +1 -0
  132. package/pkg/dist/functions/datedif.d.ts +5 -0
  133. package/pkg/dist/functions/datedif.js +58 -0
  134. package/pkg/dist/functions/datedif.js.map +1 -0
  135. package/pkg/dist/functions/day.d.ts +5 -0
  136. package/pkg/dist/functions/day.js +36 -0
  137. package/pkg/dist/functions/day.js.map +1 -0
  138. package/pkg/dist/functions/hour.d.ts +5 -0
  139. package/pkg/dist/functions/hour.js +36 -0
  140. package/pkg/dist/functions/hour.js.map +1 -0
  141. package/pkg/dist/functions/if.d.ts +5 -0
  142. package/pkg/dist/functions/if.js +49 -0
  143. package/pkg/dist/functions/if.js.map +1 -0
  144. package/pkg/dist/functions/ifs.d.ts +5 -0
  145. package/pkg/dist/functions/ifs.js +71 -0
  146. package/pkg/dist/functions/ifs.js.map +1 -0
  147. package/pkg/dist/functions/index.d.ts +4 -0
  148. package/pkg/dist/functions/index.js +29 -0
  149. package/pkg/dist/functions/index.js.map +1 -0
  150. package/pkg/dist/functions/mid.d.ts +5 -0
  151. package/pkg/dist/functions/mid.js +51 -0
  152. package/pkg/dist/functions/mid.js.map +1 -0
  153. package/pkg/dist/functions/minute.d.ts +5 -0
  154. package/pkg/dist/functions/minute.js +36 -0
  155. package/pkg/dist/functions/minute.js.map +1 -0
  156. package/pkg/dist/functions/month.d.ts +5 -0
  157. package/pkg/dist/functions/month.js +36 -0
  158. package/pkg/dist/functions/month.js.map +1 -0
  159. package/pkg/dist/functions/second.d.ts +5 -0
  160. package/pkg/dist/functions/second.js +36 -0
  161. package/pkg/dist/functions/second.js.map +1 -0
  162. package/pkg/dist/functions/year.d.ts +5 -0
  163. package/pkg/dist/functions/year.js +36 -0
  164. package/pkg/dist/functions/year.js.map +1 -0
  165. package/pkg/dist/grammar/generated/qformula.lang.js +43 -0
  166. package/pkg/dist/grammar/generated/qformula.lang.js.map +1 -0
  167. package/pkg/dist/grammar/qformula.grammar.d.ts +1 -0
  168. package/pkg/dist/grammar/qformula.grammar.js +2 -0
  169. package/pkg/dist/grammar/qformula.grammar.js.map +1 -0
  170. package/pkg/dist/index.js +29 -0
  171. package/pkg/dist/index.js.map +1 -0
  172. package/pkg/dist/parser/json-parser.js +139 -0
  173. package/pkg/dist/parser/json-parser.js.map +1 -0
  174. package/pkg/dist/transpiler/index.d.ts +17 -0
  175. package/pkg/dist/transpiler/index.js +82 -0
  176. package/pkg/dist/transpiler/index.js.map +1 -0
  177. package/pkg/dist/transpiler/validateFuncStructure.d.ts +2 -0
  178. package/pkg/dist/transpiler/validateFuncStructure.js +35 -0
  179. package/pkg/dist/transpiler/validateFuncStructure.js.map +1 -0
  180. package/pkg/dist/utils/customFunctions.d.ts +2 -0
  181. package/pkg/dist/utils/customFunctions.js +11 -0
  182. package/pkg/dist/utils/customFunctions.js.map +1 -0
  183. package/pkg/dist/utils/elasticsearch/index.d.ts +1 -0
  184. package/pkg/dist/utils/elasticsearch/index.js +15 -0
  185. package/pkg/dist/utils/elasticsearch/index.js.map +1 -0
  186. package/pkg/dist/utils/elasticsearch/scripts.d.ts +9 -0
  187. package/pkg/dist/utils/elasticsearch/scripts.js +33 -0
  188. package/pkg/dist/utils/elasticsearch/scripts.js.map +1 -0
  189. package/pkg/dist/utils/getMonthMaxDayAllowed.d.ts +7 -0
  190. package/pkg/dist/utils/getMonthMaxDayAllowed.js +22 -0
  191. package/pkg/dist/utils/getMonthMaxDayAllowed.js.map +1 -0
  192. package/pkg/dist/utils/getNodeValue.d.ts +2 -0
  193. package/pkg/dist/utils/getNodeValue.js +9 -0
  194. package/pkg/dist/utils/getNodeValue.js.map +1 -0
  195. package/pkg/dist/utils/index.d.ts +8 -0
  196. package/pkg/dist/utils/index.js +9 -0
  197. package/pkg/dist/utils/index.js.map +1 -0
  198. package/pkg/dist/utils/isDateParam.d.ts +2 -0
  199. package/pkg/dist/utils/isDateParam.js +10 -0
  200. package/pkg/dist/utils/isDateParam.js.map +1 -0
  201. package/pkg/dist/utils/isNumberParam.d.ts +2 -0
  202. package/pkg/dist/utils/isNumberParam.js +8 -0
  203. package/pkg/dist/utils/isNumberParam.js.map +1 -0
  204. package/pkg/dist/utils/isPositiveNumberParam.d.ts +2 -0
  205. package/pkg/dist/utils/isPositiveNumberParam.js +8 -0
  206. package/pkg/dist/utils/isPositiveNumberParam.js.map +1 -0
  207. package/pkg/dist/utils/isStringParam.d.ts +2 -0
  208. package/pkg/dist/utils/isStringParam.js +8 -0
  209. package/pkg/dist/utils/isStringParam.js.map +1 -0
  210. package/pkg/dist/utils/isValidDate.d.ts +1 -0
  211. package/pkg/dist/utils/isValidDate.js +4 -0
  212. package/pkg/dist/utils/isValidDate.js.map +1 -0
  213. package/pkg/dist/utils/sql/scripts.d.ts +2 -0
  214. package/pkg/dist/utils/sql/scripts.js +39 -0
  215. package/pkg/dist/utils/sql/scripts.js.map +1 -0
  216. package/__tests__/src/parser.test.ts +0 -29
  217. package/dist/models/ast.models.d.ts +0 -29
  218. package/dist/models/ast.models.js +0 -1
  219. package/dist/utils/utils.d.ts +0 -4
  220. package/dist/utils/utils.js +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ <a name="0.2.0"></a>
2
+ # [0.2.0](https://bitbucket.org/qrvey/qrvey_formula_lang/compare/v0.1.3...v0.2.0) (2023-04-21)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * **iffunction:** return False keyword for each lenguage in value if false case ([34a0160](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/34a0160))
8
+
9
+
10
+ ### Features
11
+
12
+ * **argumentvalidation:** add arguments validation function RB-1072 ([4e5e3fc](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/4e5e3fc))
13
+ * **datedif:** add DATEDIF function ([1354f1d](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/1354f1d))
14
+ * **datedif:** dateDif function finish ([5089728](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/5089728))
15
+ * **day:** add DAY function ([6553dae](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/6553dae))
16
+ * **errordefinition:** add and improve error defintions (TooManyArguments and InvalidArgument) ([22c9b8b](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/22c9b8b))
17
+ * **errordefinition:** add error definition for Unknow and missing arguments errors ([7054f55](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/7054f55))
18
+ * **function:** add Month, Year, Hour, Minute, Second ([e14ff0a](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/e14ff0a))
19
+ * **functions:** add ABS function and refactor folder structure of tests ([5f82ce2](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/5f82ce2))
20
+ * **functions:** add IF funciton along intgration test in elasticsearch ([c18af49](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/c18af49))
21
+ * **ifsfunction:** add IFS support for elasticsearch ([ddc9da9](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/ddc9da9))
22
+ * **ifsfunction:** add redshift and snowflake transpilation ([731cf94](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/731cf94))
23
+ * **json-parser.ts:** date parser with tests ([2e20873](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/2e20873))
24
+ * **syntax.grammar:** date grammar definition ([ccea58f](https://bitbucket.org/qrvey/qrvey_formula_lang/commits/ccea58f))
25
+
26
+
27
+
1
28
  <a name="0.1.3"></a>
2
29
  ## [0.1.3](https://bitbucket.org/qrvey/qrvey_formula_lang/compare/v0.1.2...v0.1.3) (2023-04-16)
3
30
 
@@ -0,0 +1,21 @@
1
+ export const dateFormatScript = `String DATE_FORMAT_SCRIPT(def date_value, String format) {
2
+ def isCustomDate = (date_value instanceof JodaCompatibleZonedDateTime || date_value instanceof ZonedDateTime);
3
+ if (date_value instanceof List && !isCustomDate) {
4
+ if (date_value.empty) return null;
5
+ date_value = date_value.value;
6
+ }
7
+ return DateTimeFormatter.ofPattern(format).format(date_value)
8
+ }`;
9
+
10
+ export function dateFormatToInt(format: string): string {
11
+ return `Integer.parseInt(DATE_FORMAT_SCRIPT(ZonedDateTime.parse("2023-12-31T00:00:00.000Z"), '${format}'))`;
12
+ }
13
+
14
+ export function dateParser(format: string): string {
15
+ const dateToInt = dateFormatToInt(format);
16
+ return `${dateFormatScript}\n${dateToInt}`;
17
+ }
18
+
19
+ export function dateDif(format: string) {
20
+ return `ChronoUnit.${format}.between(ZonedDateTime.parse("2023-12-31T00:00:00.000Z"), ZonedDateTime.parse("2022-12-31T00:00:00.000Z"))`;
21
+ }
@@ -0,0 +1,20 @@
1
+ export function datePart(part: string) {
2
+ return `DATE_PART('${part}', CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ))`;
3
+ }
4
+
5
+ export function dateDif(unit: string): string {
6
+ switch (unit) {
7
+ case 'day':
8
+ case 'month':
9
+ return `DATEDIFF('${unit}', CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ), CAST('2022-12-31T00:00:00.000Z' AS TIMESTAMPTZ))`;
10
+ case 'year':
11
+ return `CASE
12
+ WHEN DATEDIFF('day', CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ), CAST('DATE_PART('CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ)', year)|| '-' ||DATE_PART('CAST('2022-12-31T00:00:00.000Z' AS TIMESTAMPTZ)', month)|| '-' ||DATE_PART('CAST('2022-12-31T00:00:00.000Z' AS TIMESTAMPTZ)', day)' AS TIMESTAMPTZ)) >= 0
13
+ THEN DATEDIFF('${unit}', CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ), CAST('2022-12-31T00:00:00.000Z' AS TIMESTAMPTZ))
14
+ ELSE DATEDIFF('${unit}', CAST('2023-12-31T00:00:00.000Z' AS TIMESTAMPTZ), CAST('2022-12-31T00:00:00.000Z' AS TIMESTAMPTZ)) - 1
15
+ END`; //Indentation care, be careful
16
+
17
+ default:
18
+ return '';
19
+ }
20
+ }
@@ -0,0 +1,158 @@
1
+ import { ENGINES, Transpile } from '../../src';
2
+ import {
3
+ dateDif,
4
+ dateParser,
5
+ dateFormatToInt,
6
+ dateFormatScript,
7
+ } from './__mocks__/elasticsearchScripts';
8
+ const currentEngine = ENGINES.ELASTICSEARCH;
9
+
10
+ describe('ELASTICSEARCH Transpile program', () => {
11
+ test('Basic case', () => {
12
+ const result = Transpile('1 + 2', currentEngine);
13
+ expect(result?.expression).toBe('(1 + 2)');
14
+ });
15
+
16
+ test('MID function', () => {
17
+ const result = Transpile('MID("test", 1, 5)', currentEngine);
18
+ expect(result?.expression).toBe(`'test'.substring(1, 5)`);
19
+ });
20
+
21
+ test('Should transpile a function inside another function', () => {
22
+ const program = 'MID(MID("This is a test", 1, 5), 1, 2)';
23
+ const result = Transpile(program, currentEngine);
24
+ expect(result?.valid).toBe(true);
25
+ expect(result?.expression).toBe(
26
+ "'This is a test'.substring(1, 5).substring(1, 2)",
27
+ );
28
+ });
29
+
30
+ test('ABS function', () => {
31
+ const result = Transpile('ABS(5)', currentEngine);
32
+ expect(result?.expression).toBe(`Math.abs(5)`);
33
+ });
34
+
35
+ test('DAY function', () => {
36
+ const result = Transpile('DAY("12/31/2023")', currentEngine);
37
+ const mock = dateParser('dd').replace(/\n/g, '');
38
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
39
+ });
40
+
41
+ test('Two DAY function', () => {
42
+ const result = Transpile(
43
+ 'DAY("12/31/2023") + DAY("12/31/2023")',
44
+ currentEngine,
45
+ );
46
+ const concat = `${dateFormatScript}\n(${dateFormatToInt(
47
+ 'dd',
48
+ )} + ${dateFormatToInt('dd')})`;
49
+ const mock = concat.replace(/\n/g, '');
50
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
51
+ });
52
+
53
+ test('MONTH function', () => {
54
+ const result = Transpile('MONTH("12/31/2023")', currentEngine);
55
+ const mock = dateParser('MM').replace(/\n/g, '');
56
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
57
+ });
58
+
59
+ test('One DAY plus One Month function', () => {
60
+ const result = Transpile(
61
+ 'DAY("12/31/2023") + MONTH("12/31/2023")',
62
+ currentEngine,
63
+ );
64
+ const concat = `${dateFormatScript}\n(${dateFormatToInt(
65
+ 'dd',
66
+ )} + ${dateFormatToInt('MM')})`;
67
+ const mock = concat.replace(/\n/g, '');
68
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
69
+ });
70
+
71
+ test('YEAR function', () => {
72
+ const result = Transpile('YEAR("12/31/2023")', currentEngine);
73
+ const mock = dateParser('yyyy').replace(/\n/g, '');
74
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
75
+ });
76
+
77
+ test('HOUR function', () => {
78
+ const result = Transpile('HOUR("12/31/2023")', currentEngine);
79
+ const mock = dateParser('HH').replace(/\n/g, '');
80
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
81
+ });
82
+
83
+ test('MINUTE function', () => {
84
+ const result = Transpile('MINUTE("12/31/2023")', currentEngine);
85
+ const mock = dateParser('mm').replace(/\n/g, '');
86
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
87
+ });
88
+
89
+ test('SECOND function', () => {
90
+ const result = Transpile('SECOND("12/31/2023")', currentEngine);
91
+ const mock = dateParser('ss').replace(/\n/g, '');
92
+ expect(result?.expression?.replace(/\n/g, '')).toBe(mock);
93
+ });
94
+
95
+ test('DATEDIF function with Y unit', () => {
96
+ const result = Transpile(
97
+ 'DATEDIF("12/31/2023", "12/31/2022", "Y")',
98
+ currentEngine,
99
+ );
100
+ const mock = dateDif('YEARS');
101
+ expect(result?.expression).toBe(mock);
102
+ });
103
+
104
+ test('DATEDIF function with M unit', () => {
105
+ const result = Transpile(
106
+ 'DATEDIF("12/31/2023", "12/31/2022", "M")',
107
+ currentEngine,
108
+ );
109
+ const mock = dateDif('MONTHS');
110
+ expect(result?.expression).toBe(mock);
111
+ });
112
+
113
+ test('DATEDIF function with D unit', () => {
114
+ const result = Transpile(
115
+ 'DATEDIF("12/31/2023", "12/31/2022", "D")',
116
+ currentEngine,
117
+ );
118
+ const mock = dateDif('DAYS');
119
+ expect(result?.expression).toBe(mock);
120
+ });
121
+
122
+ test('DATEDIF function with a not valid Unit', () => {
123
+ const result = Transpile(
124
+ 'DATEDIF("12/31/2023", "12/31/2022", "X")',
125
+ currentEngine,
126
+ );
127
+ expect(result).not.toBeUndefined();
128
+ expect(result?.valid).toBe(false);
129
+ expect(result?.error).not.toBeUndefined();
130
+ expect(result?.error?.code).toBe('DATE_DIF_UNIT_PARAM');
131
+ });
132
+
133
+ test('IF function full parameters', () => {
134
+ const result = Transpile('IF(5 = 1, "True", "False")', currentEngine);
135
+ expect(result).not.toBeUndefined();
136
+ expect(result?.error).toBeUndefined();
137
+ expect(result?.expression).toBe(`((5 == 1) ? 'True' : 'False')`);
138
+ });
139
+
140
+ test('IF function basic parameters', () => {
141
+ const result = Transpile('IF(5 = 1, "True")', currentEngine);
142
+ expect(result).not.toBeUndefined();
143
+ expect(result?.error).toBeUndefined();
144
+ expect(result?.expression).toBe(`((5 == 1) ? 'True' : false)`);
145
+ });
146
+
147
+ test('IFS function basic parameters', () => {
148
+ const result = Transpile(
149
+ 'IFS(5>10, "Greater than 10", 5>5, "Greater than 5",5>0, "Greater than 0")',
150
+ currentEngine,
151
+ );
152
+ expect(result).not.toBeUndefined();
153
+ expect(result?.error).toBeUndefined();
154
+ expect(result?.expression).toBe(
155
+ `((5 > 10) ? 'Greater than 10' : ((5 > 5) ? 'Greater than 5' : ((5 > 0) ? 'Greater than 0' : null)))`,
156
+ );
157
+ });
158
+ });
@@ -0,0 +1,22 @@
1
+ import { ENGINES, Transpile } from '../../src';
2
+ import { MissingArgumentError } from '../../src/errors';
3
+
4
+ describe('Raw Expression test suite', () => {
5
+ test('should transpile a logical operator', () => {
6
+ const program = '1 < 2';
7
+ const result = Transpile(program, ENGINES.ELASTICSEARCH);
8
+ expect(result?.valid).toBe(true);
9
+ expect(result?.expression).toBe('(1 < 2)');
10
+ });
11
+
12
+ test('IFS function should not work (invalid parameters)', () => {
13
+ const result = Transpile(
14
+ 'IFS(5>10, "Greater than 10", 5>5, "Greater than 5",5>0)',
15
+ ENGINES.ELASTICSEARCH,
16
+ );
17
+ expect(result).not.toBeUndefined();
18
+ expect(result?.valid).toBe(false);
19
+ expect(result?.error).not.toBeUndefined();
20
+ expect(result?.error instanceof MissingArgumentError).toBeTruthy();
21
+ });
22
+ });
@@ -0,0 +1,127 @@
1
+ import { ENGINES, Transpile } from '../../src';
2
+ import { datePart, dateDif } from './__mocks__/sqlScripts';
3
+ const currentEngine = ENGINES.REDSHIFT;
4
+
5
+ describe('SNOWFLAKE Transpile program', () => {
6
+ test('Basic case', () => {
7
+ const result = Transpile('1 + 2', currentEngine);
8
+ expect(result?.expression).toBe('(1 + 2)');
9
+ });
10
+
11
+ test('MID function', () => {
12
+ const result = Transpile('MID("test", 1, 5)', currentEngine);
13
+ expect(result?.expression).toBe(`SUBSTRING('test', 1, 5)`);
14
+ });
15
+
16
+ test('Should transpile a function inside another function', () => {
17
+ const program = 'MID(MID("This is a test", 1, 5), 1, 2)';
18
+ const result = Transpile(program, currentEngine);
19
+ expect(result?.valid).toBe(true);
20
+ expect(result?.expression).toBe(
21
+ "SUBSTRING(SUBSTRING('This is a test', 1, 5), 1, 2)",
22
+ );
23
+ });
24
+
25
+ test('ABS function', () => {
26
+ const result = Transpile('ABS(5)', currentEngine);
27
+ expect(result?.expression).toBe(`ABS(5)`);
28
+ });
29
+
30
+ test('DAY function', () => {
31
+ const result = Transpile('DAY("12/31/2023")', currentEngine);
32
+ expect(result?.expression).toBe(datePart('day'));
33
+ });
34
+
35
+ test('MONTH function', () => {
36
+ const result = Transpile('MONTH("12/31/2023")', currentEngine);
37
+ expect(result?.expression).toBe(datePart('month'));
38
+ });
39
+
40
+ test('YEAR function', () => {
41
+ const result = Transpile('YEAR("12/31/2023")', currentEngine);
42
+ expect(result?.expression).toBe(datePart('year'));
43
+ });
44
+
45
+ test('HOUR function', () => {
46
+ const result = Transpile('HOUR("12/31/2023")', currentEngine);
47
+ expect(result?.expression).toBe(datePart('hour'));
48
+ });
49
+
50
+ test('MINUTE function', () => {
51
+ const result = Transpile('MINUTE("12/31/2023")', currentEngine);
52
+ expect(result?.expression).toBe(datePart('minute'));
53
+ });
54
+
55
+ test('SECOND function', () => {
56
+ const result = Transpile('SECOND("12/31/2023")', currentEngine);
57
+ expect(result?.expression).toBe(datePart('second'));
58
+ });
59
+
60
+ test('DATEDIF function with Y unit', () => {
61
+ const result = Transpile(
62
+ 'DATEDIF("12/31/2023", "12/31/2022", "Y")',
63
+ currentEngine,
64
+ );
65
+ const mock = dateDif('year');
66
+ expect(result?.expression).toBe(mock);
67
+ });
68
+
69
+ test('DATEDIF function with M unit', () => {
70
+ const result = Transpile(
71
+ 'DATEDIF("12/31/2023", "12/31/2022", "M")',
72
+ currentEngine,
73
+ );
74
+ const mock = dateDif('month');
75
+ expect(result?.expression).toBe(mock);
76
+ });
77
+
78
+ test('DATEDIF function with D unit', () => {
79
+ const result = Transpile(
80
+ 'DATEDIF("12/31/2023", "12/31/2022", "D")',
81
+ currentEngine,
82
+ );
83
+ const mock = dateDif('day');
84
+ expect(result?.expression).toBe(mock);
85
+ });
86
+
87
+ test('DATEDIF function with a not valid Unit', () => {
88
+ const result = Transpile(
89
+ 'DATEDIF("12/31/2023", "12/31/2022", "X")',
90
+ currentEngine,
91
+ );
92
+ expect(result).not.toBeUndefined();
93
+ expect(result?.valid).toBe(false);
94
+ expect(result?.error).not.toBeUndefined();
95
+ expect(result?.error?.code).toBe('DATE_DIF_UNIT_PARAM');
96
+ });
97
+
98
+ test('IF function full parameters', () => {
99
+ const result = Transpile('IF(5 = 1, "True", "False")', currentEngine);
100
+ expect(result).not.toBeUndefined();
101
+ expect(result?.error).toBeUndefined();
102
+ expect(result?.expression?.replace(/\n/g, '')).toBe(
103
+ `(CASE WHEN (5 = 1) THEN 'True' ELSE 'False'END)`,
104
+ );
105
+ });
106
+
107
+ test('IF function basic parameters', () => {
108
+ const result = Transpile('IF(5 = 1, "True")', currentEngine);
109
+ expect(result).not.toBeUndefined();
110
+ expect(result?.error).toBeUndefined();
111
+ expect(result?.expression?.replace(/\n/g, '')).toBe(
112
+ `(CASE WHEN (5 = 1) THEN 'True' ELSE FALSEEND)`,
113
+ );
114
+ });
115
+
116
+ test('IFS function basic parameters', () => {
117
+ const result = Transpile(
118
+ 'IFS(5>10, "Greater than 10", 5>5, "Greater than 5",5>0, "Greater than 0")',
119
+ currentEngine,
120
+ );
121
+ expect(result).not.toBeUndefined();
122
+ expect(result?.error).toBeUndefined();
123
+ expect(result?.expression?.replace(/\n/g, '')).toBe(
124
+ `(CASE WHEN (5 > 10) THEN 'Greater than 10' ELSE (CASE WHEN (5 > 5) THEN 'Greater than 5' ELSE (CASE WHEN (5 > 0) THEN 'Greater than 0' ELSE NULL END) END) END)`,
125
+ );
126
+ });
127
+ });
@@ -0,0 +1,123 @@
1
+ import { ENGINES, Transpile } from '../../src';
2
+ import { datePart, dateDif } from './__mocks__/sqlScripts';
3
+ const currentEngine = ENGINES.SNOWFLAKE;
4
+
5
+ describe('SNOWFLAKE Transpile program', () => {
6
+ test('Basic case', () => {
7
+ const result = Transpile('1 + 2', currentEngine);
8
+ expect(result?.expression).toBe('(1 + 2)');
9
+ });
10
+
11
+ test('MID function', () => {
12
+ const result = Transpile('MID("test", 1, 5)', currentEngine);
13
+ expect(result?.expression).toBe(`SUBSTRING('test', 1, 5)`);
14
+ });
15
+
16
+ test('Should transpile a function inside another function', () => {
17
+ const program = 'MID(MID("This is a test", 1, 5), 1, 2)';
18
+ const result = Transpile(program, currentEngine);
19
+ expect(result?.valid).toBe(true);
20
+ expect(result?.expression).toBe(
21
+ "SUBSTRING(SUBSTRING('This is a test', 1, 5), 1, 2)",
22
+ );
23
+ });
24
+
25
+ test('ABS function', () => {
26
+ const result = Transpile('ABS(5)', currentEngine);
27
+ expect(result?.expression).toBe(`ABS(5)`);
28
+ });
29
+
30
+ test('DAY function', () => {
31
+ const result = Transpile('DAY("12/31/2023")', currentEngine);
32
+ expect(result?.expression).toBe(datePart('day'));
33
+ });
34
+
35
+ test('MONTH function', () => {
36
+ const result = Transpile('MONTH("12/31/2023")', currentEngine);
37
+ expect(result?.expression).toBe(datePart('month'));
38
+ });
39
+
40
+ test('YEAR function', () => {
41
+ const result = Transpile('YEAR("12/31/2023")', currentEngine);
42
+ expect(result?.expression).toBe(datePart('year'));
43
+ });
44
+
45
+ test('HOUR function', () => {
46
+ const result = Transpile('HOUR("12/31/2023")', currentEngine);
47
+ expect(result?.expression).toBe(datePart('hour'));
48
+ });
49
+
50
+ test('MINUTE function', () => {
51
+ const result = Transpile('MINUTE("12/31/2023")', currentEngine);
52
+ expect(result?.expression).toBe(datePart('minute'));
53
+ });
54
+
55
+ test('SECOND function', () => {
56
+ const result = Transpile('SECOND("12/31/2023")', currentEngine);
57
+ expect(result?.expression).toBe(datePart('second'));
58
+ });
59
+
60
+ test('DATEDIF function with Y unit', () => {
61
+ const result = Transpile(
62
+ 'DATEDIF("12/31/2023", "12/31/2022", "Y")',
63
+ currentEngine,
64
+ );
65
+ const mock = dateDif('year');
66
+ expect(result?.expression).toBe(mock);
67
+ });
68
+
69
+ test('DATEDIF function with M unit', () => {
70
+ const result = Transpile(
71
+ 'DATEDIF("12/31/2023", "12/31/2022", "M")',
72
+ currentEngine,
73
+ );
74
+ const mock = dateDif('month');
75
+ expect(result?.expression).toBe(mock);
76
+ });
77
+
78
+ test('DATEDIF function with D unit', () => {
79
+ const result = Transpile(
80
+ 'DATEDIF("12/31/2023", "12/31/2022", "D")',
81
+ currentEngine,
82
+ );
83
+ const mock = dateDif('day');
84
+ expect(result?.expression).toBe(mock);
85
+ });
86
+
87
+ test('DATEDIF function with a not valid Unit', () => {
88
+ const result = Transpile(
89
+ 'DATEDIF("12/31/2023", "12/31/2022", "X")',
90
+ currentEngine,
91
+ );
92
+ expect(result).not.toBeUndefined();
93
+ expect(result?.valid).toBe(false);
94
+ expect(result?.error).not.toBeUndefined();
95
+ expect(result?.error?.code).toBe('DATE_DIF_UNIT_PARAM');
96
+ });
97
+
98
+ test('IF function full parameters', () => {
99
+ const result = Transpile('IF(5 = 1, "True", "False")', currentEngine);
100
+ expect(result).not.toBeUndefined();
101
+ expect(result?.error).toBeUndefined();
102
+ expect(result?.expression).toBe(`IFF((5 = 1), 'True', 'False')`);
103
+ });
104
+
105
+ test('IF function basic parameters', () => {
106
+ const result = Transpile('IF(5 = 1, "True")', currentEngine);
107
+ expect(result).not.toBeUndefined();
108
+ expect(result?.error).toBeUndefined();
109
+ expect(result?.expression).toBe(`IFF((5 = 1), 'True', FALSE)`);
110
+ });
111
+
112
+ test('IFS function basic parameters', () => {
113
+ const result = Transpile(
114
+ 'IFS(5>10, "Greater than 10", 5>5, "Greater than 5",5>0, "Greater than 0")',
115
+ currentEngine,
116
+ );
117
+ expect(result).not.toBeUndefined();
118
+ expect(result?.error).toBeUndefined();
119
+ expect(result?.expression).toBe(
120
+ `IFF((5 > 10), 'Greater than 10', IFF((5 > 5), 'Greater than 5', IFF((5 > 0), 'Greater than 0', NULL)))`,
121
+ );
122
+ });
123
+ });
@@ -0,0 +1,106 @@
1
+ import { AST_TYPES } from '../../src/constants';
2
+ import { calculateAST, FormulaLang } from '../../src';
3
+ import {
4
+ BinaryExpressionAST,
5
+ LiteralAST,
6
+ } from '../../src/constants/interfaces';
7
+
8
+ describe('Parser tests suite', () => {
9
+ test('Should parse a single node', () => {
10
+ const program = '1 + 2';
11
+ const parser = FormulaLang.parser;
12
+ const { topNode } = parser.parse(program);
13
+ const ast = calculateAST(program, topNode);
14
+ expect(ast?.body).not.toBeUndefined();
15
+
16
+ const body = ast?.body as BinaryExpressionAST;
17
+ expect(body.operator).toBe('+');
18
+ expect(body.type).toBe(AST_TYPES.binaryExpression);
19
+ });
20
+
21
+ test('Should fail a logic operation', () => {
22
+ const program = '1 <';
23
+ const parser = FormulaLang.parser;
24
+ const { topNode } = parser.parse(program);
25
+ const ast = calculateAST(program, topNode);
26
+ expect(ast?.body).not.toBeUndefined();
27
+
28
+ const body = ast?.body as BinaryExpressionAST;
29
+ expect(body.operator).toBe('<');
30
+ expect(body.type).toBe(AST_TYPES.binaryExpression);
31
+ });
32
+
33
+ describe('Date literals', () => {
34
+ test('Should fail with a date without time', () => {
35
+ const program = '"31/12/2022"';
36
+ const parser = FormulaLang.parser;
37
+ const { topNode } = parser.parse(program);
38
+ const ast = calculateAST(program, topNode);
39
+ expect(ast?.body).not.toBeUndefined();
40
+
41
+ const body = ast?.body as LiteralAST;
42
+ expect(body.value).toEqual('31/12/2022');
43
+ expect(body.dataType).toBe('string');
44
+ });
45
+
46
+ test('Should return a date without time', () => {
47
+ const program = '"12/31/2022"';
48
+ const parser = FormulaLang.parser;
49
+ const { topNode } = parser.parse(program);
50
+ const ast = calculateAST(program, topNode);
51
+ expect(ast?.body).not.toBeUndefined();
52
+
53
+ const body = ast?.body as LiteralAST;
54
+ expect(body.value).toEqual('2022-12-31T00:00:00.000Z');
55
+ expect(body.dataType).toBe('date');
56
+ });
57
+
58
+ test('Should return a date with time', () => {
59
+ const program = '"12/31/2022 15:22:18"';
60
+ const parser = FormulaLang.parser;
61
+ const { topNode } = parser.parse(program);
62
+ const ast = calculateAST(program, topNode);
63
+ expect(ast?.body).not.toBeUndefined();
64
+
65
+ const body = ast?.body as LiteralAST;
66
+ expect(body.value).toEqual('2022-12-31T15:22:18.000Z');
67
+ expect(body.dataType).toBe('date');
68
+ });
69
+
70
+ test('Should return a date with invalid time', () => {
71
+ const program = '"12/31/2022 15:89:18"';
72
+ const parser = FormulaLang.parser;
73
+ const { topNode } = parser.parse(program);
74
+ const ast = calculateAST(program, topNode);
75
+ expect(ast?.body).not.toBeUndefined();
76
+
77
+ const body = ast?.body as LiteralAST;
78
+ expect(body.value).toEqual('12/31/2022 15:89:18');
79
+ expect(body.dataType).toBe('string');
80
+ });
81
+
82
+ test('Should return string with invalid dates february with 31', () => {
83
+ const program = '"02/31/2022 15:23:18"';
84
+ const parser = FormulaLang.parser;
85
+ const { topNode } = parser.parse(program);
86
+ const ast = calculateAST(program, topNode);
87
+ expect(ast?.body).not.toBeUndefined();
88
+
89
+ const body = ast?.body as LiteralAST;
90
+ expect(body.value).toEqual('02/31/2022 15:23:18');
91
+ expect(body.dataType).toBe('string');
92
+ });
93
+
94
+ test('Should return date valid for leap years february with 29', () => {
95
+ const program = '"02/29/2024"';
96
+ const parser = FormulaLang.parser;
97
+ const { topNode } = parser.parse(program);
98
+ const ast = calculateAST(program, topNode);
99
+ expect(ast?.body).not.toBeUndefined();
100
+
101
+ const body = ast?.body as LiteralAST;
102
+ expect(body.value).toEqual('2024-02-29T00:00:00.000Z');
103
+ expect(body.dataType).toBe('date');
104
+ });
105
+ });
106
+ });
@@ -1,7 +1,6 @@
1
1
  import { ENGINES } from '../../src/constants';
2
2
  import { ProgramAST } from '../../src/constants/interfaces';
3
3
  import { Transpiler } from '../../src/transpiler';
4
- import { Transpile } from '../../src/index';
5
4
 
6
5
  describe('Transpiler tests suit', () => {
7
6
  test('Should resolve basic arithmetic operation', () => {
@@ -126,20 +125,4 @@ describe('Transpiler tests suit', () => {
126
125
  expect(result.valid).toBe(false);
127
126
  expect(result.error).not.toBeUndefined();
128
127
  });
129
-
130
- test('should transpile a function to elasticsearch', () => {
131
- const program = 'MID(MID("This is a test", 1, 5), 1, 2)';
132
- const result = Transpile(program, ENGINES.ELASTICSEARCH);
133
- expect(result?.valid).toBe(true);
134
- expect(result?.expression).toBe(
135
- "'This is a test'.substring(1, 5).substring(1, 2)",
136
- );
137
- });
138
-
139
- test('should transpile a logical operator', () => {
140
- const program = '1 < 2';
141
- const result = Transpile(program, ENGINES.ELASTICSEARCH);
142
- expect(result?.valid).toBe(true);
143
- expect(result?.expression).toBe('(1 < 2)');
144
- });
145
128
  });
@@ -1,6 +1,7 @@
1
1
  export declare enum ENGINES {
2
2
  ELASTICSEARCH = "elasticsearch",
3
- SNOWFLAKE = "snowflake"
3
+ SNOWFLAKE = "snowflake",
4
+ REDSHIFT = "redshift"
4
5
  }
5
6
  export declare enum AST_TYPES {
6
7
  program = "Program",
@@ -16,10 +17,13 @@ export declare enum ERROR_LIST {
16
17
  floatingPoint = "floatingPoint",
17
18
  missingString = "missingString",
18
19
  missingNumber = "missingNumber",
20
+ missingDate = "missingDate",
19
21
  greaterThanZero = "greaterThanZero",
20
22
  missingArg = "missingArg",
21
23
  unknownFunction = "unknownFunction",
22
- unknown = "unknown"
24
+ unknown = "unknown",
25
+ customDateDifParams = "customDateDifParams",
26
+ tooManyArguments = "tooManyArguments"
23
27
  }
24
28
  type iErrorsType = {
25
29
  [key in ERROR_LIST]: IErrorDictionary;
@@ -29,4 +33,10 @@ interface IErrorDictionary {
29
33
  code: string;
30
34
  }
31
35
  export declare const ERROR_DICTIONARY: iErrorsType;
36
+ export type CustomEngineOperators = {
37
+ [key in ENGINES]?: {
38
+ [T: string]: string;
39
+ };
40
+ };
41
+ export declare const CustomOperators: CustomEngineOperators;
32
42
  export {};