diff options
author | Charles Krinke | 2008-07-16 01:00:40 +0000 |
---|---|---|
committer | Charles Krinke | 2008-07-16 01:00:40 +0000 |
commit | 620f7926f3f2ad05fdb72050a87e49d0fa2357dd (patch) | |
tree | 848c3c4071ff70f6e24c7e46741bca67b39495c4 /OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs | |
parent | add migration for SceneGroupID to char(36) plus add an index. This (diff) | |
download | opensim-SC_OLD-620f7926f3f2ad05fdb72050a87e49d0fa2357dd.zip opensim-SC_OLD-620f7926f3f2ad05fdb72050a87e49d0fa2357dd.tar.gz opensim-SC_OLD-620f7926f3f2ad05fdb72050a87e49d0fa2357dd.tar.bz2 opensim-SC_OLD-620f7926f3f2ad05fdb72050a87e49d0fa2357dd.tar.xz |
Mantis#1753. Thank you kindly, Kinoc for a patch that:
Brings Yield Prolog up to date with sourceforge version 0.9.10
Patched applies to both DotNet and XEngine.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs | 1184 |
1 files changed, 973 insertions, 211 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs index 31f007f..6c981bb 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/YieldProlog/YP.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/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.DotNetEngine.Compiler.YieldProlog | 40 | namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog |
38 | { | 41 | { |
@@ -48,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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.DotNetEngine.Compiler.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. |
@@ -579,12 +616,16 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
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 | } |
@@ -598,7 +639,45 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
598 | FunctorName = YP.getValue(FunctorName); | 639 | FunctorName = YP.getValue(FunctorName); |
599 | Arity = YP.getValue(Arity); | 640 | Arity = YP.getValue(Arity); |
600 | 641 | ||
601 | 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 | ||
602 | { | 681 | { |
603 | foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term))) | 682 | foreach (bool l1 in YP.unify(FunctorName, getFunctorName(Term))) |
604 | { | 683 | { |
@@ -606,27 +685,35 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
606 | yield return false; | 685 | yield return false; |
607 | } | 686 | } |
608 | } | 687 | } |
609 | else | ||
610 | throw new NotImplementedException("Debug: must finish functor/3"); | ||
611 | } | 688 | } |
612 | 689 | ||
613 | public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value) | 690 | public static IEnumerable<bool> arg(object ArgNumber, object Term, object Value) |
614 | { | 691 | { |
615 | if (YP.var(ArgNumber)) | 692 | if (var(ArgNumber)) |
616 | throw new NotImplementedException("Debug: must finish arg/3"); | 693 | throw new PrologException(Atom.a("instantiation_error"), "Arg 1 ArgNumber is an unbound variable"); |
617 | 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) | ||
618 | { | 713 | { |
619 | int argNumberInt = convertInt(ArgNumber); | 714 | // The first ArgNumber is at 1, not 0. |
620 | if (argNumberInt < 0) | 715 | foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1])) |
621 | throw new Exception("ArgNumber must be non-negative"); | 716 | yield return false; |
622 | object[] termArgs = YP.getFunctorArgs(Term); | ||
623 | // Silently fail if argNumberInt is out of range. | ||
624 | if (argNumberInt >= 1 && argNumberInt <= termArgs.Length) | ||
625 | { | ||
626 | // The first ArgNumber is at 1, not 0. | ||
627 | foreach (bool l1 in YP.unify(Value, termArgs[argNumberInt - 1])) | ||
628 | yield return false; | ||
629 | } | ||
630 | } | 717 | } |
631 | } | 718 | } |
632 | 719 | ||
@@ -656,8 +743,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
656 | if (term1TypeCode == -2) | 743 | if (term1TypeCode == -2) |
657 | { | 744 | { |
658 | // Variable. | 745 | // Variable. |
659 | // 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 |
660 | // that less than returns false if the terms are equal, in | 747 | // that less than returns false if the terms are equal, in |
661 | // 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. |
662 | if ((Variable)Term1 != (Variable)Term2) | 749 | if ((Variable)Term1 != (Variable)Term2) |
663 | // The hash code should be unique to a Variable object. | 750 | // The hash code should be unique to a Variable object. |
@@ -700,8 +787,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
700 | } | 787 | } |
701 | 788 | ||
702 | /// <summary> | 789 | /// <summary> |
703 | /// 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, |
704 | /// 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, |
705 | /// 4 if it is Functor. | 792 | /// 4 if it is Functor. |
706 | /// Otherwise, type code is -1. | 793 | /// Otherwise, type code is -1. |
707 | /// This does not call YP.getValue(term). | 794 | /// This does not call YP.getValue(term). |
@@ -769,133 +856,446 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
769 | if (_operatorTable == null) | 856 | if (_operatorTable == null) |
770 | { | 857 | { |
771 | // Initialize. | 858 | // Initialize. |
772 | _operatorTable = new List<object[]>(); | 859 | _operatorTable = new IndexedAnswers(3); |
773 | _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") }); | 860 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a(":-") }); |
774 | _operatorTable.Add(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") }); | 861 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("xfx"), Atom.a("-->") }); |
775 | _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a(":-") }); | 862 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a(":-") }); |
776 | _operatorTable.Add(new object[] { 1200, Atom.a("fx"), Atom.a("?-") }); | 863 | _operatorTable.addAnswer(new object[] { 1200, Atom.a("fx"), Atom.a("?-") }); |
777 | _operatorTable.Add(new object[] { 1100, Atom.a("xfy"), Atom.a(";") }); | 864 | _operatorTable.addAnswer(new object[] { 1100, Atom.a("xfy"), Atom.a(";") }); |
778 | _operatorTable.Add(new object[] { 1050, Atom.a("xfy"), Atom.a("->") }); | 865 | _operatorTable.addAnswer(new object[] { 1050, Atom.a("xfy"), Atom.a("->") }); |
779 | _operatorTable.Add(new object[] { 1000, Atom.a("xfy"), Atom.a(",") }); | 866 | _operatorTable.addAnswer(new object[] { 1000, Atom.a("xfy"), Atom.a(",") }); |
780 | _operatorTable.Add(new object[] { 900, Atom.a("fy"), Atom.a("\\+") }); | 867 | _operatorTable.addAnswer(new object[] { 900, Atom.a("fy"), Atom.a("\\+") }); |
781 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=") }); | 868 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=") }); |
782 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") }); | 869 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\=") }); |
783 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("==") }); | 870 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("==") }); |
784 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") }); | 871 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("\\==") }); |
785 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@<") }); | 872 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@<") }); |
786 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") }); | 873 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@=<") }); |
787 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>") }); | 874 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>") }); |
788 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") }); | 875 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("@>=") }); |
789 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=..") }); | 876 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=..") }); |
790 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("is") }); | 877 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("is") }); |
791 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") }); | 878 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=:=") }); |
792 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") }); | 879 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=\\=") }); |
793 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("<") }); | 880 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("<") }); |
794 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a("=<") }); | 881 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a("=<") }); |
795 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">") }); | 882 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">") }); |
796 | _operatorTable.Add(new object[] { 700, Atom.a("xfx"), Atom.a(">=") }); | 883 | _operatorTable.addAnswer(new object[] { 700, Atom.a("xfx"), Atom.a(">=") }); |
797 | _operatorTable.Add(new object[] { 600, Atom.a("xfy"), Atom.a(":") }); | 884 | _operatorTable.addAnswer(new object[] { 600, Atom.a("xfy"), Atom.a(":") }); |
798 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("+") }); | 885 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("+") }); |
799 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("-") }); | 886 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("-") }); |
800 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") }); | 887 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("/\\") }); |
801 | _operatorTable.Add(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") }); | 888 | _operatorTable.addAnswer(new object[] { 500, Atom.a("yfx"), Atom.a("\\/") }); |
802 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("*") }); | 889 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("*") }); |
803 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("/") }); | 890 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("/") }); |
804 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("//") }); | 891 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("//") }); |
805 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("rem") }); | 892 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("rem") }); |
806 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("mod") }); | 893 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("mod") }); |
807 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a("<<") }); | 894 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a("<<") }); |
808 | _operatorTable.Add(new object[] { 400, Atom.a("yfx"), Atom.a(">>") }); | 895 | _operatorTable.addAnswer(new object[] { 400, Atom.a("yfx"), Atom.a(">>") }); |
809 | _operatorTable.Add(new object[] { 200, Atom.a("xfx"), Atom.a("**") }); | 896 | _operatorTable.addAnswer(new object[] { 200, Atom.a("xfx"), Atom.a("**") }); |
810 | _operatorTable.Add(new object[] { 200, Atom.a("xfy"), Atom.a("^") }); | 897 | _operatorTable.addAnswer(new object[] { 200, Atom.a("xfy"), Atom.a("^") }); |
811 | _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("-") }); | 898 | _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("-") }); |
812 | _operatorTable.Add(new object[] { 200, Atom.a("fy"), Atom.a("\\") }); | 899 | _operatorTable.addAnswer(new object[] { 200, Atom.a("fy"), Atom.a("\\") }); |
813 | // 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. |
814 | _operatorTable.Add(new object[] { 20, Atom.a("xfx"), Atom.a("<--") }); | 901 | _operatorTable.addAnswer(new object[] { 20, Atom.a("xfx"), Atom.a("<--") }); |
815 | } | 902 | } |
816 | 903 | ||
817 | object[] args = new object[] { Priority, Specifier, Operator }; | 904 | foreach (bool l1 in _operatorTable.match(new object[] { Priority, Specifier, Operator })) |
818 | foreach (object[] answer in _operatorTable) | 905 | yield return false; |
819 | { | ||
820 | foreach (bool l1 in YP.unifyArrays(args, answer)) | ||
821 | yield return false; | ||
822 | } | ||
823 | } | 906 | } |
824 | 907 | ||
825 | public static IEnumerable<bool> atom_length(object atom, object Length) | 908 | public static IEnumerable<bool> atom_length(object atom, object Length) |
826 | { | 909 | { |
827 | 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); | ||
828 | } | 929 | } |
829 | 930 | ||
830 | 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) |
831 | { | 932 | { |
832 | // Debug: Should implement for var(Start) which is a kind of search. | ||
833 | // Debug: Should we try to preserve the _declaringClass? | 933 | // Debug: Should we try to preserve the _declaringClass? |
834 | return YP.unify(Whole, Atom.a(((Atom)YP.getValue(Start))._name + | 934 | Start = YP.getValue(Start); |
835 | ((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 | } | ||
836 | } | 999 | } |
837 | 1000 | ||
838 | public static IEnumerable<bool> sub_atom | 1001 | public static IEnumerable<bool> sub_atom |
839 | (object atom, object Before, object Length, object After, object Sub_atom) | 1002 | (object atom, object Before, object Length, object After, object Sub_atom) |
840 | { | 1003 | { |
841 | // Debug: Should implement for var(atom) which is a kind of search. | ||
842 | // Debug: Should we try to preserve the _declaringClass? | 1004 | // Debug: Should we try to preserve the _declaringClass? |
843 | Atom atomAtom = (Atom)YP.getValue(atom); | 1005 | atom = YP.getValue(atom); |
844 | int beforeInt = YP.convertInt(Before); | 1006 | Before = YP.getValue(Before); |
845 | int lengthInt = YP.convertInt(Length); | 1007 | Length = YP.getValue(Length); |
846 | if (beforeInt < 0) | 1008 | After = YP.getValue(After); |
847 | throw new Exception("Before must be non-negative"); | 1009 | Sub_atom = YP.getValue(Sub_atom); |
848 | if (lengthInt < 0) | 1010 | if (atom is Variable) |
849 | throw new Exception("Length must be non-negative"); | 1011 | throw new PrologException(Atom.a("instantiation_error"), |
850 | int afterInt = atomAtom._name.Length - (beforeInt + lengthInt); | 1012 | "Expected atom(Arg1) but it is an unbound variable"); |
851 | if (afterInt >= 0) | 1013 | if (!(atom is Atom)) |
852 | { | 1014 | throw new PrologException |
853 | foreach (bool l1 in YP.unify(After, afterInt)) | 1015 | (new Functor2("type_error", Atom.a("atom"), atom), "Arg 1 Atom is not an atom"); |
1016 | if (!(Sub_atom is Variable)) | ||
1017 | { | ||
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) | ||
854 | { | 1067 | { |
855 | foreach (bool l2 in YP.unify | 1068 | foreach (bool l1 in YP.unify(After, xAfter)) |
856 | (Sub_atom, Atom.a(atomAtom._name.Substring(beforeInt, lengthInt)))) | 1069 | { |
857 | 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 | } | ||
858 | } | 1110 | } |
859 | } | 1111 | } |
860 | } | 1112 | } |
861 | 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); | ||
1156 | } | ||
1157 | } | ||
1158 | |||
862 | public static IEnumerable<bool> atom_codes(object atom, object List) | 1159 | public static IEnumerable<bool> atom_codes(object atom, object List) |
863 | { | 1160 | { |
864 | atom = YP.getValue(atom); | 1161 | atom = YP.getValue(atom); |
865 | List = YP.getValue(List); | 1162 | List = YP.getValue(List); |
866 | 1163 | ||
867 | if (nonvar(atom)) | 1164 | if (atom is Variable) |
868 | { | 1165 | { |
869 | 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; | ||
870 | object codeList = Atom.NIL; | 1193 | object codeList = Atom.NIL; |
871 | // Start from the back to make the list. | 1194 | // Start from the back to make the list. |
872 | for (int i = name.Length - 1; i >= 0; --i) | 1195 | for (int i = atomString.Length - 1; i >= 0; --i) |
873 | codeList = new ListPair((int)name[i], codeList); | 1196 | codeList = new ListPair((int)atomString[i], codeList); |
874 | return YP.unify(List, codeList); | 1197 | return YP.unify(List, codeList); |
875 | } | 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) | ||
876 | { | 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"); | ||
877 | 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 | |||
878 | char[] charArray = new char[codeArray.Length]; | 1216 | char[] charArray = new char[codeArray.Length]; |
879 | for (int i = 0; i < codeArray.Length; ++i) | 1217 | for (int i = 0; i < codeArray.Length; ++i) |
880 | charArray[i] = (char)YP.convertInt(codeArray[i]); | 1218 | { |
881 | 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); | ||
882 | } | 1253 | } |
883 | } | 1254 | } |
884 | 1255 | ||
885 | public static IEnumerable<bool> number_codes(object number, object List) | 1256 | public static IEnumerable<bool> number_codes(object Number, object List) |
886 | { | 1257 | { |
887 | number = YP.getValue(number); | 1258 | Number = YP.getValue(Number); |
888 | List = YP.getValue(List); | 1259 | List = YP.getValue(List); |
889 | 1260 | ||
890 | 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 | ||
891 | { | 1284 | { |
892 | string numberString = null; | 1285 | string numberString = null; |
893 | // Try converting to an int first. | 1286 | // Try converting to an int first. |
894 | int intNumber; | 1287 | int intNumber; |
895 | if (YP.getInt(number, out intNumber)) | 1288 | if (YP.getInt(Number, out intNumber)) |
896 | numberString = intNumber.ToString(); | 1289 | numberString = intNumber.ToString(); |
897 | else | 1290 | else |
898 | 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 | } | ||
899 | 1299 | ||
900 | object codeList = Atom.NIL; | 1300 | object codeList = Atom.NIL; |
901 | // Start from the back to make the list. | 1301 | // Start from the back to make the list. |
@@ -903,20 +1303,92 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
903 | codeList = new ListPair((int)numberString[i], codeList); | 1303 | codeList = new ListPair((int)numberString[i], codeList); |
904 | return YP.unify(List, codeList); | 1304 | return YP.unify(List, codeList); |
905 | } | 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")) | ||
906 | { | 1321 | { |
907 | object[] codeArray = ListPair.toArray(List); | ||
908 | char[] charArray = new char[codeArray.Length]; | ||
909 | for (int i = 0; i < codeArray.Length; ++i) | ||
910 | charArray[i] = (char)YP.convertInt(codeArray[i]); | ||
911 | String numberString = new String(charArray); | ||
912 | // Debug: Is there a way in C# to ask if a string parses as int without throwing an exception? | ||
913 | try | 1322 | try |
914 | { | 1323 | { |
915 | // Try an int first. | 1324 | return Int32.Parse |
916 | return YP.unify(number, Convert.ToInt32(numberString)); | 1325 | (numberString.Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier); |
917 | } | 1326 | } |
918 | catch (FormatException) { } | 1327 | catch (FormatException) |
919 | 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]); | ||
920 | } | 1392 | } |
921 | } | 1393 | } |
922 | 1394 | ||
@@ -1092,7 +1564,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1092 | } | 1564 | } |
1093 | 1565 | ||
1094 | /// <summary> | 1566 | /// <summary> |
1095 | /// 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 |
1096 | /// use "1" which will parse as an int. | 1568 | /// use "1" which will parse as an int. |
1097 | /// </summary> | 1569 | /// </summary> |
1098 | /// <param name="x"></param> | 1570 | /// <param name="x"></param> |
@@ -1117,7 +1589,13 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1117 | 1589 | ||
1118 | public static void put_code(object x) | 1590 | public static void put_code(object x) |
1119 | { | 1591 | { |
1120 | _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); | ||
1121 | } | 1599 | } |
1122 | 1600 | ||
1123 | public static void nl() | 1601 | public static void nl() |
@@ -1153,6 +1631,10 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1153 | { | 1631 | { |
1154 | Head = YP.getValue(((Functor2)TermCopy)._arg1); | 1632 | Head = YP.getValue(((Functor2)TermCopy)._arg1); |
1155 | 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"); | ||
1156 | } | 1638 | } |
1157 | else | 1639 | else |
1158 | { | 1640 | { |
@@ -1166,7 +1648,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1166 | throw new PrologException | 1648 | throw new PrologException |
1167 | (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"); |
1168 | object[] args = getFunctorArgs(Head); | 1650 | object[] args = getFunctorArgs(Head); |
1169 | if (!isDynamic(name, args.Length)) | 1651 | if (isSystemPredicate(name, args.Length)) |
1170 | throw new PrologException | 1652 | throw new PrologException |
1171 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), | 1653 | (new Functor3("permission_error", Atom.a("modify"), Atom.a("static_procedure"), |
1172 | new Functor2(Atom.SLASH, name, args.Length)), | 1654 | new Functor2(Atom.SLASH, name, args.Length)), |
@@ -1174,17 +1656,21 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1174 | 1656 | ||
1175 | if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true")) | 1657 | if (copyStore.getNUniqueVariables() == 0 && Body == Atom.a("true")) |
1176 | { | 1658 | { |
1177 | // Debug: Until IndexedAnswers supports prepend, compile the fact so we can prepend it below. | 1659 | // This is a fact with no unbound variables |
1178 | if (!prepend) | 1660 | // assertFact and prependFact use IndexedAnswers, so don't we don't need to compile. |
1179 | { | 1661 | if (prepend) |
1180 | // This is a fact with no unbound variables | 1662 | prependFact(name, args); |
1181 | // assertFact uses IndexedAnswers, so don't we don't need to compile. | 1663 | else |
1182 | assertFact(name, args); | 1664 | assertFact(name, args); |
1183 | return; | 1665 | |
1184 | } | 1666 | return; |
1185 | } | 1667 | } |
1186 | 1668 | ||
1187 | 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); | ||
1188 | 1674 | ||
1189 | // Add the clause to the entry in _predicatesStore. | 1675 | // Add the clause to the entry in _predicatesStore. |
1190 | NameArity nameArity = new NameArity(name, args.Length); | 1676 | NameArity nameArity = new NameArity(name, args.Length); |
@@ -1199,15 +1685,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1199 | clauses.Add(clause); | 1685 | clauses.Add(clause); |
1200 | } | 1686 | } |
1201 | 1687 | ||
1202 | private static bool isDynamic(Atom name, int arity) | 1688 | private static bool isSystemPredicate(Atom name, int arity) |
1203 | { | 1689 | { |
1204 | 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)) |
1205 | return false; | 1691 | return true; |
1206 | // 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. |
1207 | foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable())) | 1693 | foreach (bool l1 in YPCompiler.functorCallYPFunctionName(name, arity, new Variable())) |
1208 | return false; | 1694 | return true; |
1209 | // Debug: Do we need to check if name._module is null? | 1695 | // Debug: Do we need to check if name._module is null? |
1210 | return true; | 1696 | return false; |
1211 | } | 1697 | } |
1212 | 1698 | ||
1213 | /// <summary> | 1699 | /// <summary> |
@@ -1224,22 +1710,55 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1224 | IndexedAnswers indexedAnswers; | 1710 | IndexedAnswers indexedAnswers; |
1225 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) | 1711 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) |
1226 | { | 1712 | { |
1227 | // Create an IndexedAnswers as the first clause of the predicate. | 1713 | // Create an IndexedAnswers as the only clause of the predicate. |
1228 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); | 1714 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); |
1229 | clauses.Add(indexedAnswers = new IndexedAnswers()); | 1715 | clauses.Add(indexedAnswers = new IndexedAnswers(values.Length)); |
1230 | } | 1716 | } |
1231 | else | 1717 | else |
1232 | { | 1718 | { |
1233 | indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers; | 1719 | indexedAnswers = null; |
1720 | if (clauses.Count >= 1) | ||
1721 | indexedAnswers = clauses[clauses.Count - 1] as IndexedAnswers; | ||
1234 | if (indexedAnswers == null) | 1722 | if (indexedAnswers == null) |
1235 | // The latest clause is not an IndexedAnswers, so add one. | 1723 | // The latest clause is not an IndexedAnswers, so add one. |
1236 | clauses.Add(indexedAnswers = new IndexedAnswers()); | 1724 | clauses.Add(indexedAnswers = new IndexedAnswers(values.Length)); |
1237 | } | 1725 | } |
1238 | 1726 | ||
1239 | indexedAnswers.addAnswer(values); | 1727 | indexedAnswers.addAnswer(values); |
1240 | } | 1728 | } |
1241 | 1729 | ||
1242 | /// <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> | ||
1243 | /// 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 |
1244 | /// arguments.Length. | 1763 | /// arguments.Length. |
1245 | /// It is an error if the predicate is not defined. | 1764 | /// It is an error if the predicate is not defined. |
@@ -1251,9 +1770,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1251 | { | 1770 | { |
1252 | List<IClause> clauses; | 1771 | List<IClause> clauses; |
1253 | if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses)) | 1772 | if (!_predicatesStore.TryGetValue(new NameArity(name, arguments.Length), out clauses)) |
1254 | throw new UndefinedPredicateException | 1773 | throw new PrologException |
1255 | ("Undefined fact: " + name + "/" + arguments.Length, name, | 1774 | (new Functor2 |
1256 | arguments.Length); | 1775 | (Atom.a("existence_error"), Atom.a("procedure"), |
1776 | new Functor2(Atom.SLASH, name, arguments.Length)), | ||
1777 | "Undefined predicate: " + name + "/" + arguments.Length); | ||
1257 | 1778 | ||
1258 | if (clauses.Count == 1) | 1779 | if (clauses.Count == 1) |
1259 | // 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. |
@@ -1271,7 +1792,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1271 | /// <returns></returns> | 1792 | /// <returns></returns> |
1272 | private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments) | 1793 | private static IEnumerable<bool> matchAllClauses(List<IClause> clauses, object[] arguments) |
1273 | { | 1794 | { |
1274 | // 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 |
1275 | // 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? |
1276 | foreach (IClause clause in clauses) | 1797 | foreach (IClause clause in clauses) |
1277 | { | 1798 | { |
@@ -1297,16 +1818,122 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1297 | return matchDynamic(name, arguments); | 1818 | return matchDynamic(name, arguments); |
1298 | } | 1819 | } |
1299 | 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 | |||
1300 | /// <summary> | 1915 | /// <summary> |
1301 | /// This actually searches all clauses, not just | 1916 | /// This is deprecated for backward compatibility. You should use retractall. |
1302 | /// the ones defined with assertFact, but we keep the name for | ||
1303 | /// backwards compatibility. | ||
1304 | /// </summary> | 1917 | /// </summary> |
1305 | /// <param name="name">must be an Atom</param> | 1918 | /// <param name="name">must be an Atom</param> |
1306 | /// <param name="arguments">an array of arity number of arguments</param> | 1919 | /// <param name="arguments">an array of arity number of arguments</param> |
1307 | public static void retractFact(Atom name, object[] arguments) | 1920 | public static void retractFact(Atom name, object[] arguments) |
1308 | { | 1921 | { |
1309 | 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); | ||
1310 | List<IClause> clauses; | 1937 | List<IClause> clauses; |
1311 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) | 1938 | if (!_predicatesStore.TryGetValue(nameArity, out clauses)) |
1312 | // Can't find, so ignore. | 1939 | // Can't find, so ignore. |
@@ -1315,11 +1942,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1315 | foreach (object arg in arguments) | 1942 | foreach (object arg in arguments) |
1316 | { | 1943 | { |
1317 | if (!YP.var(arg)) | 1944 | if (!YP.var(arg)) |
1318 | 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"); | ||
1319 | } | 1947 | } |
1320 | // Set to a fresh empty IndexedAnswers. | 1948 | // Clear all clauses. |
1321 | _predicatesStore[nameArity] = (clauses = new List<IClause>()); | 1949 | _predicatesStore[nameArity] = new List<IClause>(); |
1322 | clauses.Add(new IndexedAnswers()); | ||
1323 | } | 1950 | } |
1324 | 1951 | ||
1325 | public static IEnumerable<bool> current_predicate(object NameSlashArity) | 1952 | public static IEnumerable<bool> current_predicate(object NameSlashArity) |
@@ -1328,57 +1955,99 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1328 | // 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. |
1329 | if (YP.ground(NameSlashArity)) | 1956 | if (YP.ground(NameSlashArity)) |
1330 | { | 1957 | { |
1331 | 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) | ||
1332 | { | 1984 | { |
1333 | Functor2 NameArityFunctor = (Functor2)NameSlashArity; | 1985 | foreach (bool l1 in YP.unify |
1334 | if (NameArityFunctor._name == Atom.SLASH) | 1986 | (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity)) |
1335 | { | 1987 | yield return false; |
1336 | if (_predicatesStore.ContainsKey(new NameArity | ||
1337 | ((Atom)YP.getValue(NameArityFunctor._arg1), | ||
1338 | (int)YP.getValue(NameArityFunctor._arg2)))) | ||
1339 | // The predicate is defined. | ||
1340 | yield return false; | ||
1341 | } | ||
1342 | } | 1988 | } |
1343 | yield break; | ||
1344 | } | 1989 | } |
1990 | } | ||
1345 | 1991 | ||
1346 | foreach (NameArity key in _predicatesStore.Keys) | 1992 | public static void abolish(object NameSlashArity) |
1347 | { | 1993 | { |
1348 | foreach (bool l1 in YP.unify | 1994 | NameSlashArity = YP.getValue(NameSlashArity); |
1349 | (new Functor2(Atom.SLASH, key._name, key._arity), NameSlashArity)) | 1995 | if (NameSlashArity is Variable) |
1350 | yield return false; | 1996 | throw new PrologException |
1351 | } | 1997 | ("instantiation_error", "Predicate indicator is an unbound variable"); |
1998 | Functor2 NameArityFunctor = NameSlashArity as Functor2; | ||
1999 | if (!(NameArityFunctor != null && NameArityFunctor._name == Atom.SLASH)) | ||
2000 | throw new PrologException | ||
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)); | ||
1352 | } | 2027 | } |
1353 | 2028 | ||
1354 | /// <summary> | 2029 | /// <summary> |
1355 | /// 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 |
1356 | /// declaringClass, using arguments from YP.getFunctorArgs(Goal). | 2031 | /// YP.getFunctorArgs(Goal). If not found, this throws a PrologException for existence_error. |
1357 | /// 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. |
1358 | /// If not found, this throws UndefinedPredicateException. | ||
1359 | /// </summary> | 2033 | /// </summary> |
1360 | /// <param name="Goal"></param> | 2034 | /// <param name="Goal"></param> |
1361 | /// <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> | ||
1362 | /// <returns></returns> | 2037 | /// <returns></returns> |
1363 | public static IEnumerable<bool> getIterator(object Goal, Type declaringClass) | 2038 | public static IEnumerable<bool> getIterator(object Goal, Type declaringClass) |
1364 | { | 2039 | { |
1365 | Goal = YP.getValue(Goal); | ||
1366 | if (Goal is Variable) | ||
1367 | throw new PrologException("instantiation_error", "Goal to call is an unbound variable"); | ||
1368 | #if true | ||
1369 | List<Variable> variableSetList = new List<Variable>(); | ||
1370 | addUniqueVariables(Goal, variableSetList); | ||
1371 | Variable[] variableSet = variableSetList.ToArray(); | ||
1372 | |||
1373 | // Use Atom.F since it is ignored. | ||
1374 | return YPCompiler.compileAnonymousClause | ||
1375 | (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet); | ||
1376 | #else | ||
1377 | Atom name; | 2040 | Atom name; |
1378 | object[] args; | 2041 | object[] args; |
1379 | while (true) | 2042 | while (true) |
1380 | { | 2043 | { |
1381 | 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"); | ||
1382 | args = YP.getFunctorArgs(Goal); | 2051 | args = YP.getFunctorArgs(Goal); |
1383 | if (name == Atom.HAT && args.Length == 2) | 2052 | if (name == Atom.HAT && args.Length == 2) |
1384 | // 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. |
@@ -1386,22 +2055,20 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1386 | else | 2055 | else |
1387 | break; | 2056 | break; |
1388 | } | 2057 | } |
1389 | try | 2058 | |
1390 | { | 2059 | IEnumerable<bool> simpleIterator = YPCompiler.getSimpleIterator(name, args, declaringClass); |
1391 | return (IEnumerable<bool>)declaringClass.InvokeMember | 2060 | if (simpleIterator != null) |
1392 | (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. |
1393 | } | 2062 | return simpleIterator; |
1394 | catch (TargetInvocationException exception) | 2063 | |
1395 | { | 2064 | // Compile the goal as a clause. |
1396 | throw exception.InnerException; | 2065 | List<Variable> variableSetList = new List<Variable>(); |
1397 | } | 2066 | addUniqueVariables(Goal, variableSetList); |
1398 | catch (MissingMethodException) | 2067 | Variable[] variableSet = variableSetList.ToArray(); |
1399 | { | 2068 | |
1400 | throw new UndefinedPredicateException | 2069 | // Use Atom.F since it is ignored. |
1401 | ("Cannot find predicate function: " + name + "/" + args.Length + " in " + | 2070 | return YPCompiler.compileAnonymousClause |
1402 | declaringClass.FullName, name, args.Length); | 2071 | (Functor.make(Atom.F, variableSet), Goal, declaringClass).match(variableSet); |
1403 | } | ||
1404 | #endif | ||
1405 | } | 2072 | } |
1406 | 2073 | ||
1407 | public static void throwException(object Term) | 2074 | public static void throwException(object Term) |
@@ -1415,12 +2082,12 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1415 | /// <param name="script_event"></param> | 2082 | /// <param name="script_event"></param> |
1416 | /// <param name="script_params"></param> | 2083 | /// <param name="script_params"></param> |
1417 | /// <returns></returns> | 2084 | /// <returns></returns> |
1418 | public static void script_event(object script_event, object script_params) | 2085 | public static IEnumerable<bool> script_event(object script_event, object script_params) |
1419 | { | 2086 | { |
1420 | // string function = ((Atom)YP.getValue(script_event))._name; | 2087 | // string function = ((Atom)YP.getValue(script_event))._name; |
1421 | object[] array = ListPair.toArray(script_params); | 2088 | object[] array = ListPair.toArray(script_params); |
1422 | if (array == null) | 2089 | if (array == null) |
1423 | return; // YP.fail(); | 2090 | yield return false; // return; // YP.fail(); |
1424 | if (array.Length > 1) | 2091 | if (array.Length > 1) |
1425 | { | 2092 | { |
1426 | //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue | 2093 | //m_CmdManager.m_ScriptEngine.m_EventQueManager.AddToScriptQueue |
@@ -1428,8 +2095,76 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1428 | // sortArray(array); | 2095 | // sortArray(array); |
1429 | } | 2096 | } |
1430 | //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; | ||
1431 | } | 2113 | } |
1432 | 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 | //} | ||
1433 | /// <summary> | 2168 | /// <summary> |
1434 | /// An enumerator that does zero loops. | 2169 | /// An enumerator that does zero loops. |
1435 | /// </summary> | 2170 | /// </summary> |
@@ -1644,5 +2379,32 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog | |||
1644 | } | 2379 | } |
1645 | } | 2380 | } |
1646 | #pragma warning restore 0168 | 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 | } | ||
1647 | } | 2409 | } |
1648 | } | 2410 | } |