Merge remote-tracking branch 'remote/main' into rebuild-platform

This commit is contained in:
Luke Boswell 2024-11-04 13:57:51 +11:00
commit c00db6da37
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
100 changed files with 2554 additions and 279 deletions

View file

@ -496,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(...), ...)
@ -520,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>]),
@ -665,10 +675,14 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
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,
@ -938,6 +952,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);
@ -1008,6 +1031,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| Underscore(_)
| Crash
| Dbg
| Try
| Tag(_)
| OpaqueRef(_)
| MalformedIdent(_, _)
@ -2464,6 +2488,8 @@ impl<'a> Malformed for Expr<'a> {
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(),

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;
@ -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(
@ -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) => {
@ -2172,6 +2179,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Dbg
| Expr::DbgStmt(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::Return(_, _)
| Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }
@ -2644,6 +2652,33 @@ fn expect_help<'a>(
}
}
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 +2717,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,6 +3105,20 @@ 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() {
defs.push_value_def(

View file

@ -9,6 +9,7 @@ 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
@ -21,6 +22,6 @@ pub const WHERE: &str = "where";
// These keywords are valid in headers
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH,
pub const KEYWORDS: [&str; 12] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, 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,
},
};
@ -756,6 +756,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)
}
@ -1038,6 +1043,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())
@ -1047,6 +1055,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())
}
@ -1472,6 +1481,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>,
@ -337,10 +338,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 +516,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),

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,
}