name:
src/test/js/execution/TranspilationVisitorTest.ts
-rw-r--r--
20109
1import { assert } from "chai";
2import { it, describe } from "../testutils/TestUtils";
3import { DivException } from "../../../main/js/errors/DivException";
4import { NAException } from "../../../main/js/errors/NAException";
5import { NameException } from "../../../main/js/errors/NameException";
6import { NullException } from "../../../main/js/errors/NullException";
7import { NumException } from "../../../main/js/errors/NumException";
8import { ParseException } from "../../../main/js/errors/ParseException";
9import { RefException } from "../../../main/js/errors/RefException";
10import { ValueException } from "../../../main/js/errors/ValueException";
11import { TranspilationVisitor } from "../../../main/js/execution/TranspilationVisitor";
12import { FormulaName } from "../../../main/js/formulas/FormulaName";
13import { BinaryOperationNode } from "../../../main/js/models/nodes/BinaryOperationNode";
14import { CellQuery } from "../../../main/js/models/nodes/CellQuery";
15import { ErrorNode } from "../../../main/js/models/nodes/ErrorNode";
16import { FormulaNode } from "../../../main/js/models/nodes/FormulaNode";
17import { LogicalNode } from "../../../main/js/models/nodes/LogicalNode";
18import { MultiRangeNode } from "../../../main/js/models/nodes/MultiRangeNode";
19import { Node } from "../../../main/js/models/nodes/Node";
20import { NumberNode } from "../../../main/js/models/nodes/NumberNode";
21import { RangeNode } from "../../../main/js/models/nodes/RangeNode";
22import { TextNode } from "../../../main/js/models/nodes/TextNode";
23import { UnaryMinusOperationNode } from "../../../main/js/models/nodes/UnaryMinusOperationNode";
24import { UnaryPercentOperationNode } from "../../../main/js/models/nodes/UnaryPercentOperationNode";
25import { UnaryPlusOperationNode } from "../../../main/js/models/nodes/UnaryPlusOperationNode";
26import { VariableNode } from "../../../main/js/models/nodes/VariableNode";
27import * as antlr4 from "antlr4";
28import { F7Lexer } from "../../../main/js/antlr/F7Lexer";
29import { F7Parser } from "../../../main/js/antlr/F7Parser";
30
31describe("TranspilationVisitor", function () {
32 it("should transpile numbers", function () {
33 run("10.0e2", new NumberNode(10.0e2));
34 run("10.0e-2", new NumberNode(10.0e-2));
35 run("81739821", new NumberNode(81739821));
36 run("1.00000000001", new NumberNode(1.00000000001));
37 run("199", new NumberNode(199));
38 run("201", new NumberNode(201));
39 run("9187312.222", new NumberNode(9187312.222));
40 });
41
42 it("should transpile text", function () {
43 run('"Hello Friend!"', new TextNode("Hello Friend!"));
44 run('""', new TextNode(""));
45 });
46
47 it("should transpile formulas without arguments", function () {
48 run("RAND()", new FormulaNode(FormulaName.RAND.toString()));
49 run("TRUE()", new FormulaNode(FormulaName.TRUE.toString()));
50 run("FALSE()", new FormulaNode(FormulaName.FALSE.toString()));
51 });
52
53 it("should transpile formulas with arguments", function () {
54 run(
55 "POW(2, 6)",
56 new FormulaNode(FormulaName.POW.toString(), [new NumberNode(2), new NumberNode(6)])
57 );
58 run("SUM(1.829173)", new FormulaNode(FormulaName.SUM.toString(), [new NumberNode(1.829173)]));
59 });
60
61 it("should transpile variables", function () {
62 run(
63 "My_Special_Variable_That_Might_Exist",
64 new VariableNode("My_Special_Variable_That_Might_Exist")
65 );
66 run(
67 "VARIABLE_THAT_IS_LONGER_THAN_TWO_HUNDRED_AND_FIFTY_FIVE_CHARACTERS_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FARM_OLD_MCDONALD_HAD_A_FA",
68 new ErrorNode(new ParseException())
69 );
70 });
71
72 it("should transpile logical nodes", function () {
73 run("TRUE", new LogicalNode(true));
74 run("True", new LogicalNode(true));
75 run("tRuE", new LogicalNode(true));
76 run("FALSE", new LogicalNode(false));
77 run("False", new LogicalNode(false));
78 run("fAlSe", new LogicalNode(false));
79 run("false", new LogicalNode(false));
80 });
81
82 it("should transpile errors", function () {
83 run("#NUM!", new ErrorNode(new NumException()));
84 run("#DIV/0!", new ErrorNode(new DivException()));
85 run("#VALUE!", new ErrorNode(new ValueException()));
86 run("#REF!", new ErrorNode(new RefException()));
87 run("#NAME?", new ErrorNode(new NameException()));
88 run("#NUM!", new ErrorNode(new NumException()));
89 run("#NULL!", new ErrorNode(new NullException()));
90 run("#N/A", new ErrorNode(new NAException()));
91 run("#ERROR!", new ErrorNode(new ParseException()));
92 });
93
94 it("should transpile addition operations", function () {
95 run("1 + 1", new BinaryOperationNode(new NumberNode(1), "+", new NumberNode(1)));
96 run("1 + 0", new BinaryOperationNode(new NumberNode(1), "+", new NumberNode(0)));
97 run("1 + 2.12121", new BinaryOperationNode(new NumberNode(1), "+", new NumberNode(2.12121)));
98 run(
99 "1e10 + 2.12121",
100 new BinaryOperationNode(new NumberNode(1e10), "+", new NumberNode(2.12121))
101 );
102 });
103
104 it("should transpile addition operations with error literals", function () {
105 run(
106 "3 + #VALUE!",
107 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new ValueException()))
108 );
109 run(
110 "3 + #DIV/0!",
111 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new DivException()))
112 );
113 run(
114 "3 + #NUM!",
115 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new NumException()))
116 );
117 run(
118 "3 + #NAME?",
119 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new NameException()))
120 );
121 run(
122 "3 + #NULL!",
123 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new NullException()))
124 );
125 run(
126 "3 + #N/A",
127 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new NAException()))
128 );
129 run(
130 "3 + #REF!",
131 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new RefException()))
132 );
133 run(
134 "3 + #ERROR!",
135 new BinaryOperationNode(new NumberNode(3), "+", new ErrorNode(new ParseException()))
136 );
137 });
138
139 it("should transpile subtraction operations", function () {
140 run("1 - 1", new BinaryOperationNode(new NumberNode(1), "-", new NumberNode(1)));
141 run("3 - 1", new BinaryOperationNode(new NumberNode(3), "-", new NumberNode(1)));
142 run(
143 "3.1e3 - 4.2e10",
144 new BinaryOperationNode(new NumberNode(3.1e3), "-", new NumberNode(4.2e10))
145 );
146 });
147
148 it("should transpile subtraction operations with error literals", function () {
149 run(
150 "3 - #VALUE!",
151 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new ValueException()))
152 );
153 run(
154 "3 - #DIV/0!",
155 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new DivException()))
156 );
157 run(
158 "3 - #NUM!",
159 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new NumException()))
160 );
161 run(
162 "3 - #NAME?",
163 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new NameException()))
164 );
165 run(
166 "3 - #NULL!",
167 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new NullException()))
168 );
169 run(
170 "3 - #N/A",
171 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new NAException()))
172 );
173 run(
174 "3 - #REF!",
175 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new RefException()))
176 );
177 run(
178 "3 - #ERROR!",
179 new BinaryOperationNode(new NumberNode(3), "-", new ErrorNode(new ParseException()))
180 );
181 });
182
183 it("should transpile multiplication operations", function () {
184 run("3 * 4", new BinaryOperationNode(new NumberNode(3), "*", new NumberNode(4)));
185 run("3 * 1", new BinaryOperationNode(new NumberNode(3), "*", new NumberNode(1)));
186 run("3.1e3 * 2", new BinaryOperationNode(new NumberNode(3.1e3), "*", new NumberNode(2)));
187 run("3.1e3 * 0", new BinaryOperationNode(new NumberNode(3.1e3), "*", new NumberNode(0)));
188 });
189
190 it("should transpile multiplication operations with error literals", function () {
191 run(
192 "3 * #VALUE!",
193 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new ValueException()))
194 );
195 run(
196 "3 * #DIV/0!",
197 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new DivException()))
198 );
199 run(
200 "3 * #NUM!",
201 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new NumException()))
202 );
203 run(
204 "3 * #NAME?",
205 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new NameException()))
206 );
207 run(
208 "3 * #NULL!",
209 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new NullException()))
210 );
211 run(
212 "3 * #N/A",
213 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new NAException()))
214 );
215 run(
216 "3 * #REF!",
217 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new RefException()))
218 );
219 run(
220 "3 * #ERROR!",
221 new BinaryOperationNode(new NumberNode(3), "*", new ErrorNode(new ParseException()))
222 );
223 });
224
225 it("should transpile division operations", function () {
226 run("3 / 4", new BinaryOperationNode(new NumberNode(3), "/", new NumberNode(4)));
227 run("4 / 3", new BinaryOperationNode(new NumberNode(4), "/", new NumberNode(3)));
228 run("3e3 / 2", new BinaryOperationNode(new NumberNode(3e3), "/", new NumberNode(2)));
229 run("10 / 0", new BinaryOperationNode(new NumberNode(10), "/", new NumberNode(0)));
230 });
231
232 it("should transpile division operations with error literals", function () {
233 run(
234 "3 / #VALUE!",
235 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new ValueException()))
236 );
237 run(
238 "3 / #DIV/0!",
239 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new DivException()))
240 );
241 run(
242 "3 / #NUM!",
243 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new NumException()))
244 );
245 run(
246 "3 / #NAME?",
247 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new NameException()))
248 );
249 run(
250 "3 / #NULL!",
251 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new NullException()))
252 );
253 run(
254 "3 / #N/A",
255 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new NAException()))
256 );
257 run(
258 "3 / #REF!",
259 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new RefException()))
260 );
261 run(
262 "3 / #ERROR!",
263 new BinaryOperationNode(new NumberNode(3), "/", new ErrorNode(new ParseException()))
264 );
265 });
266
267 it("should transpile exponent operations", function () {
268 run(
269 "-2^3",
270 new BinaryOperationNode(
271 new UnaryMinusOperationNode(new NumberNode(2)),
272 "^",
273 new NumberNode(3)
274 )
275 );
276 run(
277 "(-2)^3",
278 new BinaryOperationNode(
279 new UnaryMinusOperationNode(new NumberNode(2)),
280 "^",
281 new NumberNode(3)
282 )
283 );
284 run(
285 "-(2)^3",
286 new BinaryOperationNode(
287 new UnaryMinusOperationNode(new NumberNode(2)),
288 "^",
289 new NumberNode(3)
290 )
291 );
292 run(
293 "--(-2)^3",
294 new BinaryOperationNode(
295 new UnaryMinusOperationNode(
296 new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(2)))
297 ),
298 "^",
299 new NumberNode(3)
300 )
301 );
302 run(
303 "11 -2^3",
304 new BinaryOperationNode(
305 new NumberNode(11),
306 "-",
307 new BinaryOperationNode(new NumberNode(2), "^", new NumberNode(3))
308 )
309 );
310 run(
311 "(-2) ^ 3",
312 new BinaryOperationNode(
313 new UnaryMinusOperationNode(new NumberNode(2)),
314 "^",
315 new NumberNode(3)
316 )
317 );
318 run(
319 "(-3) ^ 2",
320 new BinaryOperationNode(
321 new UnaryMinusOperationNode(new NumberNode(3)),
322 "^",
323 new NumberNode(2)
324 )
325 );
326 });
327
328 it("should transpile concat operations", function () {
329 run("1 & 1", new BinaryOperationNode(new NumberNode(1), "&", new NumberNode(1)));
330 run('"One" & 0', new BinaryOperationNode(new TextNode("One"), "&", new NumberNode(0)));
331 });
332
333 it("should transpile concat operations with error literals", function () {
334 run(
335 "1 & #DIV/0!",
336 new BinaryOperationNode(new NumberNode(1), "&", new ErrorNode(new DivException()))
337 );
338 });
339
340 it("should transpile unary minus operations", function () {
341 run("-22", new UnaryMinusOperationNode(new NumberNode(22)));
342 run('-"Thing"', new UnaryMinusOperationNode(new TextNode("Thing")));
343 run("--22", new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(22))));
344 run(
345 "---22",
346 new UnaryMinusOperationNode(
347 new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(22)))
348 )
349 );
350 run(
351 "-(2 * 4)",
352 new UnaryMinusOperationNode(
353 new BinaryOperationNode(new NumberNode(2), "*", new NumberNode(4))
354 )
355 );
356 });
357
358 it("should transpile unary percent operations", function () {
359 run("88%", new UnaryPercentOperationNode(new NumberNode(88)));
360 run(
361 "(8 * 8)%",
362 new UnaryPercentOperationNode(
363 new BinaryOperationNode(new NumberNode(8), "*", new NumberNode(8))
364 )
365 );
366 });
367
368 it("should transpile to error when unary percent operation contains more than one percent sign", function () {
369 run("88%%", new ErrorNode(new ParseException()));
370 run("88%%%", new ErrorNode(new ParseException()));
371 run("88% % % %", new ErrorNode(new ParseException()));
372 });
373
374 it("should transpile unary plus operations", function () {
375 run("+22", new UnaryPlusOperationNode(new NumberNode(22)));
376 });
377
378 it("should transpile ranges with single-quoted sheet names", function () {
379 run(
380 "'Sheet Name'!D11",
381 new RangeNode(
382 CellQuery.builder()
383 .setSheet("'Sheet Name'")
384 .columnsBetween(3, 3)
385 .rowsBetween(10, 10)
386 .build()
387 )
388 );
389 run(
390 "'Sheet Name'!A1:A2",
391 new RangeNode(
392 CellQuery.builder().setSheet("'Sheet Name'").columnsBetween(0, 0).rowsBetween(0, 1).build()
393 )
394 );
395 run(
396 "'Sheet Name'!3:7",
397 new RangeNode(
398 CellQuery.builder()
399 .setSheet("'Sheet Name'")
400 .rowsBetween(2, 6)
401 .openColumnsStartingAtZero()
402 .build()
403 )
404 );
405 run(
406 "'Sheet Name'!C:A",
407 new RangeNode(
408 CellQuery.builder()
409 .setSheet("'Sheet Name'")
410 .columnsBetween(0, 2)
411 .openRowsStartingAtZero()
412 .build()
413 )
414 );
415 run(
416 "'Sheet Name'!D3:7",
417 new RangeNode(
418 CellQuery.builder()
419 .setSheet("'Sheet Name'")
420 .rowsBetween(2, 6)
421 .openColumnsStartingAt("D")
422 .build()
423 )
424 );
425 });
426
427 it("should transpile cell range with single cell", function () {
428 run("A4", new RangeNode(CellQuery.builder().columnsBetween(0, 0).rowsBetween(3, 3).build()));
429 run("D11", new RangeNode(CellQuery.builder().columnsBetween(3, 3).rowsBetween(10, 10).build()));
430 });
431
432 it("should transpile cell range with bi-cell (beginning and end)", function () {
433 run("A1:B2", new RangeNode(CellQuery.builder().columnsBetween(0, 1).rowsBetween(0, 1).build()));
434 run("B2:A1", new RangeNode(CellQuery.builder().columnsBetween(0, 1).rowsBetween(0, 1).build()));
435 run("B2:D4", new RangeNode(CellQuery.builder().columnsBetween(1, 3).rowsBetween(1, 3).build()));
436 run("B4:B4", new RangeNode(CellQuery.builder().columnsBetween(1, 1).rowsBetween(3, 3).build()));
437 run("B3:B4", new RangeNode(CellQuery.builder().columnsBetween(1, 1).rowsBetween(2, 3).build()));
438 run("B4:B3", new RangeNode(CellQuery.builder().columnsBetween(1, 1).rowsBetween(2, 3).build()));
439 });
440
441 it("should transpile cell ranges that are column-wise (eg: A:D, M:M)", function () {
442 run(
443 "A:C",
444 new RangeNode(CellQuery.builder().columnsBetween(0, 2).openRowsStartingAtZero().build())
445 );
446 run(
447 "C:A",
448 new RangeNode(CellQuery.builder().columnsBetween(0, 2).openRowsStartingAtZero().build())
449 );
450 run(
451 "B:M",
452 new RangeNode(CellQuery.builder().columnsBetween(1, 12).openRowsStartingAtZero().build())
453 );
454 run(
455 "M:B",
456 new RangeNode(CellQuery.builder().columnsBetween(1, 12).openRowsStartingAtZero().build())
457 );
458 run(
459 "M:M",
460 new RangeNode(CellQuery.builder().columnsBetween(12, 12).openRowsStartingAtZero().build())
461 );
462 });
463
464 it("should transpile cell ranges that are row-wise (eg: 1:8, 4:4)", function () {
465 run(
466 "3:7",
467 new RangeNode(CellQuery.builder().rowsBetween(2, 6).openColumnsStartingAtZero().build())
468 );
469 run(
470 "1:100",
471 new RangeNode(CellQuery.builder().rowsBetween(0, 99).openColumnsStartingAtZero().build())
472 );
473 run(
474 "7:3",
475 new RangeNode(CellQuery.builder().rowsBetween(2, 6).openColumnsStartingAtZero().build())
476 );
477 run(
478 "100:1",
479 new RangeNode(CellQuery.builder().rowsBetween(0, 99).openColumnsStartingAtZero().build())
480 );
481 });
482
483 it("should transpile cell ranges that are row-wise with column offset (eg: A2:9)", function () {
484 run(
485 "D3:7",
486 new RangeNode(CellQuery.builder().rowsBetween(2, 6).openColumnsStartingAt("D").build())
487 );
488 run(
489 "D7:3",
490 new RangeNode(CellQuery.builder().rowsBetween(2, 6).openColumnsStartingAt("D").build())
491 );
492 run(
493 "E5:10",
494 new RangeNode(CellQuery.builder().rowsBetween(4, 9).openColumnsStartingAtNumber(4).build())
495 );
496 run(
497 "D7:7",
498 new RangeNode(CellQuery.builder().rowsBetween(6, 6).openColumnsStartingAt("D").build())
499 );
500 });
501
502 it("should transpile cell ranges that are column-wise with row offset (eg: D1:M)", function () {
503 run(
504 "B3:D",
505 new RangeNode(CellQuery.builder().columnsBetween(1, 3).openRowsStartingAtNumber(2).build())
506 );
507 run(
508 "D3:B",
509 new RangeNode(CellQuery.builder().columnsBetween(1, 3).openRowsStartingAtNumber(2).build())
510 );
511 run(
512 "B33:B",
513 new RangeNode(CellQuery.builder().columnsBetween(1, 1).openRowsStartingAtNumber(32).build())
514 );
515 });
516
517 it("should transpile cell ranges that are multi-cell", function () {
518 run(
519 "C5:B4:D2:G7:L11",
520 new MultiRangeNode([
521 new RangeNode(CellQuery.builder().columnsBetween(1, 2).rowsBetween(3, 4).build()),
522 new RangeNode(CellQuery.builder().columnsBetween(3, 6).rowsBetween(1, 6).build()),
523 new RangeNode(CellQuery.builder().columnsBetween(11, 11).rowsBetween(10, 10).build()),
524 ])
525 );
526 run(
527 "A4:B5:C6:D7:E8:F9",
528 new MultiRangeNode([
529 new RangeNode(CellQuery.builder().columnsBetween(0, 1).rowsBetween(3, 4).build()),
530 new RangeNode(CellQuery.builder().columnsBetween(2, 3).rowsBetween(5, 6).build()),
531 new RangeNode(CellQuery.builder().columnsBetween(4, 5).rowsBetween(7, 8).build()),
532 ])
533 );
534 run(
535 "SUM(A1:B2:C3:D4:E5:F6)",
536 new FormulaNode(FormulaName.SUM.toString(), [
537 new MultiRangeNode([
538 new RangeNode(CellQuery.builder().columnsBetween(0, 1).rowsBetween(0, 1).build()),
539 new RangeNode(CellQuery.builder().columnsBetween(2, 3).rowsBetween(2, 3).build()),
540 new RangeNode(CellQuery.builder().columnsBetween(4, 5).rowsBetween(4, 5).build()),
541 ]),
542 ])
543 );
544 });
545
546 /**
547 * Just run the test, asserting that the result is equal to the expected node.
548 * @param input - input string.
549 * @param expected - expected node.
550 */
551 function run(input: string, expected: Node) {
552 try {
553 const chars = new antlr4.InputStream(input);
554 const lexer: any = new F7Lexer(chars);
555 const tokens = new antlr4.CommonTokenStream(lexer);
556 const parser: any = new F7Parser(tokens);
557 parser.buildParseTrees = true;
558 const tree = parser.start().block();
559 assert.deepEqual(new TranspilationVisitor().visit(tree), expected);
560 } catch (e) {
561 assert.deepEqual(new ErrorNode(e), expected);
562 }
563 }
564});