1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
|
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
* Yield Prolog 1.0.1 Released : it passes all but 9 of the 421 tests in the ISO Prolog test suite (97.8%).
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 });
}
----------- IMPORT EXTERNAL FUNCTIONS ----------
Using 'import' to call a static function
Taken mostly from http://yieldprolog.sourceforge.net/tutorial4.html
If we want to call a static function but it is not defined in the Prolog code, we can simply add an import directive.
(In Prolog, if you start a line with :- it is a directive to the compiler. Don't forget to end with a period.):
:- import('', [parent/2]).
uncle(Person, Uncle):- parent(Person, Parent), brother(Parent, Uncle).
The import directive has two arguments.
The first argument is the module where the imported function is found, which is always ''.
For C#, this means the imported function is in the same class as the calling function.
For Javascript and Python, this means the imported function is in the global scope.
The second argument to import is the comma-separated list of imported functions, where each member of the list is 'name/n', where 'name' is the name of the function and 'n' is the number of arguments.
In this example, parent has two arguments, so we use parent/2.
Note: you can use an imported function in a dynamically defined goal, or a function in another class.
:- import('', [parent/2]).
uncle(Person, Uncle) :- Goal = parent(Person, Parent), Goal, brother(Parent, Uncle).
:- import('', ['OtherClass.parent'/2]).
uncle(Person, Uncle) :- 'OtherClass.parent'(Person, Parent), brother(Parent, Uncle).
--------- Round-about Hello Wonderful world ----------
//yp
:-import('',[sayit/1]).
sayhello(X):-sayit(X).
//cs
public void default_event_state_entry()
{
llSay(0,"prolog hello.");
foreach( bool ans in sayhello(Atom.a(@"wonderful world") )){};
}
PrologCallback sayit(object ans)
{
llSay(0,"sayit1");
string msg = "one answer is :"+((Variable)ans).getValue();
llSay(0,msg);
yield return false;
}
------------------ UPDATES -----------------
Yield Prolog 1.0 Released : It passes all but 15 of the 421 tests in the ISO Prolog test suite.
New Features:
* Added support for Prolog predicates read and read_term.
* In see, Added support for a char code list as the input.
Using this as the input for "fred" makes
set_prolog_flag(double_quotes, atom) and
set_prolog_flag(double_quotes, chars) pass the ISO test suite.
Fixed Bugs:
* In atom_chars, check for unbound tail in the char list.
This makes atom_chars pass the ISO test suite.
* In current_predicate, also check for static functions.
This makes current_predicate pass the ISO test suite.
Known Issues:
Here are the 9 errors of the 421 tests in the ISO test suite in
YieldProlog\source\prolog\isoTestSuite.P .
Some of these have a good excuse for why Yield Prolog produces the error. The rest will be addressed in a future maintenance release.
Goal: call((fail, 1))
Expected: type_error(callable, (fail, 1))
Extra Solutions found: failure
Goal: call((write(3), 1))
Expected: type_error(callable, (write(3), 1))
Extra Solutions found: type_error(callable, 1)
Goal: call((1; true))
Expected: type_error(callable, (1 ; true))
Extra Solutions found: type_error(callable, 1)
Goal: (catch(true, C, write('something')), throw(blabla))
Expected: system_error
Extra Solutions found: unexpected_ball(blabla)
Goal: catch(number_chars(A,L), error(instantiation_error, _), fail)
Expected: failure
Extra Solutions found: instantiation_error
Goal: Goal: (X = 1 + 2, 'is'(Y, X * 3))
Expected: [[X <-- (1 + 2), Y <-- 9]]
Extra Solutions found: type_error(evaluable, /(+, 2))
Goal: 'is'(77, N)
Expected: instantiation_error
Extra Solutions found: N <-- 77)
Goal: \+(!, fail)
Expected: success
Extra Solutions found: failure
((X=1;X=2), \+((!,fail)))
Expected: [[X <-- 1],[X <-- 2]]
Extra Solutions found: failure
========================= 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.ScriptBaseClass {
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.ScriptBaseClass {
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;
}
}
}
}
} }
|