opensim-development – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 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 | using log4net; |
||
40 | |||
41 | namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog |
||
42 | { |
||
43 | /// <summary> |
||
44 | /// YP has static methods for general functions in Yield Prolog such as <see cref="getValue"/> |
||
45 | /// and <see cref="unify"/>. |
||
46 | /// </summary> |
||
47 | public class YP |
||
48 | { |
||
49 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
50 | private static Fail _fail = new Fail(); |
||
51 | private static Repeat _repeat = new Repeat(); |
||
52 | private static Dictionary<NameArity, List<IClause>> _predicatesStore = |
||
53 | new Dictionary<NameArity, List<IClause>>(); |
||
54 | private static TextWriter _outputStream = System.Console.Out; |
||
55 | private static TextReader _inputStream = System.Console.In; |
||
56 | private static IndexedAnswers _operatorTable = null; |
||
57 | private static Dictionary<string, object> _prologFlags = new Dictionary<string, object>(); |
||
58 | public const int MAX_ARITY = 255; |
||
59 | |||
60 | /// <summary> |
||
61 | /// An IClause is used so that dynamic predicates can call match. |
||
62 | /// </summary> |
||
63 | public interface IClause |
||
64 | { |
||
65 | IEnumerable<bool> match(object[] args); |
||
66 | IEnumerable<bool> clause(object Head, object Body); |
||
67 | } |
||
68 | |||
69 | /// <summary> |
||
70 | /// If value is a Variable, then return its getValue. Otherwise, just |
||
71 | /// return value. You should call YP.getValue on any object that |
||
72 | /// may be a Variable to get the value to pass to other functions in |
||
73 | /// your system that are not part of Yield Prolog, such as math functions |
||
74 | /// or file I/O. |
||
75 | /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html |
||
76 | /// </summary> |
||
77 | /// <param name="value"></param> |
||
78 | /// <returns></returns> |
||
79 | public static object getValue(object value) |
||
80 | { |
||
81 | if (value is Variable) |
||
82 | return ((Variable)value).getValue(); |
||
83 | else |
||
84 | return value; |
||
85 | } |
||
86 | |||
87 | /// <summary> |
||
88 | /// If arg1 or arg2 is an object with a unify method (such as Variable or |
||
89 | /// Functor) then just call its unify with the other argument. The object's |
||
90 | /// unify method will bind the values or check for equals as needed. |
||
91 | /// Otherwise, both arguments are "normal" (atomic) values so if they |
||
92 | /// are equal then succeed (yield once), else fail (don't yield). |
||
93 | /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html |
||
94 | /// </summary> |
||
95 | /// <param name="arg1"></param> |
||
96 | /// <param name="arg2"></param> |
||
97 | /// <returns></returns> |
||
98 | public static IEnumerable<bool> unify(object arg1, object arg2) |
||
99 | { |
||
100 | arg1 = getValue(arg1); |
||
101 | arg2 = getValue(arg2); |
||
102 | if (arg1 is IUnifiable) |
||
103 | return ((IUnifiable)arg1).unify(arg2); |
||
104 | else if (arg2 is IUnifiable) |
||
105 | return ((IUnifiable)arg2).unify(arg1); |
||
106 | else |
||
107 | { |
||
108 | // Arguments are "normal" types. |
||
109 | if (arg1.Equals(arg2)) |
||
110 | return new Succeed(); |
||
111 | else |
||
112 | return _fail; |
||
113 | } |
||
114 | } |
||
115 | |||
116 | /// <summary> |
||
117 | /// This is used for the lookup key in _factStore. |
||
118 | /// </summary> |
||
119 | public struct NameArity |
||
120 | { |
||
121 | public readonly Atom _name; |
||
122 | public readonly int _arity; |
||
123 | |||
124 | public NameArity(Atom name, int arity) |
||
125 | { |
||
126 | _name = name; |
||
127 | _arity = arity; |
||
128 | } |
||
129 | |||
130 | public override bool Equals(object obj) |
||
131 | { |
||
132 | if (obj is NameArity) |
||
133 | { |
||
134 | NameArity nameArity = (NameArity)obj; |
||
135 | return nameArity._name.Equals(_name) && nameArity._arity.Equals(_arity); |
||
136 | } |
||
137 | else |
||
138 | { |
||
139 | return false; |
||
140 | } |
||
141 | } |
||
142 | |||
143 | public override int GetHashCode() |
||
144 | { |
||
145 | return _name.GetHashCode() ^ _arity.GetHashCode(); |
||
146 | } |
||
147 | } |
||
148 | |||
149 | /// <summary> |
||
150 | /// Convert term to an int. |
||
151 | /// If term is a single-element List, use its first element |
||
152 | /// (to handle the char types like "a"). |
||
153 | /// If can't convert, throw a PrologException for type_error evaluable (because this is only |
||
154 | /// called from arithmetic functions). |
||
155 | /// </summary> |
||
156 | /// <param name="term"></param> |
||
157 | /// <returns></returns> |
||
158 | public static int convertInt(object term) |
||
159 | { |
||
160 | term = YP.getValue(term); |
||
161 | if (term is Functor2 && ((Functor2)term)._name == Atom.DOT && |
||
162 | YP.getValue(((Functor2)term)._arg2) == Atom.NIL) |
||
163 | // Assume it is a char type like "a". |
||
164 | term = YP.getValue(((Functor2)term)._arg1); |
||
165 | if (term is Variable) |
||
166 | throw new PrologException(Atom.a("instantiation_error"), |
||
167 | "Expected a number but the argument is an unbound variable"); |
||
168 | |||
169 | try |
||
170 | { |
||
171 | return (int)term; |
||
172 | } |
||
173 | catch (InvalidCastException) |
||
174 | { |
||
175 | throw new PrologException |
||
176 | (new Functor2 |
||
177 | ("type_error", Atom.a("evaluable"), |
||
178 | new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)), |
||
179 | "Term must be an integer"); |
||
180 | } |
||
181 | } |
||
182 | |||
183 | /// <summary> |
||
184 | /// Convert term to a double. This may convert an int to a double, etc. |
||
185 | /// If term is a single-element List, use its first element |
||
186 | /// (to handle the char types like "a"). |
||
187 | /// If can't convert, throw a PrologException for type_error evaluable (because this is only |
||
188 | /// called from arithmetic functions). |
||
189 | /// </summary> |
||
190 | /// <param name="term"></param> |
||
191 | /// <returns></returns> |
||
192 | public static double convertDouble(object term) |
||
193 | { |
||
194 | term = YP.getValue(term); |
||
195 | if (term is Functor2 && ((Functor2)term)._name == Atom.DOT && |
||
196 | YP.getValue(((Functor2)term)._arg2) == Atom.NIL) |
||
197 | // Assume it is a char type like "a". |
||
198 | term = YP.getValue(((Functor2)term)._arg1); |
||
199 | if (term is Variable) |
||
200 | throw new PrologException(Atom.a("instantiation_error"), |
||
201 | "Expected a number but the argument is an unbound variable"); |
||
202 | |||
203 | try |
||
204 | { |
||
205 | return Convert.ToDouble(term); |
||
206 | } |
||
207 | catch (InvalidCastException) |
||
208 | { |
||
209 | throw new PrologException |
||
210 | (new Functor2 |
||
211 | ("type_error", Atom.a("evaluable"), |
||
212 | new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)), |
||
213 | "Term must be an integer"); |
||
214 | } |
||
215 | } |
||
216 | |||
217 | /// <summary> |
||
218 | /// If term is an integer, set intTerm. |
||
219 | /// If term is a single-element List, use its first element |
||
220 | /// (to handle the char types like "a"). Return true for success, false if can't convert. |
||
221 | /// We use a success return value because throwing an exception is inefficient. |
||
222 | /// </summary> |
||
223 | /// <param name="term"></param> |
||
224 | /// <returns></returns> |
||
225 | public static bool getInt(object term, out int intTerm) |
||
226 | { |
||
227 | term = YP.getValue(term); |
||
228 | if (term is Functor2 && ((Functor2)term)._name == Atom.DOT && |
||
229 | YP.getValue(((Functor2)term)._arg2) == Atom.NIL) |
||
230 | // Assume it is a char type like "a". |
||
231 | term = YP.getValue(((Functor2)term)._arg1); |
||
232 | |||
233 | if (term is int) |
||
234 | { |
||
235 | intTerm = (int)term; |
||
236 | return true; |
||
237 | } |
||
238 | |||
239 | intTerm = 0; |
||
240 | return false; |
||
241 | } |
||
242 | |||
243 | public static bool equal(object x, object y) |
||
244 | { |
||
245 | x = YP.getValue(x); |
||
246 | if (x is DateTime) |
||
247 | return (DateTime)x == (DateTime)YP.getValue(y); |
||
248 | // Assume convertDouble converts an int to a double perfectly. |
||
249 | return YP.convertDouble(x) == YP.convertDouble(y); |
||
250 | } |
||
251 | |||
252 | public static bool notEqual(object x, object y) |
||
253 | { |
||
254 | x = YP.getValue(x); |
||
255 | if (x is DateTime) |
||
256 | return (DateTime)x != (DateTime)YP.getValue(y); |
||
257 | // Assume convertDouble converts an int to a double perfectly. |
||
258 | return YP.convertDouble(x) != YP.convertDouble(y); |
||
259 | } |
||
260 | |||
261 | public static bool greaterThan(object x, object y) |
||
262 | { |
||
263 | x = YP.getValue(x); |
||
264 | if (x is DateTime) |
||
265 | return (DateTime)x > (DateTime)YP.getValue(y); |
||
266 | // Assume convertDouble converts an int to a double perfectly. |
||
267 | return YP.convertDouble(x) > YP.convertDouble(y); |
||
268 | } |
||
269 | |||
270 | public static bool lessThan(object x, object y) |
||
271 | { |
||
272 | x = YP.getValue(x); |
||
273 | if (x is DateTime) |
||
274 | return (DateTime)x < (DateTime)YP.getValue(y); |
||
275 | // Assume convertDouble converts an int to a double perfectly. |
||
276 | return YP.convertDouble(x) < YP.convertDouble(y); |
||
277 | } |
||
278 | |||
279 | public static bool greaterThanOrEqual(object x, object y) |
||
280 | { |
||
281 | x = YP.getValue(x); |
||
282 | if (x is DateTime) |
||
283 | return (DateTime)x >= (DateTime)YP.getValue(y); |
||
284 | // Assume convertDouble converts an int to a double perfectly. |
||
285 | return YP.convertDouble(x) >= YP.convertDouble(y); |
||
286 | } |
||
287 | |||
288 | public static bool lessThanOrEqual(object x, object y) |
||
289 | { |
||
290 | x = YP.getValue(x); |
||
291 | if (x is DateTime) |
||
292 | return (DateTime)x <= (DateTime)YP.getValue(y); |
||
293 | // Assume convertDouble converts an int to a double perfectly. |
||
294 | return YP.convertDouble(x) <= YP.convertDouble(y); |
||
295 | } |
||
296 | |||
297 | public static object negate(object x) |
||
298 | { |
||
299 | int intX; |
||
300 | if (getInt(x, out intX)) |
||
301 | return -intX; |
||
302 | return -convertDouble(x); |
||
303 | } |
||
304 | |||
305 | public static object abs(object x) |
||
306 | { |
||
307 | int intX; |
||
308 | if (getInt(x, out intX)) |
||
309 | return Math.Abs(intX); |
||
310 | return Math.Abs(convertDouble(x)); |
||
311 | } |
||
312 | |||
313 | public static object sign(object x) |
||
314 | { |
||
315 | int intX; |
||
316 | if (getInt(x, out intX)) |
||
317 | return Math.Sign(intX); |
||
318 | return Math.Sign(convertDouble(x)); |
||
319 | } |
||
320 | |||
321 | // Use toFloat instead of float because it is a reserved keyword. |
||
322 | public static object toFloat(object x) |
||
323 | { |
||
324 | return convertDouble(x); |
||
325 | } |
||
326 | |||
327 | /// <summary> |
||
328 | /// The ISO standard returns an int. |
||
329 | /// </summary> |
||
330 | /// <param name="x"></param> |
||
331 | /// <returns></returns> |
||
332 | public static object floor(object x) |
||
333 | { |
||
334 | return (int)Math.Floor(convertDouble(x)); |
||
335 | } |
||
336 | |||
337 | /// <summary> |
||
338 | /// The ISO standard returns an int. |
||
339 | /// </summary> |
||
340 | /// <param name="x"></param> |
||
341 | /// <returns></returns> |
||
342 | public static object truncate(object x) |
||
343 | { |
||
344 | return (int)Math.Truncate(convertDouble(x)); |
||
345 | } |
||
346 | |||
347 | /// <summary> |
||
348 | /// The ISO standard returns an int. |
||
349 | /// </summary> |
||
350 | /// <param name="x"></param> |
||
351 | /// <returns></returns> |
||
352 | public static object round(object x) |
||
353 | { |
||
354 | return (int)Math.Round(convertDouble(x)); |
||
355 | } |
||
356 | |||
357 | /// <summary> |
||
358 | /// The ISO standard returns an int. |
||
359 | /// </summary> |
||
360 | /// <param name="x"></param> |
||
361 | /// <returns></returns> |
||
362 | public static object ceiling(object x) |
||
363 | { |
||
364 | return (int)Math.Ceiling(convertDouble(x)); |
||
365 | } |
||
366 | |||
367 | public static object sin(object x) |
||
368 | { |
||
369 | return Math.Sin(YP.convertDouble(x)); |
||
370 | } |
||
371 | |||
372 | public static object cos(object x) |
||
373 | { |
||
374 | return Math.Cos(YP.convertDouble(x)); |
||
375 | } |
||
376 | |||
377 | public static object atan(object x) |
||
378 | { |
||
379 | return Math.Atan(YP.convertDouble(x)); |
||
380 | } |
||
381 | |||
382 | public static object exp(object x) |
||
383 | { |
||
384 | return Math.Exp(YP.convertDouble(x)); |
||
385 | } |
||
386 | |||
387 | public static object log(object x) |
||
388 | { |
||
389 | return Math.Log(YP.convertDouble(x)); |
||
390 | } |
||
391 | |||
392 | public static object sqrt(object x) |
||
393 | { |
||
394 | return Math.Sqrt(convertDouble(x)); |
||
395 | } |
||
396 | |||
397 | public static object bitwiseComplement(object x) |
||
398 | { |
||
399 | return ~YP.convertInt(x); |
||
400 | } |
||
401 | |||
402 | public static object add(object x, object y) |
||
403 | { |
||
404 | int intX, intY; |
||
405 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
406 | return intX + intY; |
||
407 | return convertDouble(x) + convertDouble(y); |
||
408 | } |
||
409 | |||
410 | public static object subtract(object x, object y) |
||
411 | { |
||
412 | int intX, intY; |
||
413 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
414 | return intX - intY; |
||
415 | return convertDouble(x) - convertDouble(y); |
||
416 | } |
||
417 | |||
418 | public static object multiply(object x, object y) |
||
419 | { |
||
420 | int intX, intY; |
||
421 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
422 | return intX * intY; |
||
423 | return convertDouble(x) * convertDouble(y); |
||
424 | } |
||
425 | |||
426 | /// <summary> |
||
427 | /// Return floating point, even if both arguments are integer. |
||
428 | /// </summary> |
||
429 | /// <param name="x"></param> |
||
430 | /// <param name="y"></param> |
||
431 | /// <returns></returns> |
||
432 | public static object divide(object x, object y) |
||
433 | { |
||
434 | return convertDouble(x) / convertDouble(y); |
||
435 | } |
||
436 | |||
437 | public static object intDivide(object x, object y) |
||
438 | { |
||
439 | int intX, intY; |
||
440 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
441 | return intX / intY; |
||
442 | // Still allow passing a double, but treat as an int. |
||
443 | return (int)convertDouble(x) / (int)convertDouble(y); |
||
444 | } |
||
445 | |||
446 | public static object mod(object x, object y) |
||
447 | { |
||
448 | int intX, intY; |
||
449 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
450 | return intX % intY; |
||
451 | // Still allow passing a double, but treat as an int. |
||
452 | return (int)convertDouble(x) % (int)convertDouble(y); |
||
453 | } |
||
454 | |||
455 | public static object pow(object x, object y) |
||
456 | { |
||
457 | return Math.Pow(YP.convertDouble(x), YP.convertDouble(y)); |
||
458 | } |
||
459 | |||
460 | public static object bitwiseShiftRight(object x, object y) |
||
461 | { |
||
462 | return YP.convertInt(x) >> YP.convertInt(y); |
||
463 | } |
||
464 | |||
465 | public static object bitwiseShiftLeft(object x, object y) |
||
466 | { |
||
467 | return YP.convertInt(x) << YP.convertInt(y); |
||
468 | } |
||
469 | |||
470 | public static object bitwiseAnd(object x, object y) |
||
471 | { |
||
472 | return YP.convertInt(x) & YP.convertInt(y); |
||
473 | } |
||
474 | |||
475 | public static object bitwiseOr(object x, object y) |
||
476 | { |
||
477 | return YP.convertInt(x) | YP.convertInt(y); |
||
478 | } |
||
479 | |||
480 | public static object min(object x, object y) |
||
481 | { |
||
482 | int intX, intY; |
||
483 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
484 | return Math.Min(intX, intY); |
||
485 | return Math.Min(convertDouble(x), convertDouble(y)); |
||
486 | } |
||
487 | |||
488 | public static object max(object x, object y) |
||
489 | { |
||
490 | int intX, intY; |
||
491 | if (getInt(x, out intX) && getInt(y, out intY)) |
||
492 | return Math.Max(intX, intY); |
||
493 | return Math.Max(convertDouble(x), convertDouble(y)); |
||
494 | } |
||
495 | |||
496 | public static IEnumerable<bool> copy_term(object inTerm, object outTerm) |
||
497 | { |
||
498 | return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore())); |
||
499 | } |
||
500 | |||
501 | public static void addUniqueVariables(object term, List<Variable> variableSet) |
||
502 | { |
||
503 | term = YP.getValue(term); |
||
504 | if (term is IUnifiable) |
||
505 | ((IUnifiable)term).addUniqueVariables(variableSet); |
||
506 | } |
||
507 | |||
508 | public static object makeCopy(object term, Variable.CopyStore copyStore) |
||
509 | { |
||
510 | term = YP.getValue(term); |
||
511 | if (term is IUnifiable) |
||
512 | return ((IUnifiable)term).makeCopy(copyStore); |
||
513 | else |
||
514 | // term is a "normal" type. Assume it is ground. |
||
515 | return term; |
||
516 | } |
||
517 | |||
518 | /// <summary> |
||
519 | /// Sort the array in place according to termLessThan. This does not remove duplicates |
||
520 | /// </summary> |
||
521 | /// <param name="array"></param> |
||
522 | public static void sortArray(object[] array) |
||
523 | { |
||
524 | Array.Sort(array, YP.compareTerms); |
||
525 | } |
||
526 | |||
527 | /// <summary> |
||
528 | /// Sort the array in place according to termLessThan. This does not remove duplicates |
||
529 | /// </summary> |
||
530 | /// <param name="array"></param> |
||
531 | public static void sortArray(List<object> array) |
||
532 | { |
||
533 | array.Sort(YP.compareTerms); |
||
534 | } |
||
535 | |||
536 | /// <summary> |
||
537 | /// Sort List according to termLessThan, remove duplicates and unify with Sorted. |
||
538 | /// </summary> |
||
539 | /// <param name="List"></param> |
||
540 | /// <param name="Sorted"></param> |
||
541 | /// <returns></returns> |
||
542 | public static IEnumerable<bool> sort(object List, object Sorted) |
||
543 | { |
||
544 | object[] array = ListPair.toArray(List); |
||
545 | if (array == null) |
||
546 | return YP.fail(); |
||
547 | if (array.Length > 1) |
||
548 | sortArray(array); |
||
549 | return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array)); |
||
550 | } |
||
551 | |||
552 | /// <summary> |
||
553 | /// Use YP.unify to unify each of the elements of the two arrays, and yield |
||
554 | /// once if they all unify. |
||
555 | /// </summary> |
||
556 | /// <param name="array1"></param> |
||
557 | /// <param name="array2"></param> |
||
558 | /// <returns></returns> |
||
559 | public static IEnumerable<bool> unifyArrays(object[] array1, object[] array2) |
||
560 | { |
||
561 | if (array1.Length != array2.Length) |
||
562 | yield break; |
||
563 | |||
564 | IEnumerator<bool>[] iterators = new IEnumerator<bool>[array1.Length]; |
||
565 | bool gotMatch = true; |
||
566 | int nIterators = 0; |
||
567 | // Try to bind all the arguments. |
||
568 | for (int i = 0; i < array1.Length; ++i) |
||
569 | { |
||
570 | IEnumerator<bool> iterator = YP.unify(array1[i], array2[i]).GetEnumerator(); |
||
571 | iterators[nIterators++] = iterator; |
||
572 | // MoveNext() is true if YP.unify succeeds. |
||
573 | if (!iterator.MoveNext()) |
||
574 | { |
||
575 | gotMatch = false; |
||
576 | break; |
||
577 | } |
||
578 | } |
||
579 | int z = 0; |
||
580 | try |
||
581 | { |
||
582 | if (gotMatch) |
||
583 | yield return false; |
||
584 | } |
||
585 | finally |
||
586 | { |
||
587 | // Manually finalize all the iterators. |
||
588 | for (z = 0; z < nIterators; ++z) |
||
589 | iterators[z].Dispose(); |
||
590 | } |
||
591 | } |
||
592 | |||
593 | /// <summary> |
||
594 | /// Return an iterator (which you can use in a for-in loop) which does |
||
595 | /// zero iterations. This returns a pre-existing iterator which is |
||
596 | /// more efficient than letting the compiler generate a new one. |
||
597 | /// </summary> |
||
598 | /// <returns></returns> |
||
599 | public static IEnumerable<bool> fail() |
||
600 | { |
||
601 | return _fail; |
||
602 | } |
||
603 | |||
604 | /// <summary> |
||
605 | /// Return an iterator (which you can use in a for-in loop) which does |
||
606 | /// one iteration. This returns a pre-existing iterator which is |
||
607 | /// more efficient than letting the compiler generate a new one. |
||
608 | /// </summary> |
||
609 | /// <returns></returns> |
||
610 | public static IEnumerable<bool> succeed() |
||
611 | { |
||
612 | return new Succeed(); |
||
613 | } |
||
614 | |||
615 | /// <summary> |
||
616 | /// Return an iterator (which you can use in a for-in loop) which repeats |
||
617 | /// indefinitely. This returns a pre-existing iterator which is |
||
618 | /// more efficient than letting the compiler generate a new one. |
||
619 | /// </summary> |
||
620 | /// <returns></returns> |
||
621 | public static IEnumerable<bool> repeat() |
||
622 | { |
||
623 | return _repeat; |
||
624 | } |
||
625 | |||
626 | // disable warning on l1, don't see how we can |
||
627 | // code this differently |
||
628 | #pragma warning disable 0168, 0219 |
||
629 | public static IEnumerable<bool> univ(object Term, object List) |
||
630 | { |
||
631 | Term = YP.getValue(Term); |
||
632 | List = YP.getValue(List); |
||
633 | |||
634 | if (nonvar(Term)) |
||
635 | return YP.unify(new ListPair |
||
636 | (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List); |
||
637 | |||
638 | Variable Name = new Variable(); |
||
639 | Variable ArgList = new Variable(); |
||
640 | foreach (bool l1 in new ListPair(Name, ArgList).unify(List)) |
||
641 | { |
||
642 | object[] args = ListPair.toArray(ArgList); |
||
643 | if (args == null) |
||
644 | throw new PrologException |
||
645 | (new Functor2("type_error", Atom.a("list"), ArgList), |
||
646 | "Expected a list. Got: " + ArgList.getValue()); |
||
647 | if (args.Length == 0) |
||
648 | // Return the Name, even if it is not an Atom. |
||
649 | return YP.unify(Term, Name); |
||
650 | if (args.Length > MAX_ARITY) |
||
651 | throw new PrologException |
||
652 | (new Functor1("representation_error", Atom.a("max_arity")), |
||
653 | "Functor arity " + args.Length + " may not be greater than " + MAX_ARITY); |
||
654 | if (!atom(Name)) |
||
655 | throw new PrologException |
||
656 | (new Functor2("type_error", Atom.a("atom"), Name), |
||
657 | "Expected an atom. Got: " + Name.getValue()); |
||
658 | |||
659 | return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args)); |
||
660 | } |
||
661 | |||
662 | return YP.fail(); |
||
663 | } |
||
664 | |||
665 | public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity) |
||
666 | { |
||
667 | Term = YP.getValue(Term); |
||
668 | FunctorName = YP.getValue(FunctorName); |
||
669 | Arity = YP.getValue(Arity); |
||
670 | |||
671 | if (Term is Variable) |
||
672 | { |
||
673 | if (FunctorName is Variable) |
||
674 | throw new PrologException(Atom.a("instantiation_error"), |
||
675 | "Arg 2 FunctorName is an unbound variable"); |
||
676 | if (Arity is Variable) |
||
677 | throw new PrologException(Atom.a("instantiation_error"), |
||
678 | "Arg 3 Arity is an unbound variable"); |
||
679 | if (!(Arity is int)) |
||
680 | throw new PrologException |
||
681 | (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer"); |
||
682 | if (!YP.atomic(FunctorName)) |
||
683 | throw new PrologException |
||
684 | (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic"); |
||
685 | |||
686 | if ((int)Arity < 0) |
||
687 | throw new PrologException |
||
688 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity), |
||
689 | "Arity may not be less than zero"); |
||
690 | else if ((int)Arity == 0) |
||
691 | { |
||
692 | // Just unify Term with the atomic FunctorName. |
||
693 | foreach (bool l1 in YP.unify(Term, FunctorName)) |
||
694 | yield return false; |
||
695 | } |
||
696 | else |
||
697 | { |
||
698 | if ((int)Arity > MAX_ARITY) |
||
699 | throw new PrologException |
||
700 | (new Functor1("representation_error", Atom.a("max_arity")), |
||
701 | "Functor arity " + Arity + " may not be greater than " + MAX_ARITY); |
||
702 | if (!(FunctorName is Atom)) |
||
703 | throw new PrologException |
||
704 | (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom"); |
||
705 | // Construct a functor with unbound variables. |
||
706 | object[] args = new object[(int)Arity]; |
||
707 | for (int i = 0; i < args.Length; ++i) |
||
708 | args[i] = new Variable(); |
||
709 | foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args))) |
||
710 | yield return false; |
||
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 | //m_log.Debug(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 | m_log.Error("Failed to connect to server at localhost:999"); |
||
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 | m_log.Debug("Cycoutput:" + cycOutputString); |
||
2385 | //streamWriter.WriteLine("Client Message"); |
||
2386 | //m_log.Debug("Client Message"); |
||
2387 | streamWriter.Flush(); |
||
2388 | } |
||
2389 | |||
2390 | } |
||
2391 | catch |
||
2392 | { |
||
2393 | m_log.Error("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, 0219 |
||
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, 0219 |
||
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, 0219 |
||
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 | } |