Mantis#1475. Thank you kindly, Kinoc for a patch that:
This patch brings the Yield Prolog in sync with the YP r669. Biggest item is support for functions asserta and assertz , providing dynamic databases.0.6.0-stable
parent
5ab5991676
commit
80079e14e3
|
@ -149,6 +149,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
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);
|
||||
}
|
||||
|
@ -982,11 +985,22 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
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)
|
||||
{
|
||||
Term = getValue(Term);
|
||||
// Debug: Should exhaustively check for all number types.
|
||||
return Term is int || Term is double;
|
||||
return YP.integer(Term) || YP.isFloat(Term);
|
||||
}
|
||||
|
||||
public static bool atomic(object Term)
|
||||
|
@ -1060,6 +1074,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
_outputStream = Console.Out;
|
||||
}
|
||||
|
||||
public static IEnumerable<bool> current_output(object Stream)
|
||||
{
|
||||
return YP.unify(Stream, _outputStream);
|
||||
}
|
||||
|
||||
public static void write(object x)
|
||||
{
|
||||
x = YP.getValue(x);
|
||||
|
@ -1108,6 +1127,93 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
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<IClause> clauses;
|
||||
if (!_predicatesStore.TryGetValue(nameArity, out clauses))
|
||||
// Create an entry for the nameArity.
|
||||
_predicatesStore[nameArity] = (clauses = new List<IClause>());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert values at the end of the set of facts for the predicate with the
|
||||
/// name and with arity values.Length.
|
||||
/// </summary>
|
||||
/// <param name="name">must be an Atom</param>
|
||||
/// <param name="values">the array of arguments to the fact predicate.
|
||||
/// It is an error if an value has an unbound variable.</param>
|
||||
public static void assertFact(Atom name, object[] values)
|
||||
{
|
||||
NameArity nameArity = new NameArity(name, values.Length);
|
||||
|
@ -1130,7 +1236,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
indexedAnswers.addAnswer(values);
|
||||
}
|
||||
|
||||
public static IEnumerable<bool> matchFact(Atom name, object[] arguments)
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="name">must be an Atom</param>
|
||||
/// <param name="arguments">an array of arity number of arguments</param>
|
||||
/// <returns>an iterator which you can use in foreach</returns>
|
||||
public static IEnumerable<bool> matchDynamic(Atom name, object[] arguments)
|
||||
{
|
||||
List<IClause> clauses;
|
||||
if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses))
|
||||
|
@ -1147,20 +1261,46 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
|
||||
/// <summary>
|
||||
/// Call match(arguments) for each IClause in clauses. We make this a separate
|
||||
/// function so that matchFact itself does not need to be an iterator object.
|
||||
/// function so that matchDynamic itself does not need to be an iterator object.
|
||||
/// </summary>
|
||||
/// <param name="clauses"></param>
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<bool> matchAllClauses(List<IClause> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is deprecated and just calls matchDynamic. This matches all clauses,
|
||||
/// not just the ones defined with assertFact.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<bool> matchFact(Atom name, object[] arguments)
|
||||
{
|
||||
return matchDynamic(name, arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This actually searches all clauses, not just
|
||||
/// the ones defined with assertFact, but we keep the name for
|
||||
/// backwards compatibility.
|
||||
/// </summary>
|
||||
/// <param name="name">must be an Atom</param>
|
||||
/// <param name="arguments">an array of arity number of arguments</param>
|
||||
public static void retractFact(Atom name, object[] arguments)
|
||||
{
|
||||
NameArity nameArity = new NameArity(name, arguments.Length);
|
||||
|
@ -1219,40 +1359,18 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
/// <returns></returns>
|
||||
public static IEnumerable<bool> 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<Variable> variableSetList = new List<Variable>();
|
||||
addUniqueVariables(Goal, variableSetList);
|
||||
Variable[] variableSet = variableSetList.ToArray();
|
||||
object Head = Functor.make("function", variableSet);
|
||||
|
||||
object Rule = new Functor2(Atom.RULE, Head, Goal);
|
||||
object RuleList = ListPair.make(new Functor2(Atom.F, Rule, Atom.NIL));
|
||||
StringWriter functionCode = new StringWriter();
|
||||
TextWriter saveOutputStream = _outputStream;
|
||||
try
|
||||
{
|
||||
tell(functionCode);
|
||||
Variable FunctionCode = new Variable();
|
||||
foreach (bool l1 in YPCompiler.makeFunctionPseudoCode(RuleList, FunctionCode))
|
||||
{
|
||||
if (YP.termEqual(FunctionCode, Atom.a("getDeclaringClass")))
|
||||
// Ignore getDeclaringClass since we have access to the one passed in.
|
||||
continue;
|
||||
|
||||
// Debug: should check if FunctionCode is a single call.
|
||||
YPCompiler.convertFunctionCSharp(FunctionCode);
|
||||
}
|
||||
told();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore after calling tell.
|
||||
_outputStream = saveOutputStream;
|
||||
}
|
||||
return YPCompiler.compileAnonymousFunction
|
||||
(functionCode.ToString(), variableSet.Length, declaringClass).match(variableSet);
|
||||
// Use Atom.F since it is ignored.
|
||||
return YPCompiler.compileAnonymousClause
|
||||
(Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet);
|
||||
#else
|
||||
Goal = YP.getValue(Goal);
|
||||
Atom name;
|
||||
object[] args;
|
||||
while (true)
|
||||
|
@ -1436,5 +1554,91 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An enumerator that wraps another enumerator in order to catch a PrologException.
|
||||
/// </summary>
|
||||
public class Catch : IEnumerator<bool>, IEnumerable<bool>
|
||||
{
|
||||
private IEnumerator<bool> _enumerator;
|
||||
private PrologException _exception = null;
|
||||
|
||||
public Catch(IEnumerable<bool> iterator)
|
||||
{
|
||||
_enumerator = iterator.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
return _enumerator.MoveNext();
|
||||
}
|
||||
catch (PrologException exception)
|
||||
{
|
||||
_exception = exception;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="Catcher"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<bool> 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<bool> GetEnumerator()
|
||||
{
|
||||
return (IEnumerator<bool>)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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue