Browse Source

Added filter for deleting tokens after guaranteed jumps

Pascal Krieg 4 years ago
parent
commit
dabcfe4a64
1 changed files with 138 additions and 0 deletions
  1. 138 0
      languages/cpp/src/main/java/de/jplag/cpp/UnreachableCodeFilter.java

+ 138 - 0
languages/cpp/src/main/java/de/jplag/cpp/UnreachableCodeFilter.java

@@ -0,0 +1,138 @@
+package de.jplag.cpp;
+
+import static de.jplag.SharedTokenType.FILE_END;
+import static de.jplag.cpp.CPPTokenType.*;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import de.jplag.TokenType;
+
+/**
+ * Contains a basic algorithm for detecting tokens contained in unreachable code.
+ */
+public final class UnreachableCodeFilter {
+
+    private UnreachableCodeFilter() {
+    }
+
+    /**
+     * Applies the filtering on the provided token list.
+     * @param tokenList The list that will be filtered. The contents of this parameter will be modified.
+     */
+    public static void applyTo(List<de.jplag.Token> tokenList) {
+        TokenFilterState stateMachine = TokenFilterState.STATE_DEFAULT;
+
+        ListIterator<de.jplag.Token> iterator = tokenList.listIterator();
+        while (iterator.hasNext()) {
+            var token = iterator.next();
+
+            stateMachine = stateMachine.nextState(token.getType());
+
+            if (stateMachine.shouldTokenBeDeleted()) {
+                iterator.remove();
+            }
+        }
+    }
+
+    /**
+     * Represents the state of a simple state machine for C++ tokens.
+     */
+    private enum TokenFilterState {
+        STATE_DEFAULT {
+            @Override
+            TokenFilterState nextState(TokenType nextType) {
+                if (isBlockStartToken(nextType)) {
+                    return STATE_BLOCK_BEGINNING;
+                }
+                if (isJumpToken(nextType)) {
+                    return STATE_DEAD_BLOCK_BEGINNING;
+                }
+                if (nextType == C_CASE) {
+                    return STATE_CASE_BLOCK;
+                }
+                return STATE_DEFAULT;
+            }
+        },
+        STATE_BLOCK_BEGINNING {
+            @Override
+            TokenFilterState nextState(TokenType nextType) {
+                if (isBlockEndToken(nextType) || nextType == C_BLOCK_BEGIN) {
+                    return STATE_DEFAULT;
+                }
+                return STATE_BLOCK_BEGINNING;
+            }
+        },
+        STATE_DEAD_BLOCK {
+            @Override
+            TokenFilterState nextState(TokenType nextType) {
+                if (isBlockEndToken(nextType)) {
+                    return STATE_DEFAULT;
+                }
+                if (nextType == C_CASE) {
+                    return STATE_CASE_BLOCK;
+                }
+                return STATE_DEAD_BLOCK;
+            }
+
+            @Override
+            public boolean shouldTokenBeDeleted() {
+                return true;
+            }
+        },
+        // the current token starts a dead block, so everything afterwards should be deleted, until the dead block is closed.
+        STATE_DEAD_BLOCK_BEGINNING {
+            @Override
+            TokenFilterState nextState(TokenType nextType) {
+                if (isBlockEndToken(nextType)) {
+                    return STATE_DEFAULT;
+                }
+                if (nextType == C_CASE) {
+                    return STATE_CASE_BLOCK;
+                }
+                return STATE_DEAD_BLOCK;
+            }
+        },
+        // case blocks don't use braces, but the end of a case block is easy to recognize
+        STATE_CASE_BLOCK {
+            @Override
+            TokenFilterState nextState(TokenType nextType) {
+                if (isBlockEndToken(nextType)) {
+                    return STATE_DEFAULT;
+                }
+                if (isJumpToken(nextType)) {
+                    return STATE_DEAD_BLOCK_BEGINNING;
+                }
+                return STATE_CASE_BLOCK;
+            }
+        };
+
+        private static boolean isBlockStartToken(TokenType token) {
+            return token == C_WHILE || token == C_IF || token == C_FOR;
+        }
+
+        private static boolean isBlockEndToken(TokenType token) {
+            return token == C_BLOCK_END || token == FILE_END;
+        }
+
+        // jump tokens are tokens that force a code execution jump in EVERY case and therefore indicate unreachable code.
+        private static boolean isJumpToken(TokenType token) {
+            return token == C_RETURN || token == C_BREAK || token == C_CONTINUE || token == C_THROW || token == C_GOTO;
+        }
+
+        /**
+         * Determine if the current token should be deleted, because it is located in dead or unreachable code.
+         * @return true if the token corresponding to the current state is located in dead or unreachable code, false otherwise.
+         */
+        public boolean shouldTokenBeDeleted() {
+            return false;
+        }
+
+        /**
+         * Determine the next state depending on the current state and the next token type.
+         * @param nextType The type of the next token in the token list.
+         * @return the new state corresponding to the next token
+         */
+        abstract TokenFilterState nextState(TokenType nextType);
+    }
+}