mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge branch 'trunk' into applied_tag_functions
This commit is contained in:
commit
6e1f42f990
36 changed files with 1059 additions and 440 deletions
|
@ -31,14 +31,12 @@ For any other OS, checkout the [Zig installation page](https://github.com/ziglan
|
|||
|
||||
### LLVM
|
||||
|
||||
To see which version of LLVM you need, take a look at `Cargo.toml`, in particular the `branch` section of the `inkwell` dependency. It should have something like `llvmX-Y` where X and Y are the major and minor revisions of LLVM you need.
|
||||
|
||||
For Ubuntu and Debian, you can use the `Automatic installation script` at [apt.llvm.org](https://apt.llvm.org):
|
||||
```
|
||||
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
|
||||
```
|
||||
|
||||
For macOS, you can run `brew install llvm` (but before you do so, check the version with `brew info llvm`--if it's 10.0.1, you may need to install a slightly older version. See below for details.)
|
||||
For macOS, check the troubleshooting section below.
|
||||
|
||||
There are also plenty of alternative options at http://releases.llvm.org/download.html
|
||||
|
||||
|
@ -118,15 +116,30 @@ If you encounter `cannot find -lz` run `sudo apt install zlib1g-dev`.
|
|||
|
||||
### LLVM installation on macOS
|
||||
|
||||
It looks like LLVM 10.0.1 [has some issues with libxml2 on macOS](https://discourse.brew.sh/t/llvm-config-10-0-1-advertise-libxml2-tbd-as-system-libs/8593). You can install the older 10.0.0_3 by doing
|
||||
By default homebrew will try to install llvm 11, which is currently
|
||||
unsupported. You need to install an older version (10.0.0_3) by doing:
|
||||
|
||||
```
|
||||
$ brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/6616d50fb0b24dbe30f5e975210bdad63257f517/Formula/llvm.rb
|
||||
$ brew edit llvm
|
||||
|
||||
# Replace the contents of the file with https://raw.githubusercontent.com/Homebrew/homebrew-core/6616d50fb0b24dbe30f5e975210bdad63257f517/Formula/llvm.rb
|
||||
|
||||
# we expect llvm-as-10 to be present
|
||||
$ ln -s /usr/local/opt/llvm/bin/{llvm-as,llvm-as-10}
|
||||
|
||||
# "pinning" ensures that homebrew doesn't update it automatically
|
||||
$ brew pin llvm
|
||||
```
|
||||
|
||||
If that doesn't work and you get a `brew` error `Error: Calling Installation of llvm from a GitHub commit URL is disabled! Use 'brew extract llvm' to stable tap on GitHub instead.` while trying the above solution, you can follow the steps extracting the formula into your private tap (one public version is at `sladwig/tap/llvm`). If installing LLVM still fails, it might help to run `sudo xcode-select -r` before installing again.
|
||||
It might also be useful to add these exports to your shell:
|
||||
|
||||
```
|
||||
export PATH="/usr/local/opt/llvm/bin:$PATH"
|
||||
export LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"
|
||||
export CPPFLAGS="-I/usr/local/opt/llvm/include"
|
||||
```
|
||||
|
||||
If installing LLVM still fails, it might help to run `sudo xcode-select -r` before installing again.
|
||||
|
||||
### LLVM installation on Windows
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM rust:1.50-slim-buster
|
||||
FROM rust:1.51-slim-buster
|
||||
WORKDIR /earthbuild
|
||||
|
||||
prep-debian:
|
||||
|
|
|
@ -90,7 +90,11 @@ pub fn build_app<'a>() -> App<'a> {
|
|||
}
|
||||
|
||||
pub fn docs(files: Vec<PathBuf>) {
|
||||
roc_docs::generate(files, roc_builtins::std::standard_stdlib(), Path::new("./"))
|
||||
roc_docs::generate(
|
||||
files,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
Path::new("./generated-docs"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
||||
|
|
|
@ -54,7 +54,7 @@ mod cli_run {
|
|||
) {
|
||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!(compile_out.stderr);
|
||||
panic!("{}", compile_out.stderr);
|
||||
}
|
||||
assert!(compile_out.status.success());
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
interface Bool2
|
||||
interface Bool
|
||||
exposes [ not, and, or, xor, isEq, isNotEq ]
|
||||
imports []
|
||||
|
||||
## Returns #False when given #True, and vice versa.
|
||||
## Returns `False` when given `True`, and vice versa.
|
||||
not : [True, False] -> [True, False]
|
||||
|
||||
## Returns #True when given #True and #True, and #False when either argument is #False.
|
||||
## Returns `True` when given `True` and `True`, and `False` when either argument is `False`.
|
||||
##
|
||||
## `a && b` is shorthand for `Bool.and a b`
|
||||
##
|
||||
|
@ -39,7 +39,7 @@ not : [True, False] -> [True, False]
|
|||
and : Bool, Bool -> Bool
|
||||
|
||||
|
||||
## Returns #True when given #True for either argument, and #False only when given #False and #False.
|
||||
## Returns `True` when given `True` for either argument, and `False` only when given `False` and `False`.
|
||||
##
|
||||
## `a || b` is shorthand for `Bool.or a b`.
|
||||
##
|
||||
|
@ -55,14 +55,13 @@ and : Bool, Bool -> Bool
|
|||
##
|
||||
## In some languages, `&&` and `||` are special-cased in the compiler to skip
|
||||
## evaluating the expression after the operator under certain circumstances.
|
||||
##
|
||||
## In Roc, this is not the case. See the performance notes for #Bool.and for details.
|
||||
## # In Roc, this is not the case. See the performance notes for #Bool.and for details.
|
||||
or : Bool, Bool -> Bool
|
||||
|
||||
## Exclusive or
|
||||
xor : Bool, Bool -> Bool
|
||||
|
||||
## Returns #True if the two values are *structurally equal*, and #False otherwise.
|
||||
## Returns `True` if the two values are *structurally equal*, and `False` otherwise.
|
||||
##
|
||||
## `a == b` is shorthand for `Bool.isEq a b`
|
||||
##
|
||||
|
|
|
@ -51,7 +51,7 @@ interface Num2
|
|||
##
|
||||
## In practice, these are rarely needed. It's most common to write
|
||||
## number literals without any suffix.
|
||||
Num range : @Num range
|
||||
Num range : [ @Num range ]
|
||||
|
||||
## A fixed-size integer - that is, a number with no fractional component.
|
||||
##
|
||||
|
@ -102,21 +102,21 @@ Num range : @Num range
|
|||
## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly.
|
||||
## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.)
|
||||
## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds.
|
||||
Int size : Num (@Int size)
|
||||
Int size : Num [ @Int size ]
|
||||
|
||||
## A signed 8-bit integer, ranging from -128 to 127
|
||||
I8 : Int @I8
|
||||
U8 : Int @U8
|
||||
U16 : Int @U16
|
||||
I16 : Int @I16
|
||||
U32 : Int @U32
|
||||
I32 : Int @I32
|
||||
I64 : Int @I64
|
||||
U64 : Int @U64
|
||||
I128 : Int @I128
|
||||
U128 : Int @U128
|
||||
Ilen : Int @Ilen
|
||||
Nat : Int @Nat
|
||||
I8 : Int [ @I8 ]
|
||||
U8 : Int [ @U8 ]
|
||||
U16 : Int [ @U16 ]
|
||||
I16 : Int [ @I16 ]
|
||||
U32 : Int [ @U32 ]
|
||||
I32 : Int [ @I32 ]
|
||||
I64 : Int [ @I64 ]
|
||||
U64 : Int [ @U64 ]
|
||||
I128 : Int [ @I128 ]
|
||||
U128 : Int [ @U128 ]
|
||||
Ilen : Int [ @Ilen ]
|
||||
Nat : Int [ @Nat ]
|
||||
|
||||
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
|
||||
##
|
||||
|
@ -574,9 +574,9 @@ divRound : Int, Int -> Int
|
|||
|
||||
## Bitwise
|
||||
|
||||
xor : Int -> Int -> Int
|
||||
xor : Int, Int -> Int
|
||||
|
||||
and : Int -> Int -> Int
|
||||
and : Int, Int -> Int
|
||||
|
||||
not : Int -> Int
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
interface Str2
|
||||
exposes [ Str2, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
|
||||
interface Str
|
||||
exposes [ Str, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
|
||||
imports []
|
||||
## Types
|
||||
## # Types
|
||||
|
||||
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
||||
## to the basics.
|
||||
|
|
|
@ -28,7 +28,7 @@ mod test_fmt {
|
|||
|
||||
assert_eq!(buf, expected)
|
||||
}
|
||||
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
||||
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -90,9 +90,9 @@ pub enum OptLevel {
|
|||
Optimize,
|
||||
}
|
||||
|
||||
impl Into<OptimizationLevel> for OptLevel {
|
||||
fn into(self) -> OptimizationLevel {
|
||||
match self {
|
||||
impl From<OptLevel> for OptimizationLevel {
|
||||
fn from(level: OptLevel) -> Self {
|
||||
match level {
|
||||
OptLevel::Normal => OptimizationLevel::None,
|
||||
OptLevel::Optimize => OptimizationLevel::Aggressive,
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ pub enum RocCallResult<T> {
|
|||
Failure(*mut c_char),
|
||||
}
|
||||
|
||||
impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
||||
fn into(self) -> Result<T, String> {
|
||||
match self {
|
||||
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
||||
fn from(call_result: RocCallResult<T>) -> Self {
|
||||
match call_result {
|
||||
Success(value) => Ok(value),
|
||||
Failure(failure) => Err({
|
||||
let raw = unsafe { CString::from_raw(failure) };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_builtins::bitcode;
|
||||
|
|
|
@ -12,8 +12,6 @@ mod helpers;
|
|||
|
||||
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||
mod gen_num {
|
||||
//use roc_std::RocOrder;
|
||||
|
||||
#[test]
|
||||
fn i64_values() {
|
||||
assert_evals_to!("0", 0, i64);
|
||||
|
|
|
@ -5,6 +5,7 @@ use roc_can::builtins::builtin_defs_map;
|
|||
use roc_collections::all::MutMap;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
|
@ -18,6 +19,7 @@ fn promote_expr_to_module(src: &str) -> String {
|
|||
buffer
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn helper<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
|
|
|
@ -2265,6 +2265,10 @@ fn load_pkg_config<'a>(
|
|||
)))
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, parser_state)) => {
|
||||
let delta = bytes.len() - parser_state.bytes.len();
|
||||
let chomped = &bytes[..delta];
|
||||
let header_src = unsafe { std::str::from_utf8_unchecked(chomped) };
|
||||
|
||||
// make a Pkg-Config module that ultimately exposes `main` to the host
|
||||
let pkg_config_module_msg = fabricate_pkg_config_module(
|
||||
arena,
|
||||
|
@ -2275,6 +2279,7 @@ fn load_pkg_config<'a>(
|
|||
module_ids.clone(),
|
||||
ident_ids_by_module.clone(),
|
||||
&header,
|
||||
header_src,
|
||||
pkg_module_timing,
|
||||
)
|
||||
.1;
|
||||
|
@ -2870,18 +2875,24 @@ fn send_header<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
// TODO refactor so more logic is shared with `send_header`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
#[derive(Debug)]
|
||||
struct PlatformHeaderInfo<'a> {
|
||||
filename: PathBuf,
|
||||
is_root_module: bool,
|
||||
shorthand: &'a str,
|
||||
header_src: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
requires: &'a [Located<TypedIdent<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
}
|
||||
|
||||
// TODO refactor so more logic is shared with `send_header`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
info: PlatformHeaderInfo<'a>,
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
|
@ -2889,6 +2900,18 @@ fn send_header_two<'a>(
|
|||
) -> (ModuleId, Msg<'a>) {
|
||||
use inlinable_string::InlinableString;
|
||||
|
||||
let PlatformHeaderInfo {
|
||||
filename,
|
||||
shorthand,
|
||||
is_root_module,
|
||||
header_src,
|
||||
app_module_id,
|
||||
packages,
|
||||
provides,
|
||||
requires,
|
||||
imports,
|
||||
} = info;
|
||||
|
||||
let declared_name: InlinableString = "".into();
|
||||
|
||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||
|
@ -3080,7 +3103,7 @@ fn send_header_two<'a>(
|
|||
package_qualified_imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
header_src: "#builtin effect header",
|
||||
header_src,
|
||||
parse_state,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
|
@ -3209,21 +3232,27 @@ fn fabricate_pkg_config_module<'a>(
|
|||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
header: &PlatformHeader<'a>,
|
||||
header_src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
||||
header.provides.clone().into_bump_slice();
|
||||
|
||||
let info = PlatformHeaderInfo {
|
||||
filename,
|
||||
is_root_module: false,
|
||||
shorthand,
|
||||
header_src,
|
||||
app_module_id,
|
||||
packages: &[],
|
||||
provides,
|
||||
requires: header.requires.clone().into_bump_slice(),
|
||||
imports: header.imports.clone().into_bump_slice(),
|
||||
};
|
||||
|
||||
send_header_two(
|
||||
arena,
|
||||
filename,
|
||||
false,
|
||||
shorthand,
|
||||
app_module_id,
|
||||
&[],
|
||||
provides,
|
||||
header.requires.clone().into_bump_slice(),
|
||||
header.imports.clone().into_bump_slice(),
|
||||
info,
|
||||
parse_state,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
|
@ -4114,7 +4143,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn to_file_problem_report(filename: &PathBuf, error: io::ErrorKind) -> String {
|
||||
fn to_file_problem_report(filename: &Path, error: io::ErrorKind) -> String {
|
||||
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
|
|
|
@ -117,21 +117,21 @@ impl From<InlinableString> for ModuleName {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<InlinableString> for ModuleName {
|
||||
fn into(self) -> InlinableString {
|
||||
self.0
|
||||
impl From<ModuleName> for InlinableString {
|
||||
fn from(name: ModuleName) -> Self {
|
||||
name.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<&'a InlinableString> for &'a ModuleName {
|
||||
fn into(self) -> &'a InlinableString {
|
||||
&self.0
|
||||
impl<'a> From<&'a ModuleName> for &'a InlinableString {
|
||||
fn from(name: &'a ModuleName) -> Self {
|
||||
&name.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Box<str>> for ModuleName {
|
||||
fn into(self) -> Box<str> {
|
||||
self.0.to_string().into()
|
||||
impl From<ModuleName> for Box<str> {
|
||||
fn from(name: ModuleName) -> Self {
|
||||
name.0.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,9 +197,9 @@ impl<'a> From<String> for Lowercase {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<InlinableString> for Lowercase {
|
||||
fn into(self) -> InlinableString {
|
||||
self.0
|
||||
impl From<Lowercase> for InlinableString {
|
||||
fn from(lowercase: Lowercase) -> Self {
|
||||
lowercase.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,21 +234,21 @@ impl From<InlinableString> for Ident {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<InlinableString> for Ident {
|
||||
fn into(self) -> InlinableString {
|
||||
self.0
|
||||
impl From<Ident> for InlinableString {
|
||||
fn from(ident: Ident) -> Self {
|
||||
ident.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<&'a InlinableString> for &'a Ident {
|
||||
fn into(self) -> &'a InlinableString {
|
||||
&self.0
|
||||
impl<'a> From<&'a Ident> for &'a InlinableString {
|
||||
fn from(ident: &'a Ident) -> Self {
|
||||
&ident.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Box<str>> for Ident {
|
||||
fn into(self) -> Box<str> {
|
||||
self.0.to_string().into()
|
||||
impl From<Ident> for Box<str> {
|
||||
fn from(ident: Ident) -> Self {
|
||||
ident.0.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod ident;
|
||||
pub mod low_level;
|
||||
|
|
|
@ -424,6 +424,8 @@ impl<'a> Procs<'a> {
|
|||
is_self_recursive: bool,
|
||||
ret_var: Variable,
|
||||
) {
|
||||
let number_of_arguments = loc_args.len();
|
||||
|
||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
||||
Ok((_, pattern_symbols, body)) => {
|
||||
// a named closure. Since these aren't specialized by the surrounding
|
||||
|
@ -444,17 +446,22 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
|
||||
Err(error) => {
|
||||
// If the function has invalid patterns in its arguments,
|
||||
// its call sites will code gen to runtime errors. This happens
|
||||
// at the call site so we don't have to try to define the
|
||||
// function LLVM, which would be difficult considering LLVM
|
||||
// wants to know what symbols each argument corresponds to,
|
||||
// and in this case the patterns were invalid, so we don't know
|
||||
// what the symbols ought to be.
|
||||
let mut pattern_symbols = Vec::with_capacity_in(number_of_arguments, env.arena);
|
||||
|
||||
let error_msg = format!("TODO generate a RuntimeError message for {:?}", error);
|
||||
for _ in 0..number_of_arguments {
|
||||
pattern_symbols.push(env.unique_symbol());
|
||||
}
|
||||
|
||||
self.runtime_errors.insert(name, env.arena.alloc(error_msg));
|
||||
self.partial_procs.insert(
|
||||
name,
|
||||
PartialProc {
|
||||
annotation,
|
||||
pattern_symbols: pattern_symbols.into_bump_slice(),
|
||||
captured_symbols: CapturedSymbols::None,
|
||||
body: roc_can::expr::Expr::RuntimeError(error.value),
|
||||
is_self_recursive: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1788,10 +1795,11 @@ fn specialize_external<'a>(
|
|||
let snapshot = env.subs.snapshot();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
|
||||
let unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
||||
let _unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
||||
|
||||
let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||
debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
||||
// This will not hold for programs with type errors
|
||||
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||
// debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
||||
|
||||
// if this is a closure, add the closure record argument
|
||||
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
||||
|
@ -2131,9 +2139,7 @@ fn build_specialized_proc_adapter<'a>(
|
|||
arg_layouts.push(layout);
|
||||
}
|
||||
|
||||
let ret_layout = layout_cache
|
||||
.from_var(&env.arena, ret_var, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||
let ret_layout = layout_cache.from_var(&env.arena, ret_var, env.subs)?;
|
||||
|
||||
build_specialized_proc(
|
||||
env.arena,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod borrow;
|
||||
pub mod expand_rc;
|
||||
|
|
|
@ -125,7 +125,7 @@ where
|
|||
E: 'a,
|
||||
{
|
||||
move |_, state: State<'a>| {
|
||||
if state.column > min_indent {
|
||||
if state.column >= min_indent {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, indent_problem(state.line, state.column), state))
|
||||
|
|
|
@ -16,16 +16,33 @@ use roc_region::all::{Located, Position, Region};
|
|||
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
||||
fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||
|_arena, state: State<'a>| {
|
||||
if state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((
|
||||
NoProgress,
|
||||
EExpr::BadExprEnd(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_parse_expr<'a>(
|
||||
min_indent: u16,
|
||||
arena: &'a bumpalo::Bump,
|
||||
state: State<'a>,
|
||||
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
|
||||
let parser = space0_before_e(
|
||||
let parser = skip_second!(
|
||||
space0_before_e(
|
||||
move |a, s| parse_loc_expr(min_indent, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentStart,
|
||||
),
|
||||
expr_end()
|
||||
);
|
||||
|
||||
match parser.parse(arena, state) {
|
||||
|
@ -35,9 +52,27 @@ pub fn test_parse_expr<'a>(
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MultiBackpassing {
|
||||
Allow,
|
||||
Disallow,
|
||||
pub struct ExprParseOptions {
|
||||
/// Check for and accept multi-backpassing syntax
|
||||
/// This is usually true, but false within list/record literals
|
||||
/// because the comma separating backpassing arguments conflicts
|
||||
/// with the comma separating literal elements
|
||||
accept_multi_backpassing: bool,
|
||||
|
||||
/// Check for the `->` token, and raise an error if found
|
||||
/// This is usually true, but false in if-guards
|
||||
///
|
||||
/// > Just foo if foo == 2 -> ...
|
||||
check_for_arrow: bool,
|
||||
}
|
||||
|
||||
impl Default for ExprParseOptions {
|
||||
fn default() -> Self {
|
||||
ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
check_for_arrow: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
|
@ -159,7 +194,7 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
|
|||
|
||||
fn parse_loc_term<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
|
@ -167,10 +202,7 @@ fn parse_loc_term<'a>(
|
|||
loc_expr_in_parens_etc_help(min_indent),
|
||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||
loc!(specialize(
|
||||
EExpr::Lambda,
|
||||
closure_help(min_indent, multi_backpassing)
|
||||
)),
|
||||
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||
loc!(record_literal_help(min_indent)),
|
||||
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
||||
loc!(map_with_arena!(
|
||||
|
@ -183,17 +215,14 @@ fn parse_loc_term<'a>(
|
|||
|
||||
fn loc_possibly_negative_or_negated_term<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
one_of![
|
||||
|arena, state: State<'a>| {
|
||||
let initial = state;
|
||||
|
||||
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
a,
|
||||
s
|
||||
min_indent, options, a, s
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
@ -205,7 +234,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
|||
loc!(specialize(EExpr::Number, number_literal_help())),
|
||||
loc!(map_with_arena!(
|
||||
and!(loc!(word1(b'!', EExpr::Start)), |a, s| {
|
||||
parse_loc_term(min_indent, multi_backpassing, a, s)
|
||||
parse_loc_term(min_indent, options, a, s)
|
||||
}),
|
||||
|arena: &'a Bump, (loc_op, loc_expr): (Located<_>, _)| {
|
||||
Expr::UnaryOp(
|
||||
|
@ -216,7 +245,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
|||
)),
|
||||
|arena, state| {
|
||||
// TODO use parse_loc_term_better
|
||||
parse_loc_term(min_indent, multi_backpassing, arena, state)
|
||||
parse_loc_term(min_indent, options, arena, state)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -260,25 +289,19 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
|||
|
||||
fn parse_expr_start<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
one_of![
|
||||
loc!(specialize(
|
||||
EExpr::If,
|
||||
if_expr_help(min_indent, multi_backpassing)
|
||||
)),
|
||||
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
|
||||
loc!(specialize(
|
||||
EExpr::When,
|
||||
when::expr_help(min_indent, multi_backpassing)
|
||||
when::expr_help(min_indent, options)
|
||||
)),
|
||||
loc!(specialize(
|
||||
EExpr::Lambda,
|
||||
closure_help(min_indent, multi_backpassing)
|
||||
)),
|
||||
loc!(move |a, s| parse_expr_operator_chain(min_indent, multi_backpassing, start, a, s)),
|
||||
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||
loc!(move |a, s| parse_expr_operator_chain(min_indent, options, start, a, s)),
|
||||
fail_expr_start_e()
|
||||
]
|
||||
.parse(arena, state)
|
||||
|
@ -286,13 +309,13 @@ fn parse_expr_start<'a>(
|
|||
|
||||
fn parse_expr_operator_chain<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let (_, expr, state) =
|
||||
loc_possibly_negative_or_negated_term(min_indent, multi_backpassing).parse(arena, state)?;
|
||||
loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state)?;
|
||||
|
||||
let initial = state;
|
||||
let end = state.get_position();
|
||||
|
@ -309,14 +332,7 @@ fn parse_expr_operator_chain<'a>(
|
|||
end,
|
||||
};
|
||||
|
||||
parse_expr_end(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
start,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,7 +704,7 @@ struct DefState<'a> {
|
|||
}
|
||||
|
||||
fn parse_defs_end<'a>(
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
mut def_state: DefState<'a>,
|
||||
arena: &'a Bump,
|
||||
|
@ -743,7 +759,7 @@ fn parse_defs_end<'a>(
|
|||
loc_def_expr,
|
||||
);
|
||||
|
||||
parse_defs_end(multi_backpassing, start, def_state, arena, state)
|
||||
parse_defs_end(options, start, def_state, arena, state)
|
||||
}
|
||||
Ok((_, BinOp::HasType, state)) => {
|
||||
let (_, ann_type, state) = specialize(
|
||||
|
@ -765,7 +781,7 @@ fn parse_defs_end<'a>(
|
|||
ann_type,
|
||||
);
|
||||
|
||||
parse_defs_end(multi_backpassing, start, def_state, arena, state)
|
||||
parse_defs_end(options, start, def_state, arena, state)
|
||||
}
|
||||
|
||||
_ => Ok((MadeProgress, def_state, initial)),
|
||||
|
@ -774,7 +790,7 @@ fn parse_defs_end<'a>(
|
|||
}
|
||||
|
||||
fn parse_defs_expr<'a>(
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
def_state: DefState<'a>,
|
||||
arena: &'a Bump,
|
||||
|
@ -782,7 +798,7 @@ fn parse_defs_expr<'a>(
|
|||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let min_indent = start.col;
|
||||
|
||||
match parse_defs_end(multi_backpassing, start, def_state, arena, state) {
|
||||
match parse_defs_end(options, start, def_state, arena, state) {
|
||||
Err(bad) => Err(bad),
|
||||
Ok((_, def_state, state)) => {
|
||||
// this is no def, because there is no `=` or `:`; parse as an expr
|
||||
|
@ -815,7 +831,7 @@ fn parse_defs_expr<'a>(
|
|||
|
||||
fn parse_expr_operator<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
mut expr_state: ExprState<'a>,
|
||||
loc_op: Located<BinOp>,
|
||||
|
@ -835,8 +851,7 @@ fn parse_expr_operator<'a>(
|
|||
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
|
||||
// negative terms
|
||||
|
||||
let (_, negated_expr, state) =
|
||||
parse_loc_term(min_indent, multi_backpassing, arena, state)?;
|
||||
let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?;
|
||||
let new_end = state.get_position();
|
||||
|
||||
let arg = numeric_negate_expression(
|
||||
|
@ -859,14 +874,7 @@ fn parse_expr_operator<'a>(
|
|||
expr_state.spaces_after = spaces;
|
||||
expr_state.end = new_end;
|
||||
|
||||
parse_expr_end(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
start,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||
}
|
||||
BinOp::Assignment => {
|
||||
let expr_region = expr_state.expr.region;
|
||||
|
@ -915,7 +923,7 @@ fn parse_expr_operator<'a>(
|
|||
spaces_after: &[],
|
||||
};
|
||||
|
||||
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
|
||||
parse_defs_expr(options, start, def_state, arena, state)
|
||||
}
|
||||
BinOp::Backpassing => {
|
||||
let expr_region = expr_state.expr.region;
|
||||
|
@ -1062,11 +1070,9 @@ fn parse_expr_operator<'a>(
|
|||
spaces_after: &[],
|
||||
};
|
||||
|
||||
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
|
||||
parse_defs_expr(options, start, def_state, arena, state)
|
||||
}
|
||||
_ => match loc_possibly_negative_or_negated_term(min_indent, multi_backpassing)
|
||||
.parse(arena, state)
|
||||
{
|
||||
_ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) {
|
||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||
Ok((_, mut new_expr, state)) => {
|
||||
let new_end = state.get_position();
|
||||
|
@ -1104,14 +1110,7 @@ fn parse_expr_operator<'a>(
|
|||
expr_state.spaces_after = spaces;
|
||||
|
||||
// TODO new start?
|
||||
parse_expr_end(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
start,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1124,7 +1123,7 @@ fn parse_expr_operator<'a>(
|
|||
|
||||
fn parse_expr_end<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
start: Position,
|
||||
mut expr_state: ExprState<'a>,
|
||||
arena: &'a Bump,
|
||||
|
@ -1132,7 +1131,7 @@ fn parse_expr_end<'a>(
|
|||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let parser = skip_first!(
|
||||
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
|
||||
move |a, s| parse_loc_term(min_indent, multi_backpassing, a, s)
|
||||
move |a, s| parse_loc_term(min_indent, options, a, s)
|
||||
);
|
||||
|
||||
match parser.parse(arena, state) {
|
||||
|
@ -1163,14 +1162,7 @@ fn parse_expr_end<'a>(
|
|||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = new_spaces;
|
||||
|
||||
parse_expr_end(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
start,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1183,19 +1175,12 @@ fn parse_expr_end<'a>(
|
|||
expr_state.consume_spaces(arena);
|
||||
expr_state.initial = before_op;
|
||||
parse_expr_operator(
|
||||
min_indent,
|
||||
multi_backpassing,
|
||||
start,
|
||||
expr_state,
|
||||
loc_op,
|
||||
arena,
|
||||
state,
|
||||
min_indent, options, start, expr_state, loc_op, arena, state,
|
||||
)
|
||||
}
|
||||
Err((NoProgress, _, mut state)) => {
|
||||
// try multi-backpassing
|
||||
if multi_backpassing == MultiBackpassing::Allow && state.bytes.starts_with(b",")
|
||||
{
|
||||
if options.accept_multi_backpassing && state.bytes.starts_with(b",") {
|
||||
state.bytes = &state.bytes[1..];
|
||||
state.column += 1;
|
||||
|
||||
|
@ -1256,6 +1241,12 @@ fn parse_expr_end<'a>(
|
|||
Ok((MadeProgress, ret, state))
|
||||
}
|
||||
}
|
||||
} else if options.check_for_arrow && state.bytes.starts_with(b"->") {
|
||||
Err((
|
||||
MadeProgress,
|
||||
EExpr::BadOperator(&[b'-', b'>'], state.line, state.column),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
// roll back space parsing
|
||||
let state = expr_state.initial;
|
||||
|
@ -1273,7 +1264,15 @@ fn parse_loc_expr<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
parse_loc_expr_with_options(min_indent, MultiBackpassing::Allow, arena, state)
|
||||
parse_loc_expr_with_options(
|
||||
min_indent,
|
||||
ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
..Default::default()
|
||||
},
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_loc_expr_no_multi_backpassing<'a>(
|
||||
|
@ -1281,17 +1280,25 @@ pub fn parse_loc_expr_no_multi_backpassing<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
parse_loc_expr_with_options(min_indent, MultiBackpassing::Disallow, arena, state)
|
||||
parse_loc_expr_with_options(
|
||||
min_indent,
|
||||
ExprParseOptions {
|
||||
accept_multi_backpassing: false,
|
||||
..Default::default()
|
||||
},
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_loc_expr_with_options<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
let start = state.get_position();
|
||||
parse_expr_start(min_indent, multi_backpassing, start, arena, state)
|
||||
parse_expr_start(min_indent, options, start, arena, state)
|
||||
}
|
||||
|
||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||
|
@ -1443,8 +1450,13 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
|
|||
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||
|
||||
let start = state.get_position();
|
||||
let (_, def_state, state) =
|
||||
parse_defs_end(MultiBackpassing::Disallow, start, def_state, arena, state)?;
|
||||
|
||||
let options = ExprParseOptions {
|
||||
accept_multi_backpassing: false,
|
||||
check_for_arrow: true,
|
||||
};
|
||||
|
||||
let (_, def_state, state) = parse_defs_end(options, start, def_state, arena, state)?;
|
||||
|
||||
let (_, final_space, state) =
|
||||
space0_e(start.col, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||
|
@ -1482,7 +1494,7 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
|
|||
|
||||
fn closure_help<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
||||
map_with_arena!(
|
||||
skip_first!(
|
||||
|
@ -1510,7 +1522,7 @@ fn closure_help<'a>(
|
|||
// Parse the body
|
||||
space0_before_e(
|
||||
specialize_ref(ELambda::Body, move |arena, state| {
|
||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
min_indent,
|
||||
ELambda::Space,
|
||||
|
@ -1535,7 +1547,7 @@ mod when {
|
|||
/// Parser for when expressions.
|
||||
pub fn expr_help<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Expr<'a>, When<'a>> {
|
||||
then(
|
||||
and!(
|
||||
|
@ -1543,7 +1555,7 @@ mod when {
|
|||
skip_second!(
|
||||
space0_around_ee(
|
||||
specialize_ref(When::Condition, move |arena, state| {
|
||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
min_indent,
|
||||
When::Space,
|
||||
|
@ -1566,7 +1578,7 @@ mod when {
|
|||
// Everything in the branches must be indented at least as much as the case itself.
|
||||
let min_indent = case_indent;
|
||||
|
||||
let (p1, branches, state) = branches(min_indent).parse(arena, state)?;
|
||||
let (p1, branches, state) = branches(min_indent, options).parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
progress.or(p1),
|
||||
|
@ -1586,22 +1598,27 @@ mod when {
|
|||
}
|
||||
}
|
||||
|
||||
fn branches<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
|
||||
move |arena, state| {
|
||||
fn branches<'a>(
|
||||
min_indent: u16,
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let when_indent = state.indent_col;
|
||||
|
||||
let mut branches: Vec<'a, &'a WhenBranch<'a>> = Vec::with_capacity_in(2, arena);
|
||||
|
||||
// 1. Parse the first branch and get its indentation level. (It must be >= min_indent.)
|
||||
// 2. Parse the other branches. Their indentation levels must be == the first branch's.
|
||||
|
||||
let (_, (loc_first_patterns, loc_first_guard), state) =
|
||||
branch_alternatives(min_indent).parse(arena, state)?;
|
||||
let loc_first_pattern = loc_first_patterns.first().unwrap();
|
||||
let original_indent = loc_first_pattern.region.start_col;
|
||||
let indented_more = original_indent + 1;
|
||||
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), mut state) =
|
||||
branch_alternatives(min_indent, options, None).parse(arena, state)?;
|
||||
let original_indent = pattern_indent_level;
|
||||
|
||||
state.indent_col = pattern_indent_level;
|
||||
|
||||
// Parse the first "->" and the expression after it.
|
||||
let (_, loc_first_expr, mut state) =
|
||||
branch_result(indented_more).parse(arena, state)?;
|
||||
branch_result(original_indent + 1).parse(arena, state)?;
|
||||
|
||||
// Record this as the first branch, then optionally parse additional branches.
|
||||
branches.push(arena.alloc(WhenBranch {
|
||||
|
@ -1613,19 +1630,21 @@ mod when {
|
|||
let branch_parser = map!(
|
||||
and!(
|
||||
then(
|
||||
branch_alternatives(min_indent),
|
||||
move |_arena, state, _, (loc_patterns, loc_guard)| {
|
||||
match alternatives_indented_correctly(&loc_patterns, original_indent) {
|
||||
Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)),
|
||||
Err(indent) => Err((
|
||||
branch_alternatives(min_indent, options, Some(pattern_indent_level)),
|
||||
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
|
||||
if pattern_indent_level == indent_col {
|
||||
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
||||
} else {
|
||||
let indent = pattern_indent_level - indent_col;
|
||||
Err((
|
||||
MadeProgress,
|
||||
When::PatternAlignment(indent, state.line, state.column),
|
||||
state,
|
||||
)),
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
branch_result(indented_more)
|
||||
branch_result(original_indent + 1)
|
||||
),
|
||||
|((patterns, guard), expr)| {
|
||||
let patterns: Vec<'a, _> = patterns;
|
||||
|
@ -1655,16 +1674,62 @@ mod when {
|
|||
}
|
||||
}
|
||||
|
||||
Ok((MadeProgress, branches, state))
|
||||
Ok((
|
||||
MadeProgress,
|
||||
branches,
|
||||
State {
|
||||
indent_col: when_indent,
|
||||
..state
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing alternative patterns in when branches.
|
||||
fn branch_alternatives<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>), When<'a>> {
|
||||
options: ExprParseOptions,
|
||||
pattern_indent_level: Option<u16>,
|
||||
) -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(Col, Vec<'a, Located<Pattern<'a>>>),
|
||||
Option<Located<Expr<'a>>>,
|
||||
),
|
||||
When<'a>,
|
||||
> {
|
||||
let options = ExprParseOptions {
|
||||
check_for_arrow: false,
|
||||
..options
|
||||
};
|
||||
and!(
|
||||
sep_by1(word1(b'|', When::Bar), |arena, state| {
|
||||
branch_alternatives_help(min_indent, pattern_indent_level),
|
||||
one_of![
|
||||
map!(
|
||||
skip_first!(
|
||||
parser::keyword_e(keyword::IF, When::IfToken),
|
||||
// TODO we should require space before the expression but not after
|
||||
space0_around_ee(
|
||||
specialize_ref(When::IfGuard, move |arena, state| {
|
||||
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
|
||||
}),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentIfGuard,
|
||||
When::IndentArrow,
|
||||
)
|
||||
),
|
||||
Some
|
||||
),
|
||||
|_, s| Ok((NoProgress, None, s))
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn branch_single_alternative<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
|
||||
move |arena, state| {
|
||||
let (_, spaces, state) =
|
||||
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
|
||||
.parse(arena, state)?;
|
||||
|
@ -1688,45 +1753,76 @@ mod when {
|
|||
},
|
||||
state,
|
||||
))
|
||||
}),
|
||||
one_of![
|
||||
map!(
|
||||
skip_first!(
|
||||
parser::keyword_e(keyword::IF, When::IfToken),
|
||||
// TODO we should require space before the expression but not after
|
||||
space0_around_ee(
|
||||
specialize_ref(When::IfGuard, move |arena, state| {
|
||||
parse_loc_expr(min_indent, arena, state)
|
||||
}),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentIfGuard,
|
||||
When::IndentArrow,
|
||||
)
|
||||
),
|
||||
Some
|
||||
),
|
||||
|_, s| Ok((NoProgress, None, s))
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if alternatives of a when branch are indented correctly.
|
||||
fn alternatives_indented_correctly<'a>(
|
||||
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>,
|
||||
original_indent: u16,
|
||||
) -> Result<(), u16> {
|
||||
let (first, rest) = loc_patterns.split_first().unwrap();
|
||||
let first_indented_correctly = first.region.start_col == original_indent;
|
||||
if first_indented_correctly {
|
||||
for when_pattern in rest.iter() {
|
||||
if when_pattern.region.start_col < original_indent {
|
||||
return Err(original_indent - when_pattern.region.start_col);
|
||||
fn branch_alternatives_help<'a>(
|
||||
min_indent: u16,
|
||||
pattern_indent_level: Option<u16>,
|
||||
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), When<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let initial = state;
|
||||
|
||||
// put no restrictions on the indent after the spaces; we'll check it manually
|
||||
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
|
||||
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||
Ok((_progress, spaces, state)) => {
|
||||
match pattern_indent_level {
|
||||
Some(wanted) if state.column > wanted => {
|
||||
// this branch is indented too much
|
||||
Err((
|
||||
NoProgress,
|
||||
When::IndentPattern(state.line, state.column),
|
||||
initial,
|
||||
))
|
||||
}
|
||||
Some(wanted) if state.column < wanted => {
|
||||
let indent = wanted - state.column;
|
||||
Err((
|
||||
NoProgress,
|
||||
When::PatternAlignment(indent, state.line, state.column),
|
||||
initial,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let pattern_indent =
|
||||
min_indent.max(pattern_indent_level.unwrap_or(min_indent));
|
||||
// the region is not reliable for the indent col in the case of
|
||||
// parentheses around patterns
|
||||
let pattern_indent_col = state.column;
|
||||
|
||||
let parser = sep_by1(
|
||||
word1(b'|', When::Bar),
|
||||
branch_single_alternative(pattern_indent + 1),
|
||||
);
|
||||
|
||||
match parser.parse(arena, state) {
|
||||
Err((MadeProgress, fail, state)) => {
|
||||
Err((MadeProgress, fail, state))
|
||||
}
|
||||
Err((NoProgress, fail, _)) => {
|
||||
// roll back space parsing if the pattern made no progress
|
||||
Err((NoProgress, fail, initial))
|
||||
}
|
||||
|
||||
Ok((_, mut loc_patterns, state)) => {
|
||||
// tag spaces onto the first parsed pattern
|
||||
if !spaces.is_empty() {
|
||||
if let Some(first) = loc_patterns.get_mut(0) {
|
||||
*first = arena
|
||||
.alloc(first.value)
|
||||
.with_spaces_before(spaces, first.region);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((MadeProgress, (pattern_indent_col, loc_patterns), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(original_indent - first.region.start_col)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1789,7 +1885,7 @@ fn if_branch<'a>(
|
|||
|
||||
fn if_expr_help<'a>(
|
||||
min_indent: u16,
|
||||
multi_backpassing: MultiBackpassing,
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
||||
move |arena: &'a Bump, state| {
|
||||
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
|
||||
|
@ -1821,7 +1917,7 @@ fn if_expr_help<'a>(
|
|||
|
||||
let (_, else_branch, state) = space0_before_e(
|
||||
specialize_ref(If::ElseBranch, move |arena, state| {
|
||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
min_indent,
|
||||
If::Space,
|
||||
|
|
|
@ -42,15 +42,15 @@ pub enum PackageOrPath<'a> {
|
|||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ModuleName<'a>(&'a str);
|
||||
|
||||
impl<'a> Into<&'a str> for ModuleName<'a> {
|
||||
fn into(self) -> &'a str {
|
||||
self.0
|
||||
impl<'a> From<ModuleName<'a>> for &'a str {
|
||||
fn from(name: ModuleName<'a>) -> Self {
|
||||
name.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<InlinableString> for ModuleName<'a> {
|
||||
fn into(self) -> InlinableString {
|
||||
self.0.into()
|
||||
impl<'a> From<ModuleName<'a>> for InlinableString {
|
||||
fn from(name: ModuleName<'a>) -> InlinableString {
|
||||
name.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ pub enum SyntaxError<'a> {
|
|||
ReservedKeyword(Region),
|
||||
ArgumentsBeforeEquals(Region),
|
||||
NotYetImplemented(String),
|
||||
TODO,
|
||||
Todo,
|
||||
Type(Type<'a>),
|
||||
Pattern(EPattern<'a>),
|
||||
Expr(EExpr<'a>),
|
||||
|
@ -381,6 +381,7 @@ pub type Col = u16;
|
|||
pub enum EExpr<'a> {
|
||||
Start(Row, Col),
|
||||
End(Row, Col),
|
||||
BadExprEnd(Row, Col),
|
||||
Space(BadInputError, Row, Col),
|
||||
|
||||
Dot(Row, Col),
|
||||
|
@ -930,8 +931,8 @@ where
|
|||
state = next_state;
|
||||
buf.push(next_output);
|
||||
}
|
||||
Err((element_progress, fail, state)) => {
|
||||
return Err((element_progress, fail, state));
|
||||
Err((_, fail, state)) => {
|
||||
return Err((MadeProgress, fail, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>(
|
|||
EPattern::Record,
|
||||
crate::pattern::record_pattern_help(min_indent)
|
||||
)),
|
||||
loc!(number_pattern_help()),
|
||||
loc!(string_pattern_help()),
|
||||
loc!(number_pattern_help())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2550,6 +2550,247 @@ mod test_parse {
|
|||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_with_negative_numbers() {
|
||||
let arena = Bump::new();
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
|
||||
let expr1 = Num("2");
|
||||
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("-3")), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 3, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 7, 8, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
when x is
|
||||
1 -> 2
|
||||
-3 -> 4
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_with_function_application() {
|
||||
let arena = Bump::new();
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
|
||||
let num_2 = Num("2");
|
||||
let num_neg = Located::new(
|
||||
1,
|
||||
1,
|
||||
9,
|
||||
16,
|
||||
Expr::Var {
|
||||
module_name: "Num",
|
||||
ident: "neg",
|
||||
},
|
||||
);
|
||||
let expr0 = Located::new(2, 2, 5, 6, Expr::SpaceBefore(&num_2, &[Newline]));
|
||||
let expr1 = Expr::Apply(
|
||||
&num_neg,
|
||||
&*arena.alloc([&*arena.alloc(expr0)]),
|
||||
CalledVia::Space,
|
||||
);
|
||||
let loc_expr1 = Located::new(1, 2, 9, 6, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
|
||||
let loc_pattern2 = Located::new(3, 3, 4, 5, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(3, 3, 9, 10, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
when x is
|
||||
1 -> Num.neg
|
||||
2
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_if_guard() {
|
||||
let arena = Bump::new();
|
||||
|
||||
let branch1 = {
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
|
||||
let num_1 = Num("1");
|
||||
let expr1 = Located::new(
|
||||
2,
|
||||
2,
|
||||
8,
|
||||
9,
|
||||
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||
);
|
||||
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||
&*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
})
|
||||
};
|
||||
|
||||
let branch2 = {
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), &[Newline, Newline]);
|
||||
let loc_pattern1 = Located::new(4, 4, 4, 5, pattern1);
|
||||
let num_1 = Num("2");
|
||||
let expr1 = Located::new(
|
||||
5,
|
||||
5,
|
||||
8,
|
||||
9,
|
||||
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||
);
|
||||
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||
&*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
})
|
||||
};
|
||||
|
||||
let branch3 = {
|
||||
let pattern1 =
|
||||
Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), &[Newline, Newline]);
|
||||
let loc_pattern1 = Located::new(7, 7, 4, 6, pattern1);
|
||||
let num_1 = Num("3");
|
||||
let expr1 = Located::new(
|
||||
8,
|
||||
8,
|
||||
8,
|
||||
9,
|
||||
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||
);
|
||||
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||
&*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
})
|
||||
};
|
||||
|
||||
let branches = &[branch1, branch2, branch3];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
when x is
|
||||
_ ->
|
||||
1
|
||||
|
||||
_ ->
|
||||
2
|
||||
|
||||
Ok ->
|
||||
3
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_in_parens() {
|
||||
let arena = Bump::new();
|
||||
|
||||
let branch1 = {
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 6, pattern1);
|
||||
let num_1 = Num("3");
|
||||
let expr1 = Located::new(
|
||||
2,
|
||||
2,
|
||||
8,
|
||||
9,
|
||||
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||
);
|
||||
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||
&*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
})
|
||||
};
|
||||
|
||||
let branches = &[branch1];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
let loc_cond = Located::new(0, 0, 6, 7, var);
|
||||
let when = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let expected = Expr::ParensAround(&when);
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
(when x is
|
||||
Ok ->
|
||||
3)
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_with_records() {
|
||||
let arena = Bump::new();
|
||||
|
@ -2599,6 +2840,47 @@ mod test_parse {
|
|||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_in_parens_indented() {
|
||||
let arena = Bump::new();
|
||||
|
||||
let branch1 = {
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 6, pattern1);
|
||||
let num_1 = Num("3");
|
||||
let expr1 = Located::new(1, 1, 10, 11, num_1);
|
||||
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||
&*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
})
|
||||
};
|
||||
|
||||
let branches = &[branch1];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
let loc_cond = Located::new(0, 0, 6, 7, var);
|
||||
let when = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let spaced = Expr::SpaceAfter(&when, &[Newline]);
|
||||
let expected = Expr::ParensAround(&spaced);
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
(when x is
|
||||
Ok -> 3
|
||||
)
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_with_alternative_patterns() {
|
||||
let arena = Bump::new();
|
||||
|
@ -2620,9 +2902,9 @@ mod test_parse {
|
|||
let pattern2_alt =
|
||||
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
||||
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
|
||||
let loc_pattern2_alt = Located::new(3, 3, 2, 7, pattern2_alt);
|
||||
let expr2 = Num("2");
|
||||
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
|
||||
let loc_expr2 = Located::new(3, 3, 11, 12, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
||||
value: loc_expr2,
|
||||
|
|
|
@ -272,7 +272,14 @@ fn to_expr_report<'a>(
|
|||
])
|
||||
.indent(4),
|
||||
])],
|
||||
b"->" => vec![alloc.stack(vec![
|
||||
b"->" => match context {
|
||||
Context::InNode(Node::WhenBranch, _row, _col, _) => {
|
||||
return to_unexpected_arrow_report(
|
||||
alloc, filename, *row, *col, start_row, start_col,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
vec![alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The arrow "),
|
||||
alloc.parser_suggestion("->"),
|
||||
|
@ -287,7 +294,9 @@ fn to_expr_report<'a>(
|
|||
alloc.text("Green -> \"go!\"").indent(4),
|
||||
])
|
||||
.indent(4),
|
||||
])],
|
||||
])]
|
||||
}
|
||||
},
|
||||
b"!" => vec![
|
||||
alloc.reflow("The boolean negation operator "),
|
||||
alloc.parser_suggestion("!"),
|
||||
|
@ -458,6 +467,27 @@ fn to_expr_report<'a>(
|
|||
*col,
|
||||
),
|
||||
|
||||
EExpr::BadExprEnd(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I got stuck here:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Whatever I am running into is confusing me a lot! "),
|
||||
alloc.reflow("Normally I can give fairly specific hints, "),
|
||||
alloc.reflow("but something is really tripping me up this time."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "SYNTAX PROBLEM".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
EExpr::Colon(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
@ -982,7 +1012,7 @@ fn to_list_report<'a>(
|
|||
),
|
||||
|
||||
List::Open(row, col) | List::End(row, col) => {
|
||||
match dbg!(what_is_next(alloc.src_lines, row, col)) {
|
||||
match what_is_next(alloc.src_lines, row, col) {
|
||||
Next::Other(Some(',')) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
@ -1388,6 +1418,12 @@ fn to_unfinished_when_report<'a>(
|
|||
start_col: Col,
|
||||
message: RocDocBuilder<'a>,
|
||||
) -> Report<'a> {
|
||||
match what_is_next(alloc.src_lines, row, col) {
|
||||
Next::Token("->") => {
|
||||
to_unexpected_arrow_report(alloc, filename, row, col, start_row, start_col)
|
||||
}
|
||||
|
||||
_ => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
|
@ -1407,6 +1443,43 @@ fn to_unfinished_when_report<'a>(
|
|||
doc,
|
||||
title: "UNFINISHED WHEN".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_unexpected_arrow_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
row: Row,
|
||||
col: Col,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
) -> Report<'a> {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_rows_cols(row, col, row, col + 2);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I am parsing a "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
|
||||
]),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"It makes sense to see arrows around here, "),
|
||||
alloc.reflow(r"so I suspect it is something earlier."),
|
||||
alloc.reflow(
|
||||
r"Maybe this pattern is indented a bit farther from the previous patterns?",
|
||||
),
|
||||
]),
|
||||
note_for_when_error(alloc),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNEXPECTED ARROW".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn note_for_when_error<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||
|
|
|
@ -4967,7 +4967,6 @@ mod test_reporting {
|
|||
|
||||
#[test]
|
||||
fn empty_or_pattern() {
|
||||
// this should get better with time
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -4981,29 +4980,16 @@ mod test_reporting {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── MISSING EXPRESSION ──────────────────────────────────────────────────────────
|
||||
── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
I just started parsing a pattern, but I got stuck here:
|
||||
|
||||
1│ when Just 4 is
|
||||
2│ Just 4 | ->
|
||||
^
|
||||
|
||||
I was expecting to see an expression like 42 or "hello".
|
||||
Note: I may be confused by indentation
|
||||
"#
|
||||
),
|
||||
// indoc!(
|
||||
// r#"
|
||||
// ── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
|
||||
//
|
||||
// I just started parsing a pattern, but I got stuck here:
|
||||
//
|
||||
// 2│ Just 4 | ->
|
||||
// ^
|
||||
//
|
||||
// Note: I may be confused by indentation
|
||||
// "#
|
||||
// ),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -5130,6 +5116,57 @@ mod test_reporting {
|
|||
#[test]
|
||||
fn when_outdented_branch() {
|
||||
// this should get better with time
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
when 4 is
|
||||
5 -> 2
|
||||
2 -> 2
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||
|
||||
I got stuck here:
|
||||
|
||||
1│ when 4 is
|
||||
2│ 5 -> 2
|
||||
^
|
||||
|
||||
Whatever I am running into is confusing me a lot! Normally I can give
|
||||
fairly specific hints, but something is really tripping me up this
|
||||
time.
|
||||
"#
|
||||
),
|
||||
// TODO this formerly gave
|
||||
//
|
||||
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||||
//
|
||||
// I was partway through parsing a `when` expression, but I got stuck here:
|
||||
//
|
||||
// 3│ _ -> 2
|
||||
// ^
|
||||
//
|
||||
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
||||
//
|
||||
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
|
||||
// we find an efficient solution that doesn't require parsing an extra pattern for
|
||||
// every `when`, i.e. we want a good error message for the test case above, but for
|
||||
// a valid `when`, we don't want to do extra work, e.g. here
|
||||
//
|
||||
// x
|
||||
// when x is
|
||||
// n -> n
|
||||
//
|
||||
// 4
|
||||
//
|
||||
// We don't want to parse the `4` and say it's an outdented pattern!
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_over_indented_underscore() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -5140,14 +5177,45 @@ mod test_reporting {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||||
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||
|
||||
I was partway through parsing a `when` expression, but I got stuck here:
|
||||
I got stuck here:
|
||||
|
||||
3│ _ -> 2
|
||||
1│ when 4 is
|
||||
2│ 5 -> 2
|
||||
^
|
||||
|
||||
I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
||||
Whatever I am running into is confusing me a lot! Normally I can give
|
||||
fairly specific hints, but something is really tripping me up this
|
||||
time.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_over_indented_int() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
when 4 is
|
||||
5 -> Num.neg
|
||||
2 -> 2
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNEXPECTED ARROW ────────────────────────────────────────────────────────────
|
||||
|
||||
I am parsing a `when` expression right now, but this arrow is confusing
|
||||
me:
|
||||
|
||||
3│ 2 -> 2
|
||||
^^
|
||||
|
||||
It makes sense to see arrows around here, so I suspect it is something
|
||||
earlier.Maybe this pattern is indented a bit farther from the previous
|
||||
patterns?
|
||||
|
||||
Note: Here is an example of a valid `when` expression for reference.
|
||||
|
||||
|
@ -5809,21 +5877,18 @@ mod test_reporting {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── MISSING FINAL EXPRESSION ────────────────────────────────────────────────────
|
||||
── UNKNOWN OPERATOR ────────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a definition's final expression, but I
|
||||
got stuck here:
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
|
||||
1│ main = 5 -> 3
|
||||
^
|
||||
^^
|
||||
|
||||
This definition is missing a final expression. A nested definition
|
||||
must be followed by either another definition, or an expression
|
||||
The arrow -> is only used to define cases in a `when`.
|
||||
|
||||
x = 4
|
||||
y = 2
|
||||
|
||||
x + y
|
||||
when color is
|
||||
Red -> "stop!"
|
||||
Green -> "go!"
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
|
|
@ -2258,3 +2258,21 @@ fn backpassing_result() {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Shadowing { original_region: |L 3-3, C 4-5|, shadow: |L 5-5, C 6-7| Ident(\\\"x\\\") }"
|
||||
)]
|
||||
fn function_malformed_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 3
|
||||
|
||||
(\x -> x) 42
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -93,9 +93,9 @@ impl VarStore {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Variable> for VarStore {
|
||||
fn into(self) -> Variable {
|
||||
Variable(self.next)
|
||||
impl From<VarStore> for Variable {
|
||||
fn from(store: VarStore) -> Self {
|
||||
Variable(store.next)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,9 +139,9 @@ impl fmt::Debug for OptVariable {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Option<Variable>> for OptVariable {
|
||||
fn into(self) -> Option<Variable> {
|
||||
self.into_variable()
|
||||
impl From<OptVariable> for Option<Variable> {
|
||||
fn from(opt_var: OptVariable) -> Self {
|
||||
opt_var.into_variable()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,9 +180,9 @@ impl Variable {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<OptVariable> for Variable {
|
||||
fn into(self) -> OptVariable {
|
||||
OptVariable(self.0)
|
||||
impl From<Variable> for OptVariable {
|
||||
fn from(var: Variable) -> Self {
|
||||
OptVariable(var.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,9 +483,9 @@ impl fmt::Debug for Rank {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for Rank {
|
||||
fn into(self) -> usize {
|
||||
self.0
|
||||
impl From<Rank> for usize {
|
||||
fn from(rank: Rank) -> Self {
|
||||
rank.0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,29 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
|
|||
modules: files_docs,
|
||||
};
|
||||
|
||||
if !build_dir.exists() {
|
||||
fs::create_dir_all(build_dir).expect("TODO gracefully handle unable to create build dir");
|
||||
}
|
||||
|
||||
// Copy over the assets
|
||||
fs::write(
|
||||
build_dir.join("search.js"),
|
||||
include_str!("./static/search.js"),
|
||||
)
|
||||
.expect("TODO gracefully handle failing to make the search javascript");
|
||||
|
||||
fs::write(
|
||||
build_dir.join("styles.css"),
|
||||
include_str!("./static/styles.css"),
|
||||
)
|
||||
.expect("TODO gracefully handle failing to make the stylesheet");
|
||||
|
||||
fs::write(
|
||||
build_dir.join("favicon.svg"),
|
||||
include_str!("./static/favicon.svg"),
|
||||
)
|
||||
.expect("TODO gracefully handle failing to make the favicon");
|
||||
|
||||
// Register handlebars template
|
||||
let mut handlebars = handlebars::Handlebars::new();
|
||||
handlebars
|
||||
|
@ -75,7 +98,7 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
|
|||
.expect("TODO gracefully handle writing file failing");
|
||||
}
|
||||
|
||||
println!("Docs generated at {}", build_dir.display());
|
||||
println!("🎉 Docs generated in {}", build_dir.display());
|
||||
}
|
||||
|
||||
pub fn files_to_documentations(
|
||||
|
|
|
@ -66,16 +66,14 @@ a:hover {
|
|||
}
|
||||
|
||||
.pkg-and-logo {
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: var(--top-bar-bg);
|
||||
}
|
||||
|
||||
.pkg-and-logo a,
|
||||
.pkg-and-logo a:visited {
|
||||
.pkg-and-logo a, .pkg-and-logo a:visited {
|
||||
color: var(--top-bar-fg);
|
||||
}
|
||||
|
||||
|
@ -84,18 +82,11 @@ a:hover {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
.search-button {
|
||||
flex-shrink: 0;
|
||||
/* always shrink the package name before these; they have a relatively constrained length */
|
||||
flex-shrink: 0; /* always shrink the package name before these; they have a relatively constrained length */
|
||||
padding: 12px 18px;
|
||||
margin-right: 42px;
|
||||
display: none;
|
||||
/* only show this in the mobile view */
|
||||
display: none; /* only show this in the mobile view */
|
||||
}
|
||||
|
||||
.version {
|
||||
|
@ -127,6 +118,8 @@ main {
|
|||
line-height: 1.85em;
|
||||
margin-top: 2px;
|
||||
padding: 48px;
|
||||
|
||||
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
#sidebar-nav {
|
||||
|
@ -160,13 +153,11 @@ main {
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 24px;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
.top-header-triangle {
|
||||
|
@ -240,8 +231,7 @@ footer p {
|
|||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.module-name a,
|
||||
.module-name a:visited {
|
||||
.module-name a, .module-name a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
@ -259,8 +249,7 @@ footer p {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited {
|
||||
a, a:visited {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
|
@ -325,20 +314,19 @@ pre code {
|
|||
height: 0;
|
||||
}
|
||||
|
||||
#module-search,
|
||||
#module-search:focus {
|
||||
#module-search, #module-search:focus {
|
||||
opacity: 1;
|
||||
padding: 12px 16px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
/* Show the "Search" label link when the text input has a placeholder */
|
||||
#module-search:placeholder-shown+#search-link {
|
||||
#module-search:placeholder-shown + #search-link {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Hide the "Search" label link when the text input has focus */
|
||||
#module-search:focus+#search-link {
|
||||
#module-search:focus + #search-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -398,13 +386,13 @@ pre code {
|
|||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
@media only screen and (max-device-width: 480px) and (orientation: portrait) {
|
||||
.search-button {
|
||||
display: block;
|
||||
/* This is only visible in mobile. */
|
||||
display: block; /* This is only visible in mobile. */
|
||||
}
|
||||
|
||||
.top-header {
|
||||
justify-content: space-between;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
@ -443,21 +431,19 @@ pre code {
|
|||
}
|
||||
|
||||
main {
|
||||
grid-column-start: none;
|
||||
grid-column-end: none;
|
||||
grid-row-start: above-footer;
|
||||
grid-row-end: above-footer;
|
||||
padding: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
#sidebar-nav {
|
||||
grid-column-start: none;
|
||||
grid-column-end: none;
|
||||
grid-row-start: sidebar;
|
||||
grid-row-end: sidebar;
|
||||
margin-top: 0;
|
||||
padding-left: 0;
|
||||
width: auto;
|
||||
|
@ -478,12 +464,30 @@ pre code {
|
|||
font-size: 16px;
|
||||
}
|
||||
|
||||
.top-header {
|
||||
justify-content: space-between;
|
||||
body {
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: [top-header] var(--top-header-height) [before-sidebar] auto [sidebar] auto [above-footer] auto [footer] auto;
|
||||
/* [before-sidebar] 1fr [sidebar] var(--sidebar-width) [main-content] fit-content(calc(1280px - var(--sidebar-width))) [end] 1fr; */
|
||||
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
/* Display the sidebar below <main> without affecting tab index */
|
||||
flex-direction: column-reverse;
|
||||
.top-header-triangle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pkg-and-logo {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pkg-full-name {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pkg-full-name a {
|
||||
padding-top: 24px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
}
|
|
@ -6,9 +6,9 @@
|
|||
<title>The Roc Programming Language</title>
|
||||
<meta name="description" content="A language for building fast, reliable software.">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<script type="text/javascript" src="/search.js" defer></script>
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
<link rel="icon" href="favicon.svg">
|
||||
<script type="text/javascript" src="search.js" defer></script>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -96,14 +96,12 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
new_markup_node(text, node_id, HighlightStyle::Variable, markup_node_pool)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut children_ids = Vec::new();
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
let mut children_ids = vec![new_markup_node(
|
||||
"[ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
)];
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
@ -135,14 +133,12 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
markup_node_pool.add(list_node)
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut children_ids = Vec::new();
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
let mut children_ids = vec![new_markup_node(
|
||||
"{ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
)];
|
||||
|
||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(field_node_id);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate indoc;
|
||||
|
|
|
@ -59,7 +59,10 @@ pub export fn main() u8 {
|
|||
|
||||
call_the_closure(function_pointer, closure_data_pointer);
|
||||
} else {
|
||||
unreachable;
|
||||
const msg = @intToPtr([*:0]const u8, elements[1]);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
var ts2: std.os.timespec = undefined;
|
||||
|
|
|
@ -502,11 +502,11 @@ pub enum RocCallResult<T> {
|
|||
Failure(*mut c_char),
|
||||
}
|
||||
|
||||
impl<T: Sized> Into<Result<T, &'static str>> for RocCallResult<T> {
|
||||
fn into(self) -> Result<T, &'static str> {
|
||||
impl<T: Sized> From<RocCallResult<T>> for Result<T, &'static str> {
|
||||
fn from(call_result: RocCallResult<T>) -> Self {
|
||||
use RocCallResult::*;
|
||||
|
||||
match self {
|
||||
match call_result {
|
||||
Success(value) => Ok(value),
|
||||
Failure(failure) => Err({
|
||||
let msg = unsafe {
|
||||
|
@ -529,11 +529,11 @@ impl<T: Sized> Into<Result<T, &'static str>> for RocCallResult<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Sized + Copy> Into<Result<T, &'a str>> for &'a RocCallResult<T> {
|
||||
fn into(self) -> Result<T, &'a str> {
|
||||
impl<'a, T: Sized + Copy> From<&'a RocCallResult<T>> for Result<T, &'a str> {
|
||||
fn from(call_result: &'a RocCallResult<T>) -> Self {
|
||||
use RocCallResult::*;
|
||||
|
||||
match self {
|
||||
match call_result {
|
||||
Success(value) => Ok(*value),
|
||||
Failure(failure) => Err({
|
||||
let msg = unsafe {
|
||||
|
|
13
vendor/ena/src/unify/mod.rs
vendored
13
vendor/ena/src/unify/mod.rs
vendored
|
@ -31,6 +31,7 @@
|
|||
//! The best way to see how it is used is to read the `tests.rs` file;
|
||||
//! search for e.g. `UnitKey`.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker;
|
||||
use std::ops::Range;
|
||||
|
@ -361,19 +362,25 @@ impl<S: UnificationStore> UnificationTable<S> {
|
|||
}
|
||||
};
|
||||
self.redirect_root(new_rank, redirected, new_root, new_value);
|
||||
} else if rank_a > rank_b {
|
||||
} else {
|
||||
match rank_a.cmp(&rank_b) {
|
||||
Ordering::Greater => {
|
||||
// a has greater rank, so a should become b's parent,
|
||||
// i.e., b should redirect to a.
|
||||
self.redirect_root(rank_a, key_b, key_a, new_value);
|
||||
} else if rank_a < rank_b {
|
||||
}
|
||||
Ordering::Less => {
|
||||
// b has greater rank, so a should redirect to b.
|
||||
self.redirect_root(rank_b, key_a, key_b, new_value);
|
||||
} else {
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// If equal, redirect one to the other and increment the
|
||||
// other's rank.
|
||||
self.redirect_root(rank_a + 1, key_a, key_b, new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to redirect `old_root_key` (which is currently
|
||||
/// a root) to a child of `new_root_key` (which will remain a
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue