From 6b7930104bdb845d3b9c085dc04f52b6446f23b1 Mon Sep 17 00:00:00 2001
From: lbsa71
Date: Tue, 24 Jun 2008 21:09:49 +0000
Subject: * Applied patch from Melanie, mantis issue #1581 - "Refactor LSL
language, api and compiler out of XEngine" "First stage in a major Script
Engine refactor, that will result in the LSL implementaions ebing
reconverged. Not there yet, but one major part is done." Thank you, Melanie!
---
.../Shared/Api/Runtime/YieldProlog/YP.cs | 1644 ++++++++++++++++++++
1 file changed, 1644 insertions(+)
create mode 100644 OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
(limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs')
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
new file mode 100644
index 0000000..74704aa
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
@@ -0,0 +1,1644 @@
+/*
+ * 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;
+
+namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog
+{
+ ///
+ /// YP has static methods for general functions in Yield Prolog such as
+ /// and .
+ ///
+ public class YP
+ {
+ private static Fail _fail = new Fail();
+ private static Repeat _repeat = new Repeat();
+ private static Dictionary> _predicatesStore =
+ new Dictionary>();
+ private static TextWriter _outputStream = System.Console.Out;
+ private static TextReader _inputStream = System.Console.In;
+ private static List _operatorTable = null;
+
+ ///
+ /// An IClause is used so that dynamic predicates can call match.
+ ///
+ public interface IClause
+ {
+ IEnumerable match(object[] args);
+ }
+
+ public static object getValue(object value)
+ {
+ if (value is Variable)
+ return ((Variable)value).getValue();
+ else
+ return value;
+ }
+
+ public static IEnumerable 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;
+ }
+ }
+
+ ///
+ /// This is used for the lookup key in _factStore.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ /// 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 an exception.
+ ///
+ ///
+ ///
+ 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);
+
+ return (int)term;
+ }
+
+ ///
+ /// 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 an exception.
+ ///
+ ///
+ ///
+ 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");
+
+ return Convert.ToDouble(term);
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ 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));
+ }
+
+ ///
+ /// The ISO standard returns an int.
+ ///
+ ///
+ ///
+ public static object floor(object x)
+ {
+ return (int)Math.Floor(convertDouble(x));
+ }
+
+ ///
+ /// The ISO standard returns an int.
+ ///
+ ///
+ ///
+ public static object truncate(object x)
+ {
+ return (int)Math.Truncate(convertDouble(x));
+ }
+
+ ///
+ /// The ISO standard returns an int.
+ ///
+ ///
+ ///
+ public static object round(object x)
+ {
+ return (int)Math.Round(convertDouble(x));
+ }
+
+ ///
+ /// The ISO standard returns an int.
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Return floating point, even if both arguments are integer.
+ ///
+ ///
+ ///
+ ///
+ 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 copy_term(object inTerm, object outTerm)
+ {
+ return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore()));
+ }
+
+ public static void addUniqueVariables(object term, List 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;
+ }
+
+ ///
+ /// Sort the array in place according to termLessThan. This does not remove duplicates
+ ///
+ ///
+ public static void sortArray(object[] array)
+ {
+ Array.Sort(array, YP.compareTerms);
+ }
+
+ ///
+ /// Sort the array in place according to termLessThan. This does not remove duplicates
+ ///
+ ///
+ public static void sortArray(List array)
+ {
+ array.Sort(YP.compareTerms);
+ }
+
+ ///
+ /// Sort List according to termLessThan, remove duplicates and unify with Sorted.
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable 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));
+ }
+
+
+
+ ///
+ /// Use YP.unify to unify each of the elements of the two arrays, and yield
+ /// once if they all unify.
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable unifyArrays(object[] array1, object[] array2)
+ {
+ if (array1.Length != array2.Length)
+ yield break;
+
+ IEnumerator[] iterators = new IEnumerator[array1.Length];
+ bool gotMatch = true;
+ int nIterators = 0;
+ // Try to bind all the arguments.
+ for (int i = 0; i < array1.Length; ++i)
+ {
+ IEnumerator iterator = YP.unify(array1[i], array2[i]).GetEnumerator();
+ iterators[nIterators++] = iterator;
+ // MoveNext() is true if YP.unify succeeds.
+ if (!iterator.MoveNext())
+ {
+ gotMatch = false;
+ break;
+ }
+ }
+
+ try
+ {
+ if (gotMatch)
+ yield return false;
+ }
+ finally
+ {
+ // Manually finalize all the iterators.
+ for (int i = 0; i < nIterators; ++i)
+ iterators[i].Dispose();
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ public static IEnumerable fail()
+ {
+ return _fail;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ public static IEnumerable succeed()
+ {
+ return new Succeed();
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ public static IEnumerable repeat()
+ {
+ return _repeat;
+ }
+
+ public static IEnumerable 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 Exception("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 (!atom(Name))
+ throw new Exception("Expected an atom. Got: " + Name.getValue());
+
+ return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
+ }
+
+ return YP.fail();
+ }
+
+ public static IEnumerable functor(object Term, object FunctorName, object Arity)
+ {
+ Term = YP.getValue(Term);
+ FunctorName = YP.getValue(FunctorName);
+ Arity = YP.getValue(Arity);
+
+ if (!(Term is Variable))
+ {
+ foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
+ {
+ foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
+ yield return false;
+ }
+ }
+ else
+ throw new NotImplementedException("Debug: must finish functor/3");
+ }
+
+ public static IEnumerable arg(object ArgNumber, object Term, object Value)
+ {
+ if (YP.var(ArgNumber))
+ throw new NotImplementedException("Debug: must finish arg/3");
+ else
+ {
+ int argNumberInt = convertInt(ArgNumber);
+ if (argNumberInt < 0)
+ throw new Exception("ArgNumber must be non-negative");
+ 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;
+ }
+
+ ///
+ /// 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).
+ ///
+ ///
+ ///
+ 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 current_op
+ (object Priority, object Specifier, object Operator)
+ {
+ if (_operatorTable == null)
+ {
+ // Initialize.
+ _operatorTable = new List();
+ _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
+ _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
+ _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
+ _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
+ _operatorTable.Add(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
+ _operatorTable.Add(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
+ _operatorTable.Add(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
+ _operatorTable.Add(new object[] { 900, Atom.a("fy"), Atom.a("\\+") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("==") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@<") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=..") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("<") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=<") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">") });
+ _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">=") });
+ _operatorTable.Add(new object[] { 600, Atom.a("xfy"), Atom.a(":") });
+ _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("+") });
+ _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("-") });
+ _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") });
+ _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("*") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("/") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("//") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
+ _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
+ _operatorTable.Add(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
+ _operatorTable.Add(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
+ _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("-") });
+ _operatorTable.Add(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.Add(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
+ }
+
+ object[] args = new object[] { Priority, Specifier, Operator };
+ foreach (object[] answer in _operatorTable)
+ {
+ foreach (bool l1 in YP.unifyArrays(args, answer))
+ yield return false;
+ }
+ }
+
+ public static IEnumerable atom_length(object atom, object Length)
+ {
+ return YP.unify(Length, ((Atom)YP.getValue(atom))._name.Length);
+ }
+
+ public static IEnumerable atom_concat(object Start, object End, object Whole)
+ {
+ // Debug: Should implement for var(Start) which is a kind of search.
+ // Debug: Should we try to preserve the _declaringClass?
+ return YP.unify(Whole, Atom.a(((Atom)YP.getValue(Start))._name +
+ ((Atom)YP.getValue(End))._name));
+ }
+
+ public static IEnumerable sub_atom
+ (object atom, object Before, object Length, object After, object Sub_atom)
+ {
+ // Debug: Should implement for var(atom) which is a kind of search.
+ // Debug: Should we try to preserve the _declaringClass?
+ Atom atomAtom = (Atom)YP.getValue(atom);
+ int beforeInt = YP.convertInt(Before);
+ int lengthInt = YP.convertInt(Length);
+ if (beforeInt < 0)
+ throw new Exception("Before must be non-negative");
+ if (lengthInt < 0)
+ throw new Exception("Length must be non-negative");
+ int afterInt = atomAtom._name.Length - (beforeInt + lengthInt);
+ if (afterInt >= 0)
+ {
+ foreach (bool l1 in YP.unify(After, afterInt))
+ {
+ foreach (bool l2 in YP.unify
+ (Sub_atom, Atom.a(atomAtom._name.Substring(beforeInt, lengthInt))))
+ yield return false;
+ }
+ }
+ }
+
+ public static IEnumerable atom_codes(object atom, object List)
+ {
+ atom = YP.getValue(atom);
+ List = YP.getValue(List);
+
+ if (nonvar(atom))
+ {
+ string name = ((Atom)atom)._name;
+ object codeList = Atom.NIL;
+ // Start from the back to make the list.
+ for (int i = name.Length - 1; i >= 0; --i)
+ codeList = new ListPair((int)name[i], codeList);
+ return YP.unify(List, codeList);
+ }
+ {
+ object[] codeArray = ListPair.toArray(List);
+ char[] charArray = new char[codeArray.Length];
+ for (int i = 0; i < codeArray.Length; ++i)
+ charArray[i] = (char)YP.convertInt(codeArray[i]);
+ return YP.unify(atom, Atom.a(new String(charArray)));
+ }
+ }
+
+ public static IEnumerable number_codes(object number, object List)
+ {
+ number = YP.getValue(number);
+ List = YP.getValue(List);
+
+ if (nonvar(number))
+ {
+ string numberString = null;
+ // Try converting to an int first.
+ int intNumber;
+ if (YP.getInt(number, out intNumber))
+ numberString = intNumber.ToString();
+ else
+ numberString = YP.doubleToString(YP.convertDouble(number));
+
+ object codeList = Atom.NIL;
+ // Start from the back to make the list.
+ for (int i = numberString.Length - 1; i >= 0; --i)
+ codeList = new ListPair((int)numberString[i], codeList);
+ return YP.unify(List, codeList);
+ }
+ {
+ object[] codeArray = ListPair.toArray(List);
+ char[] charArray = new char[codeArray.Length];
+ for (int i = 0; i < codeArray.Length; ++i)
+ charArray[i] = (char)YP.convertInt(codeArray[i]);
+ String numberString = new String(charArray);
+ // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
+ try
+ {
+ // Try an int first.
+ return YP.unify(number, Convert.ToInt32(numberString));
+ }
+ catch (FormatException) { }
+ return YP.unify(number, Convert.ToDouble(numberString));
+ }
+ }
+
+ ///
+ /// If term is an Atom or functor type, return its name.
+ /// Otherwise, return term.
+ ///
+ ///
+ ///
+ public static object getFunctorName(object term)
+ {
+ term = YP.getValue(term);
+ if (term is Functor1)
+ return ((Functor1)term)._name;
+ else if (term is Functor2)
+ return ((Functor2)term)._name;
+ else if (term is Functor3)
+ return ((Functor3)term)._name;
+ else if (term is Functor)
+ return ((Functor)term)._name;
+ else
+ return term;
+ }
+
+ ///
+ /// If term is an Atom or functor type, return an array of its args.
+ /// Otherwise, return an empty array.
+ ///
+ ///
+ ///
+ public static object[] getFunctorArgs(object term)
+ {
+ term = YP.getValue(term);
+ if (term is Functor1)
+ {
+ Functor1 functor = (Functor1)term;
+ return new object[] { functor._arg1 };
+ }
+ else if (term is Functor2)
+ {
+ Functor2 functor = (Functor2)term;
+ return new object[] { functor._arg1, functor._arg2 };
+ }
+ else if (term is Functor3)
+ {
+ Functor3 functor = (Functor3)term;
+ return new object[] { functor._arg1, functor._arg2, functor._arg3 };
+ }
+ else if (term is Functor) {
+ Functor functor = (Functor)term;
+ return functor._args;
+ }
+ else
+ return new object[0];
+ }
+
+ public static bool var(object Term)
+ {
+ return YP.getValue(Term) is Variable;
+ }
+
+ public static bool nonvar(object Term)
+ {
+ return !YP.var(Term);
+ }
+
+ public static bool atom(object Term)
+ {
+ return YP.getValue(Term) is Atom;
+ }
+
+ public static bool integer(object Term)
+ {
+ // Debug: Should exhaustively check for all integer types.
+ return getValue(Term) is int;
+ }
+
+ // Use isFloat instead of float because it is a reserved keyword.
+ public static bool isFloat(object Term)
+ {
+ // Debug: Should exhaustively check for all float types.
+ return getValue(Term) is double;
+ }
+
+ public static bool number(object Term)
+ {
+ return YP.integer(Term) || YP.isFloat(Term);
+ }
+
+ public static bool atomic(object Term)
+ {
+ return YP.atom(Term) || YP.number(Term);
+ }
+
+ public static bool compound(object Term)
+ {
+ Term = getValue(Term);
+ return Term is Functor1 || Term is Functor2 || Term is Functor3 || Term is Functor;
+ }
+
+ public static void see(object input)
+ {
+ input = YP.getValue(input);
+ if (input is TextReader)
+ {
+ _inputStream = (TextReader)input;
+ return;
+ }
+ else if (input is Atom)
+ {
+ _inputStream = new StreamReader(((Atom)input)._name);
+ return;
+ }
+ else if (input is String)
+ {
+ _inputStream = new StreamReader((String)input);
+ return;
+ }
+ else
+ throw new InvalidOperationException("Can't open stream for " + input);
+ }
+
+ public static void seen()
+ {
+ if (_inputStream == Console.In)
+ return;
+ _inputStream.Close();
+ _inputStream = Console.In;
+ }
+
+ public static void tell(object output)
+ {
+ output = YP.getValue(output);
+ if (output is TextWriter)
+ {
+ _outputStream = (TextWriter)output;
+ return;
+ }
+ else if (output is Atom)
+ {
+ _outputStream = new StreamWriter(((Atom)output)._name);
+ return;
+ }
+ else if (output is String)
+ {
+ _outputStream = new StreamWriter((String)output);
+ return;
+ }
+ else
+ throw new InvalidOperationException("Can't open stream for " + output);
+ }
+
+ public static void told()
+ {
+ if (_outputStream == Console.Out)
+ return;
+ _outputStream.Close();
+ _outputStream = Console.Out;
+ }
+
+ public static IEnumerable current_output(object Stream)
+ {
+ return YP.unify(Stream, _outputStream);
+ }
+
+ public static void write(object x)
+ {
+ x = YP.getValue(x);
+ if (x is double)
+ _outputStream.Write(doubleToString((double)x));
+ else
+ _outputStream.Write(x.ToString());
+ }
+
+ ///
+ /// Format x as a string, making sure that it will parse as an int later. I.e., for 1.0, don't just
+ /// use "1" which will parse as an int.
+ ///
+ ///
+ ///
+ private static string doubleToString(double x)
+ {
+ string xString = x.ToString();
+ // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
+ try
+ {
+ Convert.ToInt32(xString);
+ // The string will parse as an int, not a double, so re-format so that it does.
+ // Use float if possible, else exponential if it would be too big.
+ return x.ToString(x >= 100000.0 ? "E1" : "f1");
+ }
+ catch (FormatException)
+ {
+ // Assume it will parse as a double.
+ }
+ return xString;
+ }
+
+ public static void put_code(object x)
+ {
+ _outputStream.Write((char)YP.convertInt(x));
+ }
+
+ public static void nl()
+ {
+ _outputStream.WriteLine();
+ }
+
+ public static IEnumerable get_code(object code)
+ {
+ return YP.unify(code, _inputStream.Read());
+ }
+
+ public static void asserta(object Term, Type declaringClass)
+ {
+ assertDynamic(Term, declaringClass, true);
+ }
+
+ public static void assertz(object Term, Type declaringClass)
+ {
+ assertDynamic(Term, declaringClass, false);
+ }
+
+ public static void assertDynamic(object Term, Type declaringClass, bool prepend)
+ {
+ Term = getValue(Term);
+ if (Term is Variable)
+ throw new PrologException("instantiation_error", "Term to assert is an unbound variable");
+
+ Variable.CopyStore copyStore = new Variable.CopyStore();
+ object TermCopy = makeCopy(Term, copyStore);
+ object Head, Body;
+ if (TermCopy is Functor2 && ((Functor2)TermCopy)._name == Atom.RULE)
+ {
+ Head = YP.getValue(((Functor2)TermCopy)._arg1);
+ Body = YP.getValue(((Functor2)TermCopy)._arg2);
+ }
+ else
+ {
+ Head = TermCopy;
+ Body = Atom.a("true");
+ }
+
+ Atom name = getFunctorName(Head) as Atom;
+ if (name == null)
+ // name is a non-Atom, such as a number.
+ throw new PrologException
+ (new Functor2("type_error", Atom.a("callable"), Head), "Term to assert is not callable");
+ object[] args = getFunctorArgs(Head);
+ if (!isDynamic(name, args.Length))
+ throw new PrologException
+ (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"),
+ new Functor2(Atom.SLASH, name, args.Length)),
+ "Assert cannot modify static predicate " + name + "/" + args.Length);
+
+ if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true"))
+ {
+ // Debug: Until IndexedAnswers supports prepend, compile the fact so we can prepend it below.
+ if (!prepend)
+ {
+ // This is a fact with no unbound variables
+ // assertFact uses IndexedAnswers, so don't we don't need to compile.
+ assertFact(name, args);
+ return;
+ }
+ }
+
+ IClause clause = YPCompiler.compileAnonymousClause(Head, Body, declaringClass);
+
+ // Add the clause to the entry in _predicatesStore.
+ NameArity nameArity = new NameArity(name, args.Length);
+ List clauses;
+ if (!_predicatesStore.TryGetValue(nameArity, out clauses))
+ // Create an entry for the nameArity.
+ _predicatesStore[nameArity] = (clauses = new List());
+
+ if (prepend)
+ clauses.Insert(0, clause);
+ else
+ clauses.Add(clause);
+ }
+
+ private static bool isDynamic(Atom name, int arity)
+ {
+ if (arity == 2 && (name == Atom.a(",") || name == Atom.a(";") || name == Atom.DOT))
+ return false;
+ // Use the same mapping to static predicates in YP as the compiler.
+ foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable()))
+ return false;
+ // Debug: Do we need to check if name._module is null?
+ return true;
+ }
+
+ ///
+ /// Assert values at the end of the set of facts for the predicate with the
+ /// name and with arity values.Length.
+ ///
+ /// must be an Atom
+ /// the array of arguments to the fact predicate.
+ /// It is an error if an value has an unbound variable.
+ public static void assertFact(Atom name, object[] values)
+ {
+ NameArity nameArity = new NameArity(name, values.Length);
+ List clauses;
+ IndexedAnswers indexedAnswers;
+ if (!_predicatesStore.TryGetValue(nameArity, out clauses))
+ {
+ // Create an IndexedAnswers as the first clause of the predicate.
+ _predicatesStore[nameArity] = (clauses = new List());
+ clauses.Add(indexedAnswers = new IndexedAnswers());
+ }
+ else
+ {
+ indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers;
+ if (indexedAnswers == null)
+ // The latest clause is not an IndexedAnswers, so add one.
+ clauses.Add(indexedAnswers = new IndexedAnswers());
+ }
+
+ indexedAnswers.addAnswer(values);
+ }
+
+ ///
+ /// Match all clauses of the dynamic predicate with the name and with arity
+ /// arguments.Length.
+ /// It is an error if the predicate is not defined.
+ ///
+ /// must be an Atom
+ /// an array of arity number of arguments
+ /// an iterator which you can use in foreach
+ public static IEnumerable matchDynamic(Atom name, object[] arguments)
+ {
+ List clauses;
+ if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses))
+ throw new UndefinedPredicateException
+ ("Undefined fact: " + name + "/" + arguments.Length, name,
+ arguments.Length);
+
+ if (clauses.Count == 1)
+ // Usually there is only one clause, so return it without needing to wrap it in an iterator.
+ return clauses[0].match(arguments);
+ else
+ return matchAllClauses(clauses, arguments);
+ }
+
+ ///
+ /// Call match(arguments) for each IClause in clauses. We make this a separate
+ /// function so that matchDynamic itself does not need to be an iterator object.
+ ///
+ ///
+ ///
+ ///
+ private static IEnumerable matchAllClauses(List clauses, object[] arguments)
+ {
+ // Debug: If the clause asserts another clause into this same predicate, the iterator
+ // over clauses will be corrupted. Should we take the time to copy clauses?
+ foreach (IClause clause in clauses)
+ {
+ foreach (bool lastCall in clause.match(arguments))
+ {
+ yield return false;
+ if (lastCall)
+ // This happens after a cut in a clause.
+ yield break;
+ }
+ }
+ }
+
+ ///
+ /// This is deprecated and just calls matchDynamic. This matches all clauses,
+ /// not just the ones defined with assertFact.
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable matchFact(Atom name, object[] arguments)
+ {
+ return matchDynamic(name, arguments);
+ }
+
+ ///
+ /// This actually searches all clauses, not just
+ /// the ones defined with assertFact, but we keep the name for
+ /// backwards compatibility.
+ ///
+ /// must be an Atom
+ /// an array of arity number of arguments
+ public static void retractFact(Atom name, object[] arguments)
+ {
+ NameArity nameArity = new NameArity(name, arguments.Length);
+ List clauses;
+ if (!_predicatesStore.TryGetValue(nameArity, out clauses))
+ // Can't find, so ignore.
+ return;
+
+ foreach (object arg in arguments)
+ {
+ if (!YP.var(arg))
+ throw new InvalidOperationException("All arguments must be unbound");
+ }
+ // Set to a fresh empty IndexedAnswers.
+ _predicatesStore[nameArity] = (clauses = new List());
+ clauses.Add(new IndexedAnswers());
+ }
+
+ public static IEnumerable current_predicate(object NameSlashArity)
+ {
+ NameSlashArity = YP.getValue(NameSlashArity);
+ // First check if Name and Arity are nonvar so we can do a direct lookup.
+ if (YP.ground(NameSlashArity))
+ {
+ if (NameSlashArity is Functor2)
+ {
+ Functor2 NameArityFunctor = (Functor2)NameSlashArity;
+ if (NameArityFunctor._name == Atom.SLASH)
+ {
+ if (_predicatesStore.ContainsKey(new NameArity
+ ((Atom)YP.getValue(NameArityFunctor._arg1),
+ (int)YP.getValue(NameArityFunctor._arg2))))
+ // The predicate is defined.
+ yield return false;
+ }
+ }
+ yield break;
+ }
+
+ foreach (NameArity key in _predicatesStore.Keys)
+ {
+ foreach (bool l1 in YP.unify
+ (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity))
+ yield return false;
+ }
+ }
+
+ ///
+ /// Use YP.getFunctorName(Goal) and invoke the static method of this name in the
+ /// declaringClass, using arguments from YP.getFunctorArgs(Goal).
+ /// Note that Goal must be a simple functor, not a complex expression.
+ /// If not found, this throws UndefinedPredicateException.
+ ///
+ ///
+ /// the class for looking up default function references
+ ///
+ public static IEnumerable getIterator(object Goal, Type declaringClass)
+ {
+ Goal = YP.getValue(Goal);
+ if (Goal is Variable)
+ throw new PrologException("instantiation_error", "Goal to call is an unbound variable");
+#if true
+ List variableSetList = new List();
+ addUniqueVariables(Goal, variableSetList);
+ Variable[] variableSet = variableSetList.ToArray();
+
+ // Use Atom.F since it is ignored.
+ return YPCompiler.compileAnonymousClause
+ (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet);
+#else
+ Atom name;
+ object[] args;
+ while (true)
+ {
+ name = (Atom)YP.getFunctorName(Goal);
+ args = YP.getFunctorArgs(Goal);
+ if (name == Atom.HAT && args.Length == 2)
+ // Assume this is called from a bagof operation. Skip the leading qualifiers.
+ Goal = YP.getValue(((Functor2)Goal)._arg2);
+ else
+ break;
+ }
+ try
+ {
+ return (IEnumerable)declaringClass.InvokeMember
+ (name._name, BindingFlags.InvokeMethod, null, null, args);
+ }
+ catch (TargetInvocationException exception)
+ {
+ throw exception.InnerException;
+ }
+ catch (MissingMethodException)
+ {
+ throw new UndefinedPredicateException
+ ("Cannot find predicate function: " + name + "/" + args.Length + " in " +
+ declaringClass.FullName, name, args.Length);
+ }
+#endif
+ }
+
+ public static void throwException(object Term)
+ {
+ throw new PrologException(Term);
+ }
+
+ ///
+ /// script_event calls hosting script with events as a callback method.
+ ///
+ ///
+ ///
+ ///
+ public static void script_event(object script_event, object script_params)
+ {
+ string function = ((Atom)YP.getValue(script_event))._name;
+ object[] array = ListPair.toArray(script_params);
+ if (array == null)
+ return; // YP.fail();
+ if (array.Length > 1)
+ {
+ //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue
+ //(localID, itemID, function, array);
+ // sortArray(array);
+ }
+ //return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
+ }
+
+ ///
+ /// An enumerator that does zero loops.
+ ///
+ private class Fail : IEnumerator, IEnumerable
+ {
+ public bool MoveNext()
+ {
+ return false;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return (IEnumerator)this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool Current
+ {
+ get { return true; }
+ }
+
+ object IEnumerator.Current
+ {
+ get { return true; }
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///
+ /// An enumerator that does one iteration.
+ ///
+ private class Succeed : IEnumerator, IEnumerable
+ {
+ private bool _didIteration = false;
+
+ public bool MoveNext()
+ {
+ if (!_didIteration)
+ {
+ _didIteration = true;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return (IEnumerator)this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool Current
+ {
+ get { return false; }
+ }
+
+ object IEnumerator.Current
+ {
+ get { return false; }
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///
+ /// An enumerator that repeats forever.
+ ///
+ private class Repeat : IEnumerator, IEnumerable
+ {
+ public bool MoveNext()
+ {
+ return true;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return (IEnumerator)this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool Current
+ {
+ get { return false; }
+ }
+
+ object IEnumerator.Current
+ {
+ get { return false; }
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///
+ /// An enumerator that wraps another enumerator in order to catch a PrologException.
+ ///
+ public class Catch : IEnumerator, IEnumerable
+ {
+ private IEnumerator _enumerator;
+ private PrologException _exception = null;
+
+ public Catch(IEnumerable iterator)
+ {
+ _enumerator = iterator.GetEnumerator();
+ }
+
+ ///
+ /// Call _enumerator.MoveNext(). If it throws a PrologException, set _exception
+ /// and return false. After this returns false, call unifyExceptionOrThrow.
+ /// Assume that, after this returns false, it will not be called again.
+ ///
+ ///
+ public bool MoveNext()
+ {
+ try
+ {
+ return _enumerator.MoveNext();
+ }
+ catch (PrologException exception)
+ {
+ _exception = exception;
+ return false;
+ }
+ }
+
+ ///
+ /// Call this after MoveNext() returns false to check for an exception. If
+ /// MoveNext did not get a PrologException, don't yield.
+ /// Otherwise, unify the exception with Catcher and yield so the caller can
+ /// do the handler code. However, if can't unify with Catcher then throw the exception.
+ ///
+ ///
+ ///
+ public IEnumerable unifyExceptionOrThrow(object Catcher)
+ {
+ if (_exception != null)
+ {
+ bool didUnify = false;
+ foreach (bool l1 in YP.unify(_exception._term, Catcher))
+ {
+ didUnify = true;
+ yield return false;
+ }
+ if (!didUnify)
+ throw _exception;
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return (IEnumerator)this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool Current
+ {
+ get { return _enumerator.Current; }
+ }
+
+ object IEnumerator.Current
+ {
+ get { return _enumerator.Current; }
+ }
+
+ public void Dispose()
+ {
+ _enumerator.Dispose();
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
--
cgit v1.1