diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs | 1644 |
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 | |||
31 | using System; | ||
32 | using System.Collections; | ||
33 | using System.Collections.Generic; | ||
34 | using System.IO; | ||
35 | using System.Reflection; | ||
36 | |||
37 | namespace 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 | } | ||