PageRenderTime 51ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/YieldProlog/Modules/YP.cs

https://bitbucket.org/VirtualReality/optional-modules
C# | 2698 lines | 2416 code | 87 blank | 195 comment | 134 complexity | fb1535feb1b4732324a7a8b7d42211f6 MD5 | raw file

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

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

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