diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs | 1221 |
1 files changed, 977 insertions, 244 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs index c212fb8..3d19d3e 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/YieldProlog/YP.cs | |||
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2007-2008, Jeff Thompson | 2 | * Copyright (C) 2007-2008, Jeff Thompson |
3 | * | 3 | * |
4 | * All rights reserved. | 4 | * All rights reserved. |
5 | * | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions are met: | 7 | * modification, are permitted provided that the following conditions are met: |
8 | * | 8 | * |
9 | * * Redistributions of source code must retain the above copyright | 9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. | 10 | * notice, this list of conditions and the following disclaimer. |
11 | * * Redistributions in binary form must reproduce the above copyright | 11 | * * Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the | 12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. | 13 | * documentation and/or other materials provided with the distribution. |
14 | * * Neither the name of the copyright holder nor the names of its contributors | 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 | 15 | * may be used to endorse or promote products derived from this software |
16 | * without specific prior written permission. | 16 | * without specific prior written permission. |
17 | * | 17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
@@ -33,6 +33,9 @@ using System.Collections; | |||
33 | using System.Collections.Generic; | 33 | using System.Collections.Generic; |
34 | using System.IO; | 34 | using System.IO; |
35 | using System.Reflection; | 35 | using System.Reflection; |
36 | using System.Net.Sockets; | ||
37 | using System.Text; | ||
38 | using System.Text.RegularExpressions; | ||
36 | 39 | ||
37 | namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | 40 | namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog |
38 | { | 41 | { |
@@ -48,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
48 | new Dictionary<NameArity, List<IClause>>(); | 51 | new Dictionary<NameArity, List<IClause>>(); |
49 | private static TextWriter _outputStream = System.Console.Out; | 52 | private static TextWriter _outputStream = System.Console.Out; |
50 | private static TextReader _inputStream = System.Console.In; | 53 | private static TextReader _inputStream = System.Console.In; |
51 | private static List<object[]> _operatorTable = null; | 54 | private static IndexedAnswers _operatorTable = null; |
52 | 55 | ||
53 | /// <summary> | 56 | /// <summary> |
54 | /// An IClause is used so that dynamic predicates can call match. | 57 | /// An IClause is used so that dynamic predicates can call match. |
@@ -56,6 +59,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
56 | public interface IClause | 59 | public interface IClause |
57 | { | 60 | { |
58 | IEnumerable<bool> match(object[] args); | 61 | IEnumerable<bool> match(object[] args); |
62 | IEnumerable<bool> clause(object Head, object Body); | ||
59 | } | 63 | } |
60 | 64 | ||
61 | public static object getValue(object value) | 65 | public static object getValue(object value) |
@@ -120,7 +124,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
120 | /// <summary> | 124 | /// <summary> |
121 | /// Convert term to an int. | 125 | /// Convert term to an int. |
122 | /// If term is a single-element List, use its first element | 126 | /// If term is a single-element List, use its first element |
123 | /// (to handle the char types like "a"). If can't convert, throw an exception. | 127 | /// (to handle the char types like "a"). |
128 | /// If can't convert, throw a PrologException for type_error evaluable (because this is only | ||
129 | /// called from arithmetic functions). | ||
124 | /// </summary> | 130 | /// </summary> |
125 | /// <param name="term"></param> | 131 | /// <param name="term"></param> |
126 | /// <returns></returns> | 132 | /// <returns></returns> |
@@ -131,14 +137,30 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
131 | YP.getValue(((Functor2)term)._arg2) == Atom.NIL) | 137 | YP.getValue(((Functor2)term)._arg2) == Atom.NIL) |
132 | // Assume it is a char type like "a". | 138 | // Assume it is a char type like "a". |
133 | term = YP.getValue(((Functor2)term)._arg1); | 139 | term = YP.getValue(((Functor2)term)._arg1); |
140 | if (term is Variable) | ||
141 | throw new PrologException(Atom.a("instantiation_error"), | ||
142 | "Expected a number but the argument is an unbound variable"); | ||
134 | 143 | ||
135 | return (int)term; | 144 | try |
145 | { | ||
146 | return (int)term; | ||
147 | } | ||
148 | catch (InvalidCastException) | ||
149 | { | ||
150 | throw new PrologException | ||
151 | (new Functor2 | ||
152 | ("type_error", Atom.a("evaluable"), | ||
153 | new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)), | ||
154 | "Term must be an integer"); | ||
155 | } | ||
136 | } | 156 | } |
137 | 157 | ||
138 | /// <summary> | 158 | /// <summary> |
139 | /// Convert term to a double. This may convert an int to a double, etc. | 159 | /// Convert term to a double. This may convert an int to a double, etc. |
140 | /// If term is a single-element List, use its first element | 160 | /// If term is a single-element List, use its first element |
141 | /// (to handle the char types like "a"). If can't convert, throw an exception. | 161 | /// (to handle the char types like "a"). |
162 | /// If can't convert, throw a PrologException for type_error evaluable (because this is only | ||
163 | /// called from arithmetic functions). | ||
142 | /// </summary> | 164 | /// </summary> |
143 | /// <param name="term"></param> | 165 | /// <param name="term"></param> |
144 | /// <returns></returns> | 166 | /// <returns></returns> |
@@ -153,7 +175,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
153 | throw new PrologException(Atom.a("instantiation_error"), | 175 | throw new PrologException(Atom.a("instantiation_error"), |
154 | "Expected a number but the argument is an unbound variable"); | 176 | "Expected a number but the argument is an unbound variable"); |
155 | 177 | ||
156 | return Convert.ToDouble(term); | 178 | try |
179 | { | ||
180 | return Convert.ToDouble(term); | ||
181 | } | ||
182 | catch (InvalidCastException) | ||
183 | { | ||
184 | throw new PrologException | ||
185 | (new Functor2 | ||
186 | ("type_error", Atom.a("evaluable"), | ||
187 | new Functor2(Atom.SLASH, getFunctorName(term), getFunctorArgs(term).Length)), | ||
188 | "Term must be an integer"); | ||
189 | } | ||
157 | } | 190 | } |
158 | 191 | ||
159 | /// <summary> | 192 | /// <summary> |
@@ -260,6 +293,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
260 | return Math.Sign(convertDouble(x)); | 293 | return Math.Sign(convertDouble(x)); |
261 | } | 294 | } |
262 | 295 | ||
296 | // Use toFloat instead of float because it is a reserved keyword. | ||
297 | public static object toFloat(object x) | ||
298 | { | ||
299 | return convertDouble(x); | ||
300 | } | ||
301 | |||
263 | /// <summary> | 302 | /// <summary> |
264 | /// The ISO standard returns an int. | 303 | /// The ISO standard returns an int. |
265 | /// </summary> | 304 | /// </summary> |
@@ -485,8 +524,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
485 | return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array)); | 524 | return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array)); |
486 | } | 525 | } |
487 | 526 | ||
488 | |||
489 | |||
490 | /// <summary> | 527 | /// <summary> |
491 | /// Use YP.unify to unify each of the elements of the two arrays, and yield | 528 | /// Use YP.unify to unify each of the elements of the two arrays, and yield |
492 | /// once if they all unify. | 529 | /// once if they all unify. |
@@ -561,6 +598,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
561 | return _repeat; | 598 | return _repeat; |
562 | } | 599 | } |
563 | 600 | ||
601 | // disable warning on l1, don't see how we can | ||
602 | // code this differently | ||
603 | #pragma warning disable 0168 | ||
564 | public static IEnumerable<bool> univ(object Term, object List) | 604 | public static IEnumerable<bool> univ(object Term, object List) |
565 | { | 605 | { |
566 | Term = YP.getValue(Term); | 606 | Term = YP.getValue(Term); |
@@ -572,23 +612,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
572 | 612 | ||
573 | Variable Name = new Variable(); | 613 | Variable Name = new Variable(); |
574 | Variable ArgList = new Variable(); | 614 | Variable ArgList = new Variable(); |
575 | // disable warning: don't see how we can code this differently short | ||
576 | // of rewriting the whole thing | ||
577 | #pragma warning disable 0168 | ||
578 | foreach (bool l1 in new ListPair(Name, ArgList).unify(List)) | 615 | foreach (bool l1 in new ListPair(Name, ArgList).unify(List)) |
579 | { | 616 | { |
580 | object[] args = ListPair.toArray(ArgList); | 617 | object[] args = ListPair.toArray(ArgList); |
581 | if (args == null) | 618 | if (args == null) |
582 | throw new Exception("Expected a list. Got: " + ArgList.getValue()); | 619 | throw new PrologException |
620 | (new Functor2("type_error", Atom.a("list"), ArgList), | ||
621 | "Expected a list. Got: " + ArgList.getValue()); | ||
583 | if (args.Length == 0) | 622 | if (args.Length == 0) |
584 | // Return the Name, even if it is not an Atom. | 623 | // Return the Name, even if it is not an Atom. |
585 | return YP.unify(Term, Name); | 624 | return YP.unify(Term, Name); |
586 | if (!atom(Name)) | 625 | if (!atom(Name)) |
587 | throw new Exception("Expected an atom. Got: " + Name.getValue()); | 626 | throw new PrologException |
627 | (new Functor2("type_error", Atom.a("atom"), Name), | ||
628 | "Expected an atom. Got: " + Name.getValue()); | ||
588 | 629 | ||
589 | return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args)); | 630 | return YP.unify(Term, Functor.make((Atom)YP.getValue(Name), args)); |
590 | } | 631 | } |
591 | #pragma warning restore 0168 | ||
592 | 632 | ||
593 | return YP.fail(); | 633 | return YP.fail(); |
594 | } | 634 | } |
@@ -599,43 +639,81 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
599 | FunctorName = YP.getValue(FunctorName); | 639 | FunctorName = YP.getValue(FunctorName); |
600 | Arity = YP.getValue(Arity); | 640 | Arity = YP.getValue(Arity); |
601 | 641 | ||
602 | if (!(Term is Variable)) | 642 | if (Term is Variable) |
643 | { | ||
644 | if (FunctorName is Variable) | ||
645 | throw new PrologException(Atom.a("instantiation_error"), | ||
646 | "Arg 2 FunctorName is an unbound variable"); | ||
647 | if (Arity is Variable) | ||
648 | throw new PrologException(Atom.a("instantiation_error"), | ||
649 | "Arg 3 Arity is an unbound variable"); | ||
650 | if (!(Arity is int)) | ||
651 | throw new PrologException | ||
652 | (new Functor2("type_error", Atom.a("integer"), Arity), "Arity is not an integer"); | ||
653 | if (!YP.atomic(FunctorName)) | ||
654 | throw new PrologException | ||
655 | (new Functor2("type_error", Atom.a("atomic"), FunctorName), "FunctorName is not atomic"); | ||
656 | |||
657 | if ((int)Arity < 0) | ||
658 | throw new PrologException | ||
659 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), Arity), | ||
660 | "Arity may not be less than zero"); | ||
661 | else if ((int)Arity == 0) | ||
662 | { | ||
663 | // Just unify Term with the atomic FunctorName. | ||
664 | foreach (bool l1 in YP.unify(Term, FunctorName)) | ||
665 | yield return false; | ||
666 | } | ||
667 | else | ||
668 | { | ||
669 | if (!(FunctorName is Atom)) | ||
670 | throw new PrologException | ||
671 | (new Functor2("type_error", Atom.a("atom"), FunctorName), "FunctorName is not an atom"); | ||
672 | // Construct a functor with unbound variables. | ||
673 | object[] args = new object[(int)Arity]; | ||
674 | for (int i = 0; i < args.Length; ++i) | ||
675 | args[i] = new Variable(); | ||
676 | foreach (bool l1 in YP.unify(Term, Functor.make((Atom)FunctorName, args))) | ||
677 | yield return false; | ||
678 | } | ||
679 | } | ||
680 | else | ||
603 | { | 681 | { |
604 | // disable warning: don't see how we can code this differently short | ||
605 | // of rewriting the whole thing | ||
606 | #pragma warning disable 0168 | ||
607 | foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term))) | 682 | foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term))) |
608 | { | 683 | { |
609 | foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length)) | 684 | foreach (bool l2 in YP.unify(Arity, getFunctorArgs(Term).Length)) |
610 | yield return false; | 685 | yield return false; |
611 | } | 686 | } |
612 | #pragma warning restore 0168 | ||
613 | } | 687 | } |
614 | else | ||
615 | throw new NotImplementedException("Debug: must finish functor/3"); | ||
616 | } | 688 | } |
617 | 689 | ||
618 | public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value) | 690 | public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value) |
619 | { | 691 | { |
620 | if (YP.var(ArgNumber)) | 692 | if (var(ArgNumber)) |
621 | throw new NotImplementedException("Debug: must finish arg/3"); | 693 | throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable"); |
622 | else | 694 | int argNumberInt; |
695 | if (!getInt(ArgNumber, out argNumberInt)) | ||
696 | throw new PrologException | ||
697 | (new Functor2("type_error", Atom.a("integer"), ArgNumber), "Arg 1 ArgNumber must be integer"); | ||
698 | if (argNumberInt < 0) | ||
699 | throw new PrologException | ||
700 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), argNumberInt), | ||
701 | "ArgNumber may not be less than zero"); | ||
702 | |||
703 | if (YP.var(Term)) | ||
704 | throw new PrologException(Atom.a("instantiation_error"), | ||
705 | "Arg 2 Term is an unbound variable"); | ||
706 | if (!YP.compound(Term)) | ||
707 | throw new PrologException | ||
708 | (new Functor2("type_error", Atom.a("compound"), Term), "Arg 2 Term must be compound"); | ||
709 | |||
710 | object[] termArgs = YP.getFunctorArgs(Term); | ||
711 | // Silently fail if argNumberInt is out of range. | ||
712 | if (argNumberInt >= 1 && argNumberInt <= termArgs.Length) | ||
623 | { | 713 | { |
624 | int argNumberInt = convertInt(ArgNumber); | 714 | // The first ArgNumber is at 1, not 0. |
625 | if (argNumberInt < 0) | 715 | foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1])) |
626 | throw new Exception("ArgNumber must be non-negative"); | 716 | yield return false; |
627 | object[] termArgs = YP.getFunctorArgs(Term); | ||
628 | // Silently fail if argNumberInt is out of range. | ||
629 | if (argNumberInt >= 1 && argNumberInt <= termArgs.Length) | ||
630 | { | ||
631 | // The first ArgNumber is at 1, not 0. | ||
632 | // disable warning: don't see how we can code this differently short | ||
633 | // of rewriting the whole thing | ||
634 | #pragma warning disable 0168 | ||
635 | foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1])) | ||
636 | yield return false; | ||
637 | #pragma warning restore 0168 | ||
638 | } | ||
639 | } | 717 | } |
640 | } | 718 | } |
641 | 719 | ||
@@ -665,8 +743,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
665 | if (term1TypeCode == -2) | 743 | if (term1TypeCode == -2) |
666 | { | 744 | { |
667 | // Variable. | 745 | // Variable. |
668 | // We always check for equality first because we want to be sure | 746 | // We always check for equality first because we want to be sure |
669 | // that less than returns false if the terms are equal, in | 747 | // that less than returns false if the terms are equal, in |
670 | // case that the less than check really behaves like less than or equal. | 748 | // case that the less than check really behaves like less than or equal. |
671 | if ((Variable)Term1 != (Variable)Term2) | 749 | if ((Variable)Term1 != (Variable)Term2) |
672 | // The hash code should be unique to a Variable object. | 750 | // The hash code should be unique to a Variable object. |
@@ -709,8 +787,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
709 | } | 787 | } |
710 | 788 | ||
711 | /// <summary> | 789 | /// <summary> |
712 | /// Type code is -2 if term is a Variable, 0 if it is an Atom, | 790 | /// Type code is -2 if term is a Variable, 0 if it is an Atom, |
713 | /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3, | 791 | /// 1 if it is a Functor1, 2 if it is a Functor2, 3 if it is a Functor3, |
714 | /// 4 if it is Functor. | 792 | /// 4 if it is Functor. |
715 | /// Otherwise, type code is -1. | 793 | /// Otherwise, type code is -1. |
716 | /// This does not call YP.getValue(term). | 794 | /// This does not call YP.getValue(term). |
@@ -778,101 +856,303 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
778 | if (_operatorTable == null) | 856 | if (_operatorTable == null) |
779 | { | 857 | { |
780 | // Initialize. | 858 | // Initialize. |
781 | _operatorTable = new List<object[]>(); | 859 | _operatorTable = new IndexedAnswers(3); |
782 | _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") }); | 860 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") }); |
783 | _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") }); | 861 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") }); |
784 | _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a(":-") }); | 862 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") }); |
785 | _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a("?-") }); | 863 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") }); |
786 | _operatorTable.Add(new object[] { 1100, Atom.a("xfy"), Atom.a(";") }); | 864 | _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") }); |
787 | _operatorTable.Add(new object[] { 1050, Atom.a("xfy"), Atom.a("->") }); | 865 | _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") }); |
788 | _operatorTable.Add(new object[] { 1000, Atom.a("xfy"), Atom.a(",") }); | 866 | _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") }); |
789 | _operatorTable.Add(new object[] { 900, Atom.a("fy"), Atom.a("\\+") }); | 867 | _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), Atom.a("\\+") }); |
790 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=") }); | 868 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=") }); |
791 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") }); | 869 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") }); |
792 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("==") }); | 870 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("==") }); |
793 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") }); | 871 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") }); |
794 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@<") }); | 872 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@<") }); |
795 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") }); | 873 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") }); |
796 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>") }); | 874 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>") }); |
797 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") }); | 875 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") }); |
798 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=..") }); | 876 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=..") }); |
799 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("is") }); | 877 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") }); |
800 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") }); | 878 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") }); |
801 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") }); | 879 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") }); |
802 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("<") }); | 880 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("<") }); |
803 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=<") }); | 881 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=<") }); |
804 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">") }); | 882 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">") }); |
805 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">=") }); | 883 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">=") }); |
806 | _operatorTable.Add(new object[] { 600, Atom.a("xfy"), Atom.a(":") }); | 884 | _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), Atom.a(":") }); |
807 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("+") }); | 885 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("+") }); |
808 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("-") }); | 886 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("-") }); |
809 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") }); | 887 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") }); |
810 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") }); | 888 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") }); |
811 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("*") }); | 889 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("*") }); |
812 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("/") }); | 890 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("/") }); |
813 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("//") }); | 891 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("//") }); |
814 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("rem") }); | 892 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") }); |
815 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("mod") }); | 893 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") }); |
816 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("<<") }); | 894 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") }); |
817 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a(">>") }); | 895 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") }); |
818 | _operatorTable.Add(new object[] { 200, Atom.a("xfx"), Atom.a("**") }); | 896 | _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") }); |
819 | _operatorTable.Add(new object[] { 200, Atom.a("xfy"), Atom.a("^") }); | 897 | _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") }); |
820 | _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("-") }); | 898 | _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") }); |
821 | _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("\\") }); | 899 | _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") }); |
822 | // Debug: This is hacked in to run the Prolog test suite until we implement op/3. | 900 | // Debug: This is hacked in to run the Prolog test suite until we implement op/3. |
823 | _operatorTable.Add(new object[] { 20, Atom.a("xfx"), Atom.a("<--") }); | 901 | _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") }); |
824 | } | 902 | } |
825 | 903 | ||
826 | object[] args = new object[] { Priority, Specifier, Operator }; | 904 | foreach (bool l1 in _operatorTable.match(new object[] { Priority, Specifier, Operator })) |
827 | foreach (object[] answer in _operatorTable) | 905 | yield return false; |
828 | { | ||
829 | // disable warning: don't see how we can code this differently short | ||
830 | // of rewriting the whole thing | ||
831 | #pragma warning disable 0168 | ||
832 | foreach (bool l1 in YP.unifyArrays(args, answer)) | ||
833 | yield return false; | ||
834 | #pragma warning restore 0168 | ||
835 | } | ||
836 | } | 906 | } |
837 | 907 | ||
838 | public static IEnumerable<bool> atom_length(object atom, object Length) | 908 | public static IEnumerable<bool> atom_length(object atom, object Length) |
839 | { | 909 | { |
840 | return YP.unify(Length, ((Atom)YP.getValue(atom))._name.Length); | 910 | atom = YP.getValue(atom); |
911 | Length = YP.getValue(Length); | ||
912 | if (atom is Variable) | ||
913 | throw new PrologException(Atom.a("instantiation_error"), | ||
914 | "Expected atom(Arg1) but it is an unbound variable"); | ||
915 | if (!(atom is Atom)) | ||
916 | throw new PrologException | ||
917 | (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom"); | ||
918 | if (!(Length is Variable)) | ||
919 | { | ||
920 | if (!(Length is int)) | ||
921 | throw new PrologException | ||
922 | (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer"); | ||
923 | if ((int)Length < 0) | ||
924 | throw new PrologException | ||
925 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length), | ||
926 | "Length must not be less than zero"); | ||
927 | } | ||
928 | return YP.unify(Length, ((Atom)atom)._name.Length); | ||
841 | } | 929 | } |
842 | 930 | ||
843 | public static IEnumerable<bool> atom_concat(object Start, object End, object Whole) | 931 | public static IEnumerable<bool> atom_concat(object Start, object End, object Whole) |
844 | { | 932 | { |
845 | // Debug: Should implement for var(Start) which is a kind of search. | ||
846 | // Debug: Should we try to preserve the _declaringClass? | 933 | // Debug: Should we try to preserve the _declaringClass? |
847 | return YP.unify(Whole, Atom.a(((Atom)YP.getValue(Start))._name + | 934 | Start = YP.getValue(Start); |
848 | ((Atom)YP.getValue(End))._name)); | 935 | End = YP.getValue(End); |
936 | Whole = YP.getValue(Whole); | ||
937 | if (Whole is Variable) | ||
938 | { | ||
939 | if (Start is Variable) | ||
940 | throw new PrologException(Atom.a("instantiation_error"), | ||
941 | "Arg 1 Start and arg 3 Whole are both var"); | ||
942 | if (End is Variable) | ||
943 | throw new PrologException(Atom.a("instantiation_error"), | ||
944 | "Arg 2 End and arg 3 Whole are both var"); | ||
945 | if (!(Start is Atom)) | ||
946 | throw new PrologException | ||
947 | (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not an atom"); | ||
948 | if (!(End is Atom)) | ||
949 | throw new PrologException | ||
950 | (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not an atom"); | ||
951 | |||
952 | foreach (bool l1 in YP.unify(Whole, Atom.a(((Atom)Start)._name + ((Atom)End)._name))) | ||
953 | yield return false; | ||
954 | } | ||
955 | else | ||
956 | { | ||
957 | if (!(Whole is Atom)) | ||
958 | throw new PrologException | ||
959 | (new Functor2("type_error", Atom.a("atom"), Whole), "Arg 3 Whole is not an atom"); | ||
960 | bool gotStartLength = false; | ||
961 | int startLength = 0; | ||
962 | if (!(Start is Variable)) | ||
963 | { | ||
964 | if (!(Start is Atom)) | ||
965 | throw new PrologException | ||
966 | (new Functor2("type_error", Atom.a("atom"), Start), "Arg 1 Start is not var or atom"); | ||
967 | startLength = ((Atom)Start)._name.Length; | ||
968 | gotStartLength = true; | ||
969 | } | ||
970 | |||
971 | bool gotEndLength = false; | ||
972 | int endLength = 0; | ||
973 | if (!(End is Variable)) | ||
974 | { | ||
975 | if (!(End is Atom)) | ||
976 | throw new PrologException | ||
977 | (new Functor2("type_error", Atom.a("atom"), End), "Arg 2 End is not var or atom"); | ||
978 | endLength = ((Atom)End)._name.Length; | ||
979 | gotEndLength = true; | ||
980 | } | ||
981 | |||
982 | // We are doing a search through all possible Start and End which concatenate to Whole. | ||
983 | string wholeString = ((Atom)Whole)._name; | ||
984 | for (int i = 0; i <= wholeString.Length; ++i) | ||
985 | { | ||
986 | // If we got either startLength or endLength, we know the lengths have to match so check | ||
987 | // the lengths instead of constructing an Atom to do it. | ||
988 | if (gotStartLength && startLength != i) | ||
989 | continue; | ||
990 | if (gotEndLength && endLength != wholeString.Length - i) | ||
991 | continue; | ||
992 | foreach (bool l1 in YP.unify(Start, Atom.a(wholeString.Substring(0, i)))) | ||
993 | { | ||
994 | foreach (bool l2 in YP.unify(End, Atom.a(wholeString.Substring(i, wholeString.Length - i)))) | ||
995 | yield return false; | ||
996 | } | ||
997 | } | ||
998 | } | ||
849 | } | 999 | } |
850 | 1000 | ||
851 | public static IEnumerable<bool> sub_atom | 1001 | public static IEnumerable<bool> sub_atom |
852 | (object atom, object Before, object Length, object After, object Sub_atom) | 1002 | (object atom, object Before, object Length, object After, object Sub_atom) |
853 | { | 1003 | { |
854 | // Debug: Should implement for var(atom) which is a kind of search. | ||
855 | // Debug: Should we try to preserve the _declaringClass? | 1004 | // Debug: Should we try to preserve the _declaringClass? |
856 | Atom atomAtom = (Atom)YP.getValue(atom); | 1005 | atom = YP.getValue(atom); |
857 | int beforeInt = YP.convertInt(Before); | 1006 | Before = YP.getValue(Before); |
858 | int lengthInt = YP.convertInt(Length); | 1007 | Length = YP.getValue(Length); |
859 | if (beforeInt < 0) | 1008 | After = YP.getValue(After); |
860 | throw new Exception("Before must be non-negative"); | 1009 | Sub_atom = YP.getValue(Sub_atom); |
861 | if (lengthInt < 0) | 1010 | if (atom is Variable) |
862 | throw new Exception("Length must be non-negative"); | 1011 | throw new PrologException(Atom.a("instantiation_error"), |
863 | int afterInt = atomAtom._name.Length - (beforeInt + lengthInt); | 1012 | "Expected atom(Arg1) but it is an unbound variable"); |
864 | if (afterInt >= 0) | 1013 | if (!(atom is Atom)) |
865 | { | 1014 | throw new PrologException |
866 | // disable warning: don't see how we can code this differently short | 1015 | (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom"); |
867 | // of rewriting the whole thing | 1016 | if (!(Sub_atom is Variable)) |
868 | #pragma warning disable 0168 | 1017 | { |
869 | foreach (bool l1 in YP.unify(After, afterInt)) | 1018 | if (!(Sub_atom is Atom)) |
1019 | throw new PrologException | ||
1020 | (new Functor2("type_error", Atom.a("atom"), Sub_atom), "Sub_atom is not var or atom"); | ||
1021 | } | ||
1022 | |||
1023 | bool beforeIsInt = false; | ||
1024 | bool lengthIsInt = false; | ||
1025 | bool afterIsInt = false; | ||
1026 | if (!(Before is Variable)) | ||
1027 | { | ||
1028 | if (!(Before is int)) | ||
1029 | throw new PrologException | ||
1030 | (new Functor2("type_error", Atom.a("integer"), Before), "Before must be var or integer"); | ||
1031 | beforeIsInt = true; | ||
1032 | if ((int)Before < 0) | ||
1033 | throw new PrologException | ||
1034 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), Before), | ||
1035 | "Before must not be less than zero"); | ||
1036 | } | ||
1037 | if (!(Length is Variable)) | ||
1038 | { | ||
1039 | if (!(Length is int)) | ||
1040 | throw new PrologException | ||
1041 | (new Functor2("type_error", Atom.a("integer"), Length), "Length must be var or integer"); | ||
1042 | lengthIsInt = true; | ||
1043 | if ((int)Length < 0) | ||
1044 | throw new PrologException | ||
1045 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), Length), | ||
1046 | "Length must not be less than zero"); | ||
1047 | } | ||
1048 | if (!(After is Variable)) | ||
1049 | { | ||
1050 | if (!(After is int)) | ||
1051 | throw new PrologException | ||
1052 | (new Functor2("type_error", Atom.a("integer"), After), "After must be var or integer"); | ||
1053 | afterIsInt = true; | ||
1054 | if ((int)After < 0) | ||
1055 | throw new PrologException | ||
1056 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), After), | ||
1057 | "After must not be less than zero"); | ||
1058 | } | ||
1059 | |||
1060 | Atom atomAtom = (Atom)atom; | ||
1061 | int atomLength = atomAtom._name.Length; | ||
1062 | if (beforeIsInt && lengthIsInt) | ||
1063 | { | ||
1064 | // Special case: the caller is just trying to extract a substring, so do it quickly. | ||
1065 | int xAfter = atomLength - (int)Before - (int)Length; | ||
1066 | if (xAfter >= 0) | ||
870 | { | 1067 | { |
871 | foreach (bool l2 in YP.unify | 1068 | foreach (bool l1 in YP.unify(After, xAfter)) |
872 | (Sub_atom, Atom.a(atomAtom._name.Substring(beforeInt, lengthInt)))) | 1069 | { |
873 | yield return false; | 1070 | foreach (bool l2 in YP.unify |
1071 | (Sub_atom, Atom.a(atomAtom._name.Substring((int)Before, (int)Length)))) | ||
1072 | yield return false; | ||
1073 | } | ||
1074 | } | ||
1075 | } | ||
1076 | else if (afterIsInt && lengthIsInt) | ||
1077 | { | ||
1078 | // Special case: the caller is just trying to extract a substring, so do it quickly. | ||
1079 | int xBefore = atomLength - (int)After - (int)Length; | ||
1080 | if (xBefore >= 0) | ||
1081 | { | ||
1082 | foreach (bool l1 in YP.unify(Before, xBefore)) | ||
1083 | { | ||
1084 | foreach (bool l2 in YP.unify | ||
1085 | (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, (int)Length)))) | ||
1086 | yield return false; | ||
1087 | } | ||
1088 | } | ||
1089 | } | ||
1090 | else | ||
1091 | { | ||
1092 | // We are underconstrained and doing a search, so go through all possibilities. | ||
1093 | for (int xBefore = 0; xBefore <= atomLength; ++xBefore) | ||
1094 | { | ||
1095 | foreach (bool l1 in YP.unify(Before, xBefore)) | ||
1096 | { | ||
1097 | for (int xLength = 0; xLength <= (atomLength - xBefore); ++xLength) | ||
1098 | { | ||
1099 | foreach (bool l2 in YP.unify(Length, xLength)) | ||
1100 | { | ||
1101 | foreach (bool l3 in YP.unify(After, atomLength - (xBefore + xLength))) | ||
1102 | { | ||
1103 | foreach (bool l4 in YP.unify | ||
1104 | (Sub_atom, Atom.a(atomAtom._name.Substring(xBefore, xLength)))) | ||
1105 | yield return false; | ||
1106 | } | ||
1107 | } | ||
1108 | } | ||
1109 | } | ||
874 | } | 1110 | } |
875 | #pragma warning restore 0168 | 1111 | } |
1112 | } | ||
1113 | |||
1114 | public static IEnumerable<bool> atom_chars(object atom, object List) | ||
1115 | { | ||
1116 | atom = YP.getValue(atom); | ||
1117 | List = YP.getValue(List); | ||
1118 | |||
1119 | if (atom is Variable) | ||
1120 | { | ||
1121 | if (List is Variable) | ||
1122 | throw new PrologException(Atom.a("instantiation_error"), | ||
1123 | "Arg 1 Atom and arg 2 List are both unbound variables"); | ||
1124 | object[] codeArray = ListPair.toArray(List); | ||
1125 | if (codeArray == null) | ||
1126 | throw new PrologException | ||
1127 | (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list"); | ||
1128 | |||
1129 | char[] charArray = new char[codeArray.Length]; | ||
1130 | for (int i = 0; i < codeArray.Length; ++i) | ||
1131 | { | ||
1132 | object listAtom = YP.getValue(codeArray[i]); | ||
1133 | if (listAtom is Variable) | ||
1134 | throw new PrologException(Atom.a("instantiation_error"), | ||
1135 | "Arg 2 List has an element which is an unbound variable"); | ||
1136 | if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1)) | ||
1137 | throw new PrologException | ||
1138 | (new Functor2("type_error", Atom.a("character"), listAtom), | ||
1139 | "Arg 2 List has an element which is not a one character atom"); | ||
1140 | charArray[i] = ((Atom)listAtom)._name[0]; | ||
1141 | } | ||
1142 | return YP.unify(atom, Atom.a(new String(charArray))); | ||
1143 | } | ||
1144 | else | ||
1145 | { | ||
1146 | if (!(atom is Atom)) | ||
1147 | throw new PrologException | ||
1148 | (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom"); | ||
1149 | |||
1150 | string atomString = ((Atom)atom)._name; | ||
1151 | object charList = Atom.NIL; | ||
1152 | // Start from the back to make the list. | ||
1153 | for (int i = atomString.Length - 1; i >= 0; --i) | ||
1154 | charList = new ListPair(Atom.a(atomString.Substring(i, 1)), charList); | ||
1155 | return YP.unify(List, charList); | ||
876 | } | 1156 | } |
877 | } | 1157 | } |
878 | 1158 | ||
@@ -881,38 +1161,141 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
881 | atom = YP.getValue(atom); | 1161 | atom = YP.getValue(atom); |
882 | List = YP.getValue(List); | 1162 | List = YP.getValue(List); |
883 | 1163 | ||
884 | if (nonvar(atom)) | 1164 | if (atom is Variable) |
885 | { | 1165 | { |
886 | string name = ((Atom)atom)._name; | 1166 | if (List is Variable) |
1167 | throw new PrologException(Atom.a("instantiation_error"), | ||
1168 | "Arg 1 Atom and arg 2 List are both unbound variables"); | ||
1169 | object[] codeArray = ListPair.toArray(List); | ||
1170 | if (codeArray == null) | ||
1171 | throw new PrologException | ||
1172 | (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list"); | ||
1173 | |||
1174 | char[] charArray = new char[codeArray.Length]; | ||
1175 | for (int i = 0; i < codeArray.Length; ++i) | ||
1176 | { | ||
1177 | int codeInt; | ||
1178 | if (!getInt(codeArray[i], out codeInt) || codeInt < 0) | ||
1179 | throw new PrologException | ||
1180 | (new Functor1("representation_error", Atom.a("character_code")), | ||
1181 | "Element of Arg 2 List is not a character code"); | ||
1182 | charArray[i] = (char)codeInt; | ||
1183 | } | ||
1184 | return YP.unify(atom, Atom.a(new String(charArray))); | ||
1185 | } | ||
1186 | else | ||
1187 | { | ||
1188 | if (!(atom is Atom)) | ||
1189 | throw new PrologException | ||
1190 | (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not var or atom"); | ||
1191 | |||
1192 | string atomString = ((Atom)atom)._name; | ||
887 | object codeList = Atom.NIL; | 1193 | object codeList = Atom.NIL; |
888 | // Start from the back to make the list. | 1194 | // Start from the back to make the list. |
889 | for (int i = name.Length - 1; i >= 0; --i) | 1195 | for (int i = atomString.Length - 1; i >= 0; --i) |
890 | codeList = new ListPair((int)name[i], codeList); | 1196 | codeList = new ListPair((int)atomString[i], codeList); |
891 | return YP.unify(List, codeList); | 1197 | return YP.unify(List, codeList); |
892 | } | 1198 | } |
1199 | } | ||
1200 | |||
1201 | public static IEnumerable<bool> number_chars(object Number, object List) | ||
1202 | { | ||
1203 | Number = YP.getValue(Number); | ||
1204 | List = YP.getValue(List); | ||
1205 | |||
1206 | if (Number is Variable) | ||
893 | { | 1207 | { |
1208 | if (List is Variable) | ||
1209 | throw new PrologException(Atom.a("instantiation_error"), | ||
1210 | "Arg 1 Number and arg 2 List are both unbound variables"); | ||
894 | object[] codeArray = ListPair.toArray(List); | 1211 | object[] codeArray = ListPair.toArray(List); |
1212 | if (codeArray == null) | ||
1213 | throw new PrologException | ||
1214 | (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list"); | ||
1215 | |||
895 | char[] charArray = new char[codeArray.Length]; | 1216 | char[] charArray = new char[codeArray.Length]; |
896 | for (int i = 0; i < codeArray.Length; ++i) | 1217 | for (int i = 0; i < codeArray.Length; ++i) |
897 | charArray[i] = (char)YP.convertInt(codeArray[i]); | 1218 | { |
898 | return YP.unify(atom, Atom.a(new String(charArray))); | 1219 | object listAtom = YP.getValue(codeArray[i]); |
1220 | if (listAtom is Variable) | ||
1221 | throw new PrologException(Atom.a("instantiation_error"), | ||
1222 | "Arg 2 List has an element which is an unbound variable"); | ||
1223 | if (!(listAtom is Atom && ((Atom)listAtom)._name.Length == 1)) | ||
1224 | throw new PrologException | ||
1225 | (new Functor2("type_error", Atom.a("character"), listAtom), | ||
1226 | "Arg 2 List has an element which is not a one character atom"); | ||
1227 | charArray[i] = ((Atom)listAtom)._name[0]; | ||
1228 | } | ||
1229 | return YP.unify(Number, parseNumberString(charArray)); | ||
1230 | } | ||
1231 | else | ||
1232 | { | ||
1233 | string numberString = null; | ||
1234 | // Try converting to an int first. | ||
1235 | int intNumber; | ||
1236 | if (YP.getInt(Number, out intNumber)) | ||
1237 | numberString = intNumber.ToString(); | ||
1238 | else | ||
1239 | { | ||
1240 | if (!YP.number(Number)) | ||
1241 | throw new PrologException | ||
1242 | (new Functor2("type_error", Atom.a("number"), Number), | ||
1243 | "Arg 1 Number is not var or number"); | ||
1244 | // We just checked, so convertDouble shouldn't throw an exception. | ||
1245 | numberString = YP.doubleToString(YP.convertDouble(Number)); | ||
1246 | } | ||
1247 | |||
1248 | object charList = Atom.NIL; | ||
1249 | // Start from the back to make the list. | ||
1250 | for (int i = numberString.Length - 1; i >= 0; --i) | ||
1251 | charList = new ListPair(Atom.a(numberString.Substring(i, 1)), charList); | ||
1252 | return YP.unify(List, charList); | ||
899 | } | 1253 | } |
900 | } | 1254 | } |
901 | 1255 | ||
902 | public static IEnumerable<bool> number_codes(object number, object List) | 1256 | public static IEnumerable<bool> number_codes(object Number, object List) |
903 | { | 1257 | { |
904 | number = YP.getValue(number); | 1258 | Number = YP.getValue(Number); |
905 | List = YP.getValue(List); | 1259 | List = YP.getValue(List); |
906 | 1260 | ||
907 | if (nonvar(number)) | 1261 | if (Number is Variable) |
1262 | { | ||
1263 | if (List is Variable) | ||
1264 | throw new PrologException(Atom.a("instantiation_error"), | ||
1265 | "Arg 1 Number and arg 2 List are both unbound variables"); | ||
1266 | object[] codeArray = ListPair.toArray(List); | ||
1267 | if (codeArray == null) | ||
1268 | throw new PrologException | ||
1269 | (new Functor2("type_error", Atom.a("list"), List), "Arg 2 List is not a list"); | ||
1270 | |||
1271 | char[] charArray = new char[codeArray.Length]; | ||
1272 | for (int i = 0; i < codeArray.Length; ++i) | ||
1273 | { | ||
1274 | int codeInt; | ||
1275 | if (!getInt(codeArray[i], out codeInt) || codeInt < 0) | ||
1276 | throw new PrologException | ||
1277 | (new Functor1("representation_error", Atom.a("character_code")), | ||
1278 | "Element of Arg 2 List is not a character code"); | ||
1279 | charArray[i] = (char)codeInt; | ||
1280 | } | ||
1281 | return YP.unify(Number, parseNumberString(charArray)); | ||
1282 | } | ||
1283 | else | ||
908 | { | 1284 | { |
909 | string numberString = null; | 1285 | string numberString = null; |
910 | // Try converting to an int first. | 1286 | // Try converting to an int first. |
911 | int intNumber; | 1287 | int intNumber; |
912 | if (YP.getInt(number, out intNumber)) | 1288 | if (YP.getInt(Number, out intNumber)) |
913 | numberString = intNumber.ToString(); | 1289 | numberString = intNumber.ToString(); |
914 | else | 1290 | else |
915 | numberString = YP.doubleToString(YP.convertDouble(number)); | 1291 | { |
1292 | if (!YP.number(Number)) | ||
1293 | throw new PrologException | ||
1294 | (new Functor2("type_error", Atom.a("number"), Number), | ||
1295 | "Arg 1 Number is not var or number"); | ||
1296 | // We just checked, so convertDouble shouldn't throw an exception. | ||
1297 | numberString = YP.doubleToString(YP.convertDouble(Number)); | ||
1298 | } | ||
916 | 1299 | ||
917 | object codeList = Atom.NIL; | 1300 | object codeList = Atom.NIL; |
918 | // Start from the back to make the list. | 1301 | // Start from the back to make the list. |
@@ -920,20 +1303,92 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
920 | codeList = new ListPair((int)numberString[i], codeList); | 1303 | codeList = new ListPair((int)numberString[i], codeList); |
921 | return YP.unify(List, codeList); | 1304 | return YP.unify(List, codeList); |
922 | } | 1305 | } |
1306 | } | ||
1307 | |||
1308 | /// <summary> | ||
1309 | /// Used by number_chars and number_codes. Return the number in charArray or | ||
1310 | /// throw an exception if can't parse. | ||
1311 | /// </summary> | ||
1312 | /// <param name="numberString"></param> | ||
1313 | /// <returns></returns> | ||
1314 | private static object parseNumberString(char[] charArray) | ||
1315 | { | ||
1316 | string numberString = new String(charArray); | ||
1317 | if (charArray.Length == 3 && numberString.StartsWith("0'")) | ||
1318 | // This is a char code. | ||
1319 | return (int)charArray[2]; | ||
1320 | if (numberString.StartsWith("0x")) | ||
923 | { | 1321 | { |
924 | object[] codeArray = ListPair.toArray(List); | ||
925 | char[] charArray = new char[codeArray.Length]; | ||
926 | for (int i = 0; i < codeArray.Length; ++i) | ||
927 | charArray[i] = (char)YP.convertInt(codeArray[i]); | ||
928 | String numberString = new String(charArray); | ||
929 | // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception? | ||
930 | try | 1322 | try |
931 | { | 1323 | { |
932 | // Try an int first. | 1324 | return Int32.Parse |
933 | return YP.unify(number, Convert.ToInt32(numberString)); | 1325 | (numberString.Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier); |
934 | } | 1326 | } |
935 | catch (FormatException) { } | 1327 | catch (FormatException) |
936 | return YP.unify(number, Convert.ToDouble(numberString)); | 1328 | { |
1329 | throw new PrologException | ||
1330 | (new Functor1("syntax_error", Atom.a("number_format: " + numberString)), | ||
1331 | "Arg 2 List is not a list for a hexadecimal number"); | ||
1332 | } | ||
1333 | } | ||
1334 | // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception? | ||
1335 | try | ||
1336 | { | ||
1337 | // Try an int first. | ||
1338 | return Convert.ToInt32(numberString); | ||
1339 | } | ||
1340 | catch (FormatException) { } | ||
1341 | try | ||
1342 | { | ||
1343 | return Convert.ToDouble(numberString); | ||
1344 | } | ||
1345 | catch (FormatException) | ||
1346 | { | ||
1347 | throw new PrologException | ||
1348 | (new Functor1("syntax_error", Atom.a("number_format: " + numberString)), | ||
1349 | "Arg 2 List is not a list for a number"); | ||
1350 | } | ||
1351 | } | ||
1352 | |||
1353 | public static IEnumerable<bool> char_code(object Char, object Code) | ||
1354 | { | ||
1355 | Char = YP.getValue(Char); | ||
1356 | Code = YP.getValue(Code); | ||
1357 | |||
1358 | int codeInt = 0; | ||
1359 | if (!(Code is Variable)) | ||
1360 | { | ||
1361 | // Get codeInt now so we type check it whether or not Char is Variable. | ||
1362 | if (!getInt(Code, out codeInt)) | ||
1363 | throw new PrologException | ||
1364 | (new Functor2("type_error", Atom.a("integer"), Code), | ||
1365 | "Arg 2 Code is not var or a character code"); | ||
1366 | if (codeInt < 0) | ||
1367 | throw new PrologException | ||
1368 | (new Functor1("representation_error", Atom.a("character_code")), | ||
1369 | "Arg 2 Code is not a character code"); | ||
1370 | } | ||
1371 | |||
1372 | if (Char is Variable) | ||
1373 | { | ||
1374 | if (Code is Variable) | ||
1375 | throw new PrologException(Atom.a("instantiation_error"), | ||
1376 | "Arg 1 Char and arg 2 Code are both unbound variables"); | ||
1377 | |||
1378 | return YP.unify(Char, Atom.a(new String(new char[] {(char)codeInt} ))); | ||
1379 | } | ||
1380 | else | ||
1381 | { | ||
1382 | if (!(Char is Atom) || ((Atom)Char)._name.Length != 1) | ||
1383 | throw new PrologException | ||
1384 | (new Functor2("type_error", Atom.a("character"), Char), | ||
1385 | "Arg 1 Char is not var or one-character atom"); | ||
1386 | |||
1387 | if (Code is Variable) | ||
1388 | return YP.unify(Code, (int)((Atom)Char)._name[0]); | ||
1389 | else | ||
1390 | // Use codeInt to handle whether Code is supplied as, e.g., 97 or 0'a . | ||
1391 | return YP.unify(codeInt, (int)((Atom)Char)._name[0]); | ||
937 | } | 1392 | } |
938 | } | 1393 | } |
939 | 1394 | ||
@@ -1109,7 +1564,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1109 | } | 1564 | } |
1110 | 1565 | ||
1111 | /// <summary> | 1566 | /// <summary> |
1112 | /// Format x as a string, making sure that it will parse as an int later. I.e., for 1.0, don't just | 1567 | /// Format x as a string, making sure that it won't parse as an int later. I.e., for 1.0, don't just |
1113 | /// use "1" which will parse as an int. | 1568 | /// use "1" which will parse as an int. |
1114 | /// </summary> | 1569 | /// </summary> |
1115 | /// <param name="x"></param> | 1570 | /// <param name="x"></param> |
@@ -1134,7 +1589,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1134 | 1589 | ||
1135 | public static void put_code(object x) | 1590 | public static void put_code(object x) |
1136 | { | 1591 | { |
1137 | _outputStream.Write((char)YP.convertInt(x)); | 1592 | if (var(x)) |
1593 | throw new PrologException(Atom.a("instantiation_error"), "Arg 1 is an unbound variable"); | ||
1594 | int xInt; | ||
1595 | if (!getInt(x, out xInt)) | ||
1596 | throw new PrologException | ||
1597 | (new Functor2("type_error", Atom.a("integer"), x), "Arg 1 must be integer"); | ||
1598 | _outputStream.Write((char)xInt); | ||
1138 | } | 1599 | } |
1139 | 1600 | ||
1140 | public static void nl() | 1601 | public static void nl() |
@@ -1170,6 +1631,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1170 | { | 1631 | { |
1171 | Head = YP.getValue(((Functor2)TermCopy)._arg1); | 1632 | Head = YP.getValue(((Functor2)TermCopy)._arg1); |
1172 | Body = YP.getValue(((Functor2)TermCopy)._arg2); | 1633 | Body = YP.getValue(((Functor2)TermCopy)._arg2); |
1634 | if (Head is Variable) | ||
1635 | throw new PrologException("instantiation_error", "Head to assert is an unbound variable"); | ||
1636 | if (Body is Variable) | ||
1637 | throw new PrologException("instantiation_error", "Body to assert is an unbound variable"); | ||
1173 | } | 1638 | } |
1174 | else | 1639 | else |
1175 | { | 1640 | { |
@@ -1183,7 +1648,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1183 | throw new PrologException | 1648 | throw new PrologException |
1184 | (new Functor2("type_error", Atom.a("callable"), Head), "Term to assert is not callable"); | 1649 | (new Functor2("type_error", Atom.a("callable"), Head), "Term to assert is not callable"); |
1185 | object[] args = getFunctorArgs(Head); | 1650 | object[] args = getFunctorArgs(Head); |
1186 | if (!isDynamic(name, args.Length)) | 1651 | if (isSystemPredicate(name, args.Length)) |
1187 | throw new PrologException | 1652 | throw new PrologException |
1188 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), | 1653 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), |
1189 | new Functor2(Atom.SLASH, name, args.Length)), | 1654 | new Functor2(Atom.SLASH, name, args.Length)), |
@@ -1191,17 +1656,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1191 | 1656 | ||
1192 | if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true")) | 1657 | if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true")) |
1193 | { | 1658 | { |
1194 | // Debug: Until IndexedAnswers supports prepend, compile the fact so we can prepend it below. | 1659 | // This is a fact with no unbound variables |
1195 | if (!prepend) | 1660 | // assertFact and prependFact use IndexedAnswers, so don't we don't need to compile. |
1196 | { | 1661 | if (prepend) |
1197 | // This is a fact with no unbound variables | 1662 | prependFact(name, args); |
1198 | // assertFact uses IndexedAnswers, so don't we don't need to compile. | 1663 | else |
1199 | assertFact(name, args); | 1664 | assertFact(name, args); |
1200 | return; | 1665 | |
1201 | } | 1666 | return; |
1202 | } | 1667 | } |
1203 | 1668 | ||
1204 | IClause clause = YPCompiler.compileAnonymousClause(Head, Body, declaringClass); | 1669 | IClause clause = YPCompiler.compileAnonymousClause(Head, Body, declaringClass); |
1670 | // We expect clause to be a ClauseHeadAndBody (from Compiler.compileAnonymousFunction) | ||
1671 | // so we can set the Head and Body. | ||
1672 | if (clause is ClauseHeadAndBody) | ||
1673 | ((ClauseHeadAndBody)clause).setHeadAndBody(Head, Body); | ||
1205 | 1674 | ||
1206 | // Add the clause to the entry in _predicatesStore. | 1675 | // Add the clause to the entry in _predicatesStore. |
1207 | NameArity nameArity = new NameArity(name, args.Length); | 1676 | NameArity nameArity = new NameArity(name, args.Length); |
@@ -1216,19 +1685,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1216 | clauses.Add(clause); | 1685 | clauses.Add(clause); |
1217 | } | 1686 | } |
1218 | 1687 | ||
1219 | private static bool isDynamic(Atom name, int arity) | 1688 | private static bool isSystemPredicate(Atom name, int arity) |
1220 | { | 1689 | { |
1221 | if (arity == 2 && (name == Atom.a(",") || name == Atom.a(";") || name == Atom.DOT)) | 1690 | if (arity == 2 && (name == Atom.a(",") || name == Atom.a(";") || name == Atom.DOT)) |
1222 | return false; | 1691 | return true; |
1223 | // Use the same mapping to static predicates in YP as the compiler. | 1692 | // Use the same mapping to static predicates in YP as the compiler. |
1224 | // disable warning: don't see how we can code this differently short | ||
1225 | // of rewriting the whole thing | ||
1226 | #pragma warning disable 0168 | ||
1227 | foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable())) | 1693 | foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable())) |
1228 | return false; | 1694 | return true; |
1229 | // Debug: Do we need to check if name._module is null? | 1695 | // Debug: Do we need to check if name._module is null? |
1230 | #pragma warning restore 0168 | 1696 | return false; |
1231 | return true; | ||
1232 | } | 1697 | } |
1233 | 1698 | ||
1234 | /// <summary> | 1699 | /// <summary> |
@@ -1245,22 +1710,55 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1245 | IndexedAnswers indexedAnswers; | 1710 | IndexedAnswers indexedAnswers; |
1246 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) | 1711 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) |
1247 | { | 1712 | { |
1248 | // Create an IndexedAnswers as the first clause of the predicate. | 1713 | // Create an IndexedAnswers as the only clause of the predicate. |
1249 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); | 1714 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); |
1250 | clauses.Add(indexedAnswers = new IndexedAnswers()); | 1715 | clauses.Add(indexedAnswers = new IndexedAnswers(values.Length)); |
1251 | } | 1716 | } |
1252 | else | 1717 | else |
1253 | { | 1718 | { |
1254 | indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers; | 1719 | indexedAnswers = null; |
1720 | if (clauses.Count >= 1) | ||
1721 | indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers; | ||
1255 | if (indexedAnswers == null) | 1722 | if (indexedAnswers == null) |
1256 | // The latest clause is not an IndexedAnswers, so add one. | 1723 | // The latest clause is not an IndexedAnswers, so add one. |
1257 | clauses.Add(indexedAnswers = new IndexedAnswers()); | 1724 | clauses.Add(indexedAnswers = new IndexedAnswers(values.Length)); |
1258 | } | 1725 | } |
1259 | 1726 | ||
1260 | indexedAnswers.addAnswer(values); | 1727 | indexedAnswers.addAnswer(values); |
1261 | } | 1728 | } |
1262 | 1729 | ||
1263 | /// <summary> | 1730 | /// <summary> |
1731 | /// Assert values, prepending to the front of the set of facts for the predicate with the | ||
1732 | /// name and with arity values.Length. | ||
1733 | /// </summary> | ||
1734 | /// <param name="name">must be an Atom</param> | ||
1735 | /// <param name="values">the array of arguments to the fact predicate. | ||
1736 | /// It is an error if an value has an unbound variable.</param> | ||
1737 | public static void prependFact(Atom name, object[] values) | ||
1738 | { | ||
1739 | NameArity nameArity = new NameArity(name, values.Length); | ||
1740 | List<IClause> clauses; | ||
1741 | IndexedAnswers indexedAnswers; | ||
1742 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) | ||
1743 | { | ||
1744 | // Create an IndexedAnswers as the only clause of the predicate. | ||
1745 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); | ||
1746 | clauses.Add(indexedAnswers = new IndexedAnswers(values.Length)); | ||
1747 | } | ||
1748 | else | ||
1749 | { | ||
1750 | indexedAnswers = null; | ||
1751 | if (clauses.Count >= 1) | ||
1752 | indexedAnswers = clauses[0] as IndexedAnswers; | ||
1753 | if (indexedAnswers == null) | ||
1754 | // The first clause is not an IndexedAnswers, so prepend one. | ||
1755 | clauses.Insert(0, indexedAnswers = new IndexedAnswers(values.Length)); | ||
1756 | } | ||
1757 | |||
1758 | indexedAnswers.prependAnswer(values); | ||
1759 | } | ||
1760 | |||
1761 | /// <summary> | ||
1264 | /// Match all clauses of the dynamic predicate with the name and with arity | 1762 | /// Match all clauses of the dynamic predicate with the name and with arity |
1265 | /// arguments.Length. | 1763 | /// arguments.Length. |
1266 | /// It is an error if the predicate is not defined. | 1764 | /// It is an error if the predicate is not defined. |
@@ -1272,9 +1770,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1272 | { | 1770 | { |
1273 | List<IClause> clauses; | 1771 | List<IClause> clauses; |
1274 | if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses)) | 1772 | if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses)) |
1275 | throw new UndefinedPredicateException | 1773 | throw new PrologException |
1276 | ("Undefined fact: " + name + "/" + arguments.Length, name, | 1774 | (new Functor2 |
1277 | arguments.Length); | 1775 | (Atom.a("existence_error"), Atom.a("procedure"), |
1776 | new Functor2(Atom.SLASH, name, arguments.Length)), | ||
1777 | "Undefined predicate: " + name + "/" + arguments.Length); | ||
1278 | 1778 | ||
1279 | if (clauses.Count == 1) | 1779 | if (clauses.Count == 1) |
1280 | // Usually there is only one clause, so return it without needing to wrap it in an iterator. | 1780 | // Usually there is only one clause, so return it without needing to wrap it in an iterator. |
@@ -1292,7 +1792,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1292 | /// <returns></returns> | 1792 | /// <returns></returns> |
1293 | private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments) | 1793 | private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments) |
1294 | { | 1794 | { |
1295 | // Debug: If the clause asserts another clause into this same predicate, the iterator | 1795 | // Debug: If the caller asserts another clause into this same predicate during yield, the iterator |
1296 | // over clauses will be corrupted. Should we take the time to copy clauses? | 1796 | // over clauses will be corrupted. Should we take the time to copy clauses? |
1297 | foreach (IClause clause in clauses) | 1797 | foreach (IClause clause in clauses) |
1298 | { | 1798 | { |
@@ -1318,16 +1818,122 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1318 | return matchDynamic(name, arguments); | 1818 | return matchDynamic(name, arguments); |
1319 | } | 1819 | } |
1320 | 1820 | ||
1821 | public static IEnumerable<bool> clause(object Head, object Body) | ||
1822 | { | ||
1823 | Head = getValue(Head); | ||
1824 | Body = getValue(Body); | ||
1825 | if (Head is Variable) | ||
1826 | throw new PrologException("instantiation_error", "Head is an unbound variable"); | ||
1827 | |||
1828 | Atom name = getFunctorName(Head) as Atom; | ||
1829 | if (name == null) | ||
1830 | // name is a non-Atom, such as a number. | ||
1831 | throw new PrologException | ||
1832 | (new Functor2("type_error", Atom.a("callable"), Head), "Head is not callable"); | ||
1833 | object[] args = getFunctorArgs(Head); | ||
1834 | if (isSystemPredicate(name, args.Length)) | ||
1835 | throw new PrologException | ||
1836 | (new Functor3("permission_error", Atom.a("access"), Atom.a("private_procedure"), | ||
1837 | new Functor2(Atom.SLASH, name, args.Length)), | ||
1838 | "clause cannot access private predicate " + name + "/" + args.Length); | ||
1839 | if (!(Body is Variable) && !(YP.getFunctorName(Body) is Atom)) | ||
1840 | throw new PrologException | ||
1841 | (new Functor2("type_error", Atom.a("callable"), Body), "Body is not callable"); | ||
1842 | |||
1843 | List<IClause> clauses; | ||
1844 | if (!_predicatesStore.TryGetValue(new NameArity(name, args.Length), out clauses)) | ||
1845 | yield break; | ||
1846 | // The caller can assert another clause into this same predicate during yield, so we have to | ||
1847 | // make a copy of the clauses. | ||
1848 | foreach (IClause predicateClause in clauses.ToArray()) | ||
1849 | { | ||
1850 | foreach (bool l1 in predicateClause.clause(Head, Body)) | ||
1851 | yield return false; | ||
1852 | } | ||
1853 | } | ||
1854 | |||
1855 | public static IEnumerable<bool> retract(object Term) | ||
1856 | { | ||
1857 | Term = getValue(Term); | ||
1858 | if (Term is Variable) | ||
1859 | throw new PrologException("instantiation_error", "Term to retract is an unbound variable"); | ||
1860 | |||
1861 | object Head, Body; | ||
1862 | if (Term is Functor2 && ((Functor2)Term)._name == Atom.RULE) | ||
1863 | { | ||
1864 | Head = YP.getValue(((Functor2)Term)._arg1); | ||
1865 | Body = YP.getValue(((Functor2)Term)._arg2); | ||
1866 | } | ||
1867 | else | ||
1868 | { | ||
1869 | Head = Term; | ||
1870 | Body = Atom.a("true"); | ||
1871 | } | ||
1872 | if (Head is Variable) | ||
1873 | throw new PrologException("instantiation_error", "Head is an unbound variable"); | ||
1874 | |||
1875 | Atom name = getFunctorName(Head) as Atom; | ||
1876 | if (name == null) | ||
1877 | // name is a non-Atom, such as a number. | ||
1878 | throw new PrologException | ||
1879 | (new Functor2("type_error", Atom.a("callable"), Head), "Head is not callable"); | ||
1880 | object[] args = getFunctorArgs(Head); | ||
1881 | if (isSystemPredicate(name, args.Length)) | ||
1882 | throw new PrologException | ||
1883 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), | ||
1884 | new Functor2(Atom.SLASH, name, args.Length)), | ||
1885 | "clause cannot access private predicate " + name + "/" + args.Length); | ||
1886 | if (!(Body is Variable) && !(YP.getFunctorName(Body) is Atom)) | ||
1887 | throw new PrologException | ||
1888 | (new Functor2("type_error", Atom.a("callable"), Body), "Body is not callable"); | ||
1889 | |||
1890 | List<IClause> clauses; | ||
1891 | if (!_predicatesStore.TryGetValue(new NameArity(name, args.Length), out clauses)) | ||
1892 | yield break; | ||
1893 | // The caller can assert another clause into this same predicate during yield, so we have to | ||
1894 | // make a copy of the clauses. | ||
1895 | foreach (IClause predicateClause in clauses.ToArray()) | ||
1896 | { | ||
1897 | if (predicateClause is IndexedAnswers) | ||
1898 | { | ||
1899 | // IndexedAnswers handles its own retract. Even if it removes all of its | ||
1900 | // answers, it is OK to leave it empty as one of the elements in clauses. | ||
1901 | foreach (bool l1 in ((IndexedAnswers)predicateClause).retract(Head, Body)) | ||
1902 | yield return false; | ||
1903 | } | ||
1904 | else | ||
1905 | { | ||
1906 | foreach (bool l1 in predicateClause.clause(Head, Body)) | ||
1907 | { | ||
1908 | clauses.Remove(predicateClause); | ||
1909 | yield return false; | ||
1910 | } | ||
1911 | } | ||
1912 | } | ||
1913 | } | ||
1914 | |||
1321 | /// <summary> | 1915 | /// <summary> |
1322 | /// This actually searches all clauses, not just | 1916 | /// This is deprecated for backward compatibility. You should use retractall. |
1323 | /// the ones defined with assertFact, but we keep the name for | ||
1324 | /// backwards compatibility. | ||
1325 | /// </summary> | 1917 | /// </summary> |
1326 | /// <param name="name">must be an Atom</param> | 1918 | /// <param name="name">must be an Atom</param> |
1327 | /// <param name="arguments">an array of arity number of arguments</param> | 1919 | /// <param name="arguments">an array of arity number of arguments</param> |
1328 | public static void retractFact(Atom name, object[] arguments) | 1920 | public static void retractFact(Atom name, object[] arguments) |
1329 | { | 1921 | { |
1330 | NameArity nameArity = new NameArity(name, arguments.Length); | 1922 | retractall(Functor.make(name, arguments)); |
1923 | } | ||
1924 | |||
1925 | /// <summary> | ||
1926 | /// Retract all dynamic clauses which unify with Head. If this matches all clauses in a predicate, | ||
1927 | /// the predicate is still defined. To completely remove the predicate, see abolish. | ||
1928 | /// </summary> | ||
1929 | /// <param name="Head"></param> | ||
1930 | public static void retractall(object Head) | ||
1931 | { | ||
1932 | object name = YP.getFunctorName(Head); | ||
1933 | object[] arguments = getFunctorArgs(Head); | ||
1934 | if (!(name is Atom)) | ||
1935 | return; | ||
1936 | NameArity nameArity = new NameArity((Atom)name, arguments.Length); | ||
1331 | List<IClause> clauses; | 1937 | List<IClause> clauses; |
1332 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) | 1938 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) |
1333 | // Can't find, so ignore. | 1939 | // Can't find, so ignore. |
@@ -1336,11 +1942,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1336 | foreach (object arg in arguments) | 1942 | foreach (object arg in arguments) |
1337 | { | 1943 | { |
1338 | if (!YP.var(arg)) | 1944 | if (!YP.var(arg)) |
1339 | throw new InvalidOperationException("All arguments must be unbound"); | 1945 | throw new InvalidOperationException |
1946 | ("Until matching retractall is supported, all arguments must be unbound to retract all clauses"); | ||
1340 | } | 1947 | } |
1341 | // Set to a fresh empty IndexedAnswers. | 1948 | // Clear all clauses. |
1342 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); | 1949 | _predicatesStore[nameArity] = new List<IClause>(); |
1343 | clauses.Add(new IndexedAnswers()); | ||
1344 | } | 1950 | } |
1345 | 1951 | ||
1346 | public static IEnumerable<bool> current_predicate(object NameSlashArity) | 1952 | public static IEnumerable<bool> current_predicate(object NameSlashArity) |
@@ -1349,61 +1955,99 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1349 | // First check if Name and Arity are nonvar so we can do a direct lookup. | 1955 | // First check if Name and Arity are nonvar so we can do a direct lookup. |
1350 | if (YP.ground(NameSlashArity)) | 1956 | if (YP.ground(NameSlashArity)) |
1351 | { | 1957 | { |
1352 | if (NameSlashArity is Functor2) | 1958 | Functor2 NameArityFunctor = NameSlashArity as Functor2; |
1959 | if (!(NameArityFunctor != null && NameArityFunctor._name == Atom.SLASH)) | ||
1960 | throw new PrologException | ||
1961 | (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity), | ||
1962 | "Must be a name/arity predicate indicator"); | ||
1963 | object name = YP.getValue(NameArityFunctor._arg1); | ||
1964 | object arity = YP.getValue(NameArityFunctor._arg2); | ||
1965 | if (name is Variable || arity is Variable) | ||
1966 | throw new PrologException | ||
1967 | ("instantiation_error", "Predicate indicator name or arity is an unbound variable"); | ||
1968 | if (!(name is Atom && arity is int)) | ||
1969 | throw new PrologException | ||
1970 | (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity), | ||
1971 | "Must be a name/arity predicate indicator"); | ||
1972 | if ((int)arity < 0) | ||
1973 | throw new PrologException | ||
1974 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), arity), | ||
1975 | "Arity may not be less than zero"); | ||
1976 | |||
1977 | if (_predicatesStore.ContainsKey(new NameArity((Atom)name, (int)arity))) | ||
1978 | // The predicate is defined. | ||
1979 | yield return false; | ||
1980 | } | ||
1981 | else | ||
1982 | { | ||
1983 | foreach (NameArity key in _predicatesStore.Keys) | ||
1353 | { | 1984 | { |
1354 | Functor2 NameArityFunctor = (Functor2)NameSlashArity; | 1985 | foreach (bool l1 in YP.unify |
1355 | if (NameArityFunctor._name == Atom.SLASH) | 1986 | (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity)) |
1356 | { | 1987 | yield return false; |
1357 | if (_predicatesStore.ContainsKey(new NameArity | ||
1358 | ((Atom)YP.getValue(NameArityFunctor._arg1), | ||
1359 | (int)YP.getValue(NameArityFunctor._arg2)))) | ||
1360 | // The predicate is defined. | ||
1361 | yield return false; | ||
1362 | } | ||
1363 | } | 1988 | } |
1364 | yield break; | ||
1365 | } | 1989 | } |
1990 | } | ||
1366 | 1991 | ||
1367 | foreach (NameArity key in _predicatesStore.Keys) | 1992 | public static void abolish(object NameSlashArity) |
1368 | { | 1993 | { |
1369 | // disable warning: don't see how we can code this differently short | 1994 | NameSlashArity = YP.getValue(NameSlashArity); |
1370 | // of rewriting the whole thing | 1995 | if (NameSlashArity is Variable) |
1371 | #pragma warning disable 0168 | 1996 | throw new PrologException |
1372 | foreach (bool l1 in YP.unify | 1997 | ("instantiation_error", "Predicate indicator is an unbound variable"); |
1373 | (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity)) | 1998 | Functor2 NameArityFunctor = NameSlashArity as Functor2; |
1374 | yield return false; | 1999 | if (!(NameArityFunctor != null && NameArityFunctor._name == Atom.SLASH)) |
1375 | #pragma warning restore 0168 | 2000 | throw new PrologException |
1376 | } | 2001 | (new Functor2("type_error", Atom.a("predicate_indicator"), NameSlashArity), |
2002 | "Must be a name/arity predicate indicator"); | ||
2003 | object name = YP.getValue(NameArityFunctor._arg1); | ||
2004 | object arity = YP.getValue(NameArityFunctor._arg2); | ||
2005 | if (name is Variable || arity is Variable) | ||
2006 | throw new PrologException | ||
2007 | ("instantiation_error", "Predicate indicator name or arity is an unbound variable"); | ||
2008 | if (!(name is Atom)) | ||
2009 | throw new PrologException | ||
2010 | (new Functor2("type_error", Atom.a("atom"), name), | ||
2011 | "Predicate indicator name must be an atom"); | ||
2012 | if (!(arity is int)) | ||
2013 | throw new PrologException | ||
2014 | (new Functor2("type_error", Atom.a("integer"), arity), | ||
2015 | "Predicate indicator arity must be an integer"); | ||
2016 | if ((int)arity < 0) | ||
2017 | throw new PrologException | ||
2018 | (new Functor2("domain_error", Atom.a("not_less_than_zero"), arity), | ||
2019 | "Arity may not be less than zero"); | ||
2020 | |||
2021 | if (isSystemPredicate((Atom)name, (int)arity)) | ||
2022 | throw new PrologException | ||
2023 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), | ||
2024 | new Functor2(Atom.SLASH, name, arity)), | ||
2025 | "Abolish cannot modify static predicate " + name + "/" + arity); | ||
2026 | _predicatesStore.Remove(new NameArity((Atom)name, (int)arity)); | ||
1377 | } | 2027 | } |
1378 | 2028 | ||
1379 | /// <summary> | 2029 | /// <summary> |
1380 | /// Use YP.getFunctorName(Goal) and invoke the static method of this name in the | 2030 | /// If Goal is a simple predicate, call YP.getFunctorName(Goal) using arguments from |
1381 | /// declaringClass, using arguments from YP.getFunctorArgs(Goal). | 2031 | /// YP.getFunctorArgs(Goal). If not found, this throws a PrologException for existence_error. |
1382 | /// Note that Goal must be a simple functor, not a complex expression. | 2032 | /// Otherwise, compile the goal as a single clause predicate and invoke it. |
1383 | /// If not found, this throws UndefinedPredicateException. | ||
1384 | /// </summary> | 2033 | /// </summary> |
1385 | /// <param name="Goal"></param> | 2034 | /// <param name="Goal"></param> |
1386 | /// <param name="contextClass">the class for looking up default function references</param> | 2035 | /// <param name="declaringClass">if not null, used to resolve references to the default |
2036 | /// module Atom.a("")</param> | ||
1387 | /// <returns></returns> | 2037 | /// <returns></returns> |
1388 | public static IEnumerable<bool> getIterator(object Goal, Type declaringClass) | 2038 | public static IEnumerable<bool> getIterator(object Goal, Type declaringClass) |
1389 | { | 2039 | { |
1390 | Goal = YP.getValue(Goal); | ||
1391 | if (Goal is Variable) | ||
1392 | throw new PrologException("instantiation_error", "Goal to call is an unbound variable"); | ||
1393 | #if true | ||
1394 | List<Variable> variableSetList = new List<Variable>(); | ||
1395 | addUniqueVariables(Goal, variableSetList); | ||
1396 | Variable[] variableSet = variableSetList.ToArray(); | ||
1397 | |||
1398 | // Use Atom.F since it is ignored. | ||
1399 | return YPCompiler.compileAnonymousClause | ||
1400 | (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet); | ||
1401 | #else | ||
1402 | Atom name; | 2040 | Atom name; |
1403 | object[] args; | 2041 | object[] args; |
1404 | while (true) | 2042 | while (true) |
1405 | { | 2043 | { |
1406 | name = (Atom)YP.getFunctorName(Goal); | 2044 | Goal = YP.getValue(Goal); |
2045 | if (Goal is Variable) | ||
2046 | throw new PrologException("instantiation_error", "Goal to call is an unbound variable"); | ||
2047 | name = YP.getFunctorName(Goal) as Atom; | ||
2048 | if (name == null) | ||
2049 | throw new PrologException | ||
2050 | (new Functor2("type_error", Atom.a("callable"), Goal), "Goal to call is not callable"); | ||
1407 | args = YP.getFunctorArgs(Goal); | 2051 | args = YP.getFunctorArgs(Goal); |
1408 | if (name == Atom.HAT && args.Length == 2) | 2052 | if (name == Atom.HAT && args.Length == 2) |
1409 | // Assume this is called from a bagof operation. Skip the leading qualifiers. | 2053 | // Assume this is called from a bagof operation. Skip the leading qualifiers. |
@@ -1411,22 +2055,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1411 | else | 2055 | else |
1412 | break; | 2056 | break; |
1413 | } | 2057 | } |
1414 | try | 2058 | |
1415 | { | 2059 | IEnumerable<bool> simpleIterator = YPCompiler.getSimpleIterator(name, args, declaringClass); |
1416 | return (IEnumerable<bool>)declaringClass.InvokeMember | 2060 | if (simpleIterator != null) |
1417 | (name._name, BindingFlags.InvokeMethod, null, null, args); | 2061 | // We don't need to compile since the goal is a simple predicate which we call directly. |
1418 | } | 2062 | return simpleIterator; |
1419 | catch (TargetInvocationException exception) | 2063 | |
1420 | { | 2064 | // Compile the goal as a clause. |
1421 | throw exception.InnerException; | 2065 | List<Variable> variableSetList = new List<Variable>(); |
1422 | } | 2066 | addUniqueVariables(Goal, variableSetList); |
1423 | catch (MissingMethodException) | 2067 | Variable[] variableSet = variableSetList.ToArray(); |
1424 | { | 2068 | |
1425 | throw new UndefinedPredicateException | 2069 | // Use Atom.F since it is ignored. |
1426 | ("Cannot find predicate function: " + name + "/" + args.Length + " in " + | 2070 | return YPCompiler.compileAnonymousClause |
1427 | declaringClass.FullName, name, args.Length); | 2071 | (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet); |
1428 | } | ||
1429 | #endif | ||
1430 | } | 2072 | } |
1431 | 2073 | ||
1432 | public static void throwException(object Term) | 2074 | public static void throwException(object Term) |
@@ -1440,12 +2082,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1440 | /// <param name="script_event"></param> | 2082 | /// <param name="script_event"></param> |
1441 | /// <param name="script_params"></param> | 2083 | /// <param name="script_params"></param> |
1442 | /// <returns></returns> | 2084 | /// <returns></returns> |
1443 | public static void script_event(object script_event, object script_params) | 2085 | public static IEnumerable<bool> script_event(object script_event, object script_params) |
1444 | { | 2086 | { |
1445 | // string function = ((Atom)YP.getValue(script_event))._name; | 2087 | // string function = ((Atom)YP.getValue(script_event))._name; |
1446 | object[] array = ListPair.toArray(script_params); | 2088 | object[] array = ListPair.toArray(script_params); |
1447 | if (array == null) | 2089 | if (array == null) |
1448 | return; // YP.fail(); | 2090 | yield return false; // return; // YP.fail(); |
1449 | if (array.Length > 1) | 2091 | if (array.Length > 1) |
1450 | { | 2092 | { |
1451 | //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue | 2093 | //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue |
@@ -1453,8 +2095,76 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1453 | // sortArray(array); | 2095 | // sortArray(array); |
1454 | } | 2096 | } |
1455 | //return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array)); | 2097 | //return YP.unify(Sorted, ListPair.makeWithoutRepeatedTerms(array)); |
2098 | yield return false; | ||
2099 | } | ||
2100 | |||
2101 | /* Non-prolog-ish functions for inline coding */ | ||
2102 | public static string regexString(string inData, string inPattern, string presep,string postsep) | ||
2103 | { | ||
2104 | //string str=cycMessage; | ||
2105 | //string strMatch = @"\. \#\$(.*)\)"; | ||
2106 | string results = ""; | ||
2107 | for (Match m = Regex.Match(inData,inPattern); m.Success; m=m.NextMatch()) | ||
2108 | { | ||
2109 | //Console.WriteLine( m ); | ||
2110 | results += presep+ m + postsep; | ||
2111 | } | ||
2112 | return results; | ||
1456 | } | 2113 | } |
1457 | 2114 | ||
2115 | public static string cycComm(object msgobj) | ||
2116 | { | ||
2117 | string cycInputString = msgobj.ToString(); | ||
2118 | string cycOutputString=""; | ||
2119 | TcpClient socketForServer; | ||
2120 | |||
2121 | try | ||
2122 | { | ||
2123 | socketForServer = new TcpClient("localHost", 3601); | ||
2124 | } | ||
2125 | catch | ||
2126 | { | ||
2127 | Console.WriteLine("Failed to connect to server at {0}:999", "localhost"); | ||
2128 | return ""; | ||
2129 | } | ||
2130 | |||
2131 | NetworkStream networkStream = socketForServer.GetStream(); | ||
2132 | |||
2133 | System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream); | ||
2134 | |||
2135 | System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream); | ||
2136 | |||
2137 | try | ||
2138 | { | ||
2139 | // read the data from the host and display it | ||
2140 | |||
2141 | { | ||
2142 | |||
2143 | streamWriter.WriteLine(cycInputString); | ||
2144 | streamWriter.Flush(); | ||
2145 | |||
2146 | cycOutputString = streamReader.ReadLine(); | ||
2147 | Console.WriteLine("Cycoutput:" + cycOutputString); | ||
2148 | //streamWriter.WriteLine("Client Message"); | ||
2149 | //Console.WriteLine("Client Message"); | ||
2150 | streamWriter.Flush(); | ||
2151 | } | ||
2152 | |||
2153 | } | ||
2154 | catch | ||
2155 | { | ||
2156 | Console.WriteLine("Exception reading from Server"); | ||
2157 | return ""; | ||
2158 | } | ||
2159 | // tidy up | ||
2160 | networkStream.Close(); | ||
2161 | return cycOutputString; | ||
2162 | |||
2163 | } | ||
2164 | //public static void throwException(object Term) | ||
2165 | //{ | ||
2166 | // throw new PrologException(Term); | ||
2167 | //} | ||
1458 | /// <summary> | 2168 | /// <summary> |
1459 | /// An enumerator that does zero loops. | 2169 | /// An enumerator that does zero loops. |
1460 | /// </summary> | 2170 | /// </summary> |
@@ -1628,16 +2338,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1628 | if (_exception != null) | 2338 | if (_exception != null) |
1629 | { | 2339 | { |
1630 | bool didUnify = false; | 2340 | bool didUnify = false; |
1631 | // disable warning: don't see how we can code this differently short | ||
1632 | // of rewriting the whole thing | ||
1633 | #pragma warning disable 0168 | ||
1634 | foreach (bool l1 in YP.unify(_exception._term, Catcher)) | 2341 | foreach (bool l1 in YP.unify(_exception._term, Catcher)) |
1635 | { | 2342 | { |
1636 | didUnify = true; | 2343 | didUnify = true; |
1637 | yield return false; | 2344 | yield return false; |
1638 | } | 2345 | } |
1639 | #pragma warning restore 0168 | ||
1640 | |||
1641 | if (!didUnify) | 2346 | if (!didUnify) |
1642 | throw _exception; | 2347 | throw _exception; |
1643 | } | 2348 | } |
@@ -1673,5 +2378,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog | |||
1673 | throw new NotImplementedException(); | 2378 | throw new NotImplementedException(); |
1674 | } | 2379 | } |
1675 | } | 2380 | } |
2381 | #pragma warning restore 0168 | ||
2382 | /// <summary> | ||
2383 | /// A ClauseHeadAndBody is used in Compiler.compileAnonymousFunction as a base class | ||
2384 | /// in order to implement YP.IClause. After creating the object, you must call setHeadAndBody. | ||
2385 | /// </summary> | ||
2386 | public class ClauseHeadAndBody | ||
2387 | { | ||
2388 | private object _Head; | ||
2389 | private object _Body; | ||
2390 | |||
2391 | public void setHeadAndBody(object Head, object Body) | ||
2392 | { | ||
2393 | _Head = Head; | ||
2394 | _Body = Body; | ||
2395 | } | ||
2396 | |||
2397 | public IEnumerable<bool> clause(object Head, object Body) | ||
2398 | { | ||
2399 | if (_Head == null || _Body == null) | ||
2400 | yield break; | ||
2401 | |||
2402 | foreach (bool l1 in YP.unify(Head, _Head)) | ||
2403 | { | ||
2404 | foreach (bool l2 in YP.unify(Body, _Body)) | ||
2405 | yield return false; | ||
2406 | } | ||
2407 | } | ||
2408 | } | ||
1676 | } | 2409 | } |
1677 | } | 2410 | } |