Merge remote-tracking branch 'origin/main' into suffix-tutorial

This commit is contained in:
Richard Feldman 2024-04-26 21:06:58 -04:00
commit 73d741de5a
No known key found for this signature in database
GPG Key ID: F1F21AA5B1D9E43B
60 changed files with 509 additions and 180 deletions

View File

@ -11,7 +11,7 @@ env:
# use .tar.gz for quick testing
ARCHIVE_FORMAT: .tar.br
# Make a new basic-cli git tag and set it here before starting this workflow
RELEASE_TAG: 0.9.0
RELEASE_TAG: 0.9.1
jobs:
prepare:
@ -158,7 +158,11 @@ jobs:
- name: rename nightly folder
run: mv roc_nightly* roc_nightly
- run: git clone https://github.com/roc-lang/basic-cli.git
- run: |
git clone https://github.com/roc-lang/basic-cli.git
cd basic-cli
git checkout $RELEASE_TAG
cd ..
- run: cp macos-apple-silicon-files/* ./basic-cli/platform

View File

@ -18,12 +18,15 @@ jobs:
with:
version: 0.11.0
- name: Install zlib on macOS-13
if: matrix.os == 'macos-13'
run: brew install zlib
- name: get the latest release archive for linux (x86_64)
if: startsWith(matrix.os, 'ubuntu')
run: |
curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
- name: get the latest release archive for macos (x86_64)
if: startsWith(matrix.os, 'macos')
run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-latest.tar.gz

51
Cargo.lock generated
View File

@ -2216,12 +2216,27 @@ dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"spin 0.5.2",
"untrusted 0.7.1",
"web-sys",
"winapi",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin 0.9.8",
"untrusted 0.9.0",
"windows-sys 0.52.0",
]
[[package]]
name = "rlimit"
version = "0.9.1"
@ -2650,7 +2665,7 @@ name = "roc_ident"
version = "0.0.1"
[[package]]
name = "roc_lang_srv"
name = "roc_language_server"
version = "0.0.1"
dependencies = [
"bumpalo",
@ -3229,12 +3244,12 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.7"
version = "0.21.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
dependencies = [
"log",
"ring",
"ring 0.17.8",
"rustls-webpki",
"sct",
]
@ -3250,12 +3265,12 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.101.6"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
"ring 0.17.8",
"untrusted 0.9.0",
]
[[package]]
@ -3370,8 +3385,8 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
"ring 0.16.20",
"untrusted 0.7.1",
]
[[package]]
@ -3600,6 +3615,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -4278,6 +4299,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.4.1"

View File

@ -27,7 +27,7 @@ members = [
"crates/wasi-libc-sys",
"crates/wasm_module",
"crates/wasm_interp",
"crates/lang_srv",
"crates/language_server",
]
exclude = [
@ -195,4 +195,4 @@ lto = "thin" # TODO: We could consider full here since this is only used for pac
[profile.debug-full]
inherits = "dev"
debug = true
debug = true

View File

@ -33,6 +33,8 @@ If you would like your company to become a corporate sponsor of Roc's developmen
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
* [Angelo Ceccato](https://github.com/AngeloChecked)
* [Niclas Overby](https://github.com/noverby)
* [Krzysztof G.](https://github.com/krzysztofgb)
* [Sam Mohr](https://github.com/smores56)
* [Steven Chen](https://github.com/megakilo)

View File

@ -948,7 +948,7 @@ mod cli_run {
This roc file can print its own source code. The source is:
app "ingested-file"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [
pf.Stdout,
"ingested-file.roc" as ownCode : Str,

View File

@ -1001,7 +1001,7 @@ bitwiseNot = \n ->
## ```roc
## shiftLeftBy 0b0000_0011 2 == 0b0000_1100
##
## 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100
## 0b0000_0101 |> shiftLeftBy 2 == 0b0001_0100
## ```
## In some languages `shiftLeftBy` is implemented as a binary operator `<<`.
shiftLeftBy : Int a, U8 -> Int a

View File

@ -189,14 +189,17 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
sub_arg,
sub_pat,
sub_new,
}) => Body(
loc_pattern,
apply_task_await(
arena,
loc_expr.region,
sub_arg,
sub_pat,
wrap_in_task_ok(arena, sub_new),
}) => desugar_value_def_suffixed(
arena,
Body(
loc_pattern,
apply_task_await(
arena,
loc_expr.region,
sub_arg,
sub_pat,
wrap_in_task_ok(arena, sub_new),
),
),
),
Err(..) => Body(
@ -226,19 +229,22 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
sub_arg,
sub_pat,
sub_new,
}) => AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr: apply_task_await(
arena,
body_expr.region,
sub_arg,
sub_pat,
wrap_in_task_ok(arena, sub_new),
),
},
}) => desugar_value_def_suffixed(
arena,
AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr: apply_task_await(
arena,
body_expr.region,
sub_arg,
sub_pat,
wrap_in_task_ok(arena, sub_new),
),
},
),
Err(..) => AnnotatedBody {
ann_pattern,
ann_type,

View File

@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia;
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{is_loc_expr_suffixed, wrap_in_task_ok, Pattern, ValueDef};
use roc_parse::ast::{is_loc_expr_suffixed, wrap_in_task_ok, Pattern, ValueDef, WhenBranch};
use roc_region::all::{Loc, Region};
use std::cell::Cell;
@ -564,11 +564,61 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
}
pub fn unwrap_suffixed_expression_when_help<'a>(
_arena: &'a Bump,
arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
Ok(loc_expr)
match loc_expr.value {
Expr::When(condition, branches) => {
// first unwrap any when branches values
// e.g.
// when foo is
// [] -> line! "bar"
// _ -> line! "baz"
for (branch_index, WhenBranch{value: branch_loc_expr,patterns, guard}) in branches.iter().enumerate() {
// if the branch isn't suffixed we can leave it alone
if is_loc_expr_suffixed(branch_loc_expr) {
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
Ok(unwrapped_branch_value) => unwrapped_branch_value,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new),
Err(..) => return Err(EUnwrapped::Malformed),
};
let new_branch = WhenBranch{value: *unwrapped_branch_value, patterns, guard: *guard};
let mut new_branches = Vec::new_in(arena);
let (before, rest) = branches.split_at(branch_index);
let after = &rest[1..];
new_branches.extend_from_slice(before);
new_branches.push(arena.alloc(new_branch));
new_branches.extend_from_slice(after);
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(condition, arena.alloc_slice_copy(new_branches.as_slice()))));
return unwrap_suffixed_expression(arena, new_when, maybe_def_pat);
}
}
// then unwrap the when condition
match unwrap_suffixed_expression(arena, condition, None) {
Ok(unwrapped_condition) => {
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches)));
Ok(new_when)
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when);
Ok(applied_task_await)
}
Err(EUnwrapped::UnwrappedDefExpr(..))
| Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed)
}
}
_ => internal_error!("unreachable, expected a When node to be passed into unwrap_suffixed_expression_defs_help"),
}
}
pub fn unwrap_suffixed_expression_defs_help<'a>(
@ -807,15 +857,21 @@ fn is_matching_empty_record<'a>(
is_empty_record && is_pattern_empty_record
}
fn is_matching_intermediate_answer<'a>(
pub fn is_matching_intermediate_answer<'a>(
loc_pat: &'a Loc<Pattern<'a>>,
loc_expr: &'a Loc<Expr<'a>>,
loc_new: &'a Loc<Expr<'a>>,
) -> bool {
let pat_ident = match loc_pat.value {
Pattern::Identifier { ident, .. } => Some(ident),
_ => None,
};
let exp_ident = match extract_wrapped_task_ok_value(loc_expr) {
let exp_ident = match loc_new.value {
Expr::Var {
module_name, ident, ..
} if module_name.is_empty() && ident.starts_with('#') => Some(ident),
_ => None,
};
let exp_ident_in_task = match extract_wrapped_task_ok_value(loc_new) {
Some(task_expr) => match task_expr.value {
Expr::Var {
module_name, ident, ..
@ -824,8 +880,9 @@ fn is_matching_intermediate_answer<'a>(
},
None => None,
};
match (pat_ident, exp_ident) {
(Some(a), Some(b)) => a == b,
match (pat_ident, exp_ident, exp_ident_in_task) {
(Some(a), Some(b), None) => a == b,
(Some(a), None, Some(b)) => a == b,
_ => false,
}
}

View File

@ -420,6 +420,26 @@ mod suffixed_tests {
);
}
/**
* Nested suffixed expressions
```roc
run = line! (nextMsg!)
run = Task.await nextMsg \#!a0 -> line! (#!a0)
run = Task.await nextMsg \#!a0 -> line (#!a0)
```
*/
#[test]
fn nested_simple() {
run_test(
r#"
run = line! (nextMsg!)
"#,
r##"Defs { tags: [Index(2147483648)], regions: [@0-22], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-3 Identifier { ident: "run", suffixed: 0 }, @0-22 Apply(@0-22 Var { module_name: "Task", ident: "await", suffixed: 0 }, [Var { module_name: "", ident: "nextMsg", suffixed: 0 }, @0-22 Closure([Identifier { ident: "#!a0", suffixed: 0 }], @0-22 Apply(@0-22 Var { module_name: "", ident: "line", suffixed: 0 }, [@13-21 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 })], Space))], BangSuffix))] }"##,
);
}
/**
* Nested suffixed expressions
```roc
@ -437,9 +457,8 @@ mod suffixed_tests {
Task.await [foo (#!a0) (blah stuff)] \z -> doSomething z
```
*/
#[test]
fn nested_suffixed() {
fn nested_complex() {
run_test(
r#"
main =
@ -665,4 +684,143 @@ mod suffixed_tests {
r#"Defs { tags: [Index(2147483648)], regions: [@0-103], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "copy", suffixed: 0 }, @7-103 Closure([@8-9 Identifier { ident: "a", suffixed: 0 }, @10-11 Identifier { ident: "b", suffixed: 0 }], @36-42 Apply(@36-42 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@36-42 Apply(@36-42 Var { module_name: "", ident: "line", suffixed: 0 }, [@37-42 Str(PlainLine("FOO"))], Space), @36-42 Closure([@36-42 RecordDestructure([])], @60-103 Apply(@60-103 Var { module_name: "", ident: "mapErr", suffixed: 0 }, [@60-72 Apply(@60-67 Var { module_name: "CMD", ident: "new", suffixed: 0 }, [@68-72 Str(PlainLine("cp"))], Space), @100-103 Tag("ERR")], BinOp(Pizza)))], BangSuffix)))] }"#,
);
}
/**
* Unwrap a when expression
```roc
list =
when getList! is
[] -> "empty"
_ -> "non-empty"
list =
Task.await getList \#!a0 ->
when #!a0 is
[] -> "empty"
_ -> "non-empty"
```
*/
#[test]
fn when_simple() {
run_test(
r#"
list =
when getList! is
[] -> "empty"
_ -> "non-empty"
"#,
r##"Defs { tags: [Index(2147483648)], regions: [@0-111], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "list", suffixed: 0 }, @24-111 Apply(@24-111 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@29-37 Var { module_name: "", ident: "getList", suffixed: 0 }, @24-111 Closure([@29-37 Identifier { ident: "#!a0", suffixed: 0 }], @24-111 When(@29-37 Var { module_name: "", ident: "#!a0", suffixed: 0 }, [WhenBranch { patterns: [@61-63 List([])], value: @67-74 Str(PlainLine("empty")), guard: None }, WhenBranch { patterns: [@95-96 Underscore("")], value: @100-111 Str(PlainLine("non-empty")), guard: None }]))], BangSuffix))] }"##,
);
}
/**
* Unwrap a when expression
```roc
list =
when getList! is
[] ->
line! "foo"
line! "bar"
_ ->
ok {}
list =
Task.await getList \#!a0 ->
when getList is
[] ->
line! "foo"
line! "bar"
_ ->
ok {}
list =
Task.await getList \#!a0 ->
when getList is
[] ->
Task.await line "foo" \{} -> line! "bar"
_ ->
ok {}
```
*/
#[test]
fn when_branches() {
run_test(
r#"
list =
when getList! is
[] ->
line! "foo"
line! "bar"
_ ->
ok {}
"#,
r##"Defs { tags: [Index(2147483648)], regions: [@0-195], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "list", suffixed: 0 }, @24-195 Apply(@24-195 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@29-37 Var { module_name: "", ident: "getList", suffixed: 0 }, @24-195 Closure([@29-37 Identifier { ident: "#!a0", suffixed: 0 }], @24-195 When(@29-37 Var { module_name: "", ident: "#!a0", suffixed: 0 }, [WhenBranch { patterns: [@61-63 List([])], value: @97-103 Apply(@97-103 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@97-103 Apply(@97-103 Var { module_name: "", ident: "line", suffixed: 0 }, [@98-103 Str(PlainLine("foo"))], Space), @97-103 Closure([@97-103 RecordDestructure([])], @128-139 Apply(@128-139 Var { module_name: "", ident: "line", suffixed: 0 }, [@134-139 Str(PlainLine("bar"))], Space))], BangSuffix), guard: None }, WhenBranch { patterns: [@160-161 Underscore("")], value: @190-195 Apply(@190-192 Var { module_name: "", ident: "ok", suffixed: 0 }, [@193-195 Record([])], Space), guard: None }]))], BangSuffix))] }"##,
);
}
#[test]
fn trailing_suffix_inside_when() {
run_test(
r#"
main =
result = Stdin.line!
when result is
End ->
Task.ok {}
Input name ->
Stdout.line! "Hello, $(name)"
"#,
r#"Defs { tags: [Index(2147483648)], regions: [@0-226], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @32-43 Apply(@32-43 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@32-43 Var { module_name: "Stdin", ident: "line", suffixed: 0 }, @32-43 Closure([@23-29 Identifier { ident: "result", suffixed: 0 }], @61-226 When(@66-72 Var { module_name: "", ident: "result", suffixed: 0 }, [WhenBranch { patterns: [@96-99 Tag("End")], value: @127-137 Apply(@127-134 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@135-137 Record([])], Space), guard: None }, WhenBranch { patterns: [@159-169 Apply(@159-164 Tag("Input"), [@165-169 Identifier { ident: "name", suffixed: 0 }])], value: @197-226 Apply(@197-226 Var { module_name: "Stdout", ident: "line", suffixed: 0 }, [@210-226 Str(Line([Plaintext("Hello, "), Interpolated(@220-224 Var { module_name: "", ident: "name", suffixed: 0 })]))], Space), guard: None }]))], BangSuffix))] }"#,
);
}
}
#[cfg(test)]
mod test_suffixed_helpers {
use roc_can::suffixed::is_matching_intermediate_answer;
use roc_module::called_via::CalledVia;
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr;
use roc_parse::ast::Pattern;
use roc_region::all::Loc;
#[test]
fn test_matching_answer() {
let loc_pat = Loc::at_zero(Pattern::Identifier {
ident: "#!a0",
suffixed: 0,
});
let loc_new = Loc::at_zero(Expr::Var {
module_name: "",
ident: "#!a0",
suffixed: 0,
});
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
}
#[test]
fn test_matching_answer_task_ok() {
let loc_pat = Loc::at_zero(Pattern::Identifier {
ident: "#!a0",
suffixed: 0,
});
let intermetiate = &[&Loc::at_zero(Expr::Var {
module_name: "",
ident: "#!a0",
suffixed: 0,
})];
let task_ok = Loc::at_zero(Expr::Var {
module_name: ModuleName::TASK,
ident: "ok",
suffixed: 0,
});
let loc_new = Loc::at_zero(Expr::Apply(&task_ok, intermetiate, CalledVia::BangSuffix));
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
}
}

View File

@ -2161,11 +2161,25 @@ fn report_unused_imported_modules(
Occupied(entry) => entry.into_mut(),
};
for (unused, region) in unused_imported_modules.drain() {
if !unused.is_builtin() {
existing.push(roc_problem::can::Problem::UnusedModuleImport(
unused, region,
));
// TODO this outer conditional can be replaced by just the for loop
// once we have Task as builtin. (Also the for loop doesn't need the "Task" check.)
if !unused_imported_modules.is_empty() {
let module_ids = Arc::clone(&state.arc_modules);
let module_ids = module_ids.lock();
for (unused, region) in unused_imported_modules.drain() {
if !unused.is_builtin() {
let is_task_module = match module_ids.get_name(unused) {
Some(name) => name.as_inner().as_str() == "Task",
None => false,
};
if !is_task_module {
existing.push(roc_problem::can::Problem::UnusedModuleImport(
unused, region,
));
}
}
}
}

View File

@ -1280,7 +1280,7 @@ fn roc_file_no_extension() {
indoc!(
r#"
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf

View File

@ -197,17 +197,12 @@ where
)
}
pub fn check_indent<'a, E>(
indent_problem: fn(Position) -> E,
inside_suffixed_statement: bool,
) -> impl Parser<'a, (), E>
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
where
E: 'a,
{
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
move |_, state: State<'a>, min_indent: u32| {
if state.column() >= (min_indent + extra_spaces) {
if state.column() >= min_indent {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, indent_problem(state.pos())))

View File

@ -1638,11 +1638,13 @@ fn finish_parsing_ability_def_help<'a>(
Ok((MadeProgress, (type_def, def_region), state))
}
#[allow(clippy::too_many_arguments)]
fn parse_expr_operator<'a>(
min_indent: u32,
options: ExprParseOptions,
mut expr_state: ExprState<'a>,
loc_op: Loc<BinOp>,
line_indent: u32,
arena: &'a Bump,
state: State<'a>,
initial_state: State<'a>,
@ -1687,7 +1689,8 @@ fn parse_expr_operator<'a>(
}
BinOp::Assignment => {
let expr_region = expr_state.expr.region;
let indented_more = min_indent + 1;
let indented_more = line_indent + 1;
let call = expr_state
.validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction)
@ -1894,12 +1897,18 @@ fn parse_expr_end<'a>(
state: State<'a>,
initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let inner_min_indent = if options.suffixed_found {
min_indent + 1
} else {
min_indent
};
let parser = skip_first!(
crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options)
);
match parser.parse(arena, state.clone(), min_indent) {
match parser.parse(arena, state.clone(), inner_min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((
_,
@ -2001,6 +2010,7 @@ fn parse_expr_end<'a>(
Err((NoProgress, _)) => {
let before_op = state.clone();
// try an operator
let line_indent = state.line_indent();
match loc!(operator()).parse(arena, state.clone(), min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
@ -2011,6 +2021,7 @@ fn parse_expr_end<'a>(
options,
expr_state,
loc_op,
line_indent,
arena,
state,
initial_state,

View File

@ -46,7 +46,7 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
move |arena, state: State<'a>, min_indent| {
let (_, pattern, state) = loc_pattern_help_help().parse(arena, state, min_indent)?;
let (_, pattern, state) = loc_pattern_help_help(true).parse(arena, state, min_indent)?;
let pattern_state = state.clone();
@ -82,11 +82,13 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
}
}
fn loc_pattern_help_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
fn loc_pattern_help_help<'a>(
can_have_arguments: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc_ident_pattern_help(true),
loc_ident_pattern_help(can_have_arguments),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
@ -143,7 +145,8 @@ fn loc_tag_pattern_arg<'a>(
min_indent,
)?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg().parse(arena, state, min_indent)?;
// Cannot have arguments here, pass `false` to make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
let (_, loc_pat, state) = loc_pattern_help_help(false).parse(arena, state, min_indent)?;
let Loc { region, value } = loc_pat;
@ -194,21 +197,6 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
)
}
fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
loc_ident_pattern_help(false),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
loc!(string_like_pattern_help()),
loc!(number_pattern_help())
)
}
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(

View File

@ -2,9 +2,9 @@
name = "test_syntax-fuzz"
publish = false
authors.workspace = true
edition.workspace = true
version.workspace = true
version = "0.0.0"
authors = ["Automatically generated"]
edition = "2021"
[package.metadata]
cargo-fuzz = true
@ -12,8 +12,8 @@ cargo-fuzz = true
[dependencies]
test_syntax = { path = "../../test_syntax" }
bumpalo.workspace = true
libfuzzer-sys.workspace = true
bumpalo = { version = "3.12.0", features = ["collections"] }
libfuzzer-sys = "0.4"
# Prevent this from interfering with workspaces
[workspace]

View File

@ -1,7 +1,7 @@
app "hello"
packages {
pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
}
imports [pf.Stdout]
provides [main] to pf

View File

@ -24,7 +24,7 @@ Full {
Newline,
],
package_name: @31-145 PackageName(
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
),
},
[

View File

@ -1,6 +1,6 @@
app "hello"
packages { pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br"
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br"
}
imports [pf.Stdout]
provides [main] to pf

View File

@ -0,0 +1,54 @@
When(
@5-10 Apply(
@5-7 Tag(
"Ok",
),
[
@8-10 List(
[],
),
],
Space,
),
[
WhenBranch {
patterns: [
@18-23 SpaceBefore(
Apply(
@18-20 Tag(
"Ok",
),
[
@21-23 List(
[],
),
],
),
[
Newline,
],
),
],
value: @27-29 Record(
[],
),
guard: None,
},
WhenBranch {
patterns: [
@34-35 SpaceBefore(
Underscore(
"",
),
[
Newline,
],
),
],
value: @39-41 Record(
[],
),
guard: None,
},
],
)

View File

@ -0,0 +1,3 @@
when Ok [] is
Ok [] -> {}
_ -> {}

View File

@ -482,6 +482,7 @@ mod test_snapshots {
pass/when_in_function_python_style_indent.expr,
pass/when_in_parens.expr,
pass/when_in_parens_indented.expr,
pass/when_result_list.expr,
pass/when_with_alternative_patterns.expr,
pass/when_with_function_application.expr,
pass/when_with_negative_numbers.expr,

View File

@ -1,11 +1,13 @@
interface TypeId
exposes [TypeId, fromU64, toU64]
exposes [TypeId, typeIDfromU64, typeIDtoU64]
imports []
TypeId := U64 implements [Eq, Hash]
TypeId := U64 implements [Eq, Hash, Inspect, Encoding]
toU64 : TypeId -> U64
toU64 = \@TypeId x -> x
# renamed here so we can import the functions directly as a workaround for
# https://github.com/roc-lang/roc/issues/5477
typeIDtoU64 : TypeId -> U64
typeIDtoU64 = \@TypeId x -> x
fromU64 : U64 -> TypeId
fromU64 = @TypeId
typeIDfromU64 : U64 -> TypeId
typeIDfromU64 = @TypeId

View File

@ -1,6 +1,6 @@
interface Types
exposes [Types, shape, size, alignment, target, walkShapes, entryPoints]
imports [Shape.{ Shape }, TypeId.{ TypeId }, Target.{ Target }, TypeId]
imports [Shape.{ Shape }, TypeId.{ TypeId, typeIDfromU64, typeIDtoU64 }, Target.{ Target }]
# TODO: switch AssocList uses to Dict once roc_std is updated.
Tuple1 : [T Str TypeId]
@ -23,7 +23,7 @@ Types := {
## Names and types of the entry points of the program (e.g. mainForHost)
entrypoints : List Tuple1,
target : Target,
}
} implements [Inspect, Encoding]
target : Types -> Target
target = \@Types types -> types.target
@ -34,33 +34,33 @@ entryPoints = \@Types { entrypoints } -> entrypoints
walkShapes : Types, state, (state, Shape, TypeId -> state) -> state
walkShapes = \@Types { types: shapes }, originalState, update ->
List.walkWithIndex shapes originalState \state, elem, index ->
id = TypeId.fromU64 index
id = typeIDfromU64 index
update state elem id
shape : Types, TypeId -> Shape
shape = \@Types types, id ->
when List.get types.types (TypeId.toU64 id) is
when List.get types.types (typeIDtoU64 id) is
Ok answer -> answer
Err OutOfBounds ->
idStr = Num.toStr (TypeId.toU64 id)
idStr = Num.toStr (typeIDtoU64 id)
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
alignment : Types, TypeId -> U32
alignment = \@Types types, id ->
when List.get types.aligns (TypeId.toU64 id) is
when List.get types.aligns (typeIDtoU64 id) is
Ok answer -> answer
Err OutOfBounds ->
idStr = Num.toStr (TypeId.toU64 id)
idStr = Num.toStr (typeIDtoU64 id)
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
size : Types, TypeId -> U32
size = \@Types types, id ->
when List.get types.sizes (TypeId.toU64 id) is
when List.get types.sizes (typeIDtoU64 id) is
Ok answer -> answer
Err OutOfBounds ->
idStr = Num.toStr (TypeId.toU64 id)
idStr = Num.toStr (typeIDtoU64 id)
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"

View File

@ -1,5 +1,5 @@
[package]
name = "roc_lang_srv"
name = "roc_language_server"
version = "0.0.1"
edition = "2021"

View File

@ -33,50 +33,49 @@ use self::{analysed_doc::ModuleIdToUrl, tokens::Token};
pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
/// Contains hashmaps of info about all modules that were analyzed
#[derive(Debug)]
pub(super) struct ModulesInfo {
subs: Mutex<HashMap<ModuleId, Subs>>,
exposed: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
docs: VecMap<ModuleId, ModuleDocumentation>,
struct ModulesInfo {
subs_by_module: HashMap<ModuleId, Mutex<Subs>>,
exposed_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
docs_by_module: HashMap<ModuleId, ModuleDocumentation>,
}
impl ModulesInfo {
fn with_subs<F, A>(&self, mod_id: &ModuleId, f: F) -> Option<A>
fn with_subs<F, A>(&self, module_id: &ModuleId, f: F) -> Option<A>
where
F: FnOnce(&mut Subs) -> A,
{
self.subs.lock().get_mut(mod_id).map(f)
let subs = self.subs_by_module.get(module_id)?;
Some(f(&mut subs.lock()))
}
/// Transforms some of the raw data from the analysis into a state that is
/// more useful during processes like completion.
fn from_analysis(
fn get_docs(&self, module_id: &ModuleId) -> Option<&ModuleDocumentation> {
self.docs_by_module.get(module_id)
}
fn from_loaded_module(
exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
typechecked: &MutMap<ModuleId, CheckedModule>,
docs_by_module: VecMap<ModuleId, ModuleDocumentation>,
) -> ModulesInfo {
// We wrap this in Arc because later we will go through each module's imports and
// store the full list of symbols that each imported module exposes.
// example: A imports B. B exposes [add, multiply, divide] and A will store a reference to that list.
let exposed = exposes
let exposed_by_module = exposes
.into_iter()
.map(|(module_id, symbols)| (module_id, Arc::new(symbols)))
.collect::<HashMap<_, _>>();
// Combine the subs from all modules
let all_subs = Mutex::new(
typechecked
.iter()
.map(|(module_id, checked_module)| {
(*module_id, checked_module.solved_subs.0.clone())
})
.collect::<HashMap<_, _>>(),
);
let subs_by_module = typechecked
.iter()
.map(|(module_id, checked_module)| {
(*module_id, checked_module.solved_subs.0.clone().into())
})
.collect::<HashMap<_, _>>();
let docs_by_module = docs_by_module.into_iter().collect();
ModulesInfo {
subs: all_subs,
exposed,
docs: docs_by_module,
subs_by_module,
exposed_by_module,
docs_by_module,
}
}
}
@ -84,15 +83,14 @@ impl ModulesInfo {
#[derive(Debug, Clone)]
pub(super) struct AnalyzedModule {
exposed_imports: Vec<(Symbol, Variable)>,
/// imports are grouped by which module they come from
imports: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
imports_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
module_id: ModuleId,
interns: Interns,
subs: Subs,
abilities: AbilitiesStore,
declarations: Declarations,
modules_info: Arc<ModulesInfo>,
// We need this because ModuleIds are not stable between compilations, so a ModuleId visible to
// ModuleIds are not stable between compilations, so a ModuleId visible to
// one module may not be true global to the language server.
module_id_to_url: ModuleIdToUrl,
}
@ -164,7 +162,7 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
let exposed_imports = resolve_exposed_imports(exposed_imports, &exposes);
let modules_info = Arc::new(ModulesInfo::from_analysis(
let modules_info = Arc::new(ModulesInfo::from_loaded_module(
exposes,
&typechecked,
docs_by_module,
@ -304,7 +302,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
let analyzed_module = AnalyzedModule {
exposed_imports,
imports,
imports_by_module: imports,
subs,
abilities,
declarations,
@ -342,7 +340,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
(
id,
self.modules_info
.exposed
.exposed_by_module
.get(&id)
.unwrap_or(&Arc::new(vec![]))
.clone(),

View File

@ -69,6 +69,7 @@ impl DocInfo {
let end = Position::new(self.line_info.num_lines(), 0);
Range::new(start, end)
}
pub fn get_prefix_at_position(&self, position: Position) -> String {
let position = position.to_roc_position(&self.line_info);
let offset = position.offset as usize;
@ -82,6 +83,7 @@ impl DocInfo {
String::from(symbol)
}
pub fn format(&self) -> Option<Vec<TextEdit>> {
let source = &self.source;
let arena = &Bump::new();
@ -175,10 +177,12 @@ impl AnalyzedDocument {
let (region, var) = roc_can::traverse::find_closest_type_at(pos, declarations)?;
//TODO:Can this be integrated into find closest type? is it even worth it?
let docs_opt = self
.symbol_at(position)
.and_then(|symb| modules_info.docs.get(module_id)?.get_doc_for_symbol(&symb));
//TODO: Can this be integrated into "find closest type"? Is it worth it?
let docs_opt = self.symbol_at(position).and_then(|symbol| {
modules_info
.get_docs(module_id)?
.get_doc_for_symbol(&symbol)
});
let type_str = format_var_type(var, &mut subs.clone(), module_id, interns);
@ -238,7 +242,7 @@ impl AnalyzedDocument {
subs,
declarations,
exposed_imports,
imports,
imports_by_module: imports,
modules_info,
..
} = self.module()?;
@ -299,7 +303,7 @@ impl AnalyzedDocument {
&mut subs.clone(),
module_id,
interns,
modules_info.docs.get(module_id),
modules_info.get_docs(module_id),
exposed_imports,
);
Some(completions)

View File

@ -77,7 +77,7 @@ pub(super) fn get_module_completion_items(
mod_id,
interns,
exposed_symbols,
modules_info.docs.get(mod_id),
modules_info.get_docs(mod_id),
modules_info,
)),
..Default::default()
@ -90,7 +90,7 @@ pub(super) fn get_module_completion_items(
exposed_symbols,
modules_info,
mod_id,
modules_info.docs.get(mod_id),
modules_info.get_docs(mod_id),
interns,
)
} else {

View File

@ -1,5 +1,5 @@
app "args"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout, pf.Arg, pf.Task.{ Task }]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "countdown"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop }]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "echo"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "env"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout, pf.Stderr, pf.Env, pf.Task.{ Task }]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "file-io"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [
pf.Stdout,
pf.Stderr,

View File

@ -1,5 +1,5 @@
app "form"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "http-get"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Http, pf.Task.{ Task }, pf.Stdin, pf.Stdout]
provides [main] to pf

View File

@ -1,5 +1,5 @@
app "ingested-file-bytes"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [
pf.Stdout,
"../../LICENSE" as license : _, # A type hole can also be used here.

View File

@ -1,5 +1,5 @@
app "ingested-file"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [
pf.Stdout,
"ingested-file.roc" as ownCode : Str,

View File

@ -1,5 +1,5 @@
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf

View File

@ -2,7 +2,7 @@
# Shows how Roc values can be logged
#
app "inspect-logging"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [
pf.Stdout,
Community,

View File

@ -1,6 +1,6 @@
app "example"
packages {
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
}
imports [

View File

@ -1,6 +1,6 @@
app "example"
packages {
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
}
imports [

View File

@ -3,7 +3,7 @@ let
inherit (compile-deps) zigPkg llvmPkgs llvmVersion llvmMajorMinorStr glibcPath libGccSPath;
subPackagePath = if subPackage != null then "crates/${subPackage}" else null;
mainBin = if subPackage == "lang_srv" then "roc_language_server" else "roc";
mainBin = if subPackage == "language_server" then "roc_language_server" else "roc";
filteredSource = pkgs.callPackage ./fileFilter.nix { };
in
rustPlatform.buildRustPackage {

View File

@ -18,7 +18,7 @@ let
# all rust crates in workspace.members of Cargo.toml
roc-full = callPackage ./builder.nix { };
roc-lang-server = callPackage ./builder.nix { subPackage = "lang_srv"; };
roc-lang-server = callPackage ./builder.nix { subPackage = "language_server"; };
# only the CLI crate = executable provided in nightly releases
roc-cli = callPackage ./builder.nix { subPackage = "cli"; };
};

View File

@ -24,19 +24,19 @@ view =
Desc [Ident "main", Kw "="] "<p>This defines <code class=\"ident\">main</code>, which is where our program will begin.</p><p>In Roc, <a href=\"/tutorial#https://www.roc-lang.org/tutorial#naming-things\">all definitions are constant</a>, so writing <code class=\"ident\">main =</code> again in the same scope would give an error.</p>",
Indent,
Desc [Ident "Path.fromStr", Str "\"url.txt\""] "<p>This converts the string <code>\"url.txt\"</code> into a <code>Path</code> by passing it to <code>Path.fromStr</code>.</p><p>Function arguments are separated with whitespace. Parentheses are only needed in <a href=\"/tutorial#calling-functions\">nested function calls</a>.</p>",
Indent,
Newline,
Desc [Kw "|>", Ident "storeEmail"] "<p>The <a href=\"/tutorial#the-pipe-operator\">pipe operator</a> (<code>|></code>) is syntax sugar for passing the previous value to the next function in the \"pipeline.\"</p><p>This line takes the value that <code>Path.fromStr \"url.txt\"</code> returns and passes it to <code>storeEmail</code>.</p><p>The next <code>|></code> continues the pipeline.</p>",
Newline,
Desc [Kw "|>", Ident "Task.onErr", Ident "handleErr"] "<p>If the task returned by the previous step in the pipeline fails, pass its error to <code>handleErr</code>.</p><p>The pipeline essentially does this:</p><pre><code>a = Path.fromStr \"url.txt\"\nb = storeEmail a\n\nTask.onErr b handleErr</code></pre><p>It creates a <code>Path</code> from a string, passes it to <code>storeEmail</code>, and specifies how to handle errors if storing the email fails.</p>",
Outdent,
Outdent,
Newline,
Desc [Ident "storeEmail", Kw "=", Lambda ["path"]] "<p>This <a href=\"/tutorial#defining-functions\">defines a function</a> named <code>storeEmail</code>. It takes one argument, named <code>path</code>.</p><p>In Roc, functions are ordinary values, so we assign names to them using <code>=</code> like with any other value.</p><p>The <code>\\arg1, arg2 -&gt;</code> syntax begins a function, and the part after <code>-&gt;</code> is the function's body.</p>",
Indent,
Desc [Ident "url", Kw "=", Ident "File.readUtf8!", Ident "path"] "<p>This passes <code>path</code> to the <code>File.readUtf8</code> function, which reads the contents of the file (as a <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a> string) into <code>url</code>.</p><p>The <code>!</code> operator is similar to <code>await</code> in other languages. It means “wait until the asynchronous <code>File.readUtf8</code> operation successfully completes.”</p><p>If the file read fails (maybe because <code>path</code> refers to a missing file), the rest of this function will be skipped, and the <code>handleErr</code> function will take over.</p>",
Newline,
Desc [Ident "user", Kw "=", Ident "Http.get!", Ident "url", Ident "Json.format"] "<p>This fetches the contents of the URL and decodes them as <a href=\"https://www.json.org\">JSON</a>.</p><p>If the shape of the JSON isn't compatible with the type of <code>user</code> (based on type inference), this will give a decoding error immediately.</p><p>As with all the other function calls involving the <code>!</code> operator, if there's an error, nothing else in <code>storeEmail</code> will be run, and <code>handleErr</code> will run.</p>",
Newline,
Desc [Ident "dest", Kw "=", Ident "Path.fromStr", StrInterpolation "\"" "user.name" ".txt\""] "<p>The <code>\$(user.name)</code> in this string literal will be replaced with the value stored in the <code>user</code> record's <code>name</code> field. This is <a href=\"/tutorial#string-interpolation\">string interpolation</a>.</p><p>Note that this function call doesn't involve the <code>!</code> operator. That's because <code>Path.fromStr</code> is a synchronous operation, so there's no need to use <code>!</code> to wait for it to finish.</p>",
Desc [Ident "dest", Kw "=", Ident "Path.fromStr", StrInterpolation "\"" "user.name" ".txt\""] "<p>The <code>\$(user.name)</code> in this string literal will be replaced with the value stored in the <code>user</code> record's <code>name</code> field. This is <a href=\"/tutorial#string-interpolation\">string interpolation</a>.</p><p>Note that this function call doesn't involve the <code>!</code> operator. That's because <code>Path.fromStr</code> doesn't involve any Tasks, so there's no need to use <code>!</code> to wait for it to finish.</p>",
Newline,
Desc [Ident "File.writeUtf8!", Ident "dest", Ident "user.email"] "<p>This writes <code>user.email</code> to the file, encoded as <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a>.</p><p>Since <code>File.writeUtf8</code> doesn't produce any information on success, we don't bother using <code>=</code> like we did on the other lines.</p>",
Newline,

View File

@ -146,12 +146,14 @@ If you would like your organization to become an official sponsor of Roc's devel
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
<ul id="individual-sponsors">
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a>
<li><a href="https://github.com/smores56">Sam Mohr</a>
<li><a href="https://github.com/megakilo">Steven Chen</a>
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a>
<li><a href="https://github.com/mrmizz">Alex Binaei</a>
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a>
<li><a href="https://github.com/AngeloChecked">Angelo Ceccato</a></li>
<li><a href="https://github.com/noverby">Niclas Overby</a></li>
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a></li>
<li><a href="https://github.com/smores56">Sam Mohr</a></li>
<li><a href="https://github.com/megakilo">Steven Chen</a></li>
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a></li>
<li><a href="https://github.com/mrmizz">Alex Binaei</a></li>
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a></li>
<li><a href="https://github.com/chris-packett">Chris Packett</a></li>
<li><a href="https://github.com/jamesbirtles">James Birtles</a></li>
<li><a href="https://github.com/Ivo-Balbaert">Ivo Balbaert</a></li>

View File

@ -8,7 +8,7 @@ Here is a Roc application that prints `"Hello, World!"` to the command line:
```roc
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf

View File

@ -155,7 +155,7 @@ Make a file named `main.roc` and put this in it:
```roc
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf
@ -1425,7 +1425,7 @@ Let's take a closer look at the part of `main.roc` above the `main` def:
```roc
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf
```
@ -1437,7 +1437,7 @@ The line `app "hello"` shows that this module is a Roc application. The "hello"
The remaining lines all involve the [platform](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform) this application is built on:
```roc
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf
```
@ -1536,7 +1536,7 @@ Let's start with a basic "Hello World" program.
```roc
app "cli-tutorial"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout]
provides [main] to pf
@ -1570,7 +1570,7 @@ Let's change `main` to read a line from `stdin`, and then print what we got:
```roc
app "cli-tutorial"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
imports [pf.Stdout, pf.Stdin, pf.Task]
provides [main] to pf