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

431
LINES

< > BotCompany Repo | #1017587 // NLLogicChecker_v2

JavaX fragment (include)

sclass NLLogicChecker_v2 {
  S input;
  new L<S> facts;
  new L<IfThen> rules;
  StringMatcher matcher = ai_standardMatcher();
  new L<S> posted;
  new ThreadLocal<IfThen> checkingRule;
  VF2<O, Matching> onChecking; // first arg: IfThen or Exp
  VF3<O, Matching, O> onChecked; // first arg: IfThen or Exp, last arg: Bool or Throwable
  static bool staticVerbose;
  bool useIterate, allowUnsafeEvals = true;
  new HashMap<S, FuncChecker> funcCheckers;
  new HashMap<S, FuncIterator> funcIterators;
  S followingUpOn;

  List<Map> recentHistory;
  L<S> entities;
  F2<Exp, Matching, O> evaluator = func(Exp e, Matching m) {
    S code = e.text();
    if (!allowUnsafeEvals && !isSafeCodeFragment(code)) null;
    ret nlLogic_evalExp(NLLogicChecker_v2.this, code, m);
  };
  
  abstract sclass FuncChecker {
    abstract bool check(Func e, Matching m);
  }

  abstract sclass FuncIterator {
    abstract void iterate(Func e, Matching m, Runnable onMatch);
  }

  sclass Matching {
    new VarMatches matches; // var -> string value
    new L<Exp> output;
    //L<RuleFailInfo> ruleFails;
    bool verbose = staticVerbose;
    
    toString { ret sfu(matches) + "/" + sfu(output); }
  }
  
  bool checkRule(final IfThen rule, final Matching m) {
    if (rule == null) false;
    ret logCheck(rule, m, func -> Bool { checkRule_impl(rule, m) });
  }
  
  bool checkRule_impl(IfThen rule, Matching m) {
    temp tempSetThreadLocal(checkingRule, rule);
    if (!checkExpression(rule.in, m)) false;
    m.output.add(apply(rule.out, m));
    true;
  }
  
  // returns (success, remaining conditions)
  Pair<Bool, Exp> checkFirstCondition(IfThen rule, Matching m) {
    temp tempSetThreadLocal(checkingRule, rule);
    ret checkFirstCondition(rule.in, m);
  }
  
  Pair<Bool, Exp> checkFirstCondition(Exp exp, Matching m) {
    Pair<Exp> p = nlLogic_extractFirstCondition(exp);
    if (p == null) ret pair(true, null);
    if (!checkExpression(p.a, m)) ret pair(false, null);
    ret pair(true, p.b);
  }
  
  IfThen apply(IfThen r, Matching m) {
    ret r == null ? null : IfThen(apply(r.in, m), apply(r.out, m));
  }
  
  Exp apply(Exp e, Matching m) {
    ret apply(e, m.matches);
  }
  
  Exp apply(Exp e, SS m) {
    if (e == null) null;
    if (e cast And)
      ret And(apply(e.a, m), apply(e.b, m));
    if (e cast ExpNot)
      ret ExpNot(apply(e.a, m));
    if (e cast Func) {
      if (contains(e.options, 'javaTokNoQuotes) && e.arg instanceof Sentence2)
        ret Func(e.name, Sentence2(join(replaceVars_withDollarQ(javaTokNoQuotes(e.arg.text()), m))));
      else
        ret Func(e.name, apply(e.arg, m));
    }
      
    if (e cast Sentence)
      ret Sentence(javaTok(matcher.apply(e.text(), m)));
    if (e cast Sentence2)
      ret Sentence2(matcher.apply(e.text(), m));
      
    ret e;
  }
  
  <A> A logCheck(O o, Matching m, F0<A> f) {
    callF(onChecking, o, m);
    try {
      A result = callF(f);
      callF(onChecked, o, m, result);
      ret result;
    } catch e {
      callF(onChecked, o, m, e);
      throw rethrow(e);
    }
  }
  
  final bool checkExpression(final Exp e, final Matching m) {
    ret logCheck(e, m, func -> Bool { checkExpression_impl(e, m) });
  }
  
  bool checkExpression_impl_1(Exp e, Matching m) {
    if (e cast And)
      ret checkExpression(e.a, m) && checkExpression(e.b, m);
    ret checkExpression_single(e, m);
  }
  
  bool checkExpression_single(Exp e, Matching m) {
    if (e cast ExpNot) {
      temp tempBackupMatches(m);
      ret !checkExpression(e.a, m);
    }
    if (e cast Func) {
      if (eq(e.name, 'input) && !cic(e.options, 'flexMatch)) {
        S pat = nlLogic_text(e.arg);
        if (contains(e.options, 're)) // regular expression
          ret regexpFindIC(pat, input);
          
        ret nlLogic_matchVBarPattern(this, pat, input, m);
      } else if (eq(e.name, "fact")) {
        S pat = nlLogic_text(e.arg);
        if (contains(e.options, "random"))
          ret nlLogic_matchRandomFact(this, pat, m);
        else
          ret nlLogic_matchAnyFact(this, pat, m);
      } else if (eq(e.name, "allFacts")) {
        S pat = nlLogic_text(e.arg);
        L<VarMatches> l = nlLogic_matchAllFacts(this, pat, m);
        for i over l: {
          SS vars = reverseGet(l, i); // facts are normally reversed, so re-reverse to get them in order
          int n = i+1;
          for (S var, value : vars) {
            if (!strictPutIC(m.matches, var + n, value)) false;
          }
        }
        true;
      }
    }
    false;
  }
  
  AutoCloseable tempBackupMatches(final Matching m) {
    final VarMatches oldMatches = new (m.matches);
    ret autocloseable { m.matches = oldMatches; };
  }
  
  O evalExp(Exp e, Matching m) {
    ret callF(evaluator, e, m);
  }
  
  bool checkExpression_impl(Exp e, Matching m) {
    new Matches mm;
    if (e cast Func) {
      // FUNCTION CONDITIONS
      
      FuncChecker checker = funcCheckers.get(e.name);
      if (checker != null && checker.check(e, m)) true;
      
      if (eq(e.name, "singular"))
        ret nlLogic_stringFunction(f singular, e, m.matches);
      else if (eq(e.name, 'entity)) {
        if (entities == null) {
          long time = sysNow();
          fS switched = switcheroo(input);
          print("Switched >> " + switched);
          entities = evalWithTimeoutOrNull(5000, func -> LS {
            mapMethod('text, ai_extractEntities_v1(switched))
          });
          print("Entities (" + elapsedMS(time) + " ms): " + joinWithComma(entities));
          if (entities == null) entities = new L;
        }
        for (S entity : entities)
          if (matcher.match(nlLogic_text(e.arg), entity, m.matches)) true;
      } else if (startsWith(e.name, "line", mm) && isInteger(mm.rest()) && checkingRule! != null) {
        int n = parseInt(mm.rest())-nlLogic_numberOfLinesReferenced(checkingRule->in);
        S line = n == 0 ? input : getString(get(recentHistory, l(recentHistory)+n), 'text);
        //print("Recent: " + recentHistory);
        S pat = e.arg.text();
        //print("n=" + n + ", Matching " + e + " with " + line);
        ret matcher.match(pat, line, m.matches);
      } else if (eq(e.name, 'iSaid)) {
        S line = getString(last(recentHistory), 'text);
        if (line == null) false;
        ret matcher.match(e.arg.text(), line, m.matches);
      } else if (eq(e.name, 'unknownIf)) {
        S statement = nlLogic_text(apply(e.arg, m));
        //print("Checking statement: " + statement);
        ret !cic(facts, statement) && !cic(facts, "Untrue: " + statement);
      } else if (eq(e.name, 'inputContainsTokens))
        ret jcontains(input, nlLogic_text(e.arg));
      else if (eq(e.name, 'inputStartsWith))
        ret startsWith(input, nlLogic_text(e.arg));
      else if (eq(e.name, 'anyInput))
        ret nempty(input);
      else if (eq(e.name, 'preciseInput))
        ret eq(nlLogic_text(apply(e.arg, m)), input); // Incomplete var support
      else if (eq(e.name, 'followingUpOn)) {
        S text = nlLogic_text(e.arg);
        if (eq(text, followingUpOn)) true;
        
        Map prevPrev = nextToLast(recentHistory);
        S msgID = getString(prevPrev, 'globalID);
        S input = getString(prevPrev, 'text);
        
        S ruleID = null, dollarInput = null;
        
        // e.g. "lhnshnhcklhabvmu with input=$input"
        if (match("* with input=*", text, mm) && !isDollarVar(mm.get(1))) {
          ruleID = mm.unq(0); dollarInput = mm.get(1);
        } else
          ruleID = text;
          
        //print("followingUpOn: msgID=" + msgID + ", input=" + input);
        //printStruct(+prevPrev);
        S pat = format("Rule * fired on message * ", ruleID, msgID);
        //print("pat=" + pat);
        for (S ruleFired : mL("Telegram Rule Fires"))
          if (swic(ruleFired, pat)) {
            if (dollarInput != null && !strictPutIC(m.matches, dollarInput, input)) false;
            L<S> tok = javaTokWithBrackets2(ruleFired);
            if (find3("with vars *", tok, mm)) {
              SS vars = safeUnstructMap(mm.get(0));
              if (!addMapToMapWithoutOverwritingIC(m.matches, vars)) false;
            }
            true;
          }
        false;
      } else if (eq(e.name, 'debug))
        ret true with print("[debug] " + apply(e.arg, m));
    }
    
    if (e cast Eq) {
      // EQUATION CONDITIONS
      
      Exp r = e.right;
      S var = nlLogic_text(e.left);
      
      if (endsWith(var, "!")) { // $x != bla
        temp tempBackupMatches(m);
        ret !matcher.match(dropSuffixTrim("!", var), r.text(), m.matches);
      }
      
      if (r cast Func) pcall {
        if (eq(r.name, 'eval)) {
          O result = evalExp(r.arg, m);
          if (result != null) { // Not allowing null results anymore
            LS vars = tok_splitAtComma(var);

            Bool x = ai_matchObjectWithMultipleVars(vars, result, m.matches);
            if (x != null) ret x;
              
            S sResult = str(result);
            StringMatcher _matcher = matcher;
            if (isDollarVar(var)) _matcher = new NLStringMatcher_singleDollarVar;
            else if (contains(var, ',')) { // TODO: use Pair instead - there is only like one rule using this
              if (allDollarVars(vars)
                && ai_matchMulti(vars, tok_splitAtComma(sResult), m.matches)) true;
            }
              
            ret _matcher.match(var, sResult, m.matches);
          }
        }
      }
    }
    
    if (e cast Sentence2) {
      // SENTENCE CONDITIONS
      
      S text = e.text();
      if (eq(text, 'true))
        true;
    }
    
    ret checkExpression_impl_1(e, m);
  }

  void iterate(final Exp e, final Matching m, final Runnable onMatch) {
    if (e cast And) {
      iterate(e.a, m, r { iterate(e.b, m, onMatch) });
      ret;
    }
    
    if (e cast ExpNot) {
      if (m.verbose) print("iterate not: " + e);
      final new Flag flag;
      withCancelPoint(voidfunc(final CancelPoint cp) {
        iterate(e.a, m, r {
          if (m.verbose) print("iterate not: raising flag");
          flag.raise();
          cancelTo(cp);
        });
      });
      if (!flag.isUp())
        callF(onMatch);
      ret;
    }
      
    iterate_single(e, m, onMatch);
  }

  void iterate_single(final Exp e, final Matching m, final Runnable onMatch) {
    if (e cast Func) {
      //print("Checking func iterators: " + e.name);
      FuncIterator it = funcIterators.get(e.name);
      if (it != null)
        it.iterate(e, m, onMatch);
      
      if (eq(e.name, 'fact)) {
        S pat = nlLogic_text(e.arg);
        VarMatches oldMatches = new(m.matches);
        for (S fact : facts) {
          if (m.verbose)
            print("iterate matching: " + pat + " with " + fact + " - " + sfu(m.matches));
          for (S pat2 : tok_splitAtVerticalBar(pat))
            if (matcher.match(pat2, fact, m.matches)) {
              if (m.verbose)
                print("iterate match: " + pat + " / " + fact + " - " + m);
              try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
            }
        }
        ret;
      } else if (eq(e.name, "input") && cic(e.options, 'flexMatch)) {
        S pat = nlLogic_text(e.arg);
        if (m.verbose)
          print("input flexMatch " + pat);
          
        VarMatches oldMatches = new(m.matches);
        nlLogic_flexMatch_iterate_vbar(pat, input, m, onMatch, oldMatches);
        ret;
      }
    }
    
    if (e instanceof Sentence2 && contains(e.text(), " in ")) {
      L<S> tok = javaTokPlusAllThreeBrackets(e.text());
      S var, var2;
      new Matches mm;
      if (m.verbose)
        print("iterate in eval tok: " + sfu(tok)); 
        
      if (jmatch("* in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isRoundBracketed(mm.get(1))) {
        if (m.verbose)
          print("iterate in eval: " + mm.get(1)); 
        Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(1))), m);
        VarMatches oldMatches = new(m.matches);

        for (O element : unnull(l)) {
          continue unless element != null;
          S s = str(element);
          if (m.verbose) print("iterate in eval got element: " + s);
          if (strictPutIC(m.matches, var, s))
            try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
        }
      }
      
      if (jmatch("*, * in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isDollarVar(var2 = mm.get(1)) && isRoundBracketed(mm.get(2))) {
        LS vars = ll(var, var2);
        Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(2))), m);
        VarMatches oldMatches = new(m.matches);

        for (O element : unnull(l)) {
          continue unless element != null;
          if (m.verbose) print("iterate in eval got element: " + element);
          if (isTrue(ai_matchObjectWithMultipleVars(vars, element, m.matches)))
            try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
        }
      }
    }
    
    temp tempBackupMatches(m);
    if (m.verbose)
      print("iterate fallback: " + sfu(e)); 
    if (checkExpression(e, m)) {
      if (m.verbose)
        print("iterate fallback match: " + e); 
      callF(onMatch);
    }
  }
  
  bool checkHelper(Exp e, Matching m) {
    ret nlLogic_checkHelper(e, m.matches);
  }
  
  *() {
    initCheckers();
    initIterators();
  }
  
  void initCheckers {
    mapPutMultipleKeys(funcCheckers, new FuncChecker {
      bool check(Func e, Matching m) {
        ret nlLogic_stringFunction(f ai_verbPhraseFromThirdPerson, e, m.matches);
      }
    }, 'verbPhraseFromThirdPerson, 'verbFromThirdPerson);
    
    funcCheckers.put('eval, new FuncChecker {
      bool check(Func e, Matching m) {
        ret eq("true", str(evalExp(e.arg, m)));
      }
    });
  }
  
  void initIterators {
    funcIterators.put('phrase, new FuncIterator {
      void iterate(Func e, Matching m, Runnable onMatch) {
        //if (!contains(e.options, 'words)) ret;
        S pat = e.argText();
        LS patTok = standardTok(pat);
        LS inputTok = standardTok(input);
        
        // optimizable
        for (LS inputPart : tok_marchPatternThroughInput(patTok, inputTok))
          if (matcher.match(pat, join(inputPart), m.matches))
            callF(onMatch);
      }
    });
  }
  
  // TODO: cache
  LS standardTok(S s) {
    ret javaTokWithAllBrackets(s);
  }
}

Author comment

Began life as a copy of #1017580

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1017587
Snippet name: NLLogicChecker_v2
Eternal ID of this version: #1017587/126
Text MD5: 8c896eea64cfd6f47c89910275787351
Author: stefan
Category: javax / a.i.
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-12-04 17:06:30
Source code size: 14832 bytes / 431 lines
Pitched / IR pitched: No / No
Views / Downloads: 608 / 1366
Version history: 125 change(s)
Referenced in: [show references]