internal ColumnFormula(
     CellValueCalculator cellValueCalculator,
     FormulaExpressionsCompiler formulaExpressionsCompiler)
 {
     _cellCalculator             = formulaExpressionsCompiler.CompileCellValueCalculator(cellValueCalculator);
     _aggregatedValueCalculators = formulaExpressionsCompiler.CompileAggregatedValueCalculators(cellValueCalculator.AggregatedValuesCalculators);
     _expressionText             = cellValueCalculator.ToString();
 }
        public CellValueCalculator All(FormulaExpressionsCompiler compiler)
        {
            var precalculatedAggregatedValueKey = "all:" + CalculateExpression;
            var exactEnumerableType             = typeof(IEnumerable <>).MakeGenericType(ResultType);

            return(new CellValueCalculator(
                       exactEnumerableType,
                       GetPrecalculatedAggregatedValue(exactEnumerableType, compiler.Parameters, precalculatedAggregatedValueKey),
                       ImmutableList.Create(
                           KeyValuePair.Create(precalculatedAggregatedValueKey, compiler.CallEnumerableSelect(CalculateExpression, ResultType))),
                       canBeAggregated: true));
        }
        public ColumnFormulaBuilder(
            Type rowDataType,
            TryParse <Type> tryGetPropertyTypeByName,
            TryParse <IReadOnlyCollection <MethodInfo> > tryGetFunctionsByName)
        {
            var parameters = new FormulaParameters(rowDataType);

            _formulaCompiler = new FormulaExpressionsCompiler(parameters);

            var integerLiteral  = NumericLiteral(new TryParse <int>(int.TryParse));
            var decimalLiteral  = NumericLiteral(new TryParse <decimal>(decimal.TryParse));
            var dateTimeLiteral = ParsedQuotedLiteral(new TryParse <DateTime>(DateTime.TryParse));
            var stringLiteral   = QuotedLiteral.Select(Constant);
            var literal         = dateTimeLiteral.Or(stringLiteral).Or(integerLiteral).Or(decimalLiteral);

            var quotedPropertyName = from openingBracket in Lexem("[")
                                     from nameChars in Repeat(ch => ch != ']' && ch != '\r' && ch != '\n')
                                     from closingBraket in Lexem("]")
                                     select string.Join(
                string.Empty,
                nameChars
                .Where(ch => ch != ' ' && ch != '\t')
                .Select(ch => char.IsLetterOrDigit(ch) ? ch.ToString() : $"_char_{(int)ch}_"));

            var propertyName     = quotedPropertyName.Or(Identifier);
            var propertyAccessor = from name in propertyName
                                   from propertyType in name.Try(tryGetPropertyTypeByName, n => $"Unknown property: '{n}'").StopParsingIfFailed()
                                   select new CellValueCalculator(propertyType, Expression.Property(parameters.CurrentRow, name));

            var aggregatableExpression = CreateFormulaParser(literal, propertyAccessor, tryGetFunctionsByName, parameters);

            var aggregatedPropertyAccessor = from openingBracket in Lexem("[")
                                             from aggregationMethodText in Identifier
                                             from colon in Lexem(":")
                                             from aggregationMethod in aggregationMethodText.Try <AggregationMethod>(
                Enum.TryParse,
                m => $"Invalid aggregation method {m} specified: only '" +
                string.Join("', '", Enum.GetNames(typeof(AggregationMethod)) +
                            "' are supported")).StopParsingIfFailed()
                                             from calculator in aggregatableExpression
                                             from closingBraket in Lexem("]")
                                             select aggregationMethod == AggregationMethod.all
                                                ? calculator.All(_formulaCompiler)
                                                : calculator.FirstOrLast(aggregationMethod, _formulaCompiler);

            _formulaTextParser = CreateFormulaParser(
                literal,
                aggregatedPropertyAccessor.Or(propertyAccessor),
                tryGetFunctionsByName,
                parameters);
        }
        public CellValueCalculator FirstOrLast(AggregationMethod aggregationMethod, FormulaExpressionsCompiler compiler)
        {
            var allRowsCalculator = All(compiler);
            var key = $"{aggregationMethod}:{CalculateExpression}";

            return(new CellValueCalculator(
                       ResultType,
                       GetPrecalculatedAggregatedValue(ResultType, compiler.Parameters, key),
                       allRowsCalculator.AggregatedValuesCalculators.Insert(
                           0,
                           KeyValuePair.Create(
                               key,
                               compiler.CallEnumerableFirstOrLast(
                                   allRowsCalculator.CalculateExpression,
                                   aggregationMethod,
                                   ResultType))),
                       canBeAggregated: true));
        }