/// <inheritdoc /> public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k) { ArgumentCountException.Check(this, Arity, arglist); // Check types and find the shortest list of candidate tuples that match var candidateTuples = tuples; for (var i = 0; i < Arity; i++) { var arg = arglist[i]; if (arg is LogicVariable) { continue; } ArgumentTypeException.Check(this, Signature[i], arg, arglist); var index = indices[i]; if (index != null) { if (!index.TryGetValue(arg, out var indexedTuples)) { // There are literally no tuples with the specified argument value return(false); } if (indexedTuples.Count < candidateTuples.Count) { candidateTuples = indexedTuples; } } } // Attempt to unify each candidate tuple with the argument list foreach (var t in Shuffler(candidateTuples)) { if (env.UnifyArrays(arglist, t, out BindingList <LogicVariable> unifications)) { if (k(output, unifications, env.State, predecessor)) { return(true); } } } return(false); }
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); }
/// <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); }
/// <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); }