PageRenderTime 63ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/KyanhaLLC/opensim
C# | 2701 lines | 2417 code | 86 blank | 198 comment | 134 complexity | 61356a824155d23230738390536ee451 MD5 | raw file
Possible License(s): Unlicense, MIT, BSD-3-Clause

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. int z = 0;
  522. try
  523. {
  524. if (gotMatch)
  525. yield return false;
  526. }
  527. finally
  528. {
  529. // Manually finalize all the iterators.
  530. for (z = 0; z < nIterators; ++z)
  531. iterators[z].Dispose();
  532. }
  533. }
  534. /// <summary>
  535. /// Return an iterator (which you can use in a for-in loop) which does
  536. /// zero iterations. This returns a pre-existing iterator which is
  537. /// more efficient than letting the compiler generate a new one.
  538. /// </summary>
  539. /// <returns></returns>
  540. public static IEnumerable<bool> fail()
  541. {
  542. return _fail;
  543. }
  544. /// <summary>
  545. /// Return an iterator (which you can use in a for-in loop) which does
  546. /// one iteration. This returns a pre-existing iterator which is
  547. /// more efficient than letting the compiler generate a new one.
  548. /// </summary>
  549. /// <returns></returns>
  550. public static IEnumerable<bool> succeed()
  551. {
  552. return new Succeed();
  553. }
  554. /// <summary>
  555. /// Return an iterator (which you can use in a for-in loop) which repeats
  556. /// indefinitely. This returns a pre-existing iterator which is
  557. /// more efficient than letting the compiler generate a new one.
  558. /// </summary>
  559. /// <returns></returns>
  560. public static IEnumerable<bool> repeat()
  561. {
  562. return _repeat;
  563. }
  564. // disable warning on l1, don't see how we can
  565. // code this differently
  566. #pragma warning disable 0168, 0219
  567. public static IEnumerable<bool> univ(object Term, object List)
  568. {
  569. Term = YP.getValue(Term);
  570. List = YP.getValue(List);
  571. if (nonvar(Term))
  572. return YP.unify(new ListPair
  573. (getFunctorName(Term), ListPair.make(getFunctorArgs(Term))), List);
  574. Variable Name = new Variable();
  575. Variable ArgList = new Variable();
  576. foreach (bool l1 in new ListPair(Name, ArgList).unify(List))
  577. {
  578. object[] args = ListPair.toArray(ArgList);
  579. if (args == null)
  580. throw new PrologException
  581. (new Functor2("type_error", Atom.a("list"), ArgList),
  582. "Expected a list. Got: " + ArgList.getValue());
  583. if (args.Length == 0)
  584. // Return the Name, even if it is not an Atom.
  585. return YP.unify(Term, Name);
  586. if (args.Length > MAX_ARITY)
  587. throw new PrologException
  588. (new Functor1("representation_error", Atom.a("max_arity")),
  589. "Functor arity " + args.Length + " may not be greater than " + MAX_ARITY);
  590. if (!atom(Name))
  591. throw new PrologException
  592. (new Functor2("type_error", Atom.a("atom"), Name),
  593. "Expected an atom. Got: " + Name.getValue());
  594. return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args));
  595. }
  596. return YP.fail();
  597. }
  598. public static IEnumerable<bool> functor(object Term, object FunctorName, object Arity)
  599. {
  600. Term = YP.getValue(Term);
  601. FunctorName = YP.getValue(FunctorName);
  602. Arity = YP.getValue(Arity);
  603. if (Term is Variable)
  604. {
  605. if (FunctorName is Variable)
  606. throw new PrologException(Atom.a("instantiation_error"),
  607. "Arg 2 FunctorName is an unbound variable");
  608. if (Arity is Variable)
  609. throw new PrologException(Atom.a("instantiation_error"),
  610. "Arg 3 Arity is an unbound variable");
  611. if (!(Arity is int))
  612. throw new PrologException
  613. (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer");
  614. if (!YP.atomic(FunctorName))
  615. throw new PrologException
  616. (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic");
  617. if ((int)Arity < 0)
  618. throw new PrologException
  619. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity),
  620. "Arity may not be less than zero");
  621. else if ((int)Arity == 0)
  622. {
  623. // Just unify Term with the atomic FunctorName.
  624. foreach (bool l1 in YP.unify(Term, FunctorName))
  625. yield return false;
  626. }
  627. else
  628. {
  629. if ((int)Arity > MAX_ARITY)
  630. throw new PrologException
  631. (new Functor1("representation_error", Atom.a("max_arity")),
  632. "Functor arity " + Arity + " may not be greater than " + MAX_ARITY);
  633. if (!(FunctorName is Atom))
  634. throw new PrologException
  635. (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom");
  636. // Construct a functor with unbound variables.
  637. object[] args = new object[(int)Arity];
  638. for (int i = 0; i < args.Length; ++i)
  639. args[i] = new Variable();
  640. foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args)))
  641. yield return false;
  642. }
  643. }
  644. else
  645. {
  646. foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term)))
  647. {
  648. foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length))
  649. yield return false;
  650. }
  651. }
  652. }
  653. public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value)
  654. {
  655. if (var(ArgNumber))
  656. throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable");
  657. int argNumberInt;
  658. if (!getInt(ArgNumber, out argNumberInt))
  659. throw new PrologException
  660. (new Functor2("type_error", Atom.a("integer"), ArgNumber), "Arg 1 ArgNumber must be integer");
  661. if (argNumberInt < 0)
  662. throw new PrologException
  663. (new Functor2("domain_error", Atom.a("not_less_than_zero"), argNumberInt),
  664. "ArgNumber may not be less than zero");
  665. if (YP.var(Term))
  666. throw new PrologException(Atom.a("instantiation_error"),
  667. "Arg 2 Term is an unbound variable");
  668. if (!YP.compound(Term))
  669. throw new PrologException
  670. (new Functor2("type_error", Atom.a("compound"), Term), "Arg 2 Term must be compound");
  671. object[] termArgs = YP.getFunctorArgs(Term);
  672. // Silently fail if argNumberInt is out of range.
  673. if (argNumberInt >= 1 && argNumberInt <= termArgs.Length)
  674. {
  675. // The first ArgNumber is at 1, not 0.
  676. foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1]))
  677. yield return false;
  678. }
  679. }
  680. public static bool termEqual(object Term1, object Term2)
  681. {
  682. Term1 = YP.getValue(Term1);
  683. if (Term1 is IUnifiable)
  684. return ((IUnifiable)Term1).termEqual(Term2);
  685. return Term1.Equals(YP.getValue(Term2));
  686. }
  687. public static bool termNotEqual(object Term1, object Term2)
  688. {
  689. return !termEqual(Term1, Term2);
  690. }
  691. public static bool termLessThan(object Term1, object Term2)
  692. {
  693. Term1 = YP.getValue(Term1);
  694. Term2 = YP.getValue(Term2);
  695. int term1TypeCode = getTypeCode(Term1);
  696. int term2TypeCode = getTypeCode(Term2);
  697. if (term1TypeCode != term2TypeCode)
  698. return term1TypeCode < term2TypeCode;
  699. // The terms are the same type code.
  700. if (term1TypeCode == -2)
  701. {
  702. // Variable.
  703. // We always check for equality first because we want to be sure
  704. // that less than returns false if the terms are equal, in
  705. // case that the less than check really behaves like less than or equal.
  706. if ((Variable)Term1 != (Variable)Term2)
  707. // The hash code should be unique to a Variable object.
  708. return Term1.GetHashCode() < Term2.GetHashCode();
  709. return false;
  710. }
  711. if (term1TypeCode == 0)
  712. return ((Atom)Term1)._name.CompareTo(((Atom)Term2)._name) < 0;
  713. if (term1TypeCode == 1)
  714. return ((Functor1)Term1).lessThan((Functor1)Term2);
  715. if (term1TypeCode == 2)
  716. return ((Functor2)Term1).lessThan((Functor2)Term2);
  717. if (term1TypeCode == 3)
  718. return ((Functor3)Term1).lessThan((Functor3)Term2);
  719. if (term1TypeCode == 4)
  720. return ((Functor)Term1).lessThan((Functor)Term2);
  721. // Type code is -1 for general objects. First compare their type names.
  722. // Note that this puts Double before Int32 as required by ISO Prolog.
  723. string term1TypeName = Term1.GetType().ToString();
  724. string term2TypeName = Term2.GetType().ToString();
  725. if (term1TypeName != term2TypeName)
  726. return term1TypeName.CompareTo(term2TypeName) < 0;
  727. // The terms are the same type name.
  728. if (Term1 is int)
  729. return (int)Term1 < (int)Term2;
  730. else if (Term1 is double)
  731. return (double)Term1 < (double)Term2;
  732. else if (Term1 is DateTime)
  733. return (DateTime)Term1 < (DateTime)Term2;
  734. else if (Term1 is String)
  735. return ((String)Term1).CompareTo((String)Term2) < 0;
  736. // Debug: Should we try arrays, etc.?
  737. if (!Term1.Equals(Term2))
  738. // Could be equal or greater than.
  739. return Term1.GetHashCode() < Term2.GetHashCode();
  740. return false;
  741. }
  742. /// <summary>
  743. /// Type code is -2 if term is a Variable, 0 if it is an Atom,
  744. /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3,
  745. /// 4 if it is Functor.
  746. /// Otherwise, type code is -1.
  747. /// This does not call YP.getValue(term).
  748. /// </summary>
  749. /// <param name="term"></param>
  750. /// <returns></returns>
  751. private static int getTypeCode(object term)
  752. {
  753. if (term is Variable)
  754. return -2;
  755. else if (term is Atom)
  756. return 0;
  757. else if (term is Functor1)
  758. return 1;
  759. else if (term is Functor2)
  760. return 2;
  761. else if (term is Functor3)
  762. return 3;
  763. else if (term is Functor)
  764. return 4;
  765. else
  766. return -1;
  767. }
  768. public static bool termLessThanOrEqual(object Term1, object Term2)
  769. {
  770. if (YP.termEqual(Term1, Term2))
  771. return true;
  772. return YP.termLessThan(Term1, Term2);
  773. }
  774. public static bool termGreaterThan(object Term1, object Term2)
  775. {
  776. return !YP.termLessThanOrEqual(Term1, Term2);
  777. }
  778. public static bool termGreaterThanOrEqual(object Term1, object Term2)
  779. {
  780. // termLessThan should ensure that it returns false if terms are equal,
  781. // so that this would return true.
  782. return !YP.termLessThan(Term1, Term2);
  783. }
  784. public static int compareTerms(object Term1, object Term2)
  785. {
  786. if (YP.termEqual(Term1, Term2))
  787. return 0;
  788. else if (YP.termLessThan(Term1, Term2))
  789. return -1;
  790. else
  791. return 1;
  792. }
  793. public static bool ground(object Term)
  794. {
  795. Term = YP.getValue(Term);
  796. if (Term is IUnifiable)
  797. return ((IUnifiable)Term).ground();
  798. return true;
  799. }
  800. public static IEnumerable<bool> current_op
  801. (object Priority, object Specifier, object Operator)
  802. {
  803. if (_operatorTable == null)
  804. {
  805. // Initialize.
  806. _operatorTable = new IndexedAnswers(3);
  807. _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") });
  808. _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") });
  809. _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") });
  810. _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") });
  811. _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") });
  812. _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") });
  813. _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") });
  814. _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), 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("=..") });
  824. _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") });
  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[] { 700, Atom.a("xfx"), Atom.a(">=") });
  831. _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), 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[] { 500, 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("//") });
  839. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") });
  840. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") });
  841. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") });
  842. _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") });
  843. _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") });
  844. _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") });
  845. _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") });
  846. _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") });
  847. // Debug: This is hacked in to run the Prolog test suite until we implement op/3.
  848. _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") });
  849. }
  850. return _operatorTable.match(new object[] { Priority, Specifier, Operator });
  851. }
  852. public static IEnumerable<bool> atom_length(object atom, object Length)
  853. {
  854. atom = YP.getValue(atom);
  855. Length = YP.getValue(Length);
  856. if (atom is Variable)
  857. throw new PrologException(Atom.a("instantiation_error"),
  858. "Expected atom(Arg1) but it is an unbound variable");
  859. if (!(atom is Atom))
  860. throw new PrologException
  861. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
  862. if (!(Length is Variable))
  863. {
  864. if (!(Length is int))
  865. throw new PrologException
  866. (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
  867. if ((int)Length < 0)
  868. throw new PrologException
  869. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
  870. "Length must not be less than zero");
  871. }
  872. return YP.unify(Length, ((Atom)atom)._name.Length);
  873. }
  874. public static IEnumerable<bool> atom_concat(object Start, object End, object Whole)
  875. {
  876. // Debug: Should we try to preserve the _declaringClass?
  877. Start = YP.getValue(Start);
  878. End = YP.getValue(End);
  879. Whole = YP.getValue(Whole);
  880. if (Whole is Variable)
  881. {
  882. if (Start is Variable)
  883. throw new PrologException(Atom.a("instantiation_error"),
  884. "Arg 1 Start and arg 3 Whole are both var");
  885. if (End is Variable)
  886. throw new PrologException(Atom.a("instantiation_error"),
  887. "Arg 2 End and arg 3 Whole are both var");
  888. if (!(Start is Atom))
  889. throw new PrologException
  890. (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not an atom");
  891. if (!(End is Atom))
  892. throw new PrologException
  893. (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not an atom");
  894. foreach (bool l1 in YP.unify(Whole, Atom.a(((Atom)Start)._name + ((Atom)End)._name)))
  895. yield return false;
  896. }
  897. else
  898. {
  899. if (!(Whole is Atom))
  900. throw new PrologException
  901. (new Functor2("type_error", Atom.a("atom"), Whole), "Arg 3 Whole is not an atom");
  902. bool gotStartLength = false;
  903. int startLength = 0;
  904. if (!(Start is Variable))
  905. {
  906. if (!(Start is Atom))
  907. throw new PrologException
  908. (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not var or atom");
  909. startLength = ((Atom)Start)._name.Length;
  910. gotStartLength = true;
  911. }
  912. bool gotEndLength = false;
  913. int endLength = 0;
  914. if (!(End is Variable))
  915. {
  916. if (!(End is Atom))
  917. throw new PrologException
  918. (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not var or atom");
  919. endLength = ((Atom)End)._name.Length;
  920. gotEndLength = true;
  921. }
  922. // We are doing a search through all possible Start and End which concatenate to Whole.
  923. string wholeString = ((Atom)Whole)._name;
  924. for (int i = 0; i <= wholeString.Length; ++i)
  925. {
  926. // If we got either startLength or endLength, we know the lengths have to match so check
  927. // the lengths instead of constructing an Atom to do it.
  928. if (gotStartLength && startLength != i)
  929. continue;
  930. if (gotEndLength && endLength != wholeString.Length - i)
  931. continue;
  932. foreach (bool l1 in YP.unify(Start, Atom.a(wholeString.Substring(0, i))))
  933. {
  934. foreach (bool l2 in YP.unify(End, Atom.a(wholeString.Substring(i, wholeString.Length - i))))
  935. yield return false;
  936. }
  937. }
  938. }
  939. }
  940. public static IEnumerable<bool> sub_atom
  941. (object atom, object Before, object Length, object After, object Sub_atom)
  942. {
  943. // Debug: Should we try to preserve the _declaringClass?
  944. atom = YP.getValue(atom);
  945. Before = YP.getValue(Before);
  946. Length = YP.getValue(Length);
  947. After = YP.getValue(After);
  948. Sub_atom = YP.getValue(Sub_atom);
  949. if (atom is Variable)
  950. throw new PrologException(Atom.a("instantiation_error"),
  951. "Expected atom(Arg1) but it is an unbound variable");
  952. if (!(atom is Atom))
  953. throw new PrologException
  954. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom");
  955. if (!(Sub_atom is Variable))
  956. {
  957. if (!(Sub_atom is Atom))
  958. throw new PrologException
  959. (new Functor2("type_error", Atom.a("atom"), Sub_atom), "Sub_atom is not var or atom");
  960. }
  961. bool beforeIsInt = false;
  962. bool lengthIsInt = false;
  963. bool afterIsInt = false;
  964. if (!(Before is Variable))
  965. {
  966. if (!(Before is int))
  967. throw new PrologException
  968. (new Functor2("type_error", Atom.a("integer"), Before), "Before must be var or integer");
  969. beforeIsInt = true;
  970. if ((int)Before < 0)
  971. throw new PrologException
  972. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Before),
  973. "Before must not be less than zero");
  974. }
  975. if (!(Length is Variable))
  976. {
  977. if (!(Length is int))
  978. throw new PrologException
  979. (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer");
  980. lengthIsInt = true;
  981. if ((int)Length < 0)
  982. throw new PrologException
  983. (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length),
  984. "Length must not be less than zero");
  985. }
  986. if (!(After is Variable))
  987. {
  988. if (!(After is int))
  989. throw new PrologException
  990. (new Functor2("type_error", Atom.a("integer"), After), "After must be var or integer");
  991. afterIsInt = true;
  992. if ((int)After < 0)
  993. throw new PrologException
  994. (new Functor2("domain_error", Atom.a("not_less_than_zero"), After),
  995. "After must not be less than zero");
  996. }
  997. Atom atomAtom = (Atom)atom;
  998. int atomLength = atomAtom._name.Length;
  999. if (beforeIsInt && lengthIsInt)
  1000. {
  1001. // Special case: the caller is just trying to extract a substring, so do it quickly.
  1002. int xAfter = atomLength - (int)Before - (int)Length;
  1003. if (xAfter >= 0)
  1004. {
  1005. foreach (bool l1 in YP.unify(After, xAfter))
  1006. {
  1007. foreach (bool l2 in YP.unify
  1008. (Sub_atom, Atom.a(atomAtom._name.Substring((int)Before, (int)Length))))
  1009. yield return false;
  1010. }
  1011. }
  1012. }
  1013. else if (afterIsInt && lengthIsInt)
  1014. {
  1015. // Special case: the caller is just trying to extract a substring, so do it quickly.
  1016. int xBefore = atomLength - (int)After - (int)Length;
  1017. if (xBefore >= 0)
  1018. {
  1019. foreach (bool l1 in YP.unify(Before, xBefore))
  1020. {
  1021. foreach (bool l2 in YP.unify
  1022. (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, (int)Length))))
  1023. yield return false;
  1024. }
  1025. }
  1026. }
  1027. else
  1028. {
  1029. // We are underconstrained and doing a search, so go through all possibilities.
  1030. for (int xBefore = 0; xBefore <= atomLength; ++xBefore)
  1031. {
  1032. foreach (bool l1 in YP.unify(Before, xBefore))
  1033. {
  1034. for (int xLength = 0; xLength <= (atomLength - xBefore); ++xLength)
  1035. {
  1036. foreach (bool l2 in YP.unify(Length, xLength))
  1037. {
  1038. foreach (bool l3 in YP.unify(After, atomLength - (xBefore + xLength)))
  1039. {
  1040. foreach (bool l4 in YP.unify
  1041. (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, xLength))))
  1042. yield return false;
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. }
  1049. }
  1050. public static IEnumerable<bool> atom_chars(object atom, object List)
  1051. {
  1052. atom = YP.getValue(atom);
  1053. List = YP.getValue(List);
  1054. if (atom is Variable)
  1055. {
  1056. if (List is Variable)
  1057. throw new PrologException(Atom.a("instantiation_error"),
  1058. "Arg 1 Atom and arg 2 List are both unbound variables");
  1059. object[] codeArray = ListPair.toArray(List);
  1060. if (codeArray == null)
  1061. throw new PrologException
  1062. (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list");
  1063. char[] charArray = new char[codeArray.Length];
  1064. for (int i = 0; i < codeArray.Length; ++i)
  1065. {
  1066. object listAtom = YP.getValue(codeArray[i]);
  1067. if (listAtom is Variable)
  1068. throw new PrologException(Atom.a("instantiation_error"),
  1069. "Arg 2 List has an element which is an unbound variable");
  1070. if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1))
  1071. throw new PrologException
  1072. (new Functor2("type_error", Atom.a("character"), listAtom),
  1073. "Arg 2 List has an element which is not a one character atom");
  1074. charArray[i] = ((Atom)listAtom)._name[0];
  1075. }
  1076. return YP.unify(atom, Atom.a(new String(charArray)));
  1077. }
  1078. else
  1079. {
  1080. if (!(atom is Atom))
  1081. throw new PrologException
  1082. (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom");
  1083. string atomString = ((Atom)atom)._name;
  1084. object charList = Atom.NIL;
  1085. // Start from the back to make the list.
  1086. for (int i = atomString.Length - 1; i >= 0; --i)
  1087. charList = new ListPair(Atom.a(atomString.Substring(i, 1)), charList);
  1088. return YP.unify(List, charList);
  1089. }
  1090. }
  1091. public static IEnumerable<bool> atom_codes(object atom, object List)
  1092. {
  1093. atom = YP.getValue(atom);
  1094. List = YP.getValue(List);
  1095. if (atom is Variable)
  1096. {
  1097. if (List is Variable)
  1098. throw new PrologException(Atom.a("instantiation_error"),
  1099. "Arg 1 Atom and arg 2 List are both unbound variables");
  1100. object[] codeArray = ListPair.toArray(List);
  1101. if (codeArray == null)
  1102. throw new PrologException
  1103. (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is…

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