Robin Maisch 4 лет назад
Родитель
Сommit
7ddc844fbd

+ 8 - 1
jplag.frontend.R/README.md

@@ -1,9 +1,16 @@
 # JPlag R language frontend
 
-The JPlag R frontend allows the use of JPlag with submissions in R. 
+The JPlag R frontend allows the use of JPlag with submissions in R. <br>
+It was in part adapted from a [JPLag fork by CodeGra-de](https://github.com/CodeGra-de/jplag/tree/master/jplag.frontend.R).
 
 ### R specification compatibility
 The underlying [grammar definition](https://github.com/antlr/grammars-v4/tree/master/r) was first created in June 2013, when R 3.0.1 was current. The latest commit is from April 2018, when R 3.5.0 was just released. Whether the grammar has been made to comply with any specific version of the R specification is unclear. Even if some parsing errors occur, the parser should be able to recover and still produce a valid analysis.
 
+### Token Extraction
+
+The choice of tokens is based directly on the CodeGra-de version, whereas the extraction process itself contains some fixes.
+
+Like in other frontends, e.g. for Java and C#, the tokens account for the beginning and the end of control flow structures, for control flow keywords, and some kinds of expressions. As R is very different from other programming languages in JPlag, it remains to be seen whether the R frontend can hold up to the others.
+
 ### Usage
 To use the R frontend, add the `-l R` flag in the CLI, or use a `JPlagOption` object set to `LanguageOption.R` in the Java API as described in the usage information in the [readme of the main project](https://github.com/jplag/JPlag#usage) and [in the wiki](https://github.com/jplag/JPlag/wiki/1.-How-to-Use-JPlag).

+ 51 - 264
jplag.frontend.R/src/main/java/de/jplag/R/JplagRListener.java

@@ -1,22 +1,18 @@
 package de.jplag.R;
 
+import de.jplag.R.grammar.*;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.Token;
 import org.antlr.v4.runtime.tree.ErrorNode;
 import org.antlr.v4.runtime.tree.TerminalNode;
 
-import de.jplag.R.grammar.RFilter;
-import de.jplag.R.grammar.RFilterListener;
-import de.jplag.R.grammar.RListener;
-import de.jplag.R.grammar.RParser;
-
 /**
  * Listener class for visiting the R ANTLR parse tree. Transforms the visited ANTLR token into JPlag tokens. Based on an
  * R frontend for JPlag v2.15 by Olmo Kramer, see their
  * <a href="https://github.com/CodeGra-de/jplag/tree/master/jplag.frontend.R">JPlag fork</a>.
  * @author Robin Maisch
  */
-public class JplagRListener implements RListener, RFilterListener, RTokenConstants {
+public class JplagRListener extends RCombinedBaseListener implements RTokenConstants {
 
     private final RParserAdapter parserAdapter;
 
@@ -41,333 +37,124 @@ public class JplagRListener implements RListener, RFilterListener, RTokenConstan
         parserAdapter.addToken(targetType, start.getLine(), start.getCharPositionInLine() + 1, end.getStopIndex() - start.getStartIndex() + 1);
     }
 
-    @Override
-    public void enterProg(RParser.ProgContext ctx) {
-
-    }
-
-    @Override
-    public void exitProg(RParser.ProgContext ctx) {
-
-    }
-
-    @Override
-    public void enterExpr(RParser.ExprContext ctx) {
-
+       @Override
+    public void enterIndex_statement(RParser.Index_statementContext context) {
+        transformToken(INDEX, context.getStart(), context.getStop());
     }
 
     @Override
-    public void exitExpr(RParser.ExprContext ctx) {
-
+    public void enterAccess_package(RParser.Access_packageContext context) {
+        transformToken(PACKAGE, context.getStart());
     }
 
     @Override
-    public void enterIndex_statement(RParser.Index_statementContext ctx) {
-        transformToken(INDEX, ctx.getStart());
+    public void enterFunction_definition(RParser.Function_definitionContext context) {
+        transformToken(BEGIN_FUNCTION, context.getStart());
     }
 
     @Override
-    public void exitIndex_statement(RParser.Index_statementContext ctx) {
-
+    public void exitFunction_definition(RParser.Function_definitionContext context) {
+        transformToken(END_FUNCTION, context.getStop());
     }
 
     @Override
-    public void enterAccess_package(RParser.Access_packageContext ctx) {
-        transformToken(PACKAGE, ctx.getStart());
+    public void enterFunction_call(RParser.Function_callContext context) {
+        transformToken(FUNCTION_CALL, context.getStart(), context.getStop());
     }
 
     @Override
-    public void exitAccess_package(RParser.Access_packageContext ctx) {
-
+    public void enterConstant_number(RParser.Constant_numberContext context) {
+        transformToken(NUMBER, context.getStart());
     }
 
     @Override
-    public void enterFunction_definition(RParser.Function_definitionContext ctx) {
-        transformToken(BEGIN_FUNCTION, ctx.getStart());
+    public void enterConstant_string(RParser.Constant_stringContext context) {
+        transformToken(STRING, context.getStart());
     }
 
     @Override
-    public void exitFunction_definition(RParser.Function_definitionContext ctx) {
-        transformToken(END_FUNCTION, ctx.getStop());
+    public void enterConstant_bool(RParser.Constant_boolContext context) {
+        transformToken(BOOL, context.getStart());
     }
 
     @Override
-    public void enterFunction_call(RParser.Function_callContext ctx) {
-        transformToken(FUNCTION_CALL, ctx.getStart(), ctx.getStop());
+    public void enterHelp(RParser.HelpContext context) {
+        transformToken(HELP, context.getStart());
     }
 
     @Override
-    public void exitFunction_call(RParser.Function_callContext ctx) {
-
+    public void enterIf_statement(RParser.If_statementContext context) {
+        transformToken(IF_BEGIN, context.getStart());
     }
 
     @Override
-    public void enterConstant(RParser.ConstantContext ctx) {
-
+    public void exitIf_statement(RParser.If_statementContext context) {
+        transformToken(IF_END, context.getStop());
     }
 
     @Override
-    public void exitConstant(RParser.ConstantContext ctx) {
-
+    public void enterFor_statement(RParser.For_statementContext context) {
+        transformToken(FOR_BEGIN, context.getStart());
     }
 
     @Override
-    public void enterConstant_number(RParser.Constant_numberContext ctx) {
-        transformToken(NUMBER, ctx.getStart());
+    public void exitFor_statement(RParser.For_statementContext context) {
+        transformToken(FOR_END, context.getStop());
     }
 
     @Override
-    public void exitConstant_number(RParser.Constant_numberContext ctx) {
-
+    public void enterWhile_statement(RParser.While_statementContext context) {
+        transformToken(WHILE_BEGIN, context.getStart());
     }
 
     @Override
-    public void enterConstant_string(RParser.Constant_stringContext ctx) {
-        transformToken(STRING, ctx.getStart());
+    public void exitWhile_statement(RParser.While_statementContext context) {
+        transformToken(WHILE_END, context.getStop());
     }
 
     @Override
-    public void exitConstant_string(RParser.Constant_stringContext ctx) {
-
+    public void enterRepeat_statement(RParser.Repeat_statementContext context) {
+        transformToken(REPEAT_BEGIN, context.getStart());
     }
 
     @Override
-    public void enterConstant_bool(RParser.Constant_boolContext ctx) {
-        transformToken(BOOL, ctx.getStart());
+    public void exitRepeat_statement(RParser.Repeat_statementContext context) {
+        transformToken(REPEAT_END, context.getStop());
     }
 
     @Override
-    public void exitConstant_bool(RParser.Constant_boolContext ctx) {
-
+    public void enterNext_statement(RParser.Next_statementContext context) {
+        transformToken(NEXT, context.getStart());
     }
 
     @Override
-    public void enterHelp(RParser.HelpContext ctx) {
-        transformToken(HELP, ctx.getStart());
+    public void enterBreak_statement(RParser.Break_statementContext context) {
+        transformToken(BREAK, context.getStart());
     }
 
     @Override
-    public void exitHelp(RParser.HelpContext ctx) {
-
+    public void enterCompound_statement(RParser.Compound_statementContext context) {
+        transformToken(COMPOUND_BEGIN, context.getStart());
     }
 
     @Override
-    public void enterIf_statement(RParser.If_statementContext ctx) {
-        transformToken(IF_BEGIN, ctx.getStart());
+    public void exitCompound_statement(RParser.Compound_statementContext context) {
+        transformToken(COMPOUND_END, context.getStop());
     }
 
     @Override
-    public void exitIf_statement(RParser.If_statementContext ctx) {
-        transformToken(IF_END, ctx.getStop());
+    public void enterAssign_value(RParser.Assign_valueContext context) {
+        transformToken(ASSIGN, context.getStart());
     }
 
     @Override
-    public void enterFor_statement(RParser.For_statementContext ctx) {
-        transformToken(FOR_BEGIN, ctx.getStart());
-    }
-
-    @Override
-    public void exitFor_statement(RParser.For_statementContext ctx) {
-        transformToken(FOR_END, ctx.getStop());
-    }
-
-    @Override
-    public void enterWhile_statement(RParser.While_statementContext ctx) {
-        transformToken(WHILE_BEGIN, ctx.getStart());
-    }
-
-    @Override
-    public void exitWhile_statement(RParser.While_statementContext ctx) {
-        transformToken(WHILE_END, ctx.getStop());
-    }
-
-    @Override
-    public void enterRepeat_statement(RParser.Repeat_statementContext ctx) {
-        transformToken(REPEAT_BEGIN, ctx.getStart());
-    }
-
-    @Override
-    public void exitRepeat_statement(RParser.Repeat_statementContext ctx) {
-        transformToken(REPEAT_END, ctx.getStop());
-    }
-
-    @Override
-    public void enterNext_statement(RParser.Next_statementContext ctx) {
-        transformToken(NEXT, ctx.getStart());
-    }
-
-    @Override
-    public void exitNext_statement(RParser.Next_statementContext ctx) {
-
+    public void enterAssign_func_declaration(RParser.Assign_func_declarationContext context) {
+        transformToken(ASSIGN_FUNC, context.getStart());
     }
 
     @Override
-    public void enterBreak_statement(RParser.Break_statementContext ctx) {
-        transformToken(BREAK, ctx.getStart());
-    }
-
-    @Override
-    public void exitBreak_statement(RParser.Break_statementContext ctx) {
-
-    }
-
-    @Override
-    public void enterCompound_statement(RParser.Compound_statementContext ctx) {
-        transformToken(COMPOUND_BEGIN, ctx.getStart());
-    }
-
-    @Override
-    public void exitCompound_statement(RParser.Compound_statementContext ctx) {
-        transformToken(COMPOUND_END, ctx.getStop());
-    }
-
-    @Override
-    public void enterExprlist(RParser.ExprlistContext ctx) {
-
-    }
-
-    @Override
-    public void exitExprlist(RParser.ExprlistContext ctx) {
-
-    }
-
-    @Override
-    public void enterFormlist(RParser.FormlistContext ctx) {
-
-    }
-
-    @Override
-    public void exitFormlist(RParser.FormlistContext ctx) {
-
-    }
-
-    @Override
-    public void enterForm(RParser.FormContext ctx) {
-
-    }
-
-    @Override
-    public void exitForm(RParser.FormContext ctx) {
-
-    }
-
-    @Override
-    public void enterSublist(RParser.SublistContext ctx) {
-
-    }
-
-    @Override
-    public void exitSublist(RParser.SublistContext ctx) {
-
+    public void enterAssign_value_list(RParser.Assign_value_listContext context) {
+        transformToken(ASSIGN_LIST, context.getStart());
     }
 
-    @Override
-    public void enterSub(RParser.SubContext ctx) {
-
-    }
-
-    @Override
-    public void exitSub(RParser.SubContext ctx) {
-
-    }
-
-    @Override
-    public void enterAssign_value(RParser.Assign_valueContext ctx) {
-        transformToken(ASSIGN, ctx.getStart());
-    }
-
-    @Override
-    public void exitAssign_value(RParser.Assign_valueContext ctx) {
-
-    }
-
-    @Override
-    public void enterAssign_func_declaration(RParser.Assign_func_declarationContext ctx) {
-        transformToken(ASSIGN_FUNC, ctx.getStart());
-    }
-
-    @Override
-    public void exitAssign_func_declaration(RParser.Assign_func_declarationContext ctx) {
-
-    }
-
-    @Override
-    public void enterAssign_value_list(RParser.Assign_value_listContext ctx) {
-        transformToken(ASSIGN_LIST, ctx.getStart());
-    }
-
-    @Override
-    public void exitAssign_value_list(RParser.Assign_value_listContext ctx) {
-
-    }
-
-    @Override
-    public void enterStream(RFilter.StreamContext ctx) {
-
-    }
-
-    @Override
-    public void exitStream(RFilter.StreamContext ctx) {
-
-    }
-
-    @Override
-    public void enterEat(RFilter.EatContext ctx) {
-
-    }
-
-    @Override
-    public void exitEat(RFilter.EatContext ctx) {
-
-    }
-
-    @Override
-    public void enterElement(RFilter.ElementContext ctx) {
-
-    }
-
-    @Override
-    public void exitElement(RFilter.ElementContext ctx) {
-
-    }
-
-    @Override
-    public void enterAtom(RFilter.AtomContext ctx) {
-
-    }
-
-    @Override
-    public void exitAtom(RFilter.AtomContext ctx) {
-
-    }
-
-    @Override
-    public void enterOp(RFilter.OpContext ctx) {
-
-    }
-
-    @Override
-    public void exitOp(RFilter.OpContext ctx) {
-
-    }
-
-    @Override
-    public void enterEveryRule(ParserRuleContext ctx) {
-
-    }
-
-    @Override
-    public void exitEveryRule(ParserRuleContext ctx) {
-
-    }
-
-    @Override
-    public void visitTerminal(TerminalNode node) {
-
-    }
-
-    @Override
-    public void visitErrorNode(ErrorNode node) {
-
-    }
 }

+ 341 - 0
jplag.frontend.R/src/main/java/de/jplag/R/RCombinedBaseListener.java

@@ -0,0 +1,341 @@
+package de.jplag.R;
+
+import de.jplag.R.grammar.*;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ *  Empty base implementation for {@link RListener} and {@link RFilterListener}.
+ */
+public class RCombinedBaseListener implements RListener, RFilterListener {
+    @Override
+    public void enterStream(RFilter.StreamContext context) {
+
+    }
+
+    @Override
+    public void exitStream(RFilter.StreamContext context) {
+
+    }
+
+    @Override
+    public void enterEat(RFilter.EatContext context) {
+
+    }
+
+    @Override
+    public void exitEat(RFilter.EatContext context) {
+
+    }
+
+    @Override
+    public void enterElement(RFilter.ElementContext context) {
+
+    }
+
+    @Override
+    public void exitElement(RFilter.ElementContext context) {
+
+    }
+
+    @Override
+    public void enterAtom(RFilter.AtomContext context) {
+
+    }
+
+    @Override
+    public void exitAtom(RFilter.AtomContext context) {
+
+    }
+
+    @Override
+    public void enterOp(RFilter.OpContext context) {
+
+    }
+
+    @Override
+    public void exitOp(RFilter.OpContext context) {
+
+    }
+
+    @Override
+    public void enterProg(RParser.ProgContext context) {
+
+    }
+
+    @Override
+    public void exitProg(RParser.ProgContext context) {
+
+    }
+
+    @Override
+    public void enterExpr(RParser.ExprContext context) {
+
+    }
+
+    @Override
+    public void exitExpr(RParser.ExprContext context) {
+
+    }
+
+    @Override
+    public void enterIndex_statement(RParser.Index_statementContext context) {
+
+    }
+
+    @Override
+    public void exitIndex_statement(RParser.Index_statementContext context) {
+
+    }
+
+    @Override
+    public void enterAccess_package(RParser.Access_packageContext context) {
+
+    }
+
+    @Override
+    public void exitAccess_package(RParser.Access_packageContext context) {
+
+    }
+
+    @Override
+    public void enterFunction_definition(RParser.Function_definitionContext context) {
+
+    }
+
+    @Override
+    public void exitFunction_definition(RParser.Function_definitionContext context) {
+
+    }
+
+    @Override
+    public void enterFunction_call(RParser.Function_callContext context) {
+
+    }
+
+    @Override
+    public void exitFunction_call(RParser.Function_callContext context) {
+
+    }
+
+    @Override
+    public void enterConstant(RParser.ConstantContext context) {
+
+    }
+
+    @Override
+    public void exitConstant(RParser.ConstantContext context) {
+
+    }
+
+    @Override
+    public void enterConstant_number(RParser.Constant_numberContext context) {
+
+    }
+
+    @Override
+    public void exitConstant_number(RParser.Constant_numberContext context) {
+
+    }
+
+    @Override
+    public void enterConstant_string(RParser.Constant_stringContext context) {
+
+    }
+
+    @Override
+    public void exitConstant_string(RParser.Constant_stringContext context) {
+
+    }
+
+    @Override
+    public void enterConstant_bool(RParser.Constant_boolContext context) {
+
+    }
+
+    @Override
+    public void exitConstant_bool(RParser.Constant_boolContext context) {
+
+    }
+
+    @Override
+    public void enterHelp(RParser.HelpContext context) {
+
+    }
+
+    @Override
+    public void exitHelp(RParser.HelpContext context) {
+
+    }
+
+    @Override
+    public void enterIf_statement(RParser.If_statementContext context) {
+
+    }
+
+    @Override
+    public void exitIf_statement(RParser.If_statementContext context) {
+
+    }
+
+    @Override
+    public void enterFor_statement(RParser.For_statementContext context) {
+
+    }
+
+    @Override
+    public void exitFor_statement(RParser.For_statementContext context) {
+
+    }
+
+    @Override
+    public void enterWhile_statement(RParser.While_statementContext context) {
+
+    }
+
+    @Override
+    public void exitWhile_statement(RParser.While_statementContext context) {
+
+    }
+
+    @Override
+    public void enterRepeat_statement(RParser.Repeat_statementContext context) {
+
+    }
+
+    @Override
+    public void exitRepeat_statement(RParser.Repeat_statementContext context) {
+
+    }
+
+    @Override
+    public void enterNext_statement(RParser.Next_statementContext context) {
+
+    }
+
+    @Override
+    public void exitNext_statement(RParser.Next_statementContext context) {
+
+    }
+
+    @Override
+    public void enterBreak_statement(RParser.Break_statementContext context) {
+
+    }
+
+    @Override
+    public void exitBreak_statement(RParser.Break_statementContext context) {
+
+    }
+
+    @Override
+    public void enterCompound_statement(RParser.Compound_statementContext context) {
+
+    }
+
+    @Override
+    public void exitCompound_statement(RParser.Compound_statementContext context) {
+
+    }
+
+    @Override
+    public void enterExprlist(RParser.ExprlistContext context) {
+
+    }
+
+    @Override
+    public void exitExprlist(RParser.ExprlistContext context) {
+
+    }
+
+    @Override
+    public void enterFormlist(RParser.FormlistContext context) {
+
+    }
+
+    @Override
+    public void exitFormlist(RParser.FormlistContext context) {
+
+    }
+
+    @Override
+    public void enterForm(RParser.FormContext context) {
+
+    }
+
+    @Override
+    public void exitForm(RParser.FormContext context) {
+
+    }
+
+    @Override
+    public void enterSublist(RParser.SublistContext context) {
+
+    }
+
+    @Override
+    public void exitSublist(RParser.SublistContext context) {
+
+    }
+
+    @Override
+    public void enterSub(RParser.SubContext context) {
+
+    }
+
+    @Override
+    public void exitSub(RParser.SubContext context) {
+
+    }
+
+    @Override
+    public void enterAssign_value(RParser.Assign_valueContext context) {
+
+    }
+
+    @Override
+    public void exitAssign_value(RParser.Assign_valueContext context) {
+
+    }
+
+    @Override
+    public void enterAssign_func_declaration(RParser.Assign_func_declarationContext context) {
+
+    }
+
+    @Override
+    public void exitAssign_func_declaration(RParser.Assign_func_declarationContext context) {
+
+    }
+
+    @Override
+    public void enterAssign_value_list(RParser.Assign_value_listContext context) {
+
+    }
+
+    @Override
+    public void exitAssign_value_list(RParser.Assign_value_listContext context) {
+
+    }
+
+    @Override
+    public void visitTerminal(TerminalNode node) {
+
+    }
+
+    @Override
+    public void visitErrorNode(ErrorNode node) {
+
+    }
+
+    @Override
+    public void enterEveryRule(ParserRuleContext context) {
+
+    }
+
+    @Override
+    public void exitEveryRule(ParserRuleContext context) {
+
+    }
+}

+ 0 - 2
jplag.frontend.R/src/main/java/de/jplag/R/RTokenConstants.java

@@ -9,8 +9,6 @@ import de.jplag.TokenConstants;
  * @author Robin Maisch
  */
 public interface RTokenConstants extends TokenConstants {
-    int FILE_END = 0;
-    int SEPARATOR_TOKEN = 1;
 
     int BEGIN_FUNCTION = 2;
     int END_FUNCTION = 3;

+ 6 - 6
jplag.frontend.R/src/test/java/de/jplag/R/RFrontendTest.java

@@ -24,9 +24,9 @@ import de.jplag.testutils.TestErrorConsumer;
 public class RFrontendTest {
 
     /**
-     * Regular expression for comments.
+     * Regular expression for lines that contain no code.
      */
-    private static final String R_COMMENT = "#.*";
+    private static final String R_NO_CODE_LINE = "\s*(?:#.*)?";
 
     /**
      * Test source file that is supposed to produce a complete set of tokens, i.e. all types of tokens.
@@ -66,10 +66,10 @@ public class RFrontendTest {
 
         try {
             List<String> lines = Files.readAllLines(testFile.toPath());
-            String commentExpression = getCommentExpression();
+            String emptyLineExpression = getNoCodeLineExpression();
 
             // All lines that contain code
-            var codeLines = IntStream.range(1, lines.size() + 1).filter(idx -> !lines.get(idx - 1).matches("\\s*(%s)?".formatted(commentExpression)))
+            var codeLines = IntStream.range(1, lines.size() + 1).filter(idx -> !lines.get(idx - 1).matches(emptyLineExpression))
                     .toArray();
             // All lines that contain token
             var tokenLines = IntStream.range(0, tokens.size()).mapToObj(tokens::getToken).mapToInt(Token::getLine).distinct().toArray();
@@ -106,8 +106,8 @@ public class RFrontendTest {
         assertArrayEquals(allTokens, foundTokens);
     }
 
-    private static String getCommentExpression() {
-        return R_COMMENT;
+    private static String getNoCodeLineExpression() {
+        return R_NO_CODE_LINE;
     }
 
 }

+ 2 - 2
jplag/src/main/java/de/jplag/options/LanguageOption.java

@@ -13,7 +13,7 @@ public enum LanguageOption {
     PYTHON_3("python3", "de.jplag.python3.Language"),
     C_CPP("cpp", "de.jplag.cpp.Language"),
     C_SHARP("csharp", "de.jplag.csharp.Language"),
-    R("R", "de.jplag.R.Language"),
+    R("r", "de.jplag.R.Language"),
     CHAR("char", "de.jplag.chars.Language"),
     TEXT("text", "de.jplag.text.Language"),
     SCHEME("scheme", "de.jplag.scheme.Language");
@@ -35,7 +35,7 @@ public enum LanguageOption {
     }
 
     public static LanguageOption fromDisplayName(String displayName) {
-        return Arrays.stream(LanguageOption.values()).filter(languageOption -> languageOption.displayName.equals(displayName)).findFirst()
+        return Arrays.stream(LanguageOption.values()).filter(languageOption -> languageOption.displayName.equalsIgnoreCase(displayName)).findFirst()
                 .orElse(getDefault());
     }