From 71e9c3bee8ebc1f6308e82c1c496eb5d79fe0ccd Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 12 May 2024 20:46:33 +0200 Subject: [PATCH 01/11] Add Grep exercise --- config.json | 11 + .../grep/.docs/instructions.append.md | 4 + exercises/practice/grep/.docs/instructions.md | 27 ++ exercises/practice/grep/.meta/config.json | 23 ++ .../examples/success-standard/package.yaml | 16 + .../examples/success-standard/src/Grep.hs | 33 ++ exercises/practice/grep/.meta/tests.toml | 85 ++++++ exercises/practice/grep/package.yaml | 21 ++ exercises/practice/grep/src/Grep.hs | 10 + exercises/practice/grep/stack.yaml | 1 + exercises/practice/grep/test/Tests.hs | 285 ++++++++++++++++++ 11 files changed, 516 insertions(+) create mode 100644 exercises/practice/grep/.docs/instructions.append.md create mode 100644 exercises/practice/grep/.docs/instructions.md create mode 100644 exercises/practice/grep/.meta/config.json create mode 100644 exercises/practice/grep/.meta/examples/success-standard/package.yaml create mode 100644 exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs create mode 100644 exercises/practice/grep/.meta/tests.toml create mode 100644 exercises/practice/grep/package.yaml create mode 100644 exercises/practice/grep/src/Grep.hs create mode 100644 exercises/practice/grep/stack.yaml create mode 100644 exercises/practice/grep/test/Tests.hs diff --git a/config.json b/config.json index 145ade90f..d6e6cbdcc 100644 --- a/config.json +++ b/config.json @@ -1377,6 +1377,17 @@ "topics": [ "strings" ] + }, + { + "slug": "grep", + "name": "Grep", + "uuid": "4592877f-5b1e-4c8f-89aa-9e206de696eb", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "strings" + ] } ], "foregone": [ diff --git a/exercises/practice/grep/.docs/instructions.append.md b/exercises/practice/grep/.docs/instructions.append.md new file mode 100644 index 000000000..d7007c109 --- /dev/null +++ b/exercises/practice/grep/.docs/instructions.append.md @@ -0,0 +1,4 @@ +# Hints + +To simplify the exercise the flags are already given parsed and input files read, +therefore no IO operations and argument parsing need to be performed. diff --git a/exercises/practice/grep/.docs/instructions.md b/exercises/practice/grep/.docs/instructions.md new file mode 100644 index 000000000..004f28acd --- /dev/null +++ b/exercises/practice/grep/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Search files for lines matching a search string and return all matching lines. + +The Unix [`grep`][grep] command searches files for lines that match a regular expression. +Your task is to implement a simplified `grep` command, which supports searching for fixed strings. + +The `grep` command takes three arguments: + +1. The string to search for. +2. Zero or more flags for customizing the command's behavior. +3. One or more files to search in. + +It then reads the contents of the specified files (in the order specified), finds the lines that contain the search string, and finally returns those lines in the order in which they were found. +When searching in multiple files, each matching line is prepended by the file name and a colon (':'). + +## Flags + +The `grep` command supports the following flags: + +- `-n` Prepend the line number and a colon (':') to each line in the output, placing the number after the filename (if present). +- `-l` Output only the names of the files that contain at least one matching line. +- `-i` Match using a case-insensitive comparison. +- `-v` Invert the program -- collect all lines that fail to match. +- `-x` Search only for lines where the search string matches the entire line. + +[grep]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html diff --git a/exercises/practice/grep/.meta/config.json b/exercises/practice/grep/.meta/config.json new file mode 100644 index 000000000..42d124be1 --- /dev/null +++ b/exercises/practice/grep/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "tofische" + ], + "files": { + "solution": [ + "src/Grep.hs", + "package.yaml" + ], + "test": [ + "test/Tests.hs" + ], + "example": [ + ".meta/examples/success-standard/src/Grep.hs" + ], + "invalidator": [ + "stack.yaml" + ] + }, + "blurb": "Search a file for lines matching a regular expression pattern. Return the line number and contents of each matching line.", + "source": "Conversation with Nate Foster.", + "source_url": "https://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf" +} diff --git a/exercises/practice/grep/.meta/examples/success-standard/package.yaml b/exercises/practice/grep/.meta/examples/success-standard/package.yaml new file mode 100644 index 000000000..fac688139 --- /dev/null +++ b/exercises/practice/grep/.meta/examples/success-standard/package.yaml @@ -0,0 +1,16 @@ +name: grep + +dependencies: + - base + +library: + exposed-modules: Grep + source-dirs: src + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - grep + - hspec diff --git a/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs new file mode 100644 index 000000000..2c257043f --- /dev/null +++ b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs @@ -0,0 +1,33 @@ +module Grep (grep, Flag(..)) where + +import Data.Char (toLower) +import Data.List (isInfixOf) + +data Flag = N | L | I | V | X deriving (Eq, Ord) + +type Flags = [Flag] +type File = (String, [String]) +type Files = [File] + +grep :: String -> Flags -> Files -> [String] +grep pattern flags files = concatMap grepInFile files + where + flagN = N `elem` flags + flagL = L `elem` flags + flagI = I `elem` flags + flagV = V `elem` flags + flagX = X `elem` flags + pattern' = if flagI then map toLower pattern else pattern + multiple = length files > 1 + grepInFile (fileName, content) = if flagL && not (null matchInFile) then [fileName] else matchInFile + where + matchInFile = concatMap grepInLine $ zip content [1..] + grepInLine :: (String, Int) -> [String] + grepInLine (line, lineNum) = [res | if flagV then not match else match] + where + line' = if flagI then map toLower line else line + match = if flagX then pattern' == line' else pattern' `isInfixOf` line' + res = + (if multiple then fileName <> ":" else "") <> + (if flagN then show lineNum <> ":" else "") <> + line diff --git a/exercises/practice/grep/.meta/tests.toml b/exercises/practice/grep/.meta/tests.toml new file mode 100644 index 000000000..04c51e71b --- /dev/null +++ b/exercises/practice/grep/.meta/tests.toml @@ -0,0 +1,85 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[9049fdfd-53a7-4480-a390-375203837d09] +description = "Test grepping a single file -> One file, one match, no flags" + +[76519cce-98e3-46cd-b287-aac31b1d77d6] +description = "Test grepping a single file -> One file, one match, print line numbers flag" + +[af0b6d3c-e0e8-475e-a112-c0fc10a1eb30] +description = "Test grepping a single file -> One file, one match, case-insensitive flag" + +[ff7af839-d1b8-4856-a53e-99283579b672] +description = "Test grepping a single file -> One file, one match, print file names flag" + +[8625238a-720c-4a16-81f2-924ec8e222cb] +description = "Test grepping a single file -> One file, one match, match entire lines flag" + +[2a6266b3-a60f-475c-a5f5-f5008a717d3e] +description = "Test grepping a single file -> One file, one match, multiple flags" + +[842222da-32e8-4646-89df-0d38220f77a1] +description = "Test grepping a single file -> One file, several matches, no flags" + +[4d84f45f-a1d8-4c2e-a00e-0b292233828c] +description = "Test grepping a single file -> One file, several matches, print line numbers flag" + +[0a483b66-315b-45f5-bc85-3ce353a22539] +description = "Test grepping a single file -> One file, several matches, match entire lines flag" + +[3d2ca86a-edd7-494c-8938-8eeed1c61cfa] +description = "Test grepping a single file -> One file, several matches, case-insensitive flag" + +[1f52001f-f224-4521-9456-11120cad4432] +description = "Test grepping a single file -> One file, several matches, inverted flag" + +[7a6ede7f-7dd5-4364-8bf8-0697c53a09fe] +description = "Test grepping a single file -> One file, no matches, various flags" + +[3d3dfc23-8f2a-4e34-abd6-7b7d140291dc] +description = "Test grepping a single file -> One file, one match, file flag takes precedence over line flag" + +[87b21b24-b788-4d6e-a68b-7afe9ca141fe] +description = "Test grepping a single file -> One file, several matches, inverted and match entire lines flags" + +[ba496a23-6149-41c6-a027-28064ed533e5] +description = "Test grepping multiples files at once -> Multiple files, one match, no flags" + +[4539bd36-6daa-4bc3-8e45-051f69f5aa95] +description = "Test grepping multiples files at once -> Multiple files, several matches, no flags" + +[9fb4cc67-78e2-4761-8e6b-a4b57aba1938] +description = "Test grepping multiples files at once -> Multiple files, several matches, print line numbers flag" + +[aeee1ef3-93c7-4cd5-af10-876f8c9ccc73] +description = "Test grepping multiples files at once -> Multiple files, one match, print file names flag" + +[d69f3606-7d15-4ddf-89ae-01df198e6b6c] +description = "Test grepping multiples files at once -> Multiple files, several matches, case-insensitive flag" + +[82ef739d-6701-4086-b911-007d1a3deb21] +description = "Test grepping multiples files at once -> Multiple files, several matches, inverted flag" + +[77b2eb07-2921-4ea0-8971-7636b44f5d29] +description = "Test grepping multiples files at once -> Multiple files, one match, match entire lines flag" + +[e53a2842-55bb-4078-9bb5-04ac38929989] +description = "Test grepping multiples files at once -> Multiple files, one match, multiple flags" + +[9c4f7f9a-a555-4e32-bb06-4b8f8869b2cb] +description = "Test grepping multiples files at once -> Multiple files, no matches, various flags" + +[ba5a540d-bffd-481b-bd0c-d9a30f225e01] +description = "Test grepping multiples files at once -> Multiple files, several matches, file flag takes precedence over line number flag" + +[ff406330-2f0b-4b17-9ee4-4b71c31dd6d2] +description = "Test grepping multiples files at once -> Multiple files, several matches, inverted and match entire lines flags" diff --git a/exercises/practice/grep/package.yaml b/exercises/practice/grep/package.yaml new file mode 100644 index 000000000..59ae87e2b --- /dev/null +++ b/exercises/practice/grep/package.yaml @@ -0,0 +1,21 @@ +name: grep +version: 1.0.0.0 + +dependencies: + - base + +library: + exposed-modules: Grep + source-dirs: src + ghc-options: -Wall + # dependencies: + # - foo # List here the packages you + # - bar # want to use in your solution. + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - grep + - hspec diff --git a/exercises/practice/grep/src/Grep.hs b/exercises/practice/grep/src/Grep.hs new file mode 100644 index 000000000..273b35b7e --- /dev/null +++ b/exercises/practice/grep/src/Grep.hs @@ -0,0 +1,10 @@ +module Grep (grep, Flag(..)) where + +data Flag = N | L | I | V | X deriving (Eq, Ord) + +type Flags = [Flag] +type File = (String, [String]) +type Files = [File] + +grep :: String -> Flags -> Files -> [String] +grep pattern flags files = error "You need to implement this function." diff --git a/exercises/practice/grep/stack.yaml b/exercises/practice/grep/stack.yaml new file mode 100644 index 000000000..115878212 --- /dev/null +++ b/exercises/practice/grep/stack.yaml @@ -0,0 +1 @@ +resolver: lts-20.18 diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs new file mode 100644 index 000000000..c4169c48a --- /dev/null +++ b/exercises/practice/grep/test/Tests.hs @@ -0,0 +1,285 @@ +{-# LANGUAGE RecordWildCards #-} + +import Data.Foldable (for_) +import Test.Hspec (Spec, describe, it, shouldBe) +import Test.Hspec.Runner (configFailFast, defaultConfig, hspecWith) + +import Grep (grep, Flag(..)) + +main :: IO () +main = hspecWith defaultConfig {configFailFast = True} specs + +specs :: Spec +specs = do + describe "grep" $ for_ cases test + where + test Case{..} = it description $ grep pattern flags files `shouldBe` expected + +data Case = Case { description :: String + , pattern :: String + , flags :: [Flag] + , files :: [(String, [String])] + , expected :: [String] + } + +cases :: [Case] +cases = + [ Case { description = "One file, one match, no flags" + , pattern = "Agamemnon" + , flags = [] + , files = [iliad] + , expected = ["Of Atreus, Agamemnon, King of men."] + } + , Case { description = "One file, one match, print line numbers flag" + , pattern = "Forbidden" + , flags = [N] + , files = [paradiseLost] + , expected = ["2:Of that Forbidden Tree, whose mortal tast"] + } + , Case { description = "One file, one match, case-insensitive flag" + , pattern = "FORBIDDEN" + , flags = [I] + , files = [paradiseLost] + , expected = ["Of that Forbidden Tree, whose mortal tast"] + } + , Case { description = "One file, one match, print file names flag" + , pattern = "Forbidden" + , flags = [L] + , files = [paradiseLost] + , expected = ["paradise-lost.txt"] + } + , Case { description = "One file, one match, match entire lines flag" + , pattern = "With loss of Eden, till one greater Man" + , flags = [X] + , files = [paradiseLost] + , expected = ["With loss of Eden, till one greater Man"] + } + , Case { description = "One file, one match, multiple flags" + , pattern = "OF ATREUS, Agamemnon, KIng of MEN." + , flags = [N, I, X] + , files = [iliad] + , expected = ["9:Of Atreus, Agamemnon, King of men."] + } + , Case { description = "One file, several matches, no flags" + , pattern = "may" + , flags = [] + , files = [midsummerNight] + , expected = [ + "Nor how it may concern my modesty,", + "But I beseech your grace that I may know", + "The worst that may befall me in this case," + ] + } + , Case { description = "One file, several matches, print line numbers flag" + , pattern = "may" + , flags = [N] + , files = [midsummerNight] + , expected = [ + "3:Nor how it may concern my modesty,", + "5:But I beseech your grace that I may know", + "6:The worst that may befall me in this case," + ] + } + , Case { description = "One file, several matches, match entire lines flag" + , pattern = "may" + , flags = [X] + , files = [midsummerNight] + , expected = [] + } + , Case { description = "One file, several matches, case-insensitive flag" + , pattern = "ACHILLES" + , flags = [I] + , files = [iliad] + , expected = [ + "Achilles sing, O Goddess! Peleus' son;", + "The noble Chief Achilles from the son" + ] + } + , Case { description = "One file, several matches, inverted flag" + , pattern = "Of" + , flags = [V] + , files = [paradiseLost] + , expected = [ + "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", + "That Shepherd, who first taught the chosen Seed" + ] + } + , Case { description = "One file, no matches, various flags" + , pattern = "Gandalf" + , flags = [N, L, X, I] + , files = [iliad] + , expected = [] + } + , Case { description = "One file, one match, file flag takes precedence over line flag" + , pattern = "ten" + , flags = [N, L] + , files = [iliad] + , expected = ["iliad.txt"] + } + , Case { description = "One file, several matches, inverted and match entire lines flags" + , pattern = "Illustrious into Ades premature," + , flags = [X, V] + , files = [iliad] + , expected = [ + "Achilles sing, O Goddess! Peleus' son;", + "His wrath pernicious, who ten thousand woes", + "Caused to Achaia's host, sent many a soul", + "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." + ] + } + , Case { description = "Multiple files, one match, no flags" + , pattern = "Agamemnon" + , flags = [] + , files = [iliad, midsummerNight, paradiseLost] + , expected = ["iliad.txt:Of Atreus, Agamemnon, King of men."] + } + , Case { description = "Multiple files, several matches, no flags" + , pattern = "may" + , flags = [] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [ + "midsummer-night.txt:Nor how it may concern my modesty,", + "midsummer-night.txt:But I beseech your grace that I may know", + "midsummer-night.txt:The worst that may befall me in this case," + ] + } + , Case { description = "Multiple files, several matches, print line numbers flag" + , pattern = "that" + , flags = [N] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [ + "midsummer-night.txt:5:But I beseech your grace that I may know", + "midsummer-night.txt:6:The worst that may befall me in this case,", + "paradise-lost.txt:2:Of that Forbidden Tree, whose mortal tast", + "paradise-lost.txt:6:Sing Heav'nly Muse, that on the secret top" + ] + } + , Case { description = "Multiple files, one match, print file names flag" + , pattern = "who" + , flags = [L] + , files = [iliad, midsummerNight, paradiseLost] + , expected = ["iliad.txt", "paradise-lost.txt"] + } + , Case { description = "Multiple files, several matches, case-insensitive flag" + , pattern = "TO" + , flags = [I] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [ + "iliad.txt:Caused to Achaia's host, sent many a soul", + "iliad.txt:Illustrious into Ades premature,", + "iliad.txt:And Heroes gave (so stood the will of Jove)", + "iliad.txt:To dogs and to all ravening fowls a prey,", + "midsummer-night.txt:I do entreat your grace to pardon me.", + "midsummer-night.txt:In such a presence here to plead my thoughts;", + "midsummer-night.txt:If I refuse to wed Demetrius.", + "paradise-lost.txt:Brought Death into the World, and all our woe,", + "paradise-lost.txt:Restore us, and regain the blissful Seat,", + "paradise-lost.txt:Sing Heav'nly Muse, that on the secret top" + ] + } + , Case { description = "Multiple files, several matches, inverted flag" + , pattern = "a" + , flags = [V] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [ + "iliad.txt:Achilles sing, O Goddess! Peleus' son;", + "iliad.txt:The noble Chief Achilles from the son", + "midsummer-night.txt:If I refuse to wed Demetrius." + ] + } + , Case { description = "Multiple files, one match, match entire lines flag" + , pattern = "But I beseech your grace that I may know" + , flags = [X] + , files = [iliad, midsummerNight, paradiseLost] + , expected = ["midsummer-night.txt:But I beseech your grace that I may know"] + } + , Case { description = "Multiple files, one match, multiple flags" + , pattern = "WITH LOSS OF EDEN, TILL ONE GREATER MAN" + , flags = [N, I, X] + , files = [iliad, midsummerNight, paradiseLost] + , expected = ["paradise-lost.txt:4:With loss of Eden, till one greater Man"] + } + , Case { description = "Multiple files, no matches, various flags" + , pattern = "Frodo" + , flags = [N, L, X, I] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [] + } + , Case { description = "Multiple files, several matches, file flag takes precedence over line number flag" + , pattern = "who" + , flags = [N, L] + , files = [iliad, midsummerNight, paradiseLost] + , expected = ["iliad.txt", "paradise-lost.txt"] + } + , Case { description = "Multiple files, several matches, inverted and match entire lines flags" + , pattern = "Illustrious into Ades premature," + , flags = [X, V] + , files = [iliad, midsummerNight, paradiseLost] + , expected = [ + "iliad.txt:Achilles sing, O Goddess! Peleus' son;", + "iliad.txt:His wrath pernicious, who ten thousand woes", + "iliad.txt:Caused to Achaia's host, sent many a soul", + "iliad.txt:And Heroes gave (so stood the will of Jove)", + "iliad.txt:To dogs and to all ravening fowls a prey,", + "iliad.txt:When fierce dispute had separated once", + "iliad.txt:The noble Chief Achilles from the son", + "iliad.txt:Of Atreus, Agamemnon, King of men.", + "midsummer-night.txt:I do entreat your grace to pardon me.", + "midsummer-night.txt:I know not by what power I am made bold,", + "midsummer-night.txt:Nor how it may concern my modesty,", + "midsummer-night.txt:In such a presence here to plead my thoughts;", + "midsummer-night.txt:But I beseech your grace that I may know", + "midsummer-night.txt:The worst that may befall me in this case,", + "midsummer-night.txt:If I refuse to wed Demetrius.", + "paradise-lost.txt:Of Mans First Disobedience, and the Fruit", + "paradise-lost.txt:Of that Forbidden Tree, whose mortal tast", + "paradise-lost.txt:Brought Death into the World, and all our woe,", + "paradise-lost.txt:With loss of Eden, till one greater Man", + "paradise-lost.txt:Restore us, and regain the blissful Seat,", + "paradise-lost.txt:Sing Heav'nly Muse, that on the secret top", + "paradise-lost.txt:Of Oreb, or of Sinai, didst inspire", + "paradise-lost.txt:That Shepherd, who first taught the chosen Seed" + ] + } + ] + +iliad = + ("iliad.txt", [ + "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." + ]) +midsummerNight = + ("midsummer-night.txt", [ + "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." + ]) +paradiseLost = + ("paradise-lost.txt", [ + "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" + ]) \ No newline at end of file From bb7c83dae01574d9fc565528d364889ef6378200 Mon Sep 17 00:00:00 2001 From: tofische Date: Thu, 23 May 2024 21:14:20 +0200 Subject: [PATCH 02/11] Correct test case --- exercises/practice/grep/test/Tests.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index c4169c48a..6f857f144 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -10,8 +10,7 @@ main :: IO () main = hspecWith defaultConfig {configFailFast = True} specs specs :: Spec -specs = do - describe "grep" $ for_ cases test +specs = describe "grep" $ for_ cases test where test Case{..} = it description $ grep pattern flags files `shouldBe` expected From d629d31a925f4ad10176543956723b08c834d87b Mon Sep 17 00:00:00 2001 From: Tomas Fischer Date: Tue, 6 Aug 2024 21:17:46 +0200 Subject: [PATCH 03/11] Update exercises/practice/grep/.docs/instructions.append.md Co-authored-by: Erik Schierboom --- exercises/practice/grep/.docs/instructions.append.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/grep/.docs/instructions.append.md b/exercises/practice/grep/.docs/instructions.append.md index d7007c109..1021bba96 100644 --- a/exercises/practice/grep/.docs/instructions.append.md +++ b/exercises/practice/grep/.docs/instructions.append.md @@ -1,4 +1,4 @@ -# Hints +# Instructions append To simplify the exercise the flags are already given parsed and input files read, therefore no IO operations and argument parsing need to be performed. From e22a0467c89c8c8cdbf1e924ba2f15f755bed70a Mon Sep 17 00:00:00 2001 From: Tomas Fischer Date: Tue, 6 Aug 2024 21:18:00 +0200 Subject: [PATCH 04/11] Update exercises/practice/grep/.docs/instructions.append.md Co-authored-by: Erik Schierboom --- exercises/practice/grep/.docs/instructions.append.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/grep/.docs/instructions.append.md b/exercises/practice/grep/.docs/instructions.append.md index 1021bba96..e50a69aef 100644 --- a/exercises/practice/grep/.docs/instructions.append.md +++ b/exercises/practice/grep/.docs/instructions.append.md @@ -1,4 +1,4 @@ # Instructions append -To simplify the exercise the flags are already given parsed and input files read, +To simplify the exercise the flags are already parsed and input files read, therefore no IO operations and argument parsing need to be performed. From 81564b4dfbee7317eb6af526bd672c95ed6df055 Mon Sep 17 00:00:00 2001 From: tofische Date: Sat, 8 Nov 2025 13:50:27 +0100 Subject: [PATCH 05/11] Use IO --- .../practice/grep/.docs/instructions.append.md | 3 +-- .../examples/success-standard/package.yaml | 1 + .../.meta/examples/success-standard/src/Grep.hs | 17 +++++++++++------ exercises/practice/grep/package.yaml | 4 +--- exercises/practice/grep/src/Grep.hs | 4 +--- exercises/practice/grep/test/Tests.hs | 17 ++++++++++++++--- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/exercises/practice/grep/.docs/instructions.append.md b/exercises/practice/grep/.docs/instructions.append.md index e50a69aef..0b90e1a00 100644 --- a/exercises/practice/grep/.docs/instructions.append.md +++ b/exercises/practice/grep/.docs/instructions.append.md @@ -1,4 +1,3 @@ # Instructions append -To simplify the exercise the flags are already parsed and input files read, -therefore no IO operations and argument parsing need to be performed. +To simplify the exercise the flags are already parsed. diff --git a/exercises/practice/grep/.meta/examples/success-standard/package.yaml b/exercises/practice/grep/.meta/examples/success-standard/package.yaml index fac688139..606dd09ef 100644 --- a/exercises/practice/grep/.meta/examples/success-standard/package.yaml +++ b/exercises/practice/grep/.meta/examples/success-standard/package.yaml @@ -14,3 +14,4 @@ tests: dependencies: - grep - hspec + - directory diff --git a/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs index 2c257043f..1cc0214d8 100644 --- a/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs +++ b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs @@ -2,15 +2,20 @@ module Grep (grep, Flag(..)) where import Data.Char (toLower) import Data.List (isInfixOf) +import System.IO (readFile') data Flag = N | L | I | V | X deriving (Eq, Ord) type Flags = [Flag] -type File = (String, [String]) -type Files = [File] -grep :: String -> Flags -> Files -> [String] -grep pattern flags files = concatMap grepInFile files +grep :: String -> Flags -> [FilePath] -> IO [String] +grep pattern flags files = do + content <- mapM readFile' files + let input = zip files (map lines content) + return $ grep' pattern flags input + +grep' :: String -> Flags -> [(String, [String])] -> [String] +grep' pattern flags files = concatMap grepInFile files where flagN = N `elem` flags flagL = L `elem` flags @@ -23,10 +28,10 @@ grep pattern flags files = concatMap grepInFile files where matchInFile = concatMap grepInLine $ zip content [1..] grepInLine :: (String, Int) -> [String] - grepInLine (line, lineNum) = [res | if flagV then not match else match] + grepInLine (line, lineNum) = [res | if flagV then not isMatchInLine else isMatchInLine] where line' = if flagI then map toLower line else line - match = if flagX then pattern' == line' else pattern' `isInfixOf` line' + isMatchInLine = if flagX then pattern' == line' else pattern' `isInfixOf` line' res = (if multiple then fileName <> ":" else "") <> (if flagN then show lineNum <> ":" else "") <> diff --git a/exercises/practice/grep/package.yaml b/exercises/practice/grep/package.yaml index 59ae87e2b..3a33129ae 100644 --- a/exercises/practice/grep/package.yaml +++ b/exercises/practice/grep/package.yaml @@ -8,9 +8,6 @@ library: exposed-modules: Grep source-dirs: src ghc-options: -Wall - # dependencies: - # - foo # List here the packages you - # - bar # want to use in your solution. tests: test: @@ -19,3 +16,4 @@ tests: dependencies: - grep - hspec + - directory diff --git a/exercises/practice/grep/src/Grep.hs b/exercises/practice/grep/src/Grep.hs index 273b35b7e..a2cf7dda3 100644 --- a/exercises/practice/grep/src/Grep.hs +++ b/exercises/practice/grep/src/Grep.hs @@ -3,8 +3,6 @@ module Grep (grep, Flag(..)) where data Flag = N | L | I | V | X deriving (Eq, Ord) type Flags = [Flag] -type File = (String, [String]) -type Files = [File] -grep :: String -> Flags -> Files -> [String] +grep :: String -> Flags -> [FilePath] -> IO [String] grep pattern flags files = error "You need to implement this function." diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index 6f857f144..d5f93b3d1 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -1,8 +1,9 @@ {-# LANGUAGE RecordWildCards #-} import Data.Foldable (for_) -import Test.Hspec (Spec, describe, it, shouldBe) +import Test.Hspec (Spec, describe, it, shouldBe, around) import Test.Hspec.Runner (configFailFast, defaultConfig, hspecWith) +import System.Directory (getTemporaryDirectory, removeFile, setCurrentDirectory) import Grep (grep, Flag(..)) @@ -11,8 +12,18 @@ main = hspecWith defaultConfig {configFailFast = True} specs specs :: Spec specs = describe "grep" $ for_ cases test - where - test Case{..} = it description $ grep pattern flags files `shouldBe` expected + +test testCase@Case{..} = + around (withFiles files) $ + it description $ + \files -> grep pattern flags files >>= (\content -> content `shouldBe` expected) + +withFiles files runTest = do + sys <- getTemporaryDirectory + setCurrentDirectory sys + for_ files $ \file -> writeFile (fst file) (unlines $ snd file) + runTest $ map fst files + for_ files $ \file -> removeFile (fst file) data Case = Case { description :: String , pattern :: String From 23c6e5c065fe45666ecef58f20163c0cae410c07 Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 30 Nov 2025 11:49:46 +0100 Subject: [PATCH 06/11] Updated acrions/cache --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1b4c414e1..95294c7c2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -95,7 +95,7 @@ jobs: # most of the time caches should hit. echo "::set-output name=week-no::$(date -u +%Y-%U)" - - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + - uses: actions/cache@v4 id: cache with: path: | From f6aa82c331df0e74df812e1ed023b65f64d16e25 Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 30 Nov 2025 12:38:32 +0100 Subject: [PATCH 07/11] Rename pattern due to hlint finfing --- .../examples/success-standard/src/Grep.hs | 10 ++-- exercises/practice/grep/src/Grep.hs | 2 +- exercises/practice/grep/test/Tests.hs | 56 +++++++++---------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs index 1cc0214d8..6d47b38be 100644 --- a/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs +++ b/exercises/practice/grep/.meta/examples/success-standard/src/Grep.hs @@ -9,20 +9,20 @@ data Flag = N | L | I | V | X deriving (Eq, Ord) type Flags = [Flag] grep :: String -> Flags -> [FilePath] -> IO [String] -grep pattern flags files = do +grep pat flags files = do content <- mapM readFile' files let input = zip files (map lines content) - return $ grep' pattern flags input + return $ grep' pat flags input grep' :: String -> Flags -> [(String, [String])] -> [String] -grep' pattern flags files = concatMap grepInFile files +grep' pat flags files = concatMap grepInFile files where flagN = N `elem` flags flagL = L `elem` flags flagI = I `elem` flags flagV = V `elem` flags flagX = X `elem` flags - pattern' = if flagI then map toLower pattern else pattern + pat' = if flagI then map toLower pat else pat multiple = length files > 1 grepInFile (fileName, content) = if flagL && not (null matchInFile) then [fileName] else matchInFile where @@ -31,7 +31,7 @@ grep' pattern flags files = concatMap grepInFile files grepInLine (line, lineNum) = [res | if flagV then not isMatchInLine else isMatchInLine] where line' = if flagI then map toLower line else line - isMatchInLine = if flagX then pattern' == line' else pattern' `isInfixOf` line' + isMatchInLine = if flagX then pat' == line' else pat' `isInfixOf` line' res = (if multiple then fileName <> ":" else "") <> (if flagN then show lineNum <> ":" else "") <> diff --git a/exercises/practice/grep/src/Grep.hs b/exercises/practice/grep/src/Grep.hs index a2cf7dda3..112adbe68 100644 --- a/exercises/practice/grep/src/Grep.hs +++ b/exercises/practice/grep/src/Grep.hs @@ -5,4 +5,4 @@ data Flag = N | L | I | V | X deriving (Eq, Ord) type Flags = [Flag] grep :: String -> Flags -> [FilePath] -> IO [String] -grep pattern flags files = error "You need to implement this function." +grep pat flags files = error "You need to implement this function." diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index d5f93b3d1..f5f26bf9c 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -16,7 +16,7 @@ specs = describe "grep" $ for_ cases test test testCase@Case{..} = around (withFiles files) $ it description $ - \files -> grep pattern flags files >>= (\content -> content `shouldBe` expected) + \files -> grep pat flags files >>= (\content -> content `shouldBe` expected) withFiles files runTest = do sys <- getTemporaryDirectory @@ -26,7 +26,7 @@ withFiles files runTest = do for_ files $ \file -> removeFile (fst file) data Case = Case { description :: String - , pattern :: String + , pat :: String , flags :: [Flag] , files :: [(String, [String])] , expected :: [String] @@ -35,43 +35,43 @@ data Case = Case { description :: String cases :: [Case] cases = [ Case { description = "One file, one match, no flags" - , pattern = "Agamemnon" + , pat = "Agamemnon" , flags = [] , files = [iliad] , expected = ["Of Atreus, Agamemnon, King of men."] } , Case { description = "One file, one match, print line numbers flag" - , pattern = "Forbidden" + , pat = "Forbidden" , flags = [N] , files = [paradiseLost] , expected = ["2:Of that Forbidden Tree, whose mortal tast"] } , Case { description = "One file, one match, case-insensitive flag" - , pattern = "FORBIDDEN" + , pat = "FORBIDDEN" , flags = [I] , files = [paradiseLost] , expected = ["Of that Forbidden Tree, whose mortal tast"] } , Case { description = "One file, one match, print file names flag" - , pattern = "Forbidden" + , pat = "Forbidden" , flags = [L] , files = [paradiseLost] , expected = ["paradise-lost.txt"] } , Case { description = "One file, one match, match entire lines flag" - , pattern = "With loss of Eden, till one greater Man" + , pat = "With loss of Eden, till one greater Man" , flags = [X] , files = [paradiseLost] , expected = ["With loss of Eden, till one greater Man"] } , Case { description = "One file, one match, multiple flags" - , pattern = "OF ATREUS, Agamemnon, KIng of MEN." + , pat = "OF ATREUS, Agamemnon, KIng of MEN." , flags = [N, I, X] , files = [iliad] , expected = ["9:Of Atreus, Agamemnon, King of men."] } , Case { description = "One file, several matches, no flags" - , pattern = "may" + , pat = "may" , flags = [] , files = [midsummerNight] , expected = [ @@ -81,7 +81,7 @@ cases = ] } , Case { description = "One file, several matches, print line numbers flag" - , pattern = "may" + , pat = "may" , flags = [N] , files = [midsummerNight] , expected = [ @@ -91,13 +91,13 @@ cases = ] } , Case { description = "One file, several matches, match entire lines flag" - , pattern = "may" + , pat = "may" , flags = [X] , files = [midsummerNight] , expected = [] } , Case { description = "One file, several matches, case-insensitive flag" - , pattern = "ACHILLES" + , pat = "ACHILLES" , flags = [I] , files = [iliad] , expected = [ @@ -106,7 +106,7 @@ cases = ] } , Case { description = "One file, several matches, inverted flag" - , pattern = "Of" + , pat = "Of" , flags = [V] , files = [paradiseLost] , expected = [ @@ -118,19 +118,19 @@ cases = ] } , Case { description = "One file, no matches, various flags" - , pattern = "Gandalf" + , pat = "Gandalf" , flags = [N, L, X, I] , files = [iliad] , expected = [] } , Case { description = "One file, one match, file flag takes precedence over line flag" - , pattern = "ten" + , pat = "ten" , flags = [N, L] , files = [iliad] , expected = ["iliad.txt"] } , Case { description = "One file, several matches, inverted and match entire lines flags" - , pattern = "Illustrious into Ades premature," + , pat = "Illustrious into Ades premature," , flags = [X, V] , files = [iliad] , expected = [ @@ -145,13 +145,13 @@ cases = ] } , Case { description = "Multiple files, one match, no flags" - , pattern = "Agamemnon" + , pat = "Agamemnon" , flags = [] , files = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt:Of Atreus, Agamemnon, King of men."] } , Case { description = "Multiple files, several matches, no flags" - , pattern = "may" + , pat = "may" , flags = [] , files = [iliad, midsummerNight, paradiseLost] , expected = [ @@ -161,7 +161,7 @@ cases = ] } , Case { description = "Multiple files, several matches, print line numbers flag" - , pattern = "that" + , pat = "that" , flags = [N] , files = [iliad, midsummerNight, paradiseLost] , expected = [ @@ -172,13 +172,13 @@ cases = ] } , Case { description = "Multiple files, one match, print file names flag" - , pattern = "who" + , pat = "who" , flags = [L] , files = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt", "paradise-lost.txt"] } , Case { description = "Multiple files, several matches, case-insensitive flag" - , pattern = "TO" + , pat = "TO" , flags = [I] , files = [iliad, midsummerNight, paradiseLost] , expected = [ @@ -195,7 +195,7 @@ cases = ] } , Case { description = "Multiple files, several matches, inverted flag" - , pattern = "a" + , pat = "a" , flags = [V] , files = [iliad, midsummerNight, paradiseLost] , expected = [ @@ -205,31 +205,31 @@ cases = ] } , Case { description = "Multiple files, one match, match entire lines flag" - , pattern = "But I beseech your grace that I may know" + , pat = "But I beseech your grace that I may know" , flags = [X] , files = [iliad, midsummerNight, paradiseLost] , expected = ["midsummer-night.txt:But I beseech your grace that I may know"] } , Case { description = "Multiple files, one match, multiple flags" - , pattern = "WITH LOSS OF EDEN, TILL ONE GREATER MAN" + , pat = "WITH LOSS OF EDEN, TILL ONE GREATER MAN" , flags = [N, I, X] , files = [iliad, midsummerNight, paradiseLost] , expected = ["paradise-lost.txt:4:With loss of Eden, till one greater Man"] } , Case { description = "Multiple files, no matches, various flags" - , pattern = "Frodo" + , pat = "Frodo" , flags = [N, L, X, I] , files = [iliad, midsummerNight, paradiseLost] , expected = [] } , Case { description = "Multiple files, several matches, file flag takes precedence over line number flag" - , pattern = "who" + , pat = "who" , flags = [N, L] , files = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt", "paradise-lost.txt"] } , Case { description = "Multiple files, several matches, inverted and match entire lines flags" - , pattern = "Illustrious into Ades premature," + , pat = "Illustrious into Ades premature," , flags = [X, V] , files = [iliad, midsummerNight, paradiseLost] , expected = [ @@ -292,4 +292,4 @@ paradiseLost = "Sing Heav'nly Muse, that on the secret top", "Of Oreb, or of Sinai, didst inspire", "That Shepherd, who first taught the chosen Seed" - ]) \ No newline at end of file + ]) From e009a3325cd1fb0b2344bdbf53d1bca6104e52cf Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 30 Nov 2025 13:39:48 +0100 Subject: [PATCH 08/11] Rename pattern due to hlint finfing --- exercises/practice/grep/test/Tests.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index f5f26bf9c..89a3d1243 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -1,5 +1,6 @@ {-# LANGUAGE RecordWildCards #-} +import Control.Monad ((>=>)) import Data.Foldable (for_) import Test.Hspec (Spec, describe, it, shouldBe, around) import Test.Hspec.Runner (configFailFast, defaultConfig, hspecWith) @@ -16,7 +17,7 @@ specs = describe "grep" $ for_ cases test test testCase@Case{..} = around (withFiles files) $ it description $ - \files -> grep pat flags files >>= (\content -> content `shouldBe` expected) + \files -> (grep pat flags >=> (`shouldBe` expected)) files withFiles files runTest = do sys <- getTemporaryDirectory From ccbf8c7a77da74630e29a82f106211ff01f8ff11 Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 30 Nov 2025 14:28:19 +0100 Subject: [PATCH 09/11] Rename pattern due to hlint findings --- exercises/practice/grep/test/Tests.hs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index 89a3d1243..af3ed93cc 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -14,17 +14,15 @@ main = hspecWith defaultConfig {configFailFast = True} specs specs :: Spec specs = describe "grep" $ for_ cases test -test testCase@Case{..} = - around (withFiles files) $ - it description $ - \files -> (grep pat flags >=> (`shouldBe` expected)) files - -withFiles files runTest = do - sys <- getTemporaryDirectory - setCurrentDirectory sys - for_ files $ \file -> writeFile (fst file) (unlines $ snd file) - runTest $ map fst files - for_ files $ \file -> removeFile (fst file) +test testCase@Case{..} = around (withFiles files) $ it description $ + \files -> (grep pat flags >=> (`shouldBe` expected)) files + where + withFiles files runTest = do + sys <- getTemporaryDirectory + setCurrentDirectory sys + for_ files $ \file -> writeFile (fst file) (unlines $ snd file) + runTest $ map fst files + for_ files $ \file -> removeFile (fst file) data Case = Case { description :: String , pat :: String @@ -261,6 +259,7 @@ cases = } ] +iliad :: (String, [String]) iliad = ("iliad.txt", [ "Achilles sing, O Goddess! Peleus' son;", @@ -273,6 +272,8 @@ iliad = "The noble Chief Achilles from the son", "Of Atreus, Agamemnon, King of men." ]) + +midsummerNight :: (String, [String]) midsummerNight = ("midsummer-night.txt", [ "I do entreat your grace to pardon me.", @@ -283,6 +284,8 @@ midsummerNight = "The worst that may befall me in this case,", "If I refuse to wed Demetrius." ]) + +paradiseLost :: (String, [String]) paradiseLost = ("paradise-lost.txt", [ "Of Mans First Disobedience, and the Fruit", From a9c7f9c15b77746894f5823bdc78092b4ba4b208 Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 30 Nov 2025 15:20:16 +0100 Subject: [PATCH 10/11] Rename pattern due to hlint findings --- exercises/practice/grep/test/Tests.hs | 63 ++++++++++++++------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/exercises/practice/grep/test/Tests.hs b/exercises/practice/grep/test/Tests.hs index af3ed93cc..38d127773 100644 --- a/exercises/practice/grep/test/Tests.hs +++ b/exercises/practice/grep/test/Tests.hs @@ -14,20 +14,21 @@ main = hspecWith defaultConfig {configFailFast = True} specs specs :: Spec specs = describe "grep" $ for_ cases test -test testCase@Case{..} = around (withFiles files) $ it description $ +test :: Case -> Spec +test Case{..} = around (withFiles fileList) $ it description $ \files -> (grep pat flags >=> (`shouldBe` expected)) files where - withFiles files runTest = do + withFiles fl runTest = do sys <- getTemporaryDirectory setCurrentDirectory sys - for_ files $ \file -> writeFile (fst file) (unlines $ snd file) - runTest $ map fst files - for_ files $ \file -> removeFile (fst file) + for_ fl $ \file -> writeFile (fst file) (unlines $ snd file) + _ <- runTest $ map fst fl + for_ fl $ \file -> removeFile (fst file) data Case = Case { description :: String , pat :: String , flags :: [Flag] - , files :: [(String, [String])] + , fileList :: [(String, [String])] , expected :: [String] } @@ -36,43 +37,43 @@ cases = [ Case { description = "One file, one match, no flags" , pat = "Agamemnon" , flags = [] - , files = [iliad] + , fileList = [iliad] , expected = ["Of Atreus, Agamemnon, King of men."] } , Case { description = "One file, one match, print line numbers flag" , pat = "Forbidden" , flags = [N] - , files = [paradiseLost] + , fileList = [paradiseLost] , expected = ["2:Of that Forbidden Tree, whose mortal tast"] } , Case { description = "One file, one match, case-insensitive flag" , pat = "FORBIDDEN" , flags = [I] - , files = [paradiseLost] + , fileList = [paradiseLost] , expected = ["Of that Forbidden Tree, whose mortal tast"] } , Case { description = "One file, one match, print file names flag" , pat = "Forbidden" , flags = [L] - , files = [paradiseLost] + , fileList = [paradiseLost] , expected = ["paradise-lost.txt"] } , Case { description = "One file, one match, match entire lines flag" , pat = "With loss of Eden, till one greater Man" , flags = [X] - , files = [paradiseLost] + , fileList = [paradiseLost] , expected = ["With loss of Eden, till one greater Man"] } , Case { description = "One file, one match, multiple flags" , pat = "OF ATREUS, Agamemnon, KIng of MEN." , flags = [N, I, X] - , files = [iliad] + , fileList = [iliad] , expected = ["9:Of Atreus, Agamemnon, King of men."] } , Case { description = "One file, several matches, no flags" , pat = "may" , flags = [] - , files = [midsummerNight] + , fileList = [midsummerNight] , expected = [ "Nor how it may concern my modesty,", "But I beseech your grace that I may know", @@ -82,7 +83,7 @@ cases = , Case { description = "One file, several matches, print line numbers flag" , pat = "may" , flags = [N] - , files = [midsummerNight] + , fileList = [midsummerNight] , expected = [ "3:Nor how it may concern my modesty,", "5:But I beseech your grace that I may know", @@ -92,13 +93,13 @@ cases = , Case { description = "One file, several matches, match entire lines flag" , pat = "may" , flags = [X] - , files = [midsummerNight] + , fileList = [midsummerNight] , expected = [] } , Case { description = "One file, several matches, case-insensitive flag" , pat = "ACHILLES" , flags = [I] - , files = [iliad] + , fileList = [iliad] , expected = [ "Achilles sing, O Goddess! Peleus' son;", "The noble Chief Achilles from the son" @@ -107,7 +108,7 @@ cases = , Case { description = "One file, several matches, inverted flag" , pat = "Of" , flags = [V] - , files = [paradiseLost] + , fileList = [paradiseLost] , expected = [ "Brought Death into the World, and all our woe,", "With loss of Eden, till one greater Man", @@ -119,19 +120,19 @@ cases = , Case { description = "One file, no matches, various flags" , pat = "Gandalf" , flags = [N, L, X, I] - , files = [iliad] + , fileList = [iliad] , expected = [] } , Case { description = "One file, one match, file flag takes precedence over line flag" , pat = "ten" , flags = [N, L] - , files = [iliad] + , fileList = [iliad] , expected = ["iliad.txt"] } , Case { description = "One file, several matches, inverted and match entire lines flags" , pat = "Illustrious into Ades premature," , flags = [X, V] - , files = [iliad] + , fileList = [iliad] , expected = [ "Achilles sing, O Goddess! Peleus' son;", "His wrath pernicious, who ten thousand woes", @@ -146,13 +147,13 @@ cases = , Case { description = "Multiple files, one match, no flags" , pat = "Agamemnon" , flags = [] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt:Of Atreus, Agamemnon, King of men."] } , Case { description = "Multiple files, several matches, no flags" , pat = "may" , flags = [] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [ "midsummer-night.txt:Nor how it may concern my modesty,", "midsummer-night.txt:But I beseech your grace that I may know", @@ -162,7 +163,7 @@ cases = , Case { description = "Multiple files, several matches, print line numbers flag" , pat = "that" , flags = [N] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [ "midsummer-night.txt:5:But I beseech your grace that I may know", "midsummer-night.txt:6:The worst that may befall me in this case,", @@ -173,13 +174,13 @@ cases = , Case { description = "Multiple files, one match, print file names flag" , pat = "who" , flags = [L] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt", "paradise-lost.txt"] } , Case { description = "Multiple files, several matches, case-insensitive flag" , pat = "TO" , flags = [I] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [ "iliad.txt:Caused to Achaia's host, sent many a soul", "iliad.txt:Illustrious into Ades premature,", @@ -196,7 +197,7 @@ cases = , Case { description = "Multiple files, several matches, inverted flag" , pat = "a" , flags = [V] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [ "iliad.txt:Achilles sing, O Goddess! Peleus' son;", "iliad.txt:The noble Chief Achilles from the son", @@ -206,31 +207,31 @@ cases = , Case { description = "Multiple files, one match, match entire lines flag" , pat = "But I beseech your grace that I may know" , flags = [X] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = ["midsummer-night.txt:But I beseech your grace that I may know"] } , Case { description = "Multiple files, one match, multiple flags" , pat = "WITH LOSS OF EDEN, TILL ONE GREATER MAN" , flags = [N, I, X] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = ["paradise-lost.txt:4:With loss of Eden, till one greater Man"] } , Case { description = "Multiple files, no matches, various flags" , pat = "Frodo" , flags = [N, L, X, I] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [] } , Case { description = "Multiple files, several matches, file flag takes precedence over line number flag" , pat = "who" , flags = [N, L] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = ["iliad.txt", "paradise-lost.txt"] } , Case { description = "Multiple files, several matches, inverted and match entire lines flags" , pat = "Illustrious into Ades premature," , flags = [X, V] - , files = [iliad, midsummerNight, paradiseLost] + , fileList = [iliad, midsummerNight, paradiseLost] , expected = [ "iliad.txt:Achilles sing, O Goddess! Peleus' son;", "iliad.txt:His wrath pernicious, who ten thousand woes", From ce4fb736e4ebeda0e8ff52cb8aca41fd164baa4a Mon Sep 17 00:00:00 2001 From: tofische Date: Sun, 7 Dec 2025 09:24:36 +0100 Subject: [PATCH 11/11] Update workflows --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 95294c7c2..601a5bdcd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: name: Check track configuration runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 with: # main is needed in addition to HEAD, because the README check only # checks exercises changed since main. This fetches the entire repo's @@ -65,7 +65,7 @@ jobs: # and an admin only needs to make "Check exercises (lts-from-exercises)" required. - lts-from-exercises steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Install yq (for stack resolvers) run: | @@ -95,7 +95,7 @@ jobs: # most of the time caches should hit. echo "::set-output name=week-no::$(date -u +%Y-%U)" - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # V4.1.0 id: cache with: path: |