| /******************************************************************************* | |
| * 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; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using OfficeOpenXml.FormulaParsing.Excel.Operators; | |
| using OfficeOpenXml.FormulaParsing.Exceptions; | |
| using OfficeOpenXml.FormulaParsing.LexicalAnalysis; | |
| using OfficeOpenXml.FormulaParsing.Excel; | |
| using OfficeOpenXml.FormulaParsing; | |
| namespace OfficeOpenXml.FormulaParsing.ExpressionGraph | |
| { | |
| public class ExpressionGraphBuilder :IExpressionGraphBuilder | |
| { | |
| private readonly ExpressionGraph _graph = new ExpressionGraph(); | |
| private readonly IExpressionFactory _expressionFactory; | |
| private readonly ParsingContext _parsingContext; | |
| private int _tokenIndex = 0; | |
| 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]; | |
| IOperator op = null; | |
| if (token.TokenType == TokenType.Operator && OperatorsDict.Instance.TryGetValue(token.Value, out 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; | |
| } | |
| } | |
| } | |
| } |