aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
diff options
context:
space:
mode:
authorlbsa712008-06-24 21:09:49 +0000
committerlbsa712008-06-24 21:09:49 +0000
commit6b7930104bdb845d3b9c085dc04f52b6446f23b1 (patch)
tree05ee45781a455817fa400bb99f30f4d19d4eb1f8 /OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs
parentbased on positive feedback on performance of making keys fixed length (diff)
downloadopensim-SC_OLD-6b7930104bdb845d3b9c085dc04f52b6446f23b1.zip
opensim-SC_OLD-6b7930104bdb845d3b9c085dc04f52b6446f23b1.tar.gz
opensim-SC_OLD-6b7930104bdb845d3b9c085dc04f52b6446f23b1.tar.bz2
opensim-SC_OLD-6b7930104bdb845d3b9c085dc04f52b6446f23b1.tar.xz
* 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!
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs1644
1 files changed, 1644 insertions, 0 deletions
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 @@
1/*
2 * Copyright (C) 2007-2008, Jeff Thompson
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the copyright holder nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31using System;
32using System.Collections;
33using System.Collections.Generic;
34using System.IO;
35using System.Reflection;
36
37namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog
38{
39 /// <summary>
40 /// YP has static methods for general functions in Yield Prolog such as <see cref="getValue"/>
41 /// and <see cref="unify"/>.
42 /// </summary>
43 public class YP
44 {
45 private static Fail _fail = new Fail();
46 private static Repeat _repeat = new Repeat();
47 private static Dictionary<NameArity, List<IClause>> _predicatesStore =
48 new Dictionary<NameArity, List<IClause>>();
49 private static TextWriter _outputStream = System.Console.Out;
50 private static TextReader _inputStream = System.Console.In;
51 private static List<object[]> _operatorTable = null;
52
53 /// <summary>
54 /// An IClause is used so that dynamic predicates can call match.
55 /// </summary>
56 public interface IClause
57 {
58 IEnumerable<bool> match(object[] args);
59 }
60
61 public static object getValue(object value)
62 {
63 if (value is Variable)
64 return ((Variable)value).getValue();
65 else
66 return value;
67 }
68
69 public static IEnumerable<bool> unify(object arg1, object arg2)
70 {
71 arg1 = getValue(arg1);
72 arg2 = getValue(arg2);
73 if (arg1 is IUnifiable)
74 return ((IUnifiable)arg1).unify(arg2);
75 else if (arg2 is IUnifiable)
76 return ((IUnifiable)arg2).unify(arg1);
77 else
78 {
79 // Arguments are "normal" types.
80 if (arg1.Equals(arg2))
81 return new Succeed();
82 else
83 return _fail;
84 }
85 }
86
87 /// <summary>
88 /// This is used for the lookup key in _factStore.
89 /// </summary>
90 public struct NameArity
91 {
92 public readonly Atom _name;
93 public readonly int _arity;
94
95 public NameArity(Atom name, int arity)
96 {
97 _name = name;
98 _arity = arity;
99 }
100
101 public override bool Equals(object obj)
102 {
103 if (obj is NameArity)
104 {
105 NameArity nameArity = (NameArity)obj;
106 return nameArity._name.Equals(_name) && nameArity._arity.Equals(_arity);
107 }
108 else
109 {
110 return false;
111 }
112 }
113
114 public override int GetHashCode()
115 {
116 return _name.GetHashCode() ^ _arity.GetHashCode();
117 }
118 }
119
120 /// <summary>
121 /// Convert term to an int.
122 /// If term is a single-element List, use its first element
123 /// (to handle the char types like "a"). If can't convert, throw an exception.
124 /// </summary>
125 /// <param name="term"></param>
126 /// <returns></returns>
127 public static int convertInt(object term)
128 {
129 term = YP.getValue(term);
130 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
131 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
132 // Assume it is a char type like "a".
133 term = YP.getValue(((Functor2)term)._arg1);
134
135 return (int)term;
136 }
137
138 /// <summary>
139 /// Convert term to a double. This may convert an int to a double, etc.
140 /// If term is a single-element List, use its first element
141 /// (to handle the char types like "a"). If can't convert, throw an exception.
142 /// </summary>
143 /// <param name="term"></param>
144 /// <returns></returns>
145 public static double convertDouble(object term)
146 {
147 term = YP.getValue(term);
148 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
149 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
150 // Assume it is a char type like "a".
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");
155
156 return Convert.ToDouble(term);
157 }
158
159 /// <summary>
160 /// If term is an integer, set intTerm.
161 /// If term is a single-element List, use its first element
162 /// (to handle the char types like "a"). Return true for success, false if can't convert.
163 /// We use a success return value because throwing an exception is inefficient.
164 /// </summary>
165 /// <param name="term"></param>
166 /// <returns></returns>
167 public static bool getInt(object term, out int intTerm)
168 {
169 term = YP.getValue(term);
170 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
171 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
172 // Assume it is a char type like "a".
173 term = YP.getValue(((Functor2)term)._arg1);
174
175 if (term is int)
176 {
177 intTerm = (int)term;
178 return true;
179 }
180
181 intTerm = 0;
182 return false;
183 }
184
185 public static bool equal(object x, object y)
186 {
187 x = YP.getValue(x);
188 if (x is DateTime)
189 return (DateTime)x == (DateTime)YP.getValue(y);
190 // Assume convertDouble converts an int to a double perfectly.
191 return YP.convertDouble(x) == YP.convertDouble(y);
192 }
193
194 public static bool notEqual(object x, object y)
195 {
196 x = YP.getValue(x);
197 if (x is DateTime)
198 return (DateTime)x != (DateTime)YP.getValue(y);
199 // Assume convertDouble converts an int to a double perfectly.
200 return YP.convertDouble(x) != YP.convertDouble(y);
201 }
202
203 public static bool greaterThan(object x, object y)
204 {
205 x = YP.getValue(x);
206 if (x is DateTime)
207 return (DateTime)x > (DateTime)YP.getValue(y);
208 // Assume convertDouble converts an int to a double perfectly.
209 return YP.convertDouble(x) > YP.convertDouble(y);
210 }
211
212 public static bool lessThan(object x, object y)
213 {
214 x = YP.getValue(x);
215 if (x is DateTime)
216 return (DateTime)x < (DateTime)YP.getValue(y);
217 // Assume convertDouble converts an int to a double perfectly.
218 return YP.convertDouble(x) < YP.convertDouble(y);
219 }
220
221 public static bool greaterThanOrEqual(object x, object y)
222 {
223 x = YP.getValue(x);
224 if (x is DateTime)
225 return (DateTime)x >= (DateTime)YP.getValue(y);
226 // Assume convertDouble converts an int to a double perfectly.
227 return YP.convertDouble(x) >= YP.convertDouble(y);
228 }
229
230 public static bool lessThanOrEqual(object x, object y)
231 {
232 x = YP.getValue(x);
233 if (x is DateTime)
234 return (DateTime)x <= (DateTime)YP.getValue(y);
235 // Assume convertDouble converts an int to a double perfectly.
236 return YP.convertDouble(x) <= YP.convertDouble(y);
237 }
238
239 public static object negate(object x)
240 {
241 int intX;
242 if (getInt(x, out intX))
243 return -intX;
244 return -convertDouble(x);
245 }
246
247 public static object abs(object x)
248 {
249 int intX;
250 if (getInt(x, out intX))
251 return Math.Abs(intX);
252 return Math.Abs(convertDouble(x));
253 }
254
255 public static object sign(object x)
256 {
257 int intX;
258 if (getInt(x, out intX))
259 return Math.Sign(intX);
260 return Math.Sign(convertDouble(x));
261 }
262
263 /// <summary>
264 /// The ISO standard returns an int.
265 /// </summary>
266 /// <param name="x"></param>
267 /// <returns></returns>
268 public static object floor(object x)
269 {
270 return (int)Math.Floor(convertDouble(x));
271 }
272
273 /// <summary>
274 /// The ISO standard returns an int.
275 /// </summary>
276 /// <param name="x"></param>
277 /// <returns></returns>
278 public static object truncate(object x)
279 {
280 return (int)Math.Truncate(convertDouble(x));
281 }
282
283 /// <summary>
284 /// The ISO standard returns an int.
285 /// </summary>
286 /// <param name="x"></param>
287 /// <returns></returns>
288 public static object round(object x)
289 {
290 return (int)Math.Round(convertDouble(x));
291 }
292
293 /// <summary>
294 /// The ISO standard returns an int.
295 /// </summary>
296 /// <param name="x"></param>
297 /// <returns></returns>
298 public static object ceiling(object x)
299 {
300 return (int)Math.Ceiling(convertDouble(x));
301 }
302
303 public static object sin(object x)
304 {
305 return Math.Sin(YP.convertDouble(x));
306 }
307
308 public static object cos(object x)
309 {
310 return Math.Cos(YP.convertDouble(x));
311 }
312
313 public static object atan(object x)
314 {
315 return Math.Atan(YP.convertDouble(x));
316 }
317
318 public static object exp(object x)
319 {
320 return Math.Exp(YP.convertDouble(x));
321 }
322
323 public static object log(object x)
324 {
325 return Math.Log(YP.convertDouble(x));
326 }
327
328 public static object sqrt(object x)
329 {
330 return Math.Sqrt(convertDouble(x));
331 }
332
333 public static object bitwiseComplement(object x)
334 {
335 return ~YP.convertInt(x);
336 }
337
338 public static object add(object x, object y)
339 {
340 int intX, intY;
341 if (getInt(x, out intX) && getInt(y, out intY))
342 return intX + intY;
343 return convertDouble(x) + convertDouble(y);
344 }
345
346 public static object subtract(object x, object y)
347 {
348 int intX, intY;
349 if (getInt(x, out intX) && getInt(y, out intY))
350 return intX - intY;
351 return convertDouble(x) - convertDouble(y);
352 }
353
354 public static object multiply(object x, object y)
355 {
356 int intX, intY;
357 if (getInt(x, out intX) && getInt(y, out intY))
358 return intX * intY;
359 return convertDouble(x) * convertDouble(y);
360 }
361
362 /// <summary>
363 /// Return floating point, even if both arguments are integer.
364 /// </summary>
365 /// <param name="x"></param>
366 /// <param name="y"></param>
367 /// <returns></returns>
368 public static object divide(object x, object y)
369 {
370 return convertDouble(x) / convertDouble(y);
371 }
372
373 public static object intDivide(object x, object y)
374 {
375 int intX, intY;
376 if (getInt(x, out intX) && getInt(y, out intY))
377 return intX / intY;
378 // Still allow passing a double, but treat as an int.
379 return (int)convertDouble(x) / (int)convertDouble(y);
380 }
381
382 public static object mod(object x, object y)
383 {
384 int intX, intY;
385 if (getInt(x, out intX) && getInt(y, out intY))
386 return intX % intY;
387 // Still allow passing a double, but treat as an int.
388 return (int)convertDouble(x) % (int)convertDouble(y);
389 }
390
391 public static object pow(object x, object y)
392 {
393 return Math.Pow(YP.convertDouble(x), YP.convertDouble(y));
394 }
395
396 public static object bitwiseShiftRight(object x, object y)
397 {
398 return YP.convertInt(x) >> YP.convertInt(y);
399 }
400
401 public static object bitwiseShiftLeft(object x, object y)
402 {
403 return YP.convertInt(x) << YP.convertInt(y);
404 }
405
406 public static object bitwiseAnd(object x, object y)
407 {
408 return YP.convertInt(x) & YP.convertInt(y);
409 }
410
411 public static object bitwiseOr(object x, object y)
412 {
413 return YP.convertInt(x) | YP.convertInt(y);
414 }
415
416 public static object min(object x, object y)
417 {
418 int intX, intY;
419 if (getInt(x, out intX) && getInt(y, out intY))
420 return Math.Min(intX, intY);
421 return Math.Min(convertDouble(x), convertDouble(y));
422 }
423
424 public static object max(object x, object y)
425 {
426 int intX, intY;
427 if (getInt(x, out intX) && getInt(y, out intY))
428 return Math.Max(intX, intY);
429 return Math.Max(convertDouble(x), convertDouble(y));
430 }
431
432 public static IEnumerable<bool> copy_term(object inTerm, object outTerm)
433 {
434 return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore()));
435 }
436
437 public static void addUniqueVariables(object term, List<Variable> variableSet)
438 {
439 term = YP.getValue(term);
440 if (term is IUnifiable)
441 ((IUnifiable)term).addUniqueVariables(variableSet);
442 }
443
444 public static object makeCopy(object term, Variable.CopyStore copyStore)
445 {
446 term = YP.getValue(term);
447 if (term is IUnifiable)
448 return ((IUnifiable)term).makeCopy(copyStore);
449 else
450 // term is a "normal" type. Assume it is ground.
451 return term;
452 }
453
454 /// <summary>
455 /// Sort the array in place according to termLessThan. This does not remove duplicates
456 /// </summary>
457 /// <param name="array"></param>
458 public static void sortArray(object[] array)
459 {
460 Array.Sort(array, YP.compareTerms);
461 }
462
463 /// <summary>
464 /// Sort the array in place according to termLessThan. This does not remove duplicates
465 /// </summary>
466 /// <param name="array"></param>
467 public static void sortArray(List<object> array)
468 {
469 array.Sort(YP.compareTerms);
470 }
471
472 /// <summary>
473 /// Sort List according to termLessThan, remove duplicates and unify with Sorted.
474 /// </summary>
475 /// <param name="List"></param>
476 /// <param name="Sorted"></param>
477 /// <returns></returns>
478 public static IEnumerable<bool> sort(object List, object Sorted)
479 {
480 object[] array = ListPair.toArray(List);
481 if (array == null)
482 return YP.fail();
483 if (array.Length > 1)
484 sortArray(array);
485 return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
486 }
487
488
489
490 /// <summary>
491 /// Use YP.unify to unify each of the elements of the two arrays, and yield
492 /// once if they all unify.
493 /// </summary>
494 /// <param name="array1"></param>
495 /// <param name="array2"></param>
496 /// <returns></returns>
497 public static IEnumerable<bool> unifyArrays(object[] array1, object[] array2)
498 {
499 if (array1.Length != array2.Length)
500 yield break;
501
502 IEnumerator<bool>[] iterators = new IEnumerator<bool>[array1.Length];
503 bool gotMatch = true;
504 int nIterators = 0;
505 // Try to bind all the arguments.
506 for (int i = 0; i < array1.Length; ++i)
507 {
508 IEnumerator<bool> iterator = YP.unify(array1[i], array2[i]).GetEnumerator();
509 iterators[nIterators++] = iterator;
510 // MoveNext() is true if YP.unify succeeds.
511 if (!iterator.MoveNext())
512 {
513 gotMatch = false;
514 break;
515 }
516 }
517
518 try
519 {
520 if (gotMatch)
521 yield return false;
522 }
523 finally
524 {
525 // Manually finalize all the iterators.
526 for (int i = 0; i < nIterators; ++i)
527 iterators[i].Dispose();
528 }
529 }
530
531 /// <summary>
532 /// Return an iterator (which you can use in a for-in loop) which does
533 /// zero iterations. This returns a pre-existing iterator which is
534 /// more efficient than letting the compiler generate a new one.
535 /// </summary>
536 /// <returns></returns>
537 public static IEnumerable<bool> fail()
538 {
539 return _fail;
540 }
541
542 /// <summary>
543 /// Return an iterator (which you can use in a for-in loop) which does
544 /// one iteration. This returns a pre-existing iterator which is
545 /// more efficient than letting the compiler generate a new one.
546 /// </summary>
547 /// <returns></returns>
548 public static IEnumerable<bool> succeed()
549 {
550 return new Succeed();
551 }
552
553 /// <summary>
554 /// Return an iterator (which you can use in a for-in loop) which repeats
555 /// indefinitely. This returns a pre-existing iterator which is
556 /// more efficient than letting the compiler generate a new one.
557 /// </summary>
558 /// <returns></returns>
559 public static IEnumerable<bool> repeat()
560 {
561 return _repeat;
562 }
563
564 public static IEnumerable<bool> univ(object Term, object List)
565 {
566 Term = YP.getValue(Term);
567 List = YP.getValue(List);
568
569 if (nonvar(Term))
570 return YP.unify(new ListPair
571 (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List);
572
573 Variable Name = new Variable();
574 Variable ArgList = new Variable();
575 foreach (bool l1 in new ListPair(Name, ArgList).unify(List))
576 {
577 object[] args = ListPair.toArray(ArgList);
578 if (args == null)
579 throw new Exception("Expected a list. Got: " + ArgList.getValue());
580 if (args.Length == 0)
581 // Return the Name, even if it is not an Atom.
582 return YP.unify(Term, Name);
583 if (!atom(Name))
584 throw new Exception("Expected an atom. Got: " + Name.getValue());
585
586 return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
587 }
588
589 return YP.fail();
590 }
591
592 public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity)
593 {
594 Term = YP.getValue(Term);
595 FunctorName = YP.getValue(FunctorName);
596 Arity = YP.getValue(Arity);
597
598 if (!(Term is Variable))
599 {
600 foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
601 {
602 foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
603 yield return false;
604 }
605 }
606 else
607 throw new NotImplementedException("Debug: must finish functor/3");
608 }
609
610 public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value)
611 {
612 if (YP.var(ArgNumber))
613 throw new NotImplementedException("Debug: must finish arg/3");
614 else
615 {
616 int argNumberInt = convertInt(ArgNumber);
617 if (argNumberInt < 0)
618 throw new Exception("ArgNumber must be non-negative");
619 object[] termArgs = YP.getFunctorArgs(Term);
620 // Silently fail if argNumberInt is out of range.
621 if (argNumberInt >= 1 && argNumberInt <= termArgs.Length)
622 {
623 // The first ArgNumber is at 1, not 0.
624 foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1]))
625 yield return false;
626 }
627 }
628 }
629
630 public static bool termEqual(object Term1, object Term2)
631 {
632 Term1 = YP.getValue(Term1);
633 if (Term1 is IUnifiable)
634 return ((IUnifiable)Term1).termEqual(Term2);
635 return Term1.Equals(YP.getValue(Term2));
636 }
637
638 public static bool termNotEqual(object Term1, object Term2)
639 {
640 return !termEqual(Term1, Term2);
641 }
642
643 public static bool termLessThan(object Term1, object Term2)
644 {
645 Term1 = YP.getValue(Term1);
646 Term2 = YP.getValue(Term2);
647 int term1TypeCode = getTypeCode(Term1);
648 int term2TypeCode = getTypeCode(Term2);
649 if (term1TypeCode != term2TypeCode)
650 return term1TypeCode < term2TypeCode;
651
652 // The terms are the same type code.
653 if (term1TypeCode == -2)
654 {
655 // Variable.
656 // We always check for equality first because we want to be sure
657 // that less than returns false if the terms are equal, in
658 // case that the less than check really behaves like less than or equal.
659 if ((Variable)Term1 != (Variable)Term2)
660 // The hash code should be unique to a Variable object.
661 return Term1.GetHashCode() < Term2.GetHashCode();
662 return false;
663 }
664 if (term1TypeCode == 0)
665 return ((Atom)Term1)._name.CompareTo(((Atom)Term2)._name) < 0;
666 if (term1TypeCode == 1)
667 return ((Functor1)Term1).lessThan((Functor1)Term2);
668 if (term1TypeCode == 2)
669 return ((Functor2)Term1).lessThan((Functor2)Term2);
670 if (term1TypeCode == 3)
671 return ((Functor3)Term1).lessThan((Functor3)Term2);
672 if (term1TypeCode == 4)
673 return ((Functor)Term1).lessThan((Functor)Term2);
674
675 // Type code is -1 for general objects. First compare their type names.
676 // Note that this puts Double before Int32 as required by ISO Prolog.
677 string term1TypeName = Term1.GetType().ToString();
678 string term2TypeName = Term2.GetType().ToString();
679 if (term1TypeName != term2TypeName)
680 return term1TypeName.CompareTo(term2TypeName) < 0;
681
682 // The terms are the same type name.
683 if (Term1 is int)
684 return (int)Term1 < (int)Term2;
685 else if (Term1 is double)
686 return (double)Term1 < (double)Term2;
687 else if (Term1 is DateTime)
688 return (DateTime)Term1 < (DateTime)Term2;
689 else if (Term1 is String)
690 return ((String)Term1).CompareTo((String)Term2) < 0;
691 // Debug: Should we try arrays, etc.?
692
693 if (!Term1.Equals(Term2))
694 // Could be equal or greater than.
695 return Term1.GetHashCode() < Term2.GetHashCode();
696 return false;
697 }
698
699 /// <summary>
700 /// Type code is -2 if term is a Variable, 0 if it is an Atom,
701 /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3,
702 /// 4 if it is Functor.
703 /// Otherwise, type code is -1.
704 /// This does not call YP.getValue(term).
705 /// </summary>
706 /// <param name="term"></param>
707 /// <returns></returns>
708 private static int getTypeCode(object term)
709 {
710 if (term is Variable)
711 return -2;
712 else if (term is Atom)
713 return 0;
714 else if (term is Functor1)
715 return 1;
716 else if (term is Functor2)
717 return 2;
718 else if (term is Functor3)
719 return 3;
720 else if (term is Functor)
721 return 4;
722 else
723 return -1;
724 }
725
726 public static bool termLessThanOrEqual(object Term1, object Term2)
727 {
728 if (YP.termEqual(Term1, Term2))
729 return true;
730 return YP.termLessThan(Term1, Term2);
731 }
732
733 public static bool termGreaterThan(object Term1, object Term2)
734 {
735 return !YP.termLessThanOrEqual(Term1, Term2);
736 }
737
738 public static bool termGreaterThanOrEqual(object Term1, object Term2)
739 {
740 // termLessThan should ensure that it returns false if terms are equal,
741 // so that this would return true.
742 return !YP.termLessThan(Term1, Term2);
743 }
744
745 public static int compareTerms(object Term1, object Term2)
746 {
747 if (YP.termEqual(Term1, Term2))
748 return 0;
749 else if (YP.termLessThan(Term1, Term2))
750 return -1;
751 else
752 return 1;
753 }
754
755 public static bool ground(object Term)
756 {
757 Term = YP.getValue(Term);
758 if (Term is IUnifiable)
759 return ((IUnifiable)Term).ground();
760 return true;
761 }
762
763 public static IEnumerable<bool> current_op
764 (object Priority, object Specifier, object Operator)
765 {
766 if (_operatorTable == null)
767 {
768 // Initialize.
769 _operatorTable = new List<object[]>();
770 _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
771 _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
772 _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
773 _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
774 _operatorTable.Add(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
775 _operatorTable.Add(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
776 _operatorTable.Add(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
777 _operatorTable.Add(new object[] { 900, Atom.a("fy"), Atom.a("\\+") });
778 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=") });
779 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") });
780 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("==") });
781 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") });
782 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@<") });
783 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") });
784 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>") });
785 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") });
786 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=..") });
787 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
788 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") });
789 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") });
790 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("<") });
791 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=<") });
792 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">") });
793 _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">=") });
794 _operatorTable.Add(new object[] { 600, Atom.a("xfy"), Atom.a(":") });
795 _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("+") });
796 _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("-") });
797 _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") });
798 _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") });
799 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("*") });
800 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("/") });
801 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("//") });
802 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
803 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
804 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
805 _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
806 _operatorTable.Add(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
807 _operatorTable.Add(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
808 _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("-") });
809 _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("\\") });
810 // Debug: This is hacked in to run the Prolog test suite until we implement op/3.
811 _operatorTable.Add(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
812 }
813
814 object[] args = new object[] { Priority, Specifier, Operator };
815 foreach (object[] answer in _operatorTable)
816 {
817 foreach (bool l1 in YP.unifyArrays(args, answer))
818 yield return false;
819 }
820 }
821
822 public static IEnumerable<bool> atom_length(object atom, object Length)
823 {
824 return YP.unify(Length, ((Atom)YP.getValue(atom))._name.Length);
825 }
826
827 public static IEnumerable<bool> atom_concat(object Start, object End, object Whole)
828 {
829 // Debug: Should implement for var(Start) which is a kind of search.
830 // Debug: Should we try to preserve the _declaringClass?
831 return YP.unify(Whole, Atom.a(((Atom)YP.getValue(Start))._name +
832 ((Atom)YP.getValue(End))._name));
833 }
834
835 public static IEnumerable<bool> sub_atom
836 (object atom, object Before, object Length, object After, object Sub_atom)
837 {
838 // Debug: Should implement for var(atom) which is a kind of search.
839 // Debug: Should we try to preserve the _declaringClass?
840 Atom atomAtom = (Atom)YP.getValue(atom);
841 int beforeInt = YP.convertInt(Before);
842 int lengthInt = YP.convertInt(Length);
843 if (beforeInt < 0)
844 throw new Exception("Before must be non-negative");
845 if (lengthInt < 0)
846 throw new Exception("Length must be non-negative");
847 int afterInt = atomAtom._name.Length - (beforeInt + lengthInt);
848 if (afterInt >= 0)
849 {
850 foreach (bool l1 in YP.unify(After, afterInt))
851 {
852 foreach (bool l2 in YP.unify
853 (Sub_atom, Atom.a(atomAtom._name.Substring(beforeInt, lengthInt))))
854 yield return false;
855 }
856 }
857 }
858
859 public static IEnumerable<bool> atom_codes(object atom, object List)
860 {
861 atom = YP.getValue(atom);
862 List = YP.getValue(List);
863
864 if (nonvar(atom))
865 {
866 string name = ((Atom)atom)._name;
867 object codeList = Atom.NIL;
868 // Start from the back to make the list.
869 for (int i = name.Length - 1; i >= 0; --i)
870 codeList = new ListPair((int)name[i], codeList);
871 return YP.unify(List, codeList);
872 }
873 {
874 object[] codeArray = ListPair.toArray(List);
875 char[] charArray = new char[codeArray.Length];
876 for (int i = 0; i < codeArray.Length; ++i)
877 charArray[i] = (char)YP.convertInt(codeArray[i]);
878 return YP.unify(atom, Atom.a(new String(charArray)));
879 }
880 }
881
882 public static IEnumerable<bool> number_codes(object number, object List)
883 {
884 number = YP.getValue(number);
885 List = YP.getValue(List);
886
887 if (nonvar(number))
888 {
889 string numberString = null;
890 // Try converting to an int first.
891 int intNumber;
892 if (YP.getInt(number, out intNumber))
893 numberString = intNumber.ToString();
894 else
895 numberString = YP.doubleToString(YP.convertDouble(number));
896
897 object codeList = Atom.NIL;
898 // Start from the back to make the list.
899 for (int i = numberString.Length - 1; i >= 0; --i)
900 codeList = new ListPair((int)numberString[i], codeList);
901 return YP.unify(List, codeList);
902 }
903 {
904 object[] codeArray = ListPair.toArray(List);
905 char[] charArray = new char[codeArray.Length];
906 for (int i = 0; i < codeArray.Length; ++i)
907 charArray[i] = (char)YP.convertInt(codeArray[i]);
908 String numberString = new String(charArray);
909 // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
910 try
911 {
912 // Try an int first.
913 return YP.unify(number, Convert.ToInt32(numberString));
914 }
915 catch (FormatException) { }
916 return YP.unify(number, Convert.ToDouble(numberString));
917 }
918 }
919
920 /// <summary>
921 /// If term is an Atom or functor type, return its name.
922 /// Otherwise, return term.
923 /// </summary>
924 /// <param name="term"></param>
925 /// <returns></returns>
926 public static object getFunctorName(object term)
927 {
928 term = YP.getValue(term);
929 if (term is Functor1)
930 return ((Functor1)term)._name;
931 else if (term is Functor2)
932 return ((Functor2)term)._name;
933 else if (term is Functor3)
934 return ((Functor3)term)._name;
935 else if (term is Functor)
936 return ((Functor)term)._name;
937 else
938 return term;
939 }
940
941 /// <summary>
942 /// If term is an Atom or functor type, return an array of its args.
943 /// Otherwise, return an empty array.
944 /// </summary>
945 /// <param name="term"></param>
946 /// <returns></returns>
947 public static object[] getFunctorArgs(object term)
948 {
949 term = YP.getValue(term);
950 if (term is Functor1)
951 {
952 Functor1 functor = (Functor1)term;
953 return new object[] { functor._arg1 };
954 }
955 else if (term is Functor2)
956 {
957 Functor2 functor = (Functor2)term;
958 return new object[] { functor._arg1, functor._arg2 };
959 }
960 else if (term is Functor3)
961 {
962 Functor3 functor = (Functor3)term;
963 return new object[] { functor._arg1, functor._arg2, functor._arg3 };
964 }
965 else if (term is Functor) {
966 Functor functor = (Functor)term;
967 return functor._args;
968 }
969 else
970 return new object[0];
971 }
972
973 public static bool var(object Term)
974 {
975 return YP.getValue(Term) is Variable;
976 }
977
978 public static bool nonvar(object Term)
979 {
980 return !YP.var(Term);
981 }
982
983 public static bool atom(object Term)
984 {
985 return YP.getValue(Term) is Atom;
986 }
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
1001 public static bool number(object Term)
1002 {
1003 return YP.integer(Term) || YP.isFloat(Term);
1004 }
1005
1006 public static bool atomic(object Term)
1007 {
1008 return YP.atom(Term) || YP.number(Term);
1009 }
1010
1011 public static bool compound(object Term)
1012 {
1013 Term = getValue(Term);
1014 return Term is Functor1 || Term is Functor2 || Term is Functor3 || Term is Functor;
1015 }
1016
1017 public static void see(object input)
1018 {
1019 input = YP.getValue(input);
1020 if (input is TextReader)
1021 {
1022 _inputStream = (TextReader)input;
1023 return;
1024 }
1025 else if (input is Atom)
1026 {
1027 _inputStream = new StreamReader(((Atom)input)._name);
1028 return;
1029 }
1030 else if (input is String)
1031 {
1032 _inputStream = new StreamReader((String)input);
1033 return;
1034 }
1035 else
1036 throw new InvalidOperationException("Can't open stream for " + input);
1037 }
1038
1039 public static void seen()
1040 {
1041 if (_inputStream == Console.In)
1042 return;
1043 _inputStream.Close();
1044 _inputStream = Console.In;
1045 }
1046
1047 public static void tell(object output)
1048 {
1049 output = YP.getValue(output);
1050 if (output is TextWriter)
1051 {
1052 _outputStream = (TextWriter)output;
1053 return;
1054 }
1055 else if (output is Atom)
1056 {
1057 _outputStream = new StreamWriter(((Atom)output)._name);
1058 return;
1059 }
1060 else if (output is String)
1061 {
1062 _outputStream = new StreamWriter((String)output);
1063 return;
1064 }
1065 else
1066 throw new InvalidOperationException("Can't open stream for " + output);
1067 }
1068
1069 public static void told()
1070 {
1071 if (_outputStream == Console.Out)
1072 return;
1073 _outputStream.Close();
1074 _outputStream = Console.Out;
1075 }
1076
1077 public static IEnumerable<bool> current_output(object Stream)
1078 {
1079 return YP.unify(Stream, _outputStream);
1080 }
1081
1082 public static void write(object x)
1083 {
1084 x = YP.getValue(x);
1085 if (x is double)
1086 _outputStream.Write(doubleToString((double)x));
1087 else
1088 _outputStream.Write(x.ToString());
1089 }
1090
1091 /// <summary>
1092 /// Format x as a string, making sure that it will parse as an int later. I.e., for 1.0, don't just
1093 /// use "1" which will parse as an int.
1094 /// </summary>
1095 /// <param name="x"></param>
1096 /// <returns></returns>
1097 private static string doubleToString(double x)
1098 {
1099 string xString = x.ToString();
1100 // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
1101 try
1102 {
1103 Convert.ToInt32(xString);
1104 // The string will parse as an int, not a double, so re-format so that it does.
1105 // Use float if possible, else exponential if it would be too big.
1106 return x.ToString(x >= 100000.0 ? "E1" : "f1");
1107 }
1108 catch (FormatException)
1109 {
1110 // Assume it will parse as a double.
1111 }
1112 return xString;
1113 }
1114
1115 public static void put_code(object x)
1116 {
1117 _outputStream.Write((char)YP.convertInt(x));
1118 }
1119
1120 public static void nl()
1121 {
1122 _outputStream.WriteLine();
1123 }
1124
1125 public static IEnumerable<bool> get_code(object code)
1126 {
1127 return YP.unify(code, _inputStream.Read());
1128 }
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>
1217 public static void assertFact(Atom name, object[] values)
1218 {
1219 NameArity nameArity = new NameArity(name, values.Length);
1220 List<IClause> clauses;
1221 IndexedAnswers indexedAnswers;
1222 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1223 {
1224 // Create an IndexedAnswers as the first clause of the predicate.
1225 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1226 clauses.Add(indexedAnswers = new IndexedAnswers());
1227 }
1228 else
1229 {
1230 indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers;
1231 if (indexedAnswers == null)
1232 // The latest clause is not an IndexedAnswers, so add one.
1233 clauses.Add(indexedAnswers = new IndexedAnswers());
1234 }
1235
1236 indexedAnswers.addAnswer(values);
1237 }
1238
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)
1248 {
1249 List<IClause> clauses;
1250 if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses))
1251 throw new UndefinedPredicateException
1252 ("Undefined fact: " + name + "/" + arguments.Length, name,
1253 arguments.Length);
1254
1255 if (clauses.Count == 1)
1256 // Usually there is only one clause, so return it without needing to wrap it in an iterator.
1257 return clauses[0].match(arguments);
1258 else
1259 return matchAllClauses(clauses, arguments);
1260 }
1261
1262 /// <summary>
1263 /// Call match(arguments) for each IClause in clauses. We make this a separate
1264 /// function so that matchDynamic itself does not need to be an iterator object.
1265 /// </summary>
1266 /// <param name="clauses"></param>
1267 /// <param name="arguments"></param>
1268 /// <returns></returns>
1269 private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments)
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?
1273 foreach (IClause clause in clauses)
1274 {
1275 foreach (bool lastCall in clause.match(arguments))
1276 {
1277 yield return false;
1278 if (lastCall)
1279 // This happens after a cut in a clause.
1280 yield break;
1281 }
1282 }
1283 }
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>
1304 public static void retractFact(Atom name, object[] arguments)
1305 {
1306 NameArity nameArity = new NameArity(name, arguments.Length);
1307 List<IClause> clauses;
1308 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1309 // Can't find, so ignore.
1310 return;
1311
1312 foreach (object arg in arguments)
1313 {
1314 if (!YP.var(arg))
1315 throw new InvalidOperationException("All arguments must be unbound");
1316 }
1317 // Set to a fresh empty IndexedAnswers.
1318 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1319 clauses.Add(new IndexedAnswers());
1320 }
1321
1322 public static IEnumerable<bool> current_predicate(object NameSlashArity)
1323 {
1324 NameSlashArity = YP.getValue(NameSlashArity);
1325 // First check if Name and Arity are nonvar so we can do a direct lookup.
1326 if (YP.ground(NameSlashArity))
1327 {
1328 if (NameSlashArity is Functor2)
1329 {
1330 Functor2 NameArityFunctor = (Functor2)NameSlashArity;
1331 if (NameArityFunctor._name == Atom.SLASH)
1332 {
1333 if (_predicatesStore.ContainsKey(new NameArity
1334 ((Atom)YP.getValue(NameArityFunctor._arg1),
1335 (int)YP.getValue(NameArityFunctor._arg2))))
1336 // The predicate is defined.
1337 yield return false;
1338 }
1339 }
1340 yield break;
1341 }
1342
1343 foreach (NameArity key in _predicatesStore.Keys)
1344 {
1345 foreach (bool l1 in YP.unify
1346 (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity))
1347 yield return false;
1348 }
1349 }
1350
1351 /// <summary>
1352 /// Use YP.getFunctorName(Goal) and invoke the static method of this name in the
1353 /// declaringClass, using arguments from YP.getFunctorArgs(Goal).
1354 /// Note that Goal must be a simple functor, not a complex expression.
1355 /// If not found, this throws UndefinedPredicateException.
1356 /// </summary>
1357 /// <param name="Goal"></param>
1358 /// <param name="contextClass">the class for looking up default function references</param>
1359 /// <returns></returns>
1360 public static IEnumerable<bool> getIterator(object Goal, Type declaringClass)
1361 {
1362 Goal = YP.getValue(Goal);
1363 if (Goal is Variable)
1364 throw new PrologException("instantiation_error", "Goal to call is an unbound variable");
1365#if true
1366 List<Variable> variableSetList = new List<Variable>();
1367 addUniqueVariables(Goal, variableSetList);
1368 Variable[] variableSet = variableSetList.ToArray();
1369
1370 // Use Atom.F since it is ignored.
1371 return YPCompiler.compileAnonymousClause
1372 (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet);
1373#else
1374 Atom name;
1375 object[] args;
1376 while (true)
1377 {
1378 name = (Atom)YP.getFunctorName(Goal);
1379 args = YP.getFunctorArgs(Goal);
1380 if (name == Atom.HAT && args.Length == 2)
1381 // Assume this is called from a bagof operation. Skip the leading qualifiers.
1382 Goal = YP.getValue(((Functor2)Goal)._arg2);
1383 else
1384 break;
1385 }
1386 try
1387 {
1388 return (IEnumerable<bool>)declaringClass.InvokeMember
1389 (name._name, BindingFlags.InvokeMethod, null, null, args);
1390 }
1391 catch (TargetInvocationException exception)
1392 {
1393 throw exception.InnerException;
1394 }
1395 catch (MissingMethodException)
1396 {
1397 throw new UndefinedPredicateException
1398 ("Cannot find predicate function: " + name + "/" + args.Length + " in " +
1399 declaringClass.FullName, name, args.Length);
1400 }
1401#endif
1402 }
1403
1404 public static void throwException(object Term)
1405 {
1406 throw new PrologException(Term);
1407 }
1408
1409 /// <summary>
1410 /// script_event calls hosting script with events as a callback method.
1411 /// </summary>
1412 /// <param name="script_event"></param>
1413 /// <param name="script_params"></param>
1414 /// <returns></returns>
1415 public static void script_event(object script_event, object script_params)
1416 {
1417 string function = ((Atom)YP.getValue(script_event))._name;
1418 object[] array = ListPair.toArray(script_params);
1419 if (array == null)
1420 return; // YP.fail();
1421 if (array.Length > 1)
1422 {
1423 //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue
1424 //(localID, itemID, function, array);
1425 // sortArray(array);
1426 }
1427 //return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
1428 }
1429
1430 /// <summary>
1431 /// An enumerator that does zero loops.
1432 /// </summary>
1433 private class Fail : IEnumerator<bool>, IEnumerable<bool>
1434 {
1435 public bool MoveNext()
1436 {
1437 return false;
1438 }
1439
1440 public IEnumerator<bool> GetEnumerator()
1441 {
1442 return (IEnumerator<bool>)this;
1443 }
1444
1445 IEnumerator IEnumerable.GetEnumerator()
1446 {
1447 return GetEnumerator();
1448 }
1449
1450 public bool Current
1451 {
1452 get { return true; }
1453 }
1454
1455 object IEnumerator.Current
1456 {
1457 get { return true; }
1458 }
1459
1460 public void Dispose()
1461 {
1462 }
1463
1464 public void Reset()
1465 {
1466 throw new NotImplementedException();
1467 }
1468 }
1469
1470 /// <summary>
1471 /// An enumerator that does one iteration.
1472 /// </summary>
1473 private class Succeed : IEnumerator<bool>, IEnumerable<bool>
1474 {
1475 private bool _didIteration = false;
1476
1477 public bool MoveNext()
1478 {
1479 if (!_didIteration)
1480 {
1481 _didIteration = true;
1482 return true;
1483 }
1484 else
1485 return false;
1486 }
1487
1488 public IEnumerator<bool> GetEnumerator()
1489 {
1490 return (IEnumerator<bool>)this;
1491 }
1492
1493 IEnumerator IEnumerable.GetEnumerator()
1494 {
1495 return GetEnumerator();
1496 }
1497
1498 public bool Current
1499 {
1500 get { return false; }
1501 }
1502
1503 object IEnumerator.Current
1504 {
1505 get { return false; }
1506 }
1507
1508 public void Dispose()
1509 {
1510 }
1511
1512 public void Reset()
1513 {
1514 throw new NotImplementedException();
1515 }
1516 }
1517
1518 /// <summary>
1519 /// An enumerator that repeats forever.
1520 /// </summary>
1521 private class Repeat : IEnumerator<bool>, IEnumerable<bool>
1522 {
1523 public bool MoveNext()
1524 {
1525 return true;
1526 }
1527
1528 public IEnumerator<bool> GetEnumerator()
1529 {
1530 return (IEnumerator<bool>)this;
1531 }
1532
1533 IEnumerator IEnumerable.GetEnumerator()
1534 {
1535 return GetEnumerator();
1536 }
1537
1538 public bool Current
1539 {
1540 get { return false; }
1541 }
1542
1543 object IEnumerator.Current
1544 {
1545 get { return false; }
1546 }
1547
1548 public void Dispose()
1549 {
1550 }
1551
1552 public void Reset()
1553 {
1554 throw new NotImplementedException();
1555 }
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 }
1643 }
1644}