Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a5820ba
Add Grep exercise
tofische Dec 21, 2025
512a17c
Updated config.json to reference proper package.yaml
tofische May 13, 2026
96d632e
Use exemplar instead of example for concept exercises.
tofische May 14, 2026
439c85a
bin/verify-exercises-in-docker script (#1295)
keiravillekode May 14, 2026
c63f49e
Fix typo in TESTS.md (#1284)
BNAndras May 14, 2026
ae3df78
build(deps): bump actions/checkout from 4.1.7 to 6.0.2 (#1281)
dependabot[bot] May 15, 2026
058ba41
ci uses ubuntu-24.04 (#1290)
keiravillekode May 15, 2026
6be2f84
build(deps): bump actions/cache from 4.3.0 to 5.0.5 (#1280)
dependabot[bot] May 15, 2026
68a7b4a
Rework Bools concept (#1278)
meatball133 May 15, 2026
390f26b
Use `Test.Hspec.Runner` to run concept tests, matching what is done f…
IsaacG May 16, 2026
08aa5ab
Merge branch 'main' into grep-exercise
tofische Jun 14, 2026
c61824b
Add grep to config.json
tofische Jun 21, 2026
3d8c9a3
build(deps): bump actions/cache from 4.3.0 to 5.0.5 (#1280)
dependabot[bot] May 15, 2026
2688cb8
Satisfy hlint
tofische Jun 21, 2026
7e17db3
verify-exercises-in-docker: avoid cleanup failure (#1303)
keiravillekode Jun 18, 2026
1b4ad32
Bump resolver to `lts-22.44` to match the test runner (#1304)
IsaacG Jun 18, 2026
7b97a34
build(deps): bump actions/cache from 4.3.0 to 5.0.5 (#1280)
dependabot[bot] May 15, 2026
a94309f
build(deps): bump actions/cache from 4.3.0 to 5.0.5 (#1280)
dependabot[bot] May 15, 2026
6290bd3
Update resolver
tofische Jun 22, 2026
81378df
Satisfy hlint
tofische Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/verify-exercises-in-docker
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ run_tests() {
--rm \
--network none \
--mount type=bind,src="${PWD}",dst=/solution \
--mount type=volume,dst=/solution/.stack-work \
--mount type=bind,src="${PWD}",dst=/output \
--mount type=tmpfs,dst=/tmp \
"${image}" "${slug}" /solution /output
Expand Down
11 changes: 11 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,17 @@
"topics": [
"strings"
]
},
{
"slug": "grep",
"name": "Grep",
"uuid": "d6535287-ddb0-493d-85ee-9923cdc59955",
"practices": [],
"prerequisites": [],
"difficulty": 7,
"topics": [
"io_monad"
]
}
],
"foregone": [
Expand Down
2 changes: 1 addition & 1 deletion exercises/concept/guessing-game/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/concept/lucians-luscious-lasagna/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/concept/pacman-rules/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/concept/temperature/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/concept/valentines-day/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/accumulate/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/acronym/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/affine-cipher/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/all-your-base/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/allergies/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/alphametics/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/anagram/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/armstrong-numbers/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/atbash-cipher/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/bank-account/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/beer-song/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/binary-search-tree/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/binary-search/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/binary/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/bob/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/book-store/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/bowling/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/change/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/clock/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/collatz-conjecture/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/complex-numbers/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/connect/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/crypto-square/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/custom-set/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/darts/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/diamond/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/difference-of-squares/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/dnd-character/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/dominoes/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/etl/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/flower-field/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/food-chain/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/forth/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/game-of-life/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/gigasecond/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/go-counting/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/grade-school/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
2 changes: 1 addition & 1 deletion exercises/practice/grains/stack.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolver: lts-20.18
resolver: lts-22.44
3 changes: 3 additions & 0 deletions exercises/practice/grep/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Instructions append

To simplify the exercise the flags are already parsed.
27 changes: 27 additions & 0 deletions exercises/practice/grep/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions exercises/practice/grep/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: grep

dependencies:
- base

library:
exposed-modules: Grep
source-dirs: src

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- grep
- hspec
- directory
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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]

grep :: String -> Flags -> [FilePath] -> IO [String]
grep string flags files = do
content <- mapM readFile' files
let input = zip files (map lines content)
return $ grep' string flags input

grep' :: String -> Flags -> [(String, [String])] -> [String]
grep' string 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
string' = if flagI then map toLower string else string
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 isMatchInLine else isMatchInLine]
where
line' = if flagI then map toLower line else line
isMatchInLine = if flagX then string' == line' else string' `isInfixOf` line'
res =
(if multiple then fileName <> ":" else "") <>
(if flagN then show lineNum <> ":" else "") <>
line
85 changes: 85 additions & 0 deletions exercises/practice/grep/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
19 changes: 19 additions & 0 deletions exercises/practice/grep/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: grep
version: 1.0.0.0

dependencies:
- base

library:
exposed-modules: Grep
source-dirs: src
ghc-options: -Wall

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- grep
- hspec
- directory
8 changes: 8 additions & 0 deletions exercises/practice/grep/src/Grep.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Grep (grep, Flag(..)) where

data Flag = N | L | I | V | X deriving (Eq, Ord)

type Flags = [Flag]

grep :: String -> Flags -> [FilePath] -> IO [String]
grep string flags files = error "You need to implement this function."
1 change: 1 addition & 0 deletions exercises/practice/grep/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolver: lts-22.44
Loading
Loading