/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
C# | 2701 lines | 2417 code | 86 blank | 198 comment | 134 complexity | 61356a824155d23230738390536ee451 MD5 | raw file
Possible License(s): Unlicense, MIT, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * Copyright (C) 2007-2008, Jeff Thompson
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using System.Net.Sockets;
- using System.Text;
- using System.Text.RegularExpressions;
- using log4net;
- namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog
- {
- /// <summary>
- /// YP has static methods for general functions in Yield Prolog such as <see cref="getValue"/>
- /// and <see cref="unify"/>.
- /// </summary>
- public class YP
- {
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- private static Fail _fail = new Fail();
- private static Repeat _repeat = new Repeat();
- private static Dictionary<NameArity, List<IClause>> _predicatesStore =
- new Dictionary<NameArity, List<IClause>>();
- private static TextWriter _outputStream = System.Console.Out;
- private static TextReader _inputStream = System.Console.In;
- private static IndexedAnswers _operatorTable = null;
- private static Dictionary<string, object> _prologFlags = new Dictionary<string, object>();
- public const int MAX_ARITY = 255;
- /// <summary>
- /// An IClause is used so that dynamic predicates can call match.
- /// </summary>
- public interface IClause
- {
- IEnumerable<bool> match(object[] args);
- IEnumerable<bool> clause(object Head, object Body);
- }
- /// <summary>
- /// If value is a Variable, then return its getValue. Otherwise, just
- /// return value. You should call YP.getValue on any object that
- /// may be a Variable to get the value to pass to other functions in
- /// your system that are not part of Yield Prolog, such as math functions
- /// or file I/O.
- /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
- /// </summary>
- /// <param name="value"></param>
- /// <returns></returns>
- public static object getValue(object value)
- {
- if (value is Variable)
- return ((Variable)value).getValue();
- else
- return value;
- }
- /// <summary>
- /// If arg1 or arg2 is an object with a unify method (such as Variable or
- /// Functor) then just call its unify with the other argument. The object's
- /// unify method will bind the values or check for equals as needed.
- /// Otherwise, both arguments are "normal" (atomic) values so if they
- /// are equal then succeed (yield once), else fail (don't yield).
- /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
- /// </summary>
- /// <param name="arg1"></param>
- /// <param name="arg2"></param>
- /// <returns></returns>
- public static IEnumerable<bool> unify(object arg1, object arg2)
- {
- arg1 = getValue(arg1);
- arg2 = getValue(arg2);
- if (arg1 is IUnifiable)
- return ((IUnifiable)arg1).unify(arg2);
- else if (arg2 is IUnifiable)
- return ((IUnifiable)arg2).unify(arg1);
- else
- {
- // Arguments are "normal" types.
- if (arg1.Equals(arg2))
- return new Succeed();
- else
- return _fail;
- }
- }
- /// <summary>
- /// This is used for the lookup key in _factStore.
- /// </summary>
- public struct NameArity
- {
- public readonly Atom _name;
- public readonly int _arity;
- public NameArity(Atom name, int arity)
- {
- _name = name;
- _arity = arity;
- }
- public override bool Equals(object obj)
- {
- if (obj is NameArity)
- {
- NameArity nameArity = (NameArity)obj;
- return nameArity._name.Equals(_name) && nameArity._arity.Equals(_arity);
- }
- else
- {
- return false;
- }
- }
- public override int GetHashCode()
- {
- return _name.GetHashCode() ^ _arity.GetHashCode();
- }
- }
- /// <summary>
- /// Convert term to an int.
- /// If term is a single-element List, use its first element
- /// (to handle the char types like "a").
- /// If can't convert, throw a PrologException for type_error evaluable (because this is only
- /// called from arithmetic functions).
- /// </summary>
- /// <param name="term"></param>
- /// <returns></returns>
- public static int convertInt(object term)
- {
- term = YP.getValue(term);
- if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
- YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
- // Assume it is a char type like "a".
- term = YP.getValue(((Functor2)term)._arg1);
- if (term is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Expected a number but the argument is an unbound variable");
- try
- {
- return (int)term;
- }
- catch (InvalidCastException)
- {
- throw new PrologException
- (new Functor2
- ("type_error", Atom.a("evaluable"),
- new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
- "Term must be an integer");
- }
- }
- /// <summary>
- /// Convert term to a double. This may convert an int to a double, etc.
- /// If term is a single-element List, use its first element
- /// (to handle the char types like "a").
- /// If can't convert, throw a PrologException for type_error evaluable (because this is only
- /// called from arithmetic functions).
- /// </summary>
- /// <param name="term"></param>
- /// <returns></returns>
- public static double convertDouble(object term)
- {
- term = YP.getValue(term);
- if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
- YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
- // Assume it is a char type like "a".
- term = YP.getValue(((Functor2)term)._arg1);
- if (term is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Expected a number but the argument is an unbound variable");
- try
- {
- return Convert.ToDouble(term);
- }
- catch (InvalidCastException)
- {
- throw new PrologException
- (new Functor2
- ("type_error", Atom.a("evaluable"),
- new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
- "Term must be an integer");
- }
- }
- /// <summary>
- /// If term is an integer, set intTerm.
- /// If term is a single-element List, use its first element
- /// (to handle the char types like "a"). Return true for success, false if can't convert.
- /// We use a success return value because throwing an exception is inefficient.
- /// </summary>
- /// <param name="term"></param>
- /// <returns></returns>
- public static bool getInt(object term, out int intTerm)
- {
- term = YP.getValue(term);
- if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
- YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
- // Assume it is a char type like "a".
- term = YP.getValue(((Functor2)term)._arg1);
- if (term is int)
- {
- intTerm = (int)term;
- return true;
- }
- intTerm = 0;
- return false;
- }
- public static bool equal(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x == (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) == YP.convertDouble(y);
- }
- public static bool notEqual(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x != (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) != YP.convertDouble(y);
- }
- public static bool greaterThan(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x > (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) > YP.convertDouble(y);
- }
- public static bool lessThan(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x < (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) < YP.convertDouble(y);
- }
- public static bool greaterThanOrEqual(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x >= (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) >= YP.convertDouble(y);
- }
- public static bool lessThanOrEqual(object x, object y)
- {
- x = YP.getValue(x);
- if (x is DateTime)
- return (DateTime)x <= (DateTime)YP.getValue(y);
- // Assume convertDouble converts an int to a double perfectly.
- return YP.convertDouble(x) <= YP.convertDouble(y);
- }
- public static object negate(object x)
- {
- int intX;
- if (getInt(x, out intX))
- return -intX;
- return -convertDouble(x);
- }
- public static object abs(object x)
- {
- int intX;
- if (getInt(x, out intX))
- return Math.Abs(intX);
- return Math.Abs(convertDouble(x));
- }
- public static object sign(object x)
- {
- int intX;
- if (getInt(x, out intX))
- return Math.Sign(intX);
- return Math.Sign(convertDouble(x));
- }
- // Use toFloat instead of float because it is a reserved keyword.
- public static object toFloat(object x)
- {
- return convertDouble(x);
- }
- /// <summary>
- /// The ISO standard returns an int.
- /// </summary>
- /// <param name="x"></param>
- /// <returns></returns>
- public static object floor(object x)
- {
- return (int)Math.Floor(convertDouble(x));
- }
- /// <summary>
- /// The ISO standard returns an int.
- /// </summary>
- /// <param name="x"></param>
- /// <returns></returns>
- public static object truncate(object x)
- {
- return (int)Math.Truncate(convertDouble(x));
- }
- /// <summary>
- /// The ISO standard returns an int.
- /// </summary>
- /// <param name="x"></param>
- /// <returns></returns>
- public static object round(object x)
- {
- return (int)Math.Round(convertDouble(x));
- }
- /// <summary>
- /// The ISO standard returns an int.
- /// </summary>
- /// <param name="x"></param>
- /// <returns></returns>
- public static object ceiling(object x)
- {
- return (int)Math.Ceiling(convertDouble(x));
- }
- public static object sin(object x)
- {
- return Math.Sin(YP.convertDouble(x));
- }
- public static object cos(object x)
- {
- return Math.Cos(YP.convertDouble(x));
- }
- public static object atan(object x)
- {
- return Math.Atan(YP.convertDouble(x));
- }
- public static object exp(object x)
- {
- return Math.Exp(YP.convertDouble(x));
- }
- public static object log(object x)
- {
- return Math.Log(YP.convertDouble(x));
- }
- public static object sqrt(object x)
- {
- return Math.Sqrt(convertDouble(x));
- }
- public static object bitwiseComplement(object x)
- {
- return ~YP.convertInt(x);
- }
- public static object add(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return intX + intY;
- return convertDouble(x) + convertDouble(y);
- }
- public static object subtract(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return intX - intY;
- return convertDouble(x) - convertDouble(y);
- }
- public static object multiply(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return intX * intY;
- return convertDouble(x) * convertDouble(y);
- }
- /// <summary>
- /// Return floating point, even if both arguments are integer.
- /// </summary>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <returns></returns>
- public static object divide(object x, object y)
- {
- return convertDouble(x) / convertDouble(y);
- }
- public static object intDivide(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return intX / intY;
- // Still allow passing a double, but treat as an int.
- return (int)convertDouble(x) / (int)convertDouble(y);
- }
- public static object mod(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return intX % intY;
- // Still allow passing a double, but treat as an int.
- return (int)convertDouble(x) % (int)convertDouble(y);
- }
- public static object pow(object x, object y)
- {
- return Math.Pow(YP.convertDouble(x), YP.convertDouble(y));
- }
- public static object bitwiseShiftRight(object x, object y)
- {
- return YP.convertInt(x) >> YP.convertInt(y);
- }
- public static object bitwiseShiftLeft(object x, object y)
- {
- return YP.convertInt(x) << YP.convertInt(y);
- }
- public static object bitwiseAnd(object x, object y)
- {
- return YP.convertInt(x) & YP.convertInt(y);
- }
- public static object bitwiseOr(object x, object y)
- {
- return YP.convertInt(x) | YP.convertInt(y);
- }
- public static object min(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return Math.Min(intX, intY);
- return Math.Min(convertDouble(x), convertDouble(y));
- }
- public static object max(object x, object y)
- {
- int intX, intY;
- if (getInt(x, out intX) && getInt(y, out intY))
- return Math.Max(intX, intY);
- return Math.Max(convertDouble(x), convertDouble(y));
- }
- public static IEnumerable<bool> copy_term(object inTerm, object outTerm)
- {
- return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore()));
- }
- public static void addUniqueVariables(object term, List<Variable> variableSet)
- {
- term = YP.getValue(term);
- if (term is IUnifiable)
- ((IUnifiable)term).addUniqueVariables(variableSet);
- }
- public static object makeCopy(object term, Variable.CopyStore copyStore)
- {
- term = YP.getValue(term);
- if (term is IUnifiable)
- return ((IUnifiable)term).makeCopy(copyStore);
- else
- // term is a "normal" type. Assume it is ground.
- return term;
- }
- /// <summary>
- /// Sort the array in place according to termLessThan. This does not remove duplicates
- /// </summary>
- /// <param name="array"></param>
- public static void sortArray(object[] array)
- {
- Array.Sort(array, YP.compareTerms);
- }
- /// <summary>
- /// Sort the array in place according to termLessThan. This does not remove duplicates
- /// </summary>
- /// <param name="array"></param>
- public static void sortArray(List<object> array)
- {
- array.Sort(YP.compareTerms);
- }
- /// <summary>
- /// Sort List according to termLessThan, remove duplicates and unify with Sorted.
- /// </summary>
- /// <param name="List"></param>
- /// <param name="Sorted"></param>
- /// <returns></returns>
- public static IEnumerable<bool> sort(object List, object Sorted)
- {
- object[] array = ListPair.toArray(List);
- if (array == null)
- return YP.fail();
- if (array.Length > 1)
- sortArray(array);
- return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
- }
- /// <summary>
- /// Use YP.unify to unify each of the elements of the two arrays, and yield
- /// once if they all unify.
- /// </summary>
- /// <param name="array1"></param>
- /// <param name="array2"></param>
- /// <returns></returns>
- public static IEnumerable<bool> unifyArrays(object[] array1, object[] array2)
- {
- if (array1.Length != array2.Length)
- yield break;
- IEnumerator<bool>[] iterators = new IEnumerator<bool>[array1.Length];
- bool gotMatch = true;
- int nIterators = 0;
- // Try to bind all the arguments.
- for (int i = 0; i < array1.Length; ++i)
- {
- IEnumerator<bool> iterator = YP.unify(array1[i], array2[i]).GetEnumerator();
- iterators[nIterators++] = iterator;
- // MoveNext() is true if YP.unify succeeds.
- if (!iterator.MoveNext())
- {
- gotMatch = false;
- break;
- }
- }
- int z = 0;
- try
- {
- if (gotMatch)
- yield return false;
- }
- finally
- {
- // Manually finalize all the iterators.
- for (z = 0; z < nIterators; ++z)
- iterators[z].Dispose();
- }
- }
- /// <summary>
- /// Return an iterator (which you can use in a for-in loop) which does
- /// zero iterations. This returns a pre-existing iterator which is
- /// more efficient than letting the compiler generate a new one.
- /// </summary>
- /// <returns></returns>
- public static IEnumerable<bool> fail()
- {
- return _fail;
- }
- /// <summary>
- /// Return an iterator (which you can use in a for-in loop) which does
- /// one iteration. This returns a pre-existing iterator which is
- /// more efficient than letting the compiler generate a new one.
- /// </summary>
- /// <returns></returns>
- public static IEnumerable<bool> succeed()
- {
- return new Succeed();
- }
- /// <summary>
- /// Return an iterator (which you can use in a for-in loop) which repeats
- /// indefinitely. This returns a pre-existing iterator which is
- /// more efficient than letting the compiler generate a new one.
- /// </summary>
- /// <returns></returns>
- public static IEnumerable<bool> repeat()
- {
- return _repeat;
- }
- // disable warning on l1, don't see how we can
- // code this differently
- #pragma warning disable 0168, 0219
- public static IEnumerable<bool> univ(object Term, object List)
- {
- Term = YP.getValue(Term);
- List = YP.getValue(List);
- if (nonvar(Term))
- return YP.unify(new ListPair
- (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List);
- Variable Name = new Variable();
- Variable ArgList = new Variable();
- foreach (bool l1 in new ListPair(Name, ArgList).unify(List))
- {
- object[] args = ListPair.toArray(ArgList);
- if (args == null)
- throw new PrologException
- (new Functor2("type_error", Atom.a("list"), ArgList),
- "Expected a list. Got: " + ArgList.getValue());
- if (args.Length == 0)
- // Return the Name, even if it is not an Atom.
- return YP.unify(Term, Name);
- if (args.Length > MAX_ARITY)
- throw new PrologException
- (new Functor1("representation_error", Atom.a("max_arity")),
- "Functor arity " + args.Length + " may not be greater than " + MAX_ARITY);
- if (!atom(Name))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), Name),
- "Expected an atom. Got: " + Name.getValue());
- return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
- }
- return YP.fail();
- }
- public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity)
- {
- Term = YP.getValue(Term);
- FunctorName = YP.getValue(FunctorName);
- Arity = YP.getValue(Arity);
- if (Term is Variable)
- {
- if (FunctorName is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 2 FunctorName is an unbound variable");
- if (Arity is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 3 Arity is an unbound variable");
- if (!(Arity is int))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer");
- if (!YP.atomic(FunctorName))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic");
- if ((int)Arity < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity),
- "Arity may not be less than zero");
- else if ((int)Arity == 0)
- {
- // Just unify Term with the atomic FunctorName.
- foreach (bool l1 in YP.unify(Term, FunctorName))
- yield return false;
- }
- else
- {
- if ((int)Arity > MAX_ARITY)
- throw new PrologException
- (new Functor1("representation_error", Atom.a("max_arity")),
- "Functor arity " + Arity + " may not be greater than " + MAX_ARITY);
- if (!(FunctorName is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom");
- // Construct a functor with unbound variables.
- object[] args = new object[(int)Arity];
- for (int i = 0; i < args.Length; ++i)
- args[i] = new Variable();
- foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args)))
- yield return false;
- }
- }
- else
- {
- foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
- {
- foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
- yield return false;
- }
- }
- }
- public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value)
- {
- if (var(ArgNumber))
- throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable");
- int argNumberInt;
- if (!getInt(ArgNumber, out argNumberInt))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), ArgNumber), "Arg 1 ArgNumber must be integer");
- if (argNumberInt < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), argNumberInt),
- "ArgNumber may not be less than zero");
- if (YP.var(Term))
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 2 Term is an unbound variable");
- if (!YP.compound(Term))
- throw new PrologException
- (new Functor2("type_error", Atom.a("compound"), Term), "Arg 2 Term must be compound");
- object[] termArgs = YP.getFunctorArgs(Term);
- // Silently fail if argNumberInt is out of range.
- if (argNumberInt >= 1 && argNumberInt <= termArgs.Length)
- {
- // The first ArgNumber is at 1, not 0.
- foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1]))
- yield return false;
- }
- }
- public static bool termEqual(object Term1, object Term2)
- {
- Term1 = YP.getValue(Term1);
- if (Term1 is IUnifiable)
- return ((IUnifiable)Term1).termEqual(Term2);
- return Term1.Equals(YP.getValue(Term2));
- }
- public static bool termNotEqual(object Term1, object Term2)
- {
- return !termEqual(Term1, Term2);
- }
- public static bool termLessThan(object Term1, object Term2)
- {
- Term1 = YP.getValue(Term1);
- Term2 = YP.getValue(Term2);
- int term1TypeCode = getTypeCode(Term1);
- int term2TypeCode = getTypeCode(Term2);
- if (term1TypeCode != term2TypeCode)
- return term1TypeCode < term2TypeCode;
- // The terms are the same type code.
- if (term1TypeCode == -2)
- {
- // Variable.
- // We always check for equality first because we want to be sure
- // that less than returns false if the terms are equal, in
- // case that the less than check really behaves like less than or equal.
- if ((Variable)Term1 != (Variable)Term2)
- // The hash code should be unique to a Variable object.
- return Term1.GetHashCode() < Term2.GetHashCode();
- return false;
- }
- if (term1TypeCode == 0)
- return ((Atom)Term1)._name.CompareTo(((Atom)Term2)._name) < 0;
- if (term1TypeCode == 1)
- return ((Functor1)Term1).lessThan((Functor1)Term2);
- if (term1TypeCode == 2)
- return ((Functor2)Term1).lessThan((Functor2)Term2);
- if (term1TypeCode == 3)
- return ((Functor3)Term1).lessThan((Functor3)Term2);
- if (term1TypeCode == 4)
- return ((Functor)Term1).lessThan((Functor)Term2);
- // Type code is -1 for general objects. First compare their type names.
- // Note that this puts Double before Int32 as required by ISO Prolog.
- string term1TypeName = Term1.GetType().ToString();
- string term2TypeName = Term2.GetType().ToString();
- if (term1TypeName != term2TypeName)
- return term1TypeName.CompareTo(term2TypeName) < 0;
- // The terms are the same type name.
- if (Term1 is int)
- return (int)Term1 < (int)Term2;
- else if (Term1 is double)
- return (double)Term1 < (double)Term2;
- else if (Term1 is DateTime)
- return (DateTime)Term1 < (DateTime)Term2;
- else if (Term1 is String)
- return ((String)Term1).CompareTo((String)Term2) < 0;
- // Debug: Should we try arrays, etc.?
- if (!Term1.Equals(Term2))
- // Could be equal or greater than.
- return Term1.GetHashCode() < Term2.GetHashCode();
- return false;
- }
- /// <summary>
- /// Type code is -2 if term is a Variable, 0 if it is an Atom,
- /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3,
- /// 4 if it is Functor.
- /// Otherwise, type code is -1.
- /// This does not call YP.getValue(term).
- /// </summary>
- /// <param name="term"></param>
- /// <returns></returns>
- private static int getTypeCode(object term)
- {
- if (term is Variable)
- return -2;
- else if (term is Atom)
- return 0;
- else if (term is Functor1)
- return 1;
- else if (term is Functor2)
- return 2;
- else if (term is Functor3)
- return 3;
- else if (term is Functor)
- return 4;
- else
- return -1;
- }
- public static bool termLessThanOrEqual(object Term1, object Term2)
- {
- if (YP.termEqual(Term1, Term2))
- return true;
- return YP.termLessThan(Term1, Term2);
- }
- public static bool termGreaterThan(object Term1, object Term2)
- {
- return !YP.termLessThanOrEqual(Term1, Term2);
- }
- public static bool termGreaterThanOrEqual(object Term1, object Term2)
- {
- // termLessThan should ensure that it returns false if terms are equal,
- // so that this would return true.
- return !YP.termLessThan(Term1, Term2);
- }
- public static int compareTerms(object Term1, object Term2)
- {
- if (YP.termEqual(Term1, Term2))
- return 0;
- else if (YP.termLessThan(Term1, Term2))
- return -1;
- else
- return 1;
- }
- public static bool ground(object Term)
- {
- Term = YP.getValue(Term);
- if (Term is IUnifiable)
- return ((IUnifiable)Term).ground();
- return true;
- }
- public static IEnumerable<bool> current_op
- (object Priority, object Specifier, object Operator)
- {
- if (_operatorTable == null)
- {
- // Initialize.
- _operatorTable = new IndexedAnswers(3);
- _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
- _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
- _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
- _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
- _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
- _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
- _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
- _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), Atom.a("\\+") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("==") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@<") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=..") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("<") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=<") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">") });
- _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">=") });
- _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), Atom.a(":") });
- _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("+") });
- _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("-") });
- _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") });
- _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("*") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("/") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("//") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
- _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
- _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
- _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
- _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") });
- _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") });
- // Debug: This is hacked in to run the Prolog test suite until we implement op/3.
- _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
- }
- return _operatorTable.match(new object[] { Priority, Specifier, Operator });
- }
- public static IEnumerable<bool> atom_length(object atom, object Length)
- {
- atom = YP.getValue(atom);
- Length = YP.getValue(Length);
- if (atom is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Expected atom(Arg1) but it is an unbound variable");
- if (!(atom is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
- if (!(Length is Variable))
- {
- if (!(Length is int))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
- if ((int)Length < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
- "Length must not be less than zero");
- }
- return YP.unify(Length, ((Atom)atom)._name.Length);
- }
- public static IEnumerable<bool> atom_concat(object Start, object End, object Whole)
- {
- // Debug: Should we try to preserve the _declaringClass?
- Start = YP.getValue(Start);
- End = YP.getValue(End);
- Whole = YP.getValue(Whole);
- if (Whole is Variable)
- {
- if (Start is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 1 Start and arg 3 Whole are both var");
- if (End is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 2 End and arg 3 Whole are both var");
- if (!(Start is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not an atom");
- if (!(End is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not an atom");
- foreach (bool l1 in YP.unify(Whole, Atom.a(((Atom)Start)._name + ((Atom)End)._name)))
- yield return false;
- }
- else
- {
- if (!(Whole is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), Whole), "Arg 3 Whole is not an atom");
- bool gotStartLength = false;
- int startLength = 0;
- if (!(Start is Variable))
- {
- if (!(Start is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not var or atom");
- startLength = ((Atom)Start)._name.Length;
- gotStartLength = true;
- }
- bool gotEndLength = false;
- int endLength = 0;
- if (!(End is Variable))
- {
- if (!(End is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not var or atom");
- endLength = ((Atom)End)._name.Length;
- gotEndLength = true;
- }
- // We are doing a search through all possible Start and End which concatenate to Whole.
- string wholeString = ((Atom)Whole)._name;
- for (int i = 0; i <= wholeString.Length; ++i)
- {
- // If we got either startLength or endLength, we know the lengths have to match so check
- // the lengths instead of constructing an Atom to do it.
- if (gotStartLength && startLength != i)
- continue;
- if (gotEndLength && endLength != wholeString.Length - i)
- continue;
- foreach (bool l1 in YP.unify(Start, Atom.a(wholeString.Substring(0, i))))
- {
- foreach (bool l2 in YP.unify(End, Atom.a(wholeString.Substring(i, wholeString.Length - i))))
- yield return false;
- }
- }
- }
- }
- public static IEnumerable<bool> sub_atom
- (object atom, object Before, object Length, object After, object Sub_atom)
- {
- // Debug: Should we try to preserve the _declaringClass?
- atom = YP.getValue(atom);
- Before = YP.getValue(Before);
- Length = YP.getValue(Length);
- After = YP.getValue(After);
- Sub_atom = YP.getValue(Sub_atom);
- if (atom is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Expected atom(Arg1) but it is an unbound variable");
- if (!(atom is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
- if (!(Sub_atom is Variable))
- {
- if (!(Sub_atom is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), Sub_atom), "Sub_atom is not var or atom");
- }
- bool beforeIsInt = false;
- bool lengthIsInt = false;
- bool afterIsInt = false;
- if (!(Before is Variable))
- {
- if (!(Before is int))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), Before), "Before must be var or integer");
- beforeIsInt = true;
- if ((int)Before < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), Before),
- "Before must not be less than zero");
- }
- if (!(Length is Variable))
- {
- if (!(Length is int))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
- lengthIsInt = true;
- if ((int)Length < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
- "Length must not be less than zero");
- }
- if (!(After is Variable))
- {
- if (!(After is int))
- throw new PrologException
- (new Functor2("type_error", Atom.a("integer"), After), "After must be var or integer");
- afterIsInt = true;
- if ((int)After < 0)
- throw new PrologException
- (new Functor2("domain_error", Atom.a("not_less_than_zero"), After),
- "After must not be less than zero");
- }
- Atom atomAtom = (Atom)atom;
- int atomLength = atomAtom._name.Length;
- if (beforeIsInt && lengthIsInt)
- {
- // Special case: the caller is just trying to extract a substring, so do it quickly.
- int xAfter = atomLength - (int)Before - (int)Length;
- if (xAfter >= 0)
- {
- foreach (bool l1 in YP.unify(After, xAfter))
- {
- foreach (bool l2 in YP.unify
- (Sub_atom, Atom.a(atomAtom._name.Substring((int)Before, (int)Length))))
- yield return false;
- }
- }
- }
- else if (afterIsInt && lengthIsInt)
- {
- // Special case: the caller is just trying to extract a substring, so do it quickly.
- int xBefore = atomLength - (int)After - (int)Length;
- if (xBefore >= 0)
- {
- foreach (bool l1 in YP.unify(Before, xBefore))
- {
- foreach (bool l2 in YP.unify
- (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, (int)Length))))
- yield return false;
- }
- }
- }
- else
- {
- // We are underconstrained and doing a search, so go through all possibilities.
- for (int xBefore = 0; xBefore <= atomLength; ++xBefore)
- {
- foreach (bool l1 in YP.unify(Before, xBefore))
- {
- for (int xLength = 0; xLength <= (atomLength - xBefore); ++xLength)
- {
- foreach (bool l2 in YP.unify(Length, xLength))
- {
- foreach (bool l3 in YP.unify(After, atomLength - (xBefore + xLength)))
- {
- foreach (bool l4 in YP.unify
- (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, xLength))))
- yield return false;
- }
- }
- }
- }
- }
- }
- }
- public static IEnumerable<bool> atom_chars(object atom, object List)
- {
- atom = YP.getValue(atom);
- List = YP.getValue(List);
- if (atom is Variable)
- {
- if (List is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 1 Atom and arg 2 List are both unbound variables");
- object[] codeArray = ListPair.toArray(List);
- if (codeArray == null)
- throw new PrologException
- (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
- char[] charArray = new char[codeArray.Length];
- for (int i = 0; i < codeArray.Length; ++i)
- {
- object listAtom = YP.getValue(codeArray[i]);
- if (listAtom is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 2 List has an element which is an unbound variable");
- if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1))
- throw new PrologException
- (new Functor2("type_error", Atom.a("character"), listAtom),
- "Arg 2 List has an element which is not a one character atom");
- charArray[i] = ((Atom)listAtom)._name[0];
- }
- return YP.unify(atom, Atom.a(new String(charArray)));
- }
- else
- {
- if (!(atom is Atom))
- throw new PrologException
- (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom");
- string atomString = ((Atom)atom)._name;
- object charList = Atom.NIL;
- // Start from the back to make the list.
- for (int i = atomString.Length - 1; i >= 0; --i)
- charList = new ListPair(Atom.a(atomString.Substring(i, 1)), charList);
- return YP.unify(List, charList);
- }
- }
- public static IEnumerable<bool> atom_codes(object atom, object List)
- {
- atom = YP.getValue(atom);
- List = YP.getValue(List);
- if (atom is Variable)
- {
- if (List is Variable)
- throw new PrologException(Atom.a("instantiation_error"),
- "Arg 1 Atom and arg 2 List are both unbound variables");
- object[] codeArray = ListPair.toArray(List);
- if (codeArray == null)
- throw new PrologException
- (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is…
Large files files are truncated, but you can click here to view the full file