| /******************************************************************************* |
| * You may amend and distribute as you like, but don't remove this header! |
| * |
| * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. |
| * See http://www.codeplex.com/EPPlus for details. |
| * |
| * Copyright (C) 2011 Jan Källman |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU Lesser General Public License for more details. |
| * |
| * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php |
| * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html |
| * |
| * All code and executables are provided "as is" with no warranty either express or implied. |
| * The author accepts no liability for any damage or loss of business that this product may cause. |
| * |
| * Code change notes: |
| * |
| * Author Change Date |
| * ****************************************************************************** |
| * Mats Alm Added 2013-03-01 (Prior file history on https://github.com/swmal/ExcelFormulaParser) |
| *******************************************************************************/ |
| |
| using System.Collections.Generic; |
| using System.Linq; |
| using OfficeOpenXml.FormulaParsing.Excel.Operators; |
| using OfficeOpenXml.FormulaParsing.Exceptions; |
| using OfficeOpenXml.FormulaParsing.LexicalAnalysis; |
| |
| namespace OfficeOpenXml.FormulaParsing.ExpressionGraph; |
| |
| public class ExpressionGraphBuilder : IExpressionGraphBuilder { |
| private readonly ExpressionGraph _graph = new(); |
| private readonly IExpressionFactory _expressionFactory; |
| private readonly ParsingContext _parsingContext; |
| private int _tokenIndex; |
| private bool _negateNextExpression; |
| |
| public ExpressionGraphBuilder(ExcelDataProvider excelDataProvider, ParsingContext parsingContext) |
| : this(new ExpressionFactory(excelDataProvider, parsingContext), parsingContext) {} |
| |
| public ExpressionGraphBuilder( |
| IExpressionFactory expressionFactory, |
| ParsingContext parsingContext) { |
| _expressionFactory = expressionFactory; |
| _parsingContext = parsingContext; |
| } |
| |
| public ExpressionGraph Build(IEnumerable<Token> tokens) { |
| _tokenIndex = 0; |
| _graph.Reset(); |
| var tokensArr = tokens != null ? tokens.ToArray() : new Token[0]; |
| BuildUp(tokensArr, null); |
| return _graph; |
| } |
| |
| private void BuildUp(Token[] tokens, Expression parent) { |
| while (_tokenIndex < tokens.Length) { |
| var token = tokens[_tokenIndex]; |
| if (token.TokenType == TokenType.Operator |
| && OperatorsDict.Instance.TryGetValue(token.Value, out var op)) { |
| SetOperatorOnExpression(parent, op); |
| } else if (token.TokenType == TokenType.Function) { |
| BuildFunctionExpression(tokens, parent, token.Value); |
| } else if (token.TokenType == TokenType.OpeningEnumerable) { |
| _tokenIndex++; |
| BuildEnumerableExpression(tokens, parent); |
| } else if (token.TokenType == TokenType.OpeningParenthesis) { |
| _tokenIndex++; |
| BuildGroupExpression(tokens, parent); |
| //if (parent is FunctionExpression) |
| //{ |
| // return; |
| //} |
| } else if (token.TokenType == TokenType.ClosingParenthesis |
| || token.TokenType == TokenType.ClosingEnumerable) { |
| break; |
| } else if (token.TokenType == TokenType.Negator) { |
| _negateNextExpression = true; |
| } else if (token.TokenType == TokenType.Percent) { |
| SetOperatorOnExpression(parent, Operator.Percent); |
| if (parent == null) { |
| _graph.Add(ConstantExpressions.Percent); |
| } else { |
| parent.AddChild(ConstantExpressions.Percent); |
| } |
| } else { |
| CreateAndAppendExpression(ref parent, token); |
| } |
| _tokenIndex++; |
| } |
| } |
| |
| private void BuildEnumerableExpression(Token[] tokens, Expression parent) { |
| if (parent == null) { |
| _graph.Add(new EnumerableExpression()); |
| BuildUp(tokens, _graph.Current); |
| } else { |
| var enumerableExpression = new EnumerableExpression(); |
| parent.AddChild(enumerableExpression); |
| BuildUp(tokens, enumerableExpression); |
| } |
| } |
| |
| private void CreateAndAppendExpression(ref Expression parent, Token token) { |
| if (IsWaste(token)) { |
| return; |
| } |
| if (parent != null |
| && (token.TokenType == TokenType.Comma || token.TokenType == TokenType.SemiColon)) { |
| parent = parent.PrepareForNextChild(); |
| return; |
| } |
| if (_negateNextExpression) { |
| token.Negate(); |
| _negateNextExpression = false; |
| } |
| var expression = _expressionFactory.Create(token); |
| if (parent == null) { |
| _graph.Add(expression); |
| } else { |
| parent.AddChild(expression); |
| } |
| } |
| |
| private bool IsWaste(Token token) { |
| if (token.TokenType == TokenType.String) { |
| return true; |
| } |
| return false; |
| } |
| |
| private void BuildFunctionExpression(Token[] tokens, Expression parent, string funcName) { |
| if (parent == null) { |
| _graph.Add(new FunctionExpression(funcName, _parsingContext, _negateNextExpression)); |
| _negateNextExpression = false; |
| HandleFunctionArguments(tokens, _graph.Current); |
| } else { |
| var func = new FunctionExpression(funcName, _parsingContext, _negateNextExpression); |
| _negateNextExpression = false; |
| parent.AddChild(func); |
| HandleFunctionArguments(tokens, func); |
| } |
| } |
| |
| private void HandleFunctionArguments(Token[] tokens, Expression function) { |
| _tokenIndex++; |
| var token = tokens.ElementAt(_tokenIndex); |
| if (token.TokenType != TokenType.OpeningParenthesis) { |
| throw new ExcelErrorValueException(eErrorType.Value); |
| } |
| _tokenIndex++; |
| BuildUp(tokens, function.Children.First()); |
| } |
| |
| private void BuildGroupExpression(Token[] tokens, Expression parent) { |
| if (parent == null) { |
| _graph.Add(new GroupExpression(_negateNextExpression)); |
| _negateNextExpression = false; |
| BuildUp(tokens, _graph.Current); |
| } else { |
| if (parent.IsGroupedExpression || parent is FunctionArgumentExpression) { |
| var newGroupExpression = new GroupExpression(_negateNextExpression); |
| _negateNextExpression = false; |
| parent.AddChild(newGroupExpression); |
| BuildUp(tokens, newGroupExpression); |
| } |
| BuildUp(tokens, parent); |
| } |
| } |
| |
| private void SetOperatorOnExpression(Expression parent, IOperator op) { |
| if (parent == null) { |
| _graph.Current.Operator = op; |
| } else { |
| Expression candidate; |
| if (parent is FunctionArgumentExpression) { |
| candidate = parent.Children.Last(); |
| } else { |
| candidate = parent.Children.Last(); |
| if (candidate is FunctionArgumentExpression) { |
| candidate = candidate.Children.Last(); |
| } |
| } |
| candidate.Operator = op; |
| } |
| } |
| } |