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;
      }
    }
  }
}

} }