/* * Copyright (c) 2012-2017 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; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.Token; import org.antlr.v4.codegen.model.RuleFunction; import org.antlr.v4.codegen.model.chunk.ActionChunk; import org.antlr.v4.codegen.model.chunk.ActionText; import org.antlr.v4.codegen.model.chunk.ArgRef; import org.antlr.v4.codegen.model.chunk.LabelRef; import org.antlr.v4.codegen.model.chunk.ListLabelRef; import org.antlr.v4.codegen.model.chunk.LocalRef; import org.antlr.v4.codegen.model.chunk.NonLocalAttrRef; import org.antlr.v4.codegen.model.chunk.QRetValueRef; import org.antlr.v4.codegen.model.chunk.RetValueRef; import org.antlr.v4.codegen.model.chunk.RulePropertyRef; import org.antlr.v4.codegen.model.chunk.RulePropertyRef_ctx; import org.antlr.v4.codegen.model.chunk.RulePropertyRef_parser; import org.antlr.v4.codegen.model.chunk.RulePropertyRef_start; import org.antlr.v4.codegen.model.chunk.RulePropertyRef_stop; import org.antlr.v4.codegen.model.chunk.RulePropertyRef_text; import org.antlr.v4.codegen.model.chunk.SetAttr; import org.antlr.v4.codegen.model.chunk.SetNonLocalAttr; import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_ctx; import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_parser; import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_start; import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_stop; import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_text; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_channel; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_index; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_int; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_line; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_pos; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_text; import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_type; import org.antlr.v4.codegen.model.chunk.TokenRef; import org.antlr.v4.codegen.model.decl.StructDecl; import org.antlr.v4.parse.ActionSplitter; import org.antlr.v4.parse.ActionSplitterListener; 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 java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** */ public class ActionTranslator implements ActionSplitterListener { public static final Map<String, Class<? extends RulePropertyRef>> thisRulePropToModelMap = new HashMap<String, Class<? extends RulePropertyRef>>(); static { thisRulePropToModelMap.put("start", ThisRulePropertyRef_start.class); thisRulePropToModelMap.put("stop", ThisRulePropertyRef_stop.class); thisRulePropToModelMap.put("text", ThisRulePropertyRef_text.class); thisRulePropToModelMap.put("ctx", ThisRulePropertyRef_ctx.class); thisRulePropToModelMap.put("parser", ThisRulePropertyRef_parser.class); } public static final Map<String, Class<? extends RulePropertyRef>> rulePropToModelMap = new HashMap<String, Class<? extends RulePropertyRef>>(); static { rulePropToModelMap.put("start", RulePropertyRef_start.class); rulePropToModelMap.put("stop", RulePropertyRef_stop.class); rulePropToModelMap.put("text", RulePropertyRef_text.class); rulePropToModelMap.put("ctx", RulePropertyRef_ctx.class); rulePropToModelMap.put("parser", RulePropertyRef_parser.class); } public static final Map<String, Class<? extends TokenPropertyRef>> tokenPropToModelMap = new HashMap<String, Class<? extends TokenPropertyRef>>(); static { tokenPropToModelMap.put("text", TokenPropertyRef_text.class); tokenPropToModelMap.put("type", TokenPropertyRef_type.class); tokenPropToModelMap.put("line", TokenPropertyRef_line.class); tokenPropToModelMap.put("index", TokenPropertyRef_index.class); tokenPropToModelMap.put("pos", TokenPropertyRef_pos.class); tokenPropToModelMap.put("channel", TokenPropertyRef_channel.class); tokenPropToModelMap.put("int", TokenPropertyRef_int.class); } CodeGenerator gen; ActionAST node; RuleFunction rf; List<ActionChunk> chunks = new ArrayList<ActionChunk>(); OutputModelFactory factory; StructDecl nodeContext; public ActionTranslator(OutputModelFactory factory, ActionAST node) { this.factory = factory; this.node = node; this.gen = factory.getGenerator(); } public static String toString(List<ActionChunk> chunks) { StringBuilder buf = new StringBuilder(); for (ActionChunk c : chunks) buf.append(c.toString()); return buf.toString(); } public static List<ActionChunk> translateAction(OutputModelFactory factory, RuleFunction rf, Token tokenWithinAction, ActionAST node) { String action = tokenWithinAction.getText(); if ( action!=null && action.length()>0 && action.charAt(0)=='{' ) { int firstCurly = action.indexOf('{'); int lastCurly = action.lastIndexOf('}'); if ( firstCurly>=0 && lastCurly>=0 ) { action = action.substring(firstCurly+1, lastCurly); // trim {...} } } return translateActionChunk(factory, rf, action, node); } public static List<ActionChunk> translateActionChunk(OutputModelFactory factory, RuleFunction rf, String action, ActionAST node) { Token tokenWithinAction = node.token; ActionTranslator translator = new ActionTranslator(factory, node); translator.rf = rf; factory.getGrammar().tool.log("action-translator", "translate " + action); String altLabel = node.getAltLabel(); if ( rf!=null ) { translator.nodeContext = rf.ruleCtx; if ( altLabel!=null ) translator.nodeContext = rf.altLabelCtxs.get(altLabel); } ANTLRStringStream in = new ANTLRStringStream(action); in.setLine(tokenWithinAction.getLine()); in.setCharPositionInLine(tokenWithinAction.getCharPositionInLine()); ActionSplitter trigger = new ActionSplitter(in, translator); // forces eval, triggers listener methods trigger.getActionTokens(); return translator.chunks; } @Override public void attr(String expr, Token x) { gen.g.tool.log("action-translator", "attr "+x); Attribute a = node.resolver.resolveToAttribute(x.getText(), node); if ( a!=null ) { switch ( a.dict.type ) { case ARG: chunks.add(new ArgRef(nodeContext,x.getText())); break; case RET: chunks.add(new RetValueRef(rf.ruleCtx, x.getText())); break; case LOCAL: chunks.add(new LocalRef(nodeContext,x.getText())); break; case PREDEFINED_RULE: chunks.add(getRulePropertyRef(x)); break; } } if ( node.resolver.resolvesToToken(x.getText(), node) ) { chunks.add(new TokenRef(nodeContext,getTokenLabel(x.getText()))); // $label return; } if ( node.resolver.resolvesToLabel(x.getText(), node) ) { chunks.add(new LabelRef(nodeContext,getTokenLabel(x.getText()))); // $x for x=ID etc... return; } if ( node.resolver.resolvesToListLabel(x.getText(), node) ) { chunks.add(new ListLabelRef(nodeContext,x.getText())); // $ids for ids+=ID etc... return; } Rule r = factory.getGrammar().getRule(x.getText()); if ( r!=null ) { chunks.add(new LabelRef(nodeContext,getRuleLabel(x.getText()))); // $r for r rule ref } } @Override public void qualifiedAttr(String expr, Token x, Token y) { gen.g.tool.log("action-translator", "qattr "+x+"."+y); if ( node.resolver.resolveToAttribute(x.getText(), node)!=null ) { // must be a member access to a predefined attribute like $ctx.foo attr(expr, x); chunks.add(new ActionText(nodeContext, "."+y.getText())); return; } Attribute a = node.resolver.resolveToAttribute(x.getText(), y.getText(), node); if ( a==null ) { // Added in response to https://github.com/antlr/antlr4/issues/1211 gen.g.tool.errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE, gen.g.fileName, x, x.getText(), "rule"); return; } switch ( a.dict.type ) { case ARG: chunks.add(new ArgRef(nodeContext,y.getText())); break; // has to be current rule case RET: chunks.add(new QRetValueRef(nodeContext, getRuleLabel(x.getText()), y.getText())); break; case PREDEFINED_RULE: chunks.add(getRulePropertyRef(x, y)); break; case TOKEN: chunks.add(getTokenPropertyRef(x, y)); break; } } @Override public void setAttr(String expr, Token x, Token rhs) { gen.g.tool.log("action-translator", "setAttr "+x+" "+rhs); List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node); SetAttr s = new SetAttr(nodeContext, x.getText(), rhsChunks); chunks.add(s); } @Override public void nonLocalAttr(String expr, Token x, Token y) { gen.g.tool.log("action-translator", "nonLocalAttr "+x+"::"+y); Rule r = factory.getGrammar().getRule(x.getText()); chunks.add(new NonLocalAttrRef(nodeContext, x.getText(), y.getText(), r.index)); } @Override public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { gen.g.tool.log("action-translator", "setNonLocalAttr "+x+"::"+y+"="+rhs); Rule r = factory.getGrammar().getRule(x.getText()); List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node); SetNonLocalAttr s = new SetNonLocalAttr(nodeContext, x.getText(), y.getText(), r.index, rhsChunks); chunks.add(s); } @Override public void text(String text) { chunks.add(new ActionText(nodeContext,text)); } TokenPropertyRef getTokenPropertyRef(Token x, Token y) { try { Class<? extends TokenPropertyRef> c = tokenPropToModelMap.get(y.getText()); Constructor<? extends TokenPropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); TokenPropertyRef ref = ctor.newInstance(nodeContext, getTokenLabel(x.getText())); return ref; } catch (Exception e) { factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e); } return null; } // $text RulePropertyRef getRulePropertyRef(Token prop) { try { Class<? extends RulePropertyRef> c = thisRulePropToModelMap.get(prop.getText()); Constructor<? extends RulePropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); RulePropertyRef ref = ctor.newInstance(nodeContext, getRuleLabel(prop.getText())); return ref; } catch (Exception e) { factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e); } return null; } RulePropertyRef getRulePropertyRef(Token x, Token prop) { Grammar g = factory.getGrammar(); try { Class<? extends RulePropertyRef> c = rulePropToModelMap.get(prop.getText()); Constructor<? extends RulePropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); RulePropertyRef ref = ctor.newInstance(nodeContext, getRuleLabel(x.getText())); return ref; } catch (Exception e) { g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e, prop.getText()); } return null; } public String getTokenLabel(String x) { if ( node.resolver.resolvesToLabel(x, node) ) return x; return factory.getGenerator().getTarget().getImplicitTokenLabel(x); } public String getRuleLabel(String x) { if ( node.resolver.resolvesToLabel(x, node) ) return x; return factory.getGenerator().getTarget().getImplicitRuleLabel(x); } }