pmd/javacc-wrapper.xml
Clément Fournier b88ddf41c0 Add generic visitor interface in pmd-core
Replace SideEffectingVisitor with JavaVisitor

The new visitor is generic. We don't actually need the
old Object->Object visitor, this could just be the new
generic visitor but erased

Port language level checker

Move delegators

Remove old accept methods

Remove reduced adapter

Cleanup some visitor

Make ant wrapper replace old visitor completely

Doc

Add DeprecatedUntil700 annotation

Add top interface for visitors

Convert JSP visitors

Checkstyle

Fix java module
2020-06-14 11:43:24 +02:00

505 lines
20 KiB
XML

<!--
~ BSD-style license; for more info see http://pmd.sourceforge.net/license.html
-->
<project name="pmd" default="alljavacc" basedir="/">
<!-- Wraps a JavaCC/JJTree task and adapts the generated sources to the PMD
codebase.
Input parameters:
- lang-name: Capitalized name of the language eg Jsp or Java.
By convention the grammar file must be named exactly
so with a .jj[t] extension.
- lang-terse-name: Terse name, used in the conventional package names
It also uses the following maven properties:
- javacc.outputDirectory: Directory in which to root the generated package tree
- javacc.jar: JAR of JavaCC in the local maven repository
- some properties of project.build
-->
<property name="target-package-dir" value="${javacc.outputDirectory}/net/sourceforge/pmd/lang/${lang-terse-name}/ast" />
<property name="stamp-file" value="${project.build.directory}/last-generated-timestamp" />
<property name="javacc-home.path" value="${project.build.directory}/lib/javacc" />
<property name="lang-ast-package" value="net.sourceforge.pmd.lang.${lang-terse-name}.ast" />
<property name="ast-api-package" value="net.sourceforge.pmd.lang.ast" />
<property name="ast-impl-package" value="${ast-api-package}.impl.javacc" />
<property name="grammar-file" value="etc/grammar/${lang-name}.jjt" />
<property name="parser-name" value="${lang-name}ParserImpl" />
<property name="parser-file" value="${target-package-dir}/${parser-name}.java" />
<property name="ast-prefix" value="AST" />
<property name="constants-itf-name" value="${parser-name}Constants" />
<property name="constants-itf-file" value="${target-package-dir}/${constants-itf-name}.java" />
<property name="token-constants-name" value="${lang-name}TokenKinds" />
<property name="token-constants-file" value="${target-package-dir}/${token-constants-name}.java" />
<property name="node-name" value="${lang-name}Node" />
<property name="base-class-name" value="Abstract${lang-name}Node" />
<!-- This will be moved to impl package when all language modules have been ported -->
<property name="base-tokenmgr" value="${ast-api-package}.AbstractTokenManager"/>
<property name="charstream-itf" value="${ast-api-package}.CharStream"/>
<property name="tokenmgr-name" value="${parser-name}TokenManager" />
<property name="tokenmgr-file" value="${target-package-dir}/${tokenmgr-name}.java" />
<!-- Visitor names -->
<property name="gen-visitor-name" value="${parser-name}Visitor" />
<property name="base-visitor-interface-name" value="${lang-name}ParserVisitor" />
<property name="base-visitor-interface-file" value="${target-package-dir}/${base-visitor-interface-name}.java" />
<property name="generic-visitor-interface-name" value="${lang-name}Visitor" />
<property name="generic-sideeffect-visitor-interface-file"
value="${target-package-dir}/${generic-visitor-interface-name}.java" />
<!-- TARGETS -->
<target name="alljavacc"
description="Generates JavaCC sources and cleans them up"
depends="checkUpToDate,init,jjtree,jjtree-ersatz,javacc,adapt-generated,default-visitor,cleanup" />
<target name="alljavacc-visitor+"
description="Like alljavacc, replaces default JJTree visitor with a generic one"
depends="alljavacc,generic-visitor-replacement" />
<target name="checkUpToDate"
description="Checks the input files are up to date">
<uptodate property="javaccBuildNotRequired" targetfile="${stamp-file}">
<srcfiles dir="etc/grammar" includes="${lang-name}.jj*" />
<srcfiles file="${ant.file}" />
</uptodate>
<echo message="Up-to-date check: javaccBuildNotRequired=${javaccBuildNotRequired}" />
<condition property="jjtreeBuildNotRequired">
<or>
<isset property="no-jjtree" />
<isset property="javaccBuildNotRequired" />
</or>
</condition>
</target>
<target name="init" unless="javaccBuildNotRequired" description="Initialize build">
<mkdir dir="${javacc-home.path}" />
<copy file="${javacc.jar}" tofile="${javacc-home.path}/javacc.jar" />
<mkdir dir="${javacc.outputDirectory}" />
<touch file="${stamp-file}" />
<delete dir="${target-package-dir}" />
<mkdir dir="${target-package-dir}" />
</target>
<target name="cleanup" unless="javaccBuildNotRequired">
<delete dir="${javacc-home.path}" />
</target>
<target name="jjtree" unless="jjtreeBuildNotRequired" description="Runs JJTree">
<!--
Token-tracking behavior is implemented in the JJTree builder,
so the grammar should have TRACK_TOKEN=false; (this is the default value)
-->
<jjtree target="etc/grammar/${lang-name}.jjt"
outputdirectory="${target-package-dir}"
nodeusesparser="false"
nodepackage="${lang-ast-package}"
javacchome="${javacc-home.path}" />
</target>
<target name="jjtree-ersatz" if="no-jjtree" unless="javaccBuildNotRequired">
<!-- If no jjtree is run, then we look for a .jj file to place
in the target dir for the javacc target to pick up on it -->
<copy file="etc/grammar/${lang-name}.jj" todir="${target-package-dir}" />
</target>
<target name="javacc" depends="jjtree" unless="javaccBuildNotRequired">
<javacc static="false"
usercharstream="true"
unicodeinput="true"
target="${target-package-dir}/${lang-name}.jj"
outputdirectory="${target-package-dir}"
javacchome="${javacc-home.path}" />
</target>
<target name="adapt-generated" unless="javaccBuildNotRequired">
<delete file="${target-package-dir}/Node.java" />
<delete file="${target-package-dir}/SimpleNode.java" />
<delete file="${target-package-dir}/JJT${parser-name}State.java" />
<delete file="${target-package-dir}/CharStream.java" />
<delete file="${target-package-dir}/ParseException.java" />
<delete file="${target-package-dir}/Token.java" />
<delete file="${target-package-dir}/TokenMgrError.java" />
<!-- Remove all duplicate files -->
<delete>
<fileset dir="${javacc.outputDirectory}">
<present present="both" targetdir="${project.build.sourceDirectory}" />
</fileset>
</delete>
<antcall target="cleanup-token-manager" />
<antcall target="cleanup-parser" />
<antcall target="cleanup-nodes" />
<!-- Map Javacc names to our names -->
<replaceregexp flags="g">
<regexp pattern="\bToken\b" />
<substitution expression="${ast-impl-package}.JavaccToken" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="(?&lt;!\.)\bCharStream\b" />
<substitution expression="${charstream-itf}" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<antcall target="constants-files" />
<!-- For compatibility -->
<replaceregexp flags="g">
<regexp pattern="\.image\b(?! =)" />
<substitution expression=".getImage()" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replace token=".beginLine" value=".getBeginLine()">
<fileset dir="${target-package-dir}" />
</replace>
<replace token=".beginColumn" value=".getBeginColumn()">
<fileset dir="${target-package-dir}" />
</replace>
<!-- Used by debug modes -->
<replace token="TokenMgrError.addEscapes" value="net.sourceforge.pmd.util.StringUtil.escapeJava">
<fileset dir="${target-package-dir}" />
</replace>
</target>
<target name="cleanup-parser">
<replace token="new Token()" value="token_source.input_stream.getTokenDocument().open()">
<fileset file="${parser-file}" />
</replace>
<replaceregexp flags="g">
<regexp pattern='jjtree.openNodeScope\((jjtn.*?)\);' />
<substitution expression='jjtree.openNodeScope(\1, getToken(1));' />
<file name="${parser-file}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern='jjtree.closeNodeScope\((jjtn.*?)\);' />
<substitution expression='jjtree.closeNodeScope(\1, getToken(0));' />
<file name="${parser-file}" />
</replaceregexp>
<!-- The tree builder is generic now -->
<replaceregexp flags="g">
<regexp pattern="\bJJT${parser-name}State\b" />
<substitution expression="${ast-impl-package}.JjtreeBuilder&lt;${base-class-name}&gt;" />
<fileset file="${parser-file}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="new ParseException\(token, exptokseq, tokenImage\);" />
<substitution expression="new ParseException(token, exptokseq);" />
<fileset file="${parser-file}" />
</replaceregexp>
<replaceregexp>
<regexp pattern="public interface"/>
<substitution expression="interface"/>
<fileset file="${target-package-dir}/${parser-name}TreeConstants.java" />
</replaceregexp>
</target>
<target name="cleanup-token-manager">
<replaceregexp>
<regexp pattern='(public )?class ${tokenmgr-name}' />
<substitution expression='class ${tokenmgr-name} extends ${base-tokenmgr}' />
<file name="${tokenmgr-file}" />
</replaceregexp>
<!-- This is a programming error and should be caught in tests -->
<replaceregexp>
<regexp pattern='throw new TokenMgrError\("Error: Ignoring invalid lexical state.*?\);' />
<substitution expression='throw new IllegalArgumentException("Invalid lexical state " + lexState);' />
<file name="${tokenmgr-file}" />
</replaceregexp>
<!-- Useless argument, also replace lex state ID with its name -->
<replaceregexp>
<regexp pattern='curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR\)' />
<substitution expression='lexStateNames[curLexState], error_line, error_column, error_after, curChar)' />
<file name="${tokenmgr-file}" />
</replaceregexp>
<!-- Patch token creation routine, delegates to the token document. -->
<replaceregexp flags="s">
<file name="${tokenmgr-file}" />
<regexp pattern="protected Token jjFillToken.*?(?=int curLexState = )" />
<substitution expression="protected Token jjFillToken() {return input_stream.getTokenDocument().createToken(jjmatchedKind, input_stream, jjstrLiteralImages[jjmatchedKind]);}" />
</replaceregexp>
<!-- This is used to allow for tokens to be immutable. The lexical actions
return the new token instead of mutating it. -->
<replaceregexp flags="sg">
<file name="${tokenmgr-file}" />
<regexp pattern="void ((Token|Skip)LexicalActions\(Token matchedToken\))\R\{(.*?)}\R(?=(private )?void)" />
<substitution expression="Token \1 { \3
return matchedToken;
}" />
</replaceregexp>
<!-- Update call sites for the previous routines -->
<replaceregexp>
<file name="${tokenmgr-file}" />
<regexp pattern="(Token|Skip)LexicalActions\(matchedToken\);" />
<substitution expression="matchedToken = \0" />
</replaceregexp>
<!-- Fix end column being now exclusive -->
<!-- JavaCC assumes it's inclusive, uses it for character positions of errors -->
<!-- This is also used in debug mode -->
<replaceregexp flags="sg">
<file name="${tokenmgr-file}" />
<regexp pattern="input_stream.getEndColumn\(\)" />
<substitution expression="(input_stream.getEndColumn() - 1)" />
</replaceregexp>
</target>
<target name="constants-files">
<replaceregexp flags="g">
<regexp pattern="implements ${constants-itf-name}," />
<substitution expression="implements" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern=", ${constants-itf-name}\b" />
<substitution expression="" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="implements ${constants-itf-name}\b" />
<substitution expression="" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replaceregexp>
<regexp pattern="package ${lang-ast-package};" />
<substitution expression="\0
import static ${lang-ast-package}.${token-constants-name}.*;" />
<fileset file="${tokenmgr-file}" />
<fileset file="${parser-file}" />
</replaceregexp>
<!-- Create constants file -->
<move file="${constants-itf-file}" tofile="${token-constants-file}" />
<replaceregexp>
<regexp pattern="(public )?interface ${constants-itf-name} \{" />
<substitution expression="/** Token kinds ({@link ${ast-impl-package}.JavaccToken#kind}) for this language. */${line.separator}
public final class ${token-constants-name} \{${line.separator}
private ${token-constants-name}() { /* Utility class */ }${line.separator}
"/>
<fileset file="${token-constants-file}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="/\*\* RegularExpression Id. \*/\R" />
<substitution expression="public static final " />
<fileset file="${token-constants-file}" />
</replaceregexp>
<replaceregexp flags="s">
<regexp pattern="/\*\* End of File. \*/\R" />
<substitution expression="public static final " />
<fileset file="${token-constants-file}" />
</replaceregexp>
<!-- Lexical states are kept package-private -->
<replaceregexp flags="g">
<regexp pattern="/\*\* Lexical state. \*/\R" />
<substitution expression="static final " />
<fileset file="${token-constants-file}" />
</replaceregexp>
<replace>
<replacetoken>/** Literal token values. */</replacetoken>
<replacevalue><![CDATA[
/** Returns a string describing the given token kind.
* Returns null if the kind is unknown.
*
* @param kind Kind of token
*
* @return A string describing the given kind
*/
public static @org.checkerframework.checker.nullness.qual.Nullable
String describe(int kind) {
return kind < 0 || kind >= tokenImage.length
? null
: tokenImage[kind];
}
/** Returns a new token manager for this language. The CharStream
* should be configured with the correct language-specific escaping
* and token document. This is not a published API, it's intended to
* be used as a basis for a CPD Tokenizer.
*/
@net.sourceforge.pmd.annotation.InternalApi
public static net.sourceforge.pmd.lang.TokenManager<%%%API_PACK%%%.impl.javacc.JavaccToken> newTokenManager(%%%API_PACK%%%.CharStream cs) {
return new %%%TOKEN_MGR_NAME%%%(cs);
}
private static final
]]> </replacevalue>
<fileset file="${token-constants-file}" />
</replace>
<replaceregexp>
<regexp pattern="%%%TOKEN_MGR_NAME%%%" />
<substitution expression="${tokenmgr-name}" />
<fileset file="${token-constants-file}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="%%%API_PACK%%%" />
<substitution expression="${ast-api-package}" />
<fileset file="${token-constants-file}" />
</replaceregexp>
</target>
<target name="cleanup-nodes">
<replaceregexp flags="g">
<regexp pattern="\bParseException\b" />
<substitution expression="${ast-api-package}.ParseException" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="extends SimpleNode\b" />
<substitution expression="extends ${base-class-name}" />
<fileset dir="${target-package-dir}" />
</replaceregexp>
<!-- Remove constructor with parser -->
<replaceregexp flags="s">
<regexp pattern="public \w+\(${parser-name}.*?}" />
<substitution expression="" />
<fileset dir="${target-package-dir}">
<include name="${ast-prefix}*" />
</fileset>
</replaceregexp>
<!-- Make constructor package-private -->
<replaceregexp>
<regexp pattern="public (\w+\(int)" />
<substitution expression="\1" />
<fileset dir="${target-package-dir}">
<include name="${ast-prefix}*" />
</fileset>
</replaceregexp>
<!-- Make constructor package-private -->
<replaceregexp>
<regexp pattern="(?&lt;!public(\R| ))class" />
<substitution expression="public class" />
<fileset dir="${target-package-dir}">
<include name="${ast-prefix}*" />
</fileset>
</replaceregexp>
<replaceregexp flags="g">
<regexp pattern="/\*\* Accept the visitor. \*\*/" />
<substitution expression="" />
<fileset dir="${target-package-dir}">
<include name="${ast-prefix}*" />
</fileset>
</replaceregexp>
<!-- Remove constructor with parser -->
<replaceregexp flags="sg">
<regexp pattern="jjtAccept\(${gen-visitor-name}" />
<substitution expression="jjtAccept\(${base-visitor-interface-name}" />
<fileset dir="${target-package-dir}">
<include name="${ast-prefix}*" />
</fileset>
</replaceregexp>
</target>
<target name="default-visitor" depends="jjtree" unless="jjtreeBuildNotRequired">
<move file="${target-package-dir}/${gen-visitor-name}.java" tofile="${base-visitor-interface-file}" />
<!-- Base visitor with Object everywhere -->
<!-- We perform most changes like adding default methods, etc on this one -->
<!-- Changes are then copied on other visitors -->
<replace file="${base-visitor-interface-file}">
<replacefilter token="public interface" value="
public interface" />
<replacefilter token="${gen-visitor-name}" value="${base-visitor-interface-name}" />
<replacefilter token="SimpleNode" value="${node-name}" />
<!-- Default methods -->
<replacefilter token="public Object visit(" value="default Object visit(" />
<replacefilter token=");" value=") { return visit((${node-name}) node, data); }" />
<replacefilter
token="default Object visit(${node-name} node, Object data) { return visit((${node-name}) node, data); }"
value="default Object visit(${node-name} node, Object data) { for (int i = 0, len = node.getNumChildren(); i &lt; len; i++) node.getChild(i).jjtAccept(this, data); return data; }">
</replacefilter>
</replace>
</target>
<target name="generic-visitor-replacement" depends="default-visitor" unless="jjtreeBuildNotRequired">
<move file="${base-visitor-interface-file}" tofile="${generic-sideeffect-visitor-interface-file}" />
<replace file="${generic-sideeffect-visitor-interface-file}">
<replacefilter token="${base-visitor-interface-name}"
value="${generic-visitor-interface-name}&lt;P, R> extends ${ast-api-package}.AstVisitor&lt;P, R>" />
<replacefilter token="Object" value="P" />
<replacefilter token="P visit" value="R visit" />
<replacefilter token="default R visit(${node-name} node, P data) { for (int i = 0, len = node.getNumChildren(); i &lt; len; i++) node.getChild(i).jjtAccept(this, data); return data; }" value="default R visit(${node-name} node, P data) { return visitNode(node, data); }" />
</replace>
</target>
</project>