PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs

https://bitbucket.org/VirtualReality/taiga
C# | 2701 lines | 2416 code | 87 blank | 198 comment | 134 complexity | da07ed504aba40384e157eacf6c0cd67 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  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. using System;
  31. using System.Collections;
  32. using System.Collections.Generic;
  33. using System.IO;
  34. using System.Reflection;
  35. using System.Net.Sockets;
  36. using System.Text;
  37. using System.Text.RegularExpressions;
  38. using log4net;
  39. namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog
  40. {
  41. /// <summary>
  42. /// YP has static methods for general functions in Yield Prolog such as <see cref="getValue"/>
  43. /// and <see cref="unify"/>.
  44. /// </summary>
  45. public class YP
  46. {
  47. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  48. private static Fail _fail = new Fail();
  49. private static Repeat _repeat = new Repeat();
  50. private static Dictionary<NameArity, List<IClause>> _predicatesStore =
  51. new Dictionary<NameArity, List<IClause>>();
  52. private static TextWriter _outputStream = System.Console.Out;
  53. private static TextReader _inputStream = System.Console.In;
  54. private static IndexedAnswers _operatorTable = null;
  55. private static Dictionary<string, object> _prologFlags = new Dictionary<string, object>();
  56. public const int MAX_ARITY = 255;
  57. /// <summary>
  58. /// An IClause is used so that dynamic predicates can call match.
  59. /// </summary>
  60. public interface IClause
  61. {
  62. IEnumerable<bool> match(object[] args);
  63. IEnumerable<bool> clause(object Head, object Body);
  64. }
  65. /// <summary>
  66. /// If value is a Variable, then return its getValue. Otherwise, just
  67. /// return value. You should call YP.getValue on any object that
  68. /// may be a Variable to get the value to pass to other functions in
  69. /// your system that are not part of Yield Prolog, such as math functions
  70. /// or file I/O.
  71. /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
  72. /// </summary>
  73. /// <param name="value"></param>
  74. /// <returns></returns>
  75. public static object getValue(object value)
  76. {
  77. if (value is Variable)
  78. return ((Variable)value).getValue();
  79. else
  80. return value;
  81. }
  82. /// <summary>
  83. /// If arg1 or arg2 is an object with a unify method (such as Variable or
  84. /// Functor) then just call its unify with the other argument. The object's
  85. /// unify method will bind the values or check for equals as needed.
  86. /// Otherwise, both arguments are "normal" (atomic) values so if they
  87. /// are equal then succeed (yield once), else fail (don't yield).
  88. /// For more details, see http://yieldprolog.sourceforge.net/tutorial1.html
  89. /// </summary>
  90. /// <param name="arg1"></param>
  91. /// <param name="arg2"></param>
  92. /// <returns></returns>
  93. public static IEnumerable<bool> unify(object arg1, object arg2)
  94. {
  95. arg1 = getValue(arg1);
  96. arg2 = getValue(arg2);
  97. if (arg1 is IUnifiable)
  98. return ((IUnifiable)arg1).unify(arg2);
  99. else if (arg2 is IUnifiable)
  100. return ((IUnifiable)arg2).unify(arg1);
  101. else
  102. {
  103. // Arguments are "normal" types.
  104. if (arg1.Equals(arg2))
  105. return new Succeed();
  106. else
  107. return _fail;
  108. }
  109. }
  110. /// <summary>
  111. /// This is used for the lookup key in _factStore.
  112. /// </summary>
  113. public struct NameArity
  114. {
  115. public readonly Atom _name;
  116. public readonly int _arity;
  117. public NameArity(Atom name, int arity)
  118. {
  119. _name = name;
  120. _arity = arity;
  121. }
  122. public override bool Equals(object obj)
  123. {
  124. if (obj is NameArity)
  125. {
  126. NameArity nameArity = (NameArity)obj;
  127. return nameArity._name.Equals(_name) && nameArity._arity.Equals(_arity);
  128. }
  129. else
  130. {
  131. return false;
  132. }
  133. }
  134. public override int GetHashCode()
  135. {
  136. return _name.GetHashCode() ^ _arity.GetHashCode();
  137. }
  138. }
  139. /// <summary>
  140. /// Convert term to an int.
  141. /// If term is a single-element List, use its first element
  142. /// (to handle the char types like "a").
  143. /// If can't convert, throw a PrologException for type_error evaluable (because this is only
  144. /// called from arithmetic functions).
  145. /// </summary>
  146. /// <param name="term"></param>
  147. /// <returns></returns>
  148. public static int convertInt(object term)
  149. {
  150. term = YP.getValue(term);
  151. if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
  152. YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
  153. // Assume it is a char type like "a".
  154. term = YP.getValue(((Functor2)term)._arg1);
  155. if (term is Variable)
  156. throw new PrologException(Atom.a("instantiation_error"),
  157. "Expected a number but the argument is an unbound variable");
  158. try
  159. {
  160. return (int)term;
  161. }
  162. catch (InvalidCastException)
  163. {
  164. throw new PrologException
  165. (new Functor2
  166. ("type_error", Atom.a("evaluable"),
  167. new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
  168. "Term must be an integer");
  169. }
  170. }
  171. /// <summary>
  172. /// Convert term to a double. This may convert an int to a double, etc.
  173. /// If term is a single-element List, use its first element
  174. /// (to handle the char types like "a").
  175. /// If can't convert, throw a PrologException for type_error evaluable (because this is only
  176. /// called from arithmetic functions).
  177. /// </summary>
  178. /// <param name="term"></param>
  179. /// <returns></returns>
  180. public static double convertDouble(object term)
  181. {
  182. term = YP.getValue(term);
  183. if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
  184. YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
  185. // Assume it is a char type like "a".
  186. term = YP.getValue(((Functor2)term)._arg1);
  187. if (term is Variable)
  188. throw new PrologException(Atom.a("instantiation_error"),
  189. "Expected a number but the argument is an unbound variable");
  190. try
  191. {
  192. return Convert.ToDouble(term);
  193. }
  194. catch (InvalidCastException)
  195. {
  196. throw new PrologException
  197. (new Functor2
  198. ("type_error", Atom.a("evaluable"),
  199. new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)),
  200. "Term must be an integer");
  201. }
  202. }
  203. /// <summary>
  204. /// If term is an integer, set intTerm.
  205. /// If term is a single-element List, use its first element
  206. /// (to handle the char types like "a"). Return true for success, false if can't convert.
  207. /// We use a success return value because throwing an exception is inefficient.
  208. /// </summary>
  209. /// <param name="term"></param>
  210. /// <returns></returns>
  211. public static bool getInt(object term, out int intTerm)
  212. {
  213. term = YP.getValue(term);
  214. if (term is Functor2 && ((Functor2)term)._name == Atom.DOT &&
  215. YP.getValue(((Functor2)term)._arg2) == Atom.NIL)
  216. // Assume it is a char type like "a".
  217. term = YP.getValue(((Functor2)term)._arg1);
  218. if (term is int)
  219. {
  220. intTerm = (int)term;
  221. return true;
  222. }
  223. intTerm = 0;
  224. return false;
  225. }
  226. public static bool equal(object x, object y)
  227. {
  228. x = YP.getValue(x);
  229. if (x is DateTime)
  230. return (DateTime)x == (DateTime)YP.getValue(y);
  231. // Assume convertDouble converts an int to a double perfectly.
  232. return YP.convertDouble(x) == YP.convertDouble(y);
  233. }
  234. public static bool notEqual(object x, object y)
  235. {
  236. x = YP.getValue(x);
  237. if (x is DateTime)
  238. return (DateTime)x != (DateTime)YP.getValue(y);
  239. // Assume convertDouble converts an int to a double perfectly.
  240. return YP.convertDouble(x) != YP.convertDouble(y);
  241. }
  242. public static bool greaterThan(object x, object y)
  243. {
  244. x = YP.getValue(x);
  245. if (x is DateTime)
  246. return (DateTime)x > (DateTime)YP.getValue(y);
  247. // Assume convertDouble converts an int to a double perfectly.
  248. return YP.convertDouble(x) > YP.convertDouble(y);
  249. }
  250. public static bool lessThan(object x, object y)
  251. {
  252. x = YP.getValue(x);
  253. if (x is DateTime)
  254. return (DateTime)x < (DateTime)YP.getValue(y);
  255. // Assume convertDouble converts an int to a double perfectly.
  256. return YP.convertDouble(x) < YP.convertDouble(y);
  257. }
  258. public static bool greaterThanOrEqual(object x, object y)
  259. {
  260. x = YP.getValue(x);
  261. if (x is DateTime)
  262. return (DateTime)x >= (DateTime)YP.getValue(y);
  263. // Assume convertDouble converts an int to a double perfectly.
  264. return YP.convertDouble(x) >= YP.convertDouble(y);
  265. }
  266. public static bool lessThanOrEqual(object x, object y)
  267. {
  268. x = YP.getValue(x);
  269. if (x is DateTime)
  270. return (DateTime)x <= (DateTime)YP.getValue(y);
  271. // Assume convertDouble converts an int to a double perfectly.
  272. return YP.convertDouble(x) <= YP.convertDouble(y);
  273. }
  274. public static object negate(object x)
  275. {
  276. int intX;
  277. if (getInt(x, out intX))
  278. return -intX;
  279. return -convertDouble(x);
  280. }
  281. public static object abs(object x)
  282. {
  283. int intX;
  284. if (getInt(x, out intX))
  285. return Math.Abs(intX);
  286. return Math.Abs(convertDouble(x));
  287. }
  288. public static object sign(object x)
  289. {
  290. int intX;
  291. if (getInt(x, out intX))
  292. return Math.Sign(intX);
  293. return Math.Sign(convertDouble(x));
  294. }
  295. // Use toFloat instead of float because it is a reserved keyword.
  296. public static object toFloat(object x)
  297. {
  298. return convertDouble(x);
  299. }
  300. /// <summary>
  301. /// The ISO standard returns an int.
  302. /// </summary>
  303. /// <param name="x"></param>
  304. /// <returns></returns>
  305. public static object floor(object x)
  306. {
  307. return (int)Math.Floor(convertDouble(x));
  308. }
  309. /// <summary>
  310. /// The ISO standard returns an int.
  311. /// </summary>
  312. /// <param name="x"></param>
  313. /// <returns></returns>
  314. public static object truncate(object x)
  315. {
  316. return (int)Math.Truncate(convertDouble(x));
  317. }
  318. /// <summary>
  319. /// The ISO standard returns an int.
  320. /// </summary>
  321. /// <param name="x"></param>
  322. /// <returns></returns>
  323. public static object round(object x)
  324. {
  325. return (int)Math.Round(convertDouble(x));
  326. }
  327. /// <summary>
  328. /// The ISO standard returns an int.
  329. /// </summary>
  330. /// <param name="x"></param>
  331. /// <returns></returns>
  332. public static object ceiling(object x)
  333. {
  334. return (int)Math.Ceiling(convertDouble(x));
  335. }
  336. public static object sin(object x)
  337. {
  338. return Math.Sin(YP.convertDouble(x));
  339. }
  340. public static object cos(object x)
  341. {
  342. return Math.Cos(YP.convertDouble(x));
  343. }
  344. public static object atan(object x)
  345. {
  346. return Math.Atan(YP.convertDouble(x));
  347. }
  348. public static object exp(object x)
  349. {
  350. return Math.Exp(YP.convertDouble(x));
  351. }
  352. public static object log(object x)
  353. {
  354. return Math.Log(YP.convertDouble(x));
  355. }
  356. public static object sqrt(object x)
  357. {
  358. return Math.Sqrt(convertDouble(x));
  359. }
  360. public static object bitwiseComplement(object x)
  361. {
  362. return ~YP.convertInt(x);
  363. }
  364. public static object add(object x, object y)
  365. {
  366. int intX, intY;
  367. if (getInt(x, out intX) && getInt(y, out intY))
  368. return intX + intY;
  369. return convertDouble(x) + convertDouble(y);
  370. }
  371. public static object subtract(object x, object y)
  372. {
  373. int intX, intY;
  374. if (getInt(x, out intX) && getInt(y, out intY))
  375. return intX - intY;
  376. return convertDouble(x) - convertDouble(y);
  377. }
  378. public static object multiply(object x, object y)
  379. {
  380. int intX, intY;
  381. if (getInt(x, out intX) && getInt(y, out intY))
  382. return intX * intY;
  383. return convertDouble(x) * convertDouble(y);
  384. }
  385. /// <summary>
  386. /// Return floating point, even if both arguments are integer.
  387. /// </summary>
  388. /// <param name="x"></param>
  389. /// <param name="y"></param>
  390. /// <returns></returns>
  391. public static object divide(object x, object y)
  392. {
  393. return convertDouble(x) / convertDouble(y);
  394. }
  395. public static object intDivide(object x, object y)
  396. {
  397. int intX, intY;
  398. if (getInt(x, out intX) && getInt(y, out intY))
  399. return intX / intY;
  400. // Still allow passing a double, but treat as an int.
  401. return (int)convertDouble(x) / (int)convertDouble(y);
  402. }
  403. public static object mod(object x, object y)
  404. {
  405. int intX, intY;
  406. if (getInt(x, out intX) && getInt(y, out intY))
  407. return intX % intY;
  408. // Still allow passing a double, but treat as an int.
  409. return (int)convertDouble(x) % (int)convertDouble(y);
  410. }
  411. public static object pow(object x, object y)
  412. {
  413. return Math.Pow(YP.convertDouble(x), YP.convertDouble(y));
  414. }
  415. public static object bitwiseShiftRight(object x, object y)
  416. {
  417. return YP.convertInt(x) >> YP.convertInt(y);
  418. }
  419. public static object bitwiseShiftLeft(object x, object y)
  420. {
  421. return YP.convertInt(x) << YP.convertInt(y);
  422. }
  423. public static object bitwiseAnd(object x, object y)
  424. {
  425. return YP.convertInt(x) & YP.convertInt(y);
  426. }
  427. public static object bitwiseOr(object x, object y)
  428. {
  429. return YP.convertInt(x) | YP.convertInt(y);
  430. }
  431. public static object min(object x, object y)
  432. {
  433. int intX, intY;
  434. if (getInt(x, out intX) && getInt(y, out intY))
  435. return Math.Min(intX, intY);
  436. return Math.Min(convertDouble(x), convertDouble(y));
  437. }
  438. public static object max(object x, object y)
  439. {
  440. int intX, intY;
  441. if (getInt(x, out intX) && getInt(y, out intY))
  442. return Math.Max(intX, intY);
  443. return Math.Max(convertDouble(x), convertDouble(y));
  444. }
  445. public static IEnumerable<bool> copy_term(object inTerm, object outTerm)
  446. {
  447. return YP.unify(outTerm, YP.makeCopy(inTerm, new Variable.CopyStore()));
  448. }
  449. public static void addUniqueVariables(object term, List<Variable> variableSet)
  450. {
  451. term = YP.getValue(term);
  452. if (term is IUnifiable)
  453. ((IUnifiable)term).addUniqueVariables(variableSet);
  454. }
  455. public static object makeCopy(object term, Variable.CopyStore copyStore)
  456. {
  457. term = YP.getValue(term);
  458. if (term is IUnifiable)
  459. return ((IUnifiable)term).makeCopy(copyStore);
  460. else
  461. // term is a "normal" type. Assume it is ground.
  462. return term;
  463. }
  464. /// <summary>
  465. /// Sort the array in place according to termLessThan. This does not remove duplicates
  466. /// </summary>
  467. /// <param name="array"></param>
  468. public static void sortArray(object[] array)
  469. {
  470. Array.Sort(array, YP.compareTerms);
  471. }
  472. /// <summary>
  473. /// Sort the array in place according to termLessThan. This does not remove duplicates
  474. /// </summary>
  475. /// <param name="array"></param>
  476. public static void sortArray(List<object> array)
  477. {
  478. array.Sort(YP.compareTerms);
  479. }
  480. /// <summary>
  481. /// Sort List according to termLessThan, remove duplicates and unify with Sorted.
  482. /// </summary>
  483. /// <param name="List"></param>
  484. /// <param name="Sorted"></param>
  485. /// <returns></returns>
  486. public static IEnumerable<bool> sort(object List, object Sorted)
  487. {
  488. object[] array = ListPair.toArray(List);
  489. if (array == null)
  490. return YP.fail();
  491. if (array.Length > 1)
  492. sortArray(array);
  493. return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array));
  494. }
  495. /// <summary>
  496. /// Use YP.unify to unify each of the elements of the two arrays, and yield
  497. /// once if they all unify.
  498. /// </summary>
  499. /// <param name="array1"></param>
  500. /// <param name="array2"></param>
  501. /// <returns></returns>
  502. public static IEnumerable<bool> unifyArrays(object[] array1, object[] array2)
  503. {
  504. if (array1.Length != array2.Length)
  505. yield break;
  506. IEnumerator<bool>[] iterators = new IEnumerator<bool>[array1.Length];
  507. bool gotMatch = true;
  508. int nIterators = 0;
  509. // Try to bind all the arguments.
  510. for (int i = 0; i < array1.Length; ++i)
  511. {
  512. IEnumerator<bool> iterator = YP.unify(array1[i], array2[i]).GetEnumerator();
  513. iterators[nIterators++] = iterator;
  514. // MoveNext() is true if YP.unify succeeds.
  515. if (!iterator.MoveNext())
  516. {
  517. gotMatch = false;
  518. break;
  519. }
  520. }
  521. try
  522. {
  523. if (gotMatch)
  524. yield return false;
  525. }
  526. finally
  527. {
  528. // Manually finalize all the iterators.
  529. for (int i = 0; i < nIterators; ++i)
  530. iterators[i].Dispose();
  531. }
  532. }
  533. /// <summary>
  534. /// Return an iterator (which you can use in a for-in loop) which does
  535. /// zero iterations. This returns a pre-existing iterator which is
  536. /// more efficient than letting the compiler generate a new one.
  537. /// </summary>
  538. /// <returns></returns>
  539. public static IEnumerable<bool> fail()
  540. {
  541. return _fail;
  542. }
  543. /// <summary>
  544. /// Return an iterator (which you can use in a for-in loop) which does
  545. /// one iteration. This returns a pre-existing iterator which is
  546. /// more efficient than letting the compiler generate a new one.
  547. /// </summary>
  548. /// <returns></returns>
  549. public static IEnumerable<bool> succeed()
  550. {
  551. return new Succeed();
  552. }
  553. /// <summary>
  554. /// Return an iterator (which you can use in a for-in loop) which repeats
  555. /// indefinitely. This returns a pre-existing iterator which is
  556. /// more efficient than letting the compiler generate a new one.
  557. /// </summary>
  558. /// <returns></returns>
  559. public static IEnumerable<bool> repeat()
  560. {
  561. return _repeat;
  562. }
  563. // disable warning on l1, don't see how we can
  564. // code this differently
  565. #pragma warning disable 0168, 0219
  566. public static IEnumerable<bool> univ(object Term, object List)
  567. {
  568. Term = YP.getValue(Term);
  569. List = YP.getValue(List);
  570. if (nonvar(Term))
  571. return YP.unify(new ListPair
  572. (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List);
  573. Variable Name = new Variable();
  574. Variable ArgList = new Variable();
  575. foreach (bool l1 in new ListPair(Name, ArgList).unify(List))
  576. {
  577. object[] args = ListPair.toArray(ArgList);
  578. if (args == null)
  579. throw new PrologException
  580. (new Functor2("type_error", Atom.a("list"), ArgList),
  581. "Expected a list. Got: " + ArgList.getValue());
  582. if (args.Length == 0)
  583. // Return the Name, even if it is not an Atom.
  584. return YP.unify(Term, Name);
  585. if (args.Length > MAX_ARITY)
  586. throw new PrologException
  587. (new Functor1("representation_error", Atom.a("max_arity")),
  588. "Functor arity " + args.Length + " may not be greater than " + MAX_ARITY);
  589. if (!atom(Name))
  590. throw new PrologException
  591. (new Functor2("type_error", Atom.a("atom"), Name),
  592. "Expected an atom. Got: " + Name.getValue());
  593. return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
  594. }
  595. return YP.fail();
  596. }
  597. public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity)
  598. {
  599. Term = YP.getValue(Term);
  600. FunctorName = YP.getValue(FunctorName);
  601. Arity = YP.getValue(Arity);
  602. if (Term is Variable)
  603. {
  604. if (FunctorName is Variable)
  605. throw new PrologException(Atom.a("instantiation_error"),
  606. "Arg 2 FunctorName is an unbound variable");
  607. if (Arity is Variable)
  608. throw new PrologException(Atom.a("instantiation_error"),
  609. "Arg 3 Arity is an unbound variable");
  610. if (!(Arity is int))
  611. throw new PrologException
  612. (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer");
  613. if (!YP.atomic(FunctorName))
  614. throw new PrologException
  615. (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic");
  616. if ((int)Arity < 0)
  617. throw new PrologException
  618. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity),
  619. "Arity may not be less than zero");
  620. else if ((int)Arity == 0)
  621. {
  622. // Just unify Term with the atomic FunctorName.
  623. foreach (bool l1 in YP.unify(Term, FunctorName))
  624. yield return false;
  625. }
  626. else
  627. {
  628. if ((int)Arity > MAX_ARITY)
  629. throw new PrologException
  630. (new Functor1("representation_error", Atom.a("max_arity")),
  631. "Functor arity " + Arity + " may not be greater than " + MAX_ARITY);
  632. if (!(FunctorName is Atom))
  633. throw new PrologException
  634. (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom");
  635. // Construct a functor with unbound variables.
  636. object[] args = new object[(int)Arity];
  637. for (int i = 0; i < args.Length; ++i)
  638. args[i] = new Variable();
  639. foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args)))
  640. yield return false;
  641. }
  642. }
  643. else
  644. {
  645. foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
  646. {
  647. foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
  648. yield return false;
  649. }
  650. }
  651. }
  652. public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value)
  653. {
  654. if (var(ArgNumber))
  655. throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable");
  656. int argNumberInt;
  657. if (!getInt(ArgNumber, out argNumberInt))
  658. throw new PrologException
  659. (new Functor2("type_error", Atom.a("integer"), ArgNumber), "Arg 1 ArgNumber must be integer");
  660. if (argNumberInt < 0)
  661. throw new PrologException
  662. (new Functor2("domain_error", Atom.a("not_less_than_zero"), argNumberInt),
  663. "ArgNumber may not be less than zero");
  664. if (YP.var(Term))
  665. throw new PrologException(Atom.a("instantiation_error"),
  666. "Arg 2 Term is an unbound variable");
  667. if (!YP.compound(Term))
  668. throw new PrologException
  669. (new Functor2("type_error", Atom.a("compound"), Term), "Arg 2 Term must be compound");
  670. object[] termArgs = YP.getFunctorArgs(Term);
  671. // Silently fail if argNumberInt is out of range.
  672. if (argNumberInt >= 1 && argNumberInt <= termArgs.Length)
  673. {
  674. // The first ArgNumber is at 1, not 0.
  675. foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1]))
  676. yield return false;
  677. }
  678. }
  679. public static bool termEqual(object Term1, object Term2)
  680. {
  681. Term1 = YP.getValue(Term1);
  682. if (Term1 is IUnifiable)
  683. return ((IUnifiable)Term1).termEqual(Term2);
  684. return Term1.Equals(YP.getValue(Term2));
  685. }
  686. public static bool termNotEqual(object Term1, object Term2)
  687. {
  688. return !termEqual(Term1, Term2);
  689. }
  690. public static bool termLessThan(object Term1, object Term2)
  691. {
  692. Term1 = YP.getValue(Term1);
  693. Term2 = YP.getValue(Term2);
  694. int term1TypeCode = getTypeCode(Term1);
  695. int term2TypeCode = getTypeCode(Term2);
  696. if (term1TypeCode != term2TypeCode)
  697. return term1TypeCode < term2TypeCode;
  698. // The terms are the same type code.
  699. if (term1TypeCode == -2)
  700. {
  701. // Variable.
  702. // We always check for equality first because we want to be sure
  703. // that less than returns false if the terms are equal, in
  704. // case that the less than check really behaves like less than or equal.
  705. if ((Variable)Term1 != (Variable)Term2)
  706. // The hash code should be unique to a Variable object.
  707. return Term1.GetHashCode() < Term2.GetHashCode();
  708. return false;
  709. }
  710. if (term1TypeCode == 0)
  711. return ((Atom)Term1)._name.CompareTo(((Atom)Term2)._name) < 0;
  712. if (term1TypeCode == 1)
  713. return ((Functor1)Term1).lessThan((Functor1)Term2);
  714. if (term1TypeCode == 2)
  715. return ((Functor2)Term1).lessThan((Functor2)Term2);
  716. if (term1TypeCode == 3)
  717. return ((Functor3)Term1).lessThan((Functor3)Term2);
  718. if (term1TypeCode == 4)
  719. return ((Functor)Term1).lessThan((Functor)Term2);
  720. // Type code is -1 for general objects. First compare their type names.
  721. // Note that this puts Double before Int32 as required by ISO Prolog.
  722. string term1TypeName = Term1.GetType().ToString();
  723. string term2TypeName = Term2.GetType().ToString();
  724. if (term1TypeName != term2TypeName)
  725. return term1TypeName.CompareTo(term2TypeName) < 0;
  726. // The terms are the same type name.
  727. if (Term1 is int)
  728. return (int)Term1 < (int)Term2;
  729. else if (Term1 is double)
  730. return (double)Term1 < (double)Term2;
  731. else if (Term1 is DateTime)
  732. return (DateTime)Term1 < (DateTime)Term2;
  733. else if (Term1 is String)
  734. return ((String)Term1).CompareTo((String)Term2) < 0;
  735. // Debug: Should we try arrays, etc.?
  736. if (!Term1.Equals(Term2))
  737. // Could be equal or greater than.
  738. return Term1.GetHashCode() < Term2.GetHashCode();
  739. return false;
  740. }
  741. /// <summary>
  742. /// Type code is -2 if term is a Variable, 0 if it is an Atom,
  743. /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3,
  744. /// 4 if it is Functor.
  745. /// Otherwise, type code is -1.
  746. /// This does not call YP.getValue(term).
  747. /// </summary>
  748. /// <param name="term"></param>
  749. /// <returns></returns>
  750. private static int getTypeCode(object term)
  751. {
  752. if (term is Variable)
  753. return -2;
  754. else if (term is Atom)
  755. return 0;
  756. else if (term is Functor1)
  757. return 1;
  758. else if (term is Functor2)
  759. return 2;
  760. else if (term is Functor3)
  761. return 3;
  762. else if (term is Functor)
  763. return 4;
  764. else
  765. return -1;
  766. }
  767. public static bool termLessThanOrEqual(object Term1, object Term2)
  768. {
  769. if (YP.termEqual(Term1, Term2))
  770. return true;
  771. return YP.termLessThan(Term1, Term2);
  772. }
  773. public static bool termGreaterThan(object Term1, object Term2)
  774. {
  775. return !YP.termLessThanOrEqual(Term1, Term2);
  776. }
  777. public static bool termGreaterThanOrEqual(object Term1, object Term2)
  778. {
  779. // termLessThan should ensure that it returns false if terms are equal,
  780. // so that this would return true.
  781. return !YP.termLessThan(Term1, Term2);
  782. }
  783. public static int compareTerms(object Term1, object Term2)
  784. {
  785. if (YP.termEqual(Term1, Term2))
  786. return 0;
  787. else if (YP.termLessThan(Term1, Term2))
  788. return -1;
  789. else
  790. return 1;
  791. }
  792. public static bool ground(object Term)
  793. {
  794. Term = YP.getValue(Term);
  795. if (Term is IUnifiable)
  796. return ((IUnifiable)Term).ground();
  797. return true;
  798. }
  799. public static IEnumerable<bool> current_op
  800. (object Priority, object Specifier, object Operator)
  801. {
  802. if (_operatorTable == null)
  803. {
  804. // Initialize.
  805. _operatorTable = new IndexedAnswers(3);
  806. _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
  807. _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
  808. _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
  809. _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
  810. _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
  811. _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
  812. _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
  813. _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), Atom.a("\\+") });
  814. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=") });
  815. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") });
  816. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("==") });
  817. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") });
  818. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@<") });
  819. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") });
  820. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>") });
  821. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") });
  822. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=..") });
  823. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
  824. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") });
  825. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") });
  826. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("<") });
  827. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=<") });
  828. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">") });
  829. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">=") });
  830. _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), Atom.a(":") });
  831. _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("+") });
  832. _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("-") });
  833. _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") });
  834. _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") });
  835. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("*") });
  836. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("/") });
  837. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("//") });
  838. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
  839. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
  840. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
  841. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
  842. _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
  843. _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
  844. _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") });
  845. _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") });
  846. // Debug: This is hacked in to run the Prolog test suite until we implement op/3.
  847. _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
  848. }
  849. return _operatorTable.match(new object[] { Priority, Specifier, Operator });
  850. }
  851. public static IEnumerable<bool> atom_length(object atom, object Length)
  852. {
  853. atom = YP.getValue(atom);
  854. Length = YP.getValue(Length);
  855. if (atom is Variable)
  856. throw new PrologException(Atom.a("instantiation_error"),
  857. "Expected atom(Arg1) but it is an unbound variable");
  858. if (!(atom is Atom))
  859. throw new PrologException
  860. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
  861. if (!(Length is Variable))
  862. {
  863. if (!(Length is int))
  864. throw new PrologException
  865. (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
  866. if ((int)Length < 0)
  867. throw new PrologException
  868. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
  869. "Length must not be less than zero");
  870. }
  871. return YP.unify(Length, ((Atom)atom)._name.Length);
  872. }
  873. public static IEnumerable<bool> atom_concat(object Start, object End, object Whole)
  874. {
  875. // Debug: Should we try to preserve the _declaringClass?
  876. Start = YP.getValue(Start);
  877. End = YP.getValue(End);
  878. Whole = YP.getValue(Whole);
  879. if (Whole is Variable)
  880. {
  881. if (Start is Variable)
  882. throw new PrologException(Atom.a("instantiation_error"),
  883. "Arg 1 Start and arg 3 Whole are both var");
  884. if (End is Variable)
  885. throw new PrologException(Atom.a("instantiation_error"),
  886. "Arg 2 End and arg 3 Whole are both var");
  887. if (!(Start is Atom))
  888. throw new PrologException
  889. (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not an atom");
  890. if (!(End is Atom))
  891. throw new PrologException
  892. (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not an atom");
  893. foreach (bool l1 in YP.unify(Whole, Atom.a(((Atom)Start)._name + ((Atom)End)._name)))
  894. yield return false;
  895. }
  896. else
  897. {
  898. if (!(Whole is Atom))
  899. throw new PrologException
  900. (new Functor2("type_error", Atom.a("atom"), Whole), "Arg 3 Whole is not an atom");
  901. bool gotStartLength = false;
  902. int startLength = 0;
  903. if (!(Start is Variable))
  904. {
  905. if (!(Start is Atom))
  906. throw new PrologException
  907. (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not var or atom");
  908. startLength = ((Atom)Start)._name.Length;
  909. gotStartLength = true;
  910. }
  911. bool gotEndLength = false;
  912. int endLength = 0;
  913. if (!(End is Variable))
  914. {
  915. if (!(End is Atom))
  916. throw new PrologException
  917. (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not var or atom");
  918. endLength = ((Atom)End)._name.Length;
  919. gotEndLength = true;
  920. }
  921. // We are doing a search through all possible Start and End which concatenate to Whole.
  922. string wholeString = ((Atom)Whole)._name;
  923. for (int i = 0; i <= wholeString.Length; ++i)
  924. {
  925. // If we got either startLength or endLength, we know the lengths have to match so check
  926. // the lengths instead of constructing an Atom to do it.
  927. if (gotStartLength && startLength != i)
  928. continue;
  929. if (gotEndLength && endLength != wholeString.Length - i)
  930. continue;
  931. foreach (bool l1 in YP.unify(Start, Atom.a(wholeString.Substring(0, i))))
  932. {
  933. foreach (bool l2 in YP.unify(End, Atom.a(wholeString.Substring(i, wholeString.Length - i))))
  934. yield return false;
  935. }
  936. }
  937. }
  938. }
  939. public static IEnumerable<bool> sub_atom
  940. (object atom, object Before, object Length, object After, object Sub_atom)
  941. {
  942. // Debug: Should we try to preserve the _declaringClass?
  943. atom = YP.getValue(atom);
  944. Before = YP.getValue(Before);
  945. Length = YP.getValue(Length);
  946. After = YP.getValue(After);
  947. Sub_atom = YP.getValue(Sub_atom);
  948. if (atom is Variable)
  949. throw new PrologException(Atom.a("instantiation_error"),
  950. "Expected atom(Arg1) but it is an unbound variable");
  951. if (!(atom is Atom))
  952. throw new PrologException
  953. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
  954. if (!(Sub_atom is Variable))
  955. {
  956. if (!(Sub_atom is Atom))
  957. throw new PrologException
  958. (new Functor2("type_error", Atom.a("atom"), Sub_atom), "Sub_atom is not var or atom");
  959. }
  960. bool beforeIsInt = false;
  961. bool lengthIsInt = false;
  962. bool afterIsInt = false;
  963. if (!(Before is Variable))
  964. {
  965. if (!(Before is int))
  966. throw new PrologException
  967. (new Functor2("type_error", Atom.a("integer"), Before), "Before must be var or integer");
  968. beforeIsInt = true;
  969. if ((int)Before < 0)
  970. throw new PrologException
  971. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Before),
  972. "Before must not be less than zero");
  973. }
  974. if (!(Length is Variable))
  975. {
  976. if (!(Length is int))
  977. throw new PrologException
  978. (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
  979. lengthIsInt = true;
  980. if ((int)Length < 0)
  981. throw new PrologException
  982. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
  983. "Length must not be less than zero");
  984. }
  985. if (!(After is Variable))
  986. {
  987. if (!(After is int))
  988. throw new PrologException
  989. (new Functor2("type_error", Atom.a("integer"), After), "After must be var or integer");
  990. afterIsInt = true;
  991. if ((int)After < 0)
  992. throw new PrologException
  993. (new Functor2("domain_error", Atom.a("not_less_than_zero"), After),
  994. "After must not be less than zero");
  995. }
  996. Atom atomAtom = (Atom)atom;
  997. int atomLength = atomAtom._name.Length;
  998. if (beforeIsInt && lengthIsInt)
  999. {
  1000. // Special case: the caller is just trying to extract a substring, so do it quickly.
  1001. int xAfter = atomLength - (int)Before - (int)Length;
  1002. if (xAfter >= 0)
  1003. {
  1004. foreach (bool l1 in YP.unify(After, xAfter))
  1005. {
  1006. foreach (bool l2 in YP.unify
  1007. (Sub_atom, Atom.a(atomAtom._name.Substring((int)Before, (int)Length))))
  1008. yield return false;
  1009. }
  1010. }
  1011. }
  1012. else if (afterIsInt && lengthIsInt)
  1013. {
  1014. // Special case: the caller is just trying to extract a substring, so do it quickly.
  1015. int xBefore = atomLength - (int)After - (int)Length;
  1016. if (xBefore >= 0)
  1017. {
  1018. foreach (bool l1 in YP.unify(Before, xBefore))
  1019. {
  1020. foreach (bool l2 in YP.unify
  1021. (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, (int)Length))))
  1022. yield return false;
  1023. }
  1024. }
  1025. }
  1026. else
  1027. {
  1028. // We are underconstrained and doing a search, so go through all possibilities.
  1029. for (int xBefore = 0; xBefore <= atomLength; ++xBefore)
  1030. {
  1031. foreach (bool l1 in YP.unify(Before, xBefore))
  1032. {
  1033. for (int xLength = 0; xLength <= (atomLength - xBefore); ++xLength)
  1034. {
  1035. foreach (bool l2 in YP.unify(Length, xLength))
  1036. {
  1037. foreach (bool l3 in YP.unify(After, atomLength - (xBefore + xLength)))
  1038. {
  1039. foreach (bool l4 in YP.unify
  1040. (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, xLength))))
  1041. yield return false;
  1042. }
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. }
  1049. public static IEnumerable<bool> atom_chars(object atom, object List)
  1050. {
  1051. atom = YP.getValue(atom);
  1052. List = YP.getValue(List);
  1053. if (atom is Variable)
  1054. {
  1055. if (List is Variable)
  1056. throw new PrologException(Atom.a("instantiation_error"),
  1057. "Arg 1 Atom and arg 2 List are both unbound variables");
  1058. object[] codeArray = ListPair.toArray(List);
  1059. if (codeArray == null)
  1060. throw new PrologException
  1061. (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
  1062. char[] charArray = new char[codeArray.Length];
  1063. for (int i = 0; i < codeArray.Length; ++i)
  1064. {
  1065. object listAtom = YP.getValue(codeArray[i]);
  1066. if (listAtom is Variable)
  1067. throw new PrologException(Atom.a("instantiation_error"),
  1068. "Arg 2 List has an element which is an unbound variable");
  1069. if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1))
  1070. throw new PrologException
  1071. (new Functor2("type_error", Atom.a("character"), listAtom),
  1072. "Arg 2 List has an element which is not a one character atom");
  1073. charArray[i] = ((Atom)listAtom)._name[0];
  1074. }
  1075. return YP.unify(atom, Atom.a(new String(charArray)));
  1076. }
  1077. else
  1078. {
  1079. if (!(atom is Atom))
  1080. throw new PrologException
  1081. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom");
  1082. string atomString = ((Atom)atom)._name;
  1083. object charList = Atom.NIL;
  1084. // Start from the back to make the list.
  1085. for (int i = atomString.Length - 1; i >= 0; --i)
  1086. charList = new ListPair(Atom.a(atomString.Substring(i, 1)), charList);
  1087. return YP.unify(List, charList);
  1088. }
  1089. }
  1090. public static IEnumerable<bool> atom_codes(object atom, object List)
  1091. {
  1092. atom = YP.getValue(atom);
  1093. List = YP.getValue(List);
  1094. if (atom is Variable)
  1095. {
  1096. if (List is Variable)
  1097. throw new PrologException(Atom.a("instantiation_error"),
  1098. "Arg 1 Atom and arg 2 List are both unbound variables");
  1099. object[] codeArray = ListPair.toArray(List);
  1100. if (codeArray == null)
  1101. throw new PrologException
  1102. (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");

Large files files are truncated, but you can click here to view the full file