blob: e025114c6c1c922cf7cc6018efd92a3b23a0a930 [file] [log] [blame]
/*******************************************************************************
* 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.ExpressionGraph;
using OfficeOpenXml.FormulaParsing;
using OfficeOpenXml.FormulaParsing.Excel.Operators;
using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
using OfficeOpenXml.FormulaParsing.Excel;
using OfficeOpenXml.FormulaParsing.Excel.Functions;
using OfficeOpenXml.FormulaParsing.ExcelUtilities;
using OfficeOpenXml.FormulaParsing.Logging;
using OfficeOpenXml.FormulaParsing.Utilities;
using System.Diagnostics;
using OfficeOpenXml.FormulaParsing.Exceptions;
namespace OfficeOpenXml.FormulaParsing
{
public class FormulaParser : IDisposable
{
private readonly ParsingContext _parsingContext;
private readonly ExcelDataProvider _excelDataProvider;
public FormulaParser(ExcelDataProvider excelDataProvider)
: this(excelDataProvider, ParsingContext.Create())
{
}
public FormulaParser(ExcelDataProvider excelDataProvider, ParsingContext parsingContext)
{
parsingContext.Parser = this;
parsingContext.ExcelDataProvider = excelDataProvider;
parsingContext.NameValueProvider = new EpplusNameValueProvider(excelDataProvider);
parsingContext.RangeAddressFactory = new RangeAddressFactory(excelDataProvider);
_parsingContext = parsingContext;
_excelDataProvider = excelDataProvider;
Configure(configuration =>
{
configuration
.SetLexer(new Lexer(_parsingContext.Configuration.FunctionRepository, _parsingContext.NameValueProvider))
.SetGraphBuilder(new ExpressionGraphBuilder(excelDataProvider, _parsingContext))
.SetExpresionCompiler(new ExpressionCompiler())
.FunctionRepository.LoadModule(new BuiltInFunctions());
});
}
public void Configure(Action<ParsingConfiguration> configMethod)
{
configMethod.Invoke(_parsingContext.Configuration);
_lexer = _parsingContext.Configuration.Lexer ?? _lexer;
_graphBuilder = _parsingContext.Configuration.GraphBuilder ?? _graphBuilder;
_compiler = _parsingContext.Configuration.ExpressionCompiler ?? _compiler;
}
private ILexer _lexer;
private IExpressionGraphBuilder _graphBuilder;
private IExpressionCompiler _compiler;
public ILexer Lexer { get { return _lexer; } }
public IEnumerable<string> FunctionNames { get { return _parsingContext.Configuration.FunctionRepository.FunctionNames; } }
internal virtual object Parse(string formula, RangeAddress rangeAddress)
{
using (var scope = _parsingContext.Scopes.NewScope(rangeAddress))
{
var tokens = _lexer.Tokenize(formula);
var graph = _graphBuilder.Build(tokens);
if (graph.Expressions.Count() == 0)
{
return null;
}
return _compiler.Compile(graph.Expressions).Result;
}
}
internal virtual object Parse(IEnumerable<Token> tokens, string worksheet, string address)
{
var rangeAddress = _parsingContext.RangeAddressFactory.Create(address);
using (var scope = _parsingContext.Scopes.NewScope(rangeAddress))
{
var graph = _graphBuilder.Build(tokens);
if (graph.Expressions.Count() == 0)
{
return null;
}
return _compiler.Compile(graph.Expressions).Result;
}
}
internal virtual object ParseCell(IEnumerable<Token> tokens, string worksheet, int row, int column)
{
var rangeAddress = _parsingContext.RangeAddressFactory.Create(worksheet, column, row);
using (var scope = _parsingContext.Scopes.NewScope(rangeAddress))
{
// _parsingContext.Dependencies.AddFormulaScope(scope);
var graph = _graphBuilder.Build(tokens);
if (graph.Expressions.Count() == 0)
{
return 0d;
}
try
{
var compileResult = _compiler.Compile(graph.Expressions);
// quick solution for the fact that an excelrange can be returned.
var rangeInfo = compileResult.Result as ExcelDataProvider.IRangeInfo;
if (rangeInfo == null)
{
return compileResult.Result ?? 0d;
}
else
{
if (rangeInfo.IsEmpty)
{
return 0d;
}
if (!rangeInfo.IsMulti)
{
return rangeInfo.First().Value ?? 0d;
}
// ok to return multicell if it is a workbook scoped name.
if (string.IsNullOrEmpty(worksheet))
{
return rangeInfo;
}
if (_parsingContext.Debug)
{
var msg = string.Format("A range with multiple cell was returned at row {0}, column {1}",
row, column);
_parsingContext.Configuration.Logger.Log(_parsingContext, msg);
}
return ExcelErrorValue.Create(eErrorType.Value);
}
}
catch(ExcelErrorValueException ex)
{
if (_parsingContext.Debug)
{
_parsingContext.Configuration.Logger.Log(_parsingContext, ex);
}
return ex.ErrorValue;
}
}
}
public virtual object Parse(string formula, string address)
{
return Parse(formula, _parsingContext.RangeAddressFactory.Create(address));
}
public virtual object Parse(string formula)
{
return Parse(formula, RangeAddress.Empty);
}
public virtual object ParseAt(string address)
{
Require.That(address).Named("address").IsNotNullOrEmpty();
var rangeAddress = _parsingContext.RangeAddressFactory.Create(address);
return ParseAt(rangeAddress.Worksheet, rangeAddress.FromRow, rangeAddress.FromCol);
}
public virtual object ParseAt(string worksheetName, int row, int col)
{
var f = _excelDataProvider.GetRangeFormula(worksheetName, row, col);
if (string.IsNullOrEmpty(f))
{
return _excelDataProvider.GetRangeValue(worksheetName, row, col);
}
else
{
return Parse(f, _parsingContext.RangeAddressFactory.Create(worksheetName,col,row));
}
//var dataItem = _excelDataProvider.GetRangeValues(address).FirstOrDefault();
//if (dataItem == null /*|| (dataItem.Value == null && dataItem.Formula == null)*/) return null;
//if (!string.IsNullOrEmpty(dataItem.Formula))
//{
// return Parse(dataItem.Formula, _parsingContext.RangeAddressFactory.Create(address));
//}
//return Parse(dataItem.Value.ToString(), _parsingContext.RangeAddressFactory.Create(address));
}
internal void InitNewCalc()
{
if(_excelDataProvider!=null)
{
_excelDataProvider.Reset();
}
}
// Praveen's Formula Parser
public ExpressionGraph.ExpressionGraph ParseToGraph(string formula)
{
using (var scope = _parsingContext.Scopes.NewScope(RangeAddress.Empty))
{
var tokens = _lexer.Tokenize(formula);
var graph = _graphBuilder.Build(tokens);
return graph;
}
}
public IFormulaParserLogger Logger
{
get { return _parsingContext.Configuration.Logger; }
}
public void Dispose()
{
if (_parsingContext.Debug)
{
_parsingContext.Configuration.Logger.Dispose();
}
}
}
}