diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs | 270 |
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 | } |