コード例 #1
0
        private static bool Call(object[] args, TextBuffer output, BindingEnvironment env,
                                 MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.CheckAtLeast(nameof(Call), 1, args);
            var call = ArgumentTypeException.Cast <object[]>(nameof(Call), args[0], args);

            if (!(call[0] is Task task))
            {
                throw new InvalidOperationException(
                          "Task argument to Call must be a task");
            }

            var taskArgs = new object[call.Length - 1 + args.Length - 1];

            var i = 0;

            for (var callIndex = 1; callIndex < call.Length; callIndex++)
            {
                taskArgs[i++] = call[callIndex];
            }
            for (var argsIndex = 1; argsIndex < args.Length; argsIndex++)
            {
                taskArgs[i++] = args[argsIndex];
            }

            return(task.Call(taskArgs, output, env, predecessor, k));
        }
コード例 #2
0
        private static bool Parse(object[] args, TextBuffer output, BindingEnvironment env,
                                  MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.CheckAtLeast(nameof(Parse), 2, args);
            var call = ArgumentTypeException.Cast <object[]>(nameof(Parse), env.Resolve(args[0]), args);
            var text = ArgumentTypeException.Cast <string[]>(nameof(Parse), env.Resolve(args[1]), args);

            if (!(call[0] is Task task))
            {
                throw new InvalidOperationException(
                          "Task argument to Parse must be a compound task, i.e. a user-defined task with methods.");
            }

            var taskArgs = new object[call.Length - 1];

            var i = 0;

            for (var callIndex = 1; callIndex < call.Length; callIndex++)
            {
                taskArgs[i++] = call[callIndex];
            }

            var parseBuffer = TextBuffer.MakeReadModeTextBuffer(text);

            return(task.Call(taskArgs, parseBuffer, env, predecessor,
                             (buffer, u, s, p) => buffer.ReadCompleted && k(output, u, s, p)));
        }
コード例 #3
0
        private static bool Eval(object[] call, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor,
                                 Step.Continuation k, string taskName)
        {
            if (!(call[0] is Task task))
            {
                throw new InvalidOperationException(
                          $"Task argument to {taskName} must be a task: {Writer.TermToString(call[0])}");
            }

            return(task.Call(call.Skip(1).ToArray(), output, env, predecessor, k));
        }
コード例 #4
0
        private static bool PreviousCall(object[] args, TextBuffer output, BindingEnvironment env,
                                         MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check(nameof(PreviousCall), 1, args);
            if (args[0] is LogicVariable)
            {
                // [PreviousCall ?var]
                foreach (var priorGoal in predecessor.GoalChain)
                {
                    var e = priorGoal.CallExpression;
                    if (env.Unify(args[0], e, out BindingList <LogicVariable> unifications) &&
                        k(output, unifications, env.State, predecessor))
                    {
                        return(true);
                    }
                }

                return(false);
            }

            // [PreviousCall [Task ?args]]
            var call = ArgumentTypeException.Cast <object[]>(nameof(PreviousCall), args[0], args);

            foreach (var priorGoal in predecessor.GoalChain)
            {
                if (priorGoal.Method.Task != call[0])
                {
                    // Don't bother making the call expression and trying to unify.
                    continue;
                }

                var e = priorGoal.CallExpression;
                if (call.Length == e.Length &&
                    env.UnifyArrays(call, e, out BindingList <LogicVariable> unifications) &&
                    k(output, unifications, env.State, predecessor))
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #5
0
        private static bool NotAny(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            // Whether the call to args below succeeded
            var success = false;

            // This always fails, since its continuation fails too
            Step.ChainFromBody("NotAny", args)
            .Try(o, e,
                 (newOut, newE, newK, newP) =>
            {
                // Remember that we succeeded, then fail
                success = true;
                return(false);
            },
                 predecessor);

            // If the call to args succeeded, fail; otherwise call continuation
            return(!success && k(o, e.Unifications, e.State, predecessor));
        }
コード例 #6
0
        private static bool Not(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            foreach (var arg in args)
            {
                if (!Term.IsGround(arg))
                {
                    throw new ArgumentInstantiationException("Not", e, args, "Use NotAny if you intend goals that aren't ground.");
                }
            }
            // Whether the call to args below succeeded
            var success = false;

            // This always fails, since its continuation fails too
            Step.ChainFromBody("Not", args)
            .Try(o, e,
                 (newOut, newE, newK, newP) =>
            {
                // Remember that we succeeded, then fail
                success = true;
                return(false);
            },
                 predecessor);

            // If the call to args succeeded, fail; otherwise call continuation
            return(!success && k(o, e.Unifications, e.State, predecessor));
        }
コード例 #7
0
 /// <summary>
 /// Call this task with the specified arguments
 /// </summary>
 /// <param name="arglist">Task arguments</param>
 /// <param name="output">Output accumulated so far</param>
 /// <param name="env">Binding environment</param>
 /// <param name="predecessor">Most recently succeeded MethodCallFrame</param>
 /// <param name="k">Continuation</param>
 /// <returns>True if task succeeded and continuation succeeded</returns>
 /// <exception cref="CallFailedException">If the task fails</exception>
 public abstract bool Call(object[] arglist, TextBuffer output, BindingEnvironment env,
                           MethodCallFrame predecessor, Step.Continuation k);
コード例 #8
0
 private static bool Min(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 {
     return(MaxMinDriver("Min", args, -1, o, e, k, predecessor));
 }
コード例 #9
0
 /// <inheritdoc />
 public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
 {
     ArgumentCountException.Check(Name, 0, arglist);
     foreach (var tokens in implementation())
     {
         if (k(output.Append(tokens), env.Unifications, env.State, predecessor))
         {
             return(true);
         }
     }
     return(false);
 }
コード例 #10
0
ファイル: GeneralPredicate.cs プロジェクト: ianhorswill/Step
 /// <inheritdoc />
 public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
 {
     foreach (var bindings in Iterator(arglist, env))
     {
         if (k(output, bindings, env.State, predecessor))
         {
             return(true);
         }
     }
     return(false);
 }
コード例 #11
0
        private static bool Once(object[] args, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
        {
            TextBuffer finalOutput = output;
            BindingList <LogicVariable> finalBindings = null;
            State           finalState = State.Empty;
            MethodCallFrame finalFrame = predecessor;
            bool            success    = false;

            GenerateSolutionsFromBody("Once", args, output, env,
                                      (o, u, d, p) =>
            {
                success       = true;
                finalOutput   = o;
                finalBindings = u;
                finalState    = d;
                finalFrame    = p;
                return(true);
            },
                                      predecessor);

            return(success && k(finalOutput, finalBindings, finalState, finalFrame));
        }
コード例 #12
0
        private static bool CompoundTask(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check("CompoundTask", 1, args);
            var arg = e.Resolve(args[0]);
            var l   = arg as LogicVariable;

            if (l == null)
            {
                // Argument is instantiated; test if it's a compound task
                return((arg is CompoundTask) && k(o, e.Unifications, e.State, predecessor));
            }
            foreach (var t in e.Module.DefinedTasks)
            {
                if (k(o, BindingList <LogicVariable> .Bind(e.Unifications, l, t), e.State, predecessor))
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #13
0
 /// <inheritdoc />
 public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
 => implementation(env.ResolveList(arglist), output, env, predecessor, k);
コード例 #14
0
 private static bool LastMethodCallFrame(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 {
     ArgumentCountException.Check("LastMethodCallFrame", 1, args);
     return(e.Unify(args[0], predecessor, out var u) &&
            k(o, u, e.State, predecessor));
 }
コード例 #15
0
        private static bool TaskSubtask(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check("TaskSubtask", 2, args);
            var task = ArgumentTypeException.Cast <CompoundTask>("TaskSubtask", args[0], args);

            foreach (var callExpression in e.Module.Subtasks(task))
            {
                if (e.Unify(args[1], callExpression, out var unifications))
                {
                    if (k(o, unifications, e.State, predecessor))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
コード例 #16
0
        private static bool TaskCalls(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            var m = e.Module;

            ArgumentCountException.Check(nameof(TaskCalls), 2, args);
            ArgumentTypeException.Check(nameof(TaskCalls), typeof(CompoundTask), args[0], args, true);
            var callerVar  = args[0] as LogicVariable;
            var callerTask = args[0] as CompoundTask;
            var callee     = args[1];
            var calleeVar  = callee as LogicVariable;

            if (callerVar == null)
            {
                // First arg is input
                if (calleeVar == null)
                {
                    // in in
                    if (m.TaskCalls(callerTask, callee))
                    {
                        return(k(o, e.Unifications, e.State, predecessor));
                    }
                }
                else
                {
                    // in out
                    foreach (var c in m.Callees(callerTask))
                    {
                        if (k(o, BindingList <LogicVariable> .Bind(e.Unifications, calleeVar, c), e.State, predecessor))
                        {
                            return(true);
                        }
                    }
                }
            }
            else
            {
                // First arg is output
                if (calleeVar == null)
                {
                    // out in
                    foreach (var caller in m.DefinedTasks)
                    {
                        if (m.TaskCalls(caller, callee))
                        {
                            if (k(o, BindingList <LogicVariable> .Bind(e.Unifications, callerVar, caller), e.State,
                                  predecessor))
                            {
                                return(true);
                            }
                        }
                    }
                }
                else
                {
                    // out out
                    foreach (var caller in m.DefinedTasks)
                    {
                        foreach (var c in m.Callees(caller))
                        {
                            if (k(o,
                                  BindingList <LogicVariable> .Bind(e.Unifications, callerVar, caller).Bind(calleeVar, c),
                                  e.State, predecessor))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }
コード例 #17
0
        private static bool FindUnique(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check("FindUnique", 3, args);
            var solution = args[0];
            var call     = args[1] as object[];

            if (call == null || call.Length == 0)
            {
                throw new ArgumentException("Invalid goal expression");
            }
            var task     = ArgumentTypeException.Cast <Task>("FindUnique", call[0], args);
            var taskArgs = call.Skip(1).ToArray();

            var result    = args[2];
            var resultSet = new HashSet <object>();

            task.Call(taskArgs, o, e, predecessor, (newO, u, s, p) =>
            {
                resultSet.Add(e.Resolve(solution, u));
                return(false);
            });
            return(e.Unify(result, resultSet.ToArray(), out var final) &&
                   k(o, final, e.State, predecessor));
        }
コード例 #18
0
ファイル: SimplePredicate.cs プロジェクト: ianhorswill/Step
 /// <inheritdoc />
 public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
 {
     ArgumentCountException.Check(Name, 0, arglist);
     return(implementation() &&
            k(output, env.Unifications, env.State, predecessor));
 }
コード例 #19
0
        private static bool Implies(object[] args, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
        {
            if (args.Length < 2)
            {
                throw new ArgumentCountException(nameof(Implies), 2, args);
            }

            var producer      = args[0];
            var producerChain = Step.ChainFromBody(nameof(Implies), producer);
            var consumer      = args.Skip(1).ToArray();
            var consumerChain = Step.ChainFromBody(nameof(Implies), consumer);

            var dynamicState = env.State;
            var resultOutput = output;
            var allTrue      = true;

            producerChain.Try(resultOutput, env,
                              (o, u, d, p) =>
            {
                // We've got a solution to the producer in u.
                // So run the consumer once with u but not d or o.
                allTrue &= consumerChain.Try(resultOutput,
                                             new BindingEnvironment(env, u, dynamicState),
                                             (o2, u2, d2, newP) =>
                {
                    // Save modifications to dynamic state, output; throw away binding state
                    dynamicState = d2;
                    resultOutput = o2;
                    // Accept this one solution to consumer; don't backtrack it.
                    return(true);
                },
                                             p);
                // Backtrack to generate the next solution for producer
                return(false);
            },
                              predecessor);

            // Use original unifications but accumulated output and state.
            return(allTrue && k(resultOutput, env.Unifications, dynamicState, predecessor));
        }
コード例 #20
0
        private static bool SaveText(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check("SaveText", 2, args);

            var textVar = e.Resolve(args[1]);

            if (textVar == null)
            {
                throw new ArgumentInstantiationException("SaveText", e, args);
            }

            var invocation = args[0] as object[];

            if (invocation == null || invocation.Length == 0)
            {
                throw new ArgumentTypeException("SaveText", typeof(Call), args[0], args);
            }
            var arglist = new object[invocation.Length - 1];

            Array.Copy(invocation, 1, arglist, 0, arglist.Length);
            var call          = new Call(invocation[0], arglist, null);
            var initialLength = o.Length;

            string[] chunk = null;
            var      frame = predecessor;

            if (call.Try(o, e,
                         (output, b, d, p) =>
            {
                frame = p;
                chunk = new string[output.Length - initialLength];
                Array.Copy(o.Buffer, initialLength, chunk, 0, output.Length - initialLength);
                return(true);
            },
                         predecessor) &&
                e.Unify(textVar, chunk, e.Unifications, out var newUnifications))
            {
                return(k(o, newUnifications, e.State, frame));
            }

            return(false);
        }
コード例 #21
0
        private static bool ExactlyOnce(object[] args, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check("ExactlyOnce", 1, args);
            TextBuffer finalOutput = output;
            BindingList <LogicVariable> finalBindings = null;
            State           finalState = State.Empty;
            MethodCallFrame finalFrame = predecessor;
            bool            failure    = true;

            var chain = Step.ChainFromBody("ExactlyOnce", args);

            chain.Try(output, env,
                      (o, u, d, p) =>
            {
                failure       = false;
                finalOutput   = o;
                finalBindings = u;
                finalState    = d;
                finalFrame    = p;
                return(true);
            },
                      predecessor);

            if (failure)
            {
                var failedCall = (Call)chain;
                throw new CallFailedException(env.Resolve(failedCall.Task), env.ResolveList(failedCall.Arglist));
            }
            return(k(finalOutput, finalBindings, finalState, finalFrame));
        }
コード例 #22
0
 /// <summary>
 /// Calls a task with the specified arguments and allows the user to provide their own continuation.
 /// The only (?) use case for this is when you want to forcibly generate multiple solutions
 /// </summary>
 internal static void GenerateSolutions(string taskName, object[] args, TextBuffer o, BindingEnvironment e, Step.Continuation k, MethodCallFrame predecessor)
 {
     new Call(StateVariableName.Named(taskName), args, null).Try(o, e, k, predecessor);
 }
コード例 #23
0
        /// <summary>
        /// Core implementation of both Max and Min
        /// </summary>
        private static bool MaxMinDriver(string taskName, object[] args,
                                         int multiplier, TextBuffer o, BindingEnvironment e,
                                         Step.Continuation k,
                                         MethodCallFrame predecessor)
        {
            var scoreVar = args[0] as LogicVariable;

            if (scoreVar == null)
            {
                throw new ArgumentInstantiationException(taskName, e, args);
            }

            var           bestScore  = multiplier * float.NegativeInfinity;
            var           bestFrame  = predecessor;
            CapturedState bestResult = new CapturedState();
            var           gotOne     = false;

            GenerateSolutionsFromBody(taskName, args.Skip(1).ToArray(), o, e,
                                      (output, u, d, p) =>
            {
                gotOne = true;

                var env = new BindingEnvironment(e, u, d);

                var maybeScore = env.Resolve(scoreVar);
                float score;
                switch (maybeScore)
                {
                case int i:
                    score = i;
                    break;

                case float f:
                    score = f;
                    break;

                case double df:
                    score = (float)df;
                    break;

                case LogicVariable _:
                    throw new ArgumentInstantiationException(taskName, new BindingEnvironment(e, u, d), args);

                default:
                    throw new ArgumentTypeException(taskName, typeof(float), maybeScore, args);
                }

                if (multiplier * score > multiplier * bestScore)
                {
                    bestScore  = score;
                    bestResult = new CapturedState(o, output, u, d);
                    bestFrame  = p;
                }

                // Always ask for another solution
                return(false);
            },
                                      predecessor);

            // When we get here, we've iterated through all solutions and kept the best one.
            // So pass it on to our continuation
            return(gotOne &&
                   k(o.Append(bestResult.Output), bestResult.Bindings, bestResult.State, bestFrame));
        }
コード例 #24
0
ファイル: Method.cs プロジェクト: ianhorswill/Step
        /// <summary>
        /// Attempt to run this method
        /// </summary>
        /// <param name="args">Arguments from the call to the method's task</param>
        /// <param name="output">Output buffer to write to</param>
        /// <param name="env">Variable binding information</param>
        /// <param name="k">Continuation to call if method succeeds</param>
        /// <param name="pre">Predecessor frame</param>
        /// <returns>True if the method and its continuation succeeded</returns>
        public bool Try(object[] args, TextBuffer output, BindingEnvironment env, MethodCallFrame pre, Step.Continuation k)
        {
            // Make stack frame for locals
            var locals = new LogicVariable[LocalVariableNames.Length];

            for (var i = 0; i < LocalVariableNames.Length; i++)
            {
                locals[i] = new LogicVariable(LocalVariableNames[i]);
            }
            var newFrame = new MethodCallFrame(this, env.Unifications, locals, env.Frame, pre);

            MethodCallFrame.CurrentFrame = newFrame;
            var newEnv = new BindingEnvironment(env, newFrame);

            if (newEnv.UnifyArrays(args, ArgumentPattern, out BindingEnvironment finalEnv))
            {
                env.Module.TraceMethod(Module.MethodTraceEvent.Enter, this, args, output, finalEnv);
                newFrame.BindingsAtCallTime = finalEnv.Unifications;
                var traceK = env.Module.Trace == null
                    ? k
                    : (newO, newU, newState, predecessor) =>
                {
                    MethodCallFrame.CurrentFrame = newFrame;
                    env.Module.TraceMethod(Module.MethodTraceEvent.Succeed, this, args, newO,
                                           new BindingEnvironment(finalEnv, newU, newState));
                    MethodCallFrame.CurrentFrame = newFrame.Caller;
                    return(k(newO, newU, newState, predecessor));
                };
                if (StepChain?.Try(output, finalEnv, traceK, newFrame) ?? traceK(output, finalEnv.Unifications, finalEnv.State, newFrame))
                {
                    return(true);
                }
            }

            MethodCallFrame.CurrentFrame = newFrame;
            env.Module.TraceMethod(Module.MethodTraceEvent.MethodFail, this, args, output, finalEnv);
            return(false);
        }
コード例 #25
0
 private static bool And(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 => Step.ChainFromBody("And", args).Try(o, e, k, predecessor);
コード例 #26
0
 private static bool Or(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 => args.Any(call =>
 {
     var tuple = ArgumentTypeException.Cast <object[]>("Or", call, args);
     return(Eval(tuple, o, e, predecessor, k, "Or"));
 });
コード例 #27
0
 /// <summary>
 /// Calls all the tasks in the body and allows the user to provide their own continuation.
 /// The only (?) use case for this is when you want to forcibly generate multiple solutions
 /// </summary>
 internal static void GenerateSolutionsFromBody(string callingTaskName, object[] body, TextBuffer o, BindingEnvironment e,
                                                Step.Continuation k,
                                                MethodCallFrame predecessor)
 {
     Step.ChainFromBody(callingTaskName, body).Try(o, e, k, predecessor);
 }
コード例 #28
0
 private static bool IgnoreOutput(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 {
     return(Step.ChainFromBody("IgnoreOutput", args).Try(
                o, e,
                (_, u, s, p) => k(o, u, s, p),
                predecessor));
 }
コード例 #29
0
        private static bool UniqueCall(object[] args, TextBuffer output, BindingEnvironment env,
                                       MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.CheckAtLeast(nameof(UniqueCall), 1, args);
            var call = ArgumentTypeException.Cast <object[]>(nameof(PreviousCall), args[0], args);

            if (!(call[0] is Task task))
            {
                throw new InvalidOperationException(
                          "Task argument to UniqueCall must be a task.");
            }

            var taskArgs = new object[call.Length - 1 + args.Length - 1];

            var i = 0;

            for (var callIndex = 1; callIndex < call.Length; callIndex++)
            {
                taskArgs[i++] = call[callIndex];
            }
            for (var argsIndex = 1; argsIndex < args.Length; argsIndex++)
            {
                taskArgs[i++] = args[argsIndex];
            }

            var fullCall = call;

            if (args.Length > 1)
            {
                fullCall    = new object[taskArgs.Length + 1];
                fullCall[0] = task;
                for (var j = 0; j < taskArgs.Length; j++)
                {
                    fullCall[j + 1] = taskArgs[j];
                }
            }

            if (task.Call(taskArgs, output, env, predecessor,
                          (o, u, s, newPredecessor) =>
            {
                foreach (var priorGoal in predecessor.GoalChain)
                {
                    if (priorGoal.Method.Task != task)
                    {
                        // Don't bother making the call expression and trying to unify.
                        continue;
                    }

                    if (env.Unify(fullCall, priorGoal.CallExpression, u, out BindingList <LogicVariable> _))
                    {
                        // We already did a call that matches this call
                        // So have the continuation return false, forcing the task.Call above to backtrack
                        // and try to generate a new solution
                        return(false);
                    }
                }

                return(k(o, u, s, newPredecessor));
            }))
            {
                return(true);
            }

            return(false);
        }
コード例 #30
0
ファイル: CompoundTask.cs プロジェクト: ianhorswill/Step
        /// <summary>
        /// Call this task with the specified arguments
        /// </summary>
        /// <param name="arglist">Task arguments</param>
        /// <param name="output">Output accumulated so far</param>
        /// <param name="env">Binding environment</param>
        /// <param name="predecessor">Most recently succeeded MethodCallFrame</param>
        /// <param name="k">Continuation</param>
        /// <returns>True if task succeeded and continuation succeeded</returns>
        /// <exception cref="CallFailedException">If the task fails</exception>
        public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
        {
            string lastToken = null;

            if (Suffix)
            {
                (lastToken, output) = output.Unappend();
                arglist             = new object[] { lastToken };
            }

            ArgumentCountException.Check(this, this.ArgCount, arglist);
            var successCount = 0;

            if (ReadCache)
            {
                // Check for a hit in the cache.  If we find one, we're done.
                if (Cache.TryGetValue(env.State, arglist, out var result))
                {
                    if (result.Success)
                    {
                        if (Function)
                        {
                            // We got a hit, and since this is a function, it's the only allowable hit.
                            // So we succeed deterministically.
                            return(env.Unify(arglist[arglist.Length - 1], result.FunctionValue,
                                             out BindingList <LogicVariable> u) &&
                                   k(output.Append(result.Text), u, env.State, predecessor));
                        }
                        else
                        {
                            successCount++;
                            if (k(output.Append(result.Text), env.Unifications, env.State, predecessor))
                            {
                                return(true);
                            }
                        }
                    }
                    else
                    {
                        // We have a match on a cached fail result, so force a failure, skipping over the methods.
                        goto failed;
                    }
                }
                else if (arglist.Any(x => x is LogicVariable))
                {
                    foreach (var pair in Cache.Bindings(env.State))
                    {
                        if (pair.Value.Success &&
                            env.UnifyArrays((object[])pair.Key, arglist,
                                            out BindingList <LogicVariable> unifications))
                        {
                            successCount++;
                            if (k(output.Append(pair.Value.Text), unifications, env.State, predecessor))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }

            if (ContainsCoolStep)
            {
                var s = env.State;
                s   = CallCounts.SetItem(s, this, CallCount(s) + 1);
                env = new BindingEnvironment(env, env.Unifications, s);
            }

            var methods = this.EffectiveMethods;

            for (var index = 0; index < methods.Count && !(this.Deterministic && successCount > 0); index++)
            {
                var method = methods[index];
                if (method.Try(arglist, output, env, predecessor,
                               (o, u, s, newPredecessor) =>
                {
                    successCount++;
                    if (WriteCache)
                    {
                        var final = env.ResolveList(arglist, u);
                        if (Term.IsGround(final))
                        {
                            s = StoreResult(s, final, new CachedResult(true,
                                                                       Function?final[final.Length - 1]:null,
                                                                       TextBuffer.Difference(output, o)));
                        }
                    }
                    return(k(o, u, s, newPredecessor));
                }))
                {
                    return(true);
                }
                if (Suffix)
                {
                    // Undo any overwriting that the method might have done
                    output.Buffer[output.Length - 1] = lastToken;
                }
            }

failed:
            var currentFrame = MethodCallFrame.CurrentFrame = env.Frame;

            if (currentFrame != null)
            {
                currentFrame.BindingsAtCallTime = env.Unifications;
            }
            if (successCount == 0 && this.MustSucceed)
            {
                throw new CallFailedException(this, arglist);
            }

            if (currentFrame != null)
            {
                env.Module.TraceMethod(Module.MethodTraceEvent.CallFail, currentFrame.Method, currentFrame.Arglist, output,
                                       env);
                MethodCallFrame.CurrentFrame = currentFrame.Predecessor;
            }

            // Failure
            return(false);
        }