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

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

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

            var i = 0;

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

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

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

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

            var i = 0;

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

            var parseBuffer = TextBuffer.MakeReadModeTextBuffer(text);

            return(task.Call(taskArgs, parseBuffer, env, predecessor,
                             (buffer, u, s, p) => buffer.ReadCompleted && k(output, u, s, p)));
        }
示例#3
0
        /// <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);
        }
    }
示例#4
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);
        }
    }
示例#5
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);
        }
示例#6
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);
        }
示例#7
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);
        }
示例#8
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));
        }
示例#9
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);
        }
示例#10
0
 private static bool Or(object[] args, TextBuffer o, BindingEnvironment e, MethodCallFrame predecessor, Step.Continuation k)
 => args.Any(call =>
 {
     var tuple = ArgumentTypeException.Cast <object[]>("Or", call, args);
     return(Eval(tuple, o, e, predecessor, k, "Or"));
 });
示例#11
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);
        }
示例#12
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);
        }
示例#13
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);
        }