Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

311
LINES

< > BotCompany Repo | #1021389 // RuleEngine2 - with multiple conditions per rule & facts

JavaX fragment (include)

1  
// should now be persistable
2  
sclass RuleEngine2 {
3  
  sclass SimplifyWithRule extends Rule {
4  
    transient O _function; // function name or F1. can return S or LS
5  
    bool isSplitRule; // splitter rather than simplifier
6  
    bool callOnTree; // function takes GazelleTree rather than S
7  
    
8  
    O function() {
9  
      if (_function == null) {
10  
        gazelle_parseInstruction(this);
11  
        assertNotNull(_function);
12  
      }
13  
      ret _function;
14  
    }
15  
  }
16  
  
17  
  abstract sclass QuickCheck {
18  
    abstract bool check(S input);
19  
  }
20  
  
21  
  sclass CicQuickCheck extends QuickCheck {
22  
    S string;
23  
    
24  
    bool check(S input) { ret cic(input, string); }
25  
  }
26  
  
27  
  sclass Rule {
28  
    S globalID;
29  
    LS in;
30  
    S out;
31  
    LS comments;
32  
    Set<S> vars;
33  
    L<GRuleLine> insWithType;
34  
    LS outTypes;
35  
    transient L inputMassagers; // func(S input) -> S for every in
36  
    transient L checkerFunctions; // func(S input) -> SS for every in
37  
    transient L matchers; // func(LS tokC, LS tokI, RuleEngine2_MatchedRule) -> SS for every in
38  
    S text; // original text
39  
    QuickCheck applicabilityQuickCheck;
40  
    S purpose = "";
41  
    Double qualityCutOff;
42  
    bool avoidRecentlyUsedMappings;
43  
    
44  
    // (optional) massage the variable map after matching
45  
    // L<func(SS varMap, LS tokCondition, LS tokInput, RuleEngine2_MatchedRule matched) -> SS newVarMap>
46  
    L mapMassagers;
47  
48  
    S asText() {
49  
      if (l(in) > 1 && eq(last(in), out))
50  
        ret join(" \n+ ", dropLast(in)) + " \n+=> " + out;
51  
      ret join(" \n+ ", in) + " \n=> " + out;
52  
    }
53  
    
54  
    void addMapMassager(O massager) {
55  
      if (massager != null) mapMassagers = addToOrCreateList(mapMassagers, massager);
56  
    }
57  
    
58  
    void setInputMassager(int i, O massager) {
59  
      inputMassagers = listSetWithCreate(inputMassagers, i, massager);
60  
    }
61  
    
62  
    void setMatcher(int i, O matcher) {
63  
      matchers = listSetWithCreate(matchers, i, matcher);
64  
    }
65  
    
66  
    void setOutType(int i, S type) {
67  
      outTypes = listSetWithCreate(outTypes, i, type);
68  
    }
69  
    
70  
    S outType() { ret first(outTypes); }
71  
    
72  
    void parseGeneralComments() {
73  
      purpose = gazelle_purposeFromComments(comments);
74  
      pcall {
75  
        S s = matchAny_firstGroup("quality cutoff = *", comments);
76  
        if (s != null) qualityCutOff = parseDouble(s);
77  
      }
78  
      avoidRecentlyUsedMappings = cic(comments, "avoid recently used mapping");
79  
    }
80  
    
81  
    toString {
82  
      ret globalID + ": " + text;
83  
    }
84  
  } // end of class Rule
85  
  
86  
  int minScore = 50; // minimal percentage score for rule matching
87  
  int maxLevel = 100, maxOutLines = 10000;
88  
  new L<Rule> rules;
89  
  bool printMappings, printGroupedData, printRejectedVars;
90  
  Set<S> facts = ciSet();
91  
  transient F2<S, S, O> callFunctionOnString = func(S function, S s) -> O { callAndMake(function, s) };
92  
  bool hasHelpers;
93  
94  
  *() {}
95  
  
96  
  // probably not used anymore
97  
  *(LPair<S> rulesWithComments) {
98  
    for (PairS p : unnull(rulesWithComments))
99  
      addRule(p.a, splitAtDoubleArrow_pair(p.a), p.b, null);
100  
  }
101  
  
102  
  void copyRulesFrom(RuleEngine2 engine) {
103  
    if (engine == null) ret;
104  
    rules.addAll(engine.rules);
105  
    hasHelpers = engine.hasHelpers;
106  
  }
107  
  
108  
  void addRules2(L<T3S> rulesWithCommentsAndID) {
109  
    new Matches m;
110  
    for (T3<S> p : unnull(rulesWithCommentsAndID)) try {
111  
      PairS p2 = splitAtDoubleArrow_pair(p.a);
112  
      if (p2 != null)
113  
        continue with addRule(p.a, p2, p.b, p.c);
114  
      
115  
      new SimplifyWithRule r;
116  
      r.globalID = p.c;
117  
      r.text = p.a;
118  
      r.comments = lines(p.b);
119  
      r.parseGeneralComments();
120  
      S s = p.a;
121  
      gazelle_parseInstruction(r);
122  
      if (r._function == null) continue;
123  
      rules.add(r);
124  
    } catch e {
125  
      printStackTrace("Exception parsing rule " + p.c, e);
126  
    }
127  
  }
128  
129  
  void addRule(S text, PairS rule, S comments, S globalID) {
130  
    if (rule == null) ret;
131  
    rule = ai_rule_pair_expandPlusArrow(rule);
132  
    LS conditions = tok_splitAtPlusAtBeginningOfLine(rule.a);
133  
    new Rule r;
134  
    r.globalID = globalID;
135  
    r.text = text;
136  
    r.comments = lines(comments);
137  
    r.parseGeneralComments();
138  
    r.in = conditions;
139  
    r.insWithType = map(conditions, func(S s) -> GRuleLine { GRuleLine(s, "standard") });
140  
    
141  
    // input massagers for "<anything>"
142  
    
143  
    for (int i = 0; i < l(r.in); i++) {
144  
      S s = r.in.get(i);
145  
      continue unless endsWith(s, "<anything>");
146  
      if (eq(s, "<anything>"))
147  
        r.checkerFunctions = listSetWithCreate(r.checkerFunctions, i, func(S s) -> SS { nempty(s) ? litmap() : null });
148  
      else if (jmatch("* says: <anything>", javaTokWithBrackets(s))) {
149  
        //print("Made input massager");
150  
        r.setInputMassager(i, func(S s) -> S { new Matches m; ret jMatchStart("* says:", javaTokWithBrackets(s), m) ? m.get(0) + " says: <anything>" : s; });
151  
      }
152  
    }
153  
    
154  
    for (Matches m : getJMatches_all("expand *", r.comments))
155  
      if (isQuoted(m.get(0))) {
156  
        fS var = m.unq(0);
157  
        for (int i = 0; i < l(r.in); i++)
158  
          r.setMatcher(i, new O { SS get(LS tokC, LS tokI, RuleEngine2_MatchedRule matched) {
159  
            if (eqic(nextToLast(tokC), var))
160  
              tokI = tok_groupLastTokensToMatchPattern(tokI, tokC);
161  
            ret zipCodeTokensToCIMap_strict_withoutEquals(tokC, tokI);
162  
          }});
163  
      }
164  
    
165  
    // "in = dialog" (probably not used anymore)
166  
        
167  
    if (jmatchAny(ll("in = dialog", "fact + event => replacement fact"), r.comments))
168  
      for (int i = 0; i < l(r.insWithType); i++)
169  
        r.insWithType.get(i).type = "dialog-" + (l(r.insWithType)-i);
170  
        
171  
    new Matches m;
172  
    for (S s : r.comments) {
173  
      if (jMatchStart("in * =", s, m) && isInteger($1)
174  
        && (startsWith($2, 'statement) || eq($2, 'condition)))
175  
        set(get(r.insWithType, parseInt($1)-1), type := $2);
176  
      else if (jMatchStart("out * =", s, m) && isInteger($1))
177  
        r.setOutType(parseInt($1)-1, $2);
178  
      else if (jMatchStart("out = ", s, m))
179  
        r.setOutType(0, m.rest());
180  
      else if (jMatchStart("in * :", s, m) && isInteger($1)) {
181  
        int i = parseInt($1)-1;
182  
        S comment = m.rest();
183  
        GRuleLine line = get(r.insWithType, i);
184  
        if (line != null) {
185  
          line.comments = addToOrCreateList(line.comments, comment);
186  
          if (match("tokenize with *", comment, m))
187  
            line.tokenizer = $1;
188  
        }
189  
      }
190  
    }
191  
    
192  
    r.out = rule.b;
193  
    r.vars = ai_wordsInBothSidesOfPair_uncurly(rule);
194  
    rules.add(r);
195  
  }
196  
  
197  
  /*
198  
  LS addFacts(Collection<S> facts) {
199  
    ret addAllAndReturnNew(this.facts, facts);
200  
  }
201  
  
202  
  void addFact(S fact) { facts.add(fact); }
203  
  
204  
  void addAndInterpretFacts(Collection<S> facts) {
205  
    addFacts(recursiveInterpretations(addFacts(facts)));
206  
  }
207  
  
208  
  void processInput(S input) {
209  
    print("\nInput: " + tok_dropCurlyBrackets(input));
210  
    temp tempIndent();
211  
    for (S in : interpretations(input)) {
212  
      print("\nInterpretation: " + formatForUser(in));
213  
      Set<S> out = litciset();
214  
      interpretRecursively(in, 5, out);
215  
    }
216  
  }
217  
  
218  
  Set<S> recursiveInterpretations(S in) {
219  
    ret recursiveInterpretations(ll(in));
220  
  }
221  
  
222  
  Set<S> recursiveInterpretations(Iterable<S> in) {
223  
    Set<S> inSet = asCISet(in);
224  
    Set<S> out = litciset();
225  
    new LinkedHashSet<S> queue;
226  
    addAll(queue, inSet);
227  
    while (l(out) < maxOutLines && nempty(queue)) {
228  
      LS intp = interpretations(popFirst(queue));
229  
      for (S i : intp)
230  
        if (!inSet.contains(i) && !out.contains(i)) {
231  
          out.add(i);
232  
          queue.add(i);
233  
          if (l(out) >= maxOutLines) break;
234  
        }
235  
    }
236  
    ret out;
237  
  }
238  
239  
  void interpretRecursively(S input, int level, Set<S> out) {
240  
    if (level <= 0) ret;
241  
    LS interpretations = interpretations(input);
242  
    temp tempIndent();
243  
    for (S in : addAllAndReturnNew(out, interpretations)) {
244  
      print(" => " + formatForUser(in));
245  
      for (S s : lithashset(in, ai_superSimpleVerbCorrector(in)))
246  
        interpretRecursively(s, level-1, out);
247  
    }
248  
  }
249  
250  
  LS defaultParse(S s) {
251  
    ret codeTokens_lazy_uncurly(javaTokNPunctuationWithBrackets_cached(s));
252  
  }
253  
254  
  LS interpretations(S input) {
255  
    LS tokI = defaultParse(input);
256  
    //print("Raw parse: " + tokI);
257  
    new LS interpretations;
258  
      
259  
    for (Rule rule : rules) {
260  
      continue unless l(rule.in) == 1;
261  
      LS tokR = defaultParse(first(rule.in));
262  
      final SS map = ciMapWithoutKeysEqualToValues(zipTwoListsToCIMap_strict(tokR, tokI));
263  
      if (map == null) continue;
264  
      
265  
      // Found matching rule
266  
      
267  
      int score = l(tokR)-l(map);
268  
      L<S> nonVars = withoutDollarVars(listMinusSet(keys(map), rule.vars));
269  
      if (printRejectedVars && nempty(nonVars)) print("Rejected vars: " + nonVars);
270  
      if (nempty(nonVars)) score = 0;
271  
      int percentScore = ratioToIntPercent(score, l(tokR));
272  
      if (printMappings) print("  " + percentScore + "% " + reverseMapCI_joinDuplicatesWithPlus(map) + " | " + rule.in);
273  
      if (percentScore < minScore) continue;
274  
      
275  
      // Make consequence
276  
      
277  
      S c = rule.out;
278  
      c = join(mapCodeTokens(javaTok(c), func(S s) -> S { getOrKeep_tryUncurlied(map, s) }));
279  
      c = join(mapCodeTokens(javaTokWithBrackets(c), func(S s) -> S { getOrKeep_tryUncurlied(map, s) }));
280  
      //c = javaTokWithBrackets_recursiveTransform(func(S s) -> S { getOrKeep(map, s) }, c);
281  
      
282  
      interpretations.add(c);
283  
    }
284  
      
285  
    ret interpretations;
286  
  }
287  
288  
  S formatForUser(S s) {
289  
    ret tok_dropCurlyBrackets(s);
290  
  }*/
291  
  
292  
  LPair<LS, S> rulesAsPairs() {
293  
    ret map(rules, func(Rule r) -> Pair<LS, S> { pair(r.in, r.out) });
294  
  }
295  
  
296  
  L<SimplifyWithRule> splitterRules() {
297  
    ret (L) [Rule r : rules | r instanceof SimplifyWithRule && ((SimplifyWithRule) r).isSplitRule];
298  
  }
299  
  
300  
  void dropRulesWhere(IF1<Rule, Bool> pred) {
301  
    rules = antiFilter(pred, rules);
302  
  }
303  
  
304  
  void deleteRule(S ruleID) {
305  
    dropRulesWhere(r -> eq(r.globalID, ruleID));
306  
  }
307  
  
308  
  Rule getRule(S ruleID) {
309  
    ret firstWhere(rules, globalID := ruleID);
310  
  }
311  
}

Author comment

Began life as a copy of #1021346

download  show line numbers  debug dex  old transpilations   

Travelled to 8 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, whxojlpjdney

No comments. add comment

Snippet ID: #1021389
Snippet name: RuleEngine2 - with multiple conditions per rule & facts
Eternal ID of this version: #1021389/123
Text MD5: e2d7beca60770ca7cfa04b2b46a91d1f
Author: stefan
Category: javax / a.i.
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-03-29 15:46:15
Source code size: 10204 bytes / 311 lines
Pitched / IR pitched: No / No
Views / Downloads: 626 / 1259
Version history: 122 change(s)
Referenced in: [show references]