YPOS: YieldProlog for OpenSim a compiler from Prolog to OpenSim compatible C# scripts Ported by Kino Coursey and Douglas Miles at Daxtron Labs. Based on Jeff Thompson Yield Prolog, http://yieldprolog.sourceforge.net/ For Prolog see http://en.wikipedia.org/wiki/Prolog INTRODUCTION This folder contains code to implement a Prolog compiler using the "Yield Statement" found in C#, Javascript, and Python. The original Yield Prolog system can transform Prolog programs into C# code. In this system we detect and extract YieldProlog code (with "//YP" as the first four characters in the script) and seperate it from any c# code ("marked by "//CS"). The YP code is transformed to C# and prepended to the "//CS" section, and passed as a bundel to the existing C# compiler. The end result is Prolog can interface to OpenSim using the existing "//CS" functionality, and C# can call the compiled Prolog. As such YP allows both declaritive and procedural programming in a 3D script enabled environment. FEATURES * Allows implementation of logic programming for objects and agents. * C#/Javascript/Python as intermediate language * Yield Prolog has relatively high speed of execution which is important in OpenSim. http://yieldprolog.sourceforge.net/benchmarks.html * It is compatable with the existing C#/Mono based system. * Yield Prolog is BSD license * Calling Prolog from C# scripts * Calling C# functions (with LSL and OS functions) from Prolog * Prolog dynamic database * Edinburgh, Cocksin & Mellish style syntax. * Compiler is generated by compiling the Prolog descrition of itself into C# * Same script entry interface as LSL TODO * Utilize ability to generate Javascript and Python code * Integrate Prolog database with Sim * Translation error reporting back to the editor * Communications via message passing * Interface to external inference engines POSSIBILITIES * Inworld expert systems * Parallel logic programming and expert systems * Ontology based processing * Knowledge based alerting, accessing and business rules For instance, listen on channel x, filter the events and broadcast alerts on channel y or send IM, emails etc. USAGE: Add "yp" as an allowed compiler OpenSim.ini [ScriptEngine.DotNetEngine] AllowedCompilers=lsl,cs,js,vb,yp Enter scripts using the inworld editing process. Scripts have the following format. The first line of a file must have "//yp". //yp //CS C# code calling a Prolog Predicate: ----------------------------------- The Prolog predicate is transformed into a C# boolean function. So the general calling format is: foreach( bool var in prolog_predicate(ARGS)) {}; I/O is via using a string reader and writer in conjunction with YP.See() and YP.Tell() StringWriter PrologOutuput= new StringWriter(); StringReader PrologInput= new StringReader(myInputString); YP.see(PrologInput); YP.tell(PrologOutuput); YP.seen(); YP.told(); StringBuilder builder = PrologOutput.GetStringBuilder(); string finaloutput = builder.ToString(); Any prolog reads and writes will be to the passed StringReader and StringWriter. In fact any TextReader/TextWriter class can be used. Strings in Prolog are stored as Atom's and you need to use an Atom object to match. \\yp wanted('bob'). \\cs string Who="bob"; foreach( bool ans in wanted(Atom.a(Who) )){}; Prolog code calling a C# function: ----------------------------------- The prolog code uses the script_event('name_of_function',ARGS) builtin, which is transformed into the function call. The C# function called uses "PrologCallback" and returns a boolean. Dynamic database assertions: ----------------------------------- void assertdb2(string predicate, string arg1, string arg2) { name = Atom.a(predicate); YP.assertFact(name, new object[] { arg1, arg2 }); } void retractdb2(string predicate, string arg1, string arg2) { name = Atom.a(predicate); YP.retractFact(name, new object[] { arg1, arg2 }); } ========================= APPENDIX A: touch test ================================ =================================== Input YP Code =================================== //yp mydb('field2','field1'). mydb('andy','jane'). mydb('carl','dan'). mydb('andy','bill'). mydb('andy','betty'). call_me(X):-mydb(X,Y) , respond(Y). respond(X):- script_event('sayit',X). //cs public void default_event_touch_start(int N ) { llSay(0,"pstart1"); foreach( bool ans in call_me(Atom.a(@"andy") )){}; llSay(0,"pstop2"); } public void default_event_state_entry() { llSay(0,"prolog tester active."); } PrologCallback sayit(object ans) { llSay(0,"sayit1"); string msg = "one answer is :"+((Variable)ans).getValue(); llSay(0,msg); yield return false; } =================================== Generated CS Code =================================== using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog;using OpenSim.Region.ScriptEngine.Common; using System.Collections.Generic; namespace SecondLife { public class Script : OpenSim.Region.ScriptEngine.Common.BuiltIn_Commands_BaseClass { static OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog.YP YP=null;public Script() { YP= new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog.YP(); } //cs public void default_event_touch_start(int N ) { llSay(0,"pstart1"); foreach( bool ans in call_me(Atom.a(@"carl") )){}; llSay(0,"pstop2"); } public void default_event_state_entry() { llSay(0,"prolog tester active."); } public IEnumerable sayit(object ans) { llSay(0,"sayit1"); string msg = "one answer is :"+((Variable)ans).getValue(); llSay(0,msg); yield return false; } //YPEncoded public IEnumerable mydb(object arg1, object arg2) { { foreach (bool l2 in YP.unify(arg1, Atom.a(@"carl"))) { foreach (bool l3 in YP.unify(arg2, Atom.a(@"dan"))) { yield return false; } } } { foreach (bool l2 in YP.unify(arg1, Atom.a(@"andy"))) { foreach (bool l3 in YP.unify(arg2, Atom.a(@"bill"))) { yield return false; } } } { foreach (bool l2 in YP.unify(arg1, Atom.a(@"andy"))) { foreach (bool l3 in YP.unify(arg2, Atom.a(@"betty"))) { yield return false; } } } } public IEnumerable call_me(object X) { { Variable Y = new Variable(); foreach (bool l2 in mydb(X, Y)) { foreach (bool l3 in respond(Y)) { yield return false; } } } } public IEnumerable respond(object X) { { foreach (bool l2 in this.sayit( X)) { yield return false; } } } } } ========================= APPENDIX B:SENSOR INFORMED SCRIPT ===================== =================================== Input YP Code =================================== //yp nop. good('Daxxon Kinoc'). good('Fluffy Kitty'). bad('Eric Evil'). bad('Spikey Plant'). prolog_notify(X) :- good(X) , script_event('accept',X). prolog_notify(X) :- bad(X) , script_event('reject',X). //cs public void default_event_state_entry() { llSay(0,"prolog sensor tester active."); // Start a sensor looking for Agents llSensorRepeat("","",AGENT, 10, PI,20); } public void default_event_sensor(int number_detected ) { int i; for(i=0;i< number_detected ;i++) { string dName = llDetectedName(i); string dOwner = llDetectedName(i); foreach(bool response in prolog_notify(Atom.a(dName)) ){}; foreach(bool response in prolog_notify(dOwner) ){}; llSay(0,"Saw "+dName); } } string decodeToString(object obj) { if (obj is Variable) { return (string) ((Variable)obj).getValue();} if (obj is Atom) { return (string) ((Atom)obj)._name;} return "unknown type"; } PrologCallback accept(object ans) { string msg = "Welcoming :"+decodeToString(ans); llSay(0,msg); yield return false; } PrologCallback reject(object ans) { string msg = "Watching :"+decodeToString(ans); llSay(0,msg); yield return false; } =================================== Generated CS Code =================================== using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog; using OpenSim.Region.ScriptEngine.Common; using System.Collections.Generic; namespace SecondLife { public class Script : OpenSim.Region.ScriptEngine.Common.BuiltIn_Commands_BaseClass { static OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog.YP YP=null; public Script() { YP= new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.YieldProlog.YP(); } //cs public void default_event_state_entry() { llSay(0,"prolog sensor tester active."); // Start a sensor looking for Agents llSensorRepeat("","",AGENT, 10, PI,20); } public void default_event_sensor(int number_detected ) { int i; for(i=0;i< number_detected ;i++) { string dName = llDetectedName(i); string dOwner = llDetectedName(i); foreach(bool response in prolog_notify(Atom.a(dName)) ){}; foreach(bool response in prolog_notify(dOwner) ){}; llSay(0,"Saw "+dName); } } string decodeToString(object obj) { if (obj is Variable) { return (string) ((Variable)obj).getValue();} if (obj is Atom) { return (string) ((Atom)obj)._name;} return "unknown type"; } public IEnumerable accept(object ans) { string msg = "Welcoming :"+decodeToString(ans); llSay(0,msg); yield return false; } public IEnumerable reject(object ans) { string msg = "Watching :"+decodeToString(ans); llSay(0,msg); yield return false; } //YPEncoded public IEnumerable yp_nop_header_nop() { { yield return false; } } public IEnumerable good(object arg1) { { foreach (bool l2 in YP.unify(arg1, Atom.a(@"Daxxon Kinoc"))) { yield return false; } } { foreach (bool l2 in YP.unify(arg1, Atom.a(@"Fluffy Kitty"))) { yield return false; } } } public IEnumerable bad(object arg1) { { foreach (bool l2 in YP.unify(arg1, Atom.a(@"Eric Evil"))) { yield return false; } } { foreach (bool l2 in YP.unify(arg1, Atom.a(@"Spikey Plant"))) { yield return false; } } } public IEnumerable prolog_notify(object X) { { foreach (bool l2 in good(X)) { foreach (bool l3 in this.accept( X)) { yield return false; } } } { foreach (bool l2 in bad(X)) { foreach (bool l3 in this.reject( X)) { yield return false; } } } } } }