diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs | 2701 |
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 | |||
31 | using System; | ||
32 | using System.Collections; | ||
33 | using System.Collections.Generic; | ||
34 | using System.IO; | ||
35 | using System.Reflection; | ||
36 | using System.Net.Sockets; | ||
37 | using System.Text; | ||
38 | using System.Text.RegularExpressions; | ||
39 | |||
40 | namespace 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 | } | ||