mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge remote-tracking branch 'origin/main' into https-packages
This commit is contained in:
commit
99f55d6ed6
@ -405,6 +405,9 @@ pub fn to_type2<'a>(
|
||||
|
||||
Type2::Variable(var)
|
||||
}
|
||||
Tuple { fields: _, ext: _ } => {
|
||||
todo!("tuple type");
|
||||
}
|
||||
Record { fields, ext, .. } => {
|
||||
let field_types_map =
|
||||
can_assigned_fields(env, scope, references, &fields.items, region);
|
||||
|
@ -1056,50 +1056,29 @@ mod cli_run {
|
||||
&[],
|
||||
indoc!(
|
||||
r#"
|
||||
── TYPE MISMATCH ─ ...known_bad/../../../../examples/cli/cli-platform/main.roc ─
|
||||
── TYPE MISMATCH ─────────────────────────────── tests/known_bad/TypeError.roc ─
|
||||
|
||||
Something is off with the type annotation of the main required symbol:
|
||||
This 2nd argument to attempt has an unexpected type:
|
||||
|
||||
2│ requires {} { main : InternalProgram }
|
||||
^^^^^^^^^^^^^^^
|
||||
15│> Task.attempt task /result ->
|
||||
16│> when result is
|
||||
17│> Ok {} -> Stdout.line "Done!"
|
||||
18│> # Type mismatch because the File.readUtf8 error case is not handled
|
||||
19│> Err {} -> Stdout.line "Problem!"
|
||||
|
||||
This #UserApp.main value is a:
|
||||
The argument is an anonymous function of type:
|
||||
|
||||
Task.Task {} * [Write [Stdout]]
|
||||
[Err {}a, Ok {}] -> Task {} *
|
||||
|
||||
But the type annotation on main says it should be:
|
||||
But attempt needs its 2nd argument to be:
|
||||
|
||||
InternalProgram.InternalProgram
|
||||
|
||||
Tip: Type comparisons between an opaque type are only ever equal if
|
||||
both types are the same opaque type. Did you mean to create an opaque
|
||||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||||
an instance of this opaque type by doing @Age 23.
|
||||
|
||||
|
||||
── TYPE MISMATCH ─ ...known_bad/../../../../examples/cli/cli-platform/main.roc ─
|
||||
|
||||
This 1st argument to toEffect has an unexpected type:
|
||||
|
||||
9│ mainForHost = InternalProgram.toEffect main
|
||||
^^^^
|
||||
|
||||
This #UserApp.main value is a:
|
||||
|
||||
Task.Task {} * [Write [Stdout]]
|
||||
|
||||
But toEffect needs its 1st argument to be:
|
||||
|
||||
InternalProgram.InternalProgram
|
||||
|
||||
Tip: Type comparisons between an opaque type are only ever equal if
|
||||
both types are the same opaque type. Did you mean to create an opaque
|
||||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||||
an instance of this opaque type by doing @Age 23.
|
||||
Result {} [FileReadErr Path.Path InternalFile.ReadErr,
|
||||
FileReadUtf8Err Path.Path [BadUtf8 Utf8ByteProblem Nat]*]* ->
|
||||
Task {} *
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
2 errors and 1 warning found in <ignored for test> ms."#
|
||||
1 error and 0 warnings found in <ignored for test> ms."#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
app "type-error"
|
||||
packages { pf: "../../../../examples/cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Program]
|
||||
imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Path, pf.File]
|
||||
provides [main] to pf
|
||||
|
||||
main =
|
||||
task =
|
||||
_ <- await (line "a")
|
||||
_ <- await (line "b")
|
||||
_ <- await (line "c")
|
||||
_ <- await (line "d")
|
||||
_ <- await (File.readUtf8 (Path.fromStr "blah.txt"))
|
||||
line "e"
|
||||
# Type mismatch because this line is missing:
|
||||
# |> Program.quick
|
||||
|
||||
Task.attempt task \result ->
|
||||
when result is
|
||||
Ok {} -> Stdout.line "Done!"
|
||||
# Type mismatch because the File.readUtf8 error case is not handled
|
||||
Err {} -> Stdout.line "Problem!"
|
||||
|
@ -448,6 +448,9 @@ pub fn find_type_def_symbols(
|
||||
As(actual, _, _) => {
|
||||
stack.push(&actual.value);
|
||||
}
|
||||
Tuple { fields: _, ext: _ } => {
|
||||
todo!("find_type_def_symbols: Tuple");
|
||||
}
|
||||
Record { fields, ext } => {
|
||||
let mut inner_stack = Vec::with_capacity(fields.items.len());
|
||||
|
||||
@ -869,6 +872,9 @@ fn can_annotation_help(
|
||||
}
|
||||
}
|
||||
|
||||
Tuple { fields: _, ext: _ } => {
|
||||
todo!("tuple");
|
||||
}
|
||||
Record { fields, ext } => {
|
||||
let ext_type = can_extension_type(
|
||||
env,
|
||||
|
@ -176,6 +176,15 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||
annot.is_multiline() || has_clauses.iter().any(|has| has.is_multiline())
|
||||
}
|
||||
|
||||
Tuple { fields, ext } => {
|
||||
match ext {
|
||||
Some(ann) if ann.value.is_multiline() => return true,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fields.items.iter().any(|field| field.value.is_multiline())
|
||||
}
|
||||
|
||||
Record { fields, ext } => {
|
||||
match ext {
|
||||
Some(ann) if ann.value.is_multiline() => return true,
|
||||
@ -297,6 +306,14 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
Tuple { fields, ext } => {
|
||||
fmt_collection(buf, indent, Braces::Round, *fields, newlines);
|
||||
|
||||
if let Some(loc_ext_ann) = *ext {
|
||||
loc_ext_ann.value.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
Record { fields, ext } => {
|
||||
fmt_collection(buf, indent, Braces::Curly, *fields, newlines);
|
||||
|
||||
|
@ -787,6 +787,10 @@ impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
),
|
||||
TypeAnnotation::Tuple { fields, ext } => TypeAnnotation::Tuple {
|
||||
fields: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
|
||||
fields: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
|
@ -533,6 +533,13 @@ pub enum TypeAnnotation<'a> {
|
||||
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
fields: Collection<'a, Loc<TypeAnnotation<'a>>>,
|
||||
/// The row type variable in an open record, e.g. the `r` in `{ name: Str }r`.
|
||||
/// This is None if it's a closed record annotation like `{ name: Str }`.
|
||||
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
|
||||
},
|
||||
|
||||
/// A tag union, e.g. `[
|
||||
TagUnion {
|
||||
/// The row type variable in an open tag union, e.g. the `a` in `[Foo, Bar]a`.
|
||||
|
@ -676,6 +676,9 @@ pub enum ETypeTagUnion<'a> {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ETypeInParens<'a> {
|
||||
/// e.g. (), which isn't a valid type
|
||||
Empty(Position),
|
||||
|
||||
End(Position),
|
||||
Open(Position),
|
||||
///
|
||||
|
@ -116,7 +116,7 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_inferred(),
|
||||
specialize(EType::TInParens, loc_type_in_parens()),
|
||||
specialize(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
|
||||
loc!(specialize(EType::TRecord, record_type(stop_at_surface_has))),
|
||||
loc!(specialize(
|
||||
EType::TTagUnion,
|
||||
@ -185,7 +185,7 @@ fn loc_applied_arg<'a>(
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_inferred(),
|
||||
specialize(EType::TInParens, loc_type_in_parens()),
|
||||
specialize(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
|
||||
loc!(specialize(EType::TRecord, record_type(stop_at_surface_has))),
|
||||
loc!(specialize(
|
||||
EType::TTagUnion,
|
||||
@ -206,16 +206,45 @@ fn loc_applied_arg<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_type_in_parens<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
|
||||
between!(
|
||||
fn loc_type_in_parens<'a>(
|
||||
stop_at_surface_has: bool,
|
||||
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
|
||||
then(
|
||||
loc!(and!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'(', ETypeInParens::Open),
|
||||
space0_around_ee(
|
||||
specialize_ref(ETypeInParens::Type, expression(true, false)),
|
||||
ETypeInParens::IndentOpen,
|
||||
word1(b',', ETypeInParens::End),
|
||||
word1(b')', ETypeInParens::End),
|
||||
ETypeInParens::Open,
|
||||
ETypeInParens::IndentEnd,
|
||||
TypeAnnotation::SpaceBefore
|
||||
),
|
||||
word1(b')', ETypeInParens::IndentEnd)
|
||||
optional(allocated(specialize_ref(
|
||||
ETypeInParens::Type,
|
||||
term(stop_at_surface_has)
|
||||
)))
|
||||
)),
|
||||
|_arena, state, progress, item| {
|
||||
let Loc {
|
||||
region,
|
||||
value: (fields, ext),
|
||||
} = item;
|
||||
if fields.len() > 1 || ext.is_some() {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Loc::at(region, TypeAnnotation::Tuple { fields, ext }),
|
||||
state,
|
||||
))
|
||||
} else if fields.len() == 1 {
|
||||
Ok((MadeProgress, fields.items[0], state))
|
||||
} else {
|
||||
debug_assert!(fields.is_empty());
|
||||
Err((progress, ETypeInParens::Empty(state.pos())))
|
||||
}
|
||||
},
|
||||
)
|
||||
.trace("type_annotation:type_in_parens")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -322,29 +351,24 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
|
||||
fn record_type<'a>(
|
||||
stop_at_surface_has: bool,
|
||||
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
|
||||
use crate::type_annotation::TypeAnnotation::*;
|
||||
|
||||
(move |arena, state, min_indent| {
|
||||
let (_, fields, state) = collection_trailing_sep_e!(
|
||||
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
|
||||
map!(
|
||||
and!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', ETypeRecord::Open),
|
||||
loc!(record_type_field()),
|
||||
word1(b',', ETypeRecord::End),
|
||||
// word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd),
|
||||
word1(b'}', ETypeRecord::End),
|
||||
ETypeRecord::Open,
|
||||
ETypeRecord::IndentEnd,
|
||||
AssignedField::SpaceBefore
|
||||
),
|
||||
optional(allocated(specialize_ref(
|
||||
ETypeRecord::Type,
|
||||
term(stop_at_surface_has)
|
||||
)))
|
||||
),
|
||||
|(fields, ext)| { TypeAnnotation::Record { fields, ext } }
|
||||
)
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let field_term = specialize_ref(ETypeRecord::Type, term(stop_at_surface_has));
|
||||
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state, min_indent)?;
|
||||
|
||||
let result = Record { fields, ext };
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
})
|
||||
.trace("type_annotation:record_type")
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
f : (Str)a -> (Str)a
|
||||
f = \x -> x
|
||||
|
||||
f ("Str", 42)
|
@ -0,0 +1,136 @@
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@0-32,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@4-20 Function(
|
||||
[
|
||||
@4-10 Tuple {
|
||||
fields: [
|
||||
@5-8 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@9-10 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
@14-20 Tuple {
|
||||
fields: [
|
||||
@15-18 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@19-20 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_type: @4-20 Function(
|
||||
[
|
||||
@4-10 Tuple {
|
||||
fields: [
|
||||
@5-8 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@9-10 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
@14-20 Tuple {
|
||||
fields: [
|
||||
@15-18 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@19-20 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @21-22 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_expr: @25-32 Closure(
|
||||
[
|
||||
@26-27 Identifier(
|
||||
"x",
|
||||
),
|
||||
],
|
||||
@31-32 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@34-47 SpaceBefore(
|
||||
Apply(
|
||||
@34-35 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
},
|
||||
[
|
||||
@36-47 Tuple(
|
||||
[
|
||||
@37-42 Str(
|
||||
PlainLine(
|
||||
"Str",
|
||||
),
|
||||
),
|
||||
@44-46 Num(
|
||||
"42",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
f : (Str)a -> (Str)a
|
||||
f = \x -> x
|
||||
|
||||
f ("Str", 42)
|
@ -0,0 +1,4 @@
|
||||
f : I64 -> (I64, I64)
|
||||
f = \x -> (x, x + 1)
|
||||
|
||||
f 42
|
@ -0,0 +1,129 @@
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@0-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@4-21 Function(
|
||||
[
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@11-21 Tuple {
|
||||
fields: [
|
||||
@12-15 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
@17-20 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_type: @4-21 Function(
|
||||
[
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@11-21 Tuple {
|
||||
fields: [
|
||||
@12-15 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
@17-20 Apply(
|
||||
"",
|
||||
"I64",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @22-23 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_expr: @26-42 Closure(
|
||||
[
|
||||
@27-28 Identifier(
|
||||
"x",
|
||||
),
|
||||
],
|
||||
@32-42 Tuple(
|
||||
[
|
||||
@33-34 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
@36-41 BinOps(
|
||||
[
|
||||
(
|
||||
@36-37 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
@38-39 Plus,
|
||||
),
|
||||
],
|
||||
@40-41 Num(
|
||||
"1",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@44-48 SpaceBefore(
|
||||
Apply(
|
||||
@44-45 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
},
|
||||
[
|
||||
@46-48 Num(
|
||||
"42",
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
f : I64 -> (I64, I64)
|
||||
f = \x -> (x, x + 1)
|
||||
|
||||
f 42
|
@ -0,0 +1,4 @@
|
||||
f : (Str, Str) -> (Str, Str)
|
||||
f = \x -> x
|
||||
|
||||
f (1, 2)
|
@ -0,0 +1,138 @@
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@0-39,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@3-27 Function(
|
||||
[
|
||||
@3-13 Tuple {
|
||||
fields: [
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@9-12 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
],
|
||||
@17-27 Tuple {
|
||||
fields: [
|
||||
@18-21 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@23-26 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_type: @3-27 Function(
|
||||
[
|
||||
@3-13 Tuple {
|
||||
fields: [
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@9-12 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
],
|
||||
@17-27 Tuple {
|
||||
fields: [
|
||||
@18-21 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@23-26 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @28-29 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_expr: @32-39 Closure(
|
||||
[
|
||||
@33-34 Identifier(
|
||||
"x",
|
||||
),
|
||||
],
|
||||
@38-39 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@41-49 SpaceBefore(
|
||||
Apply(
|
||||
@41-42 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
},
|
||||
[
|
||||
@43-49 Tuple(
|
||||
[
|
||||
@44-45 Num(
|
||||
"1",
|
||||
),
|
||||
@47-48 Num(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
f: (Str, Str) -> (Str, Str)
|
||||
f = \x -> x
|
||||
|
||||
f (1, 2)
|
@ -0,0 +1,4 @@
|
||||
f : (Str, Str)a -> (Str, Str)a
|
||||
f = \x -> x
|
||||
|
||||
f (1, 2)
|
@ -0,0 +1,154 @@
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@0-41,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@3-29 Function(
|
||||
[
|
||||
@3-14 Tuple {
|
||||
fields: [
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@9-12 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@13-14 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
@18-29 Tuple {
|
||||
fields: [
|
||||
@19-22 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@24-27 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@28-29 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_type: @3-29 Function(
|
||||
[
|
||||
@3-14 Tuple {
|
||||
fields: [
|
||||
@4-7 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@9-12 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@13-14 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
@18-29 Tuple {
|
||||
fields: [
|
||||
@19-22 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@24-27 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
ext: Some(
|
||||
@28-29 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @30-31 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_expr: @34-41 Closure(
|
||||
[
|
||||
@35-36 Identifier(
|
||||
"x",
|
||||
),
|
||||
],
|
||||
@40-41 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@43-51 SpaceBefore(
|
||||
Apply(
|
||||
@43-44 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
},
|
||||
[
|
||||
@45-51 Tuple(
|
||||
[
|
||||
@46-47 Num(
|
||||
"1",
|
||||
),
|
||||
@49-50 Num(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
f: (Str, Str)a -> (Str, Str)a
|
||||
f = \x -> x
|
||||
|
||||
f (1, 2)
|
@ -187,6 +187,8 @@ mod test_parse {
|
||||
pass/list_patterns.expr,
|
||||
pass/lowest_float.expr,
|
||||
pass/lowest_int.expr,
|
||||
pass/tuple_type.expr,
|
||||
pass/tuple_type_ext.expr,
|
||||
pass/malformed_ident_due_to_underscore.expr,
|
||||
pass/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||
pass/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||
@ -303,6 +305,8 @@ mod test_parse {
|
||||
pass/when_with_negative_numbers.expr,
|
||||
pass/when_with_numbers.expr,
|
||||
pass/when_with_records.expr,
|
||||
pass/function_with_tuple_type.expr,
|
||||
pass/function_with_tuple_ext_type.expr,
|
||||
pass/where_clause_function.expr,
|
||||
pass/where_clause_multiple_bound_abilities.expr,
|
||||
pass/where_clause_multiple_has_across_newlines.expr,
|
||||
|
@ -2907,6 +2907,27 @@ fn to_tinparens_report<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
ETypeInParens::Empty(pos) => {
|
||||
let surroundings = Region::new(start, pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow("I am partway through parsing a parenthesized type:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I was expecting to see an expression next."),
|
||||
alloc.reflow(r"Note, Roc doesn't use '()' as a null type."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "EMPTY PARENTHESES".to_string(),
|
||||
severity: Severity::RuntimeError,
|
||||
}
|
||||
}
|
||||
|
||||
ETypeInParens::End(pos) => {
|
||||
let surroundings = Region::new(start, pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
@ -4420,13 +4420,14 @@ Tab characters are not allowed."###,
|
||||
@r###"
|
||||
── UNFINISHED PARENTHESES ────────────────── tmp/type_in_parens_start/Test.roc ─
|
||||
|
||||
I just started parsing a type in parentheses, but I got stuck here:
|
||||
I am partway through parsing a type in parentheses, but I got stuck
|
||||
here:
|
||||
|
||||
4│ f : (
|
||||
^
|
||||
|
||||
Tag unions look like [Many I64, None], so I was expecting to see a tag
|
||||
name next.
|
||||
I was expecting to see a parenthesis before this, so try adding a )
|
||||
and see if that helps?
|
||||
|
||||
Note: I may be confused by indentation
|
||||
"###
|
||||
@ -4446,12 +4447,12 @@ Tab characters are not allowed."###,
|
||||
here:
|
||||
|
||||
4│ f : ( I64
|
||||
5│
|
||||
6│
|
||||
^
|
||||
|
||||
I was expecting to see a parenthesis before this, so try adding a )
|
||||
and see if that helps?
|
||||
|
||||
Note: I may be confused by indentation
|
||||
I was expecting to see a closing parenthesis before this, so try
|
||||
adding a ) and see if that helps?
|
||||
"###
|
||||
);
|
||||
|
||||
@ -6063,33 +6064,6 @@ In roc, functions are always written as a lambda, like{}
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
outdented_in_parens,
|
||||
indoc!(
|
||||
r#"
|
||||
Box : (
|
||||
Str
|
||||
)
|
||||
|
||||
4
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── NEED MORE INDENTATION ──────────────────── tmp/outdented_in_parens/Test.roc ─
|
||||
|
||||
I am partway through parsing a type in parentheses, but I got stuck
|
||||
here:
|
||||
|
||||
4│ Box : (
|
||||
5│ Str
|
||||
6│ )
|
||||
^
|
||||
|
||||
I need this parenthesis to be indented more. Try adding more spaces
|
||||
before it!
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
backpassing_type_error,
|
||||
indoc!(
|
||||
|
1
examples/cli/.gitignore
vendored
1
examples/cli/.gitignore
vendored
@ -7,3 +7,4 @@ tui
|
||||
http-get
|
||||
file-io
|
||||
env
|
||||
out.txt
|
@ -1,10 +1,11 @@
|
||||
app "args"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Arg, pf.Program.{ Program }]
|
||||
imports [pf.Stdout, pf.Arg, pf.Task.{ Task }, pf.Process]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main = Program.withArgs \args ->
|
||||
main : Task {} []
|
||||
main =
|
||||
args <- Arg.list |> Task.await
|
||||
parser =
|
||||
divCmd =
|
||||
Arg.succeed (\dividend -> \divisor -> Div (Num.toF64 dividend) (Num.toF64 divisor))
|
||||
@ -53,11 +54,10 @@ main = Program.withArgs \args ->
|
||||
runCmd cmd
|
||||
|> Num.toStr
|
||||
|> Stdout.line
|
||||
|> Program.exit 0
|
||||
|
||||
Err helpMenu ->
|
||||
Stdout.line helpMenu
|
||||
|> Program.exit 1
|
||||
{} <- Stdout.line helpMenu |> Task.await
|
||||
Process.exit 1
|
||||
|
||||
runCmd = \cmd ->
|
||||
when cmd is
|
||||
|
@ -14,8 +14,16 @@ interface Arg
|
||||
choice,
|
||||
withParser,
|
||||
program,
|
||||
list,
|
||||
]
|
||||
imports []
|
||||
imports [Effect, InternalTask, Task.{ Task }]
|
||||
|
||||
## Gives a list of the program's command-line arguments.
|
||||
list : Task (List Str) *
|
||||
list =
|
||||
Effect.args
|
||||
|> Effect.map Ok
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
## A parser for a command-line application.
|
||||
## A [NamedParser] is usually built from a [Parser] using [program].
|
||||
|
@ -9,7 +9,7 @@ DeleteErr : InternalDir.DeleteErr
|
||||
DirEntry : InternalDir.DirEntry
|
||||
|
||||
## Lists the files and directories inside the directory.
|
||||
list : Path -> Task (List Path) [DirReadErr Path ReadErr] [Read [File]]
|
||||
list : Path -> Task (List Path) [DirReadErr Path ReadErr]
|
||||
list = \path ->
|
||||
effect = Effect.map (Effect.dirList (InternalPath.toBytes path)) \result ->
|
||||
when result is
|
||||
@ -19,7 +19,7 @@ list = \path ->
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
## Deletes a directory if it's empty.
|
||||
deleteEmptyDir : Path -> Task {} [DirDeleteErr Path DeleteErr] [Write [File]]
|
||||
deleteEmptyDir : Path -> Task {} [DirDeleteErr Path DeleteErr]
|
||||
|
||||
## Recursively deletes the directory as well as all files and directories inside it.
|
||||
deleteRecursive : Path -> Task {} [DirDeleteErr Path DeleteErr] [Write [File]]
|
||||
deleteRecursive : Path -> Task {} [DirDeleteErr Path DeleteErr]
|
||||
|
@ -23,6 +23,7 @@ hosted Effect
|
||||
fileDelete,
|
||||
fileWriteUtf8,
|
||||
fileWriteBytes,
|
||||
processExit,
|
||||
]
|
||||
imports [InternalHttp.{ Request, Response }, InternalFile, InternalDir]
|
||||
generates Effect with [after, map, always, forever, loop]
|
||||
@ -43,6 +44,8 @@ envVar : Str -> Effect (Result Str {})
|
||||
exePath : Effect (Result (List U8) {})
|
||||
setCwd : List U8 -> Effect (Result {} {})
|
||||
|
||||
processExit : U8 -> Effect {}
|
||||
|
||||
# If we encounter a Unicode error in any of the args, it will be replaced with
|
||||
# the Unicode replacement char where necessary.
|
||||
args : Effect (List Str)
|
||||
|
@ -4,7 +4,7 @@ interface Env
|
||||
|
||||
## Reads the [current working directory](https://en.wikipedia.org/wiki/Working_directory)
|
||||
## from the environment. File operations on relative [Path]s are relative to this directory.
|
||||
cwd : Task Path [CwdUnavailable] [Read [Env]]
|
||||
cwd : Task Path [CwdUnavailable]
|
||||
cwd =
|
||||
effect = Effect.map Effect.cwd \bytes ->
|
||||
if List.isEmpty bytes then
|
||||
@ -17,14 +17,14 @@ cwd =
|
||||
## Sets the [current working directory](https://en.wikipedia.org/wiki/Working_directory)
|
||||
## in the environment. After changing it, file operations on relative [Path]s will be relative
|
||||
## to this directory.
|
||||
setCwd : Path -> Task {} [InvalidCwd] [Write [Env]]
|
||||
setCwd : Path -> Task {} [InvalidCwd]
|
||||
setCwd = \path ->
|
||||
Effect.setCwd (InternalPath.toBytes path)
|
||||
|> Effect.map (\result -> Result.mapErr result \{} -> InvalidCwd)
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
## Gets the path to the currently-running executable.
|
||||
exePath : Task Path [ExePathUnavailable] [Read [Env]]
|
||||
exePath : Task Path [ExePathUnavailable]
|
||||
exePath =
|
||||
effect =
|
||||
Effect.map Effect.exePath \result ->
|
||||
@ -39,7 +39,7 @@ exePath =
|
||||
## If the value is invalid Unicode, the invalid parts will be replaced with the
|
||||
## [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
## (`<60>`).
|
||||
var : Str -> Task Str [VarNotFound] [Read [Env]]
|
||||
var : Str -> Task Str [VarNotFound]
|
||||
var = \name ->
|
||||
Effect.envVar name
|
||||
|> Effect.map (\result -> Result.mapErr result \{} -> VarNotFound)
|
||||
@ -65,7 +65,7 @@ var = \name ->
|
||||
## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas
|
||||
##
|
||||
## Trying to decode into any other types will always fail with a `DecodeErr`.
|
||||
decode : Str -> Task val [VarNotFound, DecodeErr DecodeError] [Read [Env]] | val has Decoding
|
||||
decode : Str -> Task val [VarNotFound, DecodeErr DecodeError] | val has Decoding
|
||||
decode = \name ->
|
||||
Effect.envVar name
|
||||
|> Effect.map
|
||||
@ -83,7 +83,7 @@ decode = \name ->
|
||||
##
|
||||
## If any key or value contains invalid Unicode, the [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
## (`<60>`) will be used in place of any parts of keys or values that are invalid Unicode.
|
||||
dict : Task (Dict Str Str) * [Read [Env]]
|
||||
dict : Task (Dict Str Str) *
|
||||
dict =
|
||||
Effect.envDict
|
||||
|> Effect.map Ok
|
||||
|
@ -26,7 +26,7 @@ WriteErr : InternalFile.WriteErr
|
||||
## This opens the file first and closes it after writing to it.
|
||||
##
|
||||
## To write unformatted bytes to a file, you can use [File.writeBytes] instead.
|
||||
write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr] [Write [File]] | val has Encode.Encoding, fmt has Encode.EncoderFormatting
|
||||
write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr] | val has Encode.Encoding, fmt has Encode.EncoderFormatting
|
||||
write = \path, val, fmt ->
|
||||
bytes = Encode.toBytes val fmt
|
||||
|
||||
@ -41,7 +41,7 @@ write = \path, val, fmt ->
|
||||
## This opens the file first and closes it after writing to it.
|
||||
##
|
||||
## To format data before writing it to a file, you can use [File.write] instead.
|
||||
writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr] [Write [File]]
|
||||
writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr]
|
||||
writeBytes = \path, bytes ->
|
||||
toWriteTask path \pathBytes -> Effect.fileWriteBytes pathBytes bytes
|
||||
|
||||
@ -53,7 +53,7 @@ writeBytes = \path, bytes ->
|
||||
## This opens the file first and closes it after writing to it.
|
||||
##
|
||||
## To write unformatted bytes to a file, you can use [File.writeBytes] instead.
|
||||
writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr] [Write [File]]
|
||||
writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr]
|
||||
writeUtf8 = \path, str ->
|
||||
toWriteTask path \bytes -> Effect.fileWriteUtf8 bytes str
|
||||
|
||||
@ -73,7 +73,7 @@ writeUtf8 = \path, str ->
|
||||
##
|
||||
## On Windows, this will fail when attempting to delete a readonly file; the file's
|
||||
## readonly permission must be disabled before it can be successfully deleted.
|
||||
delete : Path -> Task {} [FileWriteErr Path WriteErr] [Write [File]]
|
||||
delete : Path -> Task {} [FileWriteErr Path WriteErr]
|
||||
delete = \path ->
|
||||
toWriteTask path \bytes -> Effect.fileDelete bytes
|
||||
|
||||
@ -85,7 +85,7 @@ delete = \path ->
|
||||
## This opens the file first and closes it after reading its contents.
|
||||
##
|
||||
## To read and decode data from a file, you can use `File.read` instead.
|
||||
readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr] [Read [File]]
|
||||
readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr]
|
||||
readBytes = \path ->
|
||||
toReadTask path \bytes -> Effect.fileReadBytes bytes
|
||||
|
||||
@ -100,10 +100,7 @@ readBytes = \path ->
|
||||
## To read unformatted bytes from a file, you can use [File.readBytes] instead.
|
||||
readUtf8 :
|
||||
Path
|
||||
-> Task
|
||||
Str
|
||||
[FileReadErr Path ReadErr, FileReadUtf8Err Path _]
|
||||
[Read [File]]
|
||||
-> Task Str [FileReadErr Path ReadErr, FileReadUtf8Err Path _]
|
||||
readUtf8 = \path ->
|
||||
effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result ->
|
||||
when result is
|
||||
@ -132,14 +129,14 @@ readUtf8 = \path ->
|
||||
# Err decodingErr -> Err (FileReadDecodeErr decodingErr)
|
||||
# Err readErr -> Err (FileReadErr readErr)
|
||||
# InternalTask.fromEffect effect
|
||||
toWriteTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileWriteErr Path err] [Write [File]]
|
||||
toWriteTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileWriteErr Path err]
|
||||
toWriteTask = \path, toEffect ->
|
||||
InternalPath.toBytes path
|
||||
|> toEffect
|
||||
|> InternalTask.fromEffect
|
||||
|> Task.mapFail \err -> FileWriteErr path err
|
||||
|
||||
toReadTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileReadErr Path err] [Read [File]]
|
||||
toReadTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileReadErr Path err]
|
||||
toReadTask = \path, toEffect ->
|
||||
InternalPath.toBytes path
|
||||
|> toEffect
|
||||
|
@ -103,7 +103,7 @@ errorToString = \err ->
|
||||
BadStatus code -> Str.concat "Request failed with status " (Num.toStr code)
|
||||
BadBody details -> Str.concat "Request failed. Invalid body. " details
|
||||
|
||||
send : Request -> Task Str Error [Network [Http]]
|
||||
send : Request -> Task Str Error
|
||||
send = \req ->
|
||||
# TODO: Fix our C ABI codegen so that we don't this Box.box heap allocation
|
||||
Effect.sendRequest (Box.box req)
|
||||
|
@ -1,11 +0,0 @@
|
||||
interface InternalProgram
|
||||
exposes [InternalProgram, fromEffect, toEffect]
|
||||
imports [Effect.{ Effect }]
|
||||
|
||||
InternalProgram := Effect U8
|
||||
|
||||
fromEffect : Effect U8 -> InternalProgram
|
||||
fromEffect = @InternalProgram
|
||||
|
||||
toEffect : InternalProgram -> Effect U8
|
||||
toEffect = \@InternalProgram effect -> effect
|
@ -2,16 +2,16 @@ interface InternalTask
|
||||
exposes [Task, fromEffect, toEffect, succeed, fail]
|
||||
imports [Effect.{ Effect }]
|
||||
|
||||
Task ok err fx := Effect (Result ok err)
|
||||
Task ok err := Effect (Result ok err)
|
||||
|
||||
succeed : ok -> Task ok * *
|
||||
succeed : ok -> Task ok *
|
||||
succeed = \ok -> @Task (Effect.always (Ok ok))
|
||||
|
||||
fail : err -> Task * err *
|
||||
fail : err -> Task * err
|
||||
fail = \err -> @Task (Effect.always (Err err))
|
||||
|
||||
fromEffect : Effect (Result ok err) -> Task ok err *
|
||||
fromEffect : Effect (Result ok err) -> Task ok err
|
||||
fromEffect = \effect -> @Task effect
|
||||
|
||||
toEffect : Task ok err * -> Effect (Result ok err)
|
||||
toEffect : Task ok err -> Effect (Result ok err)
|
||||
toEffect = \@Task effect -> effect
|
||||
|
13
examples/cli/cli-platform/Process.roc
Normal file
13
examples/cli/cli-platform/Process.roc
Normal file
@ -0,0 +1,13 @@
|
||||
interface Process
|
||||
exposes [exit]
|
||||
imports [Task.{ Task }, InternalTask, Effect]
|
||||
|
||||
## Exit the process with
|
||||
##
|
||||
## {} <- Stderr.line "Exiting right now!" |> Task.await
|
||||
## Process.exit 1
|
||||
exit : U8 -> Task {} *
|
||||
exit = \code ->
|
||||
Effect.processExit code
|
||||
|> Effect.map \_ -> Ok {}
|
||||
|> InternalTask.fromEffect
|
@ -1,109 +0,0 @@
|
||||
interface Program
|
||||
exposes [Program, ExitCode, noArgs, withArgs, quick, withEnv, exitCode, exit]
|
||||
imports [Task.{ Task }, InternalProgram.{ InternalProgram }, InternalTask, Effect]
|
||||
|
||||
## A [command-line interface](https://en.wikipedia.org/wiki/Command-line_interface) program.
|
||||
Program : InternalProgram
|
||||
|
||||
## An [exit status](https://en.wikipedia.org/wiki/Exit_status) code.
|
||||
ExitCode := U8
|
||||
|
||||
## Converts a [U8] to an [ExitCode].
|
||||
##
|
||||
## If you already have a [Task] and want to convert its success type
|
||||
## from `{}` to [ExitCode], you may find [Program.exit] convenient.
|
||||
exitCode : U8 -> ExitCode
|
||||
exitCode = @ExitCode
|
||||
|
||||
## Attach an [ExitCode] to a task.
|
||||
##
|
||||
## Stderr.line "I hit an error and couldn't continue."
|
||||
## |> Program.exit 1
|
||||
##
|
||||
## Note that this does not terminate the current process! By design, this platform does not have
|
||||
## a [Task] which terminates the current process. Instead, error handling should be consistently
|
||||
## done through task failures.
|
||||
##
|
||||
## To convert a [U8] directly into an [ExitCode], use [Program.exitCode].
|
||||
exit : Task {} [] fx, U8 -> Task ExitCode [] fx
|
||||
exit = \task, code ->
|
||||
Task.map task \{} -> @ExitCode code
|
||||
|
||||
## A program which runs the given task and discards the values it produces on success or failure.
|
||||
## One use for this is as an introductory [Program] when teaching someone how to use this platform.
|
||||
##
|
||||
## If the task succeeds, the program will exit with a [status](https://en.wikipedia.org/wiki/Exit_status)
|
||||
## of 0. If the task fails, the program will exit with a status of 1.
|
||||
## If the task crashes, the program will exit with a status of 2.
|
||||
##
|
||||
## For a similar program which specifies its exit status explicitly, see [Program.noArgs].
|
||||
quick : Task * * * -> Program
|
||||
quick = \task ->
|
||||
effect =
|
||||
InternalTask.toEffect task
|
||||
|> Effect.map \result ->
|
||||
when result is
|
||||
Ok _ -> 0
|
||||
Err _ -> 1
|
||||
|
||||
InternalProgram.fromEffect effect
|
||||
|
||||
## A program which uses no [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
|
||||
## and specifies an [exit status](https://en.wikipedia.org/wiki/Exit_status) [U8].
|
||||
##
|
||||
## Note that the task's failure type must be `[]`. You can satisfy that by handling all
|
||||
## the task's potential failures using something like [Task.attempt].
|
||||
##
|
||||
## For a similar program which does use command-line arguments, see [Program.withArgs].
|
||||
noArgs : Task ExitCode [] * -> Program
|
||||
noArgs = \task ->
|
||||
effect =
|
||||
InternalTask.toEffect task
|
||||
|> Effect.map \Ok (@ExitCode u8) -> u8
|
||||
|
||||
InternalProgram.fromEffect effect
|
||||
|
||||
## A program which uses [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
|
||||
## and specifies an [exit status](https://en.wikipedia.org/wiki/Exit_status) [U8].
|
||||
##
|
||||
## Note that the task's failure type must be `[]`. You can satisfy that by handling all
|
||||
## the task's potential failures using something like [Task.attempt].
|
||||
##
|
||||
## If any command-line arguments contain invalid Unicode, the invalid parts will be replaced with
|
||||
## the [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
## (`<60>`).
|
||||
withArgs : (List Str -> Task ExitCode [] *) -> Program
|
||||
withArgs = \toTask ->
|
||||
effect = Effect.after Effect.args \args ->
|
||||
toTask args
|
||||
|> InternalTask.toEffect
|
||||
|> Effect.map \Ok (@ExitCode u8) -> u8
|
||||
|
||||
InternalProgram.fromEffect effect
|
||||
|
||||
## A program which uses [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
|
||||
## and a dictionary of [environment variables](https://en.wikipedia.org/wiki/Environment_variable).
|
||||
##
|
||||
## This is a combination of [Program.withArgs] and `Env.dict`. Note that the task's failure type
|
||||
## must be `[]`. You can satisfy that by handling all the task's potential failures using
|
||||
## something like [Task.attempt].
|
||||
##
|
||||
## If any command-line arguments contain invalid Unicode, the invalid parts will be replaced with
|
||||
## the [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
## (`<60>`).
|
||||
withEnv : (List Str, Dict Str Str -> Task ExitCode [] *) -> Program
|
||||
withEnv = \toTask ->
|
||||
effect =
|
||||
args <- Effect.args |> Effect.after
|
||||
dict <- Effect.envDict |> Effect.after
|
||||
|
||||
toTask args dict
|
||||
|> InternalTask.toEffect
|
||||
|> Effect.map \Ok (@ExitCode u8) -> u8
|
||||
|
||||
InternalProgram.fromEffect effect
|
||||
|
||||
# ## A combination of [Program.withArgs] and [Env.decodeAll], with the output of [Env.decodeAll]
|
||||
# ## being passed after the command-line arguments.
|
||||
# decodedEnv : (List Str, Result env [EnvDecodingFailed Str]* -> Task U8 [] *) -> Program
|
||||
# | env has Decode
|
@ -2,13 +2,13 @@ interface Stderr
|
||||
exposes [line, write]
|
||||
imports [Effect, Task.{ Task }, InternalTask]
|
||||
|
||||
line : Str -> Task {} * [Write [Stderr]]
|
||||
line : Str -> Task {} *
|
||||
line = \str ->
|
||||
Effect.stderrLine str
|
||||
|> Effect.map (\_ -> Ok {})
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
write : Str -> Task {} * [Write [Stderr]]
|
||||
write : Str -> Task {} *
|
||||
write = \str ->
|
||||
Effect.stderrWrite str
|
||||
|> Effect.map (\_ -> Ok {})
|
||||
|
@ -2,7 +2,7 @@ interface Stdin
|
||||
exposes [line]
|
||||
imports [Effect, Task.{ Task }, InternalTask]
|
||||
|
||||
line : Task Str * [Read [Stdin]]
|
||||
line : Task Str *
|
||||
line =
|
||||
Effect.stdinLine
|
||||
|> Effect.map Ok
|
||||
|
@ -2,13 +2,13 @@ interface Stdout
|
||||
exposes [line, write]
|
||||
imports [Effect, Task.{ Task }, InternalTask]
|
||||
|
||||
line : Str -> Task {} * [Write [Stdout]]
|
||||
line : Str -> Task {} *
|
||||
line = \str ->
|
||||
Effect.stdoutLine str
|
||||
|> Effect.map (\_ -> Ok {})
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
write : Str -> Task {} * [Write [Stdout]]
|
||||
write : Str -> Task {} *
|
||||
write = \str ->
|
||||
Effect.stdoutWrite str
|
||||
|> Effect.map (\_ -> Ok {})
|
||||
|
@ -2,9 +2,9 @@ interface Task
|
||||
exposes [Task, succeed, fail, await, map, mapFail, onFail, attempt, forever, loop, fromResult]
|
||||
imports [Effect, InternalTask]
|
||||
|
||||
Task ok err fx : InternalTask.Task ok err fx
|
||||
Task ok err : InternalTask.Task ok err
|
||||
|
||||
forever : Task val err fx -> Task * err fx
|
||||
forever : Task val err -> Task * err
|
||||
forever = \task ->
|
||||
looper = \{} ->
|
||||
task
|
||||
@ -18,7 +18,7 @@ forever = \task ->
|
||||
Effect.loop {} looper
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
loop : state, (state -> Task [Step state, Done done] err fx) -> Task done err fx
|
||||
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
|
||||
loop = \state, step ->
|
||||
looper = \current ->
|
||||
step current
|
||||
@ -33,13 +33,13 @@ loop = \state, step ->
|
||||
Effect.loop state looper
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
succeed : ok -> Task ok * *
|
||||
succeed : ok -> Task ok *
|
||||
succeed = \ok -> InternalTask.succeed ok
|
||||
|
||||
fail : err -> Task * err *
|
||||
fail : err -> Task * err
|
||||
fail = \err -> InternalTask.fail err
|
||||
|
||||
attempt : Task a b fx, (Result a b -> Task c d fx) -> Task c d fx
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \task, transform ->
|
||||
effect = Effect.after
|
||||
(InternalTask.toEffect task)
|
||||
@ -50,7 +50,7 @@ attempt = \task, transform ->
|
||||
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
await : Task a err fx, (a -> Task b err fx) -> Task b err fx
|
||||
await : Task a err, (a -> Task b err) -> Task b err
|
||||
await = \task, transform ->
|
||||
effect = Effect.after
|
||||
(InternalTask.toEffect task)
|
||||
@ -61,7 +61,7 @@ await = \task, transform ->
|
||||
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
onFail : Task ok a fx, (a -> Task ok b fx) -> Task ok b fx
|
||||
onFail : Task ok a, (a -> Task ok b) -> Task ok b
|
||||
onFail = \task, transform ->
|
||||
effect = Effect.after
|
||||
(InternalTask.toEffect task)
|
||||
@ -72,7 +72,7 @@ onFail = \task, transform ->
|
||||
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
map : Task a err fx, (a -> b) -> Task b err fx
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \task, transform ->
|
||||
effect = Effect.after
|
||||
(InternalTask.toEffect task)
|
||||
@ -83,7 +83,7 @@ map = \task, transform ->
|
||||
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
mapFail : Task ok a fx, (a -> b) -> Task ok b fx
|
||||
mapFail : Task ok a, (a -> b) -> Task ok b
|
||||
mapFail = \task, transform ->
|
||||
effect = Effect.after
|
||||
(InternalTask.toEffect task)
|
||||
@ -95,7 +95,7 @@ mapFail = \task, transform ->
|
||||
InternalTask.fromEffect effect
|
||||
|
||||
## Use a Result among other Tasks by converting it into a Task.
|
||||
fromResult : Result ok err -> Task ok err *
|
||||
fromResult : Result ok err -> Task ok err
|
||||
fromResult = \result ->
|
||||
when result is
|
||||
Ok ok -> succeed ok
|
||||
|
@ -1,9 +1,9 @@
|
||||
platform "cli"
|
||||
requires {} { main : InternalProgram }
|
||||
requires {} { main : Task {} [] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Effect.{ Effect }, InternalProgram.{ InternalProgram }]
|
||||
imports [Task.{ Task }]
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Effect U8 as Fx
|
||||
mainForHost = InternalProgram.toEffect main
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost = main
|
||||
|
@ -213,7 +213,7 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> u8 {
|
||||
pub extern "C" fn rust_main() {
|
||||
let size = unsafe { roc_main_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
|
||||
@ -223,11 +223,9 @@ pub extern "C" fn rust_main() -> u8 {
|
||||
|
||||
roc_main(buffer);
|
||||
|
||||
let exit_code = call_the_closure(buffer);
|
||||
call_the_closure(buffer);
|
||||
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
exit_code
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,6 +285,11 @@ pub extern "C" fn roc_fx_setCwd(roc_path: &RocList<u8>) -> RocResult<(), ()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_processExit(exit_code: u8) {
|
||||
std::process::exit(exit_code as i32);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_exePath(_roc_str: &RocStr) -> RocResult<RocList<u8>, ()> {
|
||||
match std::env::current_exe() {
|
||||
|
@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
std::process::exit(host::rust_main() as _);
|
||||
host::rust_main();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ app "countdown"
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed }]
|
||||
provides [main] to pf
|
||||
|
||||
main = \_args ->
|
||||
main =
|
||||
_ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press <ENTER>.")
|
||||
_ <- await Stdin.line
|
||||
loop 10 tick
|
||||
|
@ -1,18 +1,14 @@
|
||||
app "echo"
|
||||
packages { pf: "https://roc-alpha.netlify.app/jDRlAFAA3738vu3-vMpLUoyxtA86Z7CaZneoOKrihbE.tar.br" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }, pf.Program.{ Program, ExitCode }]
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main = Program.noArgs mainTask
|
||||
|
||||
mainTask : Task ExitCode [] [Read [Stdin], Write [Stdout]]
|
||||
mainTask =
|
||||
main : Task {} []
|
||||
main =
|
||||
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")
|
||||
Task.loop {} (\_ -> Task.map tick Step)
|
||||
|> Program.exit 0
|
||||
Task.loop {} \_ -> Task.map tick Step
|
||||
|
||||
tick : Task.Task {} [] [Read [Stdin], Write [Stdout]]
|
||||
tick : Task.Task {} []
|
||||
tick =
|
||||
shout <- Task.await Stdin.line
|
||||
Stdout.line (echo shout)
|
||||
|
@ -1,10 +1,11 @@
|
||||
app "env"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Env, pf.Task, pf.Program.{ Program }]
|
||||
imports [pf.Stdout, pf.Stderr, pf.Env, pf.Task.{ Task }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main : Task {} []
|
||||
main =
|
||||
task =
|
||||
Env.decode "EDITOR"
|
||||
|> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!")
|
||||
|> Task.await (\{} -> Env.decode "SHLVL")
|
||||
@ -16,10 +17,14 @@ main =
|
||||
lvlStr = Num.toStr n
|
||||
|
||||
Stdout.line "Your current shell level is \(lvlStr)!")
|
||||
|> Task.await (\{} -> Env.decode "LETTERS")
|
||||
|> Task.await
|
||||
(\letters ->
|
||||
|> Task.await \{} -> Env.decode "LETTERS"
|
||||
|
||||
Task.attempt task \result ->
|
||||
when result is
|
||||
Ok letters ->
|
||||
joinedLetters = Str.joinWith letters " "
|
||||
|
||||
Stdout.line "Your favorite letters are: \(joinedLetters)")
|
||||
|> Program.quick
|
||||
Stdout.line "Your favorite letters are: \(joinedLetters)"
|
||||
|
||||
Err _ ->
|
||||
Stderr.line "I couldn't find your favorite letters in the environment variables!"
|
||||
|
@ -1,7 +1,7 @@
|
||||
app "file-io"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [
|
||||
pf.Program.{ Program, ExitCode },
|
||||
pf.Process,
|
||||
pf.Stdout,
|
||||
pf.Stderr,
|
||||
pf.Task.{ Task },
|
||||
@ -12,11 +12,8 @@ app "file-io"
|
||||
]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main = Program.noArgs mainTask
|
||||
|
||||
mainTask : Task ExitCode [] [Write [File, Stdout, Stderr], Read [File, Env]]
|
||||
mainTask =
|
||||
main : Task {} []
|
||||
main =
|
||||
path = Path.fromStr "out.txt"
|
||||
task =
|
||||
cwd <- Env.cwd |> Task.await
|
||||
@ -34,10 +31,7 @@ mainTask =
|
||||
|
||||
Task.attempt task \result ->
|
||||
when result is
|
||||
Ok {} ->
|
||||
Stdout.line "Successfully wrote a string to out.txt"
|
||||
|> Program.exit 0
|
||||
|
||||
Ok {} -> Stdout.line "Successfully wrote a string to out.txt"
|
||||
Err err ->
|
||||
msg =
|
||||
when err is
|
||||
@ -47,5 +41,5 @@ mainTask =
|
||||
FileReadErr _ _ -> "Error reading file"
|
||||
_ -> "Uh oh, there was an error!"
|
||||
|
||||
Stderr.line msg
|
||||
|> Program.exit 1
|
||||
{} <- Stderr.line msg |> Task.await
|
||||
Process.exit 1
|
||||
|
@ -1,16 +1,12 @@
|
||||
app "form"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }, pf.Program.{ Program, ExitCode }]
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main = Program.noArgs mainTask
|
||||
|
||||
mainTask : Task ExitCode [] [Read [Stdin], Write [Stdout]]
|
||||
mainTask =
|
||||
main : Task {} []
|
||||
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)! 👋"
|
||||
|> Program.exit 0
|
||||
|
@ -1,13 +1,10 @@
|
||||
app "http-get"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Http, pf.Task, pf.Stdin, pf.Stdout, pf.Program.{ Program, ExitCode }]
|
||||
imports [pf.Http, pf.Task.{ Task }, pf.Stdin, pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main = Program.noArgs mainTask
|
||||
|
||||
mainTask : Task.Task ExitCode [] [Read [Stdin], Write [Stdout], Network [Http]]
|
||||
mainTask =
|
||||
main : Task {} []
|
||||
main =
|
||||
_ <- Task.await (Stdout.line "Please enter a URL to fetch")
|
||||
|
||||
url <- Task.await Stdin.line
|
||||
@ -25,4 +22,3 @@ mainTask =
|
||||
|> Task.await
|
||||
|
||||
Stdout.line output
|
||||
|> Program.exit 0
|
||||
|
@ -1,11 +1,7 @@
|
||||
app "helloWorld"
|
||||
packages { pf: "cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Program.{ Program }]
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
main = Program.noArgs mainTask
|
||||
|
||||
mainTask =
|
||||
main =
|
||||
Stdout.line "Hello, World!"
|
||||
|> Program.exit 0
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user