Merge branch 'main' into specialize-exprs

This commit is contained in:
Agus Zubiaga 2024-11-23 01:48:51 -03:00
commit 2e96aca0fd
No known key found for this signature in database
797 changed files with 17394 additions and 12632 deletions

View file

@ -8,11 +8,12 @@ use crate::ident::Accessor;
use crate::parser::ESingleQuote;
use bumpalo::collections::{String, Vec};
use bumpalo::Bump;
use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_collections::soa::{index_push_new, slice_extend_new};
use roc_error_macros::internal_error;
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_module::ident::QualifiedModuleName;
use roc_region::all::{Loc, Position, Region};
use soa::{EitherIndex, Slice};
#[derive(Debug, Clone)]
pub struct FullAst<'a> {
@ -281,11 +282,10 @@ pub struct WhenPattern<'a> {
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StrSegment<'a> {
Plaintext(&'a str), // e.g. "foo"
Unicode(Loc<&'a str>), // e.g. "00A0" in "\u(00A0)"
EscapedChar(EscapedChar), // e.g. '\n' in "Hello!\n"
Interpolated(Loc<&'a Expr<'a>>),
DeprecatedInterpolated(Loc<&'a Expr<'a>>), // The old "$(...)" syntax - will be removed someday
Plaintext(&'a str), // e.g. "foo"
Unicode(Loc<&'a str>), // e.g. "00A0" in "\u(00A0)"
EscapedChar(EscapedChar), // e.g. '\n' in "Hello!\n"
Interpolated(Loc<&'a Expr<'a>>), // e.g. "$(expr)"
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -379,7 +379,6 @@ impl<'a> TryFrom<StrSegment<'a>> for SingleQuoteSegment<'a> {
StrSegment::Unicode(s) => Ok(SingleQuoteSegment::Unicode(s)),
StrSegment::EscapedChar(s) => Ok(SingleQuoteSegment::EscapedChar(s)),
StrSegment::Interpolated(_) => Err(ESingleQuote::InterpolationNotAllowed),
StrSegment::DeprecatedInterpolated(_) => Err(ESingleQuote::InterpolationNotAllowed),
}
}
}
@ -395,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
@ -489,7 +489,6 @@ pub enum Expr<'a> {
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Dbg,
DbgStmt(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -497,6 +496,9 @@ pub enum Expr<'a> {
// This form of debug is a desugared call to roc_dbg
LowLevelDbg(&'a (&'a str, &'a str), &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
/// The `try` keyword that performs early return on errors
Try,
// Application
/// To apply by name, do Apply(Var(...), ...)
/// To apply a tag by name, do Apply(Tag(...), ...)
@ -521,6 +523,13 @@ pub enum Expr<'a> {
&'a [&'a WhenBranch<'a>],
),
Return(
/// The return value
&'a Loc<Expr<'a>>,
/// The unused code after the return statement
Option<&'a Loc<Expr<'a>>>,
),
// Blank Space (e.g. comments, spaces, newlines) before or after an expression.
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a Expr<'a>, &'a [CommentOrNewline<'a>]),
@ -529,7 +538,6 @@ 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.
@ -662,18 +670,20 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::Tag(_) => false,
Expr::OpaqueRef(_) => false,
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
Expr::Expect(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::Dbg => false,
Expr::DbgStmt(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::Try => false,
Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value),
Expr::When(cond, branches) => {
is_expr_suffixed(&cond.value) || branches.iter().any(|x| is_when_branch_suffixed(x))
}
Expr::Return(a, b) => {
is_expr_suffixed(&a.value) || b.is_some_and(|loc_b| is_expr_suffixed(&loc_b.value))
}
Expr::SpaceBefore(a, _) => is_expr_suffixed(a),
Expr::SpaceAfter(a, _) => is_expr_suffixed(a),
Expr::MalformedIdent(_, _) => false,
Expr::MalformedClosure => false,
Expr::MalformedSuffixed(_) => false,
Expr::PrecedenceConflict(_) => false,
Expr::EmptyRecordBuilder(_) => false,
@ -699,7 +709,6 @@ fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool {
AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => {
is_assigned_value_suffixed(a)
}
AssignedField::Malformed(_) => false,
}
}
@ -816,11 +825,6 @@ pub enum ValueDef<'a> {
preceding_comment: Region,
},
ExpectFx {
condition: &'a Loc<Expr<'a>>,
preceding_comment: Region,
},
/// e.g. `import InternalHttp as Http exposing [Req]`.
ModuleImport(ModuleImport<'a>),
@ -828,6 +832,8 @@ pub enum ValueDef<'a> {
IngestedFileImport(IngestedFileImport<'a>),
Stmt(&'a Loc<Expr<'a>>),
StmtAfterExpr,
}
impl<'a> ValueDef<'a> {
@ -873,7 +879,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| OptionalValue(_, _, loc_val)
| IgnoredValue(_, _, loc_val) => break expr_stack.push(&loc_val.value),
SpaceBefore(next, _) | SpaceAfter(next, _) => current = *next,
LabelOnly(_) | Malformed(_) => break,
LabelOnly(_) => break,
}
}
}
@ -924,11 +930,6 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&a.value);
expr_stack.push(&b.value);
}
Expect(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
DbgStmt(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
@ -939,6 +940,15 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
Return(return_value, after_return) => {
if let Some(after_return) = after_return {
expr_stack.reserve(2);
expr_stack.push(&return_value.value);
expr_stack.push(&after_return.value);
} else {
expr_stack.push(&return_value.value);
}
}
Apply(fun, args, _) => {
expr_stack.reserve(args.len() + 1);
expr_stack.push(&fun.value);
@ -1009,10 +1019,10 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| Underscore(_)
| Crash
| Dbg
| Try
| Tag(_)
| OpaqueRef(_)
| MalformedIdent(_, _)
| MalformedClosure
| PrecedenceConflict(_)
| MalformedSuffixed(_) => { /* terminal */ }
}
@ -1048,10 +1058,6 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
| ValueDef::Expect {
condition,
preceding_comment: _,
}
| ValueDef::ExpectFx {
condition,
preceding_comment: _,
} => self.push_pending_from_expr(&condition.value),
ValueDef::ModuleImport(ModuleImport {
@ -1070,7 +1076,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;
@ -1313,10 +1321,10 @@ impl<'a> Defs<'a> {
self.regions.push(region);
let before = Slice::extend_new(&mut self.spaces, spaces_before.iter().copied());
let before = slice_extend_new(&mut self.spaces, spaces_before.iter().copied());
self.space_before.push(before);
let after = Slice::extend_new(&mut self.spaces, spaces_after.iter().copied());
let after = slice_extend_new(&mut self.spaces, spaces_after.iter().copied());
self.space_after.push(after);
}
@ -1327,7 +1335,7 @@ impl<'a> Defs<'a> {
spaces_before: &[CommentOrNewline<'a>],
spaces_after: &[CommentOrNewline<'a>],
) {
let value_def_index = Index::push_new(&mut self.value_defs, value_def);
let value_def_index = index_push_new(&mut self.value_defs, value_def);
let tag = EitherIndex::from_right(value_def_index);
self.push_def_help(tag, region, spaces_before, spaces_after)
}
@ -1361,7 +1369,7 @@ impl<'a> Defs<'a> {
spaces_before: &[CommentOrNewline<'a>],
spaces_after: &[CommentOrNewline<'a>],
) {
let type_def_index = Index::push_new(&mut self.type_defs, type_def);
let type_def_index = index_push_new(&mut self.type_defs, type_def);
let tag = EitherIndex::from_left(type_def_index);
self.push_def_help(tag, region, spaces_before, spaces_after)
}
@ -1376,13 +1384,13 @@ impl<'a> Defs<'a> {
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 start = self.space_before[tag_index].start() as usize;
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 start = self.space_after[tag_index].start() as usize;
let len = self.space_after[tag_index].len();
&self.spaces[start..(start + len)]
@ -1395,13 +1403,13 @@ impl<'a> Defs<'a> {
match tag_index.cmp(&target) {
std::cmp::Ordering::Less => {
// before
let type_def_index = Index::push_new(&mut before.type_defs, type_def);
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 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);
}
@ -1417,14 +1425,14 @@ impl<'a> Defs<'a> {
std::cmp::Ordering::Less => {
// before
let new_value_def_index =
Index::push_new(&mut before.value_defs, value_def);
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);
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);
}
@ -1507,10 +1515,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>>]),
@ -1574,9 +1594,6 @@ pub enum Tag<'a> {
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
/// A malformed tag, which will code gen to a runtime error
Malformed(&'a str),
}
#[derive(Debug, Clone, Copy, PartialEq)]
@ -1599,9 +1616,6 @@ pub enum AssignedField<'a, Val> {
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]),
/// A malformed assigned field, which will code gen to a runtime error
Malformed(&'a str),
}
impl<'a, Val> AssignedField<'a, Val> {
@ -1613,7 +1627,7 @@ impl<'a, Val> AssignedField<'a, Val> {
Self::RequiredValue(_, _, val)
| Self::OptionalValue(_, _, val)
| Self::IgnoredValue(_, _, val) => break Some(val),
Self::LabelOnly(_) | Self::Malformed(_) => break None,
Self::LabelOnly(_) => break None,
Self::SpaceBefore(next, _) | Self::SpaceAfter(next, _) => current = *next,
}
}
@ -2461,10 +2475,11 @@ impl<'a> Malformed for Expr<'a> {
Closure(args, body) => args.iter().any(|arg| arg.is_malformed()) || body.is_malformed(),
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
Expect(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Dbg => false,
DbgStmt(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Try => false,
Return(return_value, after_return) => return_value.is_malformed() || after_return.is_some_and(|ar| ar.is_malformed()),
Apply(func, args, _) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
BinOps(firsts, last) => firsts.iter().any(|(expr, _)| expr.is_malformed()) || last.is_malformed(),
UnaryOp(expr, _) => expr.is_malformed(),
@ -2476,7 +2491,6 @@ impl<'a> Malformed for Expr<'a> {
ParensAround(expr) => expr.is_malformed(),
MalformedIdent(_, _) |
MalformedClosure |
MalformedSuffixed(..) |
PrecedenceConflict(_) |
EmptyRecordBuilder(_) |
@ -2516,9 +2530,7 @@ impl<'a> Malformed for StrSegment<'a> {
fn is_malformed(&self) -> bool {
match self {
StrSegment::Plaintext(_) | StrSegment::Unicode(_) | StrSegment::EscapedChar(_) => false,
StrSegment::Interpolated(expr) | StrSegment::DeprecatedInterpolated(expr) => {
expr.is_malformed()
}
StrSegment::Interpolated(expr) => expr.is_malformed(),
}
}
}
@ -2553,7 +2565,6 @@ impl<'a, T: Malformed> Malformed for AssignedField<'a, T> {
AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => {
field.is_malformed()
}
AssignedField::Malformed(_) => true,
}
}
}
@ -2698,10 +2709,6 @@ impl<'a> Malformed for ValueDef<'a> {
| ValueDef::Expect {
condition,
preceding_comment: _,
}
| ValueDef::ExpectFx {
condition,
preceding_comment: _,
} => condition.is_malformed(),
ValueDef::ModuleImport(ModuleImport {
before_name: _,
@ -2717,6 +2724,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,
}
}
}
@ -2732,7 +2740,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()),
@ -2774,7 +2782,6 @@ impl<'a> Malformed for Tag<'a> {
match self {
Tag::Apply { name: _, args } => args.iter().any(|arg| arg.is_malformed()),
Tag::SpaceBefore(tag, _) | Tag::SpaceAfter(tag, _) => tag.is_malformed(),
Tag::Malformed(_) => true,
}
}
}

View file

@ -19,7 +19,7 @@ use crate::parser::{
map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first,
skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure,
EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, EPattern, ERecord,
EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem,
EReturn, EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem,
};
use crate::pattern::closure_param;
use crate::state::State;
@ -28,7 +28,7 @@ use crate::type_annotation;
use crate::{header, keyword};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::soa::Slice;
use roc_collections::soa::slice_extend_new;
use roc_error_macros::internal_error;
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region};
@ -195,6 +195,7 @@ fn loc_term_or_underscore_or_conditional<'a>(
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(crash_kw()),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(try_kw()),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
@ -217,6 +218,7 @@ fn loc_term_or_underscore<'a>(
)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(try_kw()),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
@ -235,6 +237,7 @@ fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EEx
)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(try_kw()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
@ -546,6 +549,7 @@ fn stmt_start<'a>(
EExpr::Dbg,
dbg_stmt_help(options, preceding_comment)
)),
loc(specialize_err(EExpr::Return, return_help(options))),
loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))),
map(
loc(specialize_err(EExpr::Closure, closure_help(options))),
@ -1443,6 +1447,7 @@ fn parse_stmt_operator<'a>(
let op_start = loc_op.region.start();
let op_end = loc_op.region.end();
let new_start = state.pos();
match op {
OperatorOrDef::BinOp(BinOp::Minus) if expr_state.end != op_start && op_end == new_start => {
parse_negated_term(
@ -2089,7 +2094,7 @@ pub fn merge_spaces<'a>(
fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, ()> {
let mut expr = expr.extract_spaces();
if let Expr::ParensAround(loc_expr) = &expr.item {
while let Expr::ParensAround(loc_expr) = &expr.item {
let expr_inner = loc_expr.extract_spaces();
expr.before = merge_spaces(arena, expr.before, expr_inner.before);
@ -2127,6 +2132,8 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
pattern
}
Expr::Try => Pattern::Identifier { ident: "try" },
Expr::SpaceBefore(..) | Expr::SpaceAfter(..) | Expr::ParensAround(..) => unreachable!(),
Expr::Record(fields) => {
@ -2168,11 +2175,10 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Defs(_, _)
| Expr::If { .. }
| Expr::When(_, _)
| Expr::Expect(_, _)
| Expr::Dbg
| Expr::DbgStmt(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::MalformedClosure
| Expr::Return(_, _)
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }
| Expr::EmptyRecordBuilder(_)
@ -2246,7 +2252,6 @@ fn assigned_expr_field_to_pattern_help<'a>(
arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?),
spaces,
),
AssignedField::Malformed(string) => Pattern::Malformed(string),
AssignedField::IgnoredValue(_, _, _) => return Err(()),
})
}
@ -2286,7 +2291,7 @@ pub fn parse_top_level_defs<'a>(
}
if output.tags.len() > existing_len {
let after = Slice::extend_new(&mut output.spaces, last_space.iter().copied());
let after = slice_extend_new(&mut output.spaces, last_space.iter().copied());
let last = output.tags.len() - 1;
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
output.space_after[last] = after;
@ -2613,11 +2618,9 @@ fn expect_help<'a>(
preceding_comment: Region,
) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent| {
let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect);
let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect);
let parse_expect = either(parse_expect_vanilla, parse_expect_fx);
let parse_expect = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect);
let (_, kw, state) = parse_expect.parse(arena, state, min_indent)?;
let (_, _kw, state) = parse_expect.parse(arena, state, min_indent)?;
let (_, condition, state) = parse_block(
options,
@ -2629,21 +2632,42 @@ fn expect_help<'a>(
)
.map_err(|(_, f)| (MadeProgress, f))?;
let vd = match kw {
Either::First(_) => ValueDef::Expect {
condition: arena.alloc(condition),
preceding_comment,
},
Either::Second(_) => ValueDef::ExpectFx {
condition: arena.alloc(condition),
preceding_comment,
},
let vd = ValueDef::Expect {
condition: arena.alloc(condition),
preceding_comment,
};
Ok((MadeProgress, Stmt::ValueDef(vd), state))
}
}
fn return_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, EReturn<'a>> {
(move |arena: &'a Bump, state: State<'a>, min_indent| {
let (_, return_kw, state) = loc(parser::keyword(keyword::RETURN, EReturn::Return))
.parse(arena, state, min_indent)?;
let (_, return_value, state) = parse_block(
options,
arena,
state,
true,
EReturn::IndentReturnValue,
EReturn::ReturnValue,
)
.map_err(|(_, f)| (MadeProgress, f))?;
let region = Region::span_across(&return_kw.region, &return_value.region);
let stmt = Stmt::Expr(Expr::Return(
arena.alloc(Loc::at(region, return_value.value)),
None,
));
Ok((MadeProgress, stmt, state))
})
.trace("return_help")
}
fn dbg_stmt_help<'a>(
options: ExprParseOptions,
preceding_comment: Region,
@ -2682,6 +2706,16 @@ fn dbg_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
.trace("dbg_kw")
}
fn try_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
(move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, _, next_state) =
parser::keyword("try", EExpr::Try).parse(arena, state, min_indent)?;
Ok((MadeProgress, Expr::Try, next_state))
})
.trace("try_kw")
}
fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
skip_second(
skip_first(
@ -3060,8 +3094,22 @@ fn stmts_to_defs<'a>(
while i < stmts.len() {
let sp_stmt = stmts[i];
match sp_stmt.item.value {
Stmt::Expr(Expr::Return(return_value, _after_return)) => {
if i == stmts.len() - 1 {
last_expr = Some(Loc::at_zero(Expr::Return(return_value, None)));
} else {
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
last_expr = Some(Loc::at_zero(Expr::Return(
return_value,
Some(arena.alloc(rest)),
)));
}
// don't re-process the rest of the statements, they got consumed by the early return
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,
@ -3069,10 +3117,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 {
@ -3083,10 +3127,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()));
}
@ -3110,10 +3150,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,
@ -3165,10 +3201,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

@ -4,7 +4,7 @@ use crate::ast::{
Collection, CommentOrNewline, Defs, Header, Malformed, Pattern, Spaced, Spaces, SpacesBefore,
StrLiteral, TypeAnnotation,
};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::blankspace::{space0_before_e, space0_e};
use crate::expr::merge_spaces;
use crate::ident::{self, lowercase_ident, unqualified_ident, UppercaseIdent};
use crate::parser::Progress::{self, *};
@ -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};
@ -587,7 +588,7 @@ fn requires<'a>(
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
record!(PlatformRequires {
rigids: skip_second(requires_rigids(), space0_e(ERequires::ListStart)),
signature: requires_typed_ident()
signatures: requires_typed_ident()
})
}
@ -607,18 +608,15 @@ fn requires_rigids<'a>(
}
#[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
skip_first(
fn requires_typed_ident<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>, ERequires<'a>> {
reset_min_indent(collection_trailing_sep_e(
byte(b'{', ERequires::ListStart),
skip_second(
reset_min_indent(space0_around_ee(
specialize_err(ERequires::TypedIdent, loc(typed_ident())),
ERequires::ListStart,
ERequires::ListEnd,
)),
byte(b'}', ERequires::ListStart),
),
)
specialize_err(ERequires::TypedIdent, loc(typed_ident())),
byte(b',', ERequires::ListEnd),
byte(b'}', ERequires::ListEnd),
Spaced::SpaceBefore,
))
}
#[inline(always)]
@ -955,7 +953,7 @@ pub enum HeaderType<'a> {
opt_app_module_id: Option<ModuleId>,
/// the name and type scheme of the main function (required by the platform)
/// (type scheme is currently unused)
provides: &'a [(Loc<ExposedName<'a>>, Loc<TypedIdent<'a>>)],
provides: &'a [Loc<ExposedName<'a>>],
requires: &'a [Loc<TypedIdent<'a>>],
requires_types: &'a [Loc<UppercaseIdent<'a>>],
exposes: &'a [Loc<ModuleName<'a>>],
@ -1135,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 {
@ -1239,7 +1241,7 @@ pub struct PackageHeader<'a> {
#[derive(Clone, Debug, PartialEq)]
pub struct PlatformRequires<'a> {
pub rigids: Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>,
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
pub signatures: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
}
#[derive(Clone, Debug, PartialEq)]
@ -1258,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>>>>,
@ -1393,7 +1395,7 @@ impl<'a> Malformed for PackageHeader<'a> {
impl<'a> Malformed for PlatformRequires<'a> {
fn is_malformed(&self) -> bool {
self.signature.is_malformed()
self.signatures.items.iter().any(|x| x.is_malformed())
}
}

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

@ -8,7 +8,7 @@ pub const IS: &str = "is";
pub const DBG: &str = "dbg";
pub const IMPORT: &str = "import";
pub const EXPECT: &str = "expect";
pub const EXPECT_FX: &str = "expect-fx";
pub const RETURN: &str = "return";
pub const CRASH: &str = "crash";
// These keywords are valid in imports
@ -22,5 +22,5 @@ pub const WHERE: &str = "where";
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH,
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, RETURN, CRASH,
];

View file

@ -16,7 +16,6 @@ pub mod keyword;
pub mod normalize;
pub mod number_literal;
pub mod pattern;
pub mod problems;
pub mod src64;
pub mod state;
pub mod string_literal;

View file

@ -22,9 +22,9 @@ use crate::{
parser::{
EAbility, EClosure, EExpect, EExposes, EExpr, EHeader, EIf, EImport, EImportParams,
EImports, EInParens, EList, EPackageEntry, EPackageName, EPackages, EParams, EPattern,
EProvides, ERecord, ERequires, EString, EType, ETypeAbilityImpl, ETypeApply, ETypeInParens,
ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ETypedIdent, EWhen, PInParens, PList,
PRecord, SyntaxError,
EProvides, ERecord, ERequires, EReturn, EString, EType, ETypeAbilityImpl, ETypeApply,
ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ETypedIdent, EWhen, PInParens,
PList, PRecord, SyntaxError,
},
};
@ -254,7 +254,7 @@ impl<'a> Normalize<'a> for PlatformRequires<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
PlatformRequires {
rigids: self.rigids.normalize(arena),
signature: self.signature.normalize(arena),
signatures: self.signatures.map_items(arena, |x| x.normalize(arena)),
}
}
}
@ -427,18 +427,12 @@ impl<'a> Normalize<'a> for ValueDef<'a> {
condition: arena.alloc(condition.normalize(arena)),
preceding_comment: Region::zero(),
},
ExpectFx {
condition,
preceding_comment: _,
} => ExpectFx {
condition: arena.alloc(condition.normalize(arena)),
preceding_comment: Region::zero(),
},
ModuleImport(module_import) => ModuleImport(module_import.normalize(arena)),
IngestedFileImport(ingested_file_import) => {
IngestedFileImport(ingested_file_import.normalize(arena))
}
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.normalize(arena))),
StmtAfterExpr => StmtAfterExpr,
}
}
}
@ -555,7 +549,6 @@ impl<'a, T: Normalize<'a> + Copy + std::fmt::Debug> Normalize<'a> for AssignedFi
arena.alloc(c.normalize(arena)),
),
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.normalize(arena)),
AssignedField::Malformed(a) => AssignedField::Malformed(a),
AssignedField::SpaceBefore(a, _) => a.normalize(arena),
AssignedField::SpaceAfter(a, _) => a.normalize(arena),
}
@ -649,13 +642,6 @@ fn normalize_str_segments<'a>(
}
new_segments.push(StrSegment::Interpolated(e.normalize(arena)));
}
StrSegment::DeprecatedInterpolated(e) => {
if !last_text.is_empty() {
let text = std::mem::replace(last_text, String::new_in(arena));
new_segments.push(StrSegment::Plaintext(text.into_bump_str()));
}
new_segments.push(StrSegment::Interpolated(e.normalize(arena)));
}
}
}
}
@ -680,7 +666,6 @@ impl<'a> Normalize<'a> for StrSegment<'a> {
StrSegment::Unicode(t) => StrSegment::Unicode(t.normalize(arena)),
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.normalize(arena)),
StrSegment::DeprecatedInterpolated(t) => StrSegment::Interpolated(t.normalize(arena)),
}
}
}
@ -750,10 +735,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
arena.alloc(b.normalize(arena)),
arena.alloc(c.normalize(arena)),
),
Expr::Expect(a, b) => Expr::Expect(
arena.alloc(a.normalize(arena)),
arena.alloc(b.normalize(arena)),
),
Expr::Dbg => Expr::Dbg,
Expr::DbgStmt(a, b) => Expr::DbgStmt(
arena.alloc(a.normalize(arena)),
@ -764,6 +745,11 @@ impl<'a> Normalize<'a> for Expr<'a> {
arena.alloc(a.normalize(arena)),
arena.alloc(b.normalize(arena)),
),
Expr::Try => Expr::Try,
Expr::Return(a, b) => Expr::Return(
arena.alloc(a.normalize(arena)),
b.map(|loc_b| &*arena.alloc(loc_b.normalize(arena))),
),
Expr::Apply(a, b, c) => {
Expr::Apply(arena.alloc(a.normalize(arena)), b.normalize(arena), c)
}
@ -786,7 +772,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
a.normalize(arena)
}
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
Expr::MalformedClosure => Expr::MalformedClosure,
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::SpaceBefore(a, _) => a.normalize(arena),
@ -884,8 +869,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)),
@ -939,7 +925,6 @@ impl<'a> Normalize<'a> for Tag<'a> {
name: name.normalize(arena),
args: args.normalize(arena),
},
Tag::Malformed(a) => Tag::Malformed(a),
Tag::SpaceBefore(a, _) => a.normalize(arena),
Tag::SpaceAfter(a, _) => a.normalize(arena),
}
@ -1046,6 +1031,9 @@ impl<'a> Normalize<'a> for EExpr<'a> {
EExpr::Expect(inner_err, _pos) => {
EExpr::Expect(inner_err.normalize(arena), Position::zero())
}
EExpr::Return(inner_err, _pos) => {
EExpr::Return(inner_err.normalize(arena), Position::zero())
}
EExpr::Dbg(inner_err, _pos) => EExpr::Dbg(inner_err.normalize(arena), Position::zero()),
EExpr::Import(inner_err, _pos) => {
EExpr::Import(inner_err.normalize(arena), Position::zero())
@ -1055,6 +1043,7 @@ impl<'a> Normalize<'a> for EExpr<'a> {
}
EExpr::Underscore(_pos) => EExpr::Underscore(Position::zero()),
EExpr::Crash(_pos) => EExpr::Crash(Position::zero()),
EExpr::Try(_pos) => EExpr::Try(Position::zero()),
EExpr::InParens(inner_err, _pos) => {
EExpr::InParens(inner_err.normalize(arena), Position::zero())
}
@ -1070,7 +1059,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())
}
@ -1480,6 +1468,20 @@ impl<'a> Normalize<'a> for EExpect<'a> {
}
}
}
impl<'a> Normalize<'a> for EReturn<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match self {
EReturn::Space(inner_err, _) => EReturn::Space(*inner_err, Position::zero()),
EReturn::Return(_) => EReturn::Return(Position::zero()),
EReturn::ReturnValue(inner_err, _) => {
EReturn::ReturnValue(arena.alloc(inner_err.normalize(arena)), Position::zero())
}
EReturn::IndentReturnValue(_) => EReturn::IndentReturnValue(Position::zero()),
}
}
}
impl<'a> Normalize<'a> for EIf<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match self {

View file

@ -97,6 +97,7 @@ impl_space_problem! {
EPattern<'a>,
EProvides<'a>,
ERecord<'a>,
EReturn<'a>,
ERequires<'a>,
EString<'a>,
EType<'a>,
@ -302,7 +303,6 @@ pub enum EExpr<'a> {
Start(Position),
End(Position),
BadExprEnd(Position),
StmtAfterExpr(Position),
Space(BadInputError, Position),
Dot(Position),
@ -337,10 +337,12 @@ pub enum EExpr<'a> {
Expect(EExpect<'a>, Position),
Dbg(EExpect<'a>, Position),
Import(EImport<'a>, Position),
Return(EReturn<'a>, Position),
Closure(EClosure<'a>, Position),
Underscore(Position),
Crash(Position),
Try(Position),
InParens(EInParens<'a>, Position),
Record(ERecord<'a>, Position),
@ -513,6 +515,14 @@ pub enum EExpect<'a> {
IndentCondition(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EReturn<'a> {
Space(BadInputError, Position),
Return(Position),
ReturnValue(&'a EExpr<'a>, Position),
IndentReturnValue(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EImport<'a> {
Import(Position),
@ -1046,7 +1056,11 @@ where
// the next character should not be an identifier character
// to prevent treating `whence` or `iffy` as keywords
match state.bytes().get(width) {
Some(next) if *next == b' ' || *next == b'#' || *next == b'\n' || *next == b'\r' => {
Some(
b' ' | b'#' | b'\n' | b'\r' | b'\t' | b',' | b'(' | b')' | b'[' | b']' | b'{'
| b'}' | b'"' | b'\'' | b'/' | b'\\' | b'+' | b'*' | b'%' | b'^' | b'&' | b'|'
| b'<' | b'>' | b'=' | b'!' | b'~' | b'`' | b';' | b':' | b'?' | b'.',
) => {
state = state.advance(width);
Ok((MadeProgress, (), state))
}

View file

@ -1,25 +0,0 @@
use roc_region::all::Loc;
pub type Problems = Vec<Loc<Problem>>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Problem {
// UNICODE CODE POINT
/// TODO Invalid hex code - Unicode code points must be specified using hexadecimal characters (the numbers 0-9 and letters A-F)
NonHexCharsInUnicodeCodePt,
/// TODO Invalid Unicode code point. It must be no more than \\u{10FFFF}.
UnicodeCodePtTooLarge,
InvalidUnicodeCodePt,
MalformedEscapedUnicode,
NoUnicodeDigits,
// STRING LITERAL
NewlineInLiteral,
Tab,
CarriageReturn,
NullChar,
UnsupportedEscapedChar,
// NUMBER LITERAL
OutsideSupportedRange,
}

View file

@ -364,36 +364,6 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
// This is the start of a new escape. Look at the next byte
// to figure out what type of escape it is.
match bytes.next() {
Some(b'(') => {
// Advance past the `\(` before using the expr parser
state.advance_mut(2);
let original_byte_count = state.bytes().len();
// This is an interpolated variable.
// Parse an arbitrary expression, then give a
// canonicalization error if that expression variant
// is not allowed inside a string interpolation.
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help()))),
),
byte(b')', EString::FormatEnd),
)
.parse(arena, state, min_indent)?;
// Advance the iterator past the expr we just parsed.
for _ in 0..(original_byte_count - new_state.bytes().len()) {
bytes.next();
}
segments.push(StrSegment::DeprecatedInterpolated(loc_expr));
// Reset the segment
segment_parsed_bytes = 0;
state = new_state;
}
Some(b'u') => {
// Advance past the `\u` before using the expr parser
state.advance_mut(2);
@ -444,8 +414,8 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
}
_ => {
// Invalid escape! A backslash must be followed
// by either an open paren or else one of the
// escapable characters (\n, \t, \", \\, etc)
// by one of these escapable characters:
// (\n, \t, \", \\, etc)
return Err((MadeProgress, EString::UnknownEscape(state.pos())));
}
}

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)