pmd/javacc-wrapper.xml
2020-07-01 11:06:48 +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-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-visitor-interface-file}" />
<replace file="${generic-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>