diff --git a/build.gradle.kts b/build.gradle.kts index 5d501c4..1238dc2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,14 @@ plugins { java + application +} + +application { + mainClass.set("parser.Bash") +} + +tasks.named("run") { + standardInput = System.`in` } group = "org.example" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..fe67727 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/greptest b/greptest new file mode 100644 index 0000000..c03a1c3 --- /dev/null +++ b/greptest @@ -0,0 +1,12 @@ +123 +3324 +hello +hello +fsdfsd +sdfsdf +qwer +qwerty +HELLOWORLD +dsfsdf +sdfdsf +HELLO \ No newline at end of file diff --git a/greptest1 b/greptest1 new file mode 100644 index 0000000..8293795 --- /dev/null +++ b/greptest1 @@ -0,0 +1,6 @@ +123 +3324 +hello +hello1 +123 +3324 \ No newline at end of file diff --git a/src/main/java/bntler/WordOrStringVisitor.java b/src/main/java/bntler/WordOrStringVisitor.java index 0def8d7..a551ac3 100644 --- a/src/main/java/bntler/WordOrStringVisitor.java +++ b/src/main/java/bntler/WordOrStringVisitor.java @@ -44,8 +44,8 @@ public String visitDqstr(BashParser.DqstrContext ctx) { String noQuotes = dqstr.substring(1, dqstr.length() - 1); for (Map.Entry entry : Environment.values.entrySet()) { - if (noQuotes.contains("$"+entry.getKey())) { - noQuotes = noQuotes.replace("$"+entry.getKey(), entry.getValue()); + if (noQuotes.contains("$" + entry.getKey())) { + noQuotes = noQuotes.replace("$" + entry.getKey(), entry.getValue()); } } return noQuotes; diff --git a/src/main/java/builtins/Echo.java b/src/main/java/builtins/Echo.java index f416495..26714fd 100644 --- a/src/main/java/builtins/Echo.java +++ b/src/main/java/builtins/Echo.java @@ -9,7 +9,7 @@ public static String execute(BashCommand command, @Nullable String stdin) { int i = 1; boolean flag = true; - if (!command.parts().isEmpty() && command.parts().get(i).equals("-n")) { + if (command.parts().size() > 1 && command.parts().get(i).equals("-n")) { i++; flag = false; } diff --git a/src/main/java/builtins/Grep.java b/src/main/java/builtins/Grep.java new file mode 100644 index 0000000..c0e3742 --- /dev/null +++ b/src/main/java/builtins/Grep.java @@ -0,0 +1,131 @@ +package builtins; + +import bntler.BashCommand; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; + +//lazy javadoc +//-i - ignore case +//-w - complete word +//-A - after context + +public class Grep { + + private static final int STATE_INITIAL = 0; + private static final int STATE_DASH_A_WORDS_COMPLETED = 1; + private static final int STATE_HAVE_MORE_WORDS_AFTER_DASH_A = 2; + + public static String execute(BashCommand command, @Nullable String stdin) { + + boolean ignoreCase = false; + int linesAfterMatch = 0; + boolean exactMatch = false; + + int i = 1; + while (i < command.parts().size()) { + String arg = command.parts().get(i); + if (!arg.startsWith("-")) { + break; + } + + switch (arg) { + case "-i" -> ignoreCase = true; + case "-w" -> exactMatch = true; + case "-A" -> { + if (i + 1 < command.parts().size()) { + String number = command.parts().get(i + 1); + try { + linesAfterMatch = Integer.parseInt(number); + i++; + } catch (NumberFormatException e) { + return "Invalid Argument\n"; + } + } else { + return "Invalid Argument\n"; + } + } + default -> { + return "Unsupported parameter\n"; + } + } + i++; + } + + if (i == command.parts().size()) { + return "usage: grep [-w] [-A num] [-i] \n"; + } + + String lookingFor = command.parts().get(i); + i++; + + try { + StringBuilder grepWhat = new StringBuilder(); + + if (i == command.parts().size()) { + for (String s : stdin.split("\n")) { + if (getMatch(s, lookingFor, ignoreCase, exactMatch)) { + grepWhat.append(s).append("\n"); + } + } + } else if (i == command.parts().size() - 1) { + String filename = command.parts().get(i); + searchFile(grepWhat, filename, lookingFor, linesAfterMatch, ignoreCase, exactMatch, false); + } else { + while (i < command.parts().size()) { + String filename = command.parts().get(i); + searchFile(grepWhat, filename, lookingFor, linesAfterMatch, ignoreCase, exactMatch, true); + i++; + } + } + return grepWhat.toString(); + } catch (IOException e) { + return e.getMessage(); + } + } + + + private static void searchFile(StringBuilder builder, String filename, String pattern, int linesAfterMatch, boolean ignoreCase, boolean exactMatch, boolean multiFile) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(filename)); + String line; + int linesToPrint = 0; + int stateForDoubleDash = STATE_INITIAL; + while ((line = br.readLine()) != null) { + boolean matched = getMatch(line, pattern, ignoreCase, exactMatch); + if (matched) { + if (stateForDoubleDash == STATE_HAVE_MORE_WORDS_AFTER_DASH_A) { + builder.append("--").append("\n"); + stateForDoubleDash = STATE_INITIAL; + } + linesToPrint = linesAfterMatch; + if (multiFile) { + builder.append(filename).append(":"); + } + builder.append(line).append("\n"); + } else if (linesToPrint > 0) { + builder.append(line).append("\n"); + linesToPrint--; + if (linesToPrint == 0) { + stateForDoubleDash = STATE_DASH_A_WORDS_COMPLETED; + } + } else if (stateForDoubleDash == STATE_DASH_A_WORDS_COMPLETED) { + stateForDoubleDash = STATE_HAVE_MORE_WORDS_AFTER_DASH_A; + } + } + } + + private static boolean getMatch(String line, String pattern, boolean ignoreCase, boolean exactMatch) { + if (ignoreCase) { + line = line.toLowerCase(); + pattern = pattern.toLowerCase(); + } + if (exactMatch) { + return Arrays.asList(line.toLowerCase().split(" ")).contains(pattern); + } else { + return line.contains(pattern); + } + } +} diff --git a/src/main/java/builtins/Wc.java b/src/main/java/builtins/Wc.java index 655be9d..6cbcd35 100644 --- a/src/main/java/builtins/Wc.java +++ b/src/main/java/builtins/Wc.java @@ -1,7 +1,6 @@ package builtins; import bntler.BashCommand; -import parser.Bash; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/parser/Bash.java b/src/main/java/parser/Bash.java index f74254b..e5077ee 100644 --- a/src/main/java/parser/Bash.java +++ b/src/main/java/parser/Bash.java @@ -51,11 +51,10 @@ public static String runExternal(String source) { var rootNodeGood = new PipelineVisitor().visit(rootNode); String result; try { - result = interpreter.interpret(rootNodeGood); + return interpreter.interpret(rootNodeGood); } catch (Exception e) { // e.printStackTrace(); for debug - result = e + "\n"; + return e.getMessage() + "\n"; } - return result; } } diff --git a/src/main/java/parser/Interpreter.java b/src/main/java/parser/Interpreter.java index 2312e4e..d1a58d4 100644 --- a/src/main/java/parser/Interpreter.java +++ b/src/main/java/parser/Interpreter.java @@ -4,10 +4,7 @@ import bntler.BashCommand; import bntler.BashNode; import bntler.BashPipeline; -import builtins.Cat; -import builtins.Echo; -import builtins.Pwd; -import builtins.Wc; +import builtins.*; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -46,7 +43,9 @@ public String visitCommand(BashCommand command) throws Exception { case "cat" -> { return Cat.execute(command, result); } - + case "grep" -> { + return Grep.execute(command, result); + } default -> { String strCommand = String.join(" ", command.parts()); return executeCommand(strCommand); diff --git a/src/test/java/parser/BashTest.java b/src/test/java/parser/BashTest.java index 6df69cd..b06d113 100644 --- a/src/test/java/parser/BashTest.java +++ b/src/test/java/parser/BashTest.java @@ -42,14 +42,14 @@ public void testSimpleCat() { @Test public void randomCommand() { - String expected = "java.io.IOException: Cannot run program \"rrandom\": error=2, No such file or directory\n"; + String expected = "Cannot run program \"rrandom\": error=2, No such file or directory\n"; String actual = Bash.runExternal("rrandom"); assertEquals(expected, actual); } @Test public void testSimpleVariable() { - String expected = "java.io.IOException: Cannot run program \"rrandom\": error=2, No such file or directory\n"; + String expected = "Cannot run program \"rrandom\": error=2, No such file or directory\n"; Bash.runExternal("a=rrandom"); String actual = Bash.runExternal("$a"); assertEquals(expected, actual); @@ -72,7 +72,7 @@ public void testSimpleDString() { @Test public void testSimpleVariableWithDString() { - String expected = "java.io.IOException: Cannot run program \"rrandom\": error=2, No such file or directory\n"; + String expected = "Cannot run program \"rrandom\": error=2, No such file or directory\n"; Bash.runExternal("a=rrandom"); String actual = Bash.runExternal("\"$a\""); assertEquals(expected, actual); @@ -80,7 +80,7 @@ public void testSimpleVariableWithDString() { @Test public void testSimpleVariableWithSString() { - String expected = "java.io.IOException: Cannot run program \"$a\": error=2, No such file or directory\n"; + String expected = "Cannot run program \"$a\": error=2, No such file or directory\n"; Bash.runExternal("a=rrandom"); String actual = Bash.runExternal("'$a'"); assertEquals(expected, actual); diff --git a/src/test/java/parser/GrepTest.java b/src/test/java/parser/GrepTest.java new file mode 100644 index 0000000..373e193 --- /dev/null +++ b/src/test/java/parser/GrepTest.java @@ -0,0 +1,93 @@ +package parser; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GrepTest { + + @Test + public void testSimpleGrep() { + String expected = "hello\nhello1\n"; + String actual = Bash.runExternal("grep hello greptest1"); + assertEquals(expected, actual); + } + + @Test + public void testWGrep() { + String expected = "hello\n"; + String actual = Bash.runExternal("grep -w hello greptest1"); + assertEquals(expected, actual); + } + + @Test + public void testA2WGrep() { + String expected = "hello\nhello1\n123\n"; + String actual = Bash.runExternal("grep -A 2 -w hello greptest1"); + assertEquals(expected, actual); + + String actual1 = Bash.runExternal("grep -w -A 2 hello greptest1"); + assertEquals(expected, actual1); + } + + @Test + public void testIGrep() { + String expected = "hello\nhello\nHELLOWORLD\nHELLO\n"; + String actual = Bash.runExternal("grep -i hello greptest"); + assertEquals(expected, actual); + } + + @Test + public void testIA2Grep() { + String expected = """ + hello + hello + fsdfsd + sdfsdf + -- + HELLOWORLD + dsfsdf + sdfdsf + HELLO + """; + String actual = Bash.runExternal("grep -i -A 2 hello greptest"); + assertEquals(expected, actual); + } + + @Test + public void testIA2WGrep() { + String expected = """ + hello + hello + fsdfsd + sdfsdf + -- + HELLO + """; + String actual = Bash.runExternal("grep -i -w -A 2 hello greptest"); + assertEquals(expected, actual); + } + + @Test + public void testPipe() { + String expected = """ + hello + hello + """; + String actual = Bash.runExternal("cat greptest | grep hello"); + assertEquals(expected, actual); + } + + + @Test + public void multiFile() { + String expected = """ + greptest:hello + greptest:hello + greptest1:hello + greptest1:hello1 + """; + String actual = Bash.runExternal("grep hello greptest greptest1"); + assertEquals(expected, actual); + } +}