mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-20 10:39:48 +00:00
Merge branch 'main' into specialize-exprs
This commit is contained in:
commit
2e96aca0fd
797 changed files with 17394 additions and 12632 deletions
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue