Exemple #1
0
        public static IEntityInstance Evaluated(this INode node, ComputationContext ctx, EvaluationCall evalCall)
        {
            var evaluable = node as IEvaluable;

            if (evaluable != null && evaluable.IsComputed)
            {
                return(evaluable.Evaluation?.Components ?? Environment.JokerInstance);
            }

            if (!ctx.AddVisited(node))
            {
                if (evaluable != null)
                {
                    if (!evaluable.IsComputed)
                    {
                        ctx.AddError(ErrorCode.CircularReference, node);
                    }
                }

                return(evaluable?.Evaluation?.Components ?? Environment.JokerInstance);
            }

            // todo: this is hacky, redesign this
            // some evaluation jumps out from their natural scope (like function) and evaluate "external" fields
            // to prevent keeping local names in this external context we added flag to mark the natural, nested call
            // or cross jump, in the latter case we reset the local names registry
            if (evalCall == EvaluationCall.AdHocCrossJump)
            {
                ctx.EvalLocalNames = null;
            }

            INameRegistryExtension.EnterNode(node, ref ctx.EvalLocalNames, () => new NameRegistry(ctx.Env.Options.ScopeShadowing));

            {
                var bindable = node as ILocalBindable;
                if (bindable != null && bindable.Name != null)
                {
                    // hackerish exception for variables being transformed as functor fields
                    if (bindable.Owner != null)
                    {
                        if (ctx.EvalLocalNames != null)
                        {
                            if (!ctx.EvalLocalNames.Add(bindable))
                            {
                                ctx.AddError(ErrorCode.NameAlreadyExists, bindable.Name);
                            }
                            else if (bindable.Name.Name == NameFactory.RecurFunctionName ||
                                     bindable.Name.Name == NameFactory.BaseVariableName ||
                                     bindable.Name.Name == NameFactory.SuperFunctionName)
                            {
                                ctx.AddError(ErrorCode.ReservedName, bindable.Name);
                            }
                        }
                    }
                }
            }

            if (evaluable == null || !evaluable.IsComputed)
            {
                if (node is ICustomComputable custom)
                {
                    custom.CustomEvaluate(ctx);
                }
                else
                {
                    node.ChildrenNodes.ForEach(it => Evaluated(it, ctx, EvaluationCall.Nested));
                    (node as IComputable)?.Evaluate(ctx);
                }
            }

            if (node is IScope && ctx.EvalLocalNames != null)
            {
                foreach (LocalInfo bindable_info in ctx.EvalLocalNames.RemoveLayer())
                {
                    if (bindable_info.Bindable is VariableDeclaration decl)
                    {
                        if (!bindable_info.Read)
                        {
                            ctx.AddError(ErrorCode.BindableNotUsed, decl.Name);
                        }
                    }
                    else if (!bindable_info.Used)
                    {
                        // do not report regular variables here, because we have to make difference between
                        // reading and assigning, loop label does not have such distinction
                        // and function parameter is always assigned
                        if (bindable_info.Bindable is IAnchor ||
                            (bindable_info.Bindable is FunctionParameter param && param.UsageMode == ExpressionReadMode.ReadRequired))
                        {
                            ctx.AddError(ErrorCode.BindableNotUsed, bindable_info.Bindable.Name);
                        }
                    }
                }
            }

            ctx.RemoveVisited(node);

            return(evaluable?.Evaluation?.Components ?? Environment.JokerInstance);
        }
Exemple #2
0
        public static ValidationData Validated(this INode node, ComputationContext ctx)
        {
            var evaluable = node as IEvaluable;

            if (evaluable != null && evaluable.Validation != null)
            {
                return(evaluable.Validation);
            }

            if (!ctx.AddVisited(node))
            {
                return(evaluable?.Validation);
            }

            ValidationData result = ValidationData.Create();

            INameRegistryExtension.CreateRegistry(INameRegistryExtension.EnterNode(node, ctx.ValAssignTracker),
                                                  ref ctx.ValAssignTracker, () => new AssignmentTracker());

            if (node is Loop)
            {
                ++ctx.ValLoopLevel;
            }


            {
                if (node is VariableDeclaration decl)
                {
                    ctx.ValAssignTracker?.Add(decl, ctx.ValLoopLevel);
                }
            }

            if (node is IExpression expr)
            {
                AssignmentTracker parent_tracker = ctx.ValAssignTracker;

                validateExecutionPath(node, expr.Flow.AlwaysPath, ctx, ref result);

                if (expr.Flow.ThenMaybePath != null || expr.Flow.ElseMaybePath != null)
                {
                    var branch_results = new List <ValidationData>();

                    foreach (ExecutionPath maybes in expr.Flow.ForkMaybePaths)
                    {
                        {
                            AssignmentTracker cont_tracker = null;
                            if (maybes == expr.Flow.ThenMaybePath)
                            {
                                cont_tracker = parent_tracker?.ThenBranch;
                            }
                            else if (maybes == expr.Flow.ElseMaybePath)
                            {
                                cont_tracker = parent_tracker?.ElseBranch;
                            }
                            ctx.ValAssignTracker = (cont_tracker ?? parent_tracker)?.Clone();
                        }
                        ValidationData branch_result = result.Clone();

                        validateExecutionPath(node, maybes, ctx, ref branch_result);

                        // time to remove "continue", because we are about to process
                        // step and post-check of the (could be) loop
                        if (node is IAnchor loop)
                        {
                            branch_result.RemoveInterruptionFor(loop, isBreak: false);
                        }

                        if (maybes == expr.Flow.ThenMaybePath && expr.Flow.ThenPostMaybes != null)
                        {
                            validateExecutionPath(node, expr.Flow.ThenPostMaybes, ctx, ref branch_result);
                        }

                        // we need to store the branch or kill it, because few lines below we compute intersection of the branches
                        // so we don't want to intersect with some artifact from the previous tracker
                        if (maybes == expr.Flow.ThenMaybePath)
                        {
                            parent_tracker.ThenBranch = branch_result.IsTerminated
                                ? null : (ctx.ValAssignTracker.ThenBranch ?? ctx.ValAssignTracker);
                        }
                        else if (maybes == expr.Flow.ElseMaybePath)
                        {
                            parent_tracker.ElseBranch = branch_result.IsTerminated
                                ? null : (ctx.ValAssignTracker.ElseBranch ?? ctx.ValAssignTracker);
                        }
                        else
                        {
                            throw new Exception();
                        }

                        branch_results.Add(branch_result);
                    }

                    if (expr.Flow.ExhaustiveMaybes)
                    {
                        parent_tracker?.MergeInitializations();
                    }
                    parent_tracker?.MergeAssigments();

                    result.Combine(branch_results);
                }

                ctx.ValAssignTracker = parent_tracker; // restore original tracker

                foreach (IExpression sub in expr.Flow.Enumerate)
                {
                    validateReadingValues(sub, ctx);
                }
            }


            node.ChildrenNodes.ForEach(it => Validated(it, ctx));

            if (node is IValidable verificable)
            {
                verificable.Validate(ctx);
            }

            if (node is IFunctionExit)
            {
                result.AddExit();
            }
            else if (node is LoopInterrupt loop_interrupt)
            {
                result.AddInterruption(loop_interrupt);
            }
            else if (node is IAnchor loop)
            {
                result.RemoveInterruptionFor(loop, isBreak: true);
            }
            else if (node is FunctionDefinition)
            {
                result = ValidationData.Create(); // clear it, function scope should not leak any info outside
            }
            if (evaluable != null)
            {
                evaluable.Validation = result;
            }

            ctx.RemoveVisited(node);

            return((node as IEvaluable)?.Validation);
        }