mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-01 13:34:15 +00:00
Merge attempt
This commit is contained in:
parent
7c15c16ae0
commit
12df3a04de
236 changed files with 9053 additions and 6428 deletions
|
|
@ -392,6 +392,15 @@ pub enum StrLiteral<'a> {
|
|||
Block(&'a [&'a [StrSegment<'a>]]),
|
||||
}
|
||||
|
||||
/// Values that can be tried, extracting success values or "returning early" on failure
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum TryTarget {
|
||||
/// Tasks suffixed with ! are `Task.await`ed
|
||||
Task,
|
||||
/// Results suffixed with ? are `Result.try`ed
|
||||
Result,
|
||||
}
|
||||
|
||||
/// A parsed expression. This uses lifetimes extensively for two reasons:
|
||||
///
|
||||
/// 1. It uses Bump::alloc for all allocations, which returns a reference.
|
||||
|
|
@ -423,11 +432,17 @@ pub enum Expr<'a> {
|
|||
/// e.g. `.foo` or `.0`
|
||||
AccessorFunction(Accessor<'a>),
|
||||
|
||||
/// Update the value of a field in a record, e.g. `&foo`
|
||||
RecordUpdater(&'a str),
|
||||
|
||||
/// Look up exactly one field on a tuple, e.g. `(x, y).1`.
|
||||
TupleAccess(&'a Expr<'a>, &'a str),
|
||||
|
||||
/// Task await bang - i.e. the ! in `File.readUtf8! path`
|
||||
TaskAwaitBang(&'a Expr<'a>),
|
||||
/// Early return on failures - e.g. the ! in `File.readUtf8! path`
|
||||
TrySuffix {
|
||||
target: TryTarget,
|
||||
expr: &'a Expr<'a>,
|
||||
},
|
||||
|
||||
// Collection Literals
|
||||
List(Collection<'a, &'a Loc<Expr<'a>>>),
|
||||
|
|
@ -555,9 +570,9 @@ pub fn split_loc_exprs_around<'a>(
|
|||
|
||||
/// Checks if the bang suffix is applied only at the top level of expression
|
||||
pub fn is_top_level_suffixed(expr: &Expr) -> bool {
|
||||
// TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang?
|
||||
// TODO: should we check BinOps with pizza where the last expression is TrySuffix?
|
||||
match expr {
|
||||
Expr::TaskAwaitBang(..) => true,
|
||||
Expr::TrySuffix { .. } => true,
|
||||
Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value),
|
||||
Expr::SpaceBefore(a, _) => is_top_level_suffixed(a),
|
||||
Expr::SpaceAfter(a, _) => is_top_level_suffixed(a),
|
||||
|
|
@ -571,7 +586,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
|||
// expression without arguments, `read!`
|
||||
Expr::Var { .. } => false,
|
||||
|
||||
Expr::TaskAwaitBang(..) => true,
|
||||
Expr::TrySuffix { .. } => true,
|
||||
|
||||
// expression with arguments, `line! "Foo"`
|
||||
Expr::Apply(sub_loc_expr, apply_args, _) => {
|
||||
|
|
@ -624,6 +639,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
|||
Expr::SingleQuote(_) => false,
|
||||
Expr::RecordAccess(a, _) => is_expr_suffixed(a),
|
||||
Expr::AccessorFunction(_) => false,
|
||||
Expr::RecordUpdater(_) => false,
|
||||
Expr::TupleAccess(a, _) => is_expr_suffixed(a),
|
||||
Expr::List(items) => items.iter().any(|x| is_expr_suffixed(&x.value)),
|
||||
Expr::RecordUpdate { update, fields } => {
|
||||
|
|
@ -995,7 +1011,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
|||
}
|
||||
RecordAccess(expr, _)
|
||||
| TupleAccess(expr, _)
|
||||
| TaskAwaitBang(expr)
|
||||
| TrySuffix { expr, .. }
|
||||
| SpaceBefore(expr, _)
|
||||
| SpaceAfter(expr, _)
|
||||
| ParensAround(expr) => expr_stack.push(expr),
|
||||
|
|
@ -1012,6 +1028,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
|||
| Str(_)
|
||||
| SingleQuote(_)
|
||||
| AccessorFunction(_)
|
||||
| RecordUpdater(_)
|
||||
| Var { .. }
|
||||
| Underscore(_)
|
||||
| Crash
|
||||
|
|
@ -1068,7 +1085,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
|
|||
params,
|
||||
}) => {
|
||||
if let Some(ModuleImportParams { before: _, params }) = params {
|
||||
for loc_assigned_field in params.items {
|
||||
for loc_assigned_field in params.value.items {
|
||||
if let Some(expr) = loc_assigned_field.value.value() {
|
||||
self.push_pending_from_expr(&expr.value);
|
||||
}
|
||||
|
|
@ -1116,7 +1133,7 @@ pub struct ModuleImport<'a> {
|
|||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ModuleImportParams<'a> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub params: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
|
||||
pub params: Loc<Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
|
@ -2475,6 +2492,7 @@ impl<'a> Malformed for Expr<'a> {
|
|||
Num(_) |
|
||||
NonBase10Int { .. } |
|
||||
AccessorFunction(_) |
|
||||
RecordUpdater(_) |
|
||||
Var { .. } |
|
||||
Underscore(_) |
|
||||
Tag(_) |
|
||||
|
|
@ -2486,7 +2504,7 @@ impl<'a> Malformed for Expr<'a> {
|
|||
|
||||
RecordAccess(inner, _) |
|
||||
TupleAccess(inner, _) |
|
||||
TaskAwaitBang(inner) => inner.is_malformed(),
|
||||
TrySuffix { expr: inner, .. } => inner.is_malformed(),
|
||||
|
||||
List(items) => items.is_malformed(),
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::ast::{
|
|||
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
||||
ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
|
||||
|
|
@ -169,7 +169,12 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr
|
|||
)
|
||||
)
|
||||
),
|
||||
map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
|
||||
map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(
|
||||
TryTarget::Task
|
||||
)),
|
||||
map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(
|
||||
TryTarget::Result
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -892,10 +897,10 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
|
|||
then(
|
||||
and(
|
||||
backtrackable(space0_e(EImportParams::Indent)),
|
||||
specialize_err(EImportParams::Record, record_help()),
|
||||
specialize_err(EImportParams::Record, loc(record_help())),
|
||||
),
|
||||
|arena, state, _, (before, record): (_, RecordHelp<'a>)| {
|
||||
if let Some(prefix) = record.prefix {
|
||||
|arena, state, _, (before, loc_record): (_, Loc<RecordHelp<'a>>)| {
|
||||
if let Some(prefix) = loc_record.value.prefix {
|
||||
match prefix {
|
||||
(update, RecordHelpPrefix::Update) => {
|
||||
return Err((
|
||||
|
|
@ -912,21 +917,27 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
|
|||
}
|
||||
}
|
||||
|
||||
let params = record.fields.map_items_result(arena, |loc_field| {
|
||||
match loc_field.value.to_assigned_field(arena) {
|
||||
Ok(AssignedField::IgnoredValue(_, _, _)) => Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordIgnoredFieldFound(loc_field.region),
|
||||
)),
|
||||
Ok(field) => Ok(Loc::at(loc_field.region, field)),
|
||||
Err(FoundApplyValue) => Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordApplyFound(loc_field.region),
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
let params = loc_record
|
||||
.value
|
||||
.fields
|
||||
.map_items_result(arena, |loc_field| {
|
||||
match loc_field.value.to_assigned_field(arena) {
|
||||
Ok(AssignedField::IgnoredValue(_, _, _)) => Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordIgnoredFieldFound(loc_field.region),
|
||||
)),
|
||||
Ok(field) => Ok(Loc::at(loc_field.region, field)),
|
||||
Err(FoundApplyValue) => Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordApplyFound(loc_field.region),
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
|
||||
let import_params = ModuleImportParams { before, params };
|
||||
let import_params = ModuleImportParams {
|
||||
before,
|
||||
params: Loc::at(loc_record.region, params),
|
||||
};
|
||||
|
||||
Ok((MadeProgress, import_params, state))
|
||||
},
|
||||
|
|
@ -2170,8 +2181,9 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
| Expr::SingleFieldRecordBuilder(_)
|
||||
| Expr::OptionalFieldInRecordBuilder(_, _)
|
||||
| Expr::RecordUpdate { .. }
|
||||
| Expr::RecordUpdater(_)
|
||||
| Expr::UnaryOp(_, _)
|
||||
| Expr::TaskAwaitBang(..)
|
||||
| Expr::TrySuffix { .. }
|
||||
| Expr::Crash
|
||||
| Expr::OldRecordBuilder(..)
|
||||
| Expr::RecordBuilder { .. } => return Err(()),
|
||||
|
|
@ -3240,7 +3252,7 @@ pub fn join_alias_to_body<'a>(
|
|||
/// 2. The beginning of a function call (e.g. `foo bar baz`)
|
||||
/// 3. The beginning of a definition (e.g. `foo =`)
|
||||
/// 4. The beginning of a type annotation (e.g. `foo :`)
|
||||
/// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else.
|
||||
/// 5. A reserved keyword (e.g. `if ` or `when `), meaning we should do something else.
|
||||
|
||||
fn assign_or_destructure_identifier<'a>() -> impl Parser<'a, Ident<'a>, EExpr<'a>> {
|
||||
parse_ident
|
||||
|
|
@ -3302,6 +3314,7 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
|||
answer
|
||||
}
|
||||
Ident::AccessorFunction(string) => Expr::AccessorFunction(string),
|
||||
Ident::RecordUpdaterFunction(string) => Expr::RecordUpdater(string),
|
||||
Ident::Malformed(string, problem) => Expr::MalformedIdent(string, problem),
|
||||
}
|
||||
}
|
||||
|
|
@ -3743,7 +3756,10 @@ fn apply_expr_access_chain<'a>(
|
|||
Suffix::Accessor(Accessor::TupleIndex(field)) => {
|
||||
Expr::TupleAccess(arena.alloc(value), field)
|
||||
}
|
||||
Suffix::TaskAwaitBang => Expr::TaskAwaitBang(arena.alloc(value)),
|
||||
Suffix::TrySuffix(target) => Expr::TrySuffix {
|
||||
target,
|
||||
expr: arena.alloc(value),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
|||
|
||||
fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> {
|
||||
record!(ModuleParams {
|
||||
params: specialize_err(EParams::Pattern, record_pattern_fields()),
|
||||
pattern: specialize_err(EParams::Pattern, loc(record_pattern_fields())),
|
||||
before_arrow: skip_second(
|
||||
space0_e(EParams::BeforeArrow),
|
||||
loc(two_bytes(b'-', b'>', EParams::Arrow))
|
||||
|
|
@ -943,6 +943,7 @@ pub enum HeaderType<'a> {
|
|||
Builtin {
|
||||
name: ModuleName<'a>,
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
opt_params: Option<ModuleParams<'a>>,
|
||||
},
|
||||
Package {
|
||||
/// usually something other than `pf`
|
||||
|
|
@ -966,6 +967,7 @@ pub enum HeaderType<'a> {
|
|||
Module {
|
||||
name: ModuleName<'a>,
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
opt_params: Option<ModuleParams<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -990,11 +992,54 @@ impl<'a> HeaderType<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_params(&self) -> &Option<ModuleParams<'a>> {
|
||||
match self {
|
||||
Self::Module {
|
||||
opt_params,
|
||||
name: _,
|
||||
exposes: _,
|
||||
}
|
||||
| Self::Builtin {
|
||||
opt_params,
|
||||
name: _,
|
||||
exposes: _,
|
||||
} => opt_params,
|
||||
Self::App {
|
||||
provides: _,
|
||||
to_platform: _,
|
||||
}
|
||||
| Self::Package {
|
||||
config_shorthand: _,
|
||||
exposes: _,
|
||||
exposes_ids: _,
|
||||
}
|
||||
| Self::Hosted {
|
||||
name: _,
|
||||
exposes: _,
|
||||
}
|
||||
| Self::Platform {
|
||||
opt_app_module_id: _,
|
||||
provides: _,
|
||||
requires: _,
|
||||
requires_types: _,
|
||||
exposes: _,
|
||||
exposes_ids: _,
|
||||
config_shorthand: _,
|
||||
} => &None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self {
|
||||
match self {
|
||||
HeaderType::Module { name, exposes } if module_id.is_builtin() => {
|
||||
HeaderType::Builtin { name, exposes }
|
||||
}
|
||||
HeaderType::Module {
|
||||
name,
|
||||
exposes,
|
||||
opt_params,
|
||||
} if module_id.is_builtin() => HeaderType::Builtin {
|
||||
name,
|
||||
exposes,
|
||||
opt_params,
|
||||
},
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
|
@ -1139,7 +1184,7 @@ pub struct ModuleHeader<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ModuleParams<'a> {
|
||||
pub params: Collection<'a, Loc<Pattern<'a>>>,
|
||||
pub pattern: Loc<Collection<'a, Loc<Pattern<'a>>>>,
|
||||
pub before_arrow: &'a [CommentOrNewline<'a>],
|
||||
pub after_arrow: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::ast::TryTarget;
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{BadInputError, EExpr, ParseResult, Parser};
|
||||
use crate::state::State;
|
||||
|
|
@ -45,42 +46,12 @@ pub enum Ident<'a> {
|
|||
},
|
||||
/// `.foo { foo: 42 }` or `.1 (1, 2, 3)`
|
||||
AccessorFunction(Accessor<'a>),
|
||||
/// `&foo { foo: 42 } 3`
|
||||
RecordUpdaterFunction(&'a str),
|
||||
/// .Foo or foo. or something like foo.Bar
|
||||
Malformed(&'a str, BadIdent),
|
||||
}
|
||||
|
||||
impl<'a> Ident<'a> {
|
||||
pub fn len(&self) -> usize {
|
||||
use self::Ident::*;
|
||||
|
||||
match self {
|
||||
Tag(string) | OpaqueRef(string) => string.len(),
|
||||
Access {
|
||||
module_name, parts, ..
|
||||
} => {
|
||||
let mut len = if module_name.is_empty() {
|
||||
0
|
||||
} else {
|
||||
module_name.len() + 1
|
||||
// +1 for the dot
|
||||
};
|
||||
|
||||
for part in parts.iter() {
|
||||
len += part.len() + 1 // +1 for the dot
|
||||
}
|
||||
|
||||
len - 1
|
||||
}
|
||||
AccessorFunction(string) => string.len(),
|
||||
Malformed(string, _) => string.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// This could be:
|
||||
///
|
||||
/// * A record field, e.g. "email" in `.email` or in `email:`
|
||||
|
|
@ -271,6 +242,7 @@ pub enum BadIdent {
|
|||
WeirdDotAccess(Position),
|
||||
WeirdDotQualified(Position),
|
||||
StrayDot(Position),
|
||||
StrayAmpersand(Position),
|
||||
BadOpaqueRef(Position),
|
||||
QualifiedTupleAccessor(Position),
|
||||
}
|
||||
|
|
@ -377,7 +349,7 @@ impl<'a> Accessor<'a> {
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Suffix<'a> {
|
||||
Accessor(Accessor<'a>),
|
||||
TaskAwaitBang,
|
||||
TrySuffix(TryTarget),
|
||||
}
|
||||
|
||||
/// a `.foo` or `.1` accessor function
|
||||
|
|
@ -415,6 +387,18 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<Accessor, BadIdent> {
|
|||
}
|
||||
}
|
||||
|
||||
/// a `&foo` record updater function
|
||||
fn chomp_record_updater(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||
// assumes the leading `&` has been chomped already
|
||||
match chomp_lowercase_part(buffer) {
|
||||
Ok(name) => Ok(name),
|
||||
Err(_) => {
|
||||
// we've already made progress with the initial `&`
|
||||
Err(BadIdent::StrayAmpersand(pos.bump_column(1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// a `@Token` opaque
|
||||
fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||
// assumes the leading `@` has NOT been chomped already
|
||||
|
|
@ -457,6 +441,14 @@ fn chomp_identifier_chain<'a>(
|
|||
}
|
||||
Err(fail) => return Err((1, fail)),
|
||||
},
|
||||
'&' => match chomp_record_updater(&buffer[1..], pos) {
|
||||
Ok(updater) => {
|
||||
let bytes_parsed = 1 + updater.len();
|
||||
return Ok((bytes_parsed as u32, Ident::RecordUpdaterFunction(updater)));
|
||||
}
|
||||
// return 0 bytes consumed on failure to allow parsing &&
|
||||
Err(fail) => return Err((0, fail)),
|
||||
},
|
||||
'@' => match chomp_opaque_ref(buffer, pos) {
|
||||
Ok(tagname) => {
|
||||
let bytes_parsed = tagname.len();
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ pub mod header;
|
|||
pub mod highlight;
|
||||
pub mod ident;
|
||||
pub mod keyword;
|
||||
pub mod normalize;
|
||||
pub mod number_literal;
|
||||
pub mod pattern;
|
||||
pub mod problems;
|
||||
pub mod remove_spaces;
|
||||
pub mod src64;
|
||||
pub mod state;
|
||||
pub mod string_literal;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -577,6 +577,7 @@ pub enum EPattern<'a> {
|
|||
AsIndentStart(Position),
|
||||
|
||||
AccessorFunction(Position),
|
||||
RecordUpdaterFunction(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub enum PatternType {
|
|||
DefExpr,
|
||||
FunctionArg,
|
||||
WhenBranch,
|
||||
ModuleParams,
|
||||
}
|
||||
|
||||
pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
|
|
@ -435,6 +436,10 @@ fn loc_ident_pattern_help<'a>(
|
|||
MadeProgress,
|
||||
EPattern::AccessorFunction(loc_ident.region.start()),
|
||||
)),
|
||||
Ident::RecordUpdaterFunction(_string) => Err((
|
||||
MadeProgress,
|
||||
EPattern::RecordUpdaterFunction(loc_ident.region.start()),
|
||||
)),
|
||||
Ident::Malformed(malformed, problem) => {
|
||||
debug_assert!(!malformed.is_empty());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue