org.antlr.v4.codegen.OutputModelWalker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of antlr4 Show documentation
Show all versions of antlr4 Show documentation
The ANTLR 4 grammar compiler.
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* Copyright (c) 2012 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.codegen;
import org.antlr.v4.Tool;
import org.antlr.v4.codegen.model.ModelElement;
import org.antlr.v4.codegen.model.OutputModelObject;
import org.antlr.v4.tool.ErrorType;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.compiler.FormalArgument;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/** Convert an output model tree to template hierarchy by walking
* the output model. Each output model object has a corresponding template
* of the same name. An output model object can have nested objects.
* We identify those nested objects by the list of arguments in the template
* definition. For example, here is the definition of the parser template:
*
* Parser(parser, scopes, funcs) ::= <<...>>
*
* The first template argument is always the output model object from which
* this walker will create the template. Any other arguments identify
* the field names within the output model object of nested model objects.
* So, in this case, template Parser is saying that output model object
* Parser has two fields the walker should chase called a scopes and funcs.
*
* This simple mechanism means we don't have to include code in every
* output model object that says how to create the corresponding template.
*/
public class OutputModelWalker {
Tool tool;
STGroup templates;
public OutputModelWalker(Tool tool,
STGroup templates)
{
this.tool = tool;
this.templates = templates;
}
public ST walk(OutputModelObject omo) {
// CREATE TEMPLATE FOR THIS OUTPUT OBJECT
Class extends OutputModelObject> cl = omo.getClass();
String templateName = cl.getSimpleName();
if ( templateName == null ) {
tool.errMgr.toolError(ErrorType.NO_MODEL_TO_TEMPLATE_MAPPING, cl.getSimpleName());
return new ST("["+templateName+" invalid]");
}
ST st = templates.getInstanceOf(templateName);
if ( st == null ) {
tool.errMgr.toolError(ErrorType.CODE_GEN_TEMPLATES_INCOMPLETE, templateName);
return new ST("["+templateName+" invalid]");
}
if ( st.impl.formalArguments == null ) {
tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, "");
return st;
}
Map formalArgs = st.impl.formalArguments;
// PASS IN OUTPUT MODEL OBJECT TO TEMPLATE AS FIRST ARG
Set argNames = formalArgs.keySet();
Iterator arg_it = argNames.iterator();
String modelArgName = arg_it.next(); // ordered so this is first arg
st.add(modelArgName, omo);
// COMPUTE STs FOR EACH NESTED MODEL OBJECT MARKED WITH @ModelElement AND MAKE ST ATTRIBUTE
Set usedFieldNames = new HashSet();
Field fields[] = cl.getFields();
for (Field fi : fields) {
ModelElement annotation = fi.getAnnotation(ModelElement.class);
if (annotation == null) {
continue;
}
String fieldName = fi.getName();
if (!usedFieldNames.add(fieldName)) {
tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "Model object " + omo.getClass().getSimpleName() + " has multiple fields named '" + fieldName + "'");
continue;
}
// Just don't set @ModelElement fields w/o formal arg in target ST
if ( formalArgs.get(fieldName)==null ) continue;
try {
Object o = fi.get(omo);
if ( o instanceof OutputModelObject ) { // SINGLE MODEL OBJECT?
OutputModelObject nestedOmo = (OutputModelObject)o;
ST nestedST = walk(nestedOmo);
// System.out.println("set ModelElement "+fieldName+"="+nestedST+" in "+templateName);
st.add(fieldName, nestedST);
}
else if ( o instanceof Collection || o instanceof OutputModelObject[] ) {
// LIST OF MODEL OBJECTS?
if ( o instanceof OutputModelObject[] ) {
o = Arrays.asList((OutputModelObject[])o);
}
Collection> nestedOmos = (Collection>)o;
for (Object nestedOmo : nestedOmos) {
if ( nestedOmo==null ) continue;
ST nestedST = walk((OutputModelObject)nestedOmo);
// System.out.println("set ModelElement "+fieldName+"="+nestedST+" in "+templateName);
st.add(fieldName, nestedST);
}
}
else if ( o instanceof Map, ?> ) {
Map, ?> nestedOmoMap = (Map, ?>)o;
Map