diff --git a/pmd-awl/pom.xml b/pmd-awl/pom.xml
new file mode 100644
index 00000000000..d1f2434da05
--- /dev/null
+++ b/pmd-awl/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+ pmd-awl
+ PMD AWL
+
+
+ net.sourceforge.pmd
+ pmd
+ 7.6.0
+ ../pom.xml
+
+
+
+
+
+ org.antlr
+ antlr4-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ antlr-cleanup
+ generate-sources
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maven-resources-plugin
+
+ false
+
+ ${*}
+
+
+
+
+ com.github.siom79.japicmp
+ japicmp-maven-plugin
+
+
+ true
+
+
+
+
+
+
+
+ net.sourceforge.pmd
+ pmd-core
+
+
+ org.antlr
+ antlr4-runtime
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ net.sourceforge.pmd
+ pmd-test
+ test
+
+
+ net.sourceforge.pmd
+ pmd-lang-test
+ test
+
+
+
diff --git a/pmd-awl/src/main/antlr4/net/sourceforge/pmd/lang/awl/ast/AWLLexer.g4 b/pmd-awl/src/main/antlr4/net/sourceforge/pmd/lang/awl/ast/AWLLexer.g4
new file mode 100644
index 00000000000..b2e2bdbcac4
--- /dev/null
+++ b/pmd-awl/src/main/antlr4/net/sourceforge/pmd/lang/awl/ast/AWLLexer.g4
@@ -0,0 +1,46 @@
+lexer grammar AWLLexer;
+
+PARSERTOKEN : ';' | ':' | '{' | '}' | '=' | ':=' | '[' | ']' | '(' | ')' | '..' | ',' | '+' | '-' | '.' | '#' | '%' ;
+
+INSTRUCTIONNAME : '<=' | '+AR1' | '+AR2' | '+D' | '-D' | '*D' | '/D' | '==D' | '>D' | '>=D' | 'D' |
+ '+I' | '-I' | '*I' | '/I' | '==I' | '>I' | '>=I' | 'I' | '+R' | '-R' | '*R' | '/R' | 'RND+' | 'RND-' ;
+
+LOGICCOMPARISON : '==0' | '<>0' | '>0' | '<0' | '>=0' | '<=0' ;
+
+TITLE : 'TITLE' [ \t]* '=' ~[\r\n]* ;
+
+POINTER_OP : 'P#' 'DBX'? ;
+
+ARRAY_INIT : Integer '(' '\'' AnyPrintChar '\'' ')' ;
+fragment AnyPrintChar : [\u0040-\u007E] ;
+
+ID : IdentifierStart IdentifierChar* ;
+fragment IdentifierStart : '_' | 'a' .. 'z' | 'A' .. 'Z' ;
+fragment IdentifierChar : IdentifierStart | Digit | '-' ;
+
+StringConstant : DOUBLE_QUOTED_STRING | SINGLE_QUOTED_STRING ;
+fragment DOUBLE_QUOTED_STRING : '"' AnyCharExceptDoubleQuoteNewline* '"' ;
+fragment SINGLE_QUOTED_STRING : '\'' AnyCharExceptSingleQuoteNewline* '\'' ;
+fragment AnyCharExceptDoubleQuoteNewline : [\u0000-\u0009] | [\u000B] | [\u000C] | [\u000E-\u0021] | [\u0023-\u007F] ;
+fragment AnyCharExceptSingleQuoteNewline : [\u0000-\u0009] | [\u000B] | [\u000C] | [\u000E-\u0026] | [\u0028-\u007F] ;
+
+REAL : Integer '.' Integer ;
+
+INT : Integer ;
+
+fragment Integer : Digit+ ;
+fragment Digit : [0-9] ;
+
+HEX_INT : '16#' HexDigit+ ;
+fragment HexDigit : [0-9] | [A-F] | [_] ;
+
+BIN_INT : '2#' BinDigit+ ;
+BinDigit : [0-1] ;
+
+TIME : 'S5'? ('t' | 'T') '#' Digit+ TimeUnit ;
+fragment TimeUnit : 'H' | 'h' | 'M' | 'm' | 'S' | 's' | 'MS' | 'ms' ;
+
+LineComment : '//' ~[\r\n]* -> channel(HIDDEN) ;
+BlockComment : '(*' .*? '*)' -> channel(HIDDEN) ;
+Whitespace : [ \t]+ -> skip ;
+LineTerm : [\r\n]+ -> channel(HIDDEN) ;
\ No newline at end of file
diff --git a/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/AwlLanguageModule.java b/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/AwlLanguageModule.java
new file mode 100644
index 00000000000..0d18918b523
--- /dev/null
+++ b/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/AwlLanguageModule.java
@@ -0,0 +1,23 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.awl;
+
+import net.sourceforge.pmd.cpd.CpdLexer;
+import net.sourceforge.pmd.lang.LanguagePropertyBundle;
+import net.sourceforge.pmd.lang.awl.cpd.AwlCpdLexer;
+import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase;
+
+public class AwlLanguageModule extends CpdOnlyLanguageModuleBase {
+ private static final String ID = "awl";
+
+ public AwlLanguageModule() {
+ super(LanguageMetadata.withId(ID).name("AWL").extensions("awl", "AWL"));
+ }
+
+ @Override
+ public CpdLexer createCpdLexer(LanguagePropertyBundle bundle) {
+ return new AwlCpdLexer();
+ }
+}
diff --git a/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexer.java b/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexer.java
new file mode 100644
index 00000000000..9417f75f525
--- /dev/null
+++ b/pmd-awl/src/main/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexer.java
@@ -0,0 +1,19 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.awl.cpd;
+
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Lexer;
+
+import net.sourceforge.pmd.cpd.impl.AntlrCpdLexer;
+import net.sourceforge.pmd.lang.awl.ast.AWLLexer;
+
+public class AwlCpdLexer extends AntlrCpdLexer {
+
+ @Override
+ protected Lexer getLexerForSource(CharStream charStream) {
+ return new AWLLexer(charStream);
+ }
+}
diff --git a/pmd-awl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-awl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
new file mode 100644
index 00000000000..aa86e1ec7ae
--- /dev/null
+++ b/pmd-awl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
@@ -0,0 +1 @@
+net.sourceforge.pmd.lang.awl.AwlLanguageModule
diff --git a/pmd-awl/src/test/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexerTest.java b/pmd-awl/src/test/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexerTest.java
new file mode 100644
index 00000000000..e87b289dc3b
--- /dev/null
+++ b/pmd-awl/src/test/java/net/sourceforge/pmd/lang/awl/cpd/AwlCpdLexerTest.java
@@ -0,0 +1,22 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.awl.cpd;
+
+import org.junit.jupiter.api.Test;
+
+import net.sourceforge.pmd.lang.test.cpd.CpdTextComparisonTest;
+
+class AwlCpdLexerTest extends CpdTextComparisonTest {
+
+ AwlCpdLexerTest() {
+ super("awl", ".awl");
+ }
+
+ @Test
+ void examples() {
+ doTest("awl_examples");
+ }
+
+}
diff --git a/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.awl b/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.awl
new file mode 100644
index 00000000000..12da51c5e9d
--- /dev/null
+++ b/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.awl
@@ -0,0 +1,58 @@
+// Combined some publicly available AWL/STL examples (good enough for lexer-only test)
+// from STL reference document 'Statement List (STL) for S7-300 and S7-400 Programming'
+// https://cache.industry.siemens.com/dl/files/814/109751814/att_933093/v1/STEP_7_-_Statement_List_for_S7-300_and_S7-400.pdf
+
+FUNCTION_BLOCK "fb1"
+TITLE = %version: 1 % Title information is present here % $ ^ \
+{ this_is_false := 'FALSE' }
+FAMILY : Fam1
+VERSION : 0.0
+
+// Input variables
+ VAR_INPUT
+ i_ID : DWord; // Var 1
+ CFG_Position_node : Int;
+ END_VAR
+
+// Output variables
+ VAR_OUTPUT
+ output_var_1 : Bool;
+ output_var_2 : Int;
+ output_var_3 : Bool;
+ END_VAR
+
+// Example: Statement List to control the Conveyor Belt
+O I 1.1 //Pressing either start switch turns the motor on.
+O I 1.3
+S Q 4.0
+O I 1.2 //Pressing either stop switch or opening the normally closed contact at
+//the end of the belt turns the motor off.
+O I 1.4
+ON I 1.5
+R Q 4.0
+
+// Example: Statement List to Generate a Clock Pulse (pulse duty factor 1:1)
+AN T1 //If timer T 1 has expired,
+L S5T#250ms //load the time value 250 ms into T 1 and
+SV T1 //start T 1 as an extended-pulse timer.
+NOT //Negate (invert) the result of logic operation.
+BEB //If the timer is running, end the current block.
+L MB100 //If the timer has expired, load the contents of memory byte MB100,
+INC 1 //increment the contents by 1,
+T MB100 //and transfer the result to memory byte MB100.
+
+L ID20 //Load contents of ID20 into ACCU 1.
+L ID24 //Load contents of ACCU 1 into ACCU 2. Load contents of ID24 into ACCU
+1.
+AD //Combine bits from ACCU 1 with ACCU 2 by AND, store result in ACCU
+1.
+T MD8 //Transfer result to MD8.
+
+L ID 20 //Load contents of ID20 into ACCU 1.
+AD DW#16#0FFF_EF21 //Combine bits of ACCU 1 with bit pattern of 32-bit constant
+(0000_1111_1111_1111_1110_1111_0010_0001) by AND; store result in
+ACCU 1.
+>=I
+JP NEXT //Jump to NEXT jump label if result is unequal to zero, (CC 1 = 1).
+
+END_FUNCTION_BLOCK
diff --git a/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.txt b/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.txt
new file mode 100644
index 00000000000..c50f53d7e94
--- /dev/null
+++ b/pmd-awl/src/test/resources/net/sourceforge/pmd/lang/awl/cpd/testdata/awl_examples.txt
@@ -0,0 +1,152 @@
+ [Image] or [Truncated image[ Bcol Ecol
+L5
+ [FUNCTION_BLOCK] 1 15
+ ["fb1"] 16 21
+L6
+ [TITLE = %version: 1 % Title inform[ 1 64
+L7
+ [{] 1 2
+ [this_is_false] 3 16
+ [:=] 17 19
+ ['FALSE'] 20 27
+ [}] 28 29
+L8
+ [FAMILY] 1 7
+ [:] 8 9
+ [Fam1] 10 14
+L9
+ [VERSION] 1 8
+ [:] 9 10
+ [0.0] 11 14
+L12
+ [VAR_INPUT] 4 13
+L13
+ [i_ID] 7 11
+ [:] 12 13
+ [DWord] 14 19
+ [;] 19 20
+L14
+ [CFG_Position_node] 7 24
+ [:] 25 26
+ [Int] 27 30
+ [;] 30 31
+L15
+ [END_VAR] 4 11
+L18
+ [VAR_OUTPUT] 4 14
+L19
+ [output_var_1] 7 19
+ [:] 20 21
+ [Bool] 22 26
+ [;] 26 27
+L20
+ [output_var_2] 7 19
+ [:] 20 21
+ [Int] 22 25
+ [;] 25 26
+L21
+ [output_var_3] 7 19
+ [:] 20 21
+ [Bool] 22 26
+ [;] 26 27
+L22
+ [END_VAR] 4 11
+L25
+ [O] 1 2
+ [I] 3 4
+ [1.1] 5 8
+L26
+ [O] 1 2
+ [I] 3 4
+ [1.3] 5 8
+L27
+ [S] 1 2
+ [Q] 3 4
+ [4.0] 5 8
+L28
+ [O] 1 2
+ [I] 3 4
+ [1.2] 5 8
+L30
+ [O] 1 2
+ [I] 3 4
+ [1.4] 5 8
+L31
+ [ON] 1 3
+ [I] 4 5
+ [1.5] 6 9
+L32
+ [R] 1 2
+ [Q] 3 4
+ [4.0] 5 8
+L35
+ [AN] 1 3
+ [T1] 4 6
+L36
+ [L] 1 2
+ [S5T#250ms] 3 12
+L37
+ [SV] 1 3
+ [T1] 4 6
+L38
+ [NOT] 1 4
+L39
+ [BEB] 1 4
+L40
+ [L] 1 2
+ [MB100] 3 8
+L41
+ [INC] 1 4
+ [1] 5 6
+L42
+ [T] 1 2
+ [MB100] 3 8
+L44
+ [L] 1 2
+ [ID20] 3 7
+L45
+ [L] 1 2
+ [ID24] 3 7
+L46
+ [1] 1 2
+ [.] 2 3
+L47
+ [AD] 1 3
+L48
+ [1] 1 2
+ [.] 2 3
+L49
+ [T] 1 2
+ [MD8] 3 6
+L51
+ [L] 1 2
+ [ID] 3 5
+ [20] 6 8
+L52
+ [AD] 1 3
+ [DW] 4 6
+ [#] 6 7
+ [16#0FFF_EF21] 7 19
+L53
+ [(] 1 2
+ [0000] 2 6
+ [_1111_1111_1111_1110_1111_0010_000[ 6 41
+ [)] 41 42
+ [by] 43 45
+ [AND] 46 49
+ [;] 49 50
+ [store] 51 56
+ [result] 57 63
+ [in] 64 66
+L54
+ [ACCU] 1 5
+ [1] 6 7
+ [.] 7 8
+L55
+ [>=I] 1 4
+L56
+ [JP] 1 3
+ [NEXT] 4 8
+L58
+ [END_FUNCTION_BLOCK] 1 19
+EOF
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java
index ec499a5ac75..e2fce578d82 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java
@@ -29,7 +29,7 @@
class BinaryDistributionIT extends AbstractBinaryDistributionTest {
private static final List SUPPORTED_LANGUAGES_CPD = listOf(
- "apex", "coco", "cpp", "cs", "dart", "ecmascript",
+ "apex", "awl", "coco", "cpp", "cs", "dart", "ecmascript",
"fortran", "gherkin", "go", "groovy", "html", "java", "jsp",
"julia",
"kotlin", "lua", "matlab", "modelica", "objectivec", "perl",
diff --git a/pmd-languages-deps/pom.xml b/pmd-languages-deps/pom.xml
index 214b8b679e8..6a1361a4929 100644
--- a/pmd-languages-deps/pom.xml
+++ b/pmd-languages-deps/pom.xml
@@ -17,6 +17,11 @@
pmd-apex
${project.version}
+
+ net.sourceforge.pmd
+ pmd-awl
+ ${project.version}
+
net.sourceforge.pmd
pmd-coco
diff --git a/pom.xml b/pom.xml
index acebcff366a..ea514f245f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1334,6 +1334,7 @@
pmd-apex
+ pmd-awl
pmd-coco
pmd-core
pmd-cpp