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)); }
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))); }
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)); }
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); }
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)); }
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)); }
/// <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);
private static bool Min(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k) { return(MaxMinDriver("Min", args, -1, o, e, k, predecessor)); }
/// <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); }
/// <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); }
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)); }
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); }
/// <inheritdoc /> public override bool Call(object[] arglist, TextBuffer output, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k) => implementation(env.ResolveList(arglist), output, env, predecessor, k);
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)); }
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); }
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); }
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)); }
/// <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)); }
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)); }
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); }
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)); }
/// <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); }
/// <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)); }
/// <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); }
private static bool And(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k) => Step.ChainFromBody("And", args).Try(o, e, k, predecessor);
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")); });
/// <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); }
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)); }
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); }
/// <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); }