Merge branch 'main' into inline-imports

This commit is contained in:
Agus Zubiaga 2024-04-20 12:01:11 -03:00
commit d5a38a26db
No known key found for this signature in database
667 changed files with 22300 additions and 14562 deletions

View file

@ -9,7 +9,9 @@ use crate::parser::ESingleQuote;
use bumpalo::collections::{String, Vec};
use bumpalo::Bump;
use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_error_macros::internal_error;
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_module::ident::ModuleName;
use roc_region::all::{Loc, Position, Region};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -288,6 +290,7 @@ pub enum Expr<'a> {
Var {
module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar`
ident: &'a str,
suffixed: u8, // how many `!` suffixes, for example `doTheThing!!` executes a Task that returns a Task
},
Underscore(&'a str),
@ -305,6 +308,11 @@ pub enum Expr<'a> {
Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>),
/// Multiple defs in a row
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
/// Used in place of an expression when the final expression is empty
/// This may happen if the final expression is actually a suffixed statement
EmptyDefsFinal,
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Dbg(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -340,6 +348,7 @@ pub enum Expr<'a> {
// Problems
MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedClosure,
MalformedSuffixed(&'a Loc<Expr<'a>>),
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(&'a PrecedenceConflict<'a>),
@ -347,6 +356,128 @@ pub enum Expr<'a> {
UnappliedRecordBuilder(&'a Loc<Expr<'a>>),
}
impl Expr<'_> {
pub fn increment_var_suffix(&mut self, count: u8) {
match self {
Expr::Var { suffixed, .. } => {
*suffixed += count;
}
_ => {
internal_error!("increment_var_suffix called on non-Var expression");
}
}
}
pub fn get_region_spanning_binops(&self) -> Region {
match self {
Expr::BinOps(firsts, last) => {
let mut region = last.region;
for (loc_expr, _) in firsts.iter() {
region = Region::span_across(&loc_expr.region, &region);
}
region
}
_ => internal_error!("other expr types not supported"),
}
}
}
pub fn split_loc_exprs_around<'a>(
items: &'a [&Loc<Expr<'a>>],
index: usize,
) -> (&'a [&'a Loc<Expr<'a>>], &'a [&'a Loc<Expr<'a>>]) {
let (before, rest) = items.split_at(index);
let after = &rest[1..]; // Skip the index element
(before, after)
}
pub fn is_loc_expr_suffixed(loc_expr: &Loc<Expr>) -> bool {
match loc_expr.value.extract_spaces().item {
// expression without arguments, `read!`
Expr::Var { suffixed, .. } => suffixed > 0,
// expression with arguments, `line! "Foo"`
Expr::Apply(sub_loc_expr, apply_args, _) => {
let is_function_suffixed = is_loc_expr_suffixed(sub_loc_expr);
let any_args_suffixed = apply_args.iter().any(|arg| is_loc_expr_suffixed(arg));
any_args_suffixed || is_function_suffixed
}
// expression in a pipeline, `"hi" |> say!`
Expr::BinOps(firsts, last) => {
let is_expr_suffixed = is_loc_expr_suffixed(last);
let any_chain_suffixed = firsts
.iter()
.any(|(chain_loc_expr, _)| is_loc_expr_suffixed(chain_loc_expr));
is_expr_suffixed || any_chain_suffixed
}
// expression in a if-then-else, `if isOk! then "ok" else doSomething!`
Expr::If(if_thens, final_else) => {
let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| {
is_loc_expr_suffixed(if_then) || is_loc_expr_suffixed(else_expr)
});
is_loc_expr_suffixed(final_else) || any_if_thens_suffixed
}
// expression in parens `(read!)`
Expr::ParensAround(sub_loc_expr) => {
is_loc_expr_suffixed(&Loc::at(loc_expr.region, *sub_loc_expr))
}
// expression in a closure
Expr::Closure(_, sub_loc_expr) => is_loc_expr_suffixed(sub_loc_expr),
// expressions inside a Defs
// note we ignore the final expression as it should not be suffixed
Expr::Defs(defs, _) => {
let any_defs_suffixed = defs.tags.iter().any(|tag| match tag.split() {
Ok(_) => false,
Err(value_index) => match defs.value_defs[value_index.index()] {
ValueDef::Body(_, loc_expr) => is_loc_expr_suffixed(loc_expr),
ValueDef::AnnotatedBody { body_expr, .. } => is_loc_expr_suffixed(body_expr),
_ => false,
},
});
any_defs_suffixed
}
_ => false,
}
}
pub fn wrap_in_task_ok<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc<Expr<'a>> {
arena.alloc(Loc::at(
loc_expr.region,
Expr::Apply(
arena.alloc(Loc::at(
loc_expr.region,
Expr::Var {
module_name: ModuleName::TASK,
ident: "ok",
suffixed: 0,
},
)),
arena.alloc([loc_expr]),
CalledVia::BangSuffix,
),
))
}
pub fn split_around<T>(items: &[T], target: usize) -> (&[T], &[T]) {
let (before, rest) = items.split_at(target);
let after = &rest[1..];
(before, after)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrecedenceConflict<'a> {
pub whole_region: Region,
@ -463,6 +594,8 @@ pub enum ValueDef<'a> {
/// e.g. `import "path/to/my/file.txt" as myFile : Str`
IngestedFileImport(IngestedFileImport<'a>),
Stmt(&'a Loc<Expr<'a>>),
}
impl<'a> ValueDef<'a> {
@ -499,6 +632,15 @@ impl<'a> ValueDef<'a> {
exposed: _,
})
| ValueDef::IngestedFileImport(_) => None,
ValueDef::Stmt(loc_expr) => Some(&loc_expr.value),
}
}
pub fn replace_expr(&mut self, new_expr: &'a Loc<Expr<'a>>) {
match self {
ValueDef::Body(_, expr) => *expr = new_expr,
ValueDef::AnnotatedBody { body_expr, .. } => *body_expr = new_expr,
_ => internal_error!("replacing expr in unsupported ValueDef"),
}
}
}
@ -672,7 +814,9 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| OpaqueRef(_)
| MalformedIdent(_, _)
| MalformedClosure
| PrecedenceConflict(_) => { /* terminal */ }
| PrecedenceConflict(_)
| MalformedSuffixed(_)
| EmptyDefsFinal => { /* terminal */ }
}
}
}
@ -792,6 +936,16 @@ impl<'a> Defs<'a> {
})
}
pub fn list_value_defs(&self) -> impl Iterator<Item = (usize, &ValueDef<'a>)> {
self.tags
.iter()
.enumerate()
.filter_map(|(tag_index, tag)| match tag.split() {
Ok(_) => None,
Err(value_index) => Some((tag_index, &self.value_defs[value_index.index()])),
})
}
pub fn last(&self) -> Option<Result<&TypeDef<'a>, &ValueDef<'a>>> {
self.tags.last().map(|tag| match tag.split() {
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
@ -799,6 +953,89 @@ impl<'a> Defs<'a> {
})
}
// We could have a type annotation as the last tag,
// this helper ensures we refer to the last value_def
// and that we remove the correct tag
pub fn last_value_suffixed(&self) -> Option<(Self, &'a Loc<Expr<'a>>)> {
let value_indexes =
self.tags
.clone()
.into_iter()
.enumerate()
.filter_map(|(tag_index, tag)| match tag.split() {
Ok(_) => None,
Err(value_index) => Some((tag_index, value_index.index())),
});
if let Some((tag_index, value_index)) = value_indexes.last() {
match self.value_defs[value_index] {
ValueDef::Body(
Loc {
value: Pattern::RecordDestructure(collection),
..
},
loc_expr,
) if collection.is_empty() && is_loc_expr_suffixed(loc_expr) => {
let mut new_defs = self.clone();
new_defs.remove_value_def(tag_index);
return Some((new_defs, loc_expr));
}
ValueDef::Stmt(loc_expr) if is_loc_expr_suffixed(loc_expr) => {
let mut new_defs = self.clone();
new_defs.remove_value_def(tag_index);
return Some((new_defs, loc_expr));
}
_ => {}
}
}
None
}
pub fn remove_value_def(&mut self, index: usize) {
match self
.tags
.get(index)
.expect("got an invalid index for Defs")
.split()
{
Ok(type_index) => {
let index = type_index.index();
// remove from vec
self.type_defs.remove(index);
// update all of the remaining indexes in type_defs
for (tag_index, tag) in self.tags.iter_mut().enumerate() {
// only update later indexes into type_defs
if tag_index > index && tag.split().is_ok() {
tag.decrement_index();
}
}
}
Err(value_index) => {
let index: usize = value_index.index();
// remove from vec
self.value_defs.remove(index);
// update all of the remaining indexes in value_defs
for (tag_index, tag) in self.tags.iter_mut().enumerate() {
// only update later indexes into value_defs
if tag_index > index && tag.split().is_err() {
tag.decrement_index();
}
}
}
}
self.tags.remove(index);
self.regions.remove(index);
self.space_after.remove(index);
self.space_before.remove(index);
}
/// NOTE assumes the def itself is pushed already!
fn push_def_help(
&mut self,
@ -830,6 +1067,7 @@ impl<'a> Defs<'a> {
self.push_def_help(tag, region, spaces_before, spaces_after)
}
/// Replace the `value_def` at the given index
pub fn replace_with_value_def(
&mut self,
index: usize,
@ -854,6 +1092,85 @@ impl<'a> Defs<'a> {
let tag = EitherIndex::from_left(type_def_index);
self.push_def_help(tag, region, spaces_before, spaces_after)
}
/// Split the defs around a given target index
///
/// This is useful for unwrapping suffixed `!`
pub fn split_defs_around(&self, target: usize) -> SplitDefsAround<'a> {
let mut before = Defs::default();
let mut after = Defs::default();
for (tag_index, tag) in self.tags.iter().enumerate() {
let region = self.regions[tag_index];
let space_before = {
let start = self.space_before[tag_index].start();
let len = self.space_before[tag_index].len();
&self.spaces[start..(start + len)]
};
let space_after = {
let start = self.space_after[tag_index].start();
let len = self.space_after[tag_index].len();
&self.spaces[start..(start + len)]
};
match tag.split() {
Ok(type_def_index) => {
let type_def = self.type_defs[type_def_index.index()];
match tag_index.cmp(&target) {
std::cmp::Ordering::Less => {
// before
let type_def_index = Index::push_new(&mut before.type_defs, type_def);
let tag = EitherIndex::from_left(type_def_index);
before.push_def_help(tag, region, space_before, space_after);
}
std::cmp::Ordering::Greater => {
// after
let type_def_index = Index::push_new(&mut after.type_defs, type_def);
let tag = EitherIndex::from_left(type_def_index);
after.push_def_help(tag, region, space_before, space_after);
}
std::cmp::Ordering::Equal => {
// target, do nothing
}
}
}
Err(value_def_index) => {
let value_def = self.value_defs[value_def_index.index()];
match tag_index.cmp(&target) {
std::cmp::Ordering::Less => {
// before
let new_value_def_index =
Index::push_new(&mut before.value_defs, value_def);
let tag = EitherIndex::from_right(new_value_def_index);
before.push_def_help(tag, region, space_before, space_after);
}
std::cmp::Ordering::Greater => {
// after
let new_value_def_index =
Index::push_new(&mut after.value_defs, value_def);
let tag = EitherIndex::from_right(new_value_def_index);
after.push_def_help(tag, region, space_before, space_after);
}
std::cmp::Ordering::Equal => {
// target, do nothing
}
}
}
}
}
SplitDefsAround { before, after }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SplitDefsAround<'a> {
pub before: Defs<'a>,
pub after: Defs<'a>,
}
/// Should always be a zero-argument `Apply`; we'll check this in canonicalization
@ -1061,15 +1378,6 @@ impl<'a> CommentOrNewline<'a> {
}
}
pub fn to_string_repr(&self) -> std::string::String {
use CommentOrNewline::*;
match self {
Newline => "\n".to_owned(),
LineComment(comment_str) => format!("#{comment_str}"),
DocComment(comment_str) => format!("##{comment_str}"),
}
}
pub fn comment_str(&'a self) -> Option<&'a str> {
match self {
CommentOrNewline::LineComment(s) => Some(*s),
@ -1094,7 +1402,15 @@ impl<'a> PatternAs<'a> {
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pattern<'a> {
// Identifier
Identifier(&'a str),
Identifier {
ident: &'a str,
suffixed: u8,
},
QualifiedIdentifier {
module_name: &'a str,
ident: &'a str,
suffixed: u8,
},
Tag(&'a str),
@ -1146,10 +1462,6 @@ pub enum Pattern<'a> {
// Malformed
Malformed(&'a str),
MalformedIdent(&'a str, crate::ident::BadIdent),
QualifiedIdentifier {
module_name: &'a str,
ident: &'a str,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -1215,11 +1527,22 @@ impl<'a> Pattern<'a> {
// { x, y } : { x : Int, y ? Bool }
// { x, y ? False } = rec
OptionalField(x, _) => match other {
Identifier(y) | OptionalField(y, _) => x == y,
Identifier {
ident: y,
suffixed: 0,
}
| OptionalField(y, _) => x == y,
_ => false,
},
Identifier(x) => match other {
Identifier(y) | OptionalField(y, _) => x == y,
Identifier {
ident: x,
suffixed: a,
} => match other {
Identifier {
ident: y,
suffixed: b,
} => x == y && a == b,
OptionalField(y, _) => x == y,
_ => false,
},
NumLiteral(x) => {
@ -1280,13 +1603,15 @@ impl<'a> Pattern<'a> {
QualifiedIdentifier {
module_name: a,
ident: x,
suffixed: i,
} => {
if let QualifiedIdentifier {
module_name: b,
ident: y,
suffixed: j,
} = other
{
a == b && x == y
a == b && x == y && i == j
} else {
false
}
@ -1351,6 +1676,15 @@ impl<'a> Pattern<'a> {
}
}
}
// used to check if a pattern is suffixed to report as an error
pub fn is_suffixed(&self) -> bool {
match self {
Pattern::Identifier { suffixed, .. } => *suffixed > 0,
Pattern::QualifiedIdentifier { suffixed, .. } => *suffixed > 0,
_ => false,
}
}
}
#[derive(Copy, Clone)]
pub struct Collection<'a, T> {
@ -1604,6 +1938,18 @@ impl<'a> Spaceable<'a> for ImplementsAbilities<'a> {
}
impl<'a> Expr<'a> {
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
module_name: "",
ident: "<function>",
suffixed: 0,
};
pub const REPL_RUNTIME_CRASH: Self = Expr::Var {
module_name: "",
ident: "*",
suffixed: 0,
};
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
Loc {
region,
@ -1830,6 +2176,7 @@ impl<'a> Malformed for Expr<'a> {
OpaqueRef(_) |
SingleQuote(_) | // This is just a &str - not a bunch of segments
IngestedFile(_, _) |
EmptyDefsFinal |
Crash => false,
Str(inner) => inner.is_malformed(),
@ -1863,6 +2210,7 @@ impl<'a> Malformed for Expr<'a> {
MalformedIdent(_, _) |
MalformedClosure |
MalformedSuffixed(..) |
PrecedenceConflict(_) |
MultipleRecordBuilders(_) |
UnappliedRecordBuilder(_) => true,
@ -1960,7 +2308,7 @@ impl<'a> Malformed for Pattern<'a> {
use Pattern::*;
match self {
Identifier(_) |
Identifier{ .. } |
Tag(_) |
OpaqueRef(_) => false,
Apply(func, args) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
@ -2111,6 +2459,7 @@ impl<'a> Malformed for ValueDef<'a> {
path,
name: _,
}) => path.is_malformed(),
ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(),
}
}
}