public void translateLeftRecursiveRules() {
    String language = g.getOptionString("language");
    // translate all recursive rules
    List<String> leftRecursiveRuleNames = new ArrayList<String>();
    for (Rule r : rules) {
      if (!Grammar.isTokenName(r.name)) {
        if (LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name)) {
          g.originalTokenStream = g.tokenStream;
          boolean fitsPattern = translateLeftRecursiveRule(ast, (LeftRecursiveRule) r, language);
          if (fitsPattern) leftRecursiveRuleNames.add(r.name);
        }
      }
    }

    // update all refs to recursive rules to have [0] argument
    for (GrammarAST r : ast.getNodesWithType(ANTLRParser.RULE_REF)) {
      if (r.getParent().getType() == ANTLRParser.RULE) continue; // must be rule def
      if (((GrammarASTWithOptions) r).getOptionString(PRECEDENCE_OPTION_NAME) != null)
        continue; // already has arg; must be in rewritten rule
      if (leftRecursiveRuleNames.contains(r.getText())) {
        // found ref to recursive rule not already rewritten with arg
        ((GrammarASTWithOptions) r)
            .setOption(
                PRECEDENCE_OPTION_NAME,
                (GrammarAST) new GrammarASTAdaptor().create(ANTLRParser.INT, "0"));
      }
    }
  }
  /** Return true if successful */
  public boolean translateLeftRecursiveRule(
      GrammarRootAST ast, LeftRecursiveRule r, String language) {
    // tool.log("grammar", ruleAST.toStringTree());
    GrammarAST prevRuleAST = r.ast;
    String ruleName = prevRuleAST.getChild(0).getText();
    LeftRecursiveRuleAnalyzer leftRecursiveRuleWalker =
        new LeftRecursiveRuleAnalyzer(prevRuleAST, tool, ruleName, language);
    boolean isLeftRec;
    try {
      //			System.out.println("TESTING ---------------\n"+
      //							   leftRecursiveRuleWalker.text(ruleAST));
      isLeftRec = leftRecursiveRuleWalker.rec_rule();
    } catch (RecognitionException re) {
      isLeftRec = false; // didn't match; oh well
    }
    if (!isLeftRec) return false;

    // replace old rule's AST
    GrammarAST RULES = (GrammarAST) ast.getFirstChildWithType(ANTLRParser.RULES);
    String newRuleText = leftRecursiveRuleWalker.getArtificialOpPrecRule();
    //		System.out.println("created: "+newRuleText);
    RuleAST t = parseArtificialRule(g, newRuleText);

    // reuse the name token from the original AST since it refers to the proper source location in
    // the original grammar
    ((GrammarAST) t.getChild(0)).token = ((GrammarAST) prevRuleAST.getChild(0)).getToken();

    // update grammar AST and set rule's AST.
    RULES.setChild(prevRuleAST.getChildIndex(), t);
    r.ast = t;

    // Reduce sets in newly created rule tree
    GrammarTransformPipeline transform = new GrammarTransformPipeline(g, g.tool);
    transform.reduceBlocksToSets(r.ast);
    transform.expandParameterizedLoops(r.ast);

    // Rerun semantic checks on the new rule
    RuleCollector ruleCollector = new RuleCollector(g);
    ruleCollector.visit(t, "rule");
    BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector);
    // disable the assoc element option checks because they are already
    // handled for the pre-transformed rule.
    basics.checkAssocElementOption = false;
    basics.visit(t, "rule");

    // track recursive alt info for codegen
    r.recPrimaryAlts = new ArrayList<LeftRecursiveRuleAltInfo>();
    r.recPrimaryAlts.addAll(leftRecursiveRuleWalker.prefixAlts);
    r.recPrimaryAlts.addAll(leftRecursiveRuleWalker.otherAlts);
    if (r.recPrimaryAlts.isEmpty()) {
      tool.errMgr.grammarError(
          ErrorType.NO_NON_LR_ALTS,
          g.fileName,
          ((GrammarAST) r.ast.getChild(0)).getToken(),
          r.name);
    }

    r.recOpAlts = new OrderedHashMap<Integer, LeftRecursiveRuleAltInfo>();
    r.recOpAlts.putAll(leftRecursiveRuleWalker.binaryAlts);
    r.recOpAlts.putAll(leftRecursiveRuleWalker.ternaryAlts);
    r.recOpAlts.putAll(leftRecursiveRuleWalker.suffixAlts);

    // walk alt info records and set their altAST to point to appropriate ALT subtree
    // from freshly created AST
    setAltASTPointers(r, t);

    // update Rule to just one alt and add prec alt
    ActionAST arg = (ActionAST) r.ast.getFirstChildWithType(ANTLRParser.ARG_ACTION);
    if (arg != null) {
      r.args = ScopeParser.parseTypedArgList(arg, arg.getText(), g.tool.errMgr);
      r.args.type = AttributeDict.DictType.ARG;
      r.args.ast = arg;
      arg.resolver = r.alt[1]; // todo: isn't this Rule or something?
    }

    // define labels on recursive rule refs we delete; they don't point to nodes of course
    // these are so $label in action translation works
    for (Pair<GrammarAST, String> pair : leftRecursiveRuleWalker.leftRecursiveRuleRefLabels) {
      GrammarAST labelNode = pair.a;
      GrammarAST labelOpNode = (GrammarAST) labelNode.getParent();
      GrammarAST elementNode = (GrammarAST) labelOpNode.getChild(1);
      LabelElementPair lp = new LabelElementPair(g, labelNode, elementNode, labelOpNode.getType());
      r.alt[1].labelDefs.map(labelNode.getText(), lp);
    }
    // copy to rule from walker
    r.leftRecursiveRuleRefLabels = leftRecursiveRuleWalker.leftRecursiveRuleRefLabels;

    tool.log("grammar", "added: " + t.toStringTree());
    return true;
  }