Skip to content

Commit

Permalink
Merge pull request #186 from ErikSchierboom/grep
Browse files Browse the repository at this point in the history
Add grep exercise
  • Loading branch information
ErikSchierboom committed Aug 9, 2016
2 parents adf9ee3 + 1916593 commit 4aec687
Show file tree
Hide file tree
Showing 3 changed files with 375 additions and 1 deletion.
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
"rectangles",
"sgf-parsing",
"transpose",
"tree-building"
"tree-building",
"grep"
],
"deprecated": [
"binary",
Expand Down
82 changes: 82 additions & 0 deletions exercises/grep/Example.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
module Grep

open System
open System.IO
open System.Text.RegularExpressions

type Line = { Number: int; Text: string; File: string }

[<Flags>]
type Flags =
| None = 0
| PrintLineNumbers = 1
| PrintFileNames = 2
| CaseInsensitive = 4
| Invert = 8
| MatchEntireLines = 16

let parseFlag =
function
| "-n" -> Flags.PrintLineNumbers
| "-l" -> Flags.PrintFileNames
| "-i" -> Flags.CaseInsensitive
| "-v" -> Flags.Invert
| "-x" -> Flags.MatchEntireLines
| _ -> Flags.None

let parseFlags (flags: string) =
flags.Split ' '
|> Array.fold (fun acc flag -> acc ||| parseFlag flag) Flags.None

let isMatch pattern (flags: Flags) =
let pattern' = if flags.HasFlag Flags.MatchEntireLines then sprintf "^%s$" pattern else pattern
let options = if flags.HasFlag Flags.CaseInsensitive then RegexOptions.IgnoreCase else RegexOptions.None
let regex = Regex(pattern', options)

fun text -> regex.IsMatch text <> flags.HasFlag Flags.Invert

let mkLine file index text = { File = file; Number = index + 1; Text = text }

let findMatchingLines pattern flags file =
let lineMatches line = isMatch pattern flags line.Text

file
|> File.ReadLines
|> Seq.mapi (mkLine file)
|> Seq.filter lineMatches

let formatMatchingFile file = sprintf "%s\n" file

let formatMatchingFiles pattern (flags: Flags) files =
let hasMatchingLine file = findMatchingLines pattern flags file |> Seq.isEmpty |> not

files
|> Seq.filter hasMatchingLine
|> Seq.map formatMatchingFile
|> String.Concat

let formatMatchingLine (flags: Flags) files line =
let printLineNumbers = flags.HasFlag Flags.PrintLineNumbers
let printFileName = List.length files > 1

match printLineNumbers, printFileName with
| true, true -> sprintf "%s:%i:%s\n" line.File line.Number line.Text
| true, false -> sprintf "%i:%s\n" line.Number line.Text
| false, true -> sprintf "%s:%s\n" line.File line.Text
| false, false -> sprintf "%s\n" line.Text

let formatMatchingLines pattern (flags: Flags) files =
let lineMatches = findMatchingLines pattern flags
let formatLine = formatMatchingLine flags files

files
|> Seq.collect lineMatches
|> Seq.map formatLine
|> String.Concat

let grep pattern flagArguments files =
let flags = parseFlags flagArguments

match flags.HasFlag Flags.PrintFileNames with
| true -> formatMatchingFiles pattern flags files
| false -> formatMatchingLines pattern flags files
291 changes: 291 additions & 0 deletions exercises/grep/GrepTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
module GrepTest

open NUnit.Framework

open Grep

open System.IO

let iliadFileName = "iliad.txt"
let iliadContents =
"""Achilles sing, O Goddess! Peleus' son;
His wrath pernicious, who ten thousand woes
Caused to Achaia's host, sent many a soul
Illustrious into Ades premature,
And Heroes gave (so stood the will of Jove)
To dogs and to all ravening fowls a prey,
When fierce dispute had separated once
The noble Chief Achilles from the son
Of Atreus, Agamemnon, King of men.
"""

let midsummerNightFileName = "midsummer-night.txt"
let midsummerNightContents =
"""I do entreat your grace to pardon me.
I know not by what power I am made bold,
Nor how it may concern my modesty,
In such a presence here to plead my thoughts;
But I beseech your grace that I may know
The worst that may befall me in this case,
If I refuse to wed Demetrius.
"""

let paradiseLostFileName = "paradise-lost.txt"
let paradiseLostContents =
"""Of Mans First Disobedience, and the Fruit
Of that Forbidden Tree, whose mortal tast
Brought Death into the World, and all our woe,
With loss of Eden, till one greater Man
Restore us, and regain the blissful Seat,
Sing Heav'nly Muse, that on the secret top
Of Oreb, or of Sinai, didst inspire
That Shepherd, who first taught the chosen Seed
"""

[<OneTimeSetUp>]
let init () =
File.WriteAllText(iliadFileName, iliadContents)
File.WriteAllText(midsummerNightFileName, midsummerNightContents)
File.WriteAllText(paradiseLostFileName, paradiseLostContents)

[<Test>]
let ``One file, one match, no flags`` () =
let pattern = "Agamemnon"
let flags = ""
let files = [iliadFileName]

let expected =
"Of Atreus, Agamemnon, King of men.\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, several matches, no flags`` () =
let pattern = "may"
let flags = ""
let files = [midsummerNightFileName]

let expected =
"Nor how it may concern my modesty,\n" +
"But I beseech your grace that I may know\n" +
"The worst that may befall me in this case,\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, several matches, print line numbers flag`` () =
let pattern = "may"
let flags = "-n"
let files = [midsummerNightFileName]

let expected =
"3:Nor how it may concern my modesty,\n" +
"5:But I beseech your grace that I may know\n" +
"6:The worst that may befall me in this case,\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, one match, print file names flag`` () =
let pattern = "Forbidden"
let flags = "-l"
let files = [paradiseLostFileName]

let expected =
sprintf "%s\n" paradiseLostFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, several matches, case-insensitive flag`` () =
let pattern = "ACHILLES"
let flags = "-i"
let files = [iliadFileName]

let expected =
"Achilles sing, O Goddess! Peleus' son;\n" +
"The noble Chief Achilles from the son\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, several matches, inverted flag`` () =
let pattern = "Of"
let flags = "-v"
let files = [paradiseLostFileName]

let expected =
"Brought Death into the World, and all our woe,\n" +
"With loss of Eden, till one greater Man\n" +
"Restore us, and regain the blissful Seat,\n" +
"Sing Heav'nly Muse, that on the secret top\n" +
"That Shepherd, who first taught the chosen Seed\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, one match, match entire lines flag`` () =
let pattern = "With loss of Eden, till one greater Man"
let flags = "-x"
let files = [paradiseLostFileName]

let expected =
"With loss of Eden, till one greater Man\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``One file, one match, multiple flags`` () =
let pattern = "OF ATREUS, Agamemnon, KIng of MEN."
let files = [iliadFileName]
let flags = "-n -i -x"
let expected =
"9:Of Atreus, Agamemnon, King of men.\n"

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<TestCase("", Ignore = "Remove to run test case")>]
[<TestCase("-n", Ignore = "Remove to run test case")>]
[<TestCase("-l", Ignore = "Remove to run test case")>]
[<TestCase("-x", Ignore = "Remove to run test case")>]
[<TestCase("-i", Ignore = "Remove to run test case")>]
[<TestCase("-n -l -x -i", Ignore = "Remove to run test case")>]
let ``One file, no matches, various flags`` (flags) =
let pattern = "Gandalf"
let files = [iliadFileName]
let expected = ""

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, one match, no flags`` () =
let pattern = "Agamemnon"
let flags = ""
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:Of Atreus, Agamemnon, King of men.\n" iliadFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, no flags`` () =
let pattern = "may"
let flags = ""
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:Nor how it may concern my modesty,\n" midsummerNightFileName +
sprintf "%s:But I beseech your grace that I may know\n" midsummerNightFileName +
sprintf "%s:The worst that may befall me in this case,\n" midsummerNightFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, print line numbers flag`` () =
let pattern = "that"
let flags = "-n"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:5:But I beseech your grace that I may know\n" midsummerNightFileName +
sprintf "%s:6:The worst that may befall me in this case,\n" midsummerNightFileName +
sprintf "%s:2:Of that Forbidden Tree, whose mortal tast\n" paradiseLostFileName +
sprintf "%s:6:Sing Heav'nly Muse, that on the secret top\n" paradiseLostFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, print file names flag`` () =
let pattern = "who"
let flags = "-l"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s\n" iliadFileName +
sprintf "%s\n" paradiseLostFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, case-insensitive flag`` () =
let pattern = "TO"
let flags = "-i"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:Caused to Achaia's host, sent many a soul\n" iliadFileName +
sprintf "%s:Illustrious into Ades premature,\n" iliadFileName +
sprintf "%s:And Heroes gave (so stood the will of Jove)\n" iliadFileName +
sprintf "%s:To dogs and to all ravening fowls a prey,\n" iliadFileName +
sprintf "%s:I do entreat your grace to pardon me.\n" midsummerNightFileName +
sprintf "%s:In such a presence here to plead my thoughts;\n" midsummerNightFileName +
sprintf "%s:If I refuse to wed Demetrius.\n" midsummerNightFileName +
sprintf "%s:Brought Death into the World, and all our woe,\n" paradiseLostFileName +
sprintf "%s:Restore us, and regain the blissful Seat,\n" paradiseLostFileName +
sprintf "%s:Sing Heav'nly Muse, that on the secret top\n" paradiseLostFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, inverted flag`` () =
let pattern = "a"
let flags = "-v"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:Achilles sing, O Goddess! Peleus' son;\n" iliadFileName +
sprintf "%s:The noble Chief Achilles from the son\n" iliadFileName +
sprintf "%s:If I refuse to wed Demetrius.\n" midsummerNightFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, one match, match entire lines flag`` () =
let pattern = "But I beseech your grace that I may know"
let flags = "-x"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected =
sprintf "%s:But I beseech your grace that I may know\n" midsummerNightFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Multiple files, several matches, multiple flags`` () =
let pattern = "WITH LOSS OF EDEN, TILL ONE GREATER MAN"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]
let flags = "-n -i -x"
let expected =
sprintf "%s:4:With loss of Eden, till one greater Man\n" paradiseLostFileName

Assert.That(grep pattern flags files, Is.EqualTo(expected))

[<TestCase("", Ignore = "Remove to run test case")>]
[<TestCase("-n", Ignore = "Remove to run test case")>]
[<TestCase("-l", Ignore = "Remove to run test case")>]
[<TestCase("-x", Ignore = "Remove to run test case")>]
[<TestCase("-i", Ignore = "Remove to run test case")>]
[<TestCase("-n -l -x -i", Ignore = "Remove to run test case")>]
let ``Multiple files, no matches, various flags`` (flags) =
let pattern = "Frodo"
let files = [iliadFileName; midsummerNightFileName; paradiseLostFileName]

let expected = ""

Assert.That(grep pattern flags files, Is.EqualTo(expected))

0 comments on commit 4aec687

Please sign in to comment.