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
<PROLOG CODE>
//CS
<CS CODE>
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);
<CALL PROLOG CODE HERE>
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<bool> sayit(object ans)
{
llSay(0,"sayit1");
string msg = "one answer is :"+((Variable)ans).getValue();
llSay(0,msg);
yield return false;
}
//YPEncoded
public IEnumerable<bool> 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<bool> 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<bool> 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<bool> accept(object ans)
{
string msg = "Welcoming :"+decodeToString(ans);
llSay(0,msg);
yield return false;
}
public IEnumerable<bool> reject(object ans)
{
string msg = "Watching :"+decodeToString(ans);
llSay(0,msg);
yield return false;
}
//YPEncoded
public IEnumerable<bool> yp_nop_header_nop() {
{
yield return false;
}
}
public IEnumerable<bool> 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<bool> 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<bool> 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;
}
}
}
}
} }