Merge branch 'trunk' into builtins-refactor-list-take

This commit is contained in:
satotake 2021-11-13 03:38:29 +00:00 committed by GitHub
commit 4359dcff73
29 changed files with 593 additions and 424 deletions

View file

@ -51,3 +51,4 @@ Eric Newbury <enewbury@users.noreply.github.com>
Ayaz Hafiz <ayaz.hafiz.1@gmail.com>
Johannes Maas <github@j-maas.de>
Takeshi Sato <doublequotation@gmail.com>
Joost Baas <joost@joostbaas.eu>

View file

@ -395,7 +395,8 @@ pub fn to_type2<'a>(
Type2::Variable(var)
}
Record { fields, ext, .. } => {
let field_types_map = can_assigned_fields(env, scope, references, fields, region);
let field_types_map =
can_assigned_fields(env, scope, references, &fields.items, region);
let field_types = PoolVec::with_capacity(field_types_map.len() as u32, env.pool);

View file

@ -863,6 +863,7 @@ fn get_macos_version() -> String {
.expect("Failed to convert output of command 'sw_vers -productVersion' into a utf8 string");
full_version_string
.trim_end()
.split('.')
.take(2)
.collect::<Vec<&str>>()

View file

@ -1388,6 +1388,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(flex(TVAR1)),
);
// isOk : Result * * -> bool
add_top_level_function_type!(
Symbol::RESULT_IS_OK,
vec![result_type(flex(TVAR1), flex(TVAR3))],
Box::new(bool_type()),
);
types
}

View file

@ -389,7 +389,7 @@ fn can_annotation_help(
Record { fields, ext, .. } => {
let field_types = can_assigned_fields(
env,
fields,
&fields.items,
region,
scope,
var_store,

View file

@ -194,6 +194,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
RESULT_MAP_ERR => result_map_err,
RESULT_AFTER => result_after,
RESULT_WITH_DEFAULT => result_with_default,
RESULT_IS_OK => result_is_ok,
}
}
@ -4162,6 +4163,83 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
let ret_var = var_store.fresh();
let result_var = var_store.fresh();
let mut branches = vec![];
{
// ok branch
let tag_name = TagName::Global("Ok".into());
let pattern = Pattern::AppliedTag {
whole_var: result_var,
ext_var: var_store.fresh(),
tag_name,
arguments: vec![(var_store.fresh(), no_region(Pattern::Underscore))],
};
let true_expr = Tag {
variant_var: var_store.fresh(),
ext_var: var_store.fresh(),
name: TagName::Global("True".into()),
arguments: vec![],
};
let branch = WhenBranch {
patterns: vec![no_region(pattern)],
value: no_region(true_expr),
guard: None,
};
branches.push(branch);
}
{
// err branch
let tag_name = TagName::Global("Err".into());
let pattern = Pattern::AppliedTag {
whole_var: result_var,
ext_var: var_store.fresh(),
tag_name,
arguments: vec![(var_store.fresh(), no_region(Pattern::Underscore))],
};
let false_expr = Tag {
variant_var: var_store.fresh(),
ext_var: var_store.fresh(),
name: TagName::Global("False".into()),
arguments: vec![],
};
let branch = WhenBranch {
patterns: vec![no_region(pattern)],
value: no_region(false_expr),
guard: None,
};
branches.push(branch);
}
let body = When {
cond_var: result_var,
expr_var: ret_var,
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
};
defn(
symbol,
vec![(result_var, Symbol::ARG_1)],
var_store,
body,
ret_var,
)
}
fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
let ret_var = var_store.fresh();
let func_var = var_store.fresh();

View file

@ -1,6 +1,6 @@
use crate::spaces::{fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
use bumpalo::collections::String;
use roc_parse::ast::{AssignedField, Expr, Tag, TypeAnnotation};
use roc_parse::ast::{AssignedField, Collection, Expr, Tag, TypeAnnotation};
use roc_region::all::Located;
/// Does an AST node need parens around it?
@ -183,17 +183,13 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
Apply(_, _, args) => args.iter().any(|loc_arg| loc_arg.value.is_multiline()),
As(lhs, _, rhs) => lhs.value.is_multiline() || rhs.value.is_multiline(),
Record {
fields,
ext,
final_comments: _,
} => {
Record { fields, ext } => {
match ext {
Some(ann) if ann.value.is_multiline() => return true,
_ => {}
}
fields.iter().any(|field| field.value.is_multiline())
fields.items.iter().any(|field| field.value.is_multiline())
}
TagUnion {
@ -296,16 +292,19 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
}
Record {
fields,
ext,
fields:
Collection {
items,
final_comments,
},
ext,
} => {
format_sequence!(
buf,
indent,
'{',
'}',
fields,
items,
final_comments,
newlines,
AssignedField

View file

@ -4,7 +4,7 @@ use code_builder::Align;
use roc_collections::all::MutMap;
use roc_module::symbol::Symbol;
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
use roc_mono::layout::{Layout, LayoutIds};
use roc_mono::layout::{Builtin, Layout, LayoutIds};
use crate::layout::WasmLayout;
use crate::low_level::{build_call_low_level, LowlevelBuildResult};
@ -294,16 +294,17 @@ impl<'a> WasmBackend<'a> {
_ => {
self.storage.load_symbols(&mut self.code_builder, &[*sym]);
self.code_builder.br(self.block_depth); // jump to end of function (for stack frame pop)
}
}
// jump to the "stack frame pop" code at the end of the function
self.code_builder.br(self.block_depth - 1);
Ok(())
}
Stmt::Switch {
cond_symbol,
cond_layout: _,
cond_layout,
branches,
default_branch,
ret_layout: _,
@ -321,21 +322,45 @@ impl<'a> WasmBackend<'a> {
cond_storage,
);
// create (number_of_branches - 1) new blocks.
// create a block for each branch except the default
for _ in 0..branches.len() {
self.start_block(BlockType::NoResult)
}
let is_bool = matches!(cond_layout, Layout::Builtin(Builtin::Int1));
let cond_type = WasmLayout::new(cond_layout).value_type();
// then, we jump whenever the value under scrutiny is equal to the value of a branch
for (i, (value, _, _)) in branches.iter().enumerate() {
// put the cond_symbol on the top of the stack
self.storage
.load_symbols(&mut self.code_builder, &[*cond_symbol]);
if is_bool {
// We already have a bool, don't need to compare against a const to get one
if *value == 0 {
self.code_builder.i32_eqz();
}
} else {
match cond_type {
ValueType::I32 => {
self.code_builder.i32_const(*value as i32);
// compare the 2 topmost values
self.code_builder.i32_eq();
}
ValueType::I64 => {
self.code_builder.i64_const(*value as i64);
self.code_builder.i64_eq();
}
ValueType::F32 => {
self.code_builder.f32_const(f32::from_bits(*value as u32));
self.code_builder.f32_eq();
}
ValueType::F64 => {
self.code_builder.f64_const(f64::from_bits(*value as u64));
self.code_builder.f64_eq();
}
}
}
// "break" out of `i` surrounding blocks
self.code_builder.br_if(i as u32);

View file

@ -202,10 +202,10 @@ impl<'a> CodeBuilder<'a> {
true
}
fn add_insertion(&mut self, insert_at: usize, opcode: u8, immediate: u32) {
fn add_insertion(&mut self, insert_at: usize, opcode: OpCode, immediate: u32) {
let start = self.insert_bytes.len();
self.insert_bytes.push(opcode);
self.insert_bytes.push(opcode as u8);
self.insert_bytes.encode_u32(immediate);
self.insertions.push(Insertion {
@ -213,6 +213,8 @@ impl<'a> CodeBuilder<'a> {
start,
end: self.insert_bytes.len(),
});
// println!("insert {:?} {} at byte offset {} ", opcode, immediate, insert_at);
}
/// Load a Symbol that is stored in the VM stack
@ -244,14 +246,14 @@ impl<'a> CodeBuilder<'a> {
// Symbol is not on top of the stack. Find it.
if let Some(found_index) = self.vm_stack.iter().rposition(|&s| s == symbol) {
// Insert a local.set where the value was created
self.add_insertion(pushed_at, SETLOCAL as u8, next_local_id.0);
self.add_insertion(pushed_at, SETLOCAL, next_local_id.0);
// Take the value out of the stack where local.set was inserted
self.vm_stack.remove(found_index);
// Insert a local.get at the current position
self.get_local(next_local_id);
self.vm_stack.push(symbol);
self.set_top_symbol(symbol);
// This Symbol is no longer stored in the VM stack, but in a local
None
@ -267,11 +269,11 @@ impl<'a> CodeBuilder<'a> {
Popped { pushed_at } => {
// This Symbol is being used for a second time
// Insert a local.tee where it was pushed, so we don't interfere with the first usage
self.add_insertion(pushed_at, TEELOCAL as u8, next_local_id.0);
self.add_insertion(pushed_at, TEELOCAL, next_local_id.0);
// Insert a local.get at the current position
self.get_local(next_local_id);
self.vm_stack.push(symbol);
self.set_top_symbol(symbol);
// This symbol has been promoted to a Local
// Tell the caller it no longer has a VirtualMachineSymbolState
@ -437,10 +439,12 @@ impl<'a> CodeBuilder<'a> {
let new_len = self.vm_stack.len() - pops as usize;
self.vm_stack.truncate(new_len);
if push {
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
self.vm_stack.push(Symbol::WASM_TMP);
}
self.code.push(opcode as u8);
// println!("{:10}\t{:?}", format!("{:?}", opcode), &self.vm_stack);
}
fn inst_imm8(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u8) {
@ -516,25 +520,14 @@ impl<'a> CodeBuilder<'a> {
n_args: usize,
has_return_val: bool,
) {
let stack_depth = self.vm_stack.len();
if n_args > stack_depth {
panic!(
"Trying to call to call function {:?} with {:?} values but only {:?} on the VM stack\n{:?}",
function_index, n_args, stack_depth, self
);
}
self.vm_stack.truncate(stack_depth - n_args);
if has_return_val {
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
}
self.code.push(CALL as u8);
self.inst(CALL, n_args, has_return_val);
// Write the index of the function to be called.
// Also make a RelocationEntry so the linker can see that this byte offset relates to a function by name.
// Here we initialise the offset to an index of self.code. After completing the function, we'll add
// other factors to make it relative to the code section. (All insertions will be known then.)
let offset = self.code.len() as u32;
self.code.encode_padded_u32(function_index);
// Make a RelocationEntry so the linker can see that this byte offset relates to a function by name.
// Here we initialise the offset to an index of self.code. After completing the function, we'll add
// other factors to make it relative to the code section. (All insertions will be known then.)
self.relocations.push(RelocationEntry::Index {
type_id: IndexRelocType::FunctionIndexLeb,
offset,

View file

@ -1,5 +1,5 @@
#[repr(u8)]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum OpCode {
UNREACHABLE = 0x00,
NOP = 0x01,

View file

@ -293,16 +293,12 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
Apply { name, parts }
}
ast::TypeAnnotation::Record {
fields,
ext,
final_comments: _,
} => {
ast::TypeAnnotation::Record { fields, ext } => {
let mut doc_fields = Vec::new();
let mut any_fields_include_private_tags = false;
for field in fields {
for field in fields.items {
match record_field_to_doc(in_func_type_ann, field.value) {
None => {
any_fields_include_private_tags = true;

View file

@ -2630,7 +2630,7 @@ fn parse_header<'a>(
std::str::from_utf8_unchecked(&src_bytes[..chomped])
};
let packages = header.packages.into_bump_slice();
let packages = header.packages.items;
let info = HeaderInfo {
loc_name: Located {

View file

@ -875,8 +875,8 @@ define_builtins! {
// used by the dev backend to store the pointer to where to store large return types
23 RET_POINTER: "#ret_pointer"
// used in wasm dev backend to mark values in the VM stack that have no other Symbol
24 WASM_ANONYMOUS_STACK_VALUE: "#wasm_anonymous_stack_value"
// used in wasm dev backend to mark temporary values in the VM stack
24 WASM_TMP: "#wasm_tmp"
}
1 NUM: "Num" => {
0 NUM_NUM: "Num" imported // the Num.Num type alias
@ -1082,6 +1082,7 @@ define_builtins! {
2 RESULT_MAP_ERR: "mapErr"
3 RESULT_WITH_DEFAULT: "withDefault"
4 RESULT_AFTER: "after"
5 RESULT_IS_OK: "isOk"
}
6 DICT: "Dict" => {
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias

View file

@ -5,6 +5,28 @@ use bumpalo::Bump;
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Collection<'a, T> {
pub items: &'a [T],
pub final_comments: &'a [CommentOrNewline<'a>],
}
impl<'a, T> Collection<'a, T> {
pub fn empty() -> Collection<'a, T> {
Collection {
items: &[],
final_comments: &[],
}
}
pub fn with_items(items: &'a [T]) -> Collection<'a, T> {
Collection {
items,
final_comments: &[],
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Module<'a> {
Interface { header: InterfaceHeader<'a> },
@ -231,11 +253,11 @@ pub enum TypeAnnotation<'a> {
),
Record {
fields: &'a [Loc<AssignedField<'a, TypeAnnotation<'a>>>],
fields: Collection<'a, Loc<AssignedField<'a, TypeAnnotation<'a>>>>,
/// The row type variable in an open record, e.g. the `r` in `{ name: Str }r`.
/// This is None if it's a closed record annotation like `{ name: Str }`.
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
final_comments: &'a [CommentOrNewline<'a>],
// final_comments: &'a [CommentOrNewline<'a>],
},
/// A tag union, e.g. `[

View file

@ -4,8 +4,8 @@ use crate::ident::{lowercase_ident, parse_ident, Ident};
use crate::keyword;
use crate::parser::{
self, backtrackable, optional, sep_by1, sep_by1_e, specialize, specialize_ref, then,
trailing_sep_by0, word1, word2, EExpr, EInParens, ELambda, EPattern, ERecord, EString, Either,
Expect, If, List, Number, ParseResult, Parser, State, Type, When,
trailing_sep_by0, word1, word2, EExpect, EExpr, EIf, EInParens, ELambda, EList, ENumber,
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser, State,
};
use crate::pattern::loc_closure_param;
use crate::type_annotation;
@ -801,7 +801,7 @@ fn parse_defs_end<'a>(
Err((NoProgress, _, _)) => {
let start = state.get_position();
match crate::parser::keyword_e(crate::keyword::EXPECT, Expect::Expect)
match crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect)
.parse(arena, state)
{
Err((_, _, _)) => {
@ -861,8 +861,8 @@ fn parse_defs_end<'a>(
space0_before_e(
type_annotation::located_help(min_indent + 1),
min_indent + 1,
Type::TSpace,
Type::TIndentStart,
EType::TSpace,
EType::TIndentStart,
),
)
.parse(arena, state)?;
@ -1099,8 +1099,8 @@ fn parse_expr_operator<'a>(
space0_before_e(
type_annotation::located_help(indented_more),
min_indent,
Type::TSpace,
Type::TIndentStart,
EType::TSpace,
EType::TIndentStart,
),
)
.parse(arena, state)?;
@ -1126,8 +1126,8 @@ fn parse_expr_operator<'a>(
space0_before_e(
type_annotation::located_help(indented_more),
min_indent,
Type::TSpace,
Type::TIndentStart,
EType::TSpace,
EType::TIndentStart,
),
);
@ -1651,21 +1651,21 @@ mod when {
pub fn expr_help<'a>(
min_indent: u16,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, When<'a>> {
) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
then(
and!(
when_with_indent(),
skip_second!(
space0_around_ee(
specialize_ref(When::Condition, move |arena, state| {
specialize_ref(EWhen::Condition, move |arena, state| {
parse_loc_expr_with_options(min_indent, options, arena, state)
}),
min_indent,
When::Space,
When::IndentCondition,
When::IndentIs,
EWhen::Space,
EWhen::IndentCondition,
EWhen::IndentIs,
),
parser::keyword_e(keyword::IS, When::Is)
parser::keyword_e(keyword::IS, EWhen::Is)
)
),
move |arena, state, progress, (case_indent, loc_condition)| {
@ -1673,7 +1673,7 @@ mod when {
return Err((
progress,
// TODO maybe pass case_indent here?
When::PatternAlignment(5, state.line, state.column),
EWhen::PatternAlignment(5, state.line, state.column),
state,
));
}
@ -1693,9 +1693,9 @@ mod when {
}
/// Parsing when with indentation.
fn when_with_indent<'a>() -> impl Parser<'a, u16, When<'a>> {
fn when_with_indent<'a>() -> impl Parser<'a, u16, EWhen<'a>> {
move |arena, state: State<'a>| {
parser::keyword_e(keyword::WHEN, When::When)
parser::keyword_e(keyword::WHEN, EWhen::When)
.parse(arena, state)
.map(|(progress, (), state)| (progress, state.indent_col, state))
}
@ -1704,7 +1704,7 @@ mod when {
fn branches<'a>(
min_indent: u16,
options: ExprParseOptions,
) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, EWhen<'a>> {
move |arena, state: State<'a>| {
let when_indent = state.indent_col;
@ -1741,7 +1741,7 @@ mod when {
let indent = pattern_indent_level - indent_col;
Err((
MadeProgress,
When::PatternAlignment(indent, state.line, state.column),
EWhen::PatternAlignment(indent, state.line, state.column),
state,
))
}
@ -1799,7 +1799,7 @@ mod when {
(Col, Vec<'a, Located<Pattern<'a>>>),
Option<Located<Expr<'a>>>,
),
When<'a>,
EWhen<'a>,
> {
let options = ExprParseOptions {
check_for_arrow: false,
@ -1810,16 +1810,16 @@ mod when {
one_of![
map!(
skip_first!(
parser::keyword_e(keyword::IF, When::IfToken),
parser::keyword_e(keyword::IF, EWhen::IfToken),
// TODO we should require space before the expression but not after
space0_around_ee(
specialize_ref(When::IfGuard, move |arena, state| {
specialize_ref(EWhen::IfGuard, move |arena, state| {
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
}),
min_indent,
When::Space,
When::IndentIfGuard,
When::IndentArrow,
EWhen::Space,
EWhen::IndentIfGuard,
EWhen::IndentArrow,
)
),
Some
@ -1831,17 +1831,17 @@ mod when {
fn branch_single_alternative<'a>(
min_indent: u16,
) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
) -> impl Parser<'a, Located<Pattern<'a>>, EWhen<'a>> {
move |arena, state| {
let (_, spaces, state) =
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
backtrackable(space0_e(min_indent, EWhen::Space, EWhen::IndentPattern))
.parse(arena, state)?;
let (_, loc_pattern, state) = space0_after_e(
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
specialize(EWhen::Pattern, crate::pattern::loc_pattern_help(min_indent)),
min_indent,
When::Space,
When::IndentPattern,
EWhen::Space,
EWhen::IndentPattern,
)
.parse(arena, state)?;
@ -1862,12 +1862,12 @@ mod when {
fn branch_alternatives_help<'a>(
min_indent: u16,
pattern_indent_level: Option<u16>,
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), When<'a>> {
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), EWhen<'a>> {
move |arena, state: State<'a>| {
let initial = state;
// put no restrictions on the indent after the spaces; we'll check it manually
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
match space0_e(0, EWhen::Space, EWhen::IndentPattern).parse(arena, state) {
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
Ok((_progress, spaces, state)) => {
@ -1876,7 +1876,7 @@ mod when {
// this branch is indented too much
Err((
NoProgress,
When::IndentPattern(state.line, state.column),
EWhen::IndentPattern(state.line, state.column),
initial,
))
}
@ -1884,7 +1884,7 @@ mod when {
let indent = wanted - state.column;
Err((
NoProgress,
When::PatternAlignment(indent, state.line, state.column),
EWhen::PatternAlignment(indent, state.line, state.column),
initial,
))
}
@ -1896,7 +1896,7 @@ mod when {
let pattern_indent_col = state.column;
let parser = sep_by1(
word1(b'|', When::Bar),
word1(b'|', EWhen::Bar),
branch_single_alternative(pattern_indent + 1),
);
@ -1930,16 +1930,16 @@ mod when {
}
/// Parsing the righthandside of a branch in a when conditional.
fn branch_result<'a>(indent: u16) -> impl Parser<'a, Located<Expr<'a>>, When<'a>> {
fn branch_result<'a>(indent: u16) -> impl Parser<'a, Located<Expr<'a>>, EWhen<'a>> {
skip_first!(
word2(b'-', b'>', When::Arrow),
word2(b'-', b'>', EWhen::Arrow),
space0_before_e(
specialize_ref(When::Branch, move |arena, state| parse_loc_expr(
specialize_ref(EWhen::Branch, move |arena, state| parse_loc_expr(
indent, arena, state
)),
indent,
When::Space,
When::IndentBranch,
EWhen::Space,
EWhen::IndentBranch,
)
)
}
@ -1947,38 +1947,38 @@ mod when {
fn if_branch<'a>(
min_indent: u16,
) -> impl Parser<'a, (Located<Expr<'a>>, Located<Expr<'a>>), If<'a>> {
) -> impl Parser<'a, (Located<Expr<'a>>, Located<Expr<'a>>), EIf<'a>> {
move |arena, state| {
// NOTE: only parse spaces before the expression
let (_, cond, state) = space0_around_ee(
specialize_ref(If::Condition, move |arena, state| {
specialize_ref(EIf::Condition, move |arena, state| {
parse_loc_expr(min_indent, arena, state)
}),
min_indent,
If::Space,
If::IndentCondition,
If::IndentThenToken,
EIf::Space,
EIf::IndentCondition,
EIf::IndentThenToken,
)
.parse(arena, state)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
let (_, _, state) = parser::keyword_e(keyword::THEN, If::Then)
let (_, _, state) = parser::keyword_e(keyword::THEN, EIf::Then)
.parse(arena, state)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
let (_, then_branch, state) = space0_around_ee(
specialize_ref(If::ThenBranch, move |arena, state| {
specialize_ref(EIf::ThenBranch, move |arena, state| {
parse_loc_expr(min_indent, arena, state)
}),
min_indent,
If::Space,
If::IndentThenBranch,
If::IndentElseToken,
EIf::Space,
EIf::IndentThenBranch,
EIf::IndentElseToken,
)
.parse(arena, state)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
let (_, _, state) = parser::keyword_e(keyword::ELSE, If::Else)
let (_, _, state) = parser::keyword_e(keyword::ELSE, EIf::Else)
.parse(arena, state)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
@ -1989,26 +1989,26 @@ fn if_branch<'a>(
fn expect_help<'a>(
min_indent: u16,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, Expect<'a>> {
) -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
move |arena: &'a Bump, state: State<'a>| {
let start = state.get_position();
let (_, _, state) =
parser::keyword_e(keyword::EXPECT, Expect::Expect).parse(arena, state)?;
parser::keyword_e(keyword::EXPECT, EExpect::Expect).parse(arena, state)?;
let (_, condition, state) = space0_before_e(
specialize_ref(Expect::Condition, move |arena, state| {
specialize_ref(EExpect::Condition, move |arena, state| {
parse_loc_expr_with_options(start.col + 1, options, arena, state)
}),
start.col + 1,
Expect::Space,
Expect::IndentCondition,
EExpect::Space,
EExpect::IndentCondition,
)
.parse(arena, state)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
let parse_cont = specialize_ref(
Expect::Continuation,
EExpect::Continuation,
space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s),
min_indent,
@ -2028,9 +2028,9 @@ fn expect_help<'a>(
fn if_expr_help<'a>(
min_indent: u16,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, If<'a>> {
) -> impl Parser<'a, Expr<'a>, EIf<'a>> {
move |arena: &'a Bump, state| {
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
let (_, _, state) = parser::keyword_e(keyword::IF, EIf::If).parse(arena, state)?;
let mut branches = Vec::with_capacity_in(1, arena);
@ -2044,8 +2044,8 @@ fn if_expr_help<'a>(
// try to parse another `if`
// NOTE this drops spaces between the `else` and the `if`
let optional_if = and!(
backtrackable(space0_e(min_indent, If::Space, If::IndentIf)),
parser::keyword_e(keyword::IF, If::If)
backtrackable(space0_e(min_indent, EIf::Space, EIf::IndentIf)),
parser::keyword_e(keyword::IF, EIf::If)
);
match optional_if.parse(arena, state) {
@ -2058,12 +2058,12 @@ fn if_expr_help<'a>(
};
let (_, else_branch, state) = space0_before_e(
specialize_ref(If::ElseBranch, move |arena, state| {
specialize_ref(EIf::ElseBranch, move |arena, state| {
parse_loc_expr_with_options(min_indent, options, arena, state)
}),
min_indent,
If::Space,
If::IndentElseBranch,
EIf::Space,
EIf::IndentElseBranch,
)
.parse(arena, state_final_else)
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
@ -2147,32 +2147,33 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
}
}
fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, List<'a>> {
fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EList<'a>> {
move |arena, state| {
let (_, (parsed_elems, final_comments), state) = collection_trailing_sep_e!(
word1(b'[', List::Open),
specialize_ref(List::Expr, move |a, s| parse_loc_expr_no_multi_backpassing(
min_indent, a, s
)),
word1(b',', List::End),
word1(b']', List::End),
let (_, elements, state) = collection_trailing_sep_e!(
word1(b'[', EList::Open),
specialize_ref(
EList::Expr,
move |a, s| parse_loc_expr_no_multi_backpassing(min_indent, a, s)
),
word1(b',', EList::End),
word1(b']', EList::End),
min_indent,
List::Open,
List::Space,
List::IndentEnd,
EList::Open,
EList::Space,
EList::IndentEnd,
Expr::SpaceBefore
)
.parse(arena, state)?;
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
let mut allocated = Vec::with_capacity_in(elements.items.len(), arena);
for parsed_elem in parsed_elems {
allocated.push(&*arena.alloc(parsed_elem));
for parsed_elem in elements.items {
allocated.push(parsed_elem);
}
let expr = Expr::List {
items: allocated.into_bump_slice(),
final_comments,
final_comments: elements.final_comments,
};
Ok((MadeProgress, expr, state))
@ -2341,7 +2342,7 @@ fn string_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map!(crate::string_literal::parse(), Expr::Str)
}
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(
crate::number_literal::positive_number_literal(),
|literal| {
@ -2364,7 +2365,7 @@ fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
)
}
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;

View file

@ -1,4 +1,4 @@
use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
use crate::ast::{Collection, CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
use crate::blankspace::space0_e;
use crate::ident::lowercase_ident;
use crate::parser::Progress::{self, *};
@ -81,7 +81,7 @@ pub enum To<'a> {
#[derive(Clone, Debug, PartialEq)]
pub struct AppHeader<'a> {
pub name: Loc<StrLiteral<'a>>,
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub to: Loc<To<'a>>,
@ -146,7 +146,7 @@ pub struct PlatformHeader<'a> {
pub name: Loc<PackageName<'a>>,
pub requires: PlatformRequires<'a>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub effects: Effects<'a>,

View file

@ -1,4 +1,4 @@
use crate::ast::{CommentOrNewline, Def, Module};
use crate::ast::{Collection, CommentOrNewline, Def, Module};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{
package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
@ -203,7 +203,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let (_, provides, state) =
specialize(EHeader::Provides, provides_to()).parse(arena, state)?;
let (before_packages, after_packages, package_entries) = match opt_pkgs {
let (before_packages, after_packages, packages) = match opt_pkgs {
Some(pkgs) => {
let pkgs: Packages<'a> = pkgs; // rustc must be told the type here
@ -213,7 +213,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
pkgs.entries,
)
}
None => (&[] as _, &[] as _, Vec::new_in(arena)),
None => (&[] as _, &[] as _, Collection::empty()),
};
// rustc must be told the type here
@ -229,7 +229,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let header = AppHeader {
name,
packages: package_entries,
packages,
imports,
provides: provides.entries,
to: provides.to,
@ -582,11 +582,9 @@ where
#[derive(Debug)]
struct Packages<'a> {
entries: Vec<'a, Located<PackageEntry<'a>>>,
entries: Collection<'a, Located<PackageEntry<'a>>>,
before_packages_keyword: &'a [CommentOrNewline<'a>],
after_packages_keyword: &'a [CommentOrNewline<'a>],
final_comments: &'a [CommentOrNewline<'a>],
}
#[inline(always)]
@ -615,12 +613,14 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
PackageEntry::SpaceBefore
)
),
|((before_packages_keyword, after_packages_keyword), (entries, final_comments))| {
|((before_packages_keyword, after_packages_keyword), entries): (
(_, _),
Collection<'a, _>
)| {
Packages {
entries,
before_packages_keyword,
after_packages_keyword,
final_comments,
}
}
)

View file

@ -1,5 +1,5 @@
use crate::ast::Base;
use crate::parser::{Number, ParseResult, Parser, Progress, State};
use crate::parser::{ENumber, ParseResult, Parser, Progress, State};
pub enum NumLiteral<'a> {
Float(&'a str),
@ -11,7 +11,7 @@ pub enum NumLiteral<'a> {
},
}
pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
move |_arena, state: State<'a>| {
match state.bytes.get(0) {
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
@ -19,13 +19,13 @@ pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number>
}
_ => {
// this is not a number at all
Err((Progress::NoProgress, Number::End, state))
Err((Progress::NoProgress, ENumber::End, state))
}
}
}
}
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
move |_arena, state: State<'a>| {
match state.bytes.get(0) {
Some(first_byte) if *first_byte == b'-' => {
@ -37,7 +37,7 @@ pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
}
_ => {
// this is not a number at all
Err((Progress::NoProgress, Number::End, state))
Err((Progress::NoProgress, ENumber::End, state))
}
}
}
@ -47,7 +47,7 @@ fn parse_number_base<'a>(
is_negated: bool,
bytes: &'a [u8],
state: State<'a>,
) -> ParseResult<'a, NumLiteral<'a>, Number> {
) -> ParseResult<'a, NumLiteral<'a>, ENumber> {
match bytes.get(0..2) {
Some(b"0b") => chomp_number_base(Base::Binary, is_negated, &bytes[2..], state),
Some(b"0o") => chomp_number_base(Base::Octal, is_negated, &bytes[2..], state),
@ -61,13 +61,13 @@ fn chomp_number_base<'a>(
is_negative: bool,
bytes: &'a [u8],
state: State<'a>,
) -> ParseResult<'a, NumLiteral<'a>, Number> {
) -> ParseResult<'a, NumLiteral<'a>, ENumber> {
let (_is_float, chomped) = chomp_number(bytes);
let string = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) };
let new = state.advance_without_indenting_ee(chomped + 2 + is_negative as usize, |_, _| {
Number::LineTooLong
ENumber::LineTooLong
})?;
Ok((
@ -85,24 +85,25 @@ fn chomp_number_dec<'a>(
is_negative: bool,
bytes: &'a [u8],
state: State<'a>,
) -> ParseResult<'a, NumLiteral<'a>, Number> {
) -> ParseResult<'a, NumLiteral<'a>, ENumber> {
let (is_float, chomped) = chomp_number(bytes);
if is_negative && chomped == 0 {
// we're probably actually looking at unary negation here
return Err((Progress::NoProgress, Number::End, state));
return Err((Progress::NoProgress, ENumber::End, state));
}
if !bytes.get(0).copied().unwrap_or_default().is_ascii_digit() {
// we're probably actually looking at unary negation here
return Err((Progress::NoProgress, Number::End, state));
return Err((Progress::NoProgress, ENumber::End, state));
}
let string =
unsafe { std::str::from_utf8_unchecked(&state.bytes[0..chomped + is_negative as usize]) };
let new = state
.advance_without_indenting_ee(chomped + is_negative as usize, |_, _| Number::LineTooLong)?;
let new = state.advance_without_indenting_ee(chomped + is_negative as usize, |_, _| {
ENumber::LineTooLong
})?;
Ok((
Progress::MadeProgress,

View file

@ -186,7 +186,7 @@ pub enum SyntaxError<'a> {
ArgumentsBeforeEquals(Region),
NotYetImplemented(String),
Todo,
Type(Type<'a>),
Type(EType<'a>),
Pattern(EPattern<'a>),
Expr(EExpr<'a>),
Header(EHeader<'a>),
@ -258,7 +258,7 @@ pub enum ETypedIdent<'a> {
HasType(Row, Col),
IndentHasType(Row, Col),
Name(Row, Col),
Type(Type<'a>, Row, Col),
Type(EType<'a>, Row, Col),
IndentType(Row, Col),
Identifier(Row, Col),
}
@ -394,7 +394,7 @@ pub enum EExpr<'a> {
DefMissingFinalExpr(Row, Col),
DefMissingFinalExpr2(&'a EExpr<'a>, Row, Col),
Type(Type<'a>, Row, Col),
Type(EType<'a>, Row, Col),
Pattern(&'a EPattern<'a>, Row, Col),
IndentDefBody(Row, Col),
IndentEquals(Row, Col),
@ -409,10 +409,10 @@ pub enum EExpr<'a> {
BackpassComma(Row, Col),
BackpassArrow(Row, Col),
When(When<'a>, Row, Col),
If(If<'a>, Row, Col),
When(EWhen<'a>, Row, Col),
If(EIf<'a>, Row, Col),
Expect(Expect<'a>, Row, Col),
Expect(EExpect<'a>, Row, Col),
Lambda(ELambda<'a>, Row, Col),
Underscore(Row, Col),
@ -420,15 +420,15 @@ pub enum EExpr<'a> {
InParens(EInParens<'a>, Row, Col),
Record(ERecord<'a>, Row, Col),
Str(EString<'a>, Row, Col),
Number(Number, Row, Col),
List(List<'a>, Row, Col),
Number(ENumber, Row, Col),
List(EList<'a>, Row, Col),
IndentStart(Row, Col),
IndentEnd(Row, Col),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Number {
pub enum ENumber {
End,
LineTooLong,
}
@ -502,7 +502,7 @@ pub enum ELambda<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum List<'a> {
pub enum EList<'a> {
Open(Row, Col),
End(Row, Col),
Space(BadInputError, Row, Col),
@ -514,7 +514,7 @@ pub enum List<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum When<'a> {
pub enum EWhen<'a> {
Space(BadInputError, Row, Col),
When(Row, Col),
Is(Row, Col),
@ -538,7 +538,7 @@ pub enum When<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum If<'a> {
pub enum EIf<'a> {
Space(BadInputError, Row, Col),
If(Row, Col),
Then(Row, Col),
@ -557,7 +557,7 @@ pub enum If<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expect<'a> {
pub enum EExpect<'a> {
Space(BadInputError, Row, Col),
Expect(Row, Col),
Condition(&'a EExpr<'a>, Row, Col),
@ -575,7 +575,7 @@ pub enum EPattern<'a> {
Space(BadInputError, Row, Col),
PInParens(PInParens<'a>, Row, Col),
NumLiteral(Number, Row, Col),
NumLiteral(ENumber, Row, Col),
IndentStart(Row, Col),
IndentEnd(Row, Col),
@ -614,11 +614,11 @@ pub enum PInParens<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type<'a> {
TRecord(TRecord<'a>, Row, Col),
TTagUnion(TTagUnion<'a>, Row, Col),
TInParens(TInParens<'a>, Row, Col),
TApply(TApply, Row, Col),
pub enum EType<'a> {
TRecord(ETypeRecord<'a>, Row, Col),
TTagUnion(ETypeTagUnion<'a>, Row, Col),
TInParens(ETypeInParens<'a>, Row, Col),
TApply(ETypeApply, Row, Col),
TBadTypeVariable(Row, Col),
TWildcard(Row, Col),
///
@ -633,14 +633,14 @@ pub enum Type<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TRecord<'a> {
pub enum ETypeRecord<'a> {
End(Row, Col),
Open(Row, Col),
Field(Row, Col),
Colon(Row, Col),
Optional(Row, Col),
Type(&'a Type<'a>, Row, Col),
Type(&'a EType<'a>, Row, Col),
Space(BadInputError, Row, Col),
@ -651,11 +651,11 @@ pub enum TRecord<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TTagUnion<'a> {
pub enum ETypeTagUnion<'a> {
End(Row, Col),
Open(Row, Col),
Type(&'a Type<'a>, Row, Col),
Type(&'a EType<'a>, Row, Col),
Space(BadInputError, Row, Col),
@ -664,11 +664,11 @@ pub enum TTagUnion<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TInParens<'a> {
pub enum ETypeInParens<'a> {
End(Row, Col),
Open(Row, Col),
///
Type(&'a Type<'a>, Row, Col),
Type(&'a EType<'a>, Row, Col),
///
Space(BadInputError, Row, Col),
@ -678,7 +678,7 @@ pub enum TInParens<'a> {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TApply {
pub enum ETypeApply {
///
StartNotUppercase(Row, Col),
End(Row, Col),
@ -1300,7 +1300,12 @@ macro_rules! collection_trailing_sep_e {
}
}
Ok((MadeProgress, (parsed_elems, final_comments), state))
let collection = $crate::ast::Collection {
items: parsed_elems.into_bump_slice(),
final_comments,
};
Ok((MadeProgress, collection, state))
}
)
};

View file

@ -316,7 +316,7 @@ fn lowercase_ident_pattern<'a>(
#[inline(always)]
fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
move |arena, state| {
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
let (_, fields, state) = collection_trailing_sep_e!(
// word1_check_indent!(b'{', PRecord::Open, min_indent, PRecord::IndentOpen),
word1(b'{', PRecord::Open),
record_pattern_field(min_indent),
@ -332,9 +332,9 @@ fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRec
.parse(arena, state)?;
// TODO
let _unused = final_comments;
let _unused = fields.final_comments;
let result = Pattern::RecordDestructure(fields.into_bump_slice());
let result = Pattern::RecordDestructure(fields.items);
Ok((MadeProgress, result, state))
}

View file

@ -1,63 +1,67 @@
use crate::ast::{AssignedField, Tag, TypeAnnotation};
use crate::ast::{AssignedField, Collection, Tag, TypeAnnotation};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::keyword;
use crate::parser::{
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, ParseResult,
Parser,
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, EType,
ETypeApply, ETypeInParens, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
Progress::{self, *},
State, TApply, TInParens, TRecord, TTagUnion, Type,
State,
};
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
use roc_region::all::{Located, Region};
pub fn located_help<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
pub fn located_help<'a>(
min_indent: u16,
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
expression(min_indent)
}
#[inline(always)]
fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TTagUnion<'a>> {
fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> {
move |arena, state| {
let (_, (tags, final_comments), state) = collection_trailing_sep_e!(
word1(b'[', TTagUnion::Open),
let (_, tags, state) = collection_trailing_sep_e!(
word1(b'[', ETypeTagUnion::Open),
loc!(tag_type(min_indent)),
word1(b',', TTagUnion::End),
word1(b']', TTagUnion::End),
word1(b',', ETypeTagUnion::End),
word1(b']', ETypeTagUnion::End),
min_indent,
TTagUnion::Open,
TTagUnion::Space,
TTagUnion::IndentEnd,
ETypeTagUnion::Open,
ETypeTagUnion::Space,
ETypeTagUnion::IndentEnd,
Tag::SpaceBefore
)
.parse(arena, state)?;
// This could be an open tag union, e.g. `[ Foo, Bar ]a`
let (_, ext, state) =
optional(allocated(specialize_ref(TTagUnion::Type, term(min_indent))))
let (_, ext, state) = optional(allocated(specialize_ref(
ETypeTagUnion::Type,
term(min_indent),
)))
.parse(arena, state)?;
let result = TypeAnnotation::TagUnion {
tags: tags.into_bump_slice(),
tags: tags.items,
ext,
final_comments,
final_comments: tags.final_comments,
};
Ok((MadeProgress, result, state))
}
}
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, Type<'a>> {
|_arena, state: State<'a>| Err((NoProgress, Type::TStart(state.line, state.column), state))
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.line, state.column), state))
}
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
one_of!(
loc_wildcard(),
specialize(Type::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(Type::TRecord, record_type(min_indent))),
loc!(specialize(Type::TTagUnion, tag_union_type(min_indent))),
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(EType::TRecord, record_type(min_indent))),
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
loc!(applied_type(min_indent)),
loc!(parse_type_variable),
fail_type_start(),
@ -67,14 +71,14 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Typ
map!(
and!(
skip_second!(
backtrackable(space0_e(min_indent, Type::TSpace, Type::TIndentEnd)),
crate::parser::keyword_e(keyword::AS, Type::TEnd)
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentEnd)),
crate::parser::keyword_e(keyword::AS, EType::TEnd)
),
space0_before_e(
term(min_indent),
min_indent,
Type::TSpace,
Type::TAsIndentStart
EType::TSpace,
EType::TAsIndentStart
)
),
Some
@ -103,24 +107,26 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Typ
}
/// The `*` type variable, e.g. in (List *) Wildcard,
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
map!(loc!(word1(b'*', Type::TWildcard)), |loc_val: Located<()>| {
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
map!(loc!(word1(b'*', EType::TWildcard)), |loc_val: Located<
(),
>| {
loc_val.map(|_| TypeAnnotation::Wildcard)
})
}
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
use crate::ast::Spaceable;
map_with_arena!(
and!(
backtrackable(space0_e(min_indent, Type::TSpace, Type::TIndentStart)),
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentStart)),
one_of!(
loc_wildcard(),
specialize(Type::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(Type::TRecord, record_type(min_indent))),
loc!(specialize(Type::TTagUnion, tag_union_type(min_indent))),
loc!(specialize(Type::TApply, parse_concrete_type)),
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(EType::TRecord, record_type(min_indent))),
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
loc!(specialize(EType::TApply, parse_concrete_type)),
loc!(parse_type_variable)
)
),
@ -137,28 +143,28 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
fn loc_type_in_parens<'a>(
min_indent: u16,
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETypeInParens<'a>> {
between!(
word1(b'(', TInParens::Open),
word1(b'(', ETypeInParens::Open),
space0_around_ee(
move |arena, state| specialize_ref(TInParens::Type, expression(min_indent))
move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent))
.parse(arena, state),
min_indent,
TInParens::Space,
TInParens::IndentOpen,
TInParens::IndentEnd,
ETypeInParens::Space,
ETypeInParens::IndentOpen,
ETypeInParens::IndentEnd,
),
word1(b')', TInParens::IndentEnd)
word1(b')', ETypeInParens::IndentEnd)
)
}
#[inline(always)]
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, TTagUnion<'a>> {
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>| {
let (_, name, state) = loc!(parse_tag_name(TTagUnion::End)).parse(arena, state)?;
let (_, name, state) = loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state)?;
let (_, args, state) =
specialize_ref(TTagUnion::Type, loc_applied_args_e(min_indent)).parse(arena, state)?;
let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent))
.parse(arena, state)?;
let result = if name.value.starts_with('@') {
Tag::Private {
@ -190,7 +196,7 @@ where
fn record_type_field<'a>(
min_indent: u16,
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, TRecord<'a>> {
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, ETypeRecord<'a>> {
use crate::ident::lowercase_ident;
use crate::parser::Either::*;
use AssignedField::*;
@ -201,29 +207,33 @@ fn record_type_field<'a>(
let row = state.line;
let col = state.column;
let (progress, loc_label, state) = loc!(specialize(
move |_, _, _| TRecord::Field(row, col),
move |_, _, _| ETypeRecord::Field(row, col),
lowercase_ident()
))
.parse(arena, state)?;
debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) =
space0_e(min_indent, TRecord::Space, TRecord::IndentEnd).parse(arena, state)?;
space0_e(min_indent, ETypeRecord::Space, ETypeRecord::IndentEnd).parse(arena, state)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
word1(b':', TRecord::Colon),
word1(b'?', TRecord::Optional)
word1(b':', ETypeRecord::Colon),
word1(b'?', ETypeRecord::Optional)
))
.parse(arena, state)?;
let val_parser = specialize_ref(TRecord::Type, term(min_indent));
let val_parser = specialize_ref(ETypeRecord::Type, term(min_indent));
match opt_loc_val {
Some(First(_)) => {
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, TRecord::Space, TRecord::IndentColon)
let (_, loc_val, state) = space0_before_e(
val_parser,
min_indent,
ETypeRecord::Space,
ETypeRecord::IndentColon,
)
.parse(arena, state)?;
Ok((
@ -236,8 +246,8 @@ fn record_type_field<'a>(
let (_, loc_val, state) = space0_before_e(
val_parser,
min_indent,
TRecord::Space,
TRecord::IndentOptional,
ETypeRecord::Space,
ETypeRecord::IndentOptional,
)
.parse(arena, state)?;
@ -263,42 +273,44 @@ fn record_type_field<'a>(
}
#[inline(always)]
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TRecord<'a>> {
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
use crate::type_annotation::TypeAnnotation::*;
move |arena, state| {
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
let (_, fields, state) = collection_trailing_sep_e!(
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
word1(b'{', TRecord::Open),
word1(b'{', ETypeRecord::Open),
loc!(record_type_field(min_indent)),
word1(b',', TRecord::End),
word1(b',', ETypeRecord::End),
// word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd),
word1(b'}', TRecord::End),
word1(b'}', ETypeRecord::End),
min_indent,
TRecord::Open,
TRecord::Space,
TRecord::IndentEnd,
ETypeRecord::Open,
ETypeRecord::Space,
ETypeRecord::IndentEnd,
AssignedField::SpaceBefore
)
.parse(arena, state)?;
let field_term = specialize_ref(TRecord::Type, term(min_indent));
let field_term = specialize_ref(ETypeRecord::Type, term(min_indent));
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
let result = Record {
fields: fields.into_bump_slice(),
fields: Collection {
items: fields.items,
final_comments: fields.final_comments,
},
ext,
final_comments,
};
Ok((MadeProgress, result, state))
}
}
fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, Type<'a>> {
fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
and!(
specialize(Type::TApply, parse_concrete_type),
specialize(EType::TApply, parse_concrete_type),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`
loc_applied_args_e(min_indent)
@ -322,33 +334,33 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, Type
fn loc_applied_args_e<'a>(
min_indent: u16,
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, Type<'a>> {
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, EType<'a>> {
zero_or_more!(loc_applied_arg(min_indent))
}
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
move |arena, state: State<'a>| {
let (p1, first, state) = space0_before_e(
term(min_indent),
min_indent,
Type::TSpace,
Type::TIndentStart,
EType::TSpace,
EType::TIndentStart,
)
.parse(arena, state)?;
let (p2, rest, state) = zero_or_more!(skip_first!(
word1(b',', Type::TFunctionArgument),
word1(b',', EType::TFunctionArgument),
one_of![
space0_around_ee(
term(min_indent),
min_indent,
Type::TSpace,
Type::TIndentStart,
Type::TIndentEnd
EType::TSpace,
EType::TIndentStart,
EType::TIndentEnd
),
|_, state: State<'a>| Err((
NoProgress,
Type::TFunctionArgument(state.line, state.column),
EType::TFunctionArgument(state.line, state.column),
state
))
]
@ -358,8 +370,8 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
// TODO this space0 is dropped, so newlines just before the function arrow when there
// is only one argument are not seen by the formatter. Can we do better?
let (p3, is_function, state) = optional(skip_first!(
space0_e(min_indent, Type::TSpace, Type::TIndentStart),
word2(b'-', b'>', Type::TStart)
space0_e(min_indent, EType::TSpace, EType::TIndentStart),
word2(b'-', b'>', EType::TStart)
))
.parse(arena, state)?;
@ -367,8 +379,8 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
let (p4, return_type, state) = space0_before_e(
term(min_indent),
min_indent,
Type::TSpace,
Type::TIndentStart,
EType::TSpace,
EType::TIndentStart,
)
.parse(arena, state)?;
@ -416,7 +428,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
fn parse_concrete_type<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, TypeAnnotation<'a>, TApply> {
) -> ParseResult<'a, TypeAnnotation<'a>, ETypeApply> {
let initial_bytes = state.bytes;
match crate::ident::concrete_type().parse(arena, state) {
@ -426,7 +438,7 @@ fn parse_concrete_type<'a>(
Ok((MadeProgress, answer, state))
}
Err((NoProgress, _, state)) => {
Err((NoProgress, TApply::End(state.line, state.column), state))
Err((NoProgress, ETypeApply::End(state.line, state.column), state))
}
Err((MadeProgress, _, mut state)) => {
// we made some progress, but ultimately failed.
@ -437,7 +449,7 @@ fn parse_concrete_type<'a>(
unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) };
state = state.advance_without_indenting_ee(chomped, |r, c| {
TApply::Space(crate::parser::BadInputError::LineTooLong, r, c)
ETypeApply::Space(crate::parser::BadInputError::LineTooLong, r, c)
})?;
Ok((MadeProgress, TypeAnnotation::Malformed(parsed_str), state))
@ -448,7 +460,7 @@ fn parse_concrete_type<'a>(
fn parse_type_variable<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, TypeAnnotation<'a>, Type<'a>> {
) -> ParseResult<'a, TypeAnnotation<'a>, EType<'a>> {
match crate::ident::lowercase_ident().parse(arena, state) {
Ok((_, name, state)) => {
let answer = TypeAnnotation::BoundVariable(name);
@ -457,7 +469,7 @@ fn parse_type_variable<'a>(
}
Err((progress, _, state)) => Err((
progress,
Type::TBadTypeVariable(state.line, state.column),
EType::TBadTypeVariable(state.line, state.column),
state,
)),
}

View file

@ -23,7 +23,9 @@ mod test_parse {
use roc_parse::ast::Pattern::{self, *};
use roc_parse::ast::StrLiteral::{self, *};
use roc_parse::ast::StrSegment::*;
use roc_parse::ast::{self, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch};
use roc_parse::ast::{
self, Collection, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch,
};
use roc_parse::header::{
AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To,
@ -2281,9 +2283,8 @@ mod test_parse {
6,
TypeAnnotation::SpaceBefore(
&TypeAnnotation::Record {
fields: &[],
fields: Collection::empty(),
ext: None,
final_comments: &[],
},
&[Newline],
),
@ -2320,9 +2321,8 @@ mod test_parse {
6,
TypeAnnotation::SpaceBefore(
&TypeAnnotation::Record {
fields: &[],
fields: Collection::empty(),
ext: None,
final_comments: &[],
},
&[LineComment(" comment")],
),
@ -3091,7 +3091,7 @@ mod test_parse {
#[test]
fn empty_app_header() {
let arena = Bump::new();
let packages = Vec::new_in(&arena);
let packages = Collection::empty();
let imports = Vec::new_in(&arena);
let provides = Vec::new_in(&arena);
let module_name = StrLiteral::PlainLine("test-app");
@ -3131,7 +3131,7 @@ mod test_parse {
use PackageOrPath::Path;
let arena = Bump::new();
let packages = Vec::new_in(&arena);
let packages = Collection::empty();
let imports = Vec::new_in(&arena);
let provides = Vec::new_in(&arena);
let module_name = StrLiteral::PlainLine("test-app");
@ -3180,7 +3180,7 @@ mod test_parse {
};
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
let arena = Bump::new();
let packages = bumpalo::vec![in &arena; loc_pkg_entry];
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena));
let loc_import = Located::new(2, 2, 14, 25, import);
let imports = bumpalo::vec![in &arena; loc_import];
@ -3236,7 +3236,7 @@ mod test_parse {
};
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
let arena = Bump::new();
let packages = bumpalo::vec![in &arena; loc_pkg_entry];
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena));
let loc_import = Located::new(2, 2, 14, 25, import);
let imports = bumpalo::vec![in &arena; loc_import];
@ -3309,9 +3309,8 @@ mod test_parse {
ann: Located::at(
region2,
TypeAnnotation::Record {
fields: &[],
fields: Collection::empty(),
ext: None,
final_comments: &[],
},
),
},
@ -3324,7 +3323,7 @@ mod test_parse {
name: Located::new(0, 0, 9, 23, pkg_name),
requires,
exposes: Vec::new_in(&arena),
packages: Vec::new_in(&arena),
packages: Collection::empty(),
imports: Vec::new_in(&arena),
provides: Vec::new_in(&arena),
effects,
@ -3367,7 +3366,7 @@ mod test_parse {
};
let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry);
let arena = Bump::new();
let packages = bumpalo::vec![in &arena; loc_pkg_entry];
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
let imports = Vec::new_in(&arena);
let provide_entry = Located::new(5, 5, 15, 26, Exposed("mainForHost"));
let provides = bumpalo::vec![in &arena; provide_entry];
@ -3395,9 +3394,8 @@ mod test_parse {
ann: Located::at(
region2,
TypeAnnotation::Record {
fields: &[],
fields: Collection::empty(),
ext: None,
final_comments: &[],
},
),
},

View file

@ -1019,16 +1019,16 @@ fn to_list_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
context: Context,
parse_problem: &roc_parse::parser::List<'a>,
parse_problem: &roc_parse::parser::EList<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::List;
use roc_parse::parser::EList;
match *parse_problem {
List::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
EList::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
List::Expr(expr, row, col) => to_expr_report(
EList::Expr(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::ListElement, start_row, start_col, Box::new(context)),
@ -1037,7 +1037,7 @@ fn to_list_report<'a>(
col,
),
List::Open(row, col) | List::End(row, col) => {
EList::Open(row, col) | EList::End(row, col) => {
match what_is_next(alloc.src_lines, row, col) {
Next::Other(Some(',')) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
@ -1098,7 +1098,7 @@ fn to_list_report<'a>(
}
}
List::IndentOpen(row, col) | List::IndentEnd(row, col) => {
EList::IndentOpen(row, col) | EList::IndentEnd(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -1130,16 +1130,16 @@ fn to_if_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
context: Context,
parse_problem: &roc_parse::parser::If<'a>,
parse_problem: &roc_parse::parser::EIf<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::If;
use roc_parse::parser::EIf;
match *parse_problem {
If::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
EIf::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
If::Condition(expr, row, col) => to_expr_report(
EIf::Condition(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::IfCondition, start_row, start_col, Box::new(context)),
@ -1148,7 +1148,7 @@ fn to_if_report<'a>(
col,
),
If::ThenBranch(expr, row, col) => to_expr_report(
EIf::ThenBranch(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::IfThenBranch, start_row, start_col, Box::new(context)),
@ -1157,7 +1157,7 @@ fn to_if_report<'a>(
col,
),
If::ElseBranch(expr, row, col) => to_expr_report(
EIf::ElseBranch(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::IfElseBranch, start_row, start_col, Box::new(context)),
@ -1166,10 +1166,10 @@ fn to_if_report<'a>(
col,
),
If::If(_row, _col) => unreachable!("another branch would be taken"),
If::IndentIf(_row, _col) => unreachable!("another branch would be taken"),
EIf::If(_row, _col) => unreachable!("another branch would be taken"),
EIf::IndentIf(_row, _col) => unreachable!("another branch would be taken"),
If::Then(row, col) | If::IndentThenBranch(row, col) | If::IndentThenToken(row, col) => {
EIf::Then(row, col) | EIf::IndentThenBranch(row, col) | EIf::IndentThenToken(row, col) => {
to_unfinished_if_report(
alloc,
filename,
@ -1185,7 +1185,7 @@ fn to_if_report<'a>(
)
}
If::Else(row, col) | If::IndentElseBranch(row, col) | If::IndentElseToken(row, col) => {
EIf::Else(row, col) | EIf::IndentElseBranch(row, col) | EIf::IndentElseToken(row, col) => {
to_unfinished_if_report(
alloc,
filename,
@ -1201,7 +1201,7 @@ fn to_if_report<'a>(
)
}
If::IndentCondition(row, col) => to_unfinished_if_report(
EIf::IndentCondition(row, col) => to_unfinished_if_report(
alloc,
filename,
row,
@ -1249,14 +1249,14 @@ fn to_when_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
context: Context,
parse_problem: &roc_parse::parser::When<'a>,
parse_problem: &roc_parse::parser::EWhen<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::When;
use roc_parse::parser::EWhen;
match *parse_problem {
When::IfGuard(nested, row, col) => match what_is_next(alloc.src_lines, row, col) {
EWhen::IfGuard(nested, row, col) => match what_is_next(alloc.src_lines, row, col) {
Next::Token("->") => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -1287,7 +1287,7 @@ fn to_when_report<'a>(
col,
),
},
When::Arrow(row, col) => {
EWhen::Arrow(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -1310,9 +1310,9 @@ fn to_when_report<'a>(
}
}
When::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
EWhen::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
When::Branch(expr, row, col) => to_expr_report(
EWhen::Branch(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::WhenBranch, start_row, start_col, Box::new(context)),
@ -1321,7 +1321,7 @@ fn to_when_report<'a>(
col,
),
When::Condition(expr, row, col) => to_expr_report(
EWhen::Condition(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::WhenCondition, start_row, start_col, Box::new(context)),
@ -1330,7 +1330,7 @@ fn to_when_report<'a>(
col,
),
When::Bar(row, col) => to_unfinished_when_report(
EWhen::Bar(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1344,10 +1344,10 @@ fn to_when_report<'a>(
]),
),
When::IfToken(_row, _col) => unreachable!("the if-token is optional"),
When::When(_row, _col) => unreachable!("another branch would be taken"),
EWhen::IfToken(_row, _col) => unreachable!("the if-token is optional"),
EWhen::When(_row, _col) => unreachable!("another branch would be taken"),
When::Is(row, col) | When::IndentIs(row, col) => to_unfinished_when_report(
EWhen::Is(row, col) | EWhen::IndentIs(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1361,7 +1361,7 @@ fn to_when_report<'a>(
]),
),
When::IndentCondition(row, col) => to_unfinished_when_report(
EWhen::IndentCondition(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1373,7 +1373,7 @@ fn to_when_report<'a>(
]),
),
When::IndentPattern(row, col) => to_unfinished_when_report(
EWhen::IndentPattern(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1383,7 +1383,7 @@ fn to_when_report<'a>(
alloc.concat(vec![alloc.reflow(r"I was expecting to see a pattern next")]),
),
When::IndentArrow(row, col) => to_unfinished_when_report(
EWhen::IndentArrow(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1397,7 +1397,7 @@ fn to_when_report<'a>(
]),
),
When::IndentIfGuard(row, col) => to_unfinished_when_report(
EWhen::IndentIfGuard(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1411,7 +1411,7 @@ fn to_when_report<'a>(
]),
),
When::IndentBranch(row, col) => to_unfinished_when_report(
EWhen::IndentBranch(row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1424,7 +1424,7 @@ fn to_when_report<'a>(
]),
),
When::PatternAlignment(indent, row, col) => to_unfinished_when_report(
EWhen::PatternAlignment(indent, row, col) => to_unfinished_when_report(
alloc,
filename,
row,
@ -1437,7 +1437,7 @@ fn to_when_report<'a>(
alloc.reflow(" spaces)"),
]),
),
When::Pattern(ref pat, row, col) => to_pattern_report(alloc, filename, pat, row, col),
EWhen::Pattern(ref pat, row, col) => to_pattern_report(alloc, filename, pat, row, col),
}
}
@ -2000,23 +2000,23 @@ fn to_pattern_in_parens_report<'a>(
fn to_type_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
parse_problem: &roc_parse::parser::Type<'a>,
parse_problem: &roc_parse::parser::EType<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::Type;
use roc_parse::parser::EType;
match parse_problem {
Type::TRecord(record, row, col) => to_trecord_report(alloc, filename, record, *row, *col),
Type::TTagUnion(tag_union, row, col) => {
EType::TRecord(record, row, col) => to_trecord_report(alloc, filename, record, *row, *col),
EType::TTagUnion(tag_union, row, col) => {
to_ttag_union_report(alloc, filename, tag_union, *row, *col)
}
Type::TInParens(tinparens, row, col) => {
EType::TInParens(tinparens, row, col) => {
to_tinparens_report(alloc, filename, tinparens, *row, *col)
}
Type::TApply(tapply, row, col) => to_tapply_report(alloc, filename, tapply, *row, *col),
EType::TApply(tapply, row, col) => to_tapply_report(alloc, filename, tapply, *row, *col),
Type::TFunctionArgument(row, col) => match what_is_next(alloc.src_lines, *row, *col) {
EType::TFunctionArgument(row, col) => match what_is_next(alloc.src_lines, *row, *col) {
Next::Other(Some(',')) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2037,7 +2037,7 @@ fn to_type_report<'a>(
_ => todo!(),
},
Type::TStart(row, col) => {
EType::TStart(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2061,7 +2061,7 @@ fn to_type_report<'a>(
}
}
Type::TIndentStart(row, col) => {
EType::TIndentStart(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2079,7 +2079,7 @@ fn to_type_report<'a>(
}
}
Type::TIndentEnd(row, col) => {
EType::TIndentEnd(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2097,7 +2097,7 @@ fn to_type_report<'a>(
}
}
Type::TAsIndentStart(row, col) => {
EType::TAsIndentStart(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2115,7 +2115,7 @@ fn to_type_report<'a>(
}
}
Type::TBadTypeVariable(row, col) => {
EType::TBadTypeVariable(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -2139,14 +2139,14 @@ fn to_type_report<'a>(
fn to_trecord_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
parse_problem: &roc_parse::parser::TRecord<'a>,
parse_problem: &roc_parse::parser::ETypeRecord<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::TRecord;
use roc_parse::parser::ETypeRecord;
match *parse_problem {
TRecord::Open(row, col) => match what_is_next(alloc.src_lines, row, col) {
ETypeRecord::Open(row, col) => match what_is_next(alloc.src_lines, row, col) {
Next::Keyword(keyword) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = to_keyword_region(row, col, keyword);
@ -2191,7 +2191,7 @@ fn to_trecord_report<'a>(
}
},
TRecord::End(row, col) => {
ETypeRecord::End(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2237,7 +2237,7 @@ fn to_trecord_report<'a>(
}
}
TRecord::Field(row, col) => match what_is_next(alloc.src_lines, row, col) {
ETypeRecord::Field(row, col) => match what_is_next(alloc.src_lines, row, col) {
Next::Keyword(keyword) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = to_keyword_region(row, col, keyword);
@ -2286,16 +2286,16 @@ fn to_trecord_report<'a>(
}
},
TRecord::Colon(_, _) => {
ETypeRecord::Colon(_, _) => {
unreachable!("because `{ foo }` is a valid field; the colon is not required")
}
TRecord::Optional(_, _) => {
ETypeRecord::Optional(_, _) => {
unreachable!("because `{ foo }` is a valid field; the question mark is not required")
}
TRecord::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
ETypeRecord::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
TRecord::IndentOpen(row, col) => {
ETypeRecord::IndentOpen(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2318,7 +2318,7 @@ fn to_trecord_report<'a>(
}
}
TRecord::IndentEnd(row, col) => {
ETypeRecord::IndentEnd(row, col) => {
match next_line_starts_with_close_curly(alloc.src_lines, row) {
Some((curly_row, curly_col)) => {
let surroundings =
@ -2370,29 +2370,29 @@ fn to_trecord_report<'a>(
}
}
TRecord::IndentColon(_, _) => {
ETypeRecord::IndentColon(_, _) => {
unreachable!("because `{ foo }` is a valid field; the colon is not required")
}
TRecord::IndentOptional(_, _) => {
ETypeRecord::IndentOptional(_, _) => {
unreachable!("because `{ foo }` is a valid field; the question mark is not required")
}
TRecord::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
ETypeRecord::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
}
}
fn to_ttag_union_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
parse_problem: &roc_parse::parser::TTagUnion<'a>,
parse_problem: &roc_parse::parser::ETypeTagUnion<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::TTagUnion;
use roc_parse::parser::ETypeTagUnion;
match *parse_problem {
TTagUnion::Open(row, col) => match what_is_next(alloc.src_lines, row, col) {
ETypeTagUnion::Open(row, col) => match what_is_next(alloc.src_lines, row, col) {
Next::Keyword(keyword) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = to_keyword_region(row, col, keyword);
@ -2459,7 +2459,7 @@ fn to_ttag_union_report<'a>(
}
},
TTagUnion::End(row, col) => {
ETypeTagUnion::End(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2523,9 +2523,9 @@ fn to_ttag_union_report<'a>(
}
}
TTagUnion::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
ETypeTagUnion::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
TTagUnion::IndentOpen(row, col) => {
ETypeTagUnion::IndentOpen(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2548,7 +2548,7 @@ fn to_ttag_union_report<'a>(
}
}
TTagUnion::IndentEnd(row, col) => {
ETypeTagUnion::IndentEnd(row, col) => {
match next_line_starts_with_close_square_bracket(alloc.src_lines, row) {
Some((curly_row, curly_col)) => {
let surroundings =
@ -2600,21 +2600,21 @@ fn to_ttag_union_report<'a>(
}
}
TTagUnion::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
ETypeTagUnion::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
}
}
fn to_tinparens_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
parse_problem: &roc_parse::parser::TInParens<'a>,
parse_problem: &roc_parse::parser::ETypeInParens<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::TInParens;
use roc_parse::parser::ETypeInParens;
match *parse_problem {
TInParens::Open(row, col) => {
ETypeInParens::Open(row, col) => {
match what_is_next(alloc.src_lines, row, col) {
Next::Keyword(keyword) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
@ -2686,7 +2686,7 @@ fn to_tinparens_report<'a>(
}
}
TInParens::End(row, col) => {
ETypeInParens::End(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2734,9 +2734,9 @@ fn to_tinparens_report<'a>(
}
}
TInParens::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
ETypeInParens::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
TInParens::IndentOpen(row, col) => {
ETypeInParens::IndentOpen(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -2760,7 +2760,7 @@ fn to_tinparens_report<'a>(
}
}
TInParens::IndentEnd(row, col) => {
ETypeInParens::IndentEnd(row, col) => {
match next_line_starts_with_close_parenthesis(alloc.src_lines, row) {
Some((curly_row, curly_col)) => {
let surroundings =
@ -2812,21 +2812,21 @@ fn to_tinparens_report<'a>(
}
}
TInParens::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
ETypeInParens::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
}
}
fn to_tapply_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
parse_problem: &roc_parse::parser::TApply,
parse_problem: &roc_parse::parser::ETypeApply,
_start_row: Row,
_start_col: Col,
) -> Report<'a> {
use roc_parse::parser::TApply;
use roc_parse::parser::ETypeApply;
match *parse_problem {
TApply::DoubleDot(row, col) => {
ETypeApply::DoubleDot(row, col) => {
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
@ -2842,7 +2842,7 @@ fn to_tapply_report<'a>(
severity: Severity::RuntimeError,
}
}
TApply::TrailingDot(row, col) => {
ETypeApply::TrailingDot(row, col) => {
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
@ -2864,7 +2864,7 @@ fn to_tapply_report<'a>(
severity: Severity::RuntimeError,
}
}
TApply::StartIsNumber(row, col) => {
ETypeApply::StartIsNumber(row, col) => {
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
@ -2886,7 +2886,7 @@ fn to_tapply_report<'a>(
severity: Severity::RuntimeError,
}
}
TApply::StartNotUppercase(row, col) => {
ETypeApply::StartNotUppercase(row, col) => {
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
@ -2909,7 +2909,7 @@ fn to_tapply_report<'a>(
}
}
TApply::End(row, col) => {
ETypeApply::End(row, col) => {
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
@ -2927,7 +2927,7 @@ fn to_tapply_report<'a>(
}
}
TApply::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
ETypeApply::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
}
}

View file

@ -1,19 +1,16 @@
#![cfg(not(feature = "gen-wasm"))]
#[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to;
#[cfg(feature = "gen-dev")]
use crate::helpers::dev::assert_evals_to;
// #[cfg(feature = "gen-wasm")]
// use crate::helpers::wasm::assert_evals_to;
#[cfg(feature = "gen-wasm")]
use crate::helpers::wasm::assert_evals_to;
// use crate::assert_wasm_evals_to as assert_evals_to;
use indoc::indoc;
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn eq_i64() {
assert_evals_to!(
indoc!(
@ -30,7 +27,7 @@ fn eq_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn neq_i64() {
assert_evals_to!(
indoc!(
@ -47,7 +44,7 @@ fn neq_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn eq_u64() {
assert_evals_to!(
indoc!(
@ -64,7 +61,7 @@ fn eq_u64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn neq_u64() {
assert_evals_to!(
indoc!(
@ -81,7 +78,7 @@ fn neq_u64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn eq_f64() {
assert_evals_to!(
indoc!(
@ -98,7 +95,7 @@ fn eq_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn neq_f64() {
assert_evals_to!(
indoc!(
@ -115,7 +112,7 @@ fn neq_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn eq_bool_tag() {
assert_evals_to!(
indoc!(
@ -132,7 +129,7 @@ fn eq_bool_tag() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn neq_bool_tag() {
assert_evals_to!(
indoc!(
@ -163,7 +160,7 @@ fn unit() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn newtype() {
assert_evals_to!("Identity 42 == Identity 42", true, bool);
assert_evals_to!("Identity 42 != Identity 42", false, bool);

View file

@ -571,7 +571,7 @@ fn abs_min_int_overflow() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn gen_if_fn() {
assert_evals_to!(
indoc!(

View file

@ -85,7 +85,7 @@ fn branch_third_float() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_first_int() {
assert_evals_to!(
indoc!(
@ -101,7 +101,7 @@ fn branch_first_int() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_second_int() {
assert_evals_to!(
indoc!(
@ -134,7 +134,7 @@ fn branch_third_int() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_store_variable() {
assert_evals_to!(
indoc!(
@ -221,7 +221,7 @@ fn gen_when_one_branch() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_large_when_int() {
assert_evals_to!(
indoc!(
@ -243,31 +243,31 @@ fn gen_large_when_int() {
);
}
// #[test]
// #[cfg(any(feature = "gen-llvm"))]
// fn gen_large_when_float() {
// assert_evals_to!(
// indoc!(
// r#"
// foo = \num ->
// when num is
// 0.5 -> 200.1
// -3.6 -> 111.2 # TODO adding more negative numbers reproduces parsing bugs here
// 3.6 -> 789.5
// 1.7 -> 123.3
// 2.8 -> 456.4
// _ -> 1000.6
#[test]
#[cfg(any(feature = "gen-wasm"))]
fn gen_large_when_float() {
assert_evals_to!(
indoc!(
r#"
foo = \num ->
when num is
0.5 -> 200.1
-3.6 -> 111.2 # TODO adding more negative numbers reproduces parsing bugs here
3.6 -> 789.5
1.7 -> 123.3
2.8 -> 456.4
_ -> 1000.6
// foo -3.6
// "#
// ),
// 111.2,
// f64
// );
// }
foo -3.6
"#
),
111.2,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn or_pattern() {
assert_evals_to!(
indoc!(
@ -337,7 +337,7 @@ fn return_unnamed_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_when_fn() {
assert_evals_to!(
indoc!(
@ -504,7 +504,7 @@ fn gen_multiple_defs() {
// }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn factorial() {
assert_evals_to!(
indoc!(

View file

@ -159,3 +159,33 @@ fn err_empty_tag_union() {
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn is_ok() {
assert_evals_to!(
indoc!(
r#"
result : Result I64 {}
result = Ok 2
Result.isOk result
"#
),
true,
bool
);
assert_evals_to!(
indoc!(
r#"
result : Result I64 {}
result = Err {}
Result.isOk result
"#
),
false,
bool
);
}

View file

@ -127,7 +127,7 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
let src_hash = hash_state.finish();
// Filename contains a hash of the Roc test source code. Helpful when comparing across commits.
let dir = "/tmp/roc/compiler/gen_wasm/output";
let dir = "/tmp/roc/gen_wasm";
std::fs::create_dir_all(dir).unwrap();
let path = format!("{}/test-{:016x}.wasm", dir, src_hash);

View file

@ -10,6 +10,7 @@ pub mod gen_list;
pub mod gen_num;
pub mod gen_primitives;
pub mod gen_records;
pub mod gen_result;
pub mod gen_set;
pub mod gen_str;
pub mod gen_tags;