Beispiel #1
0
        /// <inheritdoc />
        protected override IEnumerable <BindingList <LogicVariable> > Iterator(object[] args, BindingEnvironment e)
        {
            ArgumentCountException.Check(Name, 1, args);
            var arg = e.Resolve(args[0]);

            switch (arg)
            {
            case LogicVariable v:
            {
                foreach (var result in outMode())
                {
                    yield return(BindingList <LogicVariable> .Bind(e.Unifications, v, result));
                }
                break;
            }

            case T1 value:
                if (inMode(value))
                {
                    yield return(e.Unifications);
                }
                break;

            default:
                throw new ArgumentTypeException(Name, typeof(T1), arg, args);
            }
        }
        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 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));
        }
        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)));
        }
Beispiel #5
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);
 }
Beispiel #6
0
        /// <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);
        }
    }
Beispiel #7
0
        /// <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);
        }
    }
Beispiel #8
0
        /// <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);
        }
    }
Beispiel #9
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);
        }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
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);
        }
Beispiel #12
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);
        }
Beispiel #13
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);
        }
Beispiel #14
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));
        }
Beispiel #15
0
        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)}");
            }
        }
Beispiel #16
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);
        }
Beispiel #17
0
        public static void DefineFileSystemBuiltins(Module m)
        {
            void ImportFunction(string name, Func <string, string> implementation)
            {
                m[name] = new SimpleFunction <string, string>(name, implementation);
            }

            ImportFunction("PathExtension", Path.GetExtension);
            m["PathStructure"] = new GeneralNAryPredicate("PathStructure",
                                                          args =>
            {
                ArgumentCountException.Check("PathStructure", 3, args);
                if (args[2] is LogicVariable v)
                {
                    // Path argument uninstantiated
                    args[2] = Path.Combine(ArgumentTypeException.Cast <string>("PathStructure", args[0], args),
                                           ArgumentTypeException.Cast <string>("PathStructure", args[1], args));
                    return(new object[][] { args });
                }
                else
                {
                    var path = ArgumentTypeException.Cast <string>("PathStructure", args[2], args);
                    // Path argument is instantiated
                    return(new object[][]
                           { new object[] { Path.GetDirectoryName(path), Path.GetFileName(path), path } });
                }
            });

            m["DirectoryFile"] = new GeneralPredicate <string, string>("DirectoryFile",
                                                                       (d, f) => (File.Exists(f) && Path.GetDirectoryName(f) == d),
                                                                       d =>
            {
                if (Directory.Exists(d))
                {
                    return(Directory.GetFiles(d));
                }
                return(new string[0]);
            },
                                                                       f =>
            {
                if (Directory.Exists(f))
                {
                    return new[] { Path.GetDirectoryName(f) }
                }
                ;
                return(new string[0]);
            },
                                                                       null);

            m["DirectorySubdirectory"] = new GeneralPredicate <string, string>("DirectorySubdirectory",
                                                                               (d, f) => (File.Exists(f) && Path.GetDirectoryName(f) == d),
                                                                               d =>
            {
                if (Directory.Exists(d))
                {
                    return(Directory.GetDirectories(d));
                }
                return(new string[0]);
            },
                                                                               f =>
            {
                if (Directory.Exists(f))
                {
                    return new[] { Path.GetDirectoryName(f) }
                }
                ;
                return(new string[0]);
            },
                                                                               null);
        }
Beispiel #18
0
        /// <summary>
        /// Add the built-in primitives to the global module.
        /// </summary>
        internal static void DefineGlobals()
        {
            var g = Module.Global;

            Documentation.SectionIntroduction("comparison",
                                              "Predicates that test whether two values are the same or different.  Many of these use unification, in which case they are testing whether the values can be made identical through binding variables.");

            g["="] = new GeneralPrimitive("=", (args, o, e, predecessor, k) =>
            {
                ArgumentCountException.Check("=", 2, args);
                return(e.Unify(args[0], args[1], e.Unifications, out var newBindings) &&
                       k(o, newBindings, e.State, predecessor));
            }).Arguments("a", "b")
                     .Documentation("comparison", "Matches (unifies) a and b, and succeeds when they're the same.");

            g["Different"] = new SimplePredicate <object, object>("Different",
                                                                  (a, b) => !a.Equals(b) && !(a is LogicVariable) && !(b is LogicVariable))
                             .Arguments("a", "b")
                             .Documentation("comparison", "Attempts to match a and b and succeeds if they *can't* be matched");

            g[">"] = new SimplePredicate <float, float>(">", (a, b) => a > b)
                     .Arguments("a", "b")
                     .Documentation("comparison", "True when a and b are both numbers and a is larger");
            g["<"] = new SimplePredicate <float, float>("<", (a, b) => a < b)
                     .Arguments("a", "b")
                     .Documentation("comparison", "True when a and b are both numbers and a is smaller");
            g[">="] = new SimplePredicate <float, float>(">=", (a, b) => a >= b)
                      .Arguments("a", "b")
                      .Documentation("comparison", "True when a and b are both numbers and a is at least as large as b");
            g["<="] = new SimplePredicate <float, float>("<=", (a, b) => a <= b)
                      .Arguments("a", "b")
                      .Documentation("comparison", "True when a and b are both numbers and a is no larger than b");

            Documentation.SectionIntroduction("output",
                                              "Tasks that print things.");

            g["Paragraph"] = new DeterministicTextGenerator("Paragraph",
                                                            () => new[] { TextUtilities.NewParagraphToken })
                             .Arguments()
                             .Documentation("output", "Starts a new paragraph");
            g["NewLine"] = new DeterministicTextGenerator("NewLine",
                                                          () => new[] { TextUtilities.NewLineToken })
                           .Arguments()
                           .Documentation("output", "Starts a new line");
            g["FreshLine"] = new DeterministicTextGenerator("FreshLine",
                                                            () => new[] { TextUtilities.FreshLineToken })
                             .Arguments()
                             .Documentation("output", "Starts a new line, unless we're already at the start of a new line");
            g["ForceSpace"] = new DeterministicTextGenerator("ForceSpace",
                                                             () => new[] { TextUtilities.ForceSpaceToken })
                              .Arguments()
                              .Documentation("output", "Forces a space to be inserted between two tokens that wouldn't normally be separated.  For example, \"a .\" prints as \"a.\" but \"a [ForceSpace] .\" prints as \"a .\"");

            Documentation.SectionIntroduction("control flow//controlling backtracking",
                                              "Tasks that control how or whether execution backtracks.");

            g["Fail"] = new SimplePredicate("Fail", () => false)
                        .Arguments()
                        .Documentation("control flow//controlling backtracking", "Never succeeds; forces the system to backtrack immediately.");

            Documentation.SectionIntroduction("debugging",
                                              "Tasks used to help debug code.");

            g["Break"] = new SimplePredicate("Break", Break)
                         .Arguments()
                         .Documentation("debugging", "Breakpoint; pauses execution and displays the current stack in the debugger.");

            g["Throw"] = new SimpleNAryPredicate("Throw", Throw)
                         .Arguments("message", "...")
                         .Documentation("control flow", "Throws an exception (error) containing the specified message.");

            g["StringForm"] =
                new GeneralPredicate <object, string>("StringForm",
                                                      (o, s) => o.ToString() == s,
                                                      o => new [] { o.ToString() },
                                                      null, null)
                .Arguments("object", "?string_form")
                .Documentation("output", "Matches ?string_form with the printed representation of object");

            g["WriteVerbatim"] = new DeterministicTextMatcher("WriteVerbatim", (o =>
            {
                switch (o)
                {
                case null:
                    return(new[] { "null" });

                case string[] tokens:
                    return(tokens);

                default:
                    return(new[] { Writer.TermToString(o) });
                }
            }))
                                 .Arguments("object")
                                 .Documentation("output", "Prints object; _'s are printed as themselves rather than changed to spaces,");

            WritePrimitive = new DeterministicTextMatcher("Write", (o =>
            {
                switch (o)
                {
                case null:
                    return(new[] { "null" });

                case string[] tokens:
                    return(tokens.Length == 0? tokens : tokens.Skip(1).Prepend(tokens[0].Capitalize()).ToArray());

                default:
                    return(new[] { Writer.TermToString(o).Replace('_', ' ') });
                }
            }));

            g["Write"] = WritePrimitive
                         .Arguments("object")
                         .Documentation("output", "Prints object, transforming _'s to spaces");

            g["WriteCapitalized"] = new DeterministicTextMatcher("WriteCapitalized", (o =>
            {
                switch (o)
                {
                case null:
                    return(new[] { "null" });

                case string[] tokens:
                    return(tokens.Length == 0 ? tokens : tokens.Skip(1).Prepend(tokens[0].Capitalize()).ToArray());

                default:
                    return(new[] { Writer.TermToString(o).Replace('_', ' ').Capitalize() });
                }
            }))
                                    .Arguments("object")
                                    .Documentation("output", "Prints object, transforming _'s to spaces.  If the first character of the output is a lower-case letter, it will capitalize it.");

            g["WriteConcatenated"] = new DeterministicTextGenerator <object, object>("WriteConcatenated",
                                                                                     (s1, s2) => { return(new[] { $"{Writer.TermToString(s1).Replace('_', ' ')}{Writer.TermToString(s2).Replace('_', ' ')}" }); })
                                     .Arguments("object1", "object2")
                                     .Documentation("output", "Prints both objects, without a space between them, and changes and _'s to spaces.");

            Documentation.SectionIntroduction("data structures",
                                              "Predicates that create or access complex data objects.  Note that dictionaries and lists can also be used as predicates.  So [dictionary ?key ?value] is true when ?key has ?value in the dictinoary and and [list ?element] is true when ?element is an element of the list.");

            Documentation.SectionIntroduction("data structures//lists",
                                              "Predicates access lists in particular.  These work with any C# object that implements the IList interface, including Step tuples (which are the C# type object[]).");

            g["Member"] = new GeneralPredicate <object, IEnumerable <object> >("Member", (member, collection) => collection != null && collection.Contains(member), null, collection => collection ?? EmptyArray, null)
                          .Arguments("element", "collection")
                          .Documentation("data structures//lists", "True when element is an element of collection.");

            g["Length"] = new SimpleFunction <IList, int>("Length", l => l.Count)
                          .Arguments("list", "?length")
                          .Documentation("data structures//list", "True when list has exactly ?length elements");

            g["Nth"] = new GeneralNAryPredicate("Nth",
                                                args =>
            {
                ArgumentCountException.Check("Nth", 3, args);
                var list       = ArgumentTypeException.Cast <IList>("Nth", args[0], args);
                var indexVar   = args[1] as LogicVariable;
                var elementVar = args[2] as LogicVariable;

                if (indexVar == null)
                {
                    var index = ArgumentTypeException.Cast <int>("Nth", args[1], args);
                    return(new[] { new[] { list, index, list[index] } });
                }
                else if (elementVar == null)
                {
                    var elt   = args[2];
                    var index = list.IndexOf(elt);
                    if (index >= 0)
                    {
                        return new[] { new[] { list, index, args[2] } }
                    }
                    ;
                    else
                    {
                        return(new object[0][]);
                    }
                }

                throw new ArgumentInstantiationException("Nth", new BindingEnvironment(), args);
            })
                       .Arguments("list", "index", "?element")
                       .Documentation("data structures//list", "True when element of list at index is ?element");

            g["Cons"] = new GeneralNAryPredicate("Cons", args =>
            {
                ArgumentCountException.Check("Cons", 3, args);
                if (args[2] is object[] tuple)
                {
                    return new[] { new[] { tuple[0], tuple.Skip(1).ToArray(), tuple } }
                }
                ;
                if (args[1] is object[] restTuple)
                {
                    return new[] { new[] { args[0], restTuple, restTuple.Prepend(args[0]).ToArray() } }
                }
                ;
                throw new ArgumentException("Either the second or argument of Cons must be a tuple.");
            })
                        .Arguments("firstElement", "restElements", "tuple")
                        .Documentation("True when tuple starts with firstElement and continues with restElements.");

            Documentation.SectionIntroduction("metalogical",
                                              "Predicates that test the binding state of a variable.");

            g["Var"] = new SimplePredicate <object>("Var", o => o is LogicVariable)
                       .Arguments("x")
                       .Documentation("metalogical", "Succeeds when its argument is an uninstantiated variable (a variable without a value)");
            g["NonVar"] = new SimplePredicate <object>("NonVar", o => !(o is LogicVariable))
                          .Arguments("x")
                          .Documentation("metalogical", "Succeeds when its argument is a *not* an uninstantiated variable.");

            g["CopyTerm"] = new GeneralPrimitive("CopyTerm",
                                                 (args, t, b, f, k) =>
            {
                ArgumentCountException.Check("CopyTerm", 2, args);
                return(b.Unify(args[1], b.CopyTerm(args[0]), out var u) && k(t, u, b.State, f));
            })
                            .Arguments("in", "out")
                            .Documentation("metalogical",
                                           "Sets out to a copy of in with fresh variables, so that unifications to one don't affect the other");

            Documentation.SectionIntroduction("type testing",
                                              "Predicates that test what type of data object their argument is.  These fail when the argument is an unbound variable.");

            g["String"] = new SimplePredicate <object>("String", o => o is string)
                          .Arguments("x")
                          .Documentation("type testing", "Succeeds when its argument is a string");
            g["Number"] = new SimplePredicate <object>("Number", o => o is int || o is float || o is double)
                          .Arguments("x")
                          .Documentation("type testing", "Succeeds when its argument is a number");
            g["Tuple"] = new SimplePredicate <object>("Tuple", o => o is object[])
                         .Arguments("x")
                         .Documentation("type testing", "Succeeds when its argument is a tuple");
            g["BinaryTask"] = new SimplePredicate <object>("BinaryTask",
                                                           o => (o is Task c && c.ArgumentCount == 2))
                              .Arguments("x")
                              .Documentation("type testing", "Succeeds when its argument is 2-argument task");
            g["Empty"] = Cons.Empty;

            g["CountAttempts"] = new GeneralPrimitive("CountAttempts", (args, o, bindings, p, k) =>
            {
                ArgumentCountException.Check("CountAttempts", 1, args);
                ArgumentInstantiationException.Check("CountAttempts", args[0], false, bindings, args);
                int count = 0;
                while (true)
                {
                    if (k(o,
                          BindingList <LogicVariable> .Bind(bindings.Unifications, (LogicVariable)args[0], count++),
                          bindings.State,
                          p))
                    {
                        return(true);
                    }
                }
                // ReSharper disable once FunctionNeverReturns
            }).Arguments("?count")
                                 .Documentation("control flow", "Binds ?count to 0, then to increasing numbers each time the system backtracks to the call.  Used in a loop to run something repeatedly: [CountAttempts ?count] [DoSomething] [= ?count 100] will run DoSomething until ?count is 100.");

            Documentation.SectionIntroduction("randomization",
                                              "Tasks that choose random numbers or list elements.");

            g["RandomIntegerInclusive"] = new SimpleFunction <int, int, int>("RandomIntegerInclusive", Randomization.IntegerInclusive)
                                          .Arguments("min", "max", "?random")
                                          .Documentation("randomization", "Sets ?random to a random integer such that min <= ?random <= max");

            g["RandomIntegerExclusive"] = new SimpleFunction <int, int, int>("RandomIntegerExclusive", Randomization.IntegerExclusive)
                                          .Arguments("min", "max", "?random")
                                          .Documentation("randomization", "Sets ?random to a random integer such that min <= ?random < max");

            g["RandomElement"] = new GeneralPredicate <IList, object>(
                "RandomElement",
                (list, elt) => list.Contains(elt),
                list => list.BadShuffle().Cast <object>(),
                null,
                null)
                                 .Arguments("list", "?element")
                                 .Documentation("randomization", "Sets ?element to a random element of list.  If this is backtracked, it generates a random shuffle of the elements of this list.  However, not all shuffles are possible; it starts with a random element and moves to subsequent elements with a random step size.");

            Documentation.SectionIntroduction("string processing",
                                              "Predicates that test the spelling of strings.");

            g["Format"] = new SimpleFunction <string, object[], string>("Format", string.Format)
                          .Arguments("format_string, argument_list, ?formatted_string")
                          .Documentation("string processing",
                                         "True when formatted_string is the result of formatting format_string with the arguments.  This is just a wrapper for .NET's string.Format routine.");

            g["Downcase"] = new SimpleFunction <string, string>("DownCase", from =>
            {
                var b = new StringBuilder();
                foreach (var c in from)
                {
                    b.Append(char.ToLower(c));
                }
                return(b.ToString());
            })
                            .Arguments("string, ?downcased")
                            .Documentation("string processing",
                                           "True when downcased is the string with all alphabetic characters converted to lowercase.");

            g["Downcased"] = new SimpleFunction <string, string>("Downcased", from =>
            {
                var b = new StringBuilder();
                foreach (var c in from)
                {
                    b.Append(char.ToLower(c));
                }
                return(b.ToString());
            })
                             .Arguments("string, ?downcased")
                             .Documentation("string processing",
                                            "True when downcased is the string with all alphabetic characters converted to lowercase.");

            g["Upcased"] = new SimpleFunction <string, string>("Upcased", from =>
            {
                var b = new StringBuilder();
                foreach (var c in from)
                {
                    b.Append(char.ToUpper(c));
                }
                return(b.ToString());
            })
                           .Arguments("string, ?upcased")
                           .Documentation("string processing",
                                          "True when upcased is the string with all alphabetic characters converted to uppercase.");

            g["Capitalized"] = new SimpleFunction <string, string>("Downcase", from =>
            {
                var b           = new StringBuilder();
                var startOfWord = true;
                foreach (var c in from)
                {
                    b.Append(startOfWord ? char.ToUpper(c) : c);
                    startOfWord = c == ' ' || c == '_';
                }
                return(b.ToString());
            })
                               .Arguments("string, ?capitalized")
                               .Documentation("string processing",
                                              "True when capitalized is the a copy of string, which the start of each word capitalized.");


            g["StartsWithVowel"] = new SimplePredicate <object>("StartsWithVowel",
                                                                x =>
            {
                switch (x)
                {
                case string s:
                    return(StartsWithVowel(s));

                case string[] tokens:
                    return(tokens.Length > 0 && StartsWithVowel(tokens[0]));

                default:
                    return(false);
                }
            })
                                   .Arguments("string")
                                   .Documentation("string processing", "True if the string starts with a vowel.");

            g["NounSingularPlural"] =
                new GeneralPredicate <string, string>("NounSingularPlural", (s, p) => Inflection.PluralOfNoun(s) == p, s => new[] { Inflection.PluralOfNoun(s) }, p => new[] { Inflection.SingularOfNoun(p) }, null)
                .Arguments("?singular", "?plural")
                .Documentation("string processing", "True if ?plural is the English plural form of ?singular");

            Documentation.SectionIntroduction("StepRepl",
                                              "Tasks that control the behavior of StepRepl or whatever other game engine the Step code is running inside of.");

            g["EnvironmentOption"] = new SimpleNAryPredicate("EnvironmentOption",
                                                             arglist =>
            {
                ArgumentCountException.CheckAtLeast("EnvironmentOption", 1, arglist);
                var optionName = ArgumentTypeException.Cast <string>("EnvironmentOption", arglist[0], arglist);
                EnvironmentOption.Handle(optionName, arglist.Skip(1).ToArray());
                return(true);
            })
                                     .Arguments("argument", "...")
                                     .Documentation("StepRepl", "Asks StepRepl or whatever other program this Step code is running in to change its handling of step code.");

            g["Hashtable"] = new SimpleNAryFunction(
                "Hashtable",
                data =>
            {
                if ((data.Length % 2) != 0)
                {
                    throw new ArgumentException(
                        "Hashtable requires an odd number of arguments, one for the output and an equal number of keys and values");
                }
                var h = new Hashtable();
                for (var i = 0; i < data.Length; i += 2)
                {
                    h[data[i]] = data[i + 1];
                }
                return(h);
            })
                             .Arguments("?h")
                             .Documentation("data structures", "Creates a new empty hash table and stores it in ?h");

            g["Contains"] =
                new SimplePredicate <string, string>("Contains", (super, sub) => super.Contains(sub))
                .Arguments("string", "substring")
                .Documentation("string processing", "True if substring is a substring of string");

            HigherOrderBuiltins.DefineGlobals();
            ReflectionBuiltins.DefineGlobals();
            Documentation.DefineGlobals(Module.Global);
        }
Beispiel #19
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 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);
        }
Beispiel #20
0
 /// <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));
 }
Beispiel #21
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);
        }
Beispiel #22
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));
 }
        /// <inheritdoc />
        public override bool Call(object[] args, TextBuffer buffer, BindingEnvironment env, MethodCallFrame predecessor, Step.Continuation k)
        {
            ArgumentCountException.Check(Name, 1, args);
            var arg  = env.Resolve(args[0]);
            var text = arg as string[];

            if (buffer.WriteMode)
            {
                if (text == null)
                {
                    text = renderer(arg);
                }

                return(k(buffer.Append(text), env.Unifications, env.State, predecessor));
            }

            // Read mode
            if (arg is LogicVariable l)
            {
                var token = buffer.NextToken(out var newBuffer);
                if (token == null)
                {
                    return(false);
                }
                object value = token;
                switch (token)
                {
                case "null":
                    value = null;
                    break;

                case "true":
                    value = true;
                    break;

                case "false":
                    value = false;
                    break;

                default:
                    if (int.TryParse(token, out var iValue))
                    {
                        value = iValue;
                    }
                    else if (float.TryParse(token, out var fValue))
                    {
                        value = fValue;
                    }
                    break;
                }
                return(k(newBuffer,
                         BindingList <LogicVariable> .Bind(env.Unifications, l, value),
                         env.State, predecessor));
            }

            if (text == null)
            {
                text = renderer(arg);
            }
            return(buffer.Unify(text, out var result) &&
                   k(result, env.Unifications, env.State, predecessor));
        }