static L<GazelleTree> gazelle_getChildren(GazelleTree tree, O... _) {
  if (tree == null) null;
  if (tree.children != null) ret tree.children;
  
  tree.children = new L;
  
  double qualityCutOff = optPar qualityCutOff(_, -0.75);
  LS dialogHistory = cast optPar dialogHistory(_);
  
  // make rule engine etc.
  if (tree.ctx == null)
    tree.setContext(assertNotNull('evalContext, (GazelleEvalContext) callF(assertNotNull('makeEvalContext, (O) optPar makeEvalContext(_)))));
  GazelleEvalContext ctx = tree.ctx;
  RuleEngine2 engine = ctx.engine;
  
  SS inputMap = gazelle_inputMapFromLineAndHistory(tree.line, 
    concatLists(dialogHistory, tree.history()));
  if (boolPar respondingToHuman(_)) inputMap.put("_respondingToHuman" := "true");

  // Splitters
  
  for (RuleEngine2.SimplifyWithRule rule : engine.splitterRules()) pcall {
    O f = rule.function();
    Collection<S> out = cast(f instanceof S
      ? callF(engine.callFunctionOnString, (S) f, tree.line)
      : callF(f, tree.line));
    if (l(out) < 2) continue;
    
    GazelleTree child = new(joinWithVBar(out));
    child.isSplitNode = true;
    tree.add(child);
    
    for (S line : out)
      child.add(GazelleTree(line));
  }
  
  // Other rules
    
  Set<S> badComments = cast optPar badComments(_);
  Set<S> acceptablePurposes = cast optPar acceptablePurposes(_);
  bool firstConditionOnly = boolPar firstConditionOnly(_);
  O[] params = litobjectarray(debug := optPar debug(_), +tree);
  L<RuleEngine2_MatchedRule> l = sortByMethodDesc qualitySum(
    firstConditionOnly
      ? ai_ruleEngine2_rulesForInput_5(engine, inputMap, params)
      : ai_ruleEngine2_rulesForInput_5_allConditions(engine, inputMap, params));
  //print("Got rules: " + l(l));
  
  for (RuleEngine2_MatchedRule r : l) {
    if (r.relativeQuality() < qualityCutOff) continue;
    S line = r.outText();
    
    GazelleTree child = new(line);
    child.mr = r;
    if (r.rule != null) child.lineType = r.rule.outType;
    
    if (containsAny(r.rule.comments, badComments)
      || acceptablePurposes != null && !acceptablePurposes.contains(unnull(r.rule.purpose)))
      child.prediction = 'bad;
    else {
      /*ai_gazelle_analyzeStatementsForRule_Data data = ctx.dataByRule.get(r.rule.globalID);
      child.prediction = data == null ? null : ai_gazelle_predictor1_onData(r, data);*/
      child.prediction = ctx.predictor == null ? null : ctx.predictor.get(ctx, r);
    }
    tree.add(child);
  }
  gazelle_sortChildren(tree);
  ret tree.children;
}