From 5cd8bd9bd8539195e431968b474a92159cb8af3f Mon Sep 17 00:00:00 2001 From: Kostya Nedikov Date: Tue, 12 Feb 2019 21:45:53 +0300 Subject: [PATCH 1/3] done task --- bash/build.gradle | 2 + bash/gradle/wrapper/gradle-wrapper.properties | 3 +- .../kotlin/hse/nedikov/bash/logic/Command.kt | 1 + .../hse/nedikov/bash/logic/commands/Echo.kt | 1 - .../hse/nedikov/bash/logic/commands/Grep.kt | 71 +++++++++++++++++++ .../bash/logic/commands/CommandsTest.kt | 66 +++++++++++++++++ 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt diff --git a/bash/build.gradle b/bash/build.gradle index f232c95..073b581 100644 --- a/bash/build.gradle +++ b/bash/build.gradle @@ -10,6 +10,8 @@ repositories { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + compile "com.xenomachina:kotlin-argparser:2.0.7" + testCompile group: 'junit', name: 'junit', version: '4.12' } compileKotlin { diff --git a/bash/gradle/wrapper/gradle-wrapper.properties b/bash/gradle/wrapper/gradle-wrapper.properties index d2c45a4..80b357e 100644 --- a/bash/gradle/wrapper/gradle-wrapper.properties +++ b/bash/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Feb 12 13:01:42 MSK 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip diff --git a/bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt b/bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt index 25eef02..8954b4f 100644 --- a/bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt +++ b/bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt @@ -51,6 +51,7 @@ abstract class Command { "pwd" -> Pwd() "exit" -> Exit(env) "cat" -> Cat(args) + "grep" -> Grep(args) else -> OuterCommand(name, args) } } diff --git a/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Echo.kt b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Echo.kt index fc25d94..411c404 100644 --- a/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Echo.kt +++ b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Echo.kt @@ -16,7 +16,6 @@ class Echo(private val arguments: ArrayList) : Command() { val sj = StringJoiner(" ", "", "\n") arguments.forEach { sj.add(it) } output.write(sj.toString()) - output.close() } /** diff --git a/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt new file mode 100644 index 0000000..6cc6ade --- /dev/null +++ b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt @@ -0,0 +1,71 @@ +package hse.nedikov.bash.logic.commands + +import com.xenomachina.argparser.ArgParser +import com.xenomachina.argparser.ShowHelpException +import com.xenomachina.argparser.default +import hse.nedikov.bash.logic.Command +import java.io.* +import java.lang.IllegalArgumentException +import java.util.ArrayList + +/** + * grep command which prints all lines with pattern matched substring + */ +class Grep(private val arguments: ArrayList) : Command() { + /** + * Takes lines for grep from input + */ + override fun execute(input: PipedReader, output: PipedWriter) { + val args = parseArguments(::PipedGrepArgs, output) ?: return + grep(input, output, args) + } + + /** + * Takes lines for grep from file from arguments + */ + override fun execute(output: PipedWriter) { + val args = parseArguments(::GrepArgs, output) ?: return + grep(args.file, output, args) + } + + private fun parseArguments(constructor: (ArgParser) -> T, output: Writer): T? { + val arr = Array(arguments.size) { i -> arguments[i] } + try { + return ArgParser(arr).parseInto(constructor) + } catch (e: ShowHelpException) { + println(e.printUserMessage(output, "grep", 80)) + } + return null; + } + + private fun grep(input: Reader, output: PipedWriter, args: PipedGrepArgs) { + var linesToWrite = 0; + input.forEachLine { + val matchString = if (args.ignoreCase) it.toLowerCase() else it + val matchPattern = if (args.ignoreCase) args.pattern.toLowerCase() else args.pattern + val wordEnd = if (args.wordRegexp) "(\\s|^|\$)" else "" + val regex = Regex(".*?$wordEnd$matchPattern$wordEnd.*") + if (matchString.matches(regex)) { + output.write("$it\n") + linesToWrite = args.afterContext; + } else if (linesToWrite > 0) { + output.write("$it\n") + linesToWrite-- + } + } + } +} + +private open class PipedGrepArgs(parser: ArgParser) { + val ignoreCase by parser.flagging("-i", "--ignore-case", help = "ignore case distinctions") + val wordRegexp by parser.flagging("-w", "--word-regexp", help = "force PATTERN to match only whole words") + val afterContext by parser.storing("-A", "--after-context", argName = "NUM", + help = "print NUM lines of trailing context") { toInt() } + .default(0) + .addValidator { if (this.value < 0) throw IllegalArgumentException("NUM should be non negative") } + val pattern by parser.positional("PATTERN") +} + +private class GrepArgs(parser: ArgParser) : PipedGrepArgs(parser) { + val file by parser.positional("FILE") { FileReader(this) } +} \ No newline at end of file diff --git a/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt b/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt index 5b1c74a..0723522 100644 --- a/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt +++ b/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt @@ -57,6 +57,57 @@ class CommandsTest { assertEquals("1 3 16", stringFromReader(reader)) } + @Test + fun grepTest() { + val reader = Grep(list("lol")).execute(readerFromString(keklolString)) + assertEquals(""" + lol kek + kek lol + lolol lo + looolol + lol + """.trimIndent(), stringFromReader(reader)) + } + + @Test + fun grepWordRegexpTest() { + val reader = Grep(list("lol", "-w")).execute(readerFromString(keklolString)) + assertEquals(""" + lol kek + kek lol + lol + """.trimIndent(), stringFromReader(reader)) + } + + @Test + fun grepIgnoreCaseTest() { + val reader = Grep(list("LoL", "-i")).execute(readerFromString(keklolString)) + assertEquals(""" + lol kek + kek lol + lolol lo + looolol + kekek LOL + lol + """.trimIndent(), stringFromReader(reader)) + } + + @Test + fun grepAfterContextTest() { + var reader = Grep(list("lol", "-A", "5")).execute(readerFromString(keklolString)) + assertEquals(keklolString, stringFromReader(reader)) + reader = Grep(list("lol", "-A", "1")).execute(readerFromString(keklolString)) + assertEquals(""" + lol kek + kek lol + kek kek + lolol lo + looolol + kek + lol + """.trimIndent(), stringFromReader(reader)) + } + companion object { fun readerFromString(string: String): PipedReader { val reader = PipedReader() @@ -69,5 +120,20 @@ class CommandsTest { reader.readLines().forEach { joiner.add(it) } return joiner.toString() } + + val keklolString = """ + lol kek + kek lol + kek kek + ke + eeek + lolol lo + looolol + kek + kekek LOL + kekekekekek + kekekekekekekek + lol + """.trimIndent() } } \ No newline at end of file From 4132f24914a97e494413d0f09c236a57b981b65a Mon Sep 17 00:00:00 2001 From: Kostya Nedikov Date: Thu, 14 Mar 2019 23:19:14 +0300 Subject: [PATCH 2/3] refactor grep command logic, fix incorrect flags work --- .../kotlin/hse/nedikov/bash/logic/commands/Grep.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt index 6cc6ade..8cf23b8 100644 --- a/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt +++ b/bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt @@ -33,7 +33,7 @@ class Grep(private val arguments: ArrayList) : Command() { try { return ArgParser(arr).parseInto(constructor) } catch (e: ShowHelpException) { - println(e.printUserMessage(output, "grep", 80)) + e.printUserMessage(output, "grep", 80) } return null; } @@ -41,11 +41,10 @@ class Grep(private val arguments: ArrayList) : Command() { private fun grep(input: Reader, output: PipedWriter, args: PipedGrepArgs) { var linesToWrite = 0; input.forEachLine { - val matchString = if (args.ignoreCase) it.toLowerCase() else it - val matchPattern = if (args.ignoreCase) args.pattern.toLowerCase() else args.pattern - val wordEnd = if (args.wordRegexp) "(\\s|^|\$)" else "" - val regex = Regex(".*?$wordEnd$matchPattern$wordEnd.*") - if (matchString.matches(regex)) { + val wordEnd = if (args.wordRegexp) "\\b" else "" + val matchPattern = "$wordEnd${args.pattern}$wordEnd" + val regex = if (args.ignoreCase) Regex(matchPattern, RegexOption.IGNORE_CASE) else Regex(matchPattern) + if (it.contains(regex)) { output.write("$it\n") linesToWrite = args.afterContext; } else if (linesToWrite > 0) { From 8feda4090264771c4ea45de4c196d0ac04f59712 Mon Sep 17 00:00:00 2001 From: Kostya Nedikov Date: Sat, 16 Mar 2019 14:51:17 +0300 Subject: [PATCH 3/3] fix tests CRLF problem in cause of trimIdent --- .../hse/nedikov/bash/logic/commands/CommandsTest.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt b/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt index 198d5ae..d5b3e8d 100644 --- a/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt +++ b/bash/src/test/kotlin/hse/nedikov/bash/logic/commands/CommandsTest.kt @@ -66,7 +66,7 @@ class CommandsTest { lolol lo looolol lol - """.trimIndent(), stringFromReader(reader)) + """.trimIndent(), toLF(stringFromReader(reader))) } @Test @@ -76,7 +76,7 @@ class CommandsTest { lol kek kek lol lol - """.trimIndent(), stringFromReader(reader)) + """.trimIndent(), toLF(stringFromReader(reader))) } @Test @@ -89,13 +89,13 @@ class CommandsTest { looolol kekek LOL lol - """.trimIndent(), stringFromReader(reader)) + """.trimIndent(), toLF(stringFromReader(reader))) } @Test fun grepAfterContextTest() { var reader = Grep(list("lol", "-A", "5")).execute(readerFromString(keklolString)) - assertEquals(keklolString, stringFromReader(reader)) + assertEquals(keklolString, toLF(stringFromReader(reader))) reader = Grep(list("lol", "-A", "1")).execute(readerFromString(keklolString)) assertEquals(""" lol kek @@ -105,7 +105,7 @@ class CommandsTest { looolol kek lol - """.trimIndent(), stringFromReader(reader)) + """.trimIndent(), toLF(stringFromReader(reader))) } companion object { @@ -121,6 +121,9 @@ class CommandsTest { return joiner.toString() } + fun toLF(str: String) = str.replace("\r\n", "\n") + + val keklolString = """ lol kek kek lol