private void ResolveVariableValue(object sender, ResolveVariableValueEventArgs e)
        {
            // resolve variable from formulas model.
            var recursiveFormula = (from p in _model.Formulas where p.Name == e.VariableName select p).SingleOrDefault();

            if (recursiveFormula != null)
            {
                // this variable is a formula. Recurvsively execute this formula.
                e.VariableValue = FormulaExpressionContext.Evaluate(this, ref _context, ref recursiveFormula, ref _parameters, OnQuestion, ref _model).Value.Infer();
                //var context = new FormulaExpressionContext(ref _model, ref _parameters, recursiveFormula, OnQuestion);
                //e.VariableValue = context.Evaluate().Value.Infer();
            }
            else
            {
                if (_parameters.Find(p => p.Name == e.VariableName) != null)
                {
                    return;
                }
                // this variable is an input variable. Provide a question to the client for an answer.
                if (OnQuestion == null)
                {
                    throw new Exception($"In order to evaluate variable '${e.VariableName}', you need to provide a delegate callback to the client for providing an answer");
                }
                OnQuestion(this, new QuestionArgs("", new ParametersCollection()
                {
                    new Parameter(e.VariableName, null, TypeInference.Infer(e.VariableType).Type, ref _model)
                }));
            }
        }
Exemplo n.º 2
0
        public void EvaluateFormulaWithoutQA(ref IParametersCollection parameters, string formula)
        {
            void callback(FormulaExpressionContext sender, QuestionArgs args)
            {
                throw new Exception($"Can't resolve formula {formula} without answering question {args.Parameters[0].Name} first.");
            };

            var context = new FormulaExpressionContext(ref _model, ref parameters, GetFormula(formula), callback, this);

            context.Evaluate();
        }
Exemplo n.º 3
0
        public Table Match(string tableName, ref IParametersCollection parameters)
        {
            int          i = 0;
            int          nonSituationalTables = 0;
            List <Table> matchedTables        = new List <Table>();
            var          tables           = _model.GetTablesByName(tableName);
            ISituation   currentSituation = null;

            void callback(FormulaExpressionContext sender, QuestionArgs args)
            {
                throw new Exception($"{tableName} contains situational expression {currentSituation.Expression} that could not be resolved without answering a question: {args.Parameters[0].Name}. Situational tables should only contain resolveable parameters.");
            };
            foreach (var table in _model.GetTablesByName(tableName))
            {
                i++;
                if (!table.IsSituational)
                {
                    nonSituationalTables++;
                    if (nonSituationalTables > 1)
                    {
                        throw new Exception($"found multiple tables found for {tableName} that are non situational. Only one table is allowd to be non situational.");
                    }
                    if (!table.IsSituational && i != tables.Count())
                    {
                        throw new Exception($"Default table {tableName} should be specified after all situational tables.");
                    }

                    matchedTables.Add(table);
                    continue;
                }
                // evaluate situations for inclusivity (meaning one of the expressions should evaluate to true.
                foreach (var situation in table.Situations)
                {
                    currentSituation = situation;
                    var formula = new Formula(table.DebugInfo, "__temp__", new List <Function>()
                    {
                        new Function(table.DebugInfo, situation.Expression)
                    });
                    var context = new FormulaExpressionContext(ref _model, ref parameters, formula, callback, this);
                    var result  = context.Evaluate();
                    if (result.Type != TypeEnum.Boolean)
                    {
                        throw new Exception($"{tableName} contains situational expression {currentSituation.Expression} and should evaluate to a boolean, instead it resolved to a: {result.TypeAsString}.");
                    }
                    if ((bool)result.Value)
                    {
                        matchedTables.Add(table);
                    }
                }
            }
            if (matchedTables.Count == 0)
            {
                throw new Exception($"No table matches for {tableName}.");
            }
            if (matchedTables.Count > 1 && nonSituationalTables == 0)
            {
                throw new Exception($"Ambigous situation evaluation on tables named {tableName}.");
            }

            return(matchedTables[0]);
        }
Exemplo n.º 4
0
        private void ExecuteStep(ref IExecutionResult executionResult, ref IParametersCollection parameters, IStep step)
        {
            var questions = new ParametersCollection();

            if (executionResult is null)
            {
                throw new ArgumentNullException(nameof(executionResult));
            }

            if (parameters is null)
            {
                throw new ArgumentNullException(nameof(parameters));
            }

            executionResult.Stacktrace.Add(new FlowExecutionItem(step, null));


            // Prescedence is (correct order of evaluation)
            // #1 evaluate step variable (value Question) or evaluate situation (choice Question)
            // #2 evaluate formula
            // #3 evaluate break (recht)

            // Evaluate step variable if available
            // Ask a question for a direct input value from the client, if the parameter is unknown.
            // Note: in inference we assume the input value is of type double.
            // When used in combination with "recht", do not advance to the next step until recht is evaluated.
            // When used in combination with "formule", do not advance to the next step until the formule is evaluated.
            if (!string.IsNullOrEmpty(step.Value) && parameters.GetParameter(step.Value) == null)
            {
                if (QuestionCallback == null)
                {
                    throw new Exception($"In order to evaluate step variable  {step.Value}, you need to provide a delegate callback to the client for providing an answer");
                }
                questions.UpSert(new Parameter(step.Value, 0, TypeEnum.Double, ref _model));
                QuestionCallback(null, new QuestionArgs("", questions));
                // step variable has to be formulated as an input parameter by the client.
                throw new UnresolvedException($"Can't evaluate step variable {step.Value}.");
            }

            if (step.Choices != null)
            {
                bool answered = false;
                foreach (var choice in step.Choices)
                {
                    var situation = parameters.GetParameter(choice.Situation);
                    if (situation == null)
                    {
                        questions.UpSert(new Parameter(choice.Situation, false, TypeEnum.Boolean, ref _model));
                    }
                    else
                    {
                        if ((bool)situation.Value == true)
                        {
                            answered = true;
                        }
                    }
                }
                if (!answered)
                {
                    QuestionCallback(null, new QuestionArgs("", questions));
                    throw new UnresolvedException($"Choices {string.Join(',', step.Choices.Select(p => p.Situation))} can not evaluate further, before these are answered by the client.");
                }
            }

            if (!string.IsNullOrEmpty(step.Formula))
            {
                var context = new FormulaExpressionContext(ref _model, ref parameters, GetFormula(step.Formula), QuestionCallback, this);
                context.Evaluate();
            }

            // Evaluate recht if available.
            if (step.Break != null && !string.IsNullOrEmpty(step.Break.Expression))
            {
                var breakContext = new FormulaExpressionContext(
                    ref _model,
                    ref parameters,
                    new Formula(step.DebugInfo, "recht", new List <Function>()
                {
                    new Function(step.DebugInfo, step.Break.Expression)
                }),
                    QuestionCallback,
                    this);
                breakContext.Evaluate();
            }
            CheckForStopExecution(parameters, executionResult);
        }
Exemplo n.º 5
0
        private bool EvaluateSituation(ref IParametersCollection parameters, IStep step, out IStep stepActiveSituation)
        {
            if (parameters is null)
            {
                throw new ArgumentNullException(nameof(parameters));
            }

            stepActiveSituation = step.Clone();
            var match = false;
            // evaluate if the situation (condition) is appropiate, otherwise skip it.
            // not in input parameters. Evaluate the
            var parameter = parameters.GetParameter(step.Situation);

            if (step.Situation.Contains(','))
            {
                foreach (var inclusiveSituation in step.Situation.Split(',')
                         .Select(x => x.Trim())
                         .Where(x => !string.IsNullOrWhiteSpace(x))
                         .ToArray())
                {
                    parameter = parameters.GetParameter(inclusiveSituation);
                    if (parameter != null && parameter.Type == TypeEnum.Boolean)
                    {
                        if ((bool)parameter.Value == true)
                        {
                            stepActiveSituation             = step.Clone();
                            stepActiveSituation.Situation   = inclusiveSituation;
                            stepActiveSituation.SemanticKey = string.Join('.', stepActiveSituation.SemanticKey.Split('.').Take(3).ToArray()) + "." + inclusiveSituation;  // this is a hack, please refactor.
                        }
                        // seach in  content nodes for this parameter in typenum steps
                        //var s = ContentNodes.FindAll(p => p.Parameter.Type == TypeEnum.Step);
                        return(true);
                    }
                }
                throw new StepException($"Can't resolve any of the inclusive situations ${step.Situation}. One of these parameters must exist and be of boolean type.", step);
            }

            if (parameter == null)
            {
                // resolve parameter value from named formula.
                var formula = GetFormula(step.Situation);
                if (formula == null)
                {
                    throw new StepException($"can't resolve parameter '${parameter.Name}' to formula", step);
                }
                // execute the formula which adds the outcome to parameters.
                var context = new FormulaExpressionContext(ref _model, ref parameters, formula, QuestionCallback, this);
                var result  = context.Evaluate();
                //var result = Execute(ref context, ref formula, ref parameters, ref executionResult);
                if (result.Value.GetType() != typeof(bool))
                {
                    throw new StepException($"Expected situation to evalute to boolean value in worflow step {step.Name} {step.Description} but got value: {result.Value} of type {result.Value.GetType().Name} instead.", step);
                }
                match = (bool)result.Value;
            }
            else
            {
                if ((bool)parameter.Value.Infer())
                {
                    // execute this step.
                    match = true;
                }
            }
            return(match);
        }
        private static Parameter Evaluate(FormulaExpressionContext caller, ref ExpressionContext context, ref Formula formula, ref ParametersCollection parameters1, QuestionDelegate onQuestion, ref Model.Model model)
        {
            if (parameters1 is null)
            {
                throw new ArgumentNullException(nameof(parameters1));
            }

            IDynamicExpression e = null;

            if (!formula.IsSituational)
            {
                try
                {
                    e = context.CompileDynamic(formula.Functions[0].Expression);
                    var       result = e.Evaluate().Infer();
                    Parameter parameter;
                    parameter = new Parameter(formula.Name, result.Infer(), null, ref model);
                    parameter.IsCalculated = true;
                    parameters1.Add(parameter);
                    if (context.Variables.ContainsKey(parameter.Name))
                    {
                        context.Variables.Remove(parameter.Name);
                    }
                    context.Variables.Add(parameter.Name, parameter.Value.Infer());
                    return(parameter);
                }
                catch (ExpressionCompileException)
                {
                    // Function can not evaluate further, before a Question/Answer sequence is fullfilled by the client.
                    throw new UnresolvedException($"Function {formula.Functions[0].Expression} can not evaluate further, before a Question/Answer sequence is fullfilled by the client.");
                }
            }
            else
            {
                foreach (var function in formula.Functions)
                {
                    foreach (var item in parameters1)
                    {
                        if (item.Name == function.Situation && (bool)item.Value == true)
                        {
                            try
                            {
                                e = context.CompileDynamic(function.Expression);
                                var parameter = new Parameter(formula.Name, e.Evaluate().Infer(), null, ref model);
                                parameter.IsCalculated = true;
                                parameters1.Add(parameter);
                                if (context.Variables.ContainsKey(parameter.Name))
                                {
                                    context.Variables.Remove(parameter.Name);
                                }
                                context.Variables.Add(parameter.Name, parameter.Value.Infer());
                                return(parameter);
                            }
                            catch (ExpressionCompileException)
                            {
                                // Function can not evaluate further, before a Question/Answer sequence is fullfilled by the client.
                                throw new UnresolvedException($"Function {function.Expression} can not evaluate further, before a Question/Answer sequence is fullfilled by the client.");
                            }
                        }
                    }
                }
                StringBuilder situations = new StringBuilder();
                var           parameters = new ParametersCollection();
                foreach (var function in formula.Functions)
                {
                    situations.Append(function.Situation + ",");
                    var parameter = new Parameter(function.Situation, TypeInference.Infer(function).Type, null, ref model);
                    parameters.Add(parameter);
                }
                if (onQuestion == null)
                {
                    throw new Exception($"In order to evaluate variable one of the following situations:  {situations.ToString().Trim(',')}, you need to provide a delegate callback to the client for providing an answer");
                }
                onQuestion(caller, new QuestionArgs("", parameters));
                // situation has to be formulated as an input parameter by the client.
                throw new UnresolvedException($"Can't evaluate formula {formula.Name} for situation. Please specify one of the following situations: {situations.ToString().Trim(',')}.");
            }
        }