package org.antlr.tool;

import org.antlr.codegen.CodeGenerator;
import org.antlr.grammar.v3.*;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.TreeNodeStream;
import org.stringtemplate.v4.*;

import java.util.*;

/** */
public class LeftRecursiveRuleAnalyzer extends LeftRecursiveRuleWalker {
	public static enum ASSOC { left, right };

	public Grammar g;
	public CodeGenerator generator;
	public String ruleName;
	Map<Integer, Integer> tokenToPrec = new HashMap<Integer, Integer>();
	public LinkedHashMap<Integer, String> binaryAlts = new LinkedHashMap<Integer, String>();
	public LinkedHashMap<Integer, String> ternaryAlts = new LinkedHashMap<Integer, String>();
	public LinkedHashMap<Integer, String> suffixAlts = new LinkedHashMap<Integer, String>();
	public List<String> prefixAlts = new ArrayList<String>();
	public List<String> otherAlts = new ArrayList<String>();

	public GrammarAST retvals;

	public STGroup recRuleTemplates;
	public String language;

	public Map<Integer, ASSOC> altAssociativity = new HashMap<Integer, ASSOC>();

	public LeftRecursiveRuleAnalyzer(TreeNodeStream input, Grammar g, String ruleName) {
		super(input);
		this.g = g;
		this.ruleName = ruleName;
		language = (String)g.getOption("language");
		generator = new CodeGenerator(g.tool, g, language);
		generator.loadTemplates(language);
		loadPrecRuleTemplates();
	}

	public void loadPrecRuleTemplates() {
		recRuleTemplates =
			new ToolSTGroupFile(CodeGenerator.classpathTemplateRootDirectoryName+
							"/LeftRecursiveRules.stg");
		if ( !recRuleTemplates.isDefined("recRuleName") ) {
			ErrorManager.error(ErrorManager.MSG_MISSING_CODE_GEN_TEMPLATES,
							   "PrecRules");
			return;
		}
	}

	@Override
	public void setReturnValues(GrammarAST t) {
		System.out.println(t);
		retvals = t;
	}

	@Override
	public void setTokenPrec(GrammarAST t, int alt) {
		int ttype = g.getTokenType(t.getText());
		tokenToPrec.put(ttype, alt);
		ASSOC assoc = ASSOC.left;
		if ( t.terminalOptions!=null ) {
			String a = (String)t.terminalOptions.get("assoc");
			if ( a!=null ) {
				if ( a.equals(ASSOC.right.toString()) ) {
					assoc = ASSOC.right;
				}
				else {
					ErrorManager.error(ErrorManager.MSG_ILLEGAL_OPTION_VALUE, "assoc", assoc);
				}
			}
		}

		if ( altAssociativity.get(alt)!=null && altAssociativity.get(alt)!=assoc ) {
			ErrorManager.error(ErrorManager.MSG_ALL_OPS_NEED_SAME_ASSOC, alt);
		}
		altAssociativity.put(alt, assoc);

		//System.out.println("op " + alt + ": " + t.getText()+", assoc="+assoc);
	}

	@Override
	public void binaryAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {
		altTree = GrammarAST.dupTree(altTree);
		rewriteTree = GrammarAST.dupTree(rewriteTree);

		stripSynPred(altTree);
		stripLeftRecursion(altTree);

		// rewrite e to be e_[rec_arg]
		int nextPrec = nextPrecedence(alt);
		ST refST = recRuleTemplates.getInstanceOf("recRuleRef");
		refST.add("ruleName", ruleName);
		refST.add("arg", nextPrec);
		altTree = replaceRuleRefs(altTree, refST.render());

		String altText = text(altTree);
		altText = altText.trim();
		altText += "{}"; // add empty alt to prevent pred hoisting
		ST nameST = recRuleTemplates.getInstanceOf("recRuleName");
		nameST.add("ruleName", ruleName);
		rewriteTree = replaceRuleRefs(rewriteTree, "$" + nameST.render());
		String rewriteText = text(rewriteTree);
		binaryAlts.put(alt, altText + (rewriteText != null ? " " + rewriteText : ""));
		//System.out.println("binaryAlt " + alt + ": " + altText + ", rewrite=" + rewriteText);
	}

	
Convert e ? e : e → ? e : e_[nextPrec]
/** Convert e ? e : e &rarr; ? e : e_[nextPrec] */
@Override public void ternaryAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) { altTree = GrammarAST.dupTree(altTree); rewriteTree = GrammarAST.dupTree(rewriteTree); stripSynPred(altTree); stripLeftRecursion(altTree); int nextPrec = nextPrecedence(alt); ST refST = recRuleTemplates.getInstanceOf("recRuleRef"); refST.add("ruleName", ruleName); refST.add("arg", nextPrec); altTree = replaceLastRuleRef(altTree, refST.render()); String altText = text(altTree); altText = altText.trim(); altText += "{}"; // add empty alt to prevent pred hoisting ST nameST = recRuleTemplates.getInstanceOf("recRuleName"); nameST.add("ruleName", ruleName); rewriteTree = replaceRuleRefs(rewriteTree, "$" + nameST.render()); String rewriteText = text(rewriteTree); ternaryAlts.put(alt, altText + (rewriteText != null ? " " + rewriteText : "")); //System.out.println("ternaryAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); } @Override public void prefixAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) { altTree = GrammarAST.dupTree(altTree); rewriteTree = GrammarAST.dupTree(rewriteTree); stripSynPred(altTree); int nextPrec = precedence(alt); // rewrite e to be e_[rec_arg] ST refST = recRuleTemplates.getInstanceOf("recRuleRef"); refST.add("ruleName", ruleName); refST.add("arg", nextPrec); altTree = replaceRuleRefs(altTree, refST.render()); String altText = text(altTree); altText = altText.trim(); altText += "{}"; // add empty alt to prevent pred hoisting ST nameST = recRuleTemplates.getInstanceOf("recRuleName"); nameST.add("ruleName", ruleName); rewriteTree = replaceRuleRefs(rewriteTree, nameST.render()); String rewriteText = text(rewriteTree); prefixAlts.add(altText + (rewriteText != null ? " " + rewriteText : "")); //System.out.println("prefixAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); } @Override public void suffixAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) { altTree = GrammarAST.dupTree(altTree); rewriteTree = GrammarAST.dupTree(rewriteTree); stripSynPred(altTree); stripLeftRecursion(altTree); ST nameST = recRuleTemplates.getInstanceOf("recRuleName"); nameST.add("ruleName", ruleName); rewriteTree = replaceRuleRefs(rewriteTree, "$" + nameST.render()); String rewriteText = text(rewriteTree); String altText = text(altTree); altText = altText.trim(); suffixAlts.put(alt, altText + (rewriteText != null ? " " + rewriteText : "")); // System.out.println("suffixAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); } @Override public void otherAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) { altTree = GrammarAST.dupTree(altTree); rewriteTree = GrammarAST.dupTree(rewriteTree); stripSynPred(altTree); stripLeftRecursion(altTree); String altText = text(altTree); String rewriteText = text(rewriteTree); otherAlts.add(altText + (rewriteText != null ? " " + rewriteText : "")); //System.out.println("otherAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); } // --------- get transformed rules ---------------- public String getArtificialPrecStartRule() { ST ruleST = recRuleTemplates.getInstanceOf("recRuleStart"); ruleST.add("ruleName", ruleName); ruleST.add("minPrec", 0); ruleST.add("userRetvals", retvals); fillRetValAssignments(ruleST, "recRuleName"); System.out.println("start: " + ruleST); return ruleST.render(); } public String getArtificialOpPrecRule() { ST ruleST = recRuleTemplates.getInstanceOf("recRule"); ruleST.add("ruleName", ruleName); ruleST.add("buildAST", grammar.buildAST()); ST argDefST = generator.getTemplates().getInstanceOf("recRuleDefArg"); ruleST.add("precArgDef", argDefST); ST ruleArgST = generator.getTemplates().getInstanceOf("recRuleArg"); ruleST.add("argName", ruleArgST); ST setResultST = generator.getTemplates().getInstanceOf("recRuleSetResultAction"); ruleST.add("setResultAction", setResultST); ruleST.add("userRetvals", retvals); fillRetValAssignments(ruleST, "recPrimaryName"); LinkedHashMap<Integer, String> opPrecRuleAlts = new LinkedHashMap<Integer, String>(); opPrecRuleAlts.putAll(binaryAlts); opPrecRuleAlts.putAll(ternaryAlts); opPrecRuleAlts.putAll(suffixAlts); for (Map.Entry<Integer, String> entry : opPrecRuleAlts.entrySet()) { int alt = entry.getKey(); String altText = entry.getValue(); ST altST = recRuleTemplates.getInstanceOf("recRuleAlt"); ST predST = generator.getTemplates().getInstanceOf("recRuleAltPredicate"); predST.add("opPrec", precedence(alt)); predST.add("ruleName", ruleName); altST.add("pred", predST); altST.add("alt", altText); ruleST.add("alts", altST); } System.out.println(ruleST); return ruleST.render(); } public String getArtificialPrimaryRule() { ST ruleST = recRuleTemplates.getInstanceOf("recPrimaryRule"); ruleST.add("ruleName", ruleName); ruleST.add("alts", prefixAlts); ruleST.add("alts", otherAlts); ruleST.add("userRetvals", retvals); System.out.println(ruleST); return ruleST.render(); } public GrammarAST replaceRuleRefs(GrammarAST t, String name) { if ( t==null ) return null; for (GrammarAST rref : t.findAllType(RULE_REF)) { if ( rref.getText().equals(ruleName) ) rref.setText(name); } return t; } public static boolean hasImmediateRecursiveRuleRefs(GrammarAST t, String ruleName) { if ( t==null ) return false; for (GrammarAST rref : t.findAllType(RULE_REF)) { if ( rref.getText().equals(ruleName) ) return true; } return false; } public GrammarAST replaceLastRuleRef(GrammarAST t, String name) { if ( t==null ) return null; GrammarAST last = null; for (GrammarAST rref : t.findAllType(RULE_REF)) { last = rref; } if ( last !=null && last.getText().equals(ruleName) ) last.setText(name); return t; } public void stripSynPred(GrammarAST altAST) { GrammarAST t = (GrammarAST)altAST.getChild(0); if ( t.getType()==ANTLRParser.BACKTRACK_SEMPRED || t.getType()==ANTLRParser.SYNPRED || t.getType()==ANTLRParser.SYN_SEMPRED ) { altAST.deleteChild(0); // kill it } } public void stripLeftRecursion(GrammarAST altAST) { GrammarAST rref = (GrammarAST)altAST.getChild(0); if ( rref.getType()== ANTLRParser.RULE_REF && rref.getText().equals(ruleName)) { // remove rule ref altAST.deleteChild(0); // reset index so it prints properly GrammarAST newFirstChild = (GrammarAST) altAST.getChild(0); altAST.setTokenStartIndex(newFirstChild.getTokenStartIndex()); } } public String text(GrammarAST t) { if ( t==null ) return null; try { return new ANTLRTreePrinter(new CommonTreeNodeStream(t)).toString(grammar, true); } catch (Exception e) { ErrorManager.error(ErrorManager.MSG_BAD_AST_STRUCTURE, e); } return null; } public int precedence(int alt) { return numAlts-alt+1; } public int nextPrecedence(int alt) { int p = precedence(alt); if ( altAssociativity.get(alt)==ASSOC.left ) p++; return p; } public void fillRetValAssignments(ST ruleST, String srcName) { if ( retvals==null ) return; // complicated since we must be target-independent for (String name : getNamesFromArgAction(retvals.token)) { ST setRetValST = generator.getTemplates().getInstanceOf("recRuleSetReturnAction"); ST ruleNameST = recRuleTemplates.getInstanceOf(srcName); ruleNameST.add("ruleName", ruleName); setRetValST.add("src", ruleNameST); setRetValST.add("name", name); ruleST.add("userRetvalAssignments",setRetValST); } } public Collection<String> getNamesFromArgAction(Token t) { AttributeScope returnScope = grammar.createReturnScope("",t); returnScope.addAttributes(t.getText(), ','); return returnScope.attributes.keySet(); } @Override public String toString() { return "PrecRuleOperatorCollector{" + "binaryAlts=" + binaryAlts + ", rec=" + tokenToPrec + ", ternaryAlts=" + ternaryAlts + ", suffixAlts=" + suffixAlts + ", prefixAlts=" + prefixAlts + ", otherAlts=" + otherAlts + '}'; } }