Created
June 28, 2025 10:20
-
-
Save osa1/a5e7fdfa30d69125970c0797c525ede2 to your computer and use it in GitHub Desktop.
Koka example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code for the blog post: ... | |
// Tested with Koka 3.1.2. | |
import std/os/env | |
import std/os/file | |
import std/os/path | |
fun main() | |
val args = get-args() | |
val args-without-flags = args.remove(fn (arg) arg == "--test") | |
val run-test = args-without-flags.length != args.length | |
if run-test then | |
test() | |
else | |
run(args-without-flags) | |
fun run(args: list<string>): <exn, console, fsys>() | |
match args | |
Cons(pattern, files) -> | |
with fs-io | |
with console1-terminal | |
search(pattern, files) | |
Nil -> | |
println("USAGE: grep PATTERN FILE*") | |
fun search(pattern: string, files: list<string>): <fs, console1>() | |
val pattern-size = pattern.count() | |
files.foreach fn(file) | |
val contents = read-file(file.path) | |
val parts = contents.split(pattern) | |
report-matches(file, pattern-size, parts) | |
fun report-matches(file: string, pattern-size: int, parts: list<string>): <console1>() | |
if parts.length == 0 then | |
return () | |
println1(file) | |
var line := 0 | |
var column := 0 | |
parts.init.foreach fn(part) | |
part.vector.foreach fn(char) | |
if char == '\n' then | |
line := line + 1 | |
column := 0 | |
else | |
column := column + 1 | |
println1((line + 1).show ++ ":" ++ (column + 1).show) | |
effect fs | |
ctl read-file(path: path): string | |
effect console1 | |
ctl println1(s: string): () | |
val fs-io = handler | |
ctl read-file(path: path) | |
resume(read-text-file(path)) | |
val console1-terminal = handler | |
ctl println1(s: string) | |
println(s) | |
resume(()) | |
struct test-case | |
files: list<test-file> | |
pattern: string | |
expected-output: list<string> | |
struct test-file | |
path: path | |
contents: string | |
val test-cases: list<test-case> = [ | |
Test-case( | |
files = [Test-file("file1".path, "test\ntest"), Test-file("file2".path, "a\n test\nb")], | |
pattern = "test", | |
expected-output = ["file1", "1:1", "2:1", "file2", "2:2"] | |
), | |
] | |
fun test(): <exn>() | |
var printed-lines := Nil | |
test-cases.foreach fn (test) | |
with handler | |
ctl read-file(path_: path) | |
match test.files.find(fn (file) file.path.string == path_.string) | |
Just(file) -> resume(file.contents) | |
Nothing -> throw("file not found", ExnAssert) | |
with handler | |
ctl println1(s: string) | |
printed-lines := Cons(s, printed-lines) | |
resume(()) | |
search(test.pattern, test.files.map(fn (file) file.path.string)) | |
if printed-lines.reverse != test.expected-output then | |
throw("unexpected test output", ExnAssert) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment