aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs270
1 files changed, 237 insertions, 33 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
index a03cd30..68cfd3e 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
@@ -149,6 +149,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
149 YP.getValue(((Functor2)term)._arg2) == Atom.NIL) 149 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
150 // Assume it is a char type like "a". 150 // Assume it is a char type like "a".
151 term = YP.getValue(((Functor2)term)._arg1); 151 term = YP.getValue(((Functor2)term)._arg1);
152 if (term is Variable)
153 throw new PrologException(Atom.a("instantiation_error"),
154 "Expected a number but the argument is an unbound variable");
152 155
153 return Convert.ToDouble(term); 156 return Convert.ToDouble(term);
154 } 157 }
@@ -982,11 +985,22 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
982 return YP.getValue(Term) is Atom; 985 return YP.getValue(Term) is Atom;
983 } 986 }
984 987
988 public static bool integer(object Term)
989 {
990 // Debug: Should exhaustively check for all integer types.
991 return getValue(Term) is int;
992 }
993
994 // Use isFloat instead of float because it is a reserved keyword.
995 public static bool isFloat(object Term)
996 {
997 // Debug: Should exhaustively check for all float types.
998 return getValue(Term) is double;
999 }
1000
985 public static bool number(object Term) 1001 public static bool number(object Term)
986 { 1002 {
987 Term = getValue(Term); 1003 return YP.integer(Term) || YP.isFloat(Term);
988 // Debug: Should exhaustively check for all number types.
989 return Term is int || Term is double;
990 } 1004 }
991 1005
992 public static bool atomic(object Term) 1006 public static bool atomic(object Term)
@@ -1060,6 +1074,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1060 _outputStream = Console.Out; 1074 _outputStream = Console.Out;
1061 } 1075 }
1062 1076
1077 public static IEnumerable<bool> current_output(object Stream)
1078 {
1079 return YP.unify(Stream, _outputStream);
1080 }
1081
1063 public static void write(object x) 1082 public static void write(object x)
1064 { 1083 {
1065 x = YP.getValue(x); 1084 x = YP.getValue(x);
@@ -1108,6 +1127,93 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1108 return YP.unify(code, _inputStream.Read()); 1127 return YP.unify(code, _inputStream.Read());
1109 } 1128 }
1110 1129
1130 public static void asserta(object Term, Type declaringClass)
1131 {
1132 assertDynamic(Term, declaringClass, true);
1133 }
1134
1135 public static void assertz(object Term, Type declaringClass)
1136 {
1137 assertDynamic(Term, declaringClass, false);
1138 }
1139
1140 public static void assertDynamic(object Term, Type declaringClass, bool prepend)
1141 {
1142 Term = getValue(Term);
1143 if (Term is Variable)
1144 throw new PrologException("instantiation_error", "Term to assert is an unbound variable");
1145
1146 Variable.CopyStore copyStore = new Variable.CopyStore();
1147 object TermCopy = makeCopy(Term, copyStore);
1148 object Head, Body;
1149 if (TermCopy is Functor2 && ((Functor2)TermCopy)._name == Atom.RULE)
1150 {
1151 Head = YP.getValue(((Functor2)TermCopy)._arg1);
1152 Body = YP.getValue(((Functor2)TermCopy)._arg2);
1153 }
1154 else
1155 {
1156 Head = TermCopy;
1157 Body = Atom.a("true");
1158 }
1159
1160 Atom name = getFunctorName(Head) as Atom;
1161 if (name == null)
1162 // name is a non-Atom, such as a number.
1163 throw new PrologException
1164 (new Functor2("type_error", Atom.a("callable"), Head), "Term to assert is not callable");
1165 object[] args = getFunctorArgs(Head);
1166 if (!isDynamic(name, args.Length))
1167 throw new PrologException
1168 (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"),
1169 new Functor2(Atom.SLASH, name, args.Length)),
1170 "Assert cannot modify static predicate " + name + "/" + args.Length);
1171
1172 if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true"))
1173 {
1174 // Debug: Until IndexedAnswers supports prepend, compile the fact so we can prepend it below.
1175 if (!prepend)
1176 {
1177 // This is a fact with no unbound variables
1178 // assertFact uses IndexedAnswers, so don't we don't need to compile.
1179 assertFact(name, args);
1180 return;
1181 }
1182 }
1183
1184 IClause clause = YPCompiler.compileAnonymousClause(Head, Body, declaringClass);
1185
1186 // Add the clause to the entry in _predicatesStore.
1187 NameArity nameArity = new NameArity(name, args.Length);
1188 List<IClause> clauses;
1189 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1190 // Create an entry for the nameArity.
1191 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1192
1193 if (prepend)
1194 clauses.Insert(0, clause);
1195 else
1196 clauses.Add(clause);
1197 }
1198
1199 private static bool isDynamic(Atom name, int arity)
1200 {
1201 if (arity == 2 && (name == Atom.a(",") || name == Atom.a(";") || name == Atom.DOT))
1202 return false;
1203 // Use the same mapping to static predicates in YP as the compiler.
1204 foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable()))
1205 return false;
1206 // Debug: Do we need to check if name._module is null?
1207 return true;
1208 }
1209
1210 /// <summary>
1211 /// Assert values at the end of the set of facts for the predicate with the
1212 /// name and with arity values.Length.
1213 /// </summary>
1214 /// <param name="name">must be an Atom</param>
1215 /// <param name="values">the array of arguments to the fact predicate.
1216 /// It is an error if an value has an unbound variable.</param>
1111 public static void assertFact(Atom name, object[] values) 1217 public static void assertFact(Atom name, object[] values)
1112 { 1218 {
1113 NameArity nameArity = new NameArity(name, values.Length); 1219 NameArity nameArity = new NameArity(name, values.Length);
@@ -1130,7 +1236,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1130 indexedAnswers.addAnswer(values); 1236 indexedAnswers.addAnswer(values);
1131 } 1237 }
1132 1238
1133 public static IEnumerable<bool> matchFact(Atom name, object[] arguments) 1239 /// <summary>
1240 /// Match all clauses of the dynamic predicate with the name and with arity
1241 /// arguments.Length.
1242 /// It is an error if the predicate is not defined.
1243 /// </summary>
1244 /// <param name="name">must be an Atom</param>
1245 /// <param name="arguments">an array of arity number of arguments</param>
1246 /// <returns>an iterator which you can use in foreach</returns>
1247 public static IEnumerable<bool> matchDynamic(Atom name, object[] arguments)
1134 { 1248 {
1135 List<IClause> clauses; 1249 List<IClause> clauses;
1136 if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses)) 1250 if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses))
@@ -1147,20 +1261,46 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1147 1261
1148 /// <summary> 1262 /// <summary>
1149 /// Call match(arguments) for each IClause in clauses. We make this a separate 1263 /// Call match(arguments) for each IClause in clauses. We make this a separate
1150 /// function so that matchFact itself does not need to be an iterator object. 1264 /// function so that matchDynamic itself does not need to be an iterator object.
1151 /// </summary> 1265 /// </summary>
1152 /// <param name="clauses"></param> 1266 /// <param name="clauses"></param>
1153 /// <param name="arguments"></param> 1267 /// <param name="arguments"></param>
1154 /// <returns></returns> 1268 /// <returns></returns>
1155 private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments) 1269 private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments)
1156 { 1270 {
1271 // Debug: If the clause asserts another clause into this same predicate, the iterator
1272 // over clauses will be corrupted. Should we take the time to copy clauses?
1157 foreach (IClause clause in clauses) 1273 foreach (IClause clause in clauses)
1158 { 1274 {
1159 foreach (bool lastCall in clause.match(arguments)) 1275 foreach (bool lastCall in clause.match(arguments))
1276 {
1160 yield return false; 1277 yield return false;
1278 if (lastCall)
1279 // This happens after a cut in a clause.
1280 yield break;
1281 }
1161 } 1282 }
1162 } 1283 }
1163 1284
1285 /// <summary>
1286 /// This is deprecated and just calls matchDynamic. This matches all clauses,
1287 /// not just the ones defined with assertFact.
1288 /// </summary>
1289 /// <param name="name"></param>
1290 /// <param name="arguments"></param>
1291 /// <returns></returns>
1292 public static IEnumerable<bool> matchFact(Atom name, object[] arguments)
1293 {
1294 return matchDynamic(name, arguments);
1295 }
1296
1297 /// <summary>
1298 /// This actually searches all clauses, not just
1299 /// the ones defined with assertFact, but we keep the name for
1300 /// backwards compatibility.
1301 /// </summary>
1302 /// <param name="name">must be an Atom</param>
1303 /// <param name="arguments">an array of arity number of arguments</param>
1164 public static void retractFact(Atom name, object[] arguments) 1304 public static void retractFact(Atom name, object[] arguments)
1165 { 1305 {
1166 NameArity nameArity = new NameArity(name, arguments.Length); 1306 NameArity nameArity = new NameArity(name, arguments.Length);
@@ -1219,40 +1359,18 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1219 /// <returns></returns> 1359 /// <returns></returns>
1220 public static IEnumerable<bool> getIterator(object Goal, Type declaringClass) 1360 public static IEnumerable<bool> getIterator(object Goal, Type declaringClass)
1221 { 1361 {
1362 Goal = YP.getValue(Goal);
1363 if (Goal is Variable)
1364 throw new PrologException("instantiation_error", "Goal to call is an unbound variable");
1222#if true 1365#if true
1223 List<Variable> variableSetList = new List<Variable>(); 1366 List<Variable> variableSetList = new List<Variable>();
1224 addUniqueVariables(Goal, variableSetList); 1367 addUniqueVariables(Goal, variableSetList);
1225 Variable[] variableSet = variableSetList.ToArray(); 1368 Variable[] variableSet = variableSetList.ToArray();
1226 object Head = Functor.make("function", variableSet);
1227 1369
1228 object Rule = new Functor2(Atom.RULE, Head, Goal); 1370 // Use Atom.F since it is ignored.
1229 object RuleList = ListPair.make(new Functor2(Atom.F, Rule, Atom.NIL)); 1371 return YPCompiler.compileAnonymousClause
1230 StringWriter functionCode = new StringWriter(); 1372 (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet);
1231 TextWriter saveOutputStream = _outputStream;
1232 try
1233 {
1234 tell(functionCode);
1235 Variable FunctionCode = new Variable();
1236 foreach (bool l1 in YPCompiler.makeFunctionPseudoCode(RuleList, FunctionCode))
1237 {
1238 if (YP.termEqual(FunctionCode, Atom.a("getDeclaringClass")))
1239 // Ignore getDeclaringClass since we have access to the one passed in.
1240 continue;
1241
1242 // Debug: should check if FunctionCode is a single call.
1243 YPCompiler.convertFunctionCSharp(FunctionCode);
1244 }
1245 told();
1246 }
1247 finally
1248 {
1249 // Restore after calling tell.
1250 _outputStream = saveOutputStream;
1251 }
1252 return YPCompiler.compileAnonymousFunction
1253 (functionCode.ToString(), variableSet.Length, declaringClass).match(variableSet);
1254#else 1373#else
1255 Goal = YP.getValue(Goal);
1256 Atom name; 1374 Atom name;
1257 object[] args; 1375 object[] args;
1258 while (true) 1376 while (true)
@@ -1436,5 +1554,91 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
1436 throw new NotImplementedException(); 1554 throw new NotImplementedException();
1437 } 1555 }
1438 } 1556 }
1557
1558 /// <summary>
1559 /// An enumerator that wraps another enumerator in order to catch a PrologException.
1560 /// </summary>
1561 public class Catch : IEnumerator<bool>, IEnumerable<bool>
1562 {
1563 private IEnumerator<bool> _enumerator;
1564 private PrologException _exception = null;
1565
1566 public Catch(IEnumerable<bool> iterator)
1567 {
1568 _enumerator = iterator.GetEnumerator();
1569 }
1570
1571 /// <summary>
1572 /// Call _enumerator.MoveNext(). If it throws a PrologException, set _exception
1573 /// and return false. After this returns false, call unifyExceptionOrThrow.
1574 /// Assume that, after this returns false, it will not be called again.
1575 /// </summary>
1576 /// <returns></returns>
1577 public bool MoveNext()
1578 {
1579 try
1580 {
1581 return _enumerator.MoveNext();
1582 }
1583 catch (PrologException exception)
1584 {
1585 _exception = exception;
1586 return false;
1587 }
1588 }
1589
1590 /// <summary>
1591 /// Call this after MoveNext() returns false to check for an exception. If
1592 /// MoveNext did not get a PrologException, don't yield.
1593 /// Otherwise, unify the exception with Catcher and yield so the caller can
1594 /// do the handler code. However, if can't unify with Catcher then throw the exception.
1595 /// </summary>
1596 /// <param name="Catcher"></param>
1597 /// <returns></returns>
1598 public IEnumerable<bool> unifyExceptionOrThrow(object Catcher)
1599 {
1600 if (_exception != null)
1601 {
1602 bool didUnify = false;
1603 foreach (bool l1 in YP.unify(_exception._term, Catcher))
1604 {
1605 didUnify = true;
1606 yield return false;
1607 }
1608 if (!didUnify)
1609 throw _exception;
1610 }
1611 }
1612
1613 public IEnumerator<bool> GetEnumerator()
1614 {
1615 return (IEnumerator<bool>)this;
1616 }
1617
1618 IEnumerator IEnumerable.GetEnumerator()
1619 {
1620 return GetEnumerator();
1621 }
1622
1623 public bool Current
1624 {
1625 get { return _enumerator.Current; }
1626 }
1627
1628 object IEnumerator.Current
1629 {
1630 get { return _enumerator.Current; }
1631 }
1632
1633 public void Dispose()
1634 {
1635 _enumerator.Dispose();
1636 }
1637
1638 public void Reset()
1639 {
1640 throw new NotImplementedException();
1641 }
1642 }
1439 } 1643 }
1440} 1644}