f7
f7 is a spreadsheet formula execution library
git clone https://git.vogt.world/f7.git
Log | Files | README.md | LICENSE.md
← All files
name: src/test/java/io/protobase/f7/transpiler/TranspilationVisitorTest.java
-rw-r--r--
24952
  1package io.protobase.f7.transpiler;
  2
  3import com.google.common.collect.ImmutableList;
  4import io.protobase.f7.antlr.F7Lexer;
  5import io.protobase.f7.antlr.F7Parser;
  6import io.protobase.f7.errors.DivException;
  7import io.protobase.f7.errors.F7Exception;
  8import io.protobase.f7.errors.NAException;
  9import io.protobase.f7.errors.NameException;
 10import io.protobase.f7.errors.NullException;
 11import io.protobase.f7.errors.NumException;
 12import io.protobase.f7.errors.ParseException;
 13import io.protobase.f7.errors.RefException;
 14import io.protobase.f7.errors.ValueException;
 15import io.protobase.f7.formulas.FormulaName;
 16import io.protobase.f7.models.BinaryOperationNode;
 17import io.protobase.f7.models.CellQuery;
 18import io.protobase.f7.models.ErrorNode;
 19import io.protobase.f7.models.FormulaNode;
 20import io.protobase.f7.models.ListNode;
 21import io.protobase.f7.models.Node;
 22import io.protobase.f7.models.NumberNode;
 23import io.protobase.f7.models.RangeNode;
 24import io.protobase.f7.models.RangeQueryNode;
 25import io.protobase.f7.models.TextNode;
 26import io.protobase.f7.models.UnaryMinusOperationNode;
 27import io.protobase.f7.models.UnaryPercentOperationNode;
 28import io.protobase.f7.models.VariableNode;
 29import io.protobase.f7.spreadsheet.ParseErrorListener;
 30import org.antlr.v4.runtime.CharStreams;
 31import org.antlr.v4.runtime.CommonTokenStream;
 32import org.antlr.v4.runtime.misc.ParseCancellationException;
 33import org.junit.Test;
 34
 35import java.io.ByteArrayInputStream;
 36import java.io.IOException;
 37import java.nio.charset.StandardCharsets;
 38
 39import static com.google.common.truth.Truth.assertThat;
 40
 41public class TranspilationVisitorTest {
 42  private static final TranspilationVisitor visitor = new TranspilationVisitor();
 43
 44  @Test
 45  public void numberNode() {
 46    run("10.0e2", new NumberNode(10.0e2));
 47    run("10.0e-2", new NumberNode(10.0e-2));
 48    run("81739821", new NumberNode(81739821.0));
 49    run("1.00000000001", new NumberNode(1.00000000001));
 50    run("199", new NumberNode(199.0));
 51    run("201", new NumberNode(201.0));
 52    run("9187312.222", new NumberNode(9187312.222));
 53  }
 54
 55  @Test
 56  public void textNode() {
 57    run("\"Hello Friend!\"", new TextNode("Hello Friend!"));
 58    run("\"\"", new TextNode(""));
 59  }
 60
 61  @Test
 62  public void listNode() {
 63    run("{1.1}", ListNode.builder()
 64        .value(0, 0, new NumberNode(1.1))
 65        .build());
 66    run("{1.1, 4.4, \"Third\"}", ListNode.builder()
 67        .value(0, 0, new NumberNode(1.1))
 68        .value(1, 0, new NumberNode(4.4))
 69        .value(2, 0, new TextNode("Third"))
 70        .build());
 71  }
 72
 73  @Test
 74  public void listNode_nested() {
 75    run("{1, {2}, {{3}}}", ListNode.builder()
 76        .value(0, 0, new NumberNode(1.0))
 77        .value(1, 0, new NumberNode(2.0))
 78        .value(2, 0, new NumberNode(3.0))
 79        .build());
 80    run("{1, {2}, {{3}}, {4}, {{{{{5}}}}}}", ListNode.builder()
 81        .value(0, 0, new NumberNode(1.0))
 82        .value(1, 0, new NumberNode(2.0))
 83        .value(2, 0, new NumberNode(3.0))
 84        .value(3, 0, new NumberNode(4.0))
 85        .value(4, 0, new NumberNode(5.0))
 86        .build());
 87    run("{}", new ErrorNode(new RefException()));
 88    run("{1, {}}", ListNode.builder()
 89        .value(0, 0, new NumberNode(1.0))
 90        .value(1, 0, new ErrorNode(new RefException()))
 91        .build());
 92    run("{1, {}, {{}}}", ListNode.builder()
 93        .value(0, 0, new NumberNode(1.0))
 94        .value(1, 0, new ErrorNode(new RefException()))
 95        .value(2, 0, new ErrorNode(new RefException()))
 96        .build());
 97    run("{1, {2}, {{3}}, {{{4, 5, 6, {}, {8}}}}}", ListNode.builder()
 98        .value(0, 0, new NumberNode(1.0))
 99        .value(1, 0, new NumberNode(2.0))
100        .value(2, 0, new NumberNode(3.0))
101        .value(3, 0, new NumberNode(4.0))
102        .value(4, 0, new NumberNode(5.0))
103        .value(5, 0, new NumberNode(6.0))
104        .value(6, 0, new ErrorNode(new RefException()))
105        .value(7, 0, new NumberNode(8.0))
106        .build());
107    run("{{{{{{1}}}}}}", ListNode.builder()
108        .value(0, 0, new NumberNode(1.0))
109        .build());
110    run("{{10, {}, {{}}}}", ListNode.builder()
111        .value(0, 0, new NumberNode(10.0))
112        .value(1, 0, new ErrorNode(new RefException()))
113        .value(2, 0, new ErrorNode(new RefException()))
114        .build());
115  }
116
117  @Test
118  public void listNode_multiDimensional() {
119    run("{1,2,3}", ListNode.builder()
120        .value(0, 0, new NumberNode(1.0))
121        .value(1, 0, new NumberNode(2.0))
122        .value(2, 0, new NumberNode(3.0))
123        .build());
124
125    run("{{1;2;3},{4;5;6}}", ListNode.builder()
126        .value(0, 0, new NumberNode(1.0))
127        .value(0, 1, new NumberNode(2.0))
128        .value(0, 2, new NumberNode(3.0))
129        .value(1, 0, new NumberNode(4.0))
130        .value(1, 1, new NumberNode(5.0))
131        .value(1, 2, new NumberNode(6.0))
132        .build());
133
134    run("{{1,2,3};{4,5,6}}", ListNode.builder()
135        .value(0, 0, new NumberNode(1.0))
136        .value(1, 0, new NumberNode(2.0))
137        .value(2, 0, new NumberNode(3.0))
138        .value(0, 1, new NumberNode(4.0))
139        .value(1, 1, new NumberNode(5.0))
140        .value(2, 1, new NumberNode(6.0))
141        .build());
142
143    run("{{1,2,3},{4,5,6}}", ListNode.builder()
144        .value(0, 0, new NumberNode(1.0))
145        .value(1, 0, new NumberNode(2.0))
146        .value(2, 0, new NumberNode(3.0))
147        .value(3, 0, new NumberNode(4.0))
148        .value(4, 0, new NumberNode(5.0))
149        .value(5, 0, new NumberNode(6.0))
150        .build());
151
152    run("{{1;2;3}}", ListNode.builder()
153        .value(0, 0, new NumberNode(1.0))
154        .value(0, 1, new NumberNode(2.0))
155        .value(0, 2, new NumberNode(3.0))
156        .build());
157
158    run("{{1,2;3,4}}", ListNode.builder()
159        .value(0, 0, new NumberNode(1.0))
160        .value(1, 0, new NumberNode(2.0))
161        .value(0, 1, new NumberNode(3.0))
162        .value(1, 1, new NumberNode(4.0))
163        .build());
164
165    run("{1,2;3,4}", ListNode.builder()
166        .value(0, 0, new NumberNode(1.0))
167        .value(1, 0, new NumberNode(2.0))
168        .value(0, 1, new NumberNode(3.0))
169        .value(1, 1, new NumberNode(4.0))
170        .build());
171
172    run("{1;{2;3;4}}", ListNode.builder()
173        .value(0, 0, new NumberNode(1.0))
174        .value(0, 1, new NumberNode(2.0))
175        .value(0, 2, new NumberNode(3.0))
176        .value(0, 3, new NumberNode(4.0))
177        .build());
178
179    run("{{{1.1, 2.1, 3.1};{1.2, 2.2, 3.2}};{{1.3, 2.3, 3.3};{1.4, 2.4, 3.4}};{{1.5, 2.5, 3.5};{1.6, 2.6, 3.6}}}", ListNode.builder()
180        .value(0, 0, new NumberNode(1.1))
181        .value(1, 0, new NumberNode(2.1))
182        .value(2, 0, new NumberNode(3.1))
183        .value(0, 1, new NumberNode(1.2))
184        .value(1, 1, new NumberNode(2.2))
185        .value(2, 1, new NumberNode(3.2))
186        .value(0, 2, new NumberNode(1.3))
187        .value(1, 2, new NumberNode(2.3))
188        .value(2, 2, new NumberNode(3.3))
189        .value(0, 3, new NumberNode(1.4))
190        .value(1, 3, new NumberNode(2.4))
191        .value(2, 3, new NumberNode(3.4))
192        .value(0, 4, new NumberNode(1.5))
193        .value(1, 4, new NumberNode(2.5))
194        .value(2, 4, new NumberNode(3.5))
195        .value(0, 5, new NumberNode(1.6))
196        .value(1, 5, new NumberNode(2.6))
197        .value(2, 5, new NumberNode(3.6))
198        .build());
199  }
200
201  @Test
202  public void listNode_multiDimensionalAndMissingValues() {
203    run("{1,2;3;4;5;6}", ListNode.builder()
204        .value(0, 0, new NumberNode(1.0))
205        .value(1, 0, new NumberNode(2.0))
206        .value(0, 1, new NumberNode(3.0))
207        .value(0, 2, new NumberNode(4.0))
208        .value(0, 3, new NumberNode(5.0))
209        .value(0, 4, new NumberNode(6.0))
210        .build());
211  }
212
213  @Test
214  public void formulaNode_empty() {
215    run("RAND()", new FormulaNode(FormulaName.RAND.toString()));
216    run("TRUE()", new FormulaNode(FormulaName.TRUE.toString()));
217    run("FALSE()", new FormulaNode(FormulaName.FALSE.toString()));
218  }
219
220  @Test
221  public void formulaNode() {
222    run("POW(2, 6)", FormulaNode.builder()
223        .name(FormulaName.POW.toString())
224        .value(new NumberNode(2.0))
225        .value(new NumberNode(6.0))
226        .build());
227    run("SUM(1.829173, {8.12983271})", FormulaNode.builder()
228        .name(FormulaName.SUM.toString())
229        .value(new NumberNode(1.829173))
230        .value(ListNode.builder().value(0, 0, new NumberNode(8.12983271)).build())
231        .build());
232  }
233
234  @Test
235  public void variableNode() {
236    run("TRUE", new VariableNode("TRUE"));
237    run("True", new VariableNode("True"));
238    run("tRuE", new VariableNode("tRuE"));
239    run("FALSE", new VariableNode("FALSE"));
240    run("False", new VariableNode("False"));
241    run("fAlSe", new VariableNode("fAlSe"));
242    run("false", new VariableNode("false"));
243    run("My_Special_Variable_That_Might_Exist", new VariableNode("My_Special_Variable_That_Might_Exist"));
244    run("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",
245        new ErrorNode(new ParseException()));
246  }
247
248  @Test
249  public void errorNode() {
250    run("#NUM!", new ErrorNode(new NumException()));
251    run("#DIV/0!", new ErrorNode(new DivException()));
252    run("#VALUE!", new ErrorNode(new ValueException()));
253    run("#REF!", new ErrorNode(new RefException()));
254    run("#NAME?", new ErrorNode(new NameException()));
255    run("#NUM!", new ErrorNode(new NumException()));
256    run("#N/A", new ErrorNode(new NAException()));
257    run("#ERROR!", new ErrorNode(new ParseException()));
258  }
259
260  @Test
261  public void testAddition() {
262    run("1 + 1", new BinaryOperationNode(new NumberNode(1.0), "+", new NumberNode(1.0)));
263    run("1 + 0", new BinaryOperationNode(new NumberNode(1.0), "+", new NumberNode(0.0)));
264    run("1 + 2.12121", new BinaryOperationNode(new NumberNode(1.0), "+", new NumberNode(2.12121)));
265    run("1e10 + 2.12121", new BinaryOperationNode(new NumberNode(1e10), "+", new NumberNode(2.12121)));
266  }
267
268  @Test
269  public void testAddition_withList() {
270    ListNode singleOne = ListNode.builder()
271        .value(0, 0, new NumberNode(1.0))
272        .build();
273    run("1 + {1}", new BinaryOperationNode(new NumberNode(1.0), "+", singleOne));
274    run("1 + {1, 44}", new BinaryOperationNode(new NumberNode(1.0), "+", ListNode.builder()
275        .value(0, 0, new NumberNode(1.0))
276        .value(1, 0, new NumberNode(44.0))
277        .build()));
278    run("{1} + {1}", new BinaryOperationNode(singleOne, "+", singleOne));
279  }
280
281  @Test
282  public void testAddition_withErrors() {
283    run("3 + #VALUE!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new ValueException())));
284    run("3 + #DIV/0!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new DivException())));
285    run("3 + #NUM!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new NumException())));
286    run("3 + #NAME?", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new NameException())));
287    run("3 + #NULL!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new NullException())));
288    run("3 + #N/A", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new NAException())));
289    run("3 + #REF!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new RefException())));
290    run("3 + #ERROR!", new BinaryOperationNode(new NumberNode(3.0), "+", new ErrorNode(new ParseException())));
291  }
292
293  @Test
294  public void testSubtraction() {
295    run("1 - 1", new BinaryOperationNode(new NumberNode(1.0), "-", new NumberNode(1.0)));
296    run("3 - 1", new BinaryOperationNode(new NumberNode(3.0), "-", new NumberNode(1.0)));
297    run("3.1e3 - 4.2e10", new BinaryOperationNode(new NumberNode(3.1e3), "-", new NumberNode(4.2e10)));
298  }
299
300  @Test
301  public void testSubtraction_withList() {
302    ListNode singleOne = ListNode.builder()
303        .value(0, 0, new NumberNode(1.0))
304        .build();
305    run("1 - {1}", new BinaryOperationNode(new NumberNode(1.0), "-", singleOne));
306    run("1 - {1, 44}", new BinaryOperationNode(new NumberNode(1.0), "-", ListNode.builder()
307        .value(0, 0, new NumberNode(1.0))
308        .value(1, 0, new NumberNode(44.0))
309        .build()));
310    run("{1} - {1}", new BinaryOperationNode(singleOne, "-", singleOne));
311  }
312
313  @Test
314  public void testSubtraction_withErrors() {
315    run("3 - #VALUE!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new ValueException())));
316    run("3 - #DIV/0!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new DivException())));
317    run("3 - #NUM!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new NumException())));
318    run("3 - #NAME?", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new NameException())));
319    run("3 - #NULL!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new NullException())));
320    run("3 - #N/A", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new NAException())));
321    run("3 - #REF!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new RefException())));
322    run("3 - #ERROR!", new BinaryOperationNode(new NumberNode(3.0), "-", new ErrorNode(new ParseException())));
323  }
324
325  @Test
326  public void testMultiplication() {
327    run("3 * 4", new BinaryOperationNode(new NumberNode(3.0), "*", new NumberNode(4.0)));
328    run("3 * 1", new BinaryOperationNode(new NumberNode(3.0), "*", new NumberNode(1.0)));
329    run("3.1e3 * 2", new BinaryOperationNode(new NumberNode(3.1e3), "*", new NumberNode(2.0)));
330    run("3.1e3 * 0", new BinaryOperationNode(new NumberNode(3.1e3), "*", new NumberNode(0.0)));
331  }
332
333  @Test
334  public void testMultiplication_withList() {
335    run("3 * {4}", new BinaryOperationNode(new NumberNode(3.0), "*", ListNode.builder()
336        .value(0, 0, new NumberNode(4.0))
337        .build()));
338    run("3 * {4, TRUE}", new BinaryOperationNode(new NumberNode(3.0), "*", ListNode.builder()
339        .value(0, 0, new NumberNode(4.0))
340        .value(1, 0, new VariableNode("TRUE"))
341        .build()));
342  }
343
344  @Test
345  public void testMultiplication_withErrors() {
346    run("3 * #VALUE!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new ValueException())));
347    run("3 * #DIV/0!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new DivException())));
348    run("3 * #NUM!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new NumException())));
349    run("3 * #NAME?", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new NameException())));
350    run("3 * #NULL!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new NullException())));
351    run("3 * #N/A", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new NAException())));
352    run("3 * #REF!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new RefException())));
353    run("3 * #ERROR!", new BinaryOperationNode(new NumberNode(3.0), "*", new ErrorNode(new ParseException())));
354  }
355
356  @Test
357  public void testDivision() {
358    run("3 / 4", new BinaryOperationNode(new NumberNode(3.0), "/", new NumberNode(4.0)));
359    run("4 / 3", new BinaryOperationNode(new NumberNode(4.0), "/", new NumberNode(3.0)));
360    run("3e3 / 2", new BinaryOperationNode(new NumberNode(3e3), "/", new NumberNode(2.0)));
361    run("10 / 0", new BinaryOperationNode(new NumberNode(10.0), "/", new NumberNode(0.0)));
362  }
363
364  @Test
365  public void testDivision_withList() {
366    run("3 / {4}", new BinaryOperationNode(new NumberNode(3.0), "/", ListNode.builder()
367        .value(0, 0, new NumberNode(4.0))
368        .build()));
369    run("3 / {4, TRUE}", new BinaryOperationNode(new NumberNode(3.0), "/", ListNode.builder()
370        .value(0, 0, new NumberNode(4.0))
371        .value(1, 0, new VariableNode("TRUE"))
372        .build()));
373  }
374
375  @Test
376  public void testDivision_withErrors() {
377    run("3 / #VALUE!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new ValueException())));
378    run("3 / #DIV/0!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new DivException())));
379    run("3 / #NUM!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new NumException())));
380    run("3 / #NAME?", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new NameException())));
381    run("3 / #NULL!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new NullException())));
382    run("3 / #N/A", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new NAException())));
383    run("3 / #REF!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new RefException())));
384    run("3 / #ERROR!", new BinaryOperationNode(new NumberNode(3.0), "/", new ErrorNode(new ParseException())));
385  }
386
387  @Test
388  public void testPower() {
389    run("-2^3", new BinaryOperationNode(new UnaryMinusOperationNode(new NumberNode(2.0)), "^", new NumberNode(3.0)));
390    run("(-2)^3", new BinaryOperationNode(new UnaryMinusOperationNode(new NumberNode(2.0)), "^", new NumberNode(3.0)));
391    run("-(2)^3", new BinaryOperationNode(new UnaryMinusOperationNode(new NumberNode(2.0)), "^", new NumberNode(3.0)));
392    run("--(-2)^3", new BinaryOperationNode(new UnaryMinusOperationNode(new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(2.0)))), "^", new NumberNode(3.0)));
393    run("11 -2^3", new BinaryOperationNode(new NumberNode(11.0), "-", new BinaryOperationNode(new NumberNode(2.0), "^", new NumberNode(3.0))));
394    run("(-2) ^ 3", new BinaryOperationNode(new UnaryMinusOperationNode(new NumberNode(2.0)), "^", new NumberNode(3.0)));
395    run("(-3) ^ 2", new BinaryOperationNode(new UnaryMinusOperationNode(new NumberNode(3.0)), "^", new NumberNode(2.0)));
396  }
397
398  @Test
399  public void testConcatOperation() {
400    run("1 & 1", new BinaryOperationNode(new NumberNode(1.0), "&", new NumberNode(1.0)));
401    run("\"One\" & 0", new BinaryOperationNode(new TextNode("One"), "&", new NumberNode(0.0)));
402  }
403
404
405  @Test
406  public void testConcatOperation_withErrors() {
407    run("1 & #DIV/0!", new BinaryOperationNode(new NumberNode(1.0), "&", new ErrorNode(new DivException())));
408  }
409
410  @Test
411  public void testUnaryMinusOperation() {
412    run("-22", new UnaryMinusOperationNode(new NumberNode(22.0)));
413    run("-\"Thing\"", new UnaryMinusOperationNode(new TextNode("Thing")));
414    run("--22", new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(22.0))));
415    run("---22", new UnaryMinusOperationNode(new UnaryMinusOperationNode(new UnaryMinusOperationNode(new NumberNode(22.0)))));
416    run("-(2 * 4)", new UnaryMinusOperationNode(new BinaryOperationNode(new NumberNode(2.0), "*", new NumberNode(4.0))));
417  }
418
419  @Test
420  public void testUnaryPercentOperation() {
421    run("88%", new UnaryPercentOperationNode(new NumberNode(88.0)));
422    run("(8 * 8)%", new UnaryPercentOperationNode(new BinaryOperationNode(new NumberNode(8.0), "*", new NumberNode(8.0))));
423  }
424
425  @Test
426  public void testUnaryPercentOperation_parseErrorBecauseOfRepetitivePercent() {
427    run("88%%", new ErrorNode(new ParseException()));
428    run("88%%%", new ErrorNode(new ParseException()));
429    run("88% % % %", new ErrorNode(new ParseException()));
430  }
431
432  @Test
433  public void testCellRange_singleCell() {
434    run("A4", new RangeNode(CellQuery.builder()
435        .columnsBetween(0, 0)
436        .rowsBetween(3, 3)
437        .build()));
438  }
439
440  @Test
441  public void testCellRange_biCell() {
442    run("A1:B2", new RangeNode(CellQuery.builder()
443        .columnsBetween(0, 1)
444        .rowsBetween(0, 1)
445        .build()));
446    run("B2:A1", new RangeNode(CellQuery.builder()
447        .columnsBetween(0, 1)
448        .rowsBetween(0, 1)
449        .build()));
450    run("B2:D4", new RangeNode(CellQuery.builder()
451        .columnsBetween(1, 3)
452        .rowsBetween(1, 3)
453        .build()));
454    run("B4:B4", new RangeNode(CellQuery.builder()
455        .columnsBetween(1, 1)
456        .rowsBetween(3, 3)
457        .build()));
458    run("B3:B4", new RangeNode(CellQuery.builder()
459        .columnsBetween(1, 1)
460        .rowsBetween(2, 3)
461        .build()));
462    run("B4:B3", new RangeNode(CellQuery.builder()
463        .columnsBetween(1, 1)
464        .rowsBetween(2, 3)
465        .build()));
466  }
467
468  @Test
469  public void testCellRange_multiColumn() {
470    run("A:C", new RangeNode(CellQuery.builder()
471        .columnsBetween(0, 2)
472        .openRowsStartingAtZero()
473        .build()));
474    run("C:A", new RangeNode(CellQuery.builder()
475        .columnsBetween(0, 2)
476        .openRowsStartingAtZero()
477        .build()));
478    run("B:M", new RangeNode(CellQuery.builder()
479        .columnsBetween(1, 12)
480        .openRowsStartingAtZero()
481        .build()));
482    run("M:B", new RangeNode(CellQuery.builder()
483        .columnsBetween(1, 12)
484        .openRowsStartingAtZero()
485        .build()));
486    run("M:M", new RangeNode(CellQuery.builder()
487        .columnsBetween(12, 12)
488        .openRowsStartingAtZero()
489        .build()));
490  }
491
492  @Test
493  public void testCellRange_multiRow() {
494    run("3:7", new RangeNode(CellQuery.builder()
495        .rowsBetween(2, 6)
496        .openColumnsStartingAtZero()
497        .build()));
498    run("1:100", new RangeNode(CellQuery.builder()
499        .rowsBetween(0, 99)
500        .openColumnsStartingAtZero()
501        .build()));
502    run("7:3", new RangeNode(CellQuery.builder()
503        .rowsBetween(2, 6)
504        .openColumnsStartingAtZero()
505        .build()));
506    run("100:1", new RangeNode(CellQuery.builder()
507        .rowsBetween(0, 99)
508        .openColumnsStartingAtZero()
509        .build()));
510  }
511
512  @Test
513  public void testCellRange_multiRowStartColumn() {
514    run("D3:7", new RangeNode(CellQuery.builder()
515        .rowsBetween(2, 6)
516        .openColumnsStartingAt("D")
517        .build()));
518    run("D7:3", new RangeNode(CellQuery.builder()
519        .rowsBetween(2, 6)
520        .openColumnsStartingAt("D")
521        .build()));
522    run("E5:10", new RangeNode(CellQuery.builder()
523        .rowsBetween(4, 9)
524        .openColumnsStartingAt(4)
525        .build()));
526    run("D7:7", new RangeNode(CellQuery.builder()
527        .rowsBetween(6, 6)
528        .openColumnsStartingAt("D")
529        .build()));
530  }
531
532  @Test
533  public void testCellRange_multiColumnStartRow() {
534    run("B3:D", new RangeNode(CellQuery.builder()
535        .columnsBetween(1, 3)
536        .openRowsStartingAt(2)
537        .build()));
538    run("D3:B", new RangeNode(CellQuery.builder()
539        .columnsBetween(1, 3)
540        .openRowsStartingAt(2)
541        .build()));
542    run("B33:B", new RangeNode(CellQuery.builder()
543        .columnsBetween(1, 1)
544        .openRowsStartingAt(32)
545        .build()));
546  }
547
548  @Test
549  public void testCellRange_multiCell() {
550    run("C5:B4:D2:G7:L11", new RangeQueryNode(ImmutableList.of(
551        new RangeNode(CellQuery.builder()
552            .columnsBetween(1, 2)
553            .rowsBetween(3, 4)
554            .build()),
555        new RangeNode(CellQuery.builder()
556            .columnsBetween(3, 6)
557            .rowsBetween(1, 6)
558            .build()),
559        new RangeNode(CellQuery.builder()
560            .columnsBetween(11, 11)
561            .rowsBetween(10, 10)
562            .build())
563    )));
564    run("A4:B5:C6:D7:E8:F9", new RangeQueryNode(ImmutableList.of(
565        new RangeNode(CellQuery.builder()
566            .columnsBetween(0, 1)
567            .rowsBetween(3, 4)
568            .build()),
569        new RangeNode(CellQuery.builder()
570            .columnsBetween(2, 3)
571            .rowsBetween(5, 6)
572            .build()),
573        new RangeNode(CellQuery.builder()
574            .columnsBetween(4, 5)
575            .rowsBetween(7, 8)
576            .build())
577    )));
578    run("SUM(A1:B2:C3:D4:E5:F6)", new FormulaNode(FormulaName.SUM.toString(), ImmutableList.of(new RangeQueryNode(ImmutableList.of(
579        new RangeNode(CellQuery.builder()
580            .columnsBetween(0, 1)
581            .rowsBetween(0, 1)
582            .build()),
583        new RangeNode(CellQuery.builder()
584            .columnsBetween(2, 3)
585            .rowsBetween(2, 3)
586            .build()),
587        new RangeNode(CellQuery.builder()
588            .columnsBetween(4, 5)
589            .rowsBetween(4, 5)
590            .build())
591    )))));
592  }
593
594  private Node transpile(String value) {
595    try {
596      CommonTokenStream tokens = new CommonTokenStream(new F7Lexer(CharStreams.fromStream(
597          new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)));
598      try {
599        F7Parser parser = new F7Parser(tokens);
600        parser.removeErrorListeners();
601        parser.addErrorListener(new ParseErrorListener());
602        return visitor.visit(parser.start().block());
603      } catch (ParseCancellationException parseException) {
604        return new ErrorNode(new ParseException("Parse error"));
605      } catch (F7Exception f7Exception) {
606        return new ErrorNode(f7Exception);
607      }
608    } catch (IOException io) {
609      return new ErrorNode(new ParseException("Parse error"));
610    }
611  }
612
613  protected void run(String in, Node out) {
614    assertThat(transpile(in)).isEqualTo(out);
615  }
616}