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
Charles Krinke 2008-06-07 15:43:16 +00:00
parent 5ab5991676
commit 80079e14e3
2 changed files with 1097 additions and 489 deletions

View File

@ -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();
}
}
}
}