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
|
### 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):
|
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)"
|
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
|
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
|
### 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
|
# "pinning" ensures that homebrew doesn't update it automatically
|
||||||
$ brew pin llvm
|
$ 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
|
### LLVM installation on Windows
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM rust:1.50-slim-buster
|
FROM rust:1.51-slim-buster
|
||||||
WORKDIR /earthbuild
|
WORKDIR /earthbuild
|
||||||
|
|
||||||
prep-debian:
|
prep-debian:
|
||||||
|
|
|
@ -90,7 +90,11 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn docs(files: Vec<PathBuf>) {
|
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<()> {
|
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());
|
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
panic!(compile_out.stderr);
|
panic!("{}", compile_out.stderr);
|
||||||
}
|
}
|
||||||
assert!(compile_out.status.success());
|
assert!(compile_out.status.success());
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
interface Bool2
|
interface Bool
|
||||||
exposes [ not, and, or, xor, isEq, isNotEq ]
|
exposes [ not, and, or, xor, isEq, isNotEq ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Returns #False when given #True, and vice versa.
|
## Returns `False` when given `True`, and vice versa.
|
||||||
not : [True, False] -> [True, False]
|
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`
|
## `a && b` is shorthand for `Bool.and a b`
|
||||||
##
|
##
|
||||||
|
@ -39,7 +39,7 @@ not : [True, False] -> [True, False]
|
||||||
and : Bool, Bool -> Bool
|
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`.
|
## `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
|
## In some languages, `&&` and `||` are special-cased in the compiler to skip
|
||||||
## evaluating the expression after the operator under certain circumstances.
|
## 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
|
or : Bool, Bool -> Bool
|
||||||
|
|
||||||
## Exclusive or
|
## Exclusive or
|
||||||
xor : Bool, Bool -> Bool
|
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`
|
## `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
|
## In practice, these are rarely needed. It's most common to write
|
||||||
## number literals without any suffix.
|
## 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.
|
## 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.
|
## * 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.)
|
## * 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.
|
## * 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
|
## A signed 8-bit integer, ranging from -128 to 127
|
||||||
I8 : Int @I8
|
I8 : Int [ @I8 ]
|
||||||
U8 : Int @U8
|
U8 : Int [ @U8 ]
|
||||||
U16 : Int @U16
|
U16 : Int [ @U16 ]
|
||||||
I16 : Int @I16
|
I16 : Int [ @I16 ]
|
||||||
U32 : Int @U32
|
U32 : Int [ @U32 ]
|
||||||
I32 : Int @I32
|
I32 : Int [ @I32 ]
|
||||||
I64 : Int @I64
|
I64 : Int [ @I64 ]
|
||||||
U64 : Int @U64
|
U64 : Int [ @U64 ]
|
||||||
I128 : Int @I128
|
I128 : Int [ @I128 ]
|
||||||
U128 : Int @U128
|
U128 : Int [ @U128 ]
|
||||||
Ilen : Int @Ilen
|
Ilen : Int [ @Ilen ]
|
||||||
Nat : Int @Nat
|
Nat : Int [ @Nat ]
|
||||||
|
|
||||||
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
|
## 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
|
## Bitwise
|
||||||
|
|
||||||
xor : Int -> Int -> Int
|
xor : Int, Int -> Int
|
||||||
|
|
||||||
and : Int -> Int -> Int
|
and : Int, Int -> Int
|
||||||
|
|
||||||
not : Int -> Int
|
not : Int -> Int
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
interface Str2
|
interface Str
|
||||||
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 ]
|
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 []
|
imports []
|
||||||
## Types
|
## # Types
|
||||||
|
|
||||||
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
||||||
## to the basics.
|
## to the basics.
|
||||||
|
|
|
@ -28,7 +28,7 @@ mod test_fmt {
|
||||||
|
|
||||||
assert_eq!(buf, expected)
|
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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1833,23 +1833,23 @@ mod test_fmt {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when b is
|
when b is
|
||||||
1 | 2 |
|
1 | 2 |
|
||||||
3
|
3
|
||||||
->
|
->
|
||||||
|
|
||||||
4
|
4
|
||||||
5 | 6 | 7 ->
|
5 | 6 | 7 ->
|
||||||
|
|
||||||
8
|
8
|
||||||
9
|
9
|
||||||
| 10 -> 11
|
| 10 -> 11
|
||||||
|
|
||||||
12 | 13 ->
|
12 | 13 ->
|
||||||
when c is
|
when c is
|
||||||
14 | 15 -> 16
|
14 | 15 -> 16
|
||||||
17
|
17
|
||||||
| 18 -> 19
|
| 18 -> 19
|
||||||
20 -> 21
|
20 -> 21
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
|
@ -90,9 +90,9 @@ pub enum OptLevel {
|
||||||
Optimize,
|
Optimize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<OptimizationLevel> for OptLevel {
|
impl From<OptLevel> for OptimizationLevel {
|
||||||
fn into(self) -> OptimizationLevel {
|
fn from(level: OptLevel) -> Self {
|
||||||
match self {
|
match level {
|
||||||
OptLevel::Normal => OptimizationLevel::None,
|
OptLevel::Normal => OptimizationLevel::None,
|
||||||
OptLevel::Optimize => OptimizationLevel::Aggressive,
|
OptLevel::Optimize => OptimizationLevel::Aggressive,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ pub enum RocCallResult<T> {
|
||||||
Failure(*mut c_char),
|
Failure(*mut c_char),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
||||||
fn into(self) -> Result<T, String> {
|
fn from(call_result: RocCallResult<T>) -> Self {
|
||||||
match self {
|
match call_result {
|
||||||
Success(value) => Ok(value),
|
Success(value) => Ok(value),
|
||||||
Failure(failure) => Err({
|
Failure(failure) => Err({
|
||||||
let raw = unsafe { CString::from_raw(failure) };
|
let raw = unsafe { CString::from_raw(failure) };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![warn(clippy::all, clippy::dbg_macro)]
|
#![warn(clippy::all, clippy::dbg_macro)]
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// 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 bumpalo::{collections::Vec, Bump};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
|
|
|
@ -12,8 +12,6 @@ mod helpers;
|
||||||
|
|
||||||
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||||
mod gen_num {
|
mod gen_num {
|
||||||
//use roc_std::RocOrder;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn i64_values() {
|
fn i64_values() {
|
||||||
assert_evals_to!("0", 0, i64);
|
assert_evals_to!("0", 0, i64);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn promote_expr_to_module(src: &str) -> String {
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
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
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn helper<'a>(
|
pub fn helper<'a>(
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
src: &str,
|
src: &str,
|
||||||
|
|
|
@ -2265,6 +2265,10 @@ fn load_pkg_config<'a>(
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Ok((ast::Module::Platform { header }, parser_state)) => {
|
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
|
// make a Pkg-Config module that ultimately exposes `main` to the host
|
||||||
let pkg_config_module_msg = fabricate_pkg_config_module(
|
let pkg_config_module_msg = fabricate_pkg_config_module(
|
||||||
arena,
|
arena,
|
||||||
|
@ -2275,6 +2279,7 @@ fn load_pkg_config<'a>(
|
||||||
module_ids.clone(),
|
module_ids.clone(),
|
||||||
ident_ids_by_module.clone(),
|
ident_ids_by_module.clone(),
|
||||||
&header,
|
&header,
|
||||||
|
header_src,
|
||||||
pkg_module_timing,
|
pkg_module_timing,
|
||||||
)
|
)
|
||||||
.1;
|
.1;
|
||||||
|
@ -2870,18 +2875,24 @@ fn send_header<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO refactor so more logic is shared with `send_header`
|
#[derive(Debug)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
struct PlatformHeaderInfo<'a> {
|
||||||
fn send_header_two<'a>(
|
|
||||||
arena: &'a Bump,
|
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
is_root_module: bool,
|
is_root_module: bool,
|
||||||
shorthand: &'a str,
|
shorthand: &'a str,
|
||||||
|
header_src: &'a str,
|
||||||
app_module_id: ModuleId,
|
app_module_id: ModuleId,
|
||||||
packages: &'a [Located<PackageEntry<'a>>],
|
packages: &'a [Located<PackageEntry<'a>>],
|
||||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||||
requires: &'a [Located<TypedIdent<'a>>],
|
requires: &'a [Located<TypedIdent<'a>>],
|
||||||
imports: &'a [Located<ImportsEntry<'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>,
|
parse_state: parser::State<'a>,
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
|
@ -2889,6 +2900,18 @@ fn send_header_two<'a>(
|
||||||
) -> (ModuleId, Msg<'a>) {
|
) -> (ModuleId, Msg<'a>) {
|
||||||
use inlinable_string::InlinableString;
|
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 declared_name: InlinableString = "".into();
|
||||||
|
|
||||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||||
|
@ -3080,7 +3103,7 @@ fn send_header_two<'a>(
|
||||||
package_qualified_imported_modules,
|
package_qualified_imported_modules,
|
||||||
deps_by_name,
|
deps_by_name,
|
||||||
exposes: exposed,
|
exposes: exposed,
|
||||||
header_src: "#builtin effect header",
|
header_src,
|
||||||
parse_state,
|
parse_state,
|
||||||
exposed_imports: scope,
|
exposed_imports: scope,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
@ -3209,21 +3232,27 @@ fn fabricate_pkg_config_module<'a>(
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
header: &PlatformHeader<'a>,
|
header: &PlatformHeader<'a>,
|
||||||
|
header_src: &'a str,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
) -> (ModuleId, Msg<'a>) {
|
) -> (ModuleId, Msg<'a>) {
|
||||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
||||||
header.provides.clone().into_bump_slice();
|
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(
|
send_header_two(
|
||||||
arena,
|
arena,
|
||||||
filename,
|
info,
|
||||||
false,
|
|
||||||
shorthand,
|
|
||||||
app_module_id,
|
|
||||||
&[],
|
|
||||||
provides,
|
|
||||||
header.requires.clone().into_bump_slice(),
|
|
||||||
header.imports.clone().into_bump_slice(),
|
|
||||||
parse_state,
|
parse_state,
|
||||||
module_ids,
|
module_ids,
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
|
@ -4114,7 +4143,7 @@ where
|
||||||
Ok(())
|
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 roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
|
||||||
use ven_pretty::DocAllocator;
|
use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
|
|
|
@ -117,21 +117,21 @@ impl From<InlinableString> for ModuleName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<InlinableString> for ModuleName {
|
impl From<ModuleName> for InlinableString {
|
||||||
fn into(self) -> InlinableString {
|
fn from(name: ModuleName) -> Self {
|
||||||
self.0
|
name.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<&'a InlinableString> for &'a ModuleName {
|
impl<'a> From<&'a ModuleName> for &'a InlinableString {
|
||||||
fn into(self) -> &'a InlinableString {
|
fn from(name: &'a ModuleName) -> Self {
|
||||||
&self.0
|
&name.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<Box<str>> for ModuleName {
|
impl From<ModuleName> for Box<str> {
|
||||||
fn into(self) -> Box<str> {
|
fn from(name: ModuleName) -> Self {
|
||||||
self.0.to_string().into()
|
name.0.to_string().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,9 +197,9 @@ impl<'a> From<String> for Lowercase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<InlinableString> for Lowercase {
|
impl From<Lowercase> for InlinableString {
|
||||||
fn into(self) -> InlinableString {
|
fn from(lowercase: Lowercase) -> Self {
|
||||||
self.0
|
lowercase.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,21 +234,21 @@ impl From<InlinableString> for Ident {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<InlinableString> for Ident {
|
impl From<Ident> for InlinableString {
|
||||||
fn into(self) -> InlinableString {
|
fn from(ident: Ident) -> Self {
|
||||||
self.0
|
ident.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<&'a InlinableString> for &'a Ident {
|
impl<'a> From<&'a Ident> for &'a InlinableString {
|
||||||
fn into(self) -> &'a InlinableString {
|
fn from(ident: &'a Ident) -> Self {
|
||||||
&self.0
|
&ident.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<Box<str>> for Ident {
|
impl From<Ident> for Box<str> {
|
||||||
fn into(self) -> Box<str> {
|
fn from(ident: Ident) -> Self {
|
||||||
self.0.to_string().into()
|
ident.0.to_string().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![warn(clippy::all, clippy::dbg_macro)]
|
#![warn(clippy::all, clippy::dbg_macro)]
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// 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 ident;
|
||||||
pub mod low_level;
|
pub mod low_level;
|
||||||
|
|
|
@ -424,6 +424,8 @@ impl<'a> Procs<'a> {
|
||||||
is_self_recursive: bool,
|
is_self_recursive: bool,
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
) {
|
) {
|
||||||
|
let number_of_arguments = loc_args.len();
|
||||||
|
|
||||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
||||||
Ok((_, pattern_symbols, body)) => {
|
Ok((_, pattern_symbols, body)) => {
|
||||||
// a named closure. Since these aren't specialized by the surrounding
|
// a named closure. Since these aren't specialized by the surrounding
|
||||||
|
@ -444,17 +446,22 @@ impl<'a> Procs<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
// If the function has invalid patterns in its arguments,
|
let mut pattern_symbols = Vec::with_capacity_in(number_of_arguments, env.arena);
|
||||||
// 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 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 snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.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(_));
|
// This will not hold for programs with type errors
|
||||||
debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
// 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
|
// if this is a closure, add the closure record argument
|
||||||
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
||||||
|
@ -2131,9 +2139,7 @@ fn build_specialized_proc_adapter<'a>(
|
||||||
arg_layouts.push(layout);
|
arg_layouts.push(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_layout = layout_cache
|
let ret_layout = layout_cache.from_var(&env.arena, ret_var, env.subs)?;
|
||||||
.from_var(&env.arena, ret_var, env.subs)
|
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
|
||||||
|
|
||||||
build_specialized_proc(
|
build_specialized_proc(
|
||||||
env.arena,
|
env.arena,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![warn(clippy::all, clippy::dbg_macro)]
|
#![warn(clippy::all, clippy::dbg_macro)]
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// 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 borrow;
|
||||||
pub mod expand_rc;
|
pub mod expand_rc;
|
||||||
|
|
|
@ -125,7 +125,7 @@ where
|
||||||
E: 'a,
|
E: 'a,
|
||||||
{
|
{
|
||||||
move |_, state: State<'a>| {
|
move |_, state: State<'a>| {
|
||||||
if state.column > min_indent {
|
if state.column >= min_indent {
|
||||||
Ok((NoProgress, (), state))
|
Ok((NoProgress, (), state))
|
||||||
} else {
|
} else {
|
||||||
Err((NoProgress, indent_problem(state.line, state.column), state))
|
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, *};
|
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>(
|
pub fn test_parse_expr<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
|
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
|
||||||
let parser = space0_before_e(
|
let parser = skip_second!(
|
||||||
move |a, s| parse_loc_expr(min_indent, a, s),
|
space0_before_e(
|
||||||
min_indent,
|
move |a, s| parse_loc_expr(min_indent, a, s),
|
||||||
EExpr::Space,
|
min_indent,
|
||||||
EExpr::IndentStart,
|
EExpr::Space,
|
||||||
|
EExpr::IndentStart,
|
||||||
|
),
|
||||||
|
expr_end()
|
||||||
);
|
);
|
||||||
|
|
||||||
match parser.parse(arena, state) {
|
match parser.parse(arena, state) {
|
||||||
|
@ -35,9 +52,27 @@ pub fn test_parse_expr<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum MultiBackpassing {
|
pub struct ExprParseOptions {
|
||||||
Allow,
|
/// Check for and accept multi-backpassing syntax
|
||||||
Disallow,
|
/// 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>> {
|
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>(
|
fn parse_loc_term<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'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_expr_in_parens_etc_help(min_indent),
|
||||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||||
EExpr::Lambda,
|
|
||||||
closure_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(record_literal_help(min_indent)),
|
loc!(record_literal_help(min_indent)),
|
||||||
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
||||||
loc!(map_with_arena!(
|
loc!(map_with_arena!(
|
||||||
|
@ -183,17 +215,14 @@ fn parse_loc_term<'a>(
|
||||||
|
|
||||||
fn loc_possibly_negative_or_negated_term<'a>(
|
fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of![
|
one_of![
|
||||||
|arena, state: State<'a>| {
|
|arena, state: State<'a>| {
|
||||||
let initial = state;
|
let initial = state;
|
||||||
|
|
||||||
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
|
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
|
||||||
min_indent,
|
min_indent, options, a, s
|
||||||
multi_backpassing,
|
|
||||||
a,
|
|
||||||
s
|
|
||||||
))
|
))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
@ -205,7 +234,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
loc!(specialize(EExpr::Number, number_literal_help())),
|
loc!(specialize(EExpr::Number, number_literal_help())),
|
||||||
loc!(map_with_arena!(
|
loc!(map_with_arena!(
|
||||||
and!(loc!(word1(b'!', EExpr::Start)), |a, s| {
|
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<_>, _)| {
|
|arena: &'a Bump, (loc_op, loc_expr): (Located<_>, _)| {
|
||||||
Expr::UnaryOp(
|
Expr::UnaryOp(
|
||||||
|
@ -216,7 +245,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
)),
|
)),
|
||||||
|arena, state| {
|
|arena, state| {
|
||||||
// TODO use parse_loc_term_better
|
// 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>(
|
fn parse_expr_start<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of![
|
one_of![
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
|
||||||
EExpr::If,
|
|
||||||
if_expr_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(specialize(
|
loc!(specialize(
|
||||||
EExpr::When,
|
EExpr::When,
|
||||||
when::expr_help(min_indent, multi_backpassing)
|
when::expr_help(min_indent, options)
|
||||||
)),
|
)),
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||||
EExpr::Lambda,
|
loc!(move |a, s| parse_expr_operator_chain(min_indent, options, start, a, s)),
|
||||||
closure_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(move |a, s| parse_expr_operator_chain(min_indent, multi_backpassing, start, a, s)),
|
|
||||||
fail_expr_start_e()
|
fail_expr_start_e()
|
||||||
]
|
]
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
|
@ -286,13 +309,13 @@ fn parse_expr_start<'a>(
|
||||||
|
|
||||||
fn parse_expr_operator_chain<'a>(
|
fn parse_expr_operator_chain<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let (_, expr, state) =
|
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 initial = state;
|
||||||
let end = state.get_position();
|
let end = state.get_position();
|
||||||
|
@ -309,14 +332,7 @@ fn parse_expr_operator_chain<'a>(
|
||||||
end,
|
end,
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,7 +704,7 @@ struct DefState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_defs_end<'a>(
|
fn parse_defs_end<'a>(
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut def_state: DefState<'a>,
|
mut def_state: DefState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -743,7 +759,7 @@ fn parse_defs_end<'a>(
|
||||||
loc_def_expr,
|
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)) => {
|
Ok((_, BinOp::HasType, state)) => {
|
||||||
let (_, ann_type, state) = specialize(
|
let (_, ann_type, state) = specialize(
|
||||||
|
@ -765,7 +781,7 @@ fn parse_defs_end<'a>(
|
||||||
ann_type,
|
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)),
|
_ => Ok((MadeProgress, def_state, initial)),
|
||||||
|
@ -774,7 +790,7 @@ fn parse_defs_end<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_defs_expr<'a>(
|
fn parse_defs_expr<'a>(
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
def_state: DefState<'a>,
|
def_state: DefState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -782,7 +798,7 @@ fn parse_defs_expr<'a>(
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let min_indent = start.col;
|
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),
|
Err(bad) => Err(bad),
|
||||||
Ok((_, def_state, state)) => {
|
Ok((_, def_state, state)) => {
|
||||||
// this is no def, because there is no `=` or `:`; parse as an expr
|
// 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>(
|
fn parse_expr_operator<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut expr_state: ExprState<'a>,
|
mut expr_state: ExprState<'a>,
|
||||||
loc_op: Located<BinOp>,
|
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 => {
|
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
|
||||||
// negative terms
|
// negative terms
|
||||||
|
|
||||||
let (_, negated_expr, state) =
|
let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?;
|
||||||
parse_loc_term(min_indent, multi_backpassing, arena, state)?;
|
|
||||||
let new_end = state.get_position();
|
let new_end = state.get_position();
|
||||||
|
|
||||||
let arg = numeric_negate_expression(
|
let arg = numeric_negate_expression(
|
||||||
|
@ -859,14 +874,7 @@ fn parse_expr_operator<'a>(
|
||||||
expr_state.spaces_after = spaces;
|
expr_state.spaces_after = spaces;
|
||||||
expr_state.end = new_end;
|
expr_state.end = new_end;
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BinOp::Assignment => {
|
BinOp::Assignment => {
|
||||||
let expr_region = expr_state.expr.region;
|
let expr_region = expr_state.expr.region;
|
||||||
|
@ -915,7 +923,7 @@ fn parse_expr_operator<'a>(
|
||||||
spaces_after: &[],
|
spaces_after: &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
|
parse_defs_expr(options, start, def_state, arena, state)
|
||||||
}
|
}
|
||||||
BinOp::Backpassing => {
|
BinOp::Backpassing => {
|
||||||
let expr_region = expr_state.expr.region;
|
let expr_region = expr_state.expr.region;
|
||||||
|
@ -1062,11 +1070,9 @@ fn parse_expr_operator<'a>(
|
||||||
spaces_after: &[],
|
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)
|
_ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) {
|
||||||
.parse(arena, state)
|
|
||||||
{
|
|
||||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||||
Ok((_, mut new_expr, state)) => {
|
Ok((_, mut new_expr, state)) => {
|
||||||
let new_end = state.get_position();
|
let new_end = state.get_position();
|
||||||
|
@ -1104,14 +1110,7 @@ fn parse_expr_operator<'a>(
|
||||||
expr_state.spaces_after = spaces;
|
expr_state.spaces_after = spaces;
|
||||||
|
|
||||||
// TODO new start?
|
// TODO new start?
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1123,7 @@ fn parse_expr_operator<'a>(
|
||||||
|
|
||||||
fn parse_expr_end<'a>(
|
fn parse_expr_end<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut expr_state: ExprState<'a>,
|
mut expr_state: ExprState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1132,7 +1131,7 @@ fn parse_expr_end<'a>(
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let parser = skip_first!(
|
let parser = skip_first!(
|
||||||
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
|
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) {
|
match parser.parse(arena, state) {
|
||||||
|
@ -1163,14 +1162,7 @@ fn parse_expr_end<'a>(
|
||||||
expr_state.end = new_end;
|
expr_state.end = new_end;
|
||||||
expr_state.spaces_after = new_spaces;
|
expr_state.spaces_after = new_spaces;
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1183,19 +1175,12 @@ fn parse_expr_end<'a>(
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
expr_state.initial = before_op;
|
expr_state.initial = before_op;
|
||||||
parse_expr_operator(
|
parse_expr_operator(
|
||||||
min_indent,
|
min_indent, options, start, expr_state, loc_op, arena, state,
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
loc_op,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Err((NoProgress, _, mut state)) => {
|
Err((NoProgress, _, mut state)) => {
|
||||||
// try multi-backpassing
|
// 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.bytes = &state.bytes[1..];
|
||||||
state.column += 1;
|
state.column += 1;
|
||||||
|
|
||||||
|
@ -1256,6 +1241,12 @@ fn parse_expr_end<'a>(
|
||||||
Ok((MadeProgress, ret, state))
|
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 {
|
} else {
|
||||||
// roll back space parsing
|
// roll back space parsing
|
||||||
let state = expr_state.initial;
|
let state = expr_state.initial;
|
||||||
|
@ -1273,7 +1264,15 @@ fn parse_loc_expr<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'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>(
|
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,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'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>(
|
fn parse_loc_expr_with_options<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
let start = state.get_position();
|
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.
|
/// 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)?;
|
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||||
|
|
||||||
let start = state.get_position();
|
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) =
|
let (_, final_space, state) =
|
||||||
space0_e(start.col, EExpr::Space, EExpr::IndentEnd).parse(arena, 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>(
|
fn closure_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
|
@ -1510,7 +1522,7 @@ fn closure_help<'a>(
|
||||||
// Parse the body
|
// Parse the body
|
||||||
space0_before_e(
|
space0_before_e(
|
||||||
specialize_ref(ELambda::Body, move |arena, state| {
|
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,
|
min_indent,
|
||||||
ELambda::Space,
|
ELambda::Space,
|
||||||
|
@ -1535,7 +1547,7 @@ mod when {
|
||||||
/// Parser for when expressions.
|
/// Parser for when expressions.
|
||||||
pub fn expr_help<'a>(
|
pub fn expr_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, When<'a>> {
|
) -> impl Parser<'a, Expr<'a>, When<'a>> {
|
||||||
then(
|
then(
|
||||||
and!(
|
and!(
|
||||||
|
@ -1543,7 +1555,7 @@ mod when {
|
||||||
skip_second!(
|
skip_second!(
|
||||||
space0_around_ee(
|
space0_around_ee(
|
||||||
specialize_ref(When::Condition, move |arena, state| {
|
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,
|
min_indent,
|
||||||
When::Space,
|
When::Space,
|
||||||
|
@ -1566,7 +1578,7 @@ mod when {
|
||||||
// Everything in the branches must be indented at least as much as the case itself.
|
// Everything in the branches must be indented at least as much as the case itself.
|
||||||
let min_indent = case_indent;
|
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((
|
Ok((
|
||||||
progress.or(p1),
|
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>> {
|
fn branches<'a>(
|
||||||
move |arena, state| {
|
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);
|
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.)
|
// 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.
|
// 2. Parse the other branches. Their indentation levels must be == the first branch's.
|
||||||
|
|
||||||
let (_, (loc_first_patterns, loc_first_guard), state) =
|
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), mut state) =
|
||||||
branch_alternatives(min_indent).parse(arena, state)?;
|
branch_alternatives(min_indent, options, None).parse(arena, state)?;
|
||||||
let loc_first_pattern = loc_first_patterns.first().unwrap();
|
let original_indent = pattern_indent_level;
|
||||||
let original_indent = loc_first_pattern.region.start_col;
|
|
||||||
let indented_more = original_indent + 1;
|
state.indent_col = pattern_indent_level;
|
||||||
|
|
||||||
// Parse the first "->" and the expression after it.
|
// Parse the first "->" and the expression after it.
|
||||||
let (_, loc_first_expr, mut state) =
|
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.
|
// Record this as the first branch, then optionally parse additional branches.
|
||||||
branches.push(arena.alloc(WhenBranch {
|
branches.push(arena.alloc(WhenBranch {
|
||||||
|
@ -1613,19 +1630,21 @@ mod when {
|
||||||
let branch_parser = map!(
|
let branch_parser = map!(
|
||||||
and!(
|
and!(
|
||||||
then(
|
then(
|
||||||
branch_alternatives(min_indent),
|
branch_alternatives(min_indent, options, Some(pattern_indent_level)),
|
||||||
move |_arena, state, _, (loc_patterns, loc_guard)| {
|
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
|
||||||
match alternatives_indented_correctly(&loc_patterns, original_indent) {
|
if pattern_indent_level == indent_col {
|
||||||
Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)),
|
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
||||||
Err(indent) => Err((
|
} else {
|
||||||
|
let indent = pattern_indent_level - indent_col;
|
||||||
|
Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
When::PatternAlignment(indent, state.line, state.column),
|
When::PatternAlignment(indent, state.line, state.column),
|
||||||
state,
|
state,
|
||||||
)),
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
branch_result(indented_more)
|
branch_result(original_indent + 1)
|
||||||
),
|
),
|
||||||
|((patterns, guard), expr)| {
|
|((patterns, guard), expr)| {
|
||||||
let patterns: Vec<'a, _> = patterns;
|
let patterns: Vec<'a, _> = patterns;
|
||||||
|
@ -1655,40 +1674,36 @@ mod when {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((MadeProgress, branches, state))
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
branches,
|
||||||
|
State {
|
||||||
|
indent_col: when_indent,
|
||||||
|
..state
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsing alternative patterns in when branches.
|
/// Parsing alternative patterns in when branches.
|
||||||
fn branch_alternatives<'a>(
|
fn branch_alternatives<'a>(
|
||||||
min_indent: u16,
|
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!(
|
and!(
|
||||||
sep_by1(word1(b'|', When::Bar), |arena, state| {
|
branch_alternatives_help(min_indent, pattern_indent_level),
|
||||||
let (_, spaces, state) =
|
|
||||||
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
let (_, loc_pattern, state) = space0_after_e(
|
|
||||||
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
|
|
||||||
min_indent,
|
|
||||||
When::Space,
|
|
||||||
When::IndentPattern,
|
|
||||||
)
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
if spaces.is_empty() {
|
|
||||||
loc_pattern
|
|
||||||
} else {
|
|
||||||
arena
|
|
||||||
.alloc(loc_pattern.value)
|
|
||||||
.with_spaces_before(spaces, loc_pattern.region)
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}),
|
|
||||||
one_of![
|
one_of![
|
||||||
map!(
|
map!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
|
@ -1696,7 +1711,7 @@ mod when {
|
||||||
// TODO we should require space before the expression but not after
|
// TODO we should require space before the expression but not after
|
||||||
space0_around_ee(
|
space0_around_ee(
|
||||||
specialize_ref(When::IfGuard, move |arena, state| {
|
specialize_ref(When::IfGuard, move |arena, state| {
|
||||||
parse_loc_expr(min_indent, arena, state)
|
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
When::Space,
|
When::Space,
|
||||||
|
@ -1711,22 +1726,103 @@ mod when {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if alternatives of a when branch are indented correctly.
|
fn branch_single_alternative<'a>(
|
||||||
fn alternatives_indented_correctly<'a>(
|
min_indent: u16,
|
||||||
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>,
|
) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
|
||||||
original_indent: u16,
|
move |arena, state| {
|
||||||
) -> Result<(), u16> {
|
let (_, spaces, state) =
|
||||||
let (first, rest) = loc_patterns.split_first().unwrap();
|
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
|
||||||
let first_indented_correctly = first.region.start_col == original_indent;
|
.parse(arena, state)?;
|
||||||
if first_indented_correctly {
|
|
||||||
for when_pattern in rest.iter() {
|
let (_, loc_pattern, state) = space0_after_e(
|
||||||
if when_pattern.region.start_col < original_indent {
|
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
|
||||||
return Err(original_indent - when_pattern.region.start_col);
|
min_indent,
|
||||||
|
When::Space,
|
||||||
|
When::IndentPattern,
|
||||||
|
)
|
||||||
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
if spaces.is_empty() {
|
||||||
|
loc_pattern
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(loc_pattern.value)
|
||||||
|
.with_spaces_before(spaces, loc_pattern.region)
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>(
|
fn if_expr_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
||||||
move |arena: &'a Bump, state| {
|
move |arena: &'a Bump, state| {
|
||||||
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, 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(
|
let (_, else_branch, state) = space0_before_e(
|
||||||
specialize_ref(If::ElseBranch, move |arena, state| {
|
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,
|
min_indent,
|
||||||
If::Space,
|
If::Space,
|
||||||
|
|
|
@ -42,15 +42,15 @@ pub enum PackageOrPath<'a> {
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct ModuleName<'a>(&'a str);
|
pub struct ModuleName<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> Into<&'a str> for ModuleName<'a> {
|
impl<'a> From<ModuleName<'a>> for &'a str {
|
||||||
fn into(self) -> &'a str {
|
fn from(name: ModuleName<'a>) -> Self {
|
||||||
self.0
|
name.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<InlinableString> for ModuleName<'a> {
|
impl<'a> From<ModuleName<'a>> for InlinableString {
|
||||||
fn into(self) -> InlinableString {
|
fn from(name: ModuleName<'a>) -> InlinableString {
|
||||||
self.0.into()
|
name.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ pub enum SyntaxError<'a> {
|
||||||
ReservedKeyword(Region),
|
ReservedKeyword(Region),
|
||||||
ArgumentsBeforeEquals(Region),
|
ArgumentsBeforeEquals(Region),
|
||||||
NotYetImplemented(String),
|
NotYetImplemented(String),
|
||||||
TODO,
|
Todo,
|
||||||
Type(Type<'a>),
|
Type(Type<'a>),
|
||||||
Pattern(EPattern<'a>),
|
Pattern(EPattern<'a>),
|
||||||
Expr(EExpr<'a>),
|
Expr(EExpr<'a>),
|
||||||
|
@ -381,6 +381,7 @@ pub type Col = u16;
|
||||||
pub enum EExpr<'a> {
|
pub enum EExpr<'a> {
|
||||||
Start(Row, Col),
|
Start(Row, Col),
|
||||||
End(Row, Col),
|
End(Row, Col),
|
||||||
|
BadExprEnd(Row, Col),
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
|
|
||||||
Dot(Row, Col),
|
Dot(Row, Col),
|
||||||
|
@ -930,8 +931,8 @@ where
|
||||||
state = next_state;
|
state = next_state;
|
||||||
buf.push(next_output);
|
buf.push(next_output);
|
||||||
}
|
}
|
||||||
Err((element_progress, fail, state)) => {
|
Err((_, fail, state)) => {
|
||||||
return Err((element_progress, fail, state));
|
return Err((MadeProgress, fail, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>(
|
||||||
EPattern::Record,
|
EPattern::Record,
|
||||||
crate::pattern::record_pattern_help(min_indent)
|
crate::pattern::record_pattern_help(min_indent)
|
||||||
)),
|
)),
|
||||||
|
loc!(number_pattern_help()),
|
||||||
loc!(string_pattern_help()),
|
loc!(string_pattern_help()),
|
||||||
loc!(number_pattern_help())
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2550,6 +2550,247 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
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]
|
#[test]
|
||||||
fn when_with_records() {
|
fn when_with_records() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -2599,6 +2840,47 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
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]
|
#[test]
|
||||||
fn when_with_alternative_patterns() {
|
fn when_with_alternative_patterns() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -2620,9 +2902,9 @@ mod test_parse {
|
||||||
let pattern2_alt =
|
let pattern2_alt =
|
||||||
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
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 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 {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
||||||
value: loc_expr2,
|
value: loc_expr2,
|
||||||
|
@ -2642,7 +2924,7 @@ mod test_parse {
|
||||||
when x is
|
when x is
|
||||||
"blah" | "blop" -> 1
|
"blah" | "blop" -> 1
|
||||||
"foo" |
|
"foo" |
|
||||||
"bar" -> 2
|
"bar" -> 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -272,22 +272,31 @@ fn to_expr_report<'a>(
|
||||||
])
|
])
|
||||||
.indent(4),
|
.indent(4),
|
||||||
])],
|
])],
|
||||||
b"->" => vec![alloc.stack(vec![
|
b"->" => match context {
|
||||||
alloc.concat(vec![
|
Context::InNode(Node::WhenBranch, _row, _col, _) => {
|
||||||
alloc.reflow("The arrow "),
|
return to_unexpected_arrow_report(
|
||||||
alloc.parser_suggestion("->"),
|
alloc, filename, *row, *col, start_row, start_col,
|
||||||
alloc.reflow(" is only used to define cases in a "),
|
);
|
||||||
alloc.keyword("when"),
|
}
|
||||||
alloc.reflow("."),
|
_ => {
|
||||||
]),
|
vec![alloc.stack(vec![
|
||||||
alloc
|
alloc.concat(vec![
|
||||||
.vcat(vec![
|
alloc.reflow("The arrow "),
|
||||||
alloc.text("when color is"),
|
alloc.parser_suggestion("->"),
|
||||||
alloc.text("Red -> \"stop!\"").indent(4),
|
alloc.reflow(" is only used to define cases in a "),
|
||||||
alloc.text("Green -> \"go!\"").indent(4),
|
alloc.keyword("when"),
|
||||||
])
|
alloc.reflow("."),
|
||||||
.indent(4),
|
]),
|
||||||
])],
|
alloc
|
||||||
|
.vcat(vec![
|
||||||
|
alloc.text("when color is"),
|
||||||
|
alloc.text("Red -> \"stop!\"").indent(4),
|
||||||
|
alloc.text("Green -> \"go!\"").indent(4),
|
||||||
|
])
|
||||||
|
.indent(4),
|
||||||
|
])]
|
||||||
|
}
|
||||||
|
},
|
||||||
b"!" => vec![
|
b"!" => vec![
|
||||||
alloc.reflow("The boolean negation operator "),
|
alloc.reflow("The boolean negation operator "),
|
||||||
alloc.parser_suggestion("!"),
|
alloc.parser_suggestion("!"),
|
||||||
|
@ -458,6 +467,27 @@ fn to_expr_report<'a>(
|
||||||
*col,
|
*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) => {
|
EExpr::Colon(row, col) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||||
let region = Region::from_row_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) => {
|
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(',')) => {
|
Next::Other(Some(',')) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_row_col(row, col);
|
||||||
|
@ -1387,25 +1417,68 @@ fn to_unfinished_when_report<'a>(
|
||||||
start_row: Row,
|
start_row: Row,
|
||||||
start_col: Col,
|
start_col: Col,
|
||||||
message: RocDocBuilder<'a>,
|
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);
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I was partway through parsing a "),
|
||||||
|
alloc.keyword("when"),
|
||||||
|
alloc.reflow(r" expression, but I got stuck here:"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
message,
|
||||||
|
note_for_when_error(alloc),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
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> {
|
) -> Report<'a> {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_rows_cols(row, col, row, col + 2);
|
||||||
|
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow(r"I was partway through parsing a "),
|
alloc.reflow(r"I am parsing a "),
|
||||||
alloc.keyword("when"),
|
alloc.keyword("when"),
|
||||||
alloc.reflow(r" expression, but I got stuck here:"),
|
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
|
||||||
]),
|
]),
|
||||||
alloc.region_with_subregion(surroundings, region),
|
alloc.region_with_subregion(surroundings, region),
|
||||||
message,
|
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),
|
note_for_when_error(alloc),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
filename,
|
filename,
|
||||||
doc,
|
doc,
|
||||||
title: "UNFINISHED WHEN".to_string(),
|
title: "UNEXPECTED ARROW".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4967,7 +4967,6 @@ mod test_reporting {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_or_pattern() {
|
fn empty_or_pattern() {
|
||||||
// this should get better with time
|
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -4981,29 +4980,16 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
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 | ->
|
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
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5135,29 +5121,111 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
when 4 is
|
when 4 is
|
||||||
5 -> 2
|
5 -> 2
|
||||||
_ -> 2
|
2 -> 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
── 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!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
I was partway through parsing a `when` expression, but I got stuck here:
|
#[test]
|
||||||
|
fn when_over_indented_underscore() {
|
||||||
3│ _ -> 2
|
report_problem_as(
|
||||||
^
|
indoc!(
|
||||||
|
r#"
|
||||||
I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
when 4 is
|
||||||
|
5 -> 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.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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.
|
Note: Here is an example of a valid `when` expression for reference.
|
||||||
|
|
||||||
when List.first plants is
|
when List.first plants is
|
||||||
Ok n ->
|
Ok n ->
|
||||||
n
|
n
|
||||||
|
|
||||||
Err _ ->
|
Err _ ->
|
||||||
200
|
200
|
||||||
|
|
||||||
Notice the indentation. All patterns are aligned, and each branch is
|
Notice the indentation. All patterns are aligned, and each branch is
|
||||||
indented a bit more than the corresponding pattern. That is important!
|
indented a bit more than the corresponding pattern. That is important!
|
||||||
"#
|
"#
|
||||||
|
@ -5809,21 +5877,18 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── MISSING FINAL EXPRESSION ────────────────────────────────────────────────────
|
── UNKNOWN OPERATOR ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I am partway through parsing a definition's final expression, but I
|
This looks like an operator, but it's not one I recognize!
|
||||||
got stuck here:
|
|
||||||
|
|
||||||
1│ main = 5 -> 3
|
1│ main = 5 -> 3
|
||||||
^
|
^^
|
||||||
|
|
||||||
This definition is missing a final expression. A nested definition
|
The arrow -> is only used to define cases in a `when`.
|
||||||
must be followed by either another definition, or an expression
|
|
||||||
|
when color is
|
||||||
x = 4
|
Red -> "stop!"
|
||||||
y = 2
|
Green -> "go!"
|
||||||
|
|
||||||
x + y
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2258,3 +2258,21 @@ fn backpassing_result() {
|
||||||
i64
|
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 {
|
impl From<VarStore> for Variable {
|
||||||
fn into(self) -> Variable {
|
fn from(store: VarStore) -> Self {
|
||||||
Variable(self.next)
|
Variable(store.next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,9 +139,9 @@ impl fmt::Debug for OptVariable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Option<Variable>> for OptVariable {
|
impl From<OptVariable> for Option<Variable> {
|
||||||
fn into(self) -> Option<Variable> {
|
fn from(opt_var: OptVariable) -> Self {
|
||||||
self.into_variable()
|
opt_var.into_variable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,9 +180,9 @@ impl Variable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<OptVariable> for Variable {
|
impl From<Variable> for OptVariable {
|
||||||
fn into(self) -> OptVariable {
|
fn from(var: Variable) -> Self {
|
||||||
OptVariable(self.0)
|
OptVariable(var.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,9 +483,9 @@ impl fmt::Debug for Rank {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<usize> for Rank {
|
impl From<Rank> for usize {
|
||||||
fn into(self) -> usize {
|
fn from(rank: Rank) -> Self {
|
||||||
self.0
|
rank.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,29 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
|
||||||
modules: files_docs,
|
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
|
// Register handlebars template
|
||||||
let mut handlebars = handlebars::Handlebars::new();
|
let mut handlebars = handlebars::Handlebars::new();
|
||||||
handlebars
|
handlebars
|
||||||
|
@ -75,7 +98,7 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
|
||||||
.expect("TODO gracefully handle writing file failing");
|
.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(
|
pub fn files_to_documentations(
|
||||||
|
|
|
@ -66,16 +66,14 @@ a:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.pkg-and-logo {
|
.pkg-and-logo {
|
||||||
min-width: 0;
|
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--top-bar-bg);
|
background-color: var(--top-bar-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pkg-and-logo a,
|
.pkg-and-logo a, .pkg-and-logo a:visited {
|
||||||
.pkg-and-logo a:visited {
|
|
||||||
color: var(--top-bar-fg);
|
color: var(--top-bar-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,18 +82,11 @@ a:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-container {
|
|
||||||
min-width: 0;
|
|
||||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button {
|
.search-button {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0; /* always shrink the package name before these; they have a relatively constrained length */
|
||||||
/* always shrink the package name before these; they have a relatively constrained length */
|
|
||||||
padding: 12px 18px;
|
padding: 12px 18px;
|
||||||
margin-right: 42px;
|
margin-right: 42px;
|
||||||
display: none;
|
display: none; /* only show this in the mobile view */
|
||||||
/* only show this in the mobile view */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
.version {
|
||||||
|
@ -127,6 +118,8 @@ main {
|
||||||
line-height: 1.85em;
|
line-height: 1.85em;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
padding: 48px;
|
padding: 48px;
|
||||||
|
|
||||||
|
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar-nav {
|
#sidebar-nav {
|
||||||
|
@ -160,13 +153,11 @@ main {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
flex-grow: 1;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-width: 0;
|
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-header-triangle {
|
.top-header-triangle {
|
||||||
|
@ -181,8 +172,8 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
margin: 24px 0;
|
margin: 24px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
@ -240,8 +231,7 @@ footer p {
|
||||||
margin-bottom: 48px;
|
margin-bottom: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-name a,
|
.module-name a, .module-name a:visited {
|
||||||
.module-name a:visited {
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,8 +249,7 @@ footer p {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
a,
|
a, a:visited {
|
||||||
a:visited {
|
|
||||||
color: var(--link-color);
|
color: var(--link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,20 +314,19 @@ pre code {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#module-search,
|
#module-search, #module-search:focus {
|
||||||
#module-search:focus {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show the "Search" label link when the text input has a placeholder */
|
/* 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;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide the "Search" label link when the text input has focus */
|
/* Hide the "Search" label link when the text input has focus */
|
||||||
#module-search:focus+#search-link {
|
#module-search:focus + #search-link {
|
||||||
display: none;
|
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 {
|
.search-button {
|
||||||
display: block;
|
display: block; /* This is only visible in mobile. */
|
||||||
/* This is only visible in mobile. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-header {
|
.top-header {
|
||||||
|
justify-content: space-between;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,21 +431,19 @@ pre code {
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
grid-column-start: none;
|
||||||
|
grid-column-end: none;
|
||||||
|
grid-row-start: above-footer;
|
||||||
|
grid-row-end: above-footer;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
#sidebar-nav {
|
||||||
margin: 0;
|
grid-column-start: none;
|
||||||
min-width: 320px;
|
grid-column-end: none;
|
||||||
max-width: 100%;
|
grid-row-start: sidebar;
|
||||||
}
|
grid-row-end: sidebar;
|
||||||
|
|
||||||
.content {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -478,12 +464,30 @@ pre code {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-header {
|
body {
|
||||||
justify-content: space-between;
|
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 {
|
.top-header-triangle {
|
||||||
/* Display the sidebar below <main> without affecting tab index */
|
display: none;
|
||||||
flex-direction: column-reverse;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.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>
|
<title>The Roc Programming Language</title>
|
||||||
<meta name="description" content="A language for building fast, reliable software.">
|
<meta name="description" content="A language for building fast, reliable software.">
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<link rel="icon" href="/favicon.svg">
|
<link rel="icon" href="favicon.svg">
|
||||||
<script type="text/javascript" src="/search.js" defer></script>
|
<script type="text/javascript" src="search.js" defer></script>
|
||||||
<link rel="stylesheet" href="/styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -96,14 +96,12 @@ pub fn expr2_to_markup<'a, 'b>(
|
||||||
new_markup_node(text, node_id, HighlightStyle::Variable, markup_node_pool)
|
new_markup_node(text, node_id, HighlightStyle::Variable, markup_node_pool)
|
||||||
}
|
}
|
||||||
Expr2::List { elems, .. } => {
|
Expr2::List { elems, .. } => {
|
||||||
let mut children_ids = Vec::new();
|
let mut children_ids = vec![new_markup_node(
|
||||||
|
|
||||||
children_ids.push(new_markup_node(
|
|
||||||
"[ ".to_string(),
|
"[ ".to_string(),
|
||||||
node_id,
|
node_id,
|
||||||
HighlightStyle::Bracket,
|
HighlightStyle::Bracket,
|
||||||
markup_node_pool,
|
markup_node_pool,
|
||||||
));
|
)];
|
||||||
|
|
||||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||||
let sub_expr2 = env.pool.get(node_id);
|
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)
|
markup_node_pool.add(list_node)
|
||||||
}
|
}
|
||||||
Expr2::Record { fields, .. } => {
|
Expr2::Record { fields, .. } => {
|
||||||
let mut children_ids = Vec::new();
|
let mut children_ids = vec![new_markup_node(
|
||||||
|
|
||||||
children_ids.push(new_markup_node(
|
|
||||||
"{ ".to_string(),
|
"{ ".to_string(),
|
||||||
node_id,
|
node_id,
|
||||||
HighlightStyle::Bracket,
|
HighlightStyle::Bracket,
|
||||||
markup_node_pool,
|
markup_node_pool,
|
||||||
));
|
)];
|
||||||
|
|
||||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
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);
|
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(field_node_id);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![warn(clippy::all, clippy::dbg_macro)]
|
#![warn(clippy::all, clippy::dbg_macro)]
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// 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)]
|
#[cfg_attr(test, macro_use)]
|
||||||
extern crate indoc;
|
extern crate indoc;
|
||||||
|
|
|
@ -59,7 +59,10 @@ pub export fn main() u8 {
|
||||||
|
|
||||||
call_the_closure(function_pointer, closure_data_pointer);
|
call_the_closure(function_pointer, closure_data_pointer);
|
||||||
} else {
|
} 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;
|
var ts2: std.os.timespec = undefined;
|
||||||
|
|
|
@ -502,11 +502,11 @@ pub enum RocCallResult<T> {
|
||||||
Failure(*mut c_char),
|
Failure(*mut c_char),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> Into<Result<T, &'static str>> for RocCallResult<T> {
|
impl<T: Sized> From<RocCallResult<T>> for Result<T, &'static str> {
|
||||||
fn into(self) -> Result<T, &'static str> {
|
fn from(call_result: RocCallResult<T>) -> Self {
|
||||||
use RocCallResult::*;
|
use RocCallResult::*;
|
||||||
|
|
||||||
match self {
|
match call_result {
|
||||||
Success(value) => Ok(value),
|
Success(value) => Ok(value),
|
||||||
Failure(failure) => Err({
|
Failure(failure) => Err({
|
||||||
let msg = unsafe {
|
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> {
|
impl<'a, T: Sized + Copy> From<&'a RocCallResult<T>> for Result<T, &'a str> {
|
||||||
fn into(self) -> Result<T, &'a str> {
|
fn from(call_result: &'a RocCallResult<T>) -> Self {
|
||||||
use RocCallResult::*;
|
use RocCallResult::*;
|
||||||
|
|
||||||
match self {
|
match call_result {
|
||||||
Success(value) => Ok(*value),
|
Success(value) => Ok(*value),
|
||||||
Failure(failure) => Err({
|
Failure(failure) => Err({
|
||||||
let msg = unsafe {
|
let msg = unsafe {
|
||||||
|
|
27
vendor/ena/src/unify/mod.rs
vendored
27
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;
|
//! The best way to see how it is used is to read the `tests.rs` file;
|
||||||
//! search for e.g. `UnitKey`.
|
//! search for e.g. `UnitKey`.
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::marker;
|
use std::marker;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
@ -361,17 +362,23 @@ impl<S: UnificationStore> UnificationTable<S> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.redirect_root(new_rank, redirected, new_root, new_value);
|
self.redirect_root(new_rank, redirected, new_root, new_value);
|
||||||
} else if rank_a > rank_b {
|
|
||||||
// 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 {
|
|
||||||
// b has greater rank, so a should redirect to b.
|
|
||||||
self.redirect_root(rank_b, key_a, key_b, new_value);
|
|
||||||
} else {
|
} else {
|
||||||
// If equal, redirect one to the other and increment the
|
match rank_a.cmp(&rank_b) {
|
||||||
// other's rank.
|
Ordering::Greater => {
|
||||||
self.redirect_root(rank_a + 1, key_a, key_b, new_value);
|
// 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);
|
||||||
|
}
|
||||||
|
Ordering::Less => {
|
||||||
|
// b has greater rank, so a should redirect to b.
|
||||||
|
self.redirect_root(rank_b, key_a, key_b, new_value);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue