forked from geckom/ChatScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinfer.cpp
1396 lines (1306 loc) · 48.6 KB
/
infer.cpp
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
// infer.cpp - handles queries into fact data
#include "common.h"
#define MAX_PARENTS 9980
#define MAX_QUEUE 10000
#define FOLLOW_LIMIT 50
static MEANING parents[MAX_PARENTS+20]; // nodes above where we are now
static int parentIndex = 0; // add into parents at here
static int parentWalk = 0; // retrieve from parents starting here. when reach parentIndex you have run out
static unsigned int currentBaseInfer; // start of reserved marks
static unsigned int currentFreeInfer; // marks before this are reserved
unsigned int inferMark = 0; // primary "been-here" mark for all inferencing and tree traversals
static unsigned int saveMark = 0; // tertiary mark - used in zone 1 control
static unsigned int ignoremark = 0; // mark on entries to ignore
static WORDP fact = 0;
#define ORIGINALWORD 0x00000001
#define NORMAL 0x00000002 // class and set are a group (recursive)
#define QUOTED 0x00000004 // class and set are simple words
#define PREMARK 0X00000008 // marking but save word not meaning so wont use as scan
#define NOQUEUE 0X00000010 // just marked with mark, not queued
#define QUEUE 0X00000020 // add to q
#define NOTOPIC 0X00000040 // dont follow topic/set names
#define BLOCKMEANING 0X00000080
#define FACTTYPE 0X00000100
#define FINDTOPIC 0X00000200
#define UPDICTIONARY 0X00000400
#define USERFACTS 0X00000800
#define SYSTEMFACTS 0X00001000
#define FINDCONCEPT 0X00002000
// 4 8 unused
#define USE_ORIGINAL_SUBJECT 0x00010000 // use subject as fact source
#define USE_ORIGINAL_OBJECT 0x00020000 // use object as fact source
#define RICCOCHET_USING_SUBJECT 0x00040000
#define RICCOCHET_USING_OBJECT 0x00080000
#define RICCOCHET_BITS ( USE_ORIGINAL_SUBJECT | USE_ORIGINAL_OBJECT | RICCOCHET_USING_SUBJECT | RICCOCHET_USING_OBJECT )
// queued entries pending scanning
static MEANING queue[MAX_QUEUE+20];
static unsigned int queueIndex;
// answers from inferences go in these sets
FACT* factSet[MAX_FIND_SETS+2][MAX_FIND+1];
int factFlags[MAX_FIND+1];
int factIndex[MAX_FIND+1];
unsigned int factSetNext[MAX_FIND_SETS+1]; // when walking a set over time, which index to continue from
static void AddSet2Scan(unsigned int flags,WORDP D,int depth);
unsigned int NextInferMark() // set up for a new inference
{
return ++inferMark;
}
FACT* IsConceptMember(WORDP D)
{
if (!D) return NULL;
FACT* F = GetSubjectNondeadHead(D);
while (F)
{
if (F->verb == Mmember) return F; // is a concept member so it is ok
F = GetSubjectNondeadNext(F);
}
return NULL;
}
static bool IsExcluded(WORDP set,WORDP item)
{
if (!(set->internalBits & HAS_EXCLUDE)) return false;
FACT* F = GetObjectNondeadHead(set);
while (F)
{
if (F->verb == Mexclude && Meaning2Word(F->subject) == item) break;
F = GetObjectNondeadNext(F);
}
return (F) ? true : false;
}
static bool SetContains1(MEANING set,MEANING M, unsigned int depth)
{
if (!M || !set) return false;
// the word
WORDP D = Meaning2Word(M);
unsigned int index = Meaning2Index(M);
D->inferMark = inferMark;
FACT* F = GetSubjectNondeadHead(D);
WORDP D1 = Meaning2Word(set);
// we walk up the tree from the word and see if it runs into D1, the set.
if (depth == 0)
{
SetFactBack(D,0);
if (trace & TRACE_INFER && CheckTopicTrace()) Log(STDTRACELOG,(char*)" %s ? %s ",D->word,D1->word);
}
unsigned int counter = 20000;
while (F && --counter)
{
if (index != 0 && F->subject != M); // fact doesnt apply
else if (F->verb == Mmember)
{
// if this topic or concept has exclusions, check to see if this is a marked exclusion
bool blocked = false;
WORDP object = Meaning2Word(F->object);
if (object->internalBits & HAS_EXCLUDE)
{
FACT* G = GetObjectNondeadHead(object);
while (G && !blocked)
{
if (G->verb == Mexclude && Meaning2Word(G->subject)->inferMark == inferMark) blocked = true;
else G = GetObjectNondeadNext(G);
}
}
// since this is not a marked exclusion, we can say it is a member
if (F->object == set && !blocked)
{
if (trace & TRACE_INFER && CheckTopicTrace()) // show the path from set back to word
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"within: %s ",D1->word);
WORDP path = Meaning2Word(F->subject);
while (path)
{
Log(STDTRACELOG,(char*)" %s ",path->word);
FACT* prior = Index2Fact(GetFactBack(path));
path = (prior) ? Meaning2Word(prior->subject) : 0;
}
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"");
}
return true;
}
if (!blocked && object->inferMark != inferMark)
{
SetFactBack(object,Fact2Index(F));
if (SetContains1(set,F->object,depth + 1)) return true;
}
}
else if (F->verb == Mis) // a link up the wordnet ontology
{
if (F->object == set) return true;
WORDP object = Meaning2Word(F->object);
if (object->inferMark != inferMark)
{
SetFactBack(object,Fact2Index(F));
if (SetContains1(set,F->object,depth + 1)) return true;
}
}
F = GetSubjectNondeadNext(F);
}
if (trace & TRACE_INFER && depth == 0 && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)" not within ");
}
return false;
}
bool SetContains(MEANING set,MEANING M)
{
bool answer = SetContains1(set,M,0);
ClearBacktracks();
return answer;
}
static bool AllowedMember(FACT* F, unsigned int i,unsigned int is,unsigned int index)
{
if (trace & TRACE_INFER && CheckTopicTrace()) TraceFact(F);
unsigned int localIndex = Meaning2Index(F->subject);
unsigned int pos = GetMeaningType(F->subject);
bool bad = false;
if (!i && pos )
{
if (pos & VERB && is & NOUN)
{
bad = true;
}
else if (pos & NOUN && is & VERB)
{
bad = true;
}
else if ((pos & ADJECTIVE || localIndex & ADVERB) && is & (NOUN|VERB))
{
bad = false;
}
}
else if (index && pos && pos != index) bad = true;
return !bad;
}
static void QueryFacts(WORDP original, WORDP D,unsigned int index,unsigned int store,char* kind,MEANING A)
{
if (!D || D->inferMark == inferMark) return;
D->inferMark = inferMark;
FACT* F;
FACT* G = GetSubjectNondeadHead(D);
unsigned int count = 20000;
unsigned int restriction = 0;
if (kind) // limitation on translation of word as member of a set
{
if (!strnicmp(kind,(char*)"subject",7)) restriction = NOUN;
else if (!strnicmp(kind,(char*)"verb",4)) restriction = VERB;
else if (!strnicmp(kind,(char*)"object",7)) restriction = NOUN;
}
while (G)
{
F = G;
G = GetSubjectNondeadNext(G);
if (trace & TRACE_INFER && CheckTopicTrace()) TraceFact(F);
if (!--count)
{
ReportBug((char*)"matchfacts infinite loop")
break;
}
uint64 flags = F->flags;
unsigned int fromindex = Meaning2Index(F->subject);
if (fromindex == index || !fromindex); // we allow penguins to go up to bird, then use unnamed bird to go to ~topic
else if (index ) continue; // not following this path
else if (flags & ORIGINALWORD) continue; // you must match exactly- generic not allowed to match specific wordnet meaning- hierarchy BELOW only
if (F->verb == Mmember && !AllowedMember(F,0,restriction,0)) continue; // POS doesn't match
if (F->verb == Mmember && !(flags & ORIGINALWORD))
{
WORDP object = Meaning2Word(F->object);
if (object->inferMark != inferMark)
{
if (*object->word == '~') // set, not a word association
{
if (IsExcluded(object,original)) continue; // explicitly excluded from this set
if (object->internalBits & TOPIC)
{
int topic = FindTopicIDByName(object->word);
if (topic && !(GetTopicFlags(topic) & TOPIC_SYSTEM) && HasGambits(topic)) AddFact(store,CreateFact(MakeMeaning(original,0),A,MakeMeaning(object,0),FACTTRANSIENT));
}
}
QueryFacts(original,object,0,store,kind,A);
}
}
}
}
FunctionResult QueryTopicsOf(char* word,unsigned int store,char* kind) // find topics referred to by word
{
SET_FACTSET_COUNT(store,0);
NextInferMark();
WORDP D = FindWord(word,0);
QueryFacts(D,D,0,store,kind,MakeMeaning(FindWord((char*)"a")));
if (trace & TRACE_INFER && CheckTopicTrace()) Log(STDTRACELOG,(char*)"QueryTopics: %s %d ",word,FACTSET_COUNT(store));
impliedSet = ALREADY_HANDLED;
return NOPROBLEM_BIT;
}
static bool AddWord2Scan(int flags,MEANING M,MEANING from,int depth,unsigned int type) // mark (and maybe queue) this word + implied wordnet up hierarchy + auto-equivalences
{
if (queueIndex >= MAX_QUEUE || !M) return false;
if (type && !(M & type) && GETTYPERESTRICTION(M)) return false; // not valid type restriction
// mark word or abandon marking
WORDP D = Meaning2Word(M);
unsigned int index = Meaning2Index(M);
if (D->inferMark == saveMark || (ignoremark && D->inferMark == ignoremark)) return false; // marked with a current mark
if (depth > FOLLOW_LIMIT)
{
ReportBug((char*)"Exceeding follow limit %s\r\n",D->word)
return false;
}
// concept set has exclusions, so if excluded is already marked, do not allow this topic to be marked
if (D->internalBits & HAS_EXCLUDE)
{
FACT* G = GetObjectNondeadHead(D);
while (G)
{
if (G->verb == Mexclude && Meaning2Word(G->subject)->inferMark == saveMark) return false;
G = GetObjectNondeadNext(G);
}
}
D->inferMark = saveMark;
if (flags & QUEUE) queue[queueIndex++] = M;
if (trace & TRACE_QUERY && CheckTopicTrace())
{
static char last[1000];
if (from)
{
char* mean = WriteMeaning(from);
if (stricmp(last,mean))
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"(%s=>) ",mean);
if (strlen(mean) > 999) ReportBug("Scan insert > 1000")
else strcpy(last,mean);
}
}
Log(STDTRACELOG,(flags & QUEUE) ? (char*)" %s+" : " %s. ",WriteMeaning(M));
}
// auto check all equivalences as well
FACT* F = GetSubjectNondeadHead(D);
while (F)
{
if (F->verb == Mmember) // can be member of an ordinary word (like USA member United_States_of_America), creates equivalence
{
WORDP D = Meaning2Word(F->object);
if (*D->word != '~') AddWord2Scan(flags,F->object,F->subject,depth+1,type); // member is not to a set, but to a word. So it's an equivalence
}
F = GetSubjectNondeadNext(F);
}
// and if item is generic, all synsets
if (index == 0 && !(flags & ORIGINALWORD))
{
unsigned int count = GetMeaningCount(D);
for (unsigned int i = 1; i <= count; ++i) AddWord2Scan(flags,GetMeaning(D,i),M,depth+1,type);
}
return true;
}
static bool AddWordOnly(int flags,char* word,unsigned int type) // mark (and maybe queue) this word
{
if (queueIndex >= MAX_QUEUE || !*word) return false;
// mark word or abandon marking
WORDP D = StoreWord(word,AS_IS);
if (D->inferMark == saveMark) return false; // marked with a current mark
D->inferMark = saveMark;
if (flags & QUEUE) queue[queueIndex++] = MakeMeaning(D);
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(flags & QUEUE) ? (char*)" %s+" : (char*)" %s. ",D->word);
return true;
}
static void AddWordOrSet2Scan(unsigned int how, char* word,int depth)
{
++depth;
if (!(how & ORIGINALWORD) && *word == '~' && word[1]) // recursive on set and all its members
{
WORDP D = FindWord(word,0);
if (D)
{
if (how & NOTOPIC && D->internalBits & TOPIC) {;}
else if (AddWord2Scan(how, MakeMeaning(D,0),0,depth,0)) AddSet2Scan(how,D,depth); // mark the original name and then follow its members
}
}
else
{
if (*word == '\'')
{
// WORDP D = FindWord(word);
//if (!D)
++word; // but dont harm 'tween_decks which is natural
}
AddWord2Scan(how, ReadMeaning(word, true, true), 0, depth, 0);
}
}
static void AddSet2Scan(unsigned int how,WORDP D,int depth)
{
++depth;
FACT* F = GetObjectNondeadHead(D);
while (F)
{
if (F->verb == Mmember) AddWordOrSet2Scan(how | (F->flags & ORIGINALWORD),Meaning2Word(F->subject)->word,depth);
F = GetObjectNondeadNext(F);
}
}
// used by query setup - scans noun hierarchies upwards for inference
static void ScanHierarchy(MEANING T,int savemark,unsigned int flowmark,bool up,unsigned int flag, unsigned int type)
{
if (!T) return;
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)"\r\nHierarchy: (%s=>) ",WriteMeaning(T));
if (!AddWord2Scan(flag,T,0,0,type)) return;
parentIndex = parentWalk = 0;
parents[parentIndex++] = T;
WORDP A = Meaning2Word(T);
int index = Meaning2Index(T);
int start = 1;
// find its wordnet ontological meanings, they then link synset head to synset head
// automatically store matching synset heads to this also -- THIS APPLIES ONLY TO THE ORIGINAL WORDP
MEANING* onto = GetMeaningsFromMeaning(T);
unsigned int size = GetMeaningCount(A);
if (!up || flag & BLOCKMEANING ) size = 0;// we are a specific meaning already, so are the synset head of something - or are going down
else if (index) start= size = index; // do JUST this one
else if (!size) size = 1; // even if no ontology, do it once for
for (unsigned int k = start; k <= size; ++k) // for each meaning of this word, mark its synset heads
{
MEANING T1;
if (GetMeaningCount(A))
{
T1 = (MEANING)(ulong_t)onto[k]; // this is the synset ptr.
if (T1 & SYNSET_MARKER) T1 = MakeMeaning(A,k) | SYNSET_MARKER;
else T1 = GetMaster(T1);
if (type && !(T1 & type)) continue;
if (! AddWord2Scan(flag,T1,T,0,type)) continue; // either already marked OR to be ignored
parents[parentIndex++] = T1;
}
}
while (parentWalk < parentIndex) // walk up its chains in stages
{
T = parents[parentWalk++];
if (!T) continue;
if (parentIndex > MAX_PARENTS) break; // overflow may happen. give up
WORDP D = Meaning2Word(T);
unsigned int index = Meaning2Index(T);
// now follow facts of the synset head itself or the word itself.
FACT* F = GetSubjectNondeadHead(D);
while (F)
{
WORDP verb = Meaning2Word(F->verb);
FACT* G = F;
if (trace & TRACE_QUERY && CheckTopicTrace()) TraceFact(F);
F = GetSubjectNondeadNext(F);
if (verb->inferMark != flowmark) continue;
// if the incoming ptr is generic, it can follow out any generic or pos_generic reference.
// It cannot follow out a specific reference of a particular meaning.
// An incoming non-generic ptr is always specific (never pos_generic) and can only match specific exact.
if (index && T != G->subject) continue; // generic can run all meanings out of here
MEANING x = G->object;
if (type && GETTYPERESTRICTION(G->subject ) && !(type & GETTYPERESTRICTION(G->subject ))) continue; // fact has bad type restriction on subject
if (!AddWord2Scan(flag,x,G->subject,0,type)) continue; // either already marked OR to be ignored
parents[parentIndex++] = x;
}
}
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)"\r\n");
}
static bool Riccochet(unsigned int baseFlags, FACT* G,int set,unsigned int limit,unsigned int rmarks,unsigned int rmarkv, unsigned int rmarko)
{// use two fields to select a third. Then look at facts of that third to find a matching verb.
FACT* F;
WORDP D1;
if (G->flags & (FACTSUBJECT|FACTOBJECT)) // we cant get here if the wrong field is a fact.
{
D1 = fact;
F = (baseFlags & USE_ORIGINAL_SUBJECT) ? Index2Fact(G->subject) : Index2Fact(G->object);
}
else
{
D1 = (baseFlags & USE_ORIGINAL_SUBJECT) ? Meaning2Word(G->subject) : Meaning2Word(G->object);
F = (baseFlags & RICCOCHET_USING_SUBJECT) ? GetSubjectNondeadHead(D1) : GetObjectNondeadHead(D1);
}
if (trace & TRACE_QUERY && CheckTopicTrace())
{
WORDP S = (G->flags & FACTSUBJECT) ? fact : Meaning2Word(G->subject);
WORDP V = (G->flags & FACTVERB) ? fact : Meaning2Word(G->verb);
WORDP O = (G->flags & FACTOBJECT) ? fact : Meaning2Word(G->object);
char* use = (baseFlags & RICCOCHET_USING_SUBJECT) ? (char*) "subjectfield" : (char*) "objectfield";
if (baseFlags & USE_ORIGINAL_SUBJECT) Log(STDTRACELOG,(char*)"Riccochet incoming (%s %s %s) via subject %s using %s\r\n",S->word,V->word,O->word,D1->word,use);
else Log(STDTRACELOG,(char*)"Riccochet incoming (%s %s %s) via object %s using %s\r\n",S->word,V->word,O->word,D1->word,use);
}
// walk all facts at node testnig for riccochet
while (F) // walk_of_S3
{
if (trace & TRACE_QUERY && CheckTopicTrace()) TraceFact(F);
FACT* I = F;
if (D1 == fact) F = NULL; // only the 1 main fact
else F = (baseFlags & RICCOCHET_USING_SUBJECT) ? GetSubjectNondeadNext(F) : GetObjectNondeadNext(F);
if (I->flags & FACTDEAD) continue; // cannot use this
// reasons this fact is no good
if (I->flags & MARKED_FACT) continue; // already seen this answer
if ((baseFlags & SYSTEMFACTS && I > factLocked) || (baseFlags & USERFACTS && I <= factLocked) ) continue; // restricted by owner of fact
if (rmarks && (I->flags & FACTSUBJECT || Meaning2Word(I->subject)->inferMark != rmarks)) continue; // mark must match
if (rmarkv && (I->flags & FACTVERB || Meaning2Word(I->verb)->inferMark != rmarkv)) continue; // mark must match
if (rmarko && (I->flags & FACTOBJECT || Meaning2Word(I->object)->inferMark != rmarko)) continue; // mark must match
I->flags |= MARKED_FACT;
AddFact(set,I);
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)" Found:");
TraceFact(I);
}
if (FACTSET_COUNT(set) >= limit) return false;
}
return true;
}
static bool ConceptPropogateTest(MEANING M,unsigned int mark,unsigned int depth) // is this meaning ultimately in a marked set
{
if (!depth)
{
NextInferMark();
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)"\r\n ~propogate: ");
}
FACT* F = GetSubjectNondeadHead(M);
while (F)
{
if (F->verb == Mmember && F->subject == M)
{
MEANING O = F->object;
WORDP D = Meaning2Word(O);
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)" %d->%s ",depth,D->word);
if (D->inferMark == mark) return true; // this is what we seek
if (D->inferMark != inferMark)// not already visited this pass or is marked for current query for other use
{
if (D->inferMark < currentBaseInfer || D->inferMark >= currentFreeInfer) D->inferMark = inferMark; // safely set seen this (efficiency)
if (*D->word == '~' && ConceptPropogateTest(O,mark,depth+1)) return true; // NOT setting been here- and dont follow fake members ( word member word)
}
}
F = GetSubjectNondeadNext(F);
}
return false;
}
unsigned int Query(char* kind, char* subjectword, char* verbword, char* objectword, unsigned int count, char* fromset, char* toset, char* propogate, char* match)
{
int store = GetSetID(toset);
if (store == ILLEGAL_FACTSET) store = 0;
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACETABLOG,(char*)"QUERY: @%d %s ",store,kind);
WORDP C = FindWord(kind,0);
if (!C || !(C->internalBits & QUERY_KIND))
{
ReportBug((char*)"Illegal query name: %s",kind)
return 0;
}
char copy[MAX_WORD_SIZE]; // hold the actual control value, so we can overwrite it
char* control = NULL;
if (C->w.userValue && *C->w.userValue)
{
strcpy(copy,C->w.userValue);
control = copy;
}
else
{
ReportBug((char*)"query control lacks data %s",kind)
return 0;
}
// get correct forms of arguments - _ is an empty argument, but legal
char word[MAX_WORD_SIZE];
int n;
if (!strchr(subjectword,' ')) // anything with a natural space in it should be left alone
{
n = BurstWord(subjectword);
if (n > 1) strcpy(subjectword,JoinWords(n,false));
}
if (!strchr(verbword,' ')) // anything with a natural space in it should be left alone
{
n = BurstWord(verbword);
if (n > 1) strcpy(verbword,JoinWords(n,false));
}
if (!strchr(objectword,' ')) // anything with a natural space in it should be left alone
{
n = BurstWord(objectword);
if (n > 1) strcpy(objectword,JoinWords(n,false));
}
if (!strchr(match,' ')) // anything with a natural space in it should be left alone
{
n = BurstWord(match);
if (n > 1) strcpy(match,JoinWords(n,false));
}
if (!strchr(propogate,' ')) // anything with a natural space in it should be left alone
{
n = BurstWord(propogate);
if (n > 1) strcpy(propogate,JoinWords(n,false));
}
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACETABLOG,(char*)" control: %s s/v/o:[%s %s %s] count:%d ",control,subjectword,verbword,objectword,count);
if (*fromset != '?') Log(STDTRACELOG,(char*)"fromset:%s ",fromset);
if (*toset != '?') Log(STDTRACELOG,(char*)"toset:%s ",toset);
if (*propogate != '?') Log(STDTRACELOG,(char*)"propogate:%s",propogate);
if (*match != '?') Log(STDTRACELOG,(char*)"match:%s",match);
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"");
}
// handle what sets are involved
if (impliedOp == 0 || impliedOp == '=') SET_FACTSET_COUNT(store,0); // auto kill content
else if (impliedOp != '+')
{
SET_FACTSET_COUNT(store,0); // we dont support other ops like -=
return 0;
}
int from = GetSetID(fromset);
if (from == ILLEGAL_FACTSET) from = 0;
unsigned int baseFlags = 0;
if (!stricmp(fromset,(char*)"user")) baseFlags |= USERFACTS;
if (!stricmp(fromset,(char*)"system")) baseFlags |= SYSTEMFACTS;
queueIndex = 0;
ignoremark = 0;
unsigned int baseMark = inferMark; // offsets of this value
currentBaseInfer = baseMark + 1; // start of used marks
// process initialization
nextsearch: // can do multiple searches, thought they have the same basemark so can be used across searchs (or not) up to 9 marks
#ifdef INFORMATION
# first segment describes what to initially mark and initially queue for processing (sources of facts)
# Values:
# 1..9 = set global tag to this label - 0 means turn off global tag
# i = use argument tag on words to ignore during a tag or queue operation
# Next char is tag label. 0 means no ignoremark
# s/v/o/p/m/~set/tick-word = use subject/verb/object/progogate/match/factset argument as item to process or use named set or given word
# S/V/O choice is a fact id
# This is automatically marked using the current mark and is followed by
# q = queue items (sets will follow to all members recursively and wordnet identities will propogate up)
# Q = queue this exact word only
# t = tag (no queue) items
# e = expandtag (no queue) (any set gets all things below it tagged)
# h = tag propogation from base (such propogation might be large)
# 1ST char after h is mark on verbs to propogate thru
# 2nd char is t or q (for tag or mark/queue)
# 3rd char (< >) after h is whether to propogate up from left/subject to object or down/right from object to subject when propogating
# n = implied all topics marked to ignore on object
# f = use given facts in from as items to process -- f@n means use this set
# This is followed by
# s/v/o/f = use corresponding field of fact or entire fact
# the value to process will be marked AND may or may not get stored, depending on following flag being q or m
#endif
// ZONE 1 - mark and queue setups
char myset[10];
int baseOffset = 0; // facts come from this side, and go out the verb or other side
char* choice;
char* at;
int qMark = 0;
int mark = 0;
int whichset = 0;
fact = StoreWord((char*)"fact");// for foreign languages insure word is there
char maxmark = '0'; // deepest mark user has used
if (trace & TRACE_QUERY && CheckTopicTrace())
{
// convert all _ and periods to spaces for easier viewing
char* underscore;
while ((underscore = strchr(control,'_'))) *underscore = ' ';
char* colon = strchr(control,':');
if (colon) *colon = 0;
Log(STDTRACELOG,(char*)"@@@ Control1 mark/queue: %s\r\n",control);
if (colon) *colon = ':';
Log(STDTRACETABLOG,(char*)"");
}
--control;
bool facttype = false;
while (*++control && *control != ':' )
{
choice = NULL;
switch(*control)
{
case '_': case '.': case ' ': // does nothing per se
continue;
case '0': // means NO savemark
saveMark = 0;
continue;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // set current marks
saveMark = baseMark + *control - '0';
if (*control > maxmark) maxmark = *control;
continue;
case 'n': // ignore all member facts involving topic as object
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)" ignore all member facts w topics as objects ");
++control;
baseFlags |= NOTOPIC;
continue;
case '~': case '\'': // use named set or named word
choice = control;
at = strchr(control,'.'); // set name ends with _ or . or space (generated)
if (!at) at = strchr(control,'_');
if (!at) at = strchr(control,' ');
if (!at)
{
ReportBug((char*)"Couldnt find end of name %s in control",control)
return 0; // couldn't find end of name
}
*at = 0;
control = at; // skip past to end
break;
case 'i':
++control;
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)" ignore #%c results ", *control);
ignoremark = (*control == '0') ? 0 : (baseMark + (*control - '0'));
break;
case 's':
choice = subjectword;
mark = 0;
break;
case 'S':
choice = subjectword;
mark = 0;
facttype = true;
break;
case 'v': // automatically quote verbs. NEVER let them wander
if (*verbword == '\'') strcpy(word,verbword);
else
{
*word = '\'';
strcpy(word+1,verbword);
}
choice = word;
mark = 1;
break;
case 'V':
choice = verbword;
mark = 1;
facttype = true;
break;
case 'o':
choice = objectword;
mark = 2;
break;
case 'O':
choice = objectword;
mark = 2;
facttype = true;
break;
case 'p':
choice = propogate;
break;
case 'm':
choice = match;
break;
case 'f': // we have incoming facts to use
whichset = (control[1] == '@') ? GetSetID(++control) : from; // only allowed sets 1-9
if (whichset == ILLEGAL_FACTSET) return 0;
sprintf(myset,"@%d",whichset);
choice = myset;
break;
default:
ReportBug((char*)"Bad control code for query init %s(%s) %s",C->word,C->w.userValue,control)
return 0;
}
if (choice) // we have something to follow
{
++control; // now see flags on the choice
unsigned int flags = baseFlags;
// dont treat 'tween_decks as an originalword request
if (choice[0] == '\'' ) // && !IsAlphaUTF8(choice[1])) //dont expand this beyond its first leve -- $$tmp would come in with its value, which if set would fan out. '$$tmp gets just its value
{
flags |= ORIGINALWORD;
++choice;
}
if (choice[0] == '^') // replace the function arg
{
strcpy(word,FNVAR(choice+1));
choice = word;
}
// dynamic choices
if (choice[0] == '_')
{
int wild = GetWildcardID(choice);
if (wild == ILLEGAL_MATCHVARIABLE){;}
else if (flags != 0)
{
choice = wildcardOriginalText[wild];
if (*choice != '~') flags = 0; // '_0 treated as normal word unquoted (original meaning) unless its a set, then it must not expand instead
}
else choice = wildcardCanonicalText[wild];
}
else if (choice[0] == USERVAR_PREFIX && choice[1]) choice = GetUserVariable(choice);
else if (choice[0] == SYSVAR_PREFIX && choice[1]) choice = SystemVariable(choice,NULL);
else if (choice[0] == '@' )
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"FactField: %c(%d) ",saveMark-baseMark+'0',saveMark);
}
choice = NULL;
for (unsigned int j = 1; j <= FACTSET_COUNT(whichset); ++j)
{
FACT* F = factSet[whichset][j];
if (F->flags & FACTDEAD) continue; // cannot use this
MEANING M;
if (*control == 'f') // whole fact can be queued. It cannot be marked as on the queue
{
queue[queueIndex++] = Fact2Index(F);
continue;
}
else if (*control == 's') M = F->subject;
else if (*control == 'v') M = F->verb;
else if (*control == 'o') M = F->object;
else
{
ReportBug((char*)"bad control for query %s(%s) %s",C->word,C->w.userValue,control)
return 0;
}
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)" %s ",WriteMeaning(M));
AddWord2Scan((control[1] == 'q') ? (QUEUE|flags) : flags,M,0,0,0);
}
continue;
}
else if (facttype) // choice is a fact
{
if (!IsDigit(*choice)) return 0; // illegal fact reference
unsigned int f = atoi(choice);
if (atoi(choice) > (int) Fact2Index(factFree)) return 0; // beyond legal range
// we can q it but we dont mark it....
queue[queueIndex++] = f;
baseFlags |= FACTTYPE;
facttype = false;
continue;
}
if (choice[0] == '\\') // accept this unchanged
{
if (trace & TRACE_QUERY && CheckTopicTrace()) Log(STDTRACELOG,(char*)" raw ");
++choice;
}
// for non-factset values of choice
char buf[1000];
if (trace & TRACE_QUERY && CheckTopicTrace()) sprintf(buf,(char*)"%s #%c(%d)",choice,saveMark-baseMark+'0',saveMark);
if (*control == 'q')
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"Tag+Queue: %s ",buf);
}
qMark = saveMark; // if we q more later, use this mark by default
if (*choice) AddWordOrSet2Scan(QUEUE|flags,choice,0); // mark and queue items
}
else if (*control == 'Q')
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"Tag+QueueWord: %s ",buf);
}
qMark = saveMark; // if we q more later, use this mark by default
if (*choice) AddWordOnly(QUEUE|flags,choice,0); // mark and queue item
}
else if (*control == 't')
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"Tag: %s ",buf);
if (flags & ORIGINALWORD) Log(STDTRACELOG,(char*)" don't expand ");
}
if (!*choice);
else if (*choice == '\'') AddWord2Scan(flags, ReadMeaning(choice+1,true,true),0,0,0); // ignore unneeded quote
else AddWord2Scan(flags, ReadMeaning(choice,true,true),0,0,0);
}
else if (*control == 'T') // tag and dont follow
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"Tag: %s ",buf);
if (flags & ORIGINALWORD) Log(STDTRACELOG,(char*)" don't expand ");
}
qMark = saveMark; // if we q more later, use this mark by default
if (*choice) AddWordOnly(flags,choice,0); // mark and queue item
}
else if (*control == 'e')
{
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)" ExpandTag: %s ",buf);
}
if (*choice) AddWordOrSet2Scan(flags,choice,0); // tag but dont queue
}
else if (*control == '<' || *control == '>') // chase hierarchy (exclude VERB hierarchy-- we infer on nouns)
{ // callArgumentList are: flowverbs, queue or mark,
char kind = *control; //< or >
int flows = baseMark + *++control - '0'; // mark for flow
unsigned int flag = (*++control == 'q') ? QUEUE : 0;
if (trace & TRACE_QUERY && CheckTopicTrace())
{
if (flag) Log(STDTRACELOG,(char*)" Tag+Queue Propogate %c ",kind);
else Log(STDTRACELOG,(char*)" Tag Propogate %c ",kind);
}
// if (flag & QUEUE) flag |= BLOCKMEANING;
// mark subject 0 and object 2 are nouns, 1 is verb
if (*choice) ScanHierarchy(ReadMeaning(choice,true,true),saveMark,flows,kind == '<',flag, (mark != 1) ? NOUN : VERB);
}
else
{
ReportBug((char*)"bad follow argument %s",control)
return 0;
}
if (trace & TRACE_QUERY && CheckTopicTrace())
{
Log(STDTRACELOG,(char*)"\r\n");
Log(STDTRACETABLOG,(char*)"");
}
}
else if (baseFlags & USERFACTS) // transfer over user flags
{
unsigned int total = factFree - factLocked;
if (total >= MAX_QUEUE) total = MAX_QUEUE - 1;
FACT* F = factFree - total;
while (++F <= factFree) queue[queueIndex++] = Fact2Index(F);
baseFlags |= FACTTYPE;
}
}
inferMark += maxmark - '0'; // update to use up marks we have involved
currentFreeInfer = inferMark;
ignoremark = 0; // require be restated if a matching requirement
// ZONE 2 - how to use contents of the queue
// given items in queue, what field from a queued entry to use facts from
if (*control) control = SkipWhitespace(control+1); // skip over the : and past any white space
if (!strncmp(control,(char*)"queue",5)) control = SkipWhitespace(control+5); // just a label defining the field
if (trace & TRACE_QUERY && CheckTopicTrace())
{
char* colon = strchr(control+1,':');
if (colon) *colon = 0;
if (control[1])
{
Log(STDTRACELOG,(char*)"@@@ Control2 queue use: %s\r\n",control+1);
Log(STDTRACETABLOG,(char*)"");
}
if (colon) *colon = ':';
}
if (*control && --control) while (*++control && *control != ':' )
{
switch (*control)
{
case '_': case '.': case ' ': // does nothing per se
continue;
case 's':
baseOffset = 0;
break;
case 'v':
baseOffset = 1;
break;
case 'o':
baseOffset = 2;
break;
case 'f':
baseFlags |= FACTTYPE;
break; // queued items are facts instead of meaning
case 'e':
baseOffset = 3;
break;
default:
ReportBug((char*)"Bad control code for #2 (queue test) %s(%s) %s",C->word,C->w.userValue,control)
return 0;
}
}
whichset = store;
// ZONE 3 - how to detect facts we can return as answers and where they go
// set marks to compare on (test criteria for saving an answer)
bool sentences = false;
bool sentencev = false;
bool sentenceo = false;
unsigned int marks = 0, markv = 0, marko = 0;
unsigned int markns = 0, marknv = 0, markno = 0;
unsigned int rmarks = 0, rmarkv = 0, rmarko = 0;
unsigned int intersectMark = 0, propogateVerb = 0;
unsigned int systemFlags = 0;
unsigned int ultimateSubjectMember = 0;
unsigned int ultimateVerbMember = 0;
unsigned int ultimateObjectMember = 0;
unsigned int factflags = 0;
saveMark = qMark; // default q value is what we used before
if (*control) control = SkipWhitespace(control+1); // skip over the : and past any white space
if (!strncmp(control,(char*)"match",5)) control = SkipWhitespace(control + 5); // just a comment label defining what the field does
if (trace & TRACE_QUERY && *control && CheckTopicTrace())
{
char* colon = strchr(control+1,':');
if (colon) *colon = 0;
if (control[1])
{
Log(STDTRACELOG,(char*)"@@@ Control3 match requirements: %s\r\n",control+1); // if there is data
Log(STDTRACETABLOG,(char*)"");
}
if (colon) *colon = ':';
}
bool noSystemFlag = false;
if (*control && --control) while (*++control && *control != ':' )
{
switch (*control)
{
case '_': case '.': case ' ': // does nothing per se
continue;
case '!': // do NOT match this
++control;