Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the type of Stdin.line to Task [Input Str, End] * #114

Merged
merged 6 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions ci/expect_scripts/piping.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/expect

# uncomment line below for debugging
# exp_internal 1

set timeout 7

source ./ci/expect_scripts/shared-code.exp

# Spawn the command to pipe in the data to the script B
spawn bash -c "echo -e \"test\n123\" | ./examples/piping"

# Expect the output
expect -exact "I read 2 lines from stdin.\r\n" {
expect eof {
check_exit_and_segfault
}
}

puts stderr "\nError: output was different from expected value."
exit 1
12 changes: 7 additions & 5 deletions examples/echo.roc
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ app "echo"
main : Task {} I32
main =
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")
Task.loop {} \_ ->
Task.map tick Step

tick : Task {} I32
tick =
Task.loop {} tick

tick : {} -> Task [Step {}, Done {}] *
tick = \{} ->
shout <- Task.await Stdin.line

Stdout.line (echo shout)
when shout is
Input s -> Stdout.line (echo s) |> Task.map Step
End -> Stdout.line (echo "Received end of input (EOF).") |> Task.map Done

echo : Str -> Str
echo = \shout ->
Expand Down
10 changes: 9 additions & 1 deletion examples/form.roc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ main : Task {} I32
main =
_ <- await (Stdout.line "What's your first name?")
firstName <- await Stdin.line

_ <- await (Stdout.line "What's your last name?")
lastName <- await Stdin.line
Stdout.line "Hi, \(firstName) \(lastName)! 👋"

Stdout.line "Hi, \(unwrap firstName) \(unwrap lastName)! 👋"

unwrap : [Input Str, End] -> Str
unwrap = \input ->
when input is
Input line -> line
End -> "Received end of input (EOF)."
31 changes: 19 additions & 12 deletions examples/http-get.roc
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@ main : Task {} I32
main =
_ <- Task.await (Stdout.line "Enter a URL to fetch. It must contain a scheme like \"http://\" or \"https://\".")

url <- Task.await Stdin.line
input <- Task.await Stdin.line

request = {
method: Get,
headers: [],
url,
body: Http.emptyBody,
timeout: NoTimeout,
}
when input is
End ->
Stdout.line "I received end-of-input (EOF) instead of a URL."

output <- Http.send request
|> Task.onErr (\err -> err |> Http.errorToString |> Task.ok)
|> Task.await
Input url ->
request = {
method: Get,
headers: [],
url,
body: Http.emptyBody,
timeout: NoTimeout,
}

Stdout.line output
output <- Http.send request
|> Task.onErr \err -> err
|> Http.errorToString
|> Task.ok
|> Task.await

Stdout.line output
22 changes: 22 additions & 0 deletions examples/piping.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
app "piping"
packages { pf: "../src/main.roc" }
imports [
pf.Stdout,
pf.Stdin,
pf.Task.{ Task },
]
provides [main] to pf

# Try piping in some text like this: `echo -e "test\n123" | roc piping.roc`
main =
lines <- Task.loop 0 count |> Task.await
Stdout.line "I read \(Num.toStr lines) lines from stdin."

count = \n ->
result <- Stdin.line |> Task.await
state =
when result is
Input _ -> Step (n + 1)
End -> Done n
Task.ok state

7 changes: 6 additions & 1 deletion examples/tcp-client.roc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ tick : Tcp.Stream -> Task.Task {} _
tick = \stream ->
_ <- Stdout.write "> " |> await

outMsg <- Stdin.line |> await
input <- Stdin.line |> await

outMsg = when input is
End -> "Received end of input (EOF)."
Input msg -> msg

_ <- Tcp.writeUtf8 "\(outMsg)\n" stream |> await

inMsg <- Tcp.readLine stream |> await
Expand Down
2 changes: 1 addition & 1 deletion src/Effect.roc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ stdoutLine : Str -> Effect {}
stdoutWrite : Str -> Effect {}
stderrLine : Str -> Effect {}
stderrWrite : Str -> Effect {}
stdinLine : Effect Str
stdinLine : Effect (Result Str {})
stdinBytes : Effect (List U8)
ttyModeCanonical : Effect {}
ttyModeRaw : Effect {}
Expand Down
7 changes: 5 additions & 2 deletions src/Stdin.roc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ interface Stdin
## (e.g. because the user pressed Enter in the terminal), so using it can result in the appearance of the
## programming having gotten stuck. It's often helpful to print a prompt first, so
## the user knows it's necessary to enter something before the program will continue.
line : Task Str *
line : Task [Input Str, End] *
line =
Effect.stdinLine
|> Effect.map Ok
|> Effect.map \r ->
when r is
Ok str -> Ok (Input str)
Err _ -> Ok End
|> InternalTask.fromEffect

## Read bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)).
Expand Down
24 changes: 13 additions & 11 deletions src/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,8 @@ pub fn init() {
];
std::mem::forget(std::hint::black_box(funcs));
if cfg!(unix) {
let unix_funcs: &[*const extern "C" fn()] = &[
roc_getppid as _,
roc_mmap as _,
roc_shm_open as _,
];
let unix_funcs: &[*const extern "C" fn()] =
&[roc_getppid as _, roc_mmap as _, roc_shm_open as _];
std::mem::forget(std::hint::black_box(unix_funcs));
}
}
Expand Down Expand Up @@ -350,11 +347,14 @@ pub extern "C" fn roc_fx_exePath(_roc_str: &RocStr) -> RocResult<RocList<u8>, ()
}

#[no_mangle]
pub extern "C" fn roc_fx_stdinLine() -> RocStr {
pub extern "C" fn roc_fx_stdinLine() -> RocResult<RocStr, ()> { // () is used for EOF
let stdin = std::io::stdin();
let line1 = stdin.lock().lines().next().unwrap().unwrap();

RocStr::from(line1.as_str())
match stdin.lock().lines().next() {
None => RocResult::err(()),
Some(Ok(str)) => RocResult::ok(RocStr::from(str.as_str())),
Some(Err(err)) => panic!("Failed to get next line from stdin:\n\t{:?}", err),
}
}

#[no_mangle]
Expand Down Expand Up @@ -516,10 +516,12 @@ pub extern "C" fn roc_fx_sleepMillis(milliseconds: u64) {
}

#[no_mangle]
pub extern "C" fn roc_fx_dirList(_roc_path: &RocList<u8>) -> RocResult<RocList<RocList<u8>>, IOError> {
// match std::fs::read_dir(path_from_roc_path(roc_path)) {
pub extern "C" fn roc_fx_dirList(
_roc_path: &RocList<u8>,
) -> RocResult<RocList<RocList<u8>>, IOError> {
// match std::fs::read_dir(path_from_roc_path(roc_path)) {
// Ok(dir_entries) => {

// let entries = dir_entries
// .filter_map(|opt_dir_entry| match opt_dir_entry {
// Ok(entry) => Some(os_str_to_roc_path(entry.path().into_os_string().as_os_str())),
Expand Down
Loading