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.cs2701
1 files changed, 0 insertions, 2701 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
deleted file mode 100644
index 694e733..0000000
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs
+++ /dev/null
@@ -1,2701 +0,0 @@
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;
36using System.Net.Sockets;
37using System.Text;
38using System.Text.RegularExpressions;
39
40namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog
41{
42 /// <summary>
43 /// YP has static methods for general functions in Yield Prolog such as <see cref="getValue"/>
44 /// and <see cref="unify"/>.
45 /// </summary>
46 public class YP
47 {
48 private static Fail _fail = new Fail();
49 private static Repeat _repeat = new Repeat();
50 private static Dictionary<NameArity, List<IClause>> _predicatesStore =
51 new Dictionary<NameArity, List<IClause>>();
52 private static TextWriter _outputStream = System.Console.Out;
53 private static TextReader _inputStream = System.Console.In;
54 private static IndexedAnswers _operatorTable = null;
55 private static Dictionary<string, object> _prologFlags = new Dictionary<string, object>();
56 public const int MAX_ARITY = 255;
57
58 /// <summary>
59 /// An IClause is used so that dynamic predicates can call match.
60 /// </summary>
61 public interface IClause
62 {
63 IEnumerable<bool> match(object[] args);
64 IEnumerable<bool> clause(object Head, object Body);
65 }
66
67 /// <summary>
68 /// If value is a Variable, then return its getValue. Otherwise, just
69 /// return value. You should call YP.getValue on any object that
70 /// may be a Variable to get the value to pass to other functions in
71 /// your system that are not part of Yield Prolog, such as math functions
72 /// or file I/O.
73 /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
74 /// </summary>
75 /// <param name="value"></param>
76 /// <returns></returns>
77 public static object getValue(object value)
78 {
79 if (value is Variable)
80 return ((Variable)value).getValue();
81 else
82 return value;
83 }
84
85 /// <summary>
86 /// If arg1 or arg2 is an object with a unify method (such as Variable or
87 /// Functor) then just call its unify with the other argument. The object's
88 /// unify method will bind the values or check for equals as needed.
89 /// Otherwise, both arguments are "normal" (atomic) values so if they
90 /// are equal then succeed (yield once), else fail (don't yield).
91 /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
92 /// </summary>
93 /// <param name="arg1"></param>
94 /// <param name="arg2"></param>
95 /// <returns></returns>
96 public static IEnumerable<bool> unify(object arg1, object arg2)
97 {
98 arg1 = getValue(arg1);
99 arg2 = getValue(arg2);
100 if (arg1 is IUnifiable)
101 return ((IUnifiable)arg1).unify(arg2);
102 else if (arg2 is IUnifiable)
103 return ((IUnifiable)arg2).unify(arg1);
104 else
105 {
106 // Arguments are "normal" types.
107 if (arg1.Equals(arg2))
108 return new Succeed();
109 else
110 return _fail;
111 }
112 }
113
114 /// <summary>
115 /// This is used for the lookup key in _factStore.
116 /// </summary>
117 public struct NameArity
118 {
119 public readonly Atom _name;
120 public readonly int _arity;
121
122 public NameArity(Atom name, int arity)
123 {
124 _name = name;
125 _arity = arity;
126 }
127
128 public override bool Equals(object obj)
129 {
130 if (obj is NameArity)
131 {
132 NameArity nameArity = (NameArity)obj;
133 return nameArity._name.Equals(_name) && nameArity._arity.Equals(_arity);
134 }
135 else
136 {
137 return false;
138 }
139 }
140
141 public override int GetHashCode()
142 {
143 return _name.GetHashCode() ^ _arity.GetHashCode();
144 }
145 }
146
147 /// <summary>
148 /// Convert term to an int.
149 /// If term is a single-element List, use its first element
150 /// (to handle the char types like "a").
151 /// If can't convert, throw a PrologException for type_error evaluable (because this is only
152 /// called from arithmetic functions).
153 /// </summary>
154 /// <param name="term"></param>
155 /// <returns></returns>
156 public static int convertInt(object term)
157 {
158 term = YP.getValue(term);
159 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
160 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
161 // Assume it is a char type like "a".
162 term = YP.getValue(((Functor2)term)._arg1);
163 if (term is Variable)
164 throw new PrologException(Atom.a("instantiation_error"),
165 "Expected a number but the argument is an unbound variable");
166
167 try
168 {
169 return (int)term;
170 }
171 catch (InvalidCastException)
172 {
173 throw new PrologException
174 (new Functor2
175 ("type_error", Atom.a("evaluable"),
176 new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
177 "Term must be an integer");
178 }
179 }
180
181 /// <summary>
182 /// Convert term to a double. This may convert an int to a double, etc.
183 /// If term is a single-element List, use its first element
184 /// (to handle the char types like "a").
185 /// If can't convert, throw a PrologException for type_error evaluable (because this is only
186 /// called from arithmetic functions).
187 /// </summary>
188 /// <param name="term"></param>
189 /// <returns></returns>
190 public static double convertDouble(object term)
191 {
192 term = YP.getValue(term);
193 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
194 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
195 // Assume it is a char type like "a".
196 term = YP.getValue(((Functor2)term)._arg1);
197 if (term is Variable)
198 throw new PrologException(Atom.a("instantiation_error"),
199 "Expected a number but the argument is an unbound variable");
200
201 try
202 {
203 return Convert.ToDouble(term);
204 }
205 catch (InvalidCastException)
206 {
207 throw new PrologException
208 (new Functor2
209 ("type_error", Atom.a("evaluable"),
210 new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
211 "Term must be an integer");
212 }
213 }
214
215 /// <summary>
216 /// If term is an integer, set intTerm.
217 /// If term is a single-element List, use its first element
218 /// (to handle the char types like "a"). Return true for success, false if can't convert.
219 /// We use a success return value because throwing an exception is inefficient.
220 /// </summary>
221 /// <param name="term"></param>
222 /// <returns></returns>
223 public static bool getInt(object term, out int intTerm)
224 {
225 term = YP.getValue(term);
226 if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
227 YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
228 // Assume it is a char type like "a".
229 term = YP.getValue(((Functor2)term)._arg1);
230
231 if (term is int)
232 {
233 intTerm = (int)term;
234 return true;
235 }
236
237 intTerm = 0;
238 return false;
239 }
240
241 public static bool equal(object x, object y)
242 {
243 x = YP.getValue(x);
244 if (x is DateTime)
245 return (DateTime)x == (DateTime)YP.getValue(y);
246 // Assume convertDouble converts an int to a double perfectly.
247 return YP.convertDouble(x) == YP.convertDouble(y);
248 }
249
250 public static bool notEqual(object x, object y)
251 {
252 x = YP.getValue(x);
253 if (x is DateTime)
254 return (DateTime)x != (DateTime)YP.getValue(y);
255 // Assume convertDouble converts an int to a double perfectly.
256 return YP.convertDouble(x) != YP.convertDouble(y);
257 }
258
259 public static bool greaterThan(object x, object y)
260 {
261 x = YP.getValue(x);
262 if (x is DateTime)
263 return (DateTime)x > (DateTime)YP.getValue(y);
264 // Assume convertDouble converts an int to a double perfectly.
265 return YP.convertDouble(x) > YP.convertDouble(y);
266 }
267
268 public static bool lessThan(object x, object y)
269 {
270 x = YP.getValue(x);
271 if (x is DateTime)
272 return (DateTime)x < (DateTime)YP.getValue(y);
273 // Assume convertDouble converts an int to a double perfectly.
274 return YP.convertDouble(x) < YP.convertDouble(y);
275 }
276
277 public static bool greaterThanOrEqual(object x, object y)
278 {
279 x = YP.getValue(x);
280 if (x is DateTime)
281 return (DateTime)x >= (DateTime)YP.getValue(y);
282 // Assume convertDouble converts an int to a double perfectly.
283 return YP.convertDouble(x) >= YP.convertDouble(y);
284 }
285
286 public static bool lessThanOrEqual(object x, object y)
287 {
288 x = YP.getValue(x);
289 if (x is DateTime)
290 return (DateTime)x <= (DateTime)YP.getValue(y);
291 // Assume convertDouble converts an int to a double perfectly.
292 return YP.convertDouble(x) <= YP.convertDouble(y);
293 }
294
295 public static object negate(object x)
296 {
297 int intX;
298 if (getInt(x, out intX))
299 return -intX;
300 return -convertDouble(x);
301 }
302
303 public static object abs(object x)
304 {
305 int intX;
306 if (getInt(x, out intX))
307 return Math.Abs(intX);
308 return Math.Abs(convertDouble(x));
309 }
310
311 public static object sign(object x)
312 {
313 int intX;
314 if (getInt(x, out intX))
315 return Math.Sign(intX);
316 return Math.Sign(convertDouble(x));
317 }
318
319 // Use toFloat instead of float because it is a reserved keyword.
320 public static object toFloat(object x)
321 {
322 return convertDouble(x);
323 }
324
325 /// <summary>
326 /// The ISO standard returns an int.
327 /// </summary>
328 /// <param name="x"></param>
329 /// <returns></returns>
330 public static object floor(object x)
331 {
332 return (int)Math.Floor(convertDouble(x));
333 }
334
335 /// <summary>
336 /// The ISO standard returns an int.
337 /// </summary>
338 /// <param name="x"></param>
339 /// <returns></returns>
340 public static object truncate(object x)
341 {
342 return (int)Math.Truncate(convertDouble(x));
343 }
344
345 /// <summary>
346 /// The ISO standard returns an int.
347 /// </summary>
348 /// <param name="x"></param>
349 /// <returns></returns>
350 public static object round(object x)
351 {
352 return (int)Math.Round(convertDouble(x));
353 }
354
355 /// <summary>
356 /// The ISO standard returns an int.
357 /// </summary>
358 /// <param name="x"></param>
359 /// <returns></returns>
360 public static object ceiling(object x)
361 {
362 return (int)Math.Ceiling(convertDouble(x));
363 }
364
365 public static object sin(object x)
366 {
367 return Math.Sin(YP.convertDouble(x));
368 }
369
370 public static object cos(object x)
371 {
372 return Math.Cos(YP.convertDouble(x));
373 }
374
375 public static object atan(object x)
376 {
377 return Math.Atan(YP.convertDouble(x));
378 }
379
380 public static object exp(object x)
381 {
382 return Math.Exp(YP.convertDouble(x));
383 }
384
385 public static object log(object x)
386 {
387 return Math.Log(YP.convertDouble(x));
388 }
389
390 public static object sqrt(object x)
391 {
392 return Math.Sqrt(convertDouble(x));
393 }
394
395 public static object bitwiseComplement(object x)
396 {
397 return ~YP.convertInt(x);
398 }
399
400 public static object add(object x, object y)
401 {
402 int intX, intY;
403 if (getInt(x, out intX) && getInt(y, out intY))
404 return intX + intY;
405 return convertDouble(x) + convertDouble(y);
406 }
407
408 public static object subtract(object x, object y)
409 {
410 int intX, intY;
411 if (getInt(x, out intX) && getInt(y, out intY))
412 return intX - intY;
413 return convertDouble(x) - convertDouble(y);
414 }
415
416 public static object multiply(object x, object y)
417 {
418 int intX, intY;
419 if (getInt(x, out intX) && getInt(y, out intY))
420 return intX * intY;
421 return convertDouble(x) * convertDouble(y);
422 }
423
424 /// <summary>
425 /// Return floating point, even if both arguments are integer.
426 /// </summary>
427 /// <param name="x"></param>
428 /// <param name="y"></param>
429 /// <returns></returns>
430 public static object divide(object x, object y)
431 {
432 return convertDouble(x) / convertDouble(y);
433 }
434
435 public static object intDivide(object x, object y)
436 {
437 int intX, intY;
438 if (getInt(x, out intX) && getInt(y, out intY))
439 return intX / intY;
440 // Still allow passing a double, but treat as an int.
441 return (int)convertDouble(x) / (int)convertDouble(y);
442 }
443
444 public static object mod(object x, object y)
445 {
446 int intX, intY;
447 if (getInt(x, out intX) && getInt(y, out intY))
448 return intX % intY;
449 // Still allow passing a double, but treat as an int.
450 return (int)convertDouble(x) % (int)convertDouble(y);
451 }
452
453 public static object pow(object x, object y)
454 {
455 return Math.Pow(YP.convertDouble(x), YP.convertDouble(y));
456 }
457
458 public static object bitwiseShiftRight(object x, object y)
459 {
460 return YP.convertInt(x) >> YP.convertInt(y);
461 }
462
463 public static object bitwiseShiftLeft(object x, object y)
464 {
465 return YP.convertInt(x) << YP.convertInt(y);
466 }
467
468 public static object bitwiseAnd(object x, object y)
469 {
470 return YP.convertInt(x) & YP.convertInt(y);
471 }
472
473 public static object bitwiseOr(object x, object y)
474 {
475 return YP.convertInt(x) | YP.convertInt(y);
476 }
477
478 public static object min(object x, object y)
479 {
480 int intX, intY;
481 if (getInt(x, out intX) && getInt(y, out intY))
482 return Math.Min(intX, intY);
483 return Math.Min(convertDouble(x), convertDouble(y));
484 }
485
486 public static object max(object x, object y)
487 {
488 int intX, intY;
489 if (getInt(x, out intX) && getInt(y, out intY))
490 return Math.Max(intX, intY);
491 return Math.Max(convertDouble(x), convertDouble(y));
492 }
493
494 public static IEnumerable<bool> copy_term(object inTerm, object outTerm)
495 {
496 return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore()));
497 }
498
499 public static void addUniqueVariables(object term, List<Variable> variableSet)
500 {
501 term = YP.getValue(term);
502 if (term is IUnifiable)
503 ((IUnifiable)term).addUniqueVariables(variableSet);
504 }
505
506 public static object makeCopy(object term, Variable.CopyStore copyStore)
507 {
508 term = YP.getValue(term);
509 if (term is IUnifiable)
510 return ((IUnifiable)term).makeCopy(copyStore);
511 else
512 // term is a "normal" type. Assume it is ground.
513 return term;
514 }
515
516 /// <summary>
517 /// Sort the array in place according to termLessThan. This does not remove duplicates
518 /// </summary>
519 /// <param name="array"></param>
520 public static void sortArray(object[] array)
521 {
522 Array.Sort(array, YP.compareTerms);
523 }
524
525 /// <summary>
526 /// Sort the array in place according to termLessThan. This does not remove duplicates
527 /// </summary>
528 /// <param name="array"></param>
529 public static void sortArray(List<object> array)
530 {
531 array.Sort(YP.compareTerms);
532 }
533
534 /// <summary>
535 /// Sort List according to termLessThan, remove duplicates and unify with Sorted.
536 /// </summary>
537 /// <param name="List"></param>
538 /// <param name="Sorted"></param>
539 /// <returns></returns>
540 public static IEnumerable<bool> sort(object List, object Sorted)
541 {
542 object[] array = ListPair.toArray(List);
543 if (array == null)
544 return YP.fail();
545 if (array.Length > 1)
546 sortArray(array);
547 return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
548 }
549
550 /// <summary>
551 /// Use YP.unify to unify each of the elements of the two arrays, and yield
552 /// once if they all unify.
553 /// </summary>
554 /// <param name="array1"></param>
555 /// <param name="array2"></param>
556 /// <returns></returns>
557 public static IEnumerable<bool> unifyArrays(object[] array1, object[] array2)
558 {
559 if (array1.Length != array2.Length)
560 yield break;
561
562 IEnumerator<bool>[] iterators = new IEnumerator<bool>[array1.Length];
563 bool gotMatch = true;
564 int nIterators = 0;
565 // Try to bind all the arguments.
566 for (int i = 0; i < array1.Length; ++i)
567 {
568 IEnumerator<bool> iterator = YP.unify(array1[i], array2[i]).GetEnumerator();
569 iterators[nIterators++] = iterator;
570 // MoveNext() is true if YP.unify succeeds.
571 if (!iterator.MoveNext())
572 {
573 gotMatch = false;
574 break;
575 }
576 }
577
578 try
579 {
580 if (gotMatch)
581 yield return false;
582 }
583 finally
584 {
585 // Manually finalize all the iterators.
586 for (int i = 0; i < nIterators; ++i)
587 iterators[i].Dispose();
588 }
589 }
590
591 /// <summary>
592 /// Return an iterator (which you can use in a for-in loop) which does
593 /// zero iterations. This returns a pre-existing iterator which is
594 /// more efficient than letting the compiler generate a new one.
595 /// </summary>
596 /// <returns></returns>
597 public static IEnumerable<bool> fail()
598 {
599 return _fail;
600 }
601
602 /// <summary>
603 /// Return an iterator (which you can use in a for-in loop) which does
604 /// one iteration. This returns a pre-existing iterator which is
605 /// more efficient than letting the compiler generate a new one.
606 /// </summary>
607 /// <returns></returns>
608 public static IEnumerable<bool> succeed()
609 {
610 return new Succeed();
611 }
612
613 /// <summary>
614 /// Return an iterator (which you can use in a for-in loop) which repeats
615 /// indefinitely. This returns a pre-existing iterator which is
616 /// more efficient than letting the compiler generate a new one.
617 /// </summary>
618 /// <returns></returns>
619 public static IEnumerable<bool> repeat()
620 {
621 return _repeat;
622 }
623
624 // disable warning on l1, don't see how we can
625 // code this differently
626 #pragma warning disable 0168
627 public static IEnumerable<bool> univ(object Term, object List)
628 {
629 Term = YP.getValue(Term);
630 List = YP.getValue(List);
631
632 if (nonvar(Term))
633 return YP.unify(new ListPair
634 (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List);
635
636 Variable Name = new Variable();
637 Variable ArgList = new Variable();
638 foreach (bool l1 in new ListPair(Name, ArgList).unify(List))
639 {
640 object[] args = ListPair.toArray(ArgList);
641 if (args == null)
642 throw new PrologException
643 (new Functor2("type_error", Atom.a("list"), ArgList),
644 "Expected a list. Got: " + ArgList.getValue());
645 if (args.Length == 0)
646 // Return the Name, even if it is not an Atom.
647 return YP.unify(Term, Name);
648 if (args.Length > MAX_ARITY)
649 throw new PrologException
650 (new Functor1("representation_error", Atom.a("max_arity")),
651 "Functor arity " + args.Length + " may not be greater than " + MAX_ARITY);
652 if (!atom(Name))
653 throw new PrologException
654 (new Functor2("type_error", Atom.a("atom"), Name),
655 "Expected an atom. Got: " + Name.getValue());
656
657 return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
658 }
659
660 return YP.fail();
661 }
662
663 public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity)
664 {
665 Term = YP.getValue(Term);
666 FunctorName = YP.getValue(FunctorName);
667 Arity = YP.getValue(Arity);
668
669 if (Term is Variable)
670 {
671 if (FunctorName is Variable)
672 throw new PrologException(Atom.a("instantiation_error"),
673 "Arg 2 FunctorName is an unbound variable");
674 if (Arity is Variable)
675 throw new PrologException(Atom.a("instantiation_error"),
676 "Arg 3 Arity is an unbound variable");
677 if (!(Arity is int))
678 throw new PrologException
679 (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer");
680 if (!YP.atomic(FunctorName))
681 throw new PrologException
682 (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic");
683
684 if ((int)Arity < 0)
685 throw new PrologException
686 (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity),
687 "Arity may not be less than zero");
688 else if ((int)Arity == 0)
689 {
690 // Just unify Term with the atomic FunctorName.
691 foreach (bool l1 in YP.unify(Term, FunctorName))
692 yield return false;
693 }
694 else
695 {
696 if ((int)Arity > MAX_ARITY)
697 throw new PrologException
698 (new Functor1("representation_error", Atom.a("max_arity")),
699 "Functor arity " + Arity + " may not be greater than " + MAX_ARITY);
700 if (!(FunctorName is Atom))
701 throw new PrologException
702 (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom");
703 // Construct a functor with unbound variables.
704 object[] args = new object[(int)Arity];
705 for (int i = 0; i < args.Length; ++i)
706 args[i] = new Variable();
707 #pragma warning disable 0219
708 foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args)))
709 yield return false;
710 #pragma warning restore 0219
711 }
712 }
713 else
714 {
715 foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
716 {
717 foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
718 yield return false;
719 }
720 }
721 }
722
723 public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value)
724 {
725 if (var(ArgNumber))
726 throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable");
727 int argNumberInt;
728 if (!getInt(ArgNumber, out argNumberInt))
729 throw new PrologException
730 (new Functor2("type_error", Atom.a("integer"), ArgNumber), "Arg 1 ArgNumber must be integer");
731 if (argNumberInt < 0)
732 throw new PrologException
733 (new Functor2("domain_error", Atom.a("not_less_than_zero"), argNumberInt),
734 "ArgNumber may not be less than zero");
735
736 if (YP.var(Term))
737 throw new PrologException(Atom.a("instantiation_error"),
738 "Arg 2 Term is an unbound variable");
739 if (!YP.compound(Term))
740 throw new PrologException
741 (new Functor2("type_error", Atom.a("compound"), Term), "Arg 2 Term must be compound");
742
743 object[] termArgs = YP.getFunctorArgs(Term);
744 // Silently fail if argNumberInt is out of range.
745 if (argNumberInt >= 1 && argNumberInt <= termArgs.Length)
746 {
747 // The first ArgNumber is at 1, not 0.
748 foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1]))
749 yield return false;
750 }
751 }
752
753 public static bool termEqual(object Term1, object Term2)
754 {
755 Term1 = YP.getValue(Term1);
756 if (Term1 is IUnifiable)
757 return ((IUnifiable)Term1).termEqual(Term2);
758 return Term1.Equals(YP.getValue(Term2));
759 }
760
761 public static bool termNotEqual(object Term1, object Term2)
762 {
763 return !termEqual(Term1, Term2);
764 }
765
766 public static bool termLessThan(object Term1, object Term2)
767 {
768 Term1 = YP.getValue(Term1);
769 Term2 = YP.getValue(Term2);
770 int term1TypeCode = getTypeCode(Term1);
771 int term2TypeCode = getTypeCode(Term2);
772 if (term1TypeCode != term2TypeCode)
773 return term1TypeCode < term2TypeCode;
774
775 // The terms are the same type code.
776 if (term1TypeCode == -2)
777 {
778 // Variable.
779 // We always check for equality first because we want to be sure
780 // that less than returns false if the terms are equal, in
781 // case that the less than check really behaves like less than or equal.
782 if ((Variable)Term1 != (Variable)Term2)
783 // The hash code should be unique to a Variable object.
784 return Term1.GetHashCode() < Term2.GetHashCode();
785 return false;
786 }
787 if (term1TypeCode == 0)
788 return ((Atom)Term1)._name.CompareTo(((Atom)Term2)._name) < 0;
789 if (term1TypeCode == 1)
790 return ((Functor1)Term1).lessThan((Functor1)Term2);
791 if (term1TypeCode == 2)
792 return ((Functor2)Term1).lessThan((Functor2)Term2);
793 if (term1TypeCode == 3)
794 return ((Functor3)Term1).lessThan((Functor3)Term2);
795 if (term1TypeCode == 4)
796 return ((Functor)Term1).lessThan((Functor)Term2);
797
798 // Type code is -1 for general objects. First compare their type names.
799 // Note that this puts Double before Int32 as required by ISO Prolog.
800 string term1TypeName = Term1.GetType().ToString();
801 string term2TypeName = Term2.GetType().ToString();
802 if (term1TypeName != term2TypeName)
803 return term1TypeName.CompareTo(term2TypeName) < 0;
804
805 // The terms are the same type name.
806 if (Term1 is int)
807 return (int)Term1 < (int)Term2;
808 else if (Term1 is double)
809 return (double)Term1 < (double)Term2;
810 else if (Term1 is DateTime)
811 return (DateTime)Term1 < (DateTime)Term2;
812 else if (Term1 is String)
813 return ((String)Term1).CompareTo((String)Term2) < 0;
814 // Debug: Should we try arrays, etc.?
815
816 if (!Term1.Equals(Term2))
817 // Could be equal or greater than.
818 return Term1.GetHashCode() < Term2.GetHashCode();
819 return false;
820 }
821
822 /// <summary>
823 /// Type code is -2 if term is a Variable, 0 if it is an Atom,
824 /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3,
825 /// 4 if it is Functor.
826 /// Otherwise, type code is -1.
827 /// This does not call YP.getValue(term).
828 /// </summary>
829 /// <param name="term"></param>
830 /// <returns></returns>
831 private static int getTypeCode(object term)
832 {
833 if (term is Variable)
834 return -2;
835 else if (term is Atom)
836 return 0;
837 else if (term is Functor1)
838 return 1;
839 else if (term is Functor2)
840 return 2;
841 else if (term is Functor3)
842 return 3;
843 else if (term is Functor)
844 return 4;
845 else
846 return -1;
847 }
848
849 public static bool termLessThanOrEqual(object Term1, object Term2)
850 {
851 if (YP.termEqual(Term1, Term2))
852 return true;
853 return YP.termLessThan(Term1, Term2);
854 }
855
856 public static bool termGreaterThan(object Term1, object Term2)
857 {
858 return !YP.termLessThanOrEqual(Term1, Term2);
859 }
860
861 public static bool termGreaterThanOrEqual(object Term1, object Term2)
862 {
863 // termLessThan should ensure that it returns false if terms are equal,
864 // so that this would return true.
865 return !YP.termLessThan(Term1, Term2);
866 }
867
868 public static int compareTerms(object Term1, object Term2)
869 {
870 if (YP.termEqual(Term1, Term2))
871 return 0;
872 else if (YP.termLessThan(Term1, Term2))
873 return -1;
874 else
875 return 1;
876 }
877
878 public static bool ground(object Term)
879 {
880 Term = YP.getValue(Term);
881 if (Term is IUnifiable)
882 return ((IUnifiable)Term).ground();
883 return true;
884 }
885
886 public static IEnumerable<bool> current_op
887 (object Priority, object Specifier, object Operator)
888 {
889 if (_operatorTable == null)
890 {
891 // Initialize.
892 _operatorTable = new IndexedAnswers(3);
893 _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
894 _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
895 _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
896 _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
897 _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
898 _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
899 _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
900 _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), Atom.a("\\+") });
901 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=") });
902 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") });
903 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("==") });
904 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") });
905 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@<") });
906 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") });
907 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>") });
908 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") });
909 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=..") });
910 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
911 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") });
912 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") });
913 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("<") });
914 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=<") });
915 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">") });
916 _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">=") });
917 _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), Atom.a(":") });
918 _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("+") });
919 _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("-") });
920 _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") });
921 _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") });
922 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("*") });
923 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("/") });
924 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("//") });
925 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
926 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
927 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
928 _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
929 _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
930 _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
931 _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") });
932 _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") });
933 // Debug: This is hacked in to run the Prolog test suite until we implement op/3.
934 _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
935 }
936
937 return _operatorTable.match(new object[] { Priority, Specifier, Operator });
938 }
939
940 public static IEnumerable<bool> atom_length(object atom, object Length)
941 {
942 atom = YP.getValue(atom);
943 Length = YP.getValue(Length);
944 if (atom is Variable)
945 throw new PrologException(Atom.a("instantiation_error"),
946 "Expected atom(Arg1) but it is an unbound variable");
947 if (!(atom is Atom))
948 throw new PrologException
949 (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
950 if (!(Length is Variable))
951 {
952 if (!(Length is int))
953 throw new PrologException
954 (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
955 if ((int)Length < 0)
956 throw new PrologException
957 (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
958 "Length must not be less than zero");
959 }
960 return YP.unify(Length, ((Atom)atom)._name.Length);
961 }
962
963 public static IEnumerable<bool> atom_concat(object Start, object End, object Whole)
964 {
965 // Debug: Should we try to preserve the _declaringClass?
966 Start = YP.getValue(Start);
967 End = YP.getValue(End);
968 Whole = YP.getValue(Whole);
969 if (Whole is Variable)
970 {
971 if (Start is Variable)
972 throw new PrologException(Atom.a("instantiation_error"),
973 "Arg 1 Start and arg 3 Whole are both var");
974 if (End is Variable)
975 throw new PrologException(Atom.a("instantiation_error"),
976 "Arg 2 End and arg 3 Whole are both var");
977 if (!(Start is Atom))
978 throw new PrologException
979 (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not an atom");
980 if (!(End is Atom))
981 throw new PrologException
982 (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not an atom");
983
984 foreach (bool l1 in YP.unify(Whole, Atom.a(((Atom)Start)._name + ((Atom)End)._name)))
985 yield return false;
986 }
987 else
988 {
989 if (!(Whole is Atom))
990 throw new PrologException
991 (new Functor2("type_error", Atom.a("atom"), Whole), "Arg 3 Whole is not an atom");
992 bool gotStartLength = false;
993 int startLength = 0;
994 if (!(Start is Variable))
995 {
996 if (!(Start is Atom))
997 throw new PrologException
998 (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not var or atom");
999 startLength = ((Atom)Start)._name.Length;
1000 gotStartLength = true;
1001 }
1002
1003 bool gotEndLength = false;
1004 int endLength = 0;
1005 if (!(End is Variable))
1006 {
1007 if (!(End is Atom))
1008 throw new PrologException
1009 (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not var or atom");
1010 endLength = ((Atom)End)._name.Length;
1011 gotEndLength = true;
1012 }
1013
1014 // We are doing a search through all possible Start and End which concatenate to Whole.
1015 string wholeString = ((Atom)Whole)._name;
1016 for (int i = 0; i <= wholeString.Length; ++i)
1017 {
1018 // If we got either startLength or endLength, we know the lengths have to match so check
1019 // the lengths instead of constructing an Atom to do it.
1020 if (gotStartLength && startLength != i)
1021 continue;
1022 if (gotEndLength && endLength != wholeString.Length - i)
1023 continue;
1024 foreach (bool l1 in YP.unify(Start, Atom.a(wholeString.Substring(0, i))))
1025 {
1026 foreach (bool l2 in YP.unify(End, Atom.a(wholeString.Substring(i, wholeString.Length - i))))
1027 yield return false;
1028 }
1029 }
1030 }
1031 }
1032
1033 public static IEnumerable<bool> sub_atom
1034 (object atom, object Before, object Length, object After, object Sub_atom)
1035 {
1036 // Debug: Should we try to preserve the _declaringClass?
1037 atom = YP.getValue(atom);
1038 Before = YP.getValue(Before);
1039 Length = YP.getValue(Length);
1040 After = YP.getValue(After);
1041 Sub_atom = YP.getValue(Sub_atom);
1042 if (atom is Variable)
1043 throw new PrologException(Atom.a("instantiation_error"),
1044 "Expected atom(Arg1) but it is an unbound variable");
1045 if (!(atom is Atom))
1046 throw new PrologException
1047 (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
1048 if (!(Sub_atom is Variable))
1049 {
1050 if (!(Sub_atom is Atom))
1051 throw new PrologException
1052 (new Functor2("type_error", Atom.a("atom"), Sub_atom), "Sub_atom is not var or atom");
1053 }
1054
1055 bool beforeIsInt = false;
1056 bool lengthIsInt = false;
1057 bool afterIsInt = false;
1058 if (!(Before is Variable))
1059 {
1060 if (!(Before is int))
1061 throw new PrologException
1062 (new Functor2("type_error", Atom.a("integer"), Before), "Before must be var or integer");
1063 beforeIsInt = true;
1064 if ((int)Before < 0)
1065 throw new PrologException
1066 (new Functor2("domain_error", Atom.a("not_less_than_zero"), Before),
1067 "Before must not be less than zero");
1068 }
1069 if (!(Length is Variable))
1070 {
1071 if (!(Length is int))
1072 throw new PrologException
1073 (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
1074 lengthIsInt = true;
1075 if ((int)Length < 0)
1076 throw new PrologException
1077 (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
1078 "Length must not be less than zero");
1079 }
1080 if (!(After is Variable))
1081 {
1082 if (!(After is int))
1083 throw new PrologException
1084 (new Functor2("type_error", Atom.a("integer"), After), "After must be var or integer");
1085 afterIsInt = true;
1086 if ((int)After < 0)
1087 throw new PrologException
1088 (new Functor2("domain_error", Atom.a("not_less_than_zero"), After),
1089 "After must not be less than zero");
1090 }
1091
1092 Atom atomAtom = (Atom)atom;
1093 int atomLength = atomAtom._name.Length;
1094 if (beforeIsInt && lengthIsInt)
1095 {
1096 // Special case: the caller is just trying to extract a substring, so do it quickly.
1097 int xAfter = atomLength - (int)Before - (int)Length;
1098 if (xAfter >= 0)
1099 {
1100 foreach (bool l1 in YP.unify(After, xAfter))
1101 {
1102 foreach (bool l2 in YP.unify
1103 (Sub_atom, Atom.a(atomAtom._name.Substring((int)Before, (int)Length))))
1104 yield return false;
1105 }
1106 }
1107 }
1108 else if (afterIsInt && lengthIsInt)
1109 {
1110 // Special case: the caller is just trying to extract a substring, so do it quickly.
1111 int xBefore = atomLength - (int)After - (int)Length;
1112 if (xBefore >= 0)
1113 {
1114 foreach (bool l1 in YP.unify(Before, xBefore))
1115 {
1116 foreach (bool l2 in YP.unify
1117 (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, (int)Length))))
1118 yield return false;
1119 }
1120 }
1121 }
1122 else
1123 {
1124 // We are underconstrained and doing a search, so go through all possibilities.
1125 for (int xBefore = 0; xBefore <= atomLength; ++xBefore)
1126 {
1127 foreach (bool l1 in YP.unify(Before, xBefore))
1128 {
1129 for (int xLength = 0; xLength <= (atomLength - xBefore); ++xLength)
1130 {
1131 foreach (bool l2 in YP.unify(Length, xLength))
1132 {
1133 foreach (bool l3 in YP.unify(After, atomLength - (xBefore + xLength)))
1134 {
1135 foreach (bool l4 in YP.unify
1136 (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, xLength))))
1137 yield return false;
1138 }
1139 }
1140 }
1141 }
1142 }
1143 }
1144 }
1145
1146 public static IEnumerable<bool> atom_chars(object atom, object List)
1147 {
1148 atom = YP.getValue(atom);
1149 List = YP.getValue(List);
1150
1151 if (atom is Variable)
1152 {
1153 if (List is Variable)
1154 throw new PrologException(Atom.a("instantiation_error"),
1155 "Arg 1 Atom and arg 2 List are both unbound variables");
1156 object[] codeArray = ListPair.toArray(List);
1157 if (codeArray == null)
1158 throw new PrologException
1159 (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
1160
1161 char[] charArray = new char[codeArray.Length];
1162 for (int i = 0; i < codeArray.Length; ++i)
1163 {
1164 object listAtom = YP.getValue(codeArray[i]);
1165 if (listAtom is Variable)
1166 throw new PrologException(Atom.a("instantiation_error"),
1167 "Arg 2 List has an element which is an unbound variable");
1168 if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1))
1169 throw new PrologException
1170 (new Functor2("type_error", Atom.a("character"), listAtom),
1171 "Arg 2 List has an element which is not a one character atom");
1172 charArray[i] = ((Atom)listAtom)._name[0];
1173 }
1174 return YP.unify(atom, Atom.a(new String(charArray)));
1175 }
1176 else
1177 {
1178 if (!(atom is Atom))
1179 throw new PrologException
1180 (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom");
1181
1182 string atomString = ((Atom)atom)._name;
1183 object charList = Atom.NIL;
1184 // Start from the back to make the list.
1185 for (int i = atomString.Length - 1; i >= 0; --i)
1186 charList = new ListPair(Atom.a(atomString.Substring(i, 1)), charList);
1187 return YP.unify(List, charList);
1188 }
1189 }
1190
1191 public static IEnumerable<bool> atom_codes(object atom, object List)
1192 {
1193 atom = YP.getValue(atom);
1194 List = YP.getValue(List);
1195
1196 if (atom is Variable)
1197 {
1198 if (List is Variable)
1199 throw new PrologException(Atom.a("instantiation_error"),
1200 "Arg 1 Atom and arg 2 List are both unbound variables");
1201 object[] codeArray = ListPair.toArray(List);
1202 if (codeArray == null)
1203 throw new PrologException
1204 (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
1205
1206 char[] charArray = new char[codeArray.Length];
1207 for (int i = 0; i < codeArray.Length; ++i)
1208 {
1209 int codeInt;
1210 if (!getInt(codeArray[i], out codeInt) || codeInt < 0)
1211 throw new PrologException
1212 (new Functor1("representation_error", Atom.a("character_code")),
1213 "Element of Arg 2 List is not a character code");
1214 charArray[i] = (char)codeInt;
1215 }
1216 return YP.unify(atom, Atom.a(new String(charArray)));
1217 }
1218 else
1219 {
1220 if (!(atom is Atom))
1221 throw new PrologException
1222 (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom");
1223
1224 string atomString = ((Atom)atom)._name;
1225 object codeList = Atom.NIL;
1226 // Start from the back to make the list.
1227 for (int i = atomString.Length - 1; i >= 0; --i)
1228 codeList = new ListPair((int)atomString[i], codeList);
1229 return YP.unify(List, codeList);
1230 }
1231 }
1232
1233 public static IEnumerable<bool> number_chars(object Number, object List)
1234 {
1235 Number = YP.getValue(Number);
1236 List = YP.getValue(List);
1237
1238 if (Number is Variable)
1239 {
1240 if (List is Variable)
1241 throw new PrologException(Atom.a("instantiation_error"),
1242 "Arg 1 Number and arg 2 List are both unbound variables");
1243 object[] codeArray = ListPair.toArray(List);
1244 if (codeArray == null)
1245 throw new PrologException
1246 (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
1247
1248 char[] charArray = new char[codeArray.Length];
1249 for (int i = 0; i < codeArray.Length; ++i)
1250 {
1251 object listAtom = YP.getValue(codeArray[i]);
1252 if (listAtom is Variable)
1253 throw new PrologException(Atom.a("instantiation_error"),
1254 "Arg 2 List has an element which is an unbound variable");
1255 if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1))
1256 throw new PrologException
1257 (new Functor2("type_error", Atom.a("character"), listAtom),
1258 "Arg 2 List has an element which is not a one character atom");
1259 charArray[i] = ((Atom)listAtom)._name[0];
1260 }
1261 return YP.unify(Number, parseNumberString(charArray));
1262 }
1263 else
1264 {
1265 string numberString = null;
1266 // Try converting to an int first.
1267 int intNumber;
1268 if (YP.getInt(Number, out intNumber))
1269 numberString = intNumber.ToString();
1270 else
1271 {
1272 if (!YP.number(Number))
1273 throw new PrologException
1274 (new Functor2("type_error", Atom.a("number"), Number),
1275 "Arg 1 Number is not var or number");
1276 // We just checked, so convertDouble shouldn't throw an exception.
1277 numberString = YP.doubleToString(YP.convertDouble(Number));
1278 }
1279
1280 object charList = Atom.NIL;
1281 // Start from the back to make the list.
1282 for (int i = numberString.Length - 1; i >= 0; --i)
1283 charList = new ListPair(Atom.a(numberString.Substring(i, 1)), charList);
1284 return YP.unify(List, charList);
1285 }
1286 }
1287
1288 public static IEnumerable<bool> number_codes(object Number, object List)
1289 {
1290 Number = YP.getValue(Number);
1291 List = YP.getValue(List);
1292
1293 if (Number is Variable)
1294 {
1295 if (List is Variable)
1296 throw new PrologException(Atom.a("instantiation_error"),
1297 "Arg 1 Number and arg 2 List are both unbound variables");
1298 object[] codeArray = ListPair.toArray(List);
1299 if (codeArray == null)
1300 throw new PrologException
1301 (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
1302
1303 char[] charArray = new char[codeArray.Length];
1304 for (int i = 0; i < codeArray.Length; ++i)
1305 {
1306 int codeInt;
1307 if (!getInt(codeArray[i], out codeInt) || codeInt < 0)
1308 throw new PrologException
1309 (new Functor1("representation_error", Atom.a("character_code")),
1310 "Element of Arg 2 List is not a character code");
1311 charArray[i] = (char)codeInt;
1312 }
1313 return YP.unify(Number, parseNumberString(charArray));
1314 }
1315 else
1316 {
1317 string numberString = null;
1318 // Try converting to an int first.
1319 int intNumber;
1320 if (YP.getInt(Number, out intNumber))
1321 numberString = intNumber.ToString();
1322 else
1323 {
1324 if (!YP.number(Number))
1325 throw new PrologException
1326 (new Functor2("type_error", Atom.a("number"), Number),
1327 "Arg 1 Number is not var or number");
1328 // We just checked, so convertDouble shouldn't throw an exception.
1329 numberString = YP.doubleToString(YP.convertDouble(Number));
1330 }
1331
1332 object codeList = Atom.NIL;
1333 // Start from the back to make the list.
1334 for (int i = numberString.Length - 1; i >= 0; --i)
1335 codeList = new ListPair((int)numberString[i], codeList);
1336 return YP.unify(List, codeList);
1337 }
1338 }
1339
1340 /// <summary>
1341 /// Used by number_chars and number_codes. Return the number in charArray or
1342 /// throw an exception if can't parse.
1343 /// </summary>
1344 /// <param name="numberString"></param>
1345 /// <returns></returns>
1346 private static object parseNumberString(char[] charArray)
1347 {
1348 string numberString = new String(charArray);
1349 if (charArray.Length == 3 && numberString.StartsWith("0'"))
1350 // This is a char code.
1351 return (int)charArray[2];
1352 if (numberString.StartsWith("0x"))
1353 {
1354 try
1355 {
1356 return Int32.Parse
1357 (numberString.Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier);
1358 }
1359 catch (FormatException)
1360 {
1361 throw new PrologException
1362 (new Functor1("syntax_error", Atom.a("number_format: " + numberString)),
1363 "Arg 2 List is not a list for a hexadecimal number");
1364 }
1365 }
1366 // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
1367 try
1368 {
1369 // Try an int first.
1370 return Convert.ToInt32(numberString);
1371 }
1372 catch (FormatException) { }
1373 try
1374 {
1375 return Convert.ToDouble(numberString);
1376 }
1377 catch (FormatException)
1378 {
1379 throw new PrologException
1380 (new Functor1("syntax_error", Atom.a("number_format: " + numberString)),
1381 "Arg 2 List is not a list for a number");
1382 }
1383 }
1384
1385 public static IEnumerable<bool> char_code(object Char, object Code)
1386 {
1387 Char = YP.getValue(Char);
1388 Code = YP.getValue(Code);
1389
1390 int codeInt = 0;
1391 if (!(Code is Variable))
1392 {
1393 // Get codeInt now so we type check it whether or not Char is Variable.
1394 if (!getInt(Code, out codeInt))
1395 throw new PrologException
1396 (new Functor2("type_error", Atom.a("integer"), Code),
1397 "Arg 2 Code is not var or a character code");
1398 if (codeInt < 0)
1399 throw new PrologException
1400 (new Functor1("representation_error", Atom.a("character_code")),
1401 "Arg 2 Code is not a character code");
1402 }
1403
1404 if (Char is Variable)
1405 {
1406 if (Code is Variable)
1407 throw new PrologException(Atom.a("instantiation_error"),
1408 "Arg 1 Char and arg 2 Code are both unbound variables");
1409
1410 return YP.unify(Char, Atom.a(new String(new char[] {(char)codeInt} )));
1411 }
1412 else
1413 {
1414 if (!(Char is Atom) || ((Atom)Char)._name.Length != 1)
1415 throw new PrologException
1416 (new Functor2("type_error", Atom.a("character"), Char),
1417 "Arg 1 Char is not var or one-character atom");
1418
1419 if (Code is Variable)
1420 return YP.unify(Code, (int)((Atom)Char)._name[0]);
1421 else
1422 // Use codeInt to handle whether Code is supplied as, e.g., 97 or 0'a .
1423 return YP.unify(codeInt, (int)((Atom)Char)._name[0]);
1424 }
1425 }
1426
1427 /// <summary>
1428 /// If term is an Atom or functor type, return its name.
1429 /// Otherwise, return term.
1430 /// </summary>
1431 /// <param name="term"></param>
1432 /// <returns></returns>
1433 public static object getFunctorName(object term)
1434 {
1435 term = YP.getValue(term);
1436 if (term is Functor1)
1437 return ((Functor1)term)._name;
1438 else if (term is Functor2)
1439 return ((Functor2)term)._name;
1440 else if (term is Functor3)
1441 return ((Functor3)term)._name;
1442 else if (term is Functor)
1443 return ((Functor)term)._name;
1444 else
1445 return term;
1446 }
1447
1448 /// <summary>
1449 /// If term is an Atom or functor type, return an array of its args.
1450 /// Otherwise, return an empty array.
1451 /// </summary>
1452 /// <param name="term"></param>
1453 /// <returns></returns>
1454 public static object[] getFunctorArgs(object term)
1455 {
1456 term = YP.getValue(term);
1457 if (term is Functor1)
1458 {
1459 Functor1 functor = (Functor1)term;
1460 return new object[] { functor._arg1 };
1461 }
1462 else if (term is Functor2)
1463 {
1464 Functor2 functor = (Functor2)term;
1465 return new object[] { functor._arg1, functor._arg2 };
1466 }
1467 else if (term is Functor3)
1468 {
1469 Functor3 functor = (Functor3)term;
1470 return new object[] { functor._arg1, functor._arg2, functor._arg3 };
1471 }
1472 else if (term is Functor) {
1473 Functor functor = (Functor)term;
1474 return functor._args;
1475 }
1476 else
1477 return new object[0];
1478 }
1479
1480 public static bool var(object Term)
1481 {
1482 return YP.getValue(Term) is Variable;
1483 }
1484
1485 public static bool nonvar(object Term)
1486 {
1487 return !YP.var(Term);
1488 }
1489
1490 public static bool atom(object Term)
1491 {
1492 return YP.getValue(Term) is Atom;
1493 }
1494
1495 public static bool integer(object Term)
1496 {
1497 // Debug: Should exhaustively check for all integer types.
1498 return getValue(Term) is int;
1499 }
1500
1501 // Use isFloat instead of float because it is a reserved keyword.
1502 public static bool isFloat(object Term)
1503 {
1504 // Debug: Should exhaustively check for all float types.
1505 return getValue(Term) is double;
1506 }
1507
1508 public static bool number(object Term)
1509 {
1510 return YP.integer(Term) || YP.isFloat(Term);
1511 }
1512
1513 public static bool atomic(object Term)
1514 {
1515 return YP.atom(Term) || YP.number(Term);
1516 }
1517
1518 public static bool compound(object Term)
1519 {
1520 Term = getValue(Term);
1521 return Term is Functor1 || Term is Functor2 || Term is Functor3 || Term is Functor;
1522 }
1523
1524 /// <summary>
1525 /// If input is a TextReader, use it. If input is an Atom or String, create a StreamReader with the
1526 /// input as the filename. If input is a Prolog list, then read character codes from it.
1527 /// </summary>
1528 /// <param name="input"></param>
1529 public static void see(object input)
1530 {
1531 input = YP.getValue(input);
1532 if (input is Variable)
1533 throw new PrologException(Atom.a("instantiation_error"), "Arg is an unbound variable");
1534
1535 if (input == null)
1536 {
1537 _inputStream = null;
1538 return;
1539 }
1540 if (input is TextReader)
1541 {
1542 _inputStream = (TextReader)input;
1543 return;
1544 }
1545 else if (input is Atom)
1546 {
1547 _inputStream = new StreamReader(((Atom)input)._name);
1548 return;
1549 }
1550 else if (input is String)
1551 {
1552 _inputStream = new StreamReader((String)input);
1553 return;
1554 }
1555 else if (input is Functor2 && ((Functor2)input)._name == Atom.DOT)
1556 {
1557 _inputStream = new CodeListReader(input);
1558 return;
1559 }
1560 else
1561 throw new PrologException
1562 (new Functor2("domain_error", Atom.a("stream_or_alias"), input),
1563 "Input stream specifier not recognized");
1564 }
1565
1566 public static void seen()
1567 {
1568 if (_inputStream == null)
1569 return;
1570 if (_inputStream == Console.In)
1571 return;
1572 _inputStream.Close();
1573 _inputStream = Console.In;
1574 }
1575
1576 public static IEnumerable<bool> current_input(object Stream)
1577 {
1578 return YP.unify(Stream, _inputStream);
1579 }
1580
1581 /// <summary>
1582 /// If output is a TextWriter, use it. If output is an Atom or a String, create a StreamWriter
1583 /// with the input as the filename.
1584 /// </summary>
1585 /// <param name="output"></param>
1586 public static void tell(object output)
1587 {
1588 output = YP.getValue(output);
1589 if (output is Variable)
1590 throw new PrologException(Atom.a("instantiation_error"), "Arg is an unbound variable");
1591
1592 if (output == null)
1593 {
1594 _outputStream = null;
1595 return;
1596 }
1597 if (output is TextWriter)
1598 {
1599 _outputStream = (TextWriter)output;
1600 return;
1601 }
1602 else if (output is Atom)
1603 {
1604 _outputStream = new StreamWriter(((Atom)output)._name);
1605 return;
1606 }
1607 else if (output is String)
1608 {
1609 _outputStream = new StreamWriter((String)output);
1610 return;
1611 }
1612 else
1613 throw new PrologException
1614 (new Functor2("domain_error", Atom.a("stream_or_alias"), output),
1615 "Can't open stream for " + output);
1616 }
1617
1618 public static void told()
1619 {
1620 if (_outputStream == null)
1621 return;
1622 if (_outputStream == Console.Out)
1623 return;
1624 _outputStream.Close();
1625 _outputStream = Console.Out;
1626 }
1627
1628 public static IEnumerable<bool> current_output(object Stream)
1629 {
1630 return YP.unify(Stream, _outputStream);
1631 }
1632
1633 public static void write(object x)
1634 {
1635 if (_outputStream == null)
1636 return;
1637 x = YP.getValue(x);
1638 if (x is double)
1639 _outputStream.Write(doubleToString((double)x));
1640 else
1641 _outputStream.Write(x.ToString());
1642 }
1643
1644 /// <summary>
1645 /// Format x as a string, making sure that it won't parse as an int later. I.e., for 1.0, don't just
1646 /// use "1" which will parse as an int.
1647 /// </summary>
1648 /// <param name="x"></param>
1649 /// <returns></returns>
1650 private static string doubleToString(double x)
1651 {
1652 string xString = x.ToString();
1653 // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception?
1654 try
1655 {
1656 Convert.ToInt32(xString);
1657 // The string will parse as an int, not a double, so re-format so that it does.
1658 // Use float if possible, else exponential if it would be too big.
1659 return x.ToString(x >= 100000.0 ? "E1" : "f1");
1660 }
1661 catch (FormatException)
1662 {
1663 // Assume it will parse as a double.
1664 }
1665 return xString;
1666 }
1667
1668 public static void put_code(object x)
1669 {
1670 if (_outputStream == null)
1671 return;
1672 if (var(x))
1673 throw new PrologException(Atom.a("instantiation_error"), "Arg 1 is an unbound variable");
1674 int xInt;
1675 if (!getInt(x, out xInt))
1676 throw new PrologException
1677 (new Functor2("type_error", Atom.a("integer"), x), "Arg 1 must be integer");
1678 _outputStream.Write((char)xInt);
1679 }
1680
1681 public static void nl()
1682 {
1683 if (_outputStream == null)
1684 return;
1685 _outputStream.WriteLine();
1686 }
1687
1688 public static IEnumerable<bool> get_code(object code)
1689 {
1690 if (_inputStream == null)
1691 return YP.unify(code, -1);
1692 else
1693 return YP.unify(code, _inputStream.Read());
1694 }
1695
1696 public static void asserta(object Term, Type declaringClass)
1697 {
1698 assertDynamic(Term, declaringClass, true);
1699 }
1700
1701 public static void assertz(object Term, Type declaringClass)
1702 {
1703 assertDynamic(Term, declaringClass, false);
1704 }
1705
1706 public static void assertDynamic(object Term, Type declaringClass, bool prepend)
1707 {
1708 Term = getValue(Term);
1709 if (Term is Variable)
1710 throw new PrologException("instantiation_error", "Term to assert is an unbound variable");
1711
1712 Variable.CopyStore copyStore = new Variable.CopyStore();
1713 object TermCopy = makeCopy(Term, copyStore);
1714 object Head, Body;
1715 if (TermCopy is Functor2 && ((Functor2)TermCopy)._name == Atom.RULE)
1716 {
1717 Head = YP.getValue(((Functor2)TermCopy)._arg1);
1718 Body = YP.getValue(((Functor2)TermCopy)._arg2);
1719 if (Head is Variable)
1720 throw new PrologException("instantiation_error", "Head to assert is an unbound variable");
1721 if (Body is Variable)
1722 throw new PrologException("instantiation_error", "Body to assert is an unbound variable");
1723 }
1724 else
1725 {
1726 Head = TermCopy;
1727 Body = Atom.a("true");
1728 }
1729
1730 Atom name = getFunctorName(Head) as Atom;
1731 if (name == null)
1732 // name is a non-Atom, such as a number.
1733 throw new PrologException
1734 (new Functor2("type_error", Atom.a("callable"), Head), "Term to assert is not callable");
1735 object[] args = getFunctorArgs(Head);
1736 if (isSystemPredicate(name, args.Length))
1737 throw new PrologException
1738 (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"),
1739 new Functor2(Atom.SLASH, name, args.Length)),
1740 "Assert cannot modify static predicate " + name + "/" + args.Length);
1741
1742 if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true"))
1743 {
1744 // This is a fact with no unbound variables
1745 // assertFact and prependFact use IndexedAnswers, so don't we don't need to compile.
1746 if (prepend)
1747 prependFact(name, args);
1748 else
1749 assertFact(name, args);
1750
1751 return;
1752 }
1753
1754 IClause clause = YPCompiler.compileAnonymousClause(Head, Body, declaringClass);
1755 // We expect clause to be a ClauseHeadAndBody (from Compiler.compileAnonymousFunction)
1756 // so we can set the Head and Body.
1757 if (clause is ClauseHeadAndBody)
1758 ((ClauseHeadAndBody)clause).setHeadAndBody(Head, Body);
1759
1760 // Add the clause to the entry in _predicatesStore.
1761 NameArity nameArity = new NameArity(name, args.Length);
1762 List<IClause> clauses;
1763 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1764 // Create an entry for the nameArity.
1765 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1766
1767 if (prepend)
1768 clauses.Insert(0, clause);
1769 else
1770 clauses.Add(clause);
1771 }
1772
1773 private static bool isSystemPredicate(Atom name, int arity)
1774 {
1775 if (arity == 2 && (name == Atom.a(",") || name == Atom.a(";") || name == Atom.DOT))
1776 return true;
1777 // Use the same mapping to static predicates in YP as the compiler.
1778 foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable()))
1779 return true;
1780 // Debug: Do we need to check if name._module is null?
1781 return false;
1782 }
1783
1784 /// <summary>
1785 /// Assert values at the end of the set of facts for the predicate with the
1786 /// name and with arity values.Length.
1787 /// </summary>
1788 /// <param name="name">must be an Atom</param>
1789 /// <param name="values">the array of arguments to the fact predicate.
1790 /// It is an error if an value has an unbound variable.</param>
1791 public static void assertFact(Atom name, object[] values)
1792 {
1793 NameArity nameArity = new NameArity(name, values.Length);
1794 List<IClause> clauses;
1795 IndexedAnswers indexedAnswers;
1796 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1797 {
1798 // Create an IndexedAnswers as the only clause of the predicate.
1799 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1800 clauses.Add(indexedAnswers = new IndexedAnswers(values.Length));
1801 }
1802 else
1803 {
1804 indexedAnswers = null;
1805 if (clauses.Count >= 1)
1806 indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers;
1807 if (indexedAnswers == null)
1808 // The latest clause is not an IndexedAnswers, so add one.
1809 clauses.Add(indexedAnswers = new IndexedAnswers(values.Length));
1810 }
1811
1812 indexedAnswers.addAnswer(values);
1813 }
1814
1815 /// <summary>
1816 /// Assert values, prepending to the front of the set of facts for the predicate with the
1817 /// name and with arity values.Length.
1818 /// </summary>
1819 /// <param name="name">must be an Atom</param>
1820 /// <param name="values">the array of arguments to the fact predicate.
1821 /// It is an error if an value has an unbound variable.</param>
1822 public static void prependFact(Atom name, object[] values)
1823 {
1824 NameArity nameArity = new NameArity(name, values.Length);
1825 List<IClause> clauses;
1826 IndexedAnswers indexedAnswers;
1827 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
1828 {
1829 // Create an IndexedAnswers as the only clause of the predicate.
1830 _predicatesStore[nameArity] = (clauses = new List<IClause>());
1831 clauses.Add(indexedAnswers = new IndexedAnswers(values.Length));
1832 }
1833 else
1834 {
1835 indexedAnswers = null;
1836 if (clauses.Count >= 1)
1837 indexedAnswers = clauses[0] as IndexedAnswers;
1838 if (indexedAnswers == null)
1839 // The first clause is not an IndexedAnswers, so prepend one.
1840 clauses.Insert(0, indexedAnswers = new IndexedAnswers(values.Length));
1841 }
1842
1843 indexedAnswers.prependAnswer(values);
1844 }
1845
1846 /// <summary>
1847 /// Match all clauses of the dynamic predicate with the name and with arity
1848 /// arguments.Length.
1849 /// If the predicate is not defined, return the result of YP.unknownPredicate.
1850 /// </summary>
1851 /// <param name="name">must be an Atom</param>
1852 /// <param name="arguments">an array of arity number of arguments</param>
1853 /// <returns>an iterator which you can use in foreach</returns>
1854 public static IEnumerable<bool> matchDynamic(Atom name, object[] arguments)
1855 {
1856 List<IClause> clauses;
1857 if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses))
1858 return unknownPredicate(name, arguments.Length,
1859 "Undefined dynamic predicate: " + name + "/" + arguments.Length);
1860
1861 if (clauses.Count == 1)
1862 // Usually there is only one clause, so return it without needing to wrap it in an iterator.
1863 return clauses[0].match(arguments);
1864 else
1865 return matchAllClauses(clauses, arguments);
1866 }
1867
1868 /// <summary>
1869 /// Call match(arguments) for each IClause in clauses. We make this a separate
1870 /// function so that matchDynamic itself does not need to be an iterator object.
1871 /// </summary>
1872 /// <param name="clauses"></param>
1873 /// <param name="arguments"></param>
1874 /// <returns></returns>
1875 private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments)
1876 {
1877 // Debug: If the caller asserts another clause into this same predicate during yield, the iterator
1878 // over clauses will be corrupted. Should we take the time to copy clauses?
1879 foreach (IClause clause in clauses)
1880 {
1881 foreach (bool lastCall in clause.match(arguments))
1882 {
1883 yield return false;
1884 if (lastCall)
1885 // This happens after a cut in a clause.
1886 yield break;
1887 }
1888 }
1889 }
1890
1891 /// <summary>
1892 /// If _prologFlags["unknown"] is fail then return fail(), else if
1893 /// _prologFlags["unknown"] is warning then write the message to YP.write and
1894 /// return fail(), else throw a PrologException for existence_error. .
1895 /// </summary>
1896 /// <param name="name"></param>
1897 /// <param name="arity"></param>
1898 /// <param name="message"></param>
1899 /// <returns></returns>
1900 public static IEnumerable<bool> unknownPredicate(Atom name, int arity, string message)
1901 {
1902 establishPrologFlags();
1903
1904 if (_prologFlags["unknown"] == Atom.a("fail"))
1905 return fail();
1906 else if (_prologFlags["unknown"] == Atom.a("warning"))
1907 {
1908 write(message);
1909 nl();
1910 return fail();
1911 }
1912 else
1913 throw new PrologException
1914 (new Functor2
1915 (Atom.a("existence_error"), Atom.a("procedure"),
1916 new Functor2(Atom.SLASH, name, arity)), message);
1917 }
1918
1919 /// <summary>
1920 /// This is deprecated and just calls matchDynamic. This matches all clauses,
1921 /// not just the ones defined with assertFact.
1922 /// </summary>
1923 /// <param name="name"></param>
1924 /// <param name="arguments"></param>
1925 /// <returns></returns>
1926 public static IEnumerable<bool> matchFact(Atom name, object[] arguments)
1927 {
1928 return matchDynamic(name, arguments);
1929 }
1930
1931 public static IEnumerable<bool> clause(object Head, object Body)
1932 {
1933 Head = getValue(Head);
1934 Body = getValue(Body);
1935 if (Head is Variable)
1936 throw new PrologException("instantiation_error", "Head is an unbound variable");
1937
1938 Atom name = getFunctorName(Head) as Atom;
1939 if (name == null)
1940 // name is a non-Atom, such as a number.
1941 throw new PrologException
1942 (new Functor2("type_error", Atom.a("callable"), Head), "Head is not callable");
1943 object[] args = getFunctorArgs(Head);
1944 if (isSystemPredicate(name, args.Length))
1945 throw new PrologException
1946 (new Functor3("permission_error", Atom.a("access"), Atom.a("private_procedure"),
1947 new Functor2(Atom.SLASH, name, args.Length)),
1948 "clause cannot access private predicate " + name + "/" + args.Length);
1949 if (!(Body is Variable) && !(YP.getFunctorName(Body) is Atom))
1950 throw new PrologException
1951 (new Functor2("type_error", Atom.a("callable"), Body), "Body is not callable");
1952
1953 List<IClause> clauses;
1954 if (!_predicatesStore.TryGetValue(new NameArity(name, args.Length), out clauses))
1955 yield break;
1956 // The caller can assert another clause into this same predicate during yield, so we have to
1957 // make a copy of the clauses.
1958 foreach (IClause predicateClause in clauses.ToArray())
1959 {
1960 foreach (bool l1 in predicateClause.clause(Head, Body))
1961 yield return false;
1962 }
1963 }
1964
1965 public static IEnumerable<bool> retract(object Term)
1966 {
1967 Term = getValue(Term);
1968 if (Term is Variable)
1969 throw new PrologException("instantiation_error", "Term to retract is an unbound variable");
1970
1971 object Head, Body;
1972 if (Term is Functor2 && ((Functor2)Term)._name == Atom.RULE)
1973 {
1974 Head = YP.getValue(((Functor2)Term)._arg1);
1975 Body = YP.getValue(((Functor2)Term)._arg2);
1976 }
1977 else
1978 {
1979 Head = Term;
1980 Body = Atom.a("true");
1981 }
1982 if (Head is Variable)
1983 throw new PrologException("instantiation_error", "Head is an unbound variable");
1984
1985 Atom name = getFunctorName(Head) as Atom;
1986 if (name == null)
1987 // name is a non-Atom, such as a number.
1988 throw new PrologException
1989 (new Functor2("type_error", Atom.a("callable"), Head), "Head is not callable");
1990 object[] args = getFunctorArgs(Head);
1991 if (isSystemPredicate(name, args.Length))
1992 throw new PrologException
1993 (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"),
1994 new Functor2(Atom.SLASH, name, args.Length)),
1995 "clause cannot access private predicate " + name + "/" + args.Length);
1996 if (!(Body is Variable) && !(YP.getFunctorName(Body) is Atom))
1997 throw new PrologException
1998 (new Functor2("type_error", Atom.a("callable"), Body), "Body is not callable");
1999
2000 List<IClause> clauses;
2001 if (!_predicatesStore.TryGetValue(new NameArity(name, args.Length), out clauses))
2002 yield break;
2003 // The caller can assert another clause into this same predicate during yield, so we have to
2004 // make a copy of the clauses.
2005 foreach (IClause predicateClause in clauses.ToArray())
2006 {
2007 if (predicateClause is IndexedAnswers)
2008 {
2009 // IndexedAnswers handles its own retract. Even if it removes all of its
2010 // answers, it is OK to leave it empty as one of the elements in clauses.
2011 foreach (bool l1 in ((IndexedAnswers)predicateClause).retract(Head, Body))
2012 yield return false;
2013 }
2014 else
2015 {
2016 foreach (bool l1 in predicateClause.clause(Head, Body))
2017 {
2018 clauses.Remove(predicateClause);
2019 yield return false;
2020 }
2021 }
2022 }
2023 }
2024
2025 /// <summary>
2026 /// This is deprecated for backward compatibility. You should use retractall.
2027 /// </summary>
2028 /// <param name="name">must be an Atom</param>
2029 /// <param name="arguments">an array of arity number of arguments</param>
2030 public static void retractFact(Atom name, object[] arguments)
2031 {
2032 retractall(Functor.make(name, arguments));
2033 }
2034
2035 /// <summary>
2036 /// Retract all dynamic clauses which unify with Head. If this matches all clauses in a predicate,
2037 /// the predicate is still defined. To completely remove the predicate, see abolish.
2038 /// </summary>
2039 /// <param name="Head"></param>
2040 public static void retractall(object Head)
2041 {
2042 object name = YP.getFunctorName(Head);
2043 object[] arguments = getFunctorArgs(Head);
2044 if (!(name is Atom))
2045 return;
2046 NameArity nameArity = new NameArity((Atom)name, arguments.Length);
2047 List<IClause> clauses;
2048 if (!_predicatesStore.TryGetValue(nameArity, out clauses))
2049 // Can't find, so ignore.
2050 return;
2051
2052 foreach (object arg in arguments)
2053 {
2054 if (!YP.var(arg))
2055 throw new InvalidOperationException
2056 ("Until matching retractall is supported, all arguments must be unbound to retract all clauses");
2057 }
2058 // Clear all clauses.
2059 _predicatesStore[nameArity] = new List<IClause>();
2060 }
2061
2062 /// <summary>
2063 /// If NameSlashArity is var, match with all the dynamic predicates using the
2064 /// Name/Artity form.
2065 /// If NameSlashArity is not var, check if the Name/Arity exists as a static or
2066 /// dynamic predicate.
2067 /// </summary>
2068 /// <param name="NameSlashArity"></param>
2069 /// <param name="declaringClass">if not null, used to resolve references to the default
2070 /// module Atom.a("")</param>
2071 /// <returns></returns>
2072 public static IEnumerable<bool> current_predicate(object NameSlashArity, Type declaringClass)
2073 {
2074 NameSlashArity = YP.getValue(NameSlashArity);
2075 // First check if Name and Arity are nonvar so we can do a direct lookup.
2076 if (YP.ground(NameSlashArity))
2077 {
2078 Functor2 NameArityFunctor = NameSlashArity as Functor2;
2079 if (!(NameArityFunctor != null && NameArityFunctor._name == Atom.SLASH))
2080 throw new PrologException
2081 (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity),
2082 "Must be a name/arity predicate indicator");
2083 object name = YP.getValue(NameArityFunctor._arg1);
2084 object arity = YP.getValue(NameArityFunctor._arg2);
2085 if (name is Variable || arity is Variable)
2086 throw new PrologException
2087 ("instantiation_error", "Predicate indicator name or arity is an unbound variable");
2088 if (!(name is Atom && arity is int))
2089 throw new PrologException
2090 (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity),
2091 "Must be a name/arity predicate indicator");
2092 if ((int)arity < 0)
2093 throw new PrologException
2094 (new Functor2("domain_error", Atom.a("not_less_than_zero"), arity),
2095 "Arity may not be less than zero");
2096
2097 if (YPCompiler.isCurrentPredicate((Atom)name, (int)arity, declaringClass))
2098 // The predicate is defined.
2099 yield return false;
2100 }
2101 else
2102 {
2103 foreach (NameArity key in _predicatesStore.Keys)
2104 {
2105 foreach (bool l1 in YP.unify
2106 (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity))
2107 yield return false;
2108 }
2109 }
2110 }
2111
2112 /// <summary>
2113 /// Return true if the dynamic predicate store has an entry for the predicate
2114 /// with name and arity.
2115 /// </summary>
2116 /// <param name="name"></param>
2117 /// <param name="arity"></param>
2118 /// <returns></returns>
2119 public static bool isDynamicCurrentPredicate(Atom name, int arity)
2120 {
2121 return _predicatesStore.ContainsKey(new NameArity(name, arity));
2122 }
2123
2124 public static void abolish(object NameSlashArity)
2125 {
2126 NameSlashArity = YP.getValue(NameSlashArity);
2127 if (NameSlashArity is Variable)
2128 throw new PrologException
2129 ("instantiation_error", "Predicate indicator is an unbound variable");
2130 Functor2 NameArityFunctor = NameSlashArity as Functor2;
2131 if (!(NameArityFunctor != null && NameArityFunctor._name == Atom.SLASH))
2132 throw new PrologException
2133 (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity),
2134 "Must be a name/arity predicate indicator");
2135 object name = YP.getValue(NameArityFunctor._arg1);
2136 object arity = YP.getValue(NameArityFunctor._arg2);
2137 if (name is Variable || arity is Variable)
2138 throw new PrologException
2139 ("instantiation_error", "Predicate indicator name or arity is an unbound variable");
2140 if (!(name is Atom))
2141 throw new PrologException
2142 (new Functor2("type_error", Atom.a("atom"), name),
2143 "Predicate indicator name must be an atom");
2144 if (!(arity is int))
2145 throw new PrologException
2146 (new Functor2("type_error", Atom.a("integer"), arity),
2147 "Predicate indicator arity must be an integer");
2148 if ((int)arity < 0)
2149 throw new PrologException
2150 (new Functor2("domain_error", Atom.a("not_less_than_zero"), arity),
2151 "Arity may not be less than zero");
2152 if ((int)arity > MAX_ARITY)
2153 throw new PrologException
2154 (new Functor1("representation_error", Atom.a("max_arity")),
2155 "Arity may not be greater than " + MAX_ARITY);
2156
2157 if (isSystemPredicate((Atom)name, (int)arity))
2158 throw new PrologException
2159 (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"),
2160 new Functor2(Atom.SLASH, name, arity)),
2161 "Abolish cannot modify static predicate " + name + "/" + arity);
2162 _predicatesStore.Remove(new NameArity((Atom)name, (int)arity));
2163 }
2164
2165 /// <summary>
2166 /// If Goal is a simple predicate, call YP.getFunctorName(Goal) using arguments from
2167 /// YP.getFunctorArgs(Goal). If not found, this throws a PrologException for existence_error.
2168 /// Otherwise, compile the goal as a single clause predicate and invoke it.
2169 /// </summary>
2170 /// <param name="Goal"></param>
2171 /// <param name="declaringClass">if not null, used to resolve references to the default
2172 /// module Atom.a("")</param>
2173 /// <returns></returns>
2174 public static IEnumerable<bool> getIterator(object Goal, Type declaringClass)
2175 {
2176 Atom name;
2177 object[] args;
2178 while (true)
2179 {
2180 Goal = YP.getValue(Goal);
2181 if (Goal is Variable)
2182 throw new PrologException("instantiation_error", "Goal to call is an unbound variable");
2183 name = YP.getFunctorName(Goal) as Atom;
2184 if (name == null)
2185 throw new PrologException
2186 (new Functor2("type_error", Atom.a("callable"), Goal), "Goal to call is not callable");
2187 args = YP.getFunctorArgs(Goal);
2188 if (name == Atom.HAT && args.Length == 2)
2189 // Assume this is called from a bagof operation. Skip the leading qualifiers.
2190 Goal = YP.getValue(((Functor2)Goal)._arg2);
2191 else
2192 break;
2193 }
2194
2195 IEnumerable<bool> simpleIterator = YPCompiler.getSimpleIterator(name, args, declaringClass);
2196 if (simpleIterator != null)
2197 // We don't need to compile since the goal is a simple predicate which we call directly.
2198 return simpleIterator;
2199
2200 // Compile the goal as a clause.
2201 List<Variable> variableSetList = new List<Variable>();
2202 addUniqueVariables(Goal, variableSetList);
2203 Variable[] variableSet = variableSetList.ToArray();
2204
2205 // Use Atom.F since it is ignored.
2206 return YPCompiler.compileAnonymousClause
2207 (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet);
2208 }
2209
2210 public static void throwException(object Term)
2211 {
2212 throw new PrologException(Term);
2213 }
2214 /// <summary>
2215 /// This must be called by any function that uses YP._prologFlags to make sure
2216 /// the initial defaults are loaded.
2217 /// </summary>
2218 private static void establishPrologFlags()
2219 {
2220 if (_prologFlags.Count > 0)
2221 // Already established.
2222 return;
2223
2224 // List these in the order they appear in the ISO standard.
2225 _prologFlags["bounded"] = Atom.a("true");
2226 _prologFlags["max_integer"] = Int32.MaxValue;
2227 _prologFlags["min_integer"] = Int32.MinValue;
2228 _prologFlags["integer_rounding_function"] = Atom.a("toward_zero");
2229 _prologFlags["char_conversion"] = Atom.a("off");
2230 _prologFlags["debug"] = Atom.a("off");
2231 _prologFlags["max_arity"] = MAX_ARITY;
2232 _prologFlags["unknown"] = Atom.a("error");
2233 _prologFlags["double_quotes"] = Atom.a("codes");
2234 }
2235
2236 public static IEnumerable<bool> current_prolog_flag(object Key, object Value)
2237 {
2238 establishPrologFlags();
2239
2240 Key = YP.getValue(Key);
2241 Value = YP.getValue(Value);
2242
2243 if (Key is Variable)
2244 {
2245 // Bind all key values.
2246 foreach (string key in _prologFlags.Keys)
2247 {
2248 foreach (bool l1 in YP.unify(Key, Atom.a(key)))
2249 {
2250 foreach (bool l2 in YP.unify(Value, _prologFlags[key]))
2251 yield return false;
2252 }
2253 }
2254 }
2255 else
2256 {
2257 if (!(Key is Atom))
2258 throw new PrologException
2259 (new Functor2("type_error", Atom.a("atom"), Key), "Arg 1 Key is not an atom");
2260 if (!_prologFlags.ContainsKey(((Atom)Key)._name))
2261 throw new PrologException
2262 (new Functor2("domain_error", Atom.a("prolog_flag"), Key),
2263 "Arg 1 Key is not a recognized flag");
2264
2265 foreach (bool l1 in YP.unify(Value, _prologFlags[((Atom)Key)._name]))
2266 yield return false;
2267 }
2268 }
2269
2270 public static void set_prolog_flag(object Key, object Value)
2271 {
2272 establishPrologFlags();
2273
2274 Key = YP.getValue(Key);
2275 Value = YP.getValue(Value);
2276
2277 if (Key is Variable)
2278 throw new PrologException(Atom.a("instantiation_error"),
2279 "Arg 1 Key is an unbound variable");
2280 if (Value is Variable)
2281 throw new PrologException(Atom.a("instantiation_error"),
2282 "Arg 1 Key is an unbound variable");
2283 if (!(Key is Atom))
2284 throw new PrologException
2285 (new Functor2("type_error", Atom.a("atom"), Key), "Arg 1 Key is not an atom");
2286
2287 string keyName = ((Atom)Key)._name;
2288 if (!_prologFlags.ContainsKey(keyName))
2289 throw new PrologException
2290 (new Functor2("domain_error", Atom.a("prolog_flag"), Key),
2291 "Arg 1 Key " + Key + " is not a recognized flag");
2292
2293 bool valueIsOK = false;
2294 if (keyName == "char_conversion")
2295 valueIsOK = (Value == _prologFlags[keyName]);
2296 else if (keyName == "debug")
2297 valueIsOK = (Value == _prologFlags[keyName]);
2298 else if (keyName == "unknown")
2299 valueIsOK = (Value == Atom.a("fail") || Value == Atom.a("warning") ||
2300 Value == Atom.a("error"));
2301 else if (keyName == "double_quotes")
2302 valueIsOK = (Value == Atom.a("codes") || Value == Atom.a("chars") ||
2303 Value == Atom.a("atom"));
2304 else
2305 throw new PrologException
2306 (new Functor3("permission_error", Atom.a("modify"), Atom.a("flag"), Key),
2307 "May not modify Prolog flag " + Key);
2308
2309 if (!valueIsOK)
2310 throw new PrologException
2311 (new Functor2("domain_error", Atom.a("flag_value"), new Functor2("+", Key, Value)),
2312 "May not set arg 1 Key " + Key + " to arg 2 Value " + Value);
2313
2314 _prologFlags[keyName] = Value;
2315 }
2316 /// <summary>
2317 /// script_event calls hosting script with events as a callback method.
2318 /// </summary>
2319 /// <param name="script_event"></param>
2320 /// <param name="script_params"></param>
2321 /// <returns></returns>
2322 public static IEnumerable<bool> script_event(object script_event, object script_params)
2323 {
2324 // string function = ((Atom)YP.getValue(script_event))._name;
2325 object[] array = ListPair.toArray(script_params);
2326 if (array == null)
2327 yield return false; // return; // YP.fail();
2328 if (array.Length > 1)
2329 {
2330 //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue
2331 //(localID, itemID, function, array);
2332 // sortArray(array);
2333 }
2334 //return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
2335 yield return false;
2336 }
2337
2338 /* Non-prolog-ish functions for inline coding */
2339 public static string regexString(string inData, string inPattern, string presep,string postsep)
2340 {
2341 //string str=cycMessage;
2342 //string strMatch = @"\. \#\$(.*)\)";
2343 string results = "";
2344 for (Match m = Regex.Match(inData,inPattern); m.Success; m=m.NextMatch())
2345 {
2346 //Console.WriteLine( m );
2347 results += presep+ m + postsep;
2348 }
2349 return results;
2350 }
2351
2352 public static string cycComm(object msgobj)
2353 {
2354 string cycInputString = msgobj.ToString();
2355 string cycOutputString="";
2356 TcpClient socketForServer;
2357
2358 try
2359 {
2360 socketForServer = new TcpClient("localHost", 3601);
2361 }
2362 catch
2363 {
2364 Console.WriteLine("Failed to connect to server at {0}:999", "localhost");
2365 return "";
2366 }
2367
2368 NetworkStream networkStream = socketForServer.GetStream();
2369
2370 System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
2371
2372 System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
2373
2374 try
2375 {
2376 // read the data from the host and display it
2377
2378 {
2379
2380 streamWriter.WriteLine(cycInputString);
2381 streamWriter.Flush();
2382
2383 cycOutputString = streamReader.ReadLine();
2384 Console.WriteLine("Cycoutput:" + cycOutputString);
2385 //streamWriter.WriteLine("Client Message");
2386 //Console.WriteLine("Client Message");
2387 streamWriter.Flush();
2388 }
2389
2390 }
2391 catch
2392 {
2393 Console.WriteLine("Exception reading from Server");
2394 return "";
2395 }
2396 // tidy up
2397 networkStream.Close();
2398 return cycOutputString;
2399
2400 }
2401 //public static void throwException(object Term)
2402 //{
2403 // throw new PrologException(Term);
2404 //}
2405 /// <summary>
2406 /// An enumerator that does zero loops.
2407 /// </summary>
2408 private class Fail : IEnumerator<bool>, IEnumerable<bool>
2409 {
2410 public bool MoveNext()
2411 {
2412 return false;
2413 }
2414
2415 public IEnumerator<bool> GetEnumerator()
2416 {
2417 return (IEnumerator<bool>)this;
2418 }
2419
2420 IEnumerator IEnumerable.GetEnumerator()
2421 {
2422 return GetEnumerator();
2423 }
2424
2425 public bool Current
2426 {
2427 get { return true; }
2428 }
2429
2430 object IEnumerator.Current
2431 {
2432 get { return true; }
2433 }
2434
2435 public void Dispose()
2436 {
2437 }
2438
2439 public void Reset()
2440 {
2441 throw new NotImplementedException();
2442 }
2443 }
2444
2445 /// <summary>
2446 /// An enumerator that does one iteration.
2447 /// </summary>
2448 private class Succeed : IEnumerator<bool>, IEnumerable<bool>
2449 {
2450 private bool _didIteration = false;
2451
2452 public bool MoveNext()
2453 {
2454 if (!_didIteration)
2455 {
2456 _didIteration = true;
2457 return true;
2458 }
2459 else
2460 return false;
2461 }
2462
2463 public IEnumerator<bool> GetEnumerator()
2464 {
2465 return (IEnumerator<bool>)this;
2466 }
2467
2468 IEnumerator IEnumerable.GetEnumerator()
2469 {
2470 return GetEnumerator();
2471 }
2472
2473 public bool Current
2474 {
2475 get { return false; }
2476 }
2477
2478 object IEnumerator.Current
2479 {
2480 get { return false; }
2481 }
2482
2483 public void Dispose()
2484 {
2485 }
2486
2487 public void Reset()
2488 {
2489 throw new NotImplementedException();
2490 }
2491 }
2492
2493 /// <summary>
2494 /// An enumerator that repeats forever.
2495 /// </summary>
2496 private class Repeat : IEnumerator<bool>, IEnumerable<bool>
2497 {
2498 public bool MoveNext()
2499 {
2500 return true;
2501 }
2502
2503 public IEnumerator<bool> GetEnumerator()
2504 {
2505 return (IEnumerator<bool>)this;
2506 }
2507
2508 IEnumerator IEnumerable.GetEnumerator()
2509 {
2510 return GetEnumerator();
2511 }
2512
2513 public bool Current
2514 {
2515 get { return false; }
2516 }
2517
2518 object IEnumerator.Current
2519 {
2520 get { return false; }
2521 }
2522
2523 public void Dispose()
2524 {
2525 }
2526
2527 public void Reset()
2528 {
2529 throw new NotImplementedException();
2530 }
2531 }
2532
2533 /// <summary>
2534 /// An enumerator that wraps another enumerator in order to catch a PrologException.
2535 /// </summary>
2536 public class Catch : IEnumerator<bool>, IEnumerable<bool>
2537 {
2538 private IEnumerator<bool> _enumerator;
2539 private PrologException _exception = null;
2540
2541 /// <summary>
2542 /// Call YP.getIterator(Goal, declaringClass) and save the returned iterator.
2543 /// If getIterator throws an exception, save it the same as MoveNext().
2544 /// </summary>
2545 /// <param name="Goal"></param>
2546 /// <param name="declaringClass"></param>
2547 public Catch(object Goal, Type declaringClass)
2548 {
2549 try
2550 {
2551 _enumerator = getIterator(Goal, declaringClass).GetEnumerator();
2552 }
2553 catch (PrologException exception)
2554 {
2555 // MoveNext() will check this.
2556 _exception = exception;
2557 }
2558 }
2559
2560 /// <summary>
2561 /// Call _enumerator.MoveNext(). If it throws a PrologException, set _exception
2562 /// and return false. After this returns false, call unifyExceptionOrThrow.
2563 /// </summary>
2564 /// <returns></returns>
2565 public bool MoveNext()
2566 {
2567 if (_exception != null)
2568 return false;
2569
2570 try
2571 {
2572 return _enumerator.MoveNext();
2573 }
2574 catch (PrologException exception)
2575 {
2576 _exception = exception;
2577 return false;
2578 }
2579 }
2580
2581 /// <summary>
2582 /// Call this after MoveNext() returns false to check for an exception. If
2583 /// MoveNext did not get a PrologException, don't yield.
2584 /// Otherwise, unify the exception with Catcher and yield so the caller can
2585 /// do the handler code. However, if can't unify with Catcher then throw the exception.
2586 /// </summary>
2587 /// <param name="Catcher"></param>
2588 /// <returns></returns>
2589 public IEnumerable<bool> unifyExceptionOrThrow(object Catcher)
2590 {
2591 if (_exception != null)
2592 {
2593 bool didUnify = false;
2594 foreach (bool l1 in YP.unify(_exception._term, Catcher))
2595 {
2596 didUnify = true;
2597 yield return false;
2598 }
2599 if (!didUnify)
2600 throw _exception;
2601 }
2602 }
2603
2604 public IEnumerator<bool> GetEnumerator()
2605 {
2606 return (IEnumerator<bool>)this;
2607 }
2608
2609 IEnumerator IEnumerable.GetEnumerator()
2610 {
2611 return GetEnumerator();
2612 }
2613
2614 public bool Current
2615 {
2616 get { return _enumerator.Current; }
2617 }
2618
2619 object IEnumerator.Current
2620 {
2621 get { return _enumerator.Current; }
2622 }
2623
2624 public void Dispose()
2625 {
2626 if (_enumerator != null)
2627 _enumerator.Dispose();
2628 }
2629
2630 public void Reset()
2631 {
2632 throw new NotImplementedException();
2633 }
2634 }
2635 #pragma warning restore 0168
2636 /// <summary>
2637 /// A ClauseHeadAndBody is used in Compiler.compileAnonymousFunction as a base class
2638 /// in order to implement YP.IClause. After creating the object, you must call setHeadAndBody.
2639 /// </summary>
2640 public class ClauseHeadAndBody
2641 {
2642 private object _Head;
2643 private object _Body;
2644
2645 public void setHeadAndBody(object Head, object Body)
2646 {
2647 _Head = Head;
2648 _Body = Body;
2649 }
2650
2651 public IEnumerable<bool> clause(object Head, object Body)
2652 {
2653 if (_Head == null || _Body == null)
2654 yield break;
2655
2656 #pragma warning disable 0168
2657 foreach (bool l1 in YP.unify(Head, _Head))
2658 {
2659 foreach (bool l2 in YP.unify(Body, _Body))
2660 yield return false;
2661 }
2662 #pragma warning restore 0168
2663 }
2664 }
2665
2666 /// <summary>
2667 /// CodeListReader extends TextReader and overrides Read to read the next code from
2668 /// the CodeList which is a Prolog list of integer character codes.
2669 /// </summary>
2670 public class CodeListReader : TextReader
2671 {
2672 private object _CodeList;
2673
2674 public CodeListReader(object CodeList)
2675 {
2676 _CodeList = YP.getValue(CodeList);
2677 }
2678
2679 /// <summary>
2680 /// If the head of _CodeList is an integer, return it and advance the list. Otherwise,
2681 /// return -1 for end of file.
2682 /// </summary>
2683 /// <returns></returns>
2684 public override int Read()
2685 {
2686 Functor2 CodeListPair = _CodeList as Functor2;
2687 int code;
2688 if (!(CodeListPair != null && CodeListPair._name == Atom.DOT &&
2689 getInt(CodeListPair._arg1, out code)))
2690 {
2691 _CodeList = Atom.NIL;
2692 return -1;
2693 }
2694
2695 // Advance.
2696 _CodeList = YP.getValue(CodeListPair._arg2);
2697 return code;
2698 }
2699 }
2700 }
2701}