public override bool Try(TextBuffer output, BindingEnvironment e, Continuation k, MethodCallFrame predecessor) { if (!e.TryCopyGround(Value.Eval(e), out var expValue)) { // You can't set a variable to a non-ground value throw new ArgumentInstantiationException("set", e, new[] { (object)GlobalVariable, Value }); } if (LocalVariable == null) { return(Continue(output, new BindingEnvironment(e, e.Unifications, e.State.Bind(GlobalVariable, expValue)), k, predecessor)); } if (e.Unify(LocalVariable, expValue, out var result)) { return(Continue(output, new BindingEnvironment(e, result, e.State), k, predecessor)); } return(false); }
public override bool Try(TextBuffer output, BindingEnvironment e, Continuation k, MethodCallFrame predecessor) { var collectionValue = e.Resolve(collectionVariable); switch (collectionValue) { case Cons list: { return(list != Cons.Empty && e.Unify(element, list.First, out var bindings) && Continue(output, new BindingEnvironment(e, bindings, e.State.Bind(collectionVariable, list.Rest)), k, predecessor)); } case ImmutableStack <object> stack: { return(!stack.IsEmpty && e.Unify(element, stack.Peek(), out var bindings) && Continue(output, new BindingEnvironment(e, bindings, e.State.Bind(collectionVariable, stack.Pop())), k, predecessor)); } case ImmutableQueue <object> queue: { return(!queue.IsEmpty && e.Unify(element, queue.Peek(), out var bindings) && Continue(output, new BindingEnvironment(e, bindings, e.State.Bind(collectionVariable, queue.Dequeue())), k, predecessor)); } default: throw new ArgumentTypeException("removeNext", typeof(Cons), collectionValue, new[] { "removeNext", collectionValue }); } }
/// <inheritdoc /> protected override IEnumerable <BindingList <LogicVariable> > Iterator(object[] args, BindingEnvironment env) { ArgumentCountException.Check(Name, 2, args); var input = ArgumentTypeException.Cast <TIn>(Name, env.Resolve(args[0]), args); var result = implementation(input); if (env.Unify(args[1], result, out var bindings)) { return new[] { bindings } } ; return(EmptyBindingListArray); } }
/// <inheritdoc /> protected override IEnumerable <BindingList <LogicVariable> > Iterator(object[] args, BindingEnvironment env) { ArgumentCountException.Check(Name, 4, args); var input1 = ArgumentTypeException.Cast <TIn1>(Name, args[0], args); var input2 = ArgumentTypeException.Cast <TIn2>(Name, args[1], args); var input3 = ArgumentTypeException.Cast <TIn3>(Name, args[2], args); var result = implementation(input1, input2, input3); if (env.Unify(args[3], result, out var bindings)) { return new[] { bindings } } ; return(EmptyBindingListArray); } }
/// <inheritdoc /> protected override IEnumerable <BindingList <LogicVariable> > Iterator(object[] args, BindingEnvironment env) { ArgumentCountException.CheckAtLeast(Name, 1, args); var fArgs = new object[args.Length - 1]; Array.Copy(args, fArgs, args.Length - 1); var result = implementation(fArgs); if (env.Unify(args[args.Length - 1], result, out var bindings)) { return new[] { bindings } } ; return(EmptyBindingListArray); } }
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 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 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 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)); }
private bool CallTask(TextBuffer output, BindingEnvironment env, Continuation k, object target, object[] arglist, object originalTarget, MethodCallFrame predecessor) { switch (target) { case Task p: return(p.Call(arglist, output, env, predecessor, (newOutput, u, s, newPredecessor) => Continue(newOutput, new BindingEnvironment(env, u, s), k, newPredecessor))); case string[] text: return(Continue(output.Append(text), env, k, predecessor)); case IDictionary d: ArgumentCountException.Check(d, 2, arglist); var arg0 = arglist[0]; var v0 = arg0 as LogicVariable; var arg1 = arglist[1]; if (v0 == null) { return(d.Contains(arg0) && env.Unify(arg1, d[arg0], out var u) && Continue(output, new BindingEnvironment(env, u, env.State), k, predecessor)); } else { // Arg 0 is out foreach (DictionaryEntry e in d) { if (env.Unify(arg0, e.Key, out var unif1) && env.Unify(arg1, e.Value, unif1, out var unif2) && Continue(output, new BindingEnvironment(env, unif2, env.State), k, predecessor)) { return(true); } } } return(false); case IList l: // If it's a list in the operator position, pretend it's a call to member if (arglist.Length != 1) { throw new ArgumentCountException("<list member>", 1, arglist); } if (arglist[0] is LogicVariable l0) { foreach (var e in l) { if (Continue(output, new BindingEnvironment(env, BindingList <LogicVariable> .Bind(env.Unifications, l0, e), env.State), k, predecessor)) { return(true); } } } else { if (l.Contains(arglist[0]) && Continue(output, env, k, predecessor)) { return(true); } } return(false); case LogicVariable v: throw new ArgumentException($"Attempt to call an unbound variable {v}"); case null: throw new ArgumentException($"Null is not a valid task in call {CallSourceText(originalTarget, arglist)}"); case bool b: if (arglist.Length != 0) { throw new ArgumentCountException(b, 0, arglist); } return(b && Continue(output, env, k, predecessor)); default: if (arglist.Length == 0) { var hook = env.Module.FindTask(MentionHook, 1, false); if (hook != null) { return(MakeCall(hook, target, Next).Try(output, env, k, predecessor)); } return(Continue(output.Append(target.ToString()), env, k, predecessor)); } throw new ArgumentException($"Unknown task {target} in call {CallSourceText(originalTarget, arglist)}"); } }
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); }
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)); }
/// <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); }