All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.antlr.v4.codegen.model.RuleFunction Maven / Gradle / Ivy

There is a newer version: 4.9.0
Show newest version
/*
 * Copyright (c) 2012 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD-3-Clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

package org.antlr.v4.codegen.model;

import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.v4.codegen.OutputModelController;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.codegen.model.decl.AltLabelStructDecl;
import org.antlr.v4.codegen.model.decl.AttributeDecl;
import org.antlr.v4.codegen.model.decl.ContextRuleGetterDecl;
import org.antlr.v4.codegen.model.decl.ContextRuleListGetterDecl;
import org.antlr.v4.codegen.model.decl.ContextRuleListIndexedGetterDecl;
import org.antlr.v4.codegen.model.decl.ContextTokenGetterDecl;
import org.antlr.v4.codegen.model.decl.ContextTokenListGetterDecl;
import org.antlr.v4.codegen.model.decl.ContextTokenListIndexedGetterDecl;
import org.antlr.v4.codegen.model.decl.Decl;
import org.antlr.v4.codegen.model.decl.StructDecl;
import org.antlr.v4.misc.FrequencySet;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.parse.GrammarASTAdaptor;
import org.antlr.v4.runtime.atn.ATNSimulator;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.OrderedHashSet;
import org.antlr.v4.runtime.misc.Tuple;
import org.antlr.v4.runtime.misc.Tuple2;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.ErrorType;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.Rule;
import org.antlr.v4.tool.ast.ActionAST;
import org.antlr.v4.tool.ast.AltAST;
import org.antlr.v4.tool.ast.GrammarAST;
import org.antlr.v4.tool.ast.PredAST;
import org.antlr.v4.tool.ast.RuleAST;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.antlr.v4.parse.ANTLRParser.RULE_REF;
import static org.antlr.v4.parse.ANTLRParser.STRING_LITERAL;
import static org.antlr.v4.parse.ANTLRParser.TOKEN_REF;

/** */
public class RuleFunction extends OutputModelObject {
	public String name;
	public List modifiers;
	public String ctxType;
	public Collection ruleLabels;
	public Collection tokenLabels;
	public ATNState startState;
	public int index;
	public Rule rule;
	public boolean hasLookaheadBlock;
	public String variantOf;

	@ModelElement public List code;
	@ModelElement public OrderedHashSet locals; // TODO: move into ctx?
	@ModelElement public Collection args = null;
	@ModelElement public StructDecl ruleCtx;
	@ModelElement public Map altLabelCtxs;
	@ModelElement public Map namedActions;
	@ModelElement public Action finallyAction;
	@ModelElement public List exceptions;
	@ModelElement public List postamble;

	public RuleFunction(OutputModelFactory factory, Rule r) {
		super(factory);
		this.name = r.name;
		this.rule = r;
		if ( r.modifiers!=null && !r.modifiers.isEmpty() ) {
			this.modifiers = new ArrayList();
			for (GrammarAST t : r.modifiers) modifiers.add(t.getText());
		}
		modifiers = Utils.nodesToStrings(r.modifiers);

		index = r.index;
		int lfIndex = name.indexOf(ATNSimulator.RULE_VARIANT_DELIMITER);
		if (lfIndex >= 0) {
			variantOf = name.substring(0, lfIndex);
		}

		if (r.name.equals(r.getBaseContext())) {
			ruleCtx = new StructDecl(factory, r);
			addContextGetters(factory, r.g.contextASTs.get(r.name));

			if ( r.args!=null ) {
				Collection decls = r.args.attributes.values();
				if ( decls.size()>0 ) {
					args = new ArrayList();
					ruleCtx.addDecls(decls);
					for (Attribute a : decls) {
						args.add(new AttributeDecl(factory, a));
					}
					ruleCtx.ctorAttrs = args;
				}
			}
			if ( r.retvals!=null ) {
				ruleCtx.addDecls(r.retvals.attributes.values());
			}
			if ( r.locals!=null ) {
				ruleCtx.addDecls(r.locals.attributes.values());
			}
		}
		else {
			if (r.args != null || r.retvals != null || r.locals != null) {
				throw new UnsupportedOperationException("customized fields are not yet supported for customized context objects");
			}
		}

		ruleLabels = r.getElementLabelNames();
		tokenLabels = r.getTokenRefs();
		if ( r.exceptions!=null ) {
			exceptions = new ArrayList();
			for (GrammarAST e : r.exceptions) {
				ActionAST catchArg = (ActionAST)e.getChild(0);
				ActionAST catchAction = (ActionAST)e.getChild(1);
				exceptions.add(new ExceptionClause(factory, catchArg, catchAction));
			}
		}

		startState = factory.getGrammar().atn.ruleToStartState[r.index];
	}

	public StructDecl getEffectiveRuleContext(OutputModelController controller) {
		if (ruleCtx != null) {
			return ruleCtx;
		}

		RuleFunction effectiveRuleFunction = controller.rule(controller.getGrammar().getRule(rule.getBaseContext()));
		if (effectiveRuleFunction == this) {
			throw new IllegalStateException("Rule function does not have an effective context");
		}

		assert effectiveRuleFunction.ruleCtx != null;
		return effectiveRuleFunction.ruleCtx;
	}

	public Map getEffectiveAltLabelContexts(OutputModelController controller) {
		if (altLabelCtxs != null) {
			return altLabelCtxs;
		}

		RuleFunction effectiveRuleFunction = controller.rule(controller.getGrammar().getRule(rule.getBaseContext()));
		if (effectiveRuleFunction == this) {
			throw new IllegalStateException("Rule function does not have an effective context");
		}

		assert effectiveRuleFunction.altLabelCtxs != null;
		return effectiveRuleFunction.altLabelCtxs;
	}

	public void addContextGetters(OutputModelFactory factory, Collection contextASTs) {
		List unlabeledAlternatives = new ArrayList();
		Map> labeledAlternatives = new LinkedHashMap>();

		for (RuleAST ast : contextASTs) {
			try {
				unlabeledAlternatives.addAll(rule.g.getUnlabeledAlternatives(ast));
				for (Map.Entry>> entry : rule.g.getLabeledAlternatives(ast).entrySet()) {
					List list = labeledAlternatives.get(entry.getKey());
					if (list == null) {
						list = new ArrayList();
						labeledAlternatives.put(entry.getKey(), list);
					}

					for (Tuple2 tuple : entry.getValue()) {
						list.add(tuple.getItem2());
					}
				}
			}
			catch (RecognitionException ex) {
			}
		}

		// Add ctx labels for elements in alts with no '#' label
		if (!unlabeledAlternatives.isEmpty()) {
			Set decls = getDeclsForAllElements(unlabeledAlternatives);

			// put directly in base context
			for (Decl decl : decls) {
				ruleCtx.addDecl(decl);
			}
		}

		// make structs for '#' labeled alts, define ctx labels for elements
		altLabelCtxs = new LinkedHashMap();
		if (!labeledAlternatives.isEmpty()) {
			for (Map.Entry> entry : labeledAlternatives.entrySet()) {
				AltLabelStructDecl labelDecl = new AltLabelStructDecl(factory, rule, entry.getKey());
				altLabelCtxs.put(entry.getKey(), labelDecl);
				Set decls = getDeclsForAllElements(entry.getValue());
				for (Decl decl : decls) {
					labelDecl.addDecl(decl);
				}
			}
		}
	}

	public void fillNamedActions(OutputModelFactory factory, Rule r) {
		if ( r.finallyAction!=null ) {
			finallyAction = new Action(factory, r.finallyAction);
		}

		namedActions = new HashMap();
		for (String name : r.namedActions.keySet()) {
			ActionAST ast = r.namedActions.get(name);
			namedActions.put(name, new Action(factory, ast));
		}
	}

	/** for all alts, find which ref X or r needs List
	   Must see across alts. If any alt needs X or r as list, then
	   define as list.
	 */
	public Set getDeclsForAllElements(List altASTs) {
		Set needsList = new HashSet();
		Set nonOptional = new HashSet();
		Set suppress = new HashSet();
		List allRefs = new ArrayList();
		boolean firstAlt = true;
		IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF, STRING_LITERAL);
		for (AltAST ast : altASTs) {
			List refs = getRuleTokens(ast.getNodesWithType(reftypes));
			allRefs.addAll(refs);
			Tuple2, FrequencySet> minAndAltFreq = getElementFrequenciesForAlt(ast);
			FrequencySet minFreq = minAndAltFreq.getItem1();
			FrequencySet altFreq = minAndAltFreq.getItem2();
			for (GrammarAST t : refs) {
				String refLabelName = getLabelName(rule.g, t);
				if (refLabelName != null) {
					if (altFreq.count(refLabelName)==0) {
						suppress.add(refLabelName);
					}

					if (altFreq.count(refLabelName) > 1) {
						needsList.add(refLabelName);
					}

					if (firstAlt && minFreq.count(refLabelName) != 0) {
						nonOptional.add(refLabelName);
					}
				}
			}

			for (String ref : nonOptional.toArray(new String[nonOptional.size()])) {
				if (minFreq.count(ref) == 0) {
					nonOptional.remove(ref);
				}
			}

			firstAlt = false;
		}
		Set decls = new LinkedHashSet();
		for (GrammarAST t : allRefs) {
			String refLabelName = getLabelName(rule.g, t);
			if (refLabelName == null || suppress.contains(refLabelName)) {
				continue;
			}

			List d = getDeclForAltElement(t,
												refLabelName,
												needsList.contains(refLabelName),
												!nonOptional.contains(refLabelName));
			decls.addAll(d);
		}
		return decls;
	}

	private List getRuleTokens(List refs) {
		List result = new ArrayList(refs.size());
		for (GrammarAST ref : refs) {
			CommonTree r = ref;

			boolean ignore = false;
			while (r != null) {
				// Ignore string literals in predicates
				if (r instanceof PredAST) {
					ignore = true;
					break;
				}
				r = r.parent;
			}

			if (!ignore) {
				result.add(ref);
			}
		}

		return result;
	}

	public static String getLabelName(Grammar g, GrammarAST t) {
		String tokenText = t.getText();
		String tokenName = t.getType() != STRING_LITERAL ? tokenText : g.getTokenName(tokenText);
		if (tokenName == null || tokenName.startsWith("T__")) {
			// Do not include tokens with auto generated names
			return null;
		}

		String labelName = tokenName;
		Rule referencedRule = g.rules.get(labelName);
		if (referencedRule != null) {
			labelName = referencedRule.getBaseContext();
		}

		return labelName;
	}

	/** Given list of X and r refs in alt, compute how many of each there are */
	protected Tuple2, FrequencySet> getElementFrequenciesForAlt(AltAST ast) {
		try {
			ElementFrequenciesVisitor visitor = new ElementFrequenciesVisitor(rule.g, new CommonTreeNodeStream(new GrammarASTAdaptor(), ast));
			visitor.outerAlternative();
			if (visitor.frequencies.size() != 1) {
				factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR);
				return Tuple.create(new FrequencySet(), new FrequencySet());
			}

			return Tuple.create(visitor.getMinFrequencies(), visitor.frequencies.peek());
		}
		catch (RecognitionException ex) {
			factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex);
			return Tuple.create(new FrequencySet(), new FrequencySet());
		}
	}

	public List getDeclForAltElement(GrammarAST t, String refLabelName, boolean needList, boolean optional) {
		int lfIndex = refLabelName.indexOf(ATNSimulator.RULE_VARIANT_DELIMITER);
		if (lfIndex >= 0) {
			refLabelName = refLabelName.substring(0, lfIndex);
		}

		List decls = new ArrayList();
		if ( t.getType()==RULE_REF ) {
			Rule rref = factory.getGrammar().getRule(t.getText());
			String ctxName = factory.getTarget()
							 .getRuleFunctionContextStructName(rref);
			if ( needList) {
				if(factory.getTarget().supportsOverloadedMethods())
					decls.add( new ContextRuleListGetterDecl(factory, refLabelName, ctxName) );
				decls.add( new ContextRuleListIndexedGetterDecl(factory, refLabelName, ctxName) );
			}
			else {
				decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName, optional) );
			}
		}
		else {
			if ( needList ) {
				if(factory.getTarget().supportsOverloadedMethods())
					decls.add( new ContextTokenListGetterDecl(factory, refLabelName) );
				decls.add( new ContextTokenListIndexedGetterDecl(factory, refLabelName) );
			}
			else {
				decls.add( new ContextTokenGetterDecl(factory, refLabelName, optional) );
			}
		}
		return decls;
	}

	/** Add local var decl */
	public void addLocalDecl(Decl d) {
		if ( locals ==null ) locals = new OrderedHashSet();
		locals.add(d);
		d.isLocal = true;
	}

	/** Add decl to struct ctx for rule or alt if labeled */
	public void addContextDecl(String altLabel, Decl d) {
		CodeBlockForOuterMostAlt alt = d.getOuterMostAltCodeBlock();
		// if we found code blk and might be alt label, try to add to that label ctx
		if ( alt!=null ) {
//			System.out.println(d.name+" lives in alt "+alt.alt.altNum);
			AltLabelStructDecl altCtx = getEffectiveAltLabelContexts(factory.getController()).get(altLabel);
			if ( altCtx!=null ) { // we have an alt ctx
//				System.out.println("ctx is "+ altCtx.name);
				altCtx.addDecl(d);
				return;
			}
		}
		getEffectiveRuleContext(factory.getController()).addDecl(d); // stick in overall rule's ctx
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy