DsExcel provides GrapeCity.Documents.Excel.Expressions library which allows you to parse formula expressions. The formula expressions are exposed at semantic model level so that you can create, visit and modify the formulas by using syntax tree. The FormulaSyntaxTree class represents a formula and is the entry point for formula expressions API.
The syntax tree represents semantic model of formulas. The Parse method of FormulaSyntaxTree class can be used to get syntax tree from text. However, the text should not start with "=" and should not be surrounded with "{= }". The Root property of FormulaSyntaxTree class can be used to get the root element of syntax tree. An empty syntax tree can be created by using FormulaSyntaxTree constructor.
Refer to the following example code to generate a formula with syntax tree.
C# |
Copy Code |
---|---|
//create a new workbook var workbook = new GrapeCity.Documents.Excel.Workbook(); // Build syntax tree var multiply = new OperatorNode(OperatorKind.Multiply); var a1 = new Reference { Row = 0, Column = 0 }; var a2 = new Reference { Row = 1, Column = 0 }; multiply.Children.Add(new ReferenceNode(a1)); multiply.Children.Add(new ReferenceNode(a2)); var tree = new FormulaSyntaxTree { Root = multiply }; // Generates A1*A2 workbook.ActiveSheet.Range["A1"].Value = "'=" + tree.ToString(); //save to an excel file workbook.Save("generateformula.xlsx"); |
The SyntaxNode class represents a node in the syntax tree. The Children property can be used to get children of a non-terminal node. If the type of syntax node is a terminal node, then this collection is read-only. Similar to syntax tree, the Parse method of SyntaxNode class can be used get syntax node from text. An empty syntax node can be created by using SyntaxNode constructor.
Refer to the following example code to parse formula, modify the syntax tree by replacing the child of syntax node and convert it to a string.
C# |
Copy Code |
---|---|
//create a new workbook var workbook = new GrapeCity.Documents.Excel.Workbook(); var originalFormula = @"LET(AppUpTime,NOW()-DATE(2020,4,17)+366, YEAR(AppUpTime)-1900-1 & "" years"""; // Replace NOW() with fixed date // Get syntax tree var syntaxTree = FormulaSyntaxTree.Parse(originalFormula); // Find var nowFunction = new FunctionNode("NOW"); // Replacement var valentine2021 = new FunctionNode("DATE"); valentine2021.Children.Add(new NumberNode(2021)); valentine2021.Children.Add(new NumberNode(2)); valentine2021.Children.Add(new NumberNode(14)); // Find and replace void replaceNode(SyntaxNode lookIn, SyntaxNode find, SyntaxNode replacement) { var children = lookIn.Children; for (var i = 0; i < children.Count; i++) { var child = children[i]; if (child.Equals(find)) { children[i] = replacement; } else { replaceNode(child, find, replacement); } } } replaceNode(syntaxTree.Root, nowFunction, valentine2021); // Output original and replaced var sheet1 = workbook.ActiveSheet; sheet1.Range["A1"].Value = "Original"; sheet1.Range["A2"].Value = "'=" + originalFormula.ToString(); sheet1.Range["A3"].Value = "Replaced"; sheet1.Range["A4"].Value = "'=" + syntaxTree.ToString(); // Arrange sheet1.Range["A:A"].EntireColumn.AutoFit(); //save to an excel file workbook.Save("modifyformula.xlsx"); |
The ParseContext and UnparseContext classes contain options for converting strings to FormulaSyntaxTree and vice versa respectively. The BaseRow and BaseColumn properties can be used to specify the location of formula and IsR1C1 property can be used to specify the reference style.
Refer to the following example code to specify base row, base column and R1C1 reference style in options.
C# |
Copy Code |
---|---|
//create a new workbook var workbook = new GrapeCity.Documents.Excel.Workbook(); // Convert R1C1 to A1 var r1c1Formula = "R1C:R8C[4]*9"; // At H2 var formulaRow = 1; var formulaColumn = 7; // Parse var r1c1Option = new ParseContext { IsR1C1 = true }; var syntaxTree = FormulaSyntaxTree.Parse(r1c1Formula, r1c1Option); // ToString // Specify BaseRow and BaseColumn in a1Option. // Because row and column are absolute index in A1 format. var a1Option = new UnParseContext { BaseColumn = formulaColumn, BaseRow = formulaRow }; var converted = syntaxTree.ToString(a1Option); // Output var sheet1 = workbook.ActiveSheet; sheet1.Range["A1"].Value = "Original formula (at H2)"; sheet1.Range["A2"].Value = "'=" + r1c1Formula.ToString(); sheet1.Range["A3"].Value = "Converted"; sheet1.Range["A4"].Value = "'=" + converted.ToString(); // Arrange sheet1.Range["A:A"].EntireColumn.AutoFit(); //save to an excel file workbook.Save("parseandformatoptions.xlsx"); |
Refer to the following example code to parse formula and then print the syntax tree.
C# |
Copy Code |
---|---|
//create a new workbook var workbook = new GrapeCity.Documents.Excel.Workbook(); const string Formula = "RAND()>0.5+0.001"; // Get syntax tree var syntaxTree = FormulaSyntaxTree.Parse(Formula); // Flatten nodes var displayItems = new List<(string TypeName, int IndentLevel, string Content)>(); void flatten(SyntaxNode node, int level) { displayItems.Add((node.GetType().Name, level, node.ToString())); foreach (var child in node.Children) { flatten(child, level + 1); } } flatten(syntaxTree.Root, 0); // Output var sheet1 = workbook.Worksheets["Sheet1"]; sheet1.ShowRowOutline = false; sheet1.OutlineColumn.ColumnIndex = 1; sheet1.OutlineColumn.CollapseIndicator = new ImageSource(GetResourceStream("decreaseIndicator.png"), ImageType.PNG); sheet1.OutlineColumn.ExpandIndicator = new ImageSource(GetResourceStream("increaseIndicator.png"), ImageType.PNG); // Header sheet1.Range["A1"].Value = "Formula"; sheet1.Range["B1"].Value = "Syntax node"; sheet1.Range["C1"].Value = "Part"; // Values sheet1.Range["A2"].Value = "'=" + Formula; for (var i = 0; i < displayItems.Count; i++) { var item = displayItems[i]; var text = "'" + item.TypeName; sheet1.Range[i + 1, 1].Value = text; sheet1.Range[i + 1, 1].IndentLevel = item.IndentLevel; sheet1.Range[i + 1, 2].Value = "'" + item.Content; } // Arrange sheet1.Range["A:C"].EntireColumn.AutoFit(); sheet1.Range["B:B"].EntireColumn.ColumnWidthInPixel += 40; //save to an excel file workbook.Save("printformulasyntax.xlsx"); |
The ReferenceNode class represents a reference expression in the syntax tree.
The Reference class represents a range reference in formula. The reference can be across a cell, range, cross-worksheet, cross-worksheet 3D or cross-workbook.
The WorkbookReference class is an immutable class which represents a reference to an external workbook by name or local file path. If the workbook reference is from file path, the BaseDirectory property contains the directory information.
Note: The path separator is platform specific and affects the result of workbook reference. For example, 'C:\Temp\[Book1.xlsx]Sheet1'!A2 is a valid reference on Windows but invalid on Linux.
For example, the parsed object for a workbook referenced by name: [Book1]Sheet1!A2 will look like below:
C# |
Copy Code |
---|---|
new ReferenceNode( new Reference { Workbook=WorkbookReference.FromName("Book1"), WorksheetName="Sheet1", Row=1, Column=0 } ); |
The parsed object for a workbook referenced by file path: 'C:\Temp\[Book1.xlsx]Sheet1'!A2 will look like below:
C# |
Copy Code |
---|---|
new ReferenceNode( new Reference { Workbook=WorkbookReference.FromFilePath(@"C:\Temp\Book1.xlsx"), WorksheetName="Sheet1", Row=1, Column=0 } ) |
The parsed object for a workbook referenced from a web URI will look like below:
C# |
Copy Code |
---|---|
var worbookRef = WorkbookReference.FromUri( "https://somesite.com/files/sample.xlsx"); var model = new ReferenceNode( new Reference { Workbook = worbookRef, WorksheetName = "sheet1", Row = 8, Column = 1, } ); |
The FunctionNode class represents a function invocation expression in the syntax tree.
For example, the parsed object for Excel formula: COUNTIF(A:A,"*?") will look like below:
C# |
Copy Code |
---|---|
new FunctionNode("COUNTIF") { Children = { new ReferenceNode( new Reference { HasRow=false, LastColumn=0 } ), new TextNode("*?") } }; |
The NameNode class represents the name in a syntax tree.
For example, the parsed object for a workbook referenced by name: '[BuildingSales]JanIn2021'!RawData will look like below:
C# |
Copy Code |
---|---|
new NameNode("RawData", WorkbookReference.FromName("BuildingSales"), "JanIn2021", null); |
The parsed object for a workbook referenced by file path: 'E:\[BuildingSales.xlsx]JanIn2021'!RawData will look like below:
C# |
Copy Code |
---|---|
new NameNode("RawData", WorkbookReference.FromFilePath(@"E:\BuildingSales.xlsx"), "JanIn2021", null); |
The ErrorNode class represents an error literal node in the syntax tree. The following error types are not supported:
The ArrayNode class represents an array literal in the syntax tree. There are following array constraints:
To know more about other classes, please refer GrapeCity.Documents.Excel.Expressions API documentation.