diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml
index 043d62d9ab..ed41ac0ad8 100644
--- a/docs/_data/sidebars/pmd_sidebar.yml
+++ b/docs/_data/sidebars/pmd_sidebar.yml
@@ -406,6 +406,9 @@ entries:
- title: Gherkin
url: /pmd_languages_gherkin.html
output: web, pdf
+ - title: Julia
+ url: /pmd_languages_julia.html
+ output: web, pdf
- title: Developer Documentation
output: web, pdf
folderitems:
diff --git a/docs/pages/pmd/languages/julia.md b/docs/pages/pmd/languages/julia.md
new file mode 100644
index 0000000000..c13e4cdc95
--- /dev/null
+++ b/docs/pages/pmd/languages/julia.md
@@ -0,0 +1,15 @@
+---
+title: Julia
+permalink: pmd_languages_julia.html
+---
+
+The [Julia](https://julialang.org/) is dynamically typed, like a scripting language,
+and has good support for interactive use.
+Julia was designed from the beginning for high performance.
+Julia programs compile to efficient native code for multiple platforms via LLVM.
+
+## Support in PMD
+Starting from version 6.55.0, Gherkin support was added to CPD.
+
+### Limitations
+- Support for Julia only extends to CPD to detect code duplication in Julia source files.
diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml
index 17850a571b..48600b944e 100644
--- a/pmd-dist/pom.xml
+++ b/pmd-dist/pom.xml
@@ -179,6 +179,11 @@
pmd-jsp
${project.version}
+
+ net.sourceforge.pmd
+ pmd-julia
+ ${project.version}
+
net.sourceforge.pmd
pmd-kotlin
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
index 36cf4f1b2e..ac31bdeb9f 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
@@ -28,10 +28,10 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest {
static {
// note: apex, javascript, visualforce, and scala require java8
if (PMDExecutor.isJava7Test()) {
- SUPPORTED_LANGUAGES_CPD = "Supported languages: [cpp, cs, dart, fortran, gherkin, go, groovy, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, swift, tsql, xml]";
+ SUPPORTED_LANGUAGES_CPD = "Supported languages: [cpp, cs, dart, fortran, gherkin, go, groovy, java, jsp, julia, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, swift, tsql, xml]";
SUPPORTED_LANGUAGES_PMD = "java, jsp, modelica, plsql, pom, vm, wsdl, xml, xsl";
} else {
- SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, gherkin, go, groovy, html, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, tsql, vf, xml]";
+ SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, gherkin, go, groovy, html, java, jsp, julia, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, tsql, vf, xml]";
SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, modelica, plsql, pom, scala, vf, vm, wsdl, xml, xsl";
}
}
diff --git a/pmd-julia/pom.xml b/pmd-julia/pom.xml
new file mode 100644
index 0000000000..f60ba10c04
--- /dev/null
+++ b/pmd-julia/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+ pmd-julia
+ PMD Julia
+
+
+ net.sourceforge.pmd
+ pmd
+ 6.55.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+ org.antlr
+ antlr4-maven-plugin
+
+
+
+ maven-resources-plugin
+
+ false
+
+ ${*}
+
+
+
+
+
+
+
+
+ net.sourceforge.pmd
+ pmd-core
+
+
+ org.antlr
+ antlr4-runtime
+
+
+
+ junit
+ junit
+ test
+
+
+ net.sourceforge.pmd
+ pmd-test
+ test
+
+
+ net.sourceforge.pmd
+ pmd-lang-test
+ test
+
+
+
diff --git a/pmd-julia/src/main/antlr4/net/sourceforge/pmd/lang/julia/ast/Julia.g4 b/pmd-julia/src/main/antlr4/net/sourceforge/pmd/lang/julia/ast/Julia.g4
new file mode 100644
index 0000000000..2bd1e56efe
--- /dev/null
+++ b/pmd-julia/src/main/antlr4/net/sourceforge/pmd/lang/julia/ast/Julia.g4
@@ -0,0 +1,163 @@
+grammar Julia;
+
+// Parser
+
+// TODO
+
+// Calculate cyclomatic complexity
+// Create tokenizer for CPD
+// Publish first version 0.1 in Github and remain public for all increments
+
+main
+ : functionBody (functionDefinition functionBody)* END? EOF
+ ;
+
+functionDefinition
+ : functionDefinition1
+ | functionDefinition2
+ ;
+
+functionDefinition1
+ : FUNCTION IDENTIFIER? anyToken*? ('(' anyToken*? ')' whereClause*? functionBody)? END
+ ;
+
+functionDefinition2
+ : functionIdentifier '(' anyToken*? ')' whereClause*? '=' functionBody
+ ;
+
+functionIdentifier
+ : IDENTIFIER
+ | '(' anyToken*? ')' // Operator
+ ;
+
+whereClause
+ : WHERE anyToken*?
+ ;
+
+functionBody
+ : anyToken*? (statement anyToken*?)*?
+ ;
+
+statement
+ : beginStatement
+ | doStatement
+ | forStatement
+ | functionDefinition1
+ | ifStatement
+ | letStatement
+ | macroStatement
+ | structStatement
+ | tryCatchStatement
+ | typeStatement
+ | whileStatement
+ ;
+
+beginStatement
+ : BEGIN functionBody END
+ ;
+
+doStatement
+ : DO functionBody END
+ ;
+
+forStatement
+ : FOR functionBody END
+ ;
+
+ifStatement
+ : IF functionBody (ELSIF functionBody)* (ELSE functionBody)? END
+ ;
+
+letStatement
+ : LET functionBody END
+ ;
+
+macroStatement
+ : MACRO functionBody END
+ ;
+
+structStatement
+ : STRUCT functionBody END
+ ;
+
+tryCatchStatement
+ : TRY functionBody (CATCH functionBody)? (FINALLY functionBody)? END
+ ;
+
+typeStatement
+ : TYPE functionBody END
+ ;
+
+whileStatement
+ : WHILE functionBody END
+ ;
+
+anyToken
+ : ANY
+ | BEGIN
+ | CATCH
+ | CHAR
+ | DO
+ | ELSE
+ | ELSIF
+ | END
+ | EXTERNALCOMMAND
+ | FINALLY
+ | FOR
+ | FUNCTION
+ | IDENTIFIER
+ | IF
+ | LET
+ | MACRO
+ | STRING
+ | STRUCT
+ | TRY
+ | TYPE
+ | WHERE
+ | WHILE
+ | '(' anyToken*? ')'
+ | '[' anyToken*? ']'
+ | '{' anyToken*? '}'
+ | '='
+ | '&&' // short-circuit
+ | '||' // short-circuit
+ | '==' // to disambiguate from "="
+ ;
+
+// Lexer
+
+COMMENTS : '#' (~[=\r\n]~[\r\n]*)? -> skip; // skip #= because otherwise multiline comments are not recognized, see next line
+MULTILINECOMMENTS1 : '#=' .*? '=#' -> skip;
+MULTILINECOMMENTS2 : '```' .*? '```' -> skip;
+MULTILINESTRING : '"""' ('\\"'|.)*? '"""' -> skip;
+NL : '\r'? '\n' -> skip ;
+WHITESPACE : [ \t]+ -> skip ;
+
+BEGIN : 'begin' ;
+CATCH : 'catch' ;
+CHAR : '\'' '\\'? .? '\'' ;
+DO : 'do' ;
+ELSE : 'else' ;
+ELSIF : 'elsif' ;
+END : 'end' ;
+EXTERNALCOMMAND : '`' .*? '`' ;
+FINALLY : 'finally' ;
+FOR : 'for' ;
+FUNCTION : 'function' ;
+IF : 'if' ;
+LET : 'let' ;
+MACRO : 'macro' ;
+STRING : '"' ('\\\\'|'\\"'|'$(' ('$(' .*? ')'|'"' .*? '"'|.)*? ')'|.)*? '"';
+STRUCT : 'struct' ;
+TRY : 'try' ;
+TYPE : 'type' ;
+WHERE : 'where' ;
+WHILE : 'while' ;
+
+IDENTIFIER : [$a-zA-Z_] [a-zA-Z_0-9]* ;
+
+ANY : . ;
+
+
+
+
diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/ast/package-info.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/ast/package-info.java
new file mode 100644
index 0000000000..f72bcf2f32
--- /dev/null
+++ b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/ast/package-info.java
@@ -0,0 +1,8 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+/**
+ * Contains the Antlr grammar for Julia.
+ */
+package net.sourceforge.pmd.lang.julia.ast;
diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java
new file mode 100644
index 0000000000..d6bd001607
--- /dev/null
+++ b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java
@@ -0,0 +1,20 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.julia.cpd;
+
+import net.sourceforge.pmd.cpd.AbstractLanguage;
+
+/**
+ * Language implementation for Julia.
+ */
+public class JuliaLanguage extends AbstractLanguage {
+
+ /**
+ * Creates a new Julia Language instance.
+ */
+ public JuliaLanguage() {
+ super("Julia", "julia", new JuliaTokenizer(), ".jl");
+ }
+}
diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizer.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizer.java
new file mode 100644
index 0000000000..17ca973212
--- /dev/null
+++ b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizer.java
@@ -0,0 +1,30 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.julia.cpd;
+
+import org.antlr.v4.runtime.CharStream;
+
+import net.sourceforge.pmd.cpd.AntlrTokenizer;
+import net.sourceforge.pmd.cpd.SourceCode;
+import net.sourceforge.pmd.cpd.token.AntlrTokenFilter;
+import net.sourceforge.pmd.lang.antlr.AntlrTokenManager;
+import net.sourceforge.pmd.lang.julia.ast.JuliaLexer;
+
+/**
+ * The Julia Tokenizer.
+ */
+public class JuliaTokenizer extends AntlrTokenizer {
+
+ @Override
+ protected AntlrTokenManager getLexerForSource(SourceCode sourceCode) {
+ CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode);
+ return new AntlrTokenManager(new JuliaLexer(charStream), sourceCode.getFileName());
+ }
+
+ @Override
+ protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) {
+ return new AntlrTokenFilter(tokenManager);
+ }
+}
diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/package-info.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/package-info.java
new file mode 100644
index 0000000000..e052fae87a
--- /dev/null
+++ b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/package-info.java
@@ -0,0 +1,8 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+/**
+ * Contains Julia tokenizer and language classes.
+ */
+package net.sourceforge.pmd.lang.julia.cpd;
diff --git a/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
new file mode 100644
index 0000000000..bd23fbae93
--- /dev/null
+++ b/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
@@ -0,0 +1 @@
+net.sourceforge.pmd.lang.julia.cpd.JuliaLanguage
diff --git a/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java b/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java
new file mode 100644
index 0000000000..7a0fdda562
--- /dev/null
+++ b/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java
@@ -0,0 +1,34 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cpd;
+
+import java.util.Properties;
+
+import org.junit.Test;
+
+import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest;
+import net.sourceforge.pmd.lang.julia.cpd.JuliaTokenizer;
+
+public class JuliaTokenizerTest extends CpdTextComparisonTest {
+ public JuliaTokenizerTest() {
+ super(".jl");
+ }
+
+ @Override
+ protected String getResourcePrefix() {
+ return "../lang/julia/cpd/testdata";
+ }
+
+ @Override
+ public Tokenizer newTokenizer(Properties properties) {
+ JuliaTokenizer tok = new JuliaTokenizer();
+ return tok;
+ }
+
+ @Test
+ public void testMathExample() {
+ doTest("mathExample");
+ }
+}
diff --git a/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.jl b/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.jl
new file mode 100644
index 0000000000..8c70fdef3e
--- /dev/null
+++ b/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.jl
@@ -0,0 +1,33 @@
+# function to calculate the volume of a sphere
+function sphere_vol(r)
+ # julia allows Unicode names (in UTF-8 encoding)
+ # so either "pi" or the symbol π can be used
+ return 4/3*pi*r^3
+end
+
+# functions can also be defined more succinctly
+quadratic(a, sqr_term, b) = (-b + sqr_term) / 2a
+
+# calculates x for 0 = a*x^2+b*x+c, arguments types can be defined in function definitions
+function quadratic2(a::Float64, b::Float64, c::Float64)
+ # unlike other languages 2a is equivalent to 2*a
+ # a^2 is used instead of a**2 or pow(a,2)
+ sqr_term = sqrt(b^2-4a*c)
+ r1 = quadratic(a, sqr_term, b)
+ r2 = quadratic(a, -sqr_term, b)
+ # multiple values can be returned from a function using tuples
+ # if the return keyword is omitted, the last term is returned
+ r1, r2
+end
+
+vol = sphere_vol(3)
+# @printf allows number formatting but does not automatically append the \n to statements, see below
+using Printf
+@printf "volume = %0.3f\n" vol
+#> volume = 113.097
+
+quad1, quad2 = quadratic2(2.0, -2.0, -12.0)
+println("result 1: ", quad1)
+#> result 1: 3.0
+println("result 2: ", quad2)
+#> result 2: -2.0
\ No newline at end of file
diff --git a/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.txt b/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.txt
new file mode 100644
index 0000000000..47d6f6a52b
--- /dev/null
+++ b/pmd-julia/src/test/resources/net/sourceforge/pmd/lang/julia/cpd/testdata/mathExample.txt
@@ -0,0 +1,153 @@
+ [Image] or [Truncated image[ Bcol Ecol
+L2
+ [function] 1 8
+ [sphere_vol] 10 19
+ [(] 20 20
+ [r] 21 21
+ [)] 22 22
+L5
+ [return] 3 8
+ [4] 10 10
+ [/] 11 11
+ [3] 12 12
+ [*] 13 13
+ [pi] 14 15
+ [*] 16 16
+ [r] 17 17
+ [^] 18 18
+ [3] 19 19
+L6
+ [end] 1 3
+L9
+ [quadratic] 1 9
+ [(] 10 10
+ [a] 11 11
+ [,] 12 12
+ [sqr_term] 14 21
+ [,] 22 22
+ [b] 24 24
+ [)] 25 25
+ [=] 27 27
+ [(] 29 29
+ [-] 30 30
+ [b] 31 31
+ [+] 33 33
+ [sqr_term] 35 42
+ [)] 43 43
+ [/] 45 45
+ [2] 47 47
+ [a] 48 48
+L12
+ [function] 1 8
+ [quadratic2] 10 19
+ [(] 20 20
+ [a] 21 21
+ [:] 22 22
+ [:] 23 23
+ [Float64] 24 30
+ [,] 31 31
+ [b] 33 33
+ [:] 34 34
+ [:] 35 35
+ [Float64] 36 42
+ [,] 43 43
+ [c] 45 45
+ [:] 46 46
+ [:] 47 47
+ [Float64] 48 54
+ [)] 55 55
+L15
+ [sqr_term] 3 10
+ [=] 12 12
+ [sqrt] 14 17
+ [(] 18 18
+ [b] 19 19
+ [^] 20 20
+ [2] 21 21
+ [-] 22 22
+ [4] 23 23
+ [a] 24 24
+ [*] 25 25
+ [c] 26 26
+ [)] 27 27
+L16
+ [r1] 3 4
+ [=] 6 6
+ [quadratic] 8 16
+ [(] 17 17
+ [a] 18 18
+ [,] 19 19
+ [sqr_term] 21 28
+ [,] 29 29
+ [b] 31 31
+ [)] 32 32
+L17
+ [r2] 3 4
+ [=] 6 6
+ [quadratic] 8 16
+ [(] 17 17
+ [a] 18 18
+ [,] 19 19
+ [-] 21 21
+ [sqr_term] 22 29
+ [,] 30 30
+ [b] 32 32
+ [)] 33 33
+L20
+ [r1] 3 4
+ [,] 5 5
+ [r2] 7 8
+L21
+ [end] 1 3
+L23
+ [vol] 1 3
+ [=] 5 5
+ [sphere_vol] 7 16
+ [(] 17 17
+ [3] 18 18
+ [)] 19 19
+L25
+ [using] 1 5
+ [Printf] 7 12
+L26
+ [@] 1 1
+ [printf] 2 7
+ ["volume = %0.3f\\n"] 9 26
+ [vol] 28 30
+L29
+ [quad1] 1 5
+ [,] 6 6
+ [quad2] 8 12
+ [=] 14 14
+ [quadratic2] 16 25
+ [(] 26 26
+ [2] 27 27
+ [.] 28 28
+ [0] 29 29
+ [,] 30 30
+ [-] 32 32
+ [2] 33 33
+ [.] 34 34
+ [0] 35 35
+ [,] 36 36
+ [-] 38 38
+ [1] 39 39
+ [2] 40 40
+ [.] 41 41
+ [0] 42 42
+ [)] 43 43
+L30
+ [println] 1 7
+ [(] 8 8
+ ["result 1: "] 9 20
+ [,] 21 21
+ [quad1] 23 27
+ [)] 28 28
+L32
+ [println] 1 7
+ [(] 8 8
+ ["result 2: "] 9 20
+ [,] 21 21
+ [quad2] 23 27
+ [)] 28 28
+EOF
diff --git a/pom.xml b/pom.xml
index b2ea18544a..d855504328 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1085,6 +1085,7 @@
pmd-lua
pmd-java
pmd-jsp
+ pmd-julia
pmd-kotlin
pmd-matlab
pmd-modelica