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

This commit is contained in:
Luke Boswell 2024-10-09 09:21:46 +11:00
commit e3afeaa7ff
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
138 changed files with 3544 additions and 4651 deletions

View file

@ -618,6 +618,16 @@ pub fn listDropAt(
) callconv(.C) RocList {
const size = list.len();
const size_u64 = @as(u64, @intCast(size));
// NOTE
// we need to return an empty list explicitly,
// because we rely on the pointer field being null if the list is empty
// which also requires duplicating the utils.decref call to spend the RC token
if (size <= 1) {
list.decref(alignment, element_width, elements_refcounted, dec);
return RocList.empty();
}
// If droping the first or last element, return a seamless slice.
// For simplicity, do this by calling listSublist.
// In the future, we can test if it is faster to manually inline the important parts here.
@ -638,25 +648,16 @@ pub fn listDropAt(
// were >= than `size`, and we know `size` fits in usize.
const drop_index: usize = @intCast(drop_index_u64);
if (elements_refcounted) {
const element = source_ptr + drop_index * element_width;
dec(element);
}
// NOTE
// we need to return an empty list explicitly,
// because we rely on the pointer field being null if the list is empty
// which also requires duplicating the utils.decref call to spend the RC token
if (size < 2) {
list.decref(alignment, element_width, elements_refcounted, dec);
return RocList.empty();
}
if (list.isUnique()) {
var i = drop_index;
const copy_target = source_ptr;
if (elements_refcounted) {
const element = source_ptr + drop_index * element_width;
dec(element);
}
const copy_target = source_ptr + (drop_index * element_width);
const copy_source = copy_target + element_width;
std.mem.copyForwards(u8, copy_target[i..size], copy_source[i..size]);
const copy_size = (size - drop_index - 1) * element_width;
std.mem.copyForwards(u8, copy_target[0..copy_size], copy_source[0..copy_size]);
var new_list = list;

View file

@ -509,33 +509,33 @@ removeHelper = \buckets, bucketIndex, distAndFingerprint, data, key ->
## is missing. This is more efficient than doing both a `Dict.get` and then a
## `Dict.insert` call, and supports being piped.
## ```roc
## alterValue : [Present Bool, Missing] -> [Present Bool, Missing]
## alterValue : Result Bool [Missing] -> Result Bool [Missing]
## alterValue = \possibleValue ->
## when possibleValue is
## Missing -> Present Bool.false
## Present value -> if value then Missing else Present Bool.true
## Err -> Ok Bool.false
## Ok value -> if value then Err Missing else Ok Bool.true
##
## expect Dict.update (Dict.empty {}) "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
## ```
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v
update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v
update = \@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }, key, alter ->
{ bucketIndex, result } = find (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) key
when result is
Ok value ->
when alter (Present value) is
Present newValue ->
when alter (Ok value) is
Ok newValue ->
bucket = listGetUnsafe buckets bucketIndex
newData = List.set data (Num.toU64 bucket.dataIndex) (key, newValue)
@Dict { buckets, data: newData, maxBucketCapacity, maxLoadFactor, shifts }
Missing ->
Err Missing ->
removeBucket (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) bucketIndex
Err KeyNotFound ->
when alter Missing is
Present newValue ->
when alter (Err Missing) is
Ok newValue ->
if List.len data >= maxBucketCapacity then
# Need to reallocate let regular insert handle that.
insert (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) key newValue
@ -556,7 +556,7 @@ update = \@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }, key
distAndFingerprint = incrementDistN baseDistAndFingerprint (Num.toU32 dist)
insertHelper buckets data bucketIndex distAndFingerprint key newValue maxBucketCapacity maxLoadFactor shifts
Missing ->
Err Missing ->
@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }
circularDist = \start, end, size ->
@ -1216,8 +1216,8 @@ expect
List.walk badKeys (Dict.empty {}) \acc, k ->
Dict.update acc k \val ->
when val is
Present p -> Present (p |> Num.addWrap 1)
Missing -> Present 0
Ok p -> Ok (p |> Num.addWrap 1)
Err Missing -> Ok 0
allInsertedCorrectly =
List.walk badKeys Bool.true \acc, k ->

View file

@ -548,6 +548,23 @@ tau = 2 * pi
## [F64] or [F32] value, the returned string will be `"NaN"`, `"∞"`, or `"-∞"`.
##
toStr : Num * -> Str
## Convert an [Int] to a new [Int] of the expected type:
##
## ```roc
## # Casts a U8 to a U16
## x : U16
## x = Num.intCast 255u8
## ```
##
## In the case of downsizing, information is lost:
##
## ```roc
## # returns 0, as the bits were truncated.
## x : U8
## x = Num.intCast 256u16
## ```
##
intCast : Int a -> Int b
compare : Num a, Num a -> [LT, EQ, GT]

View file

@ -367,6 +367,8 @@ module [
withCapacity,
withPrefix,
contains,
dropPrefix,
dropSuffix,
]
import Bool exposing [Bool]
@ -1052,3 +1054,37 @@ contains = \haystack, needle ->
when firstMatch haystack needle is
Some _index -> Bool.true
None -> Bool.false
## Drops the given prefix [Str] from the start of a [Str]
## If the prefix is not found, returns the original string.
##
## ```roc
## expect Str.dropPrefix "bar" "foo" == "bar"
## expect Str.dropPrefix "foobar" "foo" == "bar"
## ```
dropPrefix : Str, Str -> Str
dropPrefix = \haystack, prefix ->
if Str.startsWith haystack prefix then
start = Str.countUtf8Bytes prefix
len = Num.subWrap (Str.countUtf8Bytes haystack) start
substringUnsafe haystack start len
else
haystack
## Drops the given suffix [Str] from the end of a [Str]
## If the suffix is not found, returns the original string.
##
## ```roc
## expect Str.dropSuffix "bar" "foo" == "bar"
## expect Str.dropSuffix "barfoo" "foo" == "bar"
## ```
dropSuffix : Str, Str -> Str
dropSuffix = \haystack, suffix ->
if Str.endsWith haystack suffix then
start = 0
len = Num.subWrap (Str.countUtf8Bytes haystack) (Str.countUtf8Bytes suffix)
substringUnsafe haystack start len
else
haystack

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ use roc_collections::{MutMap, VecSet};
use roc_module::ident::{Ident, ModuleName};
use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol};
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region};
use roc_region::all::{LineInfo, Loc, Region};
use roc_types::subs::Variable;
/// The canonicalization environment for a particular module.
@ -44,11 +44,19 @@ pub struct Env<'a> {
pub arena: &'a Bump,
pub opt_shorthand: Option<&'a str>,
pub src: &'a str,
/// Lazily calculated line info. This data is only needed if the code contains calls to `dbg`,
/// otherwise we can leave it as `None` and never pay the cost of scanning the source an extra
/// time.
line_info: &'a mut Option<LineInfo>,
}
impl<'a> Env<'a> {
pub fn new(
arena: &'a Bump,
src: &'a str,
home: ModuleId,
module_path: &'a Path,
dep_idents: &'a IdentIdsByModule,
@ -57,6 +65,7 @@ impl<'a> Env<'a> {
) -> Env<'a> {
Env {
arena,
src,
home,
module_path,
dep_idents,
@ -69,6 +78,7 @@ impl<'a> Env<'a> {
top_level_symbols: VecSet::default(),
home_params_record: None,
opt_shorthand,
line_info: arena.alloc(None),
}
}
@ -219,4 +229,11 @@ impl<'a> Env<'a> {
pub fn problem(&mut self, problem: Problem) {
self.problems.push(problem)
}
pub fn line_info(&mut self) -> &LineInfo {
if self.line_info.is_none() {
*self.line_info = Some(LineInfo::new(self.src));
}
self.line_info.as_ref().unwrap()
}
}

View file

@ -1013,11 +1013,8 @@ pub fn canonicalize_expr<'a>(
can_defs_with_return(env, var_store, inner_scope, env.arena.alloc(defs), loc_ret)
})
}
ast::Expr::OldRecordBuilder(_) => {
internal_error!("Old record builder should have been desugared by now")
}
ast::Expr::RecordBuilder { .. } => {
internal_error!("New record builder should have been desugared by now")
internal_error!("Record builder should have been desugared by now")
}
ast::Expr::Backpassing(_, _, _) => {
internal_error!("Backpassing should have been desugared by now")
@ -1209,8 +1206,20 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::Dbg | ast::Expr::DbgStmt(_, _) => {
internal_error!("Dbg should have been desugared by now")
ast::Expr::Dbg => {
// Dbg was not desugared as either part of an `Apply` or a `Pizza` binop, so it's
// invalid.
env.problem(Problem::UnappliedDbg { region });
let invalid_dbg_expr = crate::desugar::desugar_invalid_dbg_expr(env, scope, region);
let (loc_expr, output) =
canonicalize_expr(env, var_store, scope, region, invalid_dbg_expr);
(loc_expr.value, output)
}
ast::Expr::DbgStmt(_, _) => {
internal_error!("DbgStmt should have been desugared by now")
}
ast::Expr::LowLevelDbg((source_location, source), message, continuation) => {
let mut output = Output::default();
@ -1249,7 +1258,11 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::If(if_thens, final_else_branch) => {
ast::Expr::If {
if_thens,
final_else: final_else_branch,
..
} => {
let mut branches = Vec::with_capacity(if_thens.len());
let mut output = Output::default();
@ -1340,22 +1353,6 @@ pub fn canonicalize_expr<'a>(
use roc_problem::can::RuntimeError::*;
(RuntimeError(MalformedSuffixed(region)), Output::default())
}
ast::Expr::MultipleOldRecordBuilders(sub_expr) => {
use roc_problem::can::RuntimeError::*;
let problem = MultipleOldRecordBuilders(sub_expr.region);
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
}
ast::Expr::UnappliedOldRecordBuilder(sub_expr) => {
use roc_problem::can::RuntimeError::*;
let problem = UnappliedOldRecordBuilder(sub_expr.region);
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
}
ast::Expr::EmptyRecordBuilder(sub_expr) => {
use roc_problem::can::RuntimeError::*;
@ -2536,8 +2533,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
.iter()
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
ast::Expr::MalformedSuffixed(loc_expr)
| ast::Expr::MultipleOldRecordBuilders(loc_expr)
| ast::Expr::UnappliedOldRecordBuilder(loc_expr)
| ast::Expr::EmptyRecordBuilder(loc_expr)
| ast::Expr::SingleFieldRecordBuilder(loc_expr)
| ast::Expr::OptionalFieldInRecordBuilder(_, loc_expr)
@ -2560,7 +2555,11 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
.iter()
.all(|(loc_expr, _binop)| is_valid_interpolation(&loc_expr.value))
}
ast::Expr::If(branches, final_branch) => {
ast::Expr::If {
if_thens: branches,
final_else: final_branch,
..
} => {
is_valid_interpolation(&final_branch.value)
&& branches.iter().all(|(loc_before, loc_after)| {
is_valid_interpolation(&loc_before.value)
@ -2583,27 +2582,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::AssignedField::SpaceAfter(_, _) => false,
})
}
ast::Expr::OldRecordBuilder(fields) => {
fields.iter().all(|loc_field| match loc_field.value {
ast::OldRecordBuilderField::Value(_label, comments, loc_expr) => {
comments.is_empty() && is_valid_interpolation(&loc_expr.value)
}
ast::OldRecordBuilderField::ApplyValue(
_label,
comments_before,
comments_after,
loc_expr,
) => {
comments_before.is_empty()
&& comments_after.is_empty()
&& is_valid_interpolation(&loc_expr.value)
}
ast::OldRecordBuilderField::Malformed(_)
| ast::OldRecordBuilderField::LabelOnly(_) => true,
ast::OldRecordBuilderField::SpaceBefore(_, _)
| ast::OldRecordBuilderField::SpaceAfter(_, _) => false,
})
}
ast::Expr::RecordBuilder { mapper, fields } => {
is_valid_interpolation(&mapper.value)
&& fields.iter().all(|loc_field| match loc_field.value {

View file

@ -3,6 +3,7 @@ use std::path::Path;
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::{canonicalize_annotation, AnnotationFor};
use crate::def::{canonicalize_defs, report_unused_imports, Def};
use crate::desugar::desugar_record_destructures;
use crate::env::Env;
use crate::expr::{
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
@ -240,6 +241,7 @@ pub fn canonicalize_module_defs<'a>(
);
let mut env = Env::new(
arena,
src,
home,
arena.alloc(Path::new(module_path)),
dep_idents,
@ -266,16 +268,7 @@ pub fn canonicalize_module_defs<'a>(
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
crate::desugar::desugar_defs_node_values(
arena,
var_store,
loc_defs,
src,
&mut None,
module_path,
true,
&mut env.problems,
);
crate::desugar::desugar_defs_node_values(&mut env, &mut scope, loc_defs, true);
let mut rigid_variables = RigidVariables::default();
@ -334,13 +327,16 @@ pub fn canonicalize_module_defs<'a>(
before_arrow: _,
after_arrow: _,
}| {
let desugared_patterns =
desugar_record_destructures(&mut env, &mut scope, pattern.value);
let (destructs, _) = canonicalize_record_destructs(
&mut env,
var_store,
&mut scope,
&mut output,
PatternType::ModuleParams,
&pattern.value,
&desugared_patterns,
pattern.region,
PermitShadows(false),
);

View file

@ -914,7 +914,10 @@ pub fn canonicalize_record_destructs<'a>(
}
};
}
_ => unreachable!("Any other pattern should have given a parse error"),
_ => unreachable!(
"Any other pattern should have given a parse error: {:?}",
loc_pattern.value
),
}
}

View file

@ -466,7 +466,7 @@ impl Scope {
self.home.register_debug_idents(&self.locals.ident_ids)
}
/// Generates a unique, new symbol like "$1" or "$5",
/// Generates a unique, new symbol like "1" or "5",
/// using the home module as the module_id.
///
/// This is used, for example, during canonicalization of an Expr::Closure
@ -475,6 +475,12 @@ impl Scope {
Symbol::new(self.home, self.locals.gen_unique())
}
/// Generates a unique new symbol and return the symbol's unqualified identifier name.
pub fn gen_unique_symbol_name(&mut self) -> &str {
let ident_id = self.locals.gen_unique();
self.locals.ident_ids.get_name(ident_id).unwrap()
}
/// Introduce a new ignored variable (variable starting with an underscore).
/// The underscore itself should not be included in `ident`.
pub fn introduce_ignored_local(&mut self, ident: &str, region: Region) {

View file

@ -131,7 +131,7 @@ pub fn unwrap_suffixed_expression<'a>(
Expr::When(..) => unwrap_suffixed_expression_when_help(arena, loc_expr, maybe_def_pat),
Expr::If(..) => {
Expr::If { .. } => {
unwrap_suffixed_expression_if_then_else_help(arena, loc_expr, maybe_def_pat)
}
@ -325,7 +325,14 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
match maybe_def_pat {
Some(..) => {
return Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target });
}
None => {
return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
}
}
}
// function is another expression
@ -357,7 +364,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
match loc_expr.value {
Expr::If(if_thens, final_else_branch) => {
Expr::If {
if_thens,
final_else: final_else_branch,
indented_else,
} => {
for (index, if_then) in if_thens.iter().enumerate() {
let (current_if_then_statement, current_if_then_expression) = if_then;
@ -376,10 +387,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
@ -411,10 +423,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
@ -439,10 +452,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
@ -465,10 +479,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
let unwrapped_if_then = apply_try_function(
@ -494,10 +509,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let after_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(after_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(after_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
let after_if_then = apply_try_function(
@ -512,7 +528,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let before_if_then = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(before, after_if_then),
Expr::If {
if_thens: before,
final_else: after_if_then,
indented_else: false,
},
));
return unwrap_suffixed_expression(
@ -532,7 +552,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
Ok(unwrapped_final_else) => {
return Ok(arena.alloc(Loc::at(
loc_expr.region,
Expr::If(if_thens, unwrapped_final_else),
Expr::If {
if_thens,
final_else: unwrapped_final_else,
indented_else,
},
)));
}
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
@ -556,7 +580,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(if_thens, unwrapped_final_else),
Expr::If {
if_thens,
final_else: unwrapped_final_else,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);

View file

@ -46,6 +46,24 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
let var = var_store.fresh();
let qualified_module_ids = PackageModuleIds::default();
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
expr_str,
home,
Path::new("Test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
@ -53,22 +71,8 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(
arena,
&mut var_store,
&loc_expr,
expr_str,
&mut None,
arena.alloc("TestPath"),
&mut Default::default(),
);
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
scope.add_alias(
Symbol::NUM_INT,
Region::zero(),
@ -81,15 +85,6 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
roc_types::types::AliasKind::Structural,
);
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
home,
Path::new("Test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
let (loc_expr, output) = canonicalize_expr(
&mut env,
&mut var_store,

View file

@ -1,6 +1,6 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 449
assertion_line: 463
expression: snapshot
---
Defs {
@ -44,7 +44,7 @@ Defs {
value_defs: [
Body(
@15-26 Identifier {
ident: "64",
ident: "1",
},
@15-26 ParensAround(
Defs(
@ -66,7 +66,7 @@ Defs {
value_defs: [
Body(
@20-25 Identifier {
ident: "63",
ident: "0",
},
@20-25 Apply(
@22-23 Var {
@ -101,14 +101,14 @@ Defs {
[
@20-25 Var {
module_name: "",
ident: "63",
ident: "0",
},
],
Space,
),
@20-25 Var {
module_name: "",
ident: "63",
ident: "0",
},
),
),
@ -129,14 +129,14 @@ Defs {
[
@15-26 Var {
module_name: "",
ident: "64",
ident: "1",
},
],
Space,
),
@15-26 Var {
module_name: "",
ident: "64",
ident: "1",
},
),
),

View file

@ -63,8 +63,8 @@ Defs {
),
],
},
@66-130 If(
[
@66-130 If {
if_thens: [
(
@69-70 Var {
module_name: "",
@ -76,11 +76,12 @@ Defs {
},
),
],
@128-130 Var {
final_else: @128-130 Var {
module_name: "",
ident: "c",
},
),
indented_else: false,
},
),
guard: None,
},

View file

@ -136,8 +136,8 @@ Defs {
ident: "#!1_arg",
},
],
@109-298 If(
[
@109-298 If {
if_thens: [
(
@112-122 Apply(
@112-113 Var {
@ -244,7 +244,7 @@ Defs {
),
),
],
Apply(
final_else: Apply(
Var {
module_name: "Task",
ident: "await",
@ -269,8 +269,8 @@ Defs {
ident: "#!3_arg",
},
],
@109-298 If(
[
@109-298 If {
if_thens: [
(
@187-209 ParensAround(
Var {
@ -366,7 +366,7 @@ Defs {
),
),
],
@283-298 Apply(
final_else: @283-298 Apply(
@283-298 Var {
module_name: "",
ident: "line",
@ -380,12 +380,14 @@ Defs {
],
Space,
),
),
indented_else: false,
},
),
],
BangSuffix,
),
),
indented_else: false,
},
),
],
BangSuffix,

View file

@ -101,8 +101,8 @@ Defs {
ident: "#!0_arg",
},
],
@76-189 If(
[
@76-189 If {
if_thens: [
(
@79-87 Var {
module_name: "",
@ -124,7 +124,7 @@ Defs {
),
),
],
@125-132 Apply(
final_else: @125-132 Apply(
@125-132 Var {
module_name: "Task",
ident: "await",
@ -140,8 +140,8 @@ Defs {
ident: "#!1_arg",
},
],
@76-189 If(
[
@76-189 If {
if_thens: [
(
@125-132 Var {
module_name: "",
@ -163,7 +163,7 @@ Defs {
),
),
],
@178-189 Apply(
final_else: @178-189 Apply(
@178-182 Var {
module_name: "",
ident: "line",
@ -177,12 +177,14 @@ Defs {
],
Space,
),
),
indented_else: false,
},
),
],
BangSuffix,
),
),
indented_else: false,
},
),
],
BangSuffix,

View file

@ -0,0 +1,289 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
Index(2147483649),
Index(2147483650),
],
regions: [
@0-80,
@82-227,
@229-266,
],
space_before: [
Slice(start = 0, length = 0),
Slice(start = 0, length = 2),
Slice(start = 2, length = 2),
],
space_after: [
Slice(start = 0, length = 0),
Slice(start = 2, length = 0),
Slice(start = 4, length = 1),
],
spaces: [
Newline,
Newline,
Newline,
Newline,
Newline,
],
type_defs: [],
value_defs: [
Body(
@0-3 Identifier {
ident: "inc",
},
@6-80 Closure(
[
@7-8 Identifier {
ident: "i",
},
],
@16-80 If {
if_thens: [
(
@19-24 Apply(
@21-22 Var {
module_name: "Num",
ident: "isGt",
},
[
@19-20 Var {
module_name: "",
ident: "i",
},
@23-24 Num(
"2",
),
],
BinOp(
GreaterThan,
),
),
@38-52 Apply(
@38-41 Tag(
"Err",
),
[
@42-52 Tag(
"MaxReached",
),
],
Space,
),
),
],
final_else: @70-80 Apply(
@70-72 Tag(
"Ok",
),
[
@74-79 ParensAround(
Apply(
@76-77 Var {
module_name: "Num",
ident: "add",
},
[
@74-75 Var {
module_name: "",
ident: "i",
},
@78-79 Num(
"1",
),
],
BinOp(
Plus,
),
),
),
],
Space,
),
indented_else: false,
},
),
),
Expect {
condition: @93-227 Defs(
Defs {
tags: [
Index(2147483648),
Index(2147483649),
],
regions: [
@99-189,
@203-208,
],
space_before: [
Slice(start = 0, length = 0),
Slice(start = 0, length = 1),
],
space_after: [
Slice(start = 0, length = 0),
Slice(start = 1, length = 0),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
Body(
@93-96 Identifier {
ident: "run",
},
@99-189 Closure(
[
@100-101 Identifier {
ident: "i",
},
],
@132-172 Apply(
@132-172 Var {
module_name: "Result",
ident: "try",
},
[
@132-152 Apply(
@132-152 Var {
module_name: "",
ident: "inc",
},
[
@132-133 Var {
module_name: "",
ident: "i",
},
],
BinOp(
Pizza,
),
),
@132-172 Closure(
[
@132-152 Identifier {
ident: "#!0_arg",
},
],
@132-172 Apply(
@132-172 Var {
module_name: "Result",
ident: "try",
},
[
@132-172 Apply(
@132-172 Var {
module_name: "",
ident: "inc",
},
[
@132-152 Var {
module_name: "",
ident: "#!0_arg",
},
],
BinOp(
Pizza,
),
),
@132-172 Closure(
[
@113-117 Identifier {
ident: "newi",
},
],
@182-189 Apply(
@182-184 Tag(
"Ok",
),
[
@185-189 Var {
module_name: "",
ident: "newi",
},
],
Space,
),
),
],
QuestionSuffix,
),
),
],
QuestionSuffix,
),
),
),
Body(
@194-200 Identifier {
ident: "result",
},
@203-208 Apply(
@203-206 Var {
module_name: "",
ident: "run",
},
[
@207-208 Num(
"0",
),
],
Space,
),
),
],
},
@213-227 Apply(
@220-222 Var {
module_name: "Bool",
ident: "isEq",
},
[
@213-219 Var {
module_name: "",
ident: "result",
},
@223-227 Apply(
@223-225 Tag(
"Ok",
),
[
@226-227 Num(
"2",
),
],
Space,
),
],
BinOp(
Equals,
),
),
),
preceding_comment: @82-82,
},
Body(
@229-233 Identifier {
ident: "main",
},
@240-266 Apply(
@240-266 Var {
module_name: "Stdout",
ident: "line",
},
[
@253-266 Str(
PlainLine(
"Hello world",
),
),
],
Space,
),
),
],
}

View file

@ -0,0 +1,75 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
Index(2147483649),
],
regions: [
@0-33,
@35-45,
],
space_before: [
Slice(start = 0, length = 0),
Slice(start = 0, length = 2),
],
space_after: [
Slice(start = 0, length = 0),
Slice(start = 2, length = 1),
],
spaces: [
Newline,
Newline,
Newline,
],
type_defs: [],
value_defs: [
AnnotatedBody {
ann_pattern: @0-3 Identifier {
ident: "run",
},
ann_type: @6-15 Apply(
"",
"Task",
[
@11-13 Record {
fields: [],
ext: None,
},
@14-15 Inferred,
],
),
lines_between: [
Newline,
],
body_pattern: @16-19 Identifier {
ident: "run",
},
body_expr: @22-33 Apply(
@22-33 Var {
module_name: "",
ident: "line",
},
[
@28-33 Str(
PlainLine(
"foo",
),
),
],
Space,
),
},
Body(
@35-39 Identifier {
ident: "main",
},
@42-45 Var {
module_name: "",
ident: "run",
},
),
],
}

View file

@ -1,6 +1,6 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 459
assertion_line: 473
expression: snapshot
---
Defs {
@ -8,7 +8,7 @@ Defs {
Index(2147483648),
],
regions: [
@0-19,
@0-51,
],
space_before: [
Slice(start = 0, length = 0),
@ -25,13 +25,13 @@ Defs {
@0-4 Identifier {
ident: "main",
},
@11-19 Defs(
@11-51 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@11-12,
@11-40,
],
space_before: [
Slice(start = 0, length = 0),
@ -43,36 +43,98 @@ Defs {
type_defs: [],
value_defs: [
Body(
@11-12 Identifier {
ident: "63",
@11-40 Identifier {
ident: "1",
},
@11-12 Num(
"1",
@11-40 Apply(
@31-38 Var {
module_name: "Num",
ident: "add",
},
[
@11-23 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@11-12,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@11-12 Identifier {
ident: "0",
},
@11-12 Num(
"1",
),
),
],
},
@11-23 LowLevelDbg(
(
"test.roc:2",
" ",
),
@11-12 Apply(
@11-12 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@11-12 Var {
module_name: "",
ident: "0",
},
],
Space,
),
@11-12 Var {
module_name: "",
ident: "0",
},
),
),
@39-40 Num(
"2",
),
],
BinOp(
Pizza,
),
),
),
],
},
@11-19 LowLevelDbg(
@11-51 LowLevelDbg(
(
"test.roc:2",
" ",
" main =\n 1\n ",
),
@11-12 Apply(
@11-12 Var {
@11-40 Apply(
@11-40 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@11-12 Var {
@11-40 Var {
module_name: "",
ident: "63",
ident: "1",
},
],
Space,
),
@11-12 Var {
@11-40 Var {
module_name: "",
ident: "63",
ident: "1",
},
),
),

View file

@ -19,7 +19,6 @@ mod test_can {
use roc_can::expr::{ClosureData, IntValue, Recursive};
use roc_can::pattern::Pattern;
use roc_module::called_via::CalledVia;
use roc_module::symbol::Symbol;
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_region::all::{Loc, Position, Region};
use roc_types::subs::Variable;
@ -653,101 +652,6 @@ mod test_can {
}
// RECORD BUILDERS
#[test]
fn old_record_builder_desugar() {
let src = indoc!(
r#"
succeed = \_ -> crash "succeed"
apply = \_ -> crash "get"
d = 3
succeed {
a: 1,
b: <- apply "b",
c: <- apply "c",
d
}
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems.len(), 0);
// Assert that we desugar to:
//
// (apply "c") ((apply "b") (succeed \b -> \c -> { a: 1, b, c, d }))
// (apply "c") ..
let (apply_c, c_to_b) = simplify_curried_call(&out.loc_expr.value);
assert_apply_call(apply_c, "c", &out.interns);
// (apply "b") ..
let (apply_b, b_to_succeed) = simplify_curried_call(c_to_b);
assert_apply_call(apply_b, "b", &out.interns);
// (succeed ..)
let (succeed, b_closure) = simplify_curried_call(b_to_succeed);
match succeed {
Var(sym, _) => assert_eq!(sym.as_str(&out.interns), "succeed"),
_ => panic!("Not calling succeed: {:?}", succeed),
}
// \b -> ..
let (b_sym, c_closure) = simplify_builder_closure(b_closure);
// \c -> ..
let (c_sym, c_body) = simplify_builder_closure(c_closure);
// { a: 1, b, c, d }
match c_body {
Record { fields, .. } => {
match get_field_expr(fields, "a") {
Num(_, num_str, _, _) => {
assert_eq!(num_str.to_string(), "1");
}
expr => panic!("a is not a Num: {:?}", expr),
}
assert_eq!(get_field_var_sym(fields, "b"), b_sym);
assert_eq!(get_field_var_sym(fields, "c"), c_sym);
assert_eq!(get_field_var_sym(fields, "d").as_str(&out.interns), "d");
}
_ => panic!("Closure body wasn't a Record: {:?}", c_body),
}
}
fn simplify_curried_call(expr: &Expr) -> (&Expr, &Expr) {
match expr {
LetNonRec(_, loc_expr) => simplify_curried_call(&loc_expr.value),
Call(fun, args, _) => (&fun.1.value, &args[0].1.value),
_ => panic!("Final Expr is not a Call: {:?}", expr),
}
}
fn assert_apply_call(expr: &Expr, expected: &str, interns: &roc_module::symbol::Interns) {
match simplify_curried_call(expr) {
(Var(sym, _), Str(val)) => {
assert_eq!(sym.as_str(interns), "apply");
assert_eq!(val.to_string(), expected);
}
call => panic!("Not a valid (get {}) call: {:?}", expected, call),
};
}
fn simplify_builder_closure(expr: &Expr) -> (Symbol, &Expr) {
use roc_can::pattern::Pattern::*;
match expr {
Closure(closure) => match &closure.arguments[0].2.value {
Identifier(sym) => (*sym, &closure.loc_body.value),
pattern => panic!("Not an identifier pattern: {:?}", pattern),
},
_ => panic!("Not a closure: {:?}", expr),
}
}
fn get_field_expr<'a>(
fields: &'a roc_collections::SendMap<roc_module::ident::Lowercase, roc_can::expr::Field>,
@ -769,97 +673,7 @@ mod test_can {
}
#[test]
fn old_record_builder_field_names_do_not_shadow() {
let src = indoc!(
r#"
succeed = \_ -> crash "succeed"
parse = \_ -> crash "parse"
number = "42"
succeed {
number: <- parse number,
raw: number,
}
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems.len(), 0);
let (_, number_to_succeed) = simplify_curried_call(&out.loc_expr.value);
let (_, number_closure) = simplify_curried_call(number_to_succeed);
let (apply_number_sym, record) = simplify_builder_closure(number_closure);
match record {
Record { fields, .. } => {
assert_eq!(get_field_var_sym(fields, "number"), apply_number_sym);
match get_field_expr(fields, "raw") {
Var(number_sym, _) => {
assert_ne!(number_sym.ident_id(), apply_number_sym.ident_id());
assert_eq!(number_sym.as_str(&out.interns), "number")
}
expr => panic!("a is not a Num: {:?}", expr),
}
}
_ => panic!("Closure body wasn't a Record: {:?}", record),
}
}
#[test]
fn multiple_old_record_builders_error() {
let src = indoc!(
r#"
succeed
{ a: <- apply "a" }
{ b: <- apply "b" }
"#
);
let arena = Bump::new();
let CanExprOut {
problems, loc_expr, ..
} = can_expr_with(&arena, test_home(), src);
assert_eq!(problems.len(), 1);
assert!(problems.iter().all(|problem| matches!(
problem,
Problem::RuntimeError(roc_problem::can::RuntimeError::MultipleOldRecordBuilders { .. })
)));
assert!(matches!(
loc_expr.value,
Expr::RuntimeError(roc_problem::can::RuntimeError::MultipleOldRecordBuilders { .. })
));
}
#[test]
fn hanging_old_record_builder() {
let src = indoc!(
r#"
{ a: <- apply "a" }
"#
);
let arena = Bump::new();
let CanExprOut {
problems, loc_expr, ..
} = can_expr_with(&arena, test_home(), src);
assert_eq!(problems.len(), 1);
assert!(problems.iter().all(|problem| matches!(
problem,
Problem::RuntimeError(roc_problem::can::RuntimeError::UnappliedOldRecordBuilder { .. })
)));
assert!(matches!(
loc_expr.value,
Expr::RuntimeError(roc_problem::can::RuntimeError::UnappliedOldRecordBuilder { .. })
));
}
#[test]
fn new_record_builder_desugar() {
fn record_builder_desugar() {
let src = indoc!(
r#"
map2 = \a, b, combine -> combine a b

View file

@ -6,25 +6,39 @@ mod suffixed_tests {
use bumpalo::Bump;
use insta::assert_snapshot;
use roc_can::desugar::desugar_defs_node_values;
use roc_can::env::Env;
use roc_can::scope::Scope;
use roc_module::symbol::{IdentIds, ModuleIds, PackageModuleIds};
use roc_parse::test_helpers::parse_defs_with;
use roc_types::subs::VarStore;
use std::path::Path;
macro_rules! run_test {
($src:expr) => {{
let arena = &Bump::new();
let mut var_store = VarStore::default();
let mut defs = parse_defs_with(arena, indoc!($src)).unwrap();
desugar_defs_node_values(
arena,
&mut var_store,
&mut defs,
$src,
&mut None,
"test.roc",
true,
&mut Default::default(),
let home = ModuleIds::default().get_or_insert(&"Test".into());
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
let dep_idents = IdentIds::exposed_builtins(0);
let qualified_module_ids = PackageModuleIds::default();
let mut env = Env::new(
arena,
$src,
home,
Path::new("test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
let mut defs = parse_defs_with(arena, indoc!($src)).unwrap();
desugar_defs_node_values(&mut env, &mut scope, &mut defs, true);
let snapshot = format!("{:#?}", &defs);
println!("{}", snapshot);
assert_snapshot!(snapshot);
@ -454,6 +468,19 @@ mod suffixed_tests {
);
}
#[test]
fn pizza_dbg() {
run_test!(
r#"
main =
1
|> dbg
|> Num.add 2
|> dbg
"#
)
}
#[test]
fn apply_argument_single() {
run_test!(
@ -579,6 +606,44 @@ mod suffixed_tests {
"##
);
}
#[test]
fn issue_7081() {
run_test!(
r##"
inc = \i ->
if i > 2 then
Err MaxReached
else
Ok (i + 1)
expect
run = \i ->
newi =
i
|> inc?
|> inc?
Ok newi
result = run 0
result == Ok 2
main =
Stdout.line! "Hello world"
"##
);
}
#[test]
fn issue_7103() {
run_test!(
r##"
run : Task {} _
run = line! "foo"
main = run
"##
);
}
}
#[cfg(test)]

View file

@ -5917,9 +5917,9 @@
"dev": true
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
@ -5930,7 +5930,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@ -6091,13 +6091,19 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -7249,6 +7255,23 @@
"node": ">= 10"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@ -7597,9 +7620,9 @@
}
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"dev": true,
"engines": {
"node": ">= 0.8"
@ -7704,6 +7727,27 @@
"integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
"dev": true
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz",
@ -8646,37 +8690,37 @@
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
"dev": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@ -8883,13 +8927,13 @@
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dev": true,
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@ -9249,10 +9293,13 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/function.prototype.name": {
"version": "1.1.5",
@ -9300,15 +9347,19 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -9590,12 +9641,12 @@
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.1"
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -9640,6 +9691,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -13153,10 +13216,13 @@
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
"dev": true
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
@ -13538,10 +13604,13 @@
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -13889,9 +13958,9 @@
"dev": true
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
"dev": true
},
"node_modules/path-type": {
@ -15501,12 +15570,12 @@
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dev": true,
"dependencies": {
"side-channel": "^1.0.4"
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@ -16265,9 +16334,9 @@
}
},
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"version": "2.79.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
@ -16560,9 +16629,9 @@
"dev": true
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dev": true,
"dependencies": {
"debug": "2.6.9",
@ -16598,6 +16667,15 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -16692,20 +16770,37 @@
}
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dev": true,
"dependencies": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@ -16743,14 +16838,18 @@
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"

View file

@ -5,7 +5,7 @@ use crate::{
};
use roc_parse::ast::{
AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, ImplementsAbilities,
ImplementsAbility, ImplementsClause, OldRecordBuilderField, Tag, TypeAnnotation, TypeHeader,
ImplementsAbility, ImplementsClause, Tag, TypeAnnotation, TypeHeader,
};
use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc;
@ -524,101 +524,6 @@ fn format_assigned_field_help<T>(
}
}
impl<'a> Formattable for OldRecordBuilderField<'a> {
fn is_multiline(&self) -> bool {
is_multiline_record_builder_field_help(self)
}
fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) {
// we abuse the `Newlines` type to decide between multiline or single-line layout
format_record_builder_field_help(self, buf, indent, newlines == Newlines::Yes);
}
}
fn is_multiline_record_builder_field_help(afield: &OldRecordBuilderField<'_>) -> bool {
use self::OldRecordBuilderField::*;
match afield {
Value(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(),
ApplyValue(_, colon_spaces, arrow_spaces, ann) => {
!colon_spaces.is_empty() || !arrow_spaces.is_empty() || ann.value.is_multiline()
}
LabelOnly(_) => false,
SpaceBefore(_, _) | SpaceAfter(_, _) => true,
Malformed(text) => text.chars().any(|c| c == '\n'),
}
}
fn format_record_builder_field_help(
zelf: &OldRecordBuilderField,
buf: &mut Buf,
indent: u16,
is_multiline: bool,
) {
use self::OldRecordBuilderField::*;
match zelf {
Value(name, spaces, ann) => {
if is_multiline {
buf.newline();
}
buf.indent(indent);
buf.push_str(name.value);
if !spaces.is_empty() {
fmt_spaces(buf, spaces.iter(), indent);
}
buf.push(':');
buf.spaces(1);
ann.value.format(buf, indent);
}
ApplyValue(name, colon_spaces, arrow_spaces, ann) => {
if is_multiline {
buf.newline();
buf.indent(indent);
}
buf.push_str(name.value);
if !colon_spaces.is_empty() {
fmt_spaces(buf, colon_spaces.iter(), indent);
}
buf.push(':');
buf.spaces(1);
if !arrow_spaces.is_empty() {
fmt_spaces(buf, arrow_spaces.iter(), indent);
}
buf.push_str("<-");
buf.spaces(1);
ann.value.format(buf, indent);
}
LabelOnly(name) => {
if is_multiline {
buf.newline();
buf.indent(indent);
}
buf.push_str(name.value);
}
SpaceBefore(sub_field, spaces) => {
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
format_record_builder_field_help(sub_field, buf, indent, is_multiline);
}
SpaceAfter(sub_field, spaces) => {
format_record_builder_field_help(sub_field, buf, indent, is_multiline);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
}
Malformed(raw) => {
buf.push_str(raw);
}
}
}
impl<'a> Formattable for Tag<'a> {
fn is_multiline(&self) -> bool {
use self::Tag::*;

View file

@ -10,7 +10,7 @@ use crate::Buf;
use roc_module::called_via::{self, BinOp};
use roc_parse::ast::{
is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
OldRecordBuilderField, Pattern, TryTarget, WhenBranch,
Pattern, TryTarget, WhenBranch,
};
use roc_parse::ast::{StrLiteral, StrSegment};
use roc_parse::ident::Accessor;
@ -71,7 +71,11 @@ impl<'a> Formattable for Expr<'a> {
"LowLevelDbg should only exist after desugaring, not during formatting"
),
If(branches, final_else) => {
If {
if_thens: branches,
final_else,
..
} => {
final_else.is_multiline()
|| branches
.iter()
@ -87,8 +91,6 @@ impl<'a> Formattable for Expr<'a> {
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
expr: loc_subexpr, ..
})
| MultipleOldRecordBuilders(loc_subexpr)
| UnappliedOldRecordBuilder(loc_subexpr)
| EmptyRecordBuilder(loc_subexpr)
| SingleFieldRecordBuilder(loc_subexpr)
| OptionalFieldInRecordBuilder(_, loc_subexpr) => loc_subexpr.is_multiline(),
@ -114,7 +116,6 @@ impl<'a> Formattable for Expr<'a> {
Record(fields) => is_collection_multiline(fields),
Tuple(fields) => is_collection_multiline(fields),
RecordUpdate { fields, .. } => is_collection_multiline(fields),
OldRecordBuilder(fields) => is_collection_multiline(fields),
RecordBuilder { fields, .. } => is_collection_multiline(fields),
}
}
@ -240,10 +241,7 @@ impl<'a> Formattable for Expr<'a> {
a.extract_spaces().item.is_multiline()
&& matches!(
a.value.extract_spaces().item,
Expr::Tuple(_)
| Expr::List(_)
| Expr::Record(_)
| Expr::OldRecordBuilder(_)
Expr::Tuple(_) | Expr::List(_) | Expr::Record(_)
)
&& a.extract_spaces().before == [CommentOrNewline::Newline]
})
@ -388,16 +386,6 @@ impl<'a> Formattable for Expr<'a> {
assigned_field_to_space_before,
);
}
OldRecordBuilder(fields) => {
fmt_record_like(
buf,
None,
*fields,
indent,
format_record_builder_field_multiline,
record_builder_field_to_space_before,
);
}
Closure(loc_patterns, loc_ret) => {
fmt_closure(buf, loc_patterns, loc_ret, indent);
}
@ -464,8 +452,19 @@ impl<'a> Formattable for Expr<'a> {
LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting"
),
If(branches, final_else) => {
fmt_if(buf, branches, final_else, self.is_multiline(), indent);
If {
if_thens: branches,
final_else,
indented_else,
} => {
fmt_if(
buf,
branches,
final_else,
self.is_multiline(),
*indented_else,
indent,
);
}
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
Tuple(items) => fmt_collection(buf, indent, Braces::Round, *items, Newlines::No),
@ -548,8 +547,6 @@ impl<'a> Formattable for Expr<'a> {
}
MalformedClosure => {}
PrecedenceConflict { .. } => {}
MultipleOldRecordBuilders { .. } => {}
UnappliedOldRecordBuilder { .. } => {}
EmptyRecordBuilder { .. } => {}
SingleFieldRecordBuilder { .. } => {}
OptionalFieldInRecordBuilder(_, _) => {}
@ -610,11 +607,7 @@ pub(crate) fn format_sq_literal(buf: &mut Buf, s: &str) {
fn is_outdentable(expr: &Expr) -> bool {
matches!(
expr.extract_spaces().item,
Expr::Tuple(_)
| Expr::List(_)
| Expr::Record(_)
| Expr::OldRecordBuilder(_)
| Expr::Closure(..)
Expr::Tuple(_) | Expr::List(_) | Expr::Record(_) | Expr::Closure(..)
)
}
@ -1076,6 +1069,7 @@ fn fmt_if<'a>(
branches: &'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)],
final_else: &'a Loc<Expr<'a>>,
is_multiline: bool,
indented_else: bool,
indent: u16,
) {
// let is_multiline_then = loc_then.is_multiline();
@ -1208,16 +1202,22 @@ fn fmt_if<'a>(
}
}
buf.indent(indent);
if is_multiline {
if indented_else {
buf.indent(indent + INDENT);
buf.push_str("else");
buf.newline();
buf.newline();
} else if is_multiline {
buf.indent(indent);
buf.push_str("else");
buf.newline();
} else {
buf.indent(indent);
buf.push_str(" else");
buf.spaces(1);
}
final_else.format(buf, return_indent);
let indent = if indented_else { indent } else { return_indent };
final_else.format(buf, indent);
}
fn fmt_closure<'a>(
@ -1622,113 +1622,6 @@ fn assigned_field_to_space_before<'a, T>(
}
}
fn format_record_builder_field_multiline(
buf: &mut Buf,
field: &OldRecordBuilderField,
indent: u16,
separator_prefix: &str,
) {
use self::OldRecordBuilderField::*;
match field {
Value(name, spaces, ann) => {
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
if !spaces.is_empty() {
fmt_spaces(buf, spaces.iter(), indent);
buf.indent(indent);
}
buf.push_str(separator_prefix);
buf.push_str(":");
if ann.value.is_multiline() {
buf.newline();
ann.value.format(buf, indent + INDENT);
} else {
buf.spaces(1);
ann.value.format(buf, indent);
}
buf.push(',');
}
ApplyValue(name, colon_spaces, arrow_spaces, ann) => {
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
if !colon_spaces.is_empty() {
fmt_spaces(buf, colon_spaces.iter(), indent);
buf.indent(indent);
}
buf.push_str(separator_prefix);
buf.push(':');
buf.spaces(1);
if !arrow_spaces.is_empty() {
fmt_spaces(buf, arrow_spaces.iter(), indent);
buf.indent(indent + INDENT);
}
buf.push_str("<-");
if ann.value.is_multiline() {
buf.newline();
ann.value.format(buf, indent + INDENT);
} else {
buf.spaces(1);
ann.value.format(buf, indent);
}
buf.push(',');
}
LabelOnly(name) => {
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
buf.push(',');
}
SpaceBefore(sub_field, _spaces) => {
// We have something like that:
// ```
// # comment
// field,
// ```
// we'd like to preserve this
format_record_builder_field_multiline(buf, sub_field, indent, separator_prefix);
}
SpaceAfter(sub_field, spaces) => {
// We have something like that:
// ```
// field # comment
// , otherfield
// ```
// we'd like to transform it into:
// ```
// field,
// # comment
// otherfield
// ```
format_record_builder_field_multiline(buf, sub_field, indent, separator_prefix);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, indent);
}
Malformed(raw) => {
buf.push_str(raw);
}
}
}
fn record_builder_field_to_space_before<'a>(
field: &'a OldRecordBuilderField<'a>,
) -> Option<(&OldRecordBuilderField<'a>, &'a [CommentOrNewline<'a>])> {
match field {
OldRecordBuilderField::SpaceBefore(sub_field, spaces) => Some((sub_field, spaces)),
_ => None,
}
}
fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
match expr {
Expr::BinOps(left_side, _) => {
@ -1753,7 +1646,7 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::Pizza => true,
})
}
Expr::If(_, _) => true,
Expr::If { .. } => true,
Expr::SpaceBefore(e, _) => sub_expr_requests_parens(e),
Expr::SpaceAfter(e, _) => sub_expr_requests_parens(e),
_ => false,

View file

@ -2915,130 +2915,108 @@ fn list_literal<'a, 'ctx>(
let list_length = elems.len();
let list_length_intval = env.ptr_int().const_int(list_length as _, false);
// TODO re-enable, currently causes morphic segfaults because it tries to update
// constants in-place...
// if element_type.is_int_type() {
if false {
let element_type = element_type.into_int_type();
let is_refcounted = layout_interner.contains_refcounted(element_layout);
let is_all_constant = elems.iter().all(|e| e.is_literal());
if is_all_constant && !is_refcounted {
// Build a global literal in-place instead of GEPing and storing individual elements.
// Exceptions:
// - Anything that is refcounted has nested pointers,
// and nested pointers in globals will break the surgical linker.
// Ignore such cases for now.
let element_width = layout_interner.stack_size(element_layout);
let size = list_length * element_width as usize;
let alignment = layout_interner
.alignment_bytes(element_layout)
.max(env.target.ptr_width() as u32);
let mut is_all_constant = true;
let zero_elements =
(env.target.ptr_width() as u8 as f64 / element_width as f64).ceil() as usize;
let refcount_slot_bytes = alignment as usize;
// runtime-evaluated elements
let mut runtime_evaluated_elements = Vec::with_capacity_in(list_length, env.arena);
let refcount_slot_elements =
(refcount_slot_bytes as f64 / element_width as f64).ceil() as usize;
// set up a global that contains all the literal elements of the array
// any variables or expressions are represented as `undef`
let global = {
let mut global_elements = Vec::with_capacity_in(list_length, env.arena);
let data_bytes = list_length * element_width as usize;
// Add zero bytes that represent the refcount
//
// - if all elements are const, then we store the whole list as a constant.
// It then needs a refcount before the first element.
// - but if the list is not all constants, then we will just copy the constant values,
// and we do not need that refcount at the start
//
// In the latter case, we won't store the zeros in the globals
// (we slice them off again below)
for _ in 0..zero_elements {
global_elements.push(element_type.const_zero());
assert!(refcount_slot_elements > 0);
let global = if element_type.is_int_type() {
let element_type = element_type.into_int_type();
let mut bytes = Vec::with_capacity_in(refcount_slot_elements + data_bytes, env.arena);
// Fill the refcount slot with nulls
for _ in 0..(refcount_slot_elements) {
bytes.push(element_type.const_zero());
}
// Copy the elements from the list literal into the array
for (index, element) in elems.iter().enumerate() {
match element {
ListLiteralElement::Literal(literal) => {
let val = build_exp_literal(env, layout_interner, element_layout, literal);
global_elements.push(val.into_int_value());
}
ListLiteralElement::Symbol(symbol) => {
let val = scope.load_symbol(symbol);
// here we'd like to furthermore check for intval.is_const().
// if all elements are const for LLVM, we could make the array a constant.
// BUT morphic does not know about this, and could allow us to modify that
// array in-place. That would cause a segfault. So, we'll have to find
// constants ourselves and cannot lean on LLVM here.
is_all_constant = false;
runtime_evaluated_elements.push((index, val));
global_elements.push(element_type.get_undef());
}
};
for element in elems.iter() {
let literal = element.get_literal().expect("is_all_constant is true");
let val = build_exp_literal(env, layout_interner, element_layout, &literal);
bytes.push(val.into_int_value());
}
let const_elements = if is_all_constant {
global_elements.into_bump_slice()
} else {
&global_elements[zero_elements..]
};
// use None for the address space (e.g. Const does not work)
let typ = element_type.array_type(const_elements.len() as u32);
let typ = element_type.array_type(bytes.len() as u32);
let global = env.module.add_global(typ, None, "roc__list_literal");
global.set_constant(true);
global.set_alignment(alignment);
global.set_unnamed_addr(true);
global.set_linkage(inkwell::module::Linkage::Private);
global.set_initializer(&element_type.const_array(bytes.into_bump_slice()));
global
} else if element_type.is_float_type() {
let element_type = element_type.into_float_type();
let mut bytes = Vec::with_capacity_in(refcount_slot_elements + data_bytes, env.arena);
global.set_initializer(&element_type.const_array(const_elements));
global.as_pointer_value()
};
if is_all_constant {
// all elements are constants, so we can use the memory in the constants section directly
// here we make a pointer to the first actual element (skipping the 0 bytes that
// represent the refcount)
let zero = env.ptr_int().const_zero();
let offset = env.ptr_int().const_int(zero_elements as _, false);
let ptr = unsafe {
env.builder.new_build_in_bounds_gep(
element_type,
global,
&[zero, offset],
"first_element_pointer",
)
};
super::build_list::store_list(env, ptr, list_length_intval).into()
} else {
// some of our elements are non-constant, so we must allocate space on the heap
let ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);
// then, copy the relevant segment from the constant section into the heap
env.builder
.build_memcpy(
ptr,
alignment,
global,
alignment,
env.ptr_int().const_int(size as _, false),
)
.unwrap();
// then replace the `undef`s with the values that we evaluate at runtime
for (index, val) in runtime_evaluated_elements {
let index_val = ctx.i64_type().const_int(index as u64, false);
let elem_ptr = unsafe {
builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index")
};
builder.new_build_store(elem_ptr, val);
// Fill the refcount slot with nulls
for _ in 0..(refcount_slot_elements) {
bytes.push(element_type.const_zero());
}
super::build_list::store_list(env, ptr, list_length_intval).into()
}
// Copy the elements from the list literal into the array
for element in elems.iter() {
let literal = element.get_literal().expect("is_all_constant is true");
let val = build_exp_literal(env, layout_interner, element_layout, &literal);
bytes.push(val.into_float_value());
}
let typ = element_type.array_type(bytes.len() as u32);
let global = env.module.add_global(typ, None, "roc__list_literal");
global.set_initializer(&element_type.const_array(bytes.into_bump_slice()));
global
} else {
internal_error!("unexpected element type: {:?}", element_type);
};
global.set_constant(true);
global.set_alignment(alignment);
global.set_unnamed_addr(true);
global.set_linkage(inkwell::module::Linkage::Private);
// Refcount the global itself in a new allocation.
// Necessary because morphic MAY attempt to update the global in-place, which is
// illegal if the global is in the read-only section.
let with_rc_ptr = global.as_pointer_value();
let const_data_ptr = unsafe {
env.builder.new_build_in_bounds_gep(
element_type,
with_rc_ptr,
&[env
.ptr_int()
.const_int(refcount_slot_elements as u64, false)],
"get_data_ptr",
)
};
let data_ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);
let byte_size = env
.ptr_int()
.const_int(list_length as u64 * element_width as u64, false);
builder
.build_memcpy(data_ptr, alignment, const_data_ptr, alignment, byte_size)
.unwrap();
super::build_list::store_list(env, data_ptr, list_length_intval).into()
} else {
let ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);

View file

@ -282,16 +282,9 @@ pub(crate) struct RocUnion<'ctx> {
tag_type: Option<TagType>,
}
fn is_multiple_of(big: u32, small: u32) -> bool {
match small {
0 => true, // 0 is a multiple of all n, because n * 0 = 0
n => big % n == 0,
}
}
impl<'ctx> RocUnion<'ctx> {
pub const TAG_ID_INDEX: u32 = 2;
pub const TAG_DATA_INDEX: u32 = 1;
pub const TAG_ID_INDEX: u32 = 1;
pub const TAG_DATA_INDEX: u32 = 0;
fn new(
context: &'ctx Context,
@ -301,45 +294,24 @@ impl<'ctx> RocUnion<'ctx> {
) -> Self {
let bytes = round_up_to_alignment(data_width, data_align);
let byte_array_type = if is_multiple_of(bytes, 8) && is_multiple_of(data_align, 8) {
context
.i64_type()
.array_type(bytes / 8)
.as_basic_type_enum()
} else {
context.i8_type().array_type(bytes).as_basic_type_enum()
};
let alignment_array_type = alignment_type(context, data_align)
.array_type(0)
let align_type = alignment_type(context, data_align);
let byte_array_type = align_type
.array_type(bytes / data_align)
.as_basic_type_enum();
let struct_type = if let Some(tag_type) = tag_type {
let tag_width = match tag_type {
TagType::I8 => 1,
TagType::I16 => 2,
};
let tag_padding = round_up_to_alignment(tag_width, data_align) - tag_width;
let tag_padding_type = context
.i8_type()
.array_type(tag_padding)
.as_basic_type_enum();
context.struct_type(
&[
alignment_array_type,
byte_array_type,
match tag_type {
TagType::I8 => context.i8_type().into(),
TagType::I16 => context.i16_type().into(),
},
tag_padding_type,
],
false,
)
} else {
context.struct_type(&[alignment_array_type, byte_array_type], false)
context.struct_type(&[byte_array_type], false)
};
Self {

View file

@ -161,23 +161,6 @@ pub fn can_expr_with<'a>(
// ensure the Test module is accessible in our tests
module_ids.get_or_insert(&PQModuleName::Unqualified("Test".into()));
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
// If we did this *during* canonicalization, then each time we
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(
arena,
&mut var_store,
&loc_expr,
expr_str,
&mut None,
arena.alloc("TestPath"),
&mut Default::default(),
);
let mut scope = Scope::new(
home,
"TestPath".into(),
@ -188,12 +171,23 @@ pub fn can_expr_with<'a>(
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
expr_str,
home,
Path::new("Test.roc"),
&dep_idents,
&module_ids,
None,
);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
// If we did this *during* canonicalization, then each time we
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let (loc_expr, output) = canonicalize_expr(
&mut env,
&mut var_store,

View file

@ -4299,8 +4299,9 @@ mod test_reporting {
4 f :: I64
^^
I have no specific suggestion for this operator, see TODO for the full
list of operators in Roc.
I have no specific suggestion for this operator, see
https://www.roc-lang.org/tutorial#operator-desugaring-table for the
full list of operators in Roc.
"#
);
@ -4339,6 +4340,14 @@ mod test_reporting {
However, I already saw the final expression in that series of
definitions.
Tip: An expression like `4`, `"hello"`, or `functionCall MyThing` is
like `return 4` in other programming languages. To me, it seems like
you did `return 4` followed by more code in the lines after, that code
would never be executed!
Tip: If you are working with `Task`, this error can happen if you
forgot a `!` somewhere.
"###
);
@ -4972,30 +4981,6 @@ mod test_reporting {
"###
);
test_report!(
record_builder_in_module_params,
indoc!(
r"
import Menu {
echo,
name: <- applyName
}
"
),@r###"
OLD-STYLE RECORD BUILDER IN MODULE PARAMS in ...r_in_module_params/Test.roc
I was partway through parsing module params, but I got stuck here:
4 import Menu {
5 echo,
6 name: <- applyName
^^^^^^^^^^^^^^^^^^
This looks like an old-style record builder field, but those are not
allowed in module params.
"###
);
test_report!(
record_update_in_module_params,
indoc!(
@ -6068,8 +6053,9 @@ All branches in an `if` must have the same type!
5 5 ** 3
^^
I have no specific suggestion for this operator, see TODO for the full
list of operators in Roc.
I have no specific suggestion for this operator, see
https://www.roc-lang.org/tutorial#operator-desugaring-table for the
full list of operators in Roc.
"#
);
@ -10661,163 +10647,6 @@ All branches in an `if` must have the same type!
// Record Builders
test_report!(
optional_field_in_old_record_builder,
indoc!(
r#"
{
a: <- apply "a",
b,
c ? "optional"
}
"#
),
@r#"
BAD OLD-STYLE RECORD BUILDER in ...nal_field_in_old_record_builder/Test.roc
I am partway through parsing a record builder, and I found an optional
field:
1 app "test" provides [main] to "./platform"
2
3 main =
4 {
5 a: <- apply "a",
6 b,
7 c ? "optional"
^^^^^^^^^^^^^^
Optional fields can only appear when you destructure a record.
"#
);
test_report!(
record_update_old_builder,
indoc!(
r#"
{ rec &
a: <- apply "a",
b: 3
}
"#
),
@r#"
BAD RECORD UPDATE in tmp/record_update_old_builder/Test.roc
I am partway through parsing a record update, and I found an old-style
record builder field:
1 app "test" provides [main] to "./platform"
2
3 main =
4 { rec &
5 a: <- apply "a",
^^^^^^^^^^^^^^^
Old-style record builders cannot be updated like records.
"#
);
test_report!(
multiple_old_record_builders,
indoc!(
r#"
succeed
{ a: <- apply "a" }
{ b: <- apply "b" }
"#
),
@r#"
MULTIPLE OLD-STYLE RECORD BUILDERS in /code/proj/Main.roc
This function is applied to multiple old-style record builders:
4> succeed
5> { a: <- apply "a" }
6> { b: <- apply "b" }
Note: Functions can only take at most one old-style record builder!
Tip: You can combine them or apply them separately.
"#
);
test_report!(
unapplied_old_record_builder,
indoc!(
r#"
{ a: <- apply "a" }
"#
),
@r#"
UNAPPLIED OLD-STYLE RECORD BUILDER in /code/proj/Main.roc
This old-style record builder was not applied to a function:
4 { a: <- apply "a" }
^^^^^^^^^^^^^^^^^^^
However, we need a function to construct the record.
Note: Functions must be applied directly. The pipe operator (|>) cannot be used.
"#
);
test_report!(
old_record_builder_apply_non_function,
indoc!(
r#"
succeed = \_ -> crash ""
succeed {
a: <- "a",
}
"#
),
@r#"
TOO MANY ARGS in /code/proj/Main.roc
This value is not a function, but it was given 1 argument:
7 a: <- "a",
^^^
Tip: Remove <- to assign the field directly.
"#
);
// Skipping test because opaque types defined in the same module
// do not fail with the special opaque type error
//
// test_report!(
// record_builder_apply_opaque,
// indoc!(
// r#"
// succeed = \_ -> crash ""
// Decode := {}
// get : Str -> Decode
// get = \_ -> @Decode {}
// succeed {
// a: <- get "a",
// # missing |> apply ^
// }
// "#
// ),
// @r#"
// ── TOO MANY ARGS in /code/proj/Main.roc ────────────────────────────────────────
// This value is an opaque type, so it cannot be called with an argument:
// 12│ a: <- get "a",
// ^^^^^^^
// Hint: Did you mean to apply it to a function first?
// "#
// );
test_report!(
empty_record_builder,
indoc!(
@ -14521,7 +14350,7 @@ All branches in an `if` must have the same type!
4 1 + dbg + 2
^^^
This `63` value is a:
This value is a:
{}
@ -14555,7 +14384,7 @@ All branches in an `if` must have the same type!
4 1 + dbg "" "" + 2
^^^^^^^^^
This `63` value is a:
This value is a:
{}

View file

@ -784,7 +784,7 @@ impl<'a> State<'a> {
number_of_workers: usize,
exec_mode: ExecutionMode,
) -> Self {
let cache_dir = roc_packaging::cache::roc_cache_dir();
let cache_dir = roc_packaging::cache::roc_cache_packages_dir();
let dependencies = Dependencies::new(exec_mode.goal_phase());
Self {
@ -1665,7 +1665,7 @@ fn state_thread_step<'a>(
Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized)))
}
Msg::FailedToReadFile { filename, error } => {
let buf = to_file_problem_report_string(filename, error);
let buf = to_file_problem_report_string(filename, error, true);
Err(LoadingProblem::FormattedReport(buf))
}
@ -1839,7 +1839,7 @@ pub fn report_loading_problem(
}
LoadingProblem::FormattedReport(report) => report,
LoadingProblem::FileProblem { filename, error } => {
to_file_problem_report_string(filename, error)
to_file_problem_report_string(filename, error, true)
}
LoadingProblem::NoPlatformPackage {
filename,

View file

@ -75,10 +75,6 @@ pub enum CalledVia {
/// e.g. "$(first) $(last)" is transformed into Str.concat (Str.concat first " ") last.
StringInterpolation,
/// This call is the result of desugaring an old style Record Builder field.
/// e.g. succeed { a <- get "a" } is transformed into (get "a") (succeed \a -> { a })
OldRecordBuilder,
/// This call is the result of desugaring a map2-based Record Builder field. e.g.
/// ```roc
/// { Task.parallel <-

View file

@ -123,6 +123,10 @@ impl Symbol {
.any(|(_, (s, _))| *s == self)
}
pub fn is_generated(self, interns: &Interns) -> bool {
self.ident_ids(interns).is_generated_id(self.ident_id())
}
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
interns
.module_ids
@ -137,24 +141,15 @@ impl Symbol {
}
pub fn as_str(self, interns: &Interns) -> &str {
let ident_ids = interns
.all_ident_ids
.get(&self.module_id())
self.ident_ids(interns)
.get_name(self.ident_id())
.unwrap_or_else(|| {
internal_error!(
"ident_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
"ident_string's IdentIds did not contain an entry for {} in module {:?}",
self.ident_id().0,
self.module_id()
)
});
ident_ids.get_name(self.ident_id()).unwrap_or_else(|| {
internal_error!(
"ident_string's IdentIds did not contain an entry for {} in module {:?}",
self.ident_id().0,
self.module_id()
)
})
})
}
pub const fn as_u64(self) -> u64 {
@ -186,6 +181,19 @@ impl Symbol {
pub fn contains(self, needle: &str) -> bool {
format!("{self:?}").contains(needle)
}
fn ident_ids(self, interns: &Interns) -> &IdentIds {
interns
.all_ident_ids
.get(&self.module_id())
.unwrap_or_else(|| {
internal_error!(
"ident_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
)
})
}
}
/// Rather than displaying as this:
@ -438,13 +446,6 @@ impl fmt::Debug for ModuleId {
}
}
/// pf.Task
/// 1. build mapping from short name to package
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
/// 4. throw away short names. stash the module id in the can env under the resolved module name
/// 5. test:
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PackageQualified<'a, T> {
Unqualified(T),
@ -707,6 +708,12 @@ impl IdentIds {
IdentId(self.interner.insert_index_str() as u32)
}
pub fn is_generated_id(&self, id: IdentId) -> bool {
self.interner
.try_get(id.0 as usize)
.map_or(false, |str| str.starts_with(|c: char| c.is_ascii_digit()))
}
#[inline(always)]
pub fn get_id(&self, ident_name: &str) -> Option<IdentId> {
self.interner
@ -1373,6 +1380,8 @@ define_builtins! {
46 STR_REPLACE_FIRST: "replaceFirst"
47 STR_REPLACE_LAST: "replaceLast"
48 STR_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity"
49 STR_DROP_PREFIX: "dropPrefix"
50 STR_DROP_SUFFIX: "dropSuffix"
}
6 LIST: "List" => {
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias

View file

@ -396,7 +396,7 @@ impl<'state, 'a> State<'state, 'a> {
None => unreachable!(
"\n\tNo borrow signature for {name:?} layout.\n\n\t\
Tip 1: This can happen when you call a function with less arguments than it expects.\n\t\
Like `Arg.list!` instead of `Arg.list! {{}}`.\n\t
Like `Arg.list!` instead of `Arg.list! {{}}`.\n\t\
Tip 2: `roc check yourfile.roc` can sometimes give you a helpful error.
"
)

View file

@ -49,6 +49,17 @@ impl<'a> ListLiteralElement<'a> {
_ => None,
}
}
pub fn get_literal(&self) -> Option<Literal<'a>> {
match self {
Self::Literal(l) => Some(*l),
_ => None,
}
}
pub fn is_literal(&self) -> bool {
matches!(self, Self::Literal(_))
}
}
pub enum NumLiteral {

View file

@ -456,13 +456,6 @@ pub enum Expr<'a> {
Tuple(Collection<'a, &'a Loc<Expr<'a>>>),
// Record Builders
/// Applicative record builders, e.g.
/// build {
/// foo: <- getData Foo,
/// bar: <- getData Bar,
/// }
OldRecordBuilder(Collection<'a, Loc<OldRecordBuilderField<'a>>>),
/// Mapper-based record builders, e.g.
/// { Task.parallel <-
/// foo: Task.getData Foo,
@ -512,7 +505,11 @@ pub enum Expr<'a> {
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
// Conditionals
If(&'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)], &'a Loc<Expr<'a>>),
If {
if_thens: &'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)],
final_else: &'a Loc<Expr<'a>>,
indented_else: bool,
},
When(
/// The condition
&'a Loc<Expr<'a>>,
@ -537,8 +534,6 @@ pub enum Expr<'a> {
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(&'a PrecedenceConflict<'a>),
MultipleOldRecordBuilders(&'a Loc<Expr<'a>>),
UnappliedOldRecordBuilder(&'a Loc<Expr<'a>>),
EmptyRecordBuilder(&'a Loc<Expr<'a>>),
SingleFieldRecordBuilder(&'a Loc<Expr<'a>>),
OptionalFieldInRecordBuilder(&'a Loc<&'a str>, &'a Loc<Expr<'a>>),
@ -608,7 +603,11 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
}
// expression in a if-then-else, `if isOk! then "ok" else doSomething!`
Expr::If(if_thens, final_else) => {
Expr::If {
if_thens,
final_else,
..
} => {
let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| {
is_expr_suffixed(&if_then.value) || is_expr_suffixed(&else_expr.value)
});
@ -655,9 +654,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
.iter()
.any(|field| is_assigned_value_suffixed(&field.value)),
Expr::Tuple(items) => items.iter().any(|x| is_expr_suffixed(&x.value)),
Expr::OldRecordBuilder(items) => items
.iter()
.any(|rbf| is_record_builder_field_suffixed(&rbf.value)),
Expr::RecordBuilder { mapper: _, fields } => fields
.iter()
.any(|field| is_assigned_value_suffixed(&field.value)),
@ -680,8 +676,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::MalformedClosure => false,
Expr::MalformedSuffixed(_) => false,
Expr::PrecedenceConflict(_) => false,
Expr::MultipleOldRecordBuilders(_) => false,
Expr::UnappliedOldRecordBuilder(_) => false,
Expr::EmptyRecordBuilder(_) => false,
Expr::SingleFieldRecordBuilder(_) => false,
Expr::OptionalFieldInRecordBuilder(_, _) => false,
@ -709,17 +703,6 @@ fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool {
}
}
fn is_record_builder_field_suffixed(field: &OldRecordBuilderField<'_>) -> bool {
match field {
OldRecordBuilderField::Value(_, _, a) => is_expr_suffixed(&a.value),
OldRecordBuilderField::ApplyValue(_, _, _, a) => is_expr_suffixed(&a.value),
OldRecordBuilderField::LabelOnly(_) => false,
OldRecordBuilderField::SpaceBefore(a, _) => is_record_builder_field_suffixed(a),
OldRecordBuilderField::SpaceAfter(a, _) => is_record_builder_field_suffixed(a),
OldRecordBuilderField::Malformed(_) => false,
}
}
pub fn split_around<T>(items: &[T], target: usize) -> (&[T], &[T]) {
let (before, rest) = items.split_at(target);
let after = &rest[1..];
@ -927,26 +910,6 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&loc_expr.value);
}
}
OldRecordBuilder(fields) => {
expr_stack.reserve(fields.len());
for loc_record_builder_field in fields.items {
let mut current_field = loc_record_builder_field.value;
loop {
use OldRecordBuilderField::*;
match current_field {
Value(_, _, loc_val) => break expr_stack.push(&loc_val.value),
ApplyValue(_, _, _, loc_val) => {
break expr_stack.push(&loc_val.value)
}
SpaceBefore(next_field, _) => current_field = *next_field,
SpaceAfter(next_field, _) => current_field = *next_field,
LabelOnly(_) | Malformed(_) => break,
}
}
}
}
RecordBuilder {
mapper: map2,
fields,
@ -993,14 +956,18 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&expr.value);
}
UnaryOp(expr, _) => expr_stack.push(&expr.value),
If(ifs, alternate) => {
expr_stack.reserve(ifs.len() * 2 + 1);
If {
if_thens,
final_else,
..
} => {
expr_stack.reserve(if_thens.len() * 2 + 1);
for (condition, consequent) in ifs.iter() {
for (condition, consequent) in if_thens.iter() {
expr_stack.push(&condition.value);
expr_stack.push(&consequent.value);
}
expr_stack.push(&alternate.value);
expr_stack.push(&final_else.value);
}
When(condition, branches) => {
expr_stack.reserve(branches.len() + 1);
@ -1027,9 +994,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| SpaceAfter(expr, _)
| ParensAround(expr) => expr_stack.push(expr),
MultipleOldRecordBuilders(loc_expr)
| UnappliedOldRecordBuilder(loc_expr)
| EmptyRecordBuilder(loc_expr)
EmptyRecordBuilder(loc_expr)
| SingleFieldRecordBuilder(loc_expr)
| OptionalFieldInRecordBuilder(_, loc_expr) => expr_stack.push(&loc_expr.value),
@ -1655,30 +1620,6 @@ impl<'a, Val> AssignedField<'a, Val> {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OldRecordBuilderField<'a> {
// A field with a value, e.g. `{ name: "blah" }`
Value(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Expr<'a>>),
// A field with a function we can apply to build part of the record, e.g. `{ name: <- apply getName }`
ApplyValue(
Loc<&'a str>,
&'a [CommentOrNewline<'a>],
&'a [CommentOrNewline<'a>],
&'a Loc<Expr<'a>>,
),
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(Loc<&'a str>),
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a OldRecordBuilderField<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a OldRecordBuilderField<'a>, &'a [CommentOrNewline<'a>]),
/// A malformed assigned field, which will code gen to a runtime error
Malformed(&'a str),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommentOrNewline<'a> {
Newline,
@ -2213,15 +2154,6 @@ impl<'a, Val> Spaceable<'a> for AssignedField<'a, Val> {
}
}
impl<'a> Spaceable<'a> for OldRecordBuilderField<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
OldRecordBuilderField::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
OldRecordBuilderField::SpaceAfter(self, spaces)
}
}
impl<'a> Spaceable<'a> for Tag<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
Tag::SpaceBefore(self, spaces)
@ -2524,7 +2456,6 @@ impl<'a> Malformed for Expr<'a> {
Record(items) => items.is_malformed(),
Tuple(items) => items.is_malformed(),
OldRecordBuilder(items) => items.is_malformed(),
RecordBuilder { mapper: map2, fields } => map2.is_malformed() || fields.is_malformed(),
Closure(args, body) => args.iter().any(|arg| arg.is_malformed()) || body.is_malformed(),
@ -2537,7 +2468,7 @@ impl<'a> Malformed for Expr<'a> {
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(),
If(chain, els) => chain.iter().any(|(cond, body)| cond.is_malformed() || body.is_malformed()) || els.is_malformed(),
If { if_thens, final_else, ..} => if_thens.iter().any(|(cond, body)| cond.is_malformed() || body.is_malformed()) || final_else.is_malformed(),
When(cond, branches) => cond.is_malformed() || branches.iter().any(|branch| branch.is_malformed()),
SpaceBefore(expr, _) |
@ -2548,8 +2479,6 @@ impl<'a> Malformed for Expr<'a> {
MalformedClosure |
MalformedSuffixed(..) |
PrecedenceConflict(_) |
MultipleOldRecordBuilders(_) |
UnappliedOldRecordBuilder(_) |
EmptyRecordBuilder(_) |
SingleFieldRecordBuilder(_) |
OptionalFieldInRecordBuilder(_, _) => true,
@ -2629,19 +2558,6 @@ impl<'a, T: Malformed> Malformed for AssignedField<'a, T> {
}
}
impl<'a> Malformed for OldRecordBuilderField<'a> {
fn is_malformed(&self) -> bool {
match self {
OldRecordBuilderField::Value(_, _, expr)
| OldRecordBuilderField::ApplyValue(_, _, _, expr) => expr.is_malformed(),
OldRecordBuilderField::LabelOnly(_) => false,
OldRecordBuilderField::SpaceBefore(field, _)
| OldRecordBuilderField::SpaceAfter(field, _) => field.is_malformed(),
OldRecordBuilderField::Malformed(_) => true,
}
}
}
impl<'a> Malformed for Pattern<'a> {
fn is_malformed(&self) -> bool {
use Pattern::*;

View file

@ -2,8 +2,8 @@ use crate::ast::{
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore,
TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
ModuleImportParams, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
use crate::blankspace::{
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
@ -925,15 +925,11 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
.fields
.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => Err((
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),
)),
field => Ok(Loc::at(loc_field.region, field)),
}
})?;
@ -2170,7 +2166,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Backpassing(_, _, _)
| Expr::BinOps { .. }
| Expr::Defs(_, _)
| Expr::If(_, _)
| Expr::If { .. }
| Expr::When(_, _)
| Expr::Expect(_, _)
| Expr::Dbg
@ -2179,8 +2175,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }
| Expr::MultipleOldRecordBuilders { .. }
| Expr::UnappliedOldRecordBuilder { .. }
| Expr::EmptyRecordBuilder(_)
| Expr::SingleFieldRecordBuilder(_)
| Expr::OptionalFieldInRecordBuilder(_, _)
@ -2189,7 +2183,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::UnaryOp(_, _)
| Expr::TrySuffix { .. }
| Expr::Crash
| Expr::OldRecordBuilder(..)
| Expr::RecordBuilder { .. } => return Err(()),
Expr::Str(string) => Pattern::StrLiteral(string),
@ -2704,6 +2697,8 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
let (_, _, state) =
parser::keyword(keyword::IF, EIf::If).parse(arena, state, min_indent)?;
let if_indent = state.line_indent();
let mut branches = Vec::with_capacity_in(1, arena);
let mut loop_state = state;
@ -2730,16 +2725,37 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
}
};
let (_, else_branch, state) = parse_block(
let else_indent = state_final_else.line_indent();
let indented_else = else_indent > if_indent;
let min_indent = if !indented_else {
else_indent + 1
} else {
if_indent
};
let (_, loc_first_space, state_final_else) =
loc_space0_e(EIf::IndentElseBranch).parse(arena, state_final_else, min_indent)?;
let allow_defs = !loc_first_space.value.is_empty();
// use parse_block_inner so we can set min_indent
let (_, else_branch, state) = parse_block_inner(
options,
arena,
state_final_else,
true,
min_indent,
EIf::IndentElseBranch,
EIf::ElseBranch,
loc_first_space,
allow_defs,
)?;
let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch));
let expr = Expr::If {
if_thens: branches.into_bump_slice(),
final_else: arena.alloc(else_branch),
indented_else,
};
Ok((MadeProgress, expr, state))
}
@ -3367,38 +3383,12 @@ pub enum RecordField<'a> {
LabelOnly(Loc<&'a str>),
SpaceBefore(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]),
ApplyValue(
Loc<&'a str>,
&'a [CommentOrNewline<'a>],
&'a [CommentOrNewline<'a>],
&'a Loc<Expr<'a>>,
),
}
#[derive(Debug)]
pub struct FoundApplyValue;
#[derive(Debug)]
pub enum NotOldBuilderFieldValue {
FoundOptionalValue,
FoundIgnoredValue,
}
impl<'a> RecordField<'a> {
fn is_apply_value(&self) -> bool {
let mut current = self;
loop {
match current {
RecordField::ApplyValue(_, _, _, _) => break true,
RecordField::SpaceBefore(field, _) | RecordField::SpaceAfter(field, _) => {
current = *field;
}
_ => break false,
}
}
}
fn is_ignored_value(&self) -> bool {
let mut current = self;
@ -3413,74 +3403,34 @@ impl<'a> RecordField<'a> {
}
}
pub fn to_assigned_field(
self,
arena: &'a Bump,
) -> Result<AssignedField<'a, Expr<'a>>, FoundApplyValue> {
pub fn to_assigned_field(self, arena: &'a Bump) -> AssignedField<'a, Expr<'a>> {
use AssignedField::*;
match self {
RecordField::RequiredValue(loc_label, spaces, loc_expr) => {
Ok(RequiredValue(loc_label, spaces, loc_expr))
RequiredValue(loc_label, spaces, loc_expr)
}
RecordField::OptionalValue(loc_label, spaces, loc_expr) => {
Ok(OptionalValue(loc_label, spaces, loc_expr))
OptionalValue(loc_label, spaces, loc_expr)
}
RecordField::IgnoredValue(loc_label, spaces, loc_expr) => {
Ok(IgnoredValue(loc_label, spaces, loc_expr))
IgnoredValue(loc_label, spaces, loc_expr)
}
RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)),
RecordField::ApplyValue(_, _, _, _) => Err(FoundApplyValue),
RecordField::LabelOnly(loc_label) => LabelOnly(loc_label),
RecordField::SpaceBefore(field, spaces) => {
let assigned_field = field.to_assigned_field(arena)?;
let assigned_field = field.to_assigned_field(arena);
Ok(SpaceBefore(arena.alloc(assigned_field), spaces))
SpaceBefore(arena.alloc(assigned_field), spaces)
}
RecordField::SpaceAfter(field, spaces) => {
let assigned_field = field.to_assigned_field(arena)?;
let assigned_field = field.to_assigned_field(arena);
Ok(SpaceAfter(arena.alloc(assigned_field), spaces))
}
}
}
fn to_builder_field(
self,
arena: &'a Bump,
) -> Result<OldRecordBuilderField<'a>, NotOldBuilderFieldValue> {
use OldRecordBuilderField::*;
match self {
RecordField::RequiredValue(loc_label, spaces, loc_expr) => {
Ok(Value(loc_label, spaces, loc_expr))
}
RecordField::OptionalValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundOptionalValue),
RecordField::IgnoredValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundIgnoredValue),
RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)),
RecordField::ApplyValue(loc_label, colon_spaces, arrow_spaces, loc_expr) => {
Ok(ApplyValue(loc_label, colon_spaces, arrow_spaces, loc_expr))
}
RecordField::SpaceBefore(field, spaces) => {
let builder_field = field.to_builder_field(arena)?;
Ok(SpaceBefore(arena.alloc(builder_field), spaces))
}
RecordField::SpaceAfter(field, spaces) => {
let builder_field = field.to_builder_field(arena)?;
Ok(SpaceAfter(arena.alloc(builder_field), spaces))
SpaceAfter(arena.alloc(assigned_field), spaces)
}
}
}
@ -3534,14 +3484,10 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
match field_data {
Either::First((loc_label, (spaces, opt_loc_val))) => {
match opt_loc_val {
Some(Either::First((_, RecordFieldExpr::Value(loc_val)))) => {
Some(Either::First((_, loc_val))) => {
RequiredValue(loc_label, spaces, arena.alloc(loc_val))
}
Some(Either::First((_, RecordFieldExpr::Apply(arrow_spaces, loc_val)))) => {
ApplyValue(loc_label, spaces, arrow_spaces, arena.alloc(loc_val))
}
Some(Either::Second((_, loc_val))) => {
OptionalValue(loc_label, spaces, arena.alloc(loc_val))
}
@ -3568,34 +3514,17 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
)
}
enum RecordFieldExpr<'a> {
Apply(&'a [CommentOrNewline<'a>], Loc<Expr<'a>>),
Value(Loc<Expr<'a>>),
}
fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> {
fn record_field_expr<'a>() -> impl Parser<'a, Loc<Expr<'a>>, ERecord<'a>> {
map_with_arena(
and(
spaces(),
either(
and(
two_bytes(b'<', b'-', ERecord::Arrow),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
specialize_err_ref(ERecord::Expr, loc_expr(false)),
),
),
|arena: &'a bumpalo::Bump, (spaces, either)| match either {
Either::First((_, loc_expr)) => RecordFieldExpr::Apply(spaces, loc_expr),
Either::Second(loc_expr) => RecordFieldExpr::Value({
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}),
and(spaces(), specialize_err_ref(ERecord::Expr, loc_expr(false))),
|arena: &'a bumpalo::Bump, (spaces, loc_expr)| {
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
},
)
}
@ -3663,13 +3592,11 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
record_update_help(arena, update, record.fields)
}
Some((mapper, RecordHelpPrefix::Mapper)) => {
new_record_builder_help(arena, mapper, record.fields)
record_builder_help(arena, mapper, record.fields)
}
None => {
let special_field_found = record.fields.iter().find_map(|field| {
if field.value.is_apply_value() {
Some(old_record_builder_help(arena, record.fields))
} else if field.value.is_ignored_value() {
if field.value.is_ignored_value() {
Some(Err(EExpr::RecordUpdateIgnoredField(field.region)))
} else {
None
@ -3678,7 +3605,7 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
special_field_found.unwrap_or_else(|| {
let fields = record.fields.map_items(arena, |loc_field| {
loc_field.map(|field| field.to_assigned_field(arena).unwrap())
loc_field.map(|field| field.to_assigned_field(arena))
});
Ok(Expr::Record(fields))
@ -3705,14 +3632,13 @@ fn record_update_help<'a>(
) -> Result<Expr<'a>, EExpr<'a>> {
let result = fields.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => {
AssignedField::IgnoredValue(_, _, _) => {
Err(EExpr::RecordUpdateIgnoredField(loc_field.region))
}
Ok(builder_field) => Ok(Loc {
builder_field => Ok(Loc {
region: loc_field.region,
value: builder_field,
}),
Err(FoundApplyValue) => Err(EExpr::RecordUpdateOldBuilderField(loc_field.region)),
}
});
@ -3722,19 +3648,18 @@ fn record_update_help<'a>(
})
}
fn new_record_builder_help<'a>(
fn record_builder_help<'a>(
arena: &'a Bump,
mapper: Loc<Expr<'a>>,
fields: Collection<'a, Loc<RecordField<'a>>>,
) -> Result<Expr<'a>, EExpr<'a>> {
let result = fields.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(builder_field) => Ok(Loc {
region: loc_field.region,
value: builder_field,
}),
Err(FoundApplyValue) => Err(EExpr::RecordBuilderOldBuilderField(loc_field.region)),
}
let builder_field = loc_field.value.to_assigned_field(arena);
Ok(Loc {
region: loc_field.region,
value: builder_field,
})
});
result.map(|fields| Expr::RecordBuilder {
@ -3743,28 +3668,6 @@ fn new_record_builder_help<'a>(
})
}
fn old_record_builder_help<'a>(
arena: &'a Bump,
fields: Collection<'a, Loc<RecordField<'a>>>,
) -> Result<Expr<'a>, EExpr<'a>> {
let result = fields.map_items_result(arena, |loc_field| {
match loc_field.value.to_builder_field(arena) {
Ok(builder_field) => Ok(Loc {
region: loc_field.region,
value: builder_field,
}),
Err(NotOldBuilderFieldValue::FoundOptionalValue) => {
Err(EExpr::OptionalValueInOldRecordBuilder(loc_field.region))
}
Err(NotOldBuilderFieldValue::FoundIgnoredValue) => {
Err(EExpr::IgnoredValueInOldRecordBuilder(loc_field.region))
}
}
});
result.map(Expr::OldRecordBuilder)
}
fn apply_expr_access_chain<'a>(
arena: &'a Bump,
value: Expr<'a>,

View file

@ -1258,13 +1258,13 @@ pub struct PlatformHeader<'a> {
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ImportsEntry<'a> {
/// e.g. `Task` or `Task.{ Task, after }`
/// 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>>>>,
),
/// e.g. `pf.Task` or `pf.Task.{ after }` or `pf.{ Task.{ Task, after } }`
/// e.g. `pf.Stdout` or `pf.Stdout exposing [line]`
Package(
&'a str,
ModuleName<'a>,

View file

@ -1,905 +0,0 @@
use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::expr::merge_spaces;
use crate::header::{
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, HostedHeader,
ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem,
ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader, PackagesKeyword,
PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
TypedIdent,
};
use crate::ident::{self, lowercase_ident, unqualified_ident, UppercaseIdent};
use crate::parser::Progress::{self, *};
use crate::parser::{
and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map,
map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed,
two_bytes, zero_or_more, EExposes, EHeader, EImports, EPackages, EParams, EProvides, ERequires,
ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
};
use crate::pattern::record_pattern_fields;
use crate::state::State;
use crate::string_literal::{self, parse_str_literal};
use crate::type_annotation;
use roc_region::all::{Loc, Position, Region};
fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|_arena, state: State<'a>, _min_indent: u32| {
if state.has_reached_end() {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, SyntaxError::NotEndOfFile(state.pos())))
}
}
}
pub fn parse_module_defs<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
defs: Defs<'a>,
) -> Result<Defs<'a>, SyntaxError<'a>> {
let min_indent = 0;
match crate::expr::parse_top_level_defs(arena, state.clone(), defs) {
Ok((_, defs, state)) => match end_of_file().parse(arena, state, min_indent) {
Ok(_) => Ok(defs),
Err((_, fail)) => Err(fail),
},
Err((_, fail)) => Err(SyntaxError::Expr(fail, state.pos())),
}
}
pub fn parse_header<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<(Module<'a>, State<'a>), SourceError<'a, EHeader<'a>>> {
let min_indent = 0;
match header().parse(arena, state.clone(), min_indent) {
Ok((_, module, state)) => Ok((module, state)),
Err((_, fail)) => Err(SourceError::new(fail, &state)),
}
}
pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
use crate::parser::keyword;
record!(Module {
comments: space0_e(EHeader::IndentStart),
header: one_of![
map(
skip_first(
keyword("module", EHeader::Start),
increment_min_indent(module_header())
),
Header::Module
),
map(
skip_first(
keyword("interface", EHeader::Start),
increment_min_indent(interface_header())
),
Header::Module
),
map(
skip_first(
keyword("app", EHeader::Start),
increment_min_indent(one_of![app_header(), old_app_header()])
),
Header::App
),
map(
skip_first(
keyword("package", EHeader::Start),
increment_min_indent(one_of![package_header(), old_package_header()])
),
Header::Package
),
map(
skip_first(
keyword("platform", EHeader::Start),
increment_min_indent(platform_header())
),
Header::Platform
),
map(
skip_first(
keyword("hosted", EHeader::Start),
increment_min_indent(hosted_header())
),
Header::Hosted
),
]
})
}
#[inline(always)]
fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
record!(ModuleHeader {
after_keyword: space0_e(EHeader::IndentStart),
params: optional(specialize_err(EHeader::Params, module_params())),
exposes: specialize_err(EHeader::Exposes, exposes_list()),
interface_imports: succeed(None)
})
.trace("module_header")
}
fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> {
record!(ModuleParams {
params: specialize_err(EParams::Pattern, record_pattern_fields()),
before_arrow: skip_second(
space0_e(EParams::BeforeArrow),
loc(two_bytes(b'-', b'>', EParams::Arrow))
),
after_arrow: space0_e(EParams::AfterArrow),
})
}
// TODO does this need to be a macro?
macro_rules! merge_n_spaces {
($arena:expr, $($slice:expr),*) => {
{
let mut merged = bumpalo::collections::Vec::with_capacity_in(0 $(+ $slice.len())*, $arena);
$(merged.extend_from_slice($slice);)*
merged.into_bump_slice()
}
};
}
/// Parse old interface headers so we can format them into module headers
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
let after_keyword = map_with_arena(
and(
skip_second(
space0_e(EHeader::IndentStart),
loc(module_name_help(EHeader::ModuleName)),
),
specialize_err(EHeader::Exposes, exposes_kw()),
),
|arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
merge_n_spaces!(arena, before_name, kw.before, kw.after)
},
);
record!(ModuleHeader {
after_keyword: after_keyword,
params: succeed(None),
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
interface_imports: map(
specialize_err(EHeader::Imports, imports()),
imports_none_if_empty
)
.trace("imports"),
})
.trace("interface_header")
}
fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option<ImportsKeywordItem<'_>> {
if value.item.is_empty() {
None
} else {
Some(value)
}
}
#[inline(always)]
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
record!(HostedHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc(module_name_help(EHeader::ModuleName)),
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
imports: specialize_err(EHeader::Imports, imports()),
})
.trace("hosted_header")
}
fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> {
use encode_unicode::CharExt;
let mut chomped = 0;
if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if first_letter.is_uppercase() {
chomped += width;
} else {
return Err(Progress::NoProgress);
}
}
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
// After the first character, only these are allowed:
//
// * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers
// * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric()
// * A '.' separating module parts
if ch.is_alphabetic() || ch.is_ascii_digit() {
chomped += width;
} else if ch == '.' {
chomped += width;
if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if first_letter.is_uppercase() {
chomped += width;
} else if first_letter == '{' {
// the .{ starting a `Foo.{ bar, baz }` importing clauses
chomped -= width;
break;
} else {
return Err(Progress::MadeProgress);
}
}
} else {
// we're done
break;
}
}
let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
Ok(name)
}
#[inline(always)]
fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
|_, mut state: State<'a>, _min_indent: u32| match chomp_module_name(state.bytes()) {
Ok(name) => {
let width = name.len();
state = state.advance(width);
Ok((MadeProgress, ModuleName::new(name), state))
}
Err(progress) => Err((progress, ())),
}
}
#[inline(always)]
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
record!(AppHeader {
before_provides: space0_e(EHeader::IndentStart),
provides: specialize_err(EHeader::Exposes, exposes_list()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
old_imports: succeed(None),
old_provides_to_new_package: succeed(None),
})
.trace("app_header")
}
struct OldAppHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub packages: Option<Loc<OldAppPackages<'a>>>,
pub imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
pub provides: ProvidesTo<'a>,
}
type OldAppPackages<'a> =
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>;
#[inline(always)]
fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let old = record!(OldAppHeader {
before_name: skip_second(
space0_e(EHeader::IndentStart),
loc(crate::parser::specialize_err(
EHeader::AppName,
string_literal::parse_str_literal()
))
),
packages: optional(specialize_err(EHeader::Packages, loc(packages()))),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
});
map_with_arena(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
let mut before_packages: &'a [CommentOrNewline] = &[];
let packages = match old.packages {
Some(packages) => {
before_packages = merge_spaces(
arena,
packages.value.keyword.before,
packages.value.keyword.after,
);
if let To::ExistingPackage(platform_shorthand) = old.provides.to.value {
packages.map(|coll| {
coll.item.map_items(arena, |loc_spaced_pkg| {
if loc_spaced_pkg.value.item().shorthand == platform_shorthand {
loc_spaced_pkg.map(|spaced_pkg| {
spaced_pkg.map(arena, |pkg| {
let mut new_pkg = *pkg;
new_pkg.platform_marker = Some(merge_spaces(
arena,
old.provides.to_keyword.before,
old.provides.to_keyword.after,
));
new_pkg
})
})
} else {
*loc_spaced_pkg
}
})
})
} else {
packages.map(|kw| kw.item)
}
}
None => Loc {
region: Region::zero(),
value: Collection::empty(),
},
};
let provides = match old.provides.types {
Some(types) => {
let mut combined_items = bumpalo::collections::Vec::with_capacity_in(
old.provides.entries.items.len() + types.items.len(),
arena,
);
combined_items.extend_from_slice(old.provides.entries.items);
for loc_spaced_type_ident in types.items {
combined_items.push(loc_spaced_type_ident.map(|spaced_type_ident| {
spaced_type_ident.map(arena, |type_ident| {
ExposedName::new(From::from(*type_ident))
})
}));
}
let value_comments = old.provides.entries.final_comments();
let type_comments = types.final_comments();
let mut combined_comments = bumpalo::collections::Vec::with_capacity_in(
value_comments.len() + type_comments.len(),
arena,
);
combined_comments.extend_from_slice(value_comments);
combined_comments.extend_from_slice(type_comments);
Collection::with_items_and_comments(
arena,
combined_items.into_bump_slice(),
combined_comments.into_bump_slice(),
)
}
None => old.provides.entries,
};
AppHeader {
before_provides: merge_spaces(
arena,
old.before_name,
old.provides.provides_keyword.before,
),
provides,
before_packages: merge_spaces(
arena,
before_packages,
old.provides.provides_keyword.after,
),
packages,
old_imports: old.imports.and_then(imports_none_if_empty),
old_provides_to_new_package: match old.provides.to.value {
To::NewPackage(new_pkg) => Some(new_pkg),
To::ExistingPackage(_) => None,
},
}
})
}
#[inline(always)]
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
record!(PackageHeader {
before_exposes: space0_e(EHeader::IndentStart),
exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
})
.trace("package_header")
}
#[derive(Debug, Clone, PartialEq)]
struct OldPackageHeader<'a> {
before_name: &'a [CommentOrNewline<'a>],
exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
packages:
Loc<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
}
#[inline(always)]
fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
map_with_arena(
record!(OldPackageHeader {
before_name: skip_second(
space0_e(EHeader::IndentStart),
specialize_err(EHeader::PackageName, package_name())
),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, loc(packages())),
}),
|arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| {
let before_exposes = merge_n_spaces!(
arena,
old.before_name,
old.exposes.keyword.before,
old.exposes.keyword.after
);
let before_packages = merge_spaces(
arena,
old.packages.value.keyword.before,
old.packages.value.keyword.after,
);
PackageHeader {
before_exposes,
exposes: old.exposes.item,
before_packages,
packages: old.packages.map(|kw| kw.item),
}
},
)
.trace("old_package_header")
}
#[inline(always)]
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
record!(PlatformHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc(specialize_err(EHeader::PlatformName, package_name())),
requires: specialize_err(EHeader::Requires, requires()),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, packages()),
imports: specialize_err(EHeader::Imports, imports()),
provides: specialize_err(EHeader::Provides, provides_exposed()),
})
.trace("platform_header")
}
fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
one_of![
specialize_err(
|_, pos| EProvides::Identifier(pos),
map(lowercase_ident(), To::ExistingPackage)
),
specialize_err(EProvides::Package, map(package_name(), To::NewPackage))
]
}
#[inline(always)]
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
record!(ProvidesTo {
provides_keyword: spaces_around_keyword(
ProvidesKeyword,
EProvides::Provides,
EProvides::IndentProvides,
EProvides::IndentListStart
),
entries: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
byte(b']', EProvides::ListEnd),
Spaced::SpaceBefore
),
types: optional(backtrackable(provides_types())),
to_keyword: spaces_around_keyword(
ToKeyword,
EProvides::To,
EProvides::IndentTo,
EProvides::IndentListStart
),
to: loc(provides_to_package()),
})
.trace("provides_to")
}
fn provides_exposed<'a>() -> impl Parser<
'a,
KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
EProvides<'a>,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
ProvidesKeyword,
EProvides::Provides,
EProvides::IndentProvides,
EProvides::IndentListStart
),
item: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
byte(b']', EProvides::ListEnd),
Spaced::SpaceBefore
),
})
}
#[inline(always)]
fn provides_types<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, EProvides<'a>> {
skip_first(
// We only support spaces here, not newlines, because this is not intended
// to be the design forever. Someday it will hopefully work like Elm,
// where platform authors can provide functions like Browser.sandbox which
// present an API based on ordinary-looking type variables.
zero_or_more(byte(
b' ',
// HACK: If this errors, EProvides::Provides is not an accurate reflection
// of what went wrong. However, this is both skipped and zero_or_more,
// so this error should never be visible to anyone in practice!
EProvides::Provides,
)),
collection_trailing_sep_e(
byte(b'{', EProvides::ListStart),
provides_type_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
byte(b'}', EProvides::ListEnd),
Spaced::SpaceBefore,
),
)
}
fn provides_type_entry<'a, F, E>(
to_expectation: F,
) -> impl Parser<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>, E>
where
F: Fn(Position) -> E,
F: Copy,
E: 'a,
{
loc(map(
specialize_err(move |_, pos| to_expectation(pos), ident::uppercase()),
Spaced::Item,
))
}
fn exposes_entry<'a, F, E>(
to_expectation: F,
) -> impl Parser<'a, Loc<Spaced<'a, ExposedName<'a>>>, E>
where
F: Fn(Position) -> E,
F: Copy,
E: 'a,
{
loc(map(
specialize_err(move |_, pos| to_expectation(pos), unqualified_ident()),
|n| Spaced::Item(ExposedName::new(n)),
))
}
#[inline(always)]
fn requires<'a>(
) -> impl Parser<'a, KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>, ERequires<'a>> {
record!(KeywordItem {
keyword: spaces_around_keyword(
RequiresKeyword,
ERequires::Requires,
ERequires::IndentRequires,
ERequires::IndentListStart
),
item: platform_requires(),
})
}
#[inline(always)]
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()
})
}
#[inline(always)]
fn requires_rigids<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, ERequires<'a>> {
collection_trailing_sep_e(
byte(b'{', ERequires::ListStart),
specialize_err(
|_, pos| ERequires::Rigid(pos),
loc(map(ident::uppercase(), Spaced::Item)),
),
byte(b',', ERequires::ListEnd),
byte(b'}', ERequires::ListEnd),
Spaced::SpaceBefore,
)
}
#[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
skip_first(
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),
),
)
}
#[inline(always)]
fn exposes_values_kw<'a>() -> impl Parser<
'a,
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
EExposes,
> {
record!(KeywordItem {
keyword: exposes_kw(),
item: exposes_list()
})
}
#[inline(always)]
fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> {
spaces_around_keyword(
ExposesKeyword,
EExposes::Exposes,
EExposes::IndentExposes,
EExposes::IndentListStart,
)
}
#[inline(always)]
fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, EExposes>
{
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore,
)
}
pub fn spaces_around_keyword<'a, K: Keyword, E>(
keyword_item: K,
expectation: fn(Position) -> E,
indent_problem1: fn(Position) -> E,
indent_problem2: fn(Position) -> E,
) -> impl Parser<'a, Spaces<'a, K>, E>
where
E: 'a + SpaceProblem,
{
map(
and(
skip_second(
// parse any leading space before the keyword
backtrackable(space0_e(indent_problem1)),
// parse the keyword
crate::parser::keyword(K::KEYWORD, expectation),
),
// parse the trailing space
space0_e(indent_problem2),
),
move |(before, after)| Spaces {
before,
item: keyword_item,
after,
},
)
}
#[inline(always)]
fn exposes_modules<'a>() -> impl Parser<
'a,
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
EExposes,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
ExposesKeyword,
EExposes::Exposes,
EExposes::IndentExposes,
EExposes::IndentListStart
),
item: exposes_module_collection(),
})
}
fn exposes_module_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>, EExposes> {
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore,
)
}
fn exposes_module<'a, F, E>(
to_expectation: F,
) -> impl Parser<'a, Loc<Spaced<'a, ModuleName<'a>>>, E>
where
F: Fn(Position) -> E,
F: Copy,
E: 'a,
{
loc(map(
specialize_err(move |_, pos| to_expectation(pos), module_name()),
Spaced::Item,
))
}
#[inline(always)]
fn packages<'a>() -> impl Parser<
'a,
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
EPackages<'a>,
> {
record!(KeywordItem {
keyword: packages_kw(),
item: packages_collection()
})
}
#[inline(always)]
fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> {
spaces_around_keyword(
PackagesKeyword,
EPackages::Packages,
EPackages::IndentPackages,
EPackages::IndentListStart,
)
}
#[inline(always)]
fn packages_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>, EPackages<'a>> {
collection_trailing_sep_e(
byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc(package_entry())),
byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore,
)
}
fn imports<'a>() -> impl Parser<
'a,
KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
EImports,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
ImportsKeyword,
EImports::Imports,
EImports::IndentImports,
EImports::IndentListStart
),
item: collection_trailing_sep_e(
byte(b'[', EImports::ListStart),
loc(imports_entry()),
byte(b',', EImports::ListEnd),
byte(b']', EImports::ListEnd),
Spaced::SpaceBefore
)
})
.trace("imports")
}
#[inline(always)]
pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
// e.g.
//
// printLine : Str -> Task {} *
map(
and(
and(
loc(specialize_err(
|_, pos| ETypedIdent::Identifier(pos),
lowercase_ident(),
)),
space0_e(ETypedIdent::IndentHasType),
),
skip_first(
byte(b':', ETypedIdent::HasType),
space0_before_e(
specialize_err(
ETypedIdent::Type,
reset_min_indent(type_annotation::located(true)),
),
ETypedIdent::IndentType,
),
),
),
|((ident, spaces_before_colon), ann)| {
Spaced::Item(TypedIdent {
ident,
spaces_before_colon,
ann,
})
},
)
}
fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> {
specialize_err(|_, pos| EImports::Shorthand(pos), lowercase_ident())
}
pub fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
where
F: Fn(Position) -> E,
E: 'a,
F: 'a,
{
specialize_err(move |_, pos| to_expectation(pos), module_name())
}
#[inline(always)]
fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> {
type Temp<'a> = (
(Option<&'a str>, ModuleName<'a>),
Option<Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
);
let spaced_import = |((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
let entry = match opt_shortname {
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
None => ImportsEntry::Module(module_name, exposed_values),
};
Spaced::Item(entry)
};
one_of!(
map(
and(
and(
// e.g. `pf.`
optional(backtrackable(skip_second(
shortname(),
byte(b'.', EImports::ShorthandDot)
))),
// e.g. `Task`
module_name_help(EImports::ModuleName)
),
// e.g. `.{ Task, after}`
optional(skip_first(
byte(b'.', EImports::ExposingDot),
collection_trailing_sep_e(
byte(b'{', EImports::SetStart),
exposes_entry(EImports::Identifier),
byte(b',', EImports::SetEnd),
byte(b'}', EImports::SetEnd),
Spaced::SpaceBefore
)
))
),
spaced_import
)
.trace("normal_import"),
map(
and(
and(
// e.g. "filename"
// TODO: str literal allows for multiline strings. We probably don't want that for file names.
specialize_err(|_, pos| EImports::StrLiteral(pos), parse_str_literal()),
// e.g. as
and(
and(
space0_e(EImports::AsKeyword),
two_bytes(b'a', b's', EImports::AsKeyword)
),
space0_e(EImports::AsKeyword)
)
),
// e.g. file : Str
specialize_err(|_, pos| EImports::TypedIdent(pos), typed_ident())
),
|((file_name, _), typed_ident)| {
// TODO: look at blacking block strings during parsing.
Spaced::Item(ImportsEntry::IngestedFile(file_name, typed_ident))
}
)
.trace("ingest_file_import")
)
.trace("imports_entry")
}

View file

@ -8,9 +8,9 @@ use crate::{
AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header,
Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation,
IngestedFileImport, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern,
PatternAs, Spaced, Spaces, SpacesBefore, StrLiteral, StrSegment, Tag, TypeAnnotation,
TypeDef, TypeHeader, ValueDef, WhenBranch,
IngestedFileImport, ModuleImport, ModuleImportParams, Pattern, PatternAs, Spaced, Spaces,
SpacesBefore, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
WhenBranch,
},
header::{
AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword,
@ -562,30 +562,6 @@ impl<'a, T: Normalize<'a> + Copy + std::fmt::Debug> Normalize<'a> for AssignedFi
}
}
impl<'a> Normalize<'a> for OldRecordBuilderField<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match *self {
OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value(
a.normalize(arena),
&[],
arena.alloc(c.normalize(arena)),
),
OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue(
a.normalize(arena),
&[],
&[],
arena.alloc(c.normalize(arena)),
),
OldRecordBuilderField::LabelOnly(a) => {
OldRecordBuilderField::LabelOnly(a.normalize(arena))
}
OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a),
OldRecordBuilderField::SpaceBefore(a, _) => a.normalize(arena),
OldRecordBuilderField::SpaceAfter(a, _) => a.normalize(arena),
}
}
}
impl<'a> Normalize<'a> for StrLiteral<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match *self {
@ -738,7 +714,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
fields: fields.normalize(arena),
},
Expr::Record(a) => Expr::Record(a.normalize(arena)),
Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.normalize(arena)),
Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder {
mapper: arena.alloc(mapper.normalize(arena)),
fields: fields.normalize(arena),
@ -796,7 +771,15 @@ impl<'a> Normalize<'a> for Expr<'a> {
Expr::UnaryOp(a, b) => {
Expr::UnaryOp(arena.alloc(a.normalize(arena)), b.normalize(arena))
}
Expr::If(a, b) => Expr::If(a.normalize(arena), arena.alloc(b.normalize(arena))),
Expr::If {
if_thens,
final_else,
indented_else,
} => Expr::If {
if_thens: if_thens.normalize(arena),
final_else: arena.alloc(final_else.normalize(arena)),
indented_else,
},
Expr::When(a, b) => Expr::When(arena.alloc(a.normalize(arena)), b.normalize(arena)),
Expr::ParensAround(a) => {
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
@ -809,12 +792,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
Expr::SpaceBefore(a, _) => a.normalize(arena),
Expr::SpaceAfter(a, _) => a.normalize(arena),
Expr::SingleQuote(a) => Expr::Num(a),
Expr::MultipleOldRecordBuilders(a) => {
Expr::MultipleOldRecordBuilders(arena.alloc(a.normalize(arena)))
}
Expr::UnappliedOldRecordBuilder(a) => {
Expr::UnappliedOldRecordBuilder(arena.alloc(a.normalize(arena)))
}
Expr::EmptyRecordBuilder(a) => {
Expr::EmptyRecordBuilder(arena.alloc(a.normalize(arena)))
}
@ -1084,12 +1061,6 @@ impl<'a> Normalize<'a> for EExpr<'a> {
EExpr::Record(inner_err, _pos) => {
EExpr::Record(inner_err.normalize(arena), Position::zero())
}
EExpr::OptionalValueInOldRecordBuilder(_pos) => {
EExpr::OptionalValueInOldRecordBuilder(Region::zero())
}
EExpr::IgnoredValueInOldRecordBuilder(_pos) => {
EExpr::OptionalValueInOldRecordBuilder(Region::zero())
}
EExpr::Str(inner_err, _pos) => EExpr::Str(inner_err.normalize(arena), Position::zero()),
EExpr::Number(inner_err, _pos) => EExpr::Number(inner_err.clone(), Position::zero()),
EExpr::List(inner_err, _pos) => {
@ -1323,7 +1294,6 @@ impl<'a> Normalize<'a> for EImportParams<'a> {
EImportParams::Record(inner_err.normalize(arena), Position::zero())
}
EImportParams::RecordUpdateFound(_) => EImportParams::RecordUpdateFound(Region::zero()),
EImportParams::RecordApplyFound(_) => EImportParams::RecordApplyFound(Region::zero()),
EImportParams::RecordIgnoredFieldFound(_) => {
EImportParams::RecordIgnoredFieldFound(Region::zero())
}

View file

@ -344,8 +344,6 @@ pub enum EExpr<'a> {
InParens(EInParens<'a>, Position),
Record(ERecord<'a>, Position),
OptionalValueInOldRecordBuilder(Region),
IgnoredValueInOldRecordBuilder(Region),
RecordUpdateOldBuilderField(Region),
RecordUpdateIgnoredField(Region),
RecordBuilderOldBuilderField(Region),
@ -551,7 +549,6 @@ pub enum EImportParams<'a> {
Record(ERecord<'a>, Position),
RecordUpdateFound(Region),
RecordBuilderFound(Region),
RecordApplyFound(Region),
RecordIgnoredFieldFound(Region),
Space(BadInputError, Position),
}

View file

@ -5,7 +5,7 @@ use crate::ast::{
use crate::blankspace::{
space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
};
use crate::expr::{record_field, FoundApplyValue};
use crate::expr::record_field;
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
use crate::keyword;
use crate::parser::{
@ -565,11 +565,10 @@ fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, ETyp
fn ability_impl_field<'a>() -> impl Parser<'a, AssignedField<'a, Expr<'a>>, ERecord<'a>> {
then(record_field(), move |arena, state, _, field| {
match field.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => {
AssignedField::IgnoredValue(_, _, _) => {
Err((MadeProgress, ERecord::Field(state.pos())))
}
Ok(assigned_field) => Ok((MadeProgress, assigned_field, state)),
Err(FoundApplyValue) => Err((MadeProgress, ERecord::Field(state.pos()))),
assigned_field => Ok((MadeProgress, assigned_field, state)),
}
})
}

View file

@ -427,8 +427,6 @@ impl Problem {
| Problem::RuntimeError(RuntimeError::EmptySingleQuote(region))
| Problem::RuntimeError(RuntimeError::MultipleCharsInSingleQuote(region))
| Problem::RuntimeError(RuntimeError::DegenerateBranch(region))
| Problem::RuntimeError(RuntimeError::MultipleOldRecordBuilders(region))
| Problem::RuntimeError(RuntimeError::UnappliedOldRecordBuilder(region))
| Problem::RuntimeError(RuntimeError::EmptyRecordBuilder(region))
| Problem::RuntimeError(RuntimeError::SingleFieldRecordBuilder(region))
| Problem::RuntimeError(RuntimeError::OptionalFieldInRecordBuilder {
@ -686,9 +684,6 @@ pub enum RuntimeError {
DegenerateBranch(Region),
MultipleOldRecordBuilders(Region),
UnappliedOldRecordBuilder(Region),
EmptyRecordBuilder(Region),
SingleFieldRecordBuilder(Region),
OptionalFieldInRecordBuilder {
@ -739,8 +734,6 @@ impl RuntimeError {
| RuntimeError::DegenerateBranch(region)
| RuntimeError::InvalidInterpolation(region)
| RuntimeError::InvalidHexadecimal(region)
| RuntimeError::MultipleOldRecordBuilders(region)
| RuntimeError::UnappliedOldRecordBuilder(region)
| RuntimeError::EmptyRecordBuilder(region)
| RuntimeError::SingleFieldRecordBuilder(region)
| RuntimeError::OptionalFieldInRecordBuilder {

View file

@ -547,6 +547,11 @@ fn list_drop_at() {
RocList::from_slice(&[2, 3]),
RocList<i64>
);
assert_evals_to!(
"List.dropAt [1, 2, 3] 1",
RocList::from_slice(&[1, 3]),
RocList<i64>
);
assert_evals_to!(
"List.dropAt [0, 0, 0] 3",
RocList::from_slice(&[0, 0, 0]),

View file

@ -1988,3 +1988,75 @@ fn str_contains_self() {
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn str_drop_prefix() {
assert_evals_to!(
r#"
Str.dropPrefix "" "foo"
"#,
RocStr::from(""),
RocStr
);
assert_evals_to!(
r#"
Str.dropPrefix "bar" "foo"
"#,
RocStr::from("bar"),
RocStr
);
assert_evals_to!(
r#"
Str.dropPrefix "foobar" "foo"
"#,
RocStr::from("bar"),
RocStr
);
assert_evals_to!(
r#"
Str.dropPrefix "fooBarThisIsDefinitelyAReallyLongAndNotaShortString" "foo"
"#,
RocStr::from("BarThisIsDefinitelyAReallyLongAndNotaShortString"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn str_drop_suffix() {
assert_evals_to!(
r#"
Str.dropSuffix "" "foo"
"#,
RocStr::from(""),
RocStr
);
assert_evals_to!(
r#"
Str.dropSuffix "bar" "foo"
"#,
RocStr::from("bar"),
RocStr
);
assert_evals_to!(
r#"
Str.dropSuffix "barfoo" "foo"
"#,
RocStr::from("bar"),
RocStr
);
assert_evals_to!(
r#"
Str.dropSuffix "BarThisIsDefinitelyAReallyLongAndNotaShortStringfoo" "foo"
"#,
RocStr::from("BarThisIsDefinitelyAReallyLongAndNotaShortString"),
RocStr
);
}

View file

@ -41,8 +41,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.238 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.238;
let Str.248 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.248;
procedure Test.1 (Test.5):
ret Test.5;

View file

@ -43,14 +43,14 @@ procedure Num.96 (#Attr.2):
ret Num.282;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.4 : I64 = 1i64;
let Test.1 : I64 = 2i64;
let Test.2 : Str = CallByName Inspect.33 Test.1;
dbg Test.2;
dec Test.2;
let Test.3 : I64 = CallByName Num.19 Test.4 Test.1;
ret Test.3;
let Test.5 : I64 = 1i64;
let Test.2 : I64 = 2i64;
let Test.3 : Str = CallByName Inspect.33 Test.2;
dbg Test.3;
dec Test.3;
let Test.4 : I64 = CallByName Num.19 Test.5 Test.2;
ret Test.4;

View file

@ -44,8 +44,8 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.1 ():
let Test.4 : Str = "";

View file

@ -40,19 +40,19 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.238 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.238;
let Str.248 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.248;
procedure Test.0 ():
let Test.4 : Str = "Hello ";
let Test.1 : Str = "world";
inc Test.1;
let Test.2 : Str = CallByName Inspect.33 Test.1;
dbg Test.2;
dec Test.2;
let Test.7 : Str = "!";
let Test.5 : Str = CallByName Str.3 Test.1 Test.7;
dec Test.7;
let Test.3 : Str = CallByName Str.3 Test.4 Test.5;
dec Test.5;
ret Test.3;
let Test.5 : Str = "Hello ";
let Test.2 : Str = "world";
inc Test.2;
let Test.3 : Str = CallByName Inspect.33 Test.2;
dbg Test.3;
dec Test.3;
let Test.8 : Str = "!";
let Test.6 : Str = CallByName Str.3 Test.2 Test.8;
dec Test.8;
let Test.4 : Str = CallByName Str.3 Test.5 Test.6;
dec Test.6;
ret Test.4;

View file

@ -39,18 +39,18 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.3 : I64 = 1i64;
let Test.4 : Str = CallByName Inspect.33 Test.3;
dbg Test.4;
dec Test.4;
let Test.5 : Str = CallByName Inspect.33 Test.3;
dbg Test.5;
dec Test.5;
let Test.6 : Str = CallByName Inspect.33 Test.3;
dbg Test.6;
dec Test.6;
ret Test.3;
let Test.6 : I64 = 1i64;
let Test.7 : Str = CallByName Inspect.33 Test.6;
dbg Test.7;
dec Test.7;
let Test.8 : Str = CallByName Inspect.33 Test.6;
dbg Test.8;
dec Test.8;
let Test.9 : Str = CallByName Inspect.33 Test.6;
dbg Test.9;
dec Test.9;
ret Test.6;

View file

@ -40,8 +40,8 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.3 : Str = "";

View file

@ -1,29 +1,29 @@
procedure Dict.1 (Dict.730):
let Dict.739 : List {U32, U32} = Array [];
let Dict.740 : List {[], []} = Array [];
let Dict.741 : U64 = 0i64;
procedure Dict.1 (Dict.731):
let Dict.740 : List {U32, U32} = Array [];
let Dict.741 : List {[], []} = Array [];
let Dict.742 : U64 = 0i64;
let Dict.51 : Float32 = CallByName Dict.51;
let Dict.52 : U8 = CallByName Dict.52;
let Dict.738 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.739, Dict.740, Dict.741, Dict.51, Dict.52};
let Dict.739 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.740, Dict.741, Dict.742, Dict.51, Dict.52};
ret Dict.739;
procedure Dict.4 (Dict.737):
let Dict.163 : List {[], []} = StructAtIndex 1 Dict.737;
let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.737;
dec #Derived_gen.0;
let Dict.738 : U64 = CallByName List.6 Dict.163;
dec Dict.163;
ret Dict.738;
procedure Dict.4 (Dict.736):
let Dict.163 : List {[], []} = StructAtIndex 1 Dict.736;
let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.736;
dec #Derived_gen.0;
let Dict.737 : U64 = CallByName List.6 Dict.163;
dec Dict.163;
ret Dict.737;
procedure Dict.51 ():
let Dict.745 : Float32 = 0.8f64;
ret Dict.745;
let Dict.746 : Float32 = 0.8f64;
ret Dict.746;
procedure Dict.52 ():
let Dict.743 : U8 = 64i64;
let Dict.744 : U8 = 3i64;
let Dict.742 : U8 = CallByName Num.75 Dict.743 Dict.744;
ret Dict.742;
let Dict.744 : U8 = 64i64;
let Dict.745 : U8 = 3i64;
let Dict.743 : U8 = CallByName Num.75 Dict.744 Dict.745;
ret Dict.743;
procedure List.6 (#Attr.2):
let List.625 : U64 = lowlevel ListLenU64 #Attr.2;

View file

@ -164,32 +164,32 @@ procedure Num.96 (#Attr.2):
ret Num.285;
procedure Str.12 (#Attr.2):
let Str.248 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.248;
let Str.258 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.258;
procedure Str.36 (#Attr.2):
let Str.249 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.249;
let Str.259 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.259;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.45 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.45 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.45;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.325 : Str = CallByName Encode.23 Test.56;

View file

@ -105,32 +105,32 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.246;
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.24 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.24 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.24;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.292 : Str = CallByName Encode.23 Test.56;

View file

@ -112,32 +112,32 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.246;
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.28;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.296 : Str = CallByName Encode.23 Test.56;

View file

@ -38,32 +38,32 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.246;
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.3 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.3 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.3;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.259 : Str = CallByName Encode.23 Test.56;

View file

@ -110,32 +110,32 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.246;
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.27 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.27 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.27;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.297 : Str = CallByName Encode.23 Test.56;

View file

@ -113,32 +113,32 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.246;
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256;
procedure Str.43 (#Attr.2):
let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.243;
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
procedure Str.9 (Str.71):
let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71;
let Str.240 : Int1 = StructAtIndex 2 Str.72;
if Str.240 then
let Str.242 : Str = StructAtIndex 1 Str.72;
let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242;
ret Str.241;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
else
let Str.238 : U8 = StructAtIndex 3 Str.72;
let Str.239 : U64 = StructAtIndex 0 Str.72;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.72;
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.28;
let Str.237 : {U64, U8} = Struct {Str.239, Str.238};
let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237;
ret Str.236;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
ret Str.246;
procedure Test.20 (Test.56):
let Test.301 : Str = CallByName Encode.23 Test.56;

File diff suppressed because it is too large Load diff

View file

@ -178,8 +178,8 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.2 : List I64 = Array [1i64, 2i64, 3i64];

View file

@ -292,8 +292,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.283;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.237;
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
procedure Test.0 ():
let Test.4 : Str = "bar";

View file

@ -209,8 +209,8 @@ procedure Num.96 (#Attr.2):
ret Num.282;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.3 : Decimal = 3dec;

View file

@ -179,8 +179,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.3 : Str = "foo";

View file

@ -186,8 +186,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.3 : Str = "foo";

View file

@ -40,8 +40,8 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.2 : Str = "abc";

View file

@ -179,8 +179,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.4 : Str = "foo";

View file

@ -182,8 +182,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.5 : Str = "foo";

View file

@ -45,27 +45,27 @@ procedure Num.22 (#Attr.2, #Attr.3):
let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.281;
procedure Str.27 (Str.82):
let Str.236 : [C Int1, C I64] = CallByName Str.64 Str.82;
ret Str.236;
procedure Str.27 (Str.84):
let Str.246 : [C Int1, C I64] = CallByName Str.66 Str.84;
ret Str.246;
procedure Str.42 (#Attr.2):
let Str.244 : {I64, U8} = lowlevel StrToNum #Attr.2;
ret Str.244;
let Str.254 : {I64, U8} = lowlevel StrToNum #Attr.2;
ret Str.254;
procedure Str.64 (Str.189):
let Str.190 : {I64, U8} = CallByName Str.42 Str.189;
let Str.242 : U8 = StructAtIndex 1 Str.190;
let Str.243 : U8 = 0i64;
let Str.239 : Int1 = CallByName Bool.11 Str.242 Str.243;
if Str.239 then
let Str.241 : I64 = StructAtIndex 0 Str.190;
let Str.240 : [C Int1, C I64] = TagId(1) Str.241;
ret Str.240;
procedure Str.66 (Str.191):
let Str.192 : {I64, U8} = CallByName Str.42 Str.191;
let Str.252 : U8 = StructAtIndex 1 Str.192;
let Str.253 : U8 = 0i64;
let Str.249 : Int1 = CallByName Bool.11 Str.252 Str.253;
if Str.249 then
let Str.251 : I64 = StructAtIndex 0 Str.192;
let Str.250 : [C Int1, C I64] = TagId(1) Str.251;
ret Str.250;
else
let Str.238 : Int1 = false;
let Str.237 : [C Int1, C I64] = TagId(0) Str.238;
ret Str.237;
let Str.248 : Int1 = false;
let Str.247 : [C Int1, C I64] = TagId(0) Str.248;
ret Str.247;
procedure Test.0 ():
let Test.3 : Int1 = CallByName Bool.2;

View file

@ -19,30 +19,30 @@ procedure Decode.26 (Decode.109, Decode.110):
ret Decode.126;
procedure Str.12 (#Attr.2):
let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.245;
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.27 (Str.82):
let Str.236 : [C {}, C I64] = CallByName Str.64 Str.82;
ret Str.236;
procedure Str.27 (Str.84):
let Str.246 : [C {}, C I64] = CallByName Str.66 Str.84;
ret Str.246;
procedure Str.42 (#Attr.2):
let Str.244 : {I64, U8} = lowlevel StrToNum #Attr.2;
ret Str.244;
let Str.254 : {I64, U8} = lowlevel StrToNum #Attr.2;
ret Str.254;
procedure Str.64 (Str.189):
let Str.190 : {I64, U8} = CallByName Str.42 Str.189;
let Str.242 : U8 = StructAtIndex 1 Str.190;
let Str.243 : U8 = 0i64;
let Str.239 : Int1 = CallByName Bool.11 Str.242 Str.243;
if Str.239 then
let Str.241 : I64 = StructAtIndex 0 Str.190;
let Str.240 : [C {}, C I64] = TagId(1) Str.241;
ret Str.240;
procedure Str.66 (Str.191):
let Str.192 : {I64, U8} = CallByName Str.42 Str.191;
let Str.252 : U8 = StructAtIndex 1 Str.192;
let Str.253 : U8 = 0i64;
let Str.249 : Int1 = CallByName Bool.11 Str.252 Str.253;
if Str.249 then
let Str.251 : I64 = StructAtIndex 0 Str.192;
let Str.250 : [C {}, C I64] = TagId(1) Str.251;
ret Str.250;
else
let Str.238 : {} = Struct {};
let Str.237 : [C {}, C I64] = TagId(0) Str.238;
ret Str.237;
let Str.248 : {} = Struct {};
let Str.247 : [C {}, C I64] = TagId(0) Str.248;
ret Str.247;
procedure Test.103 ():
let Test.101 : [C Str, C {List U8, I64}] = CallByName Test.19;

View file

@ -1,6 +1,6 @@
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.1 (Test.5):
let Test.16 : [C {}, C U64, C Str] = TagId(0) Test.5;

View file

@ -71,12 +71,12 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.283;
procedure Str.16 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
ret Str.246;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.237;
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
procedure Test.1 ():
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";

View file

@ -70,8 +70,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.283;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.237;
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
procedure Test.1 ():
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";

View file

@ -0,0 +1,59 @@
procedure Inspect.278 (Inspect.279, Inspect.277):
let Inspect.318 : Str = CallByName Num.96 Inspect.277;
let Inspect.317 : Str = CallByName Inspect.63 Inspect.279 Inspect.318;
dec Inspect.318;
ret Inspect.317;
procedure Inspect.30 (Inspect.147):
ret Inspect.147;
procedure Inspect.33 (Inspect.152):
let Inspect.322 : Str = CallByName Inspect.5 Inspect.152;
let Inspect.321 : Str = CallByName Inspect.64 Inspect.322;
ret Inspect.321;
procedure Inspect.39 (Inspect.301):
let Inspect.311 : Str = "";
ret Inspect.311;
procedure Inspect.5 (Inspect.150):
let Inspect.312 : I64 = CallByName Inspect.57 Inspect.150;
let Inspect.309 : {} = Struct {};
let Inspect.308 : Str = CallByName Inspect.39 Inspect.309;
let Inspect.307 : Str = CallByName Inspect.278 Inspect.308 Inspect.312;
ret Inspect.307;
procedure Inspect.57 (Inspect.277):
let Inspect.313 : I64 = CallByName Inspect.30 Inspect.277;
ret Inspect.313;
procedure Inspect.63 (Inspect.300, Inspect.296):
let Inspect.320 : Str = CallByName Str.3 Inspect.300 Inspect.296;
ret Inspect.320;
procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.281;
procedure Num.96 (#Attr.2):
let Num.282 : Str = lowlevel NumToStr #Attr.2;
ret Num.282;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.4 : I64 = 1i64;
let Test.5 : Str = CallByName Inspect.33 Test.4;
dbg Test.5;
dec Test.5;
let Test.9 : I64 = 2i64;
let Test.3 : I64 = CallByName Num.19 Test.4 Test.9;
let Test.6 : Str = CallByName Inspect.33 Test.3;
dbg Test.6;
dec Test.6;
ret Test.3;

View file

@ -3,8 +3,8 @@ procedure Bool.11 (#Attr.2, #Attr.3):
ret Bool.23;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.237;
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
procedure Test.2 (Test.7):
let Test.24 : Str = ".trace(\"";

View file

@ -3,8 +3,8 @@ procedure Num.20 (#Attr.2, #Attr.3):
ret Num.281;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.238 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.238;
let Str.248 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.248;
procedure Test.11 (Test.29, #Attr.12):
let Test.32 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;

View file

@ -99,12 +99,12 @@ procedure Num.96 (#Attr.2):
ret Num.281;
procedure Str.12 (#Attr.2):
let Str.237 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.237;
let Str.247 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.247;
procedure Str.36 (#Attr.2):
let Str.238 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.238;
let Str.248 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.248;
procedure Test.20 (Test.58):
let Test.295 : Str = CallByName Encode.23 Test.58;

View file

@ -192,12 +192,12 @@ procedure Num.96 (#Attr.2):
ret Num.285;
procedure Str.12 (#Attr.2):
let Str.240 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.240;
let Str.250 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.250;
procedure Str.36 (#Attr.2):
let Str.241 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.241;
let Str.251 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.251;
procedure Test.20 (Test.58):
inc Test.58;

View file

@ -3295,6 +3295,18 @@ fn dbg_inside_string() {
)
}
#[mono_test]
fn pizza_dbg() {
indoc!(
r#"
1
|> dbg
|> Num.add 2
|> dbg
"#
)
}
#[mono_test]
fn linked_list_reverse() {
indoc!(

View file

@ -0,0 +1 @@
Expr(If(IndentElseBranch(@31), @0), @0)

View file

@ -0,0 +1,4 @@
if thing then
whatever
else
something better

View file

@ -1,6 +1,6 @@
SpaceAfter(
If(
[
If {
if_thens: [
(
@3-6 Var {
module_name: "",
@ -60,7 +60,7 @@ SpaceAfter(
),
),
],
@49-50 SpaceBefore(
final_else: @49-50 SpaceBefore(
Var {
module_name: "",
ident: "c",
@ -71,7 +71,8 @@ SpaceAfter(
),
],
),
),
indented_else: false,
},
[
LineComment(
" 4",

View file

@ -1,6 +1,6 @@
SpaceAfter(
If(
[
If {
if_thens: [
(
@3-5 Var {
module_name: "",
@ -40,7 +40,7 @@ SpaceAfter(
),
),
],
@42-43 SpaceBefore(
final_else: @42-43 SpaceBefore(
Num(
"3",
),
@ -48,7 +48,8 @@ SpaceAfter(
Newline,
],
),
),
indented_else: false,
},
[
Newline,
],

View file

@ -0,0 +1,16 @@
SpaceAfter(
BinOps(
[
(
@0-1 Num(
"1",
),
@2-4 Pizza,
),
],
@5-8 Dbg,
),
[
Newline,
],
)

View file

@ -0,0 +1 @@
1 |> dbg

View file

@ -8,8 +8,8 @@ SpaceAfter(
@2-3 Star,
),
],
@4-30 If(
[
@4-30 If {
if_thens: [
(
@7-16 Var {
module_name: "Bool",
@ -20,10 +20,11 @@ SpaceAfter(
),
),
],
@29-30 Num(
final_else: @29-30 Num(
"1",
),
),
indented_else: false,
},
),
[
Newline,

View file

@ -3,8 +3,8 @@ Record(
@1-31 RequiredValue(
@1-2 "x",
[],
@5-31 If(
[
@5-31 If {
if_thens: [
(
@8-17 Var {
module_name: "Bool",
@ -15,10 +15,11 @@ Record(
),
),
],
@30-31 Num(
final_else: @30-31 Num(
"2",
),
),
indented_else: false,
},
),
@33-37 RequiredValue(
@33-34 "y",

View file

@ -1986,7 +1986,7 @@ mod test_fmt {
}
#[test]
fn new_record_builder() {
fn record_builder() {
expr_formats_same(indoc!(
r"
{ shoes <- leftShoe: nothing }
@ -2077,170 +2077,6 @@ mod test_fmt {
);
}
#[test]
fn old_record_builder() {
expr_formats_same(indoc!(
r#"
{ a: 1, b: <- get "b" |> batch, c: <- get "c" |> batch, d }
"#
));
expr_formats_to(
indoc!(
r#"
{ a: 1, b: <- get "b" |> batch, c:<- get "c" |> batch }
"#
),
indoc!(
r#"
{ a: 1, b: <- get "b" |> batch, c: <- get "c" |> batch }
"#
),
);
expr_formats_same(indoc!(
r#"
{
a: 1,
b: <- get "b" |> batch,
c: <- get "c" |> batch,
d,
}
"#
));
expr_formats_to(
indoc!(
r#"
{ a: 1, b: <- get "b" |> batch,
c: <- get "c" |> batch, d }
"#
),
indoc!(
r#"
{
a: 1,
b: <- get "b" |> batch,
c: <- get "c" |> batch,
d,
}
"#
),
);
}
#[test]
fn multiline_record_builder_field() {
expr_formats_to(
indoc!(
r#"
succeed {
a: <- get "a" |> map (\x -> x * 2)
|> batch,
b: <- get "b" |> batch,
c: items
|> List.map \x -> x * 2
}
"#
),
indoc!(
r#"
succeed {
a: <-
get "a"
|> map (\x -> x * 2)
|> batch,
b: <- get "b" |> batch,
c:
items
|> List.map \x -> x * 2,
}
"#
),
);
expr_formats_same(indoc!(
r#"
succeed {
a: # I like to comment in weird places
<- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
));
expr_formats_same(indoc!(
r#"
succeed {
a:
# I like to comment in weird places
<- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
));
}
#[test]
fn outdentable_record_builders() {
expr_formats_to(
indoc!(
r#"
succeed { a: <- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
),
indoc!(
r#"
succeed {
a: <- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
),
);
expr_formats_to(
indoc!(
r#"
succeed
{
a: <- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
),
indoc!(
r#"
succeed {
a: <- get "a" |> batch,
b: <- get "b" |> batch,
}
"#
),
);
}
#[test]
fn can_format_multiple_record_builders() {
expr_formats_to(
indoc!(
r#"
succeed { a: <- get "a" }
{ b: <- get "b" }
"#
),
indoc!(
r#"
succeed
{ a: <- get "a" }
{ b: <- get "b" }
"#
),
);
}
#[test]
fn final_comments_in_records() {
expr_formats_same(indoc!(
@ -3628,6 +3464,60 @@ mod test_fmt {
));
}
#[test]
fn early_return_else() {
expr_formats_same(indoc!(
r"
if foo then
bar
else
baz
"
));
expr_formats_to(
indoc!(
r"
if thing then
whatever
else
too close
"
),
indoc!(
r"
if thing then
whatever
else
too close
"
),
);
expr_formats_to(
indoc!(
r"
if isGrowing plant then
LetBe
else
Water
"
),
indoc!(
r"
if isGrowing plant then
LetBe
else
Water
"
),
);
}
#[test]
fn multi_line_application() {
expr_formats_same(indoc!(

View file

@ -202,6 +202,7 @@ mod test_snapshots {
fail/expression_indentation_end.expr,
fail/if_guard_without_condition.expr,
fail/if_missing_else.expr,
fail/if_outdented_else_branch.expr,
fail/if_outdented_then.expr,
fail/import_with_lowercase_alias.moduledefs,
fail/imports_missing_comma.header,
@ -442,6 +443,7 @@ mod test_snapshots {
pass/pattern_as_spaces.expr,
pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929
pass/pizza_bang.moduledefs,
pass/pizza_dbg.expr,
pass/plus_if.expr,
pass/plus_when.expr,
pass/pos_inf_float.expr,

View file

@ -46,8 +46,8 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.0 ():
let Test.4 : {} = Struct {};

View file

@ -49,8 +49,8 @@ procedure Inspect.64 (Inspect.302):
ret Inspect.302;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.236;
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
procedure Test.2 (Test.3):
let Test.4 : Str = CallByName Inspect.33 Test.3;