Merge pull request #3132 from rtfeldman/parse-def-soa

This commit is contained in:
Richard Feldman 2022-05-28 01:55:01 -04:00 committed by GitHub
commit ea644c3360
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1577 additions and 816 deletions

View file

@ -8,6 +8,90 @@ use crate::lang::{core::expr::expr_to_expr2::loc_expr_to_expr2, env::Env, scope:
use super::def2::Def2; use super::def2::Def2;
fn spaces_to_comments(spaces: &[CommentOrNewline]) -> Option<String> {
if !spaces.is_empty() && !all_newlines(spaces) {
let mut all_comments_str = String::new();
for comment in spaces.iter().filter(|c_or_nl| !c_or_nl.is_newline()) {
all_comments_str.push_str(&comment.to_string_repr());
}
Some(all_comments_str)
} else {
None
}
}
pub fn toplevel_defs_to_defs2<'a>(
arena: &'a Bump,
env: &mut Env<'a>,
scope: &mut Scope,
parsed_defs: roc_parse::ast::Defs<'a>,
region: Region,
) -> Vec<Def2> {
let mut result = Vec::with_capacity(parsed_defs.tags.len());
for (index, def) in parsed_defs.defs().enumerate() {
let mut def = match def {
Err(roc_parse::ast::ValueDef::Body(&loc_pattern, &loc_expr)) => {
let expr2 = loc_expr_to_expr2(arena, loc_expr, env, scope, region).0;
let expr_id = env.pool.add(expr2);
use roc_parse::ast::Pattern::*;
match loc_pattern.value {
Identifier(id_str) => {
let identifier_id =
env.ident_ids.get_or_insert(&Ident(IdentStr::from(id_str)));
// TODO support with annotation
Def2::ValueDef {
identifier_id,
expr_id,
}
}
other => {
unimplemented!(
"I don't yet know how to convert the pattern {:?} into an expr2",
other
)
}
}
}
other => {
unimplemented!(
"I don't know how to make an expr2 from this def yet: {:?}",
other
)
}
};
let spaces_before = &parsed_defs.spaces[parsed_defs.space_before[index].indices()];
let spaces_after = &parsed_defs.spaces[parsed_defs.space_after[index].indices()];
if let Some(comments) = spaces_to_comments(spaces_before) {
let inner_def_id = env.pool.add(def);
def = Def2::CommentsBefore {
comments,
def_id: inner_def_id,
};
}
if let Some(comments) = spaces_to_comments(spaces_after) {
let inner_def_id = env.pool.add(def);
def = Def2::CommentsAfter {
comments,
def_id: inner_def_id,
};
}
result.push(def)
}
result
}
pub fn defs_to_defs2<'a>( pub fn defs_to_defs2<'a>(
arena: &'a Bump, arena: &'a Bump,
env: &mut Env<'a>, env: &mut Env<'a>,

View file

@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use crate::FormatMode; use crate::FormatMode;
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::{internal_error, user_error}; use roc_error_macros::{internal_error, user_error};
use roc_fmt::def::fmt_def; use roc_fmt::def::fmt_toplevel_defs;
use roc_fmt::module::fmt_module; use roc_fmt::module::fmt_module;
use roc_fmt::spaces::RemoveSpaces; use roc_fmt::spaces::RemoveSpaces;
use roc_fmt::{Ast, Buf}; use roc_fmt::{Ast, Buf};
@ -71,7 +71,7 @@ pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), Str
user_error!("Unexpected parse failure when parsing this formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, e) user_error!("Unexpected parse failure when parsing this formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, e)
})); }));
let mut buf = Buf::new_in(&arena); let mut buf = Buf::new_in(&arena);
fmt_all(&arena, &mut buf, ast); fmt_all(&mut buf, ast);
let reparsed_ast = arena.alloc(parse_all(&arena, buf.as_str()).unwrap_or_else(|e| { let reparsed_ast = arena.alloc(parse_all(&arena, buf.as_str()).unwrap_or_else(|e| {
let mut fail_file = file.clone(); let mut fail_file = file.clone();
@ -118,7 +118,7 @@ pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), Str
// Now verify that the resultant formatting is _stable_ - i.e. that it doesn't change again if re-formatted // Now verify that the resultant formatting is _stable_ - i.e. that it doesn't change again if re-formatted
let mut reformatted_buf = Buf::new_in(&arena); let mut reformatted_buf = Buf::new_in(&arena);
fmt_all(&arena, &mut reformatted_buf, reparsed_ast); fmt_all(&mut reformatted_buf, reparsed_ast);
if buf.as_str() != reformatted_buf.as_str() { if buf.as_str() != reformatted_buf.as_str() {
let mut unstable_1_file = file.clone(); let mut unstable_1_file = file.clone();
unstable_1_file.set_extension("roc-format-unstable-1"); unstable_1_file.set_extension("roc-format-unstable-1");
@ -163,11 +163,10 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result<Ast<'a>, SyntaxError<'
Ok(Ast { module, defs }) Ok(Ast { module, defs })
} }
fn fmt_all<'a>(arena: &'a Bump, buf: &mut Buf<'a>, ast: &'a Ast) { fn fmt_all<'a>(buf: &mut Buf<'a>, ast: &'a Ast) {
fmt_module(buf, &ast.module); fmt_module(buf, &ast.module);
for def in &ast.defs {
fmt_def(buf, arena.alloc(def.value), 0); fmt_toplevel_defs(buf, &ast.defs, 0);
}
buf.fmt_end_of_file(); buf.fmt_end_of_file();
} }

View file

@ -465,6 +465,197 @@ pub(crate) fn canonicalize_defs<'a>(
scope.register_debug_idents(); scope.register_debug_idents();
} }
let (aliases, symbols_introduced) = canonicalize_type_defs(
env,
&mut output,
var_store,
scope,
pending_type_defs,
pattern_type,
);
// Now that we have the scope completely assembled, and shadowing resolved,
// we're ready to canonicalize any body exprs.
canonicalize_value_defs(
env,
output,
var_store,
scope,
&value_defs,
pattern_type,
aliases,
symbols_introduced,
)
}
#[inline(always)]
pub(crate) fn canonicalize_toplevel_defs<'a>(
env: &mut Env<'a>,
mut output: Output,
var_store: &mut VarStore,
scope: &mut Scope,
loc_defs: &'a mut roc_parse::ast::Defs<'a>,
pattern_type: PatternType,
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
// Canonicalizing defs while detecting shadowing involves a multi-step process:
//
// 1. Go through each of the patterns.
// 2. For each identifier pattern, get the scope.symbol() for the ident. (That symbol will use the home module for its module.)
// 3. If that symbol is already in scope, then we're about to shadow it. Error!
// 4. Otherwise, add it to the scope immediately, so we can detect shadowing within the same
// pattern (e.g. (Foo a a) = ...)
// 5. Add this canonicalized pattern and its corresponding ast::Expr to pending_exprs.
// 5. Once every pattern has been processed and added to scope, go back and canonicalize the exprs from
// pending_exprs, this time building up a canonical def for each one.
//
// This way, whenever any expr is doing lookups, it knows everything that's in scope -
// even defs that appear after it in the source.
//
// This naturally handles recursion too, because a given expr which refers
// to itself won't be processed until after its def has been added to scope.
let mut pending_type_defs = Vec::with_capacity(loc_defs.type_defs.len());
let mut value_defs = Vec::with_capacity(loc_defs.value_defs.len());
for (index, either_index) in loc_defs.tags.iter().enumerate() {
match either_index.split() {
Ok(type_index) => {
let type_def = &loc_defs.type_defs[type_index.index()];
pending_type_defs.push(to_pending_type_def(env, type_def, scope, pattern_type));
}
Err(value_index) => {
let value_def = &loc_defs.value_defs[value_index.index()];
let region = loc_defs.regions[index];
value_defs.push(Loc::at(region, value_def));
}
}
}
if cfg!(debug_assertions) {
scope.register_debug_idents();
}
let (aliases, symbols_introduced) = canonicalize_type_defs(
env,
&mut output,
var_store,
scope,
pending_type_defs,
pattern_type,
);
// Now that we have the scope completely assembled, and shadowing resolved,
// we're ready to canonicalize any body exprs.
canonicalize_value_defs(
env,
output,
var_store,
scope,
&value_defs,
pattern_type,
aliases,
symbols_introduced,
)
}
#[allow(clippy::too_many_arguments)]
fn canonicalize_value_defs<'a>(
env: &mut Env<'a>,
mut output: Output,
var_store: &mut VarStore,
scope: &mut Scope,
value_defs: &[Loc<&'a roc_parse::ast::ValueDef<'a>>],
pattern_type: PatternType,
mut aliases: VecMap<Symbol, Alias>,
mut symbols_introduced: MutMap<Symbol, Region>,
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
// Canonicalize all the patterns, record shadowing problems, and store
// the ast::Expr values in pending_exprs for further canonicalization
// once we've finished assembling the entire scope.
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
for loc_def in value_defs {
let mut new_output = Output::default();
match to_pending_value_def(
env,
var_store,
loc_def.value,
scope,
&mut new_output,
pattern_type,
) {
None => { /* skip */ }
Some(pending_def) => {
// Record the ast::Expr for later. We'll do another pass through these
// once we have the entire scope assembled. If we were to canonicalize
// the exprs right now, they wouldn't have symbols in scope from defs
// that get would have gotten added later in the defs list!
pending_value_defs.push(pending_def);
output.union(new_output);
}
}
}
let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len());
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
for (s, r) in BindingsFromPattern::new(pending_def.loc_pattern()) {
// store the top-level defs, used to ensure that closures won't capture them
if let PatternType::TopLevelDef = pattern_type {
env.top_level_symbols.insert(s);
}
symbols_introduced.insert(s, r);
debug_assert_eq!(env.home, s.module_id());
debug_assert!(
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
"{:?}",
s
);
symbol_to_index.push((s.ident_id(), def_index as u32));
}
}
let capacity = pending_value_defs.len();
let mut defs = Vec::with_capacity(capacity);
let mut def_ordering = DefOrdering::from_symbol_to_id(env.home, symbol_to_index, capacity);
for (def_id, pending_def) in pending_value_defs.into_iter().enumerate() {
let temp_output = canonicalize_pending_value_def(
env,
pending_def,
output,
scope,
var_store,
pattern_type,
&mut aliases,
);
output = temp_output.output;
defs.push(Some(temp_output.def));
def_ordering.insert_symbol_references(def_id as u32, &temp_output.references)
}
let can_defs = CanDefs {
defs,
def_ordering,
aliases,
};
(can_defs, output, symbols_introduced)
}
fn canonicalize_type_defs<'a>(
env: &mut Env<'a>,
output: &mut Output,
var_store: &mut VarStore,
scope: &mut Scope,
pending_type_defs: Vec<PendingTypeDef<'a>>,
pattern_type: PatternType,
) -> (VecMap<Symbol, Alias>, MutMap<Symbol, Region>) {
enum TypeDef<'a> { enum TypeDef<'a> {
Alias( Alias(
Loc<Symbol>, Loc<Symbol>,
@ -547,7 +738,7 @@ pub(crate) fn canonicalize_defs<'a>(
TypeDef::Alias(name, vars, ann) => { TypeDef::Alias(name, vars, ann) => {
let alias = canonicalize_alias( let alias = canonicalize_alias(
env, env,
&mut output, output,
var_store, var_store,
scope, scope,
&pending_abilities_in_scope, &pending_abilities_in_scope,
@ -565,7 +756,7 @@ pub(crate) fn canonicalize_defs<'a>(
TypeDef::Opaque(name, vars, ann, derived) => { TypeDef::Opaque(name, vars, ann, derived) => {
let alias_and_derives = canonicalize_opaque( let alias_and_derives = canonicalize_opaque(
env, env,
&mut output, output,
var_store, var_store,
scope, scope,
&pending_abilities_in_scope, &pending_abilities_in_scope,
@ -590,7 +781,7 @@ pub(crate) fn canonicalize_defs<'a>(
// Now that we know the alias dependency graph, we can try to insert recursion variables // Now that we know the alias dependency graph, we can try to insert recursion variables
// where aliases are recursive tag unions, or detect illegal recursions. // where aliases are recursive tag unions, or detect illegal recursions.
let mut aliases = correct_mutual_recursive_type_alias(env, aliases, var_store); let aliases = correct_mutual_recursive_type_alias(env, aliases, var_store);
for (symbol, alias) in aliases.iter() { for (symbol, alias) in aliases.iter() {
scope.add_alias( scope.add_alias(
@ -605,7 +796,7 @@ pub(crate) fn canonicalize_defs<'a>(
// Resolve all pending abilities, to add them to scope. // Resolve all pending abilities, to add them to scope.
resolve_abilities( resolve_abilities(
env, env,
&mut output, output,
var_store, var_store,
scope, scope,
abilities, abilities,
@ -613,89 +804,7 @@ pub(crate) fn canonicalize_defs<'a>(
pattern_type, pattern_type,
); );
// Now that we have the scope completely assembled, and shadowing resolved, (aliases, symbols_introduced)
// we're ready to canonicalize any body exprs.
// Canonicalize all the patterns, record shadowing problems, and store
// the ast::Expr values in pending_exprs for further canonicalization
// once we've finished assembling the entire scope.
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
for loc_def in value_defs.into_iter() {
let mut new_output = Output::default();
match to_pending_value_def(
env,
var_store,
loc_def.value,
scope,
&mut new_output,
pattern_type,
) {
None => { /* skip */ }
Some(pending_def) => {
// Record the ast::Expr for later. We'll do another pass through these
// once we have the entire scope assembled. If we were to canonicalize
// the exprs right now, they wouldn't have symbols in scope from defs
// that get would have gotten added later in the defs list!
pending_value_defs.push(pending_def);
output.union(new_output);
}
}
}
let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len());
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
for (s, r) in BindingsFromPattern::new(pending_def.loc_pattern()) {
// store the top-level defs, used to ensure that closures won't capture them
if let PatternType::TopLevelDef = pattern_type {
env.top_level_symbols.insert(s);
}
symbols_introduced.insert(s, r);
debug_assert_eq!(env.home, s.module_id());
debug_assert!(
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
"{:?}",
s
);
symbol_to_index.push((s.ident_id(), def_index as u32));
}
}
let capacity = pending_value_defs.len();
let mut defs = Vec::with_capacity(capacity);
let mut def_ordering = DefOrdering::from_symbol_to_id(env.home, symbol_to_index, capacity);
for (def_id, pending_def) in pending_value_defs.into_iter().enumerate() {
let temp_output = canonicalize_pending_value_def(
env,
pending_def,
output,
scope,
var_store,
pattern_type,
&mut aliases,
);
output = temp_output.output;
defs.push(Some(temp_output.def));
def_ordering.insert_symbol_references(def_id as u32, &temp_output.references)
}
(
CanDefs {
defs,
def_ordering,
// The result needs a thread-safe `SendMap`
aliases,
},
output,
symbols_introduced,
)
} }
/// Resolve all pending abilities, to add them to scope. /// Resolve all pending abilities, to add them to scope.

View file

@ -1,10 +1,10 @@
use crate::abilities::AbilitiesStore; use crate::abilities::AbilitiesStore;
use crate::annotation::canonicalize_annotation; use crate::annotation::canonicalize_annotation;
use crate::def::{canonicalize_defs, sort_can_defs, Declaration, Def}; use crate::def::{canonicalize_toplevel_defs, sort_can_defs, Declaration, Def};
use crate::effect_module::HostedGeneratedFunctions; use crate::effect_module::HostedGeneratedFunctions;
use crate::env::Env; use crate::env::Env;
use crate::expr::{ClosureData, Expr, Output, PendingDerives}; use crate::expr::{ClosureData, Expr, Output, PendingDerives};
use crate::operator::desugar_def; use crate::operator::desugar_toplevel_defs;
use crate::pattern::Pattern; use crate::pattern::Pattern;
use crate::scope::Scope; use crate::scope::Scope;
use bumpalo::Bump; use bumpalo::Bump;
@ -12,7 +12,7 @@ use roc_collections::{MutMap, SendMap, VecSet};
use roc_module::ident::Ident; use roc_module::ident::Ident;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol};
use roc_parse::ast::{self, TypeAnnotation}; use roc_parse::ast::{Defs, TypeAnnotation};
use roc_parse::header::HeaderFor; use roc_parse::header::HeaderFor;
use roc_parse::pattern::PatternType; use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError}; use roc_problem::can::{Problem, RuntimeError};
@ -160,7 +160,7 @@ fn has_no_implementation(expr: &Expr) -> bool {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a>( pub fn canonicalize_module_defs<'a>(
arena: &'a Bump, arena: &'a Bump,
loc_defs: &'a [Loc<ast::Def<'a>>], loc_defs: &'a mut Defs<'a>,
header_for: &roc_parse::header::HeaderFor, header_for: &roc_parse::header::HeaderFor,
home: ModuleId, home: ModuleId,
module_ids: &'a ModuleIds, module_ids: &'a ModuleIds,
@ -198,15 +198,7 @@ pub fn canonicalize_module_defs<'a>(
// visited a BinOp node we'd recursively try to apply this to each of its nested // 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 // operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily. // rules multiple times unnecessarily.
let mut desugared = desugar_toplevel_defs(arena, loc_defs);
bumpalo::collections::Vec::with_capacity_in(loc_defs.len() + num_deps, arena);
for loc_def in loc_defs.iter() {
desugared.push(&*arena.alloc(Loc {
value: desugar_def(arena, &loc_def.value),
region: loc_def.region,
}));
}
let mut lookups = Vec::with_capacity(num_deps); let mut lookups = Vec::with_capacity(num_deps);
let mut rigid_variables = RigidVariables::default(); let mut rigid_variables = RigidVariables::default();
@ -282,12 +274,12 @@ pub fn canonicalize_module_defs<'a>(
} }
} }
let (defs, output, symbols_introduced) = canonicalize_defs( let (defs, output, symbols_introduced) = canonicalize_toplevel_defs(
&mut env, &mut env,
Output::default(), Output::default(),
var_store, var_store,
&mut scope, &mut scope,
&desugared, loc_defs,
PatternType::TopLevelDef, PatternType::TopLevelDef,
); );

View file

@ -135,6 +135,12 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
} }
} }
pub fn desugar_toplevel_defs<'a>(arena: &'a Bump, defs: &mut roc_parse::ast::Defs<'a>) {
for value_def in defs.value_defs.iter_mut() {
*value_def = desugar_value_def(arena, arena.alloc(*value_def));
}
}
/// Reorder the expression tree based on operator precedence and associativity rules, /// Reorder the expression tree based on operator precedence and associativity rules,
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes. /// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc<Expr<'a>> { pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc<Expr<'a>> {

View file

@ -3,37 +3,49 @@ use crate::pattern::fmt_pattern;
use crate::spaces::{fmt_spaces, INDENT}; use crate::spaces::{fmt_spaces, INDENT};
use crate::Buf; use crate::Buf;
use roc_parse::ast::{ use roc_parse::ast::{
AbilityMember, Def, Expr, ExtractSpaces, Pattern, TypeAnnotation, TypeHeader, AbilityMember, Def, Defs, Expr, ExtractSpaces, Pattern, TypeAnnotation, TypeDef, TypeHeader,
ValueDef,
}; };
use roc_region::all::Loc; use roc_region::all::Loc;
/// A Located formattable value is also formattable /// A Located formattable value is also formattable
impl<'a> Formattable for Def<'a> {
impl<'a> Formattable for Defs<'a> {
fn is_multiline(&self) -> bool {
!self.tags.is_empty()
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
for (index, def) in self.defs().enumerate() {
let spaces_before = &self.spaces[self.space_before[index].indices()];
let spaces_after = &self.spaces[self.space_after[index].indices()];
fmt_spaces(buf, spaces_before.iter(), indent);
match def {
Ok(type_def) => type_def.format(buf, indent),
Err(value_def) => value_def.format(buf, indent),
}
fmt_spaces(buf, spaces_after.iter(), indent);
}
}
}
impl<'a> Formattable for TypeDef<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
use roc_parse::ast::Def::*;
use roc_parse::ast::TypeDef::*; use roc_parse::ast::TypeDef::*;
use roc_parse::ast::ValueDef::*;
match self { match self {
Type(def) => match def { Alias { ann, .. } => ann.is_multiline(),
Alias { ann, .. } => ann.is_multiline(), Opaque { typ, .. } => typ.is_multiline(),
Opaque { typ, .. } => typ.is_multiline(), Ability { members, .. } => members.iter().any(|d| d.is_multiline()),
Ability { members, .. } => members.iter().any(|d| d.is_multiline()),
},
Value(def) => match def {
Annotation(loc_pattern, loc_annotation) => {
loc_pattern.is_multiline() || loc_annotation.is_multiline()
}
Body(loc_pattern, loc_expr) => {
loc_pattern.is_multiline() || loc_expr.is_multiline()
}
AnnotatedBody { .. } => true,
Expect(loc_expr) => loc_expr.is_multiline(),
},
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
spaces.iter().any(|s| s.is_comment()) || sub_def.is_multiline()
}
NotYetImplemented(s) => todo!("{}", s),
} }
} }
@ -44,218 +56,268 @@ impl<'a> Formattable for Def<'a> {
_newlines: Newlines, _newlines: Newlines,
indent: u16, indent: u16,
) { ) {
use roc_parse::ast::Def::*;
use roc_parse::ast::TypeDef::*; use roc_parse::ast::TypeDef::*;
match self {
Alias {
header: TypeHeader { name, vars },
ann,
} => {
buf.indent(indent);
buf.push_str(name.value);
for var in *vars {
buf.spaces(1);
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
}
buf.push_str(" :");
buf.spaces(1);
ann.format(buf, indent + INDENT)
}
Opaque {
header: TypeHeader { name, vars },
typ: ann,
derived,
} => {
buf.indent(indent);
buf.push_str(name.value);
for var in *vars {
buf.spaces(1);
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
}
buf.push_str(" :=");
buf.spaces(1);
let ann_is_where_clause =
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
let ann_has_spaces_before = matches!(&ann.value, TypeAnnotation::SpaceBefore(..));
// Always put the has-derived clause on a newline if it is itself multiline, or
// the annotation has a where-has clause.
let derived_multiline = if let Some(derived) = derived {
!derived.value.is_empty() && (derived.is_multiline() || ann_is_where_clause)
} else {
false
};
let make_multiline = ann.is_multiline() || derived_multiline;
// If the annotation has spaces before, a newline will already be printed.
if make_multiline && !ann_has_spaces_before {
buf.newline();
buf.indent(indent + INDENT);
}
ann.format(buf, indent + INDENT);
if let Some(derived) = derived {
if !make_multiline {
buf.spaces(1);
}
derived.format_with_options(
buf,
Parens::NotNeeded,
Newlines::from_bool(make_multiline),
indent + INDENT,
);
}
}
Ability {
header: TypeHeader { name, vars },
loc_has: _,
members,
} => {
buf.indent(indent);
buf.push_str(name.value);
for var in *vars {
buf.spaces(1);
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
}
buf.push_str(" has");
if !self.is_multiline() {
debug_assert_eq!(members.len(), 1);
buf.push_str(" ");
members[0].format(buf, indent + INDENT);
} else {
for demand in members.iter() {
buf.newline();
buf.indent(indent + INDENT);
demand.format(buf, indent + INDENT);
}
}
}
}
}
}
impl<'a> Formattable for ValueDef<'a> {
fn is_multiline(&self) -> bool {
use roc_parse::ast::ValueDef::*; use roc_parse::ast::ValueDef::*;
match self { match self {
Type(def) => match def { Annotation(loc_pattern, loc_annotation) => {
Alias { loc_pattern.is_multiline() || loc_annotation.is_multiline()
header: TypeHeader { name, vars }, }
ann, Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
} => { AnnotatedBody { .. } => true,
buf.indent(indent); Expect(loc_expr) => loc_expr.is_multiline(),
buf.push_str(name.value); }
}
for var in *vars { fn format_with_options<'buf>(
buf.spaces(1); &self,
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); buf: &mut Buf<'buf>,
} _parens: Parens,
_newlines: Newlines,
indent: u16,
) {
use roc_parse::ast::ValueDef::*;
match self {
Annotation(loc_pattern, loc_annotation) => {
loc_pattern.format(buf, indent);
if loc_annotation.is_multiline() {
buf.push_str(" :"); buf.push_str(" :");
buf.spaces(1);
ann.format(buf, indent + INDENT) let should_outdent = match loc_annotation.value {
} TypeAnnotation::SpaceBefore(sub_def, spaces) => match sub_def {
Opaque { TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => {
header: TypeHeader { name, vars }, let is_only_newlines = spaces.iter().all(|s| s.is_newline());
typ: ann, is_only_newlines && sub_def.is_multiline()
derived, }
} => { _ => false,
buf.indent(indent); },
buf.push_str(name.value); TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => true,
_ => false,
for var in *vars {
buf.spaces(1);
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
}
buf.push_str(" :=");
buf.spaces(1);
let ann_is_where_clause =
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
let ann_has_spaces_before =
matches!(&ann.value, TypeAnnotation::SpaceBefore(..));
// Always put the has-derived clause on a newline if it is itself multiline, or
// the annotation has a where-has clause.
let derived_multiline = if let Some(derived) = derived {
!derived.value.is_empty() && (derived.is_multiline() || ann_is_where_clause)
} else {
false
}; };
let make_multiline = ann.is_multiline() || derived_multiline; if should_outdent {
// If the annotation has spaces before, a newline will already be printed.
if make_multiline && !ann_has_spaces_before {
buf.newline();
buf.indent(indent + INDENT);
}
ann.format(buf, indent + INDENT);
if let Some(derived) = derived {
if !make_multiline {
buf.spaces(1);
}
derived.format_with_options(
buf,
Parens::NotNeeded,
Newlines::from_bool(make_multiline),
indent + INDENT,
);
}
}
Ability {
header: TypeHeader { name, vars },
loc_has: _,
members,
} => {
buf.indent(indent);
buf.push_str(name.value);
for var in *vars {
buf.spaces(1); buf.spaces(1);
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); match loc_annotation.value {
} TypeAnnotation::SpaceBefore(sub_def, _) => {
sub_def.format_with_options(
buf.push_str(" has"); buf,
Parens::NotNeeded,
if !self.is_multiline() { Newlines::No,
debug_assert_eq!(members.len(), 1); indent,
buf.push_str(" "); );
members[0].format(buf, indent + INDENT); }
} else { _ => {
for demand in members.iter() { loc_annotation.format_with_options(
buf.newline(); buf,
buf.indent(indent + INDENT); Parens::NotNeeded,
demand.format(buf, indent + INDENT); Newlines::No,
} indent,
} );
}
},
Value(def) => match def {
Annotation(loc_pattern, loc_annotation) => {
loc_pattern.format(buf, indent);
if loc_annotation.is_multiline() {
buf.push_str(" :");
let should_outdent = match loc_annotation.value {
TypeAnnotation::SpaceBefore(sub_def, spaces) => match sub_def {
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => {
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
is_only_newlines && sub_def.is_multiline()
}
_ => false,
},
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => true,
_ => false,
};
if should_outdent {
buf.spaces(1);
match loc_annotation.value {
TypeAnnotation::SpaceBefore(sub_def, _) => {
sub_def.format_with_options(
buf,
Parens::NotNeeded,
Newlines::No,
indent,
);
}
_ => {
loc_annotation.format_with_options(
buf,
Parens::NotNeeded,
Newlines::No,
indent,
);
}
} }
} else {
loc_annotation.format_with_options(
buf,
Parens::NotNeeded,
Newlines::Yes,
indent + INDENT,
);
} }
} else { } else {
buf.spaces(1);
buf.push_str(":");
buf.spaces(1);
loc_annotation.format_with_options( loc_annotation.format_with_options(
buf,
Parens::NotNeeded,
Newlines::No,
indent,
);
}
}
Body(loc_pattern, loc_expr) => {
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
}
Expect(condition) => fmt_expect(buf, condition, self.is_multiline(), indent),
AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr,
} => {
let is_type_multiline = ann_type.is_multiline();
let is_type_function = matches!(
ann_type.value,
TypeAnnotation::Function(..)
| TypeAnnotation::SpaceBefore(TypeAnnotation::Function(..), ..)
| TypeAnnotation::SpaceAfter(TypeAnnotation::Function(..), ..)
);
let next_indent = if is_type_multiline {
indent + INDENT
} else {
indent
};
ann_pattern.format(buf, indent);
buf.push_str(" :");
if is_type_multiline && is_type_function {
ann_type.format_with_options(
buf, buf,
Parens::NotNeeded, Parens::NotNeeded,
Newlines::Yes, Newlines::Yes,
next_indent, indent + INDENT,
); );
} else {
buf.spaces(1);
ann_type.format(buf, indent);
} }
} else {
if let Some(comment_str) = comment { buf.spaces(1);
buf.push_str(" #"); buf.push_str(":");
buf.spaces(1); buf.spaces(1);
buf.push_str(comment_str.trim()); loc_annotation.format_with_options(
} buf,
Parens::NotNeeded,
buf.newline(); Newlines::No,
fmt_body(buf, &body_pattern.value, &body_expr.value, indent); indent,
);
} }
}, }
Body(loc_pattern, loc_expr) => {
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
}
Expect(condition) => fmt_expect(buf, condition, self.is_multiline(), indent),
AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr,
} => {
let is_type_multiline = ann_type.is_multiline();
let is_type_function = matches!(
ann_type.value,
TypeAnnotation::Function(..)
| TypeAnnotation::SpaceBefore(TypeAnnotation::Function(..), ..)
| TypeAnnotation::SpaceAfter(TypeAnnotation::Function(..), ..)
);
let next_indent = if is_type_multiline {
indent + INDENT
} else {
indent
};
ann_pattern.format(buf, indent);
buf.push_str(" :");
if is_type_multiline && is_type_function {
ann_type.format_with_options(
buf,
Parens::NotNeeded,
Newlines::Yes,
next_indent,
);
} else {
buf.spaces(1);
ann_type.format(buf, indent);
}
if let Some(comment_str) = comment {
buf.push_str(" #");
buf.spaces(1);
buf.push_str(comment_str.trim());
}
buf.newline();
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
}
}
}
}
impl<'a> Formattable for Def<'a> {
fn is_multiline(&self) -> bool {
use roc_parse::ast::Def::*;
match self {
Type(def) => def.is_multiline(),
Value(def) => def.is_multiline(),
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
spaces.iter().any(|s| s.is_comment()) || sub_def.is_multiline()
}
NotYetImplemented(s) => todo!("{}", s),
}
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
parens: Parens,
newlines: Newlines,
indent: u16,
) {
use roc_parse::ast::Def::*;
match self {
Type(def) => def.format_with_options(buf, parens, newlines, indent),
Value(def) => def.format_with_options(buf, parens, newlines, indent),
SpaceBefore(sub_def, spaces) => { SpaceBefore(sub_def, spaces) => {
fmt_spaces(buf, spaces.iter(), indent); fmt_spaces(buf, spaces.iter(), indent);
@ -290,6 +352,22 @@ pub fn fmt_def<'a, 'buf>(buf: &mut Buf<'buf>, def: &Def<'a>, indent: u16) {
def.format(buf, indent); def.format(buf, indent);
} }
pub fn fmt_value_def<'a, 'buf>(
buf: &mut Buf<'buf>,
def: &roc_parse::ast::ValueDef<'a>,
indent: u16,
) {
def.format(buf, indent);
}
pub fn fmt_type_def<'a, 'buf>(buf: &mut Buf<'buf>, def: &roc_parse::ast::TypeDef<'a>, indent: u16) {
def.format(buf, indent);
}
pub fn fmt_toplevel_defs<'a, 'buf>(buf: &mut Buf<'buf>, defs: &Defs<'a>, indent: u16) {
defs.format(buf, indent);
}
pub fn fmt_body<'a, 'buf>( pub fn fmt_body<'a, 'buf>(
buf: &mut Buf<'buf>, buf: &mut Buf<'buf>,
pattern: &'a Pattern<'a>, pattern: &'a Pattern<'a>,

View file

@ -116,7 +116,6 @@ impl<'a> Formattable for Expr<'a> {
) { ) {
use self::Expr::*; use self::Expr::*;
//dbg!(self);
let apply_needs_parens = parens == Parens::InApply; let apply_needs_parens = parens == Parens::InApply;
match self { match self {

View file

@ -10,13 +10,12 @@ pub mod pattern;
pub mod spaces; pub mod spaces;
use bumpalo::{collections::String, Bump}; use bumpalo::{collections::String, Bump};
use roc_parse::ast::{Def, Module}; use roc_parse::ast::Module;
use roc_region::all::Loc;
#[derive(Debug, PartialEq)] #[derive(Debug)]
pub struct Ast<'a> { pub struct Ast<'a> {
pub module: Module<'a>, pub module: Module<'a>,
pub defs: bumpalo::collections::vec::Vec<'a, Loc<Def<'a>>>, pub defs: roc_parse::ast::Defs<'a>,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -184,10 +184,16 @@ impl<'a> RemoveSpaces<'a> for Ast<'a> {
Ast { Ast {
module: self.module.remove_spaces(arena), module: self.module.remove_spaces(arena),
defs: { defs: {
let mut defs = Vec::with_capacity_in(self.defs.len(), arena); let mut defs = self.defs.clone();
for d in &self.defs {
defs.push(d.remove_spaces(arena)) for type_def in defs.type_defs.iter_mut() {
*type_def = type_def.remove_spaces(arena);
} }
for value_def in defs.value_defs.iter_mut() {
*value_def = value_def.remove_spaces(arena);
}
defs defs
}, },
} }

View file

@ -7,7 +7,7 @@ extern crate roc_fmt;
mod test_fmt { mod test_fmt {
use bumpalo::Bump; use bumpalo::Bump;
use roc_fmt::annotation::{Formattable, Newlines, Parens}; use roc_fmt::annotation::{Formattable, Newlines, Parens};
use roc_fmt::def::fmt_def; use roc_fmt::def::fmt_toplevel_defs;
use roc_fmt::module::fmt_module; use roc_fmt::module::fmt_module;
use roc_fmt::Buf; use roc_fmt::Buf;
use roc_parse::ast::Module; use roc_parse::ast::Module;
@ -88,11 +88,12 @@ mod test_fmt {
match module_defs().parse(arena, state) { match module_defs().parse(arena, state) {
Ok((_, loc_defs, _)) => { Ok((_, loc_defs, _)) => {
for loc_def in loc_defs { fmt_toplevel_defs(buf, &loc_defs, 0);
fmt_def(buf, arena.alloc(loc_def.value), 0);
}
} }
Err(error) => panic!("Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error) Err(error) => panic!(
r"Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n",
src, error
),
} }
} }

View file

@ -4,10 +4,9 @@ use crate::file::LoadedModule;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
use roc_module::symbol::IdentIds; use roc_module::symbol::IdentIds;
use roc_parse::ast::AssignedField;
use roc_parse::ast::{self, ExtractSpaces, TypeHeader}; use roc_parse::ast::{self, ExtractSpaces, TypeHeader};
use roc_parse::ast::{AssignedField, Def};
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef}; use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
use roc_region::all::Loc;
// Documentation generation requirements // Documentation generation requirements
@ -97,22 +96,12 @@ pub struct Tag {
pub values: Vec<TypeAnnotation>, pub values: Vec<TypeAnnotation>,
} }
pub fn generate_module_docs<'a>( pub fn generate_module_docs(
scope: Scope, scope: Scope,
module_name: ModuleName, module_name: ModuleName,
parsed_defs: &'a [Loc<ast::Def<'a>>], parsed_defs: &roc_parse::ast::Defs,
) -> ModuleDocumentation { ) -> ModuleDocumentation {
let (entries, _) = let entries = generate_entry_docs(&scope.locals.ident_ids, parsed_defs);
parsed_defs
.iter()
.fold((vec![], None), |(acc, maybe_comments_after), def| {
generate_entry_doc(
&scope.locals.ident_ids,
acc,
maybe_comments_after,
&def.value,
)
});
ModuleDocumentation { ModuleDocumentation {
name: module_name.as_str().to_string(), name: module_name.as_str().to_string(),
@ -148,176 +137,152 @@ fn detached_docs_from_comments_and_new_lines<'a>(
detached_docs detached_docs
} }
fn generate_entry_doc<'a>( fn generate_entry_docs<'a>(
ident_ids: &'a IdentIds, ident_ids: &'a IdentIds,
mut acc: Vec<DocEntry>, defs: &roc_parse::ast::Defs<'a>,
before_comments_or_new_lines: Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>, ) -> Vec<DocEntry> {
def: &'a ast::Def<'a>,
) -> (
Vec<DocEntry>,
Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>,
) {
use roc_parse::ast::Pattern; use roc_parse::ast::Pattern;
match def { let mut acc = Vec::with_capacity(defs.tags.len());
Def::SpaceBefore(sub_def, comments_or_new_lines) => { let mut before_comments_or_new_lines = None;
// Comments before a definition are attached to the current definition
for detached_doc in detached_docs_from_comments_and_new_lines(comments_or_new_lines) { for (index, either_index) in defs.tags.iter().enumerate() {
acc.push(DetachedDoc(detached_doc)); let spaces_before = &defs.spaces[defs.space_before[index].indices()];
}
generate_entry_doc(ident_ids, acc, Some(comments_or_new_lines), sub_def) for detached_doc in detached_docs_from_comments_and_new_lines(spaces_before) {
acc.push(DetachedDoc(detached_doc));
} }
Def::SpaceAfter(sub_def, comments_or_new_lines) => { match either_index.split() {
let (new_acc, _) = Err(value_index) => match &defs.value_defs[value_index.index()] {
// If there are comments before, attach to this definition ValueDef::Annotation(loc_pattern, loc_ann) => {
generate_entry_doc(ident_ids, acc, before_comments_or_new_lines, sub_def); if let Pattern::Identifier(identifier) = loc_pattern.value {
// Check if the definition is exposed
// Comments after a definition are attached to the next definition if ident_ids.get_id(&identifier.into()).is_some() {
(new_acc, Some(comments_or_new_lines)) let name = identifier.to_string();
} let doc_def = DocDef {
name,
Def::Value(def) => match def { type_annotation: type_to_docs(false, loc_ann.value),
ValueDef::Annotation(loc_pattern, loc_ann) => match loc_pattern.value { type_vars: Vec::new(),
Pattern::Identifier(identifier) => { docs: before_comments_or_new_lines
// Check if the definition is exposed .and_then(comments_or_new_lines_to_docs),
if ident_ids.get_id(&identifier.into()).is_some() { };
let name = identifier.to_string(); acc.push(DocEntry::DocDef(doc_def));
let doc_def = DocDef {
name,
type_annotation: type_to_docs(false, loc_ann.value),
type_vars: Vec::new(),
docs: before_comments_or_new_lines
.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
(acc, None)
}
_ => (acc, None),
},
ValueDef::AnnotatedBody {
ann_pattern,
ann_type,
..
} => match ann_pattern.value {
Pattern::Identifier(identifier) => {
// Check if the definition is exposed
if ident_ids.get_id(&identifier.into()).is_some() {
let doc_def = DocDef {
name: identifier.to_string(),
type_annotation: type_to_docs(false, ann_type.value),
type_vars: Vec::new(),
docs: before_comments_or_new_lines
.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
(acc, None)
}
_ => (acc, None),
},
ValueDef::Body(_, _) => (acc, None),
ValueDef::Expect(c) => todo!("documentation for tests {:?}", c),
},
Def::Type(def) => match def {
TypeDef::Alias {
header: TypeHeader { name, vars },
ann,
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let doc_def = DocDef {
name: name.value.to_string(),
type_annotation: type_to_docs(false, ann.value),
type_vars,
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
(acc, None)
}
TypeDef::Opaque {
header: TypeHeader { name, vars },
..
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let doc_def = DocDef {
name: name.value.to_string(),
type_annotation: TypeAnnotation::NoTypeAnn,
type_vars,
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
(acc, None)
}
TypeDef::Ability {
header: TypeHeader { name, vars },
members,
..
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let members = members
.iter()
.map(|mem| {
let extracted = mem.name.value.extract_spaces();
let (type_annotation, able_variables) =
ability_member_type_to_docs(mem.typ.value);
AbilityMember {
name: extracted.item.to_string(),
type_annotation,
able_variables,
docs: comments_or_new_lines_to_docs(extracted.before),
} }
}) }
.collect(); }
let doc_def = DocDef { ValueDef::AnnotatedBody {
name: name.value.to_string(), ann_pattern,
type_annotation: TypeAnnotation::Ability { members }, ann_type,
type_vars, ..
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), } => {
}; if let Pattern::Identifier(identifier) = ann_pattern.value {
acc.push(DocEntry::DocDef(doc_def)); // Check if the definition is exposed
if ident_ids.get_id(&identifier.into()).is_some() {
let doc_def = DocDef {
name: identifier.to_string(),
type_annotation: type_to_docs(false, ann_type.value),
type_vars: Vec::new(),
docs: before_comments_or_new_lines
.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
}
}
(acc, None) ValueDef::Body(_, _) => (),
}
},
Def::NotYetImplemented(s) => todo!("{}", s), ValueDef::Expect(c) => todo!("documentation for tests {:?}", c),
},
Ok(type_index) => match &defs.type_defs[type_index.index()] {
TypeDef::Alias {
header: TypeHeader { name, vars },
ann,
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let doc_def = DocDef {
name: name.value.to_string(),
type_annotation: type_to_docs(false, ann.value),
type_vars,
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
TypeDef::Opaque {
header: TypeHeader { name, vars },
..
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let doc_def = DocDef {
name: name.value.to_string(),
type_annotation: TypeAnnotation::NoTypeAnn,
type_vars,
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
TypeDef::Ability {
header: TypeHeader { name, vars },
members,
..
} => {
let mut type_vars = Vec::new();
for var in vars.iter() {
if let Pattern::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string());
}
}
let members = members
.iter()
.map(|mem| {
let extracted = mem.name.value.extract_spaces();
let (type_annotation, able_variables) =
ability_member_type_to_docs(mem.typ.value);
AbilityMember {
name: extracted.item.to_string(),
type_annotation,
able_variables,
docs: comments_or_new_lines_to_docs(extracted.before),
}
})
.collect();
let doc_def = DocDef {
name: name.value.to_string(),
type_annotation: TypeAnnotation::Ability { members },
type_vars,
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
};
acc.push(DocEntry::DocDef(doc_def));
}
},
}
before_comments_or_new_lines = Some(&defs.spaces[defs.space_after[index].indices()]);
} }
acc
} }
fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation { fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation {

View file

@ -33,7 +33,7 @@ use roc_mono::ir::{
UpdateModeIds, UpdateModeIds,
}; };
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem}; use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
@ -616,7 +616,7 @@ struct ParsedModule<'a> {
imported_modules: MutMap<ModuleId, Region>, imported_modules: MutMap<ModuleId, Region>,
exposed_ident_ids: IdentIds, exposed_ident_ids: IdentIds,
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
parsed_defs: &'a [Loc<roc_parse::ast::Def<'a>>], parsed_defs: Defs<'a>,
module_name: ModuleNameEnum<'a>, module_name: ModuleNameEnum<'a>,
symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>, symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>,
header_for: HeaderFor<'a>, header_for: HeaderFor<'a>,
@ -3884,6 +3884,9 @@ fn canonicalize_and_constrain<'a>(
// _before has an underscore because it's unused in --release builds // _before has an underscore because it's unused in --release builds
let _before = roc_types::types::get_type_clone_count(); let _before = roc_types::types::get_type_clone_count();
let parsed_defs_for_docs = parsed_defs.clone();
let parsed_defs = arena.alloc(parsed_defs);
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
let module_output = canonicalize_module_defs( let module_output = canonicalize_module_defs(
arena, arena,
@ -3925,7 +3928,7 @@ fn canonicalize_and_constrain<'a>(
let docs = crate::docs::generate_module_docs( let docs = crate::docs::generate_module_docs(
module_output.scope.clone(), module_output.scope.clone(),
name.as_str().into(), name.as_str().into(),
parsed_defs, &parsed_defs_for_docs,
); );
Some(docs) Some(docs)
@ -4025,8 +4028,6 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
} }
}; };
let parsed_defs = parsed_defs.into_bump_slice();
// Record the parse end time once, to avoid checking the time a second time // Record the parse end time once, to avoid checking the time a second time
// immediately afterward (for the beginning of canonicalization). // immediately afterward (for the beginning of canonicalization).
let parse_end = SystemTime::now(); let parse_end = SystemTime::now();

View file

@ -4,6 +4,7 @@ use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PlatformHeader};
use crate::ident::Ident; use crate::ident::Ident;
use bumpalo::collections::{String, Vec}; use bumpalo::collections::{String, Vec};
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_module::called_via::{BinOp, CalledVia, UnaryOp}; use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
@ -333,6 +334,89 @@ pub enum ValueDef<'a> {
Expect(&'a Loc<Expr<'a>>), Expect(&'a Loc<Expr<'a>>),
} }
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Defs<'a> {
pub tags: std::vec::Vec<EitherIndex<TypeDef<'a>, ValueDef<'a>>>,
pub regions: std::vec::Vec<Region>,
pub space_before: std::vec::Vec<Slice<CommentOrNewline<'a>>>,
pub space_after: std::vec::Vec<Slice<CommentOrNewline<'a>>>,
pub spaces: std::vec::Vec<CommentOrNewline<'a>>,
pub type_defs: std::vec::Vec<TypeDef<'a>>,
pub value_defs: std::vec::Vec<ValueDef<'a>>,
}
impl<'a> Defs<'a> {
pub fn defs(&self) -> impl Iterator<Item = Result<&TypeDef<'a>, &ValueDef<'a>>> {
self.tags.iter().map(|tag| match tag.split() {
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
Err(value_index) => Err(&self.value_defs[value_index.index()]),
})
}
pub fn last(&self) -> Option<Result<&TypeDef<'a>, &ValueDef<'a>>> {
self.tags.last().map(|tag| match tag.split() {
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
Err(value_index) => Err(&self.value_defs[value_index.index()]),
})
}
/// NOTE assumes the def itself is pushed already!
fn push_def_help(
&mut self,
tag: EitherIndex<TypeDef<'a>, ValueDef<'a>>,
region: Region,
spaces_before: &[CommentOrNewline<'a>],
spaces_after: &[CommentOrNewline<'a>],
) {
self.tags.push(tag);
self.regions.push(region);
let before = Slice::extend_new(&mut self.spaces, spaces_before.iter().copied());
self.space_before.push(before);
let after = Slice::extend_new(&mut self.spaces, spaces_after.iter().copied());
self.space_after.push(after);
}
pub fn push_value_def(
&mut self,
value_def: ValueDef<'a>,
region: Region,
spaces_before: &[CommentOrNewline<'a>],
spaces_after: &[CommentOrNewline<'a>],
) {
let value_def_index = Index::push_new(&mut self.value_defs, value_def);
let tag = EitherIndex::from_right(value_def_index);
self.push_def_help(tag, region, spaces_before, spaces_after)
}
pub fn replace_with_value_def(
&mut self,
index: usize,
value_def: ValueDef<'a>,
region: Region,
) {
let value_def_index = Index::push_new(&mut self.value_defs, value_def);
let tag = EitherIndex::from_right(value_def_index);
self.tags[index] = tag;
self.regions[index] = region;
}
pub fn push_type_def(
&mut self,
type_def: TypeDef<'a>,
region: Region,
spaces_before: &[CommentOrNewline<'a>],
spaces_after: &[CommentOrNewline<'a>],
) {
let type_def_index = Index::push_new(&mut self.type_defs, type_def);
let tag = EitherIndex::from_left(type_def_index);
self.push_def_help(tag, region, spaces_before, spaces_after)
}
}
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Def<'a> { pub enum Def<'a> {
Type(TypeDef<'a>), Type(TypeDef<'a>),

View file

@ -1,6 +1,6 @@
use crate::ast::{ use crate::ast::{
AssignedField, Collection, CommentOrNewline, Def, Derived, Expr, ExtractSpaces, Has, Pattern, AssignedField, Collection, CommentOrNewline, Def, Defs, Derived, Expr, ExtractSpaces, Has,
Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef, Pattern, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
@ -17,6 +17,7 @@ use crate::state::State;
use crate::type_annotation; use crate::type_annotation;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::soa::Slice;
use roc_module::called_via::{BinOp, CalledVia, UnaryOp}; use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
@ -811,6 +812,337 @@ struct DefState<'a> {
spaces_after: &'a [CommentOrNewline<'a>], spaces_after: &'a [CommentOrNewline<'a>],
} }
fn parse_toplevel_defs_end<'a>(
_options: ExprParseOptions,
start_column: u32,
mut defs: Defs<'a>,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Defs<'a>, EExpr<'a>> {
let min_indent = start_column;
let mut global_state = state;
loop {
let state = global_state;
let initial = state.clone();
let mut spaces_before_current = &[] as &[_];
let state = match space0_e(min_indent, EExpr::IndentStart).parse(arena, state) {
Err((MadeProgress, _, s)) => {
return Err((MadeProgress, EExpr::DefMissingFinalExpr(s.pos()), s));
}
Ok((_, spaces, state)) => {
spaces_before_current = spaces;
state
}
Err((NoProgress, _, state)) => state,
};
let start = state.pos();
match space0_after_e(
crate::pattern::loc_pattern_help(min_indent),
min_indent,
EPattern::IndentEnd,
)
.parse(arena, state.clone())
{
Err((NoProgress, _, _)) => {
match crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect)
.parse(arena, state)
{
Err((_, _, _)) => {
// a hacky way to get expression-based error messages. TODO fix this
return Ok((NoProgress, defs, initial));
}
Ok((_, _, state)) => {
let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent,
EExpr::IndentEnd,
);
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
let end = loc_def_expr.region.end();
let region = Region::new(start, end);
let value_def = ValueDef::Expect(arena.alloc(loc_def_expr));
defs.push_value_def(value_def, region, spaces_before_current, &[]);
global_state = state;
continue;
}
}
}
Err((MadeProgress, _, _)) => {
// a hacky way to get expression-based error messages. TODO fix this
return Ok((NoProgress, defs, initial));
}
Ok((_, loc_pattern, state)) => {
// First let's check whether this is an ability definition.
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> =
match loc_pattern.value {
Pattern::Apply(
Loc {
value: Pattern::Tag(name),
region,
},
args,
) => Some((name, *region, args)),
Pattern::Tag(name) => Some((name, loc_pattern.region, &[])),
_ => None,
};
if let Some((name, name_region, args)) = opt_tag_and_args {
if let Ok((_, loc_has, state)) =
loc_has_parser(min_indent).parse(arena, state.clone())
{
let (_, (type_def, def_region), state) = finish_parsing_ability_def_help(
start_column,
Loc::at(name_region, name),
args,
loc_has,
arena,
state,
)?;
defs.push_type_def(type_def, def_region, spaces_before_current, &[]);
global_state = state;
continue;
}
}
// Otherwise, this is a def or alias.
match operator().parse(arena, state) {
Ok((_, BinOp::Assignment, state)) => {
let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent,
EExpr::IndentEnd,
);
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
{
let region =
Region::span_across(&loc_pattern.region, &loc_def_expr.region);
if spaces_before_current.len() <= 1 {
let comment = match spaces_before_current.get(0) {
Some(CommentOrNewline::LineComment(s)) => Some(*s),
Some(CommentOrNewline::DocComment(s)) => Some(*s),
_ => None,
};
match defs.last() {
Some(Err(ValueDef::Annotation(ann_pattern, ann_type))) => {
// join this body with the preceding annotation
let value_def = ValueDef::AnnotatedBody {
ann_pattern: arena.alloc(*ann_pattern),
ann_type: arena.alloc(*ann_type),
comment,
body_pattern: arena.alloc(loc_pattern),
body_expr: &*arena.alloc(loc_def_expr),
};
let region =
Region::span_across(&ann_pattern.region, &region);
defs.replace_with_value_def(
defs.tags.len() - 1,
value_def,
region,
)
}
Some(Ok(TypeDef::Alias {
header,
ann: ann_type,
})) => {
// This is a case like
// UserId x : [UserId Int]
// UserId x = UserId 42
// We optimistically parsed the first line as an alias; we now turn it
// into an annotation.
let loc_name =
arena.alloc(header.name.map(|x| Pattern::Tag(x)));
let ann_pattern = Pattern::Apply(loc_name, header.vars);
let vars_region = Region::across_all(
header.vars.iter().map(|v| &v.region),
);
let region_ann_pattern =
Region::span_across(&loc_name.region, &vars_region);
let loc_ann_pattern =
Loc::at(region_ann_pattern, ann_pattern);
let value_def = ValueDef::AnnotatedBody {
ann_pattern: arena.alloc(loc_ann_pattern),
ann_type: arena.alloc(*ann_type),
comment,
body_pattern: arena.alloc(loc_pattern),
body_expr: &*arena.alloc(loc_def_expr),
};
let region =
Region::span_across(&header.name.region, &region);
defs.replace_with_value_def(
defs.tags.len() - 1,
value_def,
region,
)
}
_ => {
// the previous and current def can't be joined up
let value_def = ValueDef::Body(
arena.alloc(loc_pattern),
&*arena.alloc(loc_def_expr),
);
defs.push_value_def(
value_def,
region,
spaces_before_current,
&[],
)
}
}
} else {
// the previous and current def can't be joined up
let value_def = ValueDef::Body(
arena.alloc(loc_pattern),
&*arena.alloc(loc_def_expr),
);
defs.push_value_def(value_def, region, spaces_before_current, &[])
}
};
global_state = state;
continue;
}
Ok((_, BinOp::IsAliasType, state)) => {
let (_, ann_type, state) =
alias_signature_with_space_before(min_indent + 1)
.parse(arena, state)?;
let region = Region::span_across(&loc_pattern.region, &ann_type.region);
// the previous and current def can't be joined up
match &loc_pattern.value {
Pattern::Apply(
Loc {
value: Pattern::Tag(name),
..
},
alias_arguments,
) => {
let name = Loc::at(loc_pattern.region, *name);
let header = TypeHeader {
name,
vars: alias_arguments,
};
let type_def = TypeDef::Alias {
header,
ann: ann_type,
};
defs.push_type_def(type_def, region, spaces_before_current, &[]);
}
Pattern::Tag(name) => {
let name = Loc::at(loc_pattern.region, *name);
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
let header = TypeHeader {
name,
vars: pattern_arguments,
};
let type_def = TypeDef::Alias {
header,
ann: ann_type,
};
defs.push_type_def(type_def, region, spaces_before_current, &[]);
}
_ => {
let value_def = ValueDef::Annotation(loc_pattern, ann_type);
defs.push_value_def(value_def, region, spaces_before_current, &[]);
}
};
global_state = state;
continue;
}
Ok((_, BinOp::IsOpaqueType, state)) => {
let (_, (signature, derived), state) =
opaque_signature_with_space_before(min_indent + 1)
.parse(arena, state)?;
let region = Region::span_across(&loc_pattern.region, &signature.region);
// the previous and current def can't be joined up
match &loc_pattern.value {
Pattern::Apply(
Loc {
value: Pattern::Tag(name),
..
},
alias_arguments,
) => {
let name = Loc::at(loc_pattern.region, *name);
let header = TypeHeader {
name,
vars: alias_arguments,
};
let type_def = TypeDef::Opaque {
header,
typ: signature,
derived,
};
defs.push_type_def(type_def, region, spaces_before_current, &[]);
}
Pattern::Tag(name) => {
let name = Loc::at(loc_pattern.region, *name);
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
let header = TypeHeader {
name,
vars: pattern_arguments,
};
let type_def = TypeDef::Opaque {
header,
typ: signature,
derived,
};
defs.push_type_def(type_def, region, spaces_before_current, &[]);
}
_ => {
let value_def = ValueDef::Annotation(loc_pattern, signature);
defs.push_value_def(value_def, region, spaces_before_current, &[]);
}
};
global_state = state;
continue;
}
_ => return Ok((MadeProgress, defs, initial)),
}
}
}
}
}
fn parse_defs_end<'a>( fn parse_defs_end<'a>(
options: ExprParseOptions, options: ExprParseOptions,
start_column: u32, start_column: u32,
@ -1309,6 +1641,32 @@ fn finish_parsing_ability_def<'a>(
arena: &'a Bump, arena: &'a Bump,
state: State<'a>, state: State<'a>,
) -> ParseResult<'a, &'a Loc<Def<'a>>, EExpr<'a>> { ) -> ParseResult<'a, &'a Loc<Def<'a>>, EExpr<'a>> {
let (_, (type_def, def_region), state) =
finish_parsing_ability_def_help(start_column, name, args, loc_has, arena, state)?;
let def = Def::Type(type_def);
let loc_def = &*(if spaces_before.is_empty() {
arena.alloc(Loc::at(def_region, def))
} else {
arena.alloc(
arena
.alloc(def)
.with_spaces_before(spaces_before, def_region),
)
});
Ok((MadeProgress, loc_def, state))
}
fn finish_parsing_ability_def_help<'a>(
start_column: u32,
name: Loc<&'a str>,
args: &'a [Loc<Pattern<'a>>],
loc_has: Loc<Has<'a>>,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> {
let mut demands = Vec::with_capacity_in(2, arena); let mut demands = Vec::with_capacity_in(2, arena);
let min_indent_for_demand = start_column + 1; let min_indent_for_demand = start_column + 1;
@ -1347,23 +1705,13 @@ fn finish_parsing_ability_def<'a>(
} }
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region); let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
let def = Def::Type(TypeDef::Ability { let type_def = TypeDef::Ability {
header: TypeHeader { name, vars: args }, header: TypeHeader { name, vars: args },
loc_has, loc_has,
members: demands.into_bump_slice(), members: demands.into_bump_slice(),
}); };
let loc_def = &*(if spaces_before.is_empty() { Ok((MadeProgress, (type_def, def_region), state))
arena.alloc(Loc::at(def_region, def))
} else {
arena.alloc(
arena
.alloc(def)
.with_spaces_before(spaces_before, def_region),
)
});
Ok((MadeProgress, loc_def, state))
} }
fn finish_parsing_ability<'a>( fn finish_parsing_ability<'a>(
@ -1960,6 +2308,43 @@ fn assigned_expr_field_to_pattern_help<'a>(
}) })
} }
pub fn toplevel_defs<'a>(min_indent: u32) -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
move |arena, state: State<'a>| {
let (_, initial_space, state) =
space0_e(min_indent, EExpr::IndentEnd).parse(arena, state)?;
let start_column = state.column();
let options = ExprParseOptions {
accept_multi_backpassing: false,
check_for_arrow: true,
};
let mut output = Defs::default();
let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied());
let (_, mut output, state) =
parse_toplevel_defs_end(options, start_column, output, arena, state)?;
let (_, final_space, state) =
space0_e(start_column, EExpr::IndentEnd).parse(arena, state)?;
if !output.tags.is_empty() {
// add surrounding whitespace
let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied());
debug_assert!(output.space_before[0].is_empty());
output.space_before[0] = before;
let last = output.tags.len() - 1;
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
output.space_after[last] = after;
}
Ok((MadeProgress, output, state))
}
}
pub fn defs<'a>(min_indent: u32) -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, EExpr<'a>> { pub fn defs<'a>(min_indent: u32) -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, EExpr<'a>> {
move |arena, state: State<'a>| { move |arena, state: State<'a>| {
let def_state = DefState { let def_state = DefState {

View file

@ -1,4 +1,4 @@
use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced}; use crate::ast::{Collection, CommentOrNewline, Def, Defs, Module, Spaced};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{ use crate::header::{
package_entry, package_name, AppHeader, ExposedName, HostedHeader, ImportsEntry, package_entry, package_name, AppHeader, ExposedName, HostedHeader, ImportsEntry,
@ -28,7 +28,19 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
} }
#[inline(always)] #[inline(always)]
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, SyntaxError<'a>> { pub fn module_defs<'a>() -> impl Parser<'a, Defs<'a>, SyntaxError<'a>> {
let min_indent = 0;
skip_second!(
specialize_region(
|e, r| SyntaxError::Expr(e, r.start()),
crate::expr::toplevel_defs(min_indent),
),
end_of_file()
)
}
#[inline(always)]
pub fn module_defs_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, SyntaxError<'a>> {
// force that we parse until the end of the input // force that we parse until the end of the input
let min_indent = 0; let min_indent = 0;
skip_second!( skip_second!(

View file

@ -1,11 +1,11 @@
use crate::ast; use crate::ast;
use crate::module::module_defs; use crate::ast::Def;
use crate::module::module_defs_help;
// use crate::module::module_defs; // use crate::module::module_defs;
use crate::parser::Parser; use crate::parser::Parser;
use crate::parser::SourceError; use crate::parser::SourceError;
use crate::parser::SyntaxError; use crate::parser::SyntaxError;
use crate::state::State; use crate::state::State;
use bumpalo::collections::Vec as BumpVec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::Loc; use roc_region::all::Loc;
use roc_region::all::Position; use roc_region::all::Position;
@ -35,10 +35,10 @@ pub fn parse_loc_with<'a>(
pub fn parse_defs_with<'a>( pub fn parse_defs_with<'a>(
arena: &'a Bump, arena: &'a Bump,
input: &'a str, input: &'a str,
) -> Result<BumpVec<'a, Loc<ast::Def<'a>>>, SyntaxError<'a>> { ) -> Result<bumpalo::collections::Vec<'a, Loc<Def<'a>>>, SyntaxError<'a>> {
let state = State::new(input.trim().as_bytes()); let state = State::new(input.trim().as_bytes());
match module_defs().parse(arena, state) { match module_defs_help().parse(arena, state) {
Ok(tuple) => Ok(tuple.1), Ok(tuple) => Ok(tuple.1),
Err(tuple) => Err(tuple.1), Err(tuple) => Err(tuple.1),
} }

View file

@ -1,22 +1,30 @@
[ Defs {
@0-7 SpaceAfter( tags: [
SpaceBefore( Index(2147483648),
Value( ],
Body( regions: [
@0-3 Identifier( @0-7,
"foo", ],
), space_before: [
@6-7 Num( Slice(start = 0, length = 0),
"1", ],
), space_after: [
), Slice(start = 0, length = 1),
), ],
[], spaces: [
LineComment(
" comment after",
), ),
[ ],
LineComment( type_defs: [],
" comment after", value_defs: [
Body(
@0-3 Identifier(
"foo",
), ),
], @6-7 Num(
), "1",
] ),
),
],
}

View file

@ -1,46 +1,54 @@
[ Defs {
@0-24 SpaceAfter( tags: [
SpaceBefore( Index(2147483648),
Value( ],
Body( regions: [
@0-4 Identifier( @0-24,
"main", ],
), space_before: [
@11-24 SpaceBefore( Slice(start = 0, length = 0),
Defs( ],
[ space_after: [
@11-17 Value( Slice(start = 0, length = 1),
Body( ],
@11-12 Identifier( spaces: [
"i", Newline,
), ],
@15-17 Num( type_defs: [],
"64", value_defs: [
), Body(
), @0-4 Identifier(
"main",
),
@11-24 SpaceBefore(
Defs(
[
@11-17 Value(
Body(
@11-12 Identifier(
"i",
),
@15-17 Num(
"64",
), ),
],
@23-24 SpaceBefore(
Var {
module_name: "",
ident: "i",
},
[
Newline,
Newline,
],
), ),
), ),
],
@23-24 SpaceBefore(
Var {
module_name: "",
ident: "i",
},
[ [
Newline, Newline,
Newline,
], ],
), ),
), ),
[
Newline,
],
), ),
[],
), ),
[ ],
Newline, }
],
),
]

View file

@ -1,103 +1,111 @@
[ Defs {
@0-115 SpaceAfter( tags: [
SpaceBefore( Index(2147483648),
Value( ],
Body( regions: [
@0-4 Identifier( @0-115,
"main", ],
), space_before: [
@11-115 SpaceBefore( Slice(start = 0, length = 0),
Defs( ],
[ space_after: [
@43-93 Value( Slice(start = 0, length = 1),
AnnotatedBody { ],
ann_pattern: @11-23 Identifier( spaces: [
"wrappedNotEq", Newline,
), ],
ann_type: @26-38 Function( type_defs: [],
[ value_defs: [
@26-27 BoundVariable( Body(
"a", @0-4 Identifier(
), "main",
@29-30 BoundVariable( ),
"a", @11-115 SpaceBefore(
), Defs(
], [
@34-38 Apply( @43-93 Value(
"", AnnotatedBody {
"Bool", ann_pattern: @11-23 Identifier(
[], "wrappedNotEq",
),
),
comment: None,
body_pattern: @43-55 Identifier(
"wrappedNotEq",
),
body_expr: @58-93 Closure(
[
@59-63 Identifier(
"num1",
),
@65-69 Identifier(
"num2",
),
],
@81-93 SpaceBefore(
BinOps(
[
(
@81-85 Var {
module_name: "",
ident: "num1",
},
@86-88 NotEquals,
),
],
@89-93 Var {
module_name: "",
ident: "num2",
},
),
[
Newline,
],
),
),
},
), ),
], ann_type: @26-38 Function(
@99-115 SpaceBefore(
Apply(
@99-111 Var {
module_name: "",
ident: "wrappedNotEq",
},
[ [
@112-113 Num( @26-27 BoundVariable(
"2", "a",
), ),
@114-115 Num( @29-30 BoundVariable(
"3", "a",
), ),
], ],
Space, @34-38 Apply(
"",
"Bool",
[],
),
), ),
[ comment: None,
Newline, body_pattern: @43-55 Identifier(
Newline, "wrappedNotEq",
], ),
), body_expr: @58-93 Closure(
[
@59-63 Identifier(
"num1",
),
@65-69 Identifier(
"num2",
),
],
@81-93 SpaceBefore(
BinOps(
[
(
@81-85 Var {
module_name: "",
ident: "num1",
},
@86-88 NotEquals,
),
],
@89-93 Var {
module_name: "",
ident: "num2",
},
),
[
Newline,
],
),
),
},
),
],
@99-115 SpaceBefore(
Apply(
@99-111 Var {
module_name: "",
ident: "wrappedNotEq",
},
[
@112-113 Num(
"2",
),
@114-115 Num(
"3",
),
],
Space,
), ),
[ [
Newline, Newline,
Newline,
], ],
), ),
), ),
[
Newline,
],
), ),
[],
), ),
[ ],
Newline, }
],
),
]

View file

@ -1,24 +1,32 @@
[ Defs {
@0-9 SpaceAfter( tags: [
SpaceBefore( Index(0),
Type( ],
Opaque { regions: [
header: TypeHeader { @0-9,
name: @0-3 "Age", ],
vars: [], space_before: [
}, Slice(start = 0, length = 0),
typ: @7-9 Apply( ],
"", space_after: [
"U8", Slice(start = 0, length = 1),
[], ],
), spaces: [
derived: None, Newline,
}, ],
type_defs: [
Opaque {
header: TypeHeader {
name: @0-3 "Age",
vars: [],
},
typ: @7-9 Apply(
"",
"U8",
[],
), ),
[], derived: None,
), },
[ ],
Newline, value_defs: [],
], }
),
]

View file

@ -1,53 +1,61 @@
[ Defs {
@0-53 SpaceAfter( tags: [
SpaceBefore( Index(0),
Type( ],
Opaque { regions: [
header: TypeHeader { @0-53,
name: @0-10 "Bookmark", ],
vars: [ space_before: [
@9-10 Identifier( Slice(start = 0, length = 0),
"a", ],
), space_after: [
], Slice(start = 0, length = 1),
}, ],
typ: @14-53 Record { spaces: [
fields: [ Newline,
@16-28 RequiredValue( ],
@16-23 "chapter", type_defs: [
[], Opaque {
@25-28 Apply( header: TypeHeader {
"", name: @0-10 "Bookmark",
"Str", vars: [
[], @9-10 Identifier(
), "a",
), ),
@30-41 RequiredValue( ],
@30-36 "stanza", },
[], typ: @14-53 Record {
@38-41 Apply( fields: [
"", @16-28 RequiredValue(
"Str", @16-23 "chapter",
[], [],
), @25-28 Apply(
), "",
@43-51 RequiredValue( "Str",
@43-48 "notes", [],
[], ),
@50-51 BoundVariable( ),
"a", @30-41 RequiredValue(
), @30-36 "stanza",
), [],
], @38-41 Apply(
ext: None, "",
}, "Str",
derived: None, [],
}, ),
), ),
[], @43-51 RequiredValue(
), @43-48 "notes",
[ [],
Newline, @50-51 BoundVariable(
], "a",
), ),
] ),
],
ext: None,
},
derived: None,
},
],
value_defs: [],
}

View file

@ -1,65 +1,68 @@
[ Defs {
@12-19 SpaceBefore( tags: [
Value( Index(2147483648),
Body( Index(2147483649),
@12-15 Identifier( Index(2147483650),
"foo", ],
), regions: [
@18-19 Num( @12-19,
"1", @33-43,
@44-57,
],
space_before: [
Slice(start = 0, length = 1),
Slice(start = 1, length = 3),
Slice(start = 4, length = 1),
],
space_after: [
Slice(start = 1, length = 0),
Slice(start = 4, length = 0),
Slice(start = 5, length = 2),
],
spaces: [
LineComment(
" comment 1",
),
Newline,
Newline,
LineComment(
" comment 2",
),
Newline,
Newline,
LineComment(
" comment n",
),
],
type_defs: [],
value_defs: [
Body(
@12-15 Identifier(
"foo",
),
@18-19 Num(
"1",
),
),
Body(
@33-36 Identifier(
"bar",
),
@39-43 Str(
PlainLine(
"hi",
), ),
), ),
), ),
[ Body(
LineComment( @44-47 Identifier(
" comment 1", "baz",
), ),
], @50-57 Str(
), PlainLine(
@33-43 SpaceBefore( "stuff",
Value(
Body(
@33-36 Identifier(
"bar",
),
@39-43 Str(
PlainLine(
"hi",
),
), ),
), ),
), ),
[ ],
Newline, }
Newline,
LineComment(
" comment 2",
),
],
),
@44-57 SpaceAfter(
SpaceBefore(
Value(
Body(
@44-47 Identifier(
"baz",
),
@50-57 Str(
PlainLine(
"stuff",
),
),
),
),
[
Newline,
],
),
[
Newline,
LineComment(
" comment n",
),
],
),
]

View file

@ -3328,8 +3328,6 @@ fn content_to_err_type(
RangedNumber(typ, range) => { RangedNumber(typ, range) => {
let err_type = var_to_err_type(subs, state, typ); let err_type = var_to_err_type(subs, state, typ);
dbg!(range);
if state.context == ErrorTypeContext::ExpandRanges { if state.context == ErrorTypeContext::ExpandRanges {
let mut types = Vec::new(); let mut types = Vec::new();
for var in range.variable_slice() { for var in range.variable_slice() {