mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Echo example in CLI
This commit is contained in:
parent
659966c878
commit
1de0918ba9
14 changed files with 59 additions and 188 deletions
2
examples/cli/.gitignore
vendored
2
examples/cli/.gitignore
vendored
|
@ -1 +1 @@
|
||||||
quicksort
|
echo
|
||||||
|
|
12
examples/cli/Echo.roc
Normal file
12
examples/cli/Echo.roc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
app "echo"
|
||||||
|
packages { base: "platform/" }
|
||||||
|
imports [ base.Task.{ after }, base.Stdout, base.Stdin ]
|
||||||
|
provides [ main ] to base
|
||||||
|
|
||||||
|
main : Task.Task {} *
|
||||||
|
main =
|
||||||
|
after (Stdout.line "What's your first name?") \{} ->
|
||||||
|
after Stdin.line \firstName ->
|
||||||
|
after (Stdout.line "What's your last name?") \{} ->
|
||||||
|
after Stdin.line \lastName ->
|
||||||
|
Stdout.line "Hi, \(firstName) \(lastName)!"
|
|
@ -1,14 +0,0 @@
|
||||||
app "cli-example"
|
|
||||||
packages { base: "platform" }
|
|
||||||
imports [ base.Task.{ Task, after }, base.Stdout ]
|
|
||||||
provides [ main ] to base
|
|
||||||
|
|
||||||
main : Task.Task {} *
|
|
||||||
main =
|
|
||||||
Stdout.write "Hello, World!"
|
|
||||||
# TODO accept args : List Str
|
|
||||||
#when Path.fromStr "Cargo.toml" is
|
|
||||||
# Ok path ->
|
|
||||||
# Task.after (Task.putLine "Our Cargo.toml:") \_ ->
|
|
||||||
# Task.after (File.readUtf8 path) (\line -> Task.putLine line)
|
|
||||||
# _ -> Task.putLine "invalid path"
|
|
|
@ -1,19 +0,0 @@
|
||||||
# CLI
|
|
||||||
|
|
||||||
To run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cargo run run Main.roc
|
|
||||||
```
|
|
||||||
|
|
||||||
To run with an optimized build of the Roc application (so the compiled app runs faster):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cargo run run --optimize Main.roc
|
|
||||||
```
|
|
||||||
|
|
||||||
To run with a release build of the `roc` compiler (so the compiler itself runs faster):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cargo run --release run Main.roc
|
|
||||||
```
|
|
BIN
examples/cli/cli-example
Executable file
BIN
examples/cli/cli-example
Executable file
Binary file not shown.
|
@ -1,83 +0,0 @@
|
||||||
interface File
|
|
||||||
exposes [ FileReadErr, FileOpenErr, FileWriteErr, DirReadErr, readUtf8 ]
|
|
||||||
imports [ Task.{ Task }, Effect.{ after }, Path ]
|
|
||||||
|
|
||||||
Path : Path.Path
|
|
||||||
|
|
||||||
# These various file errors come from the POSIX errno values - see
|
|
||||||
# http://www.virtsync.com/c-error-codes-include-errno for the actual codes, and
|
|
||||||
# https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html for documentation
|
|
||||||
#
|
|
||||||
# The goal of this design is:
|
|
||||||
# * Whenever a function returns a `Task`, that task's error type represents all the errors that could happen.
|
|
||||||
# * The errors are union-friendly; if I run a task that reads, and then another that writes, I should get all the read *and* write errors.
|
|
||||||
# * To make the errors friendlier to chaining, they should always include the `Path` of the attempted operation. This way it's possible to tell which one failed after the fact.
|
|
||||||
|
|
||||||
|
|
||||||
## These errors can happen when opening a file, before attempting to read from
|
|
||||||
## it or write to it. The #FileReadErr and #FileWriteErr tag unions begin with
|
|
||||||
## these tags and then add more specific ones.
|
|
||||||
FileOpenErr a :
|
|
||||||
[
|
|
||||||
FileNotFound Path,
|
|
||||||
PermissionDenied Path,
|
|
||||||
SymLinkLoop Path,
|
|
||||||
TooManyOpenFiles Path,
|
|
||||||
IoError Path,
|
|
||||||
UnknownError I64 Path,
|
|
||||||
]a
|
|
||||||
|
|
||||||
## Errors when attempting to read a non-directory file.
|
|
||||||
FileReadErr a :
|
|
||||||
FileOpenErr
|
|
||||||
[
|
|
||||||
FileWasDir Path,
|
|
||||||
InvalidSeek Path,
|
|
||||||
IllegalByteSequence Path,
|
|
||||||
FileBusy Path,
|
|
||||||
]a
|
|
||||||
|
|
||||||
## Errors when attempting to read a directory.
|
|
||||||
DirReadErr a :
|
|
||||||
FileOpenErr
|
|
||||||
[
|
|
||||||
FileWasNotDir Path,
|
|
||||||
]a
|
|
||||||
|
|
||||||
## Errors when attempting to write a non-directory file.
|
|
||||||
FileWriteErr a :
|
|
||||||
FileOpenErr
|
|
||||||
[
|
|
||||||
FileWasDir Path,
|
|
||||||
ReadOnlyFileSystem Path,
|
|
||||||
]a
|
|
||||||
|
|
||||||
|
|
||||||
## Read a file's raw bytes
|
|
||||||
#readBytes : Path -> Task (List U8) (FileReadErr *)
|
|
||||||
#readBytes = \path ->
|
|
||||||
# Effect.readBytes (Path.toStr path)
|
|
||||||
|
|
||||||
## Read a file's bytes and interpret them as UTF-8 encoded text.
|
|
||||||
readUtf8 : Path -> Task.Task Str (FileReadErr [ BadUtf8 ]*)
|
|
||||||
readUtf8 = \path ->
|
|
||||||
Effect.map (Effect.readAllUtf8 (Path.toStr path)) \answer ->
|
|
||||||
# errno values - see
|
|
||||||
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
|
|
||||||
when answer.errno is
|
|
||||||
0 -> Ok answer.bytes # TODO use Str.fromUtf8 to validate a byte list as UTF-8 and return (Err BadUtf8) if validation fails
|
|
||||||
1 -> Err (PermissionDenied path)
|
|
||||||
2 -> Err (FileNotFound path)
|
|
||||||
19 -> Err (FileWasDir path)
|
|
||||||
# TODO handle other errno scenarios that could come up
|
|
||||||
_ -> Err (UnknownError answer.errno path)
|
|
||||||
|
|
||||||
## Read a file's bytes, one chunk at a time, and use it to build up a state.
|
|
||||||
##
|
|
||||||
## After each chunk is read, it gets passed to a callback which builds up a
|
|
||||||
## state - optionally while running other tasks.
|
|
||||||
#readChunks : Path, U64, state, (state, List U8 -> Task state []err) -> Task state (FileReadErr err)
|
|
||||||
|
|
||||||
## Like #readChunks except after each chunk you can either `Continue`,
|
|
||||||
## specifying how many bytes you'd like to read next, or `Stop` early.
|
|
||||||
#readChunksOrStop : Path, U64, state, (state, List U8 -> [ Continue U64 (Task state []err), Stop (Task state []err) ]) -> Task state (FileReadErr err)
|
|
|
@ -1,16 +0,0 @@
|
||||||
interface Path
|
|
||||||
exposes [ Path, fromStr, toStr ]
|
|
||||||
imports []
|
|
||||||
|
|
||||||
|
|
||||||
Path : [ @Path Str ]
|
|
||||||
|
|
||||||
|
|
||||||
fromStr : Str -> Result Path [ MalformedPath ]*
|
|
||||||
fromStr = \str ->
|
|
||||||
# TODO actually validate the path - may want a Parser for this!
|
|
||||||
Ok (@Path str)
|
|
||||||
|
|
||||||
toStr : Path -> Str
|
|
||||||
toStr = \@Path str ->
|
|
||||||
str
|
|
|
@ -1,15 +1,16 @@
|
||||||
platform rtfeldman/roc-cli
|
platform folkertdev/foo
|
||||||
requires { main : Task {} [] }
|
requires { main : ThisIsTotallyIgnoredApparently } # TODO FIXME
|
||||||
exposes []
|
exposes [] # TODO FIXME actually expose modules
|
||||||
packages {}
|
packages {}
|
||||||
imports [ Task.{ Task }, File ]
|
imports [ Task ] # TODO FIXME Task.{ Task }
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects Effect
|
effects Effect
|
||||||
{
|
{
|
||||||
#readAllUtf8 : Str -> Effect { errno : I32, bytes : List U8 },
|
putChar : I64 -> Effect {},
|
||||||
putLine : Str -> Effect {},
|
putLine : Str -> Effect {},
|
||||||
getLine : Effect Str
|
getLine : Effect Str
|
||||||
}
|
}
|
||||||
|
|
||||||
mainForHost : Effect {} as Fx
|
|
||||||
|
mainForHost : Task.Task {} * as Fx # TODO FIXME Task.Task {} []
|
||||||
mainForHost = main
|
mainForHost = main
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
# Rebuilding the host from source
|
|
||||||
|
|
||||||
Run `build.sh` to manually rebuild this platform's host.
|
|
||||||
|
|
||||||
Note that the compiler currently has its own logic for rebuilding these hosts
|
|
||||||
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
|
||||||
hosts will be precompiled by platform authors and distributed in packages,
|
|
||||||
at which point only package authors will need to think about rebuilding hosts.
|
|
6
examples/cli/platform/Stdin.roc
Normal file
6
examples/cli/platform/Stdin.roc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
interface Stdin
|
||||||
|
exposes [ line ]
|
||||||
|
imports [ Effect, Task ]
|
||||||
|
|
||||||
|
line : Task.Task Str *
|
||||||
|
line = Effect.after Effect.getLine Task.always # TODO FIXME Effect.getLine should suffice
|
|
@ -1,6 +1,6 @@
|
||||||
interface Stdout
|
interface Stdout
|
||||||
exposes [ write ]
|
exposes [ line ]
|
||||||
imports [ Task.{ Task }, Effect.{ Effect } ]
|
imports [ Effect, Task ] # TODO FIXME Task.{ Task }
|
||||||
|
|
||||||
write : Str -> Effect {} #Task {} *
|
line : Str -> Task.Task {} *
|
||||||
write = \str -> Effect.putLine st
|
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
||||||
|
|
|
@ -1,34 +1,31 @@
|
||||||
interface Task
|
interface Task
|
||||||
exposes [ Task, succeed, fail, after, map, putLine ]
|
exposes [ Task, after, always, fail, map, putLine, getLine ]
|
||||||
imports [ Effect ]
|
imports [ Effect ]
|
||||||
|
|
||||||
|
Task a err : Effect.Effect (Result a err)
|
||||||
|
|
||||||
Task ok err : Effect.Effect (Result ok err)
|
always : a -> Task a *
|
||||||
|
always = \x -> Effect.always (Ok x)
|
||||||
|
|
||||||
succeed : val -> Task val *
|
|
||||||
succeed = \val ->
|
|
||||||
Effect.always (Ok val)
|
|
||||||
|
|
||||||
|
|
||||||
fail : err -> Task * err
|
fail : err -> Task * err
|
||||||
fail = \val ->
|
fail = \x -> Effect.always (Err x)
|
||||||
Effect.always (Err val)
|
|
||||||
|
|
||||||
|
getLine : Task Str *
|
||||||
after : Task a err, (a -> Task b err) -> Task b err
|
getLine = Effect.after Effect.getLine always
|
||||||
after = \effect, transform ->
|
|
||||||
Effect.after effect \result ->
|
|
||||||
when result is
|
|
||||||
Ok a -> transform a
|
|
||||||
Err err -> Task.fail err
|
|
||||||
|
|
||||||
map : Task a err, (a -> b) -> Task b err
|
|
||||||
map = \effect, transform ->
|
|
||||||
Effect.after effect \result ->
|
|
||||||
when result is
|
|
||||||
Ok a -> Task.succeed (transform a)
|
|
||||||
Err err -> Effect.always (Err err) # Task.fail err does not work. WEIRD!
|
|
||||||
|
|
||||||
putLine : Str -> Task {} *
|
putLine : Str -> Task {} *
|
||||||
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||||
|
|
||||||
|
map : Task a err, (a -> b) -> Task b err
|
||||||
|
map = \task, transform ->
|
||||||
|
Effect.map task \res ->
|
||||||
|
when res is
|
||||||
|
Ok x -> Ok (transform x)
|
||||||
|
Err e -> Err e
|
||||||
|
|
||||||
|
after : Task a err, (a -> Task b err) -> Task b err
|
||||||
|
after = \task, transform ->
|
||||||
|
Effect.after task \res ->
|
||||||
|
when res is
|
||||||
|
Ok x -> transform x
|
||||||
|
Err e -> Task.fail e
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# compile c_host.o and rust_host.o
|
|
||||||
clang -c host.c -o c_host.o
|
|
||||||
rustc host.rs -o rust_host.o
|
|
||||||
|
|
||||||
# link them together into host.o
|
|
||||||
ld -r c_host.o rust_host.o -o host.o
|
|
||||||
|
|
||||||
# clean up
|
|
||||||
rm -f c_host.o
|
|
||||||
rm -f rust_host.o
|
|
|
@ -28,6 +28,14 @@ extern "C" {
|
||||||
fn size_Fx_result() -> i64;
|
fn size_Fx_result() -> i64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn roc_fx_putChar(foo: i64) -> () {
|
||||||
|
let character = foo as u8 as char;
|
||||||
|
print!("{}", character);
|
||||||
|
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_putLine(line: RocStr) -> () {
|
pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||||
let bytes = line.as_slice();
|
let bytes = line.as_slice();
|
||||||
|
@ -78,7 +86,6 @@ pub fn rust_main() -> isize {
|
||||||
let size = unsafe { roc_main_size() } as usize;
|
let size = unsafe { roc_main_size() } as usize;
|
||||||
let layout = Layout::array::<u8>(size).unwrap();
|
let layout = Layout::array::<u8>(size).unwrap();
|
||||||
let answer = unsafe {
|
let answer = unsafe {
|
||||||
// TODO if this is 1024B or less, put it in a global scratch buffer
|
|
||||||
let buffer = std::alloc::alloc(layout);
|
let buffer = std::alloc::alloc(layout);
|
||||||
|
|
||||||
roc_main(buffer);
|
roc_main(buffer);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue