I'm working through the Crafting Interpreters book, and for extra fun, I'm playing with using Async Swift to coordinate the various parts together.
One example is the REPL. Fundamentally, it's just a function ot type (AsyncSequence<String>) -> AsyncSequence<String>
which takes input lines, and produces the evaluation results to be printed. AsyncSequence.map
seems like a good fit for this, but with one short-coming: The REPL should be able to print the prompt (e.g. "> "
) before the first line has been entered.
What's the correct way to do this transformation? I was able to make it with by pulling in AsyncAlgorithms for AsyncChannel
and combining that with a Task
. Is there a simpler way?
import AsyncAlgorithms
struct REPL { // I don't really need this struct, could just be a function I suppose.
let output = AsyncThrowingChannel<String, Error>()
init<S: AsyncSequence>(lineSource: S) where S: Sendable, S.Element == String {
Task { [output] in
let prompt = "> "
await output.send(prompt)
for try await line in lineSource {
// This is where the interpreter would actually evaluate the line
let result = "process(\(line))\n"
await output.send(result)
await output.send(prompt)
}
output.finish()
}
}
}
let repl = REPL(lineSource: FileHandle.standardInput.bytes.lines)
for try await output in repl.output {
print(output, terminator: "")
try FileHandle.standardOutput.synchronize()
}