This commit is contained in:
Luke Boswell 2024-11-11 10:22:58 +11:00
parent 743030fc99
commit 8a566dc339
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
260 changed files with 6344 additions and 2958 deletions

View file

@ -394,6 +394,7 @@ pub enum StrLiteral<'a> {
/// Values that can be tried, extracting success values or "returning early" on failure
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TryTarget {
// TODO: Remove when purity inference replaces Task fully
/// Tasks suffixed with ! are `Task.await`ed
Task,
/// Results suffixed with ? are `Result.try`ed
@ -841,6 +842,8 @@ pub enum ValueDef<'a> {
IngestedFileImport(IngestedFileImport<'a>),
Stmt(&'a Loc<Expr<'a>>),
StmtAfterExpr,
}
impl<'a> ValueDef<'a> {
@ -1093,7 +1096,9 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
}
}
ValueDef::Stmt(loc_expr) => self.push_pending_from_expr(&loc_expr.value),
ValueDef::Annotation(_, _) | ValueDef::IngestedFileImport(_) => {}
ValueDef::Annotation(_, _)
| ValueDef::IngestedFileImport(_)
| ValueDef::StmtAfterExpr => {}
}
self.index += 1;
@ -1530,10 +1535,22 @@ impl ImplementsAbilities<'_> {
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FunctionArrow {
/// ->
Pure,
/// =>
Effectful,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TypeAnnotation<'a> {
/// A function. The types of its arguments, then the type of its return value.
Function(&'a [Loc<TypeAnnotation<'a>>], &'a Loc<TypeAnnotation<'a>>),
/// A function. The types of its arguments, the type of arrow used, then the type of its return value.
Function(
&'a [Loc<TypeAnnotation<'a>>],
FunctionArrow,
&'a Loc<TypeAnnotation<'a>>,
),
/// Applying a type to some arguments (e.g. Map.Map String Int)
Apply(&'a str, &'a str, &'a [Loc<TypeAnnotation<'a>>]),
@ -2740,6 +2757,7 @@ impl<'a> Malformed for ValueDef<'a> {
annotation,
}) => path.is_malformed() || annotation.is_malformed(),
ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(),
ValueDef::StmtAfterExpr => false,
}
}
}
@ -2755,7 +2773,7 @@ impl<'a> Malformed for ModuleImportParams<'a> {
impl<'a> Malformed for TypeAnnotation<'a> {
fn is_malformed(&self) -> bool {
match self {
TypeAnnotation::Function(args, ret) => {
TypeAnnotation::Function(args, _arrow, ret) => {
args.iter().any(|arg| arg.is_malformed()) || ret.is_malformed()
}
TypeAnnotation::Apply(_, _, args) => args.iter().any(|arg| arg.is_malformed()),

View file

@ -3120,7 +3120,7 @@ fn stmts_to_defs<'a>(
break;
}
Stmt::Expr(e) => {
if is_expr_suffixed(&e) && i + 1 < stmts.len() {
if i + 1 < stmts.len() {
defs.push_value_def(
ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))),
sp_stmt.item.region,
@ -3128,10 +3128,6 @@ fn stmts_to_defs<'a>(
&[],
);
} else {
if last_expr.is_some() {
return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start()));
}
let e = if sp_stmt.before.is_empty() {
e
} else {
@ -3142,10 +3138,6 @@ fn stmts_to_defs<'a>(
}
}
Stmt::Backpassing(pats, call) => {
if last_expr.is_some() {
return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start()));
}
if i + 1 >= stmts.len() {
return Err(EExpr::BackpassContinue(sp_stmt.item.region.end()));
}
@ -3169,10 +3161,6 @@ fn stmts_to_defs<'a>(
}
Stmt::TypeDef(td) => {
if last_expr.is_some() {
return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start()));
}
if let (
TypeDef::Alias {
header,
@ -3224,10 +3212,6 @@ fn stmts_to_defs<'a>(
}
}
Stmt::ValueDef(vd) => {
if last_expr.is_some() {
return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start()));
}
// NOTE: it shouldn't be necessary to convert ValueDef::Dbg into an expr, but
// it turns out that ValueDef::Dbg exposes some bugs in the rest of the compiler.
// In particular, it seems that the solver thinks the dbg expr must be a bool.

View file

@ -19,6 +19,7 @@ use crate::pattern::record_pattern_fields;
use crate::state::State;
use crate::string_literal::{self, parse_str_literal};
use crate::type_annotation;
use roc_module::ident::IdentSuffix;
use roc_module::symbol::ModuleId;
use roc_region::all::{Loc, Position, Region};
@ -1132,6 +1133,10 @@ impl<'a> ExposedName<'a> {
pub fn as_str(&'a self) -> &'a str {
self.0
}
pub fn is_effectful_fn(&self) -> bool {
IdentSuffix::from_name(self.0).is_bang()
}
}
pub trait Keyword: Copy + Clone + Debug {
@ -1255,7 +1260,7 @@ pub struct PlatformHeader<'a> {
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ImportsEntry<'a> {
/// e.g. `Hello` or `Hello exposing [hello]` see roc-lang.org/examples/MultipleRocFiles/README.html
/// e.g. `Hello` or `Hello exposing [hello]` see roc-lang.org/examples/MultipleRocFiles/README.html
Module(
ModuleName<'a>,
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,

View file

@ -252,21 +252,27 @@ fn is_alnum(ch: char) -> bool {
}
fn chomp_lowercase_part(buffer: &[u8]) -> Result<&str, Progress> {
chomp_part(char::is_lowercase, is_alnum, buffer)
chomp_part(char::is_lowercase, is_alnum, true, buffer)
}
fn chomp_uppercase_part(buffer: &[u8]) -> Result<&str, Progress> {
chomp_part(char::is_uppercase, is_alnum, buffer)
chomp_part(char::is_uppercase, is_alnum, false, buffer)
}
fn chomp_anycase_part(buffer: &[u8]) -> Result<&str, Progress> {
chomp_part(char::is_alphabetic, is_alnum, buffer)
use encode_unicode::CharExt;
let allow_bang =
char::from_utf8_slice_start(buffer).map_or(false, |(leading, _)| leading.is_lowercase());
chomp_part(char::is_alphabetic, is_alnum, allow_bang, buffer)
}
fn chomp_integer_part(buffer: &[u8]) -> Result<&str, Progress> {
chomp_part(
|ch| char::is_ascii_digit(&ch),
|ch| char::is_ascii_digit(&ch),
false,
buffer,
)
}
@ -276,7 +282,12 @@ fn is_plausible_ident_continue(ch: char) -> bool {
}
#[inline(always)]
fn chomp_part<F, G>(leading_is_good: F, rest_is_good: G, buffer: &[u8]) -> Result<&str, Progress>
fn chomp_part<F, G>(
leading_is_good: F,
rest_is_good: G,
allow_bang: bool,
buffer: &[u8],
) -> Result<&str, Progress>
where
F: Fn(char) -> bool,
G: Fn(char) -> bool,
@ -296,6 +307,9 @@ where
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if rest_is_good(ch) {
chomped += width;
} else if allow_bang && ch == '!' {
chomped += width;
break;
} else {
// we're done
break;
@ -474,6 +488,16 @@ fn chomp_identifier_chain<'a>(
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if ch.is_alphabetic() || ch.is_ascii_digit() {
chomped += width;
} else if ch == '!' && !first_is_uppercase {
chomped += width;
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
let ident = Ident::Access {
module_name: "",
parts: arena.alloc([Accessor::RecordField(value)]),
};
return Ok((chomped as u32, ident));
} else {
// we're done
break;

View file

@ -439,6 +439,7 @@ impl<'a> Normalize<'a> for ValueDef<'a> {
IngestedFileImport(ingested_file_import.normalize(arena))
}
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.normalize(arena))),
StmtAfterExpr => StmtAfterExpr,
}
}
}
@ -881,8 +882,9 @@ impl<'a> Normalize<'a> for Pattern<'a> {
impl<'a> Normalize<'a> for TypeAnnotation<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match *self {
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
TypeAnnotation::Function(a, arrow, b) => TypeAnnotation::Function(
arena.alloc(a.normalize(arena)),
arrow,
arena.alloc(b.normalize(arena)),
),
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.normalize(arena)),
@ -1071,7 +1073,6 @@ impl<'a> Normalize<'a> for EExpr<'a> {
EExpr::IndentEnd(_pos) => EExpr::IndentEnd(Position::zero()),
EExpr::UnexpectedComma(_pos) => EExpr::UnexpectedComma(Position::zero()),
EExpr::UnexpectedTopLevelExpr(_pos) => EExpr::UnexpectedTopLevelExpr(Position::zero()),
EExpr::StmtAfterExpr(_pos) => EExpr::StmtAfterExpr(Position::zero()),
EExpr::RecordUpdateOldBuilderField(_pos) => {
EExpr::RecordUpdateOldBuilderField(Region::zero())
}

View file

@ -303,7 +303,6 @@ pub enum EExpr<'a> {
Start(Position),
End(Position),
BadExprEnd(Position),
StmtAfterExpr(Position),
Space(BadInputError, Position),
Dot(Position),

View file

@ -1,6 +1,7 @@
use crate::ast::{
AbilityImpls, AssignedField, CommentOrNewline, Expr, ImplementsAbilities, ImplementsAbility,
ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader,
AbilityImpls, AssignedField, CommentOrNewline, Expr, FunctionArrow, ImplementsAbilities,
ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation,
TypeHeader,
};
use crate::blankspace::{
space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
@ -594,16 +595,23 @@ fn expression<'a>(
],
))
.trace("type_annotation:expression:rest_args"),
skip_second(
and(
space0_e(EType::TIndentStart),
two_bytes(b'-', b'>', EType::TStart),
one_of![
map(two_bytes(b'-', b'>', EType::TStart), |_| {
FunctionArrow::Pure
}),
map(two_bytes(b'=', b'>', EType::TStart), |_| {
FunctionArrow::Effectful
}),
],
)
.trace("type_annotation:expression:arrow"),
)
.parse(arena, state.clone(), min_indent);
let (progress, annot, state) = match result {
Ok((p2, (rest, space_before_arrow), state)) => {
Ok((p2, (rest, (space_before_arrow, arrow)), state)) => {
let (p3, return_type, state) =
space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
.parse(arena, state, min_indent)?;
@ -626,7 +634,7 @@ fn expression<'a>(
let result = Loc {
region,
value: TypeAnnotation::Function(output, arena.alloc(return_type)),
value: TypeAnnotation::Function(output, arrow, arena.alloc(return_type)),
};
let progress = p1.or(p2).or(p3);
(progress, result, state)