name:
src/main/js/utils/Parsers.ts
-rw-r--r--
3062
1import { InvalidArgumentError } from "../common/errors/InvalidArgumentError";
2import { TranspilationVisitor } from "../execution/TranspilationVisitor";
3import { CellQuery } from "../models/nodes/CellQuery";
4import { ErrorNode } from "../models/nodes/ErrorNode";
5import { Node } from "../models/nodes/Node";
6import { NodeType } from "../models/nodes/NodeType";
7import { RangeNode } from "../models/nodes/RangeNode";
8import { Ref } from "../spreadsheet/Ref";
9import { AlphaUtils } from "./AlphaUtils";
10import * as antlr4 from "antlr4";
11import { F7Lexer } from "../antlr/F7Lexer";
12import { F7Parser } from "../antlr/F7Parser";
13
14export class Parsers {
15 /**
16 * Parse a range to node for execution.
17 * Eg: Sheet1!A1:B33
18 *
19 * @param range - range string
20 * @return range node.
21 */
22 static parseNamedRange(range: string): Node {
23 try {
24 const chars = new antlr4.InputStream(range);
25 const lexer: any = new F7Lexer(chars);
26 const tokens = new antlr4.CommonTokenStream(lexer);
27 const parser = new F7Parser(tokens) as any;
28 parser.buildParseTrees = true;
29 // This is the key line that is different from what tree we use when parsing normal F7 code.
30 const tree = parser.range();
31 return new TranspilationVisitor().visit(tree);
32 } catch (e) {
33 return new ErrorNode(e);
34 }
35 }
36
37 /**
38 * Parse a named range query in string format to a CellQuery, throwing an invalid argument error
39 * if we can't parse it.
40 * @param range to parse.
41 */
42 static parseNamedRangeToCellQuery(range: string): CellQuery {
43 const node = Parsers.parseNamedRange(range);
44 if (node.type === NodeType.Range) {
45 const rangeNode = <RangeNode>node;
46 return rangeNode.query;
47 }
48 throw new InvalidArgumentError(`Invalid range: ${range}`);
49 }
50
51 /**
52 * Parse formula code to a node for execution.
53 * Eg: SUM({1, 2, 3}, 888.22, "99.1")
54 *
55 * @param input - formula string.
56 * @return node
57 */
58 static parseFormulaCode(input: string): Node {
59 try {
60 const chars = new antlr4.InputStream(input);
61 const lexer: any = new F7Lexer(chars);
62 const tokens = new antlr4.CommonTokenStream(lexer);
63 const parser = new F7Parser(tokens) as any;
64 parser.buildParseTrees = true;
65 const tree = parser.start().block();
66 return new TranspilationVisitor().visit(tree);
67 } catch (e) {
68 return new ErrorNode(e);
69 }
70 }
71
72 /**
73 * Parse a reference pair, which is just two relative cell references separated by a semicolon.
74 * Eg: A1:A2
75 * @param ref string to parse.
76 */
77 static parseReferencePair(ref: string): Ref {
78 const REF_PATTERN = /^(\D+)(\d+):(\D+)(\d+)$/;
79 const matches = ref.match(REF_PATTERN);
80 if (matches) {
81 return {
82 startColumnIndex: AlphaUtils.columnToInt(matches[1]),
83 startRowIndex: AlphaUtils.rowToInt(matches[2]),
84 endColumnIndex: AlphaUtils.columnToInt(matches[3]),
85 endRowIndex: AlphaUtils.rowToInt(matches[4]),
86 };
87 }
88 throw new InvalidArgumentError(`Invalid ref: ${ref}`);
89 }
90}