2020-01-10 21:54:37 +01:00
<!--
~ 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
2020-01-10 23:23:39 +01:00
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
2020-01-10 21:54:37 +01:00
-->
2020-01-10 23:23:39 +01:00
<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" />
2020-01-10 21:54:37 +01:00
2020-01-10 23:23:39 +01:00
<property name= "javacc-home.path" value= "${project.build.directory}/lib/javacc" />
2020-01-10 21:54:37 +01:00
2020-01-11 00:58:49 +01:00
<property name= "lang-ast-package" value= "net.sourceforge.pmd.lang.${lang-terse-name}.ast" />
2020-01-10 21:54:37 +01:00
<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" />
2020-01-11 00:58:49 +01:00
2020-01-11 02:39:24 +01:00
<property name= "parser-name" value= "${lang-name}ParserImpl" />
2020-01-11 00:58:49 +01:00
<property name= "parser-file" value= "${target-package-dir}/${parser-name}.java" />
2020-01-11 02:39:24 +01:00
<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" />
2020-01-10 21:54:37 +01:00
<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" />
2020-01-11 02:39:24 +01:00
<!-- 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" />
2020-04-28 08:38:10 +02:00
<property name= "generic-visitor-interface-name" value= "${lang-name}Visitor" />
2020-01-11 02:39:24 +01:00
<property name= "generic-sideeffect-visitor-interface-file"
2020-04-28 08:38:10 +02:00
value="${target-package-dir}/${generic-visitor-interface-name}.java" />
2020-01-11 02:39:24 +01:00
2020-01-10 21:54:37 +01:00
<!-- 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+"
2020-04-28 08:38:10 +02:00
description="Like alljavacc, replaces default JJTree visitor with a generic one"
depends="alljavacc,generic-visitor-replacement" />
2020-01-10 21:54:37 +01:00
<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" />
2020-01-10 23:23:39 +01:00
<mkdir dir= "${javacc.outputDirectory}" />
2020-01-10 21:54:37 +01:00
<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" >
2020-03-19 17:57:30 +01:00
<!--
Token-tracking behavior is implemented in the JJTree builder,
so the grammar should have TRACK_TOKEN=false; (this is the default value)
-->
2020-01-10 21:54:37 +01:00
<jjtree target= "etc/grammar/${lang-name}.jjt"
outputdirectory="${target-package-dir}"
2020-01-11 02:39:24 +01:00
nodeusesparser="false"
2020-01-11 00:58:49 +01:00
nodepackage="${lang-ast-package}"
2020-01-10 21:54:37 +01:00
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"
2020-01-11 05:22:29 +01:00
unicodeinput="true"
2020-01-10 21:54:37 +01:00
target="${target-package-dir}/${lang-name}.jj"
outputdirectory="${target-package-dir}"
javacchome="${javacc-home.path}" />
</target>
<target name= "adapt-generated" unless= "javaccBuildNotRequired" >
2020-01-11 02:39:24 +01:00
<delete file= "${target-package-dir}/Node.java" />
<delete file= "${target-package-dir}/SimpleNode.java" />
<delete file= "${target-package-dir}/JJT${parser-name}State.java" />
2020-01-10 21:54:37 +01:00
<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 >
2020-01-10 23:23:39 +01:00
<fileset dir= "${javacc.outputDirectory}" >
<present present= "both" targetdir= "${project.build.sourceDirectory}" />
2020-01-10 21:54:37 +01:00
</fileset>
</delete>
2020-01-11 02:39:24 +01:00
<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= "(?<!\.)\bCharStream\b" />
<substitution expression= "${charstream-itf}" />
<fileset dir= "${target-package-dir}" />
</replaceregexp>
<antcall target= "constants-files" />
<!-- For compatibility -->
2020-01-11 05:22:29 +01:00
<replaceregexp flags= "g" >
2020-02-08 00:28:13 +01:00
<regexp pattern= "\.image\b(?! =)" />
2020-01-11 05:22:29 +01:00
<substitution expression= ".getImage()" />
<fileset dir= "${target-package-dir}" />
</replaceregexp>
2020-01-11 02:39:24 +01:00
<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<${base-class-name}>" />
<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" >
2020-01-11 05:22:29 +01:00
<replaceregexp >
<regexp pattern= '(public )?class ${tokenmgr-name}' />
<substitution expression= 'class ${tokenmgr-name} extends ${base-tokenmgr}' />
<file name= "${tokenmgr-file}" />
</replaceregexp>
2020-01-11 02:39:24 +01:00
<!-- 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>
2020-01-25 15:17:12 +01:00
<!-- Useless argument, also replace lex state ID with its name -->
2020-01-11 02:39:24 +01:00
<replaceregexp >
2020-01-25 15:17:12 +01:00
<regexp pattern= 'curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR\)' />
<substitution expression= 'lexStateNames[curLexState], error_line, error_column, error_after, curChar)' />
2020-01-11 02:39:24 +01:00
<file name= "${tokenmgr-file}" />
</replaceregexp>
2020-01-10 21:54:37 +01:00
<!-- 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}" />
2020-01-15 00:07:06 +01:00
<regexp pattern= "void ((Token|Skip)LexicalActions\(Token matchedToken\))\R\{(.*?)}\R(?=(private )?void)" />
2020-01-10 21:54:37 +01:00
<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>
2020-01-11 02:39:24 +01:00
</target>
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<target name= "constants-files" >
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<replaceregexp flags= "g" >
<regexp pattern= "implements ${constants-itf-name}," />
<substitution expression= "implements" />
<fileset dir= "${target-package-dir}" />
2020-01-10 21:54:37 +01:00
</replaceregexp>
2020-01-11 00:58:49 +01:00
<replaceregexp flags= "g" >
2020-01-11 02:39:24 +01:00
<regexp pattern= ", ${constants-itf-name}\b" />
<substitution expression= "" />
<fileset dir= "${target-package-dir}" />
2020-01-11 00:58:49 +01:00
</replaceregexp>
<replaceregexp flags= "g" >
2020-01-11 02:39:24 +01:00
<regexp pattern= "implements ${constants-itf-name}\b" />
<substitution expression= "" />
<fileset dir= "${target-package-dir}" />
2020-01-11 00:58:49 +01:00
</replaceregexp>
2020-01-11 02:39:24 +01:00
<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>
2020-01-11 00:58:49 +01:00
2020-01-11 02:39:24 +01:00
<!-- Create constants file -->
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<move file= "${constants-itf-file}" tofile= "${token-constants-file}" />
<replaceregexp >
2020-03-19 20:33:17 +01:00
<regexp pattern= "(public )?interface ${constants-itf-name} \{" />
<substitution expression= "/** T o k e n k i n d s ( { @ l i n k $ { a s t - i m p l - p a c k a g e } . J a v a c c T o k e n # k i n d } ) f o r t h i s l a n g u a g e . * / $ { l i n e . s e p a r a t o r }
public final class ${token-constants-name} \{${line.separator}
private ${token-constants-name}() { /* Utility class */ }${line.separator}
"/>
2020-01-11 02:39:24 +01:00
<fileset file= "${token-constants-file}" />
2020-01-10 21:54:37 +01:00
</replaceregexp>
<replaceregexp flags= "g" >
2020-01-15 00:07:06 +01:00
<regexp pattern= "/\*\* RegularExpression Id. \*/\R" />
2020-01-11 02:39:24 +01:00
<substitution expression= "public static final " />
<fileset file= "${token-constants-file}" />
2020-01-10 21:54:37 +01:00
</replaceregexp>
2020-01-11 02:39:24 +01:00
<replaceregexp flags= "s" >
2020-03-19 20:33:17 +01:00
<regexp pattern= "/\*\* End of File. \*/\R" />
<substitution expression= "public static final " />
2020-01-11 02:39:24 +01:00
<fileset file= "${token-constants-file}" />
</replaceregexp>
<!-- Lexical states are kept package - private -->
2020-01-10 21:54:37 +01:00
<replaceregexp flags= "g" >
2020-01-15 00:07:06 +01:00
<regexp pattern= "/\*\* Lexical state. \*/\R" />
2020-01-11 02:39:24 +01:00
<substitution expression= "static final " />
<fileset file= "${token-constants-file}" />
2020-01-10 21:54:37 +01:00
</replaceregexp>
2020-01-11 02:39:24 +01:00
<replace >
<replacetoken > /** Literal token values. */</replacetoken>
2020-03-20 16:32:09 +01:00
<replacevalue > < ![CDATA[
2020-01-11 02:39:24 +01:00
/** 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) {
2020-03-20 16:32:09 +01:00
return kind < 0 | | k i n d > = tokenImage.length
2020-01-11 02:39:24 +01:00
? null
: tokenImage[kind];
}
2020-03-20 17:07:48 +01:00
/** 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
2020-03-20 16:32:09 +01:00
public static net.sourceforge.pmd.lang.TokenManager< %%%API_PACK%%%.impl.javacc.JavaccToken> newTokenManager(%%%API_PACK%%%.CharStream cs) {
return new %%%TOKEN_MGR_NAME%%%(cs);
}
2020-01-11 02:39:24 +01:00
private static final
2020-03-20 16:32:09 +01:00
]]> </replacevalue>
2020-01-11 02:39:24 +01:00
<fileset file= "${token-constants-file}" />
</replace>
2020-03-20 16:32:09 +01:00
<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>
2020-01-11 02:39:24 +01:00
</target>
<target name= "cleanup-nodes" >
2020-01-10 21:54:37 +01:00
<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>
2020-01-11 02:39:24 +01:00
<!-- 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>
2020-01-10 21:54:37 +01:00
</replaceregexp>
2020-01-11 02:39:24 +01:00
<!-- Make constructor package - private -->
<replaceregexp >
<regexp pattern= "public (\w+\(int)" />
<substitution expression= "\1" />
<fileset dir= "${target-package-dir}" >
<include name= "${ast-prefix}*" />
</fileset>
</replaceregexp>
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<!-- Make constructor package - private -->
<replaceregexp >
2020-02-15 00:48:57 +01:00
<regexp pattern= "(?<!public(\R| ))class" />
2020-01-11 02:39:24 +01:00
<substitution expression= "public class" />
<fileset dir= "${target-package-dir}" >
<include name= "${ast-prefix}*" />
</fileset>
</replaceregexp>
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<replaceregexp flags= "g" >
<regexp pattern= "/\*\* Accept the visitor. \*\*/" />
<substitution expression= "" />
<fileset dir= "${target-package-dir}" >
<include name= "${ast-prefix}*" />
</fileset>
</replaceregexp>
2020-01-10 21:54:37 +01:00
2020-01-11 02:39:24 +01:00
<!-- 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>
2020-01-10 21:54:37 +01:00
</target>
<target name= "default-visitor" depends= "jjtree" unless= "jjtreeBuildNotRequired" >
2020-01-11 02:39:24 +01:00
<move file= "${target-package-dir}/${gen-visitor-name}.java" tofile= "${base-visitor-interface-file}" />
2020-01-10 21:54:37 +01:00
<!-- 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" />
2020-01-11 02:39:24 +01:00
<replacefilter token= "${gen-visitor-name}" value= "${base-visitor-interface-name}" />
2020-01-10 21:54:37 +01:00
<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); }"
2020-01-17 16:13:17 +01:00
value="default Object visit(${node-name} node, Object data) { for (int i = 0, len = node.getNumChildren(); i < len; i++) node.getChild(i).jjtAccept(this, data); return data; }">
2020-01-10 21:54:37 +01:00
</replacefilter>
</replace>
</target>
2020-04-28 08:38:10 +02:00
<target name= "generic-visitor-replacement" depends= "default-visitor" unless= "jjtreeBuildNotRequired" >
<move file= "${base-visitor-interface-file}" tofile= "${generic-sideeffect-visitor-interface-file}" />
2020-01-10 21:54:37 +01:00
<replace file= "${generic-sideeffect-visitor-interface-file}" >
<replacefilter token= "${base-visitor-interface-name}"
2020-04-28 08:38:10 +02:00
value="${generic-visitor-interface-name}< P, R> extends ${ast-api-package}.AstVisitor< P, R>" />
2020-01-10 21:54:37 +01:00
2020-04-28 08:38:10 +02:00
<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 < len; i++) node.getChild(i).jjtAccept(this, data); return data; }" value= "default R visit(${node-name} node, P data) { return visitNode(node, data); }" />
2020-01-10 21:54:37 +01:00
</replace>
</target>
</project>