-
Notifications
You must be signed in to change notification settings - Fork 0
/
prolog.r
1397 lines (1273 loc) · 43.7 KB
/
prolog.r
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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;; ============================================
;; Script: prolog.r
;; downloaded from: www.REBOL.org
;; on: 29-May-2013
;; at: 1:40:33.27762 UTC
;; owner: coccinelle [script library member who
;; can update this script]
;; ============================================
;; ==================================================
;; email address(es) have been munged to protect them
;; from spam harvesters.
;; If you were logged on the email addresses would
;; not be munged
;; ==================================================
REBOL [
Title: "Prolog Like Inference Engine"
Date: 08-sep-2005
Name: "PROLOG"
Version: 1.7
File: %prolog.r
Author: "Marco"
Category: []
Library: [
level: 'intermediate
platform: 'all
type: [dialect tool function]
domain: [dialects ai]
tested-under: [win]
support: %mvri--bluewin--ch
license: public-domain
see-also: none
]
History: [
{1.0 Initial version}
{1.1 Perfomrance improvement}
{1.2 Refactoring of unify and call?}
{1.3 English translation for www.rebol.org publication}
{1.4 Change misspelled wich by which (thanks Sunanda)}
{1.5 Correction of a small bug which appears with wiew 1.3}
{1.6 Correction of a small bug thanks to Martin}
{1.7 Add CALL hardcoded predicate and diagnostic (trial) engine}
]
Purpose: {
This is an inference engine wich process prolog like clause
The engine can process prolog like clauses of the form :
man [jean]
woman [mary]
human [X] [man [X]]
human [X] [woman [X]]
CUT (!) and FAIL are implemanted (it's the only hardcoded predicates in the engine)
The engine execute Rebol code placed in parenthesis (like in the parse function).
Parenthesis can be place either in the body of the clause
or as a parameter of predicates :
add [X Y (X + Y)]
human [X] [man [X] (print [X "is a human"])]
The engine consider that all words with a value that are not functions as vars.
Other words are taken as symbol.
Local vars are all words that start with an uppercase char or with underscore (_)
The anonyme var is implemented and can be either _ or none
A var is free if it's value is none, a var is bound if it's value is not none
The engine offers a set of pre-defined clauses (internal clausses)
like NOT, EQUAL?, IF, BOUND, FREE and REPEAT.
To add or remove clauses from a knowlege base use ASSERT and RETRACT function
To execute a goal, use GOAL or FOR-WHICH functions
To bench the engine use BENCH-GOAL function.
The call-trace and unify-trace words allows to enable or disable the trace capability of the engine.
}
]
; **********************************************************************************
; * Public interface.
; -------------------
; - assert allows you to add clauses to the knowledge
; - retract allows you to retract clauses from the knowledge base
; - goal allows you to get all the possible solution for a goal
; - for-which allows you to execute a block for each solution of the goal
; - bench-goal allows you to get the time to obtain all the solution for a goal
; - call-trace allows you to enable or disable the trace of goal calls
; - unify-trace allows you to enable or disable the trace of clause unification
; **********************************************************************************
assert: retract: goal: for-which: bench-goal: none
if not value? 'call-trace [call-trace: false]
if not value? 'unify-trace [unify-trace: false]
context compose [
; *******************
; * ASSERT function *
; *******************
;
; This function parse a block of clauses, transform it in a intenal form
; and append it to the knowledge base.
;
; The function creates a new base if none is passed as argument
;
; The internal format is a block that is directly used to make the call object.
;
; The parsing is done only to find the clauses but doesn't ckeck the validity of the body of the clause
; *****************************************************************************************************
set 'assert func [
"Create or update a KB block with parsed clauses. Return the KB block."
base [block! none!] "KB block or none for a new base"
clauses [block!] "Clauses block to be parsed"
/local rule name pattern goals clause
][
rule: [
['comment string!]
|
[name: word! pattern: block! goals: block! (
append-clause base to-word first name first pattern first goals
)]
|
[name: word! pattern: block! (
append-clause base to-word first name first pattern []
)]
]
if none? base [
base: copy []
]
if not parse clauses [some rule] [
make error! "Invalid clauses"
]
base
]
; ********************
; * RETRACT function *
; ********************
;
; This function rectract a clause from the knowledge base
;
; This function isn't completely tested and there is perhaps some bugs in it.
; ***************************************************************************
set 'retract func [
"Retract a clause from the block"
base [block!] "The KB block"
predicat [block!] "The predicate to retract"
/all
/local result p
][
result: base
name: pick predicat 1
if system/words/all [
clause-var? name
p: get name
][
name: p
]
either any [
equal? '_ name
clause-var? name
][
while [not empty? base] [
either remove-clause second base predicat all [
if empty? second base [
remove/part base 2
]
if not all [
break
]
][
base: skip base 2
]
]
][
while [not none? base: find/skip base name 2][
either remove-clause second base predicat all [
if empty? second base [
remove/part base 2
]
if not all [
break
]
][
base: skip base 2
]
]
]
result
]
; This is the model of the call object
; ************************************
clause: make object! [
predicat: [] ; the head of the clause
goals: [] ; the body of the clause
vars: [] ; the list of the locaql vars of the clause
save-predicat: none ; the saved head of the clause (used only for trace)
save-vars: none ; the saved vars values (restored just before to recall the gaol)
curr-goal: [] ; The surrent sub-goal
curr-goals: none ; The current list of sub-goal
curr-vars: none ; the current vars values
curr-clauses: none ; the current matched clause list
curr-call: none ; the current-call
curr-base: none ; the current knowledge base
curr-first: true ; A flag to determine if it's the first call (used only for trace)
state: [ ; the list of properties that represent the state of the call and to save in the stack
save-vars
curr-goal
curr-goals
curr-vars
curr-clauses
curr-call
curr-base
]
stack: make block! 500 ; the stack to save the states of the call
]
; Set of internal predicates.
;
; As said somewere else, nothing except CUT and FAIL is hardcoded
;
; These internal clauses are standards clauses
; ****************************************************************
internal: does [internal: assert none [
; NOT
not [X Y] [
X Y ! fail
]
not [_ _] [!]
; EQUAL?
equal? [X X] [!]
; NOT-EQUAL?
not-equal? [X X] [
! fail
]
not-equal? [_ _] [!]
; IF
if [X] [
equal? [false (not X)] !
]
; FREE
free [X] [
equal? [true (none? X)] !
]
; BOUND
bound [X] [
equal? [false (none? X)] !
]
; ADD
add [X Y (X + Y)][!]
add [X (Z - X) Z][!]
add [(Z - Y) Y Z][!]
; MULT
mult [X Y (X * Y)][!]
mult [X (Z / X) Z][!]
mult [(Z / Y) Y Z][!]
; REPEAT
repeat []
repeat [] [
repeat []
]
repeat [1] [!]
repeat [X] [
if [(X > 0)]
]
repeat [X] [
if [(X > 0)]
repeat [(X - 1)]
]
]]
; This function determine if a terme is a local var
; *************************************************
clause-var?: func [
X
/local p
][
all [
not equal? '_ X
word? X
any [
equal? "_" p: copy/part to-string X 1
not strict-equal? p lowercase copy p
]
]
]
; This function return a block with all the local var in a block
; ***************************************************************
get-clause-vars: func [
X
/local result
][
if any [
block? X
hash? X
list? X
][
result: copy []
foreach item X [
append result get-clause-vars item
]
return result
]
if all [
clause-var? X
][
return reduce [X]
]
return copy []
]
; This function transform a clause in the internal form
; *****************************************************
to-clause: func [
name [word!]
pattern [block!]
goals [block!]
/local vars result
][
result: compose/only [
predicat: (compose/only [(name) (pattern)])
goals: (goals)
vars: (vars: union get-clause-vars pattern get-clause-vars goals)
]
foreach item vars [
append result compose [(to-set-word item)]
]
if not empty? vars [append result [none]]
result
]
; Append a clause to the knowledge base
; *************************************
append-clause: func [
base [block!]
name [word!]
pattern [block!]
goals [block!]
/local clauses
][
if not clauses: select base name [
append base compose/only [(name) (clauses: copy [])]
]
append/only clauses to-clause name pattern goals
]
; This function remove a clause from the knowledge base
; *****************************************************
remove-clause: func [
base [block!]
predicat [block!]
flag [none! logic!]
/local save object result
][
local: copy []
save: copy []
result: false
local: get-clause-vars predicat
foreach item local [
append save either value? item [
get item
][
none
]
]
while [not empty? base] [
set local save
object: make clause first base
either unify predicat object/predicat [
base: remove base
result: true
if not flag [
break
]
][
base: skip base 1
]
]
set local save
result
]
; *************************************************************************************
; Unification of two termes function
; **********************************
; - unification is in the same time assignation and comparaison
; - the main idea is that if two terme are assigned, we compare them
; otherwise in case of vars, the unbound terme is assigned to the value of the other.
; - in case of block, each element is unified
; - the | indicates that the reste of the other block
; must be unified with the following terme
; - in case of parenthesis, the code within is executed
; - for the anonym vars _, the unification always occurs
; - the function stop as soon as the unification fails
; *************************************************************************************
; -----------------------------------------------------------------------------------------------------------------------------------------
; Performance consideration
; -------------------------
; In the first version I used a lot of recursive call of unify
; because it was easier to write the logic but I transform it
; into loop (mainly while) because it's faster (even if it's
; harder to understand and maintain the logic)
; -----------------------------------------------------------------------------------------------------------------------------------------
unify: func [
X [block! hash! list!]
Y [block! hash! list!]
/local V TX TY VX VY p q pX pY
][
; ************************************
; We loop on each elements of the list
; ************************************
while [on] [
if unify-trace [
print "Unification"
probe X
probe Y
]
; =====================
; If two blocks are :
; - different length
; - and without |
; the unification fails
; =====================
if not any [
equal? length? X length? Y
find X '|
find Y '|
][
if unify-trace [print "Case 1 --> Unification FAIL"]
return false
]
; ========================
; If two block are :
; - the same
; - without speciality ;-)
; unification occurs
; ========================
if all [
equal? X Y
not any [
find-special X
find-special Y
]
][
if unify-trace [print "Case 2 --> unification OK"]
return true
]
; ====================================
; This is the case when a terme is a |
; ====================================
if equal? '| pick X 1 [
either block-term? VX: get-term TX: pick X 2 [
if unify-trace [print "Case 3.X.1"]
change/part X VX 2
][
if all [
none? VX
var-term? TX
][
VX: TX
]
VX: compose/only [(VX)]
VY: compose/only [(Y)]
either unify VX VY [
if unify-trace [print "Case 3.X.2"]
return true
][
if unify-trace [print "Case 3.X.3"]
return false
]
]
]
if equal? '| pick Y 1 [
either block-term? VY: get-term TY: pick Y 2 [
if unify-trace [print "Case 3.Y.1"]
change/part Y VY 2
][
if all [
none? VY
var-term? TY
][
VY: TY
]
VX: compose/only [(X)]
VY: compose/only [(VY)]
either unify VX VY [
if unify-trace [print "Case 3.Y.2"]
return true
][
if unify-trace [print "Case 3.Y.2"]
return false
]
]
]
; =============================================
; If we are here, one of the two block is empty
; The unification fails
; =============================================
if any [
empty? X
empty? Y
][
if unify-trace [print "Case 4 --> unification FAIL"]
return false
]
; ================================================
; Here we consider the Y terme (TY)
; and for each case, we considere the X teme (TX)
; ================================================
VX: get-term TX: pick X 1
VY: get-term TY: pick Y 1
either none? VX [
if var-term? TX [
either none? VY [
if var-term? TY [
if unify-trace [print "Case 5.1"]
set TX TY
]
if unify-trace [print "Case 5.2"]
][
if unify-trace [print "Case 5.3"]
set TX VY
]
]
if unify-trace [print "Case 5.4"]
][
either none? VY [
if var-term? TY [
if unify-trace [print "Case 5.5"]
set TY VX
]
if unify-trace [print "Case 5.6"]
][
either all [
block-term? VX
block-term? VY
][
if not unify VX VY [
if unify-trace [print "Case 5.7"]
return false
]
if unify-trace [print "Case 5.8"]
][
if not equal? VX VY [
if unify-trace [print "Case 5.9"]
return false
]
if unify-trace [print "Case 5.10"]
]
]
]
; ==============================
; Here we loop to the next terme
; ==============================
x: next X
Y: next Y
]
]
; ****************************************************
; This function get the value of a term (can be a var)
; ****************************************************
get-term: func [
X
/deep
/local p q r
][
x: get-var X
if all [
paren? X
not error? p: try to-block X
][
X: p
]
if equal? '_ X [
x: none
]
if all [
deep
block-term? X
][
X: get-block X
]
X
]
; ************************************
; This function get the value of a var
; ************************************
get-var: func [
X
][
while [
var-term? X
][
X: get X
]
X
]
; ***************************************************
; This function remove as much as possible in a block
; ***************************************************
get-block: func [
X [block! hash! list!]
/local p q r block-rule
][
block-rule: [any [p:
(p: find-special2 p)
:p [
p: '_
(change/only p none) :p
|
p: '| set q [block! | hash! | list!]
(change/part p q 2) :p
|
p: '| set q word!
(either all [
var-term? q
block-term? q: get-term q
][
change/part p q 2
][
p: skip p 2
]) :p
|
p: set q paren!
(either not error? q: try to-block q [
change/only p q
][
p: next p
]) :p
|
p: set q word!
(either all [
var-term? q
not none? q: get-term q
][
change/only p q
][
p: next p
]) :p
|
set q [block! | hash! | list!]
(parse q block-rule)
|
skip
]]]
parse X block-rule
X
]
; ***********************************************
; This function determine if the terme is special
; ***********************************************
special-rule: [
[to none! | to '_ | to word! | to paren! | to block! | to hash! | to list!]
to end
]
find-special: func [
X [block! hash! list!]
][
parse x special-rule
]
; ******************************************
; This function find the first special terme
; ******************************************
find-special2: func [
X [block! hash! list!]
/local p q r
][
q: index? tail p: X
parse X [
[to '_ r: (q: minimum q index? r) | none] :p
[to '| r: (q: minimum q index? r) | none] :p
[to word! r: (q: minimum q index? r) | none] :p
[to block! r: (q: minimum q index? r) | none] :p
[to hash! r: (q: minimum q index? r) | none] :p
[to list! r: (q: minimum q index? r) | none] :p
]
at head X q
]
; *******************************************
; This function determine if a terme is a var
; *******************************************
var-term?: func [X][
all [
not equal? '_ X
word? X
value? X
not any-function? get X
]
]
; ************************************************************************
; This function determine if the terme is a list for the engine (a block?)
; ************************************************************************
block-term?: func [
X
][
find reduce [block! hash! list!] type? X
]
; **************************************************************************************
; Here we have logic that does the call of sub-goal, the matching and backward chaining
;
; The main idea for backward chaining is to keep the state of the call within an object
; and to stack the sate of the object in a block, so the state of the object
; can be restore when a backward chaining si done.
;
; The main functions are :
; - goal which is a public function allowing the call of goal
; - for-which which is also a public function allowing call of goal
; - call? which contain the logic to call every sub-goal of a goal,
; and does backward chaining
; - next-goal which calculates the next goal to call,
; clear the stack when a cut (|) is found
; and process the code placed within parenthesis
; - match-clauses that extract clauses that match the current processed sub-goal
;
; Other function are defined here :
; - bench-goal which is a bench helper
; - free-call that free that set to none all the properties of a call object
; - append-match which is used by match-clauses
; - to-vars-block which is use by for-which
; - remove-clause which is used when a cut (|) is found to remove
; the current clause in the parent call object
; **************************************************************************************
; -----------------------------------------------------------------------------------------------------------------------------------------
; Performance consideration
; -------------------------
; - All the methods are outside the object because the logic can do a lot of make
; of the object that store the state of the call.
; - I notice that to make an object with many method (function)
; is slower than an object with few method
; - But I also notice that using path to get the properties of object
; is also longer than accessing these properties a method of the object
; - In the call? function I also try to use "use" function
; (use bind/copy o in o 'self [......]) to avoid the use of path but it doesn't work.
; - "reduce" or "set" work well with "bind/copy o in o 'self" but "use" doesn't.
; - So I don't know what is the best, slow make and fast method or fast make
; and slow function. The best would be fast-fast solution
;
; - For the stack, I insert and remove data at the head of the block.
; - I do not find better performance to append data at the end of the stack
; - I tried to use list but the behavior of function on list is not the same
; as block so I didn't go further in this way.
;
; -----------------------------------------------------------------------------------------------------------------------------------------
; *****************************
; Function which execute a goal
; *****************************
set 'goal func [
"Try a goal and return the number of solution"
base [block!] "The KB to use"
goals [block!] "The goals to try"
/local curr-call i
][
curr-call: make clause to-clause 'goal [] goals
i: 0
while [call? curr-call base none] [i: i + 1]
i
]
; *****************************************************************
; Function that execute a block of code for each solution of a goal
; *****************************************************************
set 'for-which func [
[throw]
base [block!] "The KB to use"
'word [word! block!] "The word or block of word to set for each solutions (will be local)"
goals [block!] "The goals to try"
body [block!] "The block to evaluates for each solutions"
/local curr-call
][
word: to-vars-block word
curr-call: make clause to-clause 'for-which word goals
do compose/only/deep [
use (word) [
while [call? curr-call base none] (compose/deep [
set [(word)] reduce second curr-call/predicat
(body)
])
]
]
]
; *********************************
; Function which help to do a bench
; *********************************
set 'bench-goal func [
"Try a goal and return the number of solution"
count [integer!]
base [block!] "The KB to use"
goals [block!] "The goals to try"
/local curr-call curr-clause
][
curr-clause: to-clause 'bench [] goals
bench count [
curr-call: make clause curr-clause
while [call? curr-call base none] []
]
]
; ***************************************************************************************
; Function that call a goal
; - This is the main logic of the inference engine
; - It find the next goal to call, match the goal in the knowledge base, and call the sub goal
; - It's one of the most difficult logic of the script
; - This function is re-called while true is returned (to find all possible solutions)
; - What is done :
; - if it's the first call :
; - initialize the call object
; - determine the next sub-goal
; - when the first sub-goal is fail, return false (the goal is not satisfied)
; - if there no sub-goal, return true (it's a fact and the goal is stisfied)
; - if it's not the first call :
; - if the curr-goal is empty, return false (it's the second call of a fact)
; - after this initialization, a loop is done while the sub-goal list is not empty
; - if there is a call to do :
; - restore the variables of the call object
; - call the goal solved hre
; - resolve the variables (some are not solved during unification so they are solved here)
; - determine the next goal
; - if the call is successfull and also the next-goal function doesn't find a fail
; - if the sub-goal list is empty, return true (the goal is satisfied)
; - otherwise, if the current sub-goal is empty
; - free the call object
; - return false (the goal fail)
; - if there is no current-call, determine the next call to do
; - restore the current sucb-goal
; - restore the current variables
; - loop while the clauses list is not empty
; - make a new call object bound with the current call
; - try to unify the current sub-goal with the head of the call
; - if unificytion is OK, save the varables and break the loop
; - otherwise, restore the current sub-goal, the current variable and loop to the next clause
; - if there is still no call to do afeter the unification
; - if the stack is empty :
; - free the current call
; - return false (the goal is not satisfied)
; - if the stack is not empty,
; - pull the previous state (backward chaining) and loop
; ***************************************************************************************
call?: func [
o [object!]
base [block!]
parent-clauses [block! none!]
/local curr-goal X p q
][
; ================================================================================
; If the list of current sub-goal is none it's the first call
; (the call object is not initialized)
; - initialize the call object
; - determine the next sub-goal
; - if a fail is encountered, return false (the goal is not satisfied)
; - if the next goal is empty, return true (it's a fact)
; Otherwise, it's a second, third, etc... call :
; - if the current su-goal is empty, return false (it's the second call of a fact)
; - otherwise, continue the processing.
; ================================================================================
either none? o/curr-goals [
o/curr-goal: copy []
o/curr-goals: copy o/goals
o/curr-vars: copy/deep reduce o/vars
o/curr-clauses: copy []
o/curr-call: none
o/curr-first: true
o/curr-base: base
o/save-predicat: copy/deep o/predicat
o/save-vars: copy/deep o/curr-vars
if call-trace [
print ["CALL" mold/only get-block copy/deep o/predicat]
]
curr-goal: next-goal o base parent-clauses
if not curr-goal [
if call-trace [
print ["NO solution"]
]
free-call o
return false
]
if empty? curr-goal [
if call-trace [
print ["RETURN 1" mold/only get-block copy/deep o/predicat]
]
return true
]
][
if empty? o/curr-goal [
return false
]
o/predicat: copy/deep o/save-predicat
if call-trace [
print ["REDO" mold/only o/predicat]
o/curr-first: false
]
]
; ================================================
; Main loop (while there is a sub-goal to process)
; ================================================
while [not empty? o/curr-goals][
; --------------------------------------------------------------------------
; If there is a call to do :
; - restore the vars value
; - Call the sub-goal
; - resolve the variables (if the call is successfull)
; - determine the next sub-goal to do
; - if the call and naxt-goal are successfull :
; - if the next sub-goal is empty (there is no more sub-goal to do)
; - return true (the goal is satisfied)
; - otherwise
; - if the next sub-goal is empty (only when the last subgoal is a fail) :
; - free the call object
; - return false (the goal is not satisfied)
; --------------------------------------------------------------------------
if o/curr-call [
set o/vars copy/deep o/save-vars
either if call? o/curr-call o/curr-base o/curr-clauses [
foreach p o/vars [
if not none? q: get-term/deep p [
set p q
]
]
curr-goal: next-goal o base parent-clauses
][
if empty? curr-goal [
if call-trace [
print ["RETURN 2" mold/only get-block copy/deep o/predicat]
]
return true